Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 31 Dec 2014 07:03:08 +0000 (UTC)
From:      Bryan Venteicher <bryanv@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r276456 - in projects/paravirt/sys/x86: include x86
Message-ID:  <201412310703.sBV738tv065194@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bryanv
Date: Wed Dec 31 07:03:07 2014
New Revision: 276456
URL: https://svnweb.freebsd.org/changeset/base/276456

Log:
  Rework hypervisor detection
  
  To be more like the existing Xen code, detect the supported hypervisors
  from the SI_SUB_HYPERVISOR sysinit instead from identcpu(). I do prefer
  the later method, but preserve the status quo for now.
  
  Hook in the KVM callback when a CPU is stopped and misc cleanup elsewhere.

Modified:
  projects/paravirt/sys/x86/include/hypervisor.h
  projects/paravirt/sys/x86/include/kvm.h
  projects/paravirt/sys/x86/x86/bhyve.c
  projects/paravirt/sys/x86/x86/hypervisor.c
  projects/paravirt/sys/x86/x86/identcpu.c
  projects/paravirt/sys/x86/x86/kvm.c
  projects/paravirt/sys/x86/x86/kvm_clock.c
  projects/paravirt/sys/x86/x86/vmware.c

Modified: projects/paravirt/sys/x86/include/hypervisor.h
==============================================================================
--- projects/paravirt/sys/x86/include/hypervisor.h	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/include/hypervisor.h	Wed Dec 31 07:03:07 2014	(r276456)
@@ -29,18 +29,30 @@
 #ifndef _X86_HYPERVISOR_H_
 #define _X86_HYPERVISOR_H_
 
-typedef int hypervisor_info_identify_t(void);
+#include <sys/param.h>
+#include <sys/kernel.h>
 
-struct hypervisor_info {
-	const char			*hvi_name;
-	const char			*hvi_signature;
-	enum VM_GUEST			 hvi_type;
-	hypervisor_info_identify_t	*hvi_identify;
+typedef void hypervisor_init_func_t(void);
+typedef void hypervisor_op_cpu_stop_t(int);
+
+/*
+ * The guest hypervisor support may provide paravirtualized or have special
+ * requirements for various operations. The callback functions are provided
+ * when a hypervisor is detected and registered.
+ */
+struct hypervisor_ops {
+	hypervisor_op_cpu_stop_t	*hvo_cpu_stop;
 };
 
-void	hypervisor_cpuid_identify(void);
+void	hypervisor_sysinit(void *func);
+void	hypervisor_register(const char *vendor, enum VM_GUEST guest,
+	    struct hypervisor_ops *ops);
 int	hypervisor_cpuid_base(const char *signature, int leaves,
 	    uint32_t *base, uint32_t *high);
 void	hypervisor_print_info(void);
 
+#define HYPERVISOR_SYSINIT(name, func)				\
+	SYSINIT(name ## _hypervisor_sysinit, SI_SUB_HYPERVISOR,	\
+	    SI_ORDER_FIRST, hypervisor_sysinit, func)
+
 #endif /* !_X86_HYPERVISOR_H_ */

Modified: projects/paravirt/sys/x86/include/kvm.h
==============================================================================
--- projects/paravirt/sys/x86/include/kvm.h	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/include/kvm.h	Wed Dec 31 07:03:07 2014	(r276456)
@@ -43,6 +43,8 @@
 
 int		kvm_paravirt_supported(void);
 uint32_t	kvm_get_features(void);
+
+void		kvm_clock_cpu_stop(int restart);
 uint64_t	kvm_clock_tsc_freq(void);
 
 #endif /* !_X86_KVM_H_ */

Modified: projects/paravirt/sys/x86/x86/bhyve.c
==============================================================================
--- projects/paravirt/sys/x86/x86/bhyve.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/bhyve.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -32,33 +32,27 @@ __FBSDID("$FreeBSD$");
 
 #include <x86/hypervisor.h>
 
-static int	bhyve_identify(void);
-
-const struct hypervisor_info bhyve_hypervisor_info = {
-	.hvi_name =		"bhyve",
-	.hvi_signature =	"bhyve bhyve",
-	.hvi_type =		VM_GUEST_BHYVE,
-	.hvi_identify =		bhyve_identify,
-};
-
 static uint32_t bhyve_cpuid_base = -1;
 static uint32_t bhyve_cpuid_high = -1;
 
-static uint32_t
+static int
 bhyve_cpuid_identify(void)
 {
 
 	if (bhyve_cpuid_base == -1) {
-		hypervisor_cpuid_base(bhyve_hypervisor_info.hvi_signature,
-		    0, &bhyve_cpuid_base, &bhyve_cpuid_high);
+		hypervisor_cpuid_base("bhyve bhyve", 0, &bhyve_cpuid_base,
+		    &bhyve_cpuid_high);
 	}
 
-	return (bhyve_cpuid_base);
+	return (bhyve_cpuid_base > 0);
 }
 
-static int
-bhyve_identify(void)
+static void
+bhyve_init(void)
 {
 
-	return (bhyve_cpuid_identify() != 0);
+	if (bhyve_cpuid_identify() != 0)
+		hypervisor_register("bhyve", VM_GUEST_BHYVE, NULL);
 }
+
+HYPERVISOR_SYSINIT(bhyve, bhyve_init);

Modified: projects/paravirt/sys/x86/x86/hypervisor.c
==============================================================================
--- projects/paravirt/sys/x86/x86/hypervisor.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/hypervisor.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
 
 #include <machine/cpufunc.h>
 #include <machine/cpu.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
 
 #include <x86/hypervisor.h>
 
@@ -40,17 +42,41 @@ char hv_vendor[16];
 SYSCTL_STRING(_hw, OID_AUTO, hv_vendor, CTLFLAG_RD, hv_vendor, 0,
     "Hypervisor vendor");
 
-extern const struct hypervisor_info bhyve_hypervisor_info;
-extern const struct hypervisor_info kvm_hypervisor_info;
-extern const struct hypervisor_info vmware_hypervisor_info;
-
-static const struct hypervisor_info *hypervisor_infos[] = {
-	&bhyve_hypervisor_info,
-	&kvm_hypervisor_info,
-	&vmware_hypervisor_info,
-};
+void
+hypervisor_sysinit(void *func)
+{
+	hypervisor_init_func_t *init;
+
+	init = func;
+
+	/*
+	 * Call the init function if we have not already identified the
+	 * hypervisor yet. We assume the hypervisor will announce its
+	 * presence via the CPUID bit.
+	 */
+	if (vm_guest == VM_GUEST_VM && cpu_feature2 & CPUID2_HV)
+		(*init)();
+}
+
+static void
+hypervisor_register_cpu_ops(struct hypervisor_ops *ops)
+{
+
+	if (ops->hvo_cpu_stop != NULL)
+		cpu_ops.cpu_stop = ops->hvo_cpu_stop;
+}
+
+void
+hypervisor_register(const char *vendor, enum VM_GUEST guest,
+    struct hypervisor_ops *ops)
+{
 
-static const struct hypervisor_info *hv_info;
+	strlcpy(hv_vendor, vendor, sizeof(hv_vendor));
+	vm_guest = guest;
+
+	if (ops != NULL)
+		hypervisor_register_cpu_ops(ops);
+}
 
 /*
  * [RFC] CPUID usage for interaction between Hypervisors and Linux.
@@ -76,28 +102,6 @@ hypervisor_cpuid_base(const char *signat
 }
 
 void
-hypervisor_cpuid_identify(void)
-{
-	const struct hypervisor_info *hvi;
-	int i;
-
-	for (i = 0; i < nitems(hypervisor_infos); i++) {
-		hvi = hypervisor_infos[i];
-
-		if (hvi->hvi_identify() != 0) {
-			hv_info = hvi;
-			break;
-		}
-	}
-
-	if (hv_info != NULL) {
-		vm_guest = hvi->hvi_type;
-		strlcpy(hv_vendor, hvi->hvi_name, sizeof(hv_vendor));
-	} else
-		vm_guest = VM_GUEST_VM;
-}
-
-void
 hypervisor_print_info(void)
 {
 

Modified: projects/paravirt/sys/x86/x86/identcpu.c
==============================================================================
--- projects/paravirt/sys/x86/x86/identcpu.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/identcpu.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -1213,11 +1213,12 @@ identify_hypervisor(void)
 	int i;
 
 	/*
-	 * Modern hypervisors set the HV present feature bit, and are then
-	 * identifiable through a special CPUID leaf.
+	 * Modern hypervisors set the HV present feature bit and are then
+	 * identifiable through a special CPUID leaf. Hypervisors we know
+	 * about are later detected via the SI_SUB_HYPERVISOR SYSINIT().
 	 */
 	if (cpu_feature2 & CPUID2_HV) {
-		hypervisor_cpuid_identify();
+		vm_guest = VM_GUEST_VM;
 		return;
 	}
 

Modified: projects/paravirt/sys/x86/x86/kvm.c
==============================================================================
--- projects/paravirt/sys/x86/x86/kvm.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/kvm.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -30,46 +30,45 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 
+#include <machine/cpufunc.h>
+
 #include <x86/hypervisor.h>
 #include <x86/kvm.h>
 
-static int		kvm_identify(void);
-static uint32_t		kvm_cpuid_identify(void);
+static int		kvm_cpuid_identify(void);
+static void		kvm_cpu_stop(int);
 
-const struct hypervisor_info kvm_hypervisor_info = {
-	.hvi_name =		"KVM",
-	.hvi_signature =	"KVMKVMKVM\0\0",
-	.hvi_type =		VM_GUEST_KVM,
-	.hvi_identify =		kvm_identify,
+static struct hypervisor_ops kvm_ops = {
+	.hvo_cpu_stop =		kvm_cpu_stop,
 };
 
 static uint32_t kvm_cpuid_base = -1;
 static uint32_t kvm_cpuid_high = -1;
 
-static uint32_t
+static int
 kvm_cpuid_identify(void)
 {
 
 	if (kvm_cpuid_base == -1) {
-		hypervisor_cpuid_base(kvm_hypervisor_info.hvi_signature,
-		    0, &kvm_cpuid_base, &kvm_cpuid_high);
+		hypervisor_cpuid_base("KVMKVMKVM\0\0", 0, &kvm_cpuid_base,
+		    &kvm_cpuid_high);
 	}
 
-	return (kvm_cpuid_base);
+	return (kvm_cpuid_base > 0);
 }
 
-static int
-kvm_identify(void)
+static void
+kvm_cpu_stop(int restarted)
 {
 
-	return (kvm_cpuid_identify() != 0);
+	kvm_clock_cpu_stop(restarted);
 }
 
 int
 kvm_paravirt_supported(void)
 {
 
-	return (kvm_cpuid_base != -1);
+	return (kvm_cpuid_base > 0);
 }
 
 uint32_t
@@ -77,7 +76,20 @@ kvm_get_features(void)
 {
 	u_int regs[4];
 
-	do_cpuid(kvm_cpuid_identify() | KVM_CPUID_FEATURES_LEAF, regs);
+	if (kvm_paravirt_supported())
+		do_cpuid(kvm_cpuid_base | KVM_CPUID_FEATURES_LEAF, regs);
+	else
+		regs[0] = 0;
 
 	return (regs[0]);
 }
+
+static void
+kvm_init(void)
+{
+
+	if (kvm_cpuid_identify() != 0)
+		hypervisor_register("KVM", VM_GUEST_KVM, &kvm_ops);
+}
+
+HYPERVISOR_SYSINIT(kvm, kvm_init);

Modified: projects/paravirt/sys/x86/x86/kvm_clock.c
==============================================================================
--- projects/paravirt/sys/x86/x86/kvm_clock.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/kvm_clock.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -55,8 +55,9 @@ static struct timecounter kvm_clock_time
 	1000,
 };
 
-static uint32_t kvm_clock_wall_clock_msr;
-static uint32_t kvm_clock_system_time_msr;
+static int		kvm_clock_registered;
+static uint32_t		kvm_clock_wall_clock_msr;
+static uint32_t		kvm_clock_system_time_msr;
 
 uint64_t
 kvm_clock_tsc_freq(void)
@@ -102,11 +103,23 @@ kvm_clock_pcpu_system_time(void *arg)
 	wrmsr(kvm_clock_system_time_msr, data);
 }
 
+void
+kvm_clock_cpu_stop(int restarted)
+{
+
+	/*
+	 * The per-CPU shared data area must be unregistered when the CPU
+	 * is stopped. Otherwise if the guest is rebooted, the host can
+	 * continue to write into the shared data area causing corruption.
+	 */
+	if (kvm_clock_registered != 0)
+		kvm_clock_pcpu_system_time(&restarted);
+}
+
 static void
 kvm_clock_init(void)
 {
 	uint32_t features;
-	int enable;
 
 	if (vm_guest != VM_GUEST_KVM || !kvm_paravirt_supported())
 		return;
@@ -122,9 +135,9 @@ kvm_clock_init(void)
 	} else
 		return;
 
-	enable = 1;
+	kvm_clock_registered = 1;
 	smp_rendezvous(smp_no_rendevous_barrier, kvm_clock_pcpu_system_time,
-	    smp_no_rendevous_barrier, &enable);
+	    smp_no_rendevous_barrier, &kvm_clock_registered);
 
 	tc_init(&kvm_clock_timecounter);
 }

Modified: projects/paravirt/sys/x86/x86/vmware.c
==============================================================================
--- projects/paravirt/sys/x86/x86/vmware.c	Wed Dec 31 07:00:36 2014	(r276455)
+++ projects/paravirt/sys/x86/x86/vmware.c	Wed Dec 31 07:03:07 2014	(r276456)
@@ -34,40 +34,23 @@ __FBSDID("$FreeBSD$");
 #include <x86/hypervisor.h>
 #include <x86/vmware.h>
 
-static int		vmware_identify(void);
-static uint32_t		vmware_cpuid_identify(void);
-
-const struct hypervisor_info vmware_hypervisor_info = {
-	.hvi_name =		"VMware",
-	.hvi_signature =	"VMwareVMware",
-	.hvi_type =		VM_GUEST_VMWARE,
-	.hvi_identify =		vmware_identify,
-};
-
 static uint32_t vmware_cpuid_base = -1;
 static uint32_t vmware_cpuid_high = -1;
 
-static uint32_t
+static int
 vmware_cpuid_identify(void)
 {
 
+	/*
+	 * KB1009458: Mechanisms to determine if software is running in a
+	 * VMware virtual machine: http://kb.vmware.com/kb/1009458
+	 */
 	if (vmware_cpuid_base == -1) {
-		hypervisor_cpuid_base(vmware_hypervisor_info.hvi_signature,
-		    0, &vmware_cpuid_base, &vmware_cpuid_high);
+		hypervisor_cpuid_base("VMwareVMware", 0, &vmware_cpuid_base,
+		    &vmware_cpuid_high);
 	}
 
-	return (vmware_cpuid_base);
-}
-
-/*
- * KB1009458: Mechanisms to determine if software is running in a VMware
- * virtual machine: http://kb.vmware.com/kb/1009458
- */
-static int
-vmware_identify(void)
-{
-
-	return (vmware_cpuid_identify() != 0);
+	return (vmware_cpuid_base > 0);
 }
 
 uint64_t
@@ -89,3 +72,13 @@ vmware_tsc_freq(void)
 
 	return (freq);
 }
+
+static void
+vmware_init(void)
+{
+
+	if (vmware_cpuid_identify() != 0)
+		hypervisor_register("VMware", VM_GUEST_VMWARE, NULL);
+}
+
+HYPERVISOR_SYSINIT(vmware, vmware_init);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201412310703.sBV738tv065194>