/** Memory limit */
uint64_t mem_limit;
/** Initrd address */
- physaddr_t ramdisk_image;
+ void *initrd;
/** Initrd size */
- physaddr_t ramdisk_size;
+ physaddr_t initrd_size;
};
/**
/* Set initrd address */
if ( bzimg->version >= 0x0200 ) {
- bzhdr->ramdisk_image = bzimg->ramdisk_image;
- bzhdr->ramdisk_size = bzimg->ramdisk_size;
+ bzhdr->ramdisk_image = virt_to_phys ( bzimg->initrd );
+ bzhdr->ramdisk_size = bzimg->initrd_size;
}
}
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
*
*/
static int bzimage_check_initrds ( struct image *image,
struct bzimage_context *bzimg ) {
- struct image *initrd;
- physaddr_t bottom;
- size_t len = 0;
+ struct memmap_region region;
+ physaddr_t min;
+ physaddr_t max;
+ physaddr_t dest;
int rc;
/* Calculate total loaded length of initrds */
- for_each_image ( initrd ) {
-
- /* Calculate length */
- len += bzimage_load_initrd ( image, initrd, NULL );
- len = initrd_align ( len );
-
- DBGC ( image, "bzImage %s initrd %s from [%#08lx,%#08lx)%s%s\n",
- image->name, initrd->name, virt_to_phys ( initrd->data ),
- ( virt_to_phys ( initrd->data ) + initrd->len ),
- ( initrd->cmdline ? " " : "" ),
- ( initrd->cmdline ? initrd->cmdline : "" ) );
- DBGC2_MD5A ( image, virt_to_phys ( initrd->data ),
- initrd->data, initrd->len );
- }
+ bzimg->initrd_size = initrd_len();
- /* Calculate lowest usable address */
- bottom = virt_to_phys ( bzimg->pm_kernel + bzimg->pm_sz );
+ /* Succeed if there are no initrds */
+ if ( ! bzimg->initrd_size )
+ return 0;
- /* 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",
+ /* Calculate available load region after reshuffling */
+ if ( ( rc = initrd_region ( bzimg->initrd_size, ®ion ) ) != 0 ) {
+ DBGC ( image, "bzImage %s no region for initrds: %s\n",
image->name, strerror ( rc ) );
return rc;
}
- /* Check that total length fits within kernel's memory limit */
- if ( ( bottom + len ) > bzimg->mem_limit ) {
+ /* Limit region to avoiding kernel itself */
+ 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",
+ image->name );
+ 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;
}
*/
static void bzimage_load_initrds ( struct image *image,
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;
- /* 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 */
- if ( ! top )
+ if ( ! bzimg->initrd )
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 */
- for_each_image ( initrd ) {
-
- /* Calculate cumulative length of following
- * 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 ) );
+ /* Reshuffle initrds into desired order */
+ initrd_reshuffle();
- /* Record initrd location */
- if ( ! bzimg->ramdisk_image )
- bzimg->ramdisk_image = dest;
- bzimg->ramdisk_size = ( dest + len - bzimg->ramdisk_image );
- }
+ /* Load initrds */
DBGC ( image, "bzImage %s initrds at [%#08lx,%#08lx)\n",
- image->name, bzimg->ramdisk_image,
- ( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
+ image->name, virt_to_phys ( bzimg->initrd ),
+ ( virt_to_phys ( bzimg->initrd ) + bzimg->initrd_size ) );
+ len = initrd_load_all ( bzimg->initrd );
+ assert ( len == bzimg->initrd_size );
}
/**
/**
* 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 */
}
/**
- * Check that there is enough space to reshuffle initrds
+ * Load initrd
+ *
+ * @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 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
*
- * @v len Total length of initrds (including padding)
- * @v bottom Lowest physical address available for initrds
+ * 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_reshuffle_check ( size_t len, physaddr_t bottom ) {
- physaddr_t top;
+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 ) );
+
+ /* Populate region descriptor */
+ region->addr = min;
+ region->last = ( min + available - 1 );
+ region->flags = MEMMAP_FL_MEMORY;
+ region->name = "initrd";
- /* Check for available space */
- return ( ( len < available ) ? 0 : -ENOBUFS );
+ return 0;
}
/**