diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-05-03 17:48:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-03 17:48:13 +0200 |
commit | 16ecdf3c80087613c3bb424b09185a7389d74553 (patch) | |
tree | ea9bd44bb6174f89a27f4f6ecd1578f0ae8b8b5a | |
parent | 19d25fdec120c679e34eb4f3276bedcdfd6f5bed (diff) | |
parent | ce35037928f4c4c931088256853f07804ec7d235 (diff) | |
download | systemd-16ecdf3c80087613c3bb424b09185a7389d74553.tar.gz |
Merge pull request #19403 from nmeyerhans/dmi-entries
virt: differentiate Amazon EC2 bare-metal from VM instances using SMBIOS data
-rw-r--r-- | man/systemd-detect-virt.xml | 7 | ||||
-rw-r--r-- | man/systemd.unit.xml | 1 | ||||
-rw-r--r-- | src/basic/virt.c | 74 | ||||
-rw-r--r-- | src/basic/virt.h | 1 | ||||
-rw-r--r-- | src/shared/machine-id-setup.c | 9 | ||||
-rw-r--r-- | src/test/test-condition.c | 1 |
6 files changed, 81 insertions, 12 deletions
diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 654cf9b84c..14bfd19b62 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -69,7 +69,12 @@ <row> <entry><varname>kvm</varname></entry> - <entry>Linux KVM kernel virtual machine, with whatever software, except Oracle Virtualbox</entry> + <entry>Linux KVM kernel virtual machine, in combination with QEMU. Not used for other virtualizers using the KVM interfaces, such as Oracle VirtualBox or Amazon EC2 Nitro, see below.</entry> + </row> + + <row> + <entry><varname>amazon</varname></entry> + <entry>Amazon EC2 Nitro using Linux KVM</entry> </row> <row> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 7f37f01ef9..b7a5ec0362 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1192,6 +1192,7 @@ <literal>container</literal> to test against a generic type of virtualization solution, or one of <literal>qemu</literal>, <literal>kvm</literal>, + <literal>amazon</literal>, <literal>zvm</literal>, <literal>vmware</literal>, <literal>microsoft</literal>, diff --git a/src/basic/virt.c b/src/basic/virt.c index 335f59d6fc..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", @@ -149,6 +154,7 @@ static int detect_vm_dmi(void) { int id; } dmi_vendor_table[] = { { "KVM", VIRTUALIZATION_KVM }, + { "Amazon EC2", VIRTUALIZATION_AMAZON }, { "QEMU", VIRTUALIZATION_QEMU }, { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */ { "VMW", VIRTUALIZATION_VMWARE }, @@ -180,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) { @@ -344,8 +402,9 @@ int detect_vm(void) { /* We have to use the correct order here: * - * → First, try to detect Oracle Virtualbox, even if it uses KVM, as well as Xen even if it cloaks as Microsoft - * Hyper-V. Attempt to detect uml at this stage also since it runs as a user-process nested inside other VMs. + * → First, try to detect Oracle Virtualbox and Amazon EC2 Nitro, even if they use KVM, as well as Xen even if + * it cloaks as Microsoft Hyper-V. Attempt to detect uml at this stage also since it runs as a user-process + * nested inside other VMs. * * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is * overwritten. @@ -353,7 +412,7 @@ int detect_vm(void) { * → Third, try to detect from DMI. */ dmi = detect_vm_dmi(); - if (IN_SET(dmi, VIRTUALIZATION_ORACLE, VIRTUALIZATION_XEN)) { + if (IN_SET(dmi, VIRTUALIZATION_ORACLE, VIRTUALIZATION_XEN, VIRTUALIZATION_AMAZON)) { r = dmi; goto finish; } @@ -914,6 +973,7 @@ bool has_cpu_with_flag(const char *flag) { static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_NONE] = "none", [VIRTUALIZATION_KVM] = "kvm", + [VIRTUALIZATION_AMAZON] = "amazon", [VIRTUALIZATION_QEMU] = "qemu", [VIRTUALIZATION_BOCHS] = "bochs", [VIRTUALIZATION_XEN] = "xen", diff --git a/src/basic/virt.h b/src/basic/virt.h index 378c7c4d23..1eafbe2cbe 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -10,6 +10,7 @@ enum { VIRTUALIZATION_VM_FIRST, VIRTUALIZATION_KVM = VIRTUALIZATION_VM_FIRST, + VIRTUALIZATION_AMAZON, VIRTUALIZATION_QEMU, VIRTUALIZATION_BOCHS, VIRTUALIZATION_XEN, diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index dd4c74b19c..d2f989272d 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -60,13 +60,14 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) { return 0; } - } else if (detect_vm() == VIRTUALIZATION_KVM) { + } else if (IN_SET(detect_vm(), VIRTUALIZATION_KVM, VIRTUALIZATION_AMAZON, VIRTUALIZATION_QEMU)) { - /* If we are not running in a container, see if we are running in qemu/kvm and a - * machine ID was passed in via -uuid on the qemu/kvm command line */ + /* If we are not running in a container, see if we are running in a VM that provides + * a system UUID via the SMBIOS/DMI interfaces. Such environments include QEMU/KVM + * with the -uuid on the qemu command line or the Amazon EC2 Nitro hypervisor. */ if (id128_get_product(ret) >= 0) { - log_info("Initializing machine ID from KVM UUID."); + log_info("Initializing machine ID from VM UUID."); return 0; } } diff --git a/src/test/test-condition.c b/src/test/test-condition.c index db15fe3136..adba383fdd 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -575,6 +575,7 @@ static void test_condition_test_virtualization(void) { NULSTR_FOREACH(virt, "kvm\0" + "amazon\0" "qemu\0" "bochs\0" "xen\0" |