commit-grub
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[2167] 2009-05-03 Bean <address@hidden> Vladimir Serbinenko <phcoder @gm


From: Vladimir Serbinenko
Subject: [2167] 2009-05-03 Bean <address@hidden> Vladimir Serbinenko <phcoder @gmail.com>
Date: Sun, 03 May 2009 09:03:32 +0000

Revision: 2167
          http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=2167
Author:   phcoder
Date:     2009-05-03 09:03:31 +0000 (Sun, 03 May 2009)
Log Message:
-----------
2009-05-03  Bean  <address@hidden> Vladimir Serbinenko <address@hidden>

        FreeBSD 64-bit support

        * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd_helper.S 
        and loader/i386/bsd_trampoline.S
        (bsd_mod_ASFLAGS): new variable
        * include/grub/i386/bsd.h (FREEBSD_MODINFOMD_SMAP): new definition
        (FREEBSD_MODTYPE_KERNEL64): likewise
        (grub_bsd64_trampoline_start): likewise
        (grub_bsd64_trampoline_end): likewise
        (grub_bsd64_trampoline_selfjump): likewise
        (grub_bsd64_trampoline_gdt): likewise
        * include/grub/i386/loader.h (grub_unix_real_boot): moved from here ...
        * include/grub/i386/bsd.h (grub_unix_real_boot): ... moved here
        * kern/i386/loader.S (grub_unix_real_boot): moved from here ...
        * loader/i386/bsd_helper.S (grub_unix_real_boot): moved here
        * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type 
        of "attrib" member
        * loader/i386/bsd_pagetable.c: new file
        * loader/i386/bsd_trampoline.S: likewise
        * loader/i386/bsd.c (ALIGN_QWORD): new macro
        (ALIGN_VAR): likewise
        (entry_hi): new variable
        (kern_end_mdofs): likewise
        (is_64bit): likewise
        (grub_freebsd_add_meta): use ALIGN_VAR
        (grub_e820_mmap): new declaration
        (grub_freebsd_add_mmap): new function
        (grub_freebsd_add_meta_module): support 64 bit kernels
        (grub_freebsd_list_modules): use ALIGN_VAR
        (gdt_descriptor): new declaration
        (grub_freebsd_boot): support 64 bit kernels
        (grub_bsd_elf64_hook): new function
        (grub_bsd_load_elf): support elf64

Modified Paths:
--------------
    trunk/grub2/ChangeLog
    trunk/grub2/conf/i386-pc.rmk
    trunk/grub2/include/grub/i386/bsd.h
    trunk/grub2/include/grub/i386/loader.h
    trunk/grub2/kern/i386/loader.S
    trunk/grub2/loader/i386/bsd.c

Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog       2009-05-03 06:50:20 UTC (rev 2166)
+++ trunk/grub2/ChangeLog       2009-05-03 09:03:31 UTC (rev 2167)
@@ -1,3 +1,39 @@
+2009-05-03  Bean  <address@hidden> Vladimir Serbinenko <address@hidden>
+
+       FreeBSD 64-bit support
+
+       * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd_helper.S 
+       and loader/i386/bsd_trampoline.S
+       (bsd_mod_ASFLAGS): new variable
+       * include/grub/i386/bsd.h (FREEBSD_MODINFOMD_SMAP): new definition
+       (FREEBSD_MODTYPE_KERNEL64): likewise
+       (grub_bsd64_trampoline_start): likewise
+       (grub_bsd64_trampoline_end): likewise
+       (grub_bsd64_trampoline_selfjump): likewise
+       (grub_bsd64_trampoline_gdt): likewise
+       * include/grub/i386/loader.h (grub_unix_real_boot): moved from here ...
+       * include/grub/i386/bsd.h (grub_unix_real_boot): ... moved here
+       * kern/i386/loader.S (grub_unix_real_boot): moved from here ...
+       * loader/i386/bsd_helper.S (grub_unix_real_boot): moved here
+       * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type 
+       of "attrib" member
+       * loader/i386/bsd_pagetable.c: new file
+       * loader/i386/bsd_trampoline.S: likewise
+       * loader/i386/bsd.c (ALIGN_QWORD): new macro
+       (ALIGN_VAR): likewise
+       (entry_hi): new variable
+       (kern_end_mdofs): likewise
+       (is_64bit): likewise
+       (grub_freebsd_add_meta): use ALIGN_VAR
+       (grub_e820_mmap): new declaration
+       (grub_freebsd_add_mmap): new function
+       (grub_freebsd_add_meta_module): support 64 bit kernels
+       (grub_freebsd_list_modules): use ALIGN_VAR
+       (gdt_descriptor): new declaration
+       (grub_freebsd_boot): support 64 bit kernels
+       (grub_bsd_elf64_hook): new function
+       (grub_bsd_load_elf): support elf64
+
 2009-05-03  Bean  <address@hidden>
 
        * script/sh/execute.c (grub_script_execute_cmdif): Reset grub_errno

Modified: trunk/grub2/conf/i386-pc.rmk
===================================================================
--- trunk/grub2/conf/i386-pc.rmk        2009-05-03 06:50:20 UTC (rev 2166)
+++ trunk/grub2/conf/i386-pc.rmk        2009-05-03 09:03:31 UTC (rev 2167)
@@ -313,9 +313,10 @@
 aout_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For bsd.mod
-bsd_mod_SOURCES = loader/i386/bsd.c
+bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd_helper.S 
loader/i386/bsd_trampoline.S
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
+bsd_mod_ASFLAGS = $(COMMON_ASFLAGS)
 
 # For usb.mod
 usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c

Modified: trunk/grub2/include/grub/i386/bsd.h
===================================================================
--- trunk/grub2/include/grub/i386/bsd.h 2009-05-03 06:50:20 UTC (rev 2166)
+++ trunk/grub2/include/grub/i386/bsd.h 2009-05-03 09:03:31 UTC (rev 2167)
@@ -80,9 +80,12 @@
 #define FREEBSD_MODINFOMD_SHDR         0x0009  /* section header table */
 #define FREEBSD_MODINFOMD_NOCOPY       0x8000  /* don't copy this metadata to 
the kernel */
 
+#define FREEBSD_MODINFOMD_SMAP         0x1001
+
 #define FREEBSD_MODINFOMD_DEPLIST      (0x4001 | FREEBSD_MODINFOMD_NOCOPY)  /* 
depends on */
 
 #define FREEBSD_MODTYPE_KERNEL         "elf kernel"
+#define FREEBSD_MODTYPE_KERNEL64       "elf64 kernel"
 #define FREEBSD_MODTYPE_MODULE         "elf module"
 #define FREEBSD_MODTYPE_RAW            "raw"
 
@@ -224,4 +227,11 @@
   int partition;
 };
 
+void grub_unix_real_boot (grub_addr_t entry, ...)
+     __attribute__ ((cdecl,noreturn));
+
+extern grub_uint8_t grub_bsd64_trampoline_start, grub_bsd64_trampoline_end;
+extern grub_uint32_t grub_bsd64_trampoline_selfjump;
+extern grub_uint32_t grub_bsd64_trampoline_gdt;
+
 #endif /* ! GRUB_BSD_CPU_HEADER */

Modified: trunk/grub2/include/grub/i386/loader.h
===================================================================
--- trunk/grub2/include/grub/i386/loader.h      2009-05-03 06:50:20 UTC (rev 
2166)
+++ trunk/grub2/include/grub/i386/loader.h      2009-05-03 09:03:31 UTC (rev 
2167)
@@ -32,7 +32,4 @@
 
 grub_err_t EXPORT_FUNC(grub_linux16_boot) (void);
 
-void EXPORT_FUNC(grub_unix_real_boot) (grub_addr_t entry, ...)
-     __attribute__ ((cdecl,noreturn));
-
 #endif /* ! GRUB_LOADER_CPU_HEADER */

Modified: trunk/grub2/kern/i386/loader.S
===================================================================
--- trunk/grub2/kern/i386/loader.S      2009-05-03 06:50:20 UTC (rev 2166)
+++ trunk/grub2/kern/i386/loader.S      2009-05-03 09:03:31 UTC (rev 2167)
@@ -118,25 +118,3 @@
        .word   0
        .code32
 
-/*
- * Use cdecl calling convention for *BSD kernels.
- */
-
-FUNCTION(grub_unix_real_boot)
-
-        call    EXT_C(grub_dl_unload_all)
-
-       /* Interrupts should be disabled.  */
-        cli
-
-       /* Discard `grub_unix_real_boot' return address.  */
-        popl    %eax
-
-        /* Fetch `entry' address ...  */
-        popl   %eax
-
-        /*
-         * ... and put our return address in its place. The kernel will
-         * ignore it, but it expects %esp to point to it.
-         */
-        call   *%eax

Modified: trunk/grub2/loader/i386/bsd.c
===================================================================
--- trunk/grub2/loader/i386/bsd.c       2009-05-03 06:50:20 UTC (rev 2166)
+++ trunk/grub2/loader/i386/bsd.c       2009-05-03 09:03:31 UTC (rev 2167)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *  Copyright (C) 2008, 2009  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 #include <grub/cpu/bsd.h>
 #include <grub/machine/init.h>
 #include <grub/machine/memory.h>
+#include <grub/memory.h>
+#include <grub/machine/machine.h>
 #include <grub/file.h>
 #include <grub/err.h>
 #include <grub/dl.h>
@@ -33,17 +35,19 @@
 #include <grub/command.h>
 
 #define ALIGN_DWORD(a) ALIGN_UP (a, 4)
+#define ALIGN_QWORD(a) ALIGN_UP (a, 8)
+#define ALIGN_VAR(a)   ((is_64bit) ? (ALIGN_QWORD(a)) : (ALIGN_DWORD(a)))
 #define ALIGN_PAGE(a)  ALIGN_UP (a, 4096)
 
 #define MOD_BUF_ALLOC_UNIT     4096
 
 static int kernel_type;
 static grub_dl_t my_mod;
-static grub_addr_t entry, kern_start, kern_end;
+static grub_addr_t entry, entry_hi, kern_start, kern_end;
 static grub_uint32_t bootflags;
 static char *mod_buf;
-static grub_uint32_t mod_buf_len, mod_buf_max;
-static int is_elf_kernel;
+static grub_uint32_t mod_buf_len, mod_buf_max, kern_end_mdofs;
+static int is_elf_kernel, is_64bit;
 
 static const char freebsd_opts[] = "DhaCcdgmnpqrsv";
 static const grub_uint32_t freebsd_flags[] =
@@ -135,12 +139,122 @@
   if (len)
     grub_memcpy (mod_buf + mod_buf_len, data, len);
 
-  mod_buf_len = ALIGN_DWORD (mod_buf_len + len);
+  mod_buf_len = ALIGN_VAR (mod_buf_len + len);
 
   return GRUB_ERR_NONE;
 }
 
+struct grub_e820_mmap
+{
+  grub_uint64_t addr;
+  grub_uint64_t size;
+  grub_uint32_t type;
+} __attribute__((packed));
+#define GRUB_E820_RAM        1
+#define GRUB_E820_RESERVED   2
+#define GRUB_E820_ACPI       3
+#define GRUB_E820_NVS        4
+#define GRUB_E820_EXEC_CODE  5
+
 static grub_err_t
+grub_freebsd_add_mmap (void)
+{
+  grub_size_t len = 0;
+  struct grub_e820_mmap *mmap_buf = 0;
+  struct grub_e820_mmap *mmap = 0;
+  int isfirstrun = 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)
+    {
+      /* FreeBSD assumes that first 64KiB are available. 
+        Not always true but try to prevent panic somehow. */
+      if (isfirstrun && addr != 0)
+       {
+         if (mmap)
+           {
+             mmap->addr = 0;
+             mmap->size = (addr < 0x10000) ? addr : 0x10000;
+             mmap->type = GRUB_E820_RAM;
+             mmap++;
+           }
+         else
+           len += sizeof (struct grub_e820_mmap);        
+       }
+      isfirstrun = 0;
+      if (mmap)
+       {
+         mmap->addr = addr;
+         mmap->size = size;
+         switch (type)
+           {
+           case GRUB_MACHINE_MEMORY_AVAILABLE:
+             mmap->type = GRUB_E820_RAM;
+             break;
+
+#ifdef GRUB_MACHINE_MEMORY_ACPI
+           case GRUB_MACHINE_MEMORY_ACPI:
+             mmap->type = GRUB_E820_ACPI;
+             break;
+#endif
+
+#ifdef GRUB_MACHINE_MEMORY_NVS
+           case GRUB_MACHINE_MEMORY_NVS:
+             mmap->type = GRUB_E820_NVS;
+             break;
+#endif
+
+           default:
+#ifdef GRUB_MACHINE_MEMORY_CODE
+           case GRUB_MACHINE_MEMORY_CODE:
+#endif
+#ifdef GRUB_MACHINE_MEMORY_RESERVED
+           case GRUB_MACHINE_MEMORY_RESERVED:
+#endif
+             mmap->type = GRUB_E820_RESERVED;
+             break;
+           }
+
+         /* Merge regions if possible. */
+         if (mmap != mmap_buf && mmap->type == mmap[-1].type &&
+             mmap->addr == mmap[-1].addr + mmap[-1].size)
+           mmap[-1].size += mmap->size;
+         else
+           mmap++;
+       }
+      else
+       len += sizeof (struct grub_e820_mmap);
+
+      return 0;
+    }
+
+  grub_mmap_iterate (hook);
+  mmap_buf = mmap = grub_malloc (len);
+  if (! mmap)
+    return grub_errno;
+
+  isfirstrun = 1;
+  grub_mmap_iterate (hook);
+
+  len = (mmap - mmap_buf) * sizeof (struct grub_e820_mmap);
+  int i;
+  for (i = 0; i < mmap - mmap_buf; i++)
+    grub_dprintf ("bsd", "smap %d, %d:%llx - %llx\n", i,
+                 mmap_buf[i].type,
+                 (unsigned long long) mmap_buf[i].addr, 
+                 (unsigned long long) mmap_buf[i].size);
+       
+  grub_dprintf ("bsd", "%d entries in smap\n", mmap - mmap_buf);
+  grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+                        FREEBSD_MODINFOMD_SMAP, mmap_buf, len);
+
+  grub_free (mmap_buf);
+
+  return grub_errno;
+}
+
+static grub_err_t
 grub_freebsd_add_meta_module (int is_kern, int argc, char **argv,
                              grub_addr_t addr, grub_uint32_t size)
 {
@@ -166,13 +280,31 @@
       argv++;
     }
   else
-    type = (is_kern) ? FREEBSD_MODTYPE_KERNEL : FREEBSD_MODTYPE_RAW;
+    type = ((is_kern) ?
+           ((is_64bit) ? FREEBSD_MODTYPE_KERNEL64 : FREEBSD_MODTYPE_KERNEL)
+           : FREEBSD_MODTYPE_RAW);
 
-  if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type,
+  if (is_64bit)
+    {
+      grub_uint64_t addr64 = addr, size64 = size; 
+      if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type,
                              grub_strlen (type) + 1)) ||
-      (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, sizeof (addr))) ||
-      (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size, sizeof (size))))
-    return grub_errno;
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, 
+                                 sizeof (addr64))) ||
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, 
+                                 sizeof (size64))))
+       return grub_errno;
+    }
+  else
+    {
+      if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type,
+                                 grub_strlen (type) + 1)) ||
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, 
+                                 sizeof (addr))) ||
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size, 
+                                 sizeof (size))))
+       return grub_errno;
+    }
 
   if (argc)
     {
@@ -202,6 +334,23 @@
        }
     }
 
+  if (is_kern)
+    {
+      int len = (is_64bit) ? 8 : 4;
+      grub_uint64_t data = 0;
+
+      if ((grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+                                 FREEBSD_MODINFOMD_HOWTO, &data, 4)) ||
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+                                 FREEBSD_MODINFOMD_ENVP, &data, len)) ||
+         (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+                                 FREEBSD_MODINFOMD_KERNEND, &data, len)))
+       return grub_errno;
+      kern_end_mdofs = mod_buf_len - len;
+
+      return grub_freebsd_add_mmap ();
+    }
+
   return GRUB_ERR_NONE;
 }
 
@@ -241,10 +390,19 @@
          }
        }
 
-      pos = ALIGN_DWORD (pos + size);
+      pos = ALIGN_VAR (pos + size);
     }
 }
 
+/* This function would be here but it's under different licence. */
+#include "bsd_pagetable.c"
+
+struct gdt_descriptor
+{
+  grub_uint16_t limit;
+  void *base;
+} __attribute__ ((packed));
+
 static grub_err_t
 grub_freebsd_boot (void)
 {
@@ -291,6 +449,9 @@
 
   if (is_elf_kernel)
     {
+      grub_addr_t md_ofs;
+      int ofs;
+
       if (grub_freebsd_add_meta (FREEBSD_MODINFO_END, 0, 0))
        return grub_errno;
 
@@ -298,13 +459,71 @@
       bi.bi_modulep = kern_end;
 
       kern_end = ALIGN_PAGE (kern_end + mod_buf_len);
+
+      if (is_64bit)
+       kern_end += 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;
+      *((grub_uint32_t *) md_ofs) = bi.bi_envp;
+      md_ofs -= ofs;
+      *((grub_uint32_t *) md_ofs) = bootflags;
     }
 
   bi.bi_kernend = kern_end;
 
-  grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev,
-                      0, 0, 0, &bi, bi.bi_modulep, kern_end);
+  if (is_64bit)
+    {
+      grub_uint32_t *gdt;
+      grub_uint8_t *trampoline;
+      void (*launch_trampoline) (grub_addr_t entry, ...)
+       __attribute__ ((cdecl, regparm (0)));
+      grub_uint8_t *pagetable;
 
+      struct gdt_descriptor *gdtdesc;
+
+      pagetable = (grub_uint8_t *) (kern_end - 16384);
+      fill_bsd64_pagetable (pagetable);
+
+      /* Create GDT. */
+      gdt = (grub_uint32_t *) (kern_end - 4096);
+      gdt[0] = 0;
+      gdt[1] = 0;
+      gdt[2] = 0;
+      gdt[3] = 0x00209800;
+      gdt[4] = 0;
+      gdt[5] = 0x00008000;
+
+      /* Create GDT descriptor. */
+      gdtdesc = (struct gdt_descriptor *) (kern_end - 4096 + 24);
+      gdtdesc->limit = 24;
+      gdtdesc->base = gdt;
+
+      /* Prepare trampoline. */
+      trampoline = (grub_uint8_t *) (kern_end - 4096 + 24 
+                                    + sizeof (struct gdt_descriptor));
+      launch_trampoline = (void  __attribute__ ((cdecl, regparm (0))) 
+                          (*) (grub_addr_t entry, ...)) trampoline;
+      grub_bsd64_trampoline_gdt = (grub_uint32_t) gdtdesc;
+      grub_bsd64_trampoline_selfjump 
+       = (grub_uint32_t) (trampoline + 6
+                          + ((grub_uint8_t *) &grub_bsd64_trampoline_selfjump 
+                             - &grub_bsd64_trampoline_start));
+
+      /* Copy trampoline. */
+      grub_memcpy (trampoline, &grub_bsd64_trampoline_start, 
+                  &grub_bsd64_trampoline_end - &grub_bsd64_trampoline_start);
+
+      /* Launch trampoline. */
+      launch_trampoline (entry, entry_hi, pagetable, bi.bi_modulep, 
+                        kern_end);
+    }
+  else
+    grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev,
+                        0, 0, 0, &bi, bi.bi_modulep, kern_end);
+
   /* Not reached.  */
   return GRUB_ERR_NONE;
 }
@@ -486,6 +705,29 @@
 }
 
 static grub_err_t
+grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr)
+{
+  Elf64_Addr paddr;
+
+  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;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
 grub_bsd_load_elf (grub_elf_t elf)
 {
   kern_start = kern_end = 0;
@@ -495,6 +737,13 @@
       entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF;
       return grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0);
     }
+  else if (grub_elf_is_elf64 (elf))
+    {
+      is_64bit = 1;
+      entry = elf->ehdr.ehdr64.e_entry & 0xffffffff;
+      entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff;
+      return grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0);
+    }
   else
     return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
 }





reply via email to

[Prev in Thread] Current Thread [Next in Thread]