diff --git a/ChangeLog b/ChangeLog index d814309fa..d1bc0683f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2004-07-27 Marco Gerards + + * 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. + 2004-07-12 Marco Gerards * normal/arg.c (grub_arg_parse): Correct error handling after diff --git a/boot/powerpc/ieee1275/ieee1275.c b/boot/powerpc/ieee1275/ieee1275.c index 09839616c..580e82984 100644 --- a/boot/powerpc/ieee1275/ieee1275.c +++ b/boot/powerpc/ieee1275/ieee1275.c @@ -419,6 +419,25 @@ grub_ieee1275_claim (void *p, grub_size_t size, return 0; } +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, diff --git a/commands/boot.c b/commands/boot.c index 3bab54668..7d2301bd6 100644 --- a/commands/boot.c +++ b/commands/boot.c @@ -37,7 +37,7 @@ grub_cmd_boot (struct grub_arg_list *state __attribute__ ((unused)), } -#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 --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 913bc1662..cdb431316 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -41,7 +41,7 @@ grubof_SOURCES = boot/powerpc/ieee1275/cmain.c boot/powerpc/ieee1275/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 --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h index 7f71fe98d..ef3bb80c7 100644 --- a/include/grub/powerpc/ieee1275/ieee1275.h +++ b/include/grub/powerpc/ieee1275/ieee1275.h @@ -84,6 +84,7 @@ int EXPORT_FUNC(grub_ieee1275_open) (char *node, 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 --git a/include/grub/powerpc/ieee1275/loader.h b/include/grub/powerpc/ieee1275/loader.h index 29a23e73e..ddc0f406f 100644 --- a/include/grub/powerpc/ieee1275/loader.h +++ b/include/grub/powerpc/ieee1275/loader.h @@ -1,7 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002 Yoshinori K. Okuji - * Copyright (C) 2003 Jeroen Dekkers + * 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 -#include -#include +/* The symbol shared between the normal mode and rescue mode + loader. */ +void grub_load_linux (int argc, char *argv[]); -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[]); +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 --git a/kern/powerpc/ieee1275/init.c b/kern/powerpc/ieee1275/init.c index 693cdf75c..7cf5e9e86 100644 --- a/kern/powerpc/ieee1275/init.c +++ b/kern/powerpc/ieee1275/init.c @@ -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 --git a/loader/powerpc/ieee1275/linux.c b/loader/powerpc/ieee1275/linux.c new file mode 100644 index 000000000..eacafa57d --- /dev/null +++ b/loader/powerpc/ieee1275/linux.c @@ -0,0 +1,283 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +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; +} + + +GRUB_MOD_INIT +{ + grub_rescue_register_command ("linux", grub_load_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 --git a/loader/powerpc/ieee1275/linux_normal.c b/loader/powerpc/ieee1275/linux_normal.c new file mode 100644 index 000000000..b18177861 --- /dev/null +++ b/loader/powerpc/ieee1275/linux_normal.c @@ -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 +#include +#include + +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"); +}