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

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