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