mirror of https://github.com/ipxe/ipxe.git
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
parent
bc5c612f75
commit
78ae4f9899
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue