VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GIMHv.cpp@ 58311

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

VMM/GIM: Implemented Hyper-V SINT2 and SIMP faking for making Windows 10 32-bit guests happy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.4 KB
Line 
1/* $Id: GIMHv.cpp 58283 2015-10-16 15:20:38Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager, Hyper-V implementation.
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 "GIMInternal.h"
24
25#include <iprt/assert.h>
26#include <iprt/err.h>
27#include <iprt/string.h>
28#include <iprt/mem.h>
29#include <iprt/semaphore.h>
30#include <iprt/spinlock.h>
31
32#include <VBox/vmm/cpum.h>
33#include <VBox/vmm/mm.h>
34#include <VBox/vmm/ssm.h>
35#include <VBox/vmm/vm.h>
36#include <VBox/vmm/hm.h>
37#include <VBox/vmm/pdmapi.h>
38#include <VBox/version.h>
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44/**
45 * GIM Hyper-V saved-state version.
46 */
47#define GIM_HV_SAVED_STATE_VERSION UINT32_C(1)
48
49#ifdef VBOX_WITH_STATISTICS
50# define GIMHV_MSRRANGE(a_uFirst, a_uLast, a_szName) \
51 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName, { 0 }, { 0 }, { 0 }, { 0 } }
52#else
53# define GIMHV_MSRRANGE(a_uFirst, a_uLast, a_szName) \
54 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName }
55#endif
56
57
58/*********************************************************************************************************************************
59* Global Variables *
60*********************************************************************************************************************************/
61/**
62 * Array of MSR ranges supported by Hyper-V.
63 */
64static CPUMMSRRANGE const g_aMsrRanges_HyperV[] =
65{
66 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE0_START, MSR_GIM_HV_RANGE0_END, "Hyper-V range 0"),
67 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE1_START, MSR_GIM_HV_RANGE1_END, "Hyper-V range 1"),
68 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE2_START, MSR_GIM_HV_RANGE2_END, "Hyper-V range 2"),
69 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE3_START, MSR_GIM_HV_RANGE3_END, "Hyper-V range 3"),
70 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE4_START, MSR_GIM_HV_RANGE4_END, "Hyper-V range 4"),
71 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE5_START, MSR_GIM_HV_RANGE5_END, "Hyper-V range 5"),
72 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE6_START, MSR_GIM_HV_RANGE6_END, "Hyper-V range 6"),
73 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE7_START, MSR_GIM_HV_RANGE7_END, "Hyper-V range 7"),
74 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE8_START, MSR_GIM_HV_RANGE8_END, "Hyper-V range 8"),
75 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE9_START, MSR_GIM_HV_RANGE9_END, "Hyper-V range 9"),
76 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE10_START, MSR_GIM_HV_RANGE10_END, "Hyper-V range 10"),
77 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE11_START, MSR_GIM_HV_RANGE11_END, "Hyper-V range 11"),
78 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE12_START, MSR_GIM_HV_RANGE12_END, "Hyper-V range 12")
79};
80#undef GIMHV_MSRRANGE
81
82
83/*********************************************************************************************************************************
84* Internal Functions *
85*********************************************************************************************************************************/
86static int gimR3HvInitHypercallSupport(PVM pVM);
87static void gimR3HvTermHypercallSupport(PVM pVM);
88
89
90/**
91 * Initializes the Hyper-V GIM provider.
92 *
93 * @returns VBox status code.
94 * @param pVM The cross context VM structure.
95 * @param pGimCfg The GIM CFGM node.
96 */
97VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg)
98{
99 AssertReturn(pVM, VERR_INVALID_PARAMETER);
100 AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_HYPERV, VERR_INTERNAL_ERROR_5);
101
102 int rc;
103 PGIMHV pHv = &pVM->gim.s.u.Hv;
104
105 /*
106 * Read configuration.
107 */
108 PCFGMNODE pCfgHv = CFGMR3GetChild(pGimCfg, "HyperV");
109 if (pCfgHv)
110 {
111 /*
112 * Validate the Hyper-V settings.
113 */
114 rc = CFGMR3ValidateConfig(pCfgHv, "/HyperV/",
115 "VendorID"
116 "|VSInterface",
117 "" /* pszValidNodes */, "GIM/HyperV" /* pszWho */, 0 /* uInstance */);
118 if (RT_FAILURE(rc))
119 return rc;
120 }
121
122 /** @cfgm{/GIM/HyperV/VendorID, string, 'VBoxVBoxVBox'}
123 * The Hyper-V vendor signature, must be 12 characters. */
124 char szVendor[13];
125 rc = CFGMR3QueryStringDef(pCfgHv, "VendorID", szVendor, sizeof(szVendor), "VBoxVBoxVBox");
126 AssertLogRelRCReturn(rc, rc);
127
128 LogRel(("GIM: HyperV: Reporting vendor as '%s'\n", szVendor));
129 if (!RTStrNCmp(szVendor, GIM_HV_VENDOR_MICROSOFT, sizeof(GIM_HV_VENDOR_MICROSOFT) - 1))
130 {
131 LogRel(("GIM: HyperV: Warning! Posing as the Microsoft vendor, guest behavior may be altered!\n"));
132 pHv->fIsVendorMsHv = true;
133 }
134
135 if (pHv->fIsVendorMsHv)
136 {
137 /** @cfgm{/GIM/HyperV/VSInterface, bool, true}
138 * The Microsoft virtualization service interface (debugging). */
139 rc = CFGMR3QueryBoolDef(pCfgHv, "VSInterface", &pHv->fIsInterfaceVs, true);
140 AssertLogRelRCReturn(rc, rc);
141 }
142 else
143 Assert(pHv->fIsInterfaceVs == false);
144
145 /*
146 * Determine interface capabilities based on the version.
147 */
148 if (!pVM->gim.s.u32Version)
149 {
150 /* Basic features. */
151 pHv->uBaseFeat = 0
152 //| GIM_HV_BASE_FEAT_VP_RUNTIME_MSR
153 | GIM_HV_BASE_FEAT_PART_TIME_REF_COUNT_MSR
154 //| GIM_HV_BASE_FEAT_BASIC_SYNTH_IC
155 //| GIM_HV_BASE_FEAT_SYNTH_TIMER_MSRS
156 | GIM_HV_BASE_FEAT_APIC_ACCESS_MSRS
157 | GIM_HV_BASE_FEAT_HYPERCALL_MSRS
158 | GIM_HV_BASE_FEAT_VP_ID_MSR
159 | GIM_HV_BASE_FEAT_VIRT_SYS_RESET_MSR
160 //| GIM_HV_BASE_FEAT_STAT_PAGES_MSR
161 | GIM_HV_BASE_FEAT_PART_REF_TSC_MSR
162 //| GIM_HV_BASE_FEAT_GUEST_IDLE_STATE_MSR
163 | GIM_HV_BASE_FEAT_TIMER_FREQ_MSRS
164 //| GIM_HV_BASE_FEAT_DEBUG_MSRS
165 ;
166
167 /* Miscellaneous features. */
168 pHv->uMiscFeat = 0
169 //| GIM_HV_MISC_FEAT_GUEST_DEBUGGING
170 //| GIM_HV_MISC_FEAT_XMM_HYPERCALL_INPUT
171 | GIM_HV_MISC_FEAT_TIMER_FREQ
172 | GIM_HV_MISC_FEAT_GUEST_CRASH_MSRS
173 //| GIM_HV_MISC_FEAT_DEBUG_MSRS
174 ;
175
176 /* Hypervisor recommendations to the guest. */
177 pHv->uHyperHints = GIM_HV_HINT_MSR_FOR_SYS_RESET
178 | GIM_HV_HINT_RELAX_TIME_CHECKS;
179
180 /* Expose more if we're posing as Microsoft. We can, if needed, force MSR-based Hv
181 debugging by not exposing these bits while exposing the VS interface.*/
182 if (pHv->fIsVendorMsHv)
183 {
184 pHv->uMiscFeat |= GIM_HV_MISC_FEAT_GUEST_DEBUGGING
185 | GIM_HV_MISC_FEAT_DEBUG_MSRS;
186
187 pHv->uPartFlags |= GIM_HV_PART_FLAGS_DEBUGGING;
188 }
189 }
190
191 /*
192 * Populate the required fields in MMIO2 region records for registering.
193 */
194 AssertCompile(GIM_HV_PAGE_SIZE == PAGE_SIZE);
195 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
196 pRegion->iRegion = GIM_HV_HYPERCALL_PAGE_REGION_IDX;
197 pRegion->fRCMapping = false;
198 pRegion->cbRegion = PAGE_SIZE; /* Sanity checked in gimR3HvLoad(), gimR3HvEnableTscPage() & gimR3HvEnableHypercallPage() */
199 pRegion->GCPhysPage = NIL_RTGCPHYS;
200 RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V hypercall page");
201
202 pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
203 pRegion->iRegion = GIM_HV_REF_TSC_PAGE_REGION_IDX;
204 pRegion->fRCMapping = false;
205 pRegion->cbRegion = PAGE_SIZE; /* Sanity checked in gimR3HvLoad(), gimR3HvEnableTscPage() & gimR3HvEnableHypercallPage() */
206 pRegion->GCPhysPage = NIL_RTGCPHYS;
207 RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V TSC page");
208
209 /*
210 * Make sure the CPU ID bit are in accordance to the Hyper-V
211 * requirement and other paranoia checks.
212 * See "Requirements for implementing the Microsoft hypervisor interface" spec.
213 */
214 Assert(!(pHv->uPartFlags & ( GIM_HV_PART_FLAGS_CREATE_PART
215 | GIM_HV_PART_FLAGS_ACCESS_MEMORY_POOL
216 | GIM_HV_PART_FLAGS_ACCESS_PART_ID
217 | GIM_HV_PART_FLAGS_ADJUST_MSG_BUFFERS
218 | GIM_HV_PART_FLAGS_CREATE_PORT
219 | GIM_HV_PART_FLAGS_ACCESS_STATS
220 | GIM_HV_PART_FLAGS_CPU_MGMT
221 | GIM_HV_PART_FLAGS_CPU_PROFILER)));
222 Assert((pHv->uBaseFeat & (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR))
223 == (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR));
224 for (unsigned i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++)
225 {
226 PCGIMMMIO2REGION pcCur = &pHv->aMmio2Regions[i];
227 Assert(!pcCur->fRCMapping);
228 Assert(!pcCur->fMapped);
229 Assert(pcCur->GCPhysPage == NIL_RTGCPHYS);
230 }
231
232 /*
233 * Expose HVP (Hypervisor Present) bit to the guest.
234 */
235 CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP);
236
237 /*
238 * Modify the standard hypervisor leaves for Hyper-V.
239 */
240 CPUMCPUIDLEAF HyperLeaf;
241 RT_ZERO(HyperLeaf);
242 HyperLeaf.uLeaf = UINT32_C(0x40000000);
243 HyperLeaf.uEax = UINT32_C(0x40000006); /* Minimum value for Hyper-V is 0x40000005. */
244 /*
245 * Don't report vendor as 'Microsoft Hv'[1] by default, see @bugref{7270#c152}.
246 * [1]: ebx=0x7263694d ('rciM') ecx=0x666f736f ('foso') edx=0x76482074 ('vH t')
247 */
248 {
249 uint32_t uVendorEbx;
250 uint32_t uVendorEcx;
251 uint32_t uVendorEdx;
252 uVendorEbx = ((uint32_t)szVendor[ 3]) << 24 | ((uint32_t)szVendor[ 2]) << 16 | ((uint32_t)szVendor[1]) << 8
253 | (uint32_t)szVendor[ 0];
254 uVendorEcx = ((uint32_t)szVendor[ 7]) << 24 | ((uint32_t)szVendor[ 6]) << 16 | ((uint32_t)szVendor[5]) << 8
255 | (uint32_t)szVendor[ 4];
256 uVendorEdx = ((uint32_t)szVendor[11]) << 24 | ((uint32_t)szVendor[10]) << 16 | ((uint32_t)szVendor[9]) << 8
257 | (uint32_t)szVendor[ 8];
258 HyperLeaf.uEbx = uVendorEbx;
259 HyperLeaf.uEcx = uVendorEcx;
260 HyperLeaf.uEdx = uVendorEdx;
261 }
262 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
263 AssertLogRelRCReturn(rc, rc);
264
265 HyperLeaf.uLeaf = UINT32_C(0x40000001);
266 HyperLeaf.uEax = 0x31237648; /* 'Hv#1' */
267 HyperLeaf.uEbx = 0; /* Reserved */
268 HyperLeaf.uEcx = 0; /* Reserved */
269 HyperLeaf.uEdx = 0; /* Reserved */
270 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
271 AssertLogRelRCReturn(rc, rc);
272
273 /*
274 * Add Hyper-V specific leaves.
275 */
276 HyperLeaf.uLeaf = UINT32_C(0x40000002); /* MBZ until MSR_GIM_HV_GUEST_OS_ID is set by the guest. */
277 HyperLeaf.uEax = 0;
278 HyperLeaf.uEbx = 0;
279 HyperLeaf.uEcx = 0;
280 HyperLeaf.uEdx = 0;
281 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
282 AssertLogRelRCReturn(rc, rc);
283
284 HyperLeaf.uLeaf = UINT32_C(0x40000003);
285 HyperLeaf.uEax = pHv->uBaseFeat;
286 HyperLeaf.uEbx = pHv->uPartFlags;
287 HyperLeaf.uEcx = pHv->uPowMgmtFeat;
288 HyperLeaf.uEdx = pHv->uMiscFeat;
289 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
290 AssertLogRelRCReturn(rc, rc);
291
292 HyperLeaf.uLeaf = UINT32_C(0x40000004);
293 HyperLeaf.uEax = pHv->uHyperHints;
294 HyperLeaf.uEbx = 0xffffffff;
295 HyperLeaf.uEcx = 0;
296 HyperLeaf.uEdx = 0;
297 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
298 AssertLogRelRCReturn(rc, rc);
299
300 if ( pHv->fIsVendorMsHv
301 && pHv->fIsInterfaceVs)
302 {
303 HyperLeaf.uLeaf = UINT32_C(0x40000080);
304 HyperLeaf.uEax = 0;
305 HyperLeaf.uEbx = 0x7263694d; /* 'rciM' */
306 HyperLeaf.uEcx = 0x666f736f; /* 'foso'*/
307 HyperLeaf.uEdx = 0x53562074; /* 'SV t' */
308 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
309 AssertLogRelRCReturn(rc, rc);
310
311 HyperLeaf.uLeaf = UINT32_C(0x40000081);
312 HyperLeaf.uEax = 0x31235356; /* '1#SV' */
313 HyperLeaf.uEbx = 0;
314 HyperLeaf.uEcx = 0;
315 HyperLeaf.uEdx = 0;
316 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
317 AssertLogRelRCReturn(rc, rc);
318
319 HyperLeaf.uLeaf = UINT32_C(0x40000082);
320 HyperLeaf.uEax = RT_BIT_32(1);
321 HyperLeaf.uEbx = 0;
322 HyperLeaf.uEcx = 0;
323 HyperLeaf.uEdx = 0;
324 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
325 AssertLogRelRCReturn(rc, rc);
326 }
327
328 /*
329 * Insert all MSR ranges of Hyper-V.
330 */
331 for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_HyperV); i++)
332 {
333 rc = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_HyperV[i]);
334 AssertLogRelRCReturn(rc, rc);
335 }
336
337 /*
338 * Setup non-zero MSRs.
339 */
340 if (pHv->uMiscFeat & GIM_HV_MISC_FEAT_GUEST_CRASH_MSRS)
341 pHv->uCrashCtl = MSR_GIM_HV_CRASH_CTL_NOTIFY_BIT;
342 for (VMCPUID i = 0; i < pVM->cCpus; i++)
343 pVM->aCpus[i].gim.s.u.HvCpu.uSint2Msr = MSR_GIM_HV_SINT_MASKED_BIT;
344
345 /*
346 * Setup hypercall support.
347 */
348 rc = gimR3HvInitHypercallSupport(pVM);
349 AssertLogRelRCReturn(rc, rc);
350
351 return VINF_SUCCESS;
352}
353
354
355/**
356 * Initializes remaining bits of the Hyper-V provider.
357 *
358 * This is called after initializing HM and almost all other VMM components.
359 *
360 * @returns VBox status code.
361 * @param pVM The cross context VM structure.
362 */
363VMMR3_INT_DECL(int) gimR3HvInitCompleted(PVM pVM)
364{
365 PGIMHV pHv = &pVM->gim.s.u.Hv;
366 pHv->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM);
367
368 /*
369 * Determine interface capabilities based on the version.
370 */
371 if (!pVM->gim.s.u32Version)
372 {
373 /* Hypervisor capabilities; features used by the hypervisor. */
374 pHv->uHyperCaps = HMIsNestedPagingActive(pVM) ? GIM_HV_HOST_FEAT_NESTED_PAGING : 0;
375 pHv->uHyperCaps |= HMAreMsrBitmapsAvailable(pVM) ? GIM_HV_HOST_FEAT_MSR_BITMAP : 0;
376 }
377
378 CPUMCPUIDLEAF HyperLeaf;
379 RT_ZERO(HyperLeaf);
380 HyperLeaf.uLeaf = UINT32_C(0x40000006);
381 HyperLeaf.uEax = pHv->uHyperCaps;
382 HyperLeaf.uEbx = 0;
383 HyperLeaf.uEcx = 0;
384 HyperLeaf.uEdx = 0;
385 int rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
386 AssertLogRelRCReturn(rc, rc);
387
388 return rc;
389}
390
391
392#if 0
393VMMR3_INT_DECL(int) gimR3HvInitFinalize(PVM pVM)
394{
395 pVM->gim.s.pfnHypercallR3 = &GIMHvHypercall;
396 if (!HMIsEnabled(pVM))
397 {
398 rc = PDMR3LdrGetSymbolRC(pVM, NULL /* pszModule */, GIMHV_HYPERCALL, &pVM->gim.s.pfnHypercallRC);
399 AssertRCReturn(rc, rc);
400 }
401 rc = PDMR3LdrGetSymbolR0(pVM, NULL /* pszModule */, GIMHV_HYPERCALL, &pVM->gim.s.pfnHypercallR0);
402 AssertRCReturn(rc, rc);
403}
404#endif
405
406
407/**
408 * Terminates the Hyper-V GIM provider.
409 *
410 * @returns VBox status code.
411 * @param pVM The cross context VM structure.
412 */
413VMMR3_INT_DECL(int) gimR3HvTerm(PVM pVM)
414{
415 gimR3HvReset(pVM);
416 gimR3HvTermHypercallSupport(pVM);
417 return VINF_SUCCESS;
418}
419
420
421/**
422 * Applies relocations to data and code managed by this component.
423 *
424 * This function will be called at init and whenever the VMM need to relocate
425 * itself inside the GC.
426 *
427 * @param pVM The cross context VM structure.
428 * @param offDelta Relocation delta relative to old location.
429 */
430VMMR3_INT_DECL(void) gimR3HvRelocate(PVM pVM, RTGCINTPTR offDelta)
431{
432#if 0
433 int rc = PDMR3LdrGetSymbolRC(pVM, NULL /* pszModule */, GIMHV_HYPERCALL, &pVM->gim.s.pfnHypercallRC);
434 AssertFatalRC(rc);
435#endif
436}
437
438
439/**
440 * This resets Hyper-V provider MSRs and unmaps whatever Hyper-V regions that
441 * the guest may have mapped.
442 *
443 * This is called when the VM is being reset.
444 *
445 * @param pVM The cross context VM structure.
446 *
447 * @thread EMT(0).
448 */
449VMMR3_INT_DECL(void) gimR3HvReset(PVM pVM)
450{
451 VM_ASSERT_EMT0(pVM);
452
453 /*
454 * Unmap MMIO2 pages that the guest may have setup.
455 */
456 LogRel(("GIM: HyperV: Resetting MMIO2 regions and MSRs\n"));
457 PGIMHV pHv = &pVM->gim.s.u.Hv;
458 for (unsigned i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++)
459 {
460 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[i];
461#if 0
462 GIMR3Mmio2Unmap(pVM, pRegion);
463#else
464 pRegion->fMapped = false;
465 pRegion->GCPhysPage = NIL_RTGCPHYS;
466#endif
467 }
468
469 /*
470 * Reset MSRs.
471 */
472 pHv->u64GuestOsIdMsr = 0;
473 pHv->u64HypercallMsr = 0;
474 pHv->u64TscPageMsr = 0;
475 pHv->uCrashP0 = 0;
476 pHv->uCrashP1 = 0;
477 pHv->uCrashP2 = 0;
478 pHv->uCrashP3 = 0;
479 pHv->uCrashP4 = 0;
480 pHv->uDebugStatusMsr = 0;
481 pHv->uDebugPendingBufferMsr = 0;
482 pHv->uDebugSendBufferMsr = 0;
483 pHv->uDebugRecvBufferMsr = 0;
484 for (VMCPUID i = 0; i < pVM->cCpus; i++)
485 {
486 PVMCPU pVCpu = &pVM->aCpus[i];
487 pVCpu->gim.s.u.HvCpu.uSint2Msr = MSR_GIM_HV_SINT_MASKED_BIT;
488 pVCpu->gim.s.u.HvCpu.uSimpMsr = 0;
489 }
490}
491
492
493/**
494 * Returns a pointer to the MMIO2 regions supported by Hyper-V.
495 *
496 * @returns Pointer to an array of MMIO2 regions.
497 * @param pVM The cross context VM structure.
498 * @param pcRegions Where to store the number of regions in the array.
499 */
500VMMR3_INT_DECL(PGIMMMIO2REGION) gimR3HvGetMmio2Regions(PVM pVM, uint32_t *pcRegions)
501{
502 Assert(GIMIsEnabled(pVM));
503 PGIMHV pHv = &pVM->gim.s.u.Hv;
504
505 *pcRegions = RT_ELEMENTS(pHv->aMmio2Regions);
506 Assert(*pcRegions <= UINT8_MAX); /* See PGMR3PhysMMIO2Register(). */
507 return pHv->aMmio2Regions;
508}
509
510
511/**
512 * Hyper-V state-save operation.
513 *
514 * @returns VBox status code.
515 * @param pVM The cross context VM structure.
516 * @param pSSM Pointer to the SSM handle.
517 */
518VMMR3_INT_DECL(int) gimR3HvSave(PVM pVM, PSSMHANDLE pSSM)
519{
520 PCGIMHV pcHv = &pVM->gim.s.u.Hv;
521
522 /*
523 * Save the Hyper-V SSM version.
524 */
525 SSMR3PutU32(pSSM, GIM_HV_SAVED_STATE_VERSION);
526
527 /*
528 * Save per-VM MSRs.
529 */
530 SSMR3PutU64(pSSM, pcHv->u64GuestOsIdMsr);
531 SSMR3PutU64(pSSM, pcHv->u64HypercallMsr);
532 SSMR3PutU64(pSSM, pcHv->u64TscPageMsr);
533
534 /*
535 * Save Hyper-V features / capabilities.
536 */
537 SSMR3PutU32(pSSM, pcHv->uBaseFeat);
538 SSMR3PutU32(pSSM, pcHv->uPartFlags);
539 SSMR3PutU32(pSSM, pcHv->uPowMgmtFeat);
540 SSMR3PutU32(pSSM, pcHv->uMiscFeat);
541 SSMR3PutU32(pSSM, pcHv->uHyperHints);
542 SSMR3PutU32(pSSM, pcHv->uHyperCaps);
543
544 /*
545 * Save the Hypercall region.
546 */
547 PCGIMMMIO2REGION pcRegion = &pcHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
548 SSMR3PutU8(pSSM, pcRegion->iRegion);
549 SSMR3PutBool(pSSM, pcRegion->fRCMapping);
550 SSMR3PutU32(pSSM, pcRegion->cbRegion);
551 SSMR3PutGCPhys(pSSM, pcRegion->GCPhysPage);
552 SSMR3PutStrZ(pSSM, pcRegion->szDescription);
553
554 /*
555 * Save the reference TSC region.
556 */
557 pcRegion = &pcHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
558 SSMR3PutU8(pSSM, pcRegion->iRegion);
559 SSMR3PutBool(pSSM, pcRegion->fRCMapping);
560 SSMR3PutU32(pSSM, pcRegion->cbRegion);
561 SSMR3PutGCPhys(pSSM, pcRegion->GCPhysPage);
562 SSMR3PutStrZ(pSSM, pcRegion->szDescription);
563 /* Save the TSC sequence so we can bump it on restore (as the CPU frequency/offset may change). */
564 uint32_t uTscSequence = 0;
565 if ( pcRegion->fMapped
566 && MSR_GIM_HV_REF_TSC_IS_ENABLED(pcHv->u64TscPageMsr))
567 {
568 PCGIMHVREFTSC pcRefTsc = (PCGIMHVREFTSC)pcRegion->pvPageR3;
569 uTscSequence = pcRefTsc->u32TscSequence;
570 }
571
572 return SSMR3PutU32(pSSM, uTscSequence);
573}
574
575
576/**
577 * Hyper-V state-load operation, final pass.
578 *
579 * @returns VBox status code.
580 * @param pVM The cross context VM structure.
581 * @param pSSM Pointer to the SSM handle.
582 * @param uSSMVersion The GIM saved-state version.
583 */
584VMMR3_INT_DECL(int) gimR3HvLoad(PVM pVM, PSSMHANDLE pSSM, uint32_t uSSMVersion)
585{
586 /*
587 * Load the Hyper-V SSM version first.
588 */
589 uint32_t uHvSavedStatVersion;
590 int rc = SSMR3GetU32(pSSM, &uHvSavedStatVersion);
591 AssertRCReturn(rc, rc);
592 if (uHvSavedStatVersion != GIM_HV_SAVED_STATE_VERSION)
593 return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS,
594 N_("Unsupported Hyper-V saved-state version %u (expected %u)."), uHvSavedStatVersion,
595 GIM_HV_SAVED_STATE_VERSION);
596
597 /*
598 * Update the TSC frequency from TM.
599 */
600 PGIMHV pHv = &pVM->gim.s.u.Hv;
601 pHv->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM);
602
603 /*
604 * Load per-VM MSRs.
605 */
606 SSMR3GetU64(pSSM, &pHv->u64GuestOsIdMsr);
607 SSMR3GetU64(pSSM, &pHv->u64HypercallMsr);
608 SSMR3GetU64(pSSM, &pHv->u64TscPageMsr);
609
610 /*
611 * Load Hyper-V features / capabilities.
612 */
613 SSMR3GetU32(pSSM, &pHv->uBaseFeat);
614 SSMR3GetU32(pSSM, &pHv->uPartFlags);
615 SSMR3GetU32(pSSM, &pHv->uPowMgmtFeat);
616 SSMR3GetU32(pSSM, &pHv->uMiscFeat);
617 SSMR3GetU32(pSSM, &pHv->uHyperHints);
618 SSMR3GetU32(pSSM, &pHv->uHyperCaps);
619
620 /*
621 * Load and enable the Hypercall region.
622 */
623 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
624 SSMR3GetU8(pSSM, &pRegion->iRegion);
625 SSMR3GetBool(pSSM, &pRegion->fRCMapping);
626 SSMR3GetU32(pSSM, &pRegion->cbRegion);
627 SSMR3GetGCPhys(pSSM, &pRegion->GCPhysPage);
628 rc = SSMR3GetStrZ(pSSM, pRegion->szDescription, sizeof(pRegion->szDescription));
629 AssertRCReturn(rc, rc);
630
631 if (pRegion->cbRegion != PAGE_SIZE)
632 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Hypercall page region size %u invalid, expected %u"),
633 pRegion->cbRegion, PAGE_SIZE);
634
635 if (MSR_GIM_HV_HYPERCALL_PAGE_IS_ENABLED(pHv->u64HypercallMsr))
636 {
637 Assert(pRegion->GCPhysPage != NIL_RTGCPHYS);
638 if (RT_LIKELY(pRegion->fRegistered))
639 {
640 rc = gimR3HvEnableHypercallPage(pVM, pRegion->GCPhysPage);
641 if (RT_FAILURE(rc))
642 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Failed to enable the hypercall page. GCPhys=%#RGp rc=%Rrc"),
643 pRegion->GCPhysPage, rc);
644 }
645 else
646 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Hypercall MMIO2 region not registered. Missing GIM device?!"));
647 }
648
649 /*
650 * Load and enable the reference TSC region.
651 */
652 uint32_t uTscSequence;
653 pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
654 SSMR3GetU8(pSSM, &pRegion->iRegion);
655 SSMR3GetBool(pSSM, &pRegion->fRCMapping);
656 SSMR3GetU32(pSSM, &pRegion->cbRegion);
657 SSMR3GetGCPhys(pSSM, &pRegion->GCPhysPage);
658 SSMR3GetStrZ(pSSM, pRegion->szDescription, sizeof(pRegion->szDescription));
659 rc = SSMR3GetU32(pSSM, &uTscSequence);
660 AssertRCReturn(rc, rc);
661
662 if (pRegion->cbRegion != PAGE_SIZE)
663 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("TSC page region size %u invalid, expected %u"),
664 pRegion->cbRegion, PAGE_SIZE);
665
666 if (MSR_GIM_HV_REF_TSC_IS_ENABLED(pHv->u64TscPageMsr))
667 {
668 Assert(pRegion->GCPhysPage != NIL_RTGCPHYS);
669 if (pRegion->fRegistered)
670 {
671 rc = gimR3HvEnableTscPage(pVM, pRegion->GCPhysPage, true /* fUseThisTscSeq */, uTscSequence);
672 if (RT_FAILURE(rc))
673 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Failed to enable the TSC page. GCPhys=%#RGp rc=%Rrc"),
674 pRegion->GCPhysPage, rc);
675 }
676 else
677 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("TSC-page MMIO2 region not registered. Missing GIM device?!"));
678 }
679
680 return rc;
681}
682
683
684/**
685 * Enables the Hyper-V TSC page.
686 *
687 * @returns VBox status code.
688 * @param pVM The cross context VM structure.
689 * @param GCPhysTscPage Where to map the TSC page.
690 * @param fUseThisTscSeq Whether to set the TSC sequence number to the one
691 * specified in @a uTscSeq.
692 * @param uTscSeq The TSC sequence value to use. Ignored if
693 * @a fUseThisTscSeq is false.
694 */
695VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq)
696{
697 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
698 PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
699 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
700
701 int rc;
702 if (pRegion->fMapped)
703 {
704 /*
705 * Is it already enabled at the given guest-address?
706 */
707 if (pRegion->GCPhysPage == GCPhysTscPage)
708 return VINF_SUCCESS;
709
710 /*
711 * If it's mapped at a different address, unmap the previous address.
712 */
713 rc = gimR3HvDisableTscPage(pVM);
714 AssertRC(rc);
715 }
716
717 /*
718 * Map the TSC-page at the specified address.
719 */
720 Assert(!pRegion->fMapped);
721
722 /** @todo this is buggy when large pages are used due to a PGM limitation, see
723 * @bugref{7532}. Instead of the overlay style mapping, we just
724 * rewrite guest memory directly. */
725#if 0
726 rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysTscPage);
727 if (RT_SUCCESS(rc))
728 {
729 Assert(pRegion->GCPhysPage == GCPhysTscPage);
730
731 /*
732 * Update the TSC scale. Windows guests expect a non-zero TSC sequence, otherwise
733 * they fallback to using the reference count MSR which is not ideal in terms of VM-exits.
734 *
735 * Also, Hyper-V normalizes the time in 10 MHz, see:
736 * http://technet.microsoft.com/it-it/sysinternals/dn553408%28v=vs.110%29
737 */
738 PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pRegion->pvPageR3;
739 Assert(pRefTsc);
740
741 PGIMHV pHv = &pVM->gim.s.u.Hv;
742 uint64_t const u64TscKHz = pHv->cTscTicksPerSecond / UINT64_C(1000);
743 uint32_t u32TscSeq = 1;
744 if ( fUseThisTscSeq
745 && uTscSeq < UINT32_C(0xfffffffe))
746 u32TscSeq = uTscSeq + 1;
747 pRefTsc->u32TscSequence = u32TscSeq;
748 pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32;
749 pRefTsc->i64TscOffset = 0;
750
751 LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n",
752 GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence));
753
754 TMR3CpuTickParavirtEnable(pVM);
755 return VINF_SUCCESS;
756 }
757 else
758 LogRelFunc(("GIMR3Mmio2Map failed. rc=%Rrc\n", rc));
759 return VERR_GIM_OPERATION_FAILED;
760#else
761 AssertReturn(pRegion->cbRegion == PAGE_SIZE, VERR_GIM_IPE_2);
762 PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)RTMemAllocZ(PAGE_SIZE);
763 if (RT_UNLIKELY(!pRefTsc))
764 {
765 LogRelFunc(("Failed to alloc %u bytes\n", PAGE_SIZE));
766 return VERR_NO_MEMORY;
767 }
768
769 PGIMHV pHv = &pVM->gim.s.u.Hv;
770 uint64_t const u64TscKHz = pHv->cTscTicksPerSecond / UINT64_C(1000);
771 uint32_t u32TscSeq = 1;
772 if ( fUseThisTscSeq
773 && uTscSeq < UINT32_C(0xfffffffe))
774 u32TscSeq = uTscSeq + 1;
775 pRefTsc->u32TscSequence = u32TscSeq;
776 pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32;
777 pRefTsc->i64TscOffset = 0;
778
779 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysTscPage, pRefTsc, sizeof(*pRefTsc));
780 if (RT_SUCCESS(rc))
781 {
782 LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n",
783 GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence));
784
785 pRegion->GCPhysPage = GCPhysTscPage;
786 pRegion->fMapped = true;
787 TMR3CpuTickParavirtEnable(pVM);
788 }
789 else
790 {
791 LogRelFunc(("GIM: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", rc));
792 rc = VERR_GIM_OPERATION_FAILED;
793 }
794 RTMemFree(pRefTsc);
795 return rc;
796#endif
797}
798
799
800/**
801 * Disables the Hyper-V TSC page.
802 *
803 * @returns VBox status code.
804 * @param pVM The cross context VM structure.
805 */
806VMMR3_INT_DECL(int) gimR3HvDisableTscPage(PVM pVM)
807{
808 PGIMHV pHv = &pVM->gim.s.u.Hv;
809 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
810 if (pRegion->fMapped)
811 {
812#if 0
813 GIMR3Mmio2Unmap(pVM, pRegion);
814 Assert(!pRegion->fMapped);
815#else
816 pRegion->fMapped = false;
817#endif
818 LogRel(("GIM: HyperV: Disabled TSC-page\n"));
819
820 TMR3CpuTickParavirtDisable(pVM);
821 return VINF_SUCCESS;
822 }
823 return VERR_GIM_PVTSC_NOT_ENABLED;
824}
825
826
827/**
828 * Disables the Hyper-V Hypercall page.
829 *
830 * @returns VBox status code.
831 */
832VMMR3_INT_DECL(int) gimR3HvDisableHypercallPage(PVM pVM)
833{
834 PGIMHV pHv = &pVM->gim.s.u.Hv;
835 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
836 if (pRegion->fMapped)
837 {
838#if 0
839 GIMR3Mmio2Unmap(pVM, pRegion);
840 Assert(!pRegion->fMapped);
841#else
842 pRegion->fMapped = false;
843#endif
844 LogRel(("GIM: HyperV: Disabled Hypercall-page\n"));
845 return VINF_SUCCESS;
846 }
847 return VERR_GIM_HYPERCALLS_NOT_ENABLED;
848}
849
850
851/**
852 * Enables the Hyper-V Hypercall page.
853 *
854 * @returns VBox status code.
855 * @param pVM The cross context VM structure.
856 * @param GCPhysHypercallPage Where to map the hypercall page.
857 */
858VMMR3_INT_DECL(int) gimR3HvEnableHypercallPage(PVM pVM, RTGCPHYS GCPhysHypercallPage)
859{
860 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
861 PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
862 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
863
864 if (pRegion->fMapped)
865 {
866 /*
867 * Is it already enabled at the given guest-address?
868 */
869 if (pRegion->GCPhysPage == GCPhysHypercallPage)
870 return VINF_SUCCESS;
871
872 /*
873 * If it's mapped at a different address, unmap the previous address.
874 */
875 int rc2 = gimR3HvDisableHypercallPage(pVM);
876 AssertRC(rc2);
877 }
878
879 /*
880 * Map the hypercall-page at the specified address.
881 */
882 Assert(!pRegion->fMapped);
883
884 /** @todo this is buggy when large pages are used due to a PGM limitation, see
885 * @bugref{7532}. Instead of the overlay style mapping, we just
886 * rewrite guest memory directly. */
887#if 0
888 int rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysHypercallPage);
889 if (RT_SUCCESS(rc))
890 {
891 Assert(pRegion->GCPhysPage == GCPhysHypercallPage);
892
893 /*
894 * Patch the hypercall-page.
895 */
896 size_t cbWritten = 0;
897 rc = VMMPatchHypercall(pVM, pRegion->pvPageR3, PAGE_SIZE, &cbWritten);
898 if ( RT_SUCCESS(rc)
899 && cbWritten < PAGE_SIZE)
900 {
901 uint8_t *pbLast = (uint8_t *)pRegion->pvPageR3 + cbWritten;
902 *pbLast = 0xc3; /* RET */
903
904 /*
905 * Notify VMM that hypercalls are now enabled for all VCPUs.
906 */
907 for (VMCPUID i = 0; i < pVM->cCpus; i++)
908 VMMHypercallsEnable(&pVM->aCpus[i]);
909
910 LogRel(("GIM: HyperV: Enabled hypercall page at %#RGp\n", GCPhysHypercallPage));
911 return VINF_SUCCESS;
912 }
913 else
914 {
915 if (rc == VINF_SUCCESS)
916 rc = VERR_GIM_OPERATION_FAILED;
917 LogRel(("GIM: HyperV: VMMPatchHypercall failed. rc=%Rrc cbWritten=%u\n", rc, cbWritten));
918 }
919
920 GIMR3Mmio2Unmap(pVM, pRegion);
921 }
922
923 LogRel(("GIM: HyperV: GIMR3Mmio2Map failed. rc=%Rrc\n", rc));
924 return rc;
925#else
926 AssertReturn(pRegion->cbRegion == PAGE_SIZE, VERR_GIM_IPE_3);
927 void *pvHypercallPage = RTMemAllocZ(PAGE_SIZE);
928 if (RT_UNLIKELY(!pvHypercallPage))
929 {
930 LogRelFunc(("Failed to alloc %u bytes\n", PAGE_SIZE));
931 return VERR_NO_MEMORY;
932 }
933
934 /*
935 * Patch the hypercall-page.
936 */
937 size_t cbWritten = 0;
938 int rc = VMMPatchHypercall(pVM, pvHypercallPage, PAGE_SIZE, &cbWritten);
939 if ( RT_SUCCESS(rc)
940 && cbWritten < PAGE_SIZE)
941 {
942 uint8_t *pbLast = (uint8_t *)pvHypercallPage + cbWritten;
943 *pbLast = 0xc3; /* RET */
944
945 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysHypercallPage, pvHypercallPage, PAGE_SIZE);
946 if (RT_SUCCESS(rc))
947 {
948 pRegion->GCPhysPage = GCPhysHypercallPage;
949 pRegion->fMapped = true;
950 LogRel(("GIM: HyperV: Enabled hypercall page at %#RGp\n", GCPhysHypercallPage));
951 }
952 else
953 LogRel(("GIM: HyperV: PGMPhysSimpleWriteGCPhys failed during hypercall page setup. rc=%Rrc\n", rc));
954 }
955 else
956 {
957 if (rc == VINF_SUCCESS)
958 rc = VERR_GIM_OPERATION_FAILED;
959 LogRel(("GIM: HyperV: VMMPatchHypercall failed. rc=%Rrc cbWritten=%u\n", rc, cbWritten));
960 }
961
962 RTMemFree(pvHypercallPage);
963 return rc;
964#endif
965}
966
967
968/**
969 * Initializes Hyper-V guest hypercall support.
970 *
971 * @returns VBox status code.
972 * @param pVM The cross context VM structure.
973 */
974static int gimR3HvInitHypercallSupport(PVM pVM)
975{
976 int rc = VINF_SUCCESS;
977 PGIMHV pHv = &pVM->gim.s.u.Hv;
978 pHv->pbHypercallIn = (uint8_t *)RTMemAllocZ(GIM_HV_PAGE_SIZE);
979 if (RT_LIKELY(pHv->pbHypercallIn))
980 {
981 pHv->pbHypercallOut = (uint8_t *)RTMemAllocZ(GIM_HV_PAGE_SIZE);
982 if (RT_LIKELY(pHv->pbHypercallOut))
983 return VINF_SUCCESS;
984 RTMemFree(pHv->pbHypercallIn);
985 }
986 return VERR_NO_MEMORY;
987}
988
989
990/**
991 * Terminates Hyper-V guest hypercall support.
992 *
993 * @param pVM The cross context VM structure.
994 */
995static void gimR3HvTermHypercallSupport(PVM pVM)
996{
997 PGIMHV pHv = &pVM->gim.s.u.Hv;
998 RTMemFree(pHv->pbHypercallIn);
999 pHv->pbHypercallIn = NULL;
1000
1001 RTMemFree(pHv->pbHypercallOut);
1002 pHv->pbHypercallOut = NULL;
1003}
1004
1005
1006/**
1007 * Reads data from a debugger connection, asynchronous.
1008 *
1009 * @returns VBox status code.
1010 * @param pVM The cross context VM structure.
1011 * @param pvBuf Where to read the data.
1012 * @param cbBuf Size of the read buffer @a pvBuf, must be >= @a cbRead.
1013 * @param cbRead Number of bytes to read.
1014 * @param pcbRead Where to store how many bytes were really read.
1015 * @param cMsTimeout Timeout of the read operation in milliseconds.
1016 * @param fUdpPkt Whether the debug data returned in @a pvBuf needs to be
1017 * encapsulated in a UDP frame.
1018 *
1019 * @thread EMT.
1020 */
1021VMMR3_INT_DECL(int) gimR3HvDebugRead(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t cbRead, uint32_t *pcbRead,
1022 uint32_t cMsTimeout, bool fUdpPkt)
1023{
1024 NOREF(cMsTimeout); /** @todo implement timeout. */
1025 AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
1026 AssertReturn(cbBuf >= cbRead, VERR_INVALID_PARAMETER);
1027
1028 /*
1029 * Read the data.
1030 */
1031 size_t cbReallyRead = cbRead;
1032 int rc = GIMR3DebugRead(pVM, pvBuf, &cbReallyRead);
1033
1034 /*
1035 * Encapsulate it in a UDP packet if required.
1036 */
1037 if ( RT_SUCCESS(rc)
1038 && fUdpPkt
1039 && cbReallyRead > 0)
1040 {
1041 uint8_t abFrame[sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + sizeof(RTNETUDP)];
1042 if (cbReallyRead + sizeof(abFrame) <= cbBuf)
1043 {
1044 /*
1045 * Windows guests pumps ethernet frames over the Hyper-V debug connection as
1046 * explained in gimR3HvHypercallPostDebugData(). Here, we reconstruct the packet
1047 * with the guest's self-chosen IP ARP address we saved in pHv->DbgGuestAddr.
1048 *
1049 * Note! We really need to pass the minimum IPv4 header length. The Windows 10 guest
1050 * is -not- happy if we include the IPv4 options field, i.e. using sizeof(RTNETIPV4)
1051 * instead of RTNETIPV4_MIN_LEN.
1052 */
1053 PGIMHV pHv = &pVM->gim.s.u.Hv;
1054 RT_ZERO(abFrame);
1055 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
1056 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
1057 PRTNETUDP pUdpHdr = (PRTNETUDP) ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
1058
1059 /* Ethernet */
1060 pEthHdr->EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
1061 /* IPv4 */
1062 pIpHdr->ip_v = 4;
1063 pIpHdr->ip_hl = RTNETIPV4_MIN_LEN / sizeof(uint32_t);
1064 pIpHdr->ip_tos = 0;
1065 pIpHdr->ip_len = RT_H2N_U16((uint16_t)cbReallyRead + sizeof(RTNETUDP) + RTNETIPV4_MIN_LEN);
1066 pIpHdr->ip_id = 0;
1067 pIpHdr->ip_off = 0;
1068 pIpHdr->ip_ttl = 255;
1069 pIpHdr->ip_p = RTNETIPV4_PROT_UDP;
1070 pIpHdr->ip_sum = 0;
1071 pIpHdr->ip_src.u = 0;
1072 pIpHdr->ip_dst.u = pHv->DbgGuestAddr.u;
1073 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
1074 /* UDP */
1075 pUdpHdr->uh_ulen = RT_H2N_U16_C((uint16_t)cbReallyRead + sizeof(*pUdpHdr));
1076
1077 /* Make room by moving the payload and prepending the headers. */
1078 uint8_t *pbData = (uint8_t *)pvBuf;
1079 memmove(pbData + sizeof(abFrame), pbData, cbReallyRead);
1080 memcpy(pbData, &abFrame[0], sizeof(abFrame));
1081
1082 /* Update the adjusted sizes. */
1083 cbReallyRead += sizeof(abFrame);
1084 }
1085 else
1086 rc = VERR_BUFFER_UNDERFLOW;
1087 }
1088
1089 *pcbRead = (uint32_t)cbReallyRead;
1090 return rc;
1091}
1092
1093
1094/**
1095 * Writes data to the debugger connection, asynchronous.
1096 *
1097 * @returns VBox status code.
1098 * @param pVM The cross context VM structure.
1099 * @param pvData Pointer to the data to be written.
1100 * @param cbWrite Size of the write buffer @a pvData.
1101 * @param pcbWritten Where to store the number of bytes written.
1102 * @param fUdpPkt Whether the debug data in @a pvData is encapsulated in a
1103 * UDP frame.
1104 *
1105 * @thread EMT.
1106 */
1107VMMR3_INT_DECL(int) gimR3HvDebugWrite(PVM pVM, void *pvData, uint32_t cbWrite, uint32_t *pcbWritten, bool fUdpPkt)
1108{
1109 Assert(cbWrite > 0);
1110
1111 PGIMHV pHv = &pVM->gim.s.u.Hv;
1112 bool fIgnorePkt = false;
1113 uint8_t *pbData = (uint8_t *)pvData;
1114 if (fUdpPkt)
1115 {
1116 /*
1117 * Windows guests sends us ethernet frames over the Hyper-V debug connection.
1118 * It sends DHCP/ARP queries with zero'd out MAC addresses and requires fudging up the
1119 * packets somewhere.
1120 *
1121 * The Microsoft WinDbg debugger talks UDP and thus only expects the actual debug
1122 * protocol payload.
1123 *
1124 * At present, we only handle guests configured with the "nodhcp" option. This makes
1125 * the guest send ARP queries with a self-chosen IP and after a couple of attempts of
1126 * receiving no replies, the guest picks its own IP address. After this, the guest
1127 * starts sending the UDP packets we require. We thus ignore the initial ARP packets
1128 * (and to be safe all non-UDP packets) until the guest eventually starts talking
1129 * UDP. Then we can finally feed the UDP payload over the debug connection.
1130 */
1131 if (cbWrite > sizeof(RTNETETHERHDR))
1132 {
1133 PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pbData;
1134 if (pEtherHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
1135 {
1136 if (cbWrite > sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN)
1137 {
1138 size_t const cbMaxIpHdr = cbWrite - sizeof(RTNETETHERHDR) - sizeof(RTNETUDP) - 1;
1139 size_t const cbMaxIpPkt = cbWrite - sizeof(RTNETETHERHDR);
1140 PCRTNETIPV4 pIp4Hdr = (PCRTNETIPV4)(pbData + sizeof(RTNETETHERHDR));
1141 bool const fValidIp4 = RTNetIPv4IsHdrValid(pIp4Hdr, cbMaxIpHdr, cbMaxIpPkt, false /*fChecksum*/);
1142 if ( fValidIp4
1143 && pIp4Hdr->ip_p == RTNETIPV4_PROT_UDP)
1144 {
1145 uint32_t const cbIpHdr = pIp4Hdr->ip_hl * 4;
1146 uint32_t const cbMaxUdpPkt = cbWrite - sizeof(RTNETETHERHDR) - cbIpHdr;
1147 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t *)pIp4Hdr + cbIpHdr);
1148 if ( pUdpHdr->uh_ulen > RT_H2N_U16(sizeof(RTNETUDP))
1149 && pUdpHdr->uh_ulen <= RT_H2N_U16((uint16_t)cbMaxUdpPkt))
1150 {
1151 /*
1152 * Extract the UDP payload and pass it to the debugger and record the guest IP address.
1153 * Hyper-V sends UDP debugger packets with source and destination port as 0. If we don't
1154 * filter out the ports here, we would receive BOOTP, NETBIOS and other UDP sub-protocol
1155 * packets which the debugger yells as "Bad packet received from...".
1156 */
1157 if ( !pUdpHdr->uh_dport
1158 && !pUdpHdr->uh_sport)
1159 {
1160 uint32_t const cbFrameHdr = sizeof(RTNETETHERHDR) + cbIpHdr + sizeof(RTNETUDP);
1161 pbData += cbFrameHdr;
1162 cbWrite -= cbFrameHdr;
1163 pHv->DbgGuestAddr = pIp4Hdr->ip_src;
1164 }
1165 else
1166 {
1167 LogFlow(("GIM: HyperV: Ignoring UDP packet not src and dst port 0\n"));
1168 fIgnorePkt = true;
1169 }
1170 }
1171 else
1172 {
1173 LogFlow(("GIM: HyperV: Ignoring malformed UDP packet. cbMaxUdpPkt=%u UdpPkt.len=%u\n", cbMaxUdpPkt,
1174 RT_N2H_U16(pUdpHdr->uh_ulen)));
1175 fIgnorePkt = true;
1176 }
1177 }
1178 else
1179 {
1180 LogFlow(("GIM: HyperV: Ignoring non-IP / non-UDP packet. fValidIp4=%RTbool Proto=%u\n", fValidIp4,
1181 pIp4Hdr->ip_p));
1182 fIgnorePkt = true;
1183 }
1184 }
1185 else
1186 {
1187 LogFlow(("GIM: HyperV: Ignoring IPv4 packet; too short to be valid UDP. cbWrite=%u\n", cbWrite));
1188 fIgnorePkt = true;
1189 }
1190 }
1191 else
1192 {
1193 LogFlow(("GIM: HyperV: Ignoring non-IP packet. Ethertype=%#x\n", RT_N2H_U16(pEtherHdr->EtherType)));
1194 fIgnorePkt = true;
1195 }
1196 }
1197 }
1198
1199 if (!fIgnorePkt)
1200 {
1201 AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
1202 size_t cbWriteBuf = cbWrite;
1203 int rc = GIMR3DebugWrite(pVM, pbData, &cbWriteBuf);
1204 if ( RT_SUCCESS(rc)
1205 && cbWriteBuf == cbWrite)
1206 *pcbWritten = (uint32_t)cbWriteBuf;
1207 else
1208 *pcbWritten = 0;
1209 }
1210 else
1211 *pcbWritten = cbWrite;
1212
1213 return VINF_SUCCESS;
1214}
1215
1216
1217/**
1218 * Performs the HvPostDebugData hypercall.
1219 *
1220 * @returns VBox status code.
1221 * @param pVM The cross context VM structure.
1222 * @param prcHv Where to store the result of the hypercall operation.
1223 *
1224 * @thread EMT.
1225 */
1226VMMR3_INT_DECL(int) gimR3HvHypercallPostDebugData(PVM pVM, int *prcHv)
1227{
1228 AssertPtr(pVM);
1229 AssertPtr(prcHv);
1230 PGIMHV pHv = &pVM->gim.s.u.Hv;
1231 int rcHv = GIM_HV_STATUS_OPERATION_DENIED;
1232
1233 /*
1234 * Grab the parameters.
1235 */
1236 PGIMHVDEBUGPOSTIN pIn = (PGIMHVDEBUGPOSTIN)pHv->pbHypercallIn;
1237 AssertPtrReturn(pIn, VERR_GIM_IPE_1);
1238 uint32_t cbWrite = pIn->cbWrite;
1239 uint32_t fFlags = pIn->fFlags;
1240 uint8_t *pbData = ((uint8_t *)pIn) + sizeof(PGIMHVDEBUGPOSTIN);
1241
1242 PGIMHVDEBUGPOSTOUT pOut = (PGIMHVDEBUGPOSTOUT)pHv->pbHypercallOut;
1243
1244 /*
1245 * Perform the hypercall.
1246 */
1247#if 0
1248 /* Currently disabled as Windows 10 guest passes us undocumented flags. */
1249 if (fFlags & ~GIM_HV_DEBUG_POST_OPTIONS_MASK))
1250 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
1251#endif
1252 if (cbWrite > GIM_HV_DEBUG_MAX_DATA_SIZE)
1253 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
1254 else if (!cbWrite)
1255 {
1256 rcHv = GIM_HV_STATUS_SUCCESS;
1257 pOut->cbPending = 0;
1258 }
1259 else if (cbWrite > 0)
1260 {
1261 uint32_t cbWritten = 0;
1262 int rc2 = gimR3HvDebugWrite(pVM, pbData, cbWrite, &cbWritten, pHv->fIsVendorMsHv /*fUdpPkt*/);
1263 if ( RT_SUCCESS(rc2)
1264 && cbWritten == cbWrite)
1265 {
1266 pOut->cbPending = 0;
1267 rcHv = GIM_HV_STATUS_SUCCESS;
1268 }
1269 else
1270 rcHv = GIM_HV_STATUS_INSUFFICIENT_BUFFER;
1271 }
1272
1273 /*
1274 * Update the guest memory with result.
1275 */
1276 int rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut, sizeof(GIMHVDEBUGPOSTOUT));
1277 if (RT_FAILURE(rc))
1278 {
1279 LogRelMax(10, ("GIM: HyperV: HvPostDebugData failed to update guest memory. rc=%Rrc\n", rc));
1280 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
1281 }
1282
1283 *prcHv = rcHv;
1284 return rc;
1285}
1286
1287
1288/**
1289 * Performs the HvRetrieveDebugData hypercall.
1290 *
1291 * @returns VBox status code.
1292 * @param pVM The cross context VM structure.
1293 * @param prcHv Where to store the result of the hypercall operation.
1294 *
1295 * @thread EMT.
1296 */
1297VMMR3_INT_DECL(int) gimR3HvHypercallRetrieveDebugData(PVM pVM, int *prcHv)
1298{
1299 AssertPtr(pVM);
1300 AssertPtr(prcHv);
1301 PGIMHV pHv = &pVM->gim.s.u.Hv;
1302 int rcHv = GIM_HV_STATUS_OPERATION_DENIED;
1303
1304 /*
1305 * Grab the parameters.
1306 */
1307 PGIMHVDEBUGRETRIEVEIN pIn = (PGIMHVDEBUGRETRIEVEIN)pHv->pbHypercallIn;
1308 AssertPtrReturn(pIn, VERR_GIM_IPE_1);
1309 uint32_t cbRead = pIn->cbRead;
1310 uint32_t fFlags = pIn->fFlags;
1311 uint64_t uTimeout = pIn->u64Timeout;
1312 uint32_t cMsTimeout = (fFlags & GIM_HV_DEBUG_RETREIVE_LOOP) ? (uTimeout * 100) / RT_NS_1MS_64 : 0;
1313
1314 PGIMHVDEBUGRETRIEVEOUT pOut = (PGIMHVDEBUGRETRIEVEOUT)pHv->pbHypercallOut;
1315 AssertPtrReturn(pOut, VERR_GIM_IPE_2);
1316 uint32_t *pcbReallyRead = &pOut->cbRead;
1317 uint32_t *pcbRemainingRead = &pOut->cbRemaining;
1318 void *pvData = ((uint8_t *)pOut) + sizeof(GIMHVDEBUGRETRIEVEOUT);
1319
1320 /*
1321 * Perform the hypercall.
1322 */
1323 *pcbReallyRead = 0;
1324 *pcbRemainingRead = cbRead;
1325#if 0
1326 /* Currently disabled as Windows 10 guest passes us undocumented flags. */
1327 if (fFlags & ~GIM_HV_DEBUG_RETREIVE_OPTIONS_MASK)
1328 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
1329#endif
1330 if (cbRead > GIM_HV_DEBUG_MAX_DATA_SIZE)
1331 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
1332 else if (fFlags & GIM_HV_DEBUG_RETREIVE_TEST_ACTIVITY)
1333 rcHv = GIM_HV_STATUS_SUCCESS; /** @todo implement this. */
1334 else if (!cbRead)
1335 rcHv = GIM_HV_STATUS_SUCCESS;
1336 else if (cbRead > 0)
1337 {
1338 int rc2 = gimR3HvDebugRead(pVM, pvData, GIM_HV_PAGE_SIZE, cbRead, pcbReallyRead, cMsTimeout,
1339 pHv->fIsVendorMsHv /*fUdpPkt*/);
1340 Assert(*pcbReallyRead <= cbRead);
1341 if ( RT_SUCCESS(rc2)
1342 && *pcbReallyRead > 0)
1343 {
1344 *pcbRemainingRead = cbRead - *pcbReallyRead;
1345 rcHv = GIM_HV_STATUS_SUCCESS;
1346 }
1347 else
1348 rcHv = GIM_HV_STATUS_NO_DATA;
1349 }
1350
1351 /*
1352 * Update the guest memory with result.
1353 */
1354 int rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut,
1355 sizeof(GIMHVDEBUGRETRIEVEOUT) + *pcbReallyRead);
1356 if (RT_FAILURE(rc))
1357 {
1358 LogRelMax(10, ("GIM: HyperV: HvRetrieveDebugData failed to update guest memory. rc=%Rrc\n", rc));
1359 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
1360 }
1361
1362 *prcHv = rcHv;
1363 return rc;
1364}
1365
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