Skip to content

Commit fe1c6f0

Browse files
committed
efi: new 'eficonnect' command
When efi.quickboot is enabled on VMWare (which is the default for hardware release 16 and later), it may happen that not all EFI devices are connected. Due to this, browsing the devices in make_devices() just fails to find devices, in particular disks or partitions for a given disk. This typically happens when network booting, then trying to chainload to local disk (this is used in deployment tools such as Red Hat Satellite), which is done through using the following grub.cfg snippet: -------- 8< ---------------- 8< ---------------- 8< -------- unset prefix search --file --set=prefix /EFI/redhat/grubx64.efi if [ -n "$prefix" ]; then chainloader ($prefix)/EFI/redhat/grubx64/efi ... -------- 8< ---------------- 8< ---------------- 8< -------- With efi.quickboot, none of the devices are connected, causing "search" to fail. Sometimes devices are connected but not the partition of the disk matching $prefix, causing partition to not be found by "chainloader". This patch introduces a new "eficonnect pciroot|scsi|all" command whic recursively connects all EFI devices starting from a given controller type: - if 'pciroot' is specified, recursion is performed for all PCI root handles - if 'scsi' is specified, recursion is performed for all SCSI I/O handles (recommended usage to avoid connecting unwanted handles which may impact Grub performances) - if 'all' is specified, recursion is performed on all handles (not recommended since it may heavily impact Grub performances) Typical grub.cfg snippet would then be: -------- 8< ---------------- 8< ---------------- 8< -------- eficonnect scsi unset prefix search --file --set=prefix /EFI/redhat/grubx64.efi if [ -n "$prefix" ]; then chainloader ($prefix)/EFI/redhat/grubx64/efi ... -------- 8< ---------------- 8< ---------------- 8< -------- The code is easily extensible to handle other arguments in the future if needed. Signed-off-by: Renaud Métrich <[email protected]>
1 parent cc78de8 commit fe1c6f0

File tree

8 files changed

+255
-1
lines changed

8 files changed

+255
-1
lines changed

NEWS

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ New in 2.06:
1212
* BootHole and BootHole2 fixes.
1313
* ...and tons of other fixes and cleanups...
1414

15+
* New/improved platform support:
16+
* New `eficonnect' command on EFI platforms.
17+
1518
New in 2.04:
1619

1720
* GCC 8 and 9 support.
@@ -98,7 +101,7 @@ New in 2.02:
98101
* Prefer pmtimer for TSC calibration.
99102

100103
* New/improved platform support:
101-
* New `efifwsetup' and `lsefi' commands on EFI platforms.
104+
* New `efifwsetup', `lsefi' commands on EFI platforms.
102105
* New `cmosdump' and `cmosset' commands on platforms with CMOS support.
103106
* New command `pcidump' for PCI platforms.
104107
* Improve opcode parsing in ACPI halt implementation.

grub-core/Makefile.core.def

+6
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,12 @@ module = {
833833
enable = efi;
834834
};
835835

836+
module = {
837+
name = eficonnect;
838+
efi = commands/efi/eficonnect.c;
839+
enable = efi;
840+
};
841+
836842
module = {
837843
name = blocklist;
838844
common = commands/blocklist.c;

grub-core/commands/efi/eficonnect.c

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* GRUB -- GRand Unified Bootloader
3+
* Copyright (C) 2022 Free Software Foundation, Inc.
4+
*
5+
* GRUB is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* GRUB is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#include <grub/types.h>
19+
#include <grub/mm.h>
20+
#include <grub/misc.h>
21+
#include <grub/efi/api.h>
22+
#include <grub/efi/pci.h>
23+
#include <grub/efi/efi.h>
24+
#include <grub/command.h>
25+
#include <grub/err.h>
26+
#include <grub/i18n.h>
27+
28+
GRUB_MOD_LICENSE ("GPLv3+");
29+
30+
struct grub_efi_already_handled
31+
{
32+
struct grub_efi_already_handled *next;
33+
struct grub_efi_already_handled **prev;
34+
grub_efi_handle_t handle;
35+
};
36+
37+
static struct grub_efi_already_handled *already_handled;
38+
39+
static struct grub_efi_already_handled *
40+
is_in_list (grub_efi_handle_t handle)
41+
{
42+
struct grub_efi_already_handled *e;
43+
44+
FOR_LIST_ELEMENTS (e, already_handled)
45+
if (e->handle == handle)
46+
return e;
47+
48+
return NULL;
49+
}
50+
51+
static void
52+
free_handle_list (void)
53+
{
54+
struct grub_efi_already_handled *e;
55+
while ((e = already_handled) != NULL)
56+
{
57+
already_handled = already_handled->next;
58+
grub_free (e);
59+
}
60+
}
61+
62+
typedef enum searched_item_flag
63+
{
64+
SEARCHED_ITEM_FLAG_LOOP = 1,
65+
SEARCHED_ITEM_FLAG_RECURSIVE = 2
66+
} searched_item_flags;
67+
68+
typedef struct searched_item
69+
{
70+
grub_efi_guid_t guid;
71+
const char *name;
72+
searched_item_flags flags;
73+
} searched_items;
74+
75+
static grub_err_t
76+
grub_cmd_eficonnect (grub_command_t cmd __attribute__ ((unused)),
77+
int argc, char **args)
78+
{
79+
unsigned s;
80+
searched_items pciroot_items[] =
81+
{
82+
{ GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE }
83+
};
84+
searched_items scsi_items[] =
85+
{
86+
{ GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 },
87+
{ GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP },
88+
{ GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE }
89+
};
90+
searched_items *items = NULL;
91+
unsigned nitems = 0;
92+
grub_err_t grub_err = GRUB_ERR_NONE;
93+
unsigned total_connected = 0;
94+
95+
if (argc != 1)
96+
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
97+
98+
if (grub_strcmp(args[0], "pciroot") == 0)
99+
{
100+
items = pciroot_items;
101+
nitems = ARRAY_SIZE (pciroot_items);
102+
}
103+
else if (grub_strcmp(args[0], "scsi") == 0)
104+
{
105+
items = scsi_items;
106+
nitems = ARRAY_SIZE (scsi_items);
107+
}
108+
else if (grub_strcmp(args[0], N_("all")) == 0)
109+
{
110+
items = NULL;
111+
nitems = 1;
112+
}
113+
else
114+
return grub_error (GRUB_ERR_BAD_ARGUMENT,
115+
N_("unexpected argument `%s'"), args[0]);
116+
117+
for (s = 0; s < nitems; s++)
118+
{
119+
grub_efi_handle_t *handles;
120+
grub_efi_uintn_t num_handles;
121+
unsigned i, connected = 0, loop = 0;
122+
123+
loop:
124+
loop++;
125+
if (items != NULL)
126+
{
127+
grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop);
128+
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
129+
&items[s].guid, 0, &num_handles);
130+
}
131+
else
132+
handles = grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES,
133+
NULL, NULL, &num_handles);
134+
135+
if (!handles)
136+
continue;
137+
138+
for (i = 0; i < num_handles; i++)
139+
{
140+
grub_efi_handle_t handle = handles[i];
141+
grub_efi_status_t status;
142+
unsigned j;
143+
144+
/* Skip already handled handles */
145+
if (is_in_list (handle) != NULL)
146+
{
147+
grub_dprintf ("efi", " handle %p: already processed\n",
148+
handle);
149+
continue;
150+
}
151+
152+
status = grub_efi_connect_controller(handle, NULL, NULL,
153+
!items || items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0);
154+
if (status == GRUB_EFI_SUCCESS)
155+
{
156+
connected++;
157+
total_connected++;
158+
grub_dprintf ("efi", " handle %p: connected\n", handle);
159+
}
160+
else
161+
grub_dprintf ("efi", " handle %p: failed to connect (%d)\n",
162+
handle, (grub_efi_int8_t) status);
163+
164+
struct grub_efi_already_handled *item = grub_malloc(sizeof (*item));
165+
if (!item)
166+
break; /* fatal */
167+
grub_list_push (GRUB_AS_LIST_P (&already_handled), GRUB_AS_LIST (item));
168+
item->handle = handle;
169+
}
170+
171+
grub_free (handles);
172+
if (grub_err != GRUB_ERR_NONE)
173+
break; /* fatal */
174+
175+
if (items && items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected)
176+
{
177+
connected = 0;
178+
goto loop;
179+
}
180+
181+
free_handle_list ();
182+
}
183+
184+
free_handle_list ();
185+
186+
if (total_connected)
187+
grub_efidisk_reenumerate_disks ();
188+
189+
return grub_err;
190+
}
191+
192+
static grub_command_t cmd;
193+
194+
GRUB_MOD_INIT(eficonnect)
195+
{
196+
cmd = grub_register_command ("eficonnect", grub_cmd_eficonnect,
197+
/* TRANSLATORS: only translate 'all' keyword */
198+
N_("pciroot|scsi|all"),
199+
N_("Connect EFI handles."
200+
" If 'pciroot' is specified, connect PCI"
201+
" root EFI handles recursively."
202+
" If 'scsi' is specified, connect SCSI"
203+
" I/O EFI handles recursively."
204+
" If 'all' is specified, connect all"
205+
" EFI handles recursively."));
206+
}
207+
208+
GRUB_MOD_FINI(eficonnect)
209+
{
210+
grub_unregister_command (cmd);
211+
}

grub-core/commands/efi/lsefi.c

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <grub/mm.h>
2020
#include <grub/misc.h>
2121
#include <grub/efi/api.h>
22+
#include <grub/efi/disk.h>
2223
#include <grub/efi/edid.h>
2324
#include <grub/efi/pci.h>
2425
#include <grub/efi/efi.h>

grub-core/disk/efi/efidisk.c

+13
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,19 @@ enumerate_disks (void)
396396
free_devices (devices);
397397
}
398398

399+
void
400+
grub_efidisk_reenumerate_disks (void)
401+
{
402+
free_devices (fd_devices);
403+
free_devices (hd_devices);
404+
free_devices (cd_devices);
405+
fd_devices = 0;
406+
hd_devices = 0;
407+
cd_devices = 0;
408+
409+
enumerate_disks ();
410+
}
411+
399412
static int
400413
grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
401414
grub_disk_pull_t pull)

grub-core/kern/efi/efi.c

+13
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type,
9595
return buffer;
9696
}
9797

98+
grub_efi_status_t
99+
grub_efi_connect_controller (grub_efi_handle_t controller_handle,
100+
grub_efi_handle_t *driver_image_handle,
101+
grub_efi_device_path_protocol_t *remaining_device_path,
102+
grub_efi_boolean_t recursive)
103+
{
104+
grub_efi_boot_services_t *b;
105+
106+
b = grub_efi_system_table->boot_services;
107+
return efi_call_4 (b->connect_controller, controller_handle,
108+
driver_image_handle, remaining_device_path, recursive);
109+
}
110+
98111
void *
99112
grub_efi_open_protocol (grub_efi_handle_t handle,
100113
grub_efi_guid_t *protocol,

include/grub/efi/disk.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ grub_efi_handle_t
2727
EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk);
2828
char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle);
2929

30+
void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void);
31+
3032
void grub_efidisk_init (void);
3133
void grub_efidisk_fini (void);
3234

include/grub/efi/efi.h

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type,
4141
grub_efi_guid_t *protocol,
4242
void *search_key,
4343
grub_efi_uintn_t *num_handles);
44+
grub_efi_status_t
45+
EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle,
46+
grub_efi_handle_t *driver_image_handle,
47+
grub_efi_device_path_protocol_t *remaining_device_path,
48+
grub_efi_boolean_t recursive);
4449
void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle,
4550
grub_efi_guid_t *protocol,
4651
grub_efi_uint32_t attributes);

0 commit comments

Comments
 (0)