summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/i386/image
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/arch/i386/image')
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/bootsector.c137
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/bzimage.c665
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/com32.c292
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/comboot.c327
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/elfboot.c110
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/initrd.c300
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/multiboot.c488
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/nbi.c427
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/pxe_image.c129
-rw-r--r--qemu/roms/ipxe/src/arch/i386/image/sdi.c136
10 files changed, 3011 insertions, 0 deletions
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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <ipxe/console.h>
+
+/** 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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <bzimage.h>
+#include <initrd.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/cpio.h>
+#include <ipxe/features.h>
+
+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 <daniel@drv.nu>.
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <basemem.h>
+#include <comboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/io.h>
+
+/**
+ * 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 <daniel@drv.nu>.
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <basemem.h>
+#include <comboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/features.h>
+
+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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <errno.h>
+#include <elf.h>
+#include <ipxe/image.h>
+#include <ipxe/elf.h>
+#include <ipxe/features.h>
+#include <ipxe/init.h>
+
+/**
+ * @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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <errno.h>
+#include <initrd.h>
+#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/init.h>
+#include <ipxe/memblock.h>
+
+/** @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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <multiboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/io.h>
+#include <ipxe/elf.h>
+#include <ipxe/init.h>
+#include <ipxe/features.h>
+#include <ipxe/uri.h>
+#include <ipxe/version.h>
+
+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 <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <memsizes.h>
+#include <basemem_packet.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/fakedhcp.h>
+#include <ipxe/image.h>
+#include <ipxe/features.h>
+#include <ipxe/version.h>
+
+/** @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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <pxe.h>
+#include <pxe_call.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/features.h>
+#include <ipxe/console.h>
+
+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 <mbrown@fensystems.co.uk>.
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <realmode.h>
+#include <sdi.h>
+#include <ipxe/image.h>
+#include <ipxe/features.h>
+
+/** @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,
+};