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