From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with archive (Exim 4.43) id 1DlokO-00038V-4s for mharc-grub-devel@gnu.org; Fri, 24 Jun 2005 10:01:32 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1DlokH-00034M-I0 for grub-devel@gnu.org; Fri, 24 Jun 2005 10:01:26 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Dlok6-0002yV-DP for grub-devel@gnu.org; Fri, 24 Jun 2005 10:01:16 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Dlok6-0002yD-5Y for grub-devel@gnu.org; Fri, 24 Jun 2005 10:01:14 -0400 Received: from [207.69.195.67] (helo=pop-tawny.atl.sa.earthlink.net) by monty-python.gnu.org with esmtp (Exim 4.34) id 1Dlomi-0007AC-KU for grub-devel@gnu.org; Fri, 24 Jun 2005 10:03:57 -0400 Received: from user-0vvde2g.cable.mindspring.com ([63.246.184.80] helo=miracle) by pop-tawny.atl.sa.earthlink.net with esmtp (Exim 3.36 #10) id 1DlojA-0005Hb-00 for grub-devel@gnu.org; Fri, 24 Jun 2005 10:00:16 -0400 Received: from hollis by miracle with local (Exim 3.36 #1 (Debian)) id 1Dloac-0000nb-00 for ; Fri, 24 Jun 2005 08:51:26 -0500 Date: Fri, 24 Jun 2005 08:51:25 -0500 To: grub-devel@gnu.org Message-ID: <20050624135125.GA3072@miracle> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="azLHFNyN32YCQGCU" Content-Disposition: inline User-Agent: Mutt/1.5.6+20040907i From: Hollis Blanchard Subject: PPC multiboot X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: The development of GRUB 2 List-Id: The development of GRUB 2 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 24 Jun 2005 14:01:29 -0000 --azLHFNyN32YCQGCU Content-Type: text/plain; charset=us-ascii Content-Disposition: inline I did some hacking on a multiboot loader for PPC last night. I wasn't trying to properly update the multiboot spec for non-x86 architectures, but rather just use the existing structures and code. I've also attached a "hello world" PPC multiboot client. I noticed the top-level "hello" directory is a simple GRUB module, but I'm not sure how useful that is. It might be more useful to create hello/powerpc/ieee1275 and hello/i386/pc directories including the simplest possible multiboot clients. (Right now my hello application is mysteriously failing on my G3, apparently on the first stack reference, which may be the result of a firmware "quirk".) This patch is not meant to be applied, but I'm putting it out here for the curious. -Hollis Index: conf/powerpc-ieee1275.rmk =================================================================== RCS file: /cvsroot/grub/grub2/conf/powerpc-ieee1275.rmk,v retrieving revision 1.30 diff -u -p -r1.30 powerpc-ieee1275.rmk --- conf/powerpc-ieee1275.rmk 1 May 2005 03:45:35 -0000 1.30 +++ conf/powerpc-ieee1275.rmk 24 Jun 2005 13:41:24 -0000 @@ -72,7 +72,7 @@ pkgdata_MODULES = _linux.mod linux.mod f hfs.mod jfs.mod normal.mod hello.mod font.mod ls.mod \ boot.mod cmp.mod cat.mod terminal.mod fshelp.mod amiga.mod apple.mod \ pc.mod suspend.mod loopback.mod help.mod reboot.mod halt.mod sun.mod \ - default.mod timeout.mod configfile.mod + default.mod timeout.mod configfile.mod _multiboot.mod multiboot.mod # For fshelp.mod. fshelp_mod_SOURCES = fs/fshelp.c @@ -114,6 +114,14 @@ _linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_SOURCES = loader/powerpc/ieee1275/linux_normal.c linux_mod_CFLAGS = $(COMMON_CFLAGS) +# For _multiboot.mod. +_multiboot_mod_SOURCES = loader/powerpc/ieee1275/multiboot.c +_multiboot_mod_CFLAGS = $(COMMON_CFLAGS) + +# For multiboot.mod. +multiboot_mod_SOURCES = loader/powerpc/ieee1275/multiboot_normal.c +multiboot_mod_CFLAGS = $(COMMON_CFLAGS) + # For normal.mod. normal_mod_SOURCES = normal/arg.c normal/cmdline.c normal/command.c \ normal/context.c normal/main.c normal/menu.c \ Index: include/grub/powerpc/ieee1275/loader.h =================================================================== RCS file: /cvsroot/grub/grub2/include/grub/powerpc/ieee1275/loader.h,v retrieving revision 1.4 diff -u -p -r1.4 loader.h --- include/grub/powerpc/ieee1275/loader.h 31 Jan 2005 21:44:35 -0000 1.4 +++ include/grub/powerpc/ieee1275/loader.h 24 Jun 2005 13:41:25 -0000 @@ -25,6 +25,9 @@ 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[]); + void grub_linux_init (void); void grub_linux_fini (void); void grub_linux_normal_init (void); --- /dev/null 2004-12-18 12:15:49.000000000 -0600 +++ loader/powerpc/ieee1275/multiboot.c 2005-06-23 23:42:48.000000000 -0500 @@ -0,0 +1,400 @@ +/* multiboot.c - boot a multiboot OS image. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003, 2004, 2005 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_dl_t my_mod; +static struct grub_multiboot_info *mbi; +static grub_addr_t entry; + +typedef void (*kernel_entry_t) (struct grub_multiboot_info *, unsigned long, + void *); + +static grub_err_t +grub_multiboot_boot (void) +{ + kernel_entry_t kernel; + + grub_dprintf ("loader", "Jumping to entry point: 0x%x\n", entry); + + kernel = (kernel_entry_t) entry; + kernel (mbi, GRUB_MB_MAGIC2, grub_ieee1275_entry_fn); + + /* Not reached. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_multiboot_unload (void) +{ + if (mbi) + { + unsigned int i; + for (i = 0; i < mbi->mods_count; i++) + { + grub_free ((void *) + ((struct grub_mod_list *) mbi->mods_addr)[i].mod_start); + grub_free ((void *) + ((struct grub_mod_list *) mbi->mods_addr)[i].cmdline); + } + grub_free ((void *) mbi->mods_addr); + grub_free ((void *) mbi->cmdline); + grub_free (mbi); + } + + + mbi = 0; + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +static void +grub_ieee1275_mbi_memory (struct grub_multiboot_info *info) +{ + grub_ieee1275_phandle_t memory; + struct grub_ieee1275_mem_region regions[4]; /* XXX Don't hardcode me. */ + int i; + + /* XXX Examine all memory nodes, not just the first. */ + if (-1 == grub_ieee1275_finddevice ("/memory", &memory)) + return; + + if (0 != grub_ieee1275_get_property (memory, "reg", ®ions, + sizeof regions, 0)) + return; + + info->mem_lower = 0; + info->mem_upper = 0; + + for (i = 0; i < 4; i++) + info->mem_lower += regions[i].size; + + grub_dprintf ("loader", "multiboot.mem_lower = 0x%x\n", info->mem_lower); + + info->flags |= GRUB_MB_INFO_MEMORY; +} + +static void +grub_ieee1275_mbi_cmdline (struct grub_multiboot_info *info, int argc, + char *argv[]) +{ + char *cmdline; + char *p; + int i; + int len; + + for (i = 0, len = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + + cmdline = p = grub_malloc (len); + if (!cmdline) + return; + + for (i = 0; i < argc; i++) + { + p = grub_stpcpy (p, argv[i]); + *(p++) = ' '; + } + + /* Remove the space after the last word. */ + *(--p) = '\0'; + + info->cmdline = (grub_uint32_t) cmdline; + grub_dprintf ("loader", "multiboot.cmdline = 0x%x\n", info->cmdline); + + info->flags |= GRUB_MB_INFO_CMDLINE; +} + +void +grub_rescue_cmd_multiboot (int argc, char *argv[]) +{ + grub_file_t file = 0; + char buffer[GRUB_MB_SEARCH]; + struct grub_multiboot_header *header; + grub_ssize_t len; + grub_size_t size; + int i; + Elf32_Ehdr *ehdr; + + grub_dl_ref (my_mod); + + grub_loader_unset(); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "No kernel specified"); + goto fail; + } + + file = grub_file_open (argv[0]); + if (!file) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Couldn't open file"); + goto fail; + } + + len = grub_file_read (file, buffer, GRUB_MB_SEARCH); + if (len < 32) + { + grub_error (GRUB_ERR_BAD_OS, "File too small"); + goto fail; + } + + /* Look for the multiboot header in the buffer. The header should + be at least 12 bytes and aligned on a 4-byte boundary. */ + for (header = (struct grub_multiboot_header *) buffer; + ((char *) header <= buffer + len - 12) || (header = 0); + header = (struct grub_multiboot_header *) ((char *)header + 4)) + { + if (header->magic == GRUB_MB_MAGIC + && !(header->magic + header->flags + header->checksum)) + break; + } + + if (header == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "No multiboot header found"); + goto fail; + } + + if (header->flags & GRUB_MB_UNSUPPORTED) + { + grub_error (GRUB_ERR_UNKNOWN_OS, "Unsupported flag: 0x%x", header->flags); + goto fail; + } + + ehdr = (Elf32_Ehdr *) buffer; + + if (grub_dl_check_header (ehdr, sizeof(*ehdr))) + { + 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, "invalid ELF file type"); + goto fail; + } + + /* FIXME: Should we support program headers at strange locations? */ + if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > GRUB_MB_SEARCH) + { + grub_error (GRUB_ERR_UNKNOWN_OS, "Program header at a too high offset"); + goto fail; + } + + /* Determine the amount of memory that is required. */ + /* XXX discontiguous segments */ + size = 0; + for (i = 0; i < ehdr->e_phnum; i++) + { + Elf32_Phdr *phdr; + phdr = (Elf32_Phdr *) (buffer + ehdr->e_phoff + i * ehdr->e_phentsize); + + size += phdr->p_memsz; + } + if (-1 == grub_claimmap (ehdr->e_entry, size)) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't claim 0x%x bytes at 0x%x\n", + size, ehdr->e_entry); + goto fail; + } + + entry = ehdr->e_entry; + + /* Load every loadable segment in memory. */ + for (i = 0; i < ehdr->e_phnum; i++) + { + Elf32_Phdr *phdr; + phdr = (Elf32_Phdr *) (buffer + ehdr->e_phoff + i * ehdr->e_phentsize); + + 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; + } + + grub_dprintf ("loader", "Loading segment %d at 0x%x, size 0x%x\n", i, + phdr->p_paddr, phdr->p_filesz); + + if (grub_file_read (file, (void *) phdr->p_paddr, phdr->p_filesz) + != (grub_ssize_t) phdr->p_filesz) + { + grub_error (GRUB_ERR_BAD_OS, "Couldn't read segment from file"); + goto fail; + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((char *) phdr->p_paddr + phdr->p_filesz, 0, + phdr->p_memsz - phdr->p_filesz); + } + } + + mbi = grub_malloc (sizeof (struct grub_multiboot_info)); + if (!mbi) + goto fail; + + mbi->flags = 0; + + grub_ieee1275_mbi_memory (mbi); + grub_ieee1275_mbi_cmdline (mbi, argc, argv); + + mbi->flags |= GRUB_MB_INFO_BOOT_LOADER_NAME; + mbi->boot_loader_name = (grub_uint32_t) grub_strdup (PACKAGE_STRING); + + grub_loader_set (grub_multiboot_boot, grub_multiboot_unload); + + fail: + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_free (mbi); + grub_dl_unref (my_mod); + } +} + + +void +grub_rescue_cmd_module (int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_ssize_t size, len = 0; + char *module = 0, *cmdline = 0, *p; + int i; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified"); + goto fail; + } + + if (!mbi) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "You need to load the multiboot kernel first"); + goto fail; + } + + file = grub_file_open (argv[0]); + if (!file) + goto fail; + + size = grub_file_size (file); + module = grub_memalign (GRUB_MB_MOD_ALIGN, size); + if (!module) + goto fail; + + grub_dprintf ("loader", "Loading module %s at %p, size 0x%x\n", argv[0], + module, size); + + if (grub_file_read (file, module, size) != size) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file"); + goto fail; + } + + for (i = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + + cmdline = p = grub_malloc (len); + if (!cmdline) + goto fail; + + for (i = 0; i < argc; i++) + { + p = grub_stpcpy (p, argv[i]); + *(p++) = ' '; + } + + /* Remove the space after the last word. */ + *(--p) = '\0'; + + if (mbi->flags & GRUB_MB_INFO_MODS) + { + struct grub_mod_list *modlist = (struct grub_mod_list *) mbi->mods_addr; + + modlist = grub_realloc (modlist, (mbi->mods_count + 1) + * sizeof (struct grub_mod_list)); + if (!modlist) + goto fail; + mbi->mods_addr = (grub_uint32_t) modlist; + modlist += mbi->mods_count; + modlist->mod_start = (grub_uint32_t) module; + modlist->mod_end = (grub_uint32_t) module + size; + modlist->cmdline = (grub_uint32_t) cmdline; + modlist->pad = 0; + mbi->mods_count++; + } + else + { + struct grub_mod_list *modlist = grub_malloc (sizeof (struct grub_mod_list)); + if (!modlist) + goto fail; + modlist->mod_start = (grub_uint32_t) module; + modlist->mod_end = (grub_uint32_t) module + size; + modlist->cmdline = (grub_uint32_t) cmdline; + modlist->pad = 0; + mbi->mods_count = 1; + mbi->mods_addr = (grub_uint32_t) modlist; + mbi->flags |= GRUB_MB_INFO_MODS; + } + + fail: + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_free (module); + grub_free (cmdline); + } +} + + +GRUB_MOD_INIT +{ + grub_rescue_register_command ("multiboot", grub_rescue_cmd_multiboot, + "load a multiboot kernel"); + grub_rescue_register_command ("module", grub_rescue_cmd_module, + "load a multiboot module"); + my_mod = mod; +} + +GRUB_MOD_FINI +{ + grub_rescue_unregister_command ("multiboot"); + grub_rescue_unregister_command ("module"); +} --- /dev/null 2004-12-18 12:15:49.000000000 -0600 +++ loader/powerpc/ieee1275/multiboot_normal.c 2005-06-23 21:15:03.000000000 -0500 @@ -0,0 +1,61 @@ +/* multiboot_normal.c - boot another boot loader */ +/* + * 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 +#include +#include +#include + +static grub_err_t +grub_normal_cmd_multiboot (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + grub_rescue_cmd_multiboot (argc, args); + return grub_errno; +} + + +static grub_err_t +grub_normal_cmd_module (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + grub_rescue_cmd_module (argc, args); + return grub_errno; +} + +GRUB_MOD_INIT +{ + (void) mod; /* To stop warning. */ + grub_register_command ("multiboot", grub_normal_cmd_multiboot, + GRUB_COMMAND_FLAG_BOTH | GRUB_COMMAND_FLAG_NO_ARG_PARSE, + "multiboot FILE [ARGS...]", + "Load a multiboot kernel", 0); + + grub_register_command ("module", grub_normal_cmd_module, + GRUB_COMMAND_FLAG_BOTH | GRUB_COMMAND_FLAG_NO_ARG_PARSE, + "module FILE [ARGS...]", + "Load a multiboot module", 0); +} + +GRUB_MOD_FINI +{ + grub_unregister_command ("multiboot"); + grub_unregister_command ("module"); +} --azLHFNyN32YCQGCU Content-Type: application/octet-stream Content-Disposition: attachment; filename="hello-multiboot.tar.bz2" Content-Transfer-Encoding: base64 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 LDgmT1qrcB6wRipA7iJ/8XckU4UJDpiiH2A= --azLHFNyN32YCQGCU--