PGMAllBth.h revision 3ecf9412133496b2aeb090cfd33a286404ec59fb
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * VBox - Page Manager, Shadow+Guest Paging Template - All context code.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * This file is a big challenge!
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Copyright (C) 2006-2007 Sun Microsystems, Inc.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * This file is part of VirtualBox Open Source Edition (OSE), as
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * available from http://www.virtualbox.org. This file is free software;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * you can redistribute it and/or modify it under the terms of the GNU
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * General Public License (GPL) as published by the Free Software
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Foundation, in version 2 as it comes in the "COPYING" file of the
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Clara, CA 95054 USA or visit http://www.sun.com if you need
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * additional information or have any questions.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland/*******************************************************************************
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland* Internal Functions *
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland*******************************************************************************/
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, Trap0eHandler)(PVM pVM, RTGCUINT uErr, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, InvalidatePage)(PVM pVM, RTGCUINTPTR GCPtrPage);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, SyncPage)(PVM pVM, GSTPDE PdeSrc, RTGCUINTPTR GCPtrPage, unsigned cPages, unsigned uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, CheckPageFault)(PVM pVM, uint32_t uErr, PSHWPDE pPdeDst, PGSTPDE pPdeSrc, RTGCUINTPTR GCPtrPage);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, SyncPT)(PVM pVM, unsigned iPD, PGSTPD pPDSrc, RTGCUINTPTR GCPtrPage);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, VerifyAccessSyncPage)(PVM pVM, RTGCUINTPTR Addr, unsigned fPage, unsigned uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, PrefetchPage)(PVM pVM, RTGCUINTPTR GCPtrPage);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, SyncCR3)(PVM pVM, uint64_t cr0, uint64_t cr3, uint64_t cr4, bool fGlobal);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(unsigned, AssertCR3)(PVM pVM, uint64_t cr3, uint64_t cr4, RTGCUINTPTR GCPtr = 0, RTGCUINTPTR cb = ~(RTGCUINTPTR)0);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandDECLINLINE(void) PGM_BTH_NAME(SyncPageWorkerTrackDeref)(PVM pVM, PPGMPOOLPAGE pShwPage, RTHCPHYS HCPhys);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland/* Filter out some illegal combinations of guest and shadow paging, so we can remove redundant checks inside functions. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#if PGM_GST_TYPE == PGM_TYPE_PAE && PGM_SHW_TYPE != PGM_TYPE_PAE && PGM_SHW_TYPE != PGM_TYPE_NESTED
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# error "Invalid combination; PAE guest implies PAE shadow"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#if (PGM_GST_TYPE == PGM_TYPE_REAL || PGM_GST_TYPE == PGM_TYPE_PROT) \
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && !(PGM_SHW_TYPE == PGM_TYPE_32BIT || PGM_SHW_TYPE == PGM_TYPE_PAE || PGM_SHW_TYPE == PGM_TYPE_AMD64 || PGM_SHW_TYPE == PGM_TYPE_NESTED)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# error "Invalid combination; real or protected mode without paging implies 32 bits or PAE shadow paging."
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#if (PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_PAE) \
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && !(PGM_SHW_TYPE == PGM_TYPE_32BIT || PGM_SHW_TYPE == PGM_TYPE_PAE || PGM_SHW_TYPE == PGM_TYPE_NESTED)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# error "Invalid combination; 32 bits guest paging or PAE implies 32 bits or PAE shadow paging."
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#if (PGM_GST_TYPE == PGM_TYPE_AMD64 && PGM_SHW_TYPE != PGM_TYPE_AMD64 && PGM_SHW_TYPE != PGM_TYPE_NESTED) \
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || (PGM_SHW_TYPE == PGM_TYPE_AMD64 && PGM_GST_TYPE != PGM_TYPE_AMD64 && PGM_GST_TYPE != PGM_TYPE_PROT)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# error "Invalid combination; AMD64 guest implies AMD64 shadow and vice versa"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#ifdef IN_RING0 /* no mappings in VT-x and AMD-V mode */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * #PF Handler for raw-mode guest execution.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @returns VBox status code (appropriate for trap handling and GC return).
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param pVM VM Handle.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param uErr The trap error code.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param pRegFrame Trap register frame.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param pvFault The fault address.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, Trap0eHandler)(PVM pVM, RTGCUINT uErr, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#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_GST_TYPE == PGM_TYPE_AMD64) \
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# if PGM_SHW_TYPE == PGM_TYPE_PAE && PGM_GST_TYPE != PGM_TYPE_PAE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Hide the instruction fetch trap indicator for now.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @todo NXE will change this and we must fix NXE in the switcher too! */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDSrc = (RTGCUINTPTR)pvFault >> GST_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PGSTPD pPDSrc = CTXSUFF(pVM->pgm.s.pGuestPD);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# elif PGM_GST_TYPE == PGM_TYPE_PAE || PGM_GST_TYPE == PGM_TYPE_AMD64
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PGSTPD pPDSrc = pgmGstGetPaePDPtr(&pVM->pgm.s, (RTGCUINTPTR)pvFault, &iPDSrc);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland pPDSrc = pgmGstGetLongModePDPtr(&pVM->pgm.s, pvFault, &pPml4eSrc, &PdpeSrc, &iPDSrc);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Quick check for a valid guest trap. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("Trap0eHandler: guest PDPTR not present CR3=%VGp\n", (CPUMGetGuestCR3(pVM) & X86_CR3_PAGE_MASK)));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eGuestTrap; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDSrc = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDDst = (RTGCUINTPTR)pvFault >> SHW_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PD pPDDst = pVM->pgm.s.CTXMID(p,32BitPD);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDDst = (RTGCUINTPTR)pvFault >> SHW_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PDPAE pPDDst = pVM->pgm.s.CTXMID(ap,PaePDs)[0]; /* We treat this as a PD with 2048 entries, so no need to and with SHW_PD_MASK to get iPDDst */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Did we mark the PDPT as not present in SyncCR3? */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned iPDPTE = ((RTGCUINTPTR)pvFault >> SHW_PDPT_SHIFT) & SHW_PDPT_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!pVM->pgm.s.CTXMID(p,PaePDPT)->a[iPDPTE].n.u1Present)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland pVM->pgm.s.CTXMID(p,PaePDPT)->a[iPDPTE].n.u1Present = 1;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDDst = (((RTGCUINTPTR)pvFault >> SHW_PD_SHIFT) & SHW_PD_MASK);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* AMD-V nested paging */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Fake PML4 & PDPT entry; access control handled on the page table level, so allow everything. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Pml4eSrc.u = X86_PML4E_P | X86_PML4E_RW | X86_PML4E_US | X86_PML4E_NX | X86_PML4E_A;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PdpeSrc.u = X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US | X86_PDPE_NX | X86_PDPE_A;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMShwSyncLongModePDPtr(pVM, (RTGCUINTPTR)pvFault, pPml4eSrc, &PdpeSrc, &pPDDst);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg(rc == VINF_PGM_SYNC_CR3, ("Unexpected rc=%Vrc\n", rc));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * If we successfully correct the write protection fault due to dirty bit
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * tracking, or this page fault is a genuine one, then return immediately.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_START(&pVM->pgm.s.StatCheckPageFault, e);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(CheckPageFault)(pVM, uErr, &pPDDst->a[iPDDst], &pPDSrc->a[iPDSrc], (RTGCUINTPTR)pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatCheckPageFault, e);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland = rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? &pVM->pgm.s.StatTrap0eDirtyAndAccessedBits : &pVM->pgm.s.StatTrap0eGuestTrap; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogBird(("Trap0eHandler: returns %s\n", rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? "VINF_SUCCESS" : "VINF_EM_RAW_GUEST_TRAP"));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return rc == VINF_PGM_HANDLED_DIRTY_BIT_FAULT ? VINF_SUCCESS : rc;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0ePD[iPDSrc]);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * A common case is the not-present error caused by lazy page table syncing.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * It is IMPORTANT that we weed out any access to non-present shadow PDEs here
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * so we can safely assume that the shadow PT is present when calling SyncPage later.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * On failure, we ASSUME that SyncPT is out of memory or detected some kind
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * of mapping conflict and defer to SyncCR3 in R3.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * (Again, we do NOT support access handlers for non-present guest pages.)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PdeSrc.au32[0] = 0; /* faked so we don't have to #ifdef everything */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( !(uErr & X86_TRAP_PF_P) /* not set means page not present instead of page protection violation */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eSyncPT; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_START(&pVM->pgm.s.StatLazySyncPT, f);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("=>SyncPT %04x = %08x\n", iPDSrc, PdeSrc.au32[0]));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPT)(pVM, iPDSrc, pPDSrc, (RTGCUINTPTR)pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatLazySyncPT, f);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Log(("SyncPT: %d failed!! rc=%d\n", iPDSrc, rc));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland VM_FF_SET(pVM, VM_FF_PGM_SYNC_CR3); /** @todo no need to do global sync, right? */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatLazySyncPT, f);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Check if this address is within any of our mappings.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * This is *very* fast and it's gonna save us a bit of effort below and prevent
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * us from screwing ourself with MMIO2 pages which have a GC Mapping (VRam).
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * (BTW, it's impossible to have physical access handlers in a mapping.)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_START(&pVM->pgm.s.StatMapping, a);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PPGMMAPPING pMapping = CTXALLSUFF(pVM->pgm.s.pMappings);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland for ( ; pMapping; pMapping = CTXALLSUFF(pMapping->pNext))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((RTGCUINTPTR)pvFault < (RTGCUINTPTR)pMapping->GCPtr)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((RTGCUINTPTR)pvFault - (RTGCUINTPTR)pMapping->GCPtr < pMapping->cb)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * The first thing we check is if we've got an undetected conflict.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned iPT = pMapping->cb >> GST_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland while (iPT-- > 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0eConflicts);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Log(("Trap0e: Detected Conflict %VGv-%VGv\n", pMapping->GCPtr, pMapping->GCPtrLast));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland VM_FF_SET(pVM, VM_FF_PGM_SYNC_CR3); /** @todo no need to do global sync,right? */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatMapping, a);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Check if the fault address is in a virtual page access handler range.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->HyperVirtHandlers, pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = VINF_EM_RAW_EMULATE_INSTR; /* can't happen with VMX */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatTrap0eMapHandler);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatMapping, a);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Pretend we're not here and let the guest handle the trap.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland TRPMSetErrorCode(pVM, uErr & ~X86_TRAP_PF_P);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0eMap);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("PGM: Mapping access -> route trap to recompiler!\n"));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatMapping, a);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatMapping, a);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } /* pgmAreMappingsEnabled(&pVM->pgm.s) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Check if this fault address is flagged for special treatment,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * which means we'll have to figure out the physical address and
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * check flags associated with it.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * ASSUME that we can limit any special access handling to pages
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * in page tables which the guest believes to be present.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland bool fBigPagesSupported = !!(CPUMGetGuestCR4(pVM) & X86_CR4_PSE);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland | ((RTGCPHYS)pvFault & (GST_BIG_PAGE_OFFSET_MASK ^ PAGE_OFFSET_MASK));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_GCPHYS_2_PTR(pVM, PdeSrc.u & GST_PDE_PG_MASK, &pPTSrc);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned iPTESrc = ((RTGCUINTPTR)pvFault >> GST_PT_SHIFT) & GST_PT_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland GCPhys = pPTSrc->a[iPTESrc].u & GST_PTE_PG_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* No paging so the fault address is the physical address */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland GCPhys = (RTGCPHYS)((RTGCUINTPTR)pvFault & ~PAGE_OFFSET_MASK);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * If we have a GC address we'll check if it has any flags set.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_START(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = pgmPhysGetPageEx(&pVM->pgm.s, GCPhys, &pPage);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (PGM_PAGE_HAS_ANY_PHYSICAL_HANDLERS(pPage))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Physical page access handler.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const RTGCPHYS GCPhysFault = GCPhys | ((RTGCUINTPTR)pvFault & PAGE_OFFSET_MASK);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->PhysHandlers, GCPhysFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * If the region is write protected and we got a page not present fault, then sync
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * the pages. If the fault was caused by a read, then restart the instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * In case of write access continue to the GC write handler.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * ASSUMES that there is only one handler per page or that they have similar write properties.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, PGM_SYNC_NR_PAGES, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersOutOfSync);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndPhys; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg( pCur->enmType != PGMPHYSHANDLERTYPE_PHYSICAL_WRITE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || (pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE && (uErr & X86_TRAP_PF_RW)),
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ("Unexpected trap for physical handler: %08X (phys=%08x) HCPhys=%X uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = pCur->CTXALLSUFF(pfnHandler)(pVM, uErr, pRegFrame, pvFault, GCPhysFault, CTXALLSUFF(pCur->pvUser));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersPhysical);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eHndPhys; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * If the region is write protected and we got a page not present fault, then sync
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * the pages. If the fault was caused by a read, then restart the instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * In case of write access continue to the GC write handler.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( PGM_PAGE_GET_HNDL_VIRT_STATE(pPage) < PGM_PAGE_HNDL_PHYS_STATE_ALL
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, PGM_SYNC_NR_PAGES, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersOutOfSync);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndVirt; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Ok, it's an virtual page access handler.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Since it's faster to search by address, we'll do that first
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * and then retry by GCPhys if that fails.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @todo r=bird: perhaps we should consider looking up by physical address directly now? */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @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
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * page was changed without us noticing it (not-present -> present without invlpg or mov cr3, xxx)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->VirtHandlers, pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg(!((RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || ( pCur->enmType != PGMVIRTHANDLERTYPE_WRITE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || (pCur->enmType == PGMVIRTHANDLERTYPE_WRITE && (uErr & X86_TRAP_PF_RW))),
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ("Unexpected trap for virtual handler: %VGv (phys=%VGp) HCPhys=%HGp uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || pCur->enmType != PGMVIRTHANDLERTYPE_WRITE ) )
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = VINF_EM_RAW_EMULATE_INSTR; /** @todo for VMX */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersVirtual);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eHndVirt; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Unhandled part of a monitored page */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Check by physical address. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = pgmHandlerVirtualFindByPhysAddr(pVM, GCPhys + ((RTGCUINTPTR)pvFault & PAGE_OFFSET_MASK),
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || pCur->enmType != PGMVIRTHANDLERTYPE_WRITE ) )
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Assert((pCur->aPhysToVirt[iPage].Core.Key & X86_PTE_PAE_PG_MASK) == GCPhys);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland RTGCUINTPTR off = (iPage << PAGE_SHIFT) + ((RTGCUINTPTR)pvFault & PAGE_OFFSET_MASK) - ((RTGCUINTPTR)pCur->GCPtr & PAGE_OFFSET_MASK);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, off);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = VINF_EM_RAW_EMULATE_INSTR; /** @todo for VMX */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersVirtualByPhys);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eHndVirt; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * There is a handled area of the page, but this fault doesn't belong to it.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * We must emulate the instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * To avoid crashing (non-fatal) in the interpreter and go back to the recompiler
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * we first check if this was a page-not-present fault for a page with only
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * write access handlers. Restart the instruction if it wasn't a write access.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersUnhandled);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( !PGM_PAGE_HAS_ACTIVE_ALL_HANDLERS(pPage)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, PGM_SYNC_NR_PAGES, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersOutOfSync);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncHndPhys; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @todo This particular case can cause quite a lot of overhead. E.g. early stage of kernel booting in Ubuntu 6.06
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * It's writing to an unhandled part of the LDT page several million times.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMInterpretInstruction(pVM, pRegFrame, pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("PGM: PGMInterpretInstruction -> rc=%d HCPhys=%RHp%s%s\n",
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PGM_PAGE_HAS_ANY_PHYSICAL_HANDLERS(pPage) ? " phys" : "",
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PGM_PAGE_HAS_ANY_VIRTUAL_HANDLERS(pPage) ? " virt" : ""));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eHndUnhandled; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } /* if any kind of handler */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * The page isn't marked, but it might still be monitored by a virtual page access handler.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * (ASSUMES no temporary disabling of virtual handlers.)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @todo r=bird: Since the purpose is to catch out of sync pages with virtual handler(s) here,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * we should correct both the shadow page table and physical memory flags, and not only check for
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * accesses within the handler region but for access to pages with virtual handlers. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRangeGet(&CTXSUFF(pVM->pgm.s.pTrees)->VirtHandlers, pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg( !((RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || ( pCur->enmType != PGMVIRTHANDLERTYPE_WRITE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || (pCur->enmType == PGMVIRTHANDLERTYPE_WRITE && (uErr & X86_TRAP_PF_RW))),
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ("Unexpected trap for virtual handler: %08X (phys=%08x) HCPhys=%X uErr=%X, enum=%d\n", pvFault, GCPhys, pPage->HCPhys, uErr, pCur->enmType));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ( (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr < pCur->cb
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || pCur->enmType != PGMVIRTHANDLERTYPE_WRITE ) )
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = CTXSUFF(pCur->pfnHandler)(pVM, uErr, pRegFrame, pvFault, pCur->GCPtr, (RTGCUINTPTR)pvFault - (RTGCUINTPTR)pCur->GCPtr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = VINF_EM_RAW_EMULATE_INSTR; /** @todo for VMX */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersVirtualUnmarked);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eHndVirt; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* When the guest accesses invalid physical memory (e.g. probing of RAM or accessing a remapped MMIO range), then we'll fall
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * back to the recompiler to emulate the instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("pgmPhysGetPageEx %VGp failed with %Vrc\n", GCPhys, rc));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHandlersInvalid);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatHandlers, b);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * We are here only if page is present in Guest page tables and trap is not handled
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * by our handlers.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Check it for page out-of-sync situation.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_START(&pVM->pgm.s.StatOutOfSync, c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Page is not present in our page tables.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Try to sync it!
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * BTW, fPageShw is invalid in this branch!
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCPageOutOfSyncUser);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else /* supervisor */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCPageOutOfSyncSupervisor);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# if defined(LOG_ENABLED) && !defined(IN_RING0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PGMGstGetPage(pVM, pvFault, &fPageGst, &GCPhys);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Log(("Page out of sync: %VGv eip=%08x PdeSrc.n.u1User=%d fPageGst=%08llx GCPhys=%VGp scan=%d\n",
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland pvFault, pRegFrame->eip, PdeSrc.n.u1User, fPageGst, GCPhys, CSAMDoesPageNeedScanning(pVM, (RTRCPTR)pRegFrame->eip)));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* LOG_ENABLED */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# if PGM_WITH_PAGING(PGM_GST_TYPE) && !defined(IN_RING0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMGstGetPage(pVM, pvFault, &fPageGst, NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Note: can't check for X86_TRAP_ID bit, because that requires execute disable support on the CPU */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || (RTGCUINTPTR)pvFault - pRegFrame->eip < 8 /* instruction crossing a page boundary */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || ( !PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && CSAMDoesPageNeedScanning(pVM, (RTRCPTR)pRegFrame->eip)) /* any new code we encounter here */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* CSAM_DETECT_NEW_CODE_PAGES */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("CSAMExecFault %VGv\n", pRegFrame->eip));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = CSAMExecFault(pVM, (RTRCPTR)pRegFrame->eip);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * CSAM needs to perform a job in ring 3.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Sync the page before going to the host context; otherwise we'll end up in a loop if
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * CSAM fails (e.g. instruction crosses a page boundary and the next page is not present)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int rc2 = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, 1, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatOutOfSync, c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eCSAM; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && pRegFrame->ecx >= 0x100 /* early check for movswd count */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* In case of a write to a non-present supervisor shadow page, we'll take special precautions
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * to detect loading of new code pages.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Decode the instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)pRegFrame->eip, &PC);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = EMInterpretDisasOneEx(pVM, (RTGCUINTPTR)PC, pRegFrame, &Cpu, &cbOp);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* For now we'll restrict this to rep movsw/d instructions */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* CSAM_DETECT_NEW_CODE_PAGES */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Mark this page as safe.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /** @todo not correct for pages that contain both code and data!! */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Log2(("CSAMMarkPage %VGv; scanned=%d\n", pvFault, true));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) && !defined(IN_RING0) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, PGM_SYNC_NR_PAGES, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* The page was successfully synced, return to the guest. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatOutOfSync, c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSync; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * A side effect of not flushing global PDEs are out of sync pages due
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * to physical monitored regions, that are no longer valid.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Assume for now it only applies to the read/write flag
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (VBOX_SUCCESS(rc) && (uErr & X86_TRAP_PF_RW))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCPageOutOfSyncUser);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else /* supervisor */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCPageOutOfSyncSupervisor);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * 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.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_BTH_NAME(SyncPage)(pVM, PdeSrc, (RTGCUINTPTR)pvFault, 1, uErr);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Page was successfully synced, return to guest.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMGstGetPage(pVM, pvFault, &fPageGst, &GCPhys);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Assert(VBOX_SUCCESS(rc) && fPageGst & X86_PTE_RW);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("Obsolete physical monitor page out of sync %VGv - phys %VGp flags=%08llx\n", pvFault, GCPhys, (uint64_t)fPageGst));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMShwGetPage(pVM, pvFault, &fPageShw, NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg(VBOX_SUCCESS(rc) && fPageShw & X86_PTE_RW, ("rc=%Vrc fPageShw=%VX64\n", rc, fPageShw));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* VBOX_STRICT */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatOutOfSync, c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_STATS({ pVM->pgm.s.CTXSUFF(pStatTrap0eAttribution) = &pVM->pgm.s.StatTrap0eOutOfSyncObsHnd; });
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Check to see if we need to emulate the instruction as X86_CR0_WP has been cleared. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && ((CPUMGetGuestCR0(pVM) & (X86_CR0_WP|X86_CR0_PG)) == X86_CR0_PG)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland && (uErr & (X86_TRAP_PF_RW | X86_TRAP_PF_P)) == (X86_TRAP_PF_RW | X86_TRAP_PF_P))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMGstGetPage(pVM, pvFault, &fPageGst, NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMInterpretInstruction(pVM, pRegFrame, pvFault);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatTrap0eWPEmulGC);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatTrap0eWPEmulR3);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsgFailed(("Unexpected r/w page %x flag=%x\n", pvFault, (uint32_t)fPageGst));
fbde34ed705bd3e0be54fb73ceb0e689cba71312Norm Jacobs * Check for VMM page flags vs. Guest page flags consistency.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Currently only for debug purposes.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Get guest page flags. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMGstGetPage(pVM, pvFault, &fPageGst, NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGMShwGetPage(pVM, pvFault, &fPageShw, NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Compare page flags.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Note: we have AVL, A, D bits desynched.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsg((fPageShw & ~(X86_PTE_A | X86_PTE_D | X86_PTE_AVL_MASK)) == (fPageGst & ~(X86_PTE_A | X86_PTE_D | X86_PTE_AVL_MASK)),
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ("Page flags mismatch! pvFault=%VGv GCPhys=%VGp fPageShw=%08llx fPageGst=%08llx\n", pvFault, GCPhys, fPageShw, fPageGst));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsgFailed(("PGMGstGetPage rc=%Vrc\n", rc));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertMsgFailed(("PGMGCGetPage rc=%Vrc\n", rc));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* VBOX_STRICT */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_PROFILE_STOP(&pVM->pgm.s.StatOutOfSync, c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_OUT_OF_SYNC_IN_GC */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Page not present in Guest OS or invalid page table address.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * This is potential virtual page access handler food.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * For the present we'll say that our access handlers don't
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * work for this case - we've already discarded the page table
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * not present case which is identical to this.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * When we perchance find we need this, we will probably have AVL
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * trees (offset based) to operate on and we can measure their speed
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * agains mapping a page table and probably rearrange this handling
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * a bit. (Like, searching virtual ranges before checking the
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * physical address.)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Conclusion, this is a guest trap.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("PGM: Unhandled #PF -> route trap to recompiler!\n"));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatGCTrap0eUnhandled);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* present, but not a monitored page; perhaps the guest is probing physical memory */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* PGM_WITH_PAGING(PGM_GST_TYPE) */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#else /* PGM_GST_TYPE != PGM_TYPE_32BIT */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland AssertReleaseMsgFailed(("Shw=%d Gst=%d is not implemented!\n", PGM_GST_TYPE, PGM_SHW_TYPE));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#endif /* PGM_GST_TYPE != PGM_TYPE_32BIT */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Emulation of the invlpg instruction.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @returns VBox status code.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param pVM VM handle.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @param GCPtrPage Page to invalidate.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @remark ASSUMES that the guest is updating before invalidating. This order
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * isn't required by the CPU, so this is speculative and could cause
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @todo Flush page or page directory only if necessary!
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * @todo Add a #define for simply invalidating the page.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah WaterlandPGM_BTH_DECL(int, InvalidatePage)(PVM pVM, RTGCUINTPTR GCPtrPage)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland LogFlow(("InvalidatePage %VGv\n", GCPtrPage));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Get the shadow PD entry and skip out if this PD isn't present.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * (Guessing that it is frequent for a shadow PDE to not be present, do this first.)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDDst = GCPtrPage >> SHW_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PDE pPdeDst = &pVM->pgm.s.CTXMID(p,32BitPD)->a[iPDDst];
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PDEPAE pPdeDst = &pVM->pgm.s.CTXMID(ap,PaePDs[0])->a[iPDDst];
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# else /* AMD64 */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPml4 = ((RTGCUINTPTR64)GCPtrPage >> X86_PML4_SHIFT) & X86_PML4_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PML4E pPml4eDst = &CTXMID(pVM->pgm.s.p,PaePML4)->a[iPml4];
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.CTXMID(Stat,InvalidatePageSkipped));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_HCPHYS_2_PTR(pVM, pPml4eDst->u & X86_PML4E_PG_MASK, &pPDPT);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDPT = ((RTGCUINTPTR64)GCPtrPage >> SHW_PDPT_SHIFT) & SHW_PDPT_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.CTXMID(Stat,InvalidatePageSkipped));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rc = PGM_HCPHYS_2_PTR(pVM, pPdpeDst->u & X86_PDPE_PG_MASK, &pPd);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPd = (GCPtrPage >> SHW_PD_SHIFT) & SHW_PD_MASK;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.CTXMID(Stat,InvalidatePageSkipped));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * Get the guest PD entry and calc big page.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PD pPDSrc = CTXSUFF(pVM->pgm.s.pGuestPD);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const unsigned iPDSrc = GCPtrPage >> GST_PD_SHIFT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PDPAE pPDSrc = pgmGstGetPaePDPtr(&pVM->pgm.s, GCPtrPage, &iPDSrc);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# else /* AMD64 */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland PX86PDPAE pPDSrc = pgmGstGetLongModePDPtr(&pVM->pgm.s, GCPtrPage, &pPml4eSrc, &PdpeSrc, &iPDSrc);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const bool fIsBigPage = PdeSrc.b.u1Size && (cr4 & X86_CR4_PSE);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * If a CR3 Sync is pending we may ignore the invalidate page operation
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * depending on the kind of sync and if it's a global page or not.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * This doesn't make sense in GC/R0 so we'll skip it entirely there.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# ifdef PGM_SKIP_GLOBAL_PAGEDIRS_ON_NONGLOBAL_FLUSH
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland || ( VM_FF_ISSET(pVM, VM_FF_PGM_SYNC_CR3_NON_GLOBAL)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (VM_FF_ISPENDING(pVM, VM_FF_PGM_SYNC_CR3 | VM_FF_PGM_SYNC_CR3_NON_GLOBAL) )
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland STAM_COUNTER_INC(&pVM->pgm.s.StatHCInvalidatePageSkipped);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland# endif /* IN_RING3 */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Assert(pPml4eDst->n.u1Present && pPml4eDst->u & SHW_PDPT_MASK);
pPml4eDst->u = 0;
pPml4eDst->u = 0;
pPml4eDst->u = 0;
return VINF_SUCCESS;
pPdpeDst->u = 0;
pPdpeDst->u = 0;
pPdpeDst->u = 0;
return VINF_SUCCESS;
pPdeDst->u = 0;
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;
&& ( 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 .u=%VX64)-> nothing has changed!\n", GCPtrPage, iPDSrc, PdeSrc.u));
return VINF_SUCCESS;
pPdeDst->u = 0;
pPdeDst->u = 0;
return rc;
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;
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)
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);
return VINF_SUCCESS;
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;
PGM_BTH_DECL(int, CheckPageFault)(PVM pVM, uint32_t uErr, PSHWPDE pPdeDst, PGSTPDE pPdeSrc, RTGCUINTPTR GCPtrPage)
bool fBigPagesSupported = true;
unsigned uPageFaultLevel;
int rc;
uPageFaultLevel = 0;
goto UpperLevelPageFault;
PX86PDPE pPdpeSrc = &pVM->pgm.s.CTXSUFF(pGstPaePDPT)->a[(GCPtrPage >> GST_PDPT_SHIFT) & GST_PDPT_MASK];
goto UpperLevelPageFault;
goto UpperLevelPageFault;
if (fWriteFault)
return VINF_PGM_HANDLED_DIRTY_BIT_FAULT;
return VINF_PGM_NO_DIRTY_BIT_TRACKING;
# ifdef IN_GC
return VINF_EM_RAW_GUEST_TRAP;
if (fWriteFault)
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;
# ifdef IN_GC
/* Check the present bit as the shadow tables can cause different error codes by being out of sync. */
return VINF_EM_RAW_GUEST_TRAP;
AssertMsg(iPDSrc == ((GCPtrPage >> GST_PD_SHIFT) & GST_PD_MASK), ("iPDSrc=%x GCPtrPage=%VGv\n", iPDSrc, GCPtrPage));
return rc;
# ifndef PGM_WITHOUT_MAPPINGS
# ifndef IN_RING3
return VERR_ADDRESS_CONFLICT;
int rc = pgmR3SyncPTResolveConflict(pVM, pMapping, pPDSrc, GCPtrPage & (GST_PD_MASK << GST_PD_SHIFT));
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;
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));
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_GST_TYPE == PGM_TYPE_AMD64) \
unsigned iPDSrc;
if (!pPDSrc)
unsigned iPDSrc;
if (!pPDSrc)
const unsigned iPDSrc = 0;
return rc;
return rc;
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_TYPE_AMD64) \
# ifndef IN_RING0
unsigned iPDSrc;
if (pPDSrc)
return VINF_EM_RAW_GUEST_TRAP;
unsigned iPDSrc;
if (!pPDSrc)
return VINF_EM_RAW_GUEST_TRAP;
const unsigned iPDSrc = 0;
return rc;
return rc;
return VINF_EM_RAW_GUEST_TRAP;
return rc;
return VERR_INTERNAL_ERROR;
#if PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_PAE || PGM_GST_TYPE == PGM_TYPE_AMD64
# if PGM_SHW_TYPE == PGM_TYPE_32BIT || PGM_SHW_TYPE == PGM_TYPE_PAE || PGM_SHW_TYPE == PGM_TYPE_AMD64
return BTH_PGMPOOLKIND_PT_FOR_PT;
return BTH_PGMPOOLKIND_PT_FOR_BIG;
#define MY_STAM_COUNTER_INC(a) do { } while (0)
return VINF_SUCCESS;
# 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));
# if PGM_GST_TYPE == PGM_TYPE_32BIT || PGM_GST_TYPE == PGM_TYPE_PAE || PGM_GST_TYPE == PGM_TYPE_AMD64
bool fBigPagesSupported = true;
# ifndef IN_GC
unsigned iPdNoMapping;
pMapping = 0;
iPdNoMapping = ~0U;
unsigned iPDSrc;
/* Mark it as not present if there's no hypervisor mapping present. (bit flipped at the top of Trap0eHandler) */
AssertMsg(&pVM->pgm.s.CTXMID(ap,PaePDs)[iPD * 2 / 512]->a[iPD * 2 % 512] == pPDEDst, ("%p vs %p\n", &pVM->pgm.s.CTXMID(ap,PaePDs)[iPD * 2 / 512]->a[iPD * 2 % 512], pPDEDst));
&& !defined(PGM_WITHOUT_MAPPINGS)
pPDEDst += cPTs + (PGM_GST_TYPE != PGM_SHW_TYPE) * cPTs; /* Only applies to the pae shadow and 32 bits guest case */
# ifdef IN_RING3
int rc = pgmR3SyncPTResolveConflictPAE(pVM, pMapping, (iPDPTE << GST_PDPT_SHIFT) + (iPD << GST_PD_SHIFT));
return rc;
return VINF_PGM_SYNC_CR3;
# else /* (PGM_GST_TYPE != PGM_TYPE_32BIT && PGM_GST_TYPE != PGM_TYPE_PAE) || PGM_WITHOUT_MAPPINGS */
# endif /* (PGM_GST_TYPE != PGM_TYPE_32BIT && PGM_GST_TYPE != PGM_TYPE_PAE) || PGM_WITHOUT_MAPPINGS */
for (unsigned i = 0, iPdShw = iPD * 2; i < 2; i++, iPdShw++) /* pray that the compiler unrolls this */
|| !fBigPagesSupported)
|| ( !fGlobal
|| ( fBigPagesSupported
# 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), pShwPdpte->idx, iPdShw);
pgmPoolFreeByPage(pPool, pgmPoolGetPage(pPool, pPDEDst->u & SHW_PDE_PG_MASK), SHW_POOL_ROOT_IDX, iPdShw);
pPDEDst->u = 0;
pPDEDst++;
&& !defined(PGM_WITHOUT_MAPPINGS)
iPdNoMapping = ~0U;
# ifdef IN_RING3
int rc = pgmR3SyncPTResolveConflictPAE(pVM, pMapping, (iPDPTE << GST_PDPT_SHIFT) + (iPD << GST_PD_SHIFT));
return rc;
return VINF_PGM_SYNC_CR3;
if (pMapping)
pPDEDst += cPTs + (PGM_GST_TYPE != PGM_SHW_TYPE) * cPTs; /* Only applies to the pae shadow and 32 bits guest case */
# else /* (PGM_GST_TYPE != PGM_TYPE_32BIT && PGM_GST_TYPE != PGM_TYPE_PAE) || PGM_WITHOUT_MAPPINGS */
# endif /* (PGM_GST_TYPE != PGM_TYPE_32BIT && PGM_GST_TYPE != PGM_TYPE_PAE) || PGM_WITHOUT_MAPPINGS */
return VINF_SUCCESS;
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, uint64_t cr3, uint64_t cr4, RTGCUINTPTR GCPtr, RTGCUINTPTR cb)
unsigned cErrors = 0;
bool fBigPagesSupported = true;
# ifndef IN_RING0
int rc;
# ifndef IN_RING0
AssertMsgReturn(HCPhys == HCPhysShw, ("HCPhys=%VHp HCPhyswShw=%VHp (cr3)\n", HCPhys, HCPhysShw), false);
AssertMsgReturn((cr3 & GST_CR3_PAGE_MASK) == GCPhys, ("GCPhys=%VGp cr3=%VGp\n", GCPhys, (RTGCPHYS)cr3), false);
if (!pPDSrc)
AssertMsg(!pVM->pgm.s.CTXSUFF(pGstPaePDPT)->a[(GCPtr >> GST_PDPT_SHIFT) & GST_PDPT_MASK].n.u1Present, ("Guest PDTPR not present, shadow PDPTR %VX64\n", pVM->pgm.s.CTXSUFF(pGstPaePDPT)->a[(GCPtr >> GST_PDPT_SHIFT) & GST_PDPT_MASK].u));
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++;
|| !fBigPagesSupported)
cErrors++;
!= (!PdeSrc.b.u1Size || !fBigPagesSupported ? BTH_PGMPOOLKIND_PT_FOR_PT : BTH_PGMPOOLKIND_PT_FOR_BIG))
cErrors++;
if (!pPhysPage)
cErrors++;
cErrors++;
|| !fBigPagesSupported)
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;