grub-devel
[Top][All Lists]
Advanced

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

[ppc patch] grub-mkimage


From: Hollis Blanchard
Subject: [ppc patch] grub-mkimage
Date: Thu, 2 Dec 2004 23:36:46 -0600
User-agent: Mutt/1.5.6+20040722i

I'm going to be on vacation next week, so I wanted to get this out so
others could continue progress on PPC module loading... this is not
ready for inclusion, but it is functional. Comments are welcome.

Open Firmware loads GRUB as an ELF file (rather than a binary image like
x86 BIOS). So we can't just concatenate modules to the end of the grubof
ELF file; we need to put them in an ELF LOAD segment. That's what this
grub-mkimage does.

Right now there's still a manual step or two involved. You must build
a binary "note" segment (in the proper endianness). I *thought* we could
get the toolchain to build this for us with the right combination of
gcc/ld/objcopy options, but now I'm not so sure. Anyways, for now from a
PPC host:
  gcc -c note.S
  objcopy -O binary note.o note

Use the -n/--note switch to add the CHRP NOTE segment on CHRP platforms
(e.g. briQ and Pegasos). NEVER use this switch on Power Macintosh. I
guess that should be documented better in the help text, but anyways...

The endian-swapping code in grub-mkimage has not been tested, but
may work.

Right now grub-mkimage arbitrarily loads the modules starting at 3MB,
and grubof must look for them there at runtime. It's a bit awkward to
tell the grubof runtime about where the modules are loaded, since we
would need to parse the ELF file and edit it on disk (not as easy as
just hacking a binary file at reserved offsets). That same difficulty
makes it impossible right now to tell grubof runtime about the total
size of the modules loaded. One possible solution there is to end the
module area with a (0, 0) grub_module_header.

It may be possible to place module variables into their own section
containing nothing else, yet still in a LOAD segment.  Then grub-mkimage
could parse the *section* table (right now it only does segments) and
overwrite the contents of this section to inform the runtime of the
module location. I'm not convinced it's worth the effort.

I'm firmly convinced that grub_load_modules(), currently in kern/main.c,
will need to be changed to a more generic interface. For example, each
arch could provide its own grub_module_start() and grub_module_end()
functions. It is no longer a valid assumption that the modules begin
immediately after grub_end_addr in memory. But for now I will leave that
to someone (Marco?) doing the real module loading; grub-mkimage just
needs to get it into memory (which I've verified it does).

-Hollis

P.S. -o is a mandatory switch for this grub-mkimage. ELF rewriting
involves a lot of seeking, which you can't do with stdout...

Index: boot/powerpc/ieee1275/crt0.S
===================================================================
RCS file: /cvsroot/grub/grub2/boot/powerpc/ieee1275/crt0.S,v
retrieving revision 1.3
diff -u -p -r1.3 crt0.S
--- boot/powerpc/ieee1275/crt0.S        12 Oct 2004 03:56:10 -0000      1.3
+++ boot/powerpc/ieee1275/crt0.S        3 Dec 2004 05:26:48 -0000
@@ -18,21 +18,6 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-       .section ".note"
-       .align  2
-.note_section_header:
-       .long   8
-       .long   24
-       .long   0x1275
-       .string "PowerPC"
-.note_descriptor:
-       .long   0x0             /* real-mode */
-       .long   0xffffffff      /* real-base */
-       .long   0xffffffff      /* real-size */
-       .long   0xffffffff      /* virt-base */
-       .long   0xffffffff      /* virt-size */
-       .long   0x00030000      /* load-base */
-
 .extern __bss_start
 .extern _end
 
Index: conf/powerpc-ieee1275.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/powerpc-ieee1275.rmk,v
retrieving revision 1.14
diff -u -p -r1.14 powerpc-ieee1275.rmk
--- conf/powerpc-ieee1275.rmk   16 Nov 2004 23:34:44 -0000      1.14
+++ conf/powerpc-ieee1275.rmk   3 Dec 2004 05:26:49 -0000
@@ -17,9 +17,13 @@ kernel_syms.lst: $(addprefix include/gru
 
 # Utilities.
 sbin_UTILITIES = grubof
-bin_UTILITIES = grub-emu
+bin_UTILITIES = grub-emu grub-mkimage
 noinst_UTILITIES = genmoddep
 
+# For grub-mkimage.
+grub_mkimage_SOURCES = util/powerpc/ieee1275/grub-mkimage.c util/misc.c \
+        util/resolve.c 
+
 # For grub-emu
 grub_emu_SOURCES = kern/main.c kern/device.c                           \
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c          \
Index: include/grub/util/misc.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/util/misc.h,v
retrieving revision 1.5
diff -u -p -r1.5 misc.h
--- include/grub/util/misc.h    4 Apr 2004 13:46:01 -0000       1.5
+++ include/grub/util/misc.h    3 Dec 2004 05:26:49 -0000
@@ -34,9 +34,13 @@ void *xrealloc (void *ptr, size_t size);
 char *xstrdup (const char *str);
 
 char *grub_util_get_path (const char *dir, const char *file);
+size_t grub_util_get_fp_size (FILE *fp);
 size_t grub_util_get_image_size (const char *path);
+void grub_util_read_at (void *img, size_t len, off_t offset, FILE *fp);
 char *grub_util_read_image (const char *path);
 void grub_util_load_image (const char *path, char *buf);
 void grub_util_write_image (const char *img, size_t size, FILE *out);
+void grub_util_write_image_at (const void *img, size_t size, off_t offset,
+                              FILE *out);
 
 #endif /* ! GRUB_UTIL_MISC_HEADER */
Index: util/misc.c
===================================================================
RCS file: /cvsroot/grub/grub2/util/misc.c,v
retrieving revision 1.8
diff -u -p -r1.8 misc.c
--- util/misc.c 4 Apr 2004 13:46:03 -0000       1.8
+++ util/misc.c 3 Dec 2004 05:26:49 -0000
@@ -25,6 +25,7 @@
 #include <sys/stat.h>
 #include <sys/times.h>
 #include <malloc.h>
+#include <unistd.h>
 
 #include <grub/util/misc.h>
 #include <grub/mm.h>
@@ -107,6 +108,20 @@ grub_util_get_path (const char *dir, con
 }
 
 size_t
+grub_util_get_fp_size (FILE *fp)
+{
+  struct stat st;
+  
+  if (fflush (fp) == EOF)
+    grub_util_error ("fflush failed");
+
+  if (fstat (fileno (fp), &st) == -1)
+    grub_util_error ("fstat failed");
+  
+  return st.st_size;
+}
+
+size_t
 grub_util_get_image_size (const char *path)
 {
   struct stat st;
@@ -119,6 +134,16 @@ grub_util_get_image_size (const char *pa
   return st.st_size;
 }
 
+void
+grub_util_read_at (void *img, size_t size, off_t offset, FILE *fp)
+{
+  if (fseek (fp, offset, SEEK_SET) == -1)
+    grub_util_error ("fseek failed");
+
+  if (fread (img, 1, size, fp) != size)
+    grub_util_error ("read failed");
+}
+
 char *
 grub_util_read_image (const char *path)
 {
@@ -134,9 +159,8 @@ grub_util_read_image (const char *path)
   fp = fopen (path, "rb");
   if (! fp)
     grub_util_error ("cannot open %s", path);
-  
-  if (fread (img, 1, size, fp) != size)
-    grub_util_error ("cannot read %s", path);
+
+  grub_util_read_at (img, size, 0, fp);
 
   fclose (fp);
   
@@ -164,6 +188,15 @@ grub_util_load_image (const char *path, 
 }
 
 void
+grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE 
*out)
+{
+  grub_util_info ("writing 0x%x bytes at offset 0x%x", size, offset);
+  if (fseek (out, offset, SEEK_SET) == -1)
+    grub_util_error ("write failed");
+  grub_util_write_image (img, size, out);
+}
+
+void
 grub_util_write_image (const char *img, size_t size, FILE *out)
 {
   grub_util_info ("writing 0x%x bytes", size);
--- /dev/null   2004-09-05 22:56:24.000000000 -0500
+++ util/powerpc/ieee1275/grub-mkimage.c        2004-12-02 23:06:42.309752560 
-0600
@@ -0,0 +1,334 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2004  Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <grub/elf.h>
+#include <grub/util/misc.h>
+#include <grub/util/resolve.h>
+#include <grub/kernel.h>
+
+#define ALIGN_UP(addr, align) ((long)((char *)addr + align - 1) & ~(align - 1))
+
+#define MODULE_BASE 0x00300000
+
+static char *kernel_path = "grubof";
+static char *note_path = "note";
+
+void swap_ehdr (const Elf32_Ehdr *ehdr, Elf32_Ehdr *swapped)
+{
+  memcpy (swapped->e_ident, ehdr->e_ident, EI_NIDENT);
+  swapped->e_type = grub_cpu_to_be16 (ehdr->e_type);
+  swapped->e_machine = grub_cpu_to_be16 (ehdr->e_machine);
+  swapped->e_version = grub_cpu_to_be32 (ehdr->e_version);
+  swapped->e_entry = grub_cpu_to_be32 (ehdr->e_entry);
+  swapped->e_phoff = grub_cpu_to_be32 (ehdr->e_phoff);
+  swapped->e_shoff = grub_cpu_to_be32 (ehdr->e_shoff);
+  swapped->e_flags = grub_cpu_to_be32 (ehdr->e_flags);
+  swapped->e_ehsize = grub_cpu_to_be16 (ehdr->e_ehsize);
+  swapped->e_phentsize = grub_cpu_to_be16 (ehdr->e_phentsize);
+  swapped->e_phnum = grub_cpu_to_be16 (ehdr->e_phnum);
+  swapped->e_shentsize = grub_cpu_to_be16 (ehdr->e_shentsize);
+  swapped->e_shnum = grub_cpu_to_be16 (ehdr->e_shnum);
+  swapped->e_shstrndx = grub_cpu_to_be16 (ehdr->e_shstrndx);
+}
+
+void swap_phdrs (Elf32_Phdr *phdr, Elf32_Phdr *swapped, int count)
+{
+  int i;
+
+  for (i = 0; i < count; i++)
+    {
+      swapped->p_type = grub_cpu_to_be32 (phdr->p_type);
+      swapped->p_offset = grub_cpu_to_be32 (phdr->p_offset);
+      swapped->p_vaddr = grub_cpu_to_be32 (phdr->p_vaddr);
+      swapped->p_paddr = grub_cpu_to_be32 (phdr->p_paddr);
+      swapped->p_filesz = grub_cpu_to_be32 (phdr->p_filesz);
+      swapped->p_memsz = grub_cpu_to_be32 (phdr->p_memsz);
+      swapped->p_flags = grub_cpu_to_be32 (phdr->p_flags);
+      swapped->p_align = grub_cpu_to_be32 (phdr->p_align);
+    }
+}
+
+#ifdef GRUB_CPU_WORDS_BIGENDIAN
+#define grub_cpu_to_be_ehdr(x, y)
+#define grub_be_to_cpu_ehdr(x, y)
+#define grub_cpu_to_be_phdrs(x, y, z)
+#define grub_be_to_cpu_phdrs(x, y, z)
+#else /* ! WORDS_BIGENDIAN */
+#define grub_cpu_to_be_ehdr(x, y) swap_ehdr(x, y)
+#define grub_be_to_cpu_ehdr(x, y) swap_ehdr(x, y)
+#define grub_cpu_to_be_phdrs(x, y, z) swap_phdrs(x, y, z)
+#define grub_be_to_cpu_phdrs(x, y, z) swap_phdrs(x, y, z)
+#endif
+
+void load_note (Elf32_Phdr *phdr, const char *dir, FILE *out)
+{
+  char *note_img;
+  char *path;
+  int note_size;
+
+  grub_util_info ("adding CHRP NOTE segment");
+
+  path = grub_util_get_path (dir, note_path);
+  note_size = grub_util_get_image_size (path);
+  note_img = xmalloc (note_size);
+  grub_util_load_image (path, note_img);
+  free (path);
+
+  /* Write the note data to the new segment.  */
+  grub_util_write_image_at (note_img, note_size, phdr->p_offset, out);
+
+  /* Fill in the rest of the segment header.  */
+  phdr->p_type = PT_NOTE;
+  phdr->p_flags = PF_R;
+  phdr->p_align = sizeof (long);
+  phdr->p_vaddr = 0;
+  phdr->p_paddr = 0;
+  phdr->p_filesz = note_size;
+  phdr->p_memsz = 0;
+}
+
+void load_modules (Elf32_Phdr *phdr, const char *dir, char *mods[], FILE *out)
+{
+  char *module_img;
+  struct grub_util_path_list *path_list, *p;
+  size_t offset = 0;
+  size_t total_module_size;
+
+  path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods);
+
+  total_module_size = 0;
+  for (p = path_list; p; p = p->next)
+    total_module_size += (grub_util_get_image_size (p->name)
+       + sizeof (struct grub_module_header));
+
+  grub_util_info ("the total module size is 0x%x", total_module_size);
+
+  module_img = xmalloc (total_module_size);
+
+  /* Load all the modules, with headers, into module_img.  */
+  for (p = path_list; p; p = p->next)
+    {
+      struct grub_module_header *header;
+      size_t mod_size;
+
+      grub_util_info ("adding module %s", p->name);
+
+      mod_size = grub_util_get_image_size (p->name);
+
+      header = (struct grub_module_header *) (module_img + offset);
+      header->offset = grub_cpu_to_be32 (sizeof (*header));
+      header->size = grub_cpu_to_be32 (mod_size + sizeof (*header));
+
+      grub_util_load_image (p->name, module_img + offset + sizeof (*header));
+
+      offset += sizeof (*header) + mod_size;
+    }
+
+  /* Write the module data to the new segment.  */
+  grub_util_write_image_at (module_img, total_module_size, phdr->p_offset, 
out);
+
+  /* Fill in the rest of the segment header.  */
+  phdr->p_type = PT_LOAD;
+  phdr->p_flags = PF_R | PF_W | PF_X;
+  phdr->p_align = sizeof (long);
+  phdr->p_vaddr = MODULE_BASE;
+  phdr->p_paddr = MODULE_BASE;
+  phdr->p_filesz = total_module_size;
+  phdr->p_memsz = total_module_size;
+}
+
+void add_segments (char *dir, FILE *out, int chrp, char *mods[])
+{
+  Elf32_Ehdr ehdr;
+  Elf32_Phdr *phdrs = NULL;
+  Elf32_Phdr *phdr;
+  FILE *in;
+  off_t phdroff;
+  int i;
+
+  /* Read ELF header.  */
+  in = fopen (kernel_path, "rb");
+  if (! in)
+    grub_util_error ("cannot open %s", kernel_path);
+  grub_util_read_at (&ehdr, sizeof (ehdr), 0, in);
+  grub_be_to_cpu_ehdr (&ehdr, &ehdr);
+
+  phdrs = xmalloc (ehdr.e_phentsize * (ehdr.e_phnum + 2));
+
+  /* Copy all existing segments.  */
+  for (i = 0; i < ehdr.e_phnum; i++)
+    {
+      char *segment_img;
+
+      phdr = phdrs + i;
+
+      /* Read segment header.  */
+      grub_util_read_at (phdr, sizeof (Elf32_Phdr), ehdr.e_phoff
+                        + (i * ehdr.e_phentsize), in);
+      grub_be_to_cpu_phdrs (phdr, phdr, 1);
+
+      grub_util_info ("copying segment %d, type %d", i, phdr->p_type);
+
+      /* Read segment data and write it to new file.  */
+      segment_img = xmalloc (phdr->p_filesz);
+      grub_util_read_at (segment_img, phdr->p_filesz, phdr->p_offset, in);
+      grub_util_write_image_at (segment_img, phdr->p_filesz, phdr->p_offset, 
out);
+
+      free (segment_img);
+    }
+
+  if (mods[0] != NULL)
+    {
+      /* Construct new segment header for modules.  */
+      phdr = phdrs + ehdr.e_phnum;
+      ehdr.e_phnum++;
+
+      /* Fill in p_offset so the callees know where to write.  */
+      phdr->p_offset = ALIGN_UP (grub_util_get_fp_size (out), sizeof (long));
+
+      load_modules (phdr, dir, mods, out);
+    }
+
+  if (chrp)
+    {
+      /* Construct new segment header for the CHRP note.  */
+      phdr = phdrs + ehdr.e_phnum;
+      ehdr.e_phnum++;
+
+      /* Fill in p_offset so the callees know where to write.  */
+      phdr->p_offset = ALIGN_UP (grub_util_get_fp_size (out), sizeof (long));
+
+      load_note (phdr, dir, out);
+    }
+
+  /* Don't bother preserving the section headers.  */
+  ehdr.e_shoff = 0;
+  ehdr.e_shnum = 0;
+  ehdr.e_shstrndx = 0;
+
+  /* Append entire segment table to the file.  */
+  phdroff = ALIGN_UP (grub_util_get_fp_size (out), sizeof (long));
+  grub_cpu_to_be_phdrs (phdrs, phdrs, ehdr.e_phnum);
+  grub_util_write_image_at (phdrs, ehdr.e_phentsize * ehdr.e_phnum,
+                           phdroff, out);
+
+  /* Write ELF header.  */
+  ehdr.e_phoff = phdroff;
+  grub_cpu_to_be_ehdr (&ehdr, &ehdr);
+  grub_util_write_image_at (&ehdr, sizeof (ehdr), 0, out);
+
+  free (phdrs);
+}
+
+static struct option options[] =
+  {
+    {"directory", required_argument, 0, 'd'},
+    {"output", required_argument, 0, 'o'},
+    {"help", no_argument, 0, 'h'},
+    {"note", no_argument, 0, 'n'},
+    {"version", no_argument, 0, 'V'},
+    {"verbose", no_argument, 0, 'v'},
+    { 0, 0, 0, 0 },
+  };
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-mkimage --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-mkimage -o FILE [OPTION]... [MODULES]\n\
+\n\
+Make a bootable image of GRUB.\n\
+\n\
+-d, --directory=DIR     use images and modules under DIR [default=%s]\n\
+-o, --output=FILE       output a generated image to FILE\n\
+-h, --help              display this message and exit\n\
+-n, --note              add NOTE segment for CHRP Open Firmware\n\
+-V, --version           print version information and exit\n\
+-v, --verbose           print verbose messages\n\
+\n\
+Report bugs to <%s>.\n\
+", GRUB_DATADIR, PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int main (int argc, char *argv[])
+{
+  FILE *fp;
+  char *output = NULL;
+  char *dir = NULL;
+  int chrp = 0;
+
+  progname = "grub-mkimage";
+
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "d:o:hVvn", options, 0);
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+         case 'd':
+           if (dir)
+             free (dir);
+           dir = xstrdup (optarg);
+           break;
+         case 'h':
+           usage (0);
+           break;
+         case 'n':
+           chrp = 1;
+           break;
+         case 'o':
+           if (output)
+             free (output);
+           output = xstrdup (optarg);
+           break;
+         case 'V':
+           printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+           return 0;
+         case 'v':
+           verbosity++;
+           break;
+         default:
+           usage (1);
+           break;
+       }
+  }
+
+  if (!output)
+    usage (1);
+
+  fp = fopen (output, "wb");
+  if (! fp)
+    grub_util_error ("cannot open %s", output);
+
+  add_segments (dir ? : GRUB_DATADIR, fp, chrp, argv + optind);
+
+  fclose (fp);
+
+  return 0;
+}
--- /dev/null   2004-09-05 22:56:24.000000000 -0500
+++ note.S      2004-11-08 21:30:09.000000000 -0600
@@ -0,0 +1,17 @@
+/* PT_NOTE segment for IEEE1275 CHRP binding.
+   !!! NOT for use on Power Macs!!!
+ */
+
+.note_section_header:
+       .long   8
+       .long   .note_end - .note_section_header
+       .long   0x1275
+       .string "PowerPC"
+.note_descriptor:
+       .long   0xffffffff      /* real-mode */
+       .long   0x00c00000      /* real-base */
+       .long   0xffffffff      /* real-size */
+       .long   0xffffffff      /* virt-base */
+       .long   0xffffffff      /* virt-size */
+       .long   0x00004000      /* load-base */
+.note_end:




reply via email to

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