Merge pull request #24855 from kraxel/qemu
better qemu support (handle direct kernel boot etc).
This commit is contained in:
@@ -525,6 +525,23 @@
|
||||
<filename>/etc/kernel/tries</filename> when a boot loader entry is first created.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Using systemd-boot in virtual machines.</title>
|
||||
|
||||
<para>When using qemu with OVMF (UEFI Firmware for virtual machines) the <option>-kernel</option> switch
|
||||
works not only for linux kernels, but for any EFI binary, including sd-boot and unified linux
|
||||
kernels. Example command line for loading sd-boot on x64:</para>
|
||||
|
||||
<para>
|
||||
<command>qemu-system-x86_64 <replaceable>[ ... ]</replaceable>
|
||||
-kernel /usr/lib/systemd/boot/efi/systemd-bootx64.efi</command>
|
||||
</para>
|
||||
|
||||
<para>systemd-boot will detect that it was started directly instead of being loaded from ESP and will
|
||||
search for the ESP in that case, taking into account boot order information from the hypervisor (if
|
||||
available).</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
|
||||
+9
-1
@@ -16,6 +16,7 @@
|
||||
#include "linux.h"
|
||||
#include "measure.h"
|
||||
#include "pe.h"
|
||||
#include "vmm.h"
|
||||
#include "random-seed.h"
|
||||
#include "secure-boot.h"
|
||||
#include "shim.h"
|
||||
@@ -2646,6 +2647,13 @@ static void config_load_all_entries(
|
||||
config_default_entry_select(config);
|
||||
}
|
||||
|
||||
static EFI_STATUS discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, EFI_FILE **ret_dir) {
|
||||
if (is_direct_boot(loaded_image->DeviceHandle))
|
||||
return vmm_open(&loaded_image->DeviceHandle, ret_dir);
|
||||
else
|
||||
return open_volume(loaded_image->DeviceHandle, ret_dir);
|
||||
}
|
||||
|
||||
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
|
||||
_cleanup_(file_closep) EFI_FILE *root_dir = NULL;
|
||||
@@ -2682,7 +2690,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
|
||||
export_variables(loaded_image, loaded_image_path, init_usec);
|
||||
|
||||
err = open_volume(loaded_image->DeviceHandle, &root_dir);
|
||||
err = discover_root_dir(loaded_image, &root_dir);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Unable to open root directory: %r", err);
|
||||
|
||||
|
||||
@@ -389,6 +389,7 @@ systemd_boot_sources = files(
|
||||
'boot.c',
|
||||
'drivers.c',
|
||||
'random-seed.c',
|
||||
'vmm.c',
|
||||
'shim.c',
|
||||
'xbootldr.c',
|
||||
)
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "drivers.h"
|
||||
#include "efi-string.h"
|
||||
#include "string-util-fundamental.h"
|
||||
#include "util.h"
|
||||
|
||||
#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \
|
||||
{ 0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7 }}
|
||||
|
||||
#define VMM_BOOT_ORDER_GUID \
|
||||
{ 0x668f4529, 0x63d0, 0x4bb5, {0xb6, 0x5d, 0x6f, 0xbb, 0x9d, 0x36, 0xa4, 0x4a }}
|
||||
|
||||
/* detect direct boot */
|
||||
bool is_direct_boot(EFI_HANDLE device) {
|
||||
EFI_STATUS err;
|
||||
VENDOR_DEVICE_PATH *dp;
|
||||
|
||||
err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &dp);
|
||||
if (err != EFI_SUCCESS)
|
||||
return false;
|
||||
|
||||
/* 'qemu -kernel systemd-bootx64.efi' */
|
||||
if (dp->Header.Type == MEDIA_DEVICE_PATH &&
|
||||
dp->Header.SubType == MEDIA_VENDOR_DP &&
|
||||
memcmp(&dp->Guid, &(EFI_GUID)QEMU_KERNEL_LOADER_FS_MEDIA_GUID, sizeof(EFI_GUID)) == 0)
|
||||
return true;
|
||||
|
||||
/* loaded from firmware volume (sd-boot added to ovmf) */
|
||||
if (dp->Header.Type == MEDIA_DEVICE_PATH &&
|
||||
dp->Header.SubType == MEDIA_PIWG_FW_VOL_DP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool device_path_startswith(const EFI_DEVICE_PATH *dp, const EFI_DEVICE_PATH *start) {
|
||||
if (!start)
|
||||
return true;
|
||||
if (!dp)
|
||||
return false;
|
||||
for (;;) {
|
||||
if (IsDevicePathEnd(start))
|
||||
return true;
|
||||
if (IsDevicePathEnd(dp))
|
||||
return false;
|
||||
size_t l1 = DevicePathNodeLength(start);
|
||||
size_t l2 = DevicePathNodeLength(dp);
|
||||
if (l1 != l2)
|
||||
return false;
|
||||
if (memcmp(dp, start, l1) != 0)
|
||||
return false;
|
||||
start = NextDevicePathNode(start);
|
||||
dp = NextDevicePathNode(dp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try find ESP when not loaded from ESP
|
||||
*
|
||||
* Inspect all filesystems known to the firmware, try find the ESP. In case VMMBootOrderNNNN variables are
|
||||
* present they are used to inspect the filesystems in the specified order. When nothing was found or the
|
||||
* variables are not present the function will do one final search pass over all filesystems.
|
||||
*
|
||||
* Recent OVMF builds store the qemu boot order (as specified using the bootindex property on the qemu
|
||||
* command line) in VMMBootOrderNNNN. The variables contain a device path.
|
||||
*
|
||||
* Example qemu command line:
|
||||
* qemu -virtio-scsi-pci,addr=14.0 -device scsi-cd,scsi-id=4,bootindex=1
|
||||
*
|
||||
* Resulting variable:
|
||||
* VMMBootOrder0000 = PciRoot(0x0)/Pci(0x14,0x0)/Scsi(0x4,0x0)
|
||||
*/
|
||||
EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
|
||||
_cleanup_free_ EFI_HANDLE *handles = NULL;
|
||||
size_t n_handles;
|
||||
EFI_STATUS err, dp_err;
|
||||
|
||||
assert(ret_vmm_dev);
|
||||
assert(ret_vmm_dir);
|
||||
|
||||
/* find all file system handles */
|
||||
err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
for (size_t order = 0;; order++) {
|
||||
_cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
|
||||
char16_t order_str[STRLEN("VMMBootOrder") + 4 + 1];
|
||||
|
||||
SPrint(order_str, sizeof(order_str), u"VMMBootOrder%04x", order);
|
||||
dp_err = efivar_get_raw(&(EFI_GUID)VMM_BOOT_ORDER_GUID, order_str, (char**)&dp, NULL);
|
||||
|
||||
for (size_t i = 0; i < n_handles; i++) {
|
||||
_cleanup_(file_closep) EFI_FILE *root_dir = NULL, *efi_dir = NULL;
|
||||
EFI_DEVICE_PATH *fs;
|
||||
|
||||
err = BS->HandleProtocol(handles[i], &DevicePathProtocol, (void **) &fs);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
/* check against VMMBootOrderNNNN (if set) */
|
||||
if (dp_err == EFI_SUCCESS && !device_path_startswith(fs, dp))
|
||||
continue;
|
||||
|
||||
err = open_volume(handles[i], &root_dir);
|
||||
if (err != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* simple ESP check */
|
||||
err = root_dir->Open(root_dir, &efi_dir, (char16_t*) u"\\EFI",
|
||||
EFI_FILE_MODE_READ,
|
||||
EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY);
|
||||
if (err != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
*ret_vmm_dev = handles[i];
|
||||
*ret_vmm_dir = TAKE_PTR(root_dir);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (dp_err != EFI_SUCCESS)
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
assert_not_reached();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
|
||||
bool is_direct_boot(EFI_HANDLE device);
|
||||
EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
|
||||
Reference in New Issue
Block a user