xref: /illumos-gate/usr/src/uts/i86pc/dboot/dboot_multiboot2.c (revision 111b966ed3721c1cee4814c5314de6f3a9c8081d)
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/machparam.h>
23 #include <sys/systm.h>
24 #include <sys/sysmacros.h>
25 #include <sys/multiboot2.h>
26 #include <sys/multiboot2_impl.h>
27 #include <sys/efi.h>
28 
29 struct dboot_multiboot2_iterate_ctx;
30 
31 typedef boolean_t (*dboot_multiboot2_iterate_cb_t)
32 	(int, multiboot_tag_t *, struct dboot_multiboot2_iterate_ctx *);
33 
34 struct dboot_multiboot2_iterate_ctx {
35 	dboot_multiboot2_iterate_cb_t dboot_iter_callback;
36 	int dboot_iter_index;			/* item from set */
37 	uint32_t dboot_iter_tag;		/* tag to search */
38 	multiboot_tag_t *dboot_iter_tagp;	/* search result */
39 };
40 
41 /*
42  * Multiboot2 tag list elements are aligned to MULTIBOOT_TAG_ALIGN.
43  * To get the next item from the list, we first add the tag's size
44  * to the start of the current tag. Next, we round up that address to the
45  * nearest MULTIBOOT_TAG_ALIGN address.
46  */
47 
48 static multiboot_tag_t *
dboot_multiboot2_first_tag(multiboot2_info_header_t * mbi)49 dboot_multiboot2_first_tag(multiboot2_info_header_t *mbi)
50 {
51 	return (&mbi->mbi_tags[0]);
52 }
53 
54 static multiboot_tag_t *
dboot_multiboot2_next_tag(multiboot_tag_t * tag)55 dboot_multiboot2_next_tag(multiboot_tag_t *tag)
56 {
57 	if (tag == NULL || tag->mb_type == MULTIBOOT_TAG_TYPE_END)
58 		return (NULL);
59 
60 	return ((multiboot_tag_t *)P2ROUNDUP((uintptr_t)tag +
61 	    tag->mb_size, MULTIBOOT_TAG_ALIGN));
62 }
63 
64 /*
65  * Walk the tag list until we hit the first instance of a given tag or
66  * the end of the list.
67  * MB2_NEXT_TAG() will return NULL on end of list.
68  */
69 static void *
dboot_multiboot2_find_tag_impl(multiboot_tag_t * tagp,uint32_t tag)70 dboot_multiboot2_find_tag_impl(multiboot_tag_t *tagp, uint32_t tag)
71 {
72 	while (tagp != NULL && tagp->mb_type != tag) {
73 		tagp = dboot_multiboot2_next_tag(tagp);
74 	}
75 	return (tagp);
76 }
77 
78 /*
79  * Walk the entire list to find the first instance of the given tag.
80  */
81 void *
dboot_multiboot2_find_tag(multiboot2_info_header_t * mbi,uint32_t tag)82 dboot_multiboot2_find_tag(multiboot2_info_header_t *mbi, uint32_t tag)
83 {
84 	multiboot_tag_t *tagp = dboot_multiboot2_first_tag(mbi);
85 
86 	return (dboot_multiboot2_find_tag_impl(tagp, tag));
87 }
88 
89 /*
90  * dboot_multiboot2_iterate()
91  *
92  * While most tags in tag list are unique, the modules are specified
93  * one module per tag and therefore we need an mechanism to process
94  * tags in set.
95  *
96  * Arguments:
97  *	mbi: multiboot info header
98  *	data: callback context.
99  *
100  * Return value:
101  *	Processed item count.
102  * Callback returning B_TRUE will terminate the iteration.
103  */
104 static int
dboot_multiboot2_iterate(multiboot2_info_header_t * mbi,struct dboot_multiboot2_iterate_ctx * ctx)105 dboot_multiboot2_iterate(multiboot2_info_header_t *mbi,
106     struct dboot_multiboot2_iterate_ctx *ctx)
107 {
108 	dboot_multiboot2_iterate_cb_t callback = ctx->dboot_iter_callback;
109 	multiboot_tag_t *tagp;
110 	uint32_t tag = ctx->dboot_iter_tag;
111 	int index = 0;
112 
113 	tagp = dboot_multiboot2_find_tag(mbi, tag);
114 	while (tagp != NULL) {
115 		if (callback != NULL) {
116 			if (callback(index, tagp, ctx) == B_TRUE) {
117 				return (index + 1);
118 			}
119 		}
120 		tagp = dboot_multiboot2_next_tag(tagp);
121 		tagp = dboot_multiboot2_find_tag_impl(tagp, tag);
122 		index++;
123 	}
124 	return (index);
125 }
126 
127 char *
dboot_multiboot2_cmdline(multiboot2_info_header_t * mbi)128 dboot_multiboot2_cmdline(multiboot2_info_header_t *mbi)
129 {
130 	multiboot_tag_string_t *tag;
131 
132 	tag = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_CMDLINE);
133 
134 	if (tag != NULL)
135 		return (&tag->mb_string[0]);
136 	else
137 		return (NULL);
138 }
139 
140 /*
141  * Simple callback to index item in set.
142  * Terminates iteration if the indexed item is found.
143  */
144 static boolean_t
dboot_multiboot2_iterate_callback(int index,multiboot_tag_t * tagp,struct dboot_multiboot2_iterate_ctx * ctx)145 dboot_multiboot2_iterate_callback(int index, multiboot_tag_t *tagp,
146     struct dboot_multiboot2_iterate_ctx *ctx)
147 {
148 	if (index == ctx->dboot_iter_index) {
149 		ctx->dboot_iter_tagp = tagp;
150 		return (B_TRUE);
151 	}
152 	return (B_FALSE);
153 }
154 
155 int
dboot_multiboot2_modcount(multiboot2_info_header_t * mbi)156 dboot_multiboot2_modcount(multiboot2_info_header_t *mbi)
157 {
158 	struct dboot_multiboot2_iterate_ctx ctx = {
159 		.dboot_iter_callback = NULL,
160 		.dboot_iter_index = 0,
161 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
162 		.dboot_iter_tagp = NULL
163 	};
164 
165 	return (dboot_multiboot2_iterate(mbi, &ctx));
166 }
167 
168 uint32_t
dboot_multiboot2_modstart(multiboot2_info_header_t * mbi,int index)169 dboot_multiboot2_modstart(multiboot2_info_header_t *mbi, int index)
170 {
171 	multiboot_tag_module_t *tagp;
172 	struct dboot_multiboot2_iterate_ctx ctx = {
173 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
174 		.dboot_iter_index = index,
175 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
176 		.dboot_iter_tagp = NULL
177 	};
178 
179 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
180 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
181 
182 		if (tagp != NULL)
183 			return (tagp->mb_mod_start);
184 	}
185 	return (0);
186 }
187 
188 uint32_t
dboot_multiboot2_modend(multiboot2_info_header_t * mbi,int index)189 dboot_multiboot2_modend(multiboot2_info_header_t *mbi, int index)
190 {
191 	multiboot_tag_module_t *tagp;
192 	struct dboot_multiboot2_iterate_ctx ctx = {
193 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
194 		.dboot_iter_index = index,
195 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
196 		.dboot_iter_tagp = NULL
197 	};
198 
199 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
200 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
201 
202 		if (tagp != NULL)
203 			return (tagp->mb_mod_end);
204 	}
205 	return (0);
206 }
207 
208 char *
dboot_multiboot2_modcmdline(multiboot2_info_header_t * mbi,int index)209 dboot_multiboot2_modcmdline(multiboot2_info_header_t *mbi, int index)
210 {
211 	multiboot_tag_module_t *tagp;
212 	struct dboot_multiboot2_iterate_ctx ctx = {
213 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
214 		.dboot_iter_index = index,
215 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
216 		.dboot_iter_tagp = NULL
217 	};
218 
219 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
220 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
221 
222 		if (tagp != NULL)
223 			return (&tagp->mb_cmdline[0]);
224 	}
225 	return (NULL);
226 }
227 
228 multiboot_tag_mmap_t *
dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t * mbi)229 dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t *mbi)
230 {
231 	return (dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_MMAP));
232 }
233 
234 multiboot_tag_efi_mmap_t *
dboot_multiboot2_get_efi_mmap_tagp(multiboot2_info_header_t * mbi)235 dboot_multiboot2_get_efi_mmap_tagp(multiboot2_info_header_t *mbi)
236 {
237 	multiboot_tag_efi_mmap_t *tagp;
238 
239 	/* Find tag and check the descriptor version. */
240 	tagp = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_EFI_MMAP);
241 	if (tagp != NULL &&
242 	    tagp->mb_descr_vers != EFI_MEMORY_DESCRIPTOR_VERSION)
243 		tagp = NULL;
244 
245 	return (tagp);
246 }
247 
248 boolean_t
dboot_multiboot2_basicmeminfo(multiboot2_info_header_t * mbi,uint32_t * lower,uint32_t * upper)249 dboot_multiboot2_basicmeminfo(multiboot2_info_header_t *mbi,
250     uint32_t *lower, uint32_t *upper)
251 {
252 	multiboot_tag_basic_meminfo_t *mip;
253 
254 	mip = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_BASIC_MEMINFO);
255 	if (mip != NULL) {
256 		*lower = mip->mb_mem_lower;
257 		*upper = mip->mb_mem_upper;
258 		return (B_TRUE);
259 	}
260 	return (B_FALSE);
261 }
262 
263 /*
264  * Return the type of mmap entry referenced by index.
265  */
266 boolean_t
dboot_multiboot2_mmap_get_type(multiboot2_info_header_t * mbi,int index,uint32_t * typep)267 dboot_multiboot2_mmap_get_type(multiboot2_info_header_t *mbi, int index,
268     uint32_t *typep)
269 {
270 	multiboot_tag_mmap_t *mb2_mmap_tagp;
271 	multiboot_mmap_entry_t *mapentp;
272 
273 	if (dboot_multiboot2_mmap_nentries(mbi) < index)
274 		return (B_FALSE);
275 
276 	mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
277 	if (mb2_mmap_tagp == NULL)
278 		return (B_FALSE);
279 
280 	if (dboot_multiboot2_mmap_nentries(mbi) < index)
281 		return (B_FALSE);
282 
283 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
284 	    index * mb2_mmap_tagp->mb_entry_size);
285 	*typep = mapentp->mmap_type;
286 	return (B_TRUE);
287 }
288 
289 /*
290  * Return the length of mmap entry referenced by index.
291  */
292 boolean_t
dboot_multiboot2_mmap_get_length(multiboot2_info_header_t * mbi,int index,uint64_t * lengthp)293 dboot_multiboot2_mmap_get_length(multiboot2_info_header_t *mbi, int index,
294     uint64_t *lengthp)
295 {
296 	multiboot_tag_mmap_t *mb2_mmap_tagp;
297 	multiboot_mmap_entry_t *mapentp;
298 
299 	if (dboot_multiboot2_mmap_nentries(mbi) < index)
300 		return (B_FALSE);
301 
302 	mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
303 	if (mb2_mmap_tagp == NULL)
304 		return (B_FALSE);
305 
306 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
307 	    index * mb2_mmap_tagp->mb_entry_size);
308 	*lengthp = mapentp->mmap_len;
309 	return (B_TRUE);
310 }
311 
312 /*
313  * Return the address from mmap entry referenced by index.
314  */
315 boolean_t
dboot_multiboot2_mmap_get_base(multiboot2_info_header_t * mbi,int index,uint64_t * basep)316 dboot_multiboot2_mmap_get_base(multiboot2_info_header_t *mbi, int index,
317     uint64_t *basep)
318 {
319 	multiboot_tag_mmap_t *mb2_mmap_tagp;
320 	multiboot_mmap_entry_t *mapentp;
321 
322 	if (dboot_multiboot2_mmap_nentries(mbi) < index)
323 		return (B_FALSE);
324 
325 	mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
326 	if (mb2_mmap_tagp == NULL)
327 		return (B_FALSE);
328 
329 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
330 	    index * mb2_mmap_tagp->mb_entry_size);
331 	*basep = mapentp->mmap_addr;
332 	return (B_TRUE);
333 }
334 
335 /*
336  * Count and return the number of mmap entries provided by the tag.
337  */
338 int
dboot_multiboot2_mmap_nentries(multiboot2_info_header_t * mbi)339 dboot_multiboot2_mmap_nentries(multiboot2_info_header_t *mbi)
340 {
341 	multiboot_tag_mmap_t *mb2_mmap_tagp;
342 
343 	mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
344 	if (mb2_mmap_tagp != NULL) {
345 		return ((mb2_mmap_tagp->mb_size -
346 		    offsetof(multiboot_tag_mmap_t, mb_entries)) /
347 		    mb2_mmap_tagp->mb_entry_size);
348 	}
349 	return (0);
350 }
351 
352 /*
353  * Return the type of efi mmap entry referenced by index.
354  */
355 boolean_t
dboot_multiboot2_efi_mmap_get_type(multiboot2_info_header_t * mbi,int index,uint32_t * typep)356 dboot_multiboot2_efi_mmap_get_type(multiboot2_info_header_t *mbi, int index,
357     uint32_t *typep)
358 {
359 	multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
360 	EFI_MEMORY_DESCRIPTOR *mapentp;
361 
362 	if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
363 		return (B_FALSE);
364 
365 	mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
366 	if (mb2_efi_mmap_tagp == NULL)
367 		return (B_FALSE);
368 
369 	mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
370 	    index * mb2_efi_mmap_tagp->mb_descr_size);
371 
372 	*typep = mapentp->Type;
373 	return (B_TRUE);
374 }
375 
376 /*
377  * Return the length of efi mmap entry referenced by index.
378  */
379 boolean_t
dboot_multiboot2_efi_mmap_get_length(multiboot2_info_header_t * mbi,int index,uint64_t * lengthp)380 dboot_multiboot2_efi_mmap_get_length(multiboot2_info_header_t *mbi, int index,
381     uint64_t *lengthp)
382 {
383 	multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
384 	EFI_MEMORY_DESCRIPTOR *mapentp;
385 
386 	if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
387 		return (B_FALSE);
388 
389 	mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
390 	if (mb2_efi_mmap_tagp == NULL)
391 		return (B_FALSE);
392 
393 	mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
394 	    index * mb2_efi_mmap_tagp->mb_descr_size);
395 	*lengthp = mapentp->NumberOfPages << PAGESHIFT;
396 	return (B_TRUE);
397 }
398 
399 /*
400  * Return the address from efi mmap entry referenced by index.
401  */
402 boolean_t
dboot_multiboot2_efi_mmap_get_base(multiboot2_info_header_t * mbi,int index,uint64_t * basep)403 dboot_multiboot2_efi_mmap_get_base(multiboot2_info_header_t *mbi, int index,
404     uint64_t *basep)
405 {
406 	multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
407 	EFI_MEMORY_DESCRIPTOR *mapentp;
408 
409 	if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
410 		return (B_FALSE);
411 
412 	mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
413 	if (mb2_efi_mmap_tagp == NULL)
414 		return (B_FALSE);
415 
416 	mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
417 	    index * mb2_efi_mmap_tagp->mb_descr_size);
418 	*basep = mapentp->PhysicalStart;
419 	return (B_TRUE);
420 }
421 
422 /*
423  * Count and return the number of efi mmap entries provided by the tag.
424  */
425 int
dboot_multiboot2_efi_mmap_nentries(multiboot2_info_header_t * mbi)426 dboot_multiboot2_efi_mmap_nentries(multiboot2_info_header_t *mbi)
427 {
428 	multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
429 
430 	mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
431 	if (mb2_efi_mmap_tagp != NULL) {
432 		return ((mb2_efi_mmap_tagp->mb_size -
433 		    offsetof(multiboot_tag_efi_mmap_t, mb_efi_mmap)) /
434 		    mb2_efi_mmap_tagp->mb_descr_size);
435 	}
436 	return (0);
437 }
438 
439 /*
440  * Return the highest address used by info header.
441  */
442 paddr_t
dboot_multiboot2_highest_addr(multiboot2_info_header_t * mbi)443 dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi)
444 {
445 	return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size);
446 }
447