diff options
Diffstat (limited to 'src/VBox/VMM/VMMAll/REMAll.cpp')
-rw-r--r-- | src/VBox/VMM/VMMAll/REMAll.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/REMAll.cpp b/src/VBox/VMM/VMMAll/REMAll.cpp new file mode 100644 index 00000000..51506697 --- /dev/null +++ b/src/VBox/VMM/VMMAll/REMAll.cpp @@ -0,0 +1,249 @@ +/* $Id: REMAll.cpp $ */ +/** @file + * REM - Recompiled Execution Monitor, all Contexts part. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_REM +#ifdef VBOX_WITH_REM +# include <VBox/vmm/rem.h> +#endif +#include <VBox/vmm/em.h> +#include <VBox/vmm/vmm.h> +#include "REMInternal.h" +#include <VBox/vmm/vm.h> +#include <iprt/errcore.h> +#include <VBox/log.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> + + +#ifndef IN_RING3 + +/** + * Records a invlpg instruction for replaying upon REM entry. + * + * @param pVM The cross context VM structure. + * @param GCPtrPage The + */ +VMMDECL(void) REMNotifyInvalidatePage(PVM pVM, RTGCPTR GCPtrPage) +{ + /* + * Try take the REM lock and push the address onto the array. + */ + if ( pVM->rem.s.cInvalidatedPages < RT_ELEMENTS(pVM->rem.s.aGCPtrInvalidatedPages) + && EMRemTryLock(pVM) == VINF_SUCCESS) + { + uint32_t iPage = pVM->rem.s.cInvalidatedPages; + if (iPage < RT_ELEMENTS(pVM->rem.s.aGCPtrInvalidatedPages)) + { + ASMAtomicWriteU32(&pVM->rem.s.cInvalidatedPages, iPage + 1); + pVM->rem.s.aGCPtrInvalidatedPages[iPage] = GCPtrPage; + + EMRemUnlock(pVM); + return; + } + + CPUMSetChangedFlags(VMMGetCpu(pVM), CPUM_CHANGED_GLOBAL_TLB_FLUSH); /** @todo this array should be per-cpu technically speaking. */ + ASMAtomicWriteU32(&pVM->rem.s.cInvalidatedPages, 0); /** @todo leave this alone? Optimize this code? */ + + EMRemUnlock(pVM); + } + else + { + /* Fallback: Simply tell the recompiler to flush its TLB. */ + CPUMSetChangedFlags(VMMGetCpu(pVM), CPUM_CHANGED_GLOBAL_TLB_FLUSH); + ASMAtomicWriteU32(&pVM->rem.s.cInvalidatedPages, 0); /** @todo leave this alone?! Optimize this code? */ + } + + return; +} + + +/** + * Insert pending notification + * + * @param pVM The cross context VM structure. + * @param pRec Notification record to insert + */ +static void remNotifyHandlerInsert(PVM pVM, PREMHANDLERNOTIFICATION pRec) +{ + /* + * Fetch a free record. + */ + uint32_t cFlushes = 0; + uint32_t idxFree; + PREMHANDLERNOTIFICATION pFree; + do + { + idxFree = ASMAtomicUoReadU32(&pVM->rem.s.idxFreeList); + if (idxFree == UINT32_MAX) + { + do + { + cFlushes++; + Assert(cFlushes != 128); + AssertFatal(cFlushes < _1M); + VMMRZCallRing3NoCpu(pVM, VMMCALLRING3_REM_REPLAY_HANDLER_NOTIFICATIONS, 0); + idxFree = ASMAtomicUoReadU32(&pVM->rem.s.idxFreeList); + } while (idxFree == UINT32_MAX); + } + pFree = &pVM->rem.s.aHandlerNotifications[idxFree]; + } while (!ASMAtomicCmpXchgU32(&pVM->rem.s.idxFreeList, pFree->idxNext, idxFree)); + + /* + * Copy the record. + */ + pFree->enmKind = pRec->enmKind; + pFree->u = pRec->u; + + /* + * Insert it into the pending list. + */ + uint32_t idxNext; + do + { + idxNext = ASMAtomicUoReadU32(&pVM->rem.s.idxPendingList); + ASMAtomicWriteU32(&pFree->idxNext, idxNext); + ASMCompilerBarrier(); + } while (!ASMAtomicCmpXchgU32(&pVM->rem.s.idxPendingList, idxFree, idxNext)); + + VM_FF_SET(pVM, VM_FF_REM_HANDLER_NOTIFY); +} + + +/** + * Notification about a successful PGMR3HandlerPhysicalRegister() call. + * + * @param pVM The cross context VM structure. + * @param enmKind Kind of access handler. + * @param GCPhys Handler range address. + * @param cb Size of the handler range. + * @param fHasHCHandler Set if the handler have a HC callback function. + */ +VMMDECL(void) REMNotifyHandlerPhysicalRegister(PVM pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb, bool fHasHCHandler) +{ + REMHANDLERNOTIFICATION Rec; + Rec.enmKind = REMHANDLERNOTIFICATIONKIND_PHYSICAL_REGISTER; + Rec.u.PhysicalRegister.enmKind = enmKind; + Rec.u.PhysicalRegister.GCPhys = GCPhys; + Rec.u.PhysicalRegister.cb = cb; + Rec.u.PhysicalRegister.fHasHCHandler = fHasHCHandler; + remNotifyHandlerInsert(pVM, &Rec); +} + + +/** + * Notification about a successful PGMR3HandlerPhysicalDeregister() operation. + * + * @param pVM The cross context VM structure. + * @param enmKind Kind of access handler. + * @param GCPhys Handler range address. + * @param cb Size of the handler range. + * @param fHasHCHandler Set if the handler have a HC callback function. + * @param fRestoreAsRAM Whether the to restore it as normal RAM or as unassigned memory. + */ +VMMDECL(void) REMNotifyHandlerPhysicalDeregister(PVM pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhys, RTGCPHYS cb, bool fHasHCHandler, bool fRestoreAsRAM) +{ + REMHANDLERNOTIFICATION Rec; + Rec.enmKind = REMHANDLERNOTIFICATIONKIND_PHYSICAL_DEREGISTER; + Rec.u.PhysicalDeregister.enmKind = enmKind; + Rec.u.PhysicalDeregister.GCPhys = GCPhys; + Rec.u.PhysicalDeregister.cb = cb; + Rec.u.PhysicalDeregister.fHasHCHandler = fHasHCHandler; + Rec.u.PhysicalDeregister.fRestoreAsRAM = fRestoreAsRAM; + remNotifyHandlerInsert(pVM, &Rec); +} + + +/** + * Notification about a successful PGMR3HandlerPhysicalModify() call. + * + * @param pVM The cross context VM structure. + * @param enmKind Kind of access handler. + * @param GCPhysOld Old handler range address. + * @param GCPhysNew New handler range address. + * @param cb Size of the handler range. + * @param fHasHCHandler Set if the handler have a HC callback function. + * @param fRestoreAsRAM Whether the to restore it as normal RAM or as unassigned memory. + */ +VMMDECL(void) REMNotifyHandlerPhysicalModify(PVM pVM, PGMPHYSHANDLERKIND enmKind, RTGCPHYS GCPhysOld, RTGCPHYS GCPhysNew, RTGCPHYS cb, bool fHasHCHandler, bool fRestoreAsRAM) +{ + REMHANDLERNOTIFICATION Rec; + Rec.enmKind = REMHANDLERNOTIFICATIONKIND_PHYSICAL_MODIFY; + Rec.u.PhysicalModify.enmKind = enmKind; + Rec.u.PhysicalModify.GCPhysOld = GCPhysOld; + Rec.u.PhysicalModify.GCPhysNew = GCPhysNew; + Rec.u.PhysicalModify.cb = cb; + Rec.u.PhysicalModify.fHasHCHandler = fHasHCHandler; + Rec.u.PhysicalModify.fRestoreAsRAM = fRestoreAsRAM; + remNotifyHandlerInsert(pVM, &Rec); +} + +#endif /* !IN_RING3 */ + +#ifdef IN_RC +/** + * Flushes the physical handler notifications if the queue is almost full. + * + * This is for avoiding trouble in RC when changing CR3. + * + * @param pVM The cross context VM structure. + * @param pVCpu The cross context virtual CPU structure of the calling EMT. + */ +VMMDECL(void) REMNotifyHandlerPhysicalFlushIfAlmostFull(PVM pVM, PVMCPU pVCpu) +{ + Assert(pVM->cCpus == 1); NOREF(pVCpu); + + /* + * Less than 48 items means we should flush. + */ + uint32_t cFree = 0; + for (uint32_t idx = pVM->rem.s.idxFreeList; + idx != UINT32_MAX; + idx = pVM->rem.s.aHandlerNotifications[idx].idxNext) + { + Assert(idx < RT_ELEMENTS(pVM->rem.s.aHandlerNotifications)); + if (++cFree >= 48) + return; + } + AssertRelease(VM_FF_IS_SET(pVM, VM_FF_REM_HANDLER_NOTIFY)); + AssertRelease(pVM->rem.s.idxPendingList != UINT32_MAX); + + /* Ok, we gotta flush them. */ + VMMRZCallRing3NoCpu(pVM, VMMCALLRING3_REM_REPLAY_HANDLER_NOTIFICATIONS, 0); + + AssertRelease(pVM->rem.s.idxPendingList == UINT32_MAX); + AssertRelease(pVM->rem.s.idxFreeList != UINT32_MAX); +} +#endif /* IN_RC */ + + +/** + * Make REM flush all translation block upon the next call to REMR3State(). + * + * @param pVM The cross context VM structure. + */ +VMMDECL(void) REMFlushTBs(PVM pVM) +{ + LogFlow(("REMFlushTBs: fFlushTBs=%RTbool fInREM=%RTbool fInStateSync=%RTbool\n", + pVM->rem.s.fFlushTBs, pVM->rem.s.fInREM, pVM->rem.s.fInStateSync)); + pVM->rem.s.fFlushTBs = true; +} + |