+2006-04-30 Yoshinori K. Okuji <okuji@enbug.org>
+
+ 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 <marco@gnu.org>
* commands/configfile.c (grub_cmd_source): New function.
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
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
#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
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)
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
#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
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)
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
/* 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;
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;
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
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)
{
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;
--- /dev/null
+/*
+ * 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 */
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);
/* 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
#include <grub/err.h>
#include <grub/types.h>
+/* 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 */
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)
}
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);
}
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;
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)
{
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)
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);
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;
{
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;
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;
}
}
}
#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
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));
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)
{
/* 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,
/*
* 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
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;
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;
}
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) ();
}
--- /dev/null
+/* 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 <grub/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/rescue.h>
+#include <grub/dl.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/efi/chainloader.h>
+
+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);
+}
--- /dev/null
+/* 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 <grub/efi/chainloader.h>
+#include <grub/err.h>
+#include <grub/normal.h>
+#include <grub/dl.h>
+
+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");
+}
}
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:
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;
}
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)
}
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;
}
*/
#include <grub/term.h>
+#include <grub/misc.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/efi/efi.h>
{
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)
{
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
{
status = b->wait_for_event (1, &(i->wait_for_key), &index);
if (status != GRUB_EFI_SUCCESS)
- return -1;
+ return -1;
grub_console_checkkey ();
}