grub-devel
[Top][All Lists]
Advanced

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

Linux loader for the PPC


From: Marco Gerards
Subject: Linux loader for the PPC
Date: Tue, 13 Jul 2004 19:01:22 +0200
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

Hi,

Here is, as promised, the linux loader for the PPC.  Just like Tomas
did for the PC, I made the loader accessible from both rescue mode and
normal mode.  There is just one bug in this code, when a kernel is
loaded it is not possible to load another kernel (which should replace
the previous loaded kernel).  I think that problem is caused by a bug
elsewhere in GRUB 2.  

The biggest missing feature in this loader is initrd support.  It is
just 30 minutes hacking at most, but I simply don't have a
kernel+initrd around to test it with.  Most code is already there and
it won't be hard for me to add this later.

If you can not test the patch, please proof read it for me.  I started
too long at this code to be sure that I did the right stuff. ;)

I really hope people will try this to boot GNU/Linux from GRUB 2 on
the PPC.  At the moment only module loading and HFS+ support is
missing.  After that just a few small features should be added to
make this port complete.

If I don't hear any complaints, I will commit this patch tomorrow or
so.

Thanks,
Marco



2004-07-13  Marco Gerards  <address@hidden>

        * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_release): New
        function.
        * commands/boot.c: Remove the check for `GRUB_UTIL'.
        * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Add
        `loader/powerpc/ieee1275/linux.c',
        `loader/powerpc/ieee1275/linux_normal.c' and `commands/boot.c'.
        * include/grub/powerpc/ieee1275/ieee1275.h
        (grub_ieee1275_release): New prototype.
        * include/grub/powerpc/ieee1275/loader.h: Rewritten.
        * kern/powerpc/ieee1275/init.c (grub_machine_init): Initialize
        normal, boot, linux and linux_normal.
        * loader/powerpc/ieee1275/linux.c: New file.
        * loader/powerpc/ieee1275/linux_normal.c: Likewise.


diff -uprN ../grub2/boot/powerpc/ieee1275/ieee1275.c 
./boot/powerpc/ieee1275/ieee1275.c
--- ../grub2/boot/powerpc/ieee1275/ieee1275.c   2004-04-04 15:45:59.000000000 
+0200
+++ ./boot/powerpc/ieee1275/ieee1275.c  2004-07-13 16:40:59.000000000 +0200
@@ -420,6 +420,25 @@ grub_ieee1275_claim (void *p, grub_size_
 }
 
 int
+grub_ieee1275_release (void *p, grub_size_t size)
+{
+ struct release_args {
+    struct grub_ieee1275_common_hdr common;
+    void *p;
+    grub_size_t size;
+ } args;
+
+  INIT_IEEE1275_COMMON (&args.common, "release", 2, 0);
+  args.p = p;
+  args.size = size;
+  
+  if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+    return -1;
+  
+  return 0;
+}
+
+int
 grub_ieee1275_set_property (grub_ieee1275_phandle_t phandle,
                            const char *propname, void *buf,
                            grub_size_t size, grub_size_t *actual)
diff -uprN ../grub2/commands/boot.c ./commands/boot.c
--- ../grub2/commands/boot.c    2004-04-04 15:46:00.000000000 +0200
+++ ./commands/boot.c   2004-07-13 16:40:59.000000000 +0200
@@ -37,7 +37,7 @@ grub_cmd_boot (struct grub_arg_list *sta
 }
 
 
-#ifdef GRUB_UTIL
+
 void
 grub_boot_init (void)
 {
@@ -50,7 +50,7 @@ grub_boot_fini (void)
 {
   grub_unregister_command ("boot");
 }
-#else /* ! GRUB_UTIL */
+
 GRUB_MOD_INIT
 {
   (void)mod;                   /* To stop warning. */
@@ -62,4 +62,4 @@ GRUB_MOD_FINI
 {
   grub_unregister_command ("boot");
 }
-#endif /* ! GRUB_UTIL */
+
diff -uprN ../grub2/conf/powerpc-ieee1275.rmk ./conf/powerpc-ieee1275.rmk
--- ../grub2/conf/powerpc-ieee1275.rmk  2004-05-24 23:32:21.000000000 +0200
+++ ./conf/powerpc-ieee1275.rmk 2004-07-13 16:40:59.000000000 +0200
@@ -41,7 +41,7 @@ grubof_SOURCES = boot/powerpc/ieee1275/c
        kern/powerpc/ieee1275/openfw.c fs/ext2.c fs/ufs.c fs/minix.c 
normal/cmdline.c \
        normal/command.c normal/main.c normal/menu.c \
        disk/powerpc/ieee1275/ofdisk.c disk/powerpc/ieee1275/partition.c \
-       kern/env.c normal/arg.c
+       kern/env.c normal/arg.c loader/powerpc/ieee1275/linux.c 
loader/powerpc/ieee1275/linux_normal.c commands/boot.c
 grubof_HEADERS = grub/powerpc/ieee1275/ieee1275.h
 grubof_CFLAGS = $(COMMON_CFLAGS)
 grubof_ASFLAGS = $(COMMON_ASFLAGS)
diff -uprN ../grub2/include/grub/powerpc/ieee1275/ieee1275.h 
./include/grub/powerpc/ieee1275/ieee1275.h
--- ../grub2/include/grub/powerpc/ieee1275/ieee1275.h   2004-04-04 
15:46:01.000000000 +0200
+++ ./include/grub/powerpc/ieee1275/ieee1275.h  2004-07-13 16:40:59.000000000 
+0200
@@ -84,6 +84,7 @@ int EXPORT_FUNC(grub_ieee1275_open) (cha
 int EXPORT_FUNC(grub_ieee1275_close) (grub_ieee1275_ihandle_t ihandle);
 int EXPORT_FUNC(grub_ieee1275_claim) (void *p, grub_size_t size, unsigned int 
align,
                                      void **result);
+int EXPORT_FUNC(grub_ieee1275_release) (void *p, grub_size_t size);
 int EXPORT_FUNC(grub_ieee1275_set_property) (grub_ieee1275_phandle_t phandle,
                                             const char *propname, void *buf,
                                             grub_size_t size,
diff -uprN ../grub2/include/grub/powerpc/ieee1275/loader.h 
./include/grub/powerpc/ieee1275/loader.h
--- ../grub2/include/grub/powerpc/ieee1275/loader.h     2004-04-04 
15:46:01.000000000 +0200
+++ ./include/grub/powerpc/ieee1275/loader.h    2004-07-13 16:40:59.000000000 
+0200
@@ -1,7 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
- *  Copyright (C) 2003  Jeroen Dekkers <address@hidden>
+ *  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
@@ -21,31 +20,13 @@
 #ifndef GRUB_LOADER_MACHINE_HEADER
 #define GRUB_LOADER_MACHINE_HEADER     1
 
-#include <grub/types.h>
-#include <grub/symbol.h>
-#include <grub/machine/multiboot.h>
-
-extern grub_uint32_t EXPORT_VAR(grub_linux_prot_size);
-extern char *EXPORT_VAR(grub_linux_tmp_addr);
-extern char *EXPORT_VAR(grub_linux_real_addr);
-
-void EXPORT_FUNC(grub_linux_boot_zimage) (void) __attribute__ ((noreturn));
-void EXPORT_FUNC(grub_linux_boot_bzimage) (void) __attribute__ ((noreturn));
-
-/* This is an asm part of the chainloader.  */
-void EXPORT_FUNC(grub_chainloader_real_boot) (int drive, void *part_addr) 
__attribute__ ((noreturn));
-
-/* The asm part of the multiboot loader.  */
-void EXPORT_FUNC(grub_multiboot_real_boot) (grub_addr_t entry, 
-                                           struct grub_multiboot_info *mbi) 
-     __attribute__ ((noreturn));
-
-/* It is necessary to export these functions, because normal mode commands
-   reuse rescue mode commands.  */
-void grub_rescue_cmd_chainloader (int argc, char *argv[]);
-void grub_rescue_cmd_linux (int argc, char *argv[]);
-void grub_rescue_cmd_initrd (int argc, char *argv[]);
-void grub_rescue_cmd_multiboot (int argc, char *argv[]);
-void grub_rescue_cmd_module (int argc, char *argv[]);
+/* The symbol shared between the normal mode and rescue mode
+   loader.  */
+void grub_load_linux (int argc, char *argv[]);
+
+void grub_linux_init (void);
+void grub_linux_fini (void);
+void grub_linux_normal_init (void);
+void grub_linux_normal_fini (void);
 
 #endif /* ! GRUB_LOADER_MACHINE_HEADER */
diff -uprN ../grub2/kern/powerpc/ieee1275/init.c ./kern/powerpc/ieee1275/init.c
--- ../grub2/kern/powerpc/ieee1275/init.c       2004-04-30 22:46:40.000000000 
+0200
+++ ./kern/powerpc/ieee1275/init.c      2004-07-13 16:40:59.000000000 +0200
@@ -58,6 +58,10 @@ grub_machine_init (void)
   grub_env_set ("prefix", "");
 
   grub_ext2_init ();
+  grub_normal_init ();
+  grub_boot_init ();
+  grub_linux_init ();
+  grub_linux_normal_init ();
   grub_ofdisk_init ();
   grub_console_init ();
 }
diff -uprN ../grub2/loader/powerpc/ieee1275/linux.c 
./loader/powerpc/ieee1275/linux.c
--- ../grub2/loader/powerpc/ieee1275/linux.c    1970-01-01 01:00:00.000000000 
+0100
+++ ./loader/powerpc/ieee1275/linux.c   2004-07-13 15:52:00.000000000 +0200
@@ -0,0 +1,289 @@
+/* linux.c - boot Linux */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003, 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 <grub/machine/loader.h>
+#include <grub/machine/ieee1275.h>
+#include <grub/elf.h>
+#include <grub/loader.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/rescue.h>
+#include <grub/misc.h>
+
+static grub_dl_t my_mod;
+
+static int loaded;
+static int vmlinux;
+
+static grub_addr_t initrd_addr;
+static grub_size_t initrd_size;
+
+static grub_addr_t linux_addr;
+static grub_size_t linux_size;
+
+static char *linux_args;
+
+typedef void (*kernel_entry_t) (void *, unsigned long, intptr_t (void *),
+                               unsigned long, unsigned long);
+
+static grub_err_t
+grub_linux_boot (void)
+{
+  kernel_entry_t linuxmain;
+  grub_ieee1275_phandle_t chosen;
+  grub_size_t actual;
+  
+  struct bi_rec
+  {
+    unsigned long tag;
+    unsigned long size;
+    unsigned long data[0];
+  };
+  
+  grub_ieee1275_finddevice ("/chosen", &chosen);
+  
+  /* Set the command line arguments.  */
+  grub_ieee1275_set_property (chosen, "bootargs", linux_args,
+                             grub_strlen (linux_args) + 1, &actual);
+  
+  /* Boot the kernel.  */
+  linuxmain = (kernel_entry_t) linux_addr;
+  linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_release_mem (void)
+{
+  grub_free (linux_args);
+  linux_args = 0;
+  
+  if (linux_addr && grub_ieee1275_release ((void *) linux_addr, linux_size))
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not release memory");
+  
+  linux_addr = 0;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+  grub_err_t err;
+  
+  err = grub_linux_release_mem ();
+  grub_dl_unref (my_mod);
+  
+  loaded = 0;
+  
+  return err;
+}
+
+void
+grub_load_linux (int argc, char *argv[])
+{
+  grub_file_t file = 0;
+  Elf32_Ehdr ehdr;
+  Elf32_Phdr *phdrs = 0;
+  int i;  
+  int offset = 0;
+  static grub_addr_t entry;
+  int size;
+  
+  grub_dl_ref (my_mod);
+
+  if (argc == 0)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+      goto fail;
+    }
+  
+  file = grub_file_open (argv[0]);
+  if (! file)
+    goto fail;
+  
+  if (grub_file_read (file, (char *) &ehdr, sizeof (ehdr)) != sizeof (ehdr))
+    {
+      grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux elf header");
+      goto fail;
+    }
+  
+  if (!((ehdr.e_ident[EI_MAG0] == ELFMAG0) 
+       && (ehdr.e_ident[EI_MAG1] == ELFMAG1)
+       && (ehdr.e_ident[EI_MAG2] == ELFMAG2) 
+       && (ehdr.e_ident[EI_MAG3] == ELFMAG3)
+       && (ehdr.e_ident[EI_CLASS] == ELFCLASS32) 
+       && (ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+       && (ehdr.e_ident[EI_VERSION] == EV_CURRENT) 
+       && (ehdr.e_type == ET_EXEC) && (ehdr.e_machine == EM_PPC) 
+       && (ehdr.e_version == EV_CURRENT)))
+    {
+      grub_error (GRUB_ERR_UNKNOWN_OS, "No valid ELF header found");
+      goto fail;
+    }
+  
+  if (ehdr.e_type != ET_EXEC)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_OS,
+                 "This ELF file is not of the right type\n");
+      goto fail;
+    }
+
+  if (ehdr.e_machine != EM_PPC)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_OS,
+                 "This ELF file is not for the PPC32\n");
+      goto fail;
+    }
+  
+  if (ehdr.e_version != EV_CURRENT)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_OS,
+                 "Invalid ELF version\n");
+      goto fail;
+    }
+
+  /* Read the sections.  */
+  entry = ehdr.e_entry;
+  if (entry == 0xc0000000)
+    {
+      entry = 0x01400000;
+      vmlinux = 1;
+    }
+  else
+    vmlinux = 0;
+
+  phdrs = (Elf32_Phdr *) grub_malloc (ehdr.e_phnum * ehdr.e_phentsize);
+  grub_file_read (file, (void *) phdrs, ehdr.e_phnum * ehdr.e_phentsize);
+  
+  /* Release the previously used memory.  */
+  grub_loader_unset ();
+  
+  /* Determine the amount of memory that is required.  */
+  linux_size = 0;
+  for (i = 0; i < ehdr.e_phnum; i++)
+    {
+      Elf32_Phdr *phdr = phdrs + i;
+      /* XXX: Is this calculation correct?  */
+      linux_size += phdr->p_memsz + phdr->p_filesz;
+    }
+  
+  /* Reserve memory for the kernel.  */
+  linux_size += 0x100000;
+  
+  if (grub_ieee1275_claim ((void *) entry, linux_size, 0, (void *) 
&linux_addr) == -1)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not claim memory");
+      goto fail;
+    }
+  
+  /* Load every loadable segment in memory.  */
+  for (i = 0; i < ehdr.e_phnum; i++)
+    {
+      Elf32_Phdr *phdr = phdrs + i;
+
+      if (phdr->p_type == PT_LOAD)
+       {
+         if (grub_file_seek (file, phdr->p_offset) == -1)
+           {
+             grub_error (GRUB_ERR_BAD_OS, "Invalid offset in program header");
+             goto fail;
+           }
+         
+         if (grub_file_read (file, (void *) (((char *) entry) + offset) , 
phdr->p_filesz) 
+             != (grub_ssize_t) phdr->p_filesz)
+           goto fail;
+         
+         if (phdr->p_filesz < phdr->p_memsz)
+           grub_memset ((char *) (((char *) entry) + offset) + phdr->p_filesz, 
0, 
+                        phdr->p_memsz - phdr->p_filesz);
+
+         offset += phdr->p_filesz;
+       }
+    }
+  
+  size = 0;
+  for (i = 0; i < argc; i++)
+    size += grub_strlen (argv[i]);
+  
+  linux_args = grub_malloc (size + argc + 1);
+  if (!linux_args)
+    goto fail;
+  
+  for (i = 1; i < argc; i++)
+    {
+      grub_strcat (linux_args, argv[i]);
+      grub_strcat (linux_args, " ");
+    }
+  
+ fail:
+
+  if (file)
+    grub_file_close (file);
+  
+  grub_free (phdrs);
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_linux_release_mem ();
+      grub_dl_unref (my_mod);
+      loaded = 0;
+    }
+  else
+    {
+      grub_loader_set (grub_linux_boot, grub_linux_unload);
+      initrd_addr = 0xc0000000;
+      loaded = 1;
+    }
+  
+  return;
+}
+
+
+static void
+grub_rescue_cmd_linux (int argc, char *argv[])
+{
+  grub_load_linux (argc, argv);
+}
+
+GRUB_MOD_INIT
+{
+  grub_rescue_register_command ("linux", grub_rescue_cmd_linux,
+                               "load a linux kernel");
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI
+{
+  grub_rescue_unregister_command ("linux");
+}
+
+void
+grub_linux_init (void)
+{
+  grub_rescue_register_command ("linux", grub_rescue_cmd_linux,
+                               "load a linux kernel");
+}
+
+void
+grub_linux_fini (void)
+{
+  grub_rescue_unregister_command ("linux");
+}
diff -uprN ../grub2/loader/powerpc/ieee1275/linux_normal.c 
./loader/powerpc/ieee1275/linux_normal.c
--- ../grub2/loader/powerpc/ieee1275/linux_normal.c     1970-01-01 
01:00:00.000000000 +0100
+++ ./loader/powerpc/ieee1275/linux_normal.c    2004-07-13 15:45:10.000000000 
+0200
@@ -0,0 +1,63 @@
+/* linux_normal.c - boot Linux */
+/*
+ *  GRUB  --  Preliminary Universal Programming Architecture for GRUB
+ *  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 <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/machine/loader.h>
+
+static const struct grub_arg_option options[] =
+  {
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static grub_err_t
+grub_cmd_linux (struct grub_arg_list *state  __attribute__ ((unused)),
+               int argc, char **args)
+{
+  grub_load_linux (argc, args);
+  return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT
+{
+  (void) mod;
+  grub_register_command ("linux", grub_cmd_linux, GRUB_COMMAND_FLAG_BOTH,
+                        "linux [KERNELARGS...]",
+                        "Loads linux", options);
+}
+
+GRUB_MOD_FINI
+{
+  grub_unregister_command ("linux");
+}
+
+void
+grub_linux_normal_init (void)
+{
+  grub_register_command ("linux", grub_cmd_linux, GRUB_COMMAND_FLAG_BOTH,
+                        "linux [KERNELARGS...]",
+                        "Loads linux", options);
+}
+
+void
+grub_linux_normal_fini (void)
+{
+  grub_unregister_command ("linux");
+}





reply via email to

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