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

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