From 47fc64a0671a149a1fa3f8b1e039e6c44bab2153 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sat, 8 Feb 2014 10:45:23 +0100
Subject: [PATCH] WIP kernel: Add VM detection.

---
 sys/kern/subr_param.c                  | 56 +++++++++++++++++++++++++++++++++-
 sys/platform/pc32/i386/initcpu.c       |  6 ++--
 sys/platform/pc64/x86_64/initcpu.c     |  5 +--
 sys/platform/vkernel/platform/init.c   |  2 +-
 sys/platform/vkernel64/platform/init.c |  2 +-
 sys/sys/kernel.h                       | 17 +++++++++++
 sys/sys/systm.h                        |  1 +
 7 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/sys/kern/subr_param.c b/sys/kern/subr_param.c
index af9ad46..82663ba 100644
--- a/sys/kern/subr_param.c
+++ b/sys/kern/subr_param.c
@@ -33,7 +33,6 @@
  *
  *	@(#)param.c	8.3 (Berkeley) 8/20/94
  * $FreeBSD: src/sys/kern/subr_param.c,v 1.42.2.10 2002/03/09 21:05:47 silby Exp $
- * $DragonFly: src/sys/kern/subr_param.c,v 1.7 2005/06/26 22:03:22 dillon Exp $
  */
 
 #include "opt_param.h"
@@ -99,6 +98,61 @@ u_quad_t	sgrowsiz;			/* amount to grow stack */
  */
 struct	buf *swbuf;
 
+struct vmm_bname {
+	const char *str;
+	enum vmm_guest_type type;
+};
+
+static struct vmm_bname vmm_bnames[] = {
+	{ "QEMU",	VMM_GUEST_QEMU },	/* QEMU */
+	{ "Plex86",	VMM_GUEST_PLEX86 },	/* Plex86 */
+	{ "Bochs",	VMM_GUEST_BOCHS },	/* Bochs */
+	{ "Xen",	VMM_GUEST_XEN },	/* Xen */
+	{ "BHYVE",	VMM_GUEST_BHYVE },	/* bhyve */
+	{ "Seabios",	VMM_GUEST_KVM},		/* KVM */
+	{ NULL, 0 }
+};
+
+static struct vmm_bname vmm_pnames[] = {
+	{ "VMware Virtual Platform",	VMM_GUEST_VMWARE },	/* VMWare VM */
+	{ "Virtual Machine",		VMM_GUEST_VPC },	/* M$ VirtualPC */
+	{ "VirtualBox",			VMM_GUEST_VBOX },	/* Sun VirtualBox */
+	{ "Parallels Virtual Platform",	VMM_GUEST_PARALLELS },	/* Parallels VM */
+	{ "KVM",			VMM_GUEST_KVM },	/* KVM */
+	{ NULL, 0 }
+};
+
+
+/*
+ * Detect known Virtual Machine hosts by inspecting the emulated BIOS.
+ */
+enum vmm_guest_type
+detect_virtual(void)
+{
+	char *sysenv;
+	int i;
+
+	sysenv = kgetenv("smbios.bios.vendor");
+	if (sysenv != NULL) {
+		for (i = 0; vmm_bnames[i].str != NULL; i++)
+			if (strcmp(sysenv, vmm_bnames[i].str) == 0) {
+				kfreeenv(sysenv);
+				return (vmm_bnames[i].type);
+			}
+		kfreeenv(sysenv);
+	}
+	sysenv = kgetenv("smbios.system.product");
+	if (sysenv != NULL) {
+		for (i = 0; vmm_pnames[i].str != NULL; i++)
+			if (strcmp(sysenv, vmm_pnames[i].str) == 0) {
+				kfreeenv(sysenv);
+				return (vmm_pnames[i].type);
+			}
+		kfreeenv(sysenv);
+	}
+	return (VMM_GUEST_NONE);
+}
+
 /*
  * Boot time overrides that are not scaled against main memory
  */
diff --git a/sys/platform/pc32/i386/initcpu.c b/sys/platform/pc32/i386/initcpu.c
index 6df43b5..1ffe84d 100644
--- a/sys/platform/pc32/i386/initcpu.c
+++ b/sys/platform/pc32/i386/initcpu.c
@@ -359,8 +359,10 @@ initializecpu(void)
 	}
 	enable_sse();
 
-	if (cpu_feature2 & CPUID2_VMM)
-		vmm_guest = 1;
+	/* Check if we are running in a hypervisor. */
+	vmm_guest = detect_virtual();
+	if (vmm_guest == VMM_GUEST_NONE && (cpu_feature2 & CPUID2_VMM))
+		vmm_guest = VMM_GUEST_UNKNOWN;
 
 	if (cpu_vendor_id == CPU_VENDOR_AMD) {
 		switch((cpu_id & 0xFF0000)) {
diff --git a/sys/platform/pc64/x86_64/initcpu.c b/sys/platform/pc64/x86_64/initcpu.c
index 6893531..a820086 100644
--- a/sys/platform/pc64/x86_64/initcpu.c
+++ b/sys/platform/pc64/x86_64/initcpu.c
@@ -164,8 +164,9 @@ initializecpu(int cpu)
 	}
 
 	/* Check if we are running in a hypervisor. */
-	if (cpu_feature2 & CPUID2_VMM)
-		vmm_guest = 1;
+	vmm_guest = detect_virtual();
+	if (vmm_guest == VMM_GUEST_NONE && (cpu_feature2 & CPUID2_VMM))
+		vmm_guest = VMM_GUEST_UNKNOWN;
 
 #if !defined(CPU_DISABLE_AVX)
 	/*Check for XSAVE and AVX support and enable if available.*/
diff --git a/sys/platform/vkernel/platform/init.c b/sys/platform/vkernel/platform/init.c
index fc773ba..cae0b13 100644
--- a/sys/platform/vkernel/platform/init.c
+++ b/sys/platform/vkernel/platform/init.c
@@ -369,7 +369,7 @@ main(int ac, char **av)
 	setrealcpu();
 	init_kqueue();
 
-	vmm_guest = 1;
+	vmm_guest = VMM_GUEST_VKERNEL;
 
 	/*
 	 * Check TSC
diff --git a/sys/platform/vkernel64/platform/init.c b/sys/platform/vkernel64/platform/init.c
index 29fd114..68f1f36 100644
--- a/sys/platform/vkernel64/platform/init.c
+++ b/sys/platform/vkernel64/platform/init.c
@@ -422,7 +422,7 @@ int main(int ac, char **av) {
 	setrealcpu();
 	init_kqueue();
 
-	vmm_guest = 1;
+	vmm_guest = VMM_GUEST_VKERNEL;
 
 	/*
 	 * Check TSC
diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h
index 7347f9e..c66788b 100644
--- a/sys/sys/kernel.h
+++ b/sys/sys/kernel.h
@@ -87,6 +87,23 @@ extern int lbolt;			/* once a second sleep address */
 extern void *lbolt_syncer;		/* approx 1 hz but may be sped up */
 extern int vmm_guest; 			/* Running as virtual machine guest? */
 
+enum vmm_guest_type {
+	VMM_GUEST_NONE = 0,
+	VMM_GUEST_QEMU,
+	VMM_GUEST_PLEX86,
+	VMM_GUEST_BOCHS,
+	VMM_GUEST_XEN,
+	VMM_GUEST_BHYVE,
+	VMM_GUEST_KVM,
+	VMM_GUEST_VMWARE,
+	VMM_GUEST_VPC,
+	VMM_GUEST_VBOX,
+	VMM_GUEST_PARALLELS,
+	VMM_GUEST_VKERNEL,
+	VMM_GUEST_UNKNOWN,
+	VM_LAST
+};
+
 /*
  * Enumerated types for known system startup interfaces.
  *
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
index ac94cbe..d0926ea 100644
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -169,6 +169,7 @@ void	cpu_rootconf (void);
 void	cpu_vmspace_alloc(struct vmspace *);
 void	cpu_vmspace_free(struct vmspace *);
 void	cpu_vkernel_trap(struct trapframe *, int);
+enum vmm_guest_type detect_virtual(void);
 void	set_user_TLS(void);
 void	set_vkernel_fp(struct trapframe *);
 int	kvm_access_check(vm_offset_t, vm_offset_t, int);
-- 
1.8.2.2

