Blame SOURCES/0494-efi-new-connectefi-command.patch

b71686
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b71686
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
b71686
Date: Tue, 15 Feb 2022 14:05:22 +0100
b71686
Subject: [PATCH] efi: new 'connectefi' command
b71686
MIME-Version: 1.0
b71686
Content-Type: text/plain; charset=UTF-8
b71686
Content-Transfer-Encoding: 8bit
b71686
b71686
When efi.quickboot is enabled on VMWare (which is the default for
b71686
hardware release 16 and later), it may happen that not all EFI devices
b71686
are connected. Due to this, browsing the devices in make_devices() just
b71686
fails to find devices, in particular disks or partitions for a given
b71686
disk.
b71686
This typically happens when network booting, then trying to chainload to
b71686
local disk (this is used in deployment tools such as Red Hat Satellite),
b71686
which is done through using the following grub.cfg snippet:
b71686
-------- 8< ---------------- 8< ---------------- 8< --------
b71686
unset prefix
b71686
search --file --set=prefix /EFI/redhat/grubx64.efi
b71686
if [ -n "$prefix" ]; then
b71686
  chainloader ($prefix)/EFI/redhat/grubx64/efi
b71686
...
b71686
-------- 8< ---------------- 8< ---------------- 8< --------
b71686
b71686
With efi.quickboot, none of the devices are connected, causing "search"
b71686
to fail. Sometimes devices are connected but not the partition of the
b71686
disk matching $prefix, causing partition to not be found by
b71686
"chainloader".
b71686
b71686
This patch introduces a new "connectefi pciroot|scsi" command which
b71686
recursively connects all EFI devices starting from a given controller
b71686
type:
b71686
- if 'pciroot' is specified, recursion is performed for all PCI root
b71686
  handles
b71686
- if 'scsi' is specified, recursion is performed for all SCSI I/O
b71686
  handles (recommended usage to avoid connecting unwanted handles which
b71686
  may impact Grub performances)
b71686
b71686
Typical grub.cfg snippet would then be:
b71686
-------- 8< ---------------- 8< ---------------- 8< --------
b71686
connectefi scsi
b71686
unset prefix
b71686
search --file --set=prefix /EFI/redhat/grubx64.efi
b71686
if [ -n "$prefix" ]; then
b71686
  chainloader ($prefix)/EFI/redhat/grubx64/efi
b71686
...
b71686
-------- 8< ---------------- 8< ---------------- 8< --------
b71686
b71686
The code is easily extensible to handle other arguments in the future if
b71686
needed.
b71686
b71686
Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
b71686
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
b71686
(cherry picked from commit cc972c27314c841f80ab0fe8318fae06f078c680)
b71686
(cherry picked from commit 84b0c3f965a3918be64ca850139bea7c32d23ae9)
b71686
---
b71686
 grub-core/Makefile.core.def         |   6 ++
b71686
 grub-core/commands/efi/connectefi.c | 205 ++++++++++++++++++++++++++++++++++++
b71686
 grub-core/commands/efi/lsefi.c      |   1 +
b71686
 grub-core/disk/efi/efidisk.c        |  13 +++
b71686
 grub-core/kern/efi/efi.c            |  13 +++
b71686
 include/grub/efi/disk.h             |   2 +
b71686
 include/grub/efi/efi.h              |   5 +
b71686
 NEWS                                |   2 +-
b71686
 8 files changed, 246 insertions(+), 1 deletion(-)
b71686
 create mode 100644 grub-core/commands/efi/connectefi.c
b71686
b71686
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
b71686
index 612df2e9c..ef06f8c95 100644
b71686
--- a/grub-core/Makefile.core.def
b71686
+++ b/grub-core/Makefile.core.def
b71686
@@ -779,6 +779,12 @@ module = {
b71686
   enable = efi;
b71686
 };
b71686
 
b71686
+module = {
b71686
+  name = connectefi;
b71686
+  common = commands/efi/connectefi.c;
b71686
+  enable = efi;
b71686
+};
b71686
+
b71686
 module = {
b71686
   name = blocklist;
b71686
   common = commands/blocklist.c;
b71686
diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c
b71686
new file mode 100644
b71686
index 000000000..8ab75bd51
b71686
--- /dev/null
b71686
+++ b/grub-core/commands/efi/connectefi.c
b71686
@@ -0,0 +1,205 @@
b71686
+/*
b71686
+ *  GRUB  --  GRand Unified Bootloader
b71686
+ *  Copyright (C) 2022  Free Software Foundation, Inc.
b71686
+ *
b71686
+ *  GRUB is free software: you can redistribute it and/or modify
b71686
+ *  it under the terms of the GNU General Public License as published by
b71686
+ *  the Free Software Foundation, either version 3 of the License, or
b71686
+ *  (at your option) any later version.
b71686
+ *
b71686
+ *  GRUB is distributed in the hope that it will be useful,
b71686
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
b71686
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
b71686
+ *  GNU General Public License for more details.
b71686
+ *
b71686
+ *  You should have received a copy of the GNU General Public License
b71686
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
b71686
+ */
b71686
+#include <grub/types.h>
b71686
+#include <grub/mm.h>
b71686
+#include <grub/misc.h>
b71686
+#include <grub/efi/api.h>
b71686
+#include <grub/efi/pci.h>
b71686
+#include <grub/efi/efi.h>
b71686
+#include <grub/command.h>
b71686
+#include <grub/err.h>
b71686
+#include <grub/i18n.h>
b71686
+
b71686
+GRUB_MOD_LICENSE ("GPLv3+");
b71686
+
b71686
+typedef struct handle_list
b71686
+{
b71686
+  grub_efi_handle_t handle;
b71686
+  struct handle_list *next;
b71686
+} handle_list_t;
b71686
+
b71686
+static handle_list_t *already_handled = NULL;
b71686
+
b71686
+static grub_err_t
b71686
+add_handle (grub_efi_handle_t handle)
b71686
+{
b71686
+  handle_list_t *e;
b71686
+  e = grub_malloc (sizeof (*e));
b71686
+  if (! e)
b71686
+    return grub_errno;
b71686
+  e->handle = handle;
b71686
+  e->next = already_handled;
b71686
+  already_handled = e;
b71686
+  return GRUB_ERR_NONE;
b71686
+}
b71686
+
b71686
+static int
b71686
+is_in_list (grub_efi_handle_t handle)
b71686
+{
b71686
+  handle_list_t *e;
b71686
+  for (e = already_handled; e != NULL; e = e->next)
b71686
+    if (e->handle == handle)
b71686
+      return 1;
b71686
+  return 0;
b71686
+}
b71686
+
b71686
+static void
b71686
+free_handle_list (void)
b71686
+{
b71686
+  handle_list_t *e;
b71686
+  while ((e = already_handled) != NULL)
b71686
+    {
b71686
+      already_handled = already_handled->next;
b71686
+      grub_free (e);
b71686
+    }
b71686
+}
b71686
+
b71686
+typedef enum searched_item_flag
b71686
+{
b71686
+  SEARCHED_ITEM_FLAG_LOOP = 1,
b71686
+  SEARCHED_ITEM_FLAG_RECURSIVE = 2
b71686
+} searched_item_flags;
b71686
+
b71686
+typedef struct searched_item
b71686
+{
b71686
+  grub_efi_guid_t guid;
b71686
+  const char *name;
b71686
+  searched_item_flags flags;
b71686
+} searched_items;
b71686
+
b71686
+static grub_err_t
b71686
+grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)),
b71686
+		     int argc, char **args)
b71686
+{
b71686
+  unsigned s;
b71686
+  searched_items pciroot_items[] =
b71686
+    {
b71686
+      { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE }
b71686
+    };
b71686
+  searched_items scsi_items[] =
b71686
+    {
b71686
+      { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 },
b71686
+      { GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP },
b71686
+      { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE }
b71686
+    };
b71686
+  searched_items *items = NULL;
b71686
+  unsigned nitems = 0;
b71686
+  grub_err_t grub_err = GRUB_ERR_NONE;
b71686
+  unsigned total_connected = 0;
b71686
+
b71686
+  if (argc != 1)
b71686
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
b71686
+
b71686
+  if (grub_strcmp(args[0], N_("pciroot")) == 0)
b71686
+    {
b71686
+      items = pciroot_items;
b71686
+      nitems = ARRAY_SIZE (pciroot_items);
b71686
+    }
b71686
+  else if (grub_strcmp(args[0], N_("scsi")) == 0)
b71686
+    {
b71686
+      items = scsi_items;
b71686
+      nitems = ARRAY_SIZE (scsi_items);
b71686
+    }
b71686
+  else
b71686
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
b71686
+		       N_("unexpected argument `%s'"), args[0]);
b71686
+
b71686
+  for (s = 0; s < nitems; s++)
b71686
+    {
b71686
+      grub_efi_handle_t *handles;
b71686
+      grub_efi_uintn_t num_handles;
b71686
+      unsigned i, connected = 0, loop = 0;
b71686
+
b71686
+loop:
b71686
+      loop++;
b71686
+      grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop);
b71686
+
b71686
+      handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
b71686
+					&items[s].guid, 0, &num_handles);
b71686
+
b71686
+      if (!handles)
b71686
+	continue;
b71686
+
b71686
+      for (i = 0; i < num_handles; i++)
b71686
+	{
b71686
+	  grub_efi_handle_t handle = handles[i];
b71686
+	  grub_efi_status_t status;
b71686
+	  unsigned j;
b71686
+
b71686
+	  /* Skip already handled handles  */
b71686
+	  if (is_in_list (handle))
b71686
+	    {
b71686
+	      grub_dprintf ("efi", "  handle %p: already processed\n",
b71686
+				   handle);
b71686
+	      continue;
b71686
+	    }
b71686
+
b71686
+	  status = grub_efi_connect_controller(handle, NULL, NULL,
b71686
+			items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0);
b71686
+	  if (status == GRUB_EFI_SUCCESS)
b71686
+	    {
b71686
+	      connected++;
b71686
+	      total_connected++;
b71686
+	      grub_dprintf ("efi", "  handle %p: connected\n", handle);
b71686
+	    }
b71686
+	  else
b71686
+	    grub_dprintf ("efi", "  handle %p: failed to connect (%d)\n",
b71686
+				 handle, (grub_efi_int8_t) status);
b71686
+
b71686
+	  if ((grub_err = add_handle (handle)) != GRUB_ERR_NONE)
b71686
+	    break; /* fatal  */
b71686
+	}
b71686
+
b71686
+      grub_free (handles);
b71686
+      if (grub_err != GRUB_ERR_NONE)
b71686
+	break; /* fatal  */
b71686
+
b71686
+      if (items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected)
b71686
+	{
b71686
+	  connected = 0;
b71686
+	  goto loop;
b71686
+	}
b71686
+
b71686
+      free_handle_list ();
b71686
+    }
b71686
+
b71686
+  free_handle_list ();
b71686
+
b71686
+  if (total_connected)
b71686
+    grub_efidisk_reenumerate_disks ();
b71686
+
b71686
+  return grub_err;
b71686
+}
b71686
+
b71686
+static grub_command_t cmd;
b71686
+
b71686
+GRUB_MOD_INIT(connectefi)
b71686
+{
b71686
+  cmd = grub_register_command ("connectefi", grub_cmd_connectefi,
b71686
+			       N_("pciroot|scsi"),
b71686
+			       N_("Connect EFI handles."
b71686
+				  " If 'pciroot' is specified, connect PCI"
b71686
+				  " root EFI handles recursively."
b71686
+				  " If 'scsi' is specified, connect SCSI"
b71686
+				  " I/O EFI handles recursively."));
b71686
+}
b71686
+
b71686
+GRUB_MOD_FINI(connectefi)
b71686
+{
b71686
+  grub_unregister_command (cmd);
b71686
+}
b71686
diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c
b71686
index d1ce99af4..f2d2430e6 100644
b71686
--- a/grub-core/commands/efi/lsefi.c
b71686
+++ b/grub-core/commands/efi/lsefi.c
b71686
@@ -19,6 +19,7 @@
b71686
 #include <grub/mm.h>
b71686
 #include <grub/misc.h>
b71686
 #include <grub/efi/api.h>
b71686
+#include <grub/efi/disk.h>
b71686
 #include <grub/efi/edid.h>
b71686
 #include <grub/efi/pci.h>
b71686
 #include <grub/efi/efi.h>
b71686
diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c
b71686
index 5d2400f66..720a23fcc 100644
b71686
--- a/grub-core/disk/efi/efidisk.c
b71686
+++ b/grub-core/disk/efi/efidisk.c
b71686
@@ -390,6 +390,19 @@ enumerate_disks (void)
b71686
   free_devices (devices);
b71686
 }
b71686
 
b71686
+void
b71686
+grub_efidisk_reenumerate_disks (void)
b71686
+{
b71686
+  free_devices (fd_devices);
b71686
+  free_devices (hd_devices);
b71686
+  free_devices (cd_devices);
b71686
+  fd_devices = 0;
b71686
+  hd_devices = 0;
b71686
+  cd_devices = 0;
b71686
+
b71686
+  enumerate_disks ();
b71686
+}
b71686
+
b71686
 static int
b71686
 grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
b71686
 		      grub_disk_pull_t pull)
b71686
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
b71686
index 4b95a4004..286395645 100644
b71686
--- a/grub-core/kern/efi/efi.c
b71686
+++ b/grub-core/kern/efi/efi.c
b71686
@@ -95,6 +95,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type,
b71686
   return buffer;
b71686
 }
b71686
 
b71686
+grub_efi_status_t
b71686
+grub_efi_connect_controller (grub_efi_handle_t controller_handle,
b71686
+			     grub_efi_handle_t *driver_image_handle,
b71686
+			     grub_efi_device_path_protocol_t *remaining_device_path,
b71686
+			     grub_efi_boolean_t recursive)
b71686
+{
b71686
+  grub_efi_boot_services_t *b;
b71686
+
b71686
+  b = grub_efi_system_table->boot_services;
b71686
+  return efi_call_4 (b->connect_controller, controller_handle,
b71686
+		     driver_image_handle, remaining_device_path, recursive);
b71686
+}
b71686
+
b71686
 void *
b71686
 grub_efi_open_protocol (grub_efi_handle_t handle,
b71686
 			grub_efi_guid_t *protocol,
b71686
diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h
b71686
index 254475c84..6845c2f1f 100644
b71686
--- a/include/grub/efi/disk.h
b71686
+++ b/include/grub/efi/disk.h
b71686
@@ -27,6 +27,8 @@ grub_efi_handle_t
b71686
 EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk);
b71686
 char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle);
b71686
 
b71686
+void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void);
b71686
+
b71686
 void grub_efidisk_init (void);
b71686
 void grub_efidisk_fini (void);
b71686
 
b71686
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
b71686
index 570a69361..4411ffa16 100644
b71686
--- a/include/grub/efi/efi.h
b71686
+++ b/include/grub/efi/efi.h
b71686
@@ -36,6 +36,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type,
b71686
 				     grub_efi_guid_t *protocol,
b71686
 				     void *search_key,
b71686
 				     grub_efi_uintn_t *num_handles);
b71686
+grub_efi_status_t
b71686
+EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle,
b71686
+					  grub_efi_handle_t *driver_image_handle,
b71686
+					  grub_efi_device_path_protocol_t *remaining_device_path,
b71686
+					  grub_efi_boolean_t recursive);
b71686
 void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle,
b71686
 					   grub_efi_guid_t *protocol,
b71686
 					   grub_efi_uint32_t attributes);
b71686
diff --git a/NEWS b/NEWS
b71686
index 2ebd54e78..b04041507 100644
b71686
--- a/NEWS
b71686
+++ b/NEWS
b71686
@@ -66,7 +66,7 @@ New in 2.02:
b71686
   * Prefer pmtimer for TSC calibration.
b71686
 
b71686
 * New/improved platform support:
b71686
-  * New `efifwsetup' and `lsefi' commands on EFI platforms.
b71686
+  * New `efifwsetup', `lsefi' and `connectefi` commands on EFI platforms.
b71686
   * New `cmosdump' and `cmosset' commands on platforms with CMOS support.
b71686
   * New command `pcidump' for PCI platforms.
b71686
   * Improve opcode parsing in ACPI halt implementation.