Thank you for explaining. I would suggest adding some maximum number of iterations to prevent an infinite loop if perhaps the EFI is misbehaving inside the "loop:" section. Not sure the right limit, but there must be some maximum allowed assuming worst case conditions or maximum allow based on protocols.
Thanks, Andrew On Sat, May 31, 2025 at 4:32 AM Renaud Métrich <rmetr...@redhat.com> wrote: > Hi Andrew, > > Sorry I missed your email. > > IMHO the "goto" is useful here to be able to loop again at same "s" > level: we actually want to redo the operation without going one step > further. > > Let me explain with an example. > > We want to connect PCIROOT, then PCI level, then SCSI I/O level. > > There is only one PCIROOT, then you can have multiple levels of PCI. > > Whenever you enable a PCI handle, you may have new PCI handles showing > up, so you want to enable them through listing all the handles again and > processing them. > > Only once we didn't enable some new PCI handle in the current loop, we > are ready to enable the next level, which is SCSI I/O devices. > > For these devices, however, all we do is let the UEFI function enable > them recursively. > > If we were letting the UEFI function enable recursively the PCI devices, > then almost everything would be enabled and this could potentially take > a long time. > > Renaud. > > Le 3/20/25 à 1:28 AM, Andrew Hamilton a écrit : > > Hello, > > > > I just had one question about the use of the 'goto' and the 'loop' > > label in the below code... see my question below the 'loop' label. > > Let me know if I just misunderstand or am wrong. > > > > Thank you! > > Andrew > > > > On Wed, Mar 19, 2025 at 7:53 AM Renaud Métrich via Grub-devel > > <grub-devel@gnu.org> wrote: > >> When network booting is used, trying to chainload to the local disk > >> (which is used in deployment tools such as Red Hat Satellite) may fail > >> when searching for the boot loader, e.g. /EFI/redhat/shimx64.efi: > >> the boot loader file is listed, but not readable, because UEFI DISK I/O > >> and/or SCSI DISK I/O devices are not connected. > >> Sometimes devices are connected but not the partition of the disk > >> matching $prefix, causing partition to not be found by"chainloader". > >> > >> This issue was seen already on: > >> - VMWare systems with efi.quickboot enabled (which is the default) > >> - QEMU/KVM when network booting and not enabling any disk > >> - Physical hardware such as Cisco and HPE systems > >> > >> Typical chainload configuration used: > >> -------- 8< ---------------- 8< ---------------- 8< -------- > >> unset prefix > >> search --file --set=prefix /EFI/redhat/shimx64.efi > >> if [ -n "$prefix" ]; then > >> chainloader ($prefix)/EFI/redhat/shimx64.efi > >> ... > >> -------- 8< ---------------- 8< ---------------- 8< -------- > >> > >> This patch introduces a new "connectefi pciroot|disk|all" command which > >> recursively connects all EFI devices starting from a given controller > >> type: > >> - if 'pciroot' is specified, recursion is performed for all PCI root > >> handles > >> - if 'disk' is specified, recursion is performed for all SCSI and DISK > >> I/O handles (recommended usage to avoid connecting unwanted handles > >> which may impact Grub performances) > >> - if 'all' is specified, recursion is performed on all handles (not > >> recommended since it may heavily impact Grub performances) > >> > >> Typical grub.cfg snippet would then be: > >> -------- 8< ---------------- 8< ---------------- 8< -------- > >> connectefi disk > >> unset prefix > >> search --file --set=prefix /EFI/redhat/shimx64.efi > >> if [ -n "$prefix" ]; then > >> chainloader ($prefix)/EFI/redhat/shimx64.efi > >> ... > >> -------- 8< ---------------- 8< ---------------- 8< -------- > >> > >> The code is easily extensible to handle other arguments in the future if > >> needed. > >> > >> Signed-off-by: Renaud Métrich <rmetr...@redhat.com> > >> --- > >> NEWS | 5 +- > >> grub-core/Makefile.core.def | 6 + > >> grub-core/commands/efi/connectefi.c | 212 ++++++++++++++++++++++++++++ > >> grub-core/commands/efi/lsefi.c | 3 +- > >> grub-core/disk/efi/efidisk.c | 13 ++ > >> grub-core/kern/efi/efi.c | 13 ++ > >> include/grub/efi/api.h | 2 +- > >> include/grub/efi/disk.h | 2 + > >> include/grub/efi/efi.h | 5 + > >> 9 files changed, 258 insertions(+), 3 deletions(-) > >> create mode 100644 grub-core/commands/efi/connectefi.c > >> > >> diff --git a/NEWS b/NEWS > >> index 310130962..48d9dd4dd 100644 > >> --- a/NEWS > >> +++ b/NEWS > >> @@ -32,6 +32,9 @@ New in 2.06: > >> * BootHole and BootHole2 fixes. > >> * ...and tons of other fixes and cleanups... > >> > >> +* New/improved platform support: > >> + * New `connectefi' command on EFI platforms. > >> + > >> New in 2.04: > >> > >> * GCC 8 and 9 support. > >> @@ -118,7 +121,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 f70e02e69..55e43ea88 100644 > >> --- a/grub-core/Makefile.core.def > >> +++ b/grub-core/Makefile.core.def > >> @@ -840,6 +840,12 @@ module = { > >> enable = efi; > >> }; > >> > >> +module = { > >> + name = connectefi; > >> + efi = commands/efi/connectefi.c; > >> + enable = efi; > >> +}; > >> + > >> module = { > >> name = blocklist; > >> common = commands/blocklist.c; > >> diff --git a/grub-core/commands/efi/connectefi.c > b/grub-core/commands/efi/connectefi.c > >> new file mode 100644 > >> index 000000000..5049c2abe > >> --- /dev/null > >> +++ b/grub-core/commands/efi/connectefi.c > >> @@ -0,0 +1,212 @@ > >> +/* > >> + * 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 <http://www.gnu.org/licenses/>. > >> + */ > >> +#include <grub/types.h> > >> +#include <grub/mm.h> > >> +#include <grub/misc.h> > >> +#include <grub/efi/api.h> > >> +#include <grub/efi/pci.h> > >> +#include <grub/efi/efi.h> > >> +#include <grub/command.h> > >> +#include <grub/err.h> > >> +#include <grub/i18n.h> > >> + > >> +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_connectefi (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 disk_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 }, > >> + { GRUB_EFI_DISK_IO_PROTOCOL_GUID, "DISK 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], "disk") == 0) > >> + { > >> + items = disk_items; > >> + nitems = ARRAY_SIZE (disk_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: > > In the section later that uses 'goto' to get back here, is there a > > potential this code will enter an infinite loop since I assume the > > increment of 's' is being skipped by the 'goto'? Any reason the > > 'goto' later in the code can't use a 'continue'? > > > >> + 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(connectefi) > >> +{ > >> + cmd = grub_register_command ("connectefi", grub_cmd_connectefi, > >> + /* TRANSLATORS: only translate 'all' > keyword */ > >> + N_("pciroot|disk|all"), > >> + N_("Connect EFI handles." > >> + " If 'pciroot' is specified, connect > PCI" > >> + " root EFI handles recursively." > >> + " If 'disk' is specified, connect > SCSI" > >> + " and DISK I/O EFI handles > recursively." > >> + " If 'all' is specified, connect all" > >> + " EFI handles recursively.")); > >> +} > >> + > >> +GRUB_MOD_FINI(connectefi) > >> +{ > >> + grub_unregister_command (cmd); > >> +} > >> diff --git a/grub-core/commands/efi/lsefi.c > b/grub-core/commands/efi/lsefi.c > >> index bda25a3a9..8a82ababf 100644 > >> --- a/grub-core/commands/efi/lsefi.c > >> +++ b/grub-core/commands/efi/lsefi.c > >> @@ -19,6 +19,7 @@ > >> #include <grub/mm.h> > >> #include <grub/misc.h> > >> #include <grub/efi/api.h> > >> +#include <grub/efi/disk.h> > >> #include <grub/efi/edid.h> > >> #include <grub/efi/pci.h> > >> #include <grub/efi/efi.h> > >> @@ -35,7 +36,7 @@ static struct known_protocol > >> const char *name; > >> } known_protocols[] = > >> { > >> - { GRUB_EFI_DISK_IO_GUID, "disk" }, > >> + { GRUB_EFI_DISK_IO_PROTOCOL_GUID, "disk" }, > >> { GRUB_EFI_BLOCK_IO_GUID, "block" }, > >> { GRUB_EFI_SERIAL_IO_GUID, "serial" }, > >> { GRUB_EFI_SIMPLE_NETWORK_GUID, "network" }, > >> diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c > >> index 3b5ed5691..525d59838 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 b93ae3aba..b7c861b1e 100644 > >> --- a/grub-core/kern/efi/efi.c > >> +++ b/grub-core/kern/efi/efi.c > >> @@ -96,6 +96,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_guid_t *protocol, > >> diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h > >> index b686e8afe..d73a10c28 100644 > >> --- a/include/grub/efi/api.h > >> +++ b/include/grub/efi/api.h > >> @@ -79,7 +79,7 @@ > >> { 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ > >> } > >> > >> -#define GRUB_EFI_DISK_IO_GUID \ > >> +#define GRUB_EFI_DISK_IO_PROTOCOL_GUID \ > >> { 0xce345171, 0xba0b, 0x11d2, \ > >> { 0x8e, 0x4f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ > >> } > >> diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h > >> index 254475c84..6845c2f1f 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 a5cd99e5a..60d0b2bf8 100644 > >> --- a/include/grub/efi/efi.h > >> +++ b/include/grub/efi/efi.h > >> @@ -44,6 +44,11 @@ EXPORT_FUNC(grub_efi_locate_handle) > (grub_efi_locate_search_type_t search_type, > >> grub_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_guid_t *protocol, > >> grub_efi_uint32_t > attributes); > >> -- > >> 2.48.1 > >> > >> > >> _______________________________________________ > >> Grub-devel mailing list > >> Grub-devel@gnu.org > >> https://lists.gnu.org/mailman/listinfo/grub-devel >
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel