mirror of
https://github.com/ipxe/ipxe
synced 2026-05-09 18:00:12 +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:
+117
-16
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user