From a5f6408d8e8efedacd1c5d6b6c9d4430f8e61f82 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jan 2007 16:09:01 +0000 Subject: [PATCH] We can now load an initrd as well as a kernel --- src/arch/i386/image/bzimage.c | 98 +++++++++++++++++++++++++++++++-- src/arch/i386/include/bzimage.h | 2 + 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 59e95a9e..bc947769 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -35,6 +35,7 @@ #include #include #include +#include struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ); @@ -76,6 +77,10 @@ struct bzimage_exec_context { unsigned int vid_mode; /** Memory limit */ uint64_t mem_limit; + /** Initrd address */ + physaddr_t ramdisk_image; + /** Initrd size */ + physaddr_t ramdisk_size; }; /** @@ -104,8 +109,8 @@ static int bzimage_parse_cmdline ( struct image *image, } else { exec_ctx->vid_mode = strtoul ( vga, &vga, 16 ); if ( *vga && ( *vga != ' ' ) ) { - DBGC ( image, "bzImage %p strange \"vga=\"\n", - image ); + DBGC ( image, "bzImage %p strange \"vga=\"" + "terminator '%c'\n", image, *vga ); } } } @@ -116,18 +121,21 @@ static int bzimage_parse_cmdline ( struct image *image, exec_ctx->mem_limit = strtoul ( mem, &mem, 0 ); switch ( *mem ) { case 'G': + case 'g': exec_ctx->mem_limit <<= 10; case 'M': + case 'm': exec_ctx->mem_limit <<= 10; case 'K': + case 'k': exec_ctx->mem_limit <<= 10; break; case '\0': case ' ': break; default: - DBGC ( image, "bzImage %p strange \"mem=\"\n", - image ); + DBGC ( image, "bzImage %p strange \"mem=\" " + "terminator '%c'\n", image, *mem ); break; } } @@ -159,6 +167,63 @@ static int bzimage_set_cmdline ( struct image *image, return 0; } +/** + * Load initrd, if any + * + * @v image bzImage image + * @v exec_ctx Execution context + * @ret rc Return status code + */ +static int bzimage_load_initrd ( struct image *image, + struct bzimage_exec_context *exec_ctx, + struct image *initrd ) { + physaddr_t start = user_to_phys ( initrd->data, 0 ); + int rc; + + DBGC ( image, "bzImage %p loading initrd %p (%s)\n", + image, initrd, initrd->name ); + + /* Find a suitable start address */ + if ( ( start + initrd->len ) <= exec_ctx->mem_limit ) { + /* Just use initrd in situ */ + DBGC ( image, "bzImage %p using initrd as [%lx,%lx)\n", + image, start, ( start + initrd->len ) ); + } else { + for ( ; ; start -= 0x100000 ) { + /* Check that we're not going to overwrite the + * kernel itself. This check isn't totally + * accurate, but errs on the side of caution. + */ + if ( start <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) { + DBGC ( image, "bzImage %p could not find a " + "location for initrd\n", image ); + return -ENOBUFS; + } + /* Check that we are within the kernel's range */ + if ( ( start + initrd->len ) > exec_ctx->mem_limit ) + continue; + /* Prepare and verify segment */ + if ( ( rc = prep_segment ( phys_to_user ( start ), + initrd->len, + initrd->len ) ) != 0 ) + continue; + /* Copy to segment */ + DBGC ( image, "bzImage %p relocating initrd to " + "[%lx,%lx)\n", image, start, + ( start + initrd->len ) ); + memcpy_user ( phys_to_user ( start ), 0, + initrd->data, 0, initrd->len ); + break; + } + } + + /* Record initrd location */ + exec_ctx->ramdisk_image = start; + exec_ctx->ramdisk_size = initrd->len; + + return 0; +} + /** * Execute bzImage image * @@ -169,8 +234,12 @@ static int bzimage_exec ( struct image *image ) { struct bzimage_exec_context exec_ctx; struct bzimage_header bzhdr; const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + struct image *initrd; int rc; + /* Initialise context */ + memset ( &exec_ctx, 0, sizeof ( exec_ctx ) ); + /* Retrieve kernel header */ exec_ctx.rm_kernel_seg = image->priv.ul; exec_ctx.rm_kernel = real_to_user ( exec_ctx.rm_kernel_seg, 0 ); @@ -179,7 +248,11 @@ static int bzimage_exec ( struct image *image ) { exec_ctx.rm_cmdline = exec_ctx.rm_heap = ( bzhdr.heap_end_ptr + 0x200 ); exec_ctx.vid_mode = bzhdr.vid_mode; - exec_ctx.mem_limit = 0; + if ( bzhdr.version >= 0x0203 ) { + exec_ctx.mem_limit = ( bzhdr.initrd_addr_max + 1 ); + } else { + exec_ctx.mem_limit = ( BZI_INITRD_MAX + 1 ); + } /* Parse command line for bootloader parameters */ if ( ( rc = bzimage_parse_cmdline ( image, &exec_ctx, cmdline ) ) != 0) @@ -189,8 +262,20 @@ static int bzimage_exec ( struct image *image ) { if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 ) return rc; + /* Load an initrd, if one exists */ + for_each_image ( initrd ) { + if ( initrd->type == &initrd_image_type ) { + if ( ( rc = bzimage_load_initrd ( image, &exec_ctx, + initrd ) ) != 0 ) + return rc; + break; + } + } + /* Update and store kernel header */ bzhdr.vid_mode = exec_ctx.vid_mode; + bzhdr.ramdisk_image = exec_ctx.ramdisk_image; + bzhdr.ramdisk_size = exec_ctx.ramdisk_size; copy_to_user ( exec_ctx.rm_kernel, BZI_HDR_OFFSET, &bzhdr, sizeof ( bzhdr ) ); @@ -377,6 +462,9 @@ int bzimage_load ( struct image *image ) { struct bzimage_header bzhdr; int rc; + /* Initialise context */ + memset ( &load_ctx, 0, sizeof ( load_ctx ) ); + /* Load and verify header */ if ( ( rc = bzimage_load_header ( image, &load_ctx, &bzhdr ) ) != 0 ) return rc; diff --git a/src/arch/i386/include/bzimage.h b/src/arch/i386/include/bzimage.h index d97f76eb..e536b6e8 100644 --- a/src/arch/i386/include/bzimage.h +++ b/src/arch/i386/include/bzimage.h @@ -94,6 +94,8 @@ struct bzimage_header { /** bzImage special video mode "ask" */ #define BZI_VID_MODE_ASK 0xfffd +/** bzImage maximum initrd address for versions < 2.03 */ +#define BZI_INITRD_MAX 0x37ffffff /** bzImage command-line structure used by older kernels */ struct bzimage_cmdline {