mirror of
https://github.com/ipxe/ipxe
synced 2026-01-03 10:23:00 +03:00
[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:
141
src/arch/x86/image/bootsector.c
Normal file
141
src/arch/x86/image/bootsector.c
Normal 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;
|
||||
}
|
||||
669
src/arch/x86/image/bzimage.c
Normal file
669
src/arch/x86/image/bzimage.c
Normal 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,
|
||||
};
|
||||
145
src/arch/x86/image/elfboot.c
Normal file
145
src/arch/x86/image/elfboot.c
Normal 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
304
src/arch/x86/image/initrd.c
Normal 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,
|
||||
};
|
||||
492
src/arch/x86/image/multiboot.c
Normal file
492
src/arch/x86/image/multiboot.c
Normal 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
427
src/arch/x86/image/nbi.c
Normal 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,
|
||||
};
|
||||
174
src/arch/x86/image/pxe_image.c
Normal file
174
src/arch/x86/image/pxe_image.c
Normal 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
140
src/arch/x86/image/sdi.c
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user