VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/GIMAllHv.cpp@ 58126

Last change on this file since 58126 was 58123, checked in by vboxsync, 9 years ago

VMM: Made @param pVCpu more uniform and to the point.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1/* $Id: GIMAllHv.cpp 58123 2015-10-08 18:09:45Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager, Microsoft Hyper-V, All Contexts.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GIM
23#include "GIMHvInternal.h"
24#include "GIMInternal.h"
25
26#include <iprt/asm-amd64-x86.h>
27#ifdef IN_RING3
28# include <iprt/mem.h>
29#endif
30
31#include <VBox/err.h>
32#include <VBox/vmm/em.h>
33#include <VBox/vmm/hm.h>
34#include <VBox/vmm/tm.h>
35#include <VBox/vmm/vm.h>
36#include <VBox/vmm/pgm.h>
37#include <VBox/vmm/pdmdev.h>
38#include <VBox/vmm/pdmapi.h>
39
40
41#ifdef IN_RING3
42/**
43 * Helper for reading and validating slow hypercall input/output parameters.
44 *
45 * A 'slow' hypercall is one that passes parameters pointers through guest
46 * memory as opposed to a 'fast' hypercall which passes parameters through guest
47 * general-purpose registers.
48 *
49 * @returns VBox status code.
50 * @param pVM The cross context VM structure.
51 * @param pCtx Pointer to the guest-CPU context.
52 * @param fIs64BitMode Whether the guest is currently in 64-bit mode or not.
53 * @param pGCPhysIn Where to store the guest-physical address of the
54 * hypercall input page. Optional, can be NULL.
55 * @param pGCPhysOut Where to store the guest-physical address of the
56 * hypercall output page. Optional, can be NULL.
57 * @param prcHv Where to store the Hyper-V status code. Only valid
58 * to the caller when this function returns
59 * VINF_SUCCESS.
60 */
61static int gimHvReadSlowHypercallParams(PVM pVM, PCPUMCTX pCtx, bool fIs64BitMode, PRTGCPHYS pGCPhysIn, PRTGCPHYS pGCPhysOut,
62 int *prcHv)
63{
64 int rc = VINF_SUCCESS;
65 RTGCPHYS GCPhysIn = fIs64BitMode ? pCtx->rdx : (pCtx->rbx << 32) | pCtx->ecx;
66 RTGCPHYS GCPhysOut = fIs64BitMode ? pCtx->r8 : (pCtx->rdi << 32) | pCtx->esi;
67 if (pGCPhysIn)
68 *pGCPhysIn = GCPhysIn;
69 if (pGCPhysOut)
70 *pGCPhysOut = GCPhysOut;
71 if ( RT_ALIGN_64(GCPhysIn, 8) == GCPhysIn
72 && RT_ALIGN_64(GCPhysOut, 8) == GCPhysOut)
73 {
74 if ( PGMPhysIsGCPhysNormal(pVM, GCPhysIn)
75 && PGMPhysIsGCPhysNormal(pVM, GCPhysOut))
76 {
77 PGIMHV pHv = &pVM->gim.s.u.Hv;
78 rc = PGMPhysSimpleReadGCPhys(pVM, pHv->pbHypercallIn, GCPhysIn, GIM_HV_PAGE_SIZE);
79 if (RT_SUCCESS(rc))
80 {
81 rc = PGMPhysSimpleReadGCPhys(pVM, pHv->pbHypercallOut, GCPhysOut, GIM_HV_PAGE_SIZE);
82 if (RT_SUCCESS(rc))
83 {
84 *prcHv = GIM_HV_STATUS_SUCCESS;
85 return VINF_SUCCESS;
86 }
87 Log(("GIM: HyperV: gimHvReadSlowHypercallParams reading GCPhysOut=%#RGp failed. rc=%Rrc\n", GCPhysOut, rc));
88 rc = VERR_GIM_HYPERCALL_MEMORY_READ_FAILED;
89 }
90 else
91 {
92 Log(("GIM: HyperV: gimHvReadSlowHypercallParams reading GCPhysIn=%#RGp failed. rc=%Rrc\n", GCPhysIn,rc));
93 rc = VERR_GIM_HYPERCALL_MEMORY_READ_FAILED;
94 }
95 }
96 else
97 *prcHv = GIM_HV_STATUS_INVALID_PARAMETER;
98 }
99 else
100 *prcHv = GIM_HV_STATUS_INVALID_ALIGNMENT;
101 return rc;
102}
103#endif
104
105
106/**
107 * Handles all Hyper-V hypercalls.
108 *
109 * @returns VBox status code.
110 * @param pVCpu The cross context virtual CPU structure.
111 * @param pCtx Pointer to the guest-CPU context.
112 *
113 * @thread EMT.
114 */
115VMM_INT_DECL(int) gimHvHypercall(PVMCPU pVCpu, PCPUMCTX pCtx)
116{
117#ifndef IN_RING3
118 return VINF_GIM_R3_HYPERCALL;
119#else
120 PVM pVM = pVCpu->CTX_SUFF(pVM);
121
122 /*
123 * Verify that hypercalls are enabled.
124 */
125 if (!gimHvAreHypercallsEnabled(pVCpu))
126 return VERR_GIM_HYPERCALLS_NOT_ENABLED;
127
128 /*
129 * Verify guest is in ring-0 protected mode.
130 */
131 uint32_t uCpl = CPUMGetGuestCPL(pVCpu);
132 if ( uCpl
133 || CPUMIsGuestInRealModeEx(pCtx))
134 {
135 return VERR_GIM_HYPERCALL_ACCESS_DENIED;
136 }
137
138 /*
139 * Get the hypercall operation code and modes.
140 */
141 const bool fIs64BitMode = CPUMIsGuestIn64BitCodeEx(pCtx);
142 const uint64_t uHyperIn = fIs64BitMode ? pCtx->rcx : (pCtx->rdx << 32) | pCtx->eax;
143 const uint16_t uHyperOp = GIM_HV_HYPERCALL_IN_CALL_CODE(uHyperIn);
144 const bool fHyperFast = GIM_HV_HYPERCALL_IN_IS_FAST(uHyperIn);
145 const uint16_t cHyperReps = GIM_HV_HYPERCALL_IN_REP_COUNT(uHyperIn);
146 const uint16_t idxHyperRepStart = GIM_HV_HYPERCALL_IN_REP_START_IDX(uHyperIn);
147 uint64_t cHyperRepsDone = 0;
148
149 int rc = VINF_SUCCESS;
150 int rcHv = GIM_HV_STATUS_OPERATION_DENIED;
151 PGIMHV pHv = &pVM->gim.s.u.Hv;
152
153 /*
154 * Validate common hypercall input parameters.
155 */
156 if ( !GIM_HV_HYPERCALL_IN_RSVD_1(uHyperIn)
157 && !GIM_HV_HYPERCALL_IN_RSVD_2(uHyperIn)
158 && !GIM_HV_HYPERCALL_IN_RSVD_3(uHyperIn))
159 {
160 /*
161 * Perform the hypercall.
162 */
163 switch (uHyperOp)
164 {
165 case GIM_HV_HYPERCALL_OP_RETREIVE_DEBUG_DATA: /* Non-rep, memory IO. */
166 {
167 if (pHv->uPartFlags & GIM_HV_PART_FLAGS_DEBUGGING)
168 {
169 RTGCPHYS GCPhysOut;
170 rc = gimHvReadSlowHypercallParams(pVM, pCtx, fIs64BitMode, NULL /*pGCPhysIn*/, &GCPhysOut, &rcHv);
171 if ( RT_SUCCESS(rc)
172 && rcHv == GIM_HV_STATUS_SUCCESS)
173 {
174 LogRelMax(1, ("GIM: HyperV: Guest initiated debug data reception\n"));
175 rc = gimR3HvHypercallRetrieveDebugData(pVM, GCPhysOut, &rcHv);
176 if (RT_FAILURE(rc))
177 LogRelMax(10, ("GIM: HyperV: gimR3HvHypercallRetrieveDebugData failed. rc=%Rrc\n", rc));
178 }
179 }
180 else
181 rcHv = GIM_HV_STATUS_ACCESS_DENIED;
182 break;
183 }
184
185 case GIM_HV_HYPERCALL_OP_POST_DEBUG_DATA: /* Non-rep, memory IO. */
186 {
187 if (pHv->uPartFlags & GIM_HV_PART_FLAGS_DEBUGGING)
188 {
189 RTGCPHYS GCPhysOut;
190 rc = gimHvReadSlowHypercallParams(pVM, pCtx, fIs64BitMode, NULL /*pGCPhysIn*/, &GCPhysOut, &rcHv);
191 if ( RT_SUCCESS(rc)
192 && rcHv == GIM_HV_STATUS_SUCCESS)
193 {
194 LogRelMax(1, ("GIM: HyperV: Guest initiated debug data transmission\n"));
195 rc = gimR3HvHypercallPostDebugData(pVM, GCPhysOut, &rcHv);
196 if (RT_FAILURE(rc))
197 LogRelMax(10, ("GIM: HyperV: gimR3HvHypercallPostDebugData failed. rc=%Rrc\n", rc));
198 }
199 }
200 else
201 rcHv = GIM_HV_STATUS_ACCESS_DENIED;
202 break;
203 }
204
205 case GIM_HV_HYPERCALL_OP_RESET_DEBUG_SESSION: /* Non-rep, fast (register IO). */
206 {
207 if (pHv->uPartFlags & GIM_HV_PART_FLAGS_DEBUGGING)
208 {
209 uint32_t fFlags = 0;
210 if (!fHyperFast)
211 {
212 rc = gimHvReadSlowHypercallParams(pVM, pCtx, fIs64BitMode, NULL /*pGCPhysIn*/, NULL /*pGCPhysOut*/,
213 &rcHv);
214 if ( RT_SUCCESS(rc)
215 && rcHv == GIM_HV_STATUS_SUCCESS)
216 {
217 PGIMHVDEBUGRESETIN pIn = (PGIMHVDEBUGRESETIN)pHv->pbHypercallIn;
218 fFlags = pIn->fFlags;
219 }
220 }
221 else
222 {
223 rcHv = GIM_HV_STATUS_SUCCESS;
224 fFlags = fIs64BitMode ? pCtx->rdx : pCtx->ebx;
225 }
226
227 /*
228 * Since we don't really maintain our own buffers for the debug
229 * communication channel, we don't have anything to flush.
230 */
231 if (rcHv == GIM_HV_STATUS_SUCCESS)
232 {
233 if (!fFlags)
234 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
235 else
236 LogRelMax(1, ("GIM: HyperV: Guest resetting debug session\n"));
237 }
238 }
239 else
240 rcHv = GIM_HV_STATUS_ACCESS_DENIED;
241 break;
242 }
243
244 default:
245 rcHv = GIM_HV_STATUS_INVALID_HYPERCALL_CODE;
246 break;
247 }
248 }
249 else
250 rcHv = GIM_HV_STATUS_INVALID_HYPERCALL_INPUT;
251
252 /*
253 * Update the guest with results of the hypercall.
254 */
255 if (RT_SUCCESS(rc))
256 {
257 if (fIs64BitMode)
258 pCtx->rax = (cHyperRepsDone << 32) | rcHv;
259 else
260 {
261 pCtx->edx = cHyperRepsDone;
262 pCtx->eax = rcHv;
263 }
264 }
265
266 return rc;
267#endif
268}
269
270
271/**
272 * Returns whether the guest has configured and enabled the use of Hyper-V's
273 * hypercall interface.
274 *
275 * @returns true if hypercalls are enabled, false otherwise.
276 * @param pVCpu The cross context virtual CPU structure.
277 */
278VMM_INT_DECL(bool) gimHvAreHypercallsEnabled(PVMCPU pVCpu)
279{
280 return RT_BOOL(pVCpu->CTX_SUFF(pVM)->gim.s.u.Hv.u64GuestOsIdMsr != 0);
281}
282
283
284/**
285 * Returns whether the guest has configured and enabled the use of Hyper-V's
286 * paravirtualized TSC.
287 *
288 * @returns true if paravirt. TSC is enabled, false otherwise.
289 * @param pVM The cross context VM structure.
290 */
291VMM_INT_DECL(bool) gimHvIsParavirtTscEnabled(PVM pVM)
292{
293 return MSR_GIM_HV_REF_TSC_IS_ENABLED(pVM->gim.s.u.Hv.u64TscPageMsr);
294}
295
296
297#ifdef IN_RING3
298/**
299 * Gets the descriptive OS ID variant as identified via the
300 * MSR_GIM_HV_GUEST_OS_ID MSR.
301 *
302 * @returns The name.
303 * @param uGuestOsIdMsr The MSR_GIM_HV_GUEST_OS_ID MSR.
304 */
305static const char *gimHvGetGuestOsIdVariantName(uint64_t uGuestOsIdMsr)
306{
307 /* Refer the Hyper-V spec, section 3.6 "Reporting the Guest OS Identity". */
308 uint32_t uVendor = MSR_GIM_HV_GUEST_OS_ID_VENDOR(uGuestOsIdMsr);
309 if (uVendor == 1 /* Microsoft */)
310 {
311 uint32_t uOsVariant = MSR_GIM_HV_GUEST_OS_ID_OS_VARIANT(uGuestOsIdMsr);
312 switch (uOsVariant)
313 {
314 case 0: return "Undefined";
315 case 1: return "MS-DOS";
316 case 2: return "Windows 3.x";
317 case 3: return "Windows 9x";
318 case 4: return "Windows NT or derivative";
319 case 5: return "Windows CE";
320 default: return "Unknown";
321 }
322 }
323 return "Unknown";
324}
325#endif
326
327
328/**
329 * MSR read handler for Hyper-V.
330 *
331 * @returns Strict VBox status code like CPUMQueryGuestMsr().
332 * @retval VINF_CPUM_R3_MSR_READ
333 * @retval VERR_CPUM_RAISE_GP_0
334 *
335 * @param pVCpu The cross context virtual CPU structure.
336 * @param idMsr The MSR being read.
337 * @param pRange The range this MSR belongs to.
338 * @param puValue Where to store the MSR value read.
339 *
340 * @thread EMT.
341 */
342VMM_INT_DECL(VBOXSTRICTRC) gimHvReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue)
343{
344 NOREF(pRange);
345 PVM pVM = pVCpu->CTX_SUFF(pVM);
346 PGIMHV pHv = &pVM->gim.s.u.Hv;
347
348 switch (idMsr)
349 {
350 case MSR_GIM_HV_TIME_REF_COUNT:
351 {
352 /* Hyper-V reports the time in 100 ns units (10 MHz). */
353 uint64_t u64Tsc = TMCpuTickGet(pVCpu);
354 uint64_t u64TscHz = pHv->cTscTicksPerSecond;
355 uint64_t u64Tsc100Ns = u64TscHz / UINT64_C(10000000); /* 100 ns */
356 *puValue = (u64Tsc / u64Tsc100Ns);
357 return VINF_SUCCESS;
358 }
359
360 case MSR_GIM_HV_VP_INDEX:
361 *puValue = pVCpu->idCpu;
362 return VINF_SUCCESS;
363
364 case MSR_GIM_HV_TPR:
365 PDMApicReadMSR(pVM, pVCpu->idCpu, 0x80, puValue);
366 return VINF_SUCCESS;
367
368 case MSR_GIM_HV_EOI:
369 PDMApicReadMSR(pVM, pVCpu->idCpu, 0x0B, puValue);
370 return VINF_SUCCESS;
371
372 case MSR_GIM_HV_ICR:
373 PDMApicReadMSR(pVM, pVCpu->idCpu, 0x30, puValue);
374 return VINF_SUCCESS;
375
376 case MSR_GIM_HV_GUEST_OS_ID:
377 *puValue = pHv->u64GuestOsIdMsr;
378 return VINF_SUCCESS;
379
380 case MSR_GIM_HV_HYPERCALL:
381 *puValue = pHv->u64HypercallMsr;
382 return VINF_SUCCESS;
383
384 case MSR_GIM_HV_REF_TSC:
385 *puValue = pHv->u64TscPageMsr;
386 return VINF_SUCCESS;
387
388 case MSR_GIM_HV_TSC_FREQ:
389 *puValue = TMCpuTicksPerSecond(pVM);
390 return VINF_SUCCESS;
391
392 case MSR_GIM_HV_APIC_FREQ:
393 {
394 int rc = PDMApicGetTimerFreq(pVM, puValue);
395 if (RT_FAILURE(rc))
396 return VERR_CPUM_RAISE_GP_0;
397 return VINF_SUCCESS;
398 }
399
400 case MSR_GIM_HV_RESET:
401 *puValue = 0;
402 return VINF_SUCCESS;
403
404 case MSR_GIM_HV_CRASH_CTL:
405 *puValue = pHv->uCrashCtl;
406 return VINF_SUCCESS;
407
408 case MSR_GIM_HV_CRASH_P0: *puValue = pHv->uCrashP0; return VINF_SUCCESS;
409 case MSR_GIM_HV_CRASH_P1: *puValue = pHv->uCrashP1; return VINF_SUCCESS;
410 case MSR_GIM_HV_CRASH_P2: *puValue = pHv->uCrashP2; return VINF_SUCCESS;
411 case MSR_GIM_HV_CRASH_P3: *puValue = pHv->uCrashP3; return VINF_SUCCESS;
412 case MSR_GIM_HV_CRASH_P4: *puValue = pHv->uCrashP4; return VINF_SUCCESS;
413
414 case MSR_GIM_HV_DEBUG_OPTIONS_MSR:
415 {
416 if (pHv->fIsVendorMsHv)
417 {
418#ifndef IN_RING3
419 return VINF_CPUM_R3_MSR_READ;
420#else
421 LogRelMax(1, ("GIM: HyperV: Guest querying debug options MSR, returning %#x\n", GIM_HV_DEBUG_OPTIONS_MSR_ENABLE));
422 *puValue = GIM_HV_DEBUG_OPTIONS_MSR_ENABLE;
423 return VINF_SUCCESS;
424#endif
425 }
426 return VERR_CPUM_RAISE_GP_0;
427 }
428
429 default:
430 {
431#ifdef IN_RING3
432 static uint32_t s_cTimes = 0;
433 if (s_cTimes++ < 20)
434 LogRel(("GIM: HyperV: Unknown/invalid RdMsr (%#x) -> #GP(0)\n", idMsr));
435#else
436 return VINF_CPUM_R3_MSR_READ;
437#endif
438 LogFunc(("Unknown/invalid RdMsr (%#RX32) -> #GP(0)\n", idMsr));
439 break;
440 }
441 }
442
443 return VERR_CPUM_RAISE_GP_0;
444}
445
446
447/**
448 * MSR write handler for Hyper-V.
449 *
450 * @returns Strict VBox status code like CPUMSetGuestMsr().
451 * @retval VINF_CPUM_R3_MSR_WRITE
452 * @retval VERR_CPUM_RAISE_GP_0
453 *
454 * @param pVCpu The cross context virtual CPU structure.
455 * @param idMsr The MSR being written.
456 * @param pRange The range this MSR belongs to.
457 * @param uRawValue The raw value with the ignored bits not masked.
458 *
459 * @thread EMT.
460 */
461VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue)
462{
463 NOREF(pRange);
464 PVM pVM = pVCpu->CTX_SUFF(pVM);
465 PGIMHV pHv = &pVM->gim.s.u.Hv;
466
467 switch (idMsr)
468 {
469 case MSR_GIM_HV_TPR:
470 PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x80, uRawValue);
471 return VINF_SUCCESS;
472
473 case MSR_GIM_HV_EOI:
474 PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x0B, uRawValue);
475 return VINF_SUCCESS;
476
477 case MSR_GIM_HV_ICR:
478 PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x30, uRawValue);
479 return VINF_SUCCESS;
480
481 case MSR_GIM_HV_GUEST_OS_ID:
482 {
483#ifndef IN_RING3
484 return VINF_CPUM_R3_MSR_WRITE;
485#else
486 /* Disable the hypercall-page and hypercalls if 0 is written to this MSR. */
487 if (!uRawValue)
488 {
489 if (MSR_GIM_HV_HYPERCALL_PAGE_IS_ENABLED(pHv->u64HypercallMsr))
490 {
491 gimR3HvDisableHypercallPage(pVM);
492 pHv->u64HypercallMsr &= ~MSR_GIM_HV_HYPERCALL_PAGE_ENABLE_BIT;
493 LogRel(("GIM: HyperV: Hypercall page disabled via Guest OS ID MSR\n"));
494 }
495 }
496 else
497 {
498 LogRel(("GIM: HyperV: Guest OS reported ID %#RX64\n", uRawValue));
499 LogRel(("GIM: HyperV: Open-source=%RTbool Vendor=%#x OS=%#x (%s) Major=%u Minor=%u ServicePack=%u Build=%u\n",
500 MSR_GIM_HV_GUEST_OS_ID_IS_OPENSOURCE(uRawValue), MSR_GIM_HV_GUEST_OS_ID_VENDOR(uRawValue),
501 MSR_GIM_HV_GUEST_OS_ID_OS_VARIANT(uRawValue), gimHvGetGuestOsIdVariantName(uRawValue),
502 MSR_GIM_HV_GUEST_OS_ID_MAJOR_VERSION(uRawValue), MSR_GIM_HV_GUEST_OS_ID_MINOR_VERSION(uRawValue),
503 MSR_GIM_HV_GUEST_OS_ID_SERVICE_VERSION(uRawValue), MSR_GIM_HV_GUEST_OS_ID_BUILD(uRawValue)));
504
505 /* Update the CPUID leaf, see Hyper-V spec. "Microsoft Hypervisor CPUID Leaves". */
506 CPUMCPUIDLEAF HyperLeaf;
507 RT_ZERO(HyperLeaf);
508 HyperLeaf.uLeaf = UINT32_C(0x40000002);
509 HyperLeaf.uEax = MSR_GIM_HV_GUEST_OS_ID_BUILD(uRawValue);
510 HyperLeaf.uEbx = MSR_GIM_HV_GUEST_OS_ID_MINOR_VERSION(uRawValue)
511 | (MSR_GIM_HV_GUEST_OS_ID_MAJOR_VERSION(uRawValue) << 16);
512 HyperLeaf.uEcx = MSR_GIM_HV_GUEST_OS_ID_SERVICE_VERSION(uRawValue);
513 HyperLeaf.uEdx = MSR_GIM_HV_GUEST_OS_ID_SERVICE_VERSION(uRawValue)
514 | (MSR_GIM_HV_GUEST_OS_ID_BUILD(uRawValue) << 24);
515 int rc2 = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
516 AssertRC(rc2);
517 }
518
519 pHv->u64GuestOsIdMsr = uRawValue;
520
521 /*
522 * Notify VMM that hypercalls are now disabled/enabled.
523 */
524 for (VMCPUID i = 0; i < pVM->cCpus; i++)
525 {
526 if (uRawValue)
527 VMMHypercallsEnable(&pVM->aCpus[i]);
528 else
529 VMMHypercallsDisable(&pVM->aCpus[i]);
530 }
531
532 return VINF_SUCCESS;
533#endif /* IN_RING3 */
534 }
535
536 case MSR_GIM_HV_HYPERCALL:
537 {
538#ifndef IN_RING3
539 return VINF_CPUM_R3_MSR_WRITE;
540#else /* IN_RING3 */
541# if 0
542 /*
543 * For now ignore writes to the hypercall MSR (i.e. keeps it disabled).
544 * This is required to boot FreeBSD 10.1 (with Hyper-V enabled ofc),
545 * see @bugref{7270#c116}.
546 */
547 return VINF_SUCCESS;
548# else
549 /* First, update all but the hypercall page enable bit. */
550 pHv->u64HypercallMsr = (uRawValue & ~MSR_GIM_HV_HYPERCALL_PAGE_ENABLE_BIT);
551
552 /* Hypercall page can only be enabled when the guest has enabled hypercalls. */
553 bool fEnable = RT_BOOL(uRawValue & MSR_GIM_HV_HYPERCALL_PAGE_ENABLE_BIT);
554 if ( fEnable
555 && !gimHvAreHypercallsEnabled(pVCpu))
556 {
557 return VINF_SUCCESS;
558 }
559
560 /* Is the guest disabling the hypercall-page? Allow it regardless of the Guest-OS Id Msr. */
561 if (!fEnable)
562 {
563 gimR3HvDisableHypercallPage(pVM);
564 pHv->u64HypercallMsr = uRawValue;
565 return VINF_SUCCESS;
566 }
567
568 /* Enable the hypercall-page. */
569 RTGCPHYS GCPhysHypercallPage = MSR_GIM_HV_HYPERCALL_GUEST_PFN(uRawValue) << PAGE_SHIFT;
570 int rc = gimR3HvEnableHypercallPage(pVM, GCPhysHypercallPage);
571 if (RT_SUCCESS(rc))
572 {
573 pHv->u64HypercallMsr = uRawValue;
574 return VINF_SUCCESS;
575 }
576
577 return VERR_CPUM_RAISE_GP_0;
578# endif
579#endif /* IN_RING3 */
580 }
581
582 case MSR_GIM_HV_REF_TSC:
583 {
584#ifndef IN_RING3
585 return VINF_CPUM_R3_MSR_WRITE;
586#else /* IN_RING3 */
587 /* First, update all but the TSC-page enable bit. */
588 pHv->u64TscPageMsr = (uRawValue & ~MSR_GIM_HV_REF_TSC_ENABLE_BIT);
589
590 /* Is the guest disabling the TSC-page? */
591 bool fEnable = RT_BOOL(uRawValue & MSR_GIM_HV_REF_TSC_ENABLE_BIT);
592 if (!fEnable)
593 {
594 gimR3HvDisableTscPage(pVM);
595 pHv->u64TscPageMsr = uRawValue;
596 return VINF_SUCCESS;
597 }
598
599 /* Enable the TSC-page. */
600 RTGCPHYS GCPhysTscPage = MSR_GIM_HV_REF_TSC_GUEST_PFN(uRawValue) << PAGE_SHIFT;
601 int rc = gimR3HvEnableTscPage(pVM, GCPhysTscPage, false /* fUseThisTscSequence */, 0 /* uTscSequence */);
602 if (RT_SUCCESS(rc))
603 {
604 pHv->u64TscPageMsr = uRawValue;
605 return VINF_SUCCESS;
606 }
607
608 return VERR_CPUM_RAISE_GP_0;
609#endif /* IN_RING3 */
610 }
611
612 case MSR_GIM_HV_RESET:
613 {
614#ifndef IN_RING3
615 return VINF_CPUM_R3_MSR_WRITE;
616#else
617 if (MSR_GIM_HV_RESET_IS_SET(uRawValue))
618 {
619 LogRel(("GIM: HyperV: Reset initiated through MSR\n"));
620 int rc = PDMDevHlpVMReset(pVM->gim.s.pDevInsR3);
621 AssertRC(rc);
622 }
623 /* else: Ignore writes to other bits. */
624 return VINF_SUCCESS;
625#endif /* IN_RING3 */
626 }
627
628 case MSR_GIM_HV_CRASH_CTL:
629 {
630#ifndef IN_RING3
631 return VINF_CPUM_R3_MSR_WRITE;
632#else
633 if (uRawValue & MSR_GIM_HV_CRASH_CTL_NOTIFY_BIT)
634 {
635 LogRel(("GIM: HyperV: Guest indicates a fatal condition! P0=%#RX64 P1=%#RX64 P2=%#RX64 P3=%#RX64 P4=%#RX64\n",
636 pHv->uCrashP0, pHv->uCrashP1, pHv->uCrashP2, pHv->uCrashP3, pHv->uCrashP4));
637 }
638 return VINF_SUCCESS;
639#endif
640 }
641
642 case MSR_GIM_HV_CRASH_P0: pHv->uCrashP0 = uRawValue; return VINF_SUCCESS;
643 case MSR_GIM_HV_CRASH_P1: pHv->uCrashP1 = uRawValue; return VINF_SUCCESS;
644 case MSR_GIM_HV_CRASH_P2: pHv->uCrashP2 = uRawValue; return VINF_SUCCESS;
645 case MSR_GIM_HV_CRASH_P3: pHv->uCrashP3 = uRawValue; return VINF_SUCCESS;
646 case MSR_GIM_HV_CRASH_P4: pHv->uCrashP4 = uRawValue; return VINF_SUCCESS;
647
648 case MSR_GIM_HV_TIME_REF_COUNT: /* Read-only MSRs. */
649 case MSR_GIM_HV_VP_INDEX:
650 case MSR_GIM_HV_TSC_FREQ:
651 case MSR_GIM_HV_APIC_FREQ:
652 LogFunc(("WrMsr on read-only MSR %#RX32 -> #GP(0)\n", idMsr));
653 return VERR_CPUM_RAISE_GP_0;
654
655 case MSR_GIM_HV_DEBUG_OPTIONS_MSR:
656 {
657 if (pHv->fIsVendorMsHv)
658 {
659#ifndef IN_RING3
660 return VINF_CPUM_R3_MSR_WRITE;
661#else
662 LogRelMax(1, ("GIM: HyperV: Guest setting debug options MSR to %#RX64, ignoring\n", uRawValue));
663 return VINF_SUCCESS;
664#endif
665 }
666 return VERR_CPUM_RAISE_GP_0;
667 }
668
669 default:
670 {
671#ifdef IN_RING3
672 static uint32_t s_cTimes = 0;
673 if (s_cTimes++ < 20)
674 LogRel(("GIM: HyperV: Unknown/invalid WrMsr (%#x,%#x`%08x) -> #GP(0)\n", idMsr,
675 uRawValue & UINT64_C(0xffffffff00000000), uRawValue & UINT64_C(0xffffffff)));
676#else
677 return VINF_CPUM_R3_MSR_WRITE;
678#endif
679 LogFunc(("Unknown/invalid WrMsr (%#RX32,%#RX64) -> #GP(0)\n", idMsr, uRawValue));
680 break;
681 }
682 }
683
684 return VERR_CPUM_RAISE_GP_0;
685}
686
687
688/**
689 * Whether we need to trap \#UD exceptions in the guest.
690 *
691 * We only need to trap \#UD exceptions for raw-mode guests when hypercalls are
692 * enabled. For HM VMs, the hypercall would be handled via the
693 * VMCALL/VMMCALL VM-exit.
694 *
695 * @param pVCpu The cross context virtual CPU structure.
696 */
697VMM_INT_DECL(bool) gimHvShouldTrapXcptUD(PVMCPU pVCpu)
698{
699 PVM pVM = pVCpu->CTX_SUFF(pVM);
700 if ( !HMIsEnabled(pVM)
701 && gimHvAreHypercallsEnabled(pVCpu))
702 return true;
703 return false;
704}
705
706
707/**
708 * Exception handler for \#UD.
709 *
710 * @param pVCpu The cross context virtual CPU structure.
711 * @param pCtx Pointer to the guest-CPU context.
712 * @param pDis Pointer to the disassembled instruction state at RIP.
713 * Optional, can be NULL.
714 *
715 * @thread EMT.
716 */
717VMM_INT_DECL(int) gimHvXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis)
718{
719 /*
720 * If we didn't ask for #UD to be trapped, bail.
721 */
722 PVM pVM = pVCpu->CTX_SUFF(pVM);
723 if (!gimHvShouldTrapXcptUD(pVCpu))
724 return VERR_GIM_OPERATION_FAILED;
725
726 int rc = VINF_SUCCESS;
727 if (!pDis)
728 {
729 /*
730 * Disassemble the instruction at RIP to figure out if it's the Intel VMCALL instruction
731 * or the AMD VMMCALL instruction and if so, handle it as a hypercall.
732 */
733 DISCPUSTATE Dis;
734 rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, NULL /* pcbInstr */);
735 pDis = &Dis;
736 }
737
738 if (RT_SUCCESS(rc))
739 {
740 CPUMCPUVENDOR enmGuestCpuVendor = CPUMGetGuestCpuVendor(pVM);
741 if ( ( pDis->pCurInstr->uOpcode == OP_VMCALL
742 && ( enmGuestCpuVendor == CPUMCPUVENDOR_INTEL
743 || enmGuestCpuVendor == CPUMCPUVENDOR_VIA))
744 || ( pDis->pCurInstr->uOpcode == OP_VMMCALL
745 && enmGuestCpuVendor == CPUMCPUVENDOR_AMD))
746 {
747 /*
748 * Make sure guest ring-0 is the one making the hypercall.
749 */
750 if (CPUMGetGuestCPL(pVCpu))
751 return VERR_GIM_HYPERCALL_ACCESS_DENIED;
752
753 /*
754 * Update RIP and perform the hypercall.
755 */
756 pCtx->rip += pDis->cbInstr;
757 rc = gimHvHypercall(pVCpu, pCtx);
758 }
759 else
760 rc = VERR_GIM_OPERATION_FAILED;
761 }
762 return rc;
763}
764
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette