diff --git a/ChangeLog b/ChangeLog index 2dc1b5e4d..7e157220b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,80 @@ +2006-04-30 Yoshinori K. Okuji + + Extend the loader so that GRUB can accept a loader which comes + back to GRUB when a loaded image exits. Also, this change adds + support for a chainloader on EFI. + + * term/efi/console.c: Include grub/misc.h. + (grub_console_checkkey): Display a scan code on the top for + debugging. This will be removed once the EFI port gets stable. + Correct the scan code mapping. + + * kern/efi/mm.c (sort_memory_map): Sort in a descending order to + allocate memory from larger regions, in order to reduce the number + of allocated regions. Otherwise, the MacOSX loader panics. + (filter_memory_map): Avoid less than 1MB for compatibility with + other loaders. + (add_memory_regions): Allocate from the tail of a region, if + possible, to avoid allocating a region near to 1MB, for the MacOSX + loader. + + * kern/efi/init.c (grub_efi_set_prefix): Specify + GRUB_EFI_IMAGE_HANDLE to grub_efi_get_loaded_image. + + * kern/efi/efi.c (grub_efi_get_loaded_image): Accept a new + argument IMAGE_HANDLE and specify it to get a loaded image. + (grub_arch_modules_addr): Specify GRUB_EFI_IMAGE_HANDLE to + grub_efi_get_loaded_image. + (grub_efi_get_filename): Divide the legnth by the size of + grub_efi_char16_t. + (grub_efi_get_device_path): New function. + (grub_efi_print_device_path): Print End Device Path nodes. Divide + the length by the size of grub_efi_char16_t for a file path device + path node. + + * kern/loader.c (grub_loader_noreturn): New variable. + (grub_loader_set): Accept a new argument NORETURN. Set + GRUB_LOADER_NORETURN to NORETURN. + All callers changed. + (grub_loader_boot): If GRUB_LOADER_NORETURN is false, do not call + grub_machine_fini. + + * include/grub/efi/efi.h (grub_efi_get_device_path): New + prototype. + (grub_efi_get_loaded_image): Take an argument to specify an image + handle. + + * include/grub/loader.h (grub_loader_set): Added one more argument + NORETURN. + + * disk/efi/efidisk.c (make_devices): Use grub_efi_get_device_path + instead of grub_efi_open_protocol. + (grub_efidisk_get_device_name): Likewise. + (grub_efidisk_close): Print a newline. + (grub_efidisk_get_device_handle): Fixed to use + GRUB_EFI_DEVICE_PATH_SUBTYPE instead of + GRUB_EFI_DEVICE_PATH_TYPE. + + * disk/efi/efidisk.c (device_path_guid): Moved to ... + * kern/efi/efi.c (device_path_guid): ... here. + + * conf/i386-efi.rmk (pkgdata_MODULES): Added _chain.mod and + chain.mod. + (kernel_mod_HEADERS): Added efi/disk.h. + (_chain_mod_SOURCES): New variable. + (_chain_mod_CFLAGS): Likewise. + (_chain_mod_LDFLAGS): Likewise. + (chain_mod_SOURCES): Likewise. + (chain_mod_CFLAGS): Likewise. + (chain_mod_LDFLAGS): Likewise. + + * DISTLIST: Added include/grub/efi/chainloader.h, + loader/efi/chainloader.c and loader/efi/chainloader_normal.c. + + * include/grub/efi/chainloader.h: New file. + * loader/efi/chainloader.c: Likewise. + * loader/efi/chainloader_normal.c: Likewise. + 2006-04-30 Marco Gerards * commands/configfile.c (grub_cmd_source): New function. diff --git a/DISTLIST b/DISTLIST index 16899fc3a..3aeccc781 100644 --- a/DISTLIST +++ b/DISTLIST @@ -113,6 +113,7 @@ include/grub/tparm.h include/grub/types.h include/grub/video.h include/grub/efi/api.h +include/grub/efi/chainloader.h include/grub/efi/console.h include/grub/efi/console_control.h include/grub/efi/disk.h @@ -198,6 +199,8 @@ kern/sparc64/cache.S kern/sparc64/dl.c kern/sparc64/ieee1275/init.c kern/sparc64/ieee1275/openfw.c +loader/efi/chainloader.c +loader/efi/chainloader_normal.c loader/i386/pc/chainloader.c loader/i386/pc/chainloader_normal.c loader/i386/pc/linux.c diff --git a/conf/i386-efi.mk b/conf/i386-efi.mk index e4393a6e3..29bf011d0 100644 --- a/conf/i386-efi.mk +++ b/conf/i386-efi.mk @@ -111,7 +111,7 @@ genmoddep-util_genmoddep.d: util/genmoddep.c #grub_install_SOURCES = util/efi/pc/grub-install.in # Modules. -pkgdata_MODULES = kernel.mod normal.mod +pkgdata_MODULES = kernel.mod normal.mod _chain.mod chain.mod # For kernel.mod. kernel_mod_EXPORTS = no @@ -612,7 +612,7 @@ fs-kernel_mod-disk_efi_efidisk.lst: disk/efi/efidisk.c genfslist.sh kernel_mod_HEADERS = arg.h boot.h device.h disk.h dl.h elf.h env.h err.h \ file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h partition.h \ pc_partition.h rescue.h symbol.h term.h types.h \ - i386/efi/time.h efi/efi.h efi/time.h + i386/efi/time.h efi/efi.h efi/time.h efi/disk.h kernel_mod_CFLAGS = $(COMMON_CFLAGS) kernel_mod_ASFLAGS = $(COMMON_ASFLAGS) kernel_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -954,4 +954,116 @@ normal_mod_CFLAGS = $(COMMON_CFLAGS) normal_mod_ASFLAGS = $(COMMON_ASFLAGS) normal_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For _chain.mod. +_chain_mod_SOURCES = loader/efi/chainloader.c +CLEANFILES += _chain.mod mod-_chain.o mod-_chain.c pre-_chain.o _chain_mod-loader_efi_chainloader.o und-_chain.lst +ifneq ($(_chain_mod_EXPORTS),no) +CLEANFILES += def-_chain.lst +DEFSYMFILES += def-_chain.lst +endif +MOSTLYCLEANFILES += _chain_mod-loader_efi_chainloader.d +UNDSYMFILES += und-_chain.lst + +_chain.mod: pre-_chain.o mod-_chain.o + -rm -f $@ + $(CC) $(_chain_mod_LDFLAGS) $(LDFLAGS) -Wl,-r,-d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-_chain.o: _chain_mod-loader_efi_chainloader.o + -rm -f $@ + $(CC) $(_chain_mod_LDFLAGS) $(LDFLAGS) -Wl,-r,-d -o $@ $^ + +mod-_chain.o: mod-_chain.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -c -o $@ $< + +mod-_chain.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh '_chain' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(_chain_mod_EXPORTS),no) +def-_chain.lst: pre-_chain.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 _chain/' > $@ +endif + +und-_chain.lst: pre-_chain.o + echo '_chain' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +_chain_mod-loader_efi_chainloader.o: loader/efi/chainloader.c + $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -c -o $@ $< + +_chain_mod-loader_efi_chainloader.d: loader/efi/chainloader.c + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -M $< | sed 's,chainloader\.o[ :]*,_chain_mod-loader_efi_chainloader.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include _chain_mod-loader_efi_chainloader.d + +CLEANFILES += cmd-_chain_mod-loader_efi_chainloader.lst fs-_chain_mod-loader_efi_chainloader.lst +COMMANDFILES += cmd-_chain_mod-loader_efi_chainloader.lst +FSFILES += fs-_chain_mod-loader_efi_chainloader.lst + +cmd-_chain_mod-loader_efi_chainloader.lst: loader/efi/chainloader.c gencmdlist.sh + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh _chain > $@ || (rm -f $@; exit 1) + +fs-_chain_mod-loader_efi_chainloader.lst: loader/efi/chainloader.c genfslist.sh + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh _chain > $@ || (rm -f $@; exit 1) + + +_chain_mod_CFLAGS = $(COMMON_CFLAGS) +_chain_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For chain.mod. +chain_mod_SOURCES = loader/efi/chainloader_normal.c +CLEANFILES += chain.mod mod-chain.o mod-chain.c pre-chain.o chain_mod-loader_efi_chainloader_normal.o und-chain.lst +ifneq ($(chain_mod_EXPORTS),no) +CLEANFILES += def-chain.lst +DEFSYMFILES += def-chain.lst +endif +MOSTLYCLEANFILES += chain_mod-loader_efi_chainloader_normal.d +UNDSYMFILES += und-chain.lst + +chain.mod: pre-chain.o mod-chain.o + -rm -f $@ + $(CC) $(chain_mod_LDFLAGS) $(LDFLAGS) -Wl,-r,-d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-chain.o: chain_mod-loader_efi_chainloader_normal.o + -rm -f $@ + $(CC) $(chain_mod_LDFLAGS) $(LDFLAGS) -Wl,-r,-d -o $@ $^ + +mod-chain.o: mod-chain.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -c -o $@ $< + +mod-chain.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'chain' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(chain_mod_EXPORTS),no) +def-chain.lst: pre-chain.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 chain/' > $@ +endif + +und-chain.lst: pre-chain.o + echo 'chain' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +chain_mod-loader_efi_chainloader_normal.o: loader/efi/chainloader_normal.c + $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -c -o $@ $< + +chain_mod-loader_efi_chainloader_normal.d: loader/efi/chainloader_normal.c + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -M $< | sed 's,chainloader_normal\.o[ :]*,chain_mod-loader_efi_chainloader_normal.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include chain_mod-loader_efi_chainloader_normal.d + +CLEANFILES += cmd-chain_mod-loader_efi_chainloader_normal.lst fs-chain_mod-loader_efi_chainloader_normal.lst +COMMANDFILES += cmd-chain_mod-loader_efi_chainloader_normal.lst +FSFILES += fs-chain_mod-loader_efi_chainloader_normal.lst + +cmd-chain_mod-loader_efi_chainloader_normal.lst: loader/efi/chainloader_normal.c gencmdlist.sh + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh chain > $@ || (rm -f $@; exit 1) + +fs-chain_mod-loader_efi_chainloader_normal.lst: loader/efi/chainloader_normal.c genfslist.sh + set -e; $(CC) -Iloader/efi -I$(srcdir)/loader/efi $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh chain > $@ || (rm -f $@; exit 1) + + +chain_mod_CFLAGS = $(COMMON_CFLAGS) +chain_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index f42fb6ea4..b1d342f36 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -67,7 +67,7 @@ genmoddep_SOURCES = util/genmoddep.c #grub_install_SOURCES = util/efi/pc/grub-install.in # Modules. -pkgdata_MODULES = kernel.mod normal.mod +pkgdata_MODULES = kernel.mod normal.mod _chain.mod chain.mod # For kernel.mod. kernel_mod_EXPORTS = no @@ -80,7 +80,7 @@ kernel_mod_SOURCES = kern/i386/efi/startup.S kern/main.c kern/device.c \ kernel_mod_HEADERS = arg.h boot.h device.h disk.h dl.h elf.h env.h err.h \ file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h partition.h \ pc_partition.h rescue.h symbol.h term.h types.h \ - i386/efi/time.h efi/efi.h efi/time.h + i386/efi/time.h efi/efi.h efi/time.h efi/disk.h kernel_mod_CFLAGS = $(COMMON_CFLAGS) kernel_mod_ASFLAGS = $(COMMON_ASFLAGS) kernel_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -105,4 +105,14 @@ normal_mod_CFLAGS = $(COMMON_CFLAGS) normal_mod_ASFLAGS = $(COMMON_ASFLAGS) normal_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For _chain.mod. +_chain_mod_SOURCES = loader/efi/chainloader.c +_chain_mod_CFLAGS = $(COMMON_CFLAGS) +_chain_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For chain.mod. +chain_mod_SOURCES = loader/efi/chainloader_normal.c +chain_mod_CFLAGS = $(COMMON_CFLAGS) +chain_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk diff --git a/disk/efi/efidisk.c b/disk/efi/efidisk.c index 2daf3d39f..8b8b9fa8d 100644 --- a/disk/efi/efidisk.c +++ b/disk/efi/efidisk.c @@ -41,7 +41,6 @@ struct grub_efidisk_data /* GUIDs. */ static grub_efi_guid_t disk_io_guid = GRUB_EFI_DISK_IO_GUID; static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID; -static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; static struct grub_efidisk_data *fd_devices; static struct grub_efidisk_data *hd_devices; @@ -159,8 +158,7 @@ make_devices (void) grub_efi_block_io_t *bio; grub_efi_disk_io_t *dio; - dp = grub_efi_open_protocol (*handle, &device_path_guid, - GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + dp = grub_efi_get_device_path (*handle); if (! dp) continue; @@ -556,7 +554,7 @@ static void grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused))) { /* EFI disks do not allocate extra memory, so nothing to do here. */ - grub_dprintf ("efidisk", "closing %s", disk->name); + grub_dprintf ("efidisk", "closing %s\n", disk->name); } static grub_err_t @@ -683,7 +681,7 @@ grub_efidisk_get_device_handle (grub_disk_t disk) if ((GRUB_EFI_DEVICE_PATH_TYPE (c->last_device_path) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE) - && (GRUB_EFI_DEVICE_PATH_TYPE (c->last_device_path) + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c->last_device_path) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE) && (grub_partition_get_start (disk->partition) == hd.partition_start) @@ -718,8 +716,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle) { grub_efi_device_path_t *dp, *ldp; - dp = grub_efi_open_protocol (handle, &device_path_guid, - GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + dp = grub_efi_get_device_path (handle); if (! dp) return 0; diff --git a/include/grub/efi/chainloader.h b/include/grub/efi/chainloader.h new file mode 100644 index 000000000..6cdb20572 --- /dev/null +++ b/include/grub/efi/chainloader.h @@ -0,0 +1,25 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006 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. + */ + +#ifndef GRUB_EFI_CHAINLOADER_HEADER +#define GRUB_EFI_CHAINLOADER_HEADER 1 + +void grub_chainloader_cmd (const char *filename); + +#endif /* ! GRUB_EFI_CHAINLOADER_HEADER */ diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 17778fb66..f28e39241 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -49,9 +49,11 @@ EXPORT_FUNC(grub_efi_get_memory_map) (grub_efi_uintn_t *memory_map_size, grub_efi_uintn_t *map_key, grub_efi_uintn_t *descriptor_size, grub_efi_uint32_t *descriptor_version); -grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (void); +grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (grub_efi_handle_t image_handle); void EXPORT_FUNC(grub_efi_print_device_path) (grub_efi_device_path_t *dp); char *EXPORT_FUNC(grub_efi_get_filename) (grub_efi_device_path_t *dp); +grub_efi_device_path_t * +EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t handle); void grub_efi_mm_init (void); void grub_efi_mm_fini (void); diff --git a/include/grub/loader.h b/include/grub/loader.h index fee758a42..54622f520 100644 --- a/include/grub/loader.h +++ b/include/grub/loader.h @@ -1,7 +1,7 @@ /* loader.h - OS loaders */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2004,2006 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 @@ -26,11 +26,20 @@ #include #include +/* Check if a loader is loaded. */ int EXPORT_FUNC(grub_loader_is_loaded) (void); + +/* Set loader functions. NORETURN must be set to true, if BOOT won't return + to the original state. */ void EXPORT_FUNC(grub_loader_set) (grub_err_t (*boot) (void), - grub_err_t (*unload) (void)); + grub_err_t (*unload) (void), + int noreturn); + +/* Unset current loader, if any. */ void EXPORT_FUNC(grub_loader_unset) (void); +/* Call the boot hook in current loader. This may or may not return, + depending on the setting by grub_loader_set. */ grub_err_t EXPORT_FUNC(grub_loader_boot) (void); #endif /* ! GRUB_LOADER_HEADER */ diff --git a/kern/efi/efi.c b/kern/efi/efi.c index d41b67b47..12ecc9f12 100644 --- a/kern/efi/efi.c +++ b/kern/efi/efi.c @@ -36,6 +36,7 @@ grub_efi_system_table_t *grub_efi_system_table; static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; +static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; void * grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration) @@ -144,9 +145,9 @@ grub_efi_stall (grub_efi_uintn_t microseconds) } grub_efi_loaded_image_t * -grub_efi_get_loaded_image (void) +grub_efi_get_loaded_image (grub_efi_handle_t image_handle) { - return grub_efi_open_protocol (grub_efi_image_handle, + return grub_efi_open_protocol (image_handle, &loaded_image_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); } @@ -189,7 +190,7 @@ grub_arch_modules_addr (void) struct grub_module_info *info; grub_uint16_t i; - image = grub_efi_get_loaded_image (); + image = grub_efi_get_loaded_image (grub_efi_image_handle); if (! image) return 0; @@ -248,7 +249,8 @@ grub_efi_get_filename (grub_efi_device_path_t *dp) else size = 0; - len = GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4; + len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4) + / sizeof (grub_efi_char16_t)); p = grub_realloc (name, size + len * 4 + 1); if (! p) { @@ -278,6 +280,13 @@ grub_efi_get_filename (grub_efi_device_path_t *dp) return name; } +grub_efi_device_path_t * +grub_efi_get_device_path (grub_efi_handle_t handle) +{ + return grub_efi_open_protocol (handle, &device_path_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + /* Print the chain of Device Path nodes. This is mainly for debugging. */ void grub_efi_print_device_path (grub_efi_device_path_t *dp) @@ -294,12 +303,12 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) switch (subtype) { case GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE: - /* grub_printf ("/EndEntire\n"); */ - grub_putchar ('\n'); + grub_printf ("/EndEntire\n"); + //grub_putchar ('\n'); break; case GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE: - /* grub_printf ("/EndThis\n"); */ - grub_putchar ('\n'); + grub_printf ("/EndThis\n"); + //grub_putchar ('\n'); break; default: grub_printf ("/EndUnknown(%x)\n", (unsigned) subtype); @@ -633,9 +642,11 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) case GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE: { grub_efi_file_path_device_path_t *fp; - grub_uint8_t buf[(len - 4) * 4 + 1]; + grub_uint8_t buf[(len - 4) * 2 + 1]; fp = (grub_efi_file_path_device_path_t *) dp; - *grub_utf16_to_utf8 (buf, fp->path_name, len - 4) = '\0'; + *grub_utf16_to_utf8 (buf, fp->path_name, + (len - 4) / sizeof (grub_efi_char16_t)) + = '\0'; grub_printf ("/File(%s)", buf); } break; diff --git a/kern/efi/init.c b/kern/efi/init.c index 23ca144a0..c74213d33 100644 --- a/kern/efi/init.c +++ b/kern/efi/init.c @@ -46,7 +46,7 @@ grub_efi_set_prefix (void) { grub_efi_loaded_image_t *image; - image = grub_efi_get_loaded_image (); + image = grub_efi_get_loaded_image (grub_efi_image_handle); if (image) { char *device; diff --git a/kern/efi/mm.c b/kern/efi/mm.c index 534432c4e..2715efef4 100644 --- a/kern/efi/mm.c +++ b/kern/efi/mm.c @@ -184,23 +184,23 @@ sort_memory_map (grub_efi_memory_descriptor_t *memory_map, d1 < memory_map_end; d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size)) { - grub_efi_memory_descriptor_t *min_desc = d1; + grub_efi_memory_descriptor_t *max_desc = d1; for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size); d2 < memory_map_end; d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size)) { - if (min_desc->num_pages > d2->num_pages) - min_desc = d2; + if (max_desc->num_pages < d2->num_pages) + max_desc = d2; } - if (min_desc != d1) + if (max_desc != d1) { grub_efi_memory_descriptor_t tmp; tmp = *d1; - *d1 = *min_desc; - *min_desc = tmp; + *d1 = *max_desc; + *max_desc = tmp; } } } @@ -223,16 +223,17 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, #if GRUB_HOST_SIZEOF_VOID_P < 8 && desc->physical_start <= 0xffffffff #endif + && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 && desc->num_pages != 0) { grub_memcpy (filtered_desc, desc, desc_size); - /* Avoid the page at the address zero, because this is really - confusing for C programs. */ - if (filtered_desc->physical_start == 0) + /* Avoid less than 1MB, because some loaders seem to be confused. */ + if (desc->physical_start < 0x100000) { - filtered_desc->physical_start = 0x1000; - filtered_desc->num_pages--; + desc->num_pages -= BYTES_TO_PAGES (0x100000 + - desc->physical_start); + desc->physical_start = 0x100000; } #if GRUB_HOST_SIZEOF_VOID_P < 8 @@ -285,16 +286,21 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map, desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) { grub_efi_uint64_t pages; + grub_efi_physical_address_t start; void *addr; - + + start = desc->physical_start; pages = desc->num_pages; if (pages > required_pages) - pages = required_pages; + { + start += PAGES_TO_BYTES (pages - required_pages); + pages = required_pages; + } - addr = grub_efi_allocate_pages (desc->physical_start, pages); + addr = grub_efi_allocate_pages (start, pages); if (! addr) grub_fatal ("cannot allocate conventional memory %p with %u pages", - (void *) ((grub_addr_t) desc->physical_start), + (void *) ((grub_addr_t) start), (unsigned) pages); grub_mm_init_region (addr, PAGES_TO_BYTES (pages)); @@ -308,6 +314,27 @@ add_memory_regions (grub_efi_memory_descriptor_t *memory_map, grub_fatal ("too little memory"); } +#if 0 +/* Print the memory map. */ +static void +print_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + int i; + + for (desc = memory_map, i = 0; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++) + { + grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n", + desc->type, desc->physical_start, desc->virtual_start, + desc->num_pages, desc->attribute); + } +} +#endif + void grub_efi_mm_init (void) { @@ -363,6 +390,19 @@ grub_efi_mm_init (void) /* Allocate memory regions for GRUB's memory management. */ add_memory_regions (filtered_memory_map, desc_size, filtered_memory_map_end, required_pages); + +#if 0 + /* For debug. */ + map_size = MEMORY_MAP_SIZE; + + if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0) + grub_fatal ("cannot get memory map"); + + grub_printf ("printing memory map\n"); + print_memory_map (memory_map, desc_size, + NEXT_MEMORY_DESCRIPTOR (memory_map, map_size)); + grub_abort (); +#endif /* Release the memory maps. */ grub_efi_free_pages ((grub_addr_t) memory_map, diff --git a/kern/loader.c b/kern/loader.c index 772ca66b9..b799c8bcd 100644 --- a/kern/loader.c +++ b/kern/loader.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2004,2006 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 @@ -25,6 +25,7 @@ static grub_err_t (*grub_loader_boot_func) (void); static grub_err_t (*grub_loader_unload_func) (void); +static int grub_loader_noreturn; static int grub_loader_loaded; @@ -36,14 +37,16 @@ grub_loader_is_loaded (void) void grub_loader_set (grub_err_t (*boot) (void), - grub_err_t (*unload) (void)) + grub_err_t (*unload) (void), + int noreturn) { if (grub_loader_loaded && grub_loader_unload_func) grub_loader_unload_func (); grub_loader_boot_func = boot; grub_loader_unload_func = unload; - + grub_loader_noreturn = noreturn; + grub_loader_loaded = 1; } @@ -65,7 +68,8 @@ grub_loader_boot (void) if (! grub_loader_loaded) return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel"); - grub_machine_fini (); + if (grub_loader_noreturn) + grub_machine_fini (); return (grub_loader_boot_func) (); } diff --git a/loader/efi/chainloader.c b/loader/efi/chainloader.c new file mode 100644 index 000000000..3e0df8e70 --- /dev/null +++ b/loader/efi/chainloader.c @@ -0,0 +1,315 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2006 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. + */ + +/* TODO: support load options. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_dl_t my_mod; + +static grub_efi_physical_address_t address; +static grub_efi_uintn_t pages; +static grub_efi_device_path_t *file_path; +static grub_efi_handle_t image_handle; + +static grub_err_t +grub_chainloader_unload (void) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + b->unload_image (image_handle); + b->free_pages (address, pages); + grub_free (file_path); + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_chainloader_boot (void) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_uintn_t exit_data_size; + grub_efi_char16_t *exit_data; + + b = grub_efi_system_table->boot_services; + status = b->start_image (image_handle, &exit_data_size, &exit_data); + if (status != GRUB_EFI_SUCCESS) + { + if (exit_data) + { + char *buf; + + buf = grub_malloc (exit_data_size * 4 + 1); + if (buf) + { + *grub_utf16_to_utf8 ((grub_uint8_t *) buf, + exit_data, exit_data_size) = 0; + + grub_error (GRUB_ERR_BAD_OS, buf); + grub_free (buf); + } + else + grub_error (GRUB_ERR_BAD_OS, "unknown error"); + } + } + + if (exit_data) + b->free_pool (exit_data); + + grub_chainloader_unload (); + + return grub_errno; +} + +static void +copy_file_path (grub_efi_file_path_device_path_t *fp, + const char *str, grub_efi_uint16_t len) +{ + grub_efi_char16_t *p; + grub_efi_uint16_t size; + + fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE; + fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE; + size = len * sizeof (grub_efi_char16_t) + sizeof (*fp); + fp->header.length[0] = (grub_efi_uint8_t) (size & 0xff); + fp->header.length[1] = (grub_efi_uint8_t) (size >> 8); + for (p = fp->path_name; len > 0; len--, p++, str++) + { + /* FIXME: this assumes that the path is in ASCII. */ + *p = (grub_efi_char16_t) (*str == '/' ? '\\' : *str); + } +} + +static grub_efi_device_path_t * +make_file_path (grub_efi_device_path_t *dp, const char *filename) +{ + char *dir_start; + char *dir_end; + grub_size_t size; + grub_efi_device_path_t *d; + + dir_start = grub_strchr (filename, ')'); + if (! dir_start) + dir_start = (char *) filename; + else + dir_start++; + + dir_end = grub_strrchr (dir_start, '/'); + if (! dir_end) + { + grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path"); + return 0; + } + + size = 0; + d = dp; + while (1) + { + size += GRUB_EFI_DEVICE_PATH_LENGTH (d); + if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d))) + break; + d = GRUB_EFI_NEXT_DEVICE_PATH (d); + } + + file_path = grub_malloc (size + + ((grub_strlen (dir_start) + 1) + * sizeof (grub_efi_char16_t)) + + sizeof (grub_efi_file_path_device_path_t) * 2); + if (! file_path) + return 0; + + grub_memcpy (file_path, dp, size); + + /* Fill the file path for the directory. */ + d = (grub_efi_device_path_t *) ((char *) file_path + + ((char *) d - (char *) dp)); + grub_efi_print_device_path (d); + copy_file_path ((grub_efi_file_path_device_path_t *) d, + dir_start, dir_end - dir_start); + + /* Fill the file path for the file. */ + d = GRUB_EFI_NEXT_DEVICE_PATH (d); + copy_file_path ((grub_efi_file_path_device_path_t *) d, + dir_end + 1, grub_strlen (dir_end + 1)); + + /* Fill the end of device path nodes. */ + d = GRUB_EFI_NEXT_DEVICE_PATH (d); + d->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + d->length[0] = sizeof (*d); + d->length[1] = 0; + + return file_path; +} + +void +grub_chainloader_cmd (const char *filename) +{ + grub_file_t file = 0; + grub_ssize_t size; + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_handle_t dev_handle = 0; + grub_device_t dev = 0; + grub_efi_device_path_t *dp = 0; + grub_efi_loaded_image_t *loaded_image; + + grub_dl_ref (my_mod); + + /* Initialize some global variables. */ + address = 0; + image_handle = 0; + file_path = 0; + + b = grub_efi_system_table->boot_services; + + file = grub_file_open (filename); + if (! file) + goto fail; + + /* Get the root device's device path. */ + dev = grub_device_open (0); + if (! dev) + goto fail; + + if (dev->disk) + { + dev_handle = grub_efidisk_get_device_handle (dev->disk); + if (dev_handle) + dp = grub_efi_get_device_path (dev_handle); + } + + if (! dev->disk || ! dev_handle || ! dp) + { + grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device"); + goto fail; + } + + file_path = make_file_path (dp, filename); + if (! file_path) + goto fail; + + grub_printf ("file path: "); + grub_efi_print_device_path (file_path); + + size = grub_file_size (file); + pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12); + + status = b->allocate_pages (GRUB_EFI_ALLOCATE_ANY_PAGES, + GRUB_EFI_LOADER_CODE, + pages, &address); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate %u pages", pages); + goto fail; + } + + if (grub_file_read (file, (void *) ((grub_addr_t) address), size) != size) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_OS, "too small"); + + goto fail; + } + + status = b->load_image (0, grub_efi_image_handle, file_path, + (void *) ((grub_addr_t) address), size, + &image_handle); + if (status != GRUB_EFI_SUCCESS) + { + if (status == GRUB_EFI_OUT_OF_RESOURCES) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources"); + else + grub_error (GRUB_ERR_BAD_OS, "cannot load image"); + + goto fail; + } + + /* LoadImage does not set a device handler when the image is + loaded from memory, so it is necessary to set it explicitly here. + This is a mess. */ + loaded_image = grub_efi_get_loaded_image (image_handle); + if (! loaded_image) + { + grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); + goto fail; + } + loaded_image->device_handle = dev_handle; + + grub_file_close (file); + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); + return; + + fail: + + if (dev) + grub_device_close (dev); + + if (file) + grub_file_close (file); + + if (file_path) + grub_free (file_path); + + if (address) + b->free_pages (address, pages); + + grub_dl_unref (my_mod); +} + +static void +grub_rescue_cmd_chainloader (int argc, char *argv[]) +{ + if (argc == 0) + grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified"); + else + grub_chainloader_cmd (argv[0]); +} + +static const char loader_name[] = "chainloader"; + +GRUB_MOD_INIT(chainloader) +{ + grub_rescue_register_command (loader_name, + grub_rescue_cmd_chainloader, + "load another boot loader"); + my_mod = mod; +} + +GRUB_MOD_FINI(chainloader) +{ + grub_rescue_unregister_command (loader_name); +} diff --git a/loader/efi/chainloader_normal.c b/loader/efi/chainloader_normal.c new file mode 100644 index 000000000..517b3b2f7 --- /dev/null +++ b/loader/efi/chainloader_normal.c @@ -0,0 +1,49 @@ +/* chainloader_normal.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,2006 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 +chainloader_command (struct grub_arg_list *state __attribute__ ((unused)), + int argc, char **args) +{ + if (argc == 0) + grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified"); + else + grub_chainloader_cmd (args[0]); + return grub_errno; +} + +GRUB_MOD_INIT(chainloader_normal) +{ + (void) mod; /* To stop warning. */ + grub_register_command ("chainloader", chainloader_command, + GRUB_COMMAND_FLAG_BOTH, + "chainloader FILE", + "Prepare to boot another boot loader.", 0); +} + +GRUB_MOD_FINI(chainloader_normal) +{ + grub_unregister_command ("chainloader"); +} diff --git a/loader/i386/pc/chainloader.c b/loader/i386/pc/chainloader.c index 2ac94bf1d..e479dc69f 100644 --- a/loader/i386/pc/chainloader.c +++ b/loader/i386/pc/chainloader.c @@ -114,7 +114,7 @@ grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags) } grub_file_close (file); - grub_loader_set (grub_chainloader_boot, grub_chainloader_unload); + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1); return; fail: diff --git a/loader/i386/pc/linux.c b/loader/i386/pc/linux.c index f1a18a65a..da62f52b1 100644 --- a/loader/i386/pc/linux.c +++ b/loader/i386/pc/linux.c @@ -275,7 +275,7 @@ grub_rescue_cmd_linux (int argc, char *argv[]) if (grub_errno == GRUB_ERR_NONE) { grub_linux_prot_size = prot_size; - grub_loader_set (grub_linux_boot, grub_linux_unload); + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); loaded = 1; } diff --git a/loader/i386/pc/multiboot.c b/loader/i386/pc/multiboot.c index 114b27bb1..82dc428f1 100644 --- a/loader/i386/pc/multiboot.c +++ b/loader/i386/pc/multiboot.c @@ -326,7 +326,7 @@ grub_rescue_cmd_multiboot (int argc, char *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); + grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 1); fail: if (file) diff --git a/loader/powerpc/ieee1275/linux.c b/loader/powerpc/ieee1275/linux.c index b69ba5c37..790b3731b 100644 --- a/loader/powerpc/ieee1275/linux.c +++ b/loader/powerpc/ieee1275/linux.c @@ -250,7 +250,7 @@ grub_rescue_cmd_linux (int argc, char *argv[]) } else { - grub_loader_set (grub_linux_boot, grub_linux_unload); + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); initrd_addr = 0; loaded = 1; } diff --git a/term/efi/console.c b/term/efi/console.c index e4de7f59a..d97892f43 100644 --- a/term/efi/console.c +++ b/term/efi/console.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -70,12 +71,40 @@ grub_console_checkkey (void) { grub_efi_simple_input_interface_t *i; grub_efi_input_key_t key; + grub_efi_status_t status; if (read_key >= 0) return 1; i = grub_efi_system_table->con_in; - if (i->read_key_stroke (i, &key) == GRUB_EFI_SUCCESS) + status = i->read_key_stroke (i, &key); +#if 1 + switch (status) + { + case GRUB_EFI_SUCCESS: + { + grub_uint16_t xy; + + xy = grub_getxy (); + grub_gotoxy (0, 0); + grub_printf ("scan_code=%x,unicode_char=%x ", + (unsigned) key.scan_code, + (unsigned) key.unicode_char); + grub_gotoxy (xy >> 8, xy & 0xff); + } + break; + + case GRUB_EFI_NOT_READY: + //grub_printf ("not ready "); + break; + + default: + //grub_printf ("device error "); + break; + } +#endif + + if (status == GRUB_EFI_SUCCESS) { switch (key.scan_code) { @@ -83,45 +112,41 @@ grub_console_checkkey (void) read_key = key.unicode_char; break; case 0x01: - read_key = GRUB_CONSOLE_KEY_UP; + read_key = 16; break; case 0x02: - read_key = GRUB_CONSOLE_KEY_DOWN; + read_key = 14; break; case 0x03: - read_key = GRUB_CONSOLE_KEY_RIGHT; + read_key = 6; break; case 0x04: - read_key = GRUB_CONSOLE_KEY_LEFT; + read_key = 2; break; case 0x05: - read_key = GRUB_CONSOLE_KEY_HOME; + read_key = 1; break; case 0x06: - read_key = GRUB_CONSOLE_KEY_END; + read_key = 5; break; case 0x07: - read_key = GRUB_CONSOLE_KEY_IC; break; case 0x08: - read_key = GRUB_CONSOLE_KEY_DC; + read_key = 4; break; case 0x09: - read_key = GRUB_CONSOLE_KEY_PPAGE; break; case 0x0a: - read_key = GRUB_CONSOLE_KEY_NPAGE; + break; case 0x17: read_key = '\e'; break; default: - return 0; + break; } - - return 1; } - return 0; + return read_key >= 0; } static int @@ -147,7 +172,7 @@ grub_console_getkey (void) { status = b->wait_for_event (1, &(i->wait_for_key), &index); if (status != GRUB_EFI_SUCCESS) - return -1; + return -1; grub_console_checkkey (); }