]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[multiboot] Place multiboot modules low in memory
authorMichael Brown <mcb30@ipxe.org>
Sun, 15 Apr 2012 00:35:39 +0000 (01:35 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 19 Apr 2012 12:13:32 +0000 (13:13 +0100)
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>
src/arch/i386/image/elfboot.c
src/arch/i386/image/multiboot.c
src/image/elf.c
src/include/ipxe/elf.h

index 89e70a3b8fe480a544cb36dce9e8d23c1eaa1b5f..21504b7ad761d854866262ac9b8ceef6067af594 100644 (file)
@@ -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;
index 0336581f582e16f16a3ce213432659516df35cdf..004a01b1270648d9aa8719e9dbc3cdcff89233b6 100644 (file)
@@ -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++;
-       }
-
-       /* 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;
 }
 
 /**
@@ -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.
index f4ea4aab0ea1c65071296378a75ab769e783db36..26666ec5064c441a82cb0b214c5982b504a2e0f4 100644 (file)
@@ -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;
                }
        }
index e5fed2f89d68d9c05a83edf8aff6d70711f98597..ec675c0473d7ce1f26a854345605e7394645c76a 100644 (file)
@@ -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 */