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

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