From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/VMM/VMMR3/GIM.cpp | 705 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 705 insertions(+) create mode 100644 src/VBox/VMM/VMMR3/GIM.cpp (limited to 'src/VBox/VMM/VMMR3/GIM.cpp') diff --git a/src/VBox/VMM/VMMR3/GIM.cpp b/src/VBox/VMM/VMMR3/GIM.cpp new file mode 100644 index 00000000..9e9a40f9 --- /dev/null +++ b/src/VBox/VMM/VMMR3/GIM.cpp @@ -0,0 +1,705 @@ +/* $Id: GIM.cpp $ */ +/** @file + * GIM - Guest Interface Manager. + */ + +/* + * Copyright (C) 2014-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/** @page pg_gim GIM - The Guest Interface Manager + * + * The Guest Interface Manager abstracts an interface provider through which + * guests may interact with the hypervisor. + * + * @see grp_gim + * + * + * @section sec_gim_provider Providers + * + * A GIM provider implements a particular hypervisor interface such as Microsoft + * Hyper-V, Linux KVM and so on. It hooks into various components in the VMM to + * ease the guest in running under a recognized, virtualized environment. + * + * The GIM provider configured for the VM needs to be recognized by the guest OS + * in order to make use of features supported by the interface. Since it + * requires co-operation from the guest OS, a GIM provider may also be referred to + * as a paravirtualization interface. + * + * One of the goals of having a paravirtualized interface is for enabling guests + * to be more accurate and efficient when operating in a virtualized + * environment. For instance, a guest OS which interfaces to VirtualBox through + * a GIM provider may rely on the provider for supplying the correct TSC + * frequency of the host processor. The guest can then avoid caliberating the + * TSC itself, resulting in higher accuracy and better performance. + * + * At most, only one GIM provider can be active for a running VM and cannot be + * changed during the lifetime of the VM. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GIM +#include +#include +#include +#include +#include "GIMInternal.h" +#include + +#include + +#include +#include +#include + +/* Include all GIM providers. */ +#include "GIMMinimalInternal.h" +#include "GIMHvInternal.h" +#include "GIMKvmInternal.h" + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static FNSSMINTSAVEEXEC gimR3Save; +static FNSSMINTLOADEXEC gimR3Load; +static FNSSMINTLOADDONE gimR3LoadDone; + + +/** + * Initializes the GIM. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + */ +VMMR3_INT_DECL(int) GIMR3Init(PVM pVM) +{ + LogFlow(("GIMR3Init\n")); + + /* + * Assert alignment and sizes. + */ + AssertCompile(sizeof(pVM->gim.s) <= sizeof(pVM->gim.padding)); + AssertCompile(sizeof(pVM->apCpusR3[0]->gim.s) <= sizeof(pVM->apCpusR3[0]->gim.padding)); + + /* + * Initialize members. + */ + pVM->gim.s.hSemiReadOnlyMmio2Handler = NIL_PGMPHYSHANDLERTYPE; + + /* + * Register the saved state data unit. + */ + int rc = SSMR3RegisterInternal(pVM, "GIM", 0 /* uInstance */, GIM_SAVED_STATE_VERSION, sizeof(GIM), + NULL /* pfnLivePrep */, NULL /* pfnLiveExec */, NULL /* pfnLiveVote*/, + NULL /* pfnSavePrep */, gimR3Save, NULL /* pfnSaveDone */, + NULL /* pfnLoadPrep */, gimR3Load, gimR3LoadDone); + if (RT_FAILURE(rc)) + return rc; + + /* + * Read configuration. + */ + PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GIM/"); + + /* + * Validate the GIM settings. + */ + rc = CFGMR3ValidateConfig(pCfgNode, "/GIM/", /* pszNode */ + "Provider" /* pszValidValues */ + "|Version", + "HyperV", /* pszValidNodes */ + "GIM", /* pszWho */ + 0); /* uInstance */ + if (RT_FAILURE(rc)) + return rc; + + /** @cfgm{/GIM/Provider, string} + * The name of the GIM provider. The default is "none". */ + char szProvider[64]; + rc = CFGMR3QueryStringDef(pCfgNode, "Provider", szProvider, sizeof(szProvider), "None"); + AssertLogRelRCReturn(rc, rc); + + /** @cfgm{/GIM/Version, uint32_t} + * The interface version. The default is 0, which means "provide the most + * up-to-date implementation". */ + uint32_t uVersion; + rc = CFGMR3QueryU32Def(pCfgNode, "Version", &uVersion, 0 /* default */); + AssertLogRelRCReturn(rc, rc); + + /* + * Setup the GIM provider for this VM. + */ + LogRel(("GIM: Using provider '%s' (Implementation version: %u)\n", szProvider, uVersion)); + if (!RTStrCmp(szProvider, "None")) + pVM->gim.s.enmProviderId = GIMPROVIDERID_NONE; + else + { + pVM->gim.s.u32Version = uVersion; + /** @todo r=bird: Because u32Version is saved, it should be translated to the + * 'most up-to-date implementation' version number when 0. Otherwise, + * we'll have abiguities when loading the state of older VMs. */ + if (!RTStrCmp(szProvider, "Minimal")) + { + pVM->gim.s.enmProviderId = GIMPROVIDERID_MINIMAL; + rc = gimR3MinimalInit(pVM); + } + else if (!RTStrCmp(szProvider, "HyperV")) + { + pVM->gim.s.enmProviderId = GIMPROVIDERID_HYPERV; + rc = gimR3HvInit(pVM, pCfgNode); + } + else if (!RTStrCmp(szProvider, "KVM")) + { + pVM->gim.s.enmProviderId = GIMPROVIDERID_KVM; + rc = gimR3KvmInit(pVM); + } + else + rc = VMR3SetError(pVM->pUVM, VERR_GIM_INVALID_PROVIDER, RT_SRC_POS, "Provider '%s' unknown.", szProvider); + } + + /* + * Statistics. + */ + STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmit, STAMTYPE_COUNTER, "/GIM/Debug/Transmit", STAMUNIT_OCCURENCES, "Debug packets sent."); + STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmitBytes, STAMTYPE_COUNTER, "/GIM/Debug/TransmitBytes", STAMUNIT_OCCURENCES, "Debug bytes sent."); + STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecv, STAMTYPE_COUNTER, "/GIM/Debug/Receive", STAMUNIT_OCCURENCES, "Debug packets received."); + STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecvBytes, STAMTYPE_COUNTER, "/GIM/Debug/ReceiveBytes", STAMUNIT_OCCURENCES, "Debug bytes received."); + + STAM_REL_REG_USED(pVM, &pVM->gim.s.StatHypercalls, STAMTYPE_COUNTER, "/GIM/Hypercalls", STAMUNIT_OCCURENCES, "Number of hypercalls initiated."); + return rc; +} + + +/** + * Initializes the remaining bits of the GIM provider. + * + * This is called after initializing HM and most other VMM components. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @thread EMT(0) + */ +VMMR3_INT_DECL(int) GIMR3InitCompleted(PVM pVM) +{ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_MINIMAL: + return gimR3MinimalInitCompleted(pVM); + + case GIMPROVIDERID_HYPERV: + return gimR3HvInitCompleted(pVM); + + case GIMPROVIDERID_KVM: + return gimR3KvmInitCompleted(pVM); + + default: + break; + } + + if (!TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */)) + LogRel(("GIM: Warning!!! Host TSC is unstable. The guest may behave unpredictably with a paravirtualized clock.\n")); + + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{FNSSMINTSAVEEXEC} + */ +static DECLCALLBACK(int) gimR3Save(PVM pVM, PSSMHANDLE pSSM) +{ + AssertReturn(pVM, VERR_INVALID_PARAMETER); + AssertReturn(pSSM, VERR_SSM_INVALID_STATE); + + int rc = VINF_SUCCESS; +#if 0 + /* Save per-CPU data. */ + SSMR3PutU32(pSSM, pVM->cCpus); + for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) + { + PVMCPU pVCpu = pVM->apCpusR3[idCpu]; + rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ); + } +#endif + + /* + * Save per-VM data. + */ + SSMR3PutU32(pSSM, pVM->gim.s.enmProviderId); + SSMR3PutU32(pSSM, pVM->gim.s.u32Version); + + /* + * Save provider-specific data. + */ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + rc = gimR3HvSave(pVM, pSSM); + AssertRCReturn(rc, rc); + break; + + case GIMPROVIDERID_KVM: + rc = gimR3KvmSave(pVM, pSSM); + AssertRCReturn(rc, rc); + break; + + default: + break; + } + + return rc; +} + + +/** + * @callback_method_impl{FNSSMINTLOADEXEC} + */ +static DECLCALLBACK(int) gimR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) +{ + if (uPass != SSM_PASS_FINAL) + return VINF_SUCCESS; + if (uVersion != GIM_SAVED_STATE_VERSION) + return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; + + int rc; +#if 0 + /* Load per-CPU data. */ + for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) + { + PVMCPU pVCpu = pVM->apCpusR3[idCpu]; + rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ); + } +#endif + + /* + * Load per-VM data. + */ + uint32_t uProviderId; + uint32_t uProviderVersion; + + SSMR3GetU32(pSSM, &uProviderId); + rc = SSMR3GetU32(pSSM, &uProviderVersion); + AssertRCReturn(rc, rc); + + if ((GIMPROVIDERID)uProviderId != pVM->gim.s.enmProviderId) + return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider %u differs from the configured one (%u)."), + uProviderId, pVM->gim.s.enmProviderId); +#if 0 /** @todo r=bird: Figure out what you mean to do here with the version. */ + if (uProviderVersion != pVM->gim.s.u32Version) + return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider version %u differs from the configured one (%u)."), + uProviderVersion, pVM->gim.s.u32Version); +#else + pVM->gim.s.u32Version = uProviderVersion; +#endif + + /* + * Load provider-specific data. + */ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + rc = gimR3HvLoad(pVM, pSSM); + AssertRCReturn(rc, rc); + break; + + case GIMPROVIDERID_KVM: + rc = gimR3KvmLoad(pVM, pSSM); + AssertRCReturn(rc, rc); + break; + + default: + break; + } + + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{FNSSMINTLOADDONE} + */ +static DECLCALLBACK(int) gimR3LoadDone(PVM pVM, PSSMHANDLE pSSM) +{ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + return gimR3HvLoadDone(pVM, pSSM); + + default: + return VINF_SUCCESS; + } +} + + +/** + * Terminates the GIM. + * + * Termination means cleaning up and freeing all resources, + * the VM itself is, at this point, powered off or suspended. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + */ +VMMR3_INT_DECL(int) GIMR3Term(PVM pVM) +{ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + return gimR3HvTerm(pVM); + + case GIMPROVIDERID_KVM: + return gimR3KvmTerm(pVM); + + default: + break; + } + return VINF_SUCCESS; +} + + +/** + * Applies relocations to data and code managed by this + * component. This function will be called at init and + * whenever the VMM need to relocate it self inside the GC. + * + * @param pVM The cross context VM structure. + * @param offDelta Relocation delta relative to old location. + */ +VMMR3_INT_DECL(void) GIMR3Relocate(PVM pVM, RTGCINTPTR offDelta) +{ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + gimR3HvRelocate(pVM, offDelta); + break; + + default: + break; + } +} + + +/** + * The VM is being reset. + * + * For the GIM component this means unmapping and unregistering MMIO2 regions + * and other provider-specific resets. + * + * @param pVM The cross context VM structure. + */ +VMMR3_INT_DECL(void) GIMR3Reset(PVM pVM) +{ + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + return gimR3HvReset(pVM); + + case GIMPROVIDERID_KVM: + return gimR3KvmReset(pVM); + + default: + break; + } +} + + +/** + * Registers the GIM device with VMM. + * + * @param pVM The cross context VM structure. + * @param pDevIns Pointer to the GIM device instance. + * @param pDbg Pointer to the GIM device debug structure, can be + * NULL. + */ +VMMR3DECL(void) GIMR3GimDeviceRegister(PVM pVM, PPDMDEVINS pDevIns, PGIMDEBUG pDbg) +{ + pVM->gim.s.pDevInsR3 = pDevIns; + pVM->gim.s.pDbgR3 = pDbg; +} + + +/** + * Gets debug setup specified by the provider. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pDbgSetup Where to store the debug setup details. + */ +VMMR3DECL(int) GIMR3GetDebugSetup(PVM pVM, PGIMDEBUGSETUP pDbgSetup) +{ + AssertReturn(pVM, VERR_INVALID_PARAMETER); + AssertReturn(pDbgSetup, VERR_INVALID_PARAMETER); + + switch (pVM->gim.s.enmProviderId) + { + case GIMPROVIDERID_HYPERV: + return gimR3HvGetDebugSetup(pVM, pDbgSetup); + default: + break; + } + return VERR_GIM_NO_DEBUG_CONNECTION; +} + + +/** + * Read data from a host debug session. + * + * @returns VBox status code. + * + * @param pVM The cross context VM structure. + * @param pvRead The read buffer. + * @param pcbRead The size of the read buffer as well as where to store + * the number of bytes read. + * @param pfnReadComplete Callback when the buffer has been read and + * before signalling reading of the next buffer. + * Optional, can be NULL. + * @thread EMT. + */ +VMMR3_INT_DECL(int) gimR3DebugRead(PVM pVM, void *pvRead, size_t *pcbRead, PFNGIMDEBUGBUFREADCOMPLETED pfnReadComplete) +{ + PGIMDEBUG pDbg = pVM->gim.s.pDbgR3; + if (pDbg) + { + if (ASMAtomicReadBool(&pDbg->fDbgRecvBufRead) == true) + { + STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgRecv); + STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgRecvBytes, pDbg->cbDbgRecvBufRead); + + memcpy(pvRead, pDbg->pvDbgRecvBuf, pDbg->cbDbgRecvBufRead); + *pcbRead = pDbg->cbDbgRecvBufRead; + if (pfnReadComplete) + pfnReadComplete(pVM); + RTSemEventMultiSignal(pDbg->hDbgRecvThreadSem); + ASMAtomicWriteBool(&pDbg->fDbgRecvBufRead, false); + return VINF_SUCCESS; + } + else + *pcbRead = 0; + return VERR_NO_DATA; + } + return VERR_GIM_NO_DEBUG_CONNECTION; +} + + +/** + * Write data to a host debug session. + * + * @returns VBox status code. + * + * @param pVM The cross context VM structure. + * @param pvWrite The write buffer. + * @param pcbWrite The size of the write buffer as well as where to store + * the number of bytes written. + * @thread EMT. + */ +VMMR3_INT_DECL(int) gimR3DebugWrite(PVM pVM, void *pvWrite, size_t *pcbWrite) +{ + PGIMDEBUG pDbg = pVM->gim.s.pDbgR3; + if (pDbg) + { + PPDMISTREAM pDbgStream = pDbg->pDbgDrvStream; + if (pDbgStream) + { + size_t cbWrite = *pcbWrite; + int rc = pDbgStream->pfnWrite(pDbgStream, pvWrite, pcbWrite); + if ( RT_SUCCESS(rc) + && *pcbWrite == cbWrite) + { + STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgXmit); + STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgXmitBytes, *pcbWrite); + } + return rc; + } + } + return VERR_GIM_NO_DEBUG_CONNECTION; +} + +#if 0 /* ??? */ + +/** + * @callback_method_impl{FNPGMPHYSHANDLER, + * Write access handler for mapped MMIO2 pages. Currently ignores writes.} + * + * @todo In the future we might want to let the GIM provider decide what the + * handler should do (like throwing \#GP faults). + */ +static DECLCALLBACK(VBOXSTRICTRC) gimR3Mmio2WriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, + size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, + void *pvUser) +{ + RT_NOREF6(pVM, pVCpu, GCPhys, pvPhys, pvBuf, cbBuf); + RT_NOREF3(enmAccessType, enmOrigin, pvUser); + + /* + * Ignore writes to the mapped MMIO2 page. + */ + Assert(enmAccessType == PGMACCESSTYPE_WRITE); + return VINF_SUCCESS; /** @todo Hyper-V says we should \#GP(0) fault for writes to the Hypercall and TSC page. */ +} + + +/** + * Unmaps a registered MMIO2 region in the guest address space and removes any + * access handlers for it. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pRegion Pointer to the GIM MMIO2 region. + */ +VMMR3_INT_DECL(int) gimR3Mmio2Unmap(PVM pVM, PGIMMMIO2REGION pRegion) +{ + AssertPtr(pVM); + AssertPtr(pRegion); + + PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3; + AssertPtr(pDevIns); + if (pRegion->fMapped) + { + int rc = PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage); + AssertRC(rc); + + rc = PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, pRegion->GCPhysPage); + if (RT_SUCCESS(rc)) + { + pRegion->fMapped = false; + pRegion->GCPhysPage = NIL_RTGCPHYS; + } + } + return VINF_SUCCESS; +} + + +/** + * Maps a registered MMIO2 region in the guest address space. + * + * The region will be made read-only and writes from the guest will be ignored. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pRegion Pointer to the GIM MMIO2 region. + * @param GCPhysRegion Where in the guest address space to map the region. + */ +VMMR3_INT_DECL(int) GIMR3Mmio2Map(PVM pVM, PGIMMMIO2REGION pRegion, RTGCPHYS GCPhysRegion) +{ + PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3; + AssertPtr(pDevIns); + + /* The guest-physical address must be page-aligned. */ + if (GCPhysRegion & GUEST_PAGE_OFFSET_MASK) + { + LogFunc(("%s: %#RGp not paging aligned\n", pRegion->szDescription, GCPhysRegion)); + return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS; + } + + /* Allow only normal pages to be overlaid using our MMIO2 pages (disallow MMIO, ROM, reserved pages). */ + /** @todo Hyper-V doesn't seem to be very strict about this, may be relax + * later if some guest really requires it. */ + if (!PGMPhysIsGCPhysNormal(pVM, GCPhysRegion)) + { + LogFunc(("%s: %#RGp is not normal memory\n", pRegion->szDescription, GCPhysRegion)); + return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS; + } + + if (!pRegion->fRegistered) + { + LogFunc(("%s: Region has not been registered.\n", pRegion->szDescription)); + return VERR_GIM_IPE_1; + } + + /* + * Map the MMIO2 region over the specified guest-physical address. + */ + int rc = PDMDevHlpMMIOExMap(pDevIns, NULL, pRegion->iRegion, GCPhysRegion); + if (RT_SUCCESS(rc)) + { + /* + * Install access-handlers for the mapped page to prevent (ignore) writes to it + * from the guest. + */ + if (pVM->gim.s.hSemiReadOnlyMmio2Handler == NIL_PGMPHYSHANDLERTYPE) + rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE, + gimR3Mmio2WriteHandler, + NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NULL /* pszPfHandlerR0 */, + NULL /* pszModRC */, NULL /* pszHandlerRC */, NULL /* pszPfHandlerRC */, + "GIM read-only MMIO2 handler", + &pVM->gim.s.hSemiReadOnlyMmio2Handler); + if (RT_SUCCESS(rc)) + { + rc = PGMHandlerPhysicalRegister(pVM, GCPhysRegion, GCPhysRegion + (pRegion->cbRegion - 1), + pVM->gim.s.hSemiReadOnlyMmio2Handler, + NULL /* pvUserR3 */, NIL_RTR0PTR /* pvUserR0 */, NIL_RTRCPTR /* pvUserRC */, + pRegion->szDescription); + if (RT_SUCCESS(rc)) + { + pRegion->fMapped = true; + pRegion->GCPhysPage = GCPhysRegion; + return rc; + } + } + + PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, GCPhysRegion); + } + + return rc; +} + + +/** + * Registers the physical handler for the registered and mapped MMIO2 region. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pRegion Pointer to the GIM MMIO2 region. + */ +VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalRegister(PVM pVM, PGIMMMIO2REGION pRegion) +{ + AssertPtr(pRegion); + AssertReturn(pRegion->fRegistered, VERR_GIM_IPE_2); + AssertReturn(pRegion->fMapped, VERR_GIM_IPE_3); + + return PGMR3HandlerPhysicalRegister(pVM, + PGMPHYSHANDLERKIND_WRITE, + pRegion->GCPhysPage, pRegion->GCPhysPage + (pRegion->cbRegion - 1), + gimR3Mmio2WriteHandler, NULL /* pvUserR3 */, + NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NIL_RTR0PTR /* pvUserR0 */, + NULL /* pszModRC */, NULL /* pszHandlerRC */, NIL_RTRCPTR /* pvUserRC */, + pRegion->szDescription); +} + + +/** + * Deregisters the physical handler for the MMIO2 region. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pRegion Pointer to the GIM MMIO2 region. + */ +VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalDeregister(PVM pVM, PGIMMMIO2REGION pRegion) +{ + return PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage); +} + +#endif + -- cgit v1.2.3