VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.2 KB
Line 
1/* $Id: GIMHv.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager, Hyper-V implementation.
4 */
5
6/*
7 * Copyright (C) 2014-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_GIM
33#include <VBox/vmm/apic.h>
34#include <VBox/vmm/gim.h>
35#include <VBox/vmm/cpum.h>
36#include <VBox/vmm/mm.h>
37#include <VBox/vmm/ssm.h>
38#include <VBox/vmm/hm.h>
39#include <VBox/vmm/pdmapi.h>
40#include <VBox/vmm/em.h>
41#include "GIMInternal.h"
42#include <VBox/vmm/vm.h>
43
44#include <VBox/err.h>
45#include <VBox/version.h>
46
47#include <iprt/assert.h>
48#include <iprt/string.h>
49#include <iprt/mem.h>
50#include <iprt/semaphore.h>
51#include <iprt/spinlock.h>
52#include <iprt/zero.h>
53#ifdef DEBUG_ramshankar
54# include <iprt/udp.h>
55#endif
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/**
62 * GIM Hyper-V saved-state version.
63 */
64#define GIM_HV_SAVED_STATE_VERSION UINT32_C(4)
65/** Saved states, priot to saving debug UDP source/destination ports. */
66#define GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG_UDP_PORTS UINT32_C(3)
67/** Saved states, prior to any synthetic interrupt controller support. */
68#define GIM_HV_SAVED_STATE_VERSION_PRE_SYNIC UINT32_C(2)
69/** Vanilla saved states, prior to any debug support. */
70#define GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG UINT32_C(1)
71
72#ifdef VBOX_WITH_STATISTICS
73# define GIMHV_MSRRANGE(a_uFirst, a_uLast, a_szName) \
74 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName, { 0 }, { 0 }, { 0 }, { 0 } }
75#else
76# define GIMHV_MSRRANGE(a_uFirst, a_uLast, a_szName) \
77 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName }
78#endif
79
80
81/*********************************************************************************************************************************
82* Global Variables *
83*********************************************************************************************************************************/
84/**
85 * Array of MSR ranges supported by Hyper-V.
86 */
87static CPUMMSRRANGE const g_aMsrRanges_HyperV[] =
88{
89 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE0_FIRST, MSR_GIM_HV_RANGE0_LAST, "Hyper-V range 0"),
90 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE1_FIRST, MSR_GIM_HV_RANGE1_LAST, "Hyper-V range 1"),
91 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE2_FIRST, MSR_GIM_HV_RANGE2_LAST, "Hyper-V range 2"),
92 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE3_FIRST, MSR_GIM_HV_RANGE3_LAST, "Hyper-V range 3"),
93 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE4_FIRST, MSR_GIM_HV_RANGE4_LAST, "Hyper-V range 4"),
94 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE5_FIRST, MSR_GIM_HV_RANGE5_LAST, "Hyper-V range 5"),
95 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE6_FIRST, MSR_GIM_HV_RANGE6_LAST, "Hyper-V range 6"),
96 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE7_FIRST, MSR_GIM_HV_RANGE7_LAST, "Hyper-V range 7"),
97 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE8_FIRST, MSR_GIM_HV_RANGE8_LAST, "Hyper-V range 8"),
98 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE9_FIRST, MSR_GIM_HV_RANGE9_LAST, "Hyper-V range 9"),
99 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE10_FIRST, MSR_GIM_HV_RANGE10_LAST, "Hyper-V range 10"),
100 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE11_FIRST, MSR_GIM_HV_RANGE11_LAST, "Hyper-V range 11"),
101 GIMHV_MSRRANGE(MSR_GIM_HV_RANGE12_FIRST, MSR_GIM_HV_RANGE12_LAST, "Hyper-V range 12")
102};
103#undef GIMHV_MSRRANGE
104
105/**
106 * DHCP OFFER packet response to the guest (client) over the Hyper-V debug
107 * transport.
108 *
109 * - MAC: Destination: broadcast.
110 * - MAC: Source: 00:00:00:00:01 (hypervisor). It's important that it's
111 * different from the client's MAC address which is all 0's.
112 * - IP: Source: 10.0.5.1 (hypervisor)
113 * - IP: Destination: broadcast.
114 * - IP: Checksum included.
115 * - BOOTP: Client IP address: 10.0.5.5.
116 * - BOOTP: Server IP address: 10.0.5.1.
117 * - DHCP options: Subnet mask, router, lease-time, DHCP server identifier.
118 * Options are kept to a minimum required for making Windows guests happy.
119 */
120#define GIMHV_DEBUGCLIENT_IPV4 RT_H2N_U32_C(0x0a000505) /* 10.0.5.5 */
121#define GIMHV_DEBUGSERVER_IPV4 RT_H2N_U32_C(0x0a000501) /* 10.0.5.1 */
122static const uint8_t g_abDhcpOffer[] =
123{
124 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x45, 0x10,
125 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x6a, 0xb5, 0x0a, 0x00, 0x05, 0x01, 0xff, 0xff,
126 0xff, 0xff, 0x00, 0x43, 0x00, 0x44, 0x01, 0x14, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x05, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x01, 0x04, 0xff,
142 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x00, 0x05, 0x01, 0x33, 0x04, 0xff, 0xff, 0xff, 0xff, 0x36,
143 0x04, 0x0a, 0x00, 0x05, 0x01, 0xff
144};
145
146/**
147 * DHCP ACK packet response to the guest (client) over the Hyper-V debug
148 * transport.
149 *
150 * - MAC: Destination: 00:00:00:00:00 (client).
151 * - IP: Destination: 10.0.5.5 (client).
152 * - Rest are mostly similar to the DHCP offer.
153 */
154static const uint8_t g_abDhcpAck[] =
155{
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x45, 0x10,
157 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x5b, 0xb0, 0x0a, 0x00, 0x05, 0x01, 0x0a, 0x00,
158 0x05, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x14, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x05, 0x0a, 0x00, 0x05, 0x05, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x01, 0x04, 0xff,
174 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x00, 0x05, 0x01, 0x33, 0x04, 0xff, 0xff, 0xff, 0xff, 0x36,
175 0x04, 0x0a, 0x00, 0x05, 0x01, 0xff
176};
177
178/**
179 * ARP reply to the guest (client) over the Hyper-V debug transport.
180 *
181 * - MAC: Destination: 00:00:00:00:00 (client)
182 * - MAC: Source: 00:00:00:00:01 (hypervisor)
183 * - ARP: Reply: 10.0.5.1 is at Source MAC address.
184 */
185static const uint8_t g_abArpReply[] =
186{
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x01,
188 0x08, 0x00, 0x06, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x05, 0x01,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x05
190};
191
192
193/*********************************************************************************************************************************
194* Internal Functions *
195*********************************************************************************************************************************/
196static int gimR3HvInitHypercallSupport(PVM pVM);
197static void gimR3HvTermHypercallSupport(PVM pVM);
198static int gimR3HvInitDebugSupport(PVM pVM);
199static void gimR3HvTermDebugSupport(PVM pVM);
200static DECLCALLBACK(void) gimR3HvTimerCallback(PVM pVM, TMTIMERHANDLE pTimer, void *pvUser);
201
202/**
203 * Initializes the Hyper-V GIM provider.
204 *
205 * @returns VBox status code.
206 * @param pVM The cross context VM structure.
207 * @param pGimCfg The GIM CFGM node.
208 */
209VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg)
210{
211 AssertReturn(pVM, VERR_INVALID_PARAMETER);
212 AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_HYPERV, VERR_INTERNAL_ERROR_5);
213
214 PGIMHV pHv = &pVM->gim.s.u.Hv;
215
216 /*
217 * Initialize timer handles and such.
218 */
219 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
220 {
221 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
222 PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu;
223 for (uint8_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStimers); idxStimer++)
224 pHvCpu->aStimers[idxStimer].hTimer = NIL_TMTIMERHANDLE;
225 }
226
227 /*
228 * Read configuration.
229 */
230 PCFGMNODE pCfgHv = CFGMR3GetChild(pGimCfg, "HyperV");
231 if (pCfgHv)
232 {
233 /*
234 * Validate the Hyper-V settings.
235 */
236 int rc2 = CFGMR3ValidateConfig(pCfgHv, "/HyperV/",
237 "VendorID"
238 "|VSInterface"
239 "|HypercallDebugInterface",
240 "" /* pszValidNodes */, "GIM/HyperV" /* pszWho */, 0 /* uInstance */);
241 if (RT_FAILURE(rc2))
242 return rc2;
243 }
244
245 /** @cfgm{/GIM/HyperV/VendorID, string, 'VBoxVBoxVBox'}
246 * The Hyper-V vendor signature, must be 12 characters. */
247 char szVendor[13];
248 int rc = CFGMR3QueryStringDef(pCfgHv, "VendorID", szVendor, sizeof(szVendor), "VBoxVBoxVBox");
249 AssertLogRelRCReturn(rc, rc);
250 AssertLogRelMsgReturn(strlen(szVendor) == 12,
251 ("The VendorID config value must be exactly 12 chars, '%s' isn't!\n", szVendor),
252 VERR_INVALID_PARAMETER);
253
254 LogRel(("GIM: HyperV: Reporting vendor as '%s'\n", szVendor));
255 /** @todo r=bird: GIM_HV_VENDOR_MICROSOFT is 12 char and the string is max
256 * 12+terminator, so the NCmp is a little bit misleading. */
257 if (!RTStrNCmp(szVendor, GIM_HV_VENDOR_MICROSOFT, sizeof(GIM_HV_VENDOR_MICROSOFT) - 1))
258 {
259 LogRel(("GIM: HyperV: Warning! Posing as the Microsoft vendor may alter guest behaviour!\n"));
260 pHv->fIsVendorMsHv = true;
261 }
262
263 /** @cfgm{/GIM/HyperV/VSInterface, bool, true}
264 * The Microsoft virtualization service interface (debugging). */
265 rc = CFGMR3QueryBoolDef(pCfgHv, "VSInterface", &pHv->fIsInterfaceVs, false);
266 AssertLogRelRCReturn(rc, rc);
267
268 /** @cfgm{/GIM/HyperV/HypercallDebugInterface, bool, false}
269 * Whether we specify the guest to use hypercalls for debugging rather than MSRs. */
270 rc = CFGMR3QueryBoolDef(pCfgHv, "HypercallDebugInterface", &pHv->fDbgHypercallInterface, false);
271 AssertLogRelRCReturn(rc, rc);
272
273 /*
274 * Determine interface capabilities based on the version.
275 */
276 if (!pVM->gim.s.u32Version)
277 {
278 /* Basic features. */
279 pHv->uBaseFeat = 0
280 //| GIM_HV_BASE_FEAT_VP_RUNTIME_MSR
281 | GIM_HV_BASE_FEAT_PART_TIME_REF_COUNT_MSR
282 //| GIM_HV_BASE_FEAT_BASIC_SYNIC_MSRS // Both required for synethetic timers
283 //| GIM_HV_BASE_FEAT_STIMER_MSRS // Both required for synethetic timers
284 | GIM_HV_BASE_FEAT_APIC_ACCESS_MSRS
285 | GIM_HV_BASE_FEAT_HYPERCALL_MSRS
286 | GIM_HV_BASE_FEAT_VP_ID_MSR
287 | GIM_HV_BASE_FEAT_VIRT_SYS_RESET_MSR
288 //| GIM_HV_BASE_FEAT_STAT_PAGES_MSR
289 | GIM_HV_BASE_FEAT_PART_REF_TSC_MSR
290 //| GIM_HV_BASE_FEAT_GUEST_IDLE_STATE_MSR
291 | GIM_HV_BASE_FEAT_TIMER_FREQ_MSRS
292 //| GIM_HV_BASE_FEAT_DEBUG_MSRS
293 ;
294
295 /* Miscellaneous features. */
296 pHv->uMiscFeat = 0
297 //| GIM_HV_MISC_FEAT_GUEST_DEBUGGING
298 //| GIM_HV_MISC_FEAT_XMM_HYPERCALL_INPUT
299 | GIM_HV_MISC_FEAT_TIMER_FREQ
300 | GIM_HV_MISC_FEAT_GUEST_CRASH_MSRS
301 //| GIM_HV_MISC_FEAT_DEBUG_MSRS
302 ;
303
304 /* Hypervisor recommendations to the guest. */
305 pHv->uHyperHints = GIM_HV_HINT_MSR_FOR_SYS_RESET
306 | GIM_HV_HINT_RELAX_TIME_CHECKS
307 | GIM_HV_HINT_X2APIC_MSRS
308 ;
309
310 /* Partition features. */
311 pHv->uPartFlags |= GIM_HV_PART_FLAGS_EXTENDED_HYPERCALLS;
312
313 /* Expose more if we're posing as Microsoft. We can, if needed, force MSR-based Hv
314 debugging by not exposing these bits while exposing the VS interface. The better
315 way is what we do currently, via the GIM_HV_DEBUG_OPTIONS_USE_HYPERCALLS bit. */
316 if (pHv->fIsVendorMsHv)
317 {
318 pHv->uMiscFeat |= GIM_HV_MISC_FEAT_GUEST_DEBUGGING
319 | GIM_HV_MISC_FEAT_DEBUG_MSRS;
320
321 pHv->uPartFlags |= GIM_HV_PART_FLAGS_DEBUGGING;
322 }
323 }
324
325 /*
326 * Populate the required fields in MMIO2 region records for registering.
327 */
328 for (size_t i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++)
329 pHv->aMmio2Regions[i].hMmio2 = NIL_PGMMMIO2HANDLE;
330
331 AssertCompile(GIM_HV_PAGE_SIZE == GUEST_PAGE_SIZE);
332 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
333 pRegion->iRegion = GIM_HV_HYPERCALL_PAGE_REGION_IDX;
334 pRegion->fRCMapping = false;
335 pRegion->cbRegion = GIM_HV_PAGE_SIZE; /* Sanity checked in gimR3HvLoad(), gimR3HvEnableTscPage() & gimR3HvEnableHypercallPage() */
336 pRegion->GCPhysPage = NIL_RTGCPHYS;
337 RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V hypercall page");
338
339 pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
340 pRegion->iRegion = GIM_HV_REF_TSC_PAGE_REGION_IDX;
341 pRegion->fRCMapping = false;
342 pRegion->cbRegion = GIM_HV_PAGE_SIZE; /* Sanity checked in gimR3HvLoad(), gimR3HvEnableTscPage() & gimR3HvEnableHypercallPage() */
343 pRegion->GCPhysPage = NIL_RTGCPHYS;
344 RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V TSC page");
345
346 /*
347 * Make sure the CPU ID bit are in accordance with the Hyper-V
348 * requirement and other paranoia checks.
349 * See "Requirements for implementing the Microsoft hypervisor interface" spec.
350 */
351 Assert(!(pHv->uPartFlags & ( GIM_HV_PART_FLAGS_CREATE_PART
352 | GIM_HV_PART_FLAGS_ACCESS_MEMORY_POOL
353 | GIM_HV_PART_FLAGS_ACCESS_PART_ID
354 | GIM_HV_PART_FLAGS_ADJUST_MSG_BUFFERS
355 | GIM_HV_PART_FLAGS_CREATE_PORT
356 | GIM_HV_PART_FLAGS_ACCESS_STATS
357 | GIM_HV_PART_FLAGS_CPU_MGMT
358 | GIM_HV_PART_FLAGS_CPU_PROFILER)));
359 Assert((pHv->uBaseFeat & (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR))
360 == (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR));
361#ifdef VBOX_STRICT
362 for (unsigned i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++)
363 {
364 PCGIMMMIO2REGION pCur = &pHv->aMmio2Regions[i];
365 Assert(!pCur->fRCMapping);
366 Assert(!pCur->fMapped);
367 Assert(pCur->GCPhysPage == NIL_RTGCPHYS);
368 }
369#endif
370
371 /*
372 * Expose HVP (Hypervisor Present) bit to the guest.
373 */
374 CPUMR3SetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP);
375
376 /*
377 * Modify the standard hypervisor leaves for Hyper-V.
378 */
379 CPUMCPUIDLEAF HyperLeaf;
380 RT_ZERO(HyperLeaf);
381 HyperLeaf.uLeaf = UINT32_C(0x40000000);
382 if ( pHv->fIsVendorMsHv
383 && pHv->fIsInterfaceVs)
384 HyperLeaf.uEax = UINT32_C(0x40000082); /* Since we expose 0x40000082 below for the Hyper-V PV-debugging case. */
385 else
386 HyperLeaf.uEax = UINT32_C(0x40000006); /* Minimum value for Hyper-V default is 0x40000005. */
387 /*
388 * Don't report vendor as 'Microsoft Hv'[1] by default, see @bugref{7270#c152}.
389 * [1]: ebx=0x7263694d ('rciM') ecx=0x666f736f ('foso') edx=0x76482074 ('vH t')
390 */
391 {
392 uint32_t uVendorEbx;
393 uint32_t uVendorEcx;
394 uint32_t uVendorEdx;
395 uVendorEbx = ((uint32_t)szVendor[ 3]) << 24 | ((uint32_t)szVendor[ 2]) << 16 | ((uint32_t)szVendor[1]) << 8
396 | (uint32_t)szVendor[ 0];
397 uVendorEcx = ((uint32_t)szVendor[ 7]) << 24 | ((uint32_t)szVendor[ 6]) << 16 | ((uint32_t)szVendor[5]) << 8
398 | (uint32_t)szVendor[ 4];
399 uVendorEdx = ((uint32_t)szVendor[11]) << 24 | ((uint32_t)szVendor[10]) << 16 | ((uint32_t)szVendor[9]) << 8
400 | (uint32_t)szVendor[ 8];
401 HyperLeaf.uEbx = uVendorEbx;
402 HyperLeaf.uEcx = uVendorEcx;
403 HyperLeaf.uEdx = uVendorEdx;
404 }
405 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
406 AssertLogRelRCReturn(rc, rc);
407
408 HyperLeaf.uLeaf = UINT32_C(0x40000001);
409 HyperLeaf.uEax = 0x31237648; /* 'Hv#1' */
410 HyperLeaf.uEbx = 0; /* Reserved */
411 HyperLeaf.uEcx = 0; /* Reserved */
412 HyperLeaf.uEdx = 0; /* Reserved */
413 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
414 AssertLogRelRCReturn(rc, rc);
415
416 /*
417 * Add Hyper-V specific leaves.
418 */
419 HyperLeaf.uLeaf = UINT32_C(0x40000002); /* MBZ until MSR_GIM_HV_GUEST_OS_ID is set by the guest. */
420 HyperLeaf.uEax = 0;
421 HyperLeaf.uEbx = 0;
422 HyperLeaf.uEcx = 0;
423 HyperLeaf.uEdx = 0;
424 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
425 AssertLogRelRCReturn(rc, rc);
426
427 HyperLeaf.uLeaf = UINT32_C(0x40000003);
428 HyperLeaf.uEax = pHv->uBaseFeat;
429 HyperLeaf.uEbx = pHv->uPartFlags;
430 HyperLeaf.uEcx = pHv->uPowMgmtFeat;
431 HyperLeaf.uEdx = pHv->uMiscFeat;
432 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
433 AssertLogRelRCReturn(rc, rc);
434
435 HyperLeaf.uLeaf = UINT32_C(0x40000004);
436 HyperLeaf.uEax = pHv->uHyperHints;
437 HyperLeaf.uEbx = 0xffffffff;
438 HyperLeaf.uEcx = 0;
439 HyperLeaf.uEdx = 0;
440 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
441 AssertLogRelRCReturn(rc, rc);
442
443 RT_ZERO(HyperLeaf);
444 HyperLeaf.uLeaf = UINT32_C(0x40000005);
445 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
446 AssertLogRelRCReturn(rc, rc);
447
448 /* Leaf 0x40000006 is inserted in gimR3HvInitCompleted(). */
449
450 if ( pHv->fIsVendorMsHv
451 && pHv->fIsInterfaceVs)
452 {
453 HyperLeaf.uLeaf = UINT32_C(0x40000080);
454 HyperLeaf.uEax = 0;
455 HyperLeaf.uEbx = 0x7263694d; /* 'rciM' */
456 HyperLeaf.uEcx = 0x666f736f; /* 'foso'*/
457 HyperLeaf.uEdx = 0x53562074; /* 'SV t' */
458 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
459 AssertLogRelRCReturn(rc, rc);
460
461 HyperLeaf.uLeaf = UINT32_C(0x40000081);
462 HyperLeaf.uEax = 0x31235356; /* '1#SV' */
463 HyperLeaf.uEbx = 0;
464 HyperLeaf.uEcx = 0;
465 HyperLeaf.uEdx = 0;
466 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
467 AssertLogRelRCReturn(rc, rc);
468
469 HyperLeaf.uLeaf = UINT32_C(0x40000082);
470 HyperLeaf.uEax = RT_BIT_32(1);
471 HyperLeaf.uEbx = 0;
472 HyperLeaf.uEcx = 0;
473 HyperLeaf.uEdx = 0;
474 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
475 AssertLogRelRCReturn(rc, rc);
476 }
477
478 /*
479 * Insert all MSR ranges of Hyper-V.
480 */
481 for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_HyperV); i++)
482 {
483 int rc2 = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_HyperV[i]);
484 AssertLogRelRCReturn(rc2, rc2);
485 }
486
487 /*
488 * Setup non-zero MSRs.
489 */
490 if (pHv->uMiscFeat & GIM_HV_MISC_FEAT_GUEST_CRASH_MSRS)
491 pHv->uCrashCtlMsr = MSR_GIM_HV_CRASH_CTL_NOTIFY;
492 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
493 {
494 PGIMHVCPU pHvCpu = &pVM->apCpusR3[idCpu]->gim.s.u.HvCpu;
495 for (uint8_t idxSintMsr = 0; idxSintMsr < RT_ELEMENTS(pHvCpu->auSintMsrs); idxSintMsr++)
496 pHvCpu->auSintMsrs[idxSintMsr] = MSR_GIM_HV_SINT_MASKED;
497 }
498
499 /*
500 * Setup hypercall support.
501 */
502 rc = gimR3HvInitHypercallSupport(pVM);
503 AssertLogRelRCReturn(rc, rc);
504
505 /*
506 * Setup debug support.
507 */
508 rc = gimR3HvInitDebugSupport(pVM);
509 AssertLogRelRCReturn(rc, rc);
510
511 /*
512 * Setup up the per-VCPU synthetic timers.
513 */
514 if ( (pHv->uBaseFeat & GIM_HV_BASE_FEAT_STIMER_MSRS)
515 || (pHv->uBaseFeat & GIM_HV_BASE_FEAT_BASIC_SYNIC_MSRS))
516 {
517 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
518 {
519 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
520 PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu;
521
522 for (uint8_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStimers); idxStimer++)
523 {
524 PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer];
525
526 /* Associate the synthetic timer with its corresponding VCPU. */
527 pHvStimer->idCpu = pVCpu->idCpu;
528 pHvStimer->idxStimer = idxStimer;
529
530 /* Create the timer and associate the context pointers. */
531 char szName[32];
532 RTStrPrintf(szName, sizeof(szName), "Hyper-V[%u] Timer%u", pVCpu->idCpu, idxStimer);
533 rc = TMR3TimerCreate(pVM, TMCLOCK_VIRTUAL_SYNC, gimR3HvTimerCallback, pHvStimer /* pvUser */,
534 TMTIMER_FLAGS_RING0, szName, &pHvStimer->hTimer);
535 AssertLogRelRCReturn(rc, rc);
536 }
537 }
538 }
539
540 /*
541 * Register statistics.
542 */
543 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
544 {
545 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
546 PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu;
547
548 for (size_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStatStimerFired); idxStimer++)
549 {
550 int rc2 = STAMR3RegisterF(pVM, &pHvCpu->aStatStimerFired[idxStimer], STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
551 STAMUNIT_OCCURENCES, "Number of times the synthetic timer fired.",
552 "/GIM/HyperV/%u/Stimer%u_Fired", idCpu, idxStimer);
553 AssertLogRelRCReturn(rc2, rc2);
554 }
555 }
556
557 return VINF_SUCCESS;
558}
559
560
561/**
562 * Initializes remaining bits of the Hyper-V provider.
563 *
564 * This is called after initializing HM and almost all other VMM components.
565 *
566 * @returns VBox status code.
567 * @param pVM The cross context VM structure.
568 */
569VMMR3_INT_DECL(int) gimR3HvInitCompleted(PVM pVM)
570{
571 PGIMHV pHv = &pVM->gim.s.u.Hv;
572 pHv->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM);
573
574 /*
575 * Determine interface capabilities based on the version.
576 */
577 if (!pVM->gim.s.u32Version)
578 {
579 /* Hypervisor capabilities; features used by the hypervisor. */
580 pHv->uHyperCaps = HMIsNestedPagingActive(pVM) ? GIM_HV_HOST_FEAT_NESTED_PAGING : 0;
581 pHv->uHyperCaps |= HMIsMsrBitmapActive(pVM) ? GIM_HV_HOST_FEAT_MSR_BITMAP : 0;
582 }
583
584 CPUMCPUIDLEAF HyperLeaf;
585 RT_ZERO(HyperLeaf);
586 HyperLeaf.uLeaf = UINT32_C(0x40000006);
587 HyperLeaf.uEax = pHv->uHyperCaps;
588 HyperLeaf.uEbx = 0;
589 HyperLeaf.uEcx = 0;
590 HyperLeaf.uEdx = 0;
591 int rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
592 AssertLogRelRCReturn(rc, rc);
593
594 /*
595 * Inform APIC whether Hyper-V compatibility mode is enabled or not.
596 * Do this here rather than on gimR3HvInit() as it gets called after APIC
597 * has finished inserting/removing the x2APIC MSR range.
598 */
599 if (pHv->uHyperHints & GIM_HV_HINT_X2APIC_MSRS)
600 APICR3HvSetCompatMode(pVM, true);
601
602 return rc;
603}
604
605
606/**
607 * Terminates the Hyper-V GIM provider.
608 *
609 * @returns VBox status code.
610 * @param pVM The cross context VM structure.
611 */
612VMMR3_INT_DECL(int) gimR3HvTerm(PVM pVM)
613{
614 gimR3HvReset(pVM);
615 gimR3HvTermHypercallSupport(pVM);
616 gimR3HvTermDebugSupport(pVM);
617
618 PCGIMHV pHv = &pVM->gim.s.u.Hv;
619 if ( (pHv->uBaseFeat & GIM_HV_BASE_FEAT_STIMER_MSRS)
620 || (pHv->uBaseFeat & GIM_HV_BASE_FEAT_BASIC_SYNIC_MSRS))
621 {
622 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
623 {
624 PGIMHVCPU pHvCpu = &pVM->apCpusR3[idCpu]->gim.s.u.HvCpu;
625 for (uint8_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStimers); idxStimer++)
626 {
627 PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer];
628 TMR3TimerDestroy(pVM, pHvStimer->hTimer);
629 pHvStimer->hTimer = NIL_TMTIMERHANDLE;
630 }
631 }
632 }
633
634 return VINF_SUCCESS;
635}
636
637
638/**
639 * Applies relocations to data and code managed by this
640 * component. This function will be called at init and
641 * whenever the VMM need to relocate it self inside the GC.
642 *
643 * @param pVM The cross context VM structure.
644 * @param offDelta Relocation delta relative to old location.
645 */
646VMMR3_INT_DECL(void) gimR3HvRelocate(PVM pVM, RTGCINTPTR offDelta)
647{
648 RT_NOREF(pVM, offDelta);
649}
650
651
652/**
653 * This resets Hyper-V provider MSRs and unmaps whatever Hyper-V regions that
654 * the guest may have mapped.
655 *
656 * This is called when the VM is being reset.
657 *
658 * @param pVM The cross context VM structure.
659 *
660 * @thread EMT(0)
661 */
662VMMR3_INT_DECL(void) gimR3HvReset(PVM pVM)
663{
664 VM_ASSERT_EMT0(pVM);
665
666 /*
667 * Unmap MMIO2 pages that the guest may have setup.
668 */
669 LogRel(("GIM: HyperV: Resetting MMIO2 regions and MSRs\n"));
670 PGIMHV pHv = &pVM->gim.s.u.Hv;
671 for (unsigned i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++)
672 {
673 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[i];
674#if 0
675 gimR3Mmio2Unmap(pVM, pRegion);
676#else
677 pRegion->fMapped = false;
678 pRegion->GCPhysPage = NIL_RTGCPHYS;
679#endif
680 }
681
682 /*
683 * Reset MSRs.
684 */
685 pHv->u64GuestOsIdMsr = 0;
686 pHv->u64HypercallMsr = 0;
687 pHv->u64TscPageMsr = 0;
688 pHv->uCrashP0Msr = 0;
689 pHv->uCrashP1Msr = 0;
690 pHv->uCrashP2Msr = 0;
691 pHv->uCrashP3Msr = 0;
692 pHv->uCrashP4Msr = 0;
693 pHv->uDbgStatusMsr = 0;
694 pHv->uDbgPendingBufferMsr = 0;
695 pHv->uDbgSendBufferMsr = 0;
696 pHv->uDbgRecvBufferMsr = 0;
697 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
698 {
699 PGIMHVCPU pHvCpu = &pVM->apCpusR3[idCpu]->gim.s.u.HvCpu;
700 pHvCpu->uSControlMsr = 0;
701 pHvCpu->uSimpMsr = 0;
702 pHvCpu->uSiefpMsr = 0;
703 pHvCpu->uApicAssistPageMsr = 0;
704
705 for (uint8_t idxSint = 0; idxSint < RT_ELEMENTS(pHvCpu->auSintMsrs); idxSint++)
706 pHvCpu->auSintMsrs[idxSint] = MSR_GIM_HV_SINT_MASKED;
707
708 for (uint8_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStimers); idxStimer++)
709 {
710 PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer];
711 pHvStimer->uStimerConfigMsr = 0;
712 pHvStimer->uStimerCountMsr = 0;
713 }
714 }
715}
716
717
718/**
719 * Callback for when debug data is available over the debugger connection.
720 *
721 * @param pVM The cross context VM structure.
722 */
723static DECLCALLBACK(void) gimR3HvDebugBufAvail(PVM pVM)
724{
725 PGIMHV pHv = &pVM->gim.s.u.Hv;
726 RTGCPHYS GCPhysPendingBuffer = pHv->uDbgPendingBufferMsr;
727 if ( GCPhysPendingBuffer
728 && PGMPhysIsGCPhysNormal(pVM, GCPhysPendingBuffer))
729 {
730 uint8_t bPendingData = 1;
731 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysPendingBuffer, &bPendingData, sizeof(bPendingData));
732 if (RT_FAILURE(rc))
733 {
734 LogRelMax(5, ("GIM: HyperV: Failed to set pending debug receive buffer at %#RGp, rc=%Rrc\n", GCPhysPendingBuffer,
735 rc));
736 }
737 }
738}
739
740
741/**
742 * Callback for when debug data has been read from the debugger connection.
743 *
744 * This will be invoked before signalling read of the next debug buffer.
745 *
746 * @param pVM The cross context VM structure.
747 */
748static DECLCALLBACK(void) gimR3HvDebugBufReadCompleted(PVM pVM)
749{
750 PGIMHV pHv = &pVM->gim.s.u.Hv;
751 RTGCPHYS GCPhysPendingBuffer = pHv->uDbgPendingBufferMsr;
752 if ( GCPhysPendingBuffer
753 && PGMPhysIsGCPhysNormal(pVM, GCPhysPendingBuffer))
754 {
755 uint8_t bPendingData = 0;
756 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysPendingBuffer, &bPendingData, sizeof(bPendingData));
757 if (RT_FAILURE(rc))
758 {
759 LogRelMax(5, ("GIM: HyperV: Failed to clear pending debug receive buffer at %#RGp, rc=%Rrc\n", GCPhysPendingBuffer,
760 rc));
761 }
762 }
763}
764
765
766/**
767 * Get Hyper-V debug setup parameters.
768 *
769 * @returns VBox status code.
770 * @param pVM The cross context VM structure.
771 * @param pDbgSetup Where to store the debug setup details.
772 */
773VMMR3_INT_DECL(int) gimR3HvGetDebugSetup(PVM pVM, PGIMDEBUGSETUP pDbgSetup)
774{
775 Assert(pDbgSetup);
776 PGIMHV pHv = &pVM->gim.s.u.Hv;
777 if (pHv->fDbgEnabled)
778 {
779 pDbgSetup->pfnDbgRecvBufAvail = gimR3HvDebugBufAvail;
780 pDbgSetup->cbDbgRecvBuf = GIM_HV_PAGE_SIZE;
781 return VINF_SUCCESS;
782 }
783 return VERR_GIM_NO_DEBUG_CONNECTION;
784}
785
786
787/**
788 * Hyper-V state-save operation.
789 *
790 * @returns VBox status code.
791 * @param pVM The cross context VM structure.
792 * @param pSSM The saved state handle.
793 */
794VMMR3_INT_DECL(int) gimR3HvSave(PVM pVM, PSSMHANDLE pSSM)
795{
796 PCGIMHV pHv = &pVM->gim.s.u.Hv;
797
798 /*
799 * Save the Hyper-V SSM version.
800 */
801 SSMR3PutU32(pSSM, GIM_HV_SAVED_STATE_VERSION);
802
803 /*
804 * Save per-VM MSRs.
805 */
806 SSMR3PutU64(pSSM, pHv->u64GuestOsIdMsr);
807 SSMR3PutU64(pSSM, pHv->u64HypercallMsr);
808 SSMR3PutU64(pSSM, pHv->u64TscPageMsr);
809
810 /*
811 * Save Hyper-V features / capabilities.
812 */
813 SSMR3PutU32(pSSM, pHv->uBaseFeat);
814 SSMR3PutU32(pSSM, pHv->uPartFlags);
815 SSMR3PutU32(pSSM, pHv->uPowMgmtFeat);
816 SSMR3PutU32(pSSM, pHv->uMiscFeat);
817 SSMR3PutU32(pSSM, pHv->uHyperHints);
818 SSMR3PutU32(pSSM, pHv->uHyperCaps);
819
820 /*
821 * Save the Hypercall region.
822 */
823 PCGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
824 SSMR3PutU8(pSSM, pRegion->iRegion);
825 SSMR3PutBool(pSSM, pRegion->fRCMapping);
826 SSMR3PutU32(pSSM, pRegion->cbRegion);
827 SSMR3PutGCPhys(pSSM, pRegion->GCPhysPage);
828 SSMR3PutStrZ(pSSM, pRegion->szDescription);
829
830 /*
831 * Save the reference TSC region.
832 */
833 pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
834 SSMR3PutU8(pSSM, pRegion->iRegion);
835 SSMR3PutBool(pSSM, pRegion->fRCMapping);
836 SSMR3PutU32(pSSM, pRegion->cbRegion);
837 SSMR3PutGCPhys(pSSM, pRegion->GCPhysPage);
838 SSMR3PutStrZ(pSSM, pRegion->szDescription);
839 /* Save the TSC sequence so we can bump it on restore (as the CPU frequency/offset may change). */
840 uint32_t uTscSequence = 0;
841 if ( pRegion->fMapped
842 && MSR_GIM_HV_REF_TSC_IS_ENABLED(pHv->u64TscPageMsr))
843 {
844 PCGIMHVREFTSC pRefTsc = (PCGIMHVREFTSC)pRegion->pvPageR3;
845 uTscSequence = pRefTsc->u32TscSequence;
846 }
847 SSMR3PutU32(pSSM, uTscSequence);
848
849 /*
850 * Save debug support data.
851 */
852 SSMR3PutU64(pSSM, pHv->uDbgPendingBufferMsr);
853 SSMR3PutU64(pSSM, pHv->uDbgSendBufferMsr);
854 SSMR3PutU64(pSSM, pHv->uDbgRecvBufferMsr);
855 SSMR3PutU64(pSSM, pHv->uDbgStatusMsr);
856 SSMR3PutU32(pSSM, pHv->enmDbgReply);
857 SSMR3PutU32(pSSM, pHv->uDbgBootpXId);
858 SSMR3PutU32(pSSM, pHv->DbgGuestIp4Addr.u);
859 SSMR3PutU16(pSSM, pHv->uUdpGuestDstPort);
860 SSMR3PutU16(pSSM, pHv->uUdpGuestSrcPort);
861
862 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
863 {
864 PGIMHVCPU pHvCpu = &pVM->apCpusR3[idCpu]->gim.s.u.HvCpu;
865 SSMR3PutU64(pSSM, pHvCpu->uSimpMsr);
866 for (size_t idxSintMsr = 0; idxSintMsr < RT_ELEMENTS(pHvCpu->auSintMsrs); idxSintMsr++)
867 SSMR3PutU64(pSSM, pHvCpu->auSintMsrs[idxSintMsr]);
868 }
869
870 return SSMR3PutU8(pSSM, UINT8_MAX);
871}
872
873
874/**
875 * Hyper-V state-load operation, final pass.
876 *
877 * @returns VBox status code.
878 * @param pVM The cross context VM structure.
879 * @param pSSM The saved state handle.
880 */
881VMMR3_INT_DECL(int) gimR3HvLoad(PVM pVM, PSSMHANDLE pSSM)
882{
883 /*
884 * Load the Hyper-V SSM version first.
885 */
886 uint32_t uHvSavedStateVersion;
887 int rc = SSMR3GetU32(pSSM, &uHvSavedStateVersion);
888 AssertRCReturn(rc, rc);
889 if ( uHvSavedStateVersion != GIM_HV_SAVED_STATE_VERSION
890 && uHvSavedStateVersion != GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG_UDP_PORTS
891 && uHvSavedStateVersion != GIM_HV_SAVED_STATE_VERSION_PRE_SYNIC
892 && uHvSavedStateVersion != GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG)
893 return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS,
894 N_("Unsupported Hyper-V saved-state version %u (current %u)!"),
895 uHvSavedStateVersion, GIM_HV_SAVED_STATE_VERSION);
896
897 /*
898 * Update the TSC frequency from TM.
899 */
900 PGIMHV pHv = &pVM->gim.s.u.Hv;
901 pHv->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM);
902
903 /*
904 * Load per-VM MSRs.
905 */
906 SSMR3GetU64(pSSM, &pHv->u64GuestOsIdMsr);
907 SSMR3GetU64(pSSM, &pHv->u64HypercallMsr);
908 SSMR3GetU64(pSSM, &pHv->u64TscPageMsr);
909
910 /*
911 * Load Hyper-V features / capabilities.
912 */
913 SSMR3GetU32(pSSM, &pHv->uBaseFeat);
914 SSMR3GetU32(pSSM, &pHv->uPartFlags);
915 SSMR3GetU32(pSSM, &pHv->uPowMgmtFeat);
916 SSMR3GetU32(pSSM, &pHv->uMiscFeat);
917 SSMR3GetU32(pSSM, &pHv->uHyperHints);
918 SSMR3GetU32(pSSM, &pHv->uHyperCaps);
919
920 /*
921 * Load and enable the Hypercall region.
922 */
923 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
924 SSMR3GetU8(pSSM, &pRegion->iRegion);
925 SSMR3GetBool(pSSM, &pRegion->fRCMapping);
926 SSMR3GetU32(pSSM, &pRegion->cbRegion);
927 SSMR3GetGCPhys(pSSM, &pRegion->GCPhysPage);
928 rc = SSMR3GetStrZ(pSSM, pRegion->szDescription, sizeof(pRegion->szDescription));
929 AssertRCReturn(rc, rc);
930
931 if (pRegion->cbRegion != GIM_HV_PAGE_SIZE)
932 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Hypercall page region size %#x invalid, expected %#x"),
933 pRegion->cbRegion, GIM_HV_PAGE_SIZE);
934
935 if (MSR_GIM_HV_HYPERCALL_PAGE_IS_ENABLED(pHv->u64HypercallMsr))
936 {
937 Assert(pRegion->GCPhysPage != NIL_RTGCPHYS);
938 if (RT_LIKELY(pRegion->fRegistered))
939 {
940 rc = gimR3HvEnableHypercallPage(pVM, pRegion->GCPhysPage);
941 if (RT_FAILURE(rc))
942 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Failed to enable the hypercall page. GCPhys=%#RGp rc=%Rrc"),
943 pRegion->GCPhysPage, rc);
944 }
945 else
946 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Hypercall MMIO2 region not registered. Missing GIM device?!"));
947 }
948
949 /*
950 * Load and enable the reference TSC region.
951 */
952 uint32_t uTscSequence;
953 pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
954 SSMR3GetU8(pSSM, &pRegion->iRegion);
955 SSMR3GetBool(pSSM, &pRegion->fRCMapping);
956 SSMR3GetU32(pSSM, &pRegion->cbRegion);
957 SSMR3GetGCPhys(pSSM, &pRegion->GCPhysPage);
958 SSMR3GetStrZ(pSSM, pRegion->szDescription, sizeof(pRegion->szDescription));
959 rc = SSMR3GetU32(pSSM, &uTscSequence);
960 AssertRCReturn(rc, rc);
961
962 if (pRegion->cbRegion != GIM_HV_PAGE_SIZE)
963 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("TSC page region size %#x invalid, expected %#x"),
964 pRegion->cbRegion, GIM_HV_PAGE_SIZE);
965
966 if (MSR_GIM_HV_REF_TSC_IS_ENABLED(pHv->u64TscPageMsr))
967 {
968 Assert(pRegion->GCPhysPage != NIL_RTGCPHYS);
969 if (pRegion->fRegistered)
970 {
971 rc = gimR3HvEnableTscPage(pVM, pRegion->GCPhysPage, true /* fUseThisTscSeq */, uTscSequence);
972 if (RT_FAILURE(rc))
973 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Failed to enable the TSC page. GCPhys=%#RGp rc=%Rrc"),
974 pRegion->GCPhysPage, rc);
975 }
976 else
977 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("TSC-page MMIO2 region not registered. Missing GIM device?!"));
978 }
979
980 /*
981 * Load the debug support data.
982 */
983 if (uHvSavedStateVersion > GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG)
984 {
985 SSMR3GetU64(pSSM, &pHv->uDbgPendingBufferMsr);
986 SSMR3GetU64(pSSM, &pHv->uDbgSendBufferMsr);
987 SSMR3GetU64(pSSM, &pHv->uDbgRecvBufferMsr);
988 SSMR3GetU64(pSSM, &pHv->uDbgStatusMsr);
989 SSM_GET_ENUM32_RET(pSSM, pHv->enmDbgReply, GIMHVDEBUGREPLY);
990 SSMR3GetU32(pSSM, &pHv->uDbgBootpXId);
991 rc = SSMR3GetU32(pSSM, &pHv->DbgGuestIp4Addr.u);
992 AssertRCReturn(rc, rc);
993 if (uHvSavedStateVersion > GIM_HV_SAVED_STATE_VERSION_PRE_DEBUG_UDP_PORTS)
994 {
995 rc = SSMR3GetU16(pSSM, &pHv->uUdpGuestDstPort); AssertRCReturn(rc, rc);
996 rc = SSMR3GetU16(pSSM, &pHv->uUdpGuestSrcPort); AssertRCReturn(rc, rc);
997 }
998
999 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
1000 {
1001 PGIMHVCPU pHvCpu = &pVM->apCpusR3[idCpu]->gim.s.u.HvCpu;
1002 SSMR3GetU64(pSSM, &pHvCpu->uSimpMsr);
1003 if (uHvSavedStateVersion <= GIM_HV_SAVED_STATE_VERSION_PRE_SYNIC)
1004 SSMR3GetU64(pSSM, &pHvCpu->auSintMsrs[GIM_HV_VMBUS_MSG_SINT]);
1005 else
1006 {
1007 for (uint8_t idxSintMsr = 0; idxSintMsr < RT_ELEMENTS(pHvCpu->auSintMsrs); idxSintMsr++)
1008 SSMR3GetU64(pSSM, &pHvCpu->auSintMsrs[idxSintMsr]);
1009 }
1010 }
1011
1012 uint8_t bDelim;
1013 rc = SSMR3GetU8(pSSM, &bDelim);
1014 }
1015 else
1016 rc = VINF_SUCCESS;
1017
1018 return rc;
1019}
1020
1021
1022/**
1023 * Hyper-V load-done callback.
1024 *
1025 * @returns VBox status code.
1026 * @param pVM The cross context VM structure.
1027 * @param pSSM The saved state handle.
1028 */
1029VMMR3_INT_DECL(int) gimR3HvLoadDone(PVM pVM, PSSMHANDLE pSSM)
1030{
1031 if (RT_SUCCESS(SSMR3HandleGetStatus(pSSM)))
1032 {
1033 /*
1034 * Update EM on whether MSR_GIM_HV_GUEST_OS_ID allows hypercall instructions.
1035 */
1036 if (pVM->gim.s.u.Hv.u64GuestOsIdMsr)
1037 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
1038 EMSetHypercallInstructionsEnabled(pVM->apCpusR3[idCpu], true);
1039 else
1040 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
1041 EMSetHypercallInstructionsEnabled(pVM->apCpusR3[idCpu], false);
1042 }
1043 return VINF_SUCCESS;
1044}
1045
1046
1047/**
1048 * Enables the Hyper-V APIC-assist page.
1049 *
1050 * @returns VBox status code.
1051 * @param pVCpu The cross context virtual CPU structure.
1052 * @param GCPhysApicAssistPage Where to map the APIC-assist page.
1053 */
1054VMMR3_INT_DECL(int) gimR3HvEnableApicAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysApicAssistPage)
1055{
1056 PVM pVM = pVCpu->CTX_SUFF(pVM);
1057 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
1058 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
1059
1060 /*
1061 * Map the APIC-assist-page at the specified address.
1062 */
1063 /** @todo this is buggy when large pages are used due to a PGM limitation, see
1064 * @bugref{7532}. Instead of the overlay style mapping, we just
1065 * rewrite guest memory directly. */
1066 AssertCompile(sizeof(g_abRTZero64K) >= GUEST_PAGE_SIZE);
1067 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysApicAssistPage, g_abRTZero64K, GUEST_PAGE_SIZE);
1068 if (RT_SUCCESS(rc))
1069 {
1070 /** @todo Inform APIC. */
1071 LogRel(("GIM%u: HyperV: Enabled APIC-assist page at %#RGp\n", pVCpu->idCpu, GCPhysApicAssistPage));
1072 }
1073 else
1074 {
1075 LogRelFunc(("GIM%u: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", pVCpu->idCpu, rc));
1076 rc = VERR_GIM_OPERATION_FAILED;
1077 }
1078 return rc;
1079}
1080
1081
1082/**
1083 * Disables the Hyper-V APIC-assist page.
1084 *
1085 * @returns VBox status code.
1086 * @param pVCpu The cross context virtual CPU structure.
1087 */
1088VMMR3_INT_DECL(int) gimR3HvDisableApicAssistPage(PVMCPU pVCpu)
1089{
1090 LogRel(("GIM%u: HyperV: Disabled APIC-assist page\n", pVCpu->idCpu));
1091 /** @todo inform APIC */
1092 return VINF_SUCCESS;
1093}
1094
1095
1096/**
1097 * @callback_method_impl{FNTMTIMERINT, Hyper-V synthetic timer callback.}
1098 */
1099static DECLCALLBACK(void) gimR3HvTimerCallback(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser)
1100{
1101 PGIMHVSTIMER pHvStimer = (PGIMHVSTIMER)pvUser;
1102 Assert(pHvStimer);
1103 Assert(TMTimerIsLockOwner(pVM, hTimer));
1104 Assert(pHvStimer->idCpu < pVM->cCpus);
1105 Assert(pHvStimer->hTimer == hTimer);
1106 RT_NOREF(hTimer);
1107
1108 PVMCPU pVCpu = pVM->apCpusR3[pHvStimer->idCpu];
1109 PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu;
1110 Assert(pHvStimer->idxStimer < RT_ELEMENTS(pHvCpu->aStatStimerFired));
1111
1112 STAM_COUNTER_INC(&pHvCpu->aStatStimerFired[pHvStimer->idxStimer]);
1113
1114 uint64_t const uStimerConfig = pHvStimer->uStimerConfigMsr;
1115 uint16_t const idxSint = MSR_GIM_HV_STIMER_GET_SINTX(uStimerConfig);
1116 if (RT_LIKELY(idxSint < RT_ELEMENTS(pHvCpu->auSintMsrs)))
1117 {
1118 uint64_t const uSint = pHvCpu->auSintMsrs[idxSint];
1119 if (!MSR_GIM_HV_SINT_IS_MASKED(uSint))
1120 {
1121 uint8_t const uVector = MSR_GIM_HV_SINT_GET_VECTOR(uSint);
1122 bool const fAutoEoi = MSR_GIM_HV_SINT_IS_AUTOEOI(uSint);
1123 APICHvSendInterrupt(pVCpu, uVector, fAutoEoi, XAPICTRIGGERMODE_EDGE);
1124 }
1125 }
1126
1127 /* Re-arm the timer if it's periodic. */
1128 if (MSR_GIM_HV_STIMER_IS_PERIODIC(uStimerConfig))
1129 gimHvStartStimer(pVCpu, pHvStimer);
1130}
1131
1132
1133/**
1134 * Enables the Hyper-V SIEF page.
1135 *
1136 * @returns VBox status code.
1137 * @param pVCpu The cross context virtual CPU structure.
1138 * @param GCPhysSiefPage Where to map the SIEF page.
1139 */
1140VMMR3_INT_DECL(int) gimR3HvEnableSiefPage(PVMCPU pVCpu, RTGCPHYS GCPhysSiefPage)
1141{
1142 PVM pVM = pVCpu->CTX_SUFF(pVM);
1143 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
1144 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
1145
1146 /*
1147 * Map the SIEF page at the specified address.
1148 */
1149 /** @todo this is buggy when large pages are used due to a PGM limitation, see
1150 * @bugref{7532}. Instead of the overlay style mapping, we just
1151 * rewrite guest memory directly. */
1152 AssertCompile(sizeof(g_abRTZero64K) >= GUEST_PAGE_SIZE);
1153 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysSiefPage, g_abRTZero64K, GUEST_PAGE_SIZE);
1154 if (RT_SUCCESS(rc))
1155 {
1156 /** @todo SIEF setup. */
1157 LogRel(("GIM%u: HyperV: Enabled SIEF page at %#RGp\n", pVCpu->idCpu, GCPhysSiefPage));
1158 }
1159 else
1160 {
1161 LogRelFunc(("GIM%u: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", pVCpu->idCpu, rc));
1162 rc = VERR_GIM_OPERATION_FAILED;
1163 }
1164 return rc;
1165}
1166
1167
1168/**
1169 * Disables the Hyper-V SIEF page.
1170 *
1171 * @returns VBox status code.
1172 * @param pVCpu The cross context virtual CPU structure.
1173 */
1174VMMR3_INT_DECL(int) gimR3HvDisableSiefPage(PVMCPU pVCpu)
1175{
1176 LogRel(("GIM%u: HyperV: Disabled APIC-assist page\n", pVCpu->idCpu));
1177 /** @todo SIEF teardown. */
1178 return VINF_SUCCESS;
1179}
1180
1181
1182/**
1183 * Enables the Hyper-V TSC page.
1184 *
1185 * @returns VBox status code.
1186 * @param pVM The cross context VM structure.
1187 * @param GCPhysTscPage Where to map the TSC page.
1188 * @param fUseThisTscSeq Whether to set the TSC sequence number to the one
1189 * specified in @a uTscSeq.
1190 * @param uTscSeq The TSC sequence value to use. Ignored if
1191 * @a fUseThisTscSeq is false.
1192 */
1193VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq)
1194{
1195 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
1196 PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
1197 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
1198
1199 int rc;
1200 if (pRegion->fMapped)
1201 {
1202 /*
1203 * Is it already enabled at the given guest-address?
1204 */
1205 if (pRegion->GCPhysPage == GCPhysTscPage)
1206 return VINF_SUCCESS;
1207
1208 /*
1209 * If it's mapped at a different address, unmap the previous address.
1210 */
1211 rc = gimR3HvDisableTscPage(pVM);
1212 AssertRC(rc);
1213 }
1214
1215 /*
1216 * Map the TSC-page at the specified address.
1217 */
1218 Assert(!pRegion->fMapped);
1219
1220 /** @todo this is buggy when large pages are used due to a PGM limitation, see
1221 * @bugref{7532}. Instead of the overlay style mapping, we just
1222 * rewrite guest memory directly. */
1223#if 0
1224 rc = gimR3Mmio2Map(pVM, pRegion, GCPhysTscPage);
1225 if (RT_SUCCESS(rc))
1226 {
1227 Assert(pRegion->GCPhysPage == GCPhysTscPage);
1228
1229 /*
1230 * Update the TSC scale. Windows guests expect a non-zero TSC sequence, otherwise
1231 * they fallback to using the reference count MSR which is not ideal in terms of VM-exits.
1232 *
1233 * Also, Hyper-V normalizes the time in 10 MHz, see:
1234 * http://technet.microsoft.com/it-it/sysinternals/dn553408%28v=vs.110%29
1235 */
1236 PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pRegion->pvPageR3;
1237 Assert(pRefTsc);
1238
1239 PGIMHV pHv = &pVM->gim.s.u.Hv;
1240 uint64_t const u64TscKHz = pHv->cTscTicksPerSecond / UINT64_C(1000);
1241 uint32_t u32TscSeq = 1;
1242 if ( fUseThisTscSeq
1243 && uTscSeq < UINT32_C(0xfffffffe))
1244 u32TscSeq = uTscSeq + 1;
1245 pRefTsc->u32TscSequence = u32TscSeq;
1246 pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32;
1247 pRefTsc->i64TscOffset = 0;
1248
1249 LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n",
1250 GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence));
1251
1252 TMR3CpuTickParavirtEnable(pVM);
1253 return VINF_SUCCESS;
1254 }
1255 else
1256 LogRelFunc(("gimR3Mmio2Map failed. rc=%Rrc\n", rc));
1257 return VERR_GIM_OPERATION_FAILED;
1258#else
1259 AssertReturn(pRegion->cbRegion == GUEST_PAGE_SIZE, VERR_GIM_IPE_2);
1260 PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)RTMemAllocZ(GUEST_PAGE_SIZE);
1261 if (RT_UNLIKELY(!pRefTsc))
1262 {
1263 LogRelFunc(("Failed to alloc %#x bytes\n", GUEST_PAGE_SIZE));
1264 return VERR_NO_MEMORY;
1265 }
1266
1267 PGIMHV pHv = &pVM->gim.s.u.Hv;
1268 uint64_t const u64TscKHz = pHv->cTscTicksPerSecond / UINT64_C(1000);
1269 uint32_t u32TscSeq = 1;
1270 if ( fUseThisTscSeq
1271 && uTscSeq < UINT32_C(0xfffffffe))
1272 u32TscSeq = uTscSeq + 1;
1273 pRefTsc->u32TscSequence = u32TscSeq;
1274 pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32;
1275 pRefTsc->i64TscOffset = 0;
1276
1277 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysTscPage, pRefTsc, sizeof(*pRefTsc));
1278 if (RT_SUCCESS(rc))
1279 {
1280 LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n",
1281 GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence));
1282
1283 pRegion->GCPhysPage = GCPhysTscPage;
1284 pRegion->fMapped = true;
1285 TMR3CpuTickParavirtEnable(pVM);
1286 }
1287 else
1288 {
1289 LogRelFunc(("GIM: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", rc));
1290 rc = VERR_GIM_OPERATION_FAILED;
1291 }
1292 RTMemFree(pRefTsc);
1293 return rc;
1294#endif
1295}
1296
1297
1298/**
1299 * Enables the Hyper-V SIM page.
1300 *
1301 * @returns VBox status code.
1302 * @param pVCpu The cross context virtual CPU structure.
1303 * @param GCPhysSimPage Where to map the SIM page.
1304 */
1305VMMR3_INT_DECL(int) gimR3HvEnableSimPage(PVMCPU pVCpu, RTGCPHYS GCPhysSimPage)
1306{
1307 PVM pVM = pVCpu->CTX_SUFF(pVM);
1308 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
1309 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
1310
1311 /*
1312 * Map the SIMP page at the specified address.
1313 */
1314 /** @todo this is buggy when large pages are used due to a PGM limitation, see
1315 * @bugref{7532}. Instead of the overlay style mapping, we just
1316 * rewrite guest memory directly. */
1317 AssertCompile(sizeof(g_abRTZero64K) >= GUEST_PAGE_SIZE);
1318 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysSimPage, g_abRTZero64K, GUEST_PAGE_SIZE);
1319 if (RT_SUCCESS(rc))
1320 {
1321 /** @todo SIM setup. */
1322 LogRel(("GIM%u: HyperV: Enabled SIM page at %#RGp\n", pVCpu->idCpu, GCPhysSimPage));
1323 }
1324 else
1325 {
1326 LogRelFunc(("GIM%u: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", pVCpu->idCpu, rc));
1327 rc = VERR_GIM_OPERATION_FAILED;
1328 }
1329 return rc;
1330}
1331
1332
1333/**
1334 * Disables the Hyper-V SIM page.
1335 *
1336 * @returns VBox status code.
1337 * @param pVCpu The cross context virtual CPU structure.
1338 */
1339VMMR3_INT_DECL(int) gimR3HvDisableSimPage(PVMCPU pVCpu)
1340{
1341 LogRel(("GIM%u: HyperV: Disabled SIM page\n", pVCpu->idCpu));
1342 /** @todo SIM teardown. */
1343 return VINF_SUCCESS;
1344}
1345
1346
1347
1348/**
1349 * Disables the Hyper-V TSC page.
1350 *
1351 * @returns VBox status code.
1352 * @param pVM The cross context VM structure.
1353 */
1354VMMR3_INT_DECL(int) gimR3HvDisableTscPage(PVM pVM)
1355{
1356 PGIMHV pHv = &pVM->gim.s.u.Hv;
1357 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX];
1358 if (pRegion->fMapped)
1359 {
1360#if 0
1361 gimR3Mmio2Unmap(pVM, pRegion);
1362 Assert(!pRegion->fMapped);
1363#else
1364 pRegion->fMapped = false;
1365#endif
1366 LogRel(("GIM: HyperV: Disabled TSC page\n"));
1367
1368 TMR3CpuTickParavirtDisable(pVM);
1369 return VINF_SUCCESS;
1370 }
1371 return VERR_GIM_PVTSC_NOT_ENABLED;
1372}
1373
1374
1375/**
1376 * Disables the Hyper-V Hypercall page.
1377 *
1378 * @returns VBox status code.
1379 */
1380VMMR3_INT_DECL(int) gimR3HvDisableHypercallPage(PVM pVM)
1381{
1382 PGIMHV pHv = &pVM->gim.s.u.Hv;
1383 PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
1384 if (pRegion->fMapped)
1385 {
1386#if 0
1387 gimR3Mmio2Unmap(pVM, pRegion);
1388 Assert(!pRegion->fMapped);
1389#else
1390 pRegion->fMapped = false;
1391#endif
1392 LogRel(("GIM: HyperV: Disabled Hypercall-page\n"));
1393 return VINF_SUCCESS;
1394 }
1395 return VERR_GIM_HYPERCALLS_NOT_ENABLED;
1396}
1397
1398
1399/**
1400 * Enables the Hyper-V Hypercall page.
1401 *
1402 * @returns VBox status code.
1403 * @param pVM The cross context VM structure.
1404 * @param GCPhysHypercallPage Where to map the hypercall page.
1405 */
1406VMMR3_INT_DECL(int) gimR3HvEnableHypercallPage(PVM pVM, RTGCPHYS GCPhysHypercallPage)
1407{
1408 PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3;
1409 PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX];
1410 AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED);
1411
1412 if (pRegion->fMapped)
1413 {
1414 /*
1415 * Is it already enabled at the given guest-address?
1416 */
1417 if (pRegion->GCPhysPage == GCPhysHypercallPage)
1418 return VINF_SUCCESS;
1419
1420 /*
1421 * If it's mapped at a different address, unmap the previous address.
1422 */
1423 int rc2 = gimR3HvDisableHypercallPage(pVM);
1424 AssertRC(rc2);
1425 }
1426
1427 /*
1428 * Map the hypercall-page at the specified address.
1429 */
1430 Assert(!pRegion->fMapped);
1431
1432 /** @todo this is buggy when large pages are used due to a PGM limitation, see
1433 * @bugref{7532}. Instead of the overlay style mapping, we just
1434 * rewrite guest memory directly. */
1435#if 0
1436 int rc = gimR3Mmio2Map(pVM, pRegion, GCPhysHypercallPage);
1437 if (RT_SUCCESS(rc))
1438 {
1439 Assert(pRegion->GCPhysPage == GCPhysHypercallPage);
1440
1441 /*
1442 * Patch the hypercall-page.
1443 */
1444 size_t cbWritten = 0;
1445 rc = VMMPatchHypercall(pVM, pRegion->pvPageR3, GUEST_PAGE_SIZE, &cbWritten);
1446 if ( RT_SUCCESS(rc)
1447 && cbWritten < GUEST_PAGE_SIZE)
1448 {
1449 uint8_t *pbLast = (uint8_t *)pRegion->pvPageR3 + cbWritten;
1450 *pbLast = 0xc3; /* RET */
1451
1452 /*
1453 * Notify VMM that hypercalls are now enabled for all VCPUs.
1454 */
1455 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
1456 VMMHypercallsEnable(pVM->apCpusR3[idCpu]);
1457
1458 LogRel(("GIM: HyperV: Enabled hypercall page at %#RGp\n", GCPhysHypercallPage));
1459 return VINF_SUCCESS;
1460 }
1461 if (rc == VINF_SUCCESS)
1462 rc = VERR_GIM_OPERATION_FAILED;
1463 LogRel(("GIM: HyperV: VMMPatchHypercall failed. rc=%Rrc cbWritten=%u\n", rc, cbWritten));
1464
1465 gimR3Mmio2Unmap(pVM, pRegion);
1466 }
1467
1468 LogRel(("GIM: HyperV: gimR3Mmio2Map failed. rc=%Rrc\n", rc));
1469 return rc;
1470#else
1471 AssertReturn(pRegion->cbRegion == GUEST_PAGE_SIZE, VERR_GIM_IPE_3);
1472 void *pvHypercallPage = RTMemAllocZ(GUEST_PAGE_SIZE);
1473 if (RT_UNLIKELY(!pvHypercallPage))
1474 {
1475 LogRelFunc(("Failed to alloc %#x bytes\n", GUEST_PAGE_SIZE));
1476 return VERR_NO_MEMORY;
1477 }
1478
1479 /*
1480 * Patch the hypercall-page.
1481 */
1482 size_t cbHypercall = 0;
1483 int rc = GIMQueryHypercallOpcodeBytes(pVM, pvHypercallPage, GUEST_PAGE_SIZE, &cbHypercall, NULL /*puDisOpcode*/);
1484 if ( RT_SUCCESS(rc)
1485 && cbHypercall < GUEST_PAGE_SIZE)
1486 {
1487 uint8_t *pbLast = (uint8_t *)pvHypercallPage + cbHypercall;
1488 *pbLast = 0xc3; /* RET */
1489
1490 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysHypercallPage, pvHypercallPage, GUEST_PAGE_SIZE);
1491 if (RT_SUCCESS(rc))
1492 {
1493 pRegion->GCPhysPage = GCPhysHypercallPage;
1494 pRegion->fMapped = true;
1495 LogRel(("GIM: HyperV: Enabled hypercall page at %#RGp\n", GCPhysHypercallPage));
1496 }
1497 else
1498 LogRel(("GIM: HyperV: PGMPhysSimpleWriteGCPhys failed during hypercall page setup. rc=%Rrc\n", rc));
1499 }
1500 else
1501 {
1502 if (rc == VINF_SUCCESS)
1503 rc = VERR_GIM_OPERATION_FAILED;
1504 LogRel(("GIM: HyperV: VMMPatchHypercall failed. rc=%Rrc cbHypercall=%u\n", rc, cbHypercall));
1505 }
1506
1507 RTMemFree(pvHypercallPage);
1508 return rc;
1509#endif
1510}
1511
1512
1513/**
1514 * Initializes Hyper-V guest hypercall support.
1515 *
1516 * @returns VBox status code.
1517 * @param pVM The cross context VM structure.
1518 */
1519static int gimR3HvInitHypercallSupport(PVM pVM)
1520{
1521 PGIMHV pHv = &pVM->gim.s.u.Hv;
1522 pHv->pbHypercallIn = (uint8_t *)RTMemAllocZ(GIM_HV_PAGE_SIZE);
1523 if (RT_LIKELY(pHv->pbHypercallIn))
1524 {
1525 pHv->pbHypercallOut = (uint8_t *)RTMemAllocZ(GIM_HV_PAGE_SIZE);
1526 if (RT_LIKELY(pHv->pbHypercallOut))
1527 return VINF_SUCCESS;
1528 RTMemFree(pHv->pbHypercallIn);
1529 }
1530 return VERR_NO_MEMORY;
1531}
1532
1533
1534/**
1535 * Terminates Hyper-V guest hypercall support.
1536 *
1537 * @param pVM The cross context VM structure.
1538 */
1539static void gimR3HvTermHypercallSupport(PVM pVM)
1540{
1541 PGIMHV pHv = &pVM->gim.s.u.Hv;
1542 RTMemFree(pHv->pbHypercallIn);
1543 pHv->pbHypercallIn = NULL;
1544
1545 RTMemFree(pHv->pbHypercallOut);
1546 pHv->pbHypercallOut = NULL;
1547}
1548
1549
1550/**
1551 * Initializes Hyper-V guest debug support.
1552 *
1553 * @returns VBox status code.
1554 * @param pVM The cross context VM structure.
1555 */
1556static int gimR3HvInitDebugSupport(PVM pVM)
1557{
1558 PGIMHV pHv = &pVM->gim.s.u.Hv;
1559 if ( (pHv->uPartFlags & GIM_HV_PART_FLAGS_DEBUGGING)
1560 || pHv->fIsInterfaceVs)
1561 {
1562 pHv->fDbgEnabled = true;
1563 pHv->pvDbgBuffer = RTMemAllocZ(GIM_HV_PAGE_SIZE);
1564 if (!pHv->pvDbgBuffer)
1565 return VERR_NO_MEMORY;
1566 }
1567 return VINF_SUCCESS;
1568}
1569
1570
1571/**
1572 * Terminates Hyper-V guest debug support.
1573 *
1574 * @param pVM The cross context VM structure.
1575 */
1576static void gimR3HvTermDebugSupport(PVM pVM)
1577{
1578 PGIMHV pHv = &pVM->gim.s.u.Hv;
1579 if (pHv->pvDbgBuffer)
1580 {
1581 RTMemFree(pHv->pvDbgBuffer);
1582 pHv->pvDbgBuffer = NULL;
1583 }
1584}
1585
1586
1587/**
1588 * Reads data from a debugger connection, asynchronous.
1589 *
1590 * @returns VBox status code.
1591 * @param pVM The cross context VM structure.
1592 * @param pvBuf Where to read the data.
1593 * @param cbBuf Size of the read buffer @a pvBuf, must be >= @a cbRead.
1594 * @param cbRead Number of bytes to read.
1595 * @param pcbRead Where to store how many bytes were really read.
1596 * @param cMsTimeout Timeout of the read operation in milliseconds.
1597 * @param fUdpPkt Whether the debug data returned in @a pvBuf needs to be
1598 * encapsulated in a UDP frame.
1599 *
1600 * @thread EMT.
1601 */
1602VMMR3_INT_DECL(int) gimR3HvDebugRead(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t cbRead, uint32_t *pcbRead,
1603 uint32_t cMsTimeout, bool fUdpPkt)
1604{
1605 NOREF(cMsTimeout); /** @todo implement timeout. */
1606 AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
1607 AssertReturn(cbBuf >= cbRead, VERR_INVALID_PARAMETER);
1608
1609 int rc;
1610 if (!fUdpPkt)
1611 {
1612 /*
1613 * Read the raw debug data.
1614 */
1615 size_t cbReallyRead = cbRead;
1616 rc = gimR3DebugRead(pVM, pvBuf, &cbReallyRead, gimR3HvDebugBufReadCompleted);
1617 *pcbRead = (uint32_t)cbReallyRead;
1618 }
1619 else
1620 {
1621 /*
1622 * Guest requires UDP encapsulated frames.
1623 */
1624 PGIMHV pHv = &pVM->gim.s.u.Hv;
1625 rc = VERR_GIM_IPE_1;
1626 switch (pHv->enmDbgReply)
1627 {
1628 case GIMHVDEBUGREPLY_UDP:
1629 {
1630 size_t cbReallyRead = cbRead;
1631 rc = gimR3DebugRead(pVM, pvBuf, &cbReallyRead, gimR3HvDebugBufReadCompleted);
1632 if ( RT_SUCCESS(rc)
1633 && cbReallyRead > 0)
1634 {
1635 uint8_t abFrame[sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + sizeof(RTNETUDP)];
1636 if (cbReallyRead + sizeof(abFrame) <= cbBuf)
1637 {
1638 /*
1639 * Windows guests pumps ethernet frames over the Hyper-V debug connection as
1640 * explained in gimR3HvHypercallPostDebugData(). Here, we reconstruct the packet
1641 * with the guest's self-chosen IP ARP address we saved in pHv->DbgGuestAddr.
1642 *
1643 * Note! We really need to pass the minimum IPv4 header length. The Windows 10 guest
1644 * is -not- happy if we include the IPv4 options field, i.e. using sizeof(RTNETIPV4)
1645 * instead of RTNETIPV4_MIN_LEN.
1646 */
1647 RT_ZERO(abFrame);
1648 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
1649 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
1650 PRTNETUDP pUdpHdr = (PRTNETUDP) ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
1651
1652 /* Ethernet */
1653 pEthHdr->EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
1654 /* IPv4 */
1655 pIpHdr->ip_v = 4;
1656 pIpHdr->ip_hl = RTNETIPV4_MIN_LEN / sizeof(uint32_t);
1657 pIpHdr->ip_tos = 0;
1658 pIpHdr->ip_len = RT_H2N_U16((uint16_t)cbReallyRead + sizeof(RTNETUDP) + RTNETIPV4_MIN_LEN);
1659 pIpHdr->ip_id = 0;
1660 pIpHdr->ip_off = 0;
1661 pIpHdr->ip_ttl = 255;
1662 pIpHdr->ip_p = RTNETIPV4_PROT_UDP;
1663 pIpHdr->ip_sum = 0;
1664 pIpHdr->ip_src.u = 0;
1665 pIpHdr->ip_dst.u = pHv->DbgGuestIp4Addr.u;
1666 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
1667 /* UDP */
1668 pUdpHdr->uh_dport = pHv->uUdpGuestSrcPort;
1669 pUdpHdr->uh_sport = pHv->uUdpGuestDstPort;
1670 pUdpHdr->uh_ulen = RT_H2N_U16_C((uint16_t)cbReallyRead + sizeof(*pUdpHdr));
1671
1672 /* Make room by moving the payload and prepending the headers. */
1673 uint8_t *pbData = (uint8_t *)pvBuf;
1674 memmove(pbData + sizeof(abFrame), pbData, cbReallyRead);
1675 memcpy(pbData, &abFrame[0], sizeof(abFrame));
1676
1677 /* Update the adjusted sizes. */
1678 cbReallyRead += sizeof(abFrame);
1679 }
1680 else
1681 rc = VERR_BUFFER_UNDERFLOW;
1682 }
1683 *pcbRead = (uint32_t)cbReallyRead;
1684 break;
1685 }
1686
1687 case GIMHVDEBUGREPLY_ARP_REPLY:
1688 {
1689 uint32_t const cbArpReplyPkt = sizeof(g_abArpReply);
1690 if (cbBuf >= cbArpReplyPkt)
1691 {
1692 memcpy(pvBuf, g_abArpReply, cbArpReplyPkt);
1693 rc = VINF_SUCCESS;
1694 *pcbRead = cbArpReplyPkt;
1695 pHv->enmDbgReply = GIMHVDEBUGREPLY_ARP_REPLY_SENT;
1696 }
1697 else
1698 {
1699 rc = VERR_BUFFER_UNDERFLOW;
1700 *pcbRead = 0;
1701 }
1702 break;
1703 }
1704
1705 case GIMHVDEBUGREPLY_DHCP_OFFER:
1706 {
1707 uint32_t const cbDhcpOfferPkt = sizeof(g_abDhcpOffer);
1708 if (cbBuf >= cbDhcpOfferPkt)
1709 {
1710 memcpy(pvBuf, g_abDhcpOffer, cbDhcpOfferPkt);
1711 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pvBuf;
1712 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
1713 PRTNETUDP pUdpHdr = (PRTNETUDP) ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
1714 PRTNETBOOTP pBootpHdr = (PRTNETBOOTP) (pUdpHdr + 1);
1715 pBootpHdr->bp_xid = pHv->uDbgBootpXId;
1716
1717 rc = VINF_SUCCESS;
1718 *pcbRead = cbDhcpOfferPkt;
1719 pHv->enmDbgReply = GIMHVDEBUGREPLY_DHCP_OFFER_SENT;
1720 LogRel(("GIM: HyperV: Debug DHCP offered IP address %RTnaipv4, transaction Id %#x\n", pBootpHdr->bp_yiaddr,
1721 RT_N2H_U32(pHv->uDbgBootpXId)));
1722 }
1723 else
1724 {
1725 rc = VERR_BUFFER_UNDERFLOW;
1726 *pcbRead = 0;
1727 }
1728 break;
1729 }
1730
1731 case GIMHVDEBUGREPLY_DHCP_ACK:
1732 {
1733 uint32_t const cbDhcpAckPkt = sizeof(g_abDhcpAck);
1734 if (cbBuf >= cbDhcpAckPkt)
1735 {
1736 memcpy(pvBuf, g_abDhcpAck, cbDhcpAckPkt);
1737 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pvBuf;
1738 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
1739 PRTNETUDP pUdpHdr = (PRTNETUDP) ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
1740 PRTNETBOOTP pBootpHdr = (PRTNETBOOTP) (pUdpHdr + 1);
1741 pBootpHdr->bp_xid = pHv->uDbgBootpXId;
1742
1743 rc = VINF_SUCCESS;
1744 *pcbRead = cbDhcpAckPkt;
1745 pHv->enmDbgReply = GIMHVDEBUGREPLY_DHCP_ACK_SENT;
1746 LogRel(("GIM: HyperV: Debug DHCP acknowledged IP address %RTnaipv4, transaction Id %#x\n",
1747 pBootpHdr->bp_yiaddr, RT_N2H_U32(pHv->uDbgBootpXId)));
1748 }
1749 else
1750 {
1751 rc = VERR_BUFFER_UNDERFLOW;
1752 *pcbRead = 0;
1753 }
1754 break;
1755 }
1756
1757 case GIMHVDEBUGREPLY_ARP_REPLY_SENT:
1758 case GIMHVDEBUGREPLY_DHCP_OFFER_SENT:
1759 case GIMHVDEBUGREPLY_DHCP_ACK_SENT:
1760 {
1761 rc = VINF_SUCCESS;
1762 *pcbRead = 0;
1763 break;
1764 }
1765
1766 default:
1767 {
1768 AssertMsgFailed(("GIM: HyperV: Invalid/unimplemented debug reply type %u\n", pHv->enmDbgReply));
1769 rc = VERR_INTERNAL_ERROR_2;
1770 }
1771 }
1772 Assert(rc != VERR_GIM_IPE_1);
1773
1774#ifdef DEBUG_ramshankar
1775 if ( rc == VINF_SUCCESS
1776 && *pcbRead > 0)
1777 {
1778 RTSOCKET hSocket;
1779 int rc2 = RTUdpCreateClientSocket("localhost", 52000, NULL, &hSocket);
1780 if (RT_SUCCESS(rc2))
1781 {
1782 size_t cbTmpWrite = *pcbRead;
1783 RTSocketWriteNB(hSocket, pvBuf, *pcbRead, &cbTmpWrite); NOREF(cbTmpWrite);
1784 RTSocketClose(hSocket);
1785 }
1786 }
1787#endif
1788 }
1789
1790 return rc;
1791}
1792
1793
1794/**
1795 * Writes data to the debugger connection, asynchronous.
1796 *
1797 * @returns VBox status code.
1798 * @param pVM The cross context VM structure.
1799 * @param pvData Pointer to the data to be written.
1800 * @param cbWrite Size of the write buffer @a pvData.
1801 * @param pcbWritten Where to store the number of bytes written.
1802 * @param fUdpPkt Whether the debug data in @a pvData is encapsulated in a
1803 * UDP frame.
1804 *
1805 * @thread EMT.
1806 */
1807VMMR3_INT_DECL(int) gimR3HvDebugWrite(PVM pVM, void *pvData, uint32_t cbWrite, uint32_t *pcbWritten, bool fUdpPkt)
1808{
1809 Assert(cbWrite > 0);
1810
1811 PGIMHV pHv = &pVM->gim.s.u.Hv;
1812 bool fIgnorePkt = false;
1813 uint8_t *pbData = (uint8_t *)pvData;
1814 if (fUdpPkt)
1815 {
1816#ifdef DEBUG_ramshankar
1817 RTSOCKET hSocket;
1818 int rc2 = RTUdpCreateClientSocket("localhost", 52000, NULL, &hSocket);
1819 if (RT_SUCCESS(rc2))
1820 {
1821 size_t cbTmpWrite = cbWrite;
1822 RTSocketWriteNB(hSocket, pbData, cbWrite, &cbTmpWrite); NOREF(cbTmpWrite);
1823 RTSocketClose(hSocket);
1824 }
1825#endif
1826 /*
1827 * Windows guests sends us ethernet frames over the Hyper-V debug connection.
1828 * It sends DHCP/ARP queries with zero'd out MAC addresses and requires fudging up the
1829 * packets somewhere.
1830 *
1831 * The Microsoft WinDbg debugger talks UDP and thus only expects the actual debug
1832 * protocol payload.
1833 *
1834 * If the guest is configured with the "nodhcp" option it sends ARP queries with
1835 * a self-chosen IP and after a couple of attempts of receiving no replies, the guest
1836 * picks its own IP address. After this, the guest starts sending the UDP packets
1837 * we require. We thus ignore the initial ARP packets until the guest eventually
1838 * starts talking UDP. Then we can finally feed the UDP payload over the debug
1839 * connection.
1840 *
1841 * When 'kdvm.dll' is the debug transport in the guest (Windows 7), it doesn't bother
1842 * with this DHCP/ARP phase. It starts sending debug data in a UDP frame right away.
1843 */
1844 if (cbWrite > sizeof(RTNETETHERHDR))
1845 {
1846 PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pbData;
1847 if (pEtherHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
1848 {
1849 if (cbWrite > sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN)
1850 {
1851 size_t const cbMaxIpHdr = cbWrite - sizeof(RTNETETHERHDR) - sizeof(RTNETUDP) - 1;
1852 size_t const cbMaxIpPkt = cbWrite - sizeof(RTNETETHERHDR);
1853 PCRTNETIPV4 pIp4Hdr = (PCRTNETIPV4)(pbData + sizeof(RTNETETHERHDR));
1854 bool const fValidIp4 = RTNetIPv4IsHdrValid(pIp4Hdr, cbMaxIpHdr, cbMaxIpPkt, false /*fChecksum*/);
1855 if ( fValidIp4
1856 && pIp4Hdr->ip_p == RTNETIPV4_PROT_UDP)
1857 {
1858 uint32_t const cbIpHdr = pIp4Hdr->ip_hl * 4;
1859 uint32_t const cbMaxUdpPkt = cbWrite - sizeof(RTNETETHERHDR) - cbIpHdr;
1860 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t *)pIp4Hdr + cbIpHdr);
1861 if ( pUdpHdr->uh_ulen > RT_H2N_U16(sizeof(RTNETUDP))
1862 && pUdpHdr->uh_ulen <= RT_H2N_U16((uint16_t)cbMaxUdpPkt))
1863 {
1864 /*
1865 * Check for DHCP.
1866 */
1867 bool fBuggyPkt = false;
1868 size_t const cbUdpPkt = cbMaxIpPkt - cbIpHdr;
1869 if ( pUdpHdr->uh_dport == RT_N2H_U16_C(RTNETIPV4_PORT_BOOTPS)
1870 && pUdpHdr->uh_sport == RT_N2H_U16_C(RTNETIPV4_PORT_BOOTPC))
1871 {
1872 PCRTNETBOOTP pDhcpPkt = (PCRTNETBOOTP)(pUdpHdr + 1);
1873 uint8_t bMsgType;
1874 if ( cbMaxIpPkt >= cbIpHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
1875 && RTNetIPv4IsDHCPValid(pUdpHdr, pDhcpPkt, cbUdpPkt - sizeof(*pUdpHdr), &bMsgType))
1876 {
1877 switch (bMsgType)
1878 {
1879 case RTNET_DHCP_MT_DISCOVER:
1880 pHv->enmDbgReply = GIMHVDEBUGREPLY_DHCP_OFFER;
1881 pHv->uDbgBootpXId = pDhcpPkt->bp_xid;
1882 break;
1883 case RTNET_DHCP_MT_REQUEST:
1884 pHv->enmDbgReply = GIMHVDEBUGREPLY_DHCP_ACK;
1885 pHv->uDbgBootpXId = pDhcpPkt->bp_xid;
1886 break;
1887 default:
1888 LogRelMax(5, ("GIM: HyperV: Debug DHCP MsgType %#x not implemented! Packet dropped\n",
1889 bMsgType));
1890 break;
1891 }
1892 fIgnorePkt = true;
1893 }
1894 else if ( pIp4Hdr->ip_src.u == GIMHV_DEBUGCLIENT_IPV4
1895 && pIp4Hdr->ip_dst.u == 0)
1896 {
1897 /*
1898 * Windows 8.1 seems to be sending malformed BOOTP packets at the final stage of the
1899 * debugger sequence. It appears that a previously sent DHCP request buffer wasn't cleared
1900 * in the guest and they re-use it instead of sending a zero destination+source port packet
1901 * as expected below.
1902 *
1903 * We workaround Microsoft's bug here, or at least, I'm classifying it as a bug to
1904 * preserve my own sanity, see @bugref{8006#c54}.
1905 */
1906 fBuggyPkt = true;
1907 }
1908 }
1909
1910 if ( ( !pUdpHdr->uh_dport
1911 && !pUdpHdr->uh_sport)
1912 || fBuggyPkt)
1913 {
1914 /*
1915 * Extract the UDP payload and pass it to the debugger and record the guest IP address.
1916 *
1917 * Hyper-V sends UDP debugger packets with source and destination port as 0 except in the
1918 * aforementioned buggy case. The buggy packet case requires us to remember the ports and
1919 * reply to them, otherwise the guest won't receive the replies we sent with port 0.
1920 */
1921 uint32_t const cbFrameHdr = sizeof(RTNETETHERHDR) + cbIpHdr + sizeof(RTNETUDP);
1922 pbData += cbFrameHdr;
1923 cbWrite -= cbFrameHdr;
1924 pHv->DbgGuestIp4Addr.u = pIp4Hdr->ip_src.u;
1925 pHv->uUdpGuestDstPort = pUdpHdr->uh_dport;
1926 pHv->uUdpGuestSrcPort = pUdpHdr->uh_sport;
1927 pHv->enmDbgReply = GIMHVDEBUGREPLY_UDP;
1928 }
1929 else
1930 {
1931 LogFlow(("GIM: HyperV: Ignoring UDP packet SourcePort=%u DstPort=%u\n", pUdpHdr->uh_sport,
1932 pUdpHdr->uh_dport));
1933 fIgnorePkt = true;
1934 }
1935 }
1936 else
1937 {
1938 LogFlow(("GIM: HyperV: Ignoring malformed UDP packet. cbMaxUdpPkt=%u UdpPkt.len=%u\n", cbMaxUdpPkt,
1939 RT_N2H_U16(pUdpHdr->uh_ulen)));
1940 fIgnorePkt = true;
1941 }
1942 }
1943 else
1944 {
1945 LogFlow(("GIM: HyperV: Ignoring non-IP / non-UDP packet. fValidIp4=%RTbool Proto=%u\n", fValidIp4,
1946 pIp4Hdr->ip_p));
1947 fIgnorePkt = true;
1948 }
1949 }
1950 else
1951 {
1952 LogFlow(("GIM: HyperV: Ignoring IPv4 packet; too short to be valid UDP. cbWrite=%u\n", cbWrite));
1953 fIgnorePkt = true;
1954 }
1955 }
1956 else if (pEtherHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_ARP))
1957 {
1958 /*
1959 * Check for targetted ARP query.
1960 */
1961 PCRTNETARPHDR pArpHdr = (PCRTNETARPHDR)(pbData + sizeof(RTNETETHERHDR));
1962 if ( pArpHdr->ar_hlen == sizeof(RTMAC)
1963 && pArpHdr->ar_plen == sizeof(RTNETADDRIPV4)
1964 && pArpHdr->ar_htype == RT_H2N_U16(RTNET_ARP_ETHER)
1965 && pArpHdr->ar_ptype == RT_H2N_U16(RTNET_ETHERTYPE_IPV4))
1966 {
1967 uint16_t uArpOp = pArpHdr->ar_oper;
1968 if (uArpOp == RT_H2N_U16_C(RTNET_ARPOP_REQUEST))
1969 {
1970 PCRTNETARPIPV4 pArpPkt = (PCRTNETARPIPV4)pArpHdr;
1971 bool fGratuitous = pArpPkt->ar_spa.u == pArpPkt->ar_tpa.u;
1972 if ( !fGratuitous
1973 && pArpPkt->ar_spa.u == GIMHV_DEBUGCLIENT_IPV4
1974 && pArpPkt->ar_tpa.u == GIMHV_DEBUGSERVER_IPV4)
1975 {
1976 pHv->enmDbgReply = GIMHVDEBUGREPLY_ARP_REPLY;
1977 }
1978 }
1979 }
1980 fIgnorePkt = true;
1981 }
1982 else
1983 {
1984 LogFlow(("GIM: HyperV: Ignoring non-IP packet. Ethertype=%#x\n", RT_N2H_U16(pEtherHdr->EtherType)));
1985 fIgnorePkt = true;
1986 }
1987 }
1988 }
1989
1990 if (!fIgnorePkt)
1991 {
1992 AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
1993 size_t cbWriteBuf = cbWrite;
1994 int rc = gimR3DebugWrite(pVM, pbData, &cbWriteBuf);
1995 if ( RT_SUCCESS(rc)
1996 && cbWriteBuf == cbWrite)
1997 *pcbWritten = (uint32_t)cbWriteBuf;
1998 else
1999 *pcbWritten = 0;
2000 }
2001 else
2002 *pcbWritten = cbWrite;
2003
2004 return VINF_SUCCESS;
2005}
2006
2007
2008/**
2009 * Performs the HvPostDebugData hypercall.
2010 *
2011 * @returns VBox status code.
2012 * @param pVM The cross context VM structure.
2013 * @param prcHv Where to store the result of the hypercall operation.
2014 *
2015 * @thread EMT.
2016 */
2017VMMR3_INT_DECL(int) gimR3HvHypercallPostDebugData(PVM pVM, int *prcHv)
2018{
2019 AssertPtr(pVM);
2020 AssertPtr(prcHv);
2021 PGIMHV pHv = &pVM->gim.s.u.Hv;
2022 int rcHv = GIM_HV_STATUS_OPERATION_DENIED;
2023
2024 /*
2025 * Grab the parameters.
2026 */
2027 PGIMHVDEBUGPOSTIN pIn = (PGIMHVDEBUGPOSTIN)pHv->pbHypercallIn;
2028 AssertPtrReturn(pIn, VERR_GIM_IPE_1);
2029 uint32_t cbWrite = pIn->cbWrite;
2030 uint32_t fFlags = pIn->fFlags;
2031 uint8_t *pbData = ((uint8_t *)pIn) + sizeof(PGIMHVDEBUGPOSTIN);
2032
2033 PGIMHVDEBUGPOSTOUT pOut = (PGIMHVDEBUGPOSTOUT)pHv->pbHypercallOut;
2034
2035 /*
2036 * Perform the hypercall.
2037 */
2038#if 0
2039 /* Currently disabled as Windows 10 guest passes us undocumented flags. */
2040 if (fFlags & ~GIM_HV_DEBUG_POST_OPTIONS_MASK))
2041 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
2042#else
2043 RT_NOREF1(fFlags);
2044#endif
2045 if (cbWrite > GIM_HV_DEBUG_MAX_DATA_SIZE)
2046 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
2047 else if (!cbWrite)
2048 {
2049 rcHv = GIM_HV_STATUS_SUCCESS;
2050 pOut->cbPending = 0;
2051 }
2052 else if (cbWrite > 0)
2053 {
2054 uint32_t cbWritten = 0;
2055 int rc2 = gimR3HvDebugWrite(pVM, pbData, cbWrite, &cbWritten, pHv->fIsVendorMsHv /*fUdpPkt*/);
2056 if ( RT_SUCCESS(rc2)
2057 && cbWritten == cbWrite)
2058 {
2059 pOut->cbPending = 0;
2060 rcHv = GIM_HV_STATUS_SUCCESS;
2061 }
2062 else
2063 rcHv = GIM_HV_STATUS_INSUFFICIENT_BUFFER;
2064 }
2065
2066 /*
2067 * Update the guest memory with result.
2068 */
2069 int rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut, sizeof(GIMHVDEBUGPOSTOUT));
2070 if (RT_FAILURE(rc))
2071 {
2072 LogRelMax(10, ("GIM: HyperV: HvPostDebugData failed to update guest memory. rc=%Rrc\n", rc));
2073 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
2074 }
2075 else
2076 Assert(rc == VINF_SUCCESS);
2077
2078 *prcHv = rcHv;
2079 return rc;
2080}
2081
2082
2083/**
2084 * Performs the HvRetrieveDebugData hypercall.
2085 *
2086 * @returns VBox status code.
2087 * @param pVM The cross context VM structure.
2088 * @param prcHv Where to store the result of the hypercall operation.
2089 *
2090 * @thread EMT.
2091 */
2092VMMR3_INT_DECL(int) gimR3HvHypercallRetrieveDebugData(PVM pVM, int *prcHv)
2093{
2094 AssertPtr(pVM);
2095 AssertPtr(prcHv);
2096 PGIMHV pHv = &pVM->gim.s.u.Hv;
2097 int rcHv = GIM_HV_STATUS_OPERATION_DENIED;
2098
2099 /*
2100 * Grab the parameters.
2101 */
2102 PGIMHVDEBUGRETRIEVEIN pIn = (PGIMHVDEBUGRETRIEVEIN)pHv->pbHypercallIn;
2103 AssertPtrReturn(pIn, VERR_GIM_IPE_1);
2104 uint32_t cbRead = pIn->cbRead;
2105 uint32_t fFlags = pIn->fFlags;
2106 uint64_t uTimeout = pIn->u64Timeout;
2107 uint32_t cMsTimeout = (fFlags & GIM_HV_DEBUG_RETREIVE_LOOP) ? (uTimeout * 100) / RT_NS_1MS_64 : 0;
2108
2109 PGIMHVDEBUGRETRIEVEOUT pOut = (PGIMHVDEBUGRETRIEVEOUT)pHv->pbHypercallOut;
2110 AssertPtrReturn(pOut, VERR_GIM_IPE_2);
2111 uint32_t *pcbReallyRead = &pOut->cbRead;
2112 uint32_t *pcbRemainingRead = &pOut->cbRemaining;
2113 void *pvData = ((uint8_t *)pOut) + sizeof(GIMHVDEBUGRETRIEVEOUT);
2114
2115 /*
2116 * Perform the hypercall.
2117 */
2118 *pcbReallyRead = 0;
2119 *pcbRemainingRead = cbRead;
2120#if 0
2121 /* Currently disabled as Windows 10 guest passes us undocumented flags. */
2122 if (fFlags & ~GIM_HV_DEBUG_RETREIVE_OPTIONS_MASK)
2123 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
2124#endif
2125 if (cbRead > GIM_HV_DEBUG_MAX_DATA_SIZE)
2126 rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
2127 else if (fFlags & GIM_HV_DEBUG_RETREIVE_TEST_ACTIVITY)
2128 rcHv = GIM_HV_STATUS_SUCCESS; /** @todo implement this. */
2129 else if (!cbRead)
2130 rcHv = GIM_HV_STATUS_SUCCESS;
2131 else if (cbRead > 0)
2132 {
2133 int rc2 = gimR3HvDebugRead(pVM, pvData, GIM_HV_PAGE_SIZE, cbRead, pcbReallyRead, cMsTimeout,
2134 pHv->fIsVendorMsHv /*fUdpPkt*/);
2135 Assert(*pcbReallyRead <= cbRead);
2136 if ( RT_SUCCESS(rc2)
2137 && *pcbReallyRead > 0)
2138 {
2139 *pcbRemainingRead = cbRead - *pcbReallyRead;
2140 rcHv = GIM_HV_STATUS_SUCCESS;
2141 }
2142 else
2143 rcHv = GIM_HV_STATUS_NO_DATA;
2144 }
2145
2146 /*
2147 * Update the guest memory with result.
2148 */
2149 int rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut,
2150 sizeof(GIMHVDEBUGRETRIEVEOUT) + *pcbReallyRead);
2151 if (RT_FAILURE(rc))
2152 {
2153 LogRelMax(10, ("GIM: HyperV: HvRetrieveDebugData failed to update guest memory. rc=%Rrc\n", rc));
2154 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
2155 }
2156 else
2157 Assert(rc == VINF_SUCCESS);
2158
2159 *prcHv = rcHv;
2160 return rc;
2161}
2162
2163
2164/**
2165 * Performs the HvExtCallQueryCapabilities extended hypercall.
2166 *
2167 * @returns VBox status code.
2168 * @param pVM The cross context VM structure.
2169 * @param prcHv Where to store the result of the hypercall operation.
2170 *
2171 * @thread EMT.
2172 */
2173VMMR3_INT_DECL(int) gimR3HvHypercallExtQueryCap(PVM pVM, int *prcHv)
2174{
2175 AssertPtr(pVM);
2176 AssertPtr(prcHv);
2177 PGIMHV pHv = &pVM->gim.s.u.Hv;
2178
2179 /*
2180 * Grab the parameters.
2181 */
2182 PGIMHVEXTQUERYCAP pOut = (PGIMHVEXTQUERYCAP)pHv->pbHypercallOut;
2183
2184 /*
2185 * Perform the hypercall.
2186 */
2187 pOut->fCapabilities = GIM_HV_EXT_HYPERCALL_CAP_ZERO_MEM;
2188
2189 /*
2190 * Update the guest memory with result.
2191 */
2192 int rcHv;
2193 int rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut, sizeof(GIMHVEXTQUERYCAP));
2194 if (RT_SUCCESS(rc))
2195 {
2196 rcHv = GIM_HV_STATUS_SUCCESS;
2197 LogRel(("GIM: HyperV: Queried extended hypercall capabilities %#RX64 at %#RGp\n", pOut->fCapabilities,
2198 pHv->GCPhysHypercallOut));
2199 }
2200 else
2201 {
2202 rcHv = GIM_HV_STATUS_OPERATION_DENIED;
2203 LogRelMax(10, ("GIM: HyperV: HvHypercallExtQueryCap failed to update guest memory. rc=%Rrc\n", rc));
2204 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
2205 }
2206
2207 *prcHv = rcHv;
2208 return rc;
2209}
2210
2211
2212/**
2213 * Performs the HvExtCallGetBootZeroedMemory extended hypercall.
2214 *
2215 * @returns VBox status code.
2216 * @param pVM The cross context VM structure.
2217 * @param prcHv Where to store the result of the hypercall operation.
2218 *
2219 * @thread EMT.
2220 */
2221VMMR3_INT_DECL(int) gimR3HvHypercallExtGetBootZeroedMem(PVM pVM, int *prcHv)
2222{
2223 AssertPtr(pVM);
2224 AssertPtr(prcHv);
2225 PGIMHV pHv = &pVM->gim.s.u.Hv;
2226
2227 /*
2228 * Grab the parameters.
2229 */
2230 PGIMHVEXTGETBOOTZEROMEM pOut = (PGIMHVEXTGETBOOTZEROMEM)pHv->pbHypercallOut;
2231
2232 /*
2233 * Perform the hypercall.
2234 */
2235 AssertCompileMembersSameSizeAndOffset(PGMPHYSRANGES, cRanges, GIMHVEXTGETBOOTZEROMEM, cRanges);
2236 AssertCompileMembersAtSameOffset(PGMPHYSRANGES, aRanges, GIMHVEXTGETBOOTZEROMEM, aRanges);
2237 AssertCompileMembersSameSizeAndOffset(PGMPHYSRANGE, GCPhysStart, GIMHVEXTMEMRANGE, GCPhysStart);
2238 AssertCompileMembersSameSizeAndOffset(PGMPHYSRANGE, cPages, GIMHVEXTMEMRANGE, cPages);
2239 AssertCompile(RT_ELEMENTS(pOut->aRanges) == GIM_HV_MAX_BOOT_ZEROED_MEM_RANGES);
2240
2241 int rcHv;
2242 int rc = PGMR3PhysGetRamBootZeroedRanges(pVM, (PPGMPHYSRANGES)pOut, RT_ELEMENTS(pOut->aRanges));
2243 if ( RT_SUCCESS(rc)
2244 || rc == VERR_BUFFER_OVERFLOW)
2245 {
2246 /*
2247 * Update the guest memory with result.
2248 */
2249 rc = PGMPhysSimpleWriteGCPhys(pVM, pHv->GCPhysHypercallOut, pHv->pbHypercallOut, sizeof(GIMHVEXTGETBOOTZEROMEM));
2250 if (RT_SUCCESS(rc))
2251 {
2252 LogRel(("GIM: HyperV: Queried boot zeroed guest memory as %u ranges\n", pOut->cRanges));
2253 for (uint32_t i = 0; i < pOut->cRanges; i++)
2254 LogRel(("GIM: HyperV: RAM range [%u] from %#RGp to %#RGp (%u pages, %' Rhcb)\n", i, pOut->aRanges[i].GCPhysStart,
2255 pOut->aRanges[i].GCPhysStart + pOut->aRanges[i].cPages * GUEST_PAGE_SIZE - 1, pOut->aRanges[i].cPages,
2256 pOut->aRanges[i].cPages * GUEST_PAGE_SIZE));
2257 rcHv = GIM_HV_STATUS_SUCCESS;
2258 }
2259 else
2260 {
2261 LogRelMax(10, ("GIM: HyperV: HvHypercallExtGetBootZeroedMem hypercall failed to update guest memory. rc=%Rrc\n", rc));
2262 rcHv = GIM_HV_STATUS_OPERATION_DENIED;
2263 rc = VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED;
2264 }
2265 }
2266 else
2267 {
2268 LogRelMax(10, ("GIM: HyperV: HvHypercallExtGetBootZeroedMem failed. rc=%Rrc\n", rc));
2269 rcHv = GIM_HV_STATUS_OPERATION_DENIED;
2270 rc = VERR_GIM_HYPERCALL_FAILED;
2271 }
2272
2273 *prcHv = rcHv;
2274 return rc;
2275}
2276
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