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 * 49 dboot_multiboot2_first_tag(multiboot2_info_header_t *mbi) 50 { 51 return (&mbi->mbi_tags[0]); 52 } 53 54 static multiboot_tag_t * 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 * 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 * 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 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 * 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 443 dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi) 444 { 445 return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size); 446 } 447