1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #if defined(__i386__) || defined(__x86_64__)
7 #include "device-path-util.h"
9 #include "efi-string.h"
10 #include "proto/device-path.h"
11 #include "string-util-fundamental.h"
14 #define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \
15 { 0x1428f772, 0xb64a, 0x441e, { 0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7 } }
17 #define VMM_BOOT_ORDER_GUID \
18 { 0x668f4529, 0x63d0, 0x4bb5, { 0xb6, 0x5d, 0x6f, 0xbb, 0x9d, 0x36, 0xa4, 0x4a } }
20 /* detect direct boot */
21 bool is_direct_boot(EFI_HANDLE device
) {
23 VENDOR_DEVICE_PATH
*dp
; /* NB: Alignment of this structure might be quirky! */
25 err
= BS
->HandleProtocol(device
, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL
), (void **) &dp
);
26 if (err
!= EFI_SUCCESS
)
29 /* 'qemu -kernel systemd-bootx64.efi' */
30 if (dp
->Header
.Type
== MEDIA_DEVICE_PATH
&&
31 dp
->Header
.SubType
== MEDIA_VENDOR_DP
&&
32 memcmp(&dp
->Guid
, MAKE_GUID_PTR(QEMU_KERNEL_LOADER_FS_MEDIA
), sizeof(EFI_GUID
)) == 0) /* Don't change to efi_guid_equal() because EFI device path objects are not necessarily aligned! */
35 /* loaded from firmware volume (sd-boot added to ovmf) */
36 if (dp
->Header
.Type
== MEDIA_DEVICE_PATH
&&
37 dp
->Header
.SubType
== MEDIA_PIWG_FW_VOL_DP
)
44 * Try find ESP when not loaded from ESP
46 * Inspect all filesystems known to the firmware, try find the ESP. In case VMMBootOrderNNNN variables are
47 * present they are used to inspect the filesystems in the specified order. When nothing was found or the
48 * variables are not present the function will do one final search pass over all filesystems.
50 * Recent OVMF builds store the qemu boot order (as specified using the bootindex property on the qemu
51 * command line) in VMMBootOrderNNNN. The variables contain a device path.
53 * Example qemu command line:
54 * qemu -virtio-scsi-pci,addr=14.0 -device scsi-cd,scsi-id=4,bootindex=1
57 * VMMBootOrder0000 = PciRoot(0x0)/Pci(0x14,0x0)/Scsi(0x4,0x0)
59 EFI_STATUS
vmm_open(EFI_HANDLE
*ret_vmm_dev
, EFI_FILE
**ret_vmm_dir
) {
60 _cleanup_free_ EFI_HANDLE
*handles
= NULL
;
62 EFI_STATUS err
, dp_err
;
67 /* Make sure all file systems have been initialized. Only do this in VMs as this is slow
68 * on some real firmwares. */
69 (void) reconnect_all_drivers();
71 /* find all file system handles */
72 err
= BS
->LocateHandleBuffer(
73 ByProtocol
, MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
), NULL
, &n_handles
, &handles
);
74 if (err
!= EFI_SUCCESS
)
77 for (size_t order
= 0;; order
++) {
78 _cleanup_free_ EFI_DEVICE_PATH
*dp
= NULL
;
80 _cleanup_free_ char16_t
*order_str
= xasprintf("VMMBootOrder%04zx", order
);
81 dp_err
= efivar_get_raw(MAKE_GUID_PTR(VMM_BOOT_ORDER
), order_str
, (char **) &dp
, NULL
);
83 for (size_t i
= 0; i
< n_handles
; i
++) {
84 _cleanup_(file_closep
) EFI_FILE
*root_dir
= NULL
, *efi_dir
= NULL
;
87 err
= BS
->HandleProtocol(
88 handles
[i
], MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL
), (void **) &fs
);
89 if (err
!= EFI_SUCCESS
)
92 /* check against VMMBootOrderNNNN (if set) */
93 if (dp_err
== EFI_SUCCESS
&& !device_path_startswith(fs
, dp
))
96 err
= open_volume(handles
[i
], &root_dir
);
97 if (err
!= EFI_SUCCESS
)
100 /* simple ESP check */
101 err
= root_dir
->Open(root_dir
, &efi_dir
, (char16_t
*) u
"\\EFI",
103 EFI_FILE_READ_ONLY
| EFI_FILE_DIRECTORY
);
104 if (err
!= EFI_SUCCESS
)
107 *ret_vmm_dev
= handles
[i
];
108 *ret_vmm_dir
= TAKE_PTR(root_dir
);
112 if (dp_err
!= EFI_SUCCESS
)
113 return EFI_NOT_FOUND
;
115 assert_not_reached();
118 static bool cpuid_in_hypervisor(void) {
119 #if defined(__i386__) || defined(__x86_64__)
120 unsigned eax
, ebx
, ecx
, edx
;
122 /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
125 if (__get_cpuid(1, &eax
, &ebx
, &ecx
, &edx
) == 0)
128 if (FLAGS_SET(ecx
, 0x80000000U
))
135 #define SMBIOS_TABLE_GUID \
136 GUID_DEF(0xeb9d2d31, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
137 #define SMBIOS3_TABLE_GUID \
138 GUID_DEF(0xf2fd1544, 0x9794, 0x4a2c, 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94)
141 uint8_t anchor_string
[4];
142 uint8_t entry_point_structure_checksum
;
143 uint8_t entry_point_length
;
144 uint8_t major_version
;
145 uint8_t minor_version
;
146 uint16_t max_structure_size
;
147 uint8_t entry_point_revision
;
148 uint8_t formatted_area
[5];
149 uint8_t intermediate_anchor_string
[5];
150 uint8_t intermediate_checksum
;
151 uint16_t table_length
;
152 uint32_t table_address
;
153 uint16_t number_of_smbios_structures
;
154 uint8_t smbios_bcd_revision
;
155 } _packed_ SmbiosEntryPoint
;
158 uint8_t anchor_string
[5];
159 uint8_t entry_point_structure_checksum
;
160 uint8_t entry_point_length
;
161 uint8_t major_version
;
162 uint8_t minor_version
;
164 uint8_t entry_point_revision
;
166 uint32_t table_maximum_size
;
167 uint64_t table_address
;
168 } _packed_ Smbios3EntryPoint
;
174 } _packed_ SmbiosHeader
;
179 uint8_t bios_version
;
180 uint16_t bios_segment
;
181 uint8_t bios_release_date
;
183 uint64_t bios_characteristics
;
184 uint8_t bios_characteristics_ext
[2];
185 } _packed_ SmbiosTableType0
;
187 static void *find_smbios_configuration_table(uint64_t *ret_size
) {
190 Smbios3EntryPoint
*entry3
= find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE
));
191 if (entry3
&& memcmp(entry3
->anchor_string
, "_SM3_", 5) == 0 &&
192 entry3
->entry_point_length
<= sizeof(*entry3
)) {
193 *ret_size
= entry3
->table_maximum_size
;
194 return PHYSICAL_ADDRESS_TO_POINTER(entry3
->table_address
);
197 SmbiosEntryPoint
*entry
= find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE
));
198 if (entry
&& memcmp(entry
->anchor_string
, "_SM_", 4) == 0 &&
199 entry
->entry_point_length
<= sizeof(*entry
)) {
200 *ret_size
= entry
->table_length
;
201 return PHYSICAL_ADDRESS_TO_POINTER(entry
->table_address
);
207 static SmbiosHeader
*get_smbios_table(uint8_t type
) {
209 uint8_t *p
= find_smbios_configuration_table(&size
);
214 if (size
< sizeof(SmbiosHeader
))
217 SmbiosHeader
*header
= (SmbiosHeader
*) p
;
220 if (header
->type
== 127)
223 if (size
< header
->length
)
226 if (header
->type
== type
)
227 return header
; /* Yay! */
229 /* Skip over formatted area. */
230 size
-= header
->length
;
233 /* Skip over string table. */
235 while (size
> 0 && *p
!= '\0') {
244 /* Double NUL terminates string table. */
257 static bool smbios_in_hypervisor(void) {
258 /* Look up BIOS Information (Type 0). */
259 SmbiosTableType0
*type0
= (SmbiosTableType0
*) get_smbios_table(0);
260 if (!type0
|| type0
->header
.length
< sizeof(SmbiosTableType0
))
263 /* Bit 4 of 2nd BIOS characteristics extension bytes indicates virtualization. */
264 return FLAGS_SET(type0
->bios_characteristics_ext
[1], 1 << 4);
267 bool in_hypervisor(void) {
268 static int cache
= -1;
272 cache
= cpuid_in_hypervisor() || smbios_in_hypervisor();