PGMAllBth.h revision 1cab4c8b5903a7933e7c31a6b5738e629029c7b3
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * VBox - Page Manager, Shadow+Guest Paging Template - All context code.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * This file is a big challenge!
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * Copyright (C) 2006-2007 innotek GmbH
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * available from http://www.virtualbox.org. This file is free software;
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * you can redistribute it and/or modify it under the terms of the GNU
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * General Public License (GPL) as published by the Free Software
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync/*******************************************************************************
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync* Internal Functions *
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync*******************************************************************************/
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, Trap0eHandler)(PVM pVM, RTGCUINT uErr, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, InvalidatePage)(PVM pVM, RTGCUINTPTR GCPtrPage);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, SyncPage)(PVM pVM, GSTPDE PdeSrc, RTGCUINTPTR GCPtrPage, unsigned cPages, unsigned uErr);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, CheckPageFault)(PVM pVM, uint32_t uErr, PSHWPDE pPdeDst, PGSTPDE pPdeSrc, RTGCUINTPTR GCPtrPage);
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsyncPGM_BTH_DECL(int, SyncPT)(PVM pVM, unsigned iPD, PGSTPD pPDSrc, RTGCUINTPTR GCPtrPage);
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsyncPGM_BTH_DECL(int, VerifyAccessSyncPage)(PVM pVM, RTGCUINTPTR Addr, unsigned fPage, unsigned uErr);
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsyncPGM_BTH_DECL(int, PrefetchPage)(PVM pVM, RTGCUINTPTR GCPtrPage);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, SyncCR3)(PVM pVM, uint32_t cr0, uint32_t cr3, uint32_t cr4, bool fGlobal);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(unsigned, AssertCR3)(PVM pVM, uint32_t cr3, uint32_t cr4, RTGCUINTPTR GCPtr = 0, RTGCUINTPTR cb = ~(RTGCUINTPTR)0);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncDECLINLINE(void) PGM_BTH_NAME(SyncPageWorkerTrackDeref)(PVM pVM, PPGMPOOLPAGE pShwPage, RTHCPHYS HCPhys);
64743f0e8609782598af145a21b5dbdd23f3853evboxsync/* Filter out some illegal combinations of guest and shadow paging, so we can remove redundant checks inside functions. */
292dc462b10ba7ef37b871434e332aecf8ad97dfvboxsync#if PGM_GST_TYPE == PGM_TYPE_PAE && PGM_SHW_TYPE != PGM_TYPE_PAE
292dc462b10ba7ef37b871434e332aecf8ad97dfvboxsync#error "Invalid combination; PAE guest implies PAE shadow"
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync#if (PGM_GST_TYPE == PGM_TYPE_REAL || PGM_GST_TYPE == PGM_TYPE_PROT) \
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync && !(PGM_SHW_TYPE == PGM_TYPE_32BIT || PGM_SHW_TYPE == PGM_TYPE_PAE)
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync#error "Invalid combination; real or protected mode without paging implies 32 bits or PAE shadow paging."
64743f0e8609782598af145a21b5dbdd23f3853evboxsync#if (PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_PAE) \
292dc462b10ba7ef37b871434e332aecf8ad97dfvboxsync && !(PGM_SHW_TYPE == PGM_TYPE_32BIT || PGM_SHW_TYPE == PGM_TYPE_PAE)
64743f0e8609782598af145a21b5dbdd23f3853evboxsync#error "Invalid combination; 32 bits guest paging or PAE implies 32 bits or PAE shadow paging."
292dc462b10ba7ef37b871434e332aecf8ad97dfvboxsync#if (PGM_GST_TYPE == PGM_TYPE_AMD64 && PGM_SHW_TYPE != PGM_TYPE_AMD64)
292dc462b10ba7ef37b871434e332aecf8ad97dfvboxsync || (PGM_SHW_TYPE == PGM_TYPE_AMD64 && PGM_GST_TYPE != PGM_TYPE_AMD64)
64743f0e8609782598af145a21b5dbdd23f3853evboxsync#error "Invalid combination; AMD64 guest implies AMD64 shadow and vice versa"
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * #PF Handler for raw-mode guest execution.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * @returns VBox status code (appropriate for trap handling and GC return).
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * @param pVM VM Handle.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * @param uErr The trap error code.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * @param pRegFrame Trap register frame.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * @param pvFault The fault address.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsyncPGM_BTH_DECL(int, Trap0eHandler)(PVM pVM, RTGCUINT uErr, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault)
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync#if (PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_REAL || PGM_GST_TYPE == PGM_TYPE_PROT || PGM_GST_TYPE == PGM_TYPE_PAE) && PGM_SHW_TYPE != PGM_TYPE_AMD64
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync# if PGM_SHW_TYPE == PGM_TYPE_PAE && PGM_GST_TYPE != PGM_TYPE_PAE
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * Hide the instruction fetch trap indicator for now.
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync /** @todo NXE will change this and we must fix NXE in the switcher too! */
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync const unsigned iPDSrc = (RTGCUINTPTR)pvFault >> GST_PD_SHIFT;
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync# else /* PAE */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync PGSTPD pPDSrc = pgmGstGetPaePDPtr(&pVM->pgm.s, (RTGCUINTPTR)pvFault, &iPDSrc);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync const unsigned iPDSrc = 0;
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync const unsigned iPDDst = (RTGCUINTPTR)pvFault >> SHW_PD_SHIFT;
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync# else /* PAE */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync PX86PDPAE pPDDst = pVM->pgm.s.CTXMID(ap,PaePDs)[0]; /* We treat this as a PD with 2048 entries. */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * If we successfully correct the write protection fault due to dirty bit
64743f0e8609782598af145a21b5dbdd23f3853evboxsync * tracking, or this page fault is a genuine one, then return immediately.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_PROFILE_START(&pVM->pgm.s.StatCheckPageFault, e);
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync rc = PGM_BTH_NAME(CheckPageFault)(pVM, uErr, &pPDDst->a[iPDDst], &pPDSrc->a[iPDSrc], (RTGCUINTPTR)pvFault);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_PROFILE_STOP(&pVM->pgm.s.StatCheckPageFault, e);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution)
64743f0e8609782598af145a21b5dbdd23f3853evboxsync = rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? &pVM->pgm.s.StatTrap0eDirtyAndAccessedBits : &pVM->pgm.s.StatTrap0eGuestTrap; });
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync LogBird(("Trap0eHandler: returns %s\n", rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? "VINF_SUCCESS" : "VINF_EM_RAW_GUEST_TRAP"));
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync return rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? VINF_SUCCESS : rc;
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0ePD[iPDSrc]);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * A common case is the not-present error caused by lazy page table syncing.
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync * It is IMPORTANT that we weed out any access to non-present shadow PDEs here
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync * so we can safely assume that the shadow PT is present when calling SyncPage later.
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync * On failure, we ASSUME that SyncPT is out of memory or detected some kind
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync * of mapping conflict and defer to SyncCR3 in R3.
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync * (Again, we do NOT support access handlers for non-present guest pages.)
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync PdeSrc.au32[0] = 0; /* faked so we don't have to #ifdef everything */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync if ( !(uErr & X86_TRAP_PF_P) /* not set means page not present instead of page protection violation */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eSyncPT; });
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync LogFlow(("=>SyncPT %04x = %08x\n", iPDSrc, PdeSrc.au32[0]));
64743f0e8609782598af145a21b5dbdd23f3853evboxsync rc = PGM_BTH_NAME(SyncPT)(pVM, iPDSrc, pPDSrc, (RTGCUINTPTR)pvFault);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync VM_FF_SET(pVM, VM_FF_PGM_SYNC_CR3); /** @todo no need to do global sync, right? */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * Check if this address is within any of our mappings.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * This is *very* fast and it's gonna save us a bit of effort below and prevent
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * us from screwing ourself with MMIO2 pages which have a GC Mapping (VRam).
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync * (BTW, it's impossible to have physical access handlers in a mapping.)
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync PPGMMAPPING pMapping = CTXALLSUFF(pVM->pgm.s.pMappings);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync for ( ; pMapping; pMapping = CTXALLSUFF(pMapping->pNext))
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync if ((RTGCUINTPTR)pvFault < (RTGCUINTPTR)pMapping->GCPtr)
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync if ((RTGCUINTPTR)pvFault - (RTGCUINTPTR)pMapping->GCPtr < pMapping->cb)
078edd33e3a22f968a7aee5a9e042bca314c2f03vboxsync * The first thing we check is if we've got an undetected conflict.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync while (iPT-- > 0)
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0eConflicts);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync Log(("Trap0e: Detected Conflict %VGv-%VGv\n", pMapping->GCPtr, pMapping->GCPtrLast));
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync VM_FF_SET(pVM, VM_FF_PGM_SYNC_CR3); /** @todo no need to do global sync,right? */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync * Check if the fault address is in a virtual page access handler range.
41802163cf830caf53a1a619fed55afb52246eacvboxsync PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->HyperVirtHandlers, pvFault);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync && (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb
4e4a8e041182dff5c52892bee58a5d8f58f4bdadvboxsync rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync rc = VINF_EM_RAW_EMULATE_INSTR; /* can't happen with VMX */
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync STAM_COUNTER_INC(&pVM->pgm.s.StatTrap0eMapHandler);
64743f0e8609782598af145a21b5dbdd23f3853evboxsync * Pretend we're not here and let the guest handle the trap.
97f795f0be0c10aae9b31cc8f8d5cdf8ea2ab421vboxsync LogFlow(("PGM: Mapping access -> route trap to recompiler!\n"));
return VINF_EM_RAW_GUEST_TRAP;
} /* pgmAreMappingsEnabled(&pVM->pgm.s) */
# ifdef IN_GC
if (pPTSrc == 0)
PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->PhysHandlers, GCPhysFault);
if (pCur)
# ifdef PGM_SYNC_N_PAGES
STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndPhys; });
return rc;
("Unexpected trap for physical handler: %08X (phys=%08x) HCPhys=%X uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
rc = pCur->CTXALLSUFF(pfnHandler)(pVM, uErr, pRegFrame, pvFault, GCPhysFault, CTXALLSUFF(pCur->pvUser));
return rc;
# ifdef PGM_SYNC_N_PAGES
STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndVirt; });
return rc;
/** @note r=svl: true, but lookup on virtual address should remain as a fallback as phys & virt trees might be out of sync, because the
PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->VirtHandlers, pvFault);
if (pCur)
("Unexpected trap for virtual handler: %VGv (phys=%VGp) HCPhys=%HGp uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
# ifdef IN_GC
rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
return rc;
unsigned iPage;
if ( pCur
# ifdef IN_GC
RTGCUINTPTR off = (iPage << PAGE_SHIFT) + ((RTGCUINTPTR)pvFault & PAGE_OFFSET_MASK) - ((RTGCUINTPTR)pCur->GCPtr & PAGE_OFFSET_MASK);
return rc;
STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndPhys; });
return rc;
/** @todo This particular case can cause quite a lot of overhead. E.g. early stage of kernel booting in Ubuntu 6.06
return rc;
PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->VirtHandlers, pvFault);
if (pCur)
("Unexpected trap for virtual handler: %08X (phys=%08x) HCPhys=%X uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
# ifdef IN_GC
rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
return rc;
# ifdef PGM_OUT_OF_SYNC_IN_GC
pvFault, pRegFrame->eip, PdeSrc.n.u1User, fPageGst, GCPhys, CSAMDoesPageNeedScanning(pVM, (RTGCPTR)pRegFrame->eip)));
/* Note: can't check for X86_TRAP_ID bit, because that requires execute disable support on the CPU */
# ifdef CSAM_DETECT_NEW_CODE_PAGES
return rc;
# ifdef CSAM_DETECT_NEW_CODE_PAGES
rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)pRegFrame->eip, &PC);
return VINF_SUCCESS;
* Note: Do NOT use PGM_SYNC_NR_PAGES here. That only works if the page is not present, which is not true in this case.
# ifdef VBOX_STRICT
LogFlow(("Obsolete physical monitor page out of sync %VGv - phys %VGp flags=%08llx\n", pvFault, GCPhys, (uint64_t)fPageGst));
STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncObsHnd; });
return VINF_SUCCESS;
# ifdef VBOX_STRICT
AssertMsg((fPageShw & ~(X86_PTE_A | X86_PTE_D | X86_PTE_AVL_MASK)) == (fPageGst & ~(X86_PTE_A | X86_PTE_D | X86_PTE_AVL_MASK)),
("Page flags mismatch! pvFault=%p GCPhys=%VGp fPageShw=%08llx fPageGst=%08llx\n", pvFault, GCPhys, fPageShw, fPageGst));
return VINF_EM_RAW_GUEST_TRAP;
return VINF_EM_RAW_EMULATE_INSTR;
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
unsigned iPDSrc;
# ifdef IN_RING3
&& fIsBigPage
return VINF_SUCCESS;
pPdeDst->u = 0;
# ifdef PGM_SYNC_ACCESSED_BIT
pPdeDst->u = 0;
else if (!fIsBigPage)
# ifdef PGMPOOL_WITH_USER_TRACKING
/* This is very unlikely with caching/monitoring enabled. */
LogFlow(("InvalidatePage: Out-of-sync at %VGp PdeSrc=%RX64 PdeDst=%RX64 ShwGCPhys=%VGp iPDDst=%#x\n",
pPdeDst->u = 0;
# ifdef PGM_SYNC_DIRTY_BIT
&& ( PdeSrc.b.u1Dirty /** @todo rainy day: What about read-only 4M pages? not very common, but still... */
LogFlow(("Skipping flush for big page containing %VGv (PD=%X)-> nothing has changed!\n", GCPtrPage, iPDSrc));
return VINF_SUCCESS;
pPdeDst->u = 0;
pPdeDst->u = 0;
return rc;
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
#ifdef PGMPOOL_WITH_USER_TRACKING
DECLINLINE(void) PGM_BTH_NAME(SyncPageWorkerTrackDeref)(PVM pVM, PPGMPOOLPAGE pShwPage, RTHCPHYS HCPhys)
# ifdef PGMPOOL_WITH_GCPHYS_TRACKING
LogFlow(("SyncPageWorkerTrackDeref: Damn HCPhys=%VHp pShwPage->idx=%#x!!!\n", HCPhys, pShwPage->idx));
pRam;
while (iPage-- > 0)
DECLINLINE(void) PGM_BTH_NAME(SyncPageWorkerTrackAddref)(PVM pVM, PPGMPOOLPAGE pShwPage, uint16_t u16, PPGMPAGE pPage, const unsigned iPTDst)
# ifdef PGMPOOL_WITH_GCPHYS_TRACKING
if (!u16)
u16, pPage->HCPhys, (pPage->HCPhys & MM_RAM_FLAGS_NO_REFS_MASK) | ((uint64_t)u16 << MM_RAM_FLAGS_CREFS_SHIFT), iPTDst));
DECLINLINE(void) PGM_BTH_NAME(SyncPageWorker)(PVM pVM, PSHWPTE pPteDst, GSTPDE PdeSrc, GSTPTE PteSrc, PPGMPOOLPAGE pShwPage, unsigned iPTDst)
/** @todo r=bird: Are we actually handling dirty and access bits for pages with access handlers correctly? No. */
PteDst.u = (PteSrc.u & ~(X86_PTE_PAE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_PAT | X86_PTE_PCD | X86_PTE_PWT | X86_PTE_RW))
PteDst.u = 0;
#ifdef PGM_SYNC_DIRTY_BIT
# ifdef PGM_SYNC_ACCESSED_BIT
PteDst.u = 0;
* If the page is not flagged as dirty and is writable, then make it read-only, so we can set the dirty bit
PteDst.u = (PteSrc.u & ~(X86_PTE_PAE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_PAT | X86_PTE_PCD | X86_PTE_PWT | X86_PTE_RW))
PteDst.u = (PteSrc.u & ~(X86_PTE_PAE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_PAT | X86_PTE_PCD | X86_PTE_PWT))
#ifdef PGMPOOL_WITH_USER_TRACKING
PGM_BTH_NAME(SyncPageWorkerTrackAddref)(pVM, pShwPage, HCPhys >> MM_RAM_FLAGS_IDX_SHIFT, pPage, iPTDst);
Log2(("SyncPageWorker: deref! *pPteDst=%RX64 PteDst=%RX64\n", (uint64_t)pPteDst->u, (uint64_t)PteDst.u));
PGM_BTH_NAME(SyncPageWorkerTrackAddref)(pVM, pShwPage, HCPhys >> MM_RAM_FLAGS_IDX_SHIFT, pPage, iPTDst);
#ifdef PGMPOOL_WITH_USER_TRACKING
pPteDst->u = 0;
PGM_BTH_DECL(int, SyncPage)(PVM pVM, GSTPDE PdeSrc, RTGCUINTPTR GCPtrPage, unsigned cPages, unsigned uErr)
if (!fBigPage)
# ifdef PGM_SYNC_ACCESSED_BIT
if (!fBigPage)
# ifdef PGM_SYNC_N_PAGES
const unsigned offPTSrc = 0;
iPTDst = 0;
RTGCUINTPTR GCPtrCurPage = ((RTGCUINTPTR)GCPtrPage & ~(RTGCUINTPTR)(GST_PT_MASK << GST_PT_SHIFT)) | ((offPTSrc + iPTDst) << PAGE_SHIFT);
#ifndef IN_RING0
RTGCPHYS GCPhys = (PdeSrc.u & GST_PDE_BIG_PG_MASK) | ((RTGCUINTPTR)GCPtrPage & GST_BIG_PAGE_OFFSET_MASK);
PteDst.u = (PdeSrc.u & ~(X86_PTE_PAE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_PAT | X86_PTE_PCD | X86_PTE_PWT))
PteDst.u = 0;
# ifdef PGMPOOL_WITH_USER_TRACKING
PGM_BTH_NAME(SyncPageWorkerTrackAddref)(pVM, pShwPage, HCPhys >> MM_RAM_FLAGS_IDX_SHIFT, pPage, iPTDst);
# ifdef PGM_SYNC_DIRTY_BIT
return VINF_SUCCESS;
# ifdef PGM_SYNC_ACCESSED_BIT
return VINF_PGM_SYNCPAGE_MODIFIED_PDE;
# ifdef PGM_SYNC_N_PAGES
const unsigned offPTSrc = 0;
iPTDst = 0;
RTGCUINTPTR GCPtrCurPage = ((RTGCUINTPTR)GCPtrPage & ~(RTGCUINTPTR)(GST_PT_MASK << GST_PT_SHIFT)) | ((offPTSrc + iPTDst) << PAGE_SHIFT);
RTGCUINTPTR GCPtrCurPage = ((RTGCUINTPTR)GCPtrPage & ~(RTGCUINTPTR)(GST_PT_MASK << GST_PT_SHIFT)) | ((offPTSrc + iPTDst) << PAGE_SHIFT);
return VINF_SUCCESS;
return VERR_INTERNAL_ERROR;
# ifdef PGM_SYNC_DIRTY_BIT
PGM_BTH_DECL(int, CheckPageFault)(PVM pVM, uint32_t uErr, PSHWPDE pPdeDst, PGSTPDE pPdeSrc, RTGCUINTPTR GCPtrPage)
# ifdef IN_GC
return VINF_EM_RAW_GUEST_TRAP;
return VINF_PGM_HANDLED_DIRTY_BIT_FAULT;
return VINF_PGM_NO_DIRTY_BIT_TRACKING;
# ifdef IN_GC
return VINF_EM_RAW_GUEST_TRAP;
return VINF_SUCCESS;
if (pShwPage)
# ifdef VBOX_STRICT
if (pPage)
("Unexpected dirty bit tracking on monitored page %VGv (phys %VGp)!!!!!!\n", GCPtrPage, pPteSrc->u & X86_PTE_PAE_PG_MASK));
return VINF_PGM_HANDLED_DIRTY_BIT_FAULT;
# ifdef VBOX_STRICT
return VINF_PGM_NO_DIRTY_BIT_TRACKING;
return rc;
# ifndef IN_RING3
return VERR_ADDRESS_CONFLICT;
return rc;
if (fPageTable)
if (fPageTable)
| (PdeSrc.u & ~(GST_PDE_PG_MASK | X86_PDE_AVL_MASK | X86_PDE_PCD | X86_PDE_PWT | X86_PDE_PS | X86_PDE4M_G | X86_PDE4M_D));
| (PdeSrc.u & ~(GST_PDE_PG_MASK | X86_PDE_AVL_MASK | X86_PDE_PCD | X86_PDE_PWT | X86_PDE_PS | X86_PDE4M_G | X86_PDE4M_D));
return VINF_SUCCESS;
return VINF_PGM_SYNC_CR3;
# ifdef PGM_SYNC_DIRTY_BIT
if (fPageTable)
| (PdeSrc.u & ~(GST_PDE_PG_MASK | X86_PDE_AVL_MASK | X86_PDE_PCD | X86_PDE_PWT | X86_PDE_PS | X86_PDE4M_G | X86_PDE4M_D));
# ifdef PGM_SYNC_N_PAGES
iPTDst = 0;
unsigned iPTDst = 0;
const unsigned offPTSrc = 0;
# ifndef IN_RING0
Log2(("SyncPT: 4K+ %VGv PteSrc:{P=%d RW=%d U=%d raw=%08llx}%s dst.raw=%08llx iPTSrc=%x PdeSrc.u=%x physpte=%VGp\n",
pPTDst->a[iPTDst].u & PGM_PTFLAGS_TRACK_DIRTY ? " Track-Dirty" : "", pPTDst->a[iPTDst].u, iPTSrc, PdeSrc.au32[0],
* @todo It might be more efficient to sync only a part of the 4MB page (similar to what we do for 4kb PDs).
| (PdeSrc.u & ~(GST_PDE_PG_MASK | X86_PDE_AVL_MASK | X86_PDE_PCD | X86_PDE_PWT | X86_PDE_PS | X86_PDE4M_G | X86_PDE4M_D));
# ifdef PGM_SYNC_DIRTY_BIT
PteDstBase.u = PdeSrc.u & ~(GST_PDE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_PAT | X86_PTE_PCD | X86_PTE_PWT);
unsigned iPTDst = 0;
# ifdef IN_RING3
return rc;
PteDst.u = 0;
# ifndef IN_RING0
PteDst.u = 0;
# ifdef PGMPOOL_WITH_USER_TRACKING
PGM_BTH_NAME(SyncPageWorkerTrackAddref)(pVM, pShwPage, pPage->HCPhys >> MM_RAM_FLAGS_IDX_SHIFT, pPage, iPTDst); /** @todo PAGE FLAGS */
(RTGCPTR)(GCPtr | (iPTDst << SHW_PT_SHIFT)), PteDst.n.u1Present, PteDst.n.u1Write, PteDst.n.u1User, (uint64_t)PteDst.u,
iHCPage++;
iPTDst++;
else if (pRam)
iPTDst++;
# ifdef IN_GC
return rc;
rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)GCPtrPage, PGM_SYNC_NR_PAGES, 0 /* page not present */);
return rc;
return VERR_INTERNAL_ERROR;
#if (PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_REAL || PGM_GST_TYPE == PGM_TYPE_PROT || PGM_GST_TYPE == PGM_TYPE_PAE) && PGM_SHW_TYPE != PGM_TYPE_AMD64
unsigned iPDSrc;
const unsigned iPDSrc = 0;
# ifdef PGM_SYNC_ACCESSED_BIT
return rc;
return VERR_INTERNAL_ERROR;
PGM_BTH_DECL(int, VerifyAccessSyncPage)(PVM pVM, RTGCUINTPTR GCPtrPage, unsigned fPage, unsigned uErr)
#if (PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_REAL || PGM_GST_TYPE == PGM_TYPE_PROT || PGM_GST_TYPE == PGM_TYPE_PAE) && PGM_SHW_TYPE != PGM_TYPE_AMD64
# ifndef IN_RING0
unsigned iPDSrc;
const unsigned iPDSrc = 0;
return rc;
return VINF_EM_RAW_GUEST_TRAP;
return rc;
return VERR_INTERNAL_ERROR;
return BTH_PGMPOOLKIND_PT_FOR_PT;
return BTH_PGMPOOLKIND_PT_FOR_BIG;
#define MY_STAM_COUNTER_INC(a) do { } while (0)
#ifdef PGMPOOL_WITH_MONITORING
# ifdef IN_RING3
return VINF_PGM_SYNC_CR3;
MY_STAM_COUNTER_INC(fGlobal ? &pVM->pgm.s.CTXMID(Stat,SyncCR3Global) : &pVM->pgm.s.CTXMID(Stat,SyncCR3NotGlobal));
# ifndef IN_GC
unsigned iPdNoMapping;
pMapping = 0;
iPdNoMapping = ~0U;
unsigned iPDSrc;
# ifdef IN_RING3
return rc;
return VINF_PGM_SYNC_CR3;
for (unsigned i = 0, iPdShw = iPD * 2; i < 2; i++, iPdShw++) /* pray that the compiler unrolls this */
|| ( !fGlobal
# ifdef VBOX_WITH_STATISTICS
if ( !fGlobal
/** @todo a replacement strategy isn't really needed unless we're using a very small pool < 512 pages.
* The whole ageing stuff should be put in yet another set of #ifdefs. For now, let's just skip it. */
pPDEDst->u = 0;
pPDEDst++;
for (unsigned i = 0, iPdShw = iPD * 2; i < 2; i++, iPdShw++) /* pray that the compiler unrolls this */
pgmPoolFreeByPage(pPool, pgmPoolGetPage(pPool, pPDEDst->u & SHW_PDE_PG_MASK), SHW_POOL_ROOT_IDX, iPdShw);
pPDEDst->u = 0;
pPDEDst++;
iPdNoMapping = ~0U;
# ifdef IN_RING3
return rc;
return VINF_PGM_SYNC_CR3;
if (pMapping)
AssertFailed();
return VINF_SUCCESS;
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
#ifdef VBOX_STRICT
#ifdef IN_GC
#ifdef IN_RING3
PGMR3DECL(int) PGMR3DumpHierarchyHC(PVM pVM, uint32_t cr3, uint32_t cr4, bool fLongMode, unsigned cMaxDepth, PCDBGFINFOHLP pHlp);
PGM_BTH_DECL(unsigned, AssertCR3)(PVM pVM, uint32_t cr3, uint32_t cr4, RTGCUINTPTR GCPtr, RTGCUINTPTR cb)
unsigned cErrors = 0;
int rc;
AssertMsgReturn(HCPhys == HCPhysShw, ("HCPhys=%VHp HCPhyswShw=%VHp (cr3)\n", HCPhys, HCPhysShw), false);
# ifdef IN_RING3
AssertMsgReturn((cr3 & GST_CR3_PAGE_MASK) == GCPhys, ("GCPhys=%VGp cr3=%VGp\n", GCPhys, (RTGCPHYS)cr3), false);
AssertMsgFailed(("Mapping shall only have PGM_PDFLAGS_MAPPING set! PdeDst.u=%#RX64\n", (uint64_t)PdeDst.u));
cErrors++;
if (!pPoolPage)
cErrors++;
AssertMsgFailed(("PDE flags PWT and/or PCD is set at %VGv! These flags are not virtualized! PdeDst=%#RX64\n",
cErrors++;
cErrors++;
cErrors++;
cErrors++;
!= (!PdeSrc.b.u1Size || !(cr4 & X86_CR4_PSE) ? BTH_PGMPOOLKIND_PT_FOR_PT : BTH_PGMPOOLKIND_PT_FOR_BIG))
cErrors++;
if (!pPhysPage)
cErrors++;
cErrors++;
AssertMsgFailed(("Cannot map/convert guest physical address %VGp in the PDE at %VGv! PdeSrc=%#RX64\n",
cErrors++;
/// @todo We get here a lot on out-of-sync CR3 entries. The access handler should zap them to avoid false alarms here!
cErrors++;
cErrors++;
const unsigned offPTSrc = 0;
if (!(PteDst.u & (X86_PTE_P | PGM_PTFLAGS_TRACK_DIRTY))) /** @todo deal with ALL handlers and CSAM !P pages! */
#ifdef IN_RING3
AssertMsgFailed(("Out of sync (!P) PTE at %VGv! PteSrc=%#RX64 PteDst=%#RX64 pPTSrc=%VGv iPTSrc=%x PdeSrc=%x physpte=%VGp\n",
cErrors++;
uint64_t fIgnoreFlags = GST_PTE_PG_MASK | X86_PTE_AVL_MASK | X86_PTE_G | X86_PTE_D | X86_PTE_PWT | X86_PTE_PCD | X86_PTE_PAT;
# ifdef IN_RING3
cErrors++;
AssertMsgFailed(("Out of sync (phys) at %VGv! HCPhysShw=%VHp HCPhys=%VHp GCPhysGst=%VGp PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
if (!pPhysPage)
cErrors++;
AssertMsgFailed(("Invalid guest page at %VGv is writable! GCPhysGst=%VGp PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("Out of sync (phys) at %VGv! HCPhysShw=%VHp HCPhys=%VHp GCPhysGst=%VGp PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("WRITE access flagged at %VGv but the page is writable! HCPhys=%VGv PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("ALL access flagged at %VGv but the page is present! HCPhys=%VHp PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
cErrors++;
cErrors++;
cErrors++;
AssertMsgFailed(("!DIRTY page at %VGv is has mismatching accessed bit! PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("PGM_PTFLAGS_TRACK_DIRTY set at %VGv but no accessed bit emulation! PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("!ACCESSED page at %VGv is has the accessed bit set! PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
# ifdef DEBUG_sandervl
AssertMsgFailed(("Flags mismatch at %VGv! %#RX64 != %#RX64 fIgnoreFlags=%#RX64 PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
uint64_t fIgnoreFlags = X86_PDE_AVL_MASK | GST_PDE_PG_MASK | X86_PDE4M_G | X86_PDE4M_D | X86_PDE4M_PS | X86_PDE4M_PWT | X86_PDE4M_PCD;
cErrors++;
cErrors++;
AssertMsgFailed(("!DIRTY page at %VGv is has mismatching accessed bit! PteSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("PGM_PDFLAGS_TRACK_DIRTY set at %VGv but no accessed bit emulation! PdeSrc=%#RX64 PdeDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("!ACCESSED page at %VGv is has the accessed bit set! PdeSrc=%#RX64 PdeDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("Flags mismatch (B) at %VGv! %#RX64 != %#RX64 fIgnoreFlags=%#RX64 PdeSrc=%#RX64 PdeDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("The PTE at %VGv emulating a 2/4M page is marked TRACK_DIRTY! PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
# ifdef IN_RING3
cErrors++;
AssertMsgFailed(("Out of sync (phys) at %VGv! HCPhysShw=%VHp HCPhys=%VHp GCPhysGst=%VGp PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
if (!pPhysPage)
cErrors++;
AssertMsgFailed(("Invalid guest page at %VGv is writable! GCPhysGst=%VGp PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("Out of sync (phys) at %VGv! HCPhysShw=%VHp HCPhys=%VHp GCPhysGst=%VGp PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("WRITE access flagged at %VGv but the page is writable! HCPhys=%VGv PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
AssertMsgFailed(("ALL access flagged at %VGv but the page is present! HCPhys=%VGv PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
&& (PdeSrc.u & ~(fIgnoreFlags | X86_PTE_RW)) != (PteDst.u & ~fIgnoreFlags) /* lazy phys handler dereg. */
AssertMsgFailed(("Flags mismatch (BT) at %VGv! %#RX64 != %#RX64 fIgnoreFlags=%#RX64 PdeSrc=%#RX64 PteDst=%#RX64\n",
cErrors++;
# ifdef DEBUG
if (cErrors)
return cErrors;