summaryrefslogtreecommitdiff
path: root/src/basic/virt.c
diff options
context:
space:
mode:
authorNoah Meyerhans <nmeyerha@amazon.com>2021-04-30 09:30:52 -0700
committerNoah Meyerhans <nmeyerha@amazon.com>2021-04-30 12:26:22 -0700
commitce35037928f4c4c931088256853f07804ec7d235 (patch)
tree723f1c42bc6e30dfefef7f0edce91a52de3c95f1 /src/basic/virt.c
parent382a46d129899ca9027b07c325102cab173dd563 (diff)
downloadsystemd-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.c65
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) {