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

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