diff options
author | Noah Meyerhans <nmeyerha@amazon.com> | 2021-04-30 09:30:52 -0700 |
---|---|---|
committer | Noah Meyerhans <nmeyerha@amazon.com> | 2021-04-30 12:26:22 -0700 |
commit | ce35037928f4c4c931088256853f07804ec7d235 (patch) | |
tree | 723f1c42bc6e30dfefef7f0edce91a52de3c95f1 /src/basic/virt.c | |
parent | 382a46d129899ca9027b07c325102cab173dd563 (diff) | |
download | systemd-ce35037928f4c4c931088256853f07804ec7d235.tar.gz |
Use BIOS characteristics to distinguish EC2 bare-metal from VMs
DMI vendor information fields do not provide enough information for us to
distinguish between Amazon EC2 virtual machines and bare-metal instances.
SMBIOS provides a BIOS Information
table (https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf
Ch. 7) that provides a field to indicate that the current machine is a virtual
machine. On EC2 virtual machine instances, this field is set, while bare-metal
instances leave this unset, so we inspect the field via the kernel's
/sys/firemware/dmi/entries interface.
Fixes #18929
Diffstat (limited to 'src/basic/virt.c')
-rw-r--r-- | src/basic/virt.c | 65 |
1 files changed, 61 insertions, 4 deletions
diff --git a/src/basic/virt.c b/src/basic/virt.c index 03c0e431ad..9b8518bd08 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -21,6 +21,12 @@ #include "string-util.h" #include "virt.h" +enum { + SMBIOS_VM_BIT_SET, + SMBIOS_VM_BIT_UNSET, + SMBIOS_VM_BIT_UNKNOWN, +}; + #if defined(__i386__) || defined(__x86_64__) static const char *const vm_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_XEN] = "XenVMMXenVMM", @@ -134,9 +140,8 @@ static int detect_vm_device_tree(void) { #endif } -static int detect_vm_dmi(void) { #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) - +static int detect_vm_dmi_vendor(void) { static const char *const dmi_vendors[] = { "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */ "/sys/class/dmi/id/sys_vendor", @@ -181,11 +186,63 @@ static int detect_vm_dmi(void) { return dmi_vendor_table[j].id; } } -#endif + return VIRTUALIZATION_NONE; +} + +static int detect_vm_smbios(void) { + /* The SMBIOS BIOS Charateristics Extension Byte 2 (Section 2.1.2.2 of + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf), specifies that + * the 4th bit being set indicates a VM. The BIOS Characteristics table is exposed via the kernel in + * /sys/firmware/dmi/entries/0-0. Note that in the general case, this bit being unset should not + * imply that the system is running on bare-metal. For example, QEMU 3.1.0 (with or without KVM) + * with SeaBIOS does not set this bit. */ + _cleanup_free_ char *s = NULL; + size_t readsize; + int r; + + r = read_full_virtual_file("/sys/firmware/dmi/entries/0-0/raw", &s, &readsize); + if (r < 0) { + log_debug_errno(r, "Unable to read /sys/firmware/dmi/entries/0-0/raw, ignoring: %m"); + return SMBIOS_VM_BIT_UNKNOWN; + } + if (readsize < 20 || s[1] < 20) { + /* The spec indicates that byte 1 contains the size of the table, 0x12 + the number of + * extension bytes. The data we're interested in is in extension byte 2, which would be at + * 0x13. If we didn't read that much data, or if the BIOS indicates that we don't have that + * much data, we don't infer anything from the SMBIOS. */ + log_debug("Only read %zu bytes from /sys/firmware/dmi/entries/0-0/raw (expected 20)", readsize); + return SMBIOS_VM_BIT_UNKNOWN; + } - log_debug("No virtualization found in DMI"); + uint8_t byte = (uint8_t) s[19]; + if (byte & (1U<<4)) { + log_debug("DMI BIOS Extension table indicates virtualization"); + return SMBIOS_VM_BIT_SET; + } + log_debug("DMI BIOS Extension table does not indicate virtualization"); + return SMBIOS_VM_BIT_UNSET; +} +#endif /* defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) */ + +static int detect_vm_dmi(void) { +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) + + int r; + r = detect_vm_dmi_vendor(); + /* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between Amazon EC2 + * virtual machines and bare-metal instances, so we need to look at SMBIOS. */ + if (r == VIRTUALIZATION_AMAZON && detect_vm_smbios() == SMBIOS_VM_BIT_UNSET) + return VIRTUALIZATION_NONE; + + /* If we haven't identified a VM, but the firmware indicates that there is one, indicate as much. We + * have no further information about what it is. */ + if (r == VIRTUALIZATION_NONE && detect_vm_smbios() == SMBIOS_VM_BIT_SET) + return VIRTUALIZATION_VM_OTHER; + return r; +#else return VIRTUALIZATION_NONE; +#endif } static int detect_vm_xen(void) { |