]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
BSD on relocators
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Jan 2010 21:54:40 +0000 (22:54 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Jan 2010 21:54:40 +0000 (22:54 +0100)
include/grub/aout.h
include/grub/elfload.h
include/grub/i386/bsd.h
kern/elf.c
loader/aout.c
loader/i386/bsd.c
loader/i386/bsdXX.c

index c5650ddf8a2849abab607e4daf78d288019c912b..08aebba1829488b7fd652b9229e37a85632129ba 100644 (file)
@@ -85,7 +85,7 @@ union grub_aout_header
 int EXPORT_FUNC(grub_aout_get_type) (union grub_aout_header *header);
 
 grub_err_t EXPORT_FUNC(grub_aout_load) (grub_file_t file, int offset,
-                                        grub_addr_t load_addr, int load_size,
-                                        grub_addr_t bss_end_addr);
+                                        void *load_addr, int load_size,
+                                        grub_size_t bss_size);
 
 #endif /* ! GRUB_AOUT_HEADER */
index 6e09e0d05c2e9e9803d6f216cd4a06f1b14d6df1..1e640c1985272a95c7cf5bd6610be91beeadbab7 100644 (file)
@@ -54,5 +54,13 @@ int grub_elf_is_elf64 (grub_elf_t);
 grub_size_t grub_elf64_size (grub_elf_t);
 grub_err_t grub_elf64_load (grub_elf_t, grub_elf64_load_hook_t, grub_addr_t *,
                            grub_size_t *);
+grub_err_t
+grub_elf32_phdr_iterate (grub_elf_t elf,
+                        int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf32_Phdr *, void *),
+                        void *hook_arg);
+grub_err_t
+grub_elf64_phdr_iterate (grub_elf_t elf,
+                        int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf64_Phdr *, void *),
+                        void *hook_arg);
 
 #endif /* ! GRUB_ELFLOAD_HEADER */
index 8ffaf7d18010ac8c4e1ecb6a9eebdb0bedd04b89..b37a86c7f5e3b3327a10e8b9bb868548a62c94c8 100644 (file)
@@ -20,6 +20,7 @@
 #define GRUB_BSD_CPU_HEADER    1
 
 #include <grub/types.h>
+#include <grub/relocator.h>
 
 enum bsd_kernel_types
   {
@@ -197,7 +198,7 @@ struct grub_openbsd_bootargs
 struct grub_netbsd_bootinfo
 {
   grub_uint32_t bi_count;
-  void *bi_data[1];
+  grub_addr_t bi_data[1];
 };
 
 #define NETBSD_BTINFO_BOOTPATH         0
@@ -255,9 +256,11 @@ struct grub_netbsd_btinfo_bootdisk
 
 void grub_unix_real_boot (grub_addr_t entry, ...)
      __attribute__ ((cdecl,noreturn));
-grub_err_t grub_freebsd_load_elfmodule32 (grub_file_t file, int argc,
+grub_err_t grub_freebsd_load_elfmodule32 (struct grub_relocator *relocator,
+                                         grub_file_t file, int argc,
                                          char *argv[], grub_addr_t *kern_end);
-grub_err_t grub_freebsd_load_elfmodule_obj64 (grub_file_t file, int argc,
+grub_err_t grub_freebsd_load_elfmodule_obj64 (struct grub_relocator *relocator,
+                                             grub_file_t file, int argc,
                                              char *argv[],
                                              grub_addr_t *kern_end);
 grub_err_t grub_freebsd_load_elf_meta32 (grub_file_t file,
index 7625f6acd1ce33fb2f7e290601b113974430c181..c70071d6e73e6f047826ba4e8642fef8c8454e4b 100644 (file)
@@ -140,7 +140,7 @@ grub_elf32_load_phdrs (grub_elf_t elf)
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
+grub_err_t
 grub_elf32_phdr_iterate (grub_elf_t elf,
                         int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf32_Phdr *, void *),
                         void *hook_arg)
@@ -321,7 +321,7 @@ grub_elf64_load_phdrs (grub_elf_t elf)
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
+grub_err_t
 grub_elf64_phdr_iterate (grub_elf_t elf,
                         int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf64_Phdr *, void *),
                         void *hook_arg)
index 0254b6ae054a2f044904fdbdf7298e1e32d11c5f..611960f9274e43fa8530c3cbcce2605f4e72fc9c 100644 (file)
@@ -39,9 +39,8 @@ grub_aout_get_type (union grub_aout_header *header)
 
 grub_err_t
 grub_aout_load (grub_file_t file, int offset,
-                grub_addr_t load_addr,
-               int load_size,
-                grub_addr_t bss_end_addr)
+                void *load_addr,
+               int load_size, grub_size_t bss_size)
 {
   if ((grub_file_seek (file, offset)) == (grub_off_t) - 1)
     return grub_errno;
@@ -49,14 +48,13 @@ grub_aout_load (grub_file_t file, int offset,
   if (!load_size)
     load_size = file->size - offset;
 
-  grub_file_read (file, (void *) load_addr, load_size);
+  grub_file_read (file, load_addr, load_size);
 
   if (grub_errno)
     return grub_errno;
 
-  if (bss_end_addr)
-    grub_memset ((char *) load_addr + load_size, 0,
-                 bss_end_addr - load_addr - load_size);
+  if (bss_size)
+    grub_memset ((char *) load_addr + load_size, 0, bss_size);
 
   return GRUB_ERR_NONE;
 }
index 0785a3fc103758d8815c01f88a7f717945c75c40..9ee8a4b127727058e5db92ebb68c9526f5943ea8 100644 (file)
@@ -42,6 +42,8 @@
 #include <grub/disk.h>
 #include <grub/device.h>
 #include <grub/partition.h>
+#include <grub/relocator.h>
+#include <grub/cpu/relocator.h>
 
 #define ALIGN_DWORD(a) ALIGN_UP (a, 4)
 #define ALIGN_QWORD(a) ALIGN_UP (a, 8)
 static int kernel_type = KERNEL_TYPE_NONE;
 static grub_dl_t my_mod;
 static grub_addr_t entry, entry_hi, kern_start, kern_end;
+static void *kern_chunk_src;
 static grub_uint32_t bootflags;
 static char *mod_buf;
 static grub_uint32_t mod_buf_len, mod_buf_max, kern_end_mdofs;
 static int is_elf_kernel, is_64bit;
 static char *netbsd_root = NULL;
 static grub_uint32_t openbsd_root;
+struct grub_relocator *relocator = NULL;
 
 static const struct grub_arg_option freebsd_opts[] =
   {
@@ -442,24 +446,41 @@ static grub_err_t
 grub_freebsd_boot (void)
 {
   struct grub_freebsd_bootinfo bi;
-  char *p;
+  grub_uint8_t *p, *p0;
+  grub_addr_t p_target;
+  grub_size_t p_size = 0;
   grub_uint32_t bootdev, biosdev, unit, slice, part;
+  grub_err_t err;
 
   auto int iterate_env (struct grub_env_var *var);
   int iterate_env (struct grub_env_var *var)
   {
     if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
       {
-       grub_strcpy (p, &var->name[sizeof("kFreeBSD.") - 1]);
-       p += grub_strlen (p);
+       grub_strcpy ((char *) p, &var->name[sizeof("kFreeBSD.") - 1]);
+       p += grub_strlen ((char *) p);
        *(p++) = '=';
-       grub_strcpy (p, var->value);
-       p += grub_strlen (p) + 1;
+       grub_strcpy ((char *) p, var->value);
+       p += grub_strlen ((char *) p) + 1;
+      }
+
+    return 0;
+  }
+
+  auto int iterate_env_count (struct grub_env_var *var);
+  int iterate_env_count (struct grub_env_var *var)
+  {
+    if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
+      {
+       p_size += grub_strlen (&var->name[sizeof("kFreeBSD.") - 1]);
+       p_size++;
+       p_size += grub_strlen (var->value) + 1;
       }
 
     return 0;
   }
 
+
   grub_memset (&bi, 0, sizeof (bi));
   bi.bi_version = FREEBSD_BOOTINFO_VERSION;
   bi.bi_size = sizeof (bi);
@@ -470,35 +491,50 @@ grub_freebsd_boot (void)
 
   bi.bi_bios_dev = biosdev;
 
-  p = (char *) kern_end;
+  p_size = 0;
+  grub_env_iterate (iterate_env_count);
+  if (p_size)
+    p_size = ALIGN_PAGE (kern_end + p_size + 1) - kern_end;
+  if (is_elf_kernel)
+    p_size = ALIGN_PAGE (kern_end + p_size + mod_buf_len) - kern_end;
+
+  if (is_64bit)
+    p_size += 4096 * 4;
+
+  err = grub_relocator_alloc_chunk_addr (relocator, (void **) &p,
+                                        kern_end, p_size);
+  if (err)
+    return err;
+  kern_end += p_size;
+  p0 = p;
+  p_target = kern_end;
 
   grub_env_iterate (iterate_env);
 
-  if (p != (char *) kern_end)
+  if (p != p0)
     {
       *(p++) = 0;
 
-      bi.bi_envp = kern_end;
-      kern_end = ALIGN_PAGE ((grub_uint32_t) p);
+      bi.bi_envp = p_target;
     }
 
   if (is_elf_kernel)
     {
-      grub_addr_t md_ofs;
+      grub_uint8_t *md_ofs;
       int ofs;
 
       if (grub_freebsd_add_meta (FREEBSD_MODINFO_END, 0, 0))
        return grub_errno;
 
-      grub_memcpy ((char *) kern_end, mod_buf, mod_buf_len);
-      bi.bi_modulep = kern_end;
+      grub_memcpy (p, mod_buf, mod_buf_len);
+      bi.bi_modulep = (p - p0) + p_target;
+      md_ofs = p + kern_end_mdofs;
 
-      kern_end = ALIGN_PAGE (kern_end + mod_buf_len);
+      p = (ALIGN_PAGE ((p - p0) + p_target) - p_target) + p0;
 
       if (is_64bit)
-       kern_end += 4096 * 4;
+       p += 4096 * 4;
 
-      md_ofs = bi.bi_modulep + kern_end_mdofs;
       ofs = (is_64bit) ? 16 : 12;
       *((grub_uint32_t *) md_ofs) = kern_end;
       md_ofs -= ofs;
@@ -519,11 +555,11 @@ grub_freebsd_boot (void)
 
       struct gdt_descriptor *gdtdesc;
 
-      pagetable = (grub_uint8_t *) (kern_end - 16384);
+      pagetable = p - 16384;
       fill_bsd64_pagetable (pagetable);
 
       /* Create GDT. */
-      gdt = (grub_uint32_t *) (kern_end - 4096);
+      gdt = (grub_uint32_t *) (p - 4096);
       gdt[0] = 0;
       gdt[1] = 0;
       gdt[2] = 0;
@@ -532,12 +568,12 @@ grub_freebsd_boot (void)
       gdt[5] = 0x00008000;
 
       /* Create GDT descriptor. */
-      gdtdesc = (struct gdt_descriptor *) (kern_end - 4096 + 24);
+      gdtdesc = (struct gdt_descriptor *) (p - 4096 + 24);
       gdtdesc->limit = 24;
       gdtdesc->base = gdt;
 
       /* Prepare trampoline. */
-      trampoline = (grub_uint8_t *) (kern_end - 4096 + 24
+      trampoline = (grub_uint8_t *) (p - 4096 + 24
                                     + sizeof (struct gdt_descriptor));
       launch_trampoline = (void  __attribute__ ((cdecl, regparm (0)))
                           (*) (grub_addr_t entry_lo, ...)) trampoline;
@@ -556,8 +592,31 @@ grub_freebsd_boot (void)
                         kern_end);
     }
   else
-    grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev,
-                        0, 0, 0, &bi, bi.bi_modulep, kern_end);
+    {
+      struct grub_relocator32_state state;
+      grub_uint32_t *stack;
+      grub_addr_t stack_target;
+      err = grub_relocator_alloc_chunk_align (relocator, (void **) &stack,
+                                             &stack_target,
+                                             0x10000, 0x90000,
+                                             9 * sizeof (grub_uint32_t)
+                                             + sizeof (bi), 4);
+      if (err)
+       return err;
+      grub_memcpy (&stack[8], &bi, sizeof (bi));
+      state.eip = entry;
+      state.esp = stack_target;
+      stack[0] = entry; /* "Return" address.  */
+      stack[1] = bootflags | FREEBSD_RB_BOOTINFO;
+      stack[2] = bootdev;
+      stack[3] = 0;
+      stack[4] = 0;
+      stack[5] = 0;
+      stack[6] = stack_target + 9 * sizeof (grub_uint32_t);
+      stack[7] = bi.bi_modulep;
+      stack[8] = kern_end;
+      return grub_relocator32_boot (relocator, state);
+    }
 
   /* Not reached.  */
   return GRUB_ERR_NONE;
@@ -566,9 +625,23 @@ grub_freebsd_boot (void)
 static grub_err_t
 grub_openbsd_boot (void)
 {
-  char *buf = (char *) GRUB_BSD_TEMP_BUFFER;
+  grub_uint8_t *buf, *buf0;
+  grub_uint32_t *stack;
+  grub_addr_t buf_target, argbuf_target_start, argbuf_target_end;
+  grub_size_t buf_size;
   struct grub_openbsd_bios_mmap *pm;
   struct grub_openbsd_bootargs *pa;
+  struct grub_relocator32_state state;
+  grub_err_t err;
+
+  auto int NESTED_FUNC_ATTR count_hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
+  int NESTED_FUNC_ATTR count_hook (grub_uint64_t addr __attribute__ ((unused)),
+                                  grub_uint64_t size __attribute__ ((unused)),
+                                  grub_uint32_t type __attribute__ ((unused)))
+  {
+    buf_size += sizeof (struct grub_openbsd_bios_mmap);
+    return 1;
+  }
 
   auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
   int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
@@ -599,6 +672,21 @@ grub_openbsd_boot (void)
       return 0;
     }
 
+  buf_target = GRUB_BSD_TEMP_BUFFER;
+  buf_size = sizeof (struct grub_openbsd_bootargs) + 9 * sizeof (grub_uint32_t);
+  grub_mmap_iterate (count_hook);
+  buf_size += sizeof (struct grub_openbsd_bootargs);
+
+  err = grub_relocator_alloc_chunk_addr (relocator, (void **) &buf,
+                                        buf_target, buf_size);
+  if (err)
+    return err;
+  buf0 = buf;
+  stack = (grub_uint32_t *) buf;
+  buf = (grub_uint8_t *) (stack + 9);
+
+  argbuf_target_start = buf - buf0 + buf_target;
+
   pa = (struct grub_openbsd_bootargs *) buf;
 
   pa->ba_type = OPENBSD_BOOTARG_MMAP;
@@ -610,20 +698,29 @@ grub_openbsd_boot (void)
   pm->len = 0;
   pm->type = 0;
   pm++;
+  buf = (grub_uint8_t *) pm;
 
   pa->ba_size = (char *) pm - (char *) pa;
-  pa->ba_next = (struct grub_openbsd_bootargs *) pm;
+  pa->ba_next = (struct grub_openbsd_bootargs *) (buf - buf0 + buf_target);
   pa = pa->ba_next;
   pa->ba_type = OPENBSD_BOOTARG_END;
   pa++;
-
-  grub_unix_real_boot (entry, bootflags, openbsd_root, OPENBSD_BOOTARG_APIVER,
-                      0, (grub_uint32_t) (grub_mmap_get_upper () >> 10),
-                      (grub_uint32_t) (grub_mmap_get_lower () >> 10),
-                      (char *) pa - buf, buf);
-
-  /* Not reached.  */
-  return GRUB_ERR_NONE;
+  buf = (grub_uint8_t *) pa;
+  argbuf_target_end = buf - buf0 + buf_target;
+
+  state.eip = entry;
+  state.esp = ((grub_uint8_t *) stack - buf0) + buf_target;
+  stack[0] = entry;
+  stack[1] = bootflags;
+  stack[2] = openbsd_root;
+  stack[3] = OPENBSD_BOOTARG_APIVER;
+  stack[4] = 0;
+  stack[5] = grub_mmap_get_upper () >> 10;
+  stack[6] = grub_mmap_get_lower () >> 10;
+  stack[7] = argbuf_target_end - argbuf_target_start;
+  stack[8] = argbuf_target_start;
+
+  return grub_relocator32_boot (relocator, state);
 }
 
 static grub_err_t
@@ -633,7 +730,11 @@ grub_netbsd_boot (void)
   int count = 0;
   struct grub_netbsd_btinfo_mmap_header *mmap;
   struct grub_netbsd_btinfo_mmap_entry *pm;
-  void *curarg;
+  void *curarg, *arg0;
+  grub_addr_t arg_target, stack_target;
+  grub_uint32_t *stack;
+  grub_err_t err;
+  struct grub_relocator32_state state;
 
   auto int NESTED_FUNC_ATTR count_hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
   int NESTED_FUNC_ATTR count_hook (grub_uint64_t addr __attribute__ ((unused)),
@@ -675,14 +776,18 @@ grub_netbsd_boot (void)
 
   grub_mmap_iterate (count_hook);
 
-  if (kern_end + sizeof (struct grub_netbsd_btinfo_rootdevice)
-      + sizeof (struct grub_netbsd_bootinfo)
-      + sizeof (struct grub_netbsd_btinfo_mmap_header)
-      + count * sizeof (struct grub_netbsd_btinfo_mmap_entry)
-      > grub_os_area_addr + grub_os_area_size)
-    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+  arg_target = kern_end;
+  err = grub_relocator_alloc_chunk_addr 
+    (relocator, &curarg, arg_target,
+     sizeof (struct grub_netbsd_btinfo_rootdevice)
+     + sizeof (struct grub_netbsd_bootinfo)
+     + sizeof (struct grub_netbsd_btinfo_mmap_header)
+     + count * sizeof (struct grub_netbsd_btinfo_mmap_entry));
+  if (err)
+    return err;
 
-  curarg = mmap = (struct grub_netbsd_btinfo_mmap_header *) kern_end;
+  arg0 = curarg;
+  mmap = curarg;
   pm = (struct grub_netbsd_btinfo_mmap_entry *) (mmap + 1);
 
   grub_mmap_iterate (fill_hook);
@@ -703,22 +808,36 @@ grub_netbsd_boot (void)
 
       bootinfo = (struct grub_netbsd_bootinfo *) (rootdev + 1);
       bootinfo->bi_count = 2;
-      bootinfo->bi_data[0] = mmap;
-      bootinfo->bi_data[1] = rootdev;
+      bootinfo->bi_data[0] = ((grub_uint8_t *) mmap - (grub_uint8_t *) arg0)
+       + arg_target;
+      bootinfo->bi_data[1] = ((grub_uint8_t *) rootdev - (grub_uint8_t *) arg0)
+       + arg_target;
     }
   else
     {
       bootinfo = (struct grub_netbsd_bootinfo *) curarg;
       bootinfo->bi_count = 1;
-      bootinfo->bi_data[0] = mmap;
+      bootinfo->bi_data[0] = ((grub_uint8_t *) mmap - (grub_uint8_t *) arg0)
+       + arg_target;
     }
 
-  grub_unix_real_boot (entry, bootflags, 0, bootinfo,
-                      0, (grub_uint32_t) (grub_mmap_get_upper () >> 10),
-                      (grub_uint32_t) (grub_mmap_get_lower () >> 10));
-
-  /* Not reached.  */
-  return GRUB_ERR_NONE;
+  err = grub_relocator_alloc_chunk_align (relocator, (void **) &stack,
+                                         &stack_target, 0x10000, 0x90000,
+                                         7 * sizeof (grub_uint32_t), 4);
+  if (err)
+    return err;
+
+  state.eip = entry;
+  state.esp = stack_target;
+  stack[0] = entry;
+  stack[1] = bootflags;
+  stack[2] = 0;
+  stack[3] = ((grub_uint8_t *) bootinfo - (grub_uint8_t *) arg0) + arg_target;
+  stack[4] = 0;
+  stack[5] = grub_mmap_get_upper () >> 10;
+  stack[6] = grub_mmap_get_lower () >> 10;
+
+  return grub_relocator32_boot (relocator, state);
 }
 
 static grub_err_t
@@ -737,15 +856,20 @@ grub_bsd_unload (void)
   grub_free (netbsd_root);
   netbsd_root = NULL;
 
+  grub_relocator_unload (relocator);
+  relocator = NULL;
+
   return GRUB_ERR_NONE;
 }
 
 static grub_err_t
 grub_bsd_load_aout (grub_file_t file)
 {
-  grub_addr_t load_addr, bss_end_addr;
+  grub_addr_t load_addr, load_end;
   int ofs, align_page;
   union grub_aout_header ah;
+  grub_err_t err;
+  grub_size_t bss_size;
 
   if ((grub_file_seek (file, 0)) == (grub_off_t) - 1)
     return grub_errno;
@@ -775,7 +899,7 @@ grub_bsd_load_aout (grub_file_t file)
     return grub_error (GRUB_ERR_BAD_OS, "load address below 1M");
 
   kern_start = load_addr;
-  kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data;
+  load_end = kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data;
   if (align_page)
     kern_end = ALIGN_PAGE (kern_end);
 
@@ -785,13 +909,44 @@ grub_bsd_load_aout (grub_file_t file)
       if (align_page)
        kern_end = ALIGN_PAGE (kern_end);
 
-      bss_end_addr = kern_end;
+      bss_size = kern_end - load_end;
     }
   else
-    bss_end_addr = 0;
+    bss_size = 0;
+
+  relocator = grub_relocator_new ();
+  if (!relocator)
+    return grub_errno;
+
+  err = grub_relocator_alloc_chunk_addr (relocator, &kern_chunk_src,
+                                        kern_start, kern_end - kern_start);
+  if (err)
+    return err;
 
-  return grub_aout_load (file, ofs, load_addr,
-                        ah.aout32.a_text + ah.aout32.a_data, bss_end_addr);
+  return grub_aout_load (file, ofs, kern_chunk_src,
+                        ah.aout32.a_text + ah.aout32.a_data,
+                        bss_size);
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)),
+                         Elf32_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+  Elf32_Addr paddr;
+
+  if (phdr->p_type != PT_LOAD
+      && phdr->p_type != PT_DYNAMIC)
+      return 1;
+
+  paddr = phdr->p_paddr & 0xFFFFFF;
+
+  if (paddr < kern_start)
+    kern_start = paddr;
+
+  if (paddr + phdr->p_memsz > kern_end)
+    kern_end = paddr + phdr->p_memsz;
+
+  return 1;
 }
 
 static grub_err_t
@@ -810,20 +965,30 @@ grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load)
   phdr->p_paddr &= 0xFFFFFF;
   paddr = phdr->p_paddr;
 
-  if ((paddr < grub_os_area_addr)
-      || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size))
-    return grub_error (GRUB_ERR_OUT_OF_RANGE, "address 0x%x is out of range",
-                      paddr);
+  *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
+
+  return GRUB_ERR_NONE;
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)),
+                         Elf64_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+  Elf64_Addr paddr;
+
+  if (phdr->p_type != PT_LOAD
+      && phdr->p_type != PT_DYNAMIC)
+    return 1;
+
+  paddr = phdr->p_paddr & 0xffffff;
 
-  if ((!kern_start) || (paddr < kern_start))
+  if (paddr < kern_start)
     kern_start = paddr;
 
   if (paddr + phdr->p_memsz > kern_end)
     kern_end = paddr + phdr->p_memsz;
 
-  *addr = paddr;
-
-  return GRUB_ERR_NONE;
+  return 1;
 }
 
 static grub_err_t
@@ -841,18 +1006,7 @@ grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load)
   *do_load = 1;
   paddr = phdr->p_paddr & 0xffffff;
 
-  if ((paddr < grub_os_area_addr)
-      || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size))
-    return grub_error (GRUB_ERR_OUT_OF_RANGE, "address 0x%x is out of range",
-                      paddr);
-
-  if ((!kern_start) || (paddr < kern_start))
-    kern_start = paddr;
-
-  if (paddr + phdr->p_memsz > kern_end)
-    kern_end = paddr + phdr->p_memsz;
-
-  *addr = paddr;
+  *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
 
   return GRUB_ERR_NONE;
 }
@@ -860,11 +1014,22 @@ grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load)
 static grub_err_t
 grub_bsd_load_elf (grub_elf_t elf)
 {
-  kern_start = kern_end = 0;
+  grub_err_t err;
+
+  kern_end = 0;
+  kern_start = ~0;
 
   if (grub_elf_is_elf32 (elf))
     {
       entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF;
+      err = grub_elf32_phdr_iterate (elf, grub_bsd_elf32_size_hook, NULL);
+      if (err)
+       return err;
+      err = grub_relocator_alloc_chunk_addr (relocator, &kern_chunk_src,
+                                            kern_start, kern_end - kern_start);
+      if (err)
+       return err;
+
       return grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0);
     }
   else if (grub_elf_is_elf64 (elf))
@@ -885,6 +1050,15 @@ grub_bsd_load_elf (grub_elf_t elf)
          entry = elf->ehdr.ehdr64.e_entry & 0x0fffffff;
          entry_hi = 0;
        }
+
+      err = grub_elf64_phdr_iterate (elf, grub_bsd_elf64_size_hook, NULL);
+      if (err)
+       return err;
+      err = grub_relocator_alloc_chunk_addr (relocator, &kern_chunk_src,
+                                            kern_start, kern_end - kern_start);
+      if (err)
+       return err;
+
       return grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0);
     }
   else
@@ -911,6 +1085,8 @@ grub_bsd_load (int argc, char *argv[])
   if (!file)
     goto fail;
 
+  relocator = grub_relocator_new ();
+
   elf = grub_elf_file (file);
   if (elf)
     {
@@ -1059,7 +1235,7 @@ grub_cmd_netbsd (grub_extcmd_t cmd, int argc, char *argv[])
 
   if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
     {
-      grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 1);
+      grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 0);
       if (cmd->state[NETBSD_ROOT_ARG].set)
        netbsd_root = grub_strdup (cmd->state[NETBSD_ROOT_ARG].arg);
     }
@@ -1164,10 +1340,11 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
                         int argc, char *argv[])
 {
   grub_file_t file = 0;
-  grub_err_t err;
   int modargc;
   char **modargv;
   char *type;
+  grub_err_t err;
+  void *src;
 
   if (kernel_type == KERNEL_TYPE_NONE)
     return grub_error (GRUB_ERR_BAD_ARGUMENT,
@@ -1192,13 +1369,12 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
   if ((!file) || (!file->size))
     goto fail;
 
-  if (kern_end + file->size > grub_os_area_addr + grub_os_area_size)
-    {
-      grub_error (GRUB_ERR_OUT_OF_RANGE, "not enough memory for the module");
-      goto fail;
-    }
+  err = grub_relocator_alloc_chunk_addr (relocator, &src, kern_end, 
+                                        file->size);
+  if (err)
+    goto fail;
 
-  grub_file_read (file, (void *) kern_end, file->size);
+  grub_file_read (file, src, file->size);
   if (grub_errno)
     goto fail;
 
@@ -1264,9 +1440,11 @@ grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)),
     }
 
   if (is_64bit)
-    err = grub_freebsd_load_elfmodule_obj64 (file, argc, argv, &kern_end);
+    err = grub_freebsd_load_elfmodule_obj64 (relocator, file,
+                                            argc, argv, &kern_end);
   else
-    err = grub_freebsd_load_elfmodule32 (file, argc, argv, &kern_end);
+    err = grub_freebsd_load_elfmodule32 (relocator, file,
+                                        argc, argv, &kern_end);
   grub_file_close (file);
 
   return err;
index b4d5748212bb0655646c31c1db36a1fe147ad8eb..2622287f96e45350008ab0b0053e602b906f2304 100644 (file)
@@ -4,19 +4,16 @@
 #include <grub/elf.h>
 #include <grub/misc.h>
 #include <grub/i386/loader.h>
+#include <grub/cpu/relocator.h>
 
 #define ALIGN_PAGE(a)  ALIGN_UP (a, 4096)
 
 static inline grub_err_t
 load (grub_file_t file, void *where, grub_off_t off, grub_size_t size)
 {
-  if (PTR_TO_UINT32 (where) + size > grub_os_area_addr + grub_os_area_size)
-    return grub_error (GRUB_ERR_OUT_OF_RANGE,
-                      "not enough memory for the module");
   if (grub_file_seek (file, off) == (grub_off_t) -1)
     return grub_errno;
-  if (grub_file_read (file, where, size)
-      != (grub_ssize_t) size)
+  if (grub_file_read (file, where, size) != (grub_ssize_t) size)
     {
       if (grub_errno)
        return grub_errno;
@@ -75,7 +72,8 @@ read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr)
    platforms. So I keep both versions.  */
 #if OBJSYM
 grub_err_t
-SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
+SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator,
+                                         grub_file_t file, int argc,
                                          char *argv[], grub_addr_t *kern_end)
 {
   Elf_Ehdr e;
@@ -83,6 +81,8 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
   char *shdr;
   grub_addr_t curload, module;
   grub_err_t err;
+  grub_size_t chunk_size = 0;
+  void *chunk_src;
 
   err = read_headers (file, &e, &shdr);
   if (err)
@@ -90,6 +90,25 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
 
   curload = module = ALIGN_PAGE (*kern_end);
 
+  for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+                                               + e.e_shnum * e.e_shentsize);
+       s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+    {
+      if (s->sh_size == 0)
+       continue;
+
+      if (s->sh_addralign)
+       chunk_size = ALIGN_UP (chunk_size + *kern_end, s->sh_addralign)
+         - *kern_end;
+
+      chunk_size += s->sh_size;
+    }
+
+  err = grub_relocator_alloc_chunk_addr (relocator, &chunk_src,
+                                        module, chunk_size);
+  if (err)
+    return err;
+
   for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
                                                + e.e_shnum * e.e_shentsize);
        s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
@@ -109,15 +128,14 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
        {
        default:
        case SHT_PROGBITS:
-         err = load (file, UINT_TO_PTR (curload), s->sh_offset, s->sh_size);
+         err = load (file, (grub_uint8_t *) chunk_src + curload - *kern_end,
+                     s->sh_offset, s->sh_size);
          if (err)
            return err;
          break;
        case SHT_NOBITS:
-         if (curload + s->sh_size > grub_os_area_addr + grub_os_area_size)
-           return grub_error (GRUB_ERR_OUT_OF_RANGE,
-                              "not enough memory for the module");
-         grub_memset (UINT_TO_PTR (curload), 0, s->sh_size);
+         grub_memset ((grub_uint8_t *) chunk_src + curload - *kern_end, 0,
+                      s->sh_size);
          break;
        }
       curload += s->sh_size;
@@ -143,7 +161,8 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
 #else
 
 grub_err_t
-SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
+SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator,
+                                     grub_file_t file, int argc, char *argv[],
                                      grub_addr_t *kern_end)
 {
   Elf_Ehdr e;
@@ -151,6 +170,8 @@ SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
   char *shdr;
   grub_addr_t curload, module;
   grub_err_t err;
+  grub_size_t chunk_size = 0;
+  void *chunk_src;
 
   err = read_headers (file, &e, &shdr);
   if (err)
@@ -158,6 +179,24 @@ SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
 
   curload = module = ALIGN_PAGE (*kern_end);
 
+  for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+                                               + e.e_shnum * e.e_shentsize);
+       s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+    {
+      if (s->sh_size == 0)
+       continue;
+
+      if (! (s->sh_flags & SHF_ALLOC))
+       continue;
+      if (chunk_size < s->sh_addr + s->sh_size)
+       chunk_size = s->sh_addr + s->sh_size;
+    }
+
+  err = grub_relocator_alloc_chunk_addr (relocator, &chunk_src,
+                                        module, chunk_size);
+  if (err)
+    return err;
+
   for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
                                                + e.e_shnum * e.e_shentsize);
        s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
@@ -176,17 +215,15 @@ SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
        {
        default:
        case SHT_PROGBITS:
-         err = load (file, UINT_TO_PTR (module + s->sh_addr),
+         err = load (file, (grub_uint8_t *) chunk_src + module
+                     + s->sh_addr - *kern_end,
                      s->sh_offset, s->sh_size);
          if (err)
            return err;
          break;
        case SHT_NOBITS:
-         if (module + s->sh_addr + s->sh_size
-             > grub_os_area_addr + grub_os_area_size)
-           return grub_error (GRUB_ERR_OUT_OF_RANGE,
-                              "not enough memory for the module");
-         grub_memset (UINT_TO_PTR (module + s->sh_addr), 0, s->sh_size);
+         grub_memset ((grub_uint8_t *) chunk_src + module
+                     + s->sh_addr - *kern_end, 0, s->sh_size);
          break;
        }
       if (curload < module + s->sh_addr + s->sh_size)