]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/vmm.c
Merge pull request #25602 from fbuihuu/fix-TEST-73-LOCALE
[thirdparty/systemd.git] / src / boot / efi / vmm.c
CommitLineData
8fec4f95
GH
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <efi.h>
4#include <efilib.h>
5#include <stdbool.h>
6
7#include "drivers.h"
8#include "efi-string.h"
9#include "string-util-fundamental.h"
10#include "util.h"
11
12#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \
13 { 0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7 }}
14
15#define VMM_BOOT_ORDER_GUID \
16 { 0x668f4529, 0x63d0, 0x4bb5, {0xb6, 0x5d, 0x6f, 0xbb, 0x9d, 0x36, 0xa4, 0x4a }}
17
18/* detect direct boot */
19bool is_direct_boot(EFI_HANDLE device) {
20 EFI_STATUS err;
21 VENDOR_DEVICE_PATH *dp;
22
23 err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &dp);
24 if (err != EFI_SUCCESS)
25 return false;
26
27 /* 'qemu -kernel systemd-bootx64.efi' */
28 if (dp->Header.Type == MEDIA_DEVICE_PATH &&
29 dp->Header.SubType == MEDIA_VENDOR_DP &&
30 memcmp(&dp->Guid, &(EFI_GUID)QEMU_KERNEL_LOADER_FS_MEDIA_GUID, sizeof(EFI_GUID)) == 0)
31 return true;
32
33 /* loaded from firmware volume (sd-boot added to ovmf) */
34 if (dp->Header.Type == MEDIA_DEVICE_PATH &&
35 dp->Header.SubType == MEDIA_PIWG_FW_VOL_DP)
36 return true;
37
38 return false;
39}
40
41static bool device_path_startswith(const EFI_DEVICE_PATH *dp, const EFI_DEVICE_PATH *start) {
42 if (!start)
43 return true;
44 if (!dp)
45 return false;
46 for (;;) {
47 if (IsDevicePathEnd(start))
48 return true;
49 if (IsDevicePathEnd(dp))
50 return false;
51 size_t l1 = DevicePathNodeLength(start);
52 size_t l2 = DevicePathNodeLength(dp);
53 if (l1 != l2)
54 return false;
55 if (memcmp(dp, start, l1) != 0)
56 return false;
57 start = NextDevicePathNode(start);
58 dp = NextDevicePathNode(dp);
59 }
60}
61
62/*
63 * Try find ESP when not loaded from ESP
64 *
65 * Inspect all filesystems known to the firmware, try find the ESP. In case VMMBootOrderNNNN variables are
66 * present they are used to inspect the filesystems in the specified order. When nothing was found or the
67 * variables are not present the function will do one final search pass over all filesystems.
68 *
69 * Recent OVMF builds store the qemu boot order (as specified using the bootindex property on the qemu
70 * command line) in VMMBootOrderNNNN. The variables contain a device path.
71 *
72 * Example qemu command line:
73 * qemu -virtio-scsi-pci,addr=14.0 -device scsi-cd,scsi-id=4,bootindex=1
74 *
75 * Resulting variable:
76 * VMMBootOrder0000 = PciRoot(0x0)/Pci(0x14,0x0)/Scsi(0x4,0x0)
77 */
78EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
79 _cleanup_free_ EFI_HANDLE *handles = NULL;
80 size_t n_handles;
81 EFI_STATUS err, dp_err;
82
83 assert(ret_vmm_dev);
84 assert(ret_vmm_dir);
85
f6d59e2e
JJ
86 /* Make sure all file systems have been initialized. Only do this in VMs as this is slow
87 * on some real firmwares. */
88 (void) reconnect_all_drivers();
89
8fec4f95
GH
90 /* find all file system handles */
91 err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
92 if (err != EFI_SUCCESS)
93 return err;
94
95 for (size_t order = 0;; order++) {
96 _cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
97 char16_t order_str[STRLEN("VMMBootOrder") + 4 + 1];
98
99 SPrint(order_str, sizeof(order_str), u"VMMBootOrder%04x", order);
100 dp_err = efivar_get_raw(&(EFI_GUID)VMM_BOOT_ORDER_GUID, order_str, (char**)&dp, NULL);
101
102 for (size_t i = 0; i < n_handles; i++) {
103 _cleanup_(file_closep) EFI_FILE *root_dir = NULL, *efi_dir = NULL;
104 EFI_DEVICE_PATH *fs;
105
106 err = BS->HandleProtocol(handles[i], &DevicePathProtocol, (void **) &fs);
107 if (err != EFI_SUCCESS)
108 return err;
109
110 /* check against VMMBootOrderNNNN (if set) */
111 if (dp_err == EFI_SUCCESS && !device_path_startswith(fs, dp))
112 continue;
113
114 err = open_volume(handles[i], &root_dir);
115 if (err != EFI_SUCCESS)
116 continue;
117
118 /* simple ESP check */
119 err = root_dir->Open(root_dir, &efi_dir, (char16_t*) u"\\EFI",
120 EFI_FILE_MODE_READ,
121 EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY);
122 if (err != EFI_SUCCESS)
123 continue;
124
125 *ret_vmm_dev = handles[i];
126 *ret_vmm_dir = TAKE_PTR(root_dir);
127 return EFI_SUCCESS;
128 }
129
130 if (dp_err != EFI_SUCCESS)
131 return EFI_NOT_FOUND;
132 }
133 assert_not_reached();
134}