This patch converts the PowerPC Linux loader to use the ELF
infrastructure in the last patch, which gives us 64-bit ELF support. You
can see examples of how I'm using the ELF "iterate" and "load"
functions.

-Hollis

diff -r b7b3f308a91d include/grub/types.h
--- a/include/grub/types.h      Fri Oct 13 13:50:22 2006 -0500
+++ b/include/grub/types.h      Fri Oct 13 13:50:22 2006 -0500
@@ -22,6 +22,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
diff -r b7b3f308a91d loader/powerpc/ieee1275/linux.c
--- a/loader/powerpc/ieee1275/linux.c   Fri Oct 13 13:50:22 2006 -0500
+++ b/loader/powerpc/ieee1275/linux.c   Fri Oct 13 16:17:32 2006 -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,17 @@ grub_linux_unload (void)
   return err;
 }
 
-void
-grub_rescue_cmd_linux (int argc, char *argv[])
-{
-  grub_file_t file = 0;
-  Elf32_Ehdr ehdr;
-  Elf32_Phdr *phdrs = 0;
-  int i;
-  int offset = 0;
-  grub_addr_t entry;
+static grub_err_t
+grub_linux_load32 (grub_elf_t elf)
+{
+  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)
-    {
-      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
-      goto fail;
-    }
-
-  file = grub_file_open (argv[0]);
-  if (! file)
-    goto fail;
-
-  if (grub_file_read (file, (char *) &ehdr, sizeof (ehdr)) != sizeof (ehdr))
-    {
-      grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux elf header");
-      goto fail;
-    }
-
-  if (grub_dl_check_header (&ehdr, sizeof(ehdr)))
-    {
-      grub_error (GRUB_ERR_UNKNOWN_OS, "No valid ELF header found");
-      goto fail;
-    }
-
-  if (ehdr.e_type != ET_EXEC)
-    {
-      grub_error (GRUB_ERR_UNKNOWN_OS,
-                 "This ELF file is not of the right type\n");
-      goto fail;
-    }
-
-  /* Read the sections.  */
-  entry = ehdr.e_entry;
-  if (entry == 0xc0000000)
+
+  /* Linux's entry point incorrectly contains a virtual address.  */
+  entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK;
+  if (entry == 0)
     {
       entry = 0x01400000;
       vmlinux = 1;
@@ -151,26 +119,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, 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_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n", 
@@ -179,42 +144,120 @@ 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;
-    }
-  entry = linux_addr;
-
-  /* Load every loadable segment in memory.  */
-  for (i = 0; i < ehdr.e_phnum; i++)
-    {
-      Elf32_Phdr *phdr = phdrs + i;
-
-      if (phdr->p_type == PT_LOAD)
-       {
-         void *segment_addr = ((char *) entry) + offset;
-
-         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;
-           }
-
-         grub_dprintf ("loader", "Loading segment %d at %p, size 0x%x\n", i,
-                       segment_addr, phdr->p_filesz);
-
-         if (grub_file_read (file, segment_addr, phdr->p_filesz)
-             != (grub_ssize_t) phdr->p_filesz)
-           goto fail;
-
-         if (phdr->p_filesz < phdr->p_memsz)
-           grub_memset ((char *) (((char *) entry) + offset) + phdr->p_filesz, 
0,
-                        phdr->p_memsz - phdr->p_filesz);
-
-         offset += phdr->p_filesz;
-       }
+    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 (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);
+}
+
+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;
+
+  /* Linux's entry point incorrectly contains a virtual address.  */
+  entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK;
+  if (entry == 0)
+    {
+      entry = 0x01400000;
+      vmlinux = 1;
+    }
+  else
+    vmlinux = 0;
+
+  /* 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", 
+                   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.");
+
+  /* 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;
+    }
+  return grub_elf64_load (elf, offset_phdr);
+}
+
+void
+grub_rescue_cmd_linux (int argc, char *argv[])
+{
+  grub_elf_t elf = 0;
+  int i;
+  int size;
+  char *dest;
+
+  grub_dl_ref (my_mod);
+
+  if (argc == 0)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+      goto out;
+    }
+
+  elf = grub_elf_open (argv[0]);
+  if (! elf)
+    goto out;
+
+  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;
+    }
+
+  /* Release the previously used memory.  */
+  grub_loader_unset ();
+
+  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:
-
-  if (file)
-    grub_file_close (file);
-
-  grub_free (phdrs);
+out:
+
+  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

Reply via email to