|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2007 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: /** ! 22: * @file ! 23: * ! 24: * Multiboot image format ! 25: * ! 26: */ ! 27: ! 28: #include <stdio.h> ! 29: #include <errno.h> ! 30: #include <assert.h> ! 31: #include <realmode.h> ! 32: #include <multiboot.h> ! 33: #include <ipxe/uaccess.h> ! 34: #include <ipxe/image.h> ! 35: #include <ipxe/segment.h> ! 36: #include <ipxe/io.h> ! 37: #include <ipxe/elf.h> ! 38: #include <ipxe/init.h> ! 39: #include <ipxe/features.h> ! 40: ! 41: FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 ); ! 42: ! 43: /** ! 44: * Maximum number of modules we will allow for ! 45: * ! 46: * If this has bitten you: sorry. I did have a perfect scheme with a ! 47: * dynamically allocated list of modules on the protected-mode stack, ! 48: * but it was incompatible with some broken OSes that can only access ! 49: * low memory at boot time (even though we kindly set up 4GB flat ! 50: * physical addressing as per the multiboot specification. ! 51: * ! 52: */ ! 53: #define MAX_MODULES 8 ! 54: ! 55: /** ! 56: * Maximum combined length of command lines ! 57: * ! 58: * Again; sorry. Some broken OSes zero out any non-base memory that ! 59: * isn't part of the loaded module set, so we can't just use ! 60: * virt_to_phys(cmdline) to point to the command lines, even though ! 61: * this would comply with the Multiboot spec. ! 62: */ ! 63: #define MB_MAX_CMDLINE 512 ! 64: ! 65: /** Multiboot flags that we support */ ! 66: #define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \ ! 67: MB_FLAG_VIDMODE | MB_FLAG_RAW ) ! 68: ! 69: /** Compulsory feature multiboot flags */ ! 70: #define MB_COMPULSORY_FLAGS 0x0000ffff ! 71: ! 72: /** Optional feature multiboot flags */ ! 73: #define MB_OPTIONAL_FLAGS 0xffff0000 ! 74: ! 75: /** ! 76: * Multiboot flags that we don't support ! 77: * ! 78: * We only care about the compulsory feature flags (bits 0-15); we are ! 79: * allowed to ignore the optional feature flags. ! 80: */ ! 81: #define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS ) ! 82: ! 83: /** A multiboot header descriptor */ ! 84: struct multiboot_header_info { ! 85: /** The actual multiboot header */ ! 86: struct multiboot_header mb; ! 87: /** Offset of header within the multiboot image */ ! 88: size_t offset; ! 89: }; ! 90: ! 91: /** Multiboot module command lines */ ! 92: static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] ); ! 93: #define mb_cmdlines __use_data16 ( mb_cmdlines ) ! 94: ! 95: /** Offset within module command lines */ ! 96: static unsigned int mb_cmdline_offset; ! 97: ! 98: /** ! 99: * Build multiboot memory map ! 100: * ! 101: * @v image Multiboot image ! 102: * @v mbinfo Multiboot information structure ! 103: * @v mbmemmap Multiboot memory map ! 104: * @v limit Maxmimum number of memory map entries ! 105: */ ! 106: static void multiboot_build_memmap ( struct image *image, ! 107: struct multiboot_info *mbinfo, ! 108: struct multiboot_memory_map *mbmemmap, ! 109: unsigned int limit ) { ! 110: struct memory_map memmap; ! 111: unsigned int i; ! 112: ! 113: /* Get memory map */ ! 114: get_memmap ( &memmap ); ! 115: ! 116: /* Translate into multiboot format */ ! 117: memset ( mbmemmap, 0, sizeof ( *mbmemmap ) ); ! 118: for ( i = 0 ; i < memmap.count ; i++ ) { ! 119: if ( i >= limit ) { ! 120: DBGC ( image, "MULTIBOOT %p limit of %d memmap " ! 121: "entries reached\n", image, limit ); ! 122: break; ! 123: } ! 124: mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) - ! 125: sizeof ( mbmemmap[i].size ) ); ! 126: mbmemmap[i].base_addr = memmap.regions[i].start; ! 127: mbmemmap[i].length = ( memmap.regions[i].end - ! 128: memmap.regions[i].start ); ! 129: mbmemmap[i].type = MBMEM_RAM; ! 130: mbinfo->mmap_length += sizeof ( mbmemmap[i] ); ! 131: if ( memmap.regions[i].start == 0 ) ! 132: mbinfo->mem_lower = ( memmap.regions[i].end / 1024 ); ! 133: if ( memmap.regions[i].start == 0x100000 ) ! 134: mbinfo->mem_upper = ( ( memmap.regions[i].end - ! 135: 0x100000 ) / 1024 ); ! 136: } ! 137: } ! 138: ! 139: /** ! 140: * Add command line in base memory ! 141: * ! 142: * @v imgname Image name ! 143: * @v cmdline Command line ! 144: * @ret physaddr Physical address of command line ! 145: */ ! 146: physaddr_t multiboot_add_cmdline ( const char *imgname, const char *cmdline ) { ! 147: char *mb_cmdline; ! 148: ! 149: if ( ! cmdline ) ! 150: cmdline = ""; ! 151: ! 152: /* Copy command line to base memory buffer */ ! 153: mb_cmdline = ( mb_cmdlines + mb_cmdline_offset ); ! 154: mb_cmdline_offset += ! 155: ( snprintf ( mb_cmdline, ! 156: ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ), ! 157: "%s %s", imgname, cmdline ) + 1 ); ! 158: ! 159: /* Truncate to terminating NUL in buffer if necessary */ ! 160: if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) ) ! 161: mb_cmdline_offset = ( sizeof ( mb_cmdlines ) - 1 ); ! 162: ! 163: return virt_to_phys ( mb_cmdline ); ! 164: } ! 165: ! 166: /** ! 167: * Build multiboot module list ! 168: * ! 169: * @v image Multiboot image ! 170: * @v modules Module list to fill, or NULL ! 171: * @ret count Number of modules ! 172: */ ! 173: static unsigned int ! 174: multiboot_build_module_list ( struct image *image, ! 175: struct multiboot_module *modules, ! 176: unsigned int limit ) { ! 177: struct image *module_image; ! 178: struct multiboot_module *module; ! 179: unsigned int count = 0; ! 180: unsigned int insert; ! 181: physaddr_t start; ! 182: physaddr_t end; ! 183: unsigned int i; ! 184: ! 185: /* Add each image as a multiboot module */ ! 186: for_each_image ( module_image ) { ! 187: ! 188: if ( count >= limit ) { ! 189: DBGC ( image, "MULTIBOOT %p limit of %d modules " ! 190: "reached\n", image, limit ); ! 191: break; ! 192: } ! 193: ! 194: /* Do not include kernel image itself as a module */ ! 195: if ( module_image == image ) ! 196: continue; ! 197: ! 198: /* At least some OSes expect the multiboot modules to ! 199: * be in ascending order, so we have to support it. ! 200: */ ! 201: start = user_to_phys ( module_image->data, 0 ); ! 202: end = user_to_phys ( module_image->data, module_image->len ); ! 203: for ( insert = 0 ; insert < count ; insert++ ) { ! 204: if ( start < modules[insert].mod_start ) ! 205: break; ! 206: } ! 207: module = &modules[insert]; ! 208: memmove ( ( module + 1 ), module, ! 209: ( ( count - insert ) * sizeof ( *module ) ) ); ! 210: module->mod_start = start; ! 211: module->mod_end = end; ! 212: module->string = multiboot_add_cmdline ( module_image->name, ! 213: module_image->cmdline ); ! 214: module->reserved = 0; ! 215: ! 216: /* We promise to page-align modules */ ! 217: assert ( ( module->mod_start & 0xfff ) == 0 ); ! 218: ! 219: count++; ! 220: } ! 221: ! 222: /* Dump module configuration */ ! 223: for ( i = 0 ; i < count ; i++ ) { ! 224: DBGC ( image, "MULTIBOOT %p module %d is [%x,%x)\n", ! 225: image, i, modules[i].mod_start, ! 226: modules[i].mod_end ); ! 227: } ! 228: ! 229: return count; ! 230: } ! 231: ! 232: /** ! 233: * The multiboot information structure ! 234: * ! 235: * Kept in base memory because some OSes won't find it elsewhere, ! 236: * along with the other structures belonging to the Multiboot ! 237: * information table. ! 238: */ ! 239: static struct multiboot_info __bss16 ( mbinfo ); ! 240: #define mbinfo __use_data16 ( mbinfo ) ! 241: ! 242: /** The multiboot bootloader name */ ! 243: static char __data16_array ( mb_bootloader_name, [] ) = "iPXE " VERSION; ! 244: #define mb_bootloader_name __use_data16 ( mb_bootloader_name ) ! 245: ! 246: /** The multiboot memory map */ ! 247: static struct multiboot_memory_map ! 248: __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] ); ! 249: #define mbmemmap __use_data16 ( mbmemmap ) ! 250: ! 251: /** The multiboot module list */ ! 252: static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] ); ! 253: #define mbmodules __use_data16 ( mbmodules ) ! 254: ! 255: /** ! 256: * Find multiboot header ! 257: * ! 258: * @v image Multiboot file ! 259: * @v hdr Multiboot header descriptor to fill in ! 260: * @ret rc Return status code ! 261: */ ! 262: static int multiboot_find_header ( struct image *image, ! 263: struct multiboot_header_info *hdr ) { ! 264: uint32_t buf[64]; ! 265: size_t offset; ! 266: unsigned int buf_idx; ! 267: uint32_t checksum; ! 268: ! 269: /* Scan through first 8kB of image file 256 bytes at a time. ! 270: * (Use the buffering to avoid the overhead of a ! 271: * copy_from_user() for every dword.) ! 272: */ ! 273: for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) { ! 274: /* Check for end of image */ ! 275: if ( offset > image->len ) ! 276: break; ! 277: /* Refill buffer if applicable */ ! 278: buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) ); ! 279: if ( buf_idx == 0 ) { ! 280: copy_from_user ( buf, image->data, offset, ! 281: sizeof ( buf ) ); ! 282: } ! 283: /* Check signature */ ! 284: if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC ) ! 285: continue; ! 286: /* Copy header and verify checksum */ ! 287: copy_from_user ( &hdr->mb, image->data, offset, ! 288: sizeof ( hdr->mb ) ); ! 289: checksum = ( hdr->mb.magic + hdr->mb.flags + ! 290: hdr->mb.checksum ); ! 291: if ( checksum != 0 ) ! 292: continue; ! 293: /* Record offset of multiboot header and return */ ! 294: hdr->offset = offset; ! 295: return 0; ! 296: } ! 297: ! 298: /* No multiboot header found */ ! 299: return -ENOEXEC; ! 300: } ! 301: ! 302: /** ! 303: * Load raw multiboot image into memory ! 304: * ! 305: * @v image Multiboot file ! 306: * @v hdr Multiboot header descriptor ! 307: * @ret entry Entry point ! 308: * @ret rc Return status code ! 309: */ ! 310: static int multiboot_load_raw ( struct image *image, ! 311: struct multiboot_header_info *hdr, ! 312: physaddr_t *entry ) { ! 313: size_t offset; ! 314: size_t filesz; ! 315: size_t memsz; ! 316: userptr_t buffer; ! 317: int rc; ! 318: ! 319: /* Sanity check */ ! 320: if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) { ! 321: DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n", ! 322: image ); ! 323: return -EINVAL; ! 324: } ! 325: ! 326: /* Verify and prepare segment */ ! 327: offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr ); ! 328: filesz = ( hdr->mb.load_end_addr ? ! 329: ( hdr->mb.load_end_addr - hdr->mb.load_addr ) : ! 330: ( image->len - offset ) ); ! 331: memsz = ( hdr->mb.bss_end_addr ? ! 332: ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz ); ! 333: buffer = phys_to_user ( hdr->mb.load_addr ); ! 334: if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { ! 335: DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n", ! 336: image, strerror ( rc ) ); ! 337: return rc; ! 338: } ! 339: ! 340: /* Copy image to segment */ ! 341: memcpy_user ( buffer, 0, image->data, offset, filesz ); ! 342: ! 343: /* Record execution entry point */ ! 344: *entry = hdr->mb.entry_addr; ! 345: ! 346: return 0; ! 347: } ! 348: ! 349: /** ! 350: * Load ELF multiboot image into memory ! 351: * ! 352: * @v image Multiboot file ! 353: * @ret entry Entry point ! 354: * @ret rc Return status code ! 355: */ ! 356: static int multiboot_load_elf ( struct image *image, physaddr_t *entry ) { ! 357: int rc; ! 358: ! 359: /* Load ELF image*/ ! 360: if ( ( rc = elf_load ( image, entry ) ) != 0 ) { ! 361: DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n", ! 362: image, strerror ( rc ) ); ! 363: return rc; ! 364: } ! 365: ! 366: return 0; ! 367: } ! 368: ! 369: /** ! 370: * Execute multiboot image ! 371: * ! 372: * @v image Multiboot image ! 373: * @ret rc Return status code ! 374: */ ! 375: static int multiboot_exec ( struct image *image ) { ! 376: struct multiboot_header_info hdr; ! 377: physaddr_t entry; ! 378: int rc; ! 379: ! 380: /* Locate multiboot header, if present */ ! 381: if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) { ! 382: DBGC ( image, "MULTIBOOT %p has no multiboot header\n", ! 383: image ); ! 384: return rc; ! 385: } ! 386: ! 387: /* Abort if we detect flags that we cannot support */ ! 388: if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) { ! 389: DBGC ( image, "MULTIBOOT %p flags %08x not supported\n", ! 390: image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) ); ! 391: return -ENOTSUP; ! 392: } ! 393: ! 394: /* There is technically a bit MB_FLAG_RAW to indicate whether ! 395: * this is an ELF or a raw image. In practice, grub will use ! 396: * the ELF header if present, and Solaris relies on this ! 397: * behaviour. ! 398: */ ! 399: if ( ( ( rc = multiboot_load_elf ( image, &entry ) ) != 0 ) && ! 400: ( ( rc = multiboot_load_raw ( image, &hdr, &entry ) ) != 0 ) ) ! 401: return rc; ! 402: ! 403: /* Populate multiboot information structure */ ! 404: memset ( &mbinfo, 0, sizeof ( mbinfo ) ); ! 405: mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP | ! 406: MBI_FLAG_CMDLINE | MBI_FLAG_MODS ); ! 407: mb_cmdline_offset = 0; ! 408: mbinfo.cmdline = multiboot_add_cmdline ( image->name, image->cmdline ); ! 409: mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules, ! 410: ( sizeof(mbmodules) / sizeof(mbmodules[0]) ) ); ! 411: mbinfo.mods_addr = virt_to_phys ( mbmodules ); ! 412: mbinfo.mmap_addr = virt_to_phys ( mbmemmap ); ! 413: mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name ); ! 414: ! 415: /* Multiboot images may not return and have no callback ! 416: * interface, so shut everything down prior to booting the OS. ! 417: */ ! 418: shutdown_boot(); ! 419: ! 420: /* Build memory map after unhiding bootloader memory regions as part of ! 421: * shutting everything down. ! 422: */ ! 423: multiboot_build_memmap ( image, &mbinfo, mbmemmap, ! 424: ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) ); ! 425: ! 426: /* Jump to OS with flat physical addressing */ ! 427: DBGC ( image, "MULTIBOOT %p starting execution at %lx\n", ! 428: image, entry ); ! 429: __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" ! 430: "call *%%edi\n\t" ! 431: "popl %%ebp\n\t" ) ! 432: : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ), ! 433: "b" ( virt_to_phys ( &mbinfo ) ), ! 434: "D" ( entry ) ! 435: : "ecx", "edx", "esi", "memory" ); ! 436: ! 437: DBGC ( image, "MULTIBOOT %p returned\n", image ); ! 438: ! 439: /* It isn't safe to continue after calling shutdown() */ ! 440: while ( 1 ) {} ! 441: ! 442: return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ ! 443: } ! 444: ! 445: /** ! 446: * Probe multiboot image ! 447: * ! 448: * @v image Multiboot file ! 449: * @ret rc Return status code ! 450: */ ! 451: static int multiboot_probe ( struct image *image ) { ! 452: struct multiboot_header_info hdr; ! 453: int rc; ! 454: ! 455: /* Locate multiboot header, if present */ ! 456: if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) { ! 457: DBGC ( image, "MULTIBOOT %p has no multiboot header\n", ! 458: image ); ! 459: return rc; ! 460: } ! 461: DBGC ( image, "MULTIBOOT %p found header with flags %08x\n", ! 462: image, hdr.mb.flags ); ! 463: ! 464: return 0; ! 465: } ! 466: ! 467: /** Multiboot image type */ ! 468: struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = { ! 469: .name = "Multiboot", ! 470: .probe = multiboot_probe, ! 471: .exec = multiboot_exec, ! 472: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.