Annotation of qemu/roms/ipxe/src/arch/i386/image/multiboot.c, revision 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.