From e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Fri, 28 Aug 2015 09:58:54 +0800 Subject: Add qemu 2.4.0 Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang --- qemu/roms/ipxe/src/arch/i386/image/bootsector.c | 137 +++++ qemu/roms/ipxe/src/arch/i386/image/bzimage.c | 665 ++++++++++++++++++++++++ qemu/roms/ipxe/src/arch/i386/image/com32.c | 292 +++++++++++ qemu/roms/ipxe/src/arch/i386/image/comboot.c | 327 ++++++++++++ qemu/roms/ipxe/src/arch/i386/image/elfboot.c | 110 ++++ qemu/roms/ipxe/src/arch/i386/image/initrd.c | 300 +++++++++++ qemu/roms/ipxe/src/arch/i386/image/multiboot.c | 488 +++++++++++++++++ qemu/roms/ipxe/src/arch/i386/image/nbi.c | 427 +++++++++++++++ qemu/roms/ipxe/src/arch/i386/image/pxe_image.c | 129 +++++ qemu/roms/ipxe/src/arch/i386/image/sdi.c | 136 +++++ 10 files changed, 3011 insertions(+) create mode 100644 qemu/roms/ipxe/src/arch/i386/image/bootsector.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/bzimage.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/com32.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/comboot.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/elfboot.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/initrd.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/multiboot.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/nbi.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/pxe_image.c create mode 100644 qemu/roms/ipxe/src/arch/i386/image/sdi.c (limited to 'qemu/roms/ipxe/src/arch/i386/image') diff --git a/qemu/roms/ipxe/src/arch/i386/image/bootsector.c b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c new file mode 100644 index 000000000..9a089e6bb --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * x86 bootsector image format + * + */ + +#include +#include +#include +#include +#include + +/** Vector for storing original INT 18 handler + * + * We do not chain to this vector, so there is no need to place it in + * .text16. + */ +static struct segoff int18_vector; + +/** Vector for storing original INT 19 handler + * + * We do not chain to this vector, so there is no need to place it in + * .text16. + */ +static struct segoff int19_vector; + +/** Restart point for INT 18 or 19 */ +extern void bootsector_exec_fail ( void ); + +/** + * Jump to preloaded bootsector + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @v drive Drive number to pass to boot sector + * @ret rc Return status code + */ +int call_bootsector ( unsigned int segment, unsigned int offset, + unsigned int drive ) { + int discard_b, discard_D, discard_d; + + /* Reset console, since boot sector will probably use it */ + console_reset(); + + DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset ); + + /* Hook INTs 18 and 19 to capture failure paths */ + hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + &int18_vector ); + hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + &int19_vector ); + + /* Boot the loaded sector + * + * We assume that the boot sector may completely destroy our + * real-mode stack, so we preserve everything we need in + * static storage. + */ + __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */ + "popw %%cs:saved_retaddr\n\t" + /* Save stack pointer */ + "movw %%ss, %%ax\n\t" + "movw %%ax, %%cs:saved_ss\n\t" + "movw %%sp, %%cs:saved_sp\n\t" + /* Save frame pointer (gcc bug) */ + "movl %%ebp, %%cs:saved_ebp\n\t" + /* Prepare jump to boot sector */ + "pushw %%bx\n\t" + "pushw %%di\n\t" + /* Clear all registers */ + "xorl %%eax, %%eax\n\t" + "xorl %%ebx, %%ebx\n\t" + "xorl %%ecx, %%ecx\n\t" + /* %edx contains drive number */ + "xorl %%esi, %%esi\n\t" + "xorl %%edi, %%edi\n\t" + "xorl %%ebp, %%ebp\n\t" + "movw %%ax, %%ds\n\t" + "movw %%ax, %%es\n\t" + "movw %%ax, %%fs\n\t" + "movw %%ax, %%gs\n\t" + /* Jump to boot sector */ + "sti\n\t" + "lret\n\t" + /* Preserved variables */ + "\nsaved_ebp: .long 0\n\t" + "\nsaved_ss: .word 0\n\t" + "\nsaved_sp: .word 0\n\t" + "\nsaved_retaddr: .word 0\n\t" + /* Boot failure return point */ + "\nbootsector_exec_fail:\n\t" + /* Restore frame pointer (gcc bug) */ + "movl %%cs:saved_ebp, %%ebp\n\t" + /* Restore stack pointer */ + "movw %%cs:saved_ss, %%ax\n\t" + "movw %%ax, %%ss\n\t" + "movw %%cs:saved_sp, %%sp\n\t" + /* Return via saved address */ + "jmp *%%cs:saved_retaddr\n\t" ) + : "=b" ( discard_b ), "=D" ( discard_D ), + "=d" ( discard_d ) + : "b" ( segment ), "D" ( offset ), + "d" ( drive ) + : "eax", "ecx", "esi" ); + + DBG ( "Booted disk returned via INT 18 or 19\n" ); + + /* Unhook INTs 18 and 19 */ + unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + &int18_vector ); + unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + &int19_vector ); + + return -ECANCELED; +} diff --git a/qemu/roms/ipxe/src/arch/i386/image/bzimage.c b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c new file mode 100644 index 000000000..4865c394c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Linux bzImage image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 ); + +/** + * bzImage context + */ +struct bzimage_context { + /** Boot protocol version */ + unsigned int version; + /** Real-mode kernel portion load segment address */ + unsigned int rm_kernel_seg; + /** Real-mode kernel portion load address */ + userptr_t rm_kernel; + /** Real-mode kernel portion file size */ + size_t rm_filesz; + /** Real-mode heap top (offset from rm_kernel) */ + size_t rm_heap; + /** Command line (offset from rm_kernel) */ + size_t rm_cmdline; + /** Command line maximum length */ + size_t cmdline_size; + /** Real-mode kernel portion total memory size */ + size_t rm_memsz; + /** Non-real-mode kernel portion load address */ + userptr_t pm_kernel; + /** Non-real-mode kernel portion file and memory size */ + size_t pm_sz; + /** Video mode */ + unsigned int vid_mode; + /** Memory limit */ + uint64_t mem_limit; + /** Initrd address */ + physaddr_t ramdisk_image; + /** Initrd size */ + physaddr_t ramdisk_size; + + /** Command line magic block */ + struct bzimage_cmdline cmdline_magic; + /** bzImage header */ + struct bzimage_header bzhdr; +}; + +/** + * Parse bzImage header + * + * @v image bzImage file + * @v bzimg bzImage context + * @v src bzImage to parse + * @ret rc Return status code + */ +static int bzimage_parse_header ( struct image *image, + struct bzimage_context *bzimg, + userptr_t src ) { + unsigned int syssize; + int is_bzimage; + + /* Sanity check */ + if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) { + DBGC ( image, "bzImage %p too short for kernel header\n", + image ); + return -ENOEXEC; + } + + /* Read in header structures */ + memset ( bzimg, 0, sizeof ( *bzimg ) ); + copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET, + sizeof ( bzimg->cmdline_magic ) ); + copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET, + sizeof ( bzimg->bzhdr ) ); + + /* Calculate size of real-mode portion */ + bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ? + bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 ); + if ( bzimg->rm_filesz > image->len ) { + DBGC ( image, "bzImage %p too short for %zd byte of setup\n", + image, bzimg->rm_filesz ); + return -ENOEXEC; + } + bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE; + + /* Calculate size of protected-mode portion */ + bzimg->pm_sz = ( image->len - bzimg->rm_filesz ); + syssize = ( ( bzimg->pm_sz + 15 ) / 16 ); + + /* Check for signatures and determine version */ + if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) { + DBGC ( image, "bzImage %p missing 55AA signature\n", image ); + return -ENOEXEC; + } + if ( bzimg->bzhdr.header == BZI_SIGNATURE ) { + /* 2.00+ */ + bzimg->version = bzimg->bzhdr.version; + } else { + /* Pre-2.00. Check that the syssize field is correct, + * as a guard against accepting arbitrary binary data, + * since the 55AA check is pretty lax. Note that the + * syssize field is unreliable for protocols between + * 2.00 and 2.03 inclusive, so we should not always + * check this field. + */ + bzimg->version = 0x0100; + if ( bzimg->bzhdr.syssize != syssize ) { + DBGC ( image, "bzImage %p bad syssize %x (expected " + "%x)\n", image, bzimg->bzhdr.syssize, syssize ); + return -ENOEXEC; + } + } + + /* Determine image type */ + is_bzimage = ( ( bzimg->version >= 0x0200 ) ? + ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 ); + + /* Calculate load address of real-mode portion */ + bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 ); + bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 ); + + /* Allow space for the stack and heap */ + bzimg->rm_memsz += BZI_STACK_SIZE; + bzimg->rm_heap = bzimg->rm_memsz; + + /* Allow space for the command line */ + bzimg->rm_cmdline = bzimg->rm_memsz; + bzimg->rm_memsz += BZI_CMDLINE_SIZE; + + /* Calculate load address of protected-mode portion */ + bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR + : BZI_LOAD_LOW_ADDR ); + + /* Extract video mode */ + bzimg->vid_mode = bzimg->bzhdr.vid_mode; + + /* Extract memory limit */ + bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ? + bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX ); + + /* Extract command line size */ + bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ? + bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE ); + + DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx " + "cmdlen %zd\n", image, bzimg->version, + user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz, + user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz, + bzimg->cmdline_size ); + + return 0; +} + +/** + * Update bzImage header in loaded kernel + * + * @v image bzImage file + * @v bzimg bzImage context + * @v dst bzImage to update + */ +static void bzimage_update_header ( struct image *image, + struct bzimage_context *bzimg, + userptr_t dst ) { + + /* Set loader type */ + if ( bzimg->version >= 0x0200 ) + bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE; + + /* Set heap end pointer */ + if ( bzimg->version >= 0x0201 ) { + bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 ); + bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP; + } + + /* Set command line */ + if ( bzimg->version >= 0x0202 ) { + bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel, + bzimg->rm_cmdline ); + } else { + bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC; + bzimg->cmdline_magic.offset = bzimg->rm_cmdline; + if ( bzimg->version >= 0x0200 ) + bzimg->bzhdr.setup_move_size = bzimg->rm_memsz; + } + + /* Set video mode */ + bzimg->bzhdr.vid_mode = bzimg->vid_mode; + + /* Set initrd address */ + if ( bzimg->version >= 0x0200 ) { + bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image; + bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size; + } + + /* Write out header structures */ + copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic, + sizeof ( bzimg->cmdline_magic ) ); + copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr, + sizeof ( bzimg->bzhdr ) ); + + DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode ); +} + +/** + * Parse kernel command line for bootloader parameters + * + * @v image bzImage file + * @v bzimg bzImage context + * @v cmdline Kernel command line + * @ret rc Return status code + */ +static int bzimage_parse_cmdline ( struct image *image, + struct bzimage_context *bzimg, + const char *cmdline ) { + char *vga; + char *mem; + + /* Look for "vga=" */ + if ( ( vga = strstr ( cmdline, "vga=" ) ) ) { + vga += 4; + if ( strcmp ( vga, "normal" ) == 0 ) { + bzimg->vid_mode = BZI_VID_MODE_NORMAL; + } else if ( strcmp ( vga, "ext" ) == 0 ) { + bzimg->vid_mode = BZI_VID_MODE_EXT; + } else if ( strcmp ( vga, "ask" ) == 0 ) { + bzimg->vid_mode = BZI_VID_MODE_ASK; + } else { + bzimg->vid_mode = strtoul ( vga, &vga, 0 ); + if ( *vga && ( *vga != ' ' ) ) { + DBGC ( image, "bzImage %p strange \"vga=\"" + "terminator '%c'\n", image, *vga ); + } + } + } + + /* Look for "mem=" */ + if ( ( mem = strstr ( cmdline, "mem=" ) ) ) { + mem += 4; + bzimg->mem_limit = strtoul ( mem, &mem, 0 ); + switch ( *mem ) { + case 'G': + case 'g': + bzimg->mem_limit <<= 10; + case 'M': + case 'm': + bzimg->mem_limit <<= 10; + case 'K': + case 'k': + bzimg->mem_limit <<= 10; + break; + case '\0': + case ' ': + break; + default: + DBGC ( image, "bzImage %p strange \"mem=\" " + "terminator '%c'\n", image, *mem ); + break; + } + bzimg->mem_limit -= 1; + } + + return 0; +} + +/** + * Set command line + * + * @v image bzImage image + * @v bzimg bzImage context + * @v cmdline Kernel command line + */ +static void bzimage_set_cmdline ( struct image *image, + struct bzimage_context *bzimg, + const char *cmdline ) { + size_t cmdline_len; + + /* Copy command line down to real-mode portion */ + cmdline_len = ( strlen ( cmdline ) + 1 ); + if ( cmdline_len > bzimg->cmdline_size ) + cmdline_len = bzimg->cmdline_size; + copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline, + cmdline, cmdline_len ); + DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline ); +} + +/** + * Parse standalone image command line for cpio parameters + * + * @v image bzImage file + * @v cpio CPIO header + * @v cmdline Command line + */ +static void bzimage_parse_cpio_cmdline ( struct image *image, + struct cpio_header *cpio, + const char *cmdline ) { + char *arg; + char *end; + unsigned int mode; + + /* Look for "mode=" */ + if ( ( arg = strstr ( cmdline, "mode=" ) ) ) { + arg += 5; + mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); + if ( *end && ( *end != ' ' ) ) { + DBGC ( image, "bzImage %p strange \"mode=\"" + "terminator '%c'\n", image, *end ); + } + cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); + } +} + +/** + * Align initrd length + * + * @v len Length + * @ret len Length rounded up to INITRD_ALIGN + */ +static inline size_t bzimage_align ( size_t len ) { + + return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) ); +} + +/** + * Load initrd + * + * @v image bzImage image + * @v initrd initrd image + * @v address Address at which to load, or UNULL + * @ret len Length of loaded image, excluding zero-padding + */ +static size_t bzimage_load_initrd ( struct image *image, + struct image *initrd, + userptr_t address ) { + char *filename = initrd->cmdline; + char *cmdline; + struct cpio_header cpio; + size_t offset; + size_t name_len; + size_t pad_len; + + /* Do not include kernel image itself as an initrd */ + if ( initrd == image ) + return 0; + + /* Create cpio header for non-prebuilt images */ + if ( filename && filename[0] ) { + cmdline = strchr ( filename, ' ' ); + name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) ) + : strlen ( filename ) ) + 1 /* NUL */ ); + memset ( &cpio, '0', sizeof ( cpio ) ); + memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) ); + cpio_set_field ( cpio.c_mode, 0100644 ); + cpio_set_field ( cpio.c_nlink, 1 ); + cpio_set_field ( cpio.c_filesize, initrd->len ); + cpio_set_field ( cpio.c_namesize, name_len ); + if ( cmdline ) { + bzimage_parse_cpio_cmdline ( image, &cpio, + ( cmdline + 1 /* ' ' */ )); + } + offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 ); + } else { + offset = 0; + name_len = 0; + } + + /* Copy in initrd image body (and cpio header if applicable) */ + if ( address ) { + memmove_user ( address, offset, initrd->data, 0, initrd->len ); + if ( offset ) { + memset_user ( address, 0, 0, offset ); + copy_to_user ( address, 0, &cpio, sizeof ( cpio ) ); + copy_to_user ( address, sizeof ( cpio ), filename, + ( name_len - 1 /* NUL (or space) */ ) ); + } + DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)" + "%s%s\n", image, initrd, user_to_phys ( address, 0 ), + user_to_phys ( address, offset ), + user_to_phys ( address, ( offset + initrd->len ) ), + ( filename ? " " : "" ), ( filename ? filename : "" ) ); + DBGC2_MD5A ( image, user_to_phys ( address, offset ), + user_to_virt ( address, offset ), initrd->len ); + } + offset += initrd->len; + + /* Zero-pad to next INITRD_ALIGN boundary */ + pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) ); + if ( address ) + memset_user ( address, offset, 0, pad_len ); + + return offset; +} + +/** + * Check that initrds can be loaded + * + * @v image bzImage image + * @v bzimg bzImage context + * @ret rc Return status code + */ +static int bzimage_check_initrds ( struct image *image, + struct bzimage_context *bzimg ) { + struct image *initrd; + userptr_t bottom; + size_t len = 0; + int rc; + + /* Calculate total loaded length of initrds */ + for_each_image ( initrd ) { + + /* Skip kernel */ + if ( initrd == image ) + continue; + + /* Calculate length */ + len += bzimage_load_initrd ( image, initrd, UNULL ); + len = bzimage_align ( len ); + + DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n", + image, initrd, user_to_phys ( initrd->data, 0 ), + user_to_phys ( initrd->data, initrd->len ), + ( initrd->cmdline ? " " : "" ), + ( initrd->cmdline ? initrd->cmdline : "" ) ); + DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ), + user_to_virt ( initrd->data, 0 ), initrd->len ); + } + + /* Calculate lowest usable address */ + bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ); + + /* Check that total length fits within space available for + * reshuffling. This is a conservative check, since CPIO + * headers are not present during reshuffling, but this + * doesn't hurt and keeps the code simple. + */ + if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) { + DBGC ( image, "bzImage %p failed reshuffle check: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Check that total length fits within kernel's memory limit */ + if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) { + DBGC ( image, "bzImage %p not enough space for initrds\n", + image ); + return -ENOBUFS; + } + + return 0; +} + +/** + * Load initrds, if any + * + * @v image bzImage image + * @v bzimg bzImage context + */ +static void bzimage_load_initrds ( struct image *image, + struct bzimage_context *bzimg ) { + struct image *initrd; + struct image *highest = NULL; + struct image *other; + userptr_t top; + userptr_t dest; + size_t offset; + size_t len; + + /* Reshuffle initrds into desired order */ + initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) ); + + /* Find highest initrd */ + for_each_image ( initrd ) { + if ( ( highest == NULL ) || + ( userptr_sub ( initrd->data, highest->data ) > 0 ) ) { + highest = initrd; + } + } + + /* Do nothing if there are no initrds */ + if ( ! highest ) + return; + + /* Find highest usable address */ + top = userptr_add ( highest->data, bzimage_align ( highest->len ) ); + if ( user_to_phys ( top, 0 ) > bzimg->mem_limit ) + top = phys_to_user ( bzimg->mem_limit ); + DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n", + image, user_to_phys ( top, 0 ) ); + + /* Load initrds in order */ + for_each_image ( initrd ) { + + /* Calculate cumulative length of following + * initrds (including padding). + */ + offset = 0; + for_each_image ( other ) { + if ( other == initrd ) + offset = 0; + offset += bzimage_load_initrd ( image, other, UNULL ); + offset = bzimage_align ( offset ); + } + + /* Load initrd at this address */ + dest = userptr_add ( top, -offset ); + len = bzimage_load_initrd ( image, initrd, dest ); + + /* Record initrd location */ + if ( ! bzimg->ramdisk_image ) + bzimg->ramdisk_image = user_to_phys ( dest, 0 ); + bzimg->ramdisk_size = ( user_to_phys ( dest, len ) - + bzimg->ramdisk_image ); + } + DBGC ( image, "bzImage %p initrds at [%#08lx,%#08lx)\n", + image, bzimg->ramdisk_image, + ( bzimg->ramdisk_image + bzimg->ramdisk_size ) ); +} + +/** + * Execute bzImage image + * + * @v image bzImage image + * @ret rc Return status code + */ +static int bzimage_exec ( struct image *image ) { + struct bzimage_context bzimg; + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int rc; + + /* Read and parse header from image */ + if ( ( rc = bzimage_parse_header ( image, &bzimg, + image->data ) ) != 0 ) + return rc; + + /* Prepare segments */ + if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz, + bzimg.rm_memsz ) ) != 0 ) { + DBGC ( image, "bzImage %p could not prepare RM segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz, + bzimg.pm_sz ) ) != 0 ) { + DBGC ( image, "bzImage %p could not prepare PM segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Parse command line for bootloader parameters */ + if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0) + return rc; + + /* Check that initrds can be loaded */ + if ( ( rc = bzimage_check_initrds ( image, &bzimg ) ) != 0 ) + return rc; + + /* Remove kernel from image list (without invalidating image pointer) */ + unregister_image ( image_get ( image ) ); + + /* Load segments */ + memcpy_user ( bzimg.rm_kernel, 0, image->data, + 0, bzimg.rm_filesz ); + memcpy_user ( bzimg.pm_kernel, 0, image->data, + bzimg.rm_filesz, bzimg.pm_sz ); + + /* Store command line */ + bzimage_set_cmdline ( image, &bzimg, cmdline ); + + /* Prepare for exiting. Must do this before loading initrds, + * since loading the initrds will corrupt the external heap. + */ + shutdown_boot(); + + /* Load any initrds */ + bzimage_load_initrds ( image, &bzimg ); + + /* Update kernel header */ + bzimage_update_header ( image, &bzimg, bzimg.rm_kernel ); + + DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " + "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ), + bzimg.rm_kernel_seg, bzimg.rm_heap ); + + /* Jump to the kernel */ + __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + "movw %w0, %%fs\n\t" + "movw %w0, %%gs\n\t" + "movw %w0, %%ss\n\t" + "movw %w1, %%sp\n\t" + "pushw %w2\n\t" + "pushw $0\n\t" + "lret\n\t" ) + : : "r" ( bzimg.rm_kernel_seg ), + "r" ( bzimg.rm_heap ), + "r" ( bzimg.rm_kernel_seg + 0x20 ) ); + + /* There is no way for the image to return, since we provide + * no return address. + */ + assert ( 0 ); + + return -ECANCELED; /* -EIMPOSSIBLE */ +} + +/** + * Probe bzImage image + * + * @v image bzImage file + * @ret rc Return status code + */ +int bzimage_probe ( struct image *image ) { + struct bzimage_context bzimg; + int rc; + + /* Read and parse header from image */ + if ( ( rc = bzimage_parse_header ( image, &bzimg, + image->data ) ) != 0 ) + return rc; + + return 0; +} + +/** Linux bzImage image type */ +struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = { + .name = "bzImage", + .probe = bzimage_probe, + .exec = bzimage_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/com32.c b/qemu/roms/ipxe/src/arch/i386/image/com32.c new file mode 100644 index 000000000..c12ffb684 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/com32.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * @file + * + * SYSLINUX COM32 image format + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Execute COMBOOT image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_exec_loop ( struct image *image ) { + struct memory_map memmap; + unsigned int i; + int state; + uint32_t avail_mem_top; + + state = rmsetjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COM32 program */ + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Find end of block covering COM32 image loading area */ + for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) { + if ( (memmap.regions[i].start <= COM32_START_PHYS) && + (memmap.regions[i].end > COM32_START_PHYS + image->len) ) { + avail_mem_top = memmap.regions[i].end; + break; + } + } + + DBGC ( image, "COM32 %p: available memory top = 0x%x\n", + image, avail_mem_top ); + + assert ( avail_mem_top != 0 ); + + com32_external_esp = phys_to_virt ( avail_mem_top ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts(); + + /* Unregister image, so that a "boot" command doesn't + * throw us into an execution loop. We never + * reregister ourselves; COMBOOT images expect to be + * removed on exit. + */ + unregister_image ( image ); + + __asm__ __volatile__ ( + "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ + "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ + "call _virt_to_phys\n\t" /* Switch to flat physical address space */ + "sti\n\t" /* Enable interrupts */ + "pushl %0\n\t" /* Pointer to CDECL helper function */ + "pushl %1\n\t" /* Pointer to FAR call helper function */ + "pushl %2\n\t" /* Size of low memory bounce buffer */ + "pushl %3\n\t" /* Pointer to low memory bounce buffer */ + "pushl %4\n\t" /* Pointer to INT call helper function */ + "pushl %5\n\t" /* Pointer to the command line arguments */ + "pushl $6\n\t" /* Number of additional arguments */ + "call *%6\n\t" /* Execute image */ + "cli\n\t" /* Disable interrupts */ + "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ + "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ + : + : + /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), + /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), + /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + /* %5 */ "r" ( virt_to_phys ( image->cmdline ? + image->cmdline : "" ) ), + /* %6 */ "r" ( COM32_START_PHYS ) + : + "memory" ); + DBGC ( image, "COM32 %p: returned\n", image ); + break; + + case COMBOOT_EXIT: + DBGC ( image, "COM32 %p: exited\n", image ); + break; + + case COMBOOT_EXIT_RUN_KERNEL: + assert ( image->replacement ); + DBGC ( image, "COM32 %p: exited to run kernel %s\n", + image, image->replacement->name ); + break; + + case COMBOOT_EXIT_COMMAND: + DBGC ( image, "COM32 %p: exited after executing command\n", + image ); + break; + + default: + assert ( 0 ); + break; + } + + unhook_comboot_interrupts(); + comboot_force_text_mode(); + + return 0; +} + +/** + * Check image name extension + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_identify ( struct image *image ) { + const char *ext; + static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; + uint8_t buf[5]; + + if ( image->len >= 5 ) { + /* Check for magic number + * mov eax,21cd4cffh + * B8 FF 4C CD 21 + */ + copy_from_user ( buf, image->data, 0, sizeof(buf) ); + if ( ! memcmp ( buf, magic, sizeof(buf) ) ) { + DBGC ( image, "COM32 %p: found magic number\n", + image ); + return 0; + } + } + + /* Magic number not found; check filename extension */ + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COM32 %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "c32" ) ) { + DBGC ( image, "COM32 %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + + +/** + * Load COM32 image into memory + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_load_image ( struct image *image ) { + size_t filesz, memsz; + userptr_t buffer; + int rc; + + filesz = image->len; + memsz = filesz; + buffer = phys_to_user ( COM32_START_PHYS ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, filesz ); + + return 0; +} + +/** + * Prepare COM32 low memory bounce buffer + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_prepare_bounce_buffer ( struct image * image ) { + unsigned int seg; + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + seg = COM32_BOUNCE_SEG; + seg_userptr = real_to_user ( seg, 0 ); + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + filesz = 0; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe COM32 image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_probe ( struct image *image ) { + int rc; + + DBGC ( image, "COM32 %p: name '%s'\n", image, image->name ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = com32_identify ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** + * Execute COMBOOT image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_exec ( struct image *image ) { + int rc; + + /* Load image */ + if ( ( rc = com32_load_image ( image ) ) != 0 ) { + return rc; + } + + /* Prepare bounce buffer segment */ + if ( ( rc = com32_prepare_bounce_buffer ( image ) ) != 0 ) { + return rc; + } + + return com32_exec_loop ( image ); +} + +/** SYSLINUX COM32 image type */ +struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COM32", + .probe = com32_probe, + .exec = com32_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/comboot.c b/qemu/roms/ipxe/src/arch/i386/image/comboot.c new file mode 100644 index 000000000..1ec02331d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/comboot.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * @file + * + * SYSLINUX COMBOOT (16-bit) image format + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 ); + +/** + * COMBOOT PSP, copied to offset 0 of code segment + */ +struct comboot_psp { + /** INT 20 instruction, executed if COMBOOT image returns with RET */ + uint16_t int20; + /** Segment of first non-free paragraph of memory */ + uint16_t first_non_free_para; +}; + +/** Offset in PSP of command line */ +#define COMBOOT_PSP_CMDLINE_OFFSET 0x81 + +/** Maximum length of command line in PSP + * (127 bytes minus space and CR) */ +#define COMBOOT_MAX_CMDLINE_LEN 125 + + +/** + * Copy command line to PSP + * + * @v image COMBOOT image + */ +static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int cmdline_len = strlen ( cmdline ); + if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN ) + cmdline_len = COMBOOT_MAX_CMDLINE_LEN; + uint8_t len_byte = cmdline_len; + char spc = ' ', cr = '\r'; + + /* Copy length to byte before command line */ + copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1, + &len_byte, 1 ); + + /* Command line starts with space */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET, + &spc, 1 ); + + /* Copy command line */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + 1, + cmdline, cmdline_len ); + + /* Command line ends with CR */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1, + &cr, 1 ); +} + +/** + * Initialize PSP + * + * @v image COMBOOT image + * @v seg_userptr segment to initialize + */ +static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) { + struct comboot_psp psp; + + /* Fill PSP */ + + /* INT 20h instruction, byte order reversed */ + psp.int20 = 0x20CD; + + /* get_fbms() returns BIOS free base memory counter, which is in + * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */ + psp.first_non_free_para = get_fbms() << 6; + + DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n", + image, psp.first_non_free_para ); + + /* Copy the PSP to offset 0 of segment. + * The rest of the PSP was already zeroed by + * comboot_prepare_segment. */ + copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) ); + + /* Copy the command line to the PSP */ + comboot_copy_cmdline ( image, seg_userptr ); +} + +/** + * Execute COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_exec_loop ( struct image *image ) { + userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + int state; + + state = rmsetjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COMBOOT program */ + + /* Initialize PSP */ + comboot_init_psp ( image, seg_userptr ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts(); + + DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n", + COMBOOT_PSP_SEG ); + + /* Unregister image, so that a "boot" command doesn't + * throw us into an execution loop. We never + * reregister ourselves; COMBOOT images expect to be + * removed on exit. + */ + unregister_image ( image ); + + /* Store stack segment at 0x38 and stack pointer at 0x3A + * in the PSP and jump to the image */ + __asm__ __volatile__ ( + REAL_CODE ( /* Save return address with segment on old stack */ + "popw %%ax\n\t" + "pushw %%cs\n\t" + "pushw %%ax\n\t" + /* Set DS=ES=segment with image */ + "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + /* Set SS:SP to new stack (end of image segment) */ + "movw %w0, %%ss\n\t" + "xor %%sp, %%sp\n\t" + "pushw $0\n\t" + "pushw %w0\n\t" + "pushw $0x100\n\t" + /* Zero registers (some COM files assume GP regs are 0) */ + "xorw %%ax, %%ax\n\t" + "xorw %%bx, %%bx\n\t" + "xorw %%cx, %%cx\n\t" + "xorw %%dx, %%dx\n\t" + "xorw %%si, %%si\n\t" + "xorw %%di, %%di\n\t" + "xorw %%bp, %%bp\n\t" + "lret\n\t" ) + : : "r" ( COMBOOT_PSP_SEG ) : "eax" ); + DBGC ( image, "COMBOOT %p: returned\n", image ); + break; + + case COMBOOT_EXIT: + DBGC ( image, "COMBOOT %p: exited\n", image ); + break; + + case COMBOOT_EXIT_RUN_KERNEL: + assert ( image->replacement ); + DBGC ( image, "COMBOOT %p: exited to run kernel %s\n", + image, image->replacement->name ); + break; + + case COMBOOT_EXIT_COMMAND: + DBGC ( image, "COMBOOT %p: exited after executing command\n", + image ); + break; + + default: + assert ( 0 ); + break; + } + + unhook_comboot_interrupts(); + comboot_force_text_mode(); + + return 0; +} + +/** + * Check image name extension + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_identify ( struct image *image ) { + const char *ext; + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COMBOOT %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "cbt" ) ) { + DBGC ( image, "COMBOOT %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load COMBOOT image into memory, preparing a segment and returning it + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_prepare_segment ( struct image *image ) +{ + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + /* Load image in segment */ + seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + + /* Allow etra 0x100 bytes before image for PSP */ + filesz = image->len + 0x100; + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Zero PSP */ + memset_user ( seg_userptr, 0, 0, 0x100 ); + + /* Copy image to segment:0100 */ + memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len ); + + return 0; +} + +/** + * Probe COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_probe ( struct image *image ) { + int rc; + + DBGC ( image, "COMBOOT %p: name '%s'\n", + image, image->name ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = comboot_identify ( image ) ) != 0 ) { + + return rc; + } + + return 0; +} + +/** + * Execute COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_exec ( struct image *image ) { + int rc; + + /* Sanity check for filesize */ + if( image->len >= 0xFF00 ) { + DBGC( image, "COMBOOT %p: image too large\n", + image ); + return -ENOEXEC; + } + + /* Prepare segment and load image */ + if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) { + return rc; + } + + return comboot_exec_loop ( image ); +} + +/** SYSLINUX COMBOOT (16-bit) image type */ +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COMBOOT", + .probe = comboot_probe, + .exec = comboot_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/elfboot.c b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c new file mode 100644 index 000000000..0f6957f02 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * ELF bootable image + * + */ + +FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 ); + +/** + * Execute ELF image + * + * @v image ELF image + * @ret rc Return status code + */ +static int elfboot_exec ( struct image *image ) { + physaddr_t entry; + physaddr_t max; + int rc; + + /* Load the image using core ELF support */ + if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) { + DBGC ( image, "ELF %p could not load: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* An ELF image has no callback interface, so we need to shut + * down before invoking it. + */ + shutdown_boot(); + + /* Jump to OS with flat physical addressing */ + DBGC ( image, "ELF %p starting execution at %lx\n", image, entry ); + __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */ + "call *%%edi\n\t" + "popl %%ebp\n\t" /* gcc bug */ ) + : : "D" ( entry ) + : "eax", "ebx", "ecx", "edx", "esi", "memory" ); + + DBGC ( image, "ELF %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Probe ELF image + * + * @v image ELF file + * @ret rc Return status code + */ +static int elfboot_probe ( struct image *image ) { + Elf32_Ehdr ehdr; + static const uint8_t e_ident[] = { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFCLASS32, + [EI_DATA] = ELFDATA2LSB, + [EI_VERSION] = EV_CURRENT, + }; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { + DBG ( "Invalid ELF identifier\n" ); + return -ENOEXEC; + } + + return 0; +} + +/** ELF image type */ +struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "ELF", + .probe = elfboot_probe, + .exec = elfboot_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/initrd.c b/qemu/roms/ipxe/src/arch/i386/image/initrd.c new file mode 100644 index 000000000..eaba3a645 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/initrd.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Initial ramdisk (initrd) reshuffling + * + */ + +/** Maximum address available for initrd */ +userptr_t initrd_top; + +/** Minimum address available for initrd */ +userptr_t initrd_bottom; + +/** + * Squash initrds as high as possible in memory + * + * @v top Highest possible address + * @ret used Lowest address used by initrds + */ +static userptr_t initrd_squash_high ( userptr_t top ) { + userptr_t current = top; + struct image *initrd; + struct image *highest; + size_t len; + + /* Squash up any initrds already within or below the region */ + while ( 1 ) { + + /* Find the highest image not yet in its final position */ + highest = NULL; + for_each_image ( initrd ) { + if ( ( userptr_sub ( initrd->data, current ) < 0 ) && + ( ( highest == NULL ) || + ( userptr_sub ( initrd->data, + highest->data ) > 0 ) ) ) { + highest = initrd; + } + } + if ( ! highest ) + break; + + /* Move this image to its final position */ + len = ( ( highest->len + INITRD_ALIGN - 1 ) & + ~( INITRD_ALIGN - 1 ) ); + current = userptr_sub ( current, len ); + DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->" + "[%#08lx,%#08lx)\n", highest->name, + user_to_phys ( highest->data, 0 ), + user_to_phys ( highest->data, highest->len ), + user_to_phys ( current, 0 ), + user_to_phys ( current, highest->len ) ); + memmove_user ( current, 0, highest->data, 0, highest->len ); + highest->data = current; + } + + /* Copy any remaining initrds (e.g. embedded images) to the region */ + for_each_image ( initrd ) { + if ( userptr_sub ( initrd->data, top ) >= 0 ) { + len = ( ( initrd->len + INITRD_ALIGN - 1 ) & + ~( INITRD_ALIGN - 1 ) ); + current = userptr_sub ( current, len ); + DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->" + "[%#08lx,%#08lx)\n", initrd->name, + user_to_phys ( initrd->data, 0 ), + user_to_phys ( initrd->data, initrd->len ), + user_to_phys ( current, 0 ), + user_to_phys ( current, initrd->len ) ); + memcpy_user ( current, 0, initrd->data, 0, + initrd->len ); + initrd->data = current; + } + } + + return current; +} + +/** + * Swap position of two adjacent initrds + * + * @v low Lower initrd + * @v high Higher initrd + * @v free Free space + * @v free_len Length of free space + */ +static void initrd_swap ( struct image *low, struct image *high, + userptr_t free, size_t free_len ) { + size_t len = 0; + size_t frag_len; + size_t new_len; + + DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) " + "%s\n", low->name, user_to_phys ( low->data, 0 ), + user_to_phys ( low->data, low->len ), + user_to_phys ( high->data, 0 ), + user_to_phys ( high->data, high->len ), high->name ); + + /* Round down length of free space */ + free_len &= ~( INITRD_ALIGN - 1 ); + assert ( free_len > 0 ); + + /* Swap image data */ + while ( len < high->len ) { + + /* Calculate maximum fragment length */ + frag_len = ( high->len - len ); + if ( frag_len > free_len ) + frag_len = free_len; + new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) & + ~( INITRD_ALIGN - 1 ) ); + + /* Swap fragments */ + memcpy_user ( free, 0, high->data, len, frag_len ); + memmove_user ( low->data, new_len, low->data, len, low->len ); + memcpy_user ( low->data, len, free, 0, frag_len ); + len = new_len; + } + + /* Adjust data pointers */ + high->data = low->data; + low->data = userptr_add ( low->data, len ); +} + +/** + * Swap position of any two adjacent initrds not currently in the correct order + * + * @v free Free space + * @v free_len Length of free space + * @ret swapped A pair of initrds was swapped + */ +static int initrd_swap_any ( userptr_t free, size_t free_len ) { + struct image *low; + struct image *high; + size_t padded_len; + userptr_t adjacent; + + /* Find any pair of initrds that can be swapped */ + for_each_image ( low ) { + + /* Calculate location of adjacent image (if any) */ + padded_len = ( ( low->len + INITRD_ALIGN - 1 ) & + ~( INITRD_ALIGN - 1 ) ); + adjacent = userptr_add ( low->data, padded_len ); + + /* Search for adjacent image */ + for_each_image ( high ) { + + /* If we have found the adjacent image, swap and exit */ + if ( high->data == adjacent ) { + initrd_swap ( low, high, free, free_len ); + return 1; + } + + /* Stop search if all remaining potential + * adjacent images are already in the correct + * order. + */ + if ( high == low ) + break; + } + } + + /* Nothing swapped */ + return 0; +} + +/** + * Dump initrd locations (for debug) + * + */ +static void initrd_dump ( void ) { + struct image *initrd; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump initrd locations */ + for_each_image ( initrd ) { + DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n", + initrd->name, user_to_phys ( initrd->data, 0 ), + user_to_phys ( initrd->data, initrd->len ) ); + DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ), + user_to_virt ( initrd->data, 0 ), initrd->len ); + } +} + +/** + * Reshuffle initrds into desired order at top of memory + * + * @v bottom Lowest address available for initrds + * + * After this function returns, the initrds have been rearranged in + * memory and the external heap structures will have been corrupted. + * Reshuffling must therefore take place immediately prior to jumping + * to the loaded OS kernel; no further execution within iPXE is + * permitted. + */ +void initrd_reshuffle ( userptr_t bottom ) { + userptr_t top; + userptr_t used; + userptr_t free; + size_t free_len; + + /* Calculate limits of available space for initrds */ + top = initrd_top; + if ( userptr_sub ( initrd_bottom, bottom ) > 0 ) + bottom = initrd_bottom; + + /* Debug */ + DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n", + user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) ); + initrd_dump(); + + /* Squash initrds as high as possible in memory */ + used = initrd_squash_high ( top ); + + /* Calculate available free space */ + free = bottom; + free_len = userptr_sub ( used, free ); + + /* Bubble-sort initrds into desired order */ + while ( initrd_swap_any ( free, free_len ) ) {} + + /* Debug */ + initrd_dump(); +} + +/** + * Check that there is enough space to reshuffle initrds + * + * @v len Total length of initrds (including padding) + * @v bottom Lowest address available for initrds + * @ret rc Return status code + */ +int initrd_reshuffle_check ( size_t len, userptr_t bottom ) { + userptr_t top; + size_t available; + + /* Calculate limits of available space for initrds */ + top = initrd_top; + if ( userptr_sub ( initrd_bottom, bottom ) > 0 ) + bottom = initrd_bottom; + available = userptr_sub ( top, bottom ); + + /* Allow for a sensible minimum amount of free space */ + len += INITRD_MIN_FREE_LEN; + + /* Check for available space */ + return ( ( len < available ) ? 0 : -ENOBUFS ); +} + +/** + * initrd startup function + * + */ +static void initrd_startup ( void ) { + size_t len; + + /* Record largest memory block available. Do this after any + * allocations made during driver startup (e.g. large host + * memory blocks for Infiniband devices, which may still be in + * use at the time of rearranging if a SAN device is hooked) + * but before any allocations for downloaded images (which we + * can safely reuse when rearranging). + */ + len = largest_memblock ( &initrd_bottom ); + initrd_top = userptr_add ( initrd_bottom, len ); +} + +/** initrd startup function */ +struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = { + .startup = initrd_startup, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/multiboot.c b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c new file mode 100644 index 000000000..86b0bc12d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Multiboot image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 ); + +/** + * Maximum number of modules we will allow for + * + * If this has bitten you: sorry. I did have a perfect scheme with a + * dynamically allocated list of modules on the protected-mode stack, + * but it was incompatible with some broken OSes that can only access + * low memory at boot time (even though we kindly set up 4GB flat + * physical addressing as per the multiboot specification. + * + */ +#define MAX_MODULES 8 + +/** + * Maximum combined length of command lines + * + * Again; sorry. Some broken OSes zero out any non-base memory that + * isn't part of the loaded module set, so we can't just use + * virt_to_phys(cmdline) to point to the command lines, even though + * this would comply with the Multiboot spec. + */ +#define MB_MAX_CMDLINE 512 + +/** Multiboot flags that we support */ +#define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \ + MB_FLAG_VIDMODE | MB_FLAG_RAW ) + +/** Compulsory feature multiboot flags */ +#define MB_COMPULSORY_FLAGS 0x0000ffff + +/** Optional feature multiboot flags */ +#define MB_OPTIONAL_FLAGS 0xffff0000 + +/** + * Multiboot flags that we don't support + * + * We only care about the compulsory feature flags (bits 0-15); we are + * allowed to ignore the optional feature flags. + */ +#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS ) + +/** A multiboot header descriptor */ +struct multiboot_header_info { + /** The actual multiboot header */ + struct multiboot_header mb; + /** Offset of header within the multiboot image */ + size_t offset; +}; + +/** Multiboot module command lines */ +static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] ); +#define mb_cmdlines __use_data16 ( mb_cmdlines ) + +/** Offset within module command lines */ +static unsigned int mb_cmdline_offset; + +/** + * Build multiboot memory map + * + * @v image Multiboot image + * @v mbinfo Multiboot information structure + * @v mbmemmap Multiboot memory map + * @v limit Maxmimum number of memory map entries + */ +static void multiboot_build_memmap ( struct image *image, + struct multiboot_info *mbinfo, + struct multiboot_memory_map *mbmemmap, + unsigned int limit ) { + struct memory_map memmap; + unsigned int i; + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Translate into multiboot format */ + memset ( mbmemmap, 0, sizeof ( *mbmemmap ) ); + for ( i = 0 ; i < memmap.count ; i++ ) { + if ( i >= limit ) { + DBGC ( image, "MULTIBOOT %p limit of %d memmap " + "entries reached\n", image, limit ); + break; + } + mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) - + sizeof ( mbmemmap[i].size ) ); + mbmemmap[i].base_addr = memmap.regions[i].start; + mbmemmap[i].length = ( memmap.regions[i].end - + memmap.regions[i].start ); + mbmemmap[i].type = MBMEM_RAM; + mbinfo->mmap_length += sizeof ( mbmemmap[i] ); + if ( memmap.regions[i].start == 0 ) + mbinfo->mem_lower = ( memmap.regions[i].end / 1024 ); + if ( memmap.regions[i].start == 0x100000 ) + mbinfo->mem_upper = ( ( memmap.regions[i].end - + 0x100000 ) / 1024 ); + } +} + +/** + * Add command line in base memory + * + * @v image Image + * @ret physaddr Physical address of command line + */ +static physaddr_t multiboot_add_cmdline ( struct image *image ) { + char *mb_cmdline = ( mb_cmdlines + mb_cmdline_offset ); + size_t remaining = ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ); + char *buf = mb_cmdline; + size_t len; + + /* Copy image URI to base memory buffer as start of command line */ + len = ( format_uri ( image->uri, buf, remaining ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + mb_cmdline_offset += len; + buf += len; + remaining -= len; + + /* Copy command line to base memory buffer, if present */ + if ( image->cmdline ) { + mb_cmdline_offset--; /* Strip NUL */ + buf--; + remaining++; + len = ( snprintf ( buf, remaining, " %s", + image->cmdline ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + mb_cmdline_offset += len; + } + + return virt_to_phys ( mb_cmdline ); +} + +/** + * Add multiboot modules + * + * @v image Multiboot image + * @v start Start address for modules + * @v mbinfo Multiboot information structure + * @v modules Multiboot module list + * @ret rc Return status code + */ +static int multiboot_add_modules ( struct image *image, physaddr_t start, + struct multiboot_info *mbinfo, + struct multiboot_module *modules, + unsigned int limit ) { + struct image *module_image; + struct multiboot_module *module; + int rc; + + /* Add each image as a multiboot module */ + for_each_image ( module_image ) { + + if ( mbinfo->mods_count >= limit ) { + DBGC ( image, "MULTIBOOT %p limit of %d modules " + "reached\n", image, limit ); + break; + } + + /* Do not include kernel image itself as a module */ + if ( module_image == image ) + continue; + + /* Page-align the module */ + start = ( ( start + 0xfff ) & ~0xfff ); + + /* Prepare segment */ + if ( ( rc = prep_segment ( phys_to_user ( start ), + module_image->len, + module_image->len ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p could not prepare module " + "%s: %s\n", image, module_image->name, + strerror ( rc ) ); + return rc; + } + + /* Copy module */ + memcpy_user ( phys_to_user ( start ), 0, + module_image->data, 0, module_image->len ); + + /* Add module to list */ + module = &modules[mbinfo->mods_count++]; + module->mod_start = start; + module->mod_end = ( start + module_image->len ); + module->string = multiboot_add_cmdline ( module_image ); + module->reserved = 0; + DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n", + image, module_image->name, module->mod_start, + module->mod_end ); + start += module_image->len; + } + + return 0; +} + +/** + * The multiboot information structure + * + * Kept in base memory because some OSes won't find it elsewhere, + * along with the other structures belonging to the Multiboot + * information table. + */ +static struct multiboot_info __bss16 ( mbinfo ); +#define mbinfo __use_data16 ( mbinfo ) + +/** The multiboot bootloader name */ +static char __bss16_array ( mb_bootloader_name, [32] ); +#define mb_bootloader_name __use_data16 ( mb_bootloader_name ) + +/** The multiboot memory map */ +static struct multiboot_memory_map + __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] ); +#define mbmemmap __use_data16 ( mbmemmap ) + +/** The multiboot module list */ +static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] ); +#define mbmodules __use_data16 ( mbmodules ) + +/** + * Find multiboot header + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor to fill in + * @ret rc Return status code + */ +static int multiboot_find_header ( struct image *image, + struct multiboot_header_info *hdr ) { + uint32_t buf[64]; + size_t offset; + unsigned int buf_idx; + uint32_t checksum; + + /* Scan through first 8kB of image file 256 bytes at a time. + * (Use the buffering to avoid the overhead of a + * copy_from_user() for every dword.) + */ + for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) { + /* Check for end of image */ + if ( offset > image->len ) + break; + /* Refill buffer if applicable */ + buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) ); + if ( buf_idx == 0 ) { + copy_from_user ( buf, image->data, offset, + sizeof ( buf ) ); + } + /* Check signature */ + if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC ) + continue; + /* Copy header and verify checksum */ + copy_from_user ( &hdr->mb, image->data, offset, + sizeof ( hdr->mb ) ); + checksum = ( hdr->mb.magic + hdr->mb.flags + + hdr->mb.checksum ); + if ( checksum != 0 ) + continue; + /* Record offset of multiboot header and return */ + hdr->offset = offset; + return 0; + } + + /* No multiboot header found */ + return -ENOEXEC; +} + +/** + * Load raw multiboot image into memory + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor + * @ret entry Entry point + * @ret max Maximum used address + * @ret rc Return status code + */ +static int multiboot_load_raw ( struct image *image, + struct multiboot_header_info *hdr, + physaddr_t *entry, physaddr_t *max ) { + size_t offset; + size_t filesz; + size_t memsz; + userptr_t buffer; + int rc; + + /* Sanity check */ + if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) { + DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n", + image ); + return -EINVAL; + } + + /* Verify and prepare segment */ + offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr ); + filesz = ( hdr->mb.load_end_addr ? + ( hdr->mb.load_end_addr - hdr->mb.load_addr ) : + ( image->len - offset ) ); + memsz = ( hdr->mb.bss_end_addr ? + ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz ); + buffer = phys_to_user ( hdr->mb.load_addr ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, offset, filesz ); + + /* Record execution entry point and maximum used address */ + *entry = hdr->mb.entry_addr; + *max = ( hdr->mb.load_addr + memsz ); + + return 0; +} + +/** + * Load ELF multiboot image into memory + * + * @v image Multiboot file + * @ret entry Entry point + * @ret max Maximum used address + * @ret rc Return status code + */ +static int multiboot_load_elf ( struct image *image, physaddr_t *entry, + physaddr_t *max ) { + int rc; + + /* Load ELF image*/ + if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Execute multiboot image + * + * @v image Multiboot image + * @ret rc Return status code + */ +static int multiboot_exec ( struct image *image ) { + struct multiboot_header_info hdr; + physaddr_t entry; + physaddr_t max; + int rc; + + /* Locate multiboot header, if present */ + if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p has no multiboot header\n", + image ); + return rc; + } + + /* Abort if we detect flags that we cannot support */ + if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) { + DBGC ( image, "MULTIBOOT %p flags %08x not supported\n", + image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) ); + return -ENOTSUP; + } + + /* There is technically a bit MB_FLAG_RAW to indicate whether + * this is an ELF or a raw image. In practice, grub will use + * the ELF header if present, and Solaris relies on this + * behaviour. + */ + if ( ( ( rc = multiboot_load_elf ( image, &entry, &max ) ) != 0 ) && + ( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 )) + return rc; + + /* Populate multiboot information structure */ + memset ( &mbinfo, 0, sizeof ( mbinfo ) ); + mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP | + MBI_FLAG_CMDLINE | MBI_FLAG_MODS ); + mb_cmdline_offset = 0; + mbinfo.cmdline = multiboot_add_cmdline ( image ); + mbinfo.mods_addr = virt_to_phys ( mbmodules ); + mbinfo.mmap_addr = virt_to_phys ( mbmemmap ); + snprintf ( mb_bootloader_name, sizeof ( mb_bootloader_name ), + "iPXE %s", product_version ); + mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name ); + if ( ( rc = multiboot_add_modules ( image, max, &mbinfo, mbmodules, + ( sizeof ( mbmodules ) / + sizeof ( mbmodules[0] ) ) ) ) !=0) + return rc; + + /* Multiboot images may not return and have no callback + * interface, so shut everything down prior to booting the OS. + */ + shutdown_boot(); + + /* Build memory map after unhiding bootloader memory regions as part of + * shutting everything down. + */ + multiboot_build_memmap ( image, &mbinfo, mbmemmap, + ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) ); + + /* Jump to OS with flat physical addressing */ + DBGC ( image, "MULTIBOOT %p starting execution at %lx\n", + image, entry ); + __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" + "call *%%edi\n\t" + "popl %%ebp\n\t" ) + : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ), + "b" ( virt_to_phys ( &mbinfo ) ), + "D" ( entry ) + : "ecx", "edx", "esi", "memory" ); + + DBGC ( image, "MULTIBOOT %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Probe multiboot image + * + * @v image Multiboot file + * @ret rc Return status code + */ +static int multiboot_probe ( struct image *image ) { + struct multiboot_header_info hdr; + int rc; + + /* Locate multiboot header, if present */ + if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p has no multiboot header\n", + image ); + return rc; + } + DBGC ( image, "MULTIBOOT %p found header with flags %08x\n", + image, hdr.mb.flags ); + + return 0; +} + +/** Multiboot image type */ +struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = { + .name = "Multiboot", + .probe = multiboot_probe, + .exec = multiboot_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/nbi.c b/qemu/roms/ipxe/src/arch/i386/image/nbi.c new file mode 100644 index 000000000..99046144d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/nbi.c @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * NBI image format. + * + * The Net Boot Image format is defined by the "Draft Net Boot Image + * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now + * considered to be a legacy format, but it still included because a + * large amount of software (e.g. nymph, LTSP) makes use of NBI files. + * + * Etherboot does not implement the INT 78 callback interface + * described by the NBI specification. For a callback interface on + * x86 architecture, use PXE. + * + */ + +FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 ); + +/** + * An NBI image header + * + * Note that the length field uses a peculiar encoding; use the + * NBI_LENGTH() macro to decode the actual header length. + * + */ +struct imgheader { + unsigned long magic; /**< Magic number (NBI_MAGIC) */ + union { + unsigned char length; /**< Nibble-coded header length */ + unsigned long flags; /**< Image flags */ + }; + segoff_t location; /**< 16-bit seg:off header location */ + union { + segoff_t segoff; /**< 16-bit seg:off entry point */ + unsigned long linear; /**< 32-bit entry point */ + } execaddr; +} __attribute__ (( packed )); + +/** NBI magic number */ +#define NBI_MAGIC 0x1B031336UL + +/* Interpretation of the "length" fields */ +#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 ) +#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 ) +#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) ) + +/* Interpretation of the "flags" fields */ +#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) ) +#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) ) + +/** NBI header length */ +#define NBI_HEADER_LENGTH 512 + +/** + * An NBI segment header + * + * Note that the length field uses a peculiar encoding; use the + * NBI_LENGTH() macro to decode the actual header length. + * + */ +struct segheader { + unsigned char length; /**< Nibble-coded header length */ + unsigned char vendortag; /**< Vendor-defined private tag */ + unsigned char reserved; + unsigned char flags; /**< Segment flags */ + unsigned long loadaddr; /**< Load address */ + unsigned long imglength; /**< Segment length in NBI file */ + unsigned long memlength; /**< Segment length in memory */ +}; + +/* Interpretation of the "flags" fields */ +#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 ) +#define NBI_LOADADDR_ABS 0x00 +#define NBI_LOADADDR_AFTER 0x01 +#define NBI_LOADADDR_END 0x02 +#define NBI_LOADADDR_BEFORE 0x03 +#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) ) + +/* Define a type for passing info to a loaded program */ +struct ebinfo { + uint8_t major, minor; /* Version */ + uint16_t flags; /* Bit flags */ +}; + +/** + * Prepare a segment for an NBI image + * + * @v image NBI image + * @v offset Offset within NBI image + * @v filesz Length of initialised-data portion of the segment + * @v memsz Total length of the segment + * @v src Source for initialised data + * @ret rc Return status code + */ +static int nbi_prepare_segment ( struct image *image, size_t offset __unused, + userptr_t dest, size_t filesz, size_t memsz ){ + int rc; + + if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) { + DBGC ( image, "NBI %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load a segment for an NBI image + * + * @v image NBI image + * @v offset Offset within NBI image + * @v filesz Length of initialised-data portion of the segment + * @v memsz Total length of the segment + * @v src Source for initialised data + * @ret rc Return status code + */ +static int nbi_load_segment ( struct image *image, size_t offset, + userptr_t dest, size_t filesz, + size_t memsz __unused ) { + memcpy_user ( dest, 0, image->data, offset, filesz ); + return 0; +} + +/** + * Process segments of an NBI image + * + * @v image NBI image + * @v imgheader Image header information + * @v process Function to call for each segment + * @ret rc Return status code + */ +static int nbi_process_segments ( struct image *image, + struct imgheader *imgheader, + int ( * process ) ( struct image *image, + size_t offset, + userptr_t dest, + size_t filesz, + size_t memsz ) ) { + struct segheader sh; + size_t offset = 0; + size_t sh_off; + userptr_t dest; + size_t filesz; + size_t memsz; + int rc; + + /* Copy image header to target location */ + dest = real_to_user ( imgheader->location.segment, + imgheader->location.offset ); + filesz = memsz = NBI_HEADER_LENGTH; + if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 ) + return rc; + offset += filesz; + + /* Process segments in turn */ + sh_off = NBI_LENGTH ( imgheader->length ); + do { + /* Read segment header */ + copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) ); + if ( sh.length == 0 ) { + /* Avoid infinite loop? */ + DBGC ( image, "NBI %p invalid segheader length 0\n", + image ); + return -ENOEXEC; + } + + /* Calculate segment load address */ + switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) { + case NBI_LOADADDR_ABS: + dest = phys_to_user ( sh.loadaddr ); + break; + case NBI_LOADADDR_AFTER: + dest = userptr_add ( dest, memsz + sh.loadaddr ); + break; + case NBI_LOADADDR_BEFORE: + dest = userptr_add ( dest, -sh.loadaddr ); + break; + case NBI_LOADADDR_END: + /* Not correct according to the spec, but + * maintains backwards compatibility with + * previous versions of Etherboot. + */ + dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024 + - sh.loadaddr ); + break; + default: + /* Cannot be reached */ + assert ( 0 ); + } + + /* Process this segment */ + filesz = sh.imglength; + memsz = sh.memlength; + if ( ( offset + filesz ) > image->len ) { + DBGC ( image, "NBI %p segment outside file\n", image ); + return -ENOEXEC; + } + if ( ( rc = process ( image, offset, dest, + filesz, memsz ) ) != 0 ) { + return rc; + } + offset += filesz; + + /* Next segheader */ + sh_off += NBI_LENGTH ( sh.length ); + if ( sh_off >= NBI_HEADER_LENGTH ) { + DBGC ( image, "NBI %p header overflow\n", image ); + return -ENOEXEC; + } + + } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) ); + + if ( offset != image->len ) { + DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n", + image, image->len, offset ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Boot a 16-bit NBI image + * + * @v imgheader Image header information + * @ret rc Return status code, if image returns + */ +static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) { + int discard_D, discard_S, discard_b; + int rc; + + DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, + imgheader->execaddr.segoff.segment, + imgheader->execaddr.segoff.offset ); + + __asm__ __volatile__ ( + REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ + "pushw %%ds\n\t" /* far pointer to bootp data */ + "pushw %%bx\n\t" + "pushl %%esi\n\t" /* location */ + "pushw %%cs\n\t" /* lcall execaddr */ + "call 1f\n\t" + "jmp 2f\n\t" + "\n1:\n\t" + "pushl %%edi\n\t" + "lret\n\t" + "\n2:\n\t" + "addw $8,%%sp\n\t" /* clean up stack */ + "popl %%ebp\n\t" /* gcc bug */ ) + : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), + "=b" ( discard_b ) + : "D" ( imgheader->execaddr.segoff ), + "S" ( imgheader->location ), + "b" ( __from_data16 ( basemem_packet ) ) + : "ecx", "edx" ); + + return rc; +} + +/** + * Boot a 32-bit NBI image + * + * @v imgheader Image header information + * @ret rc Return status code, if image returns + */ +static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { + struct ebinfo loaderinfo = { + product_major_version, product_minor_version, + 0 + }; + int discard_D, discard_S, discard_b; + int rc; + + DBGC ( image, "NBI %p executing 32-bit image at %lx\n", + image, imgheader->execaddr.linear ); + + /* Jump to OS with flat physical addressing */ + __asm__ __volatile__ ( + PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */ + "pushl %%ebx\n\t" /* bootp data */ + "pushl %%esi\n\t" /* imgheader */ + "pushl %%eax\n\t" /* loaderinfo */ + "call *%%edi\n\t" + "addl $12, %%esp\n\t" /* clean up stack */ + "popl %%ebp\n\t" /* gcc bug */ ) + : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), + "=b" ( discard_b ) + : "D" ( imgheader->execaddr.linear ), + "S" ( ( imgheader->location.segment << 4 ) + + imgheader->location.offset ), + "b" ( virt_to_phys ( basemem_packet ) ), + "a" ( virt_to_phys ( &loaderinfo ) ) + : "ecx", "edx", "memory" ); + + return rc; +} + +/** + * Prepare DHCP parameter block for NBI image + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_prepare_dhcp ( struct image *image ) { + struct net_device *boot_netdev; + int rc; + + boot_netdev = last_opened_netdev(); + if ( ! boot_netdev ) { + DBGC ( image, "NBI %p could not identify a network device\n", + image ); + return -ENODEV; + } + + if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet, + sizeof ( basemem_packet ) ) ) != 0 ) { + DBGC ( image, "NBI %p failed to build DHCP packet\n", image ); + return rc; + } + + return 0; +} + +/** + * Execute a loaded NBI image + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_exec ( struct image *image ) { + struct imgheader imgheader; + int may_return; + int rc; + + /* Retrieve image header */ + copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) ); + + DBGC ( image, "NBI %p placing header at %hx:%hx\n", image, + imgheader.location.segment, imgheader.location.offset ); + + /* NBI files can have overlaps between segments; the bss of + * one segment may overlap the initialised data of another. I + * assume this is a design flaw, but there are images out + * there that we need to work with. We therefore do two + * passes: first to initialise the segments, then to copy the + * data. This avoids zeroing out already-copied data. + */ + if ( ( rc = nbi_process_segments ( image, &imgheader, + nbi_prepare_segment ) ) != 0 ) + return rc; + if ( ( rc = nbi_process_segments ( image, &imgheader, + nbi_load_segment ) ) != 0 ) + return rc; + + /* Prepare DHCP option block */ + if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 ) + return rc; + + /* Shut down now if NBI image will not return */ + may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); + if ( ! may_return ) + shutdown_boot(); + + /* Execute NBI image */ + if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { + rc = nbi_boot32 ( image, &imgheader ); + } else { + rc = nbi_boot16 ( image, &imgheader ); + } + + if ( ! may_return ) { + /* Cannot continue after shutdown() called */ + DBGC ( image, "NBI %p returned %d from non-returnable image\n", + image, rc ); + while ( 1 ) {} + } + + DBGC ( image, "NBI %p returned %d\n", image, rc ); + + return rc; +} + +/** + * Probe NBI image + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_probe ( struct image *image ) { + struct imgheader imgheader; + + /* If we don't have enough data give up */ + if ( image->len < NBI_HEADER_LENGTH ) { + DBGC ( image, "NBI %p too short for an NBI image\n", image ); + return -ENOEXEC; + } + + /* Check image header */ + copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) ); + if ( imgheader.magic != NBI_MAGIC ) { + DBGC ( image, "NBI %p has no NBI signature\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** NBI image type */ +struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = { + .name = "NBI", + .probe = nbi_probe, + .exec = nbi_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c new file mode 100644 index 000000000..dc28f6082 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * PXE image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 ); + +/** PXE command line */ +const char *pxe_cmdline; + +/** + * Execute PXE image + * + * @v image PXE image + * @ret rc Return status code + */ +static int pxe_exec ( struct image *image ) { + userptr_t buffer = real_to_user ( 0, 0x7c00 ); + struct net_device *netdev; + int rc; + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, image->len, image->len ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, image->len ); + + /* Arbitrarily pick the most recently opened network device */ + if ( ( netdev = last_opened_netdev() ) == NULL ) { + DBGC ( image, "IMAGE %p could not locate PXE net device\n", + image ); + return -ENODEV; + } + netdev_get ( netdev ); + + /* Activate PXE */ + pxe_activate ( netdev ); + + /* Set PXE command line */ + pxe_cmdline = image->cmdline; + + /* Reset console since PXE NBP will probably use it */ + console_reset(); + + /* Start PXE NBP */ + rc = pxe_start_nbp(); + + /* Clear PXE command line */ + pxe_cmdline = NULL; + + /* Deactivate PXE */ + pxe_deactivate(); + + /* Try to reopen network device. Ignore errors, since the NBP + * may have called PXENV_STOP_UNDI. + */ + netdev_open ( netdev ); + netdev_put ( netdev ); + + return rc; +} + +/** + * Probe PXE image + * + * @v image PXE file + * @ret rc Return status code + */ +int pxe_probe ( struct image *image ) { + + /* Images too large to fit in base memory cannot be PXE + * images. We include this check to help prevent unrecognised + * images from being marked as PXE images, since PXE images + * have no signature we can check against. + */ + if ( image->len > ( 0xa0000 - 0x7c00 ) ) + return -ENOEXEC; + + /* Rejecting zero-length images is also useful, since these + * end up looking to the user like bugs in iPXE. + */ + if ( ! image->len ) + return -ENOEXEC; + + return 0; +} + +/** PXE image type */ +struct image_type pxe_image_type __image_type ( PROBE_PXE ) = { + .name = "PXE", + .probe = pxe_probe, + .exec = pxe_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/sdi.c b/qemu/roms/ipxe/src/arch/i386/image/sdi.c new file mode 100644 index 000000000..df1c3a868 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/sdi.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * System Deployment Image (SDI) + * + * Based on the MSDN article "RAM boot using SDI in Windows XP + * Embedded with Service Pack 1", available at the time of writing + * from: + * + * http://msdn.microsoft.com/en-us/library/ms838543.aspx + */ + +FEATURE ( FEATURE_IMAGE, "SDI", DHCP_EB_FEATURE_SDI, 1 ); + +/** + * Parse SDI image header + * + * @v image SDI file + * @v sdi SDI header to fill in + * @ret rc Return status code + */ +static int sdi_parse_header ( struct image *image, struct sdi_header *sdi ) { + + /* Sanity check */ + if ( image->len < sizeof ( *sdi ) ) { + DBGC ( image, "SDI %p too short for SDI header\n", image ); + return -ENOEXEC; + } + + /* Read in header */ + copy_from_user ( sdi, image->data, 0, sizeof ( *sdi ) ); + + /* Check signature */ + if ( sdi->magic != SDI_MAGIC ) { + DBGC ( image, "SDI %p is not an SDI image\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Execute SDI image + * + * @v image SDI file + * @ret rc Return status code + */ +static int sdi_exec ( struct image *image ) { + struct sdi_header sdi; + uint32_t sdiptr; + int rc; + + /* Parse image header */ + if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 ) + return rc; + + /* Check that image is bootable */ + if ( sdi.boot_size == 0 ) { + DBGC ( image, "SDI %p is not bootable\n", image ); + return -ENOTTY; + } + DBGC ( image, "SDI %p image at %08lx+%08zx\n", + image, user_to_phys ( image->data, 0 ), image->len ); + DBGC ( image, "SDI %p boot code at %08lx+%llx\n", image, + user_to_phys ( image->data, sdi.boot_offset ), sdi.boot_size ); + + /* Copy boot code */ + memcpy_user ( real_to_user ( SDI_BOOT_SEG, SDI_BOOT_OFF ), 0, + image->data, sdi.boot_offset, sdi.boot_size ); + + /* Jump to boot code */ + sdiptr = ( user_to_phys ( image->data, 0 ) | SDI_WTF ); + __asm__ __volatile__ ( REAL_CODE ( "ljmp %0, %1\n\t" ) + : : "i" ( SDI_BOOT_SEG ), + "i" ( SDI_BOOT_OFF ), + "d" ( sdiptr ) ); + + /* There is no way for the image to return, since we provide + * no return address. + */ + assert ( 0 ); + + return -ECANCELED; /* -EIMPOSSIBLE */ +} + +/** + * Probe SDI image + * + * @v image SDI file + * @ret rc Return status code + */ +static int sdi_probe ( struct image *image ) { + struct sdi_header sdi; + int rc; + + /* Parse image */ + if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 ) + return rc; + + return 0; +} + +/** SDI image type */ +struct image_type sdi_image_type __image_type ( PROBE_NORMAL ) = { + .name = "SDI", + .probe = sdi_probe, + .exec = sdi_exec, +}; -- cgit 1.2.3-korg