[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:
Michael Brown
2025-05-23 12:13:02 +01:00
parent 11929389e4
commit 4a39b877dd
3 changed files with 177 additions and 162 deletions
+117 -16
View File
@@ -220,23 +220,19 @@ static void initrd_dump ( void ) {
/**
* 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
* 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 ( physaddr_t bottom ) {
void initrd_reshuffle ( void ) {
physaddr_t top;
/* Calculate limits of available space for initrds */
top = ( initrd_top ? initrd_top : uheap_end );
assert ( bottom >= uheap_limit );
/* Debug */
DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n", bottom, top );
initrd_dump();
/* 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 bottom Lowest physical address available for initrds
* @ret rc Return status code
* @v initrd initrd image
* @v address Address at which to load, or NULL
* @ret len Length of loaded image, excluding zero-padding
*/
int initrd_reshuffle_check ( size_t len, physaddr_t bottom ) {
physaddr_t top;
static size_t initrd_load ( struct image *initrd, void *address ) {
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;
/* Calculate limits of available space for initrds */
top = ( initrd_top ? initrd_top : uheap_end );
assert ( bottom >= uheap_limit );
available = ( top - bottom );
min = uheap_limit;
available = ( ( initrd_top ? initrd_top : uheap_end ) - min );
if ( available < len )
return -ENOSPC;
DBGC ( &images, "INITRD post-reshuffle region is [%#08lx,%#08lx)\n",
min, ( min + available ) );
/* Check for available space */
return ( ( len < available ) ? 0 : -ENOBUFS );
/* Populate region descriptor */
region->addr = min;
region->last = ( min + available - 1 );
region->flags = MEMMAP_FL_MEMORY;
region->name = "initrd";
return 0;
}
/**