Find a segment where all modules fit

When performing a multiboot of SmartOS, it would error with
ERANGE_SEGMENT. This is due to the fact that the boot_archive was >300M,
and this wasn't fitting in the same segment the kernel occupied.

With the script:

    #!ipxe
    dhcp
    chain https://netboot.smartos.org/smartos.ipxe

The following output was seen:

    https://netboot.smartos.org/os/20220210T011229Z/platform/i86pc/kernel/amd64/unix... ok
    https://netboot.smartos.org/os/20220210T011229Z/platform/i86pc/amd64/boot_archive... ok
    https://netboot.smartos.org/os/20220210T011229Z/platform/i86pc/amd64/boot_archive.hash... ok
    Could not boot: Requested memory not available (https://ipxe.org/46038101)

This commit changes where modules are placed: after placing the image,
grab the memory map and find a segment that has space for *all* modules.
Set the module starting address to this instead of concatenating all
modules right after the image.

After this, SmartOS iPXE boots ok.
pull/594/head
James MacMahon 2022-02-17 11:23:26 -05:00
parent bc5c612f75
commit 78ae4f9899
1 changed files with 102 additions and 1 deletions

View File

@ -178,6 +178,101 @@ static physaddr_t multiboot_add_cmdline ( struct image *image ) {
return virt_to_phys ( mb_cmdline );
}
/**
* What is the total size of all modules?
*
* @v image Multiboot image
* @ret total_len Return total size in bytes
*/
static size_t multiboot_total_modules_size (struct image *image) {
struct image *module_image;
size_t total_len = 0;
for_each_image ( module_image ) {
if (module_image == image) {
continue;
}
total_len += module_image->len;
}
return total_len;
}
/**
* Find a segment where all concatenatee modules fit.
*
* After placing the image, grab the memory map and find a segment that has
* space for all modules.
*
* Returns ERANGE_SEGMENT if there isn't enough room anywhere for all the
* modules, 0 otherwise.
*
* @v image Multiboot image
* @v image_max Max physical address of image
* @ret modules_start_addr Physical address to place all modules
* @ret rc Return status code
*/
static int multiboot_find_modules_start_addr(struct image *image,
physaddr_t image_max, physaddr_t * modules_start_addr) {
size_t total_len = multiboot_total_modules_size(image);
DBGC ( image, "MULTIBOOT %p total size of modules is %d\n", image,
total_len);
struct memory_map memmap;
get_memmap ( &memmap );
DBGC ( image, "MULTIBOOT %p searching for module region\n", image );
/* Find index of memory region that will fit all modules */
unsigned int i = 0;
for ( i = 0 ; i < memmap.count ; i++ ) {
uint64_t start = memmap.regions[i].start;
uint64_t end = memmap.regions[i].end;
/* If memory region contains image_max, it contains the image so don't
* overwrite it. Test fit from image_max to end.
*/
if ((start <= image_max) && (image_max < end)) {
uint64_t region_size = end - image_max;
DBGC ( image, "MULTIBOOT %p memmap region %d start %llx end %llx sz "
"%llx contains image_max %lx\n", image, i, start, end, region_size,
image_max );
if (region_size >= total_len) {
DBGC ( image, "MULTIBOOT %p memmap region %d has enough space\n",
image, i );
*modules_start_addr = image_max;
break;
}
}
/* Otherwise, test fit from start to end of segment. */
else
{
uint64_t region_size = end - start;
DBGC ( image, "MULTIBOOT %p memmap region %d start %llx end %llx sz "
"%llx\n", image, i, start, end, region_size );
if (region_size >= total_len) {
DBGC ( image, "MULTIBOOT %p memmap region %d has enough space\n",
image, i );
*modules_start_addr = start;
break;
}
}
}
if (i == memmap.count) {
DBGC ( image, "MULTIBOOT %p can't fit modules in available memory", image);
return -ENOMEM;
}
DBGC ( image, "MULTIBOOT %p module start set at %lx\n", image,
*modules_start_addr );
return 0;
}
/**
* Add multiboot modules
*
@ -416,6 +511,12 @@ static int multiboot_exec ( struct image *image ) {
( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 ))
return rc;
/* Find modules start address */
physaddr_t modules_start_addr = 0;
if ((rc = multiboot_find_modules_start_addr(image, max, &modules_start_addr)) != 0) {
return rc;
}
/* Populate multiboot information structure */
memset ( &mbinfo, 0, sizeof ( mbinfo ) );
mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
@ -427,7 +528,7 @@ static int multiboot_exec ( struct image *image ) {
snprintf ( mb_bootloader_name, sizeof ( mb_bootloader_name ),
"iPXE %s", product_version );
mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
if ( ( rc = multiboot_add_modules ( image, max, &mbinfo, mbmodules,
if ( ( rc = multiboot_add_modules ( image, modules_start_addr, &mbinfo, mbmodules,
( sizeof ( mbmodules ) /
sizeof ( mbmodules[0] ) ) ) ) !=0)
return rc;