1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
--- sys/dev/hyperv/vmbus/hv_hv.c.orig
+++ sys/dev/hyperv/vmbus/hv_hv.c
@@ -33,6 +33,7 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/pcpu.h>
#include <sys/timetc.h>
@@ -50,6 +51,9 @@
static u_int hv_get_timecount(struct timecounter *tc);
+u_int hyperv_features;
+u_int hyperv_recommends;
+
/**
* Globals
*/
@@ -211,8 +215,6 @@
hv_vmbus_g_context.hypercall_page = virt_addr;
- tc_init(&hv_timecounter); /* register virtual timecount */
-
hv_et_init();
return (0);
@@ -427,3 +429,93 @@
wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
}
+static bool
+hyperv_identify(void)
+{
+ u_int regs[4];
+ unsigned int maxLeaf;
+ unsigned int op;
+
+ if (vm_guest != VM_GUEST_HV)
+ return (false);
+
+ op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION;
+ do_cpuid(op, regs);
+ maxLeaf = regs[0];
+ if (maxLeaf < HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS)
+ return (false);
+
+ op = HV_CPU_ID_FUNCTION_HV_INTERFACE;
+ do_cpuid(op, regs);
+ if (regs[0] != 0x31237648 /* HV#1 */)
+ return (false);
+
+ op = HV_CPU_ID_FUNCTION_MS_HV_FEATURES;
+ do_cpuid(op, regs);
+ if ((regs[0] & HV_FEATURE_MSR_HYPERCALL) == 0) {
+ /*
+ * Hyper-V w/o Hypercall is impossible; someone
+ * is faking Hyper-V.
+ */
+ return (false);
+ }
+ hyperv_features = regs[0];
+
+ op = HV_CPU_ID_FUNCTION_MS_HV_VERSION;
+ do_cpuid(op, regs);
+ printf("Hyper-V Version: %d.%d.%d [SP%d]\n",
+ regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]);
+
+ printf(" Features: 0x%b\n", hyperv_features,
+ "\020"
+ "\001VPRUNTIME"
+ "\002TMREFCNT"
+ "\003SYNCIC"
+ "\004SYNCTM"
+ "\005APIC"
+ "\006HYERCALL"
+ "\007VPINDEX"
+ "\010RESET"
+ "\011STATS"
+ "\012REFTSC"
+ "\013IDLE"
+ "\014TMFREQ"
+ "\015DEBUG");
+
+ op = HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION;
+ do_cpuid(op, regs);
+ hyperv_recommends = regs[0];
+ if (bootverbose)
+ printf(" Recommends: %08x %08x\n", regs[0], regs[1]);
+
+ op = HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS;
+ do_cpuid(op, regs);
+ if (bootverbose) {
+ printf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n",
+ regs[0], regs[1], regs[2]);
+ }
+
+ if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE) {
+ op = HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE;
+ do_cpuid(op, regs);
+ if (bootverbose) {
+ printf(" HW Features: %08x AMD: %08x\n",
+ regs[0], regs[3]);
+ }
+ }
+
+ return (true);
+}
+
+static void
+hyperv_init(void *dummy __unused)
+{
+ if (!hyperv_identify())
+ return;
+
+ if (hyperv_features & HV_FEATURE_MSR_TIME_REFCNT) {
+ /* Register virtual timecount */
+ tc_init(&hv_timecounter);
+ }
+}
+SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init, NULL);
--- sys/dev/hyperv/vmbus/hv_vmbus_priv.h.orig
+++ sys/dev/hyperv/vmbus/hv_vmbus_priv.h
@@ -471,10 +471,17 @@
HV_CPU_ID_FUNCTION_MS_HV_VERSION = 0x40000002,
HV_CPU_ID_FUNCTION_MS_HV_FEATURES = 0x40000003,
HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION = 0x40000004,
- HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS = 0x40000005
-
+ HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS = 0x40000005,
+ HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE = 0x40000006
} hv_vmbus_cpuid_function;
+#define HV_FEATURE_MSR_TIME_REFCNT (1 << 1)
+#define HV_FEATURE_MSR_SYNCIC (1 << 2)
+#define HV_FEATURE_MSR_STIMER (1 << 3)
+#define HV_FEATURE_MSR_APIC (1 << 4)
+#define HV_FEATURE_MSR_HYPERCALL (1 << 5)
+#define HV_FEATURE_MSR_GUEST_IDLE (1 << 10)
+
/*
* Define the format of the SIMP register
*/
@@ -628,6 +635,9 @@
extern hv_vmbus_context hv_vmbus_g_context;
extern hv_vmbus_connection hv_vmbus_g_connection;
+extern u_int hyperv_features;
+extern u_int hyperv_recommends;
+
typedef void (*vmbus_msg_handler)(hv_vmbus_channel_msg_header *msg);
typedef struct hv_vmbus_channel_msg_table_entry {
|