[50953] | 1 | /* $Id: GIM.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * GIM - Guest Interface Manager.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[82968] | 7 | * Copyright (C) 2014-2020 Oracle Corporation
|
---|
[50953] | 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 | /** @page pg_gim GIM - The Guest Interface Manager
|
---|
| 19 | *
|
---|
| 20 | * The Guest Interface Manager abstracts an interface provider through which
|
---|
| 21 | * guests may interact with the hypervisor.
|
---|
| 22 | *
|
---|
| 23 | * @see grp_gim
|
---|
| 24 | *
|
---|
| 25 | *
|
---|
| 26 | * @section sec_gim_provider Providers
|
---|
| 27 | *
|
---|
| 28 | * A GIM provider implements a particular hypervisor interface such as Microsoft
|
---|
| 29 | * Hyper-V, Linux KVM and so on. It hooks into various components in the VMM to
|
---|
[51560] | 30 | * ease the guest in running under a recognized, virtualized environment.
|
---|
[50953] | 31 | *
|
---|
[54819] | 32 | * The GIM provider configured for the VM needs to be recognized by the guest OS
|
---|
| 33 | * in order to make use of features supported by the interface. Since it
|
---|
[76308] | 34 | * requires co-operation from the guest OS, a GIM provider may also be referred to
|
---|
[51560] | 35 | * as a paravirtualization interface.
|
---|
[50953] | 36 | *
|
---|
[54819] | 37 | * One of the goals of having a paravirtualized interface is for enabling guests
|
---|
| 38 | * to be more accurate and efficient when operating in a virtualized
|
---|
| 39 | * environment. For instance, a guest OS which interfaces to VirtualBox through
|
---|
| 40 | * a GIM provider may rely on the provider for supplying the correct TSC
|
---|
| 41 | * frequency of the host processor. The guest can then avoid caliberating the
|
---|
| 42 | * TSC itself, resulting in higher accuracy and better performance.
|
---|
[50953] | 43 | *
|
---|
[51333] | 44 | * At most, only one GIM provider can be active for a running VM and cannot be
|
---|
| 45 | * changed during the lifetime of the VM.
|
---|
[50953] | 46 | */
|
---|
| 47 |
|
---|
[57358] | 48 |
|
---|
| 49 | /*********************************************************************************************************************************
|
---|
| 50 | * Header Files *
|
---|
| 51 | *********************************************************************************************************************************/
|
---|
[50953] | 52 | #define LOG_GROUP LOG_GROUP_GIM
|
---|
[61632] | 53 | #include <VBox/vmm/gim.h>
|
---|
[50953] | 54 | #include <VBox/vmm/hm.h>
|
---|
| 55 | #include <VBox/vmm/ssm.h>
|
---|
[51560] | 56 | #include <VBox/vmm/pdmdev.h>
|
---|
[61632] | 57 | #include "GIMInternal.h"
|
---|
| 58 | #include <VBox/vmm/vm.h>
|
---|
[50953] | 59 |
|
---|
[61632] | 60 | #include <VBox/log.h>
|
---|
| 61 |
|
---|
[50953] | 62 | #include <iprt/err.h>
|
---|
[58390] | 63 | #include <iprt/semaphore.h>
|
---|
[50953] | 64 | #include <iprt/string.h>
|
---|
| 65 |
|
---|
| 66 | /* Include all GIM providers. */
|
---|
[50994] | 67 | #include "GIMMinimalInternal.h"
|
---|
[50953] | 68 | #include "GIMHvInternal.h"
|
---|
[54819] | 69 | #include "GIMKvmInternal.h"
|
---|
[50953] | 70 |
|
---|
[57358] | 71 |
|
---|
| 72 | /*********************************************************************************************************************************
|
---|
| 73 | * Internal Functions *
|
---|
| 74 | *********************************************************************************************************************************/
|
---|
[58126] | 75 | static FNSSMINTSAVEEXEC gimR3Save;
|
---|
| 76 | static FNSSMINTLOADEXEC gimR3Load;
|
---|
[72460] | 77 | static FNSSMINTLOADDONE gimR3LoadDone;
|
---|
[50953] | 78 |
|
---|
| 79 |
|
---|
| 80 | /**
|
---|
| 81 | * Initializes the GIM.
|
---|
| 82 | *
|
---|
| 83 | * @returns VBox status code.
|
---|
[58122] | 84 | * @param pVM The cross context VM structure.
|
---|
[50953] | 85 | */
|
---|
| 86 | VMMR3_INT_DECL(int) GIMR3Init(PVM pVM)
|
---|
| 87 | {
|
---|
| 88 | LogFlow(("GIMR3Init\n"));
|
---|
| 89 |
|
---|
| 90 | /*
|
---|
| 91 | * Assert alignment and sizes.
|
---|
| 92 | */
|
---|
| 93 | AssertCompile(sizeof(pVM->gim.s) <= sizeof(pVM->gim.padding));
|
---|
[80191] | 94 | AssertCompile(sizeof(pVM->apCpusR3[0]->gim.s) <= sizeof(pVM->apCpusR3[0]->gim.padding));
|
---|
[50953] | 95 |
|
---|
| 96 | /*
|
---|
[55493] | 97 | * Initialize members.
|
---|
| 98 | */
|
---|
| 99 | pVM->gim.s.hSemiReadOnlyMmio2Handler = NIL_PGMPHYSHANDLERTYPE;
|
---|
| 100 |
|
---|
| 101 | /*
|
---|
[50953] | 102 | * Register the saved state data unit.
|
---|
| 103 | */
|
---|
[52761] | 104 | int rc = SSMR3RegisterInternal(pVM, "GIM", 0 /* uInstance */, GIM_SAVED_STATE_VERSION, sizeof(GIM),
|
---|
| 105 | NULL /* pfnLivePrep */, NULL /* pfnLiveExec */, NULL /* pfnLiveVote*/,
|
---|
| 106 | NULL /* pfnSavePrep */, gimR3Save, NULL /* pfnSaveDone */,
|
---|
[72460] | 107 | NULL /* pfnLoadPrep */, gimR3Load, gimR3LoadDone);
|
---|
[50953] | 108 | if (RT_FAILURE(rc))
|
---|
| 109 | return rc;
|
---|
| 110 |
|
---|
| 111 | /*
|
---|
| 112 | * Read configuration.
|
---|
| 113 | */
|
---|
| 114 | PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GIM/");
|
---|
| 115 |
|
---|
[58248] | 116 | /*
|
---|
| 117 | * Validate the GIM settings.
|
---|
| 118 | */
|
---|
[58436] | 119 | rc = CFGMR3ValidateConfig(pCfgNode, "/GIM/", /* pszNode */
|
---|
| 120 | "Provider" /* pszValidValues */
|
---|
[58248] | 121 | "|Version",
|
---|
[58436] | 122 | "HyperV", /* pszValidNodes */
|
---|
| 123 | "GIM", /* pszWho */
|
---|
| 124 | 0); /* uInstance */
|
---|
[58248] | 125 | if (RT_FAILURE(rc))
|
---|
| 126 | return rc;
|
---|
| 127 |
|
---|
[52764] | 128 | /** @cfgm{/GIM/Provider, string}
|
---|
[50953] | 129 | * The name of the GIM provider. The default is "none". */
|
---|
| 130 | char szProvider[64];
|
---|
[50961] | 131 | rc = CFGMR3QueryStringDef(pCfgNode, "Provider", szProvider, sizeof(szProvider), "None");
|
---|
[50953] | 132 | AssertLogRelRCReturn(rc, rc);
|
---|
| 133 |
|
---|
[52764] | 134 | /** @cfgm{/GIM/Version, uint32_t}
|
---|
[51333] | 135 | * The interface version. The default is 0, which means "provide the most
|
---|
| 136 | * up-to-date implementation". */
|
---|
| 137 | uint32_t uVersion;
|
---|
| 138 | rc = CFGMR3QueryU32Def(pCfgNode, "Version", &uVersion, 0 /* default */);
|
---|
| 139 | AssertLogRelRCReturn(rc, rc);
|
---|
| 140 |
|
---|
[50953] | 141 | /*
|
---|
| 142 | * Setup the GIM provider for this VM.
|
---|
| 143 | */
|
---|
[55383] | 144 | LogRel(("GIM: Using provider '%s' (Implementation version: %u)\n", szProvider, uVersion));
|
---|
[50961] | 145 | if (!RTStrCmp(szProvider, "None"))
|
---|
[51333] | 146 | pVM->gim.s.enmProviderId = GIMPROVIDERID_NONE;
|
---|
[50953] | 147 | else
|
---|
| 148 | {
|
---|
[51333] | 149 | pVM->gim.s.u32Version = uVersion;
|
---|
[52761] | 150 | /** @todo r=bird: Because u32Version is saved, it should be translated to the
|
---|
| 151 | * 'most up-to-date implementation' version number when 0. Otherwise,
|
---|
| 152 | * we'll have abiguities when loading the state of older VMs. */
|
---|
[50994] | 153 | if (!RTStrCmp(szProvider, "Minimal"))
|
---|
[50953] | 154 | {
|
---|
[51333] | 155 | pVM->gim.s.enmProviderId = GIMPROVIDERID_MINIMAL;
|
---|
[54654] | 156 | rc = gimR3MinimalInit(pVM);
|
---|
[50994] | 157 | }
|
---|
| 158 | else if (!RTStrCmp(szProvider, "HyperV"))
|
---|
| 159 | {
|
---|
[51333] | 160 | pVM->gim.s.enmProviderId = GIMPROVIDERID_HYPERV;
|
---|
[58248] | 161 | rc = gimR3HvInit(pVM, pCfgNode);
|
---|
[50953] | 162 | }
|
---|
[54819] | 163 | else if (!RTStrCmp(szProvider, "KVM"))
|
---|
| 164 | {
|
---|
| 165 | pVM->gim.s.enmProviderId = GIMPROVIDERID_KVM;
|
---|
| 166 | rc = gimR3KvmInit(pVM);
|
---|
| 167 | }
|
---|
[50953] | 168 | else
|
---|
[55383] | 169 | rc = VMR3SetError(pVM->pUVM, VERR_GIM_INVALID_PROVIDER, RT_SRC_POS, "Provider '%s' unknown.", szProvider);
|
---|
[50953] | 170 | }
|
---|
[58390] | 171 |
|
---|
| 172 | /*
|
---|
| 173 | * Statistics.
|
---|
| 174 | */
|
---|
| 175 | STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmit, STAMTYPE_COUNTER, "/GIM/Debug/Transmit", STAMUNIT_OCCURENCES, "Debug packets sent.");
|
---|
| 176 | STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmitBytes, STAMTYPE_COUNTER, "/GIM/Debug/TransmitBytes", STAMUNIT_OCCURENCES, "Debug bytes sent.");
|
---|
| 177 | STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecv, STAMTYPE_COUNTER, "/GIM/Debug/Receive", STAMUNIT_OCCURENCES, "Debug packets received.");
|
---|
| 178 | STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecvBytes, STAMTYPE_COUNTER, "/GIM/Debug/ReceiveBytes", STAMUNIT_OCCURENCES, "Debug bytes received.");
|
---|
| 179 |
|
---|
[58391] | 180 | STAM_REL_REG_USED(pVM, &pVM->gim.s.StatHypercalls, STAMTYPE_COUNTER, "/GIM/Hypercalls", STAMUNIT_OCCURENCES, "Number of hypercalls initiated.");
|
---|
[50953] | 181 | return rc;
|
---|
| 182 | }
|
---|
| 183 |
|
---|
[52675] | 184 |
|
---|
[52699] | 185 | /**
|
---|
| 186 | * Initializes the remaining bits of the GIM provider.
|
---|
[52761] | 187 | *
|
---|
[52699] | 188 | * This is called after initializing HM and most other VMM components.
|
---|
| 189 | *
|
---|
| 190 | * @returns VBox status code.
|
---|
[58122] | 191 | * @param pVM The cross context VM structure.
|
---|
[52699] | 192 | * @thread EMT(0)
|
---|
| 193 | */
|
---|
| 194 | VMMR3_INT_DECL(int) GIMR3InitCompleted(PVM pVM)
|
---|
[51560] | 195 | {
|
---|
| 196 | switch (pVM->gim.s.enmProviderId)
|
---|
| 197 | {
|
---|
| 198 | case GIMPROVIDERID_MINIMAL:
|
---|
[54654] | 199 | return gimR3MinimalInitCompleted(pVM);
|
---|
[51560] | 200 |
|
---|
[52699] | 201 | case GIMPROVIDERID_HYPERV:
|
---|
[54654] | 202 | return gimR3HvInitCompleted(pVM);
|
---|
[52699] | 203 |
|
---|
[54819] | 204 | case GIMPROVIDERID_KVM:
|
---|
| 205 | return gimR3KvmInitCompleted(pVM);
|
---|
| 206 |
|
---|
[51560] | 207 | default:
|
---|
| 208 | break;
|
---|
| 209 | }
|
---|
[54819] | 210 |
|
---|
| 211 | if (!TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */))
|
---|
| 212 | LogRel(("GIM: Warning!!! Host TSC is unstable. The guest may behave unpredictably with a paravirtualized clock.\n"));
|
---|
| 213 |
|
---|
[52699] | 214 | return VINF_SUCCESS;
|
---|
[51560] | 215 | }
|
---|
| 216 |
|
---|
| 217 |
|
---|
[50953] | 218 | /**
|
---|
[58126] | 219 | * @callback_method_impl{FNSSMINTSAVEEXEC}
|
---|
[50953] | 220 | */
|
---|
[58126] | 221 | static DECLCALLBACK(int) gimR3Save(PVM pVM, PSSMHANDLE pSSM)
|
---|
[50953] | 222 | {
|
---|
[51643] | 223 | AssertReturn(pVM, VERR_INVALID_PARAMETER);
|
---|
| 224 | AssertReturn(pSSM, VERR_SSM_INVALID_STATE);
|
---|
| 225 |
|
---|
[52767] | 226 | int rc = VINF_SUCCESS;
|
---|
[51643] | 227 | #if 0
|
---|
[65449] | 228 | /* Save per-CPU data. */
|
---|
[52761] | 229 | SSMR3PutU32(pSSM, pVM->cCpus);
|
---|
[80191] | 230 | for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
|
---|
[51643] | 231 | {
|
---|
[80191] | 232 | PVMCPU pVCpu = pVM->apCpusR3[idCpu];
|
---|
| 233 | rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ);
|
---|
[51643] | 234 | }
|
---|
| 235 | #endif
|
---|
| 236 |
|
---|
| 237 | /*
|
---|
| 238 | * Save per-VM data.
|
---|
| 239 | */
|
---|
[52761] | 240 | SSMR3PutU32(pSSM, pVM->gim.s.enmProviderId);
|
---|
[52767] | 241 | SSMR3PutU32(pSSM, pVM->gim.s.u32Version);
|
---|
[51643] | 242 |
|
---|
| 243 | /*
|
---|
| 244 | * Save provider-specific data.
|
---|
| 245 | */
|
---|
[52767] | 246 | switch (pVM->gim.s.enmProviderId)
|
---|
[51643] | 247 | {
|
---|
[52767] | 248 | case GIMPROVIDERID_HYPERV:
|
---|
[54654] | 249 | rc = gimR3HvSave(pVM, pSSM);
|
---|
[52767] | 250 | AssertRCReturn(rc, rc);
|
---|
| 251 | break;
|
---|
[51643] | 252 |
|
---|
[54819] | 253 | case GIMPROVIDERID_KVM:
|
---|
| 254 | rc = gimR3KvmSave(pVM, pSSM);
|
---|
| 255 | AssertRCReturn(rc, rc);
|
---|
| 256 | break;
|
---|
| 257 |
|
---|
[52767] | 258 | default:
|
---|
| 259 | break;
|
---|
[51643] | 260 | }
|
---|
| 261 |
|
---|
| 262 | return rc;
|
---|
[50953] | 263 | }
|
---|
| 264 |
|
---|
| 265 |
|
---|
| 266 | /**
|
---|
[58126] | 267 | * @callback_method_impl{FNSSMINTLOADEXEC}
|
---|
[50953] | 268 | */
|
---|
[58126] | 269 | static DECLCALLBACK(int) gimR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
|
---|
[50953] | 270 | {
|
---|
[51643] | 271 | if (uPass != SSM_PASS_FINAL)
|
---|
| 272 | return VINF_SUCCESS;
|
---|
[58126] | 273 | if (uVersion != GIM_SAVED_STATE_VERSION)
|
---|
[52761] | 274 | return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
|
---|
[51643] | 275 |
|
---|
| 276 | int rc;
|
---|
| 277 | #if 0
|
---|
[65449] | 278 | /* Load per-CPU data. */
|
---|
[80191] | 279 | for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
|
---|
[51643] | 280 | {
|
---|
[80191] | 281 | PVMCPU pVCpu = pVM->apCpusR3[idCpu];
|
---|
| 282 | rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ);
|
---|
[51643] | 283 | }
|
---|
| 284 | #endif
|
---|
| 285 |
|
---|
| 286 | /*
|
---|
| 287 | * Load per-VM data.
|
---|
| 288 | */
|
---|
[52761] | 289 | uint32_t uProviderId;
|
---|
| 290 | uint32_t uProviderVersion;
|
---|
[51643] | 291 |
|
---|
[80597] | 292 | SSMR3GetU32(pSSM, &uProviderId);
|
---|
| 293 | rc = SSMR3GetU32(pSSM, &uProviderVersion);
|
---|
| 294 | AssertRCReturn(rc, rc);
|
---|
[52767] | 295 |
|
---|
[52761] | 296 | if ((GIMPROVIDERID)uProviderId != pVM->gim.s.enmProviderId)
|
---|
| 297 | return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider %u differs from the configured one (%u)."),
|
---|
| 298 | uProviderId, pVM->gim.s.enmProviderId);
|
---|
| 299 | #if 0 /** @todo r=bird: Figure out what you mean to do here with the version. */
|
---|
| 300 | if (uProviderVersion != pVM->gim.s.u32Version)
|
---|
| 301 | return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider version %u differs from the configured one (%u)."),
|
---|
| 302 | uProviderVersion, pVM->gim.s.u32Version);
|
---|
| 303 | #else
|
---|
| 304 | pVM->gim.s.u32Version = uProviderVersion;
|
---|
| 305 | #endif
|
---|
| 306 |
|
---|
[51643] | 307 | /*
|
---|
| 308 | * Load provider-specific data.
|
---|
| 309 | */
|
---|
[52767] | 310 | switch (pVM->gim.s.enmProviderId)
|
---|
[51643] | 311 | {
|
---|
[52767] | 312 | case GIMPROVIDERID_HYPERV:
|
---|
[62641] | 313 | rc = gimR3HvLoad(pVM, pSSM);
|
---|
[52767] | 314 | AssertRCReturn(rc, rc);
|
---|
| 315 | break;
|
---|
[51643] | 316 |
|
---|
[54819] | 317 | case GIMPROVIDERID_KVM:
|
---|
[62641] | 318 | rc = gimR3KvmLoad(pVM, pSSM);
|
---|
[54819] | 319 | AssertRCReturn(rc, rc);
|
---|
| 320 | break;
|
---|
| 321 |
|
---|
[52767] | 322 | default:
|
---|
| 323 | break;
|
---|
[51643] | 324 | }
|
---|
| 325 |
|
---|
[52767] | 326 | return VINF_SUCCESS;
|
---|
[50953] | 327 | }
|
---|
| 328 |
|
---|
| 329 |
|
---|
| 330 | /**
|
---|
[72460] | 331 | * @callback_method_impl{FNSSMINTLOADDONE}
|
---|
| 332 | */
|
---|
| 333 | static DECLCALLBACK(int) gimR3LoadDone(PVM pVM, PSSMHANDLE pSSM)
|
---|
| 334 | {
|
---|
| 335 | switch (pVM->gim.s.enmProviderId)
|
---|
| 336 | {
|
---|
| 337 | case GIMPROVIDERID_HYPERV:
|
---|
| 338 | return gimR3HvLoadDone(pVM, pSSM);
|
---|
| 339 |
|
---|
| 340 | default:
|
---|
| 341 | return VINF_SUCCESS;
|
---|
| 342 | }
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 |
|
---|
| 346 | /**
|
---|
[50953] | 347 | * Terminates the GIM.
|
---|
| 348 | *
|
---|
| 349 | * Termination means cleaning up and freeing all resources,
|
---|
| 350 | * the VM itself is, at this point, powered off or suspended.
|
---|
| 351 | *
|
---|
| 352 | * @returns VBox status code.
|
---|
[58122] | 353 | * @param pVM The cross context VM structure.
|
---|
[50953] | 354 | */
|
---|
| 355 | VMMR3_INT_DECL(int) GIMR3Term(PVM pVM)
|
---|
| 356 | {
|
---|
[51643] | 357 | switch (pVM->gim.s.enmProviderId)
|
---|
| 358 | {
|
---|
| 359 | case GIMPROVIDERID_HYPERV:
|
---|
[54654] | 360 | return gimR3HvTerm(pVM);
|
---|
[51643] | 361 |
|
---|
[54819] | 362 | case GIMPROVIDERID_KVM:
|
---|
| 363 | return gimR3KvmTerm(pVM);
|
---|
| 364 |
|
---|
[51643] | 365 | default:
|
---|
| 366 | break;
|
---|
| 367 | }
|
---|
[50953] | 368 | return VINF_SUCCESS;
|
---|
| 369 | }
|
---|
| 370 |
|
---|
[51560] | 371 |
|
---|
| 372 | /**
|
---|
[63648] | 373 | * Applies relocations to data and code managed by this
|
---|
| 374 | * component. This function will be called at init and
|
---|
| 375 | * whenever the VMM need to relocate it self inside the GC.
|
---|
| 376 | *
|
---|
| 377 | * @param pVM The cross context VM structure.
|
---|
| 378 | * @param offDelta Relocation delta relative to old location.
|
---|
| 379 | */
|
---|
| 380 | VMMR3_INT_DECL(void) GIMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
|
---|
| 381 | {
|
---|
| 382 | switch (pVM->gim.s.enmProviderId)
|
---|
| 383 | {
|
---|
| 384 | case GIMPROVIDERID_HYPERV:
|
---|
| 385 | gimR3HvRelocate(pVM, offDelta);
|
---|
| 386 | break;
|
---|
| 387 |
|
---|
| 388 | default:
|
---|
| 389 | break;
|
---|
| 390 | }
|
---|
| 391 | }
|
---|
| 392 |
|
---|
| 393 |
|
---|
| 394 | /**
|
---|
[51560] | 395 | * The VM is being reset.
|
---|
| 396 | *
|
---|
| 397 | * For the GIM component this means unmapping and unregistering MMIO2 regions
|
---|
| 398 | * and other provider-specific resets.
|
---|
| 399 | *
|
---|
| 400 | * @returns VBox status code.
|
---|
[58122] | 401 | * @param pVM The cross context VM structure.
|
---|
[51560] | 402 | */
|
---|
| 403 | VMMR3_INT_DECL(void) GIMR3Reset(PVM pVM)
|
---|
| 404 | {
|
---|
| 405 | switch (pVM->gim.s.enmProviderId)
|
---|
| 406 | {
|
---|
| 407 | case GIMPROVIDERID_HYPERV:
|
---|
[54654] | 408 | return gimR3HvReset(pVM);
|
---|
[51560] | 409 |
|
---|
[54819] | 410 | case GIMPROVIDERID_KVM:
|
---|
| 411 | return gimR3KvmReset(pVM);
|
---|
| 412 |
|
---|
[51560] | 413 | default:
|
---|
| 414 | break;
|
---|
| 415 | }
|
---|
| 416 | }
|
---|
| 417 |
|
---|
| 418 |
|
---|
| 419 | /**
|
---|
| 420 | * Registers the GIM device with VMM.
|
---|
| 421 | *
|
---|
[58122] | 422 | * @param pVM The cross context VM structure.
|
---|
[58126] | 423 | * @param pDevIns Pointer to the GIM device instance.
|
---|
[58390] | 424 | * @param pDbg Pointer to the GIM device debug structure, can be
|
---|
[57989] | 425 | * NULL.
|
---|
| 426 | */
|
---|
[58390] | 427 | VMMR3DECL(void) GIMR3GimDeviceRegister(PVM pVM, PPDMDEVINS pDevIns, PGIMDEBUG pDbg)
|
---|
[57989] | 428 | {
|
---|
[58126] | 429 | pVM->gim.s.pDevInsR3 = pDevIns;
|
---|
[58390] | 430 | pVM->gim.s.pDbgR3 = pDbg;
|
---|
[57989] | 431 | }
|
---|
| 432 |
|
---|
| 433 |
|
---|
| 434 | /**
|
---|
[58390] | 435 | * Gets debug setup specified by the provider.
|
---|
| 436 | *
|
---|
| 437 | * @returns VBox status code.
|
---|
| 438 | * @param pVM The cross context VM structure.
|
---|
[58393] | 439 | * @param pDbgSetup Where to store the debug setup details.
|
---|
[58390] | 440 | */
|
---|
| 441 | VMMR3DECL(int) GIMR3GetDebugSetup(PVM pVM, PGIMDEBUGSETUP pDbgSetup)
|
---|
| 442 | {
|
---|
| 443 | AssertReturn(pVM, VERR_INVALID_PARAMETER);
|
---|
| 444 | AssertReturn(pDbgSetup, VERR_INVALID_PARAMETER);
|
---|
| 445 |
|
---|
| 446 | switch (pVM->gim.s.enmProviderId)
|
---|
| 447 | {
|
---|
| 448 | case GIMPROVIDERID_HYPERV:
|
---|
| 449 | return gimR3HvGetDebugSetup(pVM, pDbgSetup);
|
---|
| 450 | default:
|
---|
| 451 | break;
|
---|
| 452 | }
|
---|
| 453 | return VERR_GIM_NO_DEBUG_CONNECTION;
|
---|
| 454 | }
|
---|
| 455 |
|
---|
| 456 |
|
---|
| 457 | /**
|
---|
[57989] | 458 | * Read data from a host debug session.
|
---|
| 459 | *
|
---|
| 460 | * @returns VBox status code.
|
---|
| 461 | *
|
---|
[58390] | 462 | * @param pVM The cross context VM structure.
|
---|
| 463 | * @param pvRead The read buffer.
|
---|
| 464 | * @param pcbRead The size of the read buffer as well as where to store
|
---|
| 465 | * the number of bytes read.
|
---|
| 466 | * @param pfnReadComplete Callback when the buffer has been read and
|
---|
[79264] | 467 | * before signalling reading of the next buffer.
|
---|
[58390] | 468 | * Optional, can be NULL.
|
---|
[57989] | 469 | * @thread EMT.
|
---|
[51560] | 470 | */
|
---|
[61771] | 471 | VMMR3_INT_DECL(int) gimR3DebugRead(PVM pVM, void *pvRead, size_t *pcbRead, PFNGIMDEBUGBUFREADCOMPLETED pfnReadComplete)
|
---|
[51560] | 472 | {
|
---|
[58390] | 473 | PGIMDEBUG pDbg = pVM->gim.s.pDbgR3;
|
---|
| 474 | if (pDbg)
|
---|
| 475 | {
|
---|
| 476 | if (ASMAtomicReadBool(&pDbg->fDbgRecvBufRead) == true)
|
---|
| 477 | {
|
---|
[61544] | 478 | STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgRecv);
|
---|
| 479 | STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgRecvBytes, pDbg->cbDbgRecvBufRead);
|
---|
[58390] | 480 |
|
---|
| 481 | memcpy(pvRead, pDbg->pvDbgRecvBuf, pDbg->cbDbgRecvBufRead);
|
---|
| 482 | *pcbRead = pDbg->cbDbgRecvBufRead;
|
---|
| 483 | if (pfnReadComplete)
|
---|
| 484 | pfnReadComplete(pVM);
|
---|
| 485 | RTSemEventMultiSignal(pDbg->hDbgRecvThreadSem);
|
---|
| 486 | ASMAtomicWriteBool(&pDbg->fDbgRecvBufRead, false);
|
---|
| 487 | return VINF_SUCCESS;
|
---|
| 488 | }
|
---|
| 489 | else
|
---|
| 490 | *pcbRead = 0;
|
---|
| 491 | return VERR_NO_DATA;
|
---|
| 492 | }
|
---|
[57989] | 493 | return VERR_GIM_NO_DEBUG_CONNECTION;
|
---|
[51560] | 494 | }
|
---|
| 495 |
|
---|
| 496 |
|
---|
| 497 | /**
|
---|
[57989] | 498 | * Write data to a host debug session.
|
---|
| 499 | *
|
---|
| 500 | * @returns VBox status code.
|
---|
| 501 | *
|
---|
[58122] | 502 | * @param pVM The cross context VM structure.
|
---|
[57989] | 503 | * @param pvWrite The write buffer.
|
---|
| 504 | * @param pcbWrite The size of the write buffer as well as where to store
|
---|
| 505 | * the number of bytes written.
|
---|
| 506 | * @thread EMT.
|
---|
| 507 | */
|
---|
[61771] | 508 | VMMR3_INT_DECL(int) gimR3DebugWrite(PVM pVM, void *pvWrite, size_t *pcbWrite)
|
---|
[57989] | 509 | {
|
---|
[58390] | 510 | PGIMDEBUG pDbg = pVM->gim.s.pDbgR3;
|
---|
| 511 | if (pDbg)
|
---|
| 512 | {
|
---|
| 513 | PPDMISTREAM pDbgStream = pDbg->pDbgDrvStream;
|
---|
| 514 | if (pDbgStream)
|
---|
| 515 | {
|
---|
| 516 | size_t cbWrite = *pcbWrite;
|
---|
| 517 | int rc = pDbgStream->pfnWrite(pDbgStream, pvWrite, pcbWrite);
|
---|
| 518 | if ( RT_SUCCESS(rc)
|
---|
| 519 | && *pcbWrite == cbWrite)
|
---|
| 520 | {
|
---|
[61544] | 521 | STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgXmit);
|
---|
| 522 | STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgXmitBytes, *pcbWrite);
|
---|
[58390] | 523 | }
|
---|
| 524 | return rc;
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
[57989] | 527 | return VERR_GIM_NO_DEBUG_CONNECTION;
|
---|
| 528 | }
|
---|
| 529 |
|
---|
[62641] | 530 | #if 0 /* ??? */
|
---|
[51560] | 531 |
|
---|
| 532 | /**
|
---|
[61771] | 533 | * @callback_method_impl{FNPGMPHYSHANDLER,
|
---|
| 534 | * Write access handler for mapped MMIO2 pages. Currently ignores writes.}
|
---|
| 535 | *
|
---|
| 536 | * @todo In the future we might want to let the GIM provider decide what the
|
---|
| 537 | * handler should do (like throwing \#GP faults).
|
---|
| 538 | */
|
---|
| 539 | static DECLCALLBACK(VBOXSTRICTRC) gimR3Mmio2WriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf,
|
---|
| 540 | size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin,
|
---|
| 541 | void *pvUser)
|
---|
| 542 | {
|
---|
[62641] | 543 | RT_NOREF6(pVM, pVCpu, GCPhys, pvPhys, pvBuf, cbBuf);
|
---|
| 544 | RT_NOREF3(enmAccessType, enmOrigin, pvUser);
|
---|
| 545 |
|
---|
[61771] | 546 | /*
|
---|
| 547 | * Ignore writes to the mapped MMIO2 page.
|
---|
| 548 | */
|
---|
| 549 | Assert(enmAccessType == PGMACCESSTYPE_WRITE);
|
---|
| 550 | return VINF_SUCCESS; /** @todo Hyper-V says we should \#GP(0) fault for writes to the Hypercall and TSC page. */
|
---|
| 551 | }
|
---|
| 552 |
|
---|
| 553 |
|
---|
| 554 | /**
|
---|
[51560] | 555 | * Unmaps a registered MMIO2 region in the guest address space and removes any
|
---|
| 556 | * access handlers for it.
|
---|
| 557 | *
|
---|
| 558 | * @returns VBox status code.
|
---|
[58122] | 559 | * @param pVM The cross context VM structure.
|
---|
[51560] | 560 | * @param pRegion Pointer to the GIM MMIO2 region.
|
---|
| 561 | */
|
---|
[61771] | 562 | VMMR3_INT_DECL(int) gimR3Mmio2Unmap(PVM pVM, PGIMMMIO2REGION pRegion)
|
---|
[51560] | 563 | {
|
---|
| 564 | AssertPtr(pVM);
|
---|
| 565 | AssertPtr(pRegion);
|
---|
| 566 |
|
---|
| 567 | PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3;
|
---|
| 568 | AssertPtr(pDevIns);
|
---|
| 569 | if (pRegion->fMapped)
|
---|
| 570 | {
|
---|
[51643] | 571 | int rc = PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage);
|
---|
| 572 | AssertRC(rc);
|
---|
| 573 |
|
---|
| 574 | rc = PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, pRegion->GCPhysPage);
|
---|
[51560] | 575 | if (RT_SUCCESS(rc))
|
---|
| 576 | {
|
---|
| 577 | pRegion->fMapped = false;
|
---|
| 578 | pRegion->GCPhysPage = NIL_RTGCPHYS;
|
---|
| 579 | }
|
---|
| 580 | }
|
---|
| 581 | return VINF_SUCCESS;
|
---|
| 582 | }
|
---|
| 583 |
|
---|
| 584 |
|
---|
| 585 | /**
|
---|
[58116] | 586 | * Maps a registered MMIO2 region in the guest address space.
|
---|
[51560] | 587 | *
|
---|
[58116] | 588 | * The region will be made read-only and writes from the guest will be ignored.
|
---|
| 589 | *
|
---|
[51560] | 590 | * @returns VBox status code.
|
---|
[58122] | 591 | * @param pVM The cross context VM structure.
|
---|
[51560] | 592 | * @param pRegion Pointer to the GIM MMIO2 region.
|
---|
| 593 | * @param GCPhysRegion Where in the guest address space to map the region.
|
---|
| 594 | */
|
---|
[51643] | 595 | VMMR3_INT_DECL(int) GIMR3Mmio2Map(PVM pVM, PGIMMMIO2REGION pRegion, RTGCPHYS GCPhysRegion)
|
---|
[51560] | 596 | {
|
---|
| 597 | PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3;
|
---|
| 598 | AssertPtr(pDevIns);
|
---|
| 599 |
|
---|
| 600 | /* The guest-physical address must be page-aligned. */
|
---|
| 601 | if (GCPhysRegion & PAGE_OFFSET_MASK)
|
---|
| 602 | {
|
---|
[51643] | 603 | LogFunc(("%s: %#RGp not paging aligned\n", pRegion->szDescription, GCPhysRegion));
|
---|
[51560] | 604 | return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
|
---|
| 605 | }
|
---|
| 606 |
|
---|
| 607 | /* Allow only normal pages to be overlaid using our MMIO2 pages (disallow MMIO, ROM, reserved pages). */
|
---|
| 608 | /** @todo Hyper-V doesn't seem to be very strict about this, may be relax
|
---|
| 609 | * later if some guest really requires it. */
|
---|
| 610 | if (!PGMPhysIsGCPhysNormal(pVM, GCPhysRegion))
|
---|
| 611 | {
|
---|
[51643] | 612 | LogFunc(("%s: %#RGp is not normal memory\n", pRegion->szDescription, GCPhysRegion));
|
---|
[51560] | 613 | return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
|
---|
| 614 | }
|
---|
| 615 |
|
---|
[51643] | 616 | if (!pRegion->fRegistered)
|
---|
[51560] | 617 | {
|
---|
[56985] | 618 | LogFunc(("%s: Region has not been registered.\n", pRegion->szDescription));
|
---|
[51643] | 619 | return VERR_GIM_IPE_1;
|
---|
[51560] | 620 | }
|
---|
| 621 |
|
---|
| 622 | /*
|
---|
[51643] | 623 | * Map the MMIO2 region over the specified guest-physical address.
|
---|
[51560] | 624 | */
|
---|
[64356] | 625 | int rc = PDMDevHlpMMIOExMap(pDevIns, NULL, pRegion->iRegion, GCPhysRegion);
|
---|
[51560] | 626 | if (RT_SUCCESS(rc))
|
---|
| 627 | {
|
---|
| 628 | /*
|
---|
[55493] | 629 | * Install access-handlers for the mapped page to prevent (ignore) writes to it
|
---|
| 630 | * from the guest.
|
---|
[51560] | 631 | */
|
---|
[55493] | 632 | if (pVM->gim.s.hSemiReadOnlyMmio2Handler == NIL_PGMPHYSHANDLERTYPE)
|
---|
| 633 | rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
|
---|
| 634 | gimR3Mmio2WriteHandler,
|
---|
[56051] | 635 | NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NULL /* pszPfHandlerR0 */,
|
---|
| 636 | NULL /* pszModRC */, NULL /* pszHandlerRC */, NULL /* pszPfHandlerRC */,
|
---|
[55493] | 637 | "GIM read-only MMIO2 handler",
|
---|
| 638 | &pVM->gim.s.hSemiReadOnlyMmio2Handler);
|
---|
[51560] | 639 | if (RT_SUCCESS(rc))
|
---|
| 640 | {
|
---|
[55493] | 641 | rc = PGMHandlerPhysicalRegister(pVM, GCPhysRegion, GCPhysRegion + (pRegion->cbRegion - 1),
|
---|
| 642 | pVM->gim.s.hSemiReadOnlyMmio2Handler,
|
---|
| 643 | NULL /* pvUserR3 */, NIL_RTR0PTR /* pvUserR0 */, NIL_RTRCPTR /* pvUserRC */,
|
---|
| 644 | pRegion->szDescription);
|
---|
| 645 | if (RT_SUCCESS(rc))
|
---|
| 646 | {
|
---|
| 647 | pRegion->fMapped = true;
|
---|
| 648 | pRegion->GCPhysPage = GCPhysRegion;
|
---|
| 649 | return rc;
|
---|
| 650 | }
|
---|
[51560] | 651 | }
|
---|
| 652 |
|
---|
| 653 | PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, GCPhysRegion);
|
---|
| 654 | }
|
---|
| 655 |
|
---|
| 656 | return rc;
|
---|
| 657 | }
|
---|
| 658 |
|
---|
[61771] | 659 |
|
---|
[51643] | 660 | /**
|
---|
| 661 | * Registers the physical handler for the registered and mapped MMIO2 region.
|
---|
| 662 | *
|
---|
| 663 | * @returns VBox status code.
|
---|
[58122] | 664 | * @param pVM The cross context VM structure.
|
---|
[51643] | 665 | * @param pRegion Pointer to the GIM MMIO2 region.
|
---|
| 666 | */
|
---|
[61771] | 667 | VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalRegister(PVM pVM, PGIMMMIO2REGION pRegion)
|
---|
[51643] | 668 | {
|
---|
| 669 | AssertPtr(pRegion);
|
---|
| 670 | AssertReturn(pRegion->fRegistered, VERR_GIM_IPE_2);
|
---|
| 671 | AssertReturn(pRegion->fMapped, VERR_GIM_IPE_3);
|
---|
| 672 |
|
---|
| 673 | return PGMR3HandlerPhysicalRegister(pVM,
|
---|
[55493] | 674 | PGMPHYSHANDLERKIND_WRITE,
|
---|
[51643] | 675 | pRegion->GCPhysPage, pRegion->GCPhysPage + (pRegion->cbRegion - 1),
|
---|
| 676 | gimR3Mmio2WriteHandler, NULL /* pvUserR3 */,
|
---|
| 677 | NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NIL_RTR0PTR /* pvUserR0 */,
|
---|
| 678 | NULL /* pszModRC */, NULL /* pszHandlerRC */, NIL_RTRCPTR /* pvUserRC */,
|
---|
| 679 | pRegion->szDescription);
|
---|
| 680 | }
|
---|
| 681 |
|
---|
| 682 |
|
---|
| 683 | /**
|
---|
| 684 | * Deregisters the physical handler for the MMIO2 region.
|
---|
| 685 | *
|
---|
| 686 | * @returns VBox status code.
|
---|
[58122] | 687 | * @param pVM The cross context VM structure.
|
---|
[51643] | 688 | * @param pRegion Pointer to the GIM MMIO2 region.
|
---|
| 689 | */
|
---|
[61771] | 690 | VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalDeregister(PVM pVM, PGIMMMIO2REGION pRegion)
|
---|
[51643] | 691 | {
|
---|
| 692 | return PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage);
|
---|
| 693 | }
|
---|
[62641] | 694 |
|
---|
[51643] | 695 | #endif
|
---|
| 696 |
|
---|