Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

PGMAllPhys.cpp

Go to the documentation of this file.
/* $Id: PGMAllPhys.cpp 4417 2007-08-29 09:12:42Z vboxsync $ */
/** @file
 * PGM - Page Manager and Monitor, Physical Memory Addressing.
 */

/*
 * Copyright (C) 2006-2007 innotek GmbH
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation,
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 * distribution. VirtualBox OSE is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 */

/** @def PGM_IGNORE_RAM_FLAGS_RESERVED
 * Don't respect the MM_RAM_FLAGS_RESERVED flag when converting to HC addresses.
 *
 * Since this flag is currently incorrectly kept set for ROM regions we will
 * have to ignore it for now so we don't break stuff.
 * 
 * @todo this has been fixed now I believe, remove this hack.
 */
00026 #define PGM_IGNORE_RAM_FLAGS_RESERVED


/*******************************************************************************
*   Header Files                                                               *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PGM_PHYS
#include <VBox/pgm.h>
#include <VBox/trpm.h>
#include <VBox/vmm.h>
#include <VBox/iom.h>
#include "PGMInternal.h"
#include <VBox/vm.h>
#include <VBox/param.h>
#include <VBox/err.h>
#include <iprt/assert.h>
#include <iprt/string.h>
#include <iprt/asm.h>
#include <VBox/log.h>
#ifdef IN_RING3
# include <iprt/thread.h>
#endif



/**
 * Checks if Address Gate 20 is enabled or not.
 *
 * @returns true if enabled.
 * @returns false if disabled.
 * @param   pVM     VM handle.
 */
00058 PGMDECL(bool) PGMPhysIsA20Enabled(PVM pVM)
{
    LogFlow(("PGMPhysIsA20Enabled %d\n", pVM->pgm.s.fA20Enabled));
    return !!pVM->pgm.s.fA20Enabled ; /* stupid MS compiler doesn't trust me. */
}


/**
 * Validates a GC physical address.
 *
 * @returns true if valid.
 * @returns false if invalid.
 * @param   pVM     The VM handle.
 * @param   GCPhys  The physical address to validate.
 */
PGMDECL(bool) PGMPhysIsGCPhysValid(PVM pVM, RTGCPHYS GCPhys)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        RTGCPHYS off = GCPhys - pRam->GCPhys;
        if (off < pRam->cb)
            return true;
    }
    return false;
}


/**
 * Checks if a GC physical address is a normal page,
 * i.e. not ROM, MMIO or reserved.
 *
 * @returns true if normal.
 * @returns false if invalid, ROM, MMIO or reserved page.
 * @param   pVM     The VM handle.
 * @param   GCPhys  The physical address to check.
 */
PGMDECL(bool) PGMPhysIsGCPhysNormal(PVM pVM, RTGCPHYS GCPhys)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        RTGCPHYS off = GCPhys - pRam->GCPhys;
        if (off < pRam->cb)
            return !(pRam->aHCPhys[off >> PAGE_SHIFT] & (MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO2));
    }
    return false;
}


/**
 * Converts a GC physical address to a HC physical address.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical
 *          page but has no physical backing.
 * @returns VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS if it's not a valid
 *          GC physical address.
 * @param   pVM     The VM handle.
 * @param   GCPhys  The GC physical address to convert.
 * @param   pHCPhys Where to store the HC physical address on success.
 */
PGMDECL(int) PGMPhysGCPhys2HCPhys(PVM pVM, RTGCPHYS GCPhys, PRTHCPHYS pHCPhys)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        RTGCPHYS off = GCPhys - pRam->GCPhys;
        if (off < pRam->cb)
        {
            if (    pRam->pvHC
                ||  (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC))
            {
                unsigned iPage = off >> PAGE_SHIFT;
                if (RT_UNLIKELY(!(pRam->aHCPhys[iPage] & X86_PTE_PAE_PG_MASK)))
                {
#ifdef IN_RING3
                    int rc = pgmr3PhysGrowRange(pVM, GCPhys);
#else
                    int rc = CTXALLMID(VMM, CallHost)(pVM, VMMCALLHOST_PGM_RAM_GROW_RANGE, GCPhys);
#endif
                    if (rc != VINF_SUCCESS)
                        return rc;
                }

                RTHCPHYS HCPhys = pRam->aHCPhys[off >> PAGE_SHIFT];
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                if (!(HCPhys & MM_RAM_FLAGS_RESERVED))
#endif
                {
                    *pHCPhys = (HCPhys & X86_PTE_PAE_PG_MASK)
                             | (off & PAGE_OFFSET_MASK);
                    return VINF_SUCCESS;
                }
            }
            return VERR_PGM_PHYS_PAGE_RESERVED;
        }
    }
    return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
}


/**
 * Converts a GC physical address to a HC pointer.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical
 *          page but has no physical backing.
 * @returns VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS if it's not a valid
 *          GC physical address.
 * @returns VERR_PGM_GCPHYS_RANGE_CROSSES_BOUNDARY if the range crosses
 *          a dynamic ram chunk boundary
 * @param   pVM     The VM handle.
 * @param   GCPhys  The GC physical address to convert.
 * @param   cbRange Physical range
 * @param   pHCPtr  Where to store the HC pointer on success.
 */
PGMDECL(int) PGMPhysGCPhys2HCPtr(PVM pVM, RTGCPHYS GCPhys, RTUINT cbRange, PRTHCPTR pHCPtr)
{
#ifdef PGM_DYNAMIC_RAM_ALLOC
    if ((GCPhys & PGM_DYNAMIC_CHUNK_BASE_MASK) != ((GCPhys+cbRange-1) & PGM_DYNAMIC_CHUNK_BASE_MASK))
    {
        AssertMsgFailed(("PGMPhysGCPhys2HCPtr %VGp - %VGp crosses a chunk boundary!!\n", GCPhys, GCPhys+cbRange));
        return VERR_PGM_GCPHYS_RANGE_CROSSES_BOUNDARY;
    }
#endif

    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        RTGCPHYS off = GCPhys - pRam->GCPhys;
        if (off < pRam->cb)
        {
            if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
            {
                unsigned iPage = off >> PAGE_SHIFT;
                if (RT_UNLIKELY(!(pRam->aHCPhys[iPage] & X86_PTE_PAE_PG_MASK)))
                {
#ifdef IN_RING3
                    int rc = pgmr3PhysGrowRange(pVM, GCPhys);
#else
                    int rc = CTXALLMID(VMM, CallHost)(pVM, VMMCALLHOST_PGM_RAM_GROW_RANGE, GCPhys);
#endif
                    if (rc != VINF_SUCCESS)
                        return rc;
                }
                unsigned idx = (off >> PGM_DYNAMIC_CHUNK_SHIFT);
                *pHCPtr = (RTHCPTR)((RTHCUINTPTR)CTXSUFF(pRam->pavHCChunk)[idx] + (off & PGM_DYNAMIC_CHUNK_OFFSET_MASK));
                return VINF_SUCCESS;
            }
            if (pRam->pvHC)
            {
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                if (!(pRam->aHCPhys[off >> PAGE_SHIFT] & MM_RAM_FLAGS_RESERVED))
#endif
                {
                    *pHCPtr = (RTHCPTR)((RTHCUINTPTR)pRam->pvHC + off);
                    return VINF_SUCCESS;
                }
            }
            return VERR_PGM_PHYS_PAGE_RESERVED;
        }
    }
    return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
}


/**
 * Validates a HC pointer.
 *
 * @returns true if valid.
 * @returns false if invalid.
 * @param   pVM     The VM handle.
 * @param   HCPtr   The pointer to validate.
 */
PGMDECL(bool) PGMPhysIsHCPtrValid(PVM pVM, RTHCPTR HCPtr)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
        {
            /** @note this is quite slow */
            for (unsigned iChunk = 0; iChunk < (pRam->cb >> PGM_DYNAMIC_CHUNK_SHIFT); iChunk++)
            {
                if (CTXSUFF(pRam->pavHCChunk)[iChunk])
                {
                    RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)CTXSUFF(pRam->pavHCChunk)[iChunk];
                    if (off < PGM_DYNAMIC_CHUNK_SIZE)
                        return true;
                }
            }
        }
        else if (pRam->pvHC)
        {
            RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)pRam->pvHC;

            if (off < pRam->cb)
                return true;
        }
    }
    return false;
}


/**
 * Converts a HC pointer to a GC physical address.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_INVALID_POINTER if the pointer is not within the
 *          GC physical memory.
 * @param   pVM     The VM handle.
 * @param   HCPtr   The HC pointer to convert.
 * @param   pGCPhys Where to store the GC physical address on success.
 */
PGMDECL(int) PGMPhysHCPtr2GCPhys(PVM pVM, RTHCPTR HCPtr, PRTGCPHYS pGCPhys)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
        {
            /** @note this is quite slow */
            for (unsigned iChunk = 0; iChunk < (pRam->cb >> PGM_DYNAMIC_CHUNK_SHIFT); iChunk++)
            {
                if (CTXSUFF(pRam->pavHCChunk)[iChunk])
                {
                    RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)CTXSUFF(pRam->pavHCChunk)[iChunk];
                    if (off < PGM_DYNAMIC_CHUNK_SIZE)
                    {
                        *pGCPhys = pRam->GCPhys + iChunk*PGM_DYNAMIC_CHUNK_SIZE + off;
                        return VINF_SUCCESS;
                    }
                }
            }
        }
        else if (pRam->pvHC)
        {
            RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)pRam->pvHC;
            if (off < pRam->cb)
            {
                *pGCPhys = pRam->GCPhys + off;
                return VINF_SUCCESS;
            }
        }
    }
    return VERR_INVALID_POINTER;
}


/**
 * Converts a HC pointer to a GC physical address.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical
 *          page but has no physical backing.
 * @returns VERR_INVALID_POINTER if the pointer is not within the
 *          GC physical memory.
 * @param   pVM     The VM handle.
 * @param   HCPtr   The HC pointer to convert.
 * @param   pHCPhys Where to store the HC physical address on success.
 */
PGMDECL(int) PGMPhysHCPtr2HCPhys(PVM pVM, RTHCPTR HCPtr, PRTHCPHYS pHCPhys)
{
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
        {
            /** @note this is quite slow */
            for (unsigned iChunk = 0; iChunk < (pRam->cb >> PGM_DYNAMIC_CHUNK_SHIFT); iChunk++)
            {
                if (CTXSUFF(pRam->pavHCChunk)[iChunk])
                {
                    RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)CTXSUFF(pRam->pavHCChunk)[iChunk];
                    if (off < PGM_DYNAMIC_CHUNK_SIZE)
                    {
                        RTHCPHYS HCPhys = pRam->aHCPhys[off >> PAGE_SHIFT];
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                        if (!(HCPhys & MM_RAM_FLAGS_RESERVED))
#endif
                        {
                            *pHCPhys = (HCPhys & X86_PTE_PAE_PG_MASK)
                                     | (off & PAGE_OFFSET_MASK);
                            return VINF_SUCCESS;
                        }
                        return VERR_PGM_PHYS_PAGE_RESERVED;
                    }
                }
            }
        }
        else if (pRam->pvHC)
        {
            RTHCUINTPTR off = (RTHCUINTPTR)HCPtr - (RTHCUINTPTR)pRam->pvHC;
            if (off < pRam->cb)
            {
                RTHCPHYS HCPhys = pRam->aHCPhys[off >> PAGE_SHIFT];
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                if (!(HCPhys & MM_RAM_FLAGS_RESERVED))
#endif
                {
                    *pHCPhys = (HCPhys & X86_PTE_PAE_PG_MASK)
                             | (off & PAGE_OFFSET_MASK);
                    return VINF_SUCCESS;
                }
                return VERR_PGM_PHYS_PAGE_RESERVED;
            }
        }
    }
    return VERR_INVALID_POINTER;
}


/**
 * Validates a HC Physical address.
 *
 * This is an extremely slow API, don't use it!
 *
 * @returns true if valid.
 * @returns false if invalid.
 * @param   pVM     The VM handle.
 * @param   HCPhys  The physical address to validate.
 */
PGMDECL(bool) PGMPhysIsHCPhysValid(PVM pVM, RTHCPHYS HCPhys)
{
    RTGCPHYS GCPhys;
    int rc = PGMPhysHCPhys2GCPhys(pVM, HCPhys, &GCPhys);
    return VBOX_SUCCESS(rc);
}


/**
 * Converts a HC physical address to a GC physical address.
 *
 * This is an extremely slow API, don't use it!
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_INVALID_POINTER if the HC physical address is
 *          not within the GC physical memory.
 * @param   pVM     The VM handle.
 * @param   HCPhys  The HC physical address to convert.
 * @param   pGCPhys Where to store the GC physical address on success.
 */
PGMDECL(int) PGMPhysHCPhys2GCPhys(PVM pVM, RTHCPHYS HCPhys, PRTGCPHYS pGCPhys)
{
    unsigned off = HCPhys & PAGE_OFFSET_MASK;
    HCPhys &= X86_PTE_PAE_PG_MASK;
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        if (    pRam->pvHC
            ||  (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC))
        {
            unsigned iPage = pRam->cb >> PAGE_SHIFT;
            while (iPage-- > 0)
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                if ((pRam->aHCPhys[iPage] & (X86_PTE_PAE_PG_MASK | MM_RAM_FLAGS_RESERVED)) == HCPhys)
#else
                if ((pRam->aHCPhys[iPage] & (X86_PTE_PAE_PG_MASK)) == HCPhys)
#endif
                {
                    *pGCPhys = pRam->GCPhys + (iPage << PAGE_SHIFT) + off;
                    return VINF_SUCCESS;
                }
        }
    }
    return VERR_INVALID_POINTER;
}


/**
 * Converts a HC physical address to a HC pointer.
 *
 * This is an extremely slow API, don't use it!
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_INVALID_POINTER if the HC physical address is
 *          not within the GC physical memory.
 * @param   pVM     The VM handle.
 * @param   HCPhys  The HC physical address to convert.
 * @param   pHCPtr  Where to store the HC pointer on success.
 */
PGMDECL(int) PGMPhysHCPhys2HCPtr(PVM pVM, RTHCPHYS HCPhys, PRTHCPTR pHCPtr)
{
    unsigned off = HCPhys & PAGE_OFFSET_MASK;
    HCPhys &= X86_PTE_PAE_PG_MASK;
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = CTXSUFF(pRam->pNext))
    {
        if (    pRam->pvHC
            ||  (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC))
        {
            unsigned iPage = pRam->cb >> PAGE_SHIFT;
            while (iPage-- > 0)
#ifndef PGM_IGNORE_RAM_FLAGS_RESERVED
                if ((pRam->aHCPhys[iPage] & (X86_PTE_PAE_PG_MASK | MM_RAM_FLAGS_RESERVED)) == HCPhys)
#else
                if ((pRam->aHCPhys[iPage] & (X86_PTE_PAE_PG_MASK)) == HCPhys)
#endif
                {
                    if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
                    {
                        unsigned idx = (iPage >> (PGM_DYNAMIC_CHUNK_SHIFT - PAGE_SHIFT));

                        *pHCPtr = (RTHCPTR)((RTHCUINTPTR)CTXSUFF(pRam->pavHCChunk)[idx] + ((iPage << PAGE_SHIFT) & PGM_DYNAMIC_CHUNK_OFFSET_MASK) + off);
                    }
                    else
                        *pHCPtr = (RTHCPTR)((RTHCUINTPTR)pRam->pvHC + (iPage << PAGE_SHIFT) + off);

                    return VINF_SUCCESS;
                }
        }
    }
    return VERR_INVALID_POINTER;
}


/**
 * Converts a guest pointer to a GC physical address.
 *
 * This uses the current CR3/CR0/CR4 of the guest.
 *
 * @returns VBox status code.
 * @param   pVM         The VM Handle
 * @param   GCPtr       The guest pointer to convert.
 * @param   pGCPhys     Where to store the HC physical address.
 */
PGMDECL(int) PGMPhysGCPtr2GCPhys(PVM pVM, RTGCPTR GCPtr, PRTGCPHYS pGCPhys)
{
    return PGM_GST_PFN(GetPage,pVM)(pVM, (RTGCUINTPTR)GCPtr, NULL, pGCPhys);
}


/**
 * Converts a guest pointer to a HC physical address.
 *
 * This uses the current CR3/CR0/CR4 of the guest.
 *
 * @returns VBox status code.
 * @param   pVM         The VM Handle
 * @param   GCPtr       The guest pointer to convert.
 * @param   pHCPhys     Where to store the HC physical address.
 */
PGMDECL(int) PGMPhysGCPtr2HCPhys(PVM pVM, RTGCPTR GCPtr, PRTHCPHYS pHCPhys)
{
    RTGCPHYS GCPhys;
    int rc = PGM_GST_PFN(GetPage,pVM)(pVM, (RTGCUINTPTR)GCPtr, NULL, &GCPhys);
    if (VBOX_SUCCESS(rc))
        rc = PGMPhysGCPhys2HCPhys(pVM, GCPhys | ((RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK), pHCPhys);
    return rc;
}


/**
 * Converts a guest pointer to a HC pointer.
 *
 * This uses the current CR3/CR0/CR4 of the guest.
 *
 * @returns VBox status code.
 * @param   pVM         The VM Handle
 * @param   GCPtr       The guest pointer to convert.
 * @param   pHCPtr      Where to store the HC virtual address.
 */
PGMDECL(int) PGMPhysGCPtr2HCPtr(PVM pVM, RTGCPTR GCPtr, PRTHCPTR pHCPtr)
{
    RTGCPHYS GCPhys;
    int rc = PGM_GST_PFN(GetPage,pVM)(pVM, (RTGCUINTPTR)GCPtr, NULL, &GCPhys);
    if (VBOX_SUCCESS(rc))
        rc = PGMPhysGCPhys2HCPtr(pVM, GCPhys | ((RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK), 1 /* we always stay within one page */, pHCPtr);
    return rc;
}


/**
 * Converts a guest virtual address to a HC pointer by specfied CR3 and flags.
 *
 * @returns VBox status code.
 * @param   pVM         The VM Handle
 * @param   GCPtr       The guest pointer to convert.
 * @param   cr3         The guest CR3.
 * @param   fFlags      Flags used for interpreting the PD correctly: X86_CR4_PSE and X86_CR4_PAE
 * @param   pHCPtr      Where to store the HC pointer.
 *
 * @remark  This function is used by the REM at a time where PGM could
 *          potentially not be in sync. It could also be used by a
 *          future DBGF API to cpu state independent conversions.
 */
PGMDECL(int) PGMPhysGCPtr2HCPtrByGstCR3(PVM pVM, RTGCPTR GCPtr, uint32_t cr3, unsigned fFlags, PRTHCPTR pHCPtr)
{
    /*
     * PAE or 32-bit?
     */
    int rc;
    if (!(fFlags & X86_CR4_PAE))
    {
        PX86PD pPD;
        rc = PGM_GCPHYS_2_PTR(pVM, cr3 & X86_CR3_PAGE_MASK, &pPD);
        if (VBOX_SUCCESS(rc))
        {
            VBOXPDE Pde = pPD->a[(RTGCUINTPTR)GCPtr >> X86_PD_SHIFT];
            if (Pde.n.u1Present)
            {
                if ((fFlags & X86_CR4_PSE) && Pde.b.u1Size)
                {   /* (big page) */
                    rc = PGMPhysGCPhys2HCPtr(pVM, (Pde.u & X86_PDE4M_PG_MASK) | ((RTGCUINTPTR)GCPtr & X86_PAGE_4M_OFFSET_MASK), 1 /* we always stay within one page */, pHCPtr);
                }
                else
                {   /* (normal page) */
                    PVBOXPT pPT;
                    rc = PGM_GCPHYS_2_PTR(pVM, Pde.u & X86_PDE_PG_MASK, &pPT);
                    if (VBOX_SUCCESS(rc))
                    {
                        VBOXPTE Pte = pPT->a[((RTGCUINTPTR)GCPtr >> X86_PT_SHIFT) & X86_PT_MASK];
                        if (Pte.n.u1Present)
                            return PGMPhysGCPhys2HCPtr(pVM, (Pte.u & X86_PTE_PG_MASK) | ((RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK), 1 /* we always stay within one page */, pHCPtr);
                        rc = VERR_PAGE_NOT_PRESENT;
                    }
                }
            }
            else
                rc = VERR_PAGE_TABLE_NOT_PRESENT;
        }
    }
    else
    {
        /** @todo long mode! */
        PX86PDPTR pPdptr;
        rc = PGM_GCPHYS_2_PTR(pVM, cr3 & X86_CR3_PAE_PAGE_MASK, &pPdptr);
        if (VBOX_SUCCESS(rc))
        {
            X86PDPE Pdpe = pPdptr->a[((RTGCUINTPTR)GCPtr >> X86_PDPTR_SHIFT) & X86_PDPTR_MASK];
            if (Pdpe.n.u1Present)
            {
                PX86PDPAE pPD;
                rc = PGM_GCPHYS_2_PTR(pVM, Pdpe.u & X86_PDPE_PG_MASK, &pPD);
                if (VBOX_SUCCESS(rc))
                {
                    X86PDEPAE Pde = pPD->a[((RTGCUINTPTR)GCPtr >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK];
                    if (Pde.n.u1Present)
                    {
                        if ((fFlags & X86_CR4_PSE) && Pde.b.u1Size)
                        {   /* (big page) */
                            rc = PGMPhysGCPhys2HCPtr(pVM, (Pde.u & X86_PDE4M_PAE_PG_MASK) | ((RTGCUINTPTR)GCPtr & X86_PAGE_4M_OFFSET_MASK), 1 /* we always stay within one page */, pHCPtr);
                        }
                        else
                        {   /* (normal page) */
                            PX86PTPAE pPT;
                            rc = PGM_GCPHYS_2_PTR(pVM, (Pde.u & X86_PDE_PAE_PG_MASK), &pPT);
                            if (VBOX_SUCCESS(rc))
                            {
                                X86PTEPAE Pte = pPT->a[((RTGCUINTPTR)GCPtr >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK];
                                if (Pte.n.u1Present)
                                    return PGMPhysGCPhys2HCPtr(pVM, (Pte.u & X86_PTE_PAE_PG_MASK) | ((RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK), 1 /* we always stay within one page */, pHCPtr);
                                rc = VERR_PAGE_NOT_PRESENT;
                            }
                        }
                    }
                    else
                        rc = VERR_PAGE_TABLE_NOT_PRESENT;
                }
            }
            else
                rc = VERR_PAGE_TABLE_NOT_PRESENT;
        }
    }
    return rc;
}


#undef LOG_GROUP
#define LOG_GROUP LOG_GROUP_PGM_PHYS_ACCESS


#ifdef IN_RING3
/**
 * Cache PGMPhys memory access
 *
 * @param   pVM             VM Handle.
 * @param   pCache          Cache structure pointer
 * @param   GCPhys          GC physical address
 * @param   pbHC            HC pointer corresponding to physical page
 */
static void pgmPhysCacheAdd(PVM pVM, PGMPHYSCACHE *pCache, RTGCPHYS GCPhys, uint8_t *pbHC)
{
    uint32_t iCacheIndex;

    GCPhys = PAGE_ADDRESS(GCPhys);
    pbHC   = (uint8_t *)PAGE_ADDRESS(pbHC);

    iCacheIndex = ((GCPhys >> PAGE_SHIFT) & PGM_MAX_PHYSCACHE_ENTRIES_MASK);

    ASMBitSet(&pCache->aEntries, iCacheIndex);

    pCache->Entry[iCacheIndex].GCPhys = GCPhys;
    pCache->Entry[iCacheIndex].pbHC   = pbHC;
}
#endif

/**
 * Read physical memory.
 *
 * This API respects access handlers and MMIO. Use PGMPhysReadGCPhys() if you
 * want to ignore those.
 *
 * @param   pVM             VM Handle.
 * @param   GCPhys          Physical address start reading from.
 * @param   pvBuf           Where to put the read bits.
 * @param   cbRead          How many bytes to read.
 */
PGMDECL(void) PGMPhysRead(PVM pVM, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead)
{
#ifdef IN_RING3
    bool fGrabbedLock = false;
#endif

    AssertMsg(cbRead > 0, ("don't even think about reading zero bytes!\n"));
    if (cbRead == 0)
        return;

    LogFlow(("PGMPhysRead: %VGp %d\n", GCPhys, cbRead));

#ifdef IN_RING3
    if (!VM_IS_EMT(pVM))
    {
        pgmLock(pVM);
        fGrabbedLock = true;
    }
#endif

    /*
     * Copy loop on ram ranges.
     */
    PPGMRAMRANGE    pCur = CTXSUFF(pVM->pgm.s.pRamRanges);
    for (;;)
    {
        /* Find range. */
        while (pCur && GCPhys > pCur->GCPhysLast)
            pCur = CTXSUFF(pCur->pNext);
        /* Inside range or not? */
        if (pCur && GCPhys >= pCur->GCPhys)
        {
            /*
             * Must work our way thru this page by page.
             */
            RTGCPHYS off = GCPhys - pCur->GCPhys;
            while (off < pCur->cb)
            {
                unsigned iPage = off >> PAGE_SHIFT;
                size_t   cb;

                /* Physical chunk in dynamically allocated range not present? */
                if (RT_UNLIKELY(!(pCur->aHCPhys[iPage] & X86_PTE_PAE_PG_MASK)))
                {
                    /* Treat it as reserved; return zeros */
                    cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                    if (cb >= cbRead)
                    {
                        memset(pvBuf, 0, cbRead);
                        goto end;
                    }
                    memset(pvBuf, 0, cb);
                }
                else
                {
                    RTHCPHYS HCPhys = pCur->aHCPhys[iPage];
                    switch (HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_VIRTUAL_ALL | MM_RAM_FLAGS_PHYSICAL_ALL | MM_RAM_FLAGS_ROM))
                    {
                        /*
                         * Normal memory or ROM.
                         */
                        case 0:
                        case MM_RAM_FLAGS_ROM:
                        case MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_RESERVED:
                        //case MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO2: /* = shadow */ - //MMIO2 isn't in the mask.
                        case MM_RAM_FLAGS_PHYSICAL_WRITE:
                        case MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_PHYSICAL_WRITE: // MMIO2 isn't in the mask.
                        case MM_RAM_FLAGS_VIRTUAL_WRITE:
                        {
#ifdef IN_GC
                            void *pvSrc = NULL;
                            PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvSrc);
                            pvSrc = (char *)pvSrc + (off & PAGE_OFFSET_MASK);
#else
                            void *pvSrc = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                            cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                            if (cb >= cbRead)
                            {
#if defined(IN_RING3) && defined(PGM_PHYSMEMACCESS_CACHING)
                                if (cbRead <= 4)
                                    pgmPhysCacheAdd(pVM, &pVM->pgm.s.pgmphysreadcache, GCPhys, (uint8_t*)pvSrc);
#endif /* IN_RING3 && PGM_PHYSMEMACCESS_CACHING */
                                memcpy(pvBuf, pvSrc, cbRead);
                                goto end;
                            }
                            memcpy(pvBuf, pvSrc, cb);
                            break;
                        }

                        /*
                         * All reserved, nothing there.
                         */
                        case MM_RAM_FLAGS_RESERVED:
                            cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                            if (cb >= cbRead)
                            {
                                memset(pvBuf, 0, cbRead);
                                goto end;
                            }
                            memset(pvBuf, 0, cb);
                            break;

                        /*
                         * Physical handler.
                         */
                        case MM_RAM_FLAGS_PHYSICAL_ALL:
                        case MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_PHYSICAL_ALL: /** r=bird: MMIO2 isn't in the mask! */
                        {
                            int rc = VINF_PGM_HANDLER_DO_DEFAULT;
                            cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
#ifdef IN_RING3 /** @todo deal with this in GC and R0! */

                            /* find and call the handler */
                            PPGMPHYSHANDLER pNode = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&pVM->pgm.s.pTreesHC->PhysHandlers, GCPhys);
                            if (pNode && pNode->pfnHandlerR3)
                            {
                                size_t cbRange = pNode->Core.KeyLast - GCPhys + 1;
                                if (cbRange < cb)
                                    cb = cbRange;
                                if (cb > cbRead)
                                    cb = cbRead;

                                void *pvSrc = PGMRAMRANGE_GETHCPTR(pCur, off)

                                /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                                rc = pNode->pfnHandlerR3(pVM, GCPhys, pvSrc, pvBuf, cb, PGMACCESSTYPE_READ, pNode->pvUserR3);
                            }
#endif /* IN_RING3 */
                            if (rc == VINF_PGM_HANDLER_DO_DEFAULT)
                            {
#ifdef IN_GC
                                void *pvSrc = NULL;
                                PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvSrc);
                                pvSrc = (char *)pvSrc + (off & PAGE_OFFSET_MASK);
#else
                                void *pvSrc = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif

                                if (cb >= cbRead)
                                {
                                    memcpy(pvBuf, pvSrc, cbRead);
                                    goto end;
                                }
                                memcpy(pvBuf, pvSrc, cb);
                            }
                            else if (cb >= cbRead)
                                goto end;
                            break;
                        }

                        case MM_RAM_FLAGS_VIRTUAL_ALL:
                        {
                            int rc = VINF_PGM_HANDLER_DO_DEFAULT;
                            cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
#ifdef IN_RING3 /** @todo deal with this in GC and R0! */
                            /* Search the whole tree for matching physical addresses (rather expensive!) */
                            PPGMVIRTHANDLER pNode;
                            unsigned iPage;
                            int rc2 = pgmHandlerVirtualFindByPhysAddr(pVM, GCPhys, &pNode, &iPage);
                            if (VBOX_SUCCESS(rc2) && pNode->pfnHandlerHC)
                            {
                                size_t cbRange = pNode->Core.KeyLast - GCPhys + 1;
                                if (cbRange < cb)
                                    cb = cbRange;
                                if (cb > cbRead)
                                    cb = cbRead;
                                RTGCUINTPTR GCPtr = ((RTGCUINTPTR)pNode->GCPtr & PAGE_BASE_GC_MASK)
                                                  + (iPage << PAGE_SHIFT) + (off & PAGE_OFFSET_MASK);

                                void *pvSrc = PGMRAMRANGE_GETHCPTR(pCur, off)

                                /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                                rc = pNode->pfnHandlerHC(pVM, (RTGCPTR)GCPtr, pvSrc, pvBuf, cb, PGMACCESSTYPE_READ, 0);
                            }
#endif /* IN_RING3 */
                            if (rc == VINF_PGM_HANDLER_DO_DEFAULT)
                            {
#ifdef IN_GC
                                void *pvSrc = NULL;
                                PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvSrc);
                                pvSrc = (char *)pvSrc + (off & PAGE_OFFSET_MASK);
#else
                                void *pvSrc = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                                if (cb >= cbRead)
                                {
                                    memcpy(pvBuf, pvSrc, cbRead);
                                    goto end;
                                }
                                memcpy(pvBuf, pvSrc, cb);
                            }
                            else if (cb >= cbRead)
                                goto end;
                            break;
                        }

                        /*
                         * The rest needs to be taken more carefully.
                         */
                        default:
#if 1                   /** @todo r=bird: Can you do this properly please. */
                            /** @todo Try MMIO; quick hack */
                            if (cbRead <= 4 && IOMMMIORead(pVM, GCPhys, (uint32_t *)pvBuf, cbRead) == VINF_SUCCESS)
                                goto end;
#endif

                            /** @todo fix me later. */
                            AssertReleaseMsgFailed(("Unknown read at %VGp size %d implement the complex physical reading case %x\n",
                                                    GCPhys, cbRead,
                                                    HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_VIRTUAL_ALL | MM_RAM_FLAGS_PHYSICAL_ALL | MM_RAM_FLAGS_ROM)));
                            cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                            break;
                    }
                }
                cbRead -= cb;
                off    += cb;
                pvBuf   = (char *)pvBuf + cb;
            }

            GCPhys = pCur->GCPhysLast + 1;
        }
        else
        {
            LogFlow(("PGMPhysRead: Unassigned %VGp size=%d\n", GCPhys, cbRead));

            /*
             * Unassigned address space.
             */
            size_t cb;
            if (    !pCur
                ||  (cb = pCur->GCPhys - GCPhys) >= cbRead)
            {
                memset(pvBuf, 0, cbRead);
                goto end;
            }

            memset(pvBuf, 0, cb);
            cbRead -= cb;
            pvBuf   = (char *)pvBuf + cb;
            GCPhys += cb;
        }
    }
end:
#ifdef IN_RING3
    if (fGrabbedLock)
        pgmUnlock(pVM);
#endif
    return;
}

/**
 * Write to physical memory.
 *
 * This API respects access handlers and MMIO. Use PGMPhysReadGCPhys() if you
 * want to ignore those.
 *
 * @param   pVM             VM Handle.
 * @param   GCPhys          Physical address to write to.
 * @param   pvBuf           What to write.
 * @param   cbWrite         How many bytes to write.
 */
PGMDECL(void) PGMPhysWrite(PVM pVM, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite)
{
#ifdef IN_RING3
    bool fGrabbedLock = false;
#endif

    AssertMsg(!pVM->pgm.s.fNoMorePhysWrites, ("Calling PGMPhysWrite after pgmR3Save()!\n"));
    AssertMsg(cbWrite > 0, ("don't even think about writing zero bytes!\n"));
    if (cbWrite == 0)
        return;

    LogFlow(("PGMPhysWrite: %VGp %d\n", GCPhys, cbWrite));

#ifdef IN_RING3
    if (!VM_IS_EMT(pVM))
    {
        pgmLock(pVM);
        fGrabbedLock = true;
    }
#endif
    /*
     * Copy loop on ram ranges.
     */
    PPGMRAMRANGE    pCur = CTXSUFF(pVM->pgm.s.pRamRanges);
    for (;;)
    {
        /* Find range. */
        while (pCur && GCPhys > pCur->GCPhysLast)
            pCur = CTXSUFF(pCur->pNext);
        /* Inside range or not? */
        if (pCur && GCPhys >= pCur->GCPhys)
        {
            /*
             * Must work our way thru this page by page.
             */
            unsigned off = GCPhys - pCur->GCPhys;
            while (off < pCur->cb)
            {
                unsigned iPage = off >> PAGE_SHIFT;

                /* Physical chunk in dynamically allocated range not present? */
                if (RT_UNLIKELY(!(pCur->aHCPhys[iPage] & X86_PTE_PAE_PG_MASK)))
                {
                    int rc;
#ifdef IN_RING3
                    if (fGrabbedLock)
                    {
                        pgmUnlock(pVM);
                        rc = pgmr3PhysGrowRange(pVM, GCPhys);
                        if (rc == VINF_SUCCESS)
                            PGMPhysWrite(pVM, GCPhys, pvBuf, cbWrite); /* try again; can't assume pCur is still valid (paranoia) */
                        return;
                    }
                    rc = pgmr3PhysGrowRange(pVM, GCPhys);
#else
                    rc = CTXALLMID(VMM, CallHost)(pVM, VMMCALLHOST_PGM_RAM_GROW_RANGE, GCPhys);
#endif
                    if (rc != VINF_SUCCESS)
                        goto end;
                }

                size_t   cb;
                RTHCPHYS HCPhys = pCur->aHCPhys[iPage];
                /** @todo r=bird: missing MM_RAM_FLAGS_ROM here, we shall not allow anyone to overwrite the ROM! */
                switch (HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_VIRTUAL_ALL | MM_RAM_FLAGS_VIRTUAL_WRITE | MM_RAM_FLAGS_PHYSICAL_ALL | MM_RAM_FLAGS_PHYSICAL_WRITE))
                {
                    /*
                     * Normal memory, MMIO2 or writable shadow ROM.
                     */
                    case 0:
                    case MM_RAM_FLAGS_MMIO2:
                    case MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO2: /* shadow rom */
                    {
#ifdef IN_GC
                        void *pvDst = NULL;
                        PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvDst);
                        pvDst = (char *)pvDst + (off & PAGE_OFFSET_MASK);
#else
                        void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                        cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                        if (cb >= cbWrite)
                        {
#if defined(IN_RING3) && defined(PGM_PHYSMEMACCESS_CACHING)
                            if (cbWrite <= 4)
                                pgmPhysCacheAdd(pVM, &pVM->pgm.s.pgmphyswritecache, GCPhys, (uint8_t*)pvDst);
#endif /* IN_RING3 && PGM_PHYSMEMACCESS_CACHING */
                            memcpy(pvDst, pvBuf, cbWrite);
                            goto end;
                        }
                        memcpy(pvDst, pvBuf, cb);
                        break;
                    }

                    /*
                     * All reserved, nothing there.
                     */
                    case MM_RAM_FLAGS_RESERVED:
                    case MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO2:
                        cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
                        if (cb >= cbWrite)
                            goto end;
                        break;

                    /*
                     * Physical handler.
                     */
                    case MM_RAM_FLAGS_PHYSICAL_ALL:
                    case MM_RAM_FLAGS_PHYSICAL_WRITE:
                    case MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_PHYSICAL_ALL:
                    case MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_PHYSICAL_WRITE:
                    {
                        int rc = VINF_PGM_HANDLER_DO_DEFAULT;
                        cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
#ifdef IN_RING3 /** @todo deal with this in GC and R0! */
                        /* find and call the handler */
                        PPGMPHYSHANDLER pNode = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&pVM->pgm.s.pTreesHC->PhysHandlers, GCPhys);
                        if (pNode && pNode->pfnHandlerR3)
                        {
                            size_t cbRange = pNode->Core.KeyLast - GCPhys + 1;
                            if (cbRange < cb)
                                cb = cbRange;
                            if (cb > cbWrite)
                                cb = cbWrite;

                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)

                            /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                            rc = pNode->pfnHandlerR3(pVM, GCPhys, pvDst, (void *)pvBuf, cb, PGMACCESSTYPE_WRITE, pNode->pvUserR3);
                        }
#endif /* IN_RING3 */
                        if (rc == VINF_PGM_HANDLER_DO_DEFAULT)
                        {
#ifdef IN_GC
                            void *pvDst = NULL;
                            PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvDst);
                            pvDst = (char *)pvDst + (off & PAGE_OFFSET_MASK);
#else
                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                            if (cb >= cbWrite)
                            {
                                memcpy(pvDst, pvBuf, cbWrite);
                                goto end;
                            }
                            memcpy(pvDst, pvBuf, cb);
                        }
                        else if (cb >= cbWrite)
                            goto end;
                        break;
                    }

                    case MM_RAM_FLAGS_VIRTUAL_ALL:
                    case MM_RAM_FLAGS_VIRTUAL_WRITE:
                    {
                        int rc = VINF_PGM_HANDLER_DO_DEFAULT;
                        cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
#ifdef IN_RING3
/** @todo deal with this in GC and R0! */
                        /* Search the whole tree for matching physical addresses (rather expensive!) */
                        PPGMVIRTHANDLER pNode;
                        unsigned iPage;
                        int rc2 = pgmHandlerVirtualFindByPhysAddr(pVM, GCPhys, &pNode, &iPage);
                        if (VBOX_SUCCESS(rc2) && pNode->pfnHandlerHC)
                        {
                            size_t cbRange = pNode->Core.KeyLast - GCPhys + 1;
                            if (cbRange < cb)
                                cb = cbRange;
                            if (cb > cbWrite)
                                cb = cbWrite;
                            RTGCUINTPTR GCPtr = ((RTGCUINTPTR)pNode->GCPtr & PAGE_BASE_GC_MASK)
                                              + (iPage << PAGE_SHIFT) + (off & PAGE_OFFSET_MASK);

                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)

                            /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                            rc = pNode->pfnHandlerHC(pVM, (RTGCPTR)GCPtr, pvDst, (void *)pvBuf, cb, PGMACCESSTYPE_WRITE, 0);
                        }
#endif /* IN_RING3 */
                        if (rc == VINF_PGM_HANDLER_DO_DEFAULT)
                        {
#ifdef IN_GC
                            void *pvDst = NULL;
                            PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvDst);
                            pvDst = (char *)pvDst + (off & PAGE_OFFSET_MASK);
#else
                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                            if (cb >= cbWrite)
                            {
                                memcpy(pvDst, pvBuf, cbWrite);
                                goto end;
                            }
                            memcpy(pvDst, pvBuf, cb);
                        }
                        else if (cb >= cbWrite)
                            goto end;
                        break;
                    }

                    /*
                     * Physical write handler + virtual write handler.
                     * Consider this a quick workaround for the CSAM + shadow caching problem.
                     *
                     * We hand it to the shadow caching first since it requires the unchanged
                     * data. CSAM will have to put up with it already being changed.
                     */
                    case MM_RAM_FLAGS_PHYSICAL_WRITE | MM_RAM_FLAGS_VIRTUAL_WRITE:
                    {
                        int rc = VINF_PGM_HANDLER_DO_DEFAULT;
                        cb = PAGE_SIZE - (off & PAGE_OFFSET_MASK);
#ifdef IN_RING3 /** @todo deal with this in GC and R0! */
                        /* 1. The physical handler */
                        PPGMPHYSHANDLER pPhysNode = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&pVM->pgm.s.pTreesHC->PhysHandlers, GCPhys);
                        if (pPhysNode && pPhysNode->pfnHandlerR3)
                        {
                            size_t cbRange = pPhysNode->Core.KeyLast - GCPhys + 1;
                            if (cbRange < cb)
                                cb = cbRange;
                            if (cb > cbWrite)
                                cb = cbWrite;

                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)

                            /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                            rc = pPhysNode->pfnHandlerR3(pVM, GCPhys, pvDst, (void *)pvBuf, cb, PGMACCESSTYPE_WRITE, pPhysNode->pvUserR3);
                        }

                        /* 2. The virtual handler (will see incorrect data) */
                        PPGMVIRTHANDLER pVirtNode;
                        unsigned iPage;
                        int rc2 = pgmHandlerVirtualFindByPhysAddr(pVM, GCPhys, &pVirtNode, &iPage);
                        if (VBOX_SUCCESS(rc2) && pVirtNode->pfnHandlerHC)
                        {
                            size_t cbRange = pVirtNode->Core.KeyLast - GCPhys + 1;
                            if (cbRange < cb)
                                cb = cbRange;
                            if (cb > cbWrite)
                                cb = cbWrite;
                            RTGCUINTPTR GCPtr = ((RTGCUINTPTR)pVirtNode->GCPtr & PAGE_BASE_GC_MASK)
                                              + (iPage << PAGE_SHIFT) + (off & PAGE_OFFSET_MASK);

                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)

                            /** @note Dangerous assumption that HC handlers don't do anything that really requires an EMT lock! */
                            rc2 = pVirtNode->pfnHandlerHC(pVM, (RTGCPTR)GCPtr, pvDst, (void *)pvBuf, cb, PGMACCESSTYPE_WRITE, 0);
                            if (    (   rc2 != VINF_PGM_HANDLER_DO_DEFAULT
                                     && rc == VINF_PGM_HANDLER_DO_DEFAULT)
                                ||  (   VBOX_FAILURE(rc2)
                                     && VBOX_SUCCESS(rc)))
                                rc = rc2;
                        }
#endif /* IN_RING3 */
                        if (rc == VINF_PGM_HANDLER_DO_DEFAULT)
                        {
#ifdef IN_GC
                            void *pvDst = NULL;
                            PGMGCDynMapHCPage(pVM, HCPhys & X86_PTE_PAE_PG_MASK, &pvDst);
                            pvDst = (char *)pvDst + (off & PAGE_OFFSET_MASK);
#else
                            void *pvDst = PGMRAMRANGE_GETHCPTR(pCur, off)
#endif
                            if (cb >= cbWrite)
                            {
                                memcpy(pvDst, pvBuf, cbWrite);
                                goto end;
                            }
                            memcpy(pvDst, pvBuf, cb);
                        }
                        else if (cb >= cbWrite)
                            goto end;
                        break;
                    }


                    /*
                     * The rest needs to be taken more carefully.
                     */
                    default:
#if 1                   /** @todo r=bird: Can you do this properly please. */
                        /** @todo Try MMIO; quick hack */
                        if (cbWrite <= 4 && IOMMMIOWrite(pVM, GCPhys, *(uint32_t *)pvBuf, cbWrite) == VINF_SUCCESS)
                            goto end;
#endif

                        /** @todo fix me later. */
                        AssertReleaseMsgFailed(("Unknown write at %VGp size %d implement the complex physical writing case %x\n",
                                                GCPhys, cbWrite,
                                                (HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_VIRTUAL_ALL | MM_RAM_FLAGS_VIRTUAL_WRITE | MM_RAM_FLAGS_PHYSICAL_ALL | MM_RAM_FLAGS_PHYSICAL_WRITE))));
                        /* skip the write */
                        cb = cbWrite;
                        break;
                }

                cbWrite -= cb;
                off    += cb;
                pvBuf   = (const char *)pvBuf + cb;
            }

            GCPhys = pCur->GCPhysLast + 1;
        }
        else
        {
            /*
             * Unassigned address space.
             */
            size_t cb;
            if (    !pCur
                ||  (cb = pCur->GCPhys - GCPhys) >= cbWrite)
                goto end;

            cbWrite -= cb;
            pvBuf   = (const char *)pvBuf + cb;
            GCPhys += cb;
        }
    }
end:
#ifdef IN_RING3
    if (fGrabbedLock)
        pgmUnlock(pVM);
#endif
    return;
}

#ifndef IN_GC /* Ring 0 & 3 only */

/**
 * Read from guest physical memory by GC physical address, bypassing
 * MMIO and access handlers.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   pvDst       The destination address.
 * @param   GCPhysSrc   The source address (GC physical address).
 * @param   cb          The number of bytes to read.
 */
PGMDECL(int) PGMPhysReadGCPhys(PVM pVM, void *pvDst, RTGCPHYS GCPhysSrc, size_t cb)
{
    /*
     * Anything to be done?
     */
    if (!cb)
        return VINF_SUCCESS;

    /*
     * Loop ram ranges.
     */
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = pRam->CTXSUFF(pNext))
    {
        RTGCPHYS off = GCPhysSrc - pRam->GCPhys;
        if (off < pRam->cb)
        {
            if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
            {
                /* Copy page by page as we're not dealing with a linear HC range. */
                for (;;)
                {
                    /* convert */
                    void *pvSrc;
                    int rc = PGMRamGCPhys2HCPtr(pVM, pRam, GCPhysSrc, &pvSrc);
                    if (VBOX_FAILURE(rc))
                        return rc;

                    /* copy */
                    size_t cbRead = PAGE_SIZE - ((RTGCUINTPTR)GCPhysSrc & PAGE_OFFSET_MASK);
                    if (cbRead >= cb)
                    {
                        memcpy(pvDst, pvSrc, cb);
                        return VINF_SUCCESS;
                    }
                    memcpy(pvDst, pvSrc, cbRead);

                    /* next */
                    cb         -= cbRead;
                    pvDst       = (uint8_t *)pvDst + cbRead;
                    GCPhysSrc  += cbRead;
                }
            }
            else if (pRam->pvHC)
            {
                /* read */
                size_t cbRead = pRam->cb - off;
                if (cbRead >= cb)
                {
                    memcpy(pvDst, (uint8_t *)pRam->pvHC + off, cb);
                    return VINF_SUCCESS;
                }
                memcpy(pvDst, (uint8_t *)pRam->pvHC + off, cbRead);

                /* next */
                cb        -= cbRead;
                pvDst      = (uint8_t *)pvDst + cbRead;
                GCPhysSrc += cbRead;
            }
            else
                return VERR_PGM_PHYS_PAGE_RESERVED;
        }
        else if (GCPhysSrc < pRam->GCPhysLast)
            break;
    }
    return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
}


/**
 * Write to guest physical memory referenced by GC pointer.
 * Write memory to GC physical address in guest physical memory.
 *
 * This will bypass MMIO and access handlers.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   GCPhysDst   The GC physical address of the destination.
 * @param   pvSrc       The source buffer.
 * @param   cb          The number of bytes to write.
 */
PGMDECL(int) PGMPhysWriteGCPhys(PVM pVM, RTGCPHYS GCPhysDst, const void *pvSrc, size_t cb)
{
    /*
     * Anything to be done?
     */
    if (!cb)
        return VINF_SUCCESS;

    LogFlow(("PGMPhysWriteGCPhys: %VGp %d\n", GCPhysDst, cb));

    /*
     * Loop ram ranges.
     */
    for (PPGMRAMRANGE pRam = CTXSUFF(pVM->pgm.s.pRamRanges);
         pRam;
         pRam = pRam->CTXSUFF(pNext))
    {
        RTGCPHYS off = GCPhysDst - pRam->GCPhys;
        if (off < pRam->cb)
        {
            if (pRam->fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
            {
                /* Copy page by page as we're not dealing with a linear HC range. */
                for (;;)
                {
                    /* convert */
                    void *pvDst;
                    int rc = PGMRamGCPhys2HCPtr(pVM, pRam, GCPhysDst, &pvDst);
                    if (VBOX_FAILURE(rc))
                        return rc;

                    /* copy */
                    size_t cbWrite = PAGE_SIZE - ((RTGCUINTPTR)GCPhysDst & PAGE_OFFSET_MASK);
                    if (cbWrite >= cb)
                    {
                        memcpy(pvDst, pvSrc, cb);
                        return VINF_SUCCESS;
                    }
                    memcpy(pvDst, pvSrc, cbWrite);

                    /* next */
                    cb         -= cbWrite;
                    pvSrc       = (uint8_t *)pvSrc + cbWrite;
                    GCPhysDst  += cbWrite;
                }
            }
            else if (pRam->pvHC)
            {
                /* write */
                size_t cbWrite = pRam->cb - off;
                if (cbWrite >= cb)
                {
                    memcpy((uint8_t *)pRam->pvHC + off, pvSrc, cb);
                    return VINF_SUCCESS;
                }
                memcpy((uint8_t *)pRam->pvHC + off, pvSrc, cbWrite);

                /* next */
                cb         -= cbWrite;
                GCPhysDst  += cbWrite;
                pvSrc       = (uint8_t *)pvSrc + cbWrite;
            }
            else
                return VERR_PGM_PHYS_PAGE_RESERVED;
        }
        else if (GCPhysDst < pRam->GCPhysLast)
            break;
    }
    return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
}


/**
 * Read from guest physical memory referenced by GC pointer.
 *
 * This function uses the current CR3/CR0/CR4 of the guest and will
 * bypass access handlers and not set any accessed bits.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   pvDst       The destination address.
 * @param   GCPtrSrc    The source address (GC pointer).
 * @param   cb          The number of bytes to read.
 */
PGMDECL(int) PGMPhysReadGCPtr(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb)
{
    /*
     * Anything to do?
     */
    if (!cb)
        return VINF_SUCCESS;

    /*
     * Optimize reads within a single page.
     */
    if (((RTGCUINTPTR)GCPtrSrc & PAGE_OFFSET_MASK) + cb <= PAGE_SIZE)
    {
        void *pvSrc;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrSrc, &pvSrc);
        if (VBOX_FAILURE(rc))
            return rc;
        memcpy(pvDst, pvSrc, cb);
        return VINF_SUCCESS;
    }

    /*
     * Page by page.
     */
    for (;;)
    {
        /* convert */
        void *pvSrc;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrSrc, &pvSrc);
        if (VBOX_FAILURE(rc))
            return rc;

        /* copy */
        size_t cbRead = PAGE_SIZE - ((RTGCUINTPTR)GCPtrSrc & PAGE_OFFSET_MASK);
        if (cbRead >= cb)
        {
            memcpy(pvDst, pvSrc, cb);
            return VINF_SUCCESS;
        }
        memcpy(pvDst, pvSrc, cbRead);

        /* next */
        cb         -= cbRead;
        pvDst       = (uint8_t *)pvDst + cbRead;
        GCPtrSrc   += cbRead;
    }
}


/**
 * Write to guest physical memory referenced by GC pointer.
 *
 * This function uses the current CR3/CR0/CR4 of the guest and will
 * bypass access handlers and not set dirty or accessed bits.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   GCPtrDst    The destination address (GC pointer).
 * @param   pvSrc       The source address.
 * @param   cb          The number of bytes to write.
 */
PGMDECL(int) PGMPhysWriteGCPtr(PVM pVM, RTGCPTR GCPtrDst, const void *pvSrc, size_t cb)
{
    /*
     * Anything to do?
     */
    if (!cb)
        return VINF_SUCCESS;

    LogFlow(("PGMPhysWriteGCPtr: %VGv %d\n", GCPtrDst, cb));

    /*
     * Optimize writes within a single page.
     */
    if (((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK) + cb <= PAGE_SIZE)
    {
        void *pvDst;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrDst, &pvDst);
        if (VBOX_FAILURE(rc))
            return rc;
        memcpy(pvDst, pvSrc, cb);
        return VINF_SUCCESS;
    }

    /*
     * Page by page.
     */
    for (;;)
    {
        /* convert */
        void *pvDst;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrDst, &pvDst);
        if (VBOX_FAILURE(rc))
            return rc;

        /* copy */
        size_t cbWrite = PAGE_SIZE - ((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK);
        if (cbWrite >= cb)
        {
            memcpy(pvDst, pvSrc, cb);
            return VINF_SUCCESS;
        }
        memcpy(pvDst, pvSrc, cbWrite);

        /* next */
        cb         -= cbWrite;
        pvSrc       = (uint8_t *)pvSrc + cbWrite;
        GCPtrDst   += cbWrite;
    }
}

/**
 * Read from guest physical memory referenced by GC pointer.
 *
 * This function uses the current CR3/CR0/CR4 of the guest and will
 * respect access handlers and set accessed bits.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   pvDst       The destination address.
 * @param   GCPtrSrc    The source address (GC pointer).
 * @param   cb          The number of bytes to read.
 */
/** @todo use the PGMPhysReadGCPtr name and rename the unsafe one to something appropriate */
PGMDECL(int) PGMPhysReadGCPtrSafe(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb)
{
    RTGCPHYS    GCPhys;
    RTGCUINTPTR offset;
    int         rc;

    /*
     * Anything to do?
     */
    if (!cb)
        return VINF_SUCCESS;

    LogFlow(("PGMPhysReadGCPtrSafe: %VGv %d\n", GCPtrSrc, cb));

    /*
     * Optimize reads within a single page.
     */
    if (((RTGCUINTPTR)GCPtrSrc & PAGE_OFFSET_MASK) + cb <= PAGE_SIZE)
    {
        /* Convert virtual to physical address */
        offset = GCPtrSrc & PAGE_OFFSET_MASK;
        rc = PGMPhysGCPtr2GCPhys(pVM, GCPtrSrc, &GCPhys);
        AssertRCReturn(rc, rc);

        /* mark the guest page as accessed. */
        rc = PGMGstModifyPage(pVM, GCPtrSrc, 1, X86_PTE_A, ~(uint64_t)(X86_PTE_A));
        AssertRC(rc);

        PGMPhysRead(pVM, GCPhys + offset, pvDst, cb);
        return VINF_SUCCESS;
    }

    /*
     * Page by page.
     */
    for (;;)
    {
        /* Convert virtual to physical address */
        offset = GCPtrSrc & PAGE_OFFSET_MASK;
        rc = PGMPhysGCPtr2GCPhys(pVM, GCPtrSrc, &GCPhys);
        AssertRCReturn(rc, rc);

        /* mark the guest page as accessed. */
        int rc = PGMGstModifyPage(pVM, GCPtrSrc, 1, X86_PTE_A, ~(uint64_t)(X86_PTE_A));
        AssertRC(rc);

        /* copy */
        size_t cbRead = PAGE_SIZE - ((RTGCUINTPTR)GCPtrSrc & PAGE_OFFSET_MASK);
        if (cbRead >= cb)
        {
            PGMPhysRead(pVM, GCPhys + offset, pvDst, cb);
            return VINF_SUCCESS;
        }
        PGMPhysRead(pVM, GCPhys + offset, pvDst, cbRead);

        /* next */
        cb         -= cbRead;
        pvDst       = (uint8_t *)pvDst + cbRead;
        GCPtrSrc   += cbRead;
    }
}


/**
 * Write to guest physical memory referenced by GC pointer.
 *
 * This function uses the current CR3/CR0/CR4 of the guest and will
 * respect access handlers and set dirty and accessed bits.
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   GCPtrDst    The destination address (GC pointer).
 * @param   pvSrc       The source address.
 * @param   cb          The number of bytes to write.
 */
/** @todo use the PGMPhysWriteGCPtr name and rename the unsafe one to something appropriate */
PGMDECL(int) PGMPhysWriteGCPtrSafe(PVM pVM, RTGCPTR GCPtrDst, const void *pvSrc, size_t cb)
{
    RTGCPHYS    GCPhys;
    RTGCUINTPTR offset;
    int         rc;

    /*
     * Anything to do?
     */
    if (!cb)
        return VINF_SUCCESS;

    LogFlow(("PGMPhysWriteGCPtrSafe: %VGv %d\n", GCPtrDst, cb));

    /*
     * Optimize writes within a single page.
     */
    if (((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK) + cb <= PAGE_SIZE)
    {
        /* Convert virtual to physical address */
        offset = GCPtrDst & PAGE_OFFSET_MASK;
        rc = PGMPhysGCPtr2GCPhys(pVM, GCPtrDst, &GCPhys);
        AssertRCReturn(rc, rc);

        /* mark the guest page as accessed and dirty. */
        rc = PGMGstModifyPage(pVM, GCPtrDst, 1, X86_PTE_A | X86_PTE_D, ~(uint64_t)(X86_PTE_A | X86_PTE_D));
        AssertRC(rc);

        PGMPhysWrite(pVM, GCPhys + offset, pvSrc, cb);
        return VINF_SUCCESS;
    }

    /*
     * Page by page.
     */
    for (;;)
    {
        /* Convert virtual to physical address */
        offset = GCPtrDst & PAGE_OFFSET_MASK;
        rc = PGMPhysGCPtr2GCPhys(pVM, GCPtrDst, &GCPhys);
        AssertRCReturn(rc, rc);

        /* mark the guest page as accessed and dirty. */
        rc = PGMGstModifyPage(pVM, GCPtrDst, 1, X86_PTE_A | X86_PTE_D, ~(uint64_t)(X86_PTE_A | X86_PTE_D));
        AssertRC(rc);

        /* copy */
        size_t cbWrite = PAGE_SIZE - ((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK);
        if (cbWrite >= cb)
        {
            PGMPhysWrite(pVM, GCPhys + offset, pvSrc, cb);
            return VINF_SUCCESS;
        }
        PGMPhysWrite(pVM, GCPhys + offset, pvSrc, cbWrite);

        /* next */
        cb         -= cbWrite;
        pvSrc       = (uint8_t *)pvSrc + cbWrite;
        GCPtrDst   += cbWrite;
    }
}

/**
 * Write to guest physical memory referenced by GC pointer and update the PTE.
 *
 * This function uses the current CR3/CR0/CR4 of the guest and will
 * bypass access handlers and set any dirty and accessed bits in the PTE.
 *
 * If you don't want to set the dirty bit, use PGMPhysWriteGCPtr().
 *
 * @returns VBox status.
 * @param   pVM         VM handle.
 * @param   GCPtrDst    The destination address (GC pointer).
 * @param   pvSrc       The source address.
 * @param   cb          The number of bytes to write.
 */
PGMDECL(int) PGMPhysWriteGCPtrDirty(PVM pVM, RTGCPTR GCPtrDst, const void *pvSrc, size_t cb)
{
    /*
     * Anything to do?
     */
    if (!cb)
        return VINF_SUCCESS;

    /*
     * Optimize writes within a single page.
     */
    if (((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK) + cb <= PAGE_SIZE)
    {
        void *pvDst;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrDst, &pvDst);
        if (VBOX_FAILURE(rc))
            return rc;
        memcpy(pvDst, pvSrc, cb);
        rc = PGMGstModifyPage(pVM, GCPtrDst, cb, X86_PTE_A | X86_PTE_D, ~(uint64_t)(X86_PTE_A | X86_PTE_D));
        AssertRC(rc);
        return VINF_SUCCESS;
    }

    /*
     * Page by page.
     */
    for (;;)
    {
        /* convert */
        void *pvDst;
        int rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrDst, &pvDst);
        if (VBOX_FAILURE(rc))
            return rc;

        /* mark the guest page as accessed and dirty. */
        rc = PGMGstModifyPage(pVM, GCPtrDst, 1, X86_PTE_A | X86_PTE_D, ~(uint64_t)(X86_PTE_A | X86_PTE_D));
        AssertRC(rc);

        /* copy */
        size_t  cbWrite = PAGE_SIZE - ((RTGCUINTPTR)GCPtrDst & PAGE_OFFSET_MASK);
        if (cbWrite >= cb)
        {
            memcpy(pvDst, pvSrc, cb);
            return VINF_SUCCESS;
        }
        memcpy(pvDst, pvSrc, cbWrite);

        /* next */
        cb         -= cbWrite;
        GCPtrDst   += cbWrite;
        pvSrc       = (char *)pvSrc + cbWrite;
    }
}

#endif /* !IN_GC */



/**
 * Performs a read of guest virtual memory for instruction emulation.
 *
 * This will check permissions, raise exceptions and update the access bits.
 *
 * The current implementation will bypass all access handlers. It may later be
 * changed to at least respect MMIO.
 *
 *
 * @returns VBox status code suitable to scheduling.
 * @retval  VINF_SUCCESS if the read was performed successfully.
 * @retval  VINF_EM_RAW_GUEST_TRAP if an exception was raised but not dispatched yet.
 * @retval  VINF_TRPM_XCPT_DISPATCHED if an exception was raised and dispatched.
 *
 * @param   pVM         The VM handle.
 * @param   pCtxCore    The context core.
 * @param   pvDst       Where to put the bytes we've read.
 * @param   GCPtrSrc    The source address.
 * @param   cb          The number of bytes to read. Not more than a page.
 *
 * @remark  This function will dynamically map physical pages in GC. This may unmap
 *          mappings done by the caller. Be careful!
 */
PGMDECL(int) PGMPhysInterpretedRead(PVM pVM, PCPUMCTXCORE pCtxCore, void *pvDst, RTGCUINTPTR GCPtrSrc, size_t cb)
{
    Assert(cb <= PAGE_SIZE);

/** @todo r=bird: This isn't perfect!
 *  -# It's not checking for reserved bits being 1.
 *  -# It's not correctly dealing with the access bit.
 *  -# It's not respecting MMIO memory or any other access handlers.
 */
    /*
     * 1. Translate virtual to physical. This may fault.
     * 2. Map the physical address.
     * 3. Do the read operation.
     * 4. Set access bits if required.
     */
    int rc;
    unsigned cb1 = PAGE_SIZE - (GCPtrSrc & PAGE_OFFSET_MASK);
    if (cb <= cb1)
    {
        /*
         * Not crossing pages.
         */
        RTGCPHYS GCPhys;
        uint64_t fFlags;
        rc = PGM_GST_PFN(GetPage,pVM)(pVM, GCPtrSrc, &fFlags, &GCPhys);
        if (VBOX_SUCCESS(rc))
        {
            /** @todo we should check reserved bits ... */
            void *pvSrc;
            rc = PGM_GCPHYS_2_PTR(pVM, GCPhys, &pvSrc);
            switch (rc)
            {
                case VINF_SUCCESS:
Log(("PGMPhysInterpretedRead: pvDst=%p pvSrc=%p cb=%d\n", pvDst, (uint8_t *)pvSrc + (GCPtrSrc & PAGE_OFFSET_MASK), cb));
                    memcpy(pvDst, (uint8_t *)pvSrc + (GCPtrSrc & PAGE_OFFSET_MASK), cb);
                    break;
                case VERR_PGM_PHYS_PAGE_RESERVED:
                case VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS:
                    memset(pvDst, 0, cb);
                    break;
                default:
                    return rc;
            }

            /** @todo access bit emulation isn't 100% correct. */
            if (!(fFlags & X86_PTE_A))
            {
                rc = PGM_GST_PFN(ModifyPage,pVM)(pVM, GCPtrSrc, 1, X86_PTE_A, ~(uint64_t)X86_PTE_A);
                AssertRC(rc);
            }
            return VINF_SUCCESS;
        }
    }
    else
    {
        /*
         * Crosses pages.
         */
        unsigned cb2 = cb - cb1;
        uint64_t fFlags1;
        RTGCPHYS GCPhys1;
        uint64_t fFlags2;
        RTGCPHYS GCPhys2;
        rc = PGM_GST_PFN(GetPage,pVM)(pVM, GCPtrSrc, &fFlags1, &GCPhys1);
        if (VBOX_SUCCESS(rc))
            rc = PGM_GST_PFN(GetPage,pVM)(pVM, GCPtrSrc + cb1, &fFlags2, &GCPhys2);
        if (VBOX_SUCCESS(rc))
        {
            /** @todo we should check reserved bits ... */
AssertMsgFailed(("cb=%d cb1=%d cb2=%d GCPtrSrc=%VGv\n", cb, cb1, cb2, GCPtrSrc));
            void *pvSrc1;
            rc = PGM_GCPHYS_2_PTR(pVM, GCPhys1, &pvSrc1);
            switch (rc)
            {
                case VINF_SUCCESS:
                    memcpy(pvDst, (uint8_t *)pvSrc1 + (GCPtrSrc & PAGE_OFFSET_MASK), cb1);
                    break;
                case VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS:
                    memset(pvDst, 0, cb1);
                    break;
                default:
                    return rc;
            }

            void *pvSrc2;
            rc = PGM_GCPHYS_2_PTR(pVM, GCPhys2, &pvSrc2);
            switch (rc)
            {
                case VINF_SUCCESS:
                    memcpy((uint8_t *)pvDst + cb2, pvSrc2, cb2);
                    break;
                case VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS:
                    memset((uint8_t *)pvDst + cb2, 0, cb2);
                    break;
                default:
                    return rc;
            }

            if (!(fFlags1 & X86_PTE_A))
            {
                rc = PGM_GST_PFN(ModifyPage,pVM)(pVM, GCPtrSrc, 1, X86_PTE_A, ~(uint64_t)X86_PTE_A);
                AssertRC(rc);
            }
            if (!(fFlags2 & X86_PTE_A))
            {
                rc = PGM_GST_PFN(ModifyPage,pVM)(pVM, GCPtrSrc + cb1, 1, X86_PTE_A, ~(uint64_t)X86_PTE_A);
                AssertRC(rc);
            }
            return VINF_SUCCESS;
        }
    }

    /*
     * Raise a #PF.
     */
    uint32_t uErr;

    /* Get the current privilege level. */
    uint32_t cpl = CPUMGetGuestCPL(pVM, pCtxCore);
    switch (rc)
    {
        case VINF_SUCCESS:
            uErr = (cpl >= 2) ? X86_TRAP_PF_RSVD | X86_TRAP_PF_US : X86_TRAP_PF_RSVD;
            break;

        case VERR_PAGE_NOT_PRESENT:
        case VERR_PAGE_TABLE_NOT_PRESENT:
            uErr = (cpl >= 2) ? X86_TRAP_PF_US : 0;
            break;

        default:
            AssertMsgFailed(("rc=%Vrc GCPtrSrc=%VGv cb=%#x\n", rc, GCPtrSrc, cb));
            return rc;
    }
    Log(("PGMPhysInterpretedRead: GCPtrSrc=%VGv cb=%#x -> #PF(%#x)\n", GCPtrSrc, cb, uErr));
    return TRPMRaiseXcptErrCR2(pVM, pCtxCore, X86_XCPT_PF, uErr, GCPtrSrc);
}

/// @todo PGMDECL(int) PGMPhysInterpretedWrite(PVM pVM, PCPUMCTXCORE pCtxCore, RTGCPTR GCPtrDst, const void *pvSrc, size_t cb)


Generated by  Doxygen 1.6.0   Back to index