VirtualBox

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

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

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

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