[Top][All Lists]
[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:
- [ppc patch] grub-mkimage,
Hollis Blanchard <=
- Re: [ppc patch] grub-mkimage, Marco Gerards, 2004/12/03
- Re: [ppc patch] grub-mkimage, Johan Rydberg, 2004/12/03
- Re: [ppc patch] grub-mkimage, Marco Gerards, 2004/12/03
- Re: [ppc patch] grub-mkimage, Johan Rydberg, 2004/12/03
- Re: [ppc patch] grub-mkimage, Marco Gerards, 2004/12/03
- Re: [ppc patch] grub-mkimage, Johan Rydberg, 2004/12/03
- Re: [ppc patch] grub-mkimage, Marco Gerards, 2004/12/03
Re: [ppc patch] grub-mkimage, Hollis Blanchard, 2004/12/03