1 /*
2  * Microsoft Hyper-V emulation. See Microsoft's
3  * Hypervisor Top Level Functional Specification for more information.
4  *
5  * Copyright (C) 2019-2022 Intel Corporation.
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #include <types.h>
11 #include <asm/guest/vm.h>
12 #include <logmsg.h>
13 #include <asm/vmx.h>
14 #include <asm/guest/hyperv.h>
15 #include <asm/tsc.h>
16 
17 #define DBG_LEVEL_HYPERV		6U
18 
19 /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) */
20 #define CPUID3A_TIME_REF_COUNT_MSR	(1U << 1U)
21 /* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) */
22 #define CPUID3A_HYPERCALL_MSR		(1U << 5U)
23 /* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) */
24 #define CPUID3A_VP_INDEX_MSR		(1U << 6U)
25 /* Partition reference TSC MSR (HV_X64_MSR_REFERENCE_TSC) */
26 #define CPUID3A_REFERENCE_TSC_MSR	(1U << 9U)
27 /* Partition local APIC and TSC frequency registers (HV_X64_MSR_TSC_FREQUENCY/HV_X64_MSR_APIC_FREQUENCY) */
28 #define CPUID3A_ACCESS_FREQUENCY_MSRS	(1U << 11U)
29 /* Frequency MSRs available */
30 #define CPUID3D_FREQ_MSRS_AVAILABLE	(1U << 8U)
31 
32 struct HV_REFERENCE_TSC_PAGE {
33 	uint32_t tsc_sequence;
34 	uint32_t reserved1;
35 	uint64_t tsc_scale;
36 	uint64_t tsc_offset;
37 	uint64_t reserved2[509];
38 };
39 
40 static inline uint64_t
u64_shl64_div_u64(uint64_t a,uint64_t divisor)41 u64_shl64_div_u64(uint64_t a, uint64_t divisor)
42 {
43 	uint64_t ret, tmp;
44 
45 	asm volatile ("divq %2" :
46 		"=a" (ret), "=d" (tmp) :
47 		"rm" (divisor), "0" (0U), "1" (a));
48 
49 	return ret;
50 }
51 
52 static inline uint64_t
u64_mul_u64_shr64(uint64_t a,uint64_t b)53 u64_mul_u64_shr64(uint64_t a, uint64_t b)
54 {
55 	uint64_t ret, disc;
56 
57 	asm volatile ("mulq %3" :
58 		"=d" (ret), "=a" (disc) :
59 		"a" (a), "r" (b));
60 
61 	return ret;
62 }
63 
64 static void
hyperv_setup_tsc_page(const struct acrn_vcpu * vcpu,uint64_t val)65 hyperv_setup_tsc_page(const struct acrn_vcpu *vcpu, uint64_t val)
66 {
67 	union hyperv_ref_tsc_page_msr *ref_tsc_page = &vcpu->vm->arch_vm.hyperv.ref_tsc_page;
68 	struct HV_REFERENCE_TSC_PAGE *p;
69 	uint32_t tsc_seq;
70 
71 	ref_tsc_page->val64 = val;
72 
73 	if (ref_tsc_page->enabled == 1U) {
74 		p = (struct HV_REFERENCE_TSC_PAGE *)gpa2hva(vcpu->vm, ref_tsc_page->gpfn << PAGE_SHIFT);
75 		if (p != NULL) {
76 			stac();
77 			p->tsc_scale = vcpu->vm->arch_vm.hyperv.tsc_scale;
78 			p->tsc_offset = vcpu->vm->arch_vm.hyperv.tsc_offset;
79 			cpu_write_memory_barrier();
80 			tsc_seq = p->tsc_sequence + 1U;
81 			if ((tsc_seq == 0xFFFFFFFFU) || (tsc_seq == 0U)) {
82 				tsc_seq = 1U;
83 			}
84 			p->tsc_sequence = tsc_seq;
85 			clac();
86 		}
87 	}
88 }
89 
90 static inline uint64_t
hyperv_scale_tsc(uint64_t scale)91 hyperv_scale_tsc(uint64_t scale)
92 {
93 	uint64_t tsc;
94 
95 	tsc = rdtsc() + exec_vmread64(VMX_TSC_OFFSET_FULL);
96 
97 	return u64_mul_u64_shr64(tsc, scale);
98 }
99 
100 static inline uint64_t
hyperv_get_ReferenceTime(struct acrn_vm * vm)101 hyperv_get_ReferenceTime(struct acrn_vm *vm)
102 {
103 	return hyperv_scale_tsc(vm->arch_vm.hyperv.tsc_scale) - vm->arch_vm.hyperv.tsc_offset;
104 }
105 
106 static void
hyperv_setup_hypercall_page(const struct acrn_vcpu * vcpu,uint64_t val)107 hyperv_setup_hypercall_page(const struct acrn_vcpu *vcpu, uint64_t val)
108 {
109 	union hyperv_hypercall_msr hypercall;
110 	uint64_t page_gpa;
111 	void *page_hva;
112 
113 	/*
114 	 * All enlightened versions of Windows operating systems invoke guest hypercalls on
115 	 * the basis of the recommendations presented by the hypervisor in CPUID.40000004:EAX.
116 	 * A conforming hypervisor must return HV_STATUS_INVALID_HYPERCALL_CODE for any
117 	 * unimplemented hypercalls.
118 	 * ACRN does not wish to handle any hypercalls at moment, the following hypercall
119 	 * code page is implemented for this purpose.
120 	 * inst32[] for 32 bits:
121 	 * 	mov eax, 0x02 ; HV_STATUS_INVALID_HYPERCALL_CODE
122 	 * 	mov edx, 0
123 	 * 	ret
124 	 * inst64[] for 64 bits:
125 	 * 	mov rax, 0x02 ; HV_STATUS_INVALID_HYPERCALL_CODE
126 	 * 	ret
127 	 */
128 	const uint8_t inst32[11] = {0xb8U, 0x02U, 0x0U, 0x0U, 0x0U, 0xbaU, 0x0U, 0x0U, 0x0U, 0x0U, 0xc3U};
129 	const uint8_t inst64[8] = {0x48U, 0xc7U, 0xc0U, 0x02U, 0x0U, 0x0U, 0x0U, 0xc3U};
130 
131 	hypercall.val64 = val;
132 
133 	if (hypercall.enabled != 0UL) {
134 		page_gpa = hypercall.gpfn << PAGE_SHIFT;
135 		page_hva = gpa2hva(vcpu->vm, page_gpa);
136 		if (page_hva != NULL) {
137 			stac();
138 			(void)memset(page_hva, 0U, PAGE_SIZE);
139 			if (get_vcpu_mode(vcpu) == CPU_MODE_64BIT) {
140 				(void)memcpy_s(page_hva, 8U, inst64, 8U);
141 			} else {
142 				(void)memcpy_s(page_hva, 11U, inst32, 11U);
143 			}
144 			clac();
145 		}
146 	}
147 }
148 
149 int32_t
hyperv_wrmsr(struct acrn_vcpu * vcpu,uint32_t msr,uint64_t wval)150 hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval)
151 {
152 	int32_t ret = 0;
153 
154 	switch (msr) {
155 	case HV_X64_MSR_GUEST_OS_ID:
156 		vcpu->vm->arch_vm.hyperv.guest_os_id.val64 = wval;
157 		if (wval == 0UL) {
158 			vcpu->vm->arch_vm.hyperv.hypercall_page.enabled = 0UL;
159 		}
160 		break;
161 	case HV_X64_MSR_HYPERCALL:
162 		if (vcpu->vm->arch_vm.hyperv.guest_os_id.val64 == 0UL) {
163 			pr_warn("hv: %s: guest_os_id is 0", __func__);
164 			break;
165 		}
166 		vcpu->vm->arch_vm.hyperv.hypercall_page.val64 = wval;
167 		hyperv_setup_hypercall_page(vcpu, wval);
168 		break;
169 	case HV_X64_MSR_REFERENCE_TSC:
170 		hyperv_setup_tsc_page(vcpu, wval);
171 		break;
172 	case HV_X64_MSR_VP_INDEX:
173 	case HV_X64_MSR_TIME_REF_COUNT:
174 	case HV_X64_MSR_TSC_FREQUENCY:
175 	case HV_X64_MSR_APIC_FREQUENCY:
176 		/* read only */
177 		/* fallthrough */
178 	default:
179 		pr_err("hv: %s: unexpected MSR[0x%x] write", __func__, msr);
180 		ret = -1;
181 		break;
182 	}
183 
184 	dev_dbg(DBG_LEVEL_HYPERV, "hv: %s: MSR=0x%x wval=0x%lx vcpuid=%d vmid=%d",
185 		__func__, msr, wval, vcpu->vcpu_id, vcpu->vm->vm_id);
186 
187 	return ret;
188 }
189 
190 int32_t
hyperv_rdmsr(struct acrn_vcpu * vcpu,uint32_t msr,uint64_t * rval)191 hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval)
192 {
193 	int32_t ret = 0;
194 
195 	switch (msr) {
196 	case HV_X64_MSR_GUEST_OS_ID:
197 		*rval = vcpu->vm->arch_vm.hyperv.guest_os_id.val64;
198 		break;
199 	case HV_X64_MSR_HYPERCALL:
200 		*rval = vcpu->vm->arch_vm.hyperv.hypercall_page.val64;
201 		break;
202 	case HV_X64_MSR_VP_INDEX:
203 		*rval = vcpu->vcpu_id;
204 		break;
205 	case HV_X64_MSR_TIME_REF_COUNT:
206 		*rval = hyperv_get_ReferenceTime(vcpu->vm);
207 		break;
208 	case HV_X64_MSR_REFERENCE_TSC:
209 		*rval = vcpu->vm->arch_vm.hyperv.ref_tsc_page.val64;
210 		break;
211 	case HV_X64_MSR_TSC_FREQUENCY:
212 		*rval = get_tsc_khz() * 1000UL;
213 		break;
214 	case HV_X64_MSR_APIC_FREQUENCY:
215 		/* vLAPIC freq is the same as TSC freq */
216 		*rval = get_tsc_khz() * 1000UL;
217 		break;
218 	default:
219 		pr_err("hv: %s: unexpected MSR[0x%x] read", __func__, msr);
220 		ret = -1;
221 		break;
222 	}
223 
224 	dev_dbg(DBG_LEVEL_HYPERV, "hv: %s: MSR=0x%x rval=0x%lx vcpuid=%d vmid=%d",
225 		__func__, msr, *rval, vcpu->vcpu_id, vcpu->vm->vm_id);
226 
227 	return ret;
228 }
229 
230 void
hyperv_init_time(struct acrn_vm * vm)231 hyperv_init_time(struct acrn_vm *vm)
232 {
233 	uint64_t tsc_scale, tsc_khz = get_tsc_khz();
234 	uint64_t tsc_offset;
235 
236 	/*
237 	 * The partition reference time is computed by the following formula:
238 	 * ReferenceTime = ((VirtualTsc * TscScale) >> 64) + TscOffset
239 	 * ReferenceTime is in 100ns units
240 	 *
241 	 * ReferenceTime =
242 	 *     VirtualTsc / (get_tsc_khz() * 1000) * 1000000000 / 100
243 	 *     + TscOffset
244 	 *
245 	 * TscScale = (10000U << 64U) / get_tsc_khz()
246 	 */
247 	tsc_scale = u64_shl64_div_u64(10000U, tsc_khz);
248 	tsc_offset = hyperv_scale_tsc(tsc_scale);
249 
250 	vm->arch_vm.hyperv.tsc_scale = tsc_scale;
251 	vm->arch_vm.hyperv.tsc_offset = tsc_offset;
252 
253 	dev_dbg(DBG_LEVEL_HYPERV, "%s, tsc_scale = 0x%lx, tsc_offset = %ld",
254 		__func__, tsc_scale, tsc_offset);
255 }
256 
257 void
hyperv_init_vcpuid_entry(uint32_t leaf,uint32_t subleaf,uint32_t flags,struct vcpuid_entry * entry)258 hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
259 			 struct vcpuid_entry *entry)
260 {
261 	entry->leaf = leaf;
262 	entry->subleaf = subleaf;
263 	entry->flags = flags;
264 
265 	switch (leaf) {
266 	case 0x40000001U: /* HV interface version */
267 		entry->eax = 0x31237648U; /* "Hv#1" */
268 		entry->ebx = 0U;
269 		entry->ecx = 0U;
270 		entry->edx = 0U;
271 		break;
272 	case 0x40000002U: /* HV system identity */
273 		entry->eax = 0U;
274 		entry->ebx = 0U;
275 		entry->ecx = 0U;
276 		entry->edx = 0U;
277 		break;
278 	case 0x40000003U: /* HV supported feature */
279 		entry->eax = CPUID3A_HYPERCALL_MSR | CPUID3A_VP_INDEX_MSR |
280 			CPUID3A_TIME_REF_COUNT_MSR | CPUID3A_REFERENCE_TSC_MSR |
281 			CPUID3A_ACCESS_FREQUENCY_MSRS;
282 		entry->ebx = 0U;
283 		entry->ecx = 0U;
284 		entry->edx = CPUID3D_FREQ_MSRS_AVAILABLE;
285 		break;
286 	case 0x40000004U: /* HV Recommended hypercall usage */
287 		entry->eax = 0U;
288 		entry->ebx = 0U;
289 		entry->ecx = 0U;
290 		entry->edx = 0U;
291 		break;
292 	case 0x40000005U: /* HV Maximum Supported Virtual & logical Processors */
293 		entry->eax = MAX_VCPUS_PER_VM;
294 		entry->ebx = 0U;
295 		entry->ecx = 0U;
296 		entry->edx = 0U;
297 		break;
298 	case 0x40000006U: /* Implementation Hardware Features */
299 		entry->eax = 0U;
300 		entry->ebx = 0U;
301 		entry->ecx = 0U;
302 		entry->edx = 0U;
303 		break;
304 	default:
305 		/* do nothing */
306 		break;
307 	}
308 
309 	dev_dbg(DBG_LEVEL_HYPERV, "hv: %s: leaf=%x subleaf=%x flags=%x eax=%x ebx=%x ecx=%x edx=%x",
310 		__func__, leaf, subleaf, flags, entry->eax, entry->ebx, entry->ecx, entry->edx);
311 }
312 
313 void
hyperv_page_destory(struct acrn_vm * vm)314 hyperv_page_destory(struct acrn_vm *vm)
315 {
316 	/* Reset the hypercall page */
317 	vm->arch_vm.hyperv.hypercall_page.enabled = 0U;
318 	/* Reset OS id */
319 	vm->arch_vm.hyperv.guest_os_id.val64 = 0UL;
320 	/* Reset the TSC page */
321 	vm->arch_vm.hyperv.ref_tsc_page.enabled = 0UL;
322 }
323