summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-05-03 17:48:13 +0200
committerGitHub <noreply@github.com>2021-05-03 17:48:13 +0200
commit16ecdf3c80087613c3bb424b09185a7389d74553 (patch)
treeea9bd44bb6174f89a27f4f6ecd1578f0ae8b8b5a
parent19d25fdec120c679e34eb4f3276bedcdfd6f5bed (diff)
parentce35037928f4c4c931088256853f07804ec7d235 (diff)
downloadsystemd-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.xml7
-rw-r--r--man/systemd.unit.xml1
-rw-r--r--src/basic/virt.c74
-rw-r--r--src/basic/virt.h1
-rw-r--r--src/shared/machine-id-setup.c9
-rw-r--r--src/test/test-condition.c1
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"