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);