mirror of
https://github.com/ipxe/ipxe
synced 2026-02-12 21:29:39 +03:00
[initrd] Split out initrd construction from bzimage.c
Provide a reusable function initrd_load_all() to load all initrds (including any constructed CPIO headers) into a contiguous memory region, and support functions to find the constructed total length and permissible post-reshuffling load address range. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -77,9 +77,9 @@ struct bzimage_context {
|
|||||||
/** Memory limit */
|
/** Memory limit */
|
||||||
uint64_t mem_limit;
|
uint64_t mem_limit;
|
||||||
/** Initrd address */
|
/** Initrd address */
|
||||||
physaddr_t ramdisk_image;
|
void *initrd;
|
||||||
/** Initrd size */
|
/** Initrd size */
|
||||||
physaddr_t ramdisk_size;
|
physaddr_t initrd_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,8 +226,8 @@ static void bzimage_update_header ( struct image *image,
|
|||||||
|
|
||||||
/* Set initrd address */
|
/* Set initrd address */
|
||||||
if ( bzimg->version >= 0x0200 ) {
|
if ( bzimg->version >= 0x0200 ) {
|
||||||
bzhdr->ramdisk_image = bzimg->ramdisk_image;
|
bzhdr->ramdisk_image = virt_to_phys ( bzimg->initrd );
|
||||||
bzhdr->ramdisk_size = bzimg->ramdisk_size;
|
bzhdr->ramdisk_size = bzimg->initrd_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,67 +316,6 @@ static void bzimage_set_cmdline ( struct image *image,
|
|||||||
image->name, rm_cmdline );
|
image->name, rm_cmdline );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load initrd
|
|
||||||
*
|
|
||||||
* @v image bzImage image
|
|
||||||
* @v initrd initrd image
|
|
||||||
* @v address Address at which to load, or NULL
|
|
||||||
* @ret len Length of loaded image, excluding zero-padding
|
|
||||||
*/
|
|
||||||
static size_t bzimage_load_initrd ( struct image *image,
|
|
||||||
struct image *initrd,
|
|
||||||
void *address ) {
|
|
||||||
const char *filename = cpio_name ( initrd );
|
|
||||||
struct cpio_header cpio;
|
|
||||||
size_t offset;
|
|
||||||
size_t cpio_len;
|
|
||||||
size_t pad_len;
|
|
||||||
size_t len;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
/* Skip hidden images */
|
|
||||||
if ( initrd->flags & IMAGE_HIDDEN )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Determine length of cpio headers for non-prebuilt images */
|
|
||||||
len = 0;
|
|
||||||
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ )
|
|
||||||
len += ( cpio_len + cpio_pad_len ( cpio_len ) );
|
|
||||||
|
|
||||||
/* Copy in initrd image body and construct any cpio headers */
|
|
||||||
if ( address ) {
|
|
||||||
memmove ( ( address + len ), initrd->data, initrd->len );
|
|
||||||
memset ( address, 0, len );
|
|
||||||
offset = 0;
|
|
||||||
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ;
|
|
||||||
i++ ) {
|
|
||||||
memcpy ( ( address + offset ), &cpio,
|
|
||||||
sizeof ( cpio ) );
|
|
||||||
memcpy ( ( address + offset + sizeof ( cpio ) ),
|
|
||||||
filename, ( cpio_len - sizeof ( cpio ) ) );
|
|
||||||
offset += ( cpio_len + cpio_pad_len ( cpio_len ) );
|
|
||||||
}
|
|
||||||
assert ( offset == len );
|
|
||||||
DBGC ( image, "bzImage %s initrd %s [%#08lx,%#08lx,%#08lx)"
|
|
||||||
"%s%s\n", image->name, initrd->name,
|
|
||||||
virt_to_phys ( address ),
|
|
||||||
( virt_to_phys ( address ) + offset ),
|
|
||||||
( virt_to_phys ( address ) + offset + initrd->len ),
|
|
||||||
( filename ? " " : "" ), ( filename ? filename : "" ) );
|
|
||||||
DBGC2_MD5A ( image, ( virt_to_phys ( address ) + offset ),
|
|
||||||
( address + offset ), initrd->len );
|
|
||||||
}
|
|
||||||
len += initrd->len;
|
|
||||||
|
|
||||||
/* Zero-pad to next INITRD_ALIGN boundary */
|
|
||||||
pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) );
|
|
||||||
if ( address )
|
|
||||||
memset ( ( address + len ), 0, pad_len );
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that initrds can be loaded
|
* Check that initrds can be loaded
|
||||||
*
|
*
|
||||||
@@ -386,48 +325,52 @@ static size_t bzimage_load_initrd ( struct image *image,
|
|||||||
*/
|
*/
|
||||||
static int bzimage_check_initrds ( struct image *image,
|
static int bzimage_check_initrds ( struct image *image,
|
||||||
struct bzimage_context *bzimg ) {
|
struct bzimage_context *bzimg ) {
|
||||||
struct image *initrd;
|
struct memmap_region region;
|
||||||
physaddr_t bottom;
|
physaddr_t min;
|
||||||
size_t len = 0;
|
physaddr_t max;
|
||||||
|
physaddr_t dest;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Calculate total loaded length of initrds */
|
/* Calculate total loaded length of initrds */
|
||||||
for_each_image ( initrd ) {
|
bzimg->initrd_size = initrd_len();
|
||||||
|
|
||||||
/* Calculate length */
|
/* Succeed if there are no initrds */
|
||||||
len += bzimage_load_initrd ( image, initrd, NULL );
|
if ( ! bzimg->initrd_size )
|
||||||
len = initrd_align ( len );
|
return 0;
|
||||||
|
|
||||||
DBGC ( image, "bzImage %s initrd %s from [%#08lx,%#08lx)%s%s\n",
|
/* Calculate available load region after reshuffling */
|
||||||
image->name, initrd->name, virt_to_phys ( initrd->data ),
|
if ( ( rc = initrd_region ( bzimg->initrd_size, ®ion ) ) != 0 ) {
|
||||||
( virt_to_phys ( initrd->data ) + initrd->len ),
|
DBGC ( image, "bzImage %s no region for initrds: %s\n",
|
||||||
( initrd->cmdline ? " " : "" ),
|
|
||||||
( initrd->cmdline ? initrd->cmdline : "" ) );
|
|
||||||
DBGC2_MD5A ( image, virt_to_phys ( initrd->data ),
|
|
||||||
initrd->data, initrd->len );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate lowest usable address */
|
|
||||||
bottom = virt_to_phys ( 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 %s failed reshuffle check: %s\n",
|
|
||||||
image->name, strerror ( rc ) );
|
image->name, strerror ( rc ) );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that total length fits within kernel's memory limit */
|
/* Limit region to avoiding kernel itself */
|
||||||
if ( ( bottom + len ) > bzimg->mem_limit ) {
|
min = virt_to_phys ( bzimg->pm_kernel + bzimg->pm_sz );
|
||||||
|
if ( min < region.addr )
|
||||||
|
min = region.addr;
|
||||||
|
|
||||||
|
/* Limit region to kernel's memory limit */
|
||||||
|
max = region.last;
|
||||||
|
if ( max > bzimg->mem_limit )
|
||||||
|
max = bzimg->mem_limit;
|
||||||
|
|
||||||
|
/* Calculate installation address */
|
||||||
|
if ( max < ( bzimg->initrd_size - 1 ) ) {
|
||||||
DBGC ( image, "bzImage %s not enough space for initrds\n",
|
DBGC ( image, "bzImage %s not enough space for initrds\n",
|
||||||
image->name );
|
image->name );
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
|
dest = ( ( max + 1 - bzimg->initrd_size ) & ~( INITRD_ALIGN - 1 ) );
|
||||||
|
if ( dest < min ) {
|
||||||
|
DBGC ( image, "bzImage %s not enough space for initrds\n",
|
||||||
|
image->name );
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
bzimg->initrd = phys_to_virt ( dest );
|
||||||
|
|
||||||
|
DBGC ( image, "bzImage %s loading initrds from %#08lx downwards\n",
|
||||||
|
image->name, max );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,63 +382,21 @@ static int bzimage_check_initrds ( struct image *image,
|
|||||||
*/
|
*/
|
||||||
static void bzimage_load_initrds ( struct image *image,
|
static void bzimage_load_initrds ( struct image *image,
|
||||||
struct bzimage_context *bzimg ) {
|
struct bzimage_context *bzimg ) {
|
||||||
struct image *initrd;
|
|
||||||
struct image *other;
|
|
||||||
physaddr_t bottom;
|
|
||||||
physaddr_t top;
|
|
||||||
physaddr_t dest;
|
|
||||||
size_t offset;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
/* Reshuffle initrds into desired order */
|
|
||||||
bottom = virt_to_phys ( bzimg->pm_kernel + bzimg->pm_sz );
|
|
||||||
initrd_reshuffle ( bottom );
|
|
||||||
|
|
||||||
/* Find highest usable address */
|
|
||||||
top = 0;
|
|
||||||
for_each_image ( initrd ) {
|
|
||||||
if ( virt_to_phys ( initrd->data ) >= top ) {
|
|
||||||
top = ( virt_to_phys ( initrd->data ) +
|
|
||||||
initrd_align ( initrd->len ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do nothing if there are no initrds */
|
/* Do nothing if there are no initrds */
|
||||||
if ( ! top )
|
if ( ! bzimg->initrd )
|
||||||
return;
|
return;
|
||||||
if ( ( top - 1UL ) > bzimg->mem_limit ) {
|
|
||||||
top = ( ( bzimg->mem_limit + 1 ) & ~( INITRD_ALIGN - 1 ) );
|
|
||||||
}
|
|
||||||
DBGC ( image, "bzImage %s loading initrds from %#08lx downwards\n",
|
|
||||||
image->name, ( top - 1UL ) );
|
|
||||||
|
|
||||||
/* Load initrds in order */
|
/* Reshuffle initrds into desired order */
|
||||||
for_each_image ( initrd ) {
|
initrd_reshuffle();
|
||||||
|
|
||||||
/* Calculate cumulative length of following
|
/* Load initrds */
|
||||||
* initrds (including padding).
|
|
||||||
*/
|
|
||||||
offset = 0;
|
|
||||||
for_each_image ( other ) {
|
|
||||||
if ( other == initrd )
|
|
||||||
offset = 0;
|
|
||||||
offset += bzimage_load_initrd ( image, other, NULL );
|
|
||||||
offset = initrd_align ( offset );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load initrd at this address */
|
|
||||||
dest = ( top - offset );
|
|
||||||
len = bzimage_load_initrd ( image, initrd,
|
|
||||||
phys_to_virt ( dest ) );
|
|
||||||
|
|
||||||
/* Record initrd location */
|
|
||||||
if ( ! bzimg->ramdisk_image )
|
|
||||||
bzimg->ramdisk_image = dest;
|
|
||||||
bzimg->ramdisk_size = ( dest + len - bzimg->ramdisk_image );
|
|
||||||
}
|
|
||||||
DBGC ( image, "bzImage %s initrds at [%#08lx,%#08lx)\n",
|
DBGC ( image, "bzImage %s initrds at [%#08lx,%#08lx)\n",
|
||||||
image->name, bzimg->ramdisk_image,
|
image->name, virt_to_phys ( bzimg->initrd ),
|
||||||
( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
|
( virt_to_phys ( bzimg->initrd ) + bzimg->initrd_size ) );
|
||||||
|
len = initrd_load_all ( bzimg->initrd );
|
||||||
|
assert ( len == bzimg->initrd_size );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -220,23 +220,19 @@ static void initrd_dump ( void ) {
|
|||||||
/**
|
/**
|
||||||
* Reshuffle initrds into desired order at top of memory
|
* Reshuffle initrds into desired order at top of memory
|
||||||
*
|
*
|
||||||
* @v bottom Lowest physical address available for initrds
|
|
||||||
*
|
|
||||||
* After this function returns, the initrds have been rearranged in
|
* After this function returns, the initrds have been rearranged in
|
||||||
* memory and the external heap structures will have been corrupted.
|
* memory and the external heap structures will have been corrupted.
|
||||||
* Reshuffling must therefore take place immediately prior to jumping
|
* Reshuffling must therefore take place immediately prior to jumping
|
||||||
* to the loaded OS kernel; no further execution within iPXE is
|
* to the loaded OS kernel; no further execution within iPXE is
|
||||||
* permitted.
|
* permitted.
|
||||||
*/
|
*/
|
||||||
void initrd_reshuffle ( physaddr_t bottom ) {
|
void initrd_reshuffle ( void ) {
|
||||||
physaddr_t top;
|
physaddr_t top;
|
||||||
|
|
||||||
/* Calculate limits of available space for initrds */
|
/* Calculate limits of available space for initrds */
|
||||||
top = ( initrd_top ? initrd_top : uheap_end );
|
top = ( initrd_top ? initrd_top : uheap_end );
|
||||||
assert ( bottom >= uheap_limit );
|
|
||||||
|
|
||||||
/* Debug */
|
/* Debug */
|
||||||
DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n", bottom, top );
|
|
||||||
initrd_dump();
|
initrd_dump();
|
||||||
|
|
||||||
/* Squash initrds as high as possible in memory */
|
/* Squash initrds as high as possible in memory */
|
||||||
@@ -250,23 +246,128 @@ void initrd_reshuffle ( physaddr_t bottom ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that there is enough space to reshuffle initrds
|
* Load initrd
|
||||||
*
|
*
|
||||||
* @v len Total length of initrds (including padding)
|
* @v initrd initrd image
|
||||||
* @v bottom Lowest physical address available for initrds
|
* @v address Address at which to load, or NULL
|
||||||
* @ret rc Return status code
|
* @ret len Length of loaded image, excluding zero-padding
|
||||||
*/
|
*/
|
||||||
int initrd_reshuffle_check ( size_t len, physaddr_t bottom ) {
|
static size_t initrd_load ( struct image *initrd, void *address ) {
|
||||||
physaddr_t top;
|
const char *filename = cpio_name ( initrd );
|
||||||
|
struct cpio_header cpio;
|
||||||
|
size_t offset;
|
||||||
|
size_t cpio_len;
|
||||||
|
size_t len;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
assert ( ( address == NULL ) ||
|
||||||
|
( ( virt_to_phys ( address ) & ( INITRD_ALIGN - 1 ) ) == 0 ));
|
||||||
|
|
||||||
|
/* Skip hidden images */
|
||||||
|
if ( initrd->flags & IMAGE_HIDDEN )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Determine length of cpio headers for non-prebuilt images */
|
||||||
|
len = 0;
|
||||||
|
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ )
|
||||||
|
len += ( cpio_len + cpio_pad_len ( cpio_len ) );
|
||||||
|
|
||||||
|
/* Copy in initrd image body and construct any cpio headers */
|
||||||
|
if ( address ) {
|
||||||
|
memmove ( ( address + len ), initrd->data, initrd->len );
|
||||||
|
memset ( address, 0, len );
|
||||||
|
offset = 0;
|
||||||
|
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ;
|
||||||
|
i++ ) {
|
||||||
|
memcpy ( ( address + offset ), &cpio,
|
||||||
|
sizeof ( cpio ) );
|
||||||
|
memcpy ( ( address + offset + sizeof ( cpio ) ),
|
||||||
|
filename, ( cpio_len - sizeof ( cpio ) ) );
|
||||||
|
offset += ( cpio_len + cpio_pad_len ( cpio_len ) );
|
||||||
|
}
|
||||||
|
assert ( offset == len );
|
||||||
|
DBGC ( &images, "INITRD %s [%#08lx,%#08lx,%#08lx)%s%s\n",
|
||||||
|
initrd->name, virt_to_phys ( address ),
|
||||||
|
( virt_to_phys ( address ) + offset ),
|
||||||
|
( virt_to_phys ( address ) + offset + initrd->len ),
|
||||||
|
( filename ? " " : "" ), ( filename ? filename : "" ) );
|
||||||
|
DBGC2_MD5A ( &images, ( virt_to_phys ( address ) + offset ),
|
||||||
|
( address + offset ), initrd->len );
|
||||||
|
}
|
||||||
|
len += initrd->len;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all initrds
|
||||||
|
*
|
||||||
|
* @v address Load address, or NULL
|
||||||
|
* @ret len Length
|
||||||
|
*
|
||||||
|
* This function is called after the point of no return, when the
|
||||||
|
* external heap has been corrupted by reshuffling and there is no way
|
||||||
|
* to resume normal execution. The caller must have previously
|
||||||
|
* ensured that there is no way for installation to this address to
|
||||||
|
* fail.
|
||||||
|
*/
|
||||||
|
size_t initrd_load_all ( void *address ) {
|
||||||
|
struct image *initrd;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t pad_len;
|
||||||
|
void *dest;
|
||||||
|
|
||||||
|
/* Load all initrds */
|
||||||
|
for_each_image ( initrd ) {
|
||||||
|
|
||||||
|
/* Zero-pad to next INITRD_ALIGN boundary */
|
||||||
|
pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) );
|
||||||
|
if ( address )
|
||||||
|
memset ( ( address + len ), 0, pad_len );
|
||||||
|
len += pad_len;
|
||||||
|
assert ( len == initrd_align ( len ) );
|
||||||
|
|
||||||
|
/* Load initrd */
|
||||||
|
dest = ( address ? ( address + len ) : NULL );
|
||||||
|
len += initrd_load ( initrd, dest );
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate post-reshuffle initrd load region
|
||||||
|
*
|
||||||
|
* @v len Length of initrds (from initrd_len())
|
||||||
|
* @v region Region descriptor to fill in
|
||||||
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* If successful, then any suitably aligned range within the region
|
||||||
|
* may be used as the load address after reshuffling. The caller does
|
||||||
|
* not need to call prep_segment() for a range in this region.
|
||||||
|
* (Calling prep_segment() would probably fail, since prior to
|
||||||
|
* reshuffling the region is still in use by the external heap.)
|
||||||
|
*/
|
||||||
|
int initrd_region ( size_t len, struct memmap_region *region ) {
|
||||||
|
physaddr_t min;
|
||||||
size_t available;
|
size_t available;
|
||||||
|
|
||||||
/* Calculate limits of available space for initrds */
|
/* Calculate limits of available space for initrds */
|
||||||
top = ( initrd_top ? initrd_top : uheap_end );
|
min = uheap_limit;
|
||||||
assert ( bottom >= uheap_limit );
|
available = ( ( initrd_top ? initrd_top : uheap_end ) - min );
|
||||||
available = ( top - bottom );
|
if ( available < len )
|
||||||
|
return -ENOSPC;
|
||||||
|
DBGC ( &images, "INITRD post-reshuffle region is [%#08lx,%#08lx)\n",
|
||||||
|
min, ( min + available ) );
|
||||||
|
|
||||||
/* Check for available space */
|
/* Populate region descriptor */
|
||||||
return ( ( len < available ) ? 0 : -ENOBUFS );
|
region->addr = min;
|
||||||
|
region->last = ( min + available - 1 );
|
||||||
|
region->flags = MEMMAP_FL_MEMORY;
|
||||||
|
region->name = "initrd";
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,13 +10,15 @@
|
|||||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <ipxe/memmap.h>
|
||||||
extern void initrd_reshuffle ( physaddr_t bottom );
|
|
||||||
extern int initrd_reshuffle_check ( size_t len, physaddr_t bottom );
|
|
||||||
|
|
||||||
/** Initial ramdisk chunk alignment */
|
/** Initial ramdisk chunk alignment */
|
||||||
#define INITRD_ALIGN 4096
|
#define INITRD_ALIGN 4096
|
||||||
|
|
||||||
|
extern void initrd_reshuffle ( void );
|
||||||
|
extern int initrd_region ( size_t len, struct memmap_region *region );
|
||||||
|
extern size_t initrd_load_all ( void *address );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Align initrd length
|
* Align initrd length
|
||||||
*
|
*
|
||||||
@@ -29,4 +31,15 @@ initrd_align ( size_t len ) {
|
|||||||
return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
|
return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get required length for initrds
|
||||||
|
*
|
||||||
|
* @ret len Required length
|
||||||
|
*/
|
||||||
|
static inline __attribute__ (( always_inline )) size_t
|
||||||
|
initrd_len ( void ) {
|
||||||
|
|
||||||
|
return initrd_load_all ( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _IPXE_INITRD_H */
|
#endif /* _IPXE_INITRD_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user