Skip to content

Commit

Permalink
[multiboot] Place multiboot modules low in memory
Browse files Browse the repository at this point in the history
Solaris assumes that there is enough space above the Multiboot modules
to use as a decompression and scratch area.  This assumption is
invalid when using iPXE, which places the Multiboot modules near the
top of (32-bit) memory.

Fix by copying the modules to an area of memory immediately following
the loaded kernel.

Debugged-by: Michael Brown <mcb30@ipxe.org>
Debugged-by: Scott McWhirter <scottm@joyent.com>
Tested-by: Robin Smidsrød <robin@smidsrod.no>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Apr 19, 2012
1 parent 2a0154d commit 62eb229
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 52 deletions.
3 changes: 2 additions & 1 deletion src/arch/i386/image/elfboot.c
Expand Up @@ -42,10 +42,11 @@ FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 );
*/
static int elfboot_exec ( struct image *image ) {
physaddr_t entry;
physaddr_t max;
int rc;

/* Load the image using core ELF support */
if ( ( rc = elf_load ( image, &entry ) ) != 0 ) {
if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) {
DBGC ( image, "ELF %p could not load: %s\n",
image, strerror ( rc ) );
return rc;
Expand Down
98 changes: 51 additions & 47 deletions src/arch/i386/image/multiboot.c
Expand Up @@ -143,7 +143,7 @@ static void multiboot_build_memmap ( struct image *image,
* @v image Image
* @ret physaddr Physical address of command line
*/
physaddr_t multiboot_add_cmdline ( struct image *image ) {
static physaddr_t multiboot_add_cmdline ( struct image *image ) {
char *mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
size_t remaining = ( sizeof ( mb_cmdlines ) - mb_cmdline_offset );
char *buf = mb_cmdline;
Expand Down Expand Up @@ -174,28 +174,26 @@ physaddr_t multiboot_add_cmdline ( struct image *image ) {
}

/**
* Build multiboot module list
* Add multiboot modules
*
* @v image Multiboot image
* @v modules Module list to fill, or NULL
* @ret count Number of modules
* @v start Start address for modules
* @v mbinfo Multiboot information structure
* @v modules Multiboot module list
* @ret rc Return status code
*/
static unsigned int
multiboot_build_module_list ( struct image *image,
struct multiboot_module *modules,
unsigned int limit ) {
static int multiboot_add_modules ( struct image *image, physaddr_t start,
struct multiboot_info *mbinfo,
struct multiboot_module *modules,
unsigned int limit ) {
struct image *module_image;
struct multiboot_module *module;
unsigned int count = 0;
unsigned int insert;
physaddr_t start;
physaddr_t end;
unsigned int i;
int rc;

/* Add each image as a multiboot module */
for_each_image ( module_image ) {

if ( count >= limit ) {
if ( mbinfo->mods_count >= limit ) {
DBGC ( image, "MULTIBOOT %p limit of %d modules "
"reached\n", image, limit );
break;
Expand All @@ -205,37 +203,36 @@ multiboot_build_module_list ( struct image *image,
if ( module_image == image )
continue;

/* At least some OSes expect the multiboot modules to
* be in ascending order, so we have to support it.
*/
start = user_to_phys ( module_image->data, 0 );
end = user_to_phys ( module_image->data, module_image->len );
for ( insert = 0 ; insert < count ; insert++ ) {
if ( start < modules[insert].mod_start )
break;
/* Page-align the module */
start = ( ( start + 0xfff ) & ~0xfff );

/* Prepare segment */
if ( ( rc = prep_segment ( phys_to_user ( start ),
module_image->len,
module_image->len ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p could not prepare module "
"%s: %s\n", image, module_image->name,
strerror ( rc ) );
return rc;
}
module = &modules[insert];
memmove ( ( module + 1 ), module,
( ( count - insert ) * sizeof ( *module ) ) );

/* Copy module */
memcpy_user ( phys_to_user ( start ), 0,
module_image->data, 0, module_image->len );

/* Add module to list */
module = &modules[mbinfo->mods_count++];
module->mod_start = start;
module->mod_end = end;
module->mod_end = ( start + module_image->len );
module->string = multiboot_add_cmdline ( module_image );
module->reserved = 0;

/* We promise to page-align modules */
assert ( ( module->mod_start & 0xfff ) == 0 );

count++;
}

/* Dump module configuration */
for ( i = 0 ; i < count ; i++ ) {
DBGC ( image, "MULTIBOOT %p module %d is [%x,%x)\n",
image, i, modules[i].mod_start,
modules[i].mod_end );
DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n",
image, module_image->name, module->mod_start,
module->mod_end );
start += module_image->len;
}

return count;
return 0;
}

/**
Expand Down Expand Up @@ -314,11 +311,12 @@ static int multiboot_find_header ( struct image *image,
* @v image Multiboot file
* @v hdr Multiboot header descriptor
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
static int multiboot_load_raw ( struct image *image,
struct multiboot_header_info *hdr,
physaddr_t *entry ) {
physaddr_t *entry, physaddr_t *max ) {
size_t offset;
size_t filesz;
size_t memsz;
Expand Down Expand Up @@ -349,8 +347,9 @@ static int multiboot_load_raw ( struct image *image,
/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, offset, filesz );

/* Record execution entry point */
/* Record execution entry point and maximum used address */
*entry = hdr->mb.entry_addr;
*max = ( hdr->mb.load_addr + memsz );

return 0;
}
Expand All @@ -360,13 +359,15 @@ static int multiboot_load_raw ( struct image *image,
*
* @v image Multiboot file
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
static int multiboot_load_elf ( struct image *image, physaddr_t *entry ) {
static int multiboot_load_elf ( struct image *image, physaddr_t *entry,
physaddr_t *max ) {
int rc;

/* Load ELF image*/
if ( ( rc = elf_load ( image, entry ) ) != 0 ) {
if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
image, strerror ( rc ) );
return rc;
Expand All @@ -384,6 +385,7 @@ static int multiboot_load_elf ( struct image *image, physaddr_t *entry ) {
static int multiboot_exec ( struct image *image ) {
struct multiboot_header_info hdr;
physaddr_t entry;
physaddr_t max;
int rc;

/* Locate multiboot header, if present */
Expand All @@ -405,8 +407,8 @@ static int multiboot_exec ( struct image *image ) {
* the ELF header if present, and Solaris relies on this
* behaviour.
*/
if ( ( ( rc = multiboot_load_elf ( image, &entry ) ) != 0 ) &&
( ( rc = multiboot_load_raw ( image, &hdr, &entry ) ) != 0 ) )
if ( ( ( rc = multiboot_load_elf ( image, &entry, &max ) ) != 0 ) &&
( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 ))
return rc;

/* Populate multiboot information structure */
Expand All @@ -415,11 +417,13 @@ static int multiboot_exec ( struct image *image ) {
MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
mb_cmdline_offset = 0;
mbinfo.cmdline = multiboot_add_cmdline ( image );
mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules,
( sizeof(mbmodules) / sizeof(mbmodules[0]) ) );
mbinfo.mods_addr = virt_to_phys ( mbmodules );
mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
if ( ( rc = multiboot_add_modules ( image, max, &mbinfo, mbmodules,
( sizeof ( mbmodules ) /
sizeof ( mbmodules[0] ) ) ) ) !=0)
return rc;

/* Multiboot images may not return and have no callback
* interface, so shut everything down prior to booting the OS.
Expand Down
18 changes: 15 additions & 3 deletions src/image/elf.c
Expand Up @@ -47,11 +47,14 @@ typedef Elf32_Off Elf_Off;
* @v phdr ELF program header
* @v ehdr ELF executable header
* @ret entry Entry point, if found
* @ret max Maximum used address
* @ret rc Return status code
*/
static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
Elf_Ehdr *ehdr, physaddr_t *entry ) {
Elf_Ehdr *ehdr, physaddr_t *entry,
physaddr_t *max ) {
physaddr_t dest;
physaddr_t end;
userptr_t buffer;
unsigned long e_offset;
int rc;
Expand Down Expand Up @@ -79,6 +82,7 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
return -ENOEXEC;
}
buffer = phys_to_user ( dest );
end = ( dest + phdr->p_memsz );

DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
Expand All @@ -93,6 +97,10 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
return rc;
}

/* Update maximum used address, if applicable */
if ( end > *max )
*max = end;

/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );

Expand All @@ -119,9 +127,10 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
*
* @v image ELF file
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
int elf_load ( struct image *image, physaddr_t *entry ) {
int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
static const uint8_t e_ident[] = {
[EI_MAG0] = ELFMAG0,
[EI_MAG1] = ELFMAG1,
Expand All @@ -143,6 +152,9 @@ int elf_load ( struct image *image, physaddr_t *entry ) {
return -ENOEXEC;
}

/* Initialise maximum used address */
*max = 0;

/* Invalidate entry point */
*entry = 0;

Expand All @@ -156,7 +168,7 @@ int elf_load ( struct image *image, physaddr_t *entry ) {
}
copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
if ( ( rc = elf_load_segment ( image, &phdr, &ehdr,
entry ) ) != 0 ) {
entry, max ) ) != 0 ) {
return rc;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/include/ipxe/elf.h
Expand Up @@ -12,6 +12,6 @@ FILE_LICENCE ( GPL2_OR_LATER );

#include <elf.h>

extern int elf_load ( struct image *image, physaddr_t *entry );
extern int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max );

#endif /* _IPXE_ELF_H */

0 comments on commit 62eb229

Please sign in to comment.