boot: Detect hypervisors using SMBIOS info

This allows skipping secure boot enrollment wait time on other arches.
This commit is contained in:
Jan Janssen
2023-01-10 14:44:29 +01:00
parent 32284ffc12
commit ba27939274
7 changed files with 163 additions and 28 deletions

View File

@@ -364,6 +364,7 @@ common_sources = files(
'assert.c',
'console.c',
'devicetree.c',
'drivers.c',
'disk.c',
'efi-string.c',
'graphics.c',
@@ -375,13 +376,12 @@ common_sources = files(
'secure-boot.c',
'ticks.c',
'util.c',
'vmm.c',
)
systemd_boot_sources = files(
'boot.c',
'drivers.c',
'shim.c',
'vmm.c',
)
stub_sources = files(

View File

@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "console.h"
#include "sbat.h"
#include "secure-boot.h"
#include "console.h"
#include "util.h"
#include "vmm.h"
bool secure_boot_enabled(void) {
bool secure = false; /* avoid false maybe-uninitialized warning */

View File

@@ -5,6 +5,7 @@
#include "ticks.h"
#include "util.h"
#include "vmm.h"
#ifdef __x86_64__
static uint64_t ticks_read(void) {

View File

@@ -2,9 +2,6 @@
#include <efi.h>
#include <efilib.h>
#if defined(__i386__) || defined(__x86_64__)
# include <cpuid.h>
#endif
#include "ticks.h"
#include "util.h"
@@ -748,20 +745,6 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
return EFI_SUCCESS;
}
#if defined(__i386__) || defined(__x86_64__)
bool in_hypervisor(void) {
uint32_t eax, ebx, ecx, edx;
/* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
* environment. */
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
return false;
return !!(ecx & 0x80000000U);
}
#endif
void *find_configuration_table(const EFI_GUID *guid) {
for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, guid))

View File

@@ -210,14 +210,6 @@ EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
#if defined(__i386__) || defined(__x86_64__)
bool in_hypervisor(void);
#else
static inline bool in_hypervisor(void) {
return false;
}
#endif
static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
return memcmp(a, b, sizeof(EFI_GUID)) == 0;
}

View File

@@ -3,6 +3,9 @@
#include <efi.h>
#include <efilib.h>
#include <stdbool.h>
#if defined(__i386__) || defined(__x86_64__)
# include <cpuid.h>
#endif
#include "drivers.h"
#include "efi-string.h"
@@ -132,3 +135,156 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
}
assert_not_reached();
}
static bool cpuid_in_hypervisor(void) {
#if defined(__i386__) || defined(__x86_64__)
unsigned eax, ebx, ecx, edx;
/* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
* environment. */
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
return false;
if (FLAGS_SET(ecx, 0x80000000U))
return true;
#endif
return false;
}
typedef struct {
uint8_t anchor_string[4];
uint8_t entry_point_structure_checksum;
uint8_t entry_point_length;
uint8_t major_version;
uint8_t minor_version;
uint16_t max_structure_size;
uint8_t entry_point_revision;
uint8_t formatted_area[5];
uint8_t intermediate_anchor_string[5];
uint8_t intermediate_checksum;
uint16_t table_length;
uint32_t table_address;
uint16_t number_of_smbios_structures;
uint8_t smbios_bcd_revision;
} _packed_ SmbiosEntryPoint;
typedef struct {
uint8_t anchor_string[5];
uint8_t entry_point_structure_checksum;
uint8_t entry_point_length;
uint8_t major_version;
uint8_t minor_version;
uint8_t docrev;
uint8_t entry_point_revision;
uint8_t reserved;
uint32_t table_maximum_size;
uint64_t table_address;
} _packed_ Smbios3EntryPoint;
typedef struct {
uint8_t type;
uint8_t length;
uint8_t handle[2];
} _packed_ SmbiosHeader;
typedef struct {
SmbiosHeader header;
uint8_t vendor;
uint8_t bios_version;
uint16_t bios_segment;
uint8_t bios_release_date;
uint8_t bios_size;
uint64_t bios_characteristics;
uint8_t bios_characteristics_ext[2];
} _packed_ SmbiosTableType0;
static void *find_smbios_configuration_table(uint64_t *ret_size) {
assert(ret_size);
Smbios3EntryPoint *entry3 = find_configuration_table(&(EFI_GUID) SMBIOS3_TABLE_GUID);
if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
entry3->entry_point_length <= sizeof(*entry3)) {
*ret_size = entry3->table_maximum_size;
return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
}
SmbiosEntryPoint *entry = find_configuration_table(&(EFI_GUID) SMBIOS_TABLE_GUID);
if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
entry->entry_point_length <= sizeof(*entry)) {
*ret_size = entry->table_length;
return PHYSICAL_ADDRESS_TO_POINTER(entry->table_address);
}
return NULL;
}
static SmbiosHeader *get_smbios_table(uint8_t type) {
uint64_t size = 0;
uint8_t *p = find_smbios_configuration_table(&size);
if (!p)
return false;
for (;;) {
if (size < sizeof(SmbiosHeader))
return NULL;
SmbiosHeader *header = (SmbiosHeader *) p;
/* End of table. */
if (header->type == 127)
return NULL;
if (size < header->length)
return NULL;
if (header->type == type)
return header; /* Yay! */
/* Skip over formatted area. */
size -= header->length;
p += header->length;
/* Skip over string table. */
for (;;) {
while (size > 0 && *p != '\0') {
p++;
size--;
}
if (size == 0)
return NULL;
p++;
size--;
/* Double NUL terminates string table. */
if (*p == '\0') {
if (size == 0)
return NULL;
p++;
break;
}
}
}
return NULL;
}
static bool smbios_in_hypervisor(void) {
/* Look up BIOS Information (Type 0). */
SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0);
if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
return false;
/* Bit 4 of 2nd BIOS characteristics extension bytes indicates virtualization. */
return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
}
bool in_hypervisor(void) {
static int cache = -1;
if (cache >= 0)
return cache;
cache = cpuid_in_hypervisor() || smbios_in_hypervisor();
return cache;
}

View File

@@ -6,3 +6,5 @@
bool is_direct_boot(EFI_HANDLE device);
EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
bool in_hypervisor(void);