diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index 5f37eb5..37da3dd 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -97,7 +97,8 @@ grub_emu_LDFLAGS = $(LIBCURSES) # Modules. pkglib_MODULES = normal.mod halt.mod reboot.mod suspend.mod cpuid.mod \ - multiboot.mod _multiboot.mod aout.mod serial.mod + multiboot.mod _multiboot.mod aout.mod serial.mod linux.mod \ + _linux.mod # For normal.mod. normal_mod_SOURCES = normal/arg.c normal/cmdline.c normal/command.c \ @@ -151,4 +152,14 @@ serial_mod_SOURCES = term/i386/pc/serial.c serial_mod_CFLAGS = $(COMMON_CFLAGS) serial_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For _linux.mod. +_linux_mod_SOURCES = loader/i386/ieee1275/linux.c +_linux_mod_CFLAGS = $(COMMON_CFLAGS) +_linux_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For linux.mod. +linux_mod_SOURCES = loader/i386/ieee1275/linux_normal.c +linux_mod_CFLAGS = $(COMMON_CFLAGS) +linux_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk diff --git a/include/grub/i386/ieee1275/loader.h b/include/grub/i386/ieee1275/loader.h index 2937bb0..942242b 100644 --- a/include/grub/i386/ieee1275/loader.h +++ b/include/grub/i386/ieee1275/loader.h @@ -27,4 +27,7 @@ void EXPORT_FUNC(grub_multiboot2_real_boot) (grub_addr_t entry, struct grub_multiboot_info *mbi) __attribute__ ((noreturn)); +void grub_rescue_cmd_linux (int argc, char *argv[]); +void grub_rescue_cmd_initrd (int argc, char *argv[]); + #endif /* ! GRUB_LOADER_MACHINE_HEADER */ diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index 444e57f..7a8e006 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -47,6 +47,9 @@ #define GRUB_LINUX_EFI_SIGNATURE \ ('E' << 24 | 'F' << 16 | 'I' << 8 | 'L') +#define GRUB_LINUX_OFW_SIGNATURE \ + (' ' << 24 | 'W' << 16 | 'F' << 8 | 'O') + #ifndef ASM_FILE /* For the Linux/i386 boot protocol version 2.03. */ @@ -154,7 +157,14 @@ struct linux_kernel_params grub_uint8_t hd1_drive_info[0x10]; /* 90 */ grub_uint16_t rom_config_len; /* a0 */ - grub_uint8_t padding6[0x1c0 - 0xa2]; + grub_uint8_t padding6[0xb0 - 0xa2]; + + grub_uint32_t ofw_signature; /* b0 */ + grub_uint32_t ofw_num_items; /* b4 */ + grub_uint32_t ofw_cif_handler; /* b8 */ + grub_uint32_t ofw_idt; /* bc */ + + grub_uint8_t padding7[0x1c0 - 0xc0]; grub_uint32_t efi_signature; /* 1c0 */ grub_uint32_t efi_system_table; /* 1c4 */ @@ -163,15 +173,15 @@ struct linux_kernel_params grub_uint32_t efi_mmap; /* 1d0 */ grub_uint32_t efi_mmap_size; /* 1d4 */ - grub_uint8_t padding7[0x1e0 - 0x1d8]; + grub_uint8_t padding8[0x1e0 - 0x1d8]; grub_uint32_t alt_mem; /* 1e0 */ - grub_uint8_t padding8[0x1e8 - 0x1e4]; + grub_uint8_t padding9[0x1e8 - 0x1e4]; grub_uint32_t mmap_size; /* 1e8 */ - grub_uint8_t padding9[0x1ff - 0x1ec]; + grub_uint8_t padding10[0x1ff - 0x1ec]; grub_uint8_t ps_mouse; /* 1ff */ } __attribute__ ((packed)); diff --git a/include/grub/i386/pc/memory.h b/include/grub/i386/pc/memory.h index 71132b0..a3e3ed7 100644 --- a/include/grub/i386/pc/memory.h +++ b/include/grub/i386/pc/memory.h @@ -76,11 +76,14 @@ /* The data segment of the pseudo real mode. */ #define GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG 0x20 -#ifndef GRUB_MACHINE_IEEE1275 #ifndef ASM_FILE + +#ifndef GRUB_MACHINE_IEEE1275 extern grub_size_t EXPORT_VAR(grub_lower_mem); -extern grub_size_t EXPORT_VAR(grub_upper_mem); #endif + +extern grub_size_t EXPORT_VAR(grub_upper_mem); + #endif #endif /* ! GRUB_MEMORY_MACHINE_HEADER */ diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 463180d..e9fe13d 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -158,7 +158,7 @@ grub_err_t EXPORT_FUNC(grub_devalias_iterate) grub_err_t EXPORT_FUNC(grub_children_iterate) (char *devpath, int (*hook) (struct grub_ieee1275_devalias *alias)); grub_err_t EXPORT_FUNC(grub_available_iterate) - (int (*hook) (grub_uint64_t, grub_uint64_t)); + (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t)); int EXPORT_FUNC(grub_claimmap) (grub_addr_t addr, grub_size_t size); char *EXPORT_FUNC(grub_ieee1275_encode_devname) (const char *path); diff --git a/kern/powerpc/ieee1275/init.c b/kern/powerpc/ieee1275/init.c index 6d08140..89c2b62 100644 --- a/kern/powerpc/ieee1275/init.c +++ b/kern/powerpc/ieee1275/init.c @@ -128,8 +128,8 @@ static void grub_claim_heap (void) { unsigned long total = 0; - auto int heap_init (grub_uint64_t addr, grub_uint64_t len); - int heap_init (grub_uint64_t addr, grub_uint64_t len) + auto int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len); + int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len) { len -= 1; /* Required for some firmware. */ @@ -174,6 +174,31 @@ static void grub_claim_heap (void) grub_available_iterate (heap_init); } +#ifdef __i386__ + +grub_uint32_t grub_upper_mem; + +/* We need to call this before grub_claim_memory. */ +static void +grub_get_extended_memory (void) +{ + auto int NESTED_FUNC_ATTR find_ext_mem (grub_uint64_t addr, grub_uint64_t len); + int NESTED_FUNC_ATTR find_ext_mem (grub_uint64_t addr, grub_uint64_t len) + { + if (addr == 0x100000) + { + grub_upper_mem = len; + return 1; + } + + return 0; + } + + grub_available_iterate (find_ext_mem); +} + +#endif + void grub_machine_init (void) { @@ -182,6 +207,7 @@ grub_machine_init (void) grub_console_init (); #ifdef __i386__ + grub_get_extended_memory (); grub_keyboard_controller_init (); #endif grub_claim_heap (); diff --git a/kern/powerpc/ieee1275/openfw.c b/kern/powerpc/ieee1275/openfw.c index 26ff3d5..ce435d3 100644 --- a/kern/powerpc/ieee1275/openfw.c +++ b/kern/powerpc/ieee1275/openfw.c @@ -147,7 +147,7 @@ nextprop: return 0; } -grub_err_t grub_available_iterate (int (*hook) (grub_uint64_t, grub_uint64_t)) +grub_err_t grub_available_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t)) { grub_ieee1275_phandle_t root; grub_ieee1275_phandle_t memory; diff --git a/loader/i386/ieee1275/linux.c b/loader/i386/ieee1275/linux.c new file mode 100755 index 0000000..bc4567e --- /dev/null +++ b/loader/i386/ieee1275/linux.c @@ -0,0 +1,289 @@ +/* linux.c - boot Linux zImage or bzImage */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_OFW_LINUX_PARAMS_ADDR 0x90000 +#define GRUB_OFW_LINUX_KERNEL_ADDR 0x100000 +#define GRUB_OFW_LINUX_INITRD_ADDR 0x800000 + +#define GRUB_OFW_LINUX_CL_OFFSET 0x1e00 +#define GRUB_OFW_LINUX_CL_LENGTH 0x100 + +static grub_dl_t my_mod; + +static grub_size_t kernel_size; +static char *kernel_addr, *kernel_cmdline; +static grub_size_t initrd_size; + +static grub_err_t +grub_linux_unload (void) +{ + grub_free (kernel_cmdline); + grub_free (kernel_addr); + kernel_cmdline = 0; + kernel_addr = 0; + initrd_size = 0; + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +/* +static int +grub_ieee1275_debug (void) +{ + struct enter_args + { + struct grub_ieee1275_common_hdr common; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0); + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return 0; +} +*/ + +static void +grub_set_bootpath (void) +{ + +} + +static grub_err_t +grub_linux_boot (void) +{ + struct linux_kernel_params *params; + struct linux_kernel_header *lh; + char *prot_code; + char *bootpath; + grub_ssize_t len; + + bootpath = grub_env_get ("root"); + if (bootpath) + grub_ieee1275_set_property (grub_ieee1275_chosen, + "bootpath", bootpath, + grub_strlen (bootpath) + 1, + &len); + + params = (struct linux_kernel_params *) GRUB_OFW_LINUX_PARAMS_ADDR; + lh = (struct linux_kernel_header *) params; + + grub_memset ((char *) params, 0, GRUB_OFW_LINUX_CL_OFFSET); + + params->alt_mem = grub_upper_mem >> 10; + params->ext_mem = params->alt_mem; + + lh->cmd_line_ptr = (char *) + (GRUB_OFW_LINUX_PARAMS_ADDR + GRUB_OFW_LINUX_CL_OFFSET); + + params->cl_magic = GRUB_LINUX_CL_MAGIC; + params->cl_offset = GRUB_OFW_LINUX_CL_OFFSET; + + params->video_width = (grub_getwh () >> 8); + params->video_height = (grub_getwh () & 0xff); + params->font_size = 16; + + params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE; + params->ofw_num_items = 1; + params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn; + params->ofw_idt = 0; + + if (initrd_size) + { + lh->type_of_loader = 1; + lh->ramdisk_image = GRUB_OFW_LINUX_INITRD_ADDR; + lh->ramdisk_size = initrd_size; + } + + if (kernel_cmdline) + grub_strcpy (lh->cmd_line_ptr, kernel_cmdline); + + prot_code = (char *) GRUB_OFW_LINUX_KERNEL_ADDR; + grub_memcpy (prot_code, kernel_addr, kernel_size); + + asm volatile ("movl %0, %%esi" : : "m" (params)); + asm volatile ("movl %%esi, %%esp" : : ); + asm volatile ("movl %0, %%ecx" : : "m" (prot_code)); + asm volatile ("xorl %%ebx, %%ebx" : : ); + asm volatile ("jmp *%%ecx" : : ); + + return GRUB_ERR_NONE; +} + +void +grub_rescue_cmd_linux (int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_kernel_header lh; + grub_uint8_t setup_sects; + grub_size_t real_size, prot_size; + int i; + char *dest; + + 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 *) &lh, sizeof (lh)) != sizeof (lh)) + { + grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux header"); + goto fail; + } + + if ((lh.boot_flag != grub_cpu_to_le16 (0xaa55)) || + (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE))) + { + grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); + goto fail; + } + + setup_sects = lh.setup_sects; + if (! setup_sects) + setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS; + + real_size = setup_sects << GRUB_DISK_SECTOR_BITS; + prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE; + + grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n", + "bzImage", real_size, prot_size); + + grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); + if (grub_errno) + goto fail; + + kernel_cmdline = grub_malloc (GRUB_OFW_LINUX_CL_LENGTH); + if (! kernel_cmdline) + goto fail; + + dest = kernel_cmdline; + for (i = 1; + i < argc + && dest + grub_strlen (argv[i]) + 1 < (kernel_cmdline + + GRUB_OFW_LINUX_CL_LENGTH); + i++) + { + *dest++ = ' '; + dest = grub_stpcpy (dest, argv[i]); + } + + kernel_addr = grub_malloc (prot_size); + if (! kernel_addr) + goto fail; + + kernel_size = prot_size; + if (grub_file_read (file, kernel_addr, prot_size) != (int) prot_size) + grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file"); + + if (grub_errno == GRUB_ERR_NONE) + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); + +fail: + + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_free (kernel_cmdline); + grub_free (kernel_addr); + kernel_cmdline = 0; + kernel_addr = 0; + + grub_dl_unref (my_mod); + } +} + +void +grub_rescue_cmd_initrd (int argc, char *argv[]) +{ + grub_file_t file = 0; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified"); + goto fail; + } + + if (! kernel_addr) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first."); + goto fail; + } + + file = grub_file_open (argv[0]); + if (! file) + goto fail; + + initrd_size = grub_file_size (file); + if (grub_file_read (file, (char *) GRUB_OFW_LINUX_INITRD_ADDR, + initrd_size) != (int) initrd_size) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file"); + goto fail; + } + +fail: + if (file) + grub_file_close (file); +} + +GRUB_MOD_INIT(linux) +{ + grub_rescue_register_command ("linux", + grub_rescue_cmd_linux, + "load linux"); + grub_rescue_register_command ("initrd", + grub_rescue_cmd_initrd, + "load initrd"); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_rescue_unregister_command ("linux"); + grub_rescue_unregister_command ("initrd"); +} diff --git a/loader/i386/ieee1275/linux_normal.c b/loader/i386/ieee1275/linux_normal.c new file mode 100755 index 0000000..69fcebb --- /dev/null +++ b/loader/i386/ieee1275/linux_normal.c @@ -0,0 +1,60 @@ +/* linux_normal.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,2005,2007 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include + +static grub_err_t +grub_normal_linux_command (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + grub_rescue_cmd_linux (argc, args); + return grub_errno; +} + + +static grub_err_t +grub_normal_initrd_command (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + grub_rescue_cmd_initrd (argc, args); + return grub_errno; +} + +GRUB_MOD_INIT(linux_normal) +{ + (void) mod; /* To stop warning. */ + grub_register_command ("linux", grub_normal_linux_command, + GRUB_COMMAND_FLAG_BOTH, + "linux FILE [ARGS...]", + "Load a linux kernel.", 0); + + grub_register_command ("initrd", grub_normal_initrd_command, + GRUB_COMMAND_FLAG_BOTH, + "initrd FILE", + "Load an initrd.", 0); +} + +GRUB_MOD_FINI(linux_normal) +{ + grub_unregister_command ("linux"); + grub_unregister_command ("initrd"); +}