Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ New in 2.02:
* Prefer pmtimer for TSC calibration.

* New/improved platform support:
* New `efifwsetup', `lsefi' and `connectefi` commands on EFI platforms.
* New `efifwsetup', `lsefi' commands on EFI platforms.
* New `cmosdump' and `cmosset' commands on platforms with CMOS support.
* New command `pcidump' for PCI platforms.
* Improve opcode parsing in ACPI halt implementation.
Expand Down
26 changes: 26 additions & 0 deletions docs/grub.texi
Original file line number Diff line number Diff line change
Expand Up @@ -4009,6 +4009,7 @@ you forget a command, you can run the command @command{help}
* cmostest:: Test bit in CMOS
* cmp:: Compare two files
* configfile:: Load a configuration file
* connectefi:: Connect EFI devices (on EFI platforms only)
* cpuid:: Check for CPU features
* crc:: Compute or check CRC32 checksums
* cryptomount:: Mount a crypto device
Expand Down Expand Up @@ -4233,6 +4234,11 @@ syntax}) to grab the first sector of the current partition with @samp{+1}.
If you specify the option @option{--force}, then load @var{file} forcibly,
whether it has a correct signature or not. This is required when you want to
load a defective boot loader, such as SCO UnixWare 7.1.

On EFI platforms, when initially network booting, or booting from another disk,
it may be required to connect the EFI devices using @command{connectefi}
command (@pxref{connectefi}) prior to chainloading to the disk, otherwise no
disk may show up or the EFI partition on the disk may not be readable.
@end deffn


Expand Down Expand Up @@ -4305,6 +4311,26 @@ after @command{configfile} returns.
@end deffn


@node connectefi
@subsection connectefi

@deffn Command connectefi pciroot|disk|scsi|all
When used with @samp{pciroot} parameter, all PCI devices are recursively
connected, which may be a slow operation, depending on the hardware.

When used with @samp{disk} or @samp{scsi} parameter (deprecated), only SCSI I/O
and DISK I/O devices are connected; this is the recommended usage when
chainloading to internal disk, unless some disks are not detected anyway, in
such case @samp{all} may be used and a bug be reported to enhance the
implementation.

When used with @samp{all} parameter, all EFI devices are recursively connected,
which may be an extremely slow operation, depending on the hardware.

Note: This command is only available on EFI platforms.
@end deffn


@node cpuid
@subsection cpuid

Expand Down
162 changes: 95 additions & 67 deletions grub-core/commands/efi/connectefi.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2022 Free Software Foundation, Inc.
* Copyright (C) 2025 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -15,123 +15,145 @@
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/

#include <grub/types.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/efi/api.h>
#include <grub/efi/pci.h>
#include <grub/efi/efi.h>
#include <grub/efi/disk.h>
#include <grub/command.h>
#include <grub/err.h>
#include <grub/i18n.h>

GRUB_MOD_LICENSE ("GPLv3+");

typedef struct handle_list
grub_efi_status_t
grub_efi_connect_controller (grub_efi_handle_t controller_handle,
grub_efi_handle_t *driver_image_handle,
grub_efi_device_path_protocol_t *remaining_device_path,
grub_efi_boolean_t recursive)
{
grub_efi_handle_t handle;
struct handle_list *next;
} handle_list_t;
grub_efi_boot_services_t *b;

static handle_list_t *already_handled = NULL;
b = grub_efi_system_table->boot_services;
return efi_call_4 (b->connect_controller, controller_handle,
driver_image_handle, remaining_device_path, recursive);
}

static grub_err_t
add_handle (grub_efi_handle_t handle)
struct grub_efi_handle_list
{
handle_list_t *e;
e = grub_malloc (sizeof (*e));
if (! e)
return grub_errno;
e->handle = handle;
e->next = already_handled;
already_handled = e;
return GRUB_ERR_NONE;
}
struct grub_efi_handle_list *next;
struct grub_efi_handle_list **prev;
grub_efi_handle_t handle;
};

typedef struct grub_efi_handle_list grub_efi_handle_list_t;

static int
is_in_list (grub_efi_handle_t handle)
static bool
is_in_list (grub_efi_handle_t handle, grub_efi_handle_list_t *handles)
{
handle_list_t *e;
for (e = already_handled; e != NULL; e = e->next)
grub_efi_handle_list_t *e;

FOR_LIST_ELEMENTS (e, handles)
if (e->handle == handle)
return 1;
return 0;
return true;

return false;
}

static void
free_handle_list (void)
free_handle_list (grub_efi_handle_list_t **handles_p)
{
handle_list_t *e;
while ((e = already_handled) != NULL)
grub_efi_handle_list_t *e;

while ((e = *handles_p) != NULL)
{
already_handled = already_handled->next;
*handles_p = e->next;
grub_free (e);
}
}

typedef enum searched_item_flag
enum searched_item_flag
{
SEARCHED_ITEM_FLAG_LOOP = 1,
SEARCHED_ITEM_FLAG_RECURSIVE = 2
} searched_item_flags;
};

typedef enum searched_item_flag searched_item_flag_t;

typedef struct searched_item
struct searched_item
{
grub_efi_guid_t guid;
const char *name;
searched_item_flags flags;
} searched_items;
searched_item_flag_t flags;
};

typedef struct searched_item searched_item_t;

static grub_err_t
grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
unsigned s;
searched_items pciroot_items[] =
int s;
searched_item_t pciroot_items[] =
{
{ GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE }
};
searched_items scsi_items[] =
searched_item_t disk_items[] =
{
{ GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 },
{ GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP },
{ GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE }
{ GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE },
{ GRUB_EFI_DISK_IO_PROTOCOL_GUID, "DISK I/O", SEARCHED_ITEM_FLAG_RECURSIVE }
};
searched_items *items = NULL;
unsigned nitems = 0;
searched_item_t *items = NULL;
int nitems = 0;
grub_err_t grub_err = GRUB_ERR_NONE;
unsigned total_connected = 0;
bool connected_devices = false;
grub_efi_handle_list_t *already_handled = NULL;

if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));

if (grub_strcmp(args[0], N_("pciroot")) == 0)
if (grub_strcmp (args[0], "pciroot") == 0)
{
items = pciroot_items;
nitems = ARRAY_SIZE (pciroot_items);
}
else if (grub_strcmp(args[0], N_("scsi")) == 0)
else if ((grub_strcmp (args[0], "disk") == 0) ||
(grub_strcmp (args[0], "scsi") == 0))
{
items = disk_items;
nitems = ARRAY_SIZE (disk_items);
}
else if (grub_strcmp (args[0], "all") == 0)
{
items = scsi_items;
nitems = ARRAY_SIZE (scsi_items);
items = NULL;
nitems = 1;
}
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("unexpected argument `%s'"), args[0]);
N_("unexpected argument: `%s'"), args[0]);

for (s = 0; s < nitems; s++)
{
grub_efi_handle_t *handles;
grub_efi_uintn_t num_handles;
unsigned i, connected = 0, loop = 0;
int i, loop = 0;
bool connected = false;

loop:
loop++;
grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop);

handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
&items[s].guid, 0, &num_handles);
if (items != NULL)
{
grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop);
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
&items[s].guid, 0, &num_handles);
}
else
handles = grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES,
NULL, NULL, &num_handles);

if (!handles)
continue;
Expand All @@ -140,48 +162,52 @@ grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)),
{
grub_efi_handle_t handle = handles[i];
grub_efi_status_t status;
unsigned j;
int j;

/* Skip already handled handles */
if (is_in_list (handle))
if (is_in_list (handle, already_handled))
{
grub_dprintf ("efi", " handle %p: already processed\n",
handle);
continue;
}

status = grub_efi_connect_controller(handle, NULL, NULL,
items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0);
status = grub_efi_connect_controller (handle, NULL, NULL,
!items || items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0);
if (status == GRUB_EFI_SUCCESS)
{
connected++;
total_connected++;
connected = true;
connected_devices = true;
grub_dprintf ("efi", " handle %p: connected\n", handle);
}
else
grub_dprintf ("efi", " handle %p: failed to connect (%d)\n",
handle, (grub_efi_int8_t) status);
grub_dprintf ("efi", " handle %p: failed to connect ("
PRIuGRUB_EFI_UINTN_T ")\n", handle, status);

if ((grub_err = add_handle (handle)) != GRUB_ERR_NONE)
grub_efi_handle_list_t *item;
item = grub_malloc (sizeof (*item));
if (item == NULL)
break; /* fatal */
grub_list_push (GRUB_AS_LIST_P (&already_handled), GRUB_AS_LIST (item));
item->handle = handle;
}

grub_free (handles);
if (grub_err != GRUB_ERR_NONE)
break; /* fatal */

if (items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected)
if (items && items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected)
{
connected = 0;
connected = false;
goto loop;
}

free_handle_list ();
free_handle_list (&already_handled);
}

free_handle_list ();
free_handle_list (&already_handled);

if (total_connected)
if (connected_devices)
grub_efidisk_reenumerate_disks ();

return grub_err;
Expand All @@ -192,12 +218,14 @@ static grub_command_t cmd;
GRUB_MOD_INIT(connectefi)
{
cmd = grub_register_command ("connectefi", grub_cmd_connectefi,
N_("pciroot|scsi"),
"pciroot|disk|scsi|all",
N_("Connect EFI handles."
" If 'pciroot' is specified, connect PCI"
" root EFI handles recursively."
" If 'scsi' is specified, connect SCSI"
" I/O EFI handles recursively."));
" If 'disk' or 'scsi' is specified, connect"
" SCSI and DISK I/O EFI handles recursively."
" If 'all' is specified, connect all"
" EFI handles recursively."));
}

GRUB_MOD_FINI(connectefi)
Expand Down
5 changes: 3 additions & 2 deletions grub-core/commands/efi/lsefi.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2012 Free Software Foundation, Inc.
* Copyright (C) 2025 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/

#include <grub/types.h>
#include <grub/mm.h>
#include <grub/misc.h>
Expand All @@ -36,7 +37,7 @@ struct known_protocol
const char *name;
} known_protocols[] =
{
{ GRUB_EFI_DISK_IO_GUID, "disk" },
{ GRUB_EFI_DISK_IO_PROTOCOL_GUID, "disk" },
{ GRUB_EFI_BLOCK_IO_GUID, "block" },
{ GRUB_EFI_SERIAL_IO_GUID, "serial" },
{ GRUB_EFI_SIMPLE_NETWORK_GUID, "network" },
Expand Down
8 changes: 4 additions & 4 deletions grub-core/disk/efi/efidisk.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
* Copyright (C) 2025 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -402,9 +402,9 @@ grub_efidisk_reenumerate_disks (void)
free_devices (fd_devices);
free_devices (hd_devices);
free_devices (cd_devices);
fd_devices = 0;
hd_devices = 0;
cd_devices = 0;
fd_devices = NULL;
hd_devices = NULL;
cd_devices = NULL;

enumerate_disks ();
}
Expand Down
Loading