diff --git a/spa/include/spa/support/cpu.h b/spa/include/spa/support/cpu.h index 80f7aefd4..455e1a2fa 100644 --- a/spa/include/spa/support/cpu.h +++ b/spa/include/spa/support/cpu.h @@ -80,13 +80,31 @@ struct spa_cpu { struct spa_interface iface; }; #define SPA_CPU_FLAG_ARMV8 (1 << 6) #define SPA_CPU_FORCE_AUTODETECT ((uint32_t)-1) + +#define SPA_CPU_VM_NONE (0) +#define SPA_CPU_VM_OTHER (1 << 0) +#define SPA_CPU_VM_KVM (1 << 1) +#define SPA_CPU_VM_QEMU (1 << 2) +#define SPA_CPU_VM_BOCHS (1 << 3) +#define SPA_CPU_VM_XEN (1 << 4) +#define SPA_CPU_VM_UML (1 << 5) +#define SPA_CPU_VM_VMWARE (1 << 6) +#define SPA_CPU_VM_ORACLE (1 << 7) +#define SPA_CPU_VM_MICROSOFT (1 << 8) +#define SPA_CPU_VM_ZVM (1 << 9) +#define SPA_CPU_VM_PARALLELS (1 << 10) +#define SPA_CPU_VM_BHYVE (1 << 11) +#define SPA_CPU_VM_QNX (1 << 12) +#define SPA_CPU_VM_ACRN (1 << 13) +#define SPA_CPU_VM_POWERVM (1 << 14) + /** * methods */ struct spa_cpu_methods { /** the version of the methods. This can be used to expand this structure in the future */ -#define SPA_VERSION_CPU_METHODS 0 +#define SPA_VERSION_CPU_METHODS 1 uint32_t version; /** get CPU flags */ @@ -100,6 +118,9 @@ struct spa_cpu_methods { /** get maximum required alignment of data */ uint32_t (*get_max_align) (void *object); + + /* check if running in a VM. Since:1 */ + uint32_t (*get_vm_type) (void *object); }; #define spa_cpu_method(o,method,version,...) \ @@ -115,6 +136,7 @@ struct spa_cpu_methods { #define spa_cpu_force_flags(c,f) spa_cpu_method(c, force_flags, 0, f) #define spa_cpu_get_count(c) spa_cpu_method(c, get_count, 0) #define spa_cpu_get_max_align(c) spa_cpu_method(c, get_max_align, 0) +#define spa_cpu_get_vm_type(c) spa_cpu_method(c, get_vm_type, 1) /** keys can be given when initializing the cpu handle */ #define SPA_KEY_CPU_FORCE "cpu.force" /**< force cpu flags */ diff --git a/spa/plugins/support/cpu.c b/spa/plugins/support/cpu.c index 6dbd80a7d..032b6669a 100644 --- a/spa/plugins/support/cpu.c +++ b/spa/plugins/support/cpu.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef __FreeBSD__ #include @@ -52,8 +53,25 @@ struct impl { uint32_t force; uint32_t count; uint32_t max_align; + uint32_t vm_type; }; +static char *read_file(const char *name, char *buffer, size_t len) +{ + int n, fd; + + if ((fd = open(name, O_RDONLY | O_CLOEXEC, 0)) < 0) + return NULL; + + if ((n = read(fd, buffer, len)) < 0) { + close(fd); + return NULL; + } + buffer[n] = 0; + close(fd); + return buffer; +} + # if defined (__i386__) || defined (__x86_64__) #include "cpu-x86.c" #define init(t) x86_init(t) @@ -116,12 +134,66 @@ impl_cpu_get_max_align(void *object) return impl->max_align; } +static uint32_t +impl_cpu_get_vm_type(void *object) +{ + struct impl *impl = object; + + if (impl->vm_type != 0) + return impl->vm_type; + +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) + 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", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; + static const struct { + const char *vendor; + int id; + } dmi_vendor_table[] = { + { "KVM", SPA_CPU_VM_KVM }, + { "QEMU", SPA_CPU_VM_QEMU }, + { "VMware", SPA_CPU_VM_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */ + { "VMW", SPA_CPU_VM_VMWARE }, + { "innotek GmbH", SPA_CPU_VM_ORACLE }, + { "Oracle Corporation", SPA_CPU_VM_ORACLE }, + { "Xen", SPA_CPU_VM_XEN }, + { "Bochs", SPA_CPU_VM_BOCHS }, + { "Parallels", SPA_CPU_VM_PARALLELS }, + /* https://wiki.freebsd.org/bhyve */ + { "BHYVE", SPA_CPU_VM_BHYVE }, + }; + uint32_t i, j; + + for (i = 0; i < SPA_N_ELEMENTS(dmi_vendors); i++) { + char buffer[256], *s; + + if ((s = read_file(dmi_vendors[i], buffer, sizeof(buffer))) == NULL) + continue; + + for (j = 0; j < SPA_N_ELEMENTS(dmi_vendor_table); j++) { + if (strstr(s, dmi_vendor_table[j].vendor) == s) { + spa_log_debug(impl->log, "Virtualization %s found in DMI (%s)", + s, dmi_vendors[i]); + impl->vm_type = dmi_vendor_table[j].id; + goto done; + } + } + } +done: +#endif + return impl->vm_type; +} + static const struct spa_cpu_methods impl_cpu = { SPA_VERSION_CPU_METHODS, .get_flags = impl_cpu_get_flags, .force_flags = impl_cpu_force_flags, .get_count = impl_cpu_get_count, .get_max_align = impl_cpu_get_max_align, + .get_vm_type = impl_cpu_get_vm_type, }; static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)