]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Detect hypervisors using SMBIOS info
authorJan Janssen <medhefgo@web.de>
Tue, 10 Jan 2023 13:44:29 +0000 (14:44 +0100)
committerJan Janssen <medhefgo@web.de>
Mon, 16 Jan 2023 17:51:21 +0000 (18:51 +0100)
This allows skipping secure boot enrollment wait time on other arches.

src/boot/efi/meson.build
src/boot/efi/secure-boot.c
src/boot/efi/ticks.c
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/efi/vmm.c
src/boot/efi/vmm.h

index 10ab5ce5ce0a4241083e086cdbc4fb2ba7e7cebd..16342f891ec0503caddb750faae3c2d6987e0231 100644 (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(
index 62128681341a1cec8714a236017e1f2f2c57b73b..55c9ba5d4c71986efaa7d8f854512f4827a69f12 100644 (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 */
index 1b74ba15d07915390ce38ee9ba7eb4c7b9fd4571..2f6ff878ca9a6e8d868aabed569b5dc22c785395 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "ticks.h"
 #include "util.h"
+#include "vmm.h"
 
 #ifdef __x86_64__
 static uint64_t ticks_read(void) {
index b1c19a5c9bba5f0c0e8d2f663a43c87fa26cd1fa..202332c2d898ef320a0613a12c212a173572cb0f 100644 (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))
index 836f223cc053a44179b9d9558c6375ef2d4d106a..32ecea7d6aea9b55f4a87541421dcb995b94172f 100644 (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;
 }
index 10d4a75ab208667c663f75628d829506d0d43a5a..3dfa92b58d4b9905e4e92b83f3c665631533f7a3 100644 (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;
+}
index 7bac1a324a364c631819c8d76063a701d773e90f..e98ec74af1cc6e4ac169f26bd6f161db950162e2 100644 (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);