xref: /openbmc/linux/arch/x86/kvm/hyperv.c (revision e7d9513b60e87f62e41090fa3a26eca796924346)
1e83d5887SAndrey Smetanin /*
2e83d5887SAndrey Smetanin  * KVM Microsoft Hyper-V emulation
3e83d5887SAndrey Smetanin  *
4e83d5887SAndrey Smetanin  * derived from arch/x86/kvm/x86.c
5e83d5887SAndrey Smetanin  *
6e83d5887SAndrey Smetanin  * Copyright (C) 2006 Qumranet, Inc.
7e83d5887SAndrey Smetanin  * Copyright (C) 2008 Qumranet, Inc.
8e83d5887SAndrey Smetanin  * Copyright IBM Corporation, 2008
9e83d5887SAndrey Smetanin  * Copyright 2010 Red Hat, Inc. and/or its affiliates.
10e83d5887SAndrey Smetanin  * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
11e83d5887SAndrey Smetanin  *
12e83d5887SAndrey Smetanin  * Authors:
13e83d5887SAndrey Smetanin  *   Avi Kivity   <avi@qumranet.com>
14e83d5887SAndrey Smetanin  *   Yaniv Kamay  <yaniv@qumranet.com>
15e83d5887SAndrey Smetanin  *   Amit Shah    <amit.shah@qumranet.com>
16e83d5887SAndrey Smetanin  *   Ben-Ami Yassour <benami@il.ibm.com>
17e83d5887SAndrey Smetanin  *   Andrey Smetanin <asmetanin@virtuozzo.com>
18e83d5887SAndrey Smetanin  *
19e83d5887SAndrey Smetanin  * This work is licensed under the terms of the GNU GPL, version 2.  See
20e83d5887SAndrey Smetanin  * the COPYING file in the top-level directory.
21e83d5887SAndrey Smetanin  *
22e83d5887SAndrey Smetanin  */
23e83d5887SAndrey Smetanin 
24e83d5887SAndrey Smetanin #include "x86.h"
25e83d5887SAndrey Smetanin #include "lapic.h"
26e83d5887SAndrey Smetanin #include "hyperv.h"
27e83d5887SAndrey Smetanin 
28e83d5887SAndrey Smetanin #include <linux/kvm_host.h>
29e83d5887SAndrey Smetanin #include <trace/events/kvm.h>
30e83d5887SAndrey Smetanin 
31e83d5887SAndrey Smetanin #include "trace.h"
32e83d5887SAndrey Smetanin 
33e83d5887SAndrey Smetanin static bool kvm_hv_msr_partition_wide(u32 msr)
34e83d5887SAndrey Smetanin {
35e83d5887SAndrey Smetanin 	bool r = false;
36e83d5887SAndrey Smetanin 
37e83d5887SAndrey Smetanin 	switch (msr) {
38e83d5887SAndrey Smetanin 	case HV_X64_MSR_GUEST_OS_ID:
39e83d5887SAndrey Smetanin 	case HV_X64_MSR_HYPERCALL:
40e83d5887SAndrey Smetanin 	case HV_X64_MSR_REFERENCE_TSC:
41e83d5887SAndrey Smetanin 	case HV_X64_MSR_TIME_REF_COUNT:
42*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_CTL:
43*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
44e83d5887SAndrey Smetanin 		r = true;
45e83d5887SAndrey Smetanin 		break;
46e83d5887SAndrey Smetanin 	}
47e83d5887SAndrey Smetanin 
48e83d5887SAndrey Smetanin 	return r;
49e83d5887SAndrey Smetanin }
50e83d5887SAndrey Smetanin 
51*e7d9513bSAndrey Smetanin static int kvm_hv_msr_get_crash_data(struct kvm_vcpu *vcpu,
52*e7d9513bSAndrey Smetanin 				     u32 index, u64 *pdata)
53*e7d9513bSAndrey Smetanin {
54*e7d9513bSAndrey Smetanin 	struct kvm_hv *hv = &vcpu->kvm->arch.hyperv;
55*e7d9513bSAndrey Smetanin 
56*e7d9513bSAndrey Smetanin 	if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param)))
57*e7d9513bSAndrey Smetanin 		return -EINVAL;
58*e7d9513bSAndrey Smetanin 
59*e7d9513bSAndrey Smetanin 	*pdata = hv->hv_crash_param[index];
60*e7d9513bSAndrey Smetanin 	return 0;
61*e7d9513bSAndrey Smetanin }
62*e7d9513bSAndrey Smetanin 
63*e7d9513bSAndrey Smetanin static int kvm_hv_msr_get_crash_ctl(struct kvm_vcpu *vcpu, u64 *pdata)
64*e7d9513bSAndrey Smetanin {
65*e7d9513bSAndrey Smetanin 	struct kvm_hv *hv = &vcpu->kvm->arch.hyperv;
66*e7d9513bSAndrey Smetanin 
67*e7d9513bSAndrey Smetanin 	*pdata = hv->hv_crash_ctl;
68*e7d9513bSAndrey Smetanin 	return 0;
69*e7d9513bSAndrey Smetanin }
70*e7d9513bSAndrey Smetanin 
71*e7d9513bSAndrey Smetanin static int kvm_hv_msr_set_crash_ctl(struct kvm_vcpu *vcpu, u64 data, bool host)
72*e7d9513bSAndrey Smetanin {
73*e7d9513bSAndrey Smetanin 	struct kvm_hv *hv = &vcpu->kvm->arch.hyperv;
74*e7d9513bSAndrey Smetanin 
75*e7d9513bSAndrey Smetanin 	if (host)
76*e7d9513bSAndrey Smetanin 		hv->hv_crash_ctl = data & HV_X64_MSR_CRASH_CTL_NOTIFY;
77*e7d9513bSAndrey Smetanin 
78*e7d9513bSAndrey Smetanin 	if (!host && (data & HV_X64_MSR_CRASH_CTL_NOTIFY)) {
79*e7d9513bSAndrey Smetanin 
80*e7d9513bSAndrey Smetanin 		vcpu_debug(vcpu, "hv crash (0x%llx 0x%llx 0x%llx 0x%llx 0x%llx)\n",
81*e7d9513bSAndrey Smetanin 			  hv->hv_crash_param[0],
82*e7d9513bSAndrey Smetanin 			  hv->hv_crash_param[1],
83*e7d9513bSAndrey Smetanin 			  hv->hv_crash_param[2],
84*e7d9513bSAndrey Smetanin 			  hv->hv_crash_param[3],
85*e7d9513bSAndrey Smetanin 			  hv->hv_crash_param[4]);
86*e7d9513bSAndrey Smetanin 
87*e7d9513bSAndrey Smetanin 		/* Send notification about crash to user space */
88*e7d9513bSAndrey Smetanin 		kvm_make_request(KVM_REQ_HV_CRASH, vcpu);
89*e7d9513bSAndrey Smetanin 	}
90*e7d9513bSAndrey Smetanin 
91*e7d9513bSAndrey Smetanin 	return 0;
92*e7d9513bSAndrey Smetanin }
93*e7d9513bSAndrey Smetanin 
94*e7d9513bSAndrey Smetanin static int kvm_hv_msr_set_crash_data(struct kvm_vcpu *vcpu,
95*e7d9513bSAndrey Smetanin 				     u32 index, u64 data)
96*e7d9513bSAndrey Smetanin {
97*e7d9513bSAndrey Smetanin 	struct kvm_hv *hv = &vcpu->kvm->arch.hyperv;
98*e7d9513bSAndrey Smetanin 
99*e7d9513bSAndrey Smetanin 	if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param)))
100*e7d9513bSAndrey Smetanin 		return -EINVAL;
101*e7d9513bSAndrey Smetanin 
102*e7d9513bSAndrey Smetanin 	hv->hv_crash_param[index] = data;
103*e7d9513bSAndrey Smetanin 	return 0;
104*e7d9513bSAndrey Smetanin }
105*e7d9513bSAndrey Smetanin 
106*e7d9513bSAndrey Smetanin static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data,
107*e7d9513bSAndrey Smetanin 			     bool host)
108e83d5887SAndrey Smetanin {
109e83d5887SAndrey Smetanin 	struct kvm *kvm = vcpu->kvm;
110e83d5887SAndrey Smetanin 	struct kvm_hv *hv = &kvm->arch.hyperv;
111e83d5887SAndrey Smetanin 
112e83d5887SAndrey Smetanin 	switch (msr) {
113e83d5887SAndrey Smetanin 	case HV_X64_MSR_GUEST_OS_ID:
114e83d5887SAndrey Smetanin 		hv->hv_guest_os_id = data;
115e83d5887SAndrey Smetanin 		/* setting guest os id to zero disables hypercall page */
116e83d5887SAndrey Smetanin 		if (!hv->hv_guest_os_id)
117e83d5887SAndrey Smetanin 			hv->hv_hypercall &= ~HV_X64_MSR_HYPERCALL_ENABLE;
118e83d5887SAndrey Smetanin 		break;
119e83d5887SAndrey Smetanin 	case HV_X64_MSR_HYPERCALL: {
120e83d5887SAndrey Smetanin 		u64 gfn;
121e83d5887SAndrey Smetanin 		unsigned long addr;
122e83d5887SAndrey Smetanin 		u8 instructions[4];
123e83d5887SAndrey Smetanin 
124e83d5887SAndrey Smetanin 		/* if guest os id is not set hypercall should remain disabled */
125e83d5887SAndrey Smetanin 		if (!hv->hv_guest_os_id)
126e83d5887SAndrey Smetanin 			break;
127e83d5887SAndrey Smetanin 		if (!(data & HV_X64_MSR_HYPERCALL_ENABLE)) {
128e83d5887SAndrey Smetanin 			hv->hv_hypercall = data;
129e83d5887SAndrey Smetanin 			break;
130e83d5887SAndrey Smetanin 		}
131e83d5887SAndrey Smetanin 		gfn = data >> HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT;
132e83d5887SAndrey Smetanin 		addr = gfn_to_hva(kvm, gfn);
133e83d5887SAndrey Smetanin 		if (kvm_is_error_hva(addr))
134e83d5887SAndrey Smetanin 			return 1;
135e83d5887SAndrey Smetanin 		kvm_x86_ops->patch_hypercall(vcpu, instructions);
136e83d5887SAndrey Smetanin 		((unsigned char *)instructions)[3] = 0xc3; /* ret */
137e83d5887SAndrey Smetanin 		if (__copy_to_user((void __user *)addr, instructions, 4))
138e83d5887SAndrey Smetanin 			return 1;
139e83d5887SAndrey Smetanin 		hv->hv_hypercall = data;
140e83d5887SAndrey Smetanin 		mark_page_dirty(kvm, gfn);
141e83d5887SAndrey Smetanin 		break;
142e83d5887SAndrey Smetanin 	}
143e83d5887SAndrey Smetanin 	case HV_X64_MSR_REFERENCE_TSC: {
144e83d5887SAndrey Smetanin 		u64 gfn;
145e83d5887SAndrey Smetanin 		HV_REFERENCE_TSC_PAGE tsc_ref;
146e83d5887SAndrey Smetanin 
147e83d5887SAndrey Smetanin 		memset(&tsc_ref, 0, sizeof(tsc_ref));
148e83d5887SAndrey Smetanin 		hv->hv_tsc_page = data;
149e83d5887SAndrey Smetanin 		if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE))
150e83d5887SAndrey Smetanin 			break;
151e83d5887SAndrey Smetanin 		gfn = data >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
152e83d5887SAndrey Smetanin 		if (kvm_write_guest(
153e83d5887SAndrey Smetanin 				kvm,
154e83d5887SAndrey Smetanin 				gfn << HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT,
155e83d5887SAndrey Smetanin 				&tsc_ref, sizeof(tsc_ref)))
156e83d5887SAndrey Smetanin 			return 1;
157e83d5887SAndrey Smetanin 		mark_page_dirty(kvm, gfn);
158e83d5887SAndrey Smetanin 		break;
159e83d5887SAndrey Smetanin 	}
160*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
161*e7d9513bSAndrey Smetanin 		return kvm_hv_msr_set_crash_data(vcpu,
162*e7d9513bSAndrey Smetanin 						 msr - HV_X64_MSR_CRASH_P0,
163*e7d9513bSAndrey Smetanin 						 data);
164*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_CTL:
165*e7d9513bSAndrey Smetanin 		return kvm_hv_msr_set_crash_ctl(vcpu, data, host);
166e83d5887SAndrey Smetanin 	default:
167e83d5887SAndrey Smetanin 		vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
168e83d5887SAndrey Smetanin 			    msr, data);
169e83d5887SAndrey Smetanin 		return 1;
170e83d5887SAndrey Smetanin 	}
171e83d5887SAndrey Smetanin 	return 0;
172e83d5887SAndrey Smetanin }
173e83d5887SAndrey Smetanin 
174e83d5887SAndrey Smetanin static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
175e83d5887SAndrey Smetanin {
176e83d5887SAndrey Smetanin 	struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv;
177e83d5887SAndrey Smetanin 
178e83d5887SAndrey Smetanin 	switch (msr) {
179e83d5887SAndrey Smetanin 	case HV_X64_MSR_APIC_ASSIST_PAGE: {
180e83d5887SAndrey Smetanin 		u64 gfn;
181e83d5887SAndrey Smetanin 		unsigned long addr;
182e83d5887SAndrey Smetanin 
183e83d5887SAndrey Smetanin 		if (!(data & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE)) {
184e83d5887SAndrey Smetanin 			hv->hv_vapic = data;
185e83d5887SAndrey Smetanin 			if (kvm_lapic_enable_pv_eoi(vcpu, 0))
186e83d5887SAndrey Smetanin 				return 1;
187e83d5887SAndrey Smetanin 			break;
188e83d5887SAndrey Smetanin 		}
189e83d5887SAndrey Smetanin 		gfn = data >> HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT;
190e83d5887SAndrey Smetanin 		addr = kvm_vcpu_gfn_to_hva(vcpu, gfn);
191e83d5887SAndrey Smetanin 		if (kvm_is_error_hva(addr))
192e83d5887SAndrey Smetanin 			return 1;
193e83d5887SAndrey Smetanin 		if (__clear_user((void __user *)addr, PAGE_SIZE))
194e83d5887SAndrey Smetanin 			return 1;
195e83d5887SAndrey Smetanin 		hv->hv_vapic = data;
196e83d5887SAndrey Smetanin 		kvm_vcpu_mark_page_dirty(vcpu, gfn);
197e83d5887SAndrey Smetanin 		if (kvm_lapic_enable_pv_eoi(vcpu,
198e83d5887SAndrey Smetanin 					    gfn_to_gpa(gfn) | KVM_MSR_ENABLED))
199e83d5887SAndrey Smetanin 			return 1;
200e83d5887SAndrey Smetanin 		break;
201e83d5887SAndrey Smetanin 	}
202e83d5887SAndrey Smetanin 	case HV_X64_MSR_EOI:
203e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_write(vcpu, APIC_EOI, data);
204e83d5887SAndrey Smetanin 	case HV_X64_MSR_ICR:
205e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_write(vcpu, APIC_ICR, data);
206e83d5887SAndrey Smetanin 	case HV_X64_MSR_TPR:
207e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data);
208e83d5887SAndrey Smetanin 	default:
209e83d5887SAndrey Smetanin 		vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
210e83d5887SAndrey Smetanin 			    msr, data);
211e83d5887SAndrey Smetanin 		return 1;
212e83d5887SAndrey Smetanin 	}
213e83d5887SAndrey Smetanin 
214e83d5887SAndrey Smetanin 	return 0;
215e83d5887SAndrey Smetanin }
216e83d5887SAndrey Smetanin 
217e83d5887SAndrey Smetanin static int kvm_hv_get_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
218e83d5887SAndrey Smetanin {
219e83d5887SAndrey Smetanin 	u64 data = 0;
220e83d5887SAndrey Smetanin 	struct kvm *kvm = vcpu->kvm;
221e83d5887SAndrey Smetanin 	struct kvm_hv *hv = &kvm->arch.hyperv;
222e83d5887SAndrey Smetanin 
223e83d5887SAndrey Smetanin 	switch (msr) {
224e83d5887SAndrey Smetanin 	case HV_X64_MSR_GUEST_OS_ID:
225e83d5887SAndrey Smetanin 		data = hv->hv_guest_os_id;
226e83d5887SAndrey Smetanin 		break;
227e83d5887SAndrey Smetanin 	case HV_X64_MSR_HYPERCALL:
228e83d5887SAndrey Smetanin 		data = hv->hv_hypercall;
229e83d5887SAndrey Smetanin 		break;
230e83d5887SAndrey Smetanin 	case HV_X64_MSR_TIME_REF_COUNT: {
231e83d5887SAndrey Smetanin 		data =
232e83d5887SAndrey Smetanin 		     div_u64(get_kernel_ns() + kvm->arch.kvmclock_offset, 100);
233e83d5887SAndrey Smetanin 		break;
234e83d5887SAndrey Smetanin 	}
235e83d5887SAndrey Smetanin 	case HV_X64_MSR_REFERENCE_TSC:
236e83d5887SAndrey Smetanin 		data = hv->hv_tsc_page;
237e83d5887SAndrey Smetanin 		break;
238*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
239*e7d9513bSAndrey Smetanin 		return kvm_hv_msr_get_crash_data(vcpu,
240*e7d9513bSAndrey Smetanin 						 msr - HV_X64_MSR_CRASH_P0,
241*e7d9513bSAndrey Smetanin 						 pdata);
242*e7d9513bSAndrey Smetanin 	case HV_X64_MSR_CRASH_CTL:
243*e7d9513bSAndrey Smetanin 		return kvm_hv_msr_get_crash_ctl(vcpu, pdata);
244e83d5887SAndrey Smetanin 	default:
245e83d5887SAndrey Smetanin 		vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
246e83d5887SAndrey Smetanin 		return 1;
247e83d5887SAndrey Smetanin 	}
248e83d5887SAndrey Smetanin 
249e83d5887SAndrey Smetanin 	*pdata = data;
250e83d5887SAndrey Smetanin 	return 0;
251e83d5887SAndrey Smetanin }
252e83d5887SAndrey Smetanin 
253e83d5887SAndrey Smetanin static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
254e83d5887SAndrey Smetanin {
255e83d5887SAndrey Smetanin 	u64 data = 0;
256e83d5887SAndrey Smetanin 	struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv;
257e83d5887SAndrey Smetanin 
258e83d5887SAndrey Smetanin 	switch (msr) {
259e83d5887SAndrey Smetanin 	case HV_X64_MSR_VP_INDEX: {
260e83d5887SAndrey Smetanin 		int r;
261e83d5887SAndrey Smetanin 		struct kvm_vcpu *v;
262e83d5887SAndrey Smetanin 
263e83d5887SAndrey Smetanin 		kvm_for_each_vcpu(r, v, vcpu->kvm) {
264e83d5887SAndrey Smetanin 			if (v == vcpu) {
265e83d5887SAndrey Smetanin 				data = r;
266e83d5887SAndrey Smetanin 				break;
267e83d5887SAndrey Smetanin 			}
268e83d5887SAndrey Smetanin 		}
269e83d5887SAndrey Smetanin 		break;
270e83d5887SAndrey Smetanin 	}
271e83d5887SAndrey Smetanin 	case HV_X64_MSR_EOI:
272e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_read(vcpu, APIC_EOI, pdata);
273e83d5887SAndrey Smetanin 	case HV_X64_MSR_ICR:
274e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_read(vcpu, APIC_ICR, pdata);
275e83d5887SAndrey Smetanin 	case HV_X64_MSR_TPR:
276e83d5887SAndrey Smetanin 		return kvm_hv_vapic_msr_read(vcpu, APIC_TASKPRI, pdata);
277e83d5887SAndrey Smetanin 	case HV_X64_MSR_APIC_ASSIST_PAGE:
278e83d5887SAndrey Smetanin 		data = hv->hv_vapic;
279e83d5887SAndrey Smetanin 		break;
280e83d5887SAndrey Smetanin 	default:
281e83d5887SAndrey Smetanin 		vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
282e83d5887SAndrey Smetanin 		return 1;
283e83d5887SAndrey Smetanin 	}
284e83d5887SAndrey Smetanin 	*pdata = data;
285e83d5887SAndrey Smetanin 	return 0;
286e83d5887SAndrey Smetanin }
287e83d5887SAndrey Smetanin 
288*e7d9513bSAndrey Smetanin int kvm_hv_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
289e83d5887SAndrey Smetanin {
290e83d5887SAndrey Smetanin 	if (kvm_hv_msr_partition_wide(msr)) {
291e83d5887SAndrey Smetanin 		int r;
292e83d5887SAndrey Smetanin 
293e83d5887SAndrey Smetanin 		mutex_lock(&vcpu->kvm->lock);
294*e7d9513bSAndrey Smetanin 		r = kvm_hv_set_msr_pw(vcpu, msr, data, host);
295e83d5887SAndrey Smetanin 		mutex_unlock(&vcpu->kvm->lock);
296e83d5887SAndrey Smetanin 		return r;
297e83d5887SAndrey Smetanin 	} else
298e83d5887SAndrey Smetanin 		return kvm_hv_set_msr(vcpu, msr, data);
299e83d5887SAndrey Smetanin }
300e83d5887SAndrey Smetanin 
301e83d5887SAndrey Smetanin int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
302e83d5887SAndrey Smetanin {
303e83d5887SAndrey Smetanin 	if (kvm_hv_msr_partition_wide(msr)) {
304e83d5887SAndrey Smetanin 		int r;
305e83d5887SAndrey Smetanin 
306e83d5887SAndrey Smetanin 		mutex_lock(&vcpu->kvm->lock);
307e83d5887SAndrey Smetanin 		r = kvm_hv_get_msr_pw(vcpu, msr, pdata);
308e83d5887SAndrey Smetanin 		mutex_unlock(&vcpu->kvm->lock);
309e83d5887SAndrey Smetanin 		return r;
310e83d5887SAndrey Smetanin 	} else
311e83d5887SAndrey Smetanin 		return kvm_hv_get_msr(vcpu, msr, pdata);
312e83d5887SAndrey Smetanin }
313e83d5887SAndrey Smetanin 
314e83d5887SAndrey Smetanin bool kvm_hv_hypercall_enabled(struct kvm *kvm)
315e83d5887SAndrey Smetanin {
316e83d5887SAndrey Smetanin 	return kvm->arch.hyperv.hv_hypercall & HV_X64_MSR_HYPERCALL_ENABLE;
317e83d5887SAndrey Smetanin }
318e83d5887SAndrey Smetanin 
319e83d5887SAndrey Smetanin int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
320e83d5887SAndrey Smetanin {
321e83d5887SAndrey Smetanin 	u64 param, ingpa, outgpa, ret;
322e83d5887SAndrey Smetanin 	uint16_t code, rep_idx, rep_cnt, res = HV_STATUS_SUCCESS, rep_done = 0;
323e83d5887SAndrey Smetanin 	bool fast, longmode;
324e83d5887SAndrey Smetanin 
325e83d5887SAndrey Smetanin 	/*
326e83d5887SAndrey Smetanin 	 * hypercall generates UD from non zero cpl and real mode
327e83d5887SAndrey Smetanin 	 * per HYPER-V spec
328e83d5887SAndrey Smetanin 	 */
329e83d5887SAndrey Smetanin 	if (kvm_x86_ops->get_cpl(vcpu) != 0 || !is_protmode(vcpu)) {
330e83d5887SAndrey Smetanin 		kvm_queue_exception(vcpu, UD_VECTOR);
331e83d5887SAndrey Smetanin 		return 0;
332e83d5887SAndrey Smetanin 	}
333e83d5887SAndrey Smetanin 
334e83d5887SAndrey Smetanin 	longmode = is_64_bit_mode(vcpu);
335e83d5887SAndrey Smetanin 
336e83d5887SAndrey Smetanin 	if (!longmode) {
337e83d5887SAndrey Smetanin 		param = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDX) << 32) |
338e83d5887SAndrey Smetanin 			(kvm_register_read(vcpu, VCPU_REGS_RAX) & 0xffffffff);
339e83d5887SAndrey Smetanin 		ingpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RBX) << 32) |
340e83d5887SAndrey Smetanin 			(kvm_register_read(vcpu, VCPU_REGS_RCX) & 0xffffffff);
341e83d5887SAndrey Smetanin 		outgpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDI) << 32) |
342e83d5887SAndrey Smetanin 			(kvm_register_read(vcpu, VCPU_REGS_RSI) & 0xffffffff);
343e83d5887SAndrey Smetanin 	}
344e83d5887SAndrey Smetanin #ifdef CONFIG_X86_64
345e83d5887SAndrey Smetanin 	else {
346e83d5887SAndrey Smetanin 		param = kvm_register_read(vcpu, VCPU_REGS_RCX);
347e83d5887SAndrey Smetanin 		ingpa = kvm_register_read(vcpu, VCPU_REGS_RDX);
348e83d5887SAndrey Smetanin 		outgpa = kvm_register_read(vcpu, VCPU_REGS_R8);
349e83d5887SAndrey Smetanin 	}
350e83d5887SAndrey Smetanin #endif
351e83d5887SAndrey Smetanin 
352e83d5887SAndrey Smetanin 	code = param & 0xffff;
353e83d5887SAndrey Smetanin 	fast = (param >> 16) & 0x1;
354e83d5887SAndrey Smetanin 	rep_cnt = (param >> 32) & 0xfff;
355e83d5887SAndrey Smetanin 	rep_idx = (param >> 48) & 0xfff;
356e83d5887SAndrey Smetanin 
357e83d5887SAndrey Smetanin 	trace_kvm_hv_hypercall(code, fast, rep_cnt, rep_idx, ingpa, outgpa);
358e83d5887SAndrey Smetanin 
359e83d5887SAndrey Smetanin 	switch (code) {
360e83d5887SAndrey Smetanin 	case HV_X64_HV_NOTIFY_LONG_SPIN_WAIT:
361e83d5887SAndrey Smetanin 		kvm_vcpu_on_spin(vcpu);
362e83d5887SAndrey Smetanin 		break;
363e83d5887SAndrey Smetanin 	default:
364e83d5887SAndrey Smetanin 		res = HV_STATUS_INVALID_HYPERCALL_CODE;
365e83d5887SAndrey Smetanin 		break;
366e83d5887SAndrey Smetanin 	}
367e83d5887SAndrey Smetanin 
368e83d5887SAndrey Smetanin 	ret = res | (((u64)rep_done & 0xfff) << 32);
369e83d5887SAndrey Smetanin 	if (longmode) {
370e83d5887SAndrey Smetanin 		kvm_register_write(vcpu, VCPU_REGS_RAX, ret);
371e83d5887SAndrey Smetanin 	} else {
372e83d5887SAndrey Smetanin 		kvm_register_write(vcpu, VCPU_REGS_RDX, ret >> 32);
373e83d5887SAndrey Smetanin 		kvm_register_write(vcpu, VCPU_REGS_RAX, ret & 0xffffffff);
374e83d5887SAndrey Smetanin 	}
375e83d5887SAndrey Smetanin 
376e83d5887SAndrey Smetanin 	return 1;
377e83d5887SAndrey Smetanin }
378