diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:29:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:29:51 +0000 |
commit | 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e (patch) | |
tree | 32451fa3cdd9321fb2591fada9891b2cb70a9cd1 /grub-core/loader/efi | |
parent | Initial commit. (diff) | |
download | grub2-6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e.tar.xz grub2-6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e.zip |
Adding upstream version 2.06.upstream/2.06upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'grub-core/loader/efi')
-rw-r--r-- | grub-core/loader/efi/appleloader.c | 242 | ||||
-rw-r--r-- | grub-core/loader/efi/chainloader.c | 444 | ||||
-rw-r--r-- | grub-core/loader/efi/fdt.c | 179 |
3 files changed, 865 insertions, 0 deletions
diff --git a/grub-core/loader/efi/appleloader.c b/grub-core/loader/efi/appleloader.c new file mode 100644 index 0000000..74888c4 --- /dev/null +++ b/grub-core/loader/efi/appleloader.c @@ -0,0 +1,242 @@ +/* appleloader.c - apple legacy boot loader. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/loader.h> +#include <grub/err.h> +#include <grub/mm.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> +#include <grub/command.h> +#include <grub/i18n.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static grub_efi_handle_t image_handle; +static grub_efi_char16_t *cmdline; + +static grub_err_t +grub_appleloader_unload (void) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_1 (b->unload_image, image_handle); + + grub_free (cmdline); + cmdline = 0; + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_appleloader_boot (void) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_3 (b->start_image, image_handle, 0, 0); + + grub_appleloader_unload (); + + return grub_errno; +} + +struct piwg_full_device_path +{ + struct grub_efi_memory_mapped_device_path comp1; + struct grub_efi_piwg_device_path comp2; + struct grub_efi_device_path end; +}; + +#define MAKE_PIWG_PATH(st, en) \ + { \ + .comp1 = \ + { \ + .header = { \ + .type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE, \ + .subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE, \ + .length = sizeof (struct grub_efi_memory_mapped_device_path) \ + }, \ + .memory_type = GRUB_EFI_MEMORY_MAPPED_IO, \ + .start_address = st, \ + .end_address = en \ + }, \ + .comp2 = \ + { \ + .header = { \ + .type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE, \ + .subtype = GRUB_EFI_PIWG_DEVICE_PATH_SUBTYPE, \ + .length = sizeof (struct grub_efi_piwg_device_path) \ + }, \ + .guid = GRUB_EFI_VENDOR_APPLE_GUID \ + }, \ + .end = \ + { \ + .type = GRUB_EFI_END_DEVICE_PATH_TYPE, \ + .subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE, \ + .length = sizeof (struct grub_efi_device_path) \ + } \ + } + +/* early 2006 Core Duo / Core Solo models */ +static struct piwg_full_device_path devpath_1 = MAKE_PIWG_PATH (0xffe00000, + 0xfff9ffff); + +/* mid-2006 Mac Pro (and probably other Core 2 models) */ +static struct piwg_full_device_path devpath_2 = MAKE_PIWG_PATH (0xffe00000, + 0xfff7ffff); + +/* mid-2007 MBP ("Santa Rosa" based models) */ +static struct piwg_full_device_path devpath_3 = MAKE_PIWG_PATH (0xffe00000, + 0xfff8ffff); + +/* early-2008 MBA */ +static struct piwg_full_device_path devpath_4 = MAKE_PIWG_PATH (0xffc00000, + 0xfff8ffff); + +/* late-2008 MB/MBP (NVidia chipset) */ +static struct piwg_full_device_path devpath_5 = MAKE_PIWG_PATH (0xffcb4000, + 0xffffbfff); + +/* mid-2010 MB/MBP (NVidia chipset) */ +static struct piwg_full_device_path devpath_6 = MAKE_PIWG_PATH (0xffcc4000, + 0xffffbfff); + +static struct piwg_full_device_path devpath_7 = MAKE_PIWG_PATH (0xff981000, + 0xffc8ffff); + +/* mid-2012 MBP retina (MacBookPro10,1) */ +static struct piwg_full_device_path devpath_8 = MAKE_PIWG_PATH (0xff990000, + 0xffb2ffff); + +struct devdata +{ + const char *model; + grub_efi_device_path_t *devpath; +}; + +struct devdata devs[] = +{ + {"Core Duo/Solo", (grub_efi_device_path_t *) &devpath_1}, + {"Mac Pro", (grub_efi_device_path_t *) &devpath_2}, + {"MBP", (grub_efi_device_path_t *) &devpath_3}, + {"MBA", (grub_efi_device_path_t *) &devpath_4}, + {"MB NV", (grub_efi_device_path_t *) &devpath_5}, + {"MB NV2", (grub_efi_device_path_t *) &devpath_6}, + {"MBP2011", (grub_efi_device_path_t *) &devpath_7}, + {"MBP2012", (grub_efi_device_path_t *) &devpath_8}, + {NULL, NULL}, +}; + +static grub_err_t +grub_cmd_appleloader (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_efi_boot_services_t *b; + grub_efi_loaded_image_t *loaded_image; + struct devdata *pdev; + + grub_dl_ref (my_mod); + + /* Initialize some global variables. */ + image_handle = 0; + + b = grub_efi_system_table->boot_services; + + for (pdev = devs ; pdev->devpath ; pdev++) + if (efi_call_6 (b->load_image, 0, grub_efi_image_handle, pdev->devpath, + NULL, 0, &image_handle) == GRUB_EFI_SUCCESS) + break; + + if (! pdev->devpath) + { + grub_error (GRUB_ERR_BAD_OS, "can't find model"); + goto fail; + } + + grub_dprintf ("appleload", "Model: %s\n", pdev->model); + + loaded_image = grub_efi_get_loaded_image (image_handle); + if (! loaded_image) + { + grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); + goto fail; + } + + if (argc > 0) + { + int i, len; + grub_efi_char16_t *p16; + + for (i = 0, len = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + + len *= sizeof (grub_efi_char16_t); + cmdline = p16 = grub_malloc (len); + if (! cmdline) + goto fail; + + for (i = 0; i < argc; i++) + { + char *p8; + + p8 = argv[i]; + while (*p8) + *(p16++) = *(p8++); + + *(p16++) = ' '; + } + *(--p16) = 0; + + loaded_image->load_options = cmdline; + loaded_image->load_options_size = len; + } + + grub_loader_set (grub_appleloader_boot, grub_appleloader_unload, 0); + + return 0; + + fail: + + grub_dl_unref (my_mod); + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(appleloader) +{ + cmd = grub_register_command ("appleloader", grub_cmd_appleloader, + N_("[OPTS]"), + /* TRANSLATORS: This command is used on EFI to + switch to BIOS mode and boot the OS requiring + BIOS. */ + N_("Boot BIOS-based system.")); + my_mod = mod; +} + +GRUB_MOD_FINI(appleloader) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c new file mode 100644 index 0000000..2bd80f4 --- /dev/null +++ b/grub-core/loader/efi/chainloader.c @@ -0,0 +1,444 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2006,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 <http://www.gnu.org/licenses/>. + */ + +/* 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/charset.h> +#include <grub/mm.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> +#include <grub/efi/disk.h> +#include <grub/command.h> +#include <grub/i18n.h> +#include <grub/net.h> +#if defined (__i386__) || defined (__x86_64__) +#include <grub/macho.h> +#include <grub/i386/macho.h> +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +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_efi_char16_t *cmdline; + +static grub_err_t +grub_chainloader_unload (void) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_1 (b->unload_image, image_handle); + efi_call_2 (b->free_pages, address, pages); + + grub_free (file_path); + grub_free (cmdline); + cmdline = 0; + file_path = 0; + + 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 = NULL; + + b = grub_efi_system_table->boot_services; + status = efi_call_3 (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, "%s", buf); + grub_free (buf); + } + } + else + grub_error (GRUB_ERR_BAD_OS, "unknown error"); + } + + if (exit_data) + efi_call_1 (b->free_pool, exit_data); + + grub_loader_unset (); + + return grub_errno; +} + +static grub_err_t +copy_file_path (grub_efi_file_path_device_path_t *fp, + const char *str, grub_efi_uint16_t len) +{ + grub_efi_char16_t *p, *path_name; + grub_efi_uint16_t size; + + fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE; + fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE; + + path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name)); + if (!path_name) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate path buffer"); + + size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8, + (const grub_uint8_t *) str, len, 0); + for (p = path_name; p < path_name + size; p++) + if (*p == '/') + *p = '\\'; + + grub_memcpy (fp->path_name, path_name, size * sizeof (*fp->path_name)); + /* File Path is NULL terminated */ + fp->path_name[size++] = '\0'; + fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp); + grub_free (path_name); + return GRUB_ERR_NONE; +} + +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 (d) + { + grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (d); + + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%" PRIuGRUB_SIZE, len); + return NULL; + } + + size += len; + if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d))) + break; + d = GRUB_EFI_NEXT_DEVICE_PATH (d); + } + + /* File Path is NULL terminated. Allocate space for 2 extra characters */ + /* FIXME why we split path in two components? */ + file_path = grub_malloc (size + + ((grub_strlen (dir_start) + 2) + * GRUB_MAX_UTF16_PER_UTF8 + * 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); + if (copy_file_path ((grub_efi_file_path_device_path_t *) d, + dir_start, dir_end - dir_start) != GRUB_ERR_NONE) + { + fail: + grub_free (file_path); + return 0; + } + + /* Fill the file path for the file. */ + d = GRUB_EFI_NEXT_DEVICE_PATH (d); + if (copy_file_path ((grub_efi_file_path_device_path_t *) d, + dir_end + 1, grub_strlen (dir_end + 1)) != GRUB_ERR_NONE) + goto fail; + + /* 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 = sizeof (*d); + + return file_path; +} + +static grub_err_t +grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_ssize_t size; + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_device_t dev = 0; + grub_efi_device_path_t *dp = 0; + grub_efi_loaded_image_t *loaded_image; + char *filename; + void *boot_image = 0; + grub_efi_handle_t dev_handle = 0; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + filename = argv[0]; + + 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, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE); + 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); + else if (dev->net && dev->net->server) + { + grub_net_network_level_address_t addr; + struct grub_net_network_level_interface *inf; + grub_net_network_level_address_t gateway; + grub_err_t err; + + err = grub_net_resolve_address (dev->net->server, &addr); + if (err) + goto fail; + + err = grub_net_route_address (addr, &gateway, &inf); + if (err) + goto fail; + + dev_handle = grub_efinet_get_device_handle (inf->card); + } + + if (dev_handle) + dp = grub_efi_get_device_path (dev_handle); + + if (! 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); + if (!size) + { + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + goto fail; + } + pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12); + + status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES, + GRUB_EFI_LOADER_CODE, + pages, &address); + if (status != GRUB_EFI_SUCCESS) + { + grub_dprintf ("chain", "Failed to allocate %u pages\n", + (unsigned int) pages); + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + + boot_image = (void *) ((grub_addr_t) address); + if (grub_file_read (file, boot_image, size) != size) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + + goto fail; + } + +#if defined (__i386__) || defined (__x86_64__) + if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) + { + struct grub_macho_fat_header *head = boot_image; + if (head->magic + == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC)) + { + grub_uint32_t i; + struct grub_macho_fat_arch *archs + = (struct grub_macho_fat_arch *) (head + 1); + for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++) + { + if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype)) + break; + } + if (i == grub_cpu_to_le32 (head->nfat_arch)) + { + grub_error (GRUB_ERR_BAD_OS, "no compatible arch found"); + goto fail; + } + if (grub_cpu_to_le32 (archs[i].offset) + > ~grub_cpu_to_le32 (archs[i].size) + || grub_cpu_to_le32 (archs[i].offset) + + grub_cpu_to_le32 (archs[i].size) + > (grub_size_t) size) + { + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + goto fail; + } + boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); + size = grub_cpu_to_le32 (archs[i].size); + } + } +#endif + + status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, + boot_image, 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; + + if (argc > 1) + { + int i, len; + grub_efi_char16_t *p16; + + for (i = 1, len = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + + len *= sizeof (grub_efi_char16_t); + cmdline = p16 = grub_malloc (len); + if (! cmdline) + goto fail; + + for (i = 1; i < argc; i++) + { + char *p8; + + p8 = argv[i]; + while (*p8) + *(p16++) = *(p8++); + + *(p16++) = ' '; + } + *(--p16) = 0; + + loaded_image->load_options = cmdline; + loaded_image->load_options_size = len; + } + + grub_file_close (file); + grub_device_close (dev); + + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); + return 0; + + fail: + + if (dev) + grub_device_close (dev); + + if (file) + grub_file_close (file); + + grub_free (file_path); + + if (address) + efi_call_2 (b->free_pages, address, pages); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(chainloader) +{ + cmd = grub_register_command ("chainloader", grub_cmd_chainloader, + 0, N_("Load another boot loader.")); + my_mod = mod; +} + +GRUB_MOD_FINI(chainloader) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c new file mode 100644 index 0000000..c86f283 --- /dev/null +++ b/grub-core/loader/efi/fdt.c @@ -0,0 +1,179 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013-2015 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/fdt.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/file.h> +#include <grub/efi/efi.h> +#include <grub/efi/fdtload.h> +#include <grub/efi/memory.h> +#include <grub/cpu/efi/memory.h> + +static void *loaded_fdt; +static void *fdt; + +#define FDT_ADDR_CELLS_STRING "#address-cells" +#define FDT_SIZE_CELLS_STRING "#size-cells" +#define FDT_ADDR_SIZE_EXTRA ((2 * grub_fdt_prop_entry_size (sizeof(grub_uint32_t))) + \ + sizeof (FDT_ADDR_CELLS_STRING) + \ + sizeof (FDT_SIZE_CELLS_STRING)) + +void * +grub_fdt_load (grub_size_t additional_size) +{ + void *raw_fdt; + unsigned int size; + + if (fdt) + { + size = GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt)); + grub_efi_free_pages ((grub_addr_t) fdt, size); + } + + if (loaded_fdt) + raw_fdt = loaded_fdt; + else + raw_fdt = grub_efi_get_firmware_fdt(); + + if (raw_fdt) + size = grub_fdt_get_totalsize (raw_fdt); + else + size = GRUB_FDT_EMPTY_TREE_SZ + FDT_ADDR_SIZE_EXTRA; + + size += additional_size; + + grub_dprintf ("linux", "allocating %d bytes for fdt\n", size); + fdt = grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS, + GRUB_EFI_BYTES_TO_PAGES (size), + GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_ACPI_RECLAIM_MEMORY); + if (!fdt) + return NULL; + + if (raw_fdt) + { + grub_memmove (fdt, raw_fdt, size - additional_size); + grub_fdt_set_totalsize (fdt, size); + } + else + { + grub_fdt_create_empty_tree (fdt, size); + grub_fdt_set_prop32 (fdt, 0, FDT_ADDR_CELLS_STRING, 2); + grub_fdt_set_prop32 (fdt, 0, FDT_SIZE_CELLS_STRING, 2); + } + return fdt; +} + +grub_err_t +grub_fdt_install (void) +{ + grub_efi_boot_services_t *b; + grub_efi_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = b->install_configuration_table (&fdt_guid, fdt); + if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, "failed to install FDT"); + + grub_dprintf ("fdt", "Installed/updated FDT configuration table @ %p\n", + fdt); + return GRUB_ERR_NONE; +} + +void +grub_fdt_unload (void) { + if (!fdt) { + return; + } + grub_efi_free_pages ((grub_addr_t) fdt, + GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt))); + fdt = NULL; +} + +static grub_err_t +grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t dtb; + void *blob = NULL; + int size; + + if (loaded_fdt) + grub_free (loaded_fdt); + loaded_fdt = NULL; + + /* No arguments means "use firmware FDT". */ + if (argc == 0) + { + return GRUB_ERR_NONE; + } + + dtb = grub_file_open (argv[0], GRUB_FILE_TYPE_DEVICE_TREE_IMAGE); + if (!dtb) + goto out; + + size = grub_file_size (dtb); + blob = grub_malloc (size); + if (!blob) + goto out; + + if (grub_file_read (dtb, blob, size) < size) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto out; + } + + if (grub_fdt_check_header (blob, size) != 0) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree")); + goto out; + } + +out: + if (dtb) + grub_file_close (dtb); + + if (blob) + { + if (grub_errno == GRUB_ERR_NONE) + loaded_fdt = blob; + else + grub_free (blob); + } + + return grub_errno; +} + +static grub_command_t cmd_devicetree; + +GRUB_MOD_INIT (fdt) +{ + cmd_devicetree = + grub_register_command_lockdown ("devicetree", grub_cmd_devicetree, 0, + N_("Load DTB file.")); +} + +GRUB_MOD_FINI (fdt) +{ + grub_unregister_command (cmd_devicetree); +} |