diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 0d01f6a0..979eb2d7 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 ); @@ -166,6 +167,63 @@ static int bzimage_set_cmdline ( struct image *image, return 0; } +/** + * 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, rounded up to 4 bytes + */ +static size_t bzimage_load_initrd ( struct image *image, + struct image *initrd, + userptr_t address ) { + char *filename = initrd->cmdline; + struct cpio_header cpio; + size_t offset = 0; + + /* Ignore images which aren't initrds */ + if ( initrd->type != &initrd_image_type ) + return 0; + + /* Create cpio header before non-prebuilt images */ + if ( filename[0] ) { + size_t name_len = ( strlen ( filename ) + 1 ); + + DBGC ( image, "bzImage %p inserting initrd %p as %s\n", + image, initrd, filename ); + 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 ( address ) { + copy_to_user ( address, offset, &cpio, + sizeof ( cpio ) ); + } + offset += sizeof ( cpio ); + if ( address ) { + copy_to_user ( address, offset, filename, + name_len ); + } + offset += name_len; + offset = ( ( offset + 0x03 ) & ~0x03 ); + } + + /* Copy in initrd image body */ + if ( address ) { + DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", + image, initrd, address, ( address + offset ) ); + memcpy_user ( address, offset, initrd->data, 0, + initrd->len ); + } + offset += initrd->len; + + offset = ( ( offset + 0x03 ) & ~0x03 ); + return offset; +} + /** * Load initrds, if any * @@ -173,21 +231,16 @@ static int bzimage_set_cmdline ( struct image *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 ) { +static int bzimage_load_initrds ( struct image *image, + struct bzimage_exec_context *exec_ctx ) { struct image *initrd; - size_t initrd_len; size_t total_len = 0; - size_t offset = 0; - physaddr_t start; + physaddr_t address; int rc; /* Add up length of all initrd images */ for_each_image ( initrd ) { - if ( initrd->type != &initrd_image_type ) - continue; - initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f ); - total_len += initrd_len; + total_len += bzimage_load_initrd ( image, initrd, UNULL ); } /* Give up if no initrd images found */ @@ -198,47 +251,39 @@ static int bzimage_load_initrd ( struct image *image, * starting from the downloaded kernel image itself and * working downwards until we hit an available region. */ - for ( start = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ; - start -= 0x100000 ) { + for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ; + address -= 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 ) ) { + if ( address <= ( 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 + total_len ) > exec_ctx->mem_limit ) + if ( ( address + total_len ) > exec_ctx->mem_limit ) continue; /* Prepare and verify segment */ - if ( ( rc = prep_segment ( phys_to_user ( start ), 0, + if ( ( rc = prep_segment ( phys_to_user ( address ), 0, total_len ) ) != 0 ) continue; /* Use this address */ break; } + /* Record initrd location */ + exec_ctx->ramdisk_image = address; + exec_ctx->ramdisk_size = total_len; + /* Construct initrd */ DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n", - image, start, ( start + total_len ) ); + image, address, ( address + total_len ) ); for_each_image ( initrd ) { - if ( initrd->type != &initrd_image_type ) - continue; - initrd_len = ( ( initrd->len + 0x0f ) & ~0x0f ); - DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", - image, initrd, ( start + offset ), - ( start + offset + initrd->len ) ); - memcpy_user ( phys_to_user ( start ), offset, - initrd->data, 0, initrd->len ); - offset += initrd_len; + address += bzimage_load_initrd ( image, initrd, + phys_to_user ( address ) ); } - assert ( offset == total_len ); - - /* Record initrd location */ - exec_ctx->ramdisk_image = start; - exec_ctx->ramdisk_size = total_len; return 0; } @@ -281,7 +326,7 @@ static int bzimage_exec ( struct image *image ) { return rc; /* Load any initrds */ - if ( ( rc = bzimage_load_initrd ( image, &exec_ctx ) ) != 0 ) + if ( ( rc = bzimage_load_initrds ( image, &exec_ctx ) ) != 0 ) return rc; /* Update and store kernel header */ diff --git a/src/core/cpio.c b/src/core/cpio.c new file mode 100644 index 00000000..7d2e8828 --- /dev/null +++ b/src/core/cpio.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * + * CPIO archives + * + */ + +#include +#include +#include + +/** + * Set field within a CPIO header + * + * @v field Field within CPIO header + * @v value Value to set + */ +void cpio_set_field ( char *field, unsigned long value ) { + char buf[9]; + + snprintf ( buf, sizeof ( buf ), "%08lx", value ); + memcpy ( field, buf, 8 ); +} diff --git a/src/include/gpxe/cpio.h b/src/include/gpxe/cpio.h new file mode 100644 index 00000000..ba6f844a --- /dev/null +++ b/src/include/gpxe/cpio.h @@ -0,0 +1,51 @@ +#ifndef _GPXE_CPIO_H +#define _GPXE_CPIO_H + +/** @file + * + * CPIO archives + * + */ + +/** A CPIO archive header + * + * All field are hexadecimal ASCII numbers padded with '0' on the + * left to the full width of the field. + */ +struct cpio_header { + /** The string "070701" or "070702" */ + char c_magic[6]; + /** File inode number */ + char c_ino[8]; + /** File mode and permissions */ + char c_mode[8]; + /** File uid */ + char c_uid[8]; + /** File gid */ + char c_gid[8]; + /** Number of links */ + char c_nlink[8]; + /** Modification time */ + char c_mtime[8]; + /** Size of data field */ + char c_filesize[8]; + /** Major part of file device number */ + char c_maj[8]; + /** Minor part of file device number */ + char c_min[8]; + /** Major part of device node reference */ + char c_rmaj[8]; + /** Minor part of device node reference */ + char c_rmin[8]; + /** Length of filename, including final NUL */ + char c_namesize[8]; + /** Checksum of data field if c_magic is 070702, othersize zero */ + char c_chksum[8]; +} __attribute__ (( packed )); + +/** CPIO magic */ +#define CPIO_MAGIC "070701" + +extern void cpio_set_field ( char *field, unsigned long value ); + +#endif /* _GPXE_CPIO_H */