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