Hello. Unlike some other types of modules (mfsroot, splash, ..)
FreeBSD relies on ELF modules being partially parsed by booter. So I
added new function freebsd_module_elf specifically for loading elf
modules. I also factorised freebsd_module_add_meta to do only parts
common to all types of modules and moved module-specific parts to
corresponding functions. I tested it only with 64-bit kernel. It works
for 64-bit kernels but doesn't work for 32-bit ones. Dumps show that
32-bit loader uses quite different format. I'll investigate the issue

-- 
Regards
Vladimir 'phcoder' Serbinenko
diff --git a/ChangeLog b/ChangeLog
index a16d539..31c8c10 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2009-06-12  Vladimir Serbinenko  <phco...@gmail.com>
+
+	Load BSD ELF modules
+
+	* conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd32.c
+	and loader/i386/bsd64.c
+	* include/grub/i386/bsd.h (FREEBSD_MODTYPE_MODULE): remove
+	(FREEBSD_MODTYPE_ELF_MODULE): new definition
+	(grub_freebsd_load_elfmodule32): new declaration
+	(grub_freebsd_load_elfmodule64): likewise
+	(grub_freebsd_load_elf_meta32): likewise
+	(grub_freebsd_load_elf_meta64): likewise
+	(grub_freebsd_add_meta): likewise
+	(grub_freebsd_add_meta_module): likewise
+	* loader/i386/bsd.c (grub_freebsd_add_meta): make global
+	(grub_freebsd_add_meta_module): likewise and move module-specific
+	parts to grub_cmd_freebsd and grub_cmd_freebsd_module
+	(grub_cmd_freebsd): add elf-kernel specific parts
+	based on grub_freebsd_add_meta_module
+	(grub_cmd_freebsd_module): add type parsing moved from
+	grub_freebsd_add_meta_module
+	(grub_cmd_freebsd_module_elf): new function
+	(cmd_freebsd_module_elf): new variable
+	(GRUB_MOD_INIT): register freebsd_module_elf
+	* loader/i386/bsd32.c: new file
+	* loader/i386/bsd64.c: likewise
+	* loader/i386/bsdXX.c: likewise
+
 2009-06-11  Pavel Roskin  <pro...@gnu.org>
 
 	* Makefile.in (uninstall): Uninstall manuals.
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index abb6fd5..47427db 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -328,7 +328,7 @@ aout_mod_CFLAGS = $(COMMON_CFLAGS)
 aout_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For bsd.mod
-bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S
+bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.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)
diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h
index 321b31f..147bb6f 100644
--- a/include/grub/i386/bsd.h
+++ b/include/grub/i386/bsd.h
@@ -86,7 +86,7 @@
 
 #define FREEBSD_MODTYPE_KERNEL		"elf kernel"
 #define FREEBSD_MODTYPE_KERNEL64	"elf64 kernel"
-#define FREEBSD_MODTYPE_MODULE		"elf module"
+#define FREEBSD_MODTYPE_ELF_MODULE	"elf obj module"
 #define FREEBSD_MODTYPE_RAW		"raw"
 
 struct grub_freebsd_bootinfo
@@ -229,6 +229,20 @@ 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,
+					  char *argv[], grub_addr_t *kern_end);
+grub_err_t grub_freebsd_load_elfmodule64 (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,
+					 grub_addr_t *kern_end);
+grub_err_t grub_freebsd_load_elf_meta64 (grub_file_t file,
+					 grub_addr_t *kern_end);
+
+grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data,
+				  grub_uint32_t len);
+grub_err_t grub_freebsd_add_meta_module (char *filename, char *type,
+					 int argc, char **argv,
+					 grub_addr_t addr, grub_uint32_t size);
 
 extern grub_uint8_t grub_bsd64_trampoline_start, grub_bsd64_trampoline_end;
 extern grub_uint32_t grub_bsd64_trampoline_selfjump;
diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c
index 6214b00..4b7a0f8 100644
--- a/loader/i386/bsd.c
+++ b/loader/i386/bsd.c
@@ -116,7 +116,7 @@ grub_bsd_get_device (grub_uint32_t * biosdev,
     grub_device_close (dev);
 }
 
-static grub_err_t
+grub_err_t
 grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len)
 {
   if (mod_buf_max < mod_buf_len + len + 8)
@@ -261,36 +261,21 @@ grub_freebsd_add_mmap (void)
   return grub_errno;
 }
 
-static grub_err_t
-grub_freebsd_add_meta_module (int is_kern, int argc, char **argv,
+grub_err_t
+grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv,
 			      grub_addr_t addr, grub_uint32_t size)
 {
-  char *name, *type;
-
-  name = grub_strrchr (argv[0], '/');
+  char *name;
+  name = grub_strrchr (filename, '/');
   if (name)
     name++;
   else
-    name = argv[0];
+    name = filename;
 
   if (grub_freebsd_add_meta (FREEBSD_MODINFO_NAME, name,
 			     grub_strlen (name) + 1))
     return grub_errno;
 
-  argc--;
-  argv++;
-
-  if ((argc) && (!grub_memcmp (argv[0], "type=", 5)))
-    {
-      type = &argv[0][5];
-      argc--;
-      argv++;
-    }
-  else
-    type = ((is_kern) ?
-	    ((is_64bit) ? FREEBSD_MODTYPE_KERNEL64 : FREEBSD_MODTYPE_KERNEL)
-	    : FREEBSD_MODTYPE_RAW);
-
   if (is_64bit)
     {
       grub_uint64_t addr64 = addr, size64 = size;
@@ -341,23 +326,6 @@ grub_freebsd_add_meta_module (int is_kern, int argc, char **argv,
 	}
     }
 
-  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;
 }
 
@@ -838,10 +806,54 @@ grub_cmd_freebsd (grub_command_t cmd __attribute__ ((unused)),
   if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
     {
       kern_end = ALIGN_PAGE (kern_end);
-      if ((is_elf_kernel) &&
-	  (grub_freebsd_add_meta_module (1, argc, argv, kern_start,
-					 kern_end - kern_start)))
-	return grub_errno;
+      if (is_elf_kernel)
+	{
+	  grub_err_t err;
+	  grub_uint64_t data = 0;
+	  grub_file_t file;
+	  int len = is_64bit ? 8 : 4;
+
+	  err = grub_freebsd_add_meta_module (argv[0], is_64bit
+					      ? FREEBSD_MODTYPE_KERNEL64
+					      : FREEBSD_MODTYPE_KERNEL,
+					      argc - 1, argv + 1,
+					      kern_start,
+					      kern_end - kern_start);
+	  if (err)
+	    return err;
+
+	  file = grub_gzfile_open (argv[0], 1);
+	  if (! file)
+	    return grub_errno;
+
+	  if (is_64bit)
+	    err = grub_freebsd_load_elf_meta64 (file, &kern_end);
+	  else
+	    err = grub_freebsd_load_elf_meta32 (file, &kern_end);
+	  if (err)
+	    return err;
+
+	  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+				       FREEBSD_MODINFOMD_HOWTO, &data, 4);
+	  if (err)
+	    return err;
+
+	  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+				       FREEBSD_MODINFOMD_ENVP, &data, len);
+	  if (err)
+	    return err;
+
+	  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+				       FREEBSD_MODINFOMD_KERNEND, &data, len);
+	  if (err)
+	    return err;
+
+	  kern_end_mdofs = mod_buf_len - len;
+
+	  err = grub_freebsd_add_mmap ();
+	  if (err)
+	    return err;
+	}
       grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 1);
     }
 
@@ -969,6 +981,10 @@ 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;
 
   if (kernel_type != KERNEL_TYPE_FREEBSD)
     return grub_error (GRUB_ERR_BAD_ARGUMENT,
@@ -996,9 +1012,27 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
     }
 
   grub_file_read (file, (void *) kern_end, file->size);
-  if ((!grub_errno) &&
-      (!grub_freebsd_add_meta_module (0, argc, argv, kern_end, file->size)))
-    kern_end = ALIGN_PAGE (kern_end + file->size);
+  if (grub_errno)
+    goto fail;
+
+  modargc = argc - 1;
+  modargv = argv + 1;
+
+  if (modargc && (! grub_memcmp (modargv[0], "type=", 5)))
+    {
+      type = &modargv[0][5];
+      modargc--;
+      modargv++;
+    }
+  else
+    type = FREEBSD_MODTYPE_RAW;
+
+  err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv,
+  				      kern_end, file->size);
+  if (err)
+    goto fail;
+
+  kern_end = ALIGN_PAGE (kern_end + file->size);
 
 fail:
   if (file)
@@ -1007,8 +1041,56 @@ fail:
   return grub_errno;
 }
 
+static grub_err_t
+grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)),
+			     int argc, char *argv[])
+{
+  grub_file_t file = 0;
+  grub_err_t err;
+
+  if (kernel_type != KERNEL_TYPE_FREEBSD)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		       "only freebsd support module");
+
+  if (! is_elf_kernel)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		       "only elf kernel support module");
+
+  /* List the current modules if no parameter.  */
+  if (! argc)
+    {
+      grub_freebsd_list_modules ();
+      return 0;
+    }
+
+  file = grub_gzfile_open (argv[0], 1);
+  if (!file)
+    return grub_errno;
+  if (!file->size)
+    {
+      grub_file_close (file);
+      return grub_errno;
+    }
+
+  if (is_64bit)
+    err = grub_freebsd_load_elfmodule64 (file, argc, argv, &kern_end);
+  else
+    err = grub_freebsd_load_elfmodule32 (file, argc, argv, &kern_end);
+
+  grub_file_close (file);
+
+  if (err)
+    return err;
+
+  kern_end = ALIGN_PAGE (kern_end + file->size);
+
+  return GRUB_ERR_NONE;
+}
+
+
 static grub_command_t cmd_freebsd, cmd_openbsd, cmd_netbsd;
 static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module;
+static grub_command_t cmd_freebsd_module_elf;
 
 GRUB_MOD_INIT (bsd)
 {
@@ -1027,6 +1109,9 @@ GRUB_MOD_INIT (bsd)
   cmd_freebsd_module =
     grub_register_command ("freebsd_module", grub_cmd_freebsd_module,
 			   0, "load freebsd module");
+  cmd_freebsd_module_elf =
+    grub_register_command ("freebsd_module_elf", grub_cmd_freebsd_module_elf,
+			   0, "load freebsd ELF module");
 
   my_mod = mod;
 }
@@ -1039,6 +1124,7 @@ GRUB_MOD_FINI (bsd)
 
   grub_unregister_command (cmd_freebsd_loadenv);
   grub_unregister_command (cmd_freebsd_module);
+  grub_unregister_command (cmd_freebsd_module_elf);
 
   if (mod_buf)
     {
diff --git a/loader/i386/bsd32.c b/loader/i386/bsd32.c
new file mode 100644
index 0000000..9aa87e3
--- /dev/null
+++ b/loader/i386/bsd32.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 32
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Shdr Elf32_Shdr
+#include <grub/types.h>
+typedef grub_uint32_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/loader/i386/bsd64.c b/loader/i386/bsd64.c
new file mode 100644
index 0000000..32fc02c
--- /dev/null
+++ b/loader/i386/bsd64.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 64
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Shdr Elf64_Shdr
+#include <grub/types.h>
+typedef grub_uint64_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/loader/i386/bsdXX.c b/loader/i386/bsdXX.c
new file mode 100644
index 0000000..a6a8d6c
--- /dev/null
+++ b/loader/i386/bsdXX.c
@@ -0,0 +1,215 @@
+#include <grub/loader.h>
+#include <grub/cpu/bsd.h>
+#include <grub/mm.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/i386/loader.h>
+
+#define ALIGN_PAGE(a)	ALIGN_UP (a, 4096)
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
+				      grub_addr_t *kern_end)
+{
+  Elf_Ehdr e;
+  Elf_Shdr *s;
+  char *shdr;
+  grub_addr_t curload, module;
+
+  if (grub_file_seek (file, 0) == (grub_off_t) -1)
+    return grub_errno;
+
+  if (grub_file_read (file, (char *) &e, sizeof (e)) != sizeof (e))
+    {
+      if (grub_errno)
+	return grub_errno;
+      else
+	return grub_error (GRUB_ERR_BAD_OS, "file is too short");
+    }
+
+  if (e.e_ident[EI_MAG0] != ELFMAG0
+      || e.e_ident[EI_MAG1] != ELFMAG1
+      || e.e_ident[EI_MAG2] != ELFMAG2
+      || e.e_ident[EI_MAG3] != ELFMAG3
+      || e.e_ident[EI_VERSION] != EV_CURRENT
+      || e.e_version != EV_CURRENT)
+    return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
+
+  if (e.e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
+    return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic");
+
+  curload = module = ALIGN_PAGE (*kern_end);
+
+  shdr = grub_malloc (e.e_shnum * e.e_shentsize);
+  if (! shdr)
+    return grub_errno;
+
+  if (grub_file_seek (file, e.e_shoff) == (grub_off_t) -1)
+    return grub_errno;
+
+  if (grub_file_read (file, shdr, e.e_shnum * e.e_shentsize)
+      != e.e_shnum * e.e_shentsize)
+    {
+      if (grub_errno)
+	return grub_errno;
+      else
+	return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+    }
+
+  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)
+	curload = ALIGN_UP (curload, s->sh_addralign);
+
+      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_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
+		    (unsigned) curload, (int) s->sh_size,
+		    (int) s->sh_addralign);
+
+      s->sh_addr = curload;
+
+      switch (s->sh_type)
+	{
+	default:
+	case SHT_PROGBITS:
+	  if (grub_file_seek (file, s->sh_offset) == (grub_off_t) -1)
+	    return grub_errno;
+	  if (grub_file_read (file, UINT_TO_PTR (curload), s->sh_size)
+	      != (grub_ssize_t) s->sh_size)
+	    {
+	      if (grub_errno)
+		return grub_errno;
+	      else
+		return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+	    }
+	  break;
+	case SHT_NOBITS:
+	  grub_memset (UINT_TO_PTR (curload), 0, s->sh_size);
+	  break;
+	}
+      curload += s->sh_size;
+    }
+
+  *kern_end = curload;
+
+  grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE,
+				argc - 1, argv + 1, module,
+				curload - module);
+  grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ELFHDR,
+			 &e, sizeof (e));
+  grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SHDR,
+			 shdr, e.e_shnum * e.e_shentsize);
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end)
+{
+  grub_err_t err;
+  Elf_Ehdr e;
+  Elf_Shdr *s;
+  char *shdr;
+  int sym, str, symsize, strsize;
+  grub_addr_t curload;
+  grub_freebsd_addr_t symstart, symend;
+
+  if (grub_file_seek (file, 0) == (grub_off_t) -1)
+    return grub_errno;
+
+  if (grub_file_read (file, (char *) &e, sizeof (e)) != sizeof (e))
+    {
+      if (! grub_errno)
+	return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
+      return grub_errno;
+    }
+
+  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+			       FREEBSD_MODINFOMD_ELFHDR, &e,
+			       sizeof (e));
+  if (err)
+    return err;
+
+  shdr = grub_malloc (e.e_shentsize * e.e_shnum);
+  if (! shdr)
+    return grub_errno;
+
+  if (grub_file_seek (file, e.e_shoff) == (grub_off_t) -1)
+    return grub_errno;
+
+  if (grub_file_read (file, shdr, e.e_shnum * e.e_shentsize)
+      != e.e_shnum * e.e_shentsize)
+    {
+      if (grub_errno)
+	return grub_errno;
+      else
+	return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+    }
+  for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+						+ e.e_shnum * e.e_shentsize);
+       s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+      if (s->sh_type == SHT_SYMTAB)
+	break;
+  if (s >= (Elf_Shdr *) ((char *) shdr
+			+ e.e_shnum * e.e_shentsize))
+    return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
+  sym = s->sh_offset;
+  symsize = s->sh_size;
+  s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+  str = s->sh_offset;
+  strsize = s->sh_size;
+
+  if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize
+      > grub_os_area_addr + grub_os_area_size)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+		       "Not enough memory for the kernel symbols");
+
+  symstart = curload = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
+  *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize;
+  curload += sizeof (grub_freebsd_addr_t);
+  if (grub_file_seek (file, sym) == (grub_off_t) -1)
+    return grub_errno;
+  if (grub_file_read (file, UINT_TO_PTR (curload), symsize) != symsize)
+    {
+      if (! grub_errno)
+	return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
+      return grub_errno;
+    }
+  curload += symsize;
+
+  *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize;
+  curload += sizeof (grub_freebsd_addr_t);
+  if (grub_file_seek (file, str) == (grub_off_t) -1)
+    return grub_errno;
+  if (grub_file_read (file, UINT_TO_PTR (curload), strsize) != strsize)
+    {
+      if (! grub_errno)
+	return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
+      return grub_errno;
+    }
+  curload += strsize;
+  curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t));
+  symend = curload;
+
+  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+			       FREEBSD_MODINFOMD_SSYM, &symstart,
+			       sizeof (symstart));
+  if (err)
+    return err;
+
+  err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
+			       FREEBSD_MODINFOMD_ESYM, &symend,
+			       sizeof (symend));
+  if (err)
+    return err;
+  *kern_end = curload;
+
+  return GRUB_ERR_NONE;
+}
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to