[bios] Add bin-x86_64-pcbios build platform

Move most arch/i386 files to arch/x86, and adjust the contents of the
Makefiles and the include/bits/*.h headers to reflect the new
locations.

This patch makes no substantive code changes, as can be seen using a
rename-aware diff (e.g. "git show -M5").

This patch does not make the pcbios platform functional for x86_64; it
merely allows it to compile without errors.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2016-02-16 15:19:01 +00:00
parent 43515f9f1a
commit f468f12b1e
155 changed files with 198 additions and 240 deletions

View File

@@ -0,0 +1,141 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @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, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
hook_bios_interrupt ( 0x19, ( intptr_t ) 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, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
unhook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail,
&int19_vector );
return -ECANCELED;
}

View File

@@ -0,0 +1,669 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @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,
};

View File

@@ -0,0 +1,145 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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? */
}
/**
* Check that ELF segment uses flat physical addressing
*
* @v image ELF file
* @v phdr ELF program header
* @v dest Destination address
* @ret rc Return status code
*/
static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr,
physaddr_t dest ) {
/* Check that ELF segment uses flat physical addressing */
if ( phdr->p_vaddr != dest ) {
DBGC ( image, "ELF %p uses virtual addressing (phys %x, "
"virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr );
return -ENOEXEC;
}
return 0;
}
/**
* 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,
};
physaddr_t entry;
physaddr_t max;
int rc;
/* Read ELF header */
copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
DBGC ( image, "Invalid ELF identifier\n" );
return -ENOEXEC;
}
/* Check that this image uses flat physical addressing */
if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment,
&entry, &max ) ) != 0 ) {
DBGC ( image, "Unloadable ELF image\n" );
return rc;
}
return 0;
}
/** ELF image type */
struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = {
.name = "ELF",
.probe = elfboot_probe,
.exec = elfboot_exec,
};

304
src/arch/x86/image/initrd.c Normal file
View File

@@ -0,0 +1,304 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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,
};

View File

@@ -0,0 +1,492 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @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,
};

427
src/arch/x86/image/nbi.c Normal file
View File

@@ -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,
};

View File

@@ -0,0 +1,174 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @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>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/IndustryStandard/PeImage.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 );
/* Construct fake DHCP packets */
pxe_fake_cached_info();
/* 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;
}
/**
* Probe PXE image (with rejection of potential EFI images)
*
* @v image PXE file
* @ret rc Return status code
*/
int pxe_probe_no_mz ( struct image *image ) {
uint16_t magic;
int rc;
/* Probe PXE image */
if ( ( rc = pxe_probe ( image ) ) != 0 )
return rc;
/* Reject image with an "MZ" signature which may indicate an
* EFI image incorrectly handed out to a BIOS system.
*/
if ( image->len >= sizeof ( magic ) ) {
copy_from_user ( &magic, image->data, 0, sizeof ( magic ) );
if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) {
DBGC ( image, "IMAGE %p may be an EFI image\n",
image );
return -ENOTTY;
}
}
return 0;
}
/** PXE image type */
struct image_type pxe_image_type[] __image_type ( PROBE_PXE ) = {
{
.name = "PXE-NBP",
.probe = pxe_probe_no_mz,
.exec = pxe_exec,
},
{
.name = "PXE-NBP (may be EFI?)",
.probe = pxe_probe,
.exec = pxe_exec,
},
};

140
src/arch/x86/image/sdi.c Normal file
View File

@@ -0,0 +1,140 @@
/*
* 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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,
};