From 62eb2294f002f16af0af9e4f623dd03c67e8cf4a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 15 Apr 2012 01:35:39 +0100 Subject: [PATCH] [multiboot] Place multiboot modules low in memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Debugged-by: Scott McWhirter Tested-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/arch/i386/image/elfboot.c | 3 +- src/arch/i386/image/multiboot.c | 98 +++++++++++++++++---------------- src/image/elf.c | 18 +++++- src/include/ipxe/elf.h | 2 +- 4 files changed, 69 insertions(+), 52 deletions(-) diff --git a/src/arch/i386/image/elfboot.c b/src/arch/i386/image/elfboot.c index 89e70a3b..21504b7a 100644 --- a/src/arch/i386/image/elfboot.c +++ b/src/arch/i386/image/elfboot.c @@ -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; diff --git a/src/arch/i386/image/multiboot.c b/src/arch/i386/image/multiboot.c index 0336581f..004a01b1 100644 --- a/src/arch/i386/image/multiboot.c +++ b/src/arch/i386/image/multiboot.c @@ -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; @@ -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; @@ -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++; + DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n", + image, module_image->name, module->mod_start, + module->mod_end ); + start += module_image->len; } - /* 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 ); - } - - return count; + return 0; } /** @@ -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; @@ -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; } @@ -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; @@ -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 */ @@ -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 */ @@ -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. diff --git a/src/image/elf.c b/src/image/elf.c index f4ea4aab..26666ec5 100644 --- a/src/image/elf.c +++ b/src/image/elf.c @@ -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; @@ -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 ), @@ -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 ); @@ -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, @@ -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; @@ -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; } } diff --git a/src/include/ipxe/elf.h b/src/include/ipxe/elf.h index e5fed2f8..ec675c04 100644 --- a/src/include/ipxe/elf.h +++ b/src/include/ipxe/elf.h @@ -12,6 +12,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include -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 */