VirtualBox

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

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

VMM: Doxygen fixes.

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

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