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