1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * dboot module utility functions for multiboot 2 tags processing.
18  */
19 
20 #include <sys/inttypes.h>
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/sysmacros.h>
24 #include <sys/multiboot2.h>
25 #include <sys/multiboot2_impl.h>
26 
27 /*
28  * Remove offsetof definition when we have usable sys/stddef.h
29  */
30 #if !defined(offsetof)
31 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
32 #define	offsetof(s, m)	__builtin_offsetof(s, m)
33 #else
34 #define	offsetof(s, m)	((size_t)(&(((s *)0)->m)))
35 #endif
36 #endif /* !offsetof */
37 
38 struct dboot_multiboot2_iterate_ctx;
39 
40 typedef boolean_t (*dboot_multiboot2_iterate_cb_t)
41 	(int, multiboot_tag_t *, struct dboot_multiboot2_iterate_ctx *);
42 
43 struct dboot_multiboot2_iterate_ctx {
44 	dboot_multiboot2_iterate_cb_t dboot_iter_callback;
45 	int dboot_iter_index;			/* item from set */
46 	uint32_t dboot_iter_tag;		/* tag to search */
47 	multiboot_tag_t *dboot_iter_tagp;	/* search result */
48 };
49 
50 /*
51  * Multiboot2 tag list elements are aligned to MULTIBOOT_TAG_ALIGN.
52  * To get the next item from the list, we first add the tag's size
53  * to the start of the current tag. Next, we round up that address to the
54  * nearest MULTIBOOT_TAG_ALIGN address.
55  */
56 
57 static multiboot_tag_t *
58 dboot_multiboot2_first_tag(multiboot2_info_header_t *mbi)
59 {
60 	return (&mbi->mbi_tags[0]);
61 }
62 
63 static multiboot_tag_t *
64 dboot_multiboot2_next_tag(multiboot_tag_t *tag)
65 {
66 	if (tag == NULL || tag->mb_type == MULTIBOOT_TAG_TYPE_END)
67 		return (NULL);
68 
69 	return ((multiboot_tag_t *)P2ROUNDUP((uintptr_t)tag +
70 	    tag->mb_size, MULTIBOOT_TAG_ALIGN));
71 }
72 
73 /*
74  * Walk the tag list until we hit the first instance of a given tag or
75  * the end of the list.
76  * MB2_NEXT_TAG() will return NULL on end of list.
77  */
78 static void *
79 dboot_multiboot2_find_tag_impl(multiboot_tag_t *tagp, uint32_t tag)
80 {
81 	while (tagp != NULL && tagp->mb_type != tag) {
82 		tagp = dboot_multiboot2_next_tag(tagp);
83 	}
84 	return (tagp);
85 }
86 
87 /*
88  * Walk the entire list to find the first instance of the given tag.
89  */
90 void *
91 dboot_multiboot2_find_tag(multiboot2_info_header_t *mbi, uint32_t tag)
92 {
93 	multiboot_tag_t *tagp = dboot_multiboot2_first_tag(mbi);
94 
95 	return (dboot_multiboot2_find_tag_impl(tagp, tag));
96 }
97 
98 /*
99  * dboot_multiboot2_iterate()
100  *
101  * While most tags in tag list are unique, the modules are specified
102  * one module per tag and therefore we need an mechanism to process
103  * tags in set.
104  *
105  * Arguments:
106  *	mbi: multiboot info header
107  *	data: callback context.
108  *
109  * Return value:
110  *	Processed item count.
111  * Callback returning B_TRUE will terminate the iteration.
112  */
113 static int
114 dboot_multiboot2_iterate(multiboot2_info_header_t *mbi,
115     struct dboot_multiboot2_iterate_ctx *ctx)
116 {
117 	dboot_multiboot2_iterate_cb_t callback = ctx->dboot_iter_callback;
118 	multiboot_tag_t *tagp;
119 	uint32_t tag = ctx->dboot_iter_tag;
120 	int index = 0;
121 
122 	tagp = dboot_multiboot2_find_tag(mbi, tag);
123 	while (tagp != NULL) {
124 		if (callback != NULL) {
125 			if (callback(index, tagp, ctx) == B_TRUE) {
126 				return (index + 1);
127 			}
128 		}
129 		tagp = dboot_multiboot2_next_tag(tagp);
130 		tagp = dboot_multiboot2_find_tag_impl(tagp, tag);
131 		index++;
132 	}
133 	return (index);
134 }
135 
136 char *
137 dboot_multiboot2_cmdline(multiboot2_info_header_t *mbi)
138 {
139 	multiboot_tag_string_t *tag;
140 
141 	tag = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_CMDLINE);
142 
143 	if (tag != NULL)
144 		return (&tag->mb_string[0]);
145 	else
146 		return (NULL);
147 }
148 
149 /*
150  * Simple callback to index item in set.
151  * Terminates iteration if the indexed item is found.
152  */
153 static boolean_t
154 dboot_multiboot2_iterate_callback(int index, multiboot_tag_t *tagp,
155     struct dboot_multiboot2_iterate_ctx *ctx)
156 {
157 	if (index == ctx->dboot_iter_index) {
158 		ctx->dboot_iter_tagp = tagp;
159 		return (B_TRUE);
160 	}
161 	return (B_FALSE);
162 }
163 
164 int
165 dboot_multiboot2_modcount(multiboot2_info_header_t *mbi)
166 {
167 	struct dboot_multiboot2_iterate_ctx ctx = {
168 		.dboot_iter_callback = NULL,
169 		.dboot_iter_index = 0,
170 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
171 		.dboot_iter_tagp = NULL
172 	};
173 
174 	return (dboot_multiboot2_iterate(mbi, &ctx));
175 }
176 
177 uint32_t
178 dboot_multiboot2_modstart(multiboot2_info_header_t *mbi, int index)
179 {
180 	multiboot_tag_module_t *tagp;
181 	struct dboot_multiboot2_iterate_ctx ctx = {
182 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
183 		.dboot_iter_index = index,
184 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
185 		.dboot_iter_tagp = NULL
186 	};
187 
188 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
189 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
190 
191 		if (tagp != NULL)
192 			return (tagp->mb_mod_start);
193 	}
194 	return (0);
195 }
196 
197 uint32_t
198 dboot_multiboot2_modend(multiboot2_info_header_t *mbi, int index)
199 {
200 	multiboot_tag_module_t *tagp;
201 	struct dboot_multiboot2_iterate_ctx ctx = {
202 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
203 		.dboot_iter_index = index,
204 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
205 		.dboot_iter_tagp = NULL
206 	};
207 
208 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
209 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
210 
211 		if (tagp != NULL)
212 			return (tagp->mb_mod_end);
213 	}
214 	return (0);
215 }
216 
217 char *
218 dboot_multiboot2_modcmdline(multiboot2_info_header_t *mbi, int index)
219 {
220 	multiboot_tag_module_t *tagp;
221 	struct dboot_multiboot2_iterate_ctx ctx = {
222 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
223 		.dboot_iter_index = index,
224 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
225 		.dboot_iter_tagp = NULL
226 	};
227 
228 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
229 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
230 
231 		if (tagp != NULL)
232 			return (&tagp->mb_cmdline[0]);
233 	}
234 	return (NULL);
235 }
236 
237 multiboot_tag_mmap_t *
238 dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t *mbi)
239 {
240 	return (dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_MMAP));
241 }
242 
243 boolean_t
244 dboot_multiboot2_basicmeminfo(multiboot2_info_header_t *mbi,
245     uint32_t *lower, uint32_t *upper)
246 {
247 	multiboot_tag_basic_meminfo_t *mip;
248 
249 	mip = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_BASIC_MEMINFO);
250 	if (mip != NULL) {
251 		*lower = mip->mb_mem_lower;
252 		*upper = mip->mb_mem_upper;
253 		return (B_TRUE);
254 	}
255 	return (B_FALSE);
256 }
257 
258 /*
259  * Return the type of mmap entry referenced by index.
260  */
261 uint32_t
262 dboot_multiboot2_mmap_get_type(multiboot2_info_header_t *mbi,
263     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
264 {
265 	multiboot_mmap_entry_t *mapentp;
266 
267 	if (mb2_mmap_tagp == NULL)
268 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
269 
270 	if (mb2_mmap_tagp == NULL)
271 		return (0);
272 
273 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
274 		return (0);
275 
276 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
277 	    index * mb2_mmap_tagp->mb_entry_size);
278 	return (mapentp->mmap_type);
279 }
280 
281 /*
282  * Return the length of mmap entry referenced by index.
283  */
284 uint64_t
285 dboot_multiboot2_mmap_get_length(multiboot2_info_header_t *mbi,
286     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
287 {
288 	multiboot_mmap_entry_t *mapentp;
289 
290 	if (mb2_mmap_tagp == NULL)
291 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
292 
293 	if (mb2_mmap_tagp == NULL)
294 		return (0);
295 
296 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
297 		return (0);
298 
299 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
300 	    index * mb2_mmap_tagp->mb_entry_size);
301 	return (mapentp->mmap_len);
302 }
303 
304 /*
305  * Return the address from mmap entry referenced by index.
306  */
307 uint64_t
308 dboot_multiboot2_mmap_get_base(multiboot2_info_header_t *mbi,
309     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
310 {
311 	multiboot_mmap_entry_t *mapentp;
312 
313 	if (mb2_mmap_tagp == NULL)
314 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
315 
316 	if (mb2_mmap_tagp == NULL)
317 		return (0);
318 
319 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
320 		return (0);
321 
322 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
323 	    index * mb2_mmap_tagp->mb_entry_size);
324 	return (mapentp->mmap_addr);
325 }
326 
327 /*
328  * Count and return the number of mmap entries provided by the tag.
329  */
330 int
331 dboot_multiboot2_mmap_nentries(multiboot2_info_header_t *mbi,
332     multiboot_tag_mmap_t *mb2_mmap_tagp)
333 {
334 	if (mb2_mmap_tagp == NULL)
335 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
336 
337 	if (mb2_mmap_tagp != NULL) {
338 		return ((mb2_mmap_tagp->mb_size -
339 		    offsetof(multiboot_tag_mmap_t, mb_entries)) /
340 		    mb2_mmap_tagp->mb_entry_size);
341 	}
342 	return (0);
343 }
344 
345 /*
346  * Return the highest address used by info header.
347  */
348 paddr_t
349 dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi)
350 {
351 	return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size);
352 }
353