diff --git a/NEWS b/NEWS index 73b8492bc4..618c3cdcde 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ New in 2.06: * BootHole and BootHole2 fixes. * ...and tons of other fixes and cleanups... +* New/improved platform support: + * New `eficonnect' command on EFI platforms. + New in 2.04: * GCC 8 and 9 support. @@ -98,7 +101,7 @@ New in 2.02: * Prefer pmtimer for TSC calibration. * New/improved platform support: - * New `efifwsetup' and `lsefi' commands on EFI platforms. + * New `efifwsetup', `lsefi' commands on EFI platforms. * New `cmosdump' and `cmosset' commands on platforms with CMOS support. * New command `pcidump' for PCI platforms. * Improve opcode parsing in ACPI halt implementation. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index d37e9d740d..b869596418 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -833,6 +833,12 @@ module = { enable = efi; }; +module = { + name = eficonnect; + efi = commands/efi/eficonnect.c; + enable = efi; +}; + module = { name = blocklist; common = commands/blocklist.c; diff --git a/grub-core/commands/efi/eficonnect.c b/grub-core/commands/efi/eficonnect.c new file mode 100644 index 0000000000..360b5b4125 --- /dev/null +++ b/grub-core/commands/efi/eficonnect.c @@ -0,0 +1,211 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_efi_already_handled +{ + struct grub_efi_already_handled *next; + struct grub_efi_already_handled **prev; + grub_efi_handle_t handle; +}; + +static struct grub_efi_already_handled *already_handled; + +static struct grub_efi_already_handled * +is_in_list (grub_efi_handle_t handle) +{ + struct grub_efi_already_handled *e; + + FOR_LIST_ELEMENTS (e, already_handled) + if (e->handle == handle) + return e; + + return NULL; +} + +static void +free_handle_list (void) +{ + struct grub_efi_already_handled *e; + while ((e = already_handled) != NULL) + { + already_handled = already_handled->next; + grub_free (e); + } +} + +typedef enum searched_item_flag +{ + SEARCHED_ITEM_FLAG_LOOP = 1, + SEARCHED_ITEM_FLAG_RECURSIVE = 2 +} searched_item_flags; + +typedef struct searched_item +{ + grub_efi_guid_t guid; + const char *name; + searched_item_flags flags; +} searched_items; + +static grub_err_t +grub_cmd_eficonnect (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + unsigned s; + searched_items pciroot_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items scsi_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 }, + { GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP }, + { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items *items = NULL; + unsigned nitems = 0; + grub_err_t grub_err = GRUB_ERR_NONE; + unsigned total_connected = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (grub_strcmp(args[0], "pciroot") == 0) + { + items = pciroot_items; + nitems = ARRAY_SIZE (pciroot_items); + } + else if (grub_strcmp(args[0], "scsi") == 0) + { + items = scsi_items; + nitems = ARRAY_SIZE (scsi_items); + } + else if (grub_strcmp(args[0], N_("all")) == 0) + { + items = NULL; + nitems = 1; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unexpected argument `%s'"), args[0]); + + for (s = 0; s < nitems; s++) + { + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + unsigned i, connected = 0, loop = 0; + +loop: + loop++; + if (items != NULL) + { + grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop); + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, + &items[s].guid, 0, &num_handles); + } + else + handles = grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES, + NULL, NULL, &num_handles); + + if (!handles) + continue; + + for (i = 0; i < num_handles; i++) + { + grub_efi_handle_t handle = handles[i]; + grub_efi_status_t status; + unsigned j; + + /* Skip already handled handles */ + if (is_in_list (handle) != NULL) + { + grub_dprintf ("efi", " handle %p: already processed\n", + handle); + continue; + } + + status = grub_efi_connect_controller(handle, NULL, NULL, + !items || items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0); + if (status == GRUB_EFI_SUCCESS) + { + connected++; + total_connected++; + grub_dprintf ("efi", " handle %p: connected\n", handle); + } + else + grub_dprintf ("efi", " handle %p: failed to connect (%d)\n", + handle, (grub_efi_int8_t) status); + + struct grub_efi_already_handled *item = grub_malloc(sizeof (*item)); + if (!item) + break; /* fatal */ + grub_list_push (GRUB_AS_LIST_P (&already_handled), GRUB_AS_LIST (item)); + item->handle = handle; + } + + grub_free (handles); + if (grub_err != GRUB_ERR_NONE) + break; /* fatal */ + + if (items && items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected) + { + connected = 0; + goto loop; + } + + free_handle_list (); + } + + free_handle_list (); + + if (total_connected) + grub_efidisk_reenumerate_disks (); + + return grub_err; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(eficonnect) +{ + cmd = grub_register_command ("eficonnect", grub_cmd_eficonnect, + /* TRANSLATORS: only translate 'all' keyword */ + N_("pciroot|scsi|all"), + N_("Connect EFI handles." + " If 'pciroot' is specified, connect PCI" + " root EFI handles recursively." + " If 'scsi' is specified, connect SCSI" + " I/O EFI handles recursively." + " If 'all' is specified, connect all" + " EFI handles recursively.")); +} + +GRUB_MOD_FINI(eficonnect) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index d1ce99af43..f2d2430e66 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index fe8ba6e6c9..062143dfff 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -396,6 +396,19 @@ enumerate_disks (void) free_devices (devices); } +void +grub_efidisk_reenumerate_disks (void) +{ + free_devices (fd_devices); + free_devices (hd_devices); + free_devices (cd_devices); + fd_devices = 0; + hd_devices = 0; + cd_devices = 0; + + enumerate_disks (); +} + static int grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, grub_disk_pull_t pull) diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 98d63efbd1..4ac2b2754e 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -95,6 +95,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, return buffer; } +grub_efi_status_t +grub_efi_connect_controller (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + return efi_call_4 (b->connect_controller, controller_handle, + driver_image_handle, remaining_device_path, recursive); +} + void * grub_efi_open_protocol (grub_efi_handle_t handle, grub_efi_guid_t *protocol, diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h index 254475c842..6845c2f1fd 100644 --- a/include/grub/efi/disk.h +++ b/include/grub/efi/disk.h @@ -27,6 +27,8 @@ grub_efi_handle_t EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk); char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle); +void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void); + void grub_efidisk_init (void); void grub_efidisk_fini (void); diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 4890b50310..449e55269f 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -41,6 +41,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type, grub_efi_guid_t *protocol, void *search_key, grub_efi_uintn_t *num_handles); +grub_efi_status_t +EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive); void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle, grub_efi_guid_t *protocol, grub_efi_uint32_t attributes);