[2167] 2009-05-03 Bean <[email protected]> Vladimir Serbinenko <phcoder @gmail.com>

Vladimir Serbinenko <[email protected]>
Newsgroups gmane.comp.boot-loaders.grub.cvs
Message-ID <[email protected]>
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  <[email protected]> Vladimir Serbinenko <[email protected]>

	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  <[email protected]> Vladimir Serbinenko <[email protected]>
+
+	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  <[email protected]>
 
 	* 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");
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.