Annotation of qemu/roms/ipxe/src/arch/i386/image/multiboot.c, revision 1.1.1.1

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.