xref: /freebsd/sys/dev/hyperv/vmbus/hyperv.c (revision b7bb4816a083c233b49ade84cecd74b37f072ebf)
1*b7bb4816SSepherosa Ziehau /*-
2*b7bb4816SSepherosa Ziehau  * Copyright (c) 2009-2012,2016 Microsoft Corp.
3*b7bb4816SSepherosa Ziehau  * Copyright (c) 2012 NetApp Inc.
4*b7bb4816SSepherosa Ziehau  * Copyright (c) 2012 Citrix Inc.
5*b7bb4816SSepherosa Ziehau  * All rights reserved.
6*b7bb4816SSepherosa Ziehau  *
7*b7bb4816SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
8*b7bb4816SSepherosa Ziehau  * modification, are permitted provided that the following conditions
9*b7bb4816SSepherosa Ziehau  * are met:
10*b7bb4816SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
11*b7bb4816SSepherosa Ziehau  *    notice unmodified, this list of conditions, and the following
12*b7bb4816SSepherosa Ziehau  *    disclaimer.
13*b7bb4816SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
14*b7bb4816SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
15*b7bb4816SSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
16*b7bb4816SSepherosa Ziehau  *
17*b7bb4816SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18*b7bb4816SSepherosa Ziehau  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19*b7bb4816SSepherosa Ziehau  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*b7bb4816SSepherosa Ziehau  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21*b7bb4816SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22*b7bb4816SSepherosa Ziehau  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23*b7bb4816SSepherosa Ziehau  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24*b7bb4816SSepherosa Ziehau  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25*b7bb4816SSepherosa Ziehau  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26*b7bb4816SSepherosa Ziehau  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*b7bb4816SSepherosa Ziehau  */
28*b7bb4816SSepherosa Ziehau 
29*b7bb4816SSepherosa Ziehau /**
30*b7bb4816SSepherosa Ziehau  * Implements low-level interactions with Hypver-V/Azure
31*b7bb4816SSepherosa Ziehau  */
32*b7bb4816SSepherosa Ziehau #include <sys/cdefs.h>
33*b7bb4816SSepherosa Ziehau __FBSDID("$FreeBSD$");
34*b7bb4816SSepherosa Ziehau 
35*b7bb4816SSepherosa Ziehau #include <sys/param.h>
36*b7bb4816SSepherosa Ziehau #include <sys/kernel.h>
37*b7bb4816SSepherosa Ziehau #include <sys/malloc.h>
38*b7bb4816SSepherosa Ziehau #include <sys/pcpu.h>
39*b7bb4816SSepherosa Ziehau #include <sys/timetc.h>
40*b7bb4816SSepherosa Ziehau #include <machine/bus.h>
41*b7bb4816SSepherosa Ziehau #include <machine/md_var.h>
42*b7bb4816SSepherosa Ziehau #include <vm/vm.h>
43*b7bb4816SSepherosa Ziehau #include <vm/vm_param.h>
44*b7bb4816SSepherosa Ziehau #include <vm/pmap.h>
45*b7bb4816SSepherosa Ziehau 
46*b7bb4816SSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
47*b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
48*b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_reg.h>
49*b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_var.h>
50*b7bb4816SSepherosa Ziehau #include <dev/hyperv/vmbus/vmbus_var.h>
51*b7bb4816SSepherosa Ziehau 
52*b7bb4816SSepherosa Ziehau #define HYPERV_FREEBSD_BUILD		0ULL
53*b7bb4816SSepherosa Ziehau #define HYPERV_FREEBSD_VERSION		((uint64_t)__FreeBSD_version)
54*b7bb4816SSepherosa Ziehau #define HYPERV_FREEBSD_OSID		0ULL
55*b7bb4816SSepherosa Ziehau 
56*b7bb4816SSepherosa Ziehau #define MSR_HV_GUESTID_BUILD_FREEBSD	\
57*b7bb4816SSepherosa Ziehau 	(HYPERV_FREEBSD_BUILD & MSR_HV_GUESTID_BUILD_MASK)
58*b7bb4816SSepherosa Ziehau #define MSR_HV_GUESTID_VERSION_FREEBSD	\
59*b7bb4816SSepherosa Ziehau 	((HYPERV_FREEBSD_VERSION << MSR_HV_GUESTID_VERSION_SHIFT) & \
60*b7bb4816SSepherosa Ziehau 	 MSR_HV_GUESTID_VERSION_MASK)
61*b7bb4816SSepherosa Ziehau #define MSR_HV_GUESTID_OSID_FREEBSD	\
62*b7bb4816SSepherosa Ziehau 	((HYPERV_FREEBSD_OSID << MSR_HV_GUESTID_OSID_SHIFT) & \
63*b7bb4816SSepherosa Ziehau 	 MSR_HV_GUESTID_OSID_MASK)
64*b7bb4816SSepherosa Ziehau 
65*b7bb4816SSepherosa Ziehau #define MSR_HV_GUESTID_FREEBSD		\
66*b7bb4816SSepherosa Ziehau 	(MSR_HV_GUESTID_BUILD_FREEBSD |	\
67*b7bb4816SSepherosa Ziehau 	 MSR_HV_GUESTID_VERSION_FREEBSD | \
68*b7bb4816SSepherosa Ziehau 	 MSR_HV_GUESTID_OSID_FREEBSD |	\
69*b7bb4816SSepherosa Ziehau 	 MSR_HV_GUESTID_OSTYPE_FREEBSD)
70*b7bb4816SSepherosa Ziehau 
71*b7bb4816SSepherosa Ziehau struct hypercall_ctx {
72*b7bb4816SSepherosa Ziehau 	void			*hc_addr;
73*b7bb4816SSepherosa Ziehau 	struct hyperv_dma	hc_dma;
74*b7bb4816SSepherosa Ziehau };
75*b7bb4816SSepherosa Ziehau 
76*b7bb4816SSepherosa Ziehau static u_int	hyperv_get_timecount(struct timecounter *tc);
77*b7bb4816SSepherosa Ziehau 
78*b7bb4816SSepherosa Ziehau u_int		hyperv_features;
79*b7bb4816SSepherosa Ziehau u_int		hyperv_recommends;
80*b7bb4816SSepherosa Ziehau 
81*b7bb4816SSepherosa Ziehau static u_int	hyperv_pm_features;
82*b7bb4816SSepherosa Ziehau static u_int	hyperv_features3;
83*b7bb4816SSepherosa Ziehau 
84*b7bb4816SSepherosa Ziehau static struct timecounter	hyperv_timecounter = {
85*b7bb4816SSepherosa Ziehau 	.tc_get_timecount	= hyperv_get_timecount,
86*b7bb4816SSepherosa Ziehau 	.tc_poll_pps		= NULL,
87*b7bb4816SSepherosa Ziehau 	.tc_counter_mask	= 0xffffffff,
88*b7bb4816SSepherosa Ziehau 	.tc_frequency		= HYPERV_TIMER_FREQ,
89*b7bb4816SSepherosa Ziehau 	.tc_name		= "Hyper-V",
90*b7bb4816SSepherosa Ziehau 	.tc_quality		= 2000,
91*b7bb4816SSepherosa Ziehau 	.tc_flags		= 0,
92*b7bb4816SSepherosa Ziehau 	.tc_priv		= NULL
93*b7bb4816SSepherosa Ziehau };
94*b7bb4816SSepherosa Ziehau 
95*b7bb4816SSepherosa Ziehau static struct hypercall_ctx	hypercall_context;
96*b7bb4816SSepherosa Ziehau 
97*b7bb4816SSepherosa Ziehau static u_int
98*b7bb4816SSepherosa Ziehau hyperv_get_timecount(struct timecounter *tc __unused)
99*b7bb4816SSepherosa Ziehau {
100*b7bb4816SSepherosa Ziehau 	return rdmsr(MSR_HV_TIME_REF_COUNT);
101*b7bb4816SSepherosa Ziehau }
102*b7bb4816SSepherosa Ziehau 
103*b7bb4816SSepherosa Ziehau /**
104*b7bb4816SSepherosa Ziehau  * @brief Invoke the specified hypercall
105*b7bb4816SSepherosa Ziehau  */
106*b7bb4816SSepherosa Ziehau static uint64_t
107*b7bb4816SSepherosa Ziehau hv_vmbus_do_hypercall(uint64_t control, void* input, void* output)
108*b7bb4816SSepherosa Ziehau {
109*b7bb4816SSepherosa Ziehau #ifdef __x86_64__
110*b7bb4816SSepherosa Ziehau 	uint64_t hv_status = 0;
111*b7bb4816SSepherosa Ziehau 	uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0;
112*b7bb4816SSepherosa Ziehau 	uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0;
113*b7bb4816SSepherosa Ziehau 	volatile void *hypercall_page = hypercall_context.hc_addr;
114*b7bb4816SSepherosa Ziehau 
115*b7bb4816SSepherosa Ziehau 	__asm__ __volatile__ ("mov %0, %%r8" : : "r" (output_address): "r8");
116*b7bb4816SSepherosa Ziehau 	__asm__ __volatile__ ("call *%3" : "=a"(hv_status):
117*b7bb4816SSepherosa Ziehau 				"c" (control), "d" (input_address),
118*b7bb4816SSepherosa Ziehau 				"m" (hypercall_page));
119*b7bb4816SSepherosa Ziehau 	return (hv_status);
120*b7bb4816SSepherosa Ziehau #else
121*b7bb4816SSepherosa Ziehau 	uint32_t control_high = control >> 32;
122*b7bb4816SSepherosa Ziehau 	uint32_t control_low = control & 0xFFFFFFFF;
123*b7bb4816SSepherosa Ziehau 	uint32_t hv_status_high = 1;
124*b7bb4816SSepherosa Ziehau 	uint32_t hv_status_low = 1;
125*b7bb4816SSepherosa Ziehau 	uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0;
126*b7bb4816SSepherosa Ziehau 	uint32_t input_address_high = input_address >> 32;
127*b7bb4816SSepherosa Ziehau 	uint32_t input_address_low = input_address & 0xFFFFFFFF;
128*b7bb4816SSepherosa Ziehau 	uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0;
129*b7bb4816SSepherosa Ziehau 	uint32_t output_address_high = output_address >> 32;
130*b7bb4816SSepherosa Ziehau 	uint32_t output_address_low = output_address & 0xFFFFFFFF;
131*b7bb4816SSepherosa Ziehau 	volatile void *hypercall_page = hypercall_context.hc_addr;
132*b7bb4816SSepherosa Ziehau 
133*b7bb4816SSepherosa Ziehau 	__asm__ __volatile__ ("call *%8" : "=d"(hv_status_high),
134*b7bb4816SSepherosa Ziehau 				"=a"(hv_status_low) : "d" (control_high),
135*b7bb4816SSepherosa Ziehau 				"a" (control_low), "b" (input_address_high),
136*b7bb4816SSepherosa Ziehau 				"c" (input_address_low),
137*b7bb4816SSepherosa Ziehau 				"D"(output_address_high),
138*b7bb4816SSepherosa Ziehau 				"S"(output_address_low), "m" (hypercall_page));
139*b7bb4816SSepherosa Ziehau 	return (hv_status_low | ((uint64_t)hv_status_high << 32));
140*b7bb4816SSepherosa Ziehau #endif /* __x86_64__ */
141*b7bb4816SSepherosa Ziehau }
142*b7bb4816SSepherosa Ziehau 
143*b7bb4816SSepherosa Ziehau /**
144*b7bb4816SSepherosa Ziehau  * @brief Post a message using the hypervisor message IPC.
145*b7bb4816SSepherosa Ziehau  * (This involves a hypercall.)
146*b7bb4816SSepherosa Ziehau  */
147*b7bb4816SSepherosa Ziehau hv_vmbus_status
148*b7bb4816SSepherosa Ziehau hv_vmbus_post_msg_via_msg_ipc(
149*b7bb4816SSepherosa Ziehau 	hv_vmbus_connection_id	connection_id,
150*b7bb4816SSepherosa Ziehau 	hv_vmbus_msg_type	message_type,
151*b7bb4816SSepherosa Ziehau 	void*			payload,
152*b7bb4816SSepherosa Ziehau 	size_t			payload_size)
153*b7bb4816SSepherosa Ziehau {
154*b7bb4816SSepherosa Ziehau 	struct alignedinput {
155*b7bb4816SSepherosa Ziehau 	    uint64_t alignment8;
156*b7bb4816SSepherosa Ziehau 	    hv_vmbus_input_post_message msg;
157*b7bb4816SSepherosa Ziehau 	};
158*b7bb4816SSepherosa Ziehau 
159*b7bb4816SSepherosa Ziehau 	hv_vmbus_input_post_message*	aligned_msg;
160*b7bb4816SSepherosa Ziehau 	hv_vmbus_status 		status;
161*b7bb4816SSepherosa Ziehau 	size_t				addr;
162*b7bb4816SSepherosa Ziehau 
163*b7bb4816SSepherosa Ziehau 	if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
164*b7bb4816SSepherosa Ziehau 	    return (EMSGSIZE);
165*b7bb4816SSepherosa Ziehau 
166*b7bb4816SSepherosa Ziehau 	addr = (size_t) malloc(sizeof(struct alignedinput), M_DEVBUF,
167*b7bb4816SSepherosa Ziehau 			    M_ZERO | M_NOWAIT);
168*b7bb4816SSepherosa Ziehau 	KASSERT(addr != 0,
169*b7bb4816SSepherosa Ziehau 	    ("Error VMBUS: malloc failed to allocate message buffer!"));
170*b7bb4816SSepherosa Ziehau 	if (addr == 0)
171*b7bb4816SSepherosa Ziehau 	    return (ENOMEM);
172*b7bb4816SSepherosa Ziehau 
173*b7bb4816SSepherosa Ziehau 	aligned_msg = (hv_vmbus_input_post_message*)
174*b7bb4816SSepherosa Ziehau 	    (HV_ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
175*b7bb4816SSepherosa Ziehau 
176*b7bb4816SSepherosa Ziehau 	aligned_msg->connection_id = connection_id;
177*b7bb4816SSepherosa Ziehau 	aligned_msg->message_type = message_type;
178*b7bb4816SSepherosa Ziehau 	aligned_msg->payload_size = payload_size;
179*b7bb4816SSepherosa Ziehau 	memcpy((void*) aligned_msg->payload, payload, payload_size);
180*b7bb4816SSepherosa Ziehau 
181*b7bb4816SSepherosa Ziehau 	status = hv_vmbus_do_hypercall(
182*b7bb4816SSepherosa Ziehau 		    HV_CALL_POST_MESSAGE, aligned_msg, 0) & 0xFFFF;
183*b7bb4816SSepherosa Ziehau 
184*b7bb4816SSepherosa Ziehau 	free((void *) addr, M_DEVBUF);
185*b7bb4816SSepherosa Ziehau 	return (status);
186*b7bb4816SSepherosa Ziehau }
187*b7bb4816SSepherosa Ziehau 
188*b7bb4816SSepherosa Ziehau /**
189*b7bb4816SSepherosa Ziehau  * @brief Signal an event on the specified connection using the hypervisor
190*b7bb4816SSepherosa Ziehau  * event IPC. (This involves a hypercall.)
191*b7bb4816SSepherosa Ziehau  */
192*b7bb4816SSepherosa Ziehau hv_vmbus_status
193*b7bb4816SSepherosa Ziehau hv_vmbus_signal_event(void *con_id)
194*b7bb4816SSepherosa Ziehau {
195*b7bb4816SSepherosa Ziehau 	hv_vmbus_status status;
196*b7bb4816SSepherosa Ziehau 
197*b7bb4816SSepherosa Ziehau 	status = hv_vmbus_do_hypercall(
198*b7bb4816SSepherosa Ziehau 		    HV_CALL_SIGNAL_EVENT,
199*b7bb4816SSepherosa Ziehau 		    con_id,
200*b7bb4816SSepherosa Ziehau 		    0) & 0xFFFF;
201*b7bb4816SSepherosa Ziehau 
202*b7bb4816SSepherosa Ziehau 	return (status);
203*b7bb4816SSepherosa Ziehau }
204*b7bb4816SSepherosa Ziehau 
205*b7bb4816SSepherosa Ziehau int
206*b7bb4816SSepherosa Ziehau hyperv_guid2str(const struct hv_guid *guid, char *buf, size_t sz)
207*b7bb4816SSepherosa Ziehau {
208*b7bb4816SSepherosa Ziehau 	const uint8_t *d = guid->data;
209*b7bb4816SSepherosa Ziehau 
210*b7bb4816SSepherosa Ziehau 	return snprintf(buf, sz, "%02x%02x%02x%02x-"
211*b7bb4816SSepherosa Ziehau 	    "%02x%02x-%02x%02x-%02x%02x-"
212*b7bb4816SSepherosa Ziehau 	    "%02x%02x%02x%02x%02x%02x",
213*b7bb4816SSepherosa Ziehau 	    d[3], d[2], d[1], d[0],
214*b7bb4816SSepherosa Ziehau 	    d[5], d[4], d[7], d[6], d[8], d[9],
215*b7bb4816SSepherosa Ziehau 	    d[10], d[11], d[12], d[13], d[14], d[15]);
216*b7bb4816SSepherosa Ziehau }
217*b7bb4816SSepherosa Ziehau 
218*b7bb4816SSepherosa Ziehau static bool
219*b7bb4816SSepherosa Ziehau hyperv_identify(void)
220*b7bb4816SSepherosa Ziehau {
221*b7bb4816SSepherosa Ziehau 	u_int regs[4];
222*b7bb4816SSepherosa Ziehau 	unsigned int maxleaf;
223*b7bb4816SSepherosa Ziehau 
224*b7bb4816SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
225*b7bb4816SSepherosa Ziehau 		return (false);
226*b7bb4816SSepherosa Ziehau 
227*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs);
228*b7bb4816SSepherosa Ziehau 	maxleaf = regs[0];
229*b7bb4816SSepherosa Ziehau 	if (maxleaf < CPUID_LEAF_HV_LIMITS)
230*b7bb4816SSepherosa Ziehau 		return (false);
231*b7bb4816SSepherosa Ziehau 
232*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_INTERFACE, regs);
233*b7bb4816SSepherosa Ziehau 	if (regs[0] != CPUID_HV_IFACE_HYPERV)
234*b7bb4816SSepherosa Ziehau 		return (false);
235*b7bb4816SSepherosa Ziehau 
236*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_FEATURES, regs);
237*b7bb4816SSepherosa Ziehau 	if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) {
238*b7bb4816SSepherosa Ziehau 		/*
239*b7bb4816SSepherosa Ziehau 		 * Hyper-V w/o Hypercall is impossible; someone
240*b7bb4816SSepherosa Ziehau 		 * is faking Hyper-V.
241*b7bb4816SSepherosa Ziehau 		 */
242*b7bb4816SSepherosa Ziehau 		return (false);
243*b7bb4816SSepherosa Ziehau 	}
244*b7bb4816SSepherosa Ziehau 	hyperv_features = regs[0];
245*b7bb4816SSepherosa Ziehau 	hyperv_pm_features = regs[2];
246*b7bb4816SSepherosa Ziehau 	hyperv_features3 = regs[3];
247*b7bb4816SSepherosa Ziehau 
248*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_IDENTITY, regs);
249*b7bb4816SSepherosa Ziehau 	printf("Hyper-V Version: %d.%d.%d [SP%d]\n",
250*b7bb4816SSepherosa Ziehau 	    regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]);
251*b7bb4816SSepherosa Ziehau 
252*b7bb4816SSepherosa Ziehau 	printf("  Features=0x%b\n", hyperv_features,
253*b7bb4816SSepherosa Ziehau 	    "\020"
254*b7bb4816SSepherosa Ziehau 	    "\001VPRUNTIME"	/* MSR_HV_VP_RUNTIME */
255*b7bb4816SSepherosa Ziehau 	    "\002TMREFCNT"	/* MSR_HV_TIME_REF_COUNT */
256*b7bb4816SSepherosa Ziehau 	    "\003SYNIC"		/* MSRs for SynIC */
257*b7bb4816SSepherosa Ziehau 	    "\004SYNTM"		/* MSRs for SynTimer */
258*b7bb4816SSepherosa Ziehau 	    "\005APIC"		/* MSR_HV_{EOI,ICR,TPR} */
259*b7bb4816SSepherosa Ziehau 	    "\006HYPERCALL"	/* MSR_HV_{GUEST_OS_ID,HYPERCALL} */
260*b7bb4816SSepherosa Ziehau 	    "\007VPINDEX"	/* MSR_HV_VP_INDEX */
261*b7bb4816SSepherosa Ziehau 	    "\010RESET"		/* MSR_HV_RESET */
262*b7bb4816SSepherosa Ziehau 	    "\011STATS"		/* MSR_HV_STATS_ */
263*b7bb4816SSepherosa Ziehau 	    "\012REFTSC"	/* MSR_HV_REFERENCE_TSC */
264*b7bb4816SSepherosa Ziehau 	    "\013IDLE"		/* MSR_HV_GUEST_IDLE */
265*b7bb4816SSepherosa Ziehau 	    "\014TMFREQ"	/* MSR_HV_{TSC,APIC}_FREQUENCY */
266*b7bb4816SSepherosa Ziehau 	    "\015DEBUG");	/* MSR_HV_SYNTH_DEBUG_ */
267*b7bb4816SSepherosa Ziehau 	printf("  PM Features=0x%b [C%u]\n",
268*b7bb4816SSepherosa Ziehau 	    (hyperv_pm_features & ~CPUPM_HV_CSTATE_MASK),
269*b7bb4816SSepherosa Ziehau 	    "\020"
270*b7bb4816SSepherosa Ziehau 	    "\005C3HPET",	/* HPET is required for C3 state */
271*b7bb4816SSepherosa Ziehau 	    CPUPM_HV_CSTATE(hyperv_pm_features));
272*b7bb4816SSepherosa Ziehau 	printf("  Features3=0x%b\n", hyperv_features3,
273*b7bb4816SSepherosa Ziehau 	    "\020"
274*b7bb4816SSepherosa Ziehau 	    "\001MWAIT"		/* MWAIT */
275*b7bb4816SSepherosa Ziehau 	    "\002DEBUG"		/* guest debug support */
276*b7bb4816SSepherosa Ziehau 	    "\003PERFMON"	/* performance monitor */
277*b7bb4816SSepherosa Ziehau 	    "\004PCPUDPE"	/* physical CPU dynamic partition event */
278*b7bb4816SSepherosa Ziehau 	    "\005XMMHC"		/* hypercall input through XMM regs */
279*b7bb4816SSepherosa Ziehau 	    "\006IDLE"		/* guest idle support */
280*b7bb4816SSepherosa Ziehau 	    "\007SLEEP"		/* hypervisor sleep support */
281*b7bb4816SSepherosa Ziehau 	    "\010NUMA"		/* NUMA distance query support */
282*b7bb4816SSepherosa Ziehau 	    "\011TMFREQ"	/* timer frequency query (TSC, LAPIC) */
283*b7bb4816SSepherosa Ziehau 	    "\012SYNCMC"	/* inject synthetic machine checks */
284*b7bb4816SSepherosa Ziehau 	    "\013CRASH"		/* MSRs for guest crash */
285*b7bb4816SSepherosa Ziehau 	    "\014DEBUGMSR"	/* MSRs for guest debug */
286*b7bb4816SSepherosa Ziehau 	    "\015NPIEP"		/* NPIEP */
287*b7bb4816SSepherosa Ziehau 	    "\016HVDIS");	/* disabling hypervisor */
288*b7bb4816SSepherosa Ziehau 
289*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_RECOMMENDS, regs);
290*b7bb4816SSepherosa Ziehau 	hyperv_recommends = regs[0];
291*b7bb4816SSepherosa Ziehau 	if (bootverbose)
292*b7bb4816SSepherosa Ziehau 		printf("  Recommends: %08x %08x\n", regs[0], regs[1]);
293*b7bb4816SSepherosa Ziehau 
294*b7bb4816SSepherosa Ziehau 	do_cpuid(CPUID_LEAF_HV_LIMITS, regs);
295*b7bb4816SSepherosa Ziehau 	if (bootverbose) {
296*b7bb4816SSepherosa Ziehau 		printf("  Limits: Vcpu:%d Lcpu:%d Int:%d\n",
297*b7bb4816SSepherosa Ziehau 		    regs[0], regs[1], regs[2]);
298*b7bb4816SSepherosa Ziehau 	}
299*b7bb4816SSepherosa Ziehau 
300*b7bb4816SSepherosa Ziehau 	if (maxleaf >= CPUID_LEAF_HV_HWFEATURES) {
301*b7bb4816SSepherosa Ziehau 		do_cpuid(CPUID_LEAF_HV_HWFEATURES, regs);
302*b7bb4816SSepherosa Ziehau 		if (bootverbose) {
303*b7bb4816SSepherosa Ziehau 			printf("  HW Features: %08x, AMD: %08x\n",
304*b7bb4816SSepherosa Ziehau 			    regs[0], regs[3]);
305*b7bb4816SSepherosa Ziehau 		}
306*b7bb4816SSepherosa Ziehau 	}
307*b7bb4816SSepherosa Ziehau 
308*b7bb4816SSepherosa Ziehau 	return (true);
309*b7bb4816SSepherosa Ziehau }
310*b7bb4816SSepherosa Ziehau 
311*b7bb4816SSepherosa Ziehau static void
312*b7bb4816SSepherosa Ziehau hyperv_init(void *dummy __unused)
313*b7bb4816SSepherosa Ziehau {
314*b7bb4816SSepherosa Ziehau 	if (!hyperv_identify()) {
315*b7bb4816SSepherosa Ziehau 		/* Not Hyper-V; reset guest id to the generic one. */
316*b7bb4816SSepherosa Ziehau 		if (vm_guest == VM_GUEST_HV)
317*b7bb4816SSepherosa Ziehau 			vm_guest = VM_GUEST_VM;
318*b7bb4816SSepherosa Ziehau 		return;
319*b7bb4816SSepherosa Ziehau 	}
320*b7bb4816SSepherosa Ziehau 
321*b7bb4816SSepherosa Ziehau 	/* Set guest id */
322*b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_GUEST_OS_ID, MSR_HV_GUESTID_FREEBSD);
323*b7bb4816SSepherosa Ziehau 
324*b7bb4816SSepherosa Ziehau 	if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
325*b7bb4816SSepherosa Ziehau 		/* Register Hyper-V timecounter */
326*b7bb4816SSepherosa Ziehau 		tc_init(&hyperv_timecounter);
327*b7bb4816SSepherosa Ziehau 	}
328*b7bb4816SSepherosa Ziehau }
329*b7bb4816SSepherosa Ziehau SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init,
330*b7bb4816SSepherosa Ziehau     NULL);
331*b7bb4816SSepherosa Ziehau 
332*b7bb4816SSepherosa Ziehau static void
333*b7bb4816SSepherosa Ziehau hypercall_memfree(void)
334*b7bb4816SSepherosa Ziehau {
335*b7bb4816SSepherosa Ziehau 	hyperv_dmamem_free(&hypercall_context.hc_dma,
336*b7bb4816SSepherosa Ziehau 	    hypercall_context.hc_addr);
337*b7bb4816SSepherosa Ziehau 	hypercall_context.hc_addr = NULL;
338*b7bb4816SSepherosa Ziehau }
339*b7bb4816SSepherosa Ziehau 
340*b7bb4816SSepherosa Ziehau static void
341*b7bb4816SSepherosa Ziehau hypercall_create(void *arg __unused)
342*b7bb4816SSepherosa Ziehau {
343*b7bb4816SSepherosa Ziehau 	uint64_t hc, hc_orig;
344*b7bb4816SSepherosa Ziehau 
345*b7bb4816SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
346*b7bb4816SSepherosa Ziehau 		return;
347*b7bb4816SSepherosa Ziehau 
348*b7bb4816SSepherosa Ziehau 	hypercall_context.hc_addr = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0,
349*b7bb4816SSepherosa Ziehau 	    PAGE_SIZE, &hypercall_context.hc_dma, BUS_DMA_WAITOK);
350*b7bb4816SSepherosa Ziehau 	if (hypercall_context.hc_addr == NULL) {
351*b7bb4816SSepherosa Ziehau 		printf("hyperv: Hypercall page allocation failed\n");
352*b7bb4816SSepherosa Ziehau 		/* Can't perform any Hyper-V specific actions */
353*b7bb4816SSepherosa Ziehau 		vm_guest = VM_GUEST_VM;
354*b7bb4816SSepherosa Ziehau 		return;
355*b7bb4816SSepherosa Ziehau 	}
356*b7bb4816SSepherosa Ziehau 
357*b7bb4816SSepherosa Ziehau 	/* Get the 'reserved' bits, which requires preservation. */
358*b7bb4816SSepherosa Ziehau 	hc_orig = rdmsr(MSR_HV_HYPERCALL);
359*b7bb4816SSepherosa Ziehau 
360*b7bb4816SSepherosa Ziehau 	/*
361*b7bb4816SSepherosa Ziehau 	 * Setup the Hypercall page.
362*b7bb4816SSepherosa Ziehau 	 *
363*b7bb4816SSepherosa Ziehau 	 * NOTE: 'reserved' bits MUST be preserved.
364*b7bb4816SSepherosa Ziehau 	 */
365*b7bb4816SSepherosa Ziehau 	hc = ((hypercall_context.hc_dma.hv_paddr >> PAGE_SHIFT) <<
366*b7bb4816SSepherosa Ziehau 	    MSR_HV_HYPERCALL_PGSHIFT) |
367*b7bb4816SSepherosa Ziehau 	    (hc_orig & MSR_HV_HYPERCALL_RSVD_MASK) |
368*b7bb4816SSepherosa Ziehau 	    MSR_HV_HYPERCALL_ENABLE;
369*b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_HYPERCALL, hc);
370*b7bb4816SSepherosa Ziehau 
371*b7bb4816SSepherosa Ziehau 	/*
372*b7bb4816SSepherosa Ziehau 	 * Confirm that Hypercall page did get setup.
373*b7bb4816SSepherosa Ziehau 	 */
374*b7bb4816SSepherosa Ziehau 	hc = rdmsr(MSR_HV_HYPERCALL);
375*b7bb4816SSepherosa Ziehau 	if ((hc & MSR_HV_HYPERCALL_ENABLE) == 0) {
376*b7bb4816SSepherosa Ziehau 		printf("hyperv: Hypercall setup failed\n");
377*b7bb4816SSepherosa Ziehau 		hypercall_memfree();
378*b7bb4816SSepherosa Ziehau 		/* Can't perform any Hyper-V specific actions */
379*b7bb4816SSepherosa Ziehau 		vm_guest = VM_GUEST_VM;
380*b7bb4816SSepherosa Ziehau 		return;
381*b7bb4816SSepherosa Ziehau 	}
382*b7bb4816SSepherosa Ziehau 	if (bootverbose)
383*b7bb4816SSepherosa Ziehau 		printf("hyperv: Hypercall created\n");
384*b7bb4816SSepherosa Ziehau }
385*b7bb4816SSepherosa Ziehau SYSINIT(hypercall_ctor, SI_SUB_DRIVERS, SI_ORDER_FIRST, hypercall_create, NULL);
386*b7bb4816SSepherosa Ziehau 
387*b7bb4816SSepherosa Ziehau static void
388*b7bb4816SSepherosa Ziehau hypercall_destroy(void *arg __unused)
389*b7bb4816SSepherosa Ziehau {
390*b7bb4816SSepherosa Ziehau 	uint64_t hc;
391*b7bb4816SSepherosa Ziehau 
392*b7bb4816SSepherosa Ziehau 	if (hypercall_context.hc_addr == NULL)
393*b7bb4816SSepherosa Ziehau 		return;
394*b7bb4816SSepherosa Ziehau 
395*b7bb4816SSepherosa Ziehau 	/* Disable Hypercall */
396*b7bb4816SSepherosa Ziehau 	hc = rdmsr(MSR_HV_HYPERCALL);
397*b7bb4816SSepherosa Ziehau 	wrmsr(MSR_HV_HYPERCALL, (hc & MSR_HV_HYPERCALL_RSVD_MASK));
398*b7bb4816SSepherosa Ziehau 	hypercall_memfree();
399*b7bb4816SSepherosa Ziehau 
400*b7bb4816SSepherosa Ziehau 	if (bootverbose)
401*b7bb4816SSepherosa Ziehau 		printf("hyperv: Hypercall destroyed\n");
402*b7bb4816SSepherosa Ziehau }
403*b7bb4816SSepherosa Ziehau SYSUNINIT(hypercall_dtor, SI_SUB_DRIVERS, SI_ORDER_FIRST, hypercall_destroy,
404*b7bb4816SSepherosa Ziehau     NULL);
405