On Fri, 2006-10-27 at 14:19 -0500, Hollis Blanchard wrote: > Here is the updated ELF loading infrastructure. A couple notes: > > It is now a module, which will be auto-loaded by loaders that need it. > However, I don't know where to put elf.c. The only similar files are > io/gzio.c and fs/fshelp.c, but clearly elf.c doesn't belong in either > of those directories. > > I have not updated i386/pc/multiboot.c to use this. That file needs > refactoring for multiboot2 anyways, so the work I'm doing for > multiboot2 will replace that loader. > > Comments please. Also see next mail to see how the PPC Linux loader > uses it.
Here is the PPC loader patch. This works on 32-bit PPC still. Once I have retested loading 64-bit kernels I intend to check both patches in. -Hollis Index: grub2-cvs/include/grub/types.h =================================================================== --- grub2-cvs.orig/include/grub/types.h 2006-10-26 17:59:09.000000000 -0500 +++ grub2-cvs/include/grub/types.h 2006-10-27 13:14:20.000000000 -0500 @@ -23,6 +23,8 @@ #include <config.h> #include <grub/cpu/types.h> +#define __unused __attribute__ ((unused)) + #ifdef GRUB_UTIL # define GRUB_CPU_SIZEOF_VOID_P SIZEOF_VOID_P # define GRUB_CPU_SIZEOF_LONG SIZEOF_LONG Index: grub2-cvs/loader/powerpc/ieee1275/linux.c =================================================================== --- grub2-cvs.orig/loader/powerpc/ieee1275/linux.c 2006-10-26 17:59:09.000000000 -0500 +++ grub2-cvs/loader/powerpc/ieee1275/linux.c 2006-10-27 13:18:31.000000000 -0500 @@ -19,6 +19,7 @@ */ #include <grub/elf.h> +#include <grub/elfload.h> #include <grub/loader.h> #include <grub/dl.h> #include <grub/mm.h> @@ -27,6 +28,9 @@ #include <grub/ieee1275/ieee1275.h> #include <grub/machine/loader.h> +#define ELF32_LOADMASK (0xc0000000UL) +#define ELF64_LOADMASK (0xc000000000000000ULL) + static grub_dl_t my_mod; static int loaded; @@ -97,53 +101,76 @@ grub_linux_unload (void) return err; } -void -grub_rescue_cmd_linux (int argc, char *argv[]) +static grub_err_t +grub_linux_load32 (grub_elf_t elf) { - grub_file_t file = 0; - Elf32_Ehdr ehdr; - Elf32_Phdr *phdrs = 0; - int i; - int offset = 0; - grub_addr_t entry; + Elf32_Addr entry; + Elf32_Addr segments_start = (Elf32_Addr) -1; + Elf32_Addr segments_end = 0; int found_addr = 0; - int size; - char *dest; - - grub_dl_ref (my_mod); - if (argc == 0) + /* Linux's entry point incorrectly contains a virtual address. */ + entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK; + if (entry == 0) { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); - goto fail; + entry = 0x01400000; + vmlinux = 1; } + else + vmlinux = 0; - file = grub_file_open (argv[0]); - if (! file) - goto fail; - - if (grub_file_read (file, (char *) &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + /* Run through the program headers to calculate the total memory size we + * should claim. */ + auto int calcsize (grub_elf_t _elf, Elf32_Phdr *phdr, void *_arg); + int calcsize (grub_elf_t __unused _elf, Elf32_Phdr *phdr, void __unused *_arg) + { + if (phdr->p_paddr < segments_start) + segments_start = phdr->p_paddr; + if (phdr->p_paddr + phdr->p_memsz > segments_end) + segments_end = phdr->p_paddr + phdr->p_memsz; + return 1; + } + grub_elf32_phdr_iterate (elf, calcsize, 0); + linux_size = segments_end - segments_start + 0x100000; + + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ + for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000) { - grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux elf header"); - goto fail; + grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n", + linux_addr, linux_size); + found_addr = grub_claimmap (linux_addr, linux_size); + if (found_addr != -1) + break; } + if (found_addr == -1) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not claim memory."); - if (grub_dl_check_header (&ehdr, sizeof(ehdr))) - { - grub_error (GRUB_ERR_UNKNOWN_OS, "No valid ELF header found"); - goto fail; + /* Now load the segments into the area we claimed. */ + auto int offset_phdr (Elf32_Phdr *phdr); + int offset_phdr (Elf32_Phdr *phdr) + { + /* Linux's program headers incorrectly contain virtual addresses. */ + phdr->p_paddr = phdr->p_paddr & ~ELF32_LOADMASK; + /* Offset to the area we claimed. */ + phdr->p_paddr += linux_addr; + return 0; } + return grub_elf32_load (elf, offset_phdr); +} - if (ehdr.e_type != ET_EXEC) - { - grub_error (GRUB_ERR_UNKNOWN_OS, - "This ELF file is not of the right type\n"); - goto fail; - } +static grub_err_t +grub_linux_load64 (grub_elf_t elf) +{ + Elf64_Addr entry; + Elf64_Addr segments_start = (Elf64_Addr) -1; + Elf64_Addr segments_end = 0; + int found_addr = 0; - /* Read the sections. */ - entry = ehdr.e_entry; - if (entry == 0xc0000000) + /* Linux's entry point incorrectly contains a virtual address. */ + entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK; + if (entry == 0) { entry = 0x01400000; vmlinux = 1; @@ -151,26 +178,23 @@ grub_rescue_cmd_linux (int argc, char *a else vmlinux = 0; - phdrs = (Elf32_Phdr *) grub_malloc (ehdr.e_phnum * ehdr.e_phentsize); - grub_file_read (file, (void *) phdrs, ehdr.e_phnum * ehdr.e_phentsize); - - /* Release the previously used memory. */ - grub_loader_unset (); - - /* Determine the amount of memory that is required. */ - linux_size = 0; - for (i = 0; i < ehdr.e_phnum; i++) - { - Elf32_Phdr *phdr = phdrs + i; - /* XXX: Is this calculation correct? */ - linux_size += phdr->p_memsz + phdr->p_filesz; - } - - /* Reserve memory for the kernel. */ - linux_size += 0x100000; - - /* For some vmlinux kernels the address set above won't work. Just - try some other addresses just like yaboot does. */ + /* Run through the program headers to calculate the total memory size we + * should claim. */ + auto int calcsize (grub_elf_t _elf, Elf64_Phdr *phdr, void *_arg); + int calcsize (grub_elf_t __unused _elf, Elf64_Phdr *phdr, void __unused *_arg) + { + if (phdr->p_paddr < segments_start) + segments_start = phdr->p_paddr; + if (phdr->p_paddr + phdr->p_memsz > segments_end) + segments_end = phdr->p_paddr + phdr->p_memsz; + return 1; + } + grub_elf64_phdr_iterate (elf, calcsize, 0); + linux_size = segments_end - segments_start + 0x100000; + + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000) { grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n", @@ -179,42 +203,61 @@ grub_rescue_cmd_linux (int argc, char *a if (found_addr != -1) break; } - if (found_addr == -1) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not claim memory"); - goto fail; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not claim memory."); + + /* Now load the segments into the area we claimed. */ + auto int offset_phdr (Elf64_Phdr *phdr); + int offset_phdr (Elf64_Phdr *phdr) + { + /* Linux's program headers incorrectly contain virtual addresses. */ + phdr->p_paddr = phdr->p_paddr & ~ELF64_LOADMASK; + /* Offset to the area we claimed. */ + phdr->p_paddr += linux_addr; + return 0; } - entry = linux_addr; + return grub_elf64_load (elf, offset_phdr); +} - /* Load every loadable segment in memory. */ - for (i = 0; i < ehdr.e_phnum; i++) - { - Elf32_Phdr *phdr = phdrs + i; +void +grub_rescue_cmd_linux (int argc, char *argv[]) +{ + grub_elf_t elf = 0; + int i; + int size; + char *dest; - if (phdr->p_type == PT_LOAD) - { - void *segment_addr = ((char *) entry) + offset; + grub_dl_ref (my_mod); - if (grub_file_seek (file, phdr->p_offset) == (grub_off_t) -1) - { - grub_error (GRUB_ERR_BAD_OS, "Invalid offset in program header"); - goto fail; - } + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); + goto out; + } - grub_dprintf ("loader", "Loading segment %d at %p, size 0x%x\n", i, - segment_addr, phdr->p_filesz); + elf = grub_elf_open (argv[0]); + if (! elf) + goto out; - if (grub_file_read (file, segment_addr, phdr->p_filesz) - != (grub_ssize_t) phdr->p_filesz) - goto fail; + if (elf->ehdr.ehdr32.e_type != ET_EXEC) + { + grub_error (GRUB_ERR_UNKNOWN_OS, + "This ELF file is not of the right type\n"); + goto out; + } - if (phdr->p_filesz < phdr->p_memsz) - grub_memset ((char *) (((char *) entry) + offset) + phdr->p_filesz, 0, - phdr->p_memsz - phdr->p_filesz); + /* Release the previously used memory. */ + grub_loader_unset (); - offset += phdr->p_filesz; - } + if (grub_elf_is_elf32 (elf)) + grub_linux_load32 (elf); + else + if (grub_elf_is_elf64 (elf)) + grub_linux_load64 (elf); + else + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unknown ELF class"); + goto out; } size = sizeof ("BOOT_IMAGE=") + grub_strlen (argv[0]); @@ -223,7 +266,7 @@ grub_rescue_cmd_linux (int argc, char *a linux_args = grub_malloc (size); if (! linux_args) - goto fail; + goto out; /* Specify the boot file. */ dest = grub_stpcpy (linux_args, "BOOT_IMAGE="); @@ -235,12 +278,10 @@ grub_rescue_cmd_linux (int argc, char *a dest = grub_stpcpy (dest, argv[i]); } - fail: +out: - if (file) - grub_file_close (file); - - grub_free (phdrs); + if (elf) + grub_elf_close (elf); if (grub_errno != GRUB_ERR_NONE) { @@ -254,8 +295,6 @@ grub_rescue_cmd_linux (int argc, char *a initrd_addr = 0; loaded = 1; } - - return; } void _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel