[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] generic ELF loading (updated)
From: |
Hollis Blanchard |
Subject: |
Re: [PATCH] generic ELF loading (updated) |
Date: |
Fri, 27 Oct 2006 14:21:16 -0500 |
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
- Re: [PATCH] generic ELF loading (updated),
Hollis Blanchard <=