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