kernel/eka/memmodel/epoc/multiple/x86/xmmu.cpp
changeset 43 96e5fb8b040d
child 286 48e57fb1237e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/multiple/x86/xmmu.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1925 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32\memmodel\epoc\multiple\x86\xmmu.cpp
+// 
+//
+
+#include <x86_mem.h>
+#include <mmubase.inl>
+#include <ramcache.h>
+#include "execs.h"
+#include <defrag.h>
+
+extern "C" void DoTotalInvalidateTLB();
+
+// Constants for X86 MMU
+const TUint32 KPdePtePresent=0x01;
+const TUint32 KPdePteWrite=0x02;
+const TUint32 KPdePteUser=0x04;
+const TUint32 KPdePteWriteThrough=0x08;
+const TUint32 KPdePteUncached=0x10;
+const TUint32 KPdePteAccessed=0x20;
+const TUint32 KPdePteDirty=0x40;
+const TUint32 KPdeLargePage=0x80;						// Pentium and above, not 486
+const TUint32 KPdePteGlobal=0x100;						// P6 and above, not 486 or Pentium
+const TUint32 KPdePtePhysAddrMask=0xfffff000u;
+const TUint32 KPdeLargePagePhysAddrMask=0xffc00000u;	// Pentium and above, not 486
+
+const TPde KPdPdePerm=KPdePtePresent|KPdePteWrite;
+const TPte KPdPtePerm=KPdePtePresent|KPdePteWrite;
+const TPde KPtPdePerm=KPdePtePresent|KPdePteWrite;
+const TPte KPtPtePerm=KPdePtePresent|KPdePteWrite;
+const TPde KPtInfoPdePerm=KPdePtePresent|KPdePteWrite;
+const TPte KPtInfoPtePerm=KPdePtePresent|KPdePteWrite;
+const TPde KRomPdePerm=KPdePtePresent|KPdePteWrite|KPdePteUser;
+const TPte KRomPtePerm=KPdePtePresent|KPdePteUser;
+const TPde KShadowPdePerm=KPdePtePresent|KPdePteWrite|KPdePteUser;
+const TPte KShadowPtePerm=KPdePtePresent|KPdePteWrite|KPdePteUser;	// unfortunately there's no RWRO
+
+// Permissions for each chunk type
+
+const TPde KStandardPtePerm=KPdePtePresent|KPdePteWrite|KPdePteUser;
+const TPte KPdePermNONO=KPdePtePresent|KPdePteWrite|KPdePteUser;
+const TPte KPdePermRONO=KPdePtePresent;
+const TPte KPdePermRORO=KPdePtePresent|KPdePteUser;
+const TPte KPdePermRWNO=KPdePtePresent|KPdePteWrite;
+const TPte KPdePermRWRW=KPdePtePresent|KPdePteWrite|KPdePteUser;
+
+LOCAL_D const TPte ChunkPtePermissions[ENumChunkTypes] =
+	{
+	KStandardPtePerm|KPdePteGlobal,		// EKernelData
+	KStandardPtePerm|KPdePteGlobal,		// EKernelStack
+	KPdePermRWNO|KPdePteGlobal,			// EKernelCode - loading
+	KPdePermRWNO,						// EDll (used for global code) - loading
+	KPdePermRORO,						// EUserCode
+	KStandardPtePerm,					// ERamDrive
+	KStandardPtePerm,					// EUserData
+	KStandardPtePerm,					// EDllData
+	KStandardPtePerm,					// EUserSelfModCode
+	KStandardPtePerm,					// ESharedKernelSingle
+	KStandardPtePerm,					// ESharedKernelMultiple
+	KStandardPtePerm,					// ESharedIo
+	KStandardPtePerm|KPdePteGlobal,		// ESharedKernelMirror
+	KStandardPtePerm|KPdePteGlobal,		// EKernelMessage
+	};
+
+LOCAL_D const TPde ChunkPdePermissions[ENumChunkTypes] =
+	{
+	KPdePermRWNO,			// EKernelData
+	KPdePermRWNO,			// EKernelStack
+	KPdePermRWNO,			// EKernelCode
+	KPdePermRWRW,			// EDll
+	KPdePermRWRW,			// EUserCode
+	KPdePermRWRW,			// ERamDrive
+	KPdePermRWRW,			// EUserData
+	KPdePermRWRW,			// EDllData
+	KPdePermRWRW,			// EUserSelfModCode
+	KPdePermRWRW,			// ESharedKernelSingle
+	KPdePermRWRW,			// ESharedKernelMultiple
+	KPdePermRWRW,			// ESharedIo
+	KPdePermRWNO,			// ESharedKernelMirror
+	KPdePermRWNO,			// EKernelMessage
+	};
+
+#if defined(KMMU)
+extern "C" void __DebugMsgFlushTLB()
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("FlushTLB"));
+	}
+
+extern "C" void __DebugMsgLocalFlushTLB()
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("FlushTLB"));
+	}
+
+extern "C" void __DebugMsgTotalFlushTLB()
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("TotalFlushTLB"));
+	}
+
+extern "C" void __DebugMsgINVLPG(int a)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("INVLPG(%08x)",a));
+	}
+#endif
+
+// Inline functions for simple transformations
+inline TLinAddr PageTableLinAddr(TInt aId)
+	{
+	return (KPageTableBase+(aId<<KPageTableShift));
+	}
+
+inline TPte* PageTable(TInt aId)
+	{
+	return (TPte*)(KPageTableBase+(aId<<KPageTableShift));
+	}
+
+inline TLinAddr PageDirectoryLinAddr(TInt aOsAsid)
+	{
+	return (KPageDirectoryBase+(aOsAsid<<KPageTableShift));
+	}
+
+extern "C" {
+
+void __fastcall DoInvalidateTLBForPage(TLinAddr /*aLinAddr*/);
+void DoInvalidateTLB();
+void DoLocalInvalidateTLB();
+
+}
+
+
+#ifdef __SMP__
+
+TSpinLock ShadowSpinLock(TSpinLock::EOrderGenericPreHigh0);	// Used when stopping other CPUs
+
+class TTLBIPI : public TGenericIPI
+	{
+public:
+	TTLBIPI();
+
+	static void InvalidateForPagesIsr(TGenericIPI*);
+	static void LocalInvalidateIsr(TGenericIPI*);
+	static void TotalInvalidateIsr(TGenericIPI*);
+	static void InvalidateIsr(TGenericIPI*);
+	static void WaitAndInvalidateIsr(TGenericIPI*);
+	void AddAddress(TLinAddr aAddr);
+	void InvalidateList();
+public:
+	volatile TInt	iFlag;
+	TInt			iCount;
+	TLinAddr		iAddr[KMaxPages];
+	};
+
+TTLBIPI::TTLBIPI()
+	:	iFlag(0), iCount(0)
+	{
+	}
+
+void TTLBIPI::LocalInvalidateIsr(TGenericIPI*)
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("TLBLocInv"));
+	DoLocalInvalidateTLB();
+	}
+
+void TTLBIPI::TotalInvalidateIsr(TGenericIPI*)
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("TLBTotInv"));
+	DoTotalInvalidateTLB();
+	}
+
+void TTLBIPI::InvalidateIsr(TGenericIPI*)
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("TLBInv"));
+	DoInvalidateTLB();
+	}
+
+void TTLBIPI::WaitAndInvalidateIsr(TGenericIPI* aTLBIPI)
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("TLBWtInv"));
+	TTLBIPI& a = *(TTLBIPI*)aTLBIPI;
+	while (!a.iFlag)
+		{}
+	if (a.iCount == 1)
+		DoInvalidateTLBForPage(a.iAddr[0]);
+	else
+		DoInvalidateTLB();
+	}
+
+void TTLBIPI::InvalidateForPagesIsr(TGenericIPI* aTLBIPI)
+	{
+	TTLBIPI& a = *(TTLBIPI*)aTLBIPI;
+	TInt i;
+	for (i=0; i<a.iCount; ++i)
+		{
+		__KTRACE_OPT(KMMU2,Kern::Printf("TLBInv %08x", a.iAddr[i]));
+		DoInvalidateTLBForPage(a.iAddr[i]);
+		}
+	}
+
+void TTLBIPI::AddAddress(TLinAddr aAddr)
+	{
+	iAddr[iCount] = aAddr;
+	if (++iCount == KMaxPages)
+		InvalidateList();
+	}
+
+void TTLBIPI::InvalidateList()
+	{
+	NKern::Lock();
+	InvalidateForPagesIsr(this);
+	QueueAllOther(&InvalidateForPagesIsr);
+	NKern::Unlock();
+	WaitCompletion();
+	iCount = 0;
+	}
+
+void LocalInvalidateTLB()
+	{
+	TTLBIPI ipi;
+	NKern::Lock();
+	DoLocalInvalidateTLB();
+	ipi.QueueAllOther(&TTLBIPI::LocalInvalidateIsr);
+	NKern::Unlock();
+	ipi.WaitCompletion();
+	}
+
+void TotalInvalidateTLB()
+	{
+	TTLBIPI ipi;
+	NKern::Lock();
+	DoTotalInvalidateTLB();
+	ipi.QueueAllOther(&TTLBIPI::TotalInvalidateIsr);
+	NKern::Unlock();
+	ipi.WaitCompletion();
+	}
+
+void InvalidateTLB()
+	{
+	TTLBIPI ipi;
+	NKern::Lock();
+	DoInvalidateTLB();
+	ipi.QueueAllOther(&TTLBIPI::InvalidateIsr);
+	NKern::Unlock();
+	ipi.WaitCompletion();
+	}
+
+void InvalidateTLBForPage(TLinAddr aAddr)
+	{
+	TTLBIPI ipi;
+	ipi.AddAddress(aAddr);
+	ipi.InvalidateList();
+	}
+
+#else
+#define	InvalidateTLBForPage(a)		DoInvalidateTLBForPage(a)
+#define	LocalInvalidateTLB()		DoLocalInvalidateTLB()
+#define	TotalInvalidateTLB()		TotalInvalidateTLB()
+#define	InvalidateTLB()				DoInvalidateTLB()
+#endif
+
+
+TPte* SafePageTableFromPde(TPde aPde)
+	{
+	if (aPde&KPdePtePresent)
+		{
+		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(aPde);
+		if (pi)
+			{
+			TInt id=pi->Offset();	// assumes page table size = page size
+			return PageTable(id);
+			}
+		}
+	return 0;
+	}
+
+TPte* SafePtePtrFromLinAddr(TLinAddr aAddress, TInt aOsAsid=0)
+	{
+	TPde pde = PageDirectory(aOsAsid)[aAddress>>KChunkShift];
+	TPte* pt = SafePageTableFromPde(pde);
+	if(pt)
+		pt += (aAddress>>KPageShift)&(KChunkMask>>KPageShift);
+	return pt;
+	}
+
+TPte* PtePtrFromLinAddr(TLinAddr aAddress, TInt aOsAsid=0)
+	{
+	TPde pde = PageDirectory(aOsAsid)[aAddress>>KChunkShift];
+	SPageInfo* pi = SPageInfo::FromPhysAddr(pde);
+	TInt id = (pi->Offset()<<KPtClusterShift) | ((pde>>KPageTableShift)&KPtClusterMask);
+	TPte* pt = PageTable(id);
+	pt += (aAddress>>KPageShift)&(KChunkMask>>KPageShift);
+	return pt;
+	}
+
+TInt X86Mmu::LinearToPhysical(TLinAddr aAddr, TInt aSize, TPhysAddr& aPhysicalAddress, TPhysAddr* aPhysicalPageList, TInt aOsAsid)
+	{
+	TPhysAddr physStart = LinearToPhysical(aAddr,aOsAsid);
+
+	TInt pageShift = iPageShift;
+	TUint32 page = aAddr>>pageShift<<pageShift;
+	TUint32 lastPage = (aAddr+aSize-1)>>pageShift<<pageShift;
+	TUint32* pageList = aPhysicalPageList;
+	TUint32 nextPhys = LinearToPhysical(page,aOsAsid);
+	TUint32 pageSize = 1<<pageShift;
+	while(page<=lastPage)
+		{
+		TPhysAddr phys = LinearToPhysical(page,aOsAsid);
+		if(pageList)
+			*pageList++ = phys;
+		if(phys!=nextPhys)
+			nextPhys = KPhysAddrInvalid;
+		else
+			nextPhys += pageSize;
+		page += pageSize;
+		}
+	if(nextPhys==KPhysAddrInvalid)
+		{
+		// Memory is discontiguous...
+		aPhysicalAddress = KPhysAddrInvalid;
+		return 1;
+		}
+	else
+		{
+		// Memory is contiguous...
+		aPhysicalAddress = physStart;
+		return KErrNone;
+		}
+	return KErrNone;
+	}
+
+TPhysAddr X86Mmu::LinearToPhysical(TLinAddr aLinAddr, TInt aOsAsid)
+//
+// Find the physical address corresponding to a given linear address in a specified OS
+// address space. Call with system locked.
+//
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("X86Mmu::LinearToPhysical(%08x,%d)",aLinAddr,aOsAsid));
+	TInt pdeIndex=aLinAddr>>KChunkShift;
+	TPde pde=PageDirectory(aOsAsid)[pdeIndex];
+	TPhysAddr pa=KPhysAddrInvalid;
+	if (pde & KPdePtePresent)
+		{
+		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pde);
+		if (pi)
+			{
+			TInt id=pi->Offset();	// assumes page table size = page size
+			TPte* pPte=PageTable(id);
+			TPte pte=pPte[(aLinAddr&KChunkMask)>>KPageShift];
+			if (pte & KPdePtePresent)
+				{
+				pa=(pte&KPdePtePhysAddrMask)+(aLinAddr&KPageMask);
+				__KTRACE_OPT(KMMU2,Kern::Printf("Mapped with page table - returning %08x",pa));
+				}
+			}
+		}
+	return pa;
+	}
+
+
+TInt X86Mmu::PreparePagesForDMA(TLinAddr /*aLinAddr*/, TInt /*aSize*/, TInt /*aOsAsid*/, TPhysAddr* /*aPhysicalPageList*/)
+	{
+	return KErrNotSupported;
+	}
+
+TInt X86Mmu::ReleasePagesFromDMA(TPhysAddr* /*aPhysicalPageList*/, TInt /*aPageCount*/)
+	{
+	return KErrNotSupported;
+	}
+
+static const TInt PermissionLookup[8]=
+	{
+	0,
+	EMapAttrReadSup|EMapAttrExecSup,
+	0,
+	EMapAttrWriteSup|EMapAttrReadSup|EMapAttrExecSup,
+	0,
+	EMapAttrReadUser|EMapAttrExecUser,
+	0,
+	EMapAttrWriteUser|EMapAttrReadUser|EMapAttrExecUser
+	};
+
+TInt X86Mmu::PageTableId(TLinAddr aAddr, TInt aOsAsid)
+	{
+	TInt id=-1;
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::PageTableId(%08x,%d)",aAddr,aOsAsid));
+	TInt pdeIndex=aAddr>>KChunkShift;
+	TPde pde=PageDirectory(aOsAsid)[pdeIndex];
+	if (pde & KPdePtePresent)
+		{
+		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pde);
+		if (pi)
+			id=pi->Offset();	// assumes page table size = page size
+		}
+	__KTRACE_OPT(KMMU,Kern::Printf("ID=%d",id));
+	return id;
+	}
+
+// Used only during boot for recovery of RAM drive
+TInt X86Mmu::BootPageTableId(TLinAddr aAddr, TPhysAddr& aPtPhys)
+	{
+	TInt id=KErrNotFound;
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu:BootPageTableId(%08x,&)",aAddr));
+	TPde* kpd=(TPde*)KPageDirectoryBase;	// kernel page directory
+	TInt pdeIndex=aAddr>>KChunkShift;
+	TPde pde = kpd[pdeIndex];
+	if (pde & KPdePtePresent)
+		{
+		aPtPhys = pde & KPdePtePhysAddrMask;
+		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pde);
+		if (pi)
+			{
+			SPageInfo::TType type = pi->Type();
+			if (type == SPageInfo::EPageTable)
+				id=pi->Offset();	// assumes page table size = page size
+			else if (type == SPageInfo::EUnused)
+				id = KErrUnknown;
+			}
+		}
+	__KTRACE_OPT(KMMU,Kern::Printf("ID=%d",id));
+	return id;
+	}
+
+TBool X86Mmu::PteIsPresent(TPte aPte)
+	{
+	return aPte & KPdePtePresent;
+	}
+
+TPhysAddr X86Mmu::PtePhysAddr(TPte aPte, TInt /*aPteIndex*/)
+	{
+	return aPte & KPdePtePhysAddrMask;
+	}
+
+TPhysAddr X86Mmu::PdePhysAddr(TLinAddr aAddr)
+	{
+	TPde* kpd = (TPde*)KPageDirectoryBase;	// kernel page directory
+	TPde pde = kpd[aAddr>>KChunkShift];
+	if (pde & (KPdePtePresent|KPdeLargePage) == (KPdePtePresent|KPdeLargePage))
+		return pde & KPdeLargePagePhysAddrMask;
+	return KPhysAddrInvalid;
+	}
+
+void X86Mmu::Init1()
+	{
+	__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("X86Mmu::Init1"));
+
+	TUint pge = TheSuperPage().iCpuId & EX86Feat_PGE;
+	iPteGlobal = pge ? KPdePteGlobal : 0;	
+	X86_UseGlobalPTEs = pge!=0;
+
+	// MmuBase data
+	iPageSize=KPageSize;
+	iPageMask=KPageMask;
+	iPageShift=KPageShift;
+	iChunkSize=KChunkSize;
+	iChunkMask=KChunkMask;
+	iChunkShift=KChunkShift;
+	iPageTableSize=KPageTableSize;
+	iPageTableMask=KPageTableMask;
+	iPageTableShift=KPageTableShift;
+	iPtClusterSize=KPtClusterSize;
+	iPtClusterMask=KPtClusterMask;
+	iPtClusterShift=KPtClusterShift;
+	iPtBlockSize=KPtBlockSize;
+	iPtBlockMask=KPtBlockMask;
+	iPtBlockShift=KPtBlockShift;
+	iPtGroupSize=KChunkSize/KPageTableSize;
+	iPtGroupMask=iPtGroupSize-1;
+	iPtGroupShift=iChunkShift-iPageTableShift;
+	//TInt* iPtBlockCount;		// dynamically allocated - Init2
+	//TInt* iPtGroupCount;		// dynamically allocated - Init2
+	iPtInfo=(SPageTableInfo*)KPageTableInfoBase;
+	iPageTableLinBase=KPageTableBase;
+	//iRamPageAllocator;		// dynamically allocated - Init2
+	//iAsyncFreeList;			// dynamically allocated - Init2
+	//iPageTableAllocator;		// dynamically allocated - Init2
+	//iPageTableLinearAllocator;// dynamically allocated - Init2
+	iPtInfoPtePerm=KPtInfoPtePerm|iPteGlobal;
+	iPtPtePerm=KPtPtePerm|iPteGlobal;
+	iPtPdePerm=KPtPdePerm;
+	iUserCodeLoadPtePerm=KPdePermRWNO;
+	iKernelCodePtePerm=KPdePermRONO|iPteGlobal;
+	iTempAddr=KTempAddr;
+	iSecondTempAddr=KSecondTempAddr;
+
+	TUint pse = TheSuperPage().iCpuId & EX86Feat_PSE;
+	iMapSizes = pse ? KPageSize|KChunkSize : KPageSize;
+
+	iDecommitThreshold=0;		// no cache consistency issues on decommit
+	iRomLinearBase = ::RomHeaderAddress;
+	iRomLinearEnd = KRomLinearEnd;
+	iShadowPtePerm = KShadowPtePerm;
+	iShadowPdePerm = KShadowPdePerm;
+
+	// Mmu data
+	TInt total_ram=TheSuperPage().iTotalRamSize;
+
+	iNumOsAsids=1024;
+	iNumGlobalPageDirs=1;
+	//iOsAsidAllocator;			// dynamically allocated - Init2
+	iGlobalPdSize=KPageTableSize;
+	iGlobalPdShift=KPageTableShift;
+	iLocalPdSize=0;
+	iLocalPdShift=0;
+	iAsidGroupSize=KChunkSize/KPageTableSize;
+	iAsidGroupMask=iAsidGroupSize-1;
+	iAsidGroupShift=iChunkShift-iGlobalPdShift;
+	iAliasSize=KPageSize;
+	iAliasMask=KPageMask;
+	iAliasShift=KPageShift;
+	iUserLocalBase=KUserLocalDataBase;
+	iUserSharedBase=KUserSharedDataBase;
+	iAsidInfo=(TUint32*)KAsidInfoBase;
+	iPdeBase=KPageDirectoryBase;
+	iPdPtePerm=KPdPtePerm|iPteGlobal;
+	iPdPdePerm=KPdPdePerm;
+	iRamDriveMask=0x00f00000;
+	iGlobalCodePtePerm=KPdePermRORO|iPteGlobal;
+
+	iMaxDllDataSize=Min(total_ram/2, 0x08000000);				// phys RAM/2 up to 128Mb
+	iMaxDllDataSize=(iMaxDllDataSize+iChunkMask)&~iChunkMask;	// round up to chunk size
+	iMaxUserCodeSize=Min(total_ram, 0x10000000);				// phys RAM up to 256Mb
+	iMaxUserCodeSize=(iMaxUserCodeSize+iChunkMask)&~iChunkMask;	// round up to chunk size
+	iUserLocalEnd=iUserSharedBase-iMaxDllDataSize;
+	iUserSharedEnd=KUserSharedDataEnd-iMaxUserCodeSize;
+	iDllDataBase=iUserLocalEnd;
+	iUserCodeBase=iUserSharedEnd;
+	__KTRACE_OPT(KMMU,Kern::Printf("ULB %08x ULE %08x USB %08x USE %08x",iUserLocalBase,iUserLocalEnd,
+																			iUserSharedBase,iUserSharedEnd));
+	__KTRACE_OPT(KMMU,Kern::Printf("DDB %08x UCB %08x",iDllDataBase,iUserCodeBase));
+
+	// X86Mmu data
+
+	// other
+	PP::MaxUserThreadStack=0x14000;			// 80K - STDLIB asks for 64K for PosixServer!!!!
+	PP::UserThreadStackGuard=0x2000;		// 8K
+	PP::MaxStackSpacePerProcess=0x200000;	// 2Mb
+	K::SupervisorThreadStackSize=0x1000;	// 4K
+	PP::SupervisorThreadStackGuard=0x1000;	// 4K
+	K::MachineConfig=(TMachineConfig*)KMachineConfigLinAddr;
+	PP::RamDriveStartAddress=KRamDriveStartAddress;
+	PP::RamDriveRange=KRamDriveMaxSize;
+	PP::RamDriveMaxSize=KRamDriveMaxSize;	// may be reduced later
+	K::MemModelAttributes=EMemModelTypeMultiple|EMemModelAttrNonExProt|EMemModelAttrKernProt|EMemModelAttrWriteProt|
+						EMemModelAttrVA|EMemModelAttrProcessProt|EMemModelAttrSameVA|EMemModelAttrSvKernProt|
+						EMemModelAttrIPCKernProt|EMemModelAttrRamCodeProt;
+
+#ifdef __SMP__
+	ApTrampolinePage = KApTrampolinePageLin;
+
+	TInt i;
+	for (i=0; i<KMaxCpus; ++i)
+		{
+		TSubScheduler& ss = TheSubSchedulers[i];
+		TLinAddr a = KIPCAlias + (i<<KChunkShift);
+		ss.i_AliasLinAddr = (TAny*)a;
+		ss.i_AliasPdePtr = (TAny*)(KPageDirectoryBase + (a>>KChunkShift)*sizeof(TPde));
+		}
+#endif
+
+	Mmu::Init1();
+	}
+
+void X86Mmu::DoInit2()
+	{
+	__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("X86Mmu::DoInit2"));
+	iTempPte=PageTable(PageTableId(iTempAddr,0))+((iTempAddr&KChunkMask)>>KPageShift);
+	iSecondTempPte=PageTable(PageTableId(iSecondTempAddr,0))+((iSecondTempAddr&KChunkMask)>>KPageShift);
+	__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iTempAddr=%08x, iTempPte=%08x, iSecondTempAddr=%08x, iSecondTempPte=%08x",
+			iTempAddr, iTempPte, iSecondTempAddr, iSecondTempPte));
+	CreateKernelSection(KKernelSectionEnd, iAliasShift);
+	CreateUserGlobalSection(KUserGlobalDataBase, KUserGlobalDataEnd);
+	iUserHwChunkAllocator=THwChunkAddressAllocator::New(0, iUserGlobalSection);
+	__ASSERT_ALWAYS(iUserHwChunkAllocator, Panic(ECreateUserGlobalSectionFailed));
+	Mmu::DoInit2();
+	}
+
+#ifndef __MMU_MACHINE_CODED__
+void X86Mmu::MapRamPages(TInt aId, SPageInfo::TType aType, TAny* aPtr, TUint32 aOffset, const TPhysAddr* aPageList, TInt aNumPages, TPte aPtePerm)
+//
+// Map a list of physical RAM pages into a specified page table with specified PTE permissions.
+// Update the page information array.
+// Call this with the system locked.
+//
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::MapRamPages() id=%d type=%d ptr=%08x off=%08x n=%d perm=%08x",
+			aId, aType, aPtr, aOffset, aNumPages, aPtePerm));
+
+	SPageTableInfo& ptinfo=iPtInfo[aId];
+	ptinfo.iCount+=aNumPages;
+	aOffset>>=KPageShift;
+	TInt ptOffset=aOffset & KPagesInPDEMask;				// entry number in page table
+	TPte* pPte=PageTable(aId)+ptOffset;						// address of first PTE
+	while(aNumPages--)
+		{
+		TPhysAddr pa = *aPageList++;
+		*pPte++ =  pa | aPtePerm;					// insert PTE
+		__KTRACE_OPT(KMMU,Kern::Printf("Writing PTE %08x to %08x",pPte[-1],pPte-1));
+		if (aType!=SPageInfo::EInvalid)
+			{
+			SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pa);
+			if(pi)
+				{
+				pi->Set(aType,aPtr,aOffset);
+				__KTRACE_OPT(KMMU,Kern::Printf("I: %d %08x %08x",aType,aPtr,aOffset));
+				++aOffset;	// increment offset for next page
+				}
+			}
+		}
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::MapPhysicalPages(TInt aId, SPageInfo::TType aType, TAny* aPtr, TUint32 aOffset, TPhysAddr aPhysAddr, TInt aNumPages, TPte aPtePerm)
+//
+// Map consecutive physical pages into a specified page table with specified PTE permissions.
+// Update the page information array if RAM pages are being mapped.
+// Call this with the system locked.
+//
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::MapPhysicalPages() id=%d type=%d ptr=%08x off=%08x phys=%08x n=%d perm=%08x",
+			aId, aType, aPtr, aOffset, aPhysAddr, aNumPages, aPtePerm));
+	SPageTableInfo& ptinfo=iPtInfo[aId];
+	ptinfo.iCount+=aNumPages;
+	aOffset>>=KPageShift;
+	TInt ptOffset=aOffset & KPagesInPDEMask;				// entry number in page table
+	TPte* pPte=(TPte*)(PageTableLinAddr(aId))+ptOffset;		// address of first PTE
+	SPageInfo* pi;
+	if(aType==SPageInfo::EInvalid)
+		pi = NULL;
+	else
+		pi = SPageInfo::SafeFromPhysAddr(aPhysAddr);
+	while(aNumPages--)
+		{
+		*pPte++ = aPhysAddr|aPtePerm;						// insert PTE
+		aPhysAddr+=KPageSize;
+		__KTRACE_OPT(KMMU,Kern::Printf("Writing PTE %08x to %08x",pPte[-1],pPte-1));
+		if (pi)
+			{
+			pi->Set(aType,aPtr,aOffset);
+			++aOffset;										// increment offset for next page
+			__KTRACE_OPT(KMMU,Kern::Printf("I: %d %08x %08x",aType,aPtr,aOffset));
+			++pi;
+			}
+		}
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::MapVirtual(TInt /*aId*/, TInt /*aNumPages*/)
+//
+// Used in the implementation of demand paging - not supported on x86
+//
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::RemapPage(TInt /*aId*/, TUint32 /*aAddr*/, TPhysAddr /*aOldAddr*/, TPhysAddr /*aNewAddr*/, TPte /*aPtePerm*/, DProcess* /*aProcess*/)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::RemapPageByAsid(TBitMapAllocator* /*aOsAsids*/, TLinAddr /*aLinAddr*/, TPhysAddr /*aOldAddr*/, TPhysAddr /*aNewAddr*/, TPte /*aPtePerm*/)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+TInt X86Mmu::UnmapPages(TInt aId, TUint32 aAddr, TInt aNumPages, TPhysAddr* aPageList, TBool aSetPagesFree, TInt& aNumPtes, TInt& aNumFree, DProcess*)
+//
+// Unmap a specified area at address aAddr in page table aId. Place physical addresses of unmapped
+// pages into aPageList, and count of unmapped pages into aNumPtes.
+// Return number of pages still mapped using this page table.
+// Call this with the system locked.
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::UnmapPages() id=%d off=%08x n=%d pl=%08x set-free=%08x",aId,aAddr,aNumPages,aPageList,aSetPagesFree));
+	TInt ptOffset=(aAddr&KChunkMask)>>KPageShift;			// entry number in page table
+	TPte* pPte=PageTable(aId)+ptOffset;						// address of first PTE
+	TInt np=0;
+	TInt nf=0;
+#ifdef __SMP__
+	TTLBIPI ipi;
+#endif
+	while(aNumPages--)
+		{
+		TPte pte=*pPte;						// get original PTE
+		*pPte++=0;							// clear PTE
+		if (pte & KPdePtePresent)
+			{
+#ifdef __SMP__
+			ipi.AddAddress(aAddr);
+#else
+			InvalidateTLBForPage(aAddr);	// flush any corresponding TLB entry
+#endif
+			++np;							// count unmapped pages
+			TPhysAddr pa=pte & KPdePtePhysAddrMask;	// physical address of unmapped page
+			if (aSetPagesFree)
+				{
+				SPageInfo* pi = SPageInfo::FromPhysAddr(pa);
+				if(iRamCache->PageUnmapped(pi))
+					{
+					pi->SetUnused();					// mark page as unused
+					if (pi->LockCount()==0)
+						{
+						*aPageList++=pa;			// store in page list
+						++nf;						// count free pages
+						}
+					}
+				}
+			else
+				*aPageList++=pa;				// store in page list
+			}
+		aAddr+=KPageSize;
+		}
+#ifdef __SMP__
+	ipi.InvalidateList();
+#endif
+	aNumPtes=np;
+	aNumFree=nf;
+	SPageTableInfo& ptinfo=iPtInfo[aId];
+	TInt r=(ptinfo.iCount-=np);
+	__DRAIN_WRITE_BUFFER;
+	__KTRACE_OPT(KMMU,Kern::Printf("Pages recovered %d Pages remaining %d NF=%d",np,r,nf));
+	return r;								// return number of pages remaining in this page table
+	}
+
+TInt X86Mmu::UnmapUnownedPages(TInt aId, TUint32 aAddr, TInt aNumPages, TPhysAddr* aPageList, TLinAddr* aLAPageList, TInt& aNumPtes, TInt& aNumFree, DProcess*)
+//
+// Unmap a specified area at address aAddr in page table aId. Place physical addresses of unmapped
+// pages into aPageList, and count of unmapped pages into aNumPtes.
+// Return number of pages still mapped using this page table.
+// Call this with the system locked.
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::UnmapPages() id=%d off=%08x n=%d pl=%08x",aId,aAddr,aNumPages,aPageList));
+	TInt ptOffset=(aAddr&KChunkMask)>>KPageShift;			// entry number in page table
+	TPte* pPte=PageTable(aId)+ptOffset;						// address of first PTE
+	TInt np=0;
+	TInt nf=0;
+#ifdef __SMP__
+	TTLBIPI ipi;
+#endif
+	while(aNumPages--)
+		{
+		TPte pte=*pPte;						// get original PTE
+		*pPte++=0;							// clear PTE
+		if (pte & KPdePtePresent)
+			{
+#ifdef __SMP__
+			ipi.AddAddress(aAddr);
+#else
+			InvalidateTLBForPage(aAddr);	// flush any corresponding TLB entry
+#endif
+			++np;							// count unmapped pages
+			TPhysAddr pa=pte & KPdePtePhysAddrMask;	// physical address of unmapped page
+
+			nf++;
+			*aPageList++=pa;				// store in page list
+			*aLAPageList++ = aAddr;
+			}
+		aAddr+=KPageSize;
+		}
+#ifdef __SMP__
+	ipi.InvalidateList();
+#endif
+	aNumPtes=np;
+	aNumFree=nf;
+	SPageTableInfo& ptinfo=iPtInfo[aId];
+	TInt r=(ptinfo.iCount-=np);
+	__DRAIN_WRITE_BUFFER;
+	__KTRACE_OPT(KMMU,Kern::Printf("Pages recovered %d Pages remaining %d NF=%d",np,r,nf));
+	return r;								// return number of pages remaining in this page table
+	}
+
+TInt X86Mmu::UnmapVirtual(TInt /*aId*/, TUint32 /*aAddr*/, TInt /*aNumPages*/, TPhysAddr* /*aPageList*/, TBool /*aSetPagesFree*/, TInt& /*aNumPtes*/, TInt& /*aNumFree*/, DProcess* /*aProcess*/)
+//
+// Used in the implementation of demand paging - not supported on x86
+//
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	return 0; // keep compiler happy
+	}
+
+TInt X86Mmu::UnmapUnownedVirtual(TInt /*aId*/, TUint32 /*aAddr*/, TInt /*aNumPages*/, TPhysAddr* /*aPageList*/, TLinAddr*  /*aLALinAddr*/, TInt& /*aNumPtes*/, TInt& /*aNumFree*/, DProcess* /*aProcess*/)
+//
+// Used in the implementation of demand paging - not supported on x86
+//
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	return 0; // keep compiler happy
+	}
+
+void X86Mmu::DoAssignPageTable(TInt aId, TLinAddr aAddr, TPde aPdePerm, const TAny* aOsAsids)
+//
+// Assign an allocated page table to map a given linear address with specified permissions.
+// This should be called with the system unlocked and the MMU mutex held.
+//
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::DoAssignPageTable %d to %08x perm %08x asid %08x",aId,aAddr,aPdePerm,aOsAsids));
+	TLinAddr ptLin=PageTableLinAddr(aId);
+	TPhysAddr ptPhys=LinearToPhysical(ptLin,0);
+	TInt pdeIndex=TInt(aAddr>>KChunkShift);
+	TInt os_asid=(TInt)aOsAsids;
+	if (TUint32(os_asid)<TUint32(iNumOsAsids))
+		{
+		// single OS ASID
+		TPde* pageDir=PageDirectory(os_asid);
+		NKern::LockSystem();
+		pageDir[pdeIndex]=ptPhys|aPdePerm;
+		NKern::UnlockSystem();
+		__KTRACE_OPT(KMMU,Kern::Printf("Writing PDE %08x to %08x",ptPhys|aPdePerm,pageDir+pdeIndex));
+		}
+	else
+		{
+		// selection of OS ASIDs or all OS ASIDs
+		const TBitMapAllocator* pB=(const TBitMapAllocator*)aOsAsids;
+		if (os_asid==-1)
+			pB=iOsAsidAllocator;	// 0's in positions which exist
+		TInt num_os_asids=pB->iSize-pB->iAvail;
+		for (os_asid=0; num_os_asids; ++os_asid)
+			{
+			if (pB->NotAllocated(os_asid,1))
+				continue;			// os_asid is not needed
+			TPde* pageDir=PageDirectory(os_asid);
+			NKern::LockSystem();
+			pageDir[pdeIndex]=ptPhys|aPdePerm;
+			NKern::UnlockSystem();
+			__KTRACE_OPT(KMMU,Kern::Printf("Writing PDE %08x to %08x",ptPhys|aPdePerm,pageDir+pdeIndex));
+			--num_os_asids;
+			}
+		}
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::RemapPageTableSingle(TPhysAddr aOld, TPhysAddr aNew, TLinAddr aAddr, TInt aOsAsid)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::RemapPageTableGlobal(TPhysAddr aOld, TPhysAddr aNew, TLinAddr aAddr)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::RemapPageTableMultiple(TPhysAddr aOld, TPhysAddr aNew, TLinAddr aAddr, const TAny* aOsAsids)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::RemapPageTableAliases(TPhysAddr aOld, TPhysAddr aNew)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+void X86Mmu::DoUnassignPageTable(TLinAddr aAddr, const TAny* aOsAsids)
+//
+// Unassign a now-empty page table currently mapping the specified linear address.
+// We assume that TLB and/or cache flushing has been done when any RAM pages were unmapped.
+// This should be called with the system unlocked and the MMU mutex held.
+//
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::DoUnassignPageTable at %08x a=%08x",aAddr,aOsAsids));
+	TInt pdeIndex=TInt(aAddr>>KChunkShift);
+	TInt os_asid=(TInt)aOsAsids;
+	TUint pde=0;
+
+	SDblQue checkedList;
+	SDblQueLink* next;
+
+	if (TUint32(os_asid)<TUint32(iNumOsAsids))
+		{
+		// single OS ASID
+		TPde* pageDir=PageDirectory(os_asid);
+		NKern::LockSystem();
+		pde = pageDir[pdeIndex];
+		pageDir[pdeIndex]=0;
+		__KTRACE_OPT(KMMU,Kern::Printf("Clearing PDE at %08x",pageDir+pdeIndex));
+
+		// remove any aliases of the page table...
+		TUint ptId = pde>>KPageTableShift;
+		while(!iAliasList.IsEmpty())
+			{
+			next = iAliasList.First()->Deque();
+			checkedList.Add(next);
+			DMemModelThread* thread = _LOFF(next, DMemModelThread, iAliasLink);
+			if(thread->iAliasOsAsid==os_asid && (thread->iAliasPde>>KPageTableShift)==ptId)
+				{
+				// the page table is being aliased by the thread, so remove it...
+				thread->iAliasPde = 0;
+				}
+			NKern::FlashSystem();
+			}
+		}
+	else
+		{
+		// selection of OS ASIDs or all OS ASIDs
+		const TBitMapAllocator* pB=(const TBitMapAllocator*)aOsAsids;
+		if (os_asid==-1)
+			pB=iOsAsidAllocator;	// 0's in positions which exist
+		TInt num_os_asids=pB->iSize-pB->iAvail;
+		for (os_asid=0; num_os_asids; ++os_asid)
+			{
+			if (pB->NotAllocated(os_asid,1))
+				continue;			// os_asid is not needed
+			TPde* pageDir=PageDirectory(os_asid);
+			NKern::LockSystem();
+			pde = pageDir[pdeIndex];
+			pageDir[pdeIndex]=0;
+			NKern::UnlockSystem();
+			__KTRACE_OPT(KMMU,Kern::Printf("Clearing PDE at %08x",pageDir+pdeIndex));
+			--num_os_asids;
+			}
+
+		// remove any aliases of the page table...
+		TUint ptId = pde>>KPageTableShift;
+		NKern::LockSystem();
+		while(!iAliasList.IsEmpty())
+			{
+			next = iAliasList.First()->Deque();
+			checkedList.Add(next);
+			DMemModelThread* thread = _LOFF(next, DMemModelThread, iAliasLink);
+			if((thread->iAliasPde>>KPageTableShift)==ptId && !pB->NotAllocated(thread->iAliasOsAsid,1))
+				{
+				// the page table is being aliased by the thread, so remove it...
+				thread->iAliasPde = 0;
+				}
+			NKern::FlashSystem();
+			}
+		}
+
+	// copy checkedList back to iAliasList
+	iAliasList.MoveFrom(&checkedList);
+
+	NKern::UnlockSystem();
+
+	__DRAIN_WRITE_BUFFER; // because page tables have been updated
+	}
+#endif
+
+// Initialise page table at physical address aXptPhys to be used as page table aXptId
+// to expand the virtual address range used for mapping page tables. Map the page table
+// at aPhysAddr as page table aId using the expanded range.
+// Assign aXptPhys to kernel's Page Directory.
+// Called with system unlocked and MMU mutex held.
+void X86Mmu::BootstrapPageTable(TInt aXptId, TPhysAddr aXptPhys, TInt aId, TPhysAddr aPhysAddr)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::BootstrapPageTable xptid=%04x, xptphys=%08x, id=%04x, phys=%08x",
+						aXptId, aXptPhys, aId, aPhysAddr));
+	
+	// put in a temporary mapping for aXptPhys
+	*iTempPte = aXptPhys | KPtPtePerm | iPteGlobal;
+	__DRAIN_WRITE_BUFFER;
+
+	// clear XPT
+	TPte* xpt=(TPte*)iTempAddr;
+	memclr(xpt, KPageSize);
+
+	// map XPT
+	xpt[aXptId & KPagesInPDEMask] = aXptPhys | KPtPtePerm | iPteGlobal;
+
+	// map other page table
+	xpt[aId & KPagesInPDEMask] = aPhysAddr | KPtPtePerm | iPteGlobal;
+
+	// remove temporary mapping
+	iTempPte=0;
+	__DRAIN_WRITE_BUFFER;
+	InvalidateTLBForPage(iTempAddr);
+
+	// initialise PtInfo...
+	TLinAddr xptAddr = PageTableLinAddr(aXptId);
+	iPtInfo[aXptId].SetGlobal(xptAddr>>KChunkShift);
+
+	// map xpt...
+	TInt pdeIndex=TInt(xptAddr>>KChunkShift);
+	TPde* pageDir=PageDirectory(0);
+	NKern::LockSystem();
+	pageDir[pdeIndex]=aXptPhys|KPtPdePerm;
+	__DRAIN_WRITE_BUFFER;
+	NKern::UnlockSystem();				
+	}
+
+void X86Mmu::FixupXPageTable(TInt aId, TLinAddr aTempMap, TPhysAddr aOld, TPhysAddr aNew)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+TInt X86Mmu::NewPageDirectory(TInt aOsAsid, TBool aSeparateGlobal, TPhysAddr& aPhysAddr, TInt& aNumPages)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::NewPageDirectory(%d,%d)",aOsAsid,aSeparateGlobal));
+	TInt r=AllocRamPages(&aPhysAddr,1, EPageFixed);
+	if (r!=KErrNone)
+		return r;
+#ifdef BTRACE_KERNEL_MEMORY
+	BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscAlloc, 1<<KPageShift);
+	Epoc::KernelMiscPages += 1;
+#endif
+	SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr);
+	NKern::LockSystem();
+	pi->SetPageDir(aOsAsid,0);
+	NKern::UnlockSystem();
+	aNumPages=1;
+	return KErrNone;
+	}
+
+inline void CopyPdes(TPde* aDest, const TPde* aSrc, TLinAddr aBase, TLinAddr aEnd)
+	{
+	memcpy(aDest+(aBase>>KChunkShift), aSrc+(aBase>>KChunkShift), ((aEnd-aBase)>>KChunkShift)*sizeof(TPde));
+	}
+
+inline void ZeroPdes(TPde* aDest, TLinAddr aBase, TLinAddr aEnd)
+	{
+	memclr(aDest+(aBase>>KChunkShift), ((aEnd-aBase)>>KChunkShift)*sizeof(TPde));
+	}
+
+void X86Mmu::InitPageDirectory(TInt aOsAsid, TBool)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::InitPageDirectory(%d)",aOsAsid));
+	TPde* newpd=PageDirectory(aOsAsid);					// new page directory
+	const TPde* kpd=(const TPde*)KPageDirectoryBase;	// kernel page directory
+	ZeroPdes(newpd, 0x00000000, KUserSharedDataEnd);	// clear user mapping area
+	ZeroPdes(newpd, KRamDriveStartAddress, KRamDriveEndAddress);	// don't copy RAM drive
+	CopyPdes(newpd, kpd, KRomLinearBase, KUserGlobalDataEnd);		// copy ROM + user global
+	CopyPdes(newpd, kpd, KRamDriveEndAddress, 0x00000000);			// copy kernel mappings
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::ClearPageTable(TInt aId, TInt aFirstIndex)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu:ClearPageTable(%d,%d)",aId,aFirstIndex));
+	TPte* pte=PageTable(aId);
+	memclr(pte+aFirstIndex, KPageSize-aFirstIndex*sizeof(TPte));
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::ApplyTopLevelPermissions(TLinAddr aAddr, TInt aOsAsid, TInt aNumPdes, TPde aPdePerm)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::ApplyTopLevelPermissions %04x:%08x->%08x count %d",
+												aOsAsid, aAddr, aPdePerm, aNumPdes));
+	TInt ix=aAddr>>KChunkShift;
+	TPde* pPde=PageDirectory(aOsAsid)+ix;
+	TPde* pPdeEnd=pPde+aNumPdes;
+	NKern::LockSystem();
+	for (; pPde<pPdeEnd; ++pPde)
+		{
+		TPde pde=*pPde;
+		if (pde)
+			*pPde = (pde&KPdePtePhysAddrMask)|aPdePerm;
+		}
+	NKern::UnlockSystem();
+	(aAddr>=KUserSharedDataEnd) ? InvalidateTLB() : LocalInvalidateTLB();
+	__DRAIN_WRITE_BUFFER;
+	}
+
+void X86Mmu::ApplyPagePermissions(TInt aId, TInt aPageOffset, TInt aNumPages, TPte aPtePerm)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu::ApplyPagePermissions %04x:%03x+%03x perm %08x",
+												aId, aPageOffset, aNumPages, aPtePerm));
+	TPte* pPte=PageTable(aId)+aPageOffset;
+	TPde* pPteEnd=pPte+aNumPages;
+	TPte g=0;
+	NKern::LockSystem();
+	for (; pPte<pPteEnd; ++pPte)
+		{
+		TPte pte=*pPte;
+		g |= pte;
+		if (pte)
+			*pPte = (pte&KPdePtePhysAddrMask)|aPtePerm;
+		}
+	NKern::UnlockSystem();
+	(g & KPdePteGlobal) ? InvalidateTLB() : LocalInvalidateTLB();
+	__DRAIN_WRITE_BUFFER;
+	}
+
+
+// Set up a page table (specified by aId) to map a 4Mb section of ROM containing aRomAddr
+// using ROM at aOrigPhys.
+void X86Mmu::InitShadowPageTable(TInt aId, TLinAddr aRomAddr, TPhysAddr aOrigPhys)
+	{
+	(void)aId, (void)aRomAddr, (void)aOrigPhys;
+	FAULT();	// Never used
+/*
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:InitShadowPageTable id=%04x aRomAddr=%08x aOrigPhys=%08x",
+		aId, aRomAddr, aOrigPhys));
+	TPte* ppte = PageTable(aId);
+	TPte* ppte_End = ppte + KChunkSize/KPageSize;
+	TPhysAddr phys = aOrigPhys - (aRomAddr & KChunkMask);
+	for (; ppte<ppte_End; ++ppte, phys+=KPageSize)
+		*ppte = phys | KRomPtePerm;
+	__DRAIN_WRITE_BUFFER;
+*/
+	}
+
+// Copy the contents of ROM at aRomAddr to a shadow page at physical address aShadowPhys
+void X86Mmu::InitShadowPage(TPhysAddr aShadowPhys, TLinAddr aRomAddr)
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:InitShadowPage aShadowPhys=%08x aRomAddr=%08x",
+		aShadowPhys, aRomAddr));
+
+	// put in a temporary mapping for aShadowPhys
+	// make it noncacheable
+	*iTempPte = aShadowPhys | KPtPtePerm | iPteGlobal;
+	__DRAIN_WRITE_BUFFER;
+
+	// copy contents of ROM
+	wordmove( (TAny*)iTempAddr, (const TAny*)aRomAddr, KPageSize );
+	__DRAIN_WRITE_BUFFER;	// make sure contents are written to memory
+
+	// remove temporary mapping
+	*iTempPte=0;
+	__DRAIN_WRITE_BUFFER;
+	InvalidateTLBForPage(iTempAddr);
+	}
+
+// Assign a shadow page table to replace a ROM section mapping
+// Enter and return with system locked
+void X86Mmu::AssignShadowPageTable(TInt aId, TLinAddr aRomAddr)
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:AssignShadowPageTable aId=%04x aRomAddr=%08x",
+		aId, aRomAddr));
+	TLinAddr ptLin=PageTableLinAddr(aId);
+	TPhysAddr ptPhys=LinearToPhysical(ptLin, 0);
+	TPde* ppde = ::InitPageDirectory + (aRomAddr>>KChunkShift);
+	TPde newpde = ptPhys | KShadowPdePerm;
+	__KTRACE_OPT(KMMU,Kern::Printf("Writing PDE %08x to %08x", newpde, ppde));
+#ifdef __SMP__
+	TTLBIPI ipi;
+	NKern::Lock();		// stop other processors passing this point
+	ShadowSpinLock.LockOnly();
+	ipi.QueueAllOther(&TTLBIPI::WaitAndInvalidateIsr);
+	ipi.WaitEntry();	// wait for other processors to stop in the ISR
+#endif
+	TInt irq=NKern::DisableAllInterrupts();
+	*ppde = newpde;		// map in the page table
+	__DRAIN_WRITE_BUFFER;	// make sure new PDE written to main memory
+	DoInvalidateTLB();	// completely flush TLB
+	NKern::RestoreInterrupts(irq);
+#ifdef __SMP__
+	ipi.iFlag = 1;		// release other processors so they can flush their TLBs
+	ipi.WaitCompletion();	// wait for other processors to flush their TLBs
+	ShadowSpinLock.UnlockOnly();
+	NKern::Unlock();
+#endif
+	}
+
+void X86Mmu::DoUnmapShadowPage(TInt aId, TLinAddr aRomAddr, TPhysAddr aOrigPhys)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu:DoUnmapShadowPage, id=%04x lin=%08x origphys=%08x", aId, aRomAddr, aOrigPhys));
+	TPte* ppte = PageTable(aId) + ((aRomAddr & KChunkMask)>>KPageShift);
+	TPte newpte = aOrigPhys | KRomPtePerm;
+	__KTRACE_OPT(KMMU,Kern::Printf("Writing PTE %08x to %08x", newpte, ppte));
+#ifdef __SMP__
+	TTLBIPI ipi;
+	ipi.AddAddress(aRomAddr);
+	NKern::Lock();		// stop other processors passing this point
+	ShadowSpinLock.LockOnly();
+	ipi.QueueAllOther(&TTLBIPI::WaitAndInvalidateIsr);
+	ipi.WaitEntry();	// wait for other processors to stop
+#endif
+	TInt irq=NKern::DisableAllInterrupts();
+	*ppte = newpte;
+	__DRAIN_WRITE_BUFFER;
+	DoInvalidateTLBForPage(aRomAddr);
+	NKern::RestoreInterrupts(irq);
+#ifdef __SMP__
+	ipi.iFlag = 1;		// release other processors so they can flush their TLBs
+	ipi.WaitCompletion();	// wait for other processors to flush their TLBs
+	ShadowSpinLock.UnlockOnly();
+	NKern::Unlock();
+#endif
+	}
+
+TInt X86Mmu::UnassignShadowPageTable(TLinAddr /*aRomAddr*/, TPhysAddr /*aOrigPhys*/)
+	{
+	// not used since we use page mappings for the ROM
+	return KErrGeneral;
+	}
+
+TInt X86Mmu::CopyToShadowMemory(TLinAddr aDest, TLinAddr aSrc, TUint32 aLength)
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:CopyToShadowMemory aDest=%08x aSrc=%08x aLength=%08x", aDest, aSrc, aLength));
+
+	// Check that destination is ROM
+	if (aDest<iRomLinearBase || (aDest+aLength) > iRomLinearEnd)
+		{
+		__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu:CopyToShadowMemory: Destination not entirely in ROM"));
+		return KErrArgument;
+		}
+
+	// do operation with RamAlloc mutex held (to prevent shadow pages from being released from under us)
+	Kern::MutexWait(*RamAllocatorMutex);
+
+	TInt r = KErrNone;
+	while (aLength)
+		{
+		// Calculate memory size to copy in this loop. A single page region will be copied per loop
+		TInt copySize = Min(aLength, iPageSize - (aDest&iPageMask));
+
+		// Get physical address
+		TPhysAddr	physAddr = LinearToPhysical(aDest&~iPageMask, 0);
+		if (KPhysAddrInvalid==physAddr)
+			{
+			r = KErrArgument;
+			break;
+			}
+		
+		//check whether it is shadowed rom
+		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(physAddr);
+		if (pi==0 || pi->Type()!=SPageInfo::EShadow)
+			{
+			__KTRACE_OPT(KMMU,Kern::Printf("X86Mmu:CopyToShadowMemory: No shadow page at this address"));
+			r = KErrArgument;
+			break;
+			}
+
+		//Temporarily map into writable memory and copy data. RamAllocator DMutex is required
+		TLinAddr tempAddr = MapTemp (physAddr, aDest&~iPageMask);
+		__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:CopyToShadowMemory Copy aDest=%08x aSrc=%08x aSize=%08x", tempAddr+(aDest&iPageMask), aSrc, copySize));
+		memcpy ((TAny*)(tempAddr+(aDest&iPageMask)), (const TAny*)aSrc, copySize);  //Kernel-to-Kernel copy is presumed
+		UnmapTemp();
+
+		//Update variables for the next loop/page
+		aDest+=copySize;
+		aSrc+=copySize;
+		aLength-=copySize;
+		}
+
+	Kern::MutexSignal(*RamAllocatorMutex);
+	return r;
+	}
+
+void X86Mmu::DoFreezeShadowPage(TInt aId, TLinAddr aRomAddr)
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu:DoFreezeShadowPage aId=%04x aRomAddr=%08x",
+		aId, aRomAddr));
+	TPte* ppte = PageTable(aId) + ((aRomAddr & KChunkMask)>>KPageShift);
+	TPte newpte = (*ppte & KPdePtePhysAddrMask) | KRomPtePerm;
+	__KTRACE_OPT(KMMU,Kern::Printf("Writing PTE %08x to %08x", newpte, ppte));
+	*ppte = newpte;
+	__DRAIN_WRITE_BUFFER;
+	InvalidateTLBForPage(aRomAddr);
+	}
+
+void X86Mmu::FlushShadow(TLinAddr aRomAddr)
+	{
+#ifdef __SMP__
+	TTLBIPI ipi;
+	ipi.AddAddress(aRomAddr);
+	NKern::Lock();		// stop other processors passing this point
+	ShadowSpinLock.LockOnly();
+	ipi.QueueAllOther(&TTLBIPI::WaitAndInvalidateIsr);
+	ipi.WaitEntry();	// wait for other processors to stop
+	DoInvalidateTLBForPage(aRomAddr);
+	ipi.iFlag = 1;		// release other processors so they can flush their TLBs
+	ipi.WaitCompletion();	// wait for other processors to flush their TLBs
+	ShadowSpinLock.UnlockOnly();
+	NKern::Unlock();
+#else
+	InvalidateTLBForPage(aRomAddr);		// remove all TLB references to original ROM page
+#endif
+	}
+
+void X86Mmu::Pagify(TInt aId, TLinAddr aLinAddr)
+	{
+	// Nothing to do on x86
+	}
+
+void X86Mmu::ClearRamDrive(TLinAddr aStart)
+	{
+	// clear the page directory entries corresponding to the RAM drive
+	TPde* kpd=(TPde*)KPageDirectoryBase;	// kernel page directory
+	ZeroPdes(kpd, aStart, KRamDriveEndAddress);
+	__DRAIN_WRITE_BUFFER;
+	}
+
+// Generic cache/TLB flush function.
+// Which things are flushed is determined by aMask.
+void X86Mmu::GenericFlush(TUint32 aMask)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf("GenericFlush %x",aMask));
+	if (aMask&(EFlushDPermChg|EFlushIPermChg))
+		InvalidateTLB();
+	}
+
+TPde X86Mmu::PdePermissions(TChunkType aChunkType, TBool aRO)
+	{
+	if (aChunkType==EUserData && aRO)
+		return KPdePtePresent|KPdePteUser;
+	return ChunkPdePermissions[aChunkType];
+	}
+
+TPte X86Mmu::PtePermissions(TChunkType aChunkType)
+	{
+	TPte pte=ChunkPtePermissions[aChunkType];
+	return (pte&~KPdePteGlobal)|(pte&iPteGlobal);
+	}
+
+const TUint FBLK=(EMapAttrFullyBlocking>>12);
+const TUint BFNC=(EMapAttrBufferedNC>>12);
+const TUint BUFC=(EMapAttrBufferedC>>12);
+const TUint L1UN=(EMapAttrL1Uncached>>12);
+const TUint WTRA=(EMapAttrCachedWTRA>>12);
+const TUint WTWA=(EMapAttrCachedWTWA>>12);
+const TUint WBRA=(EMapAttrCachedWBRA>>12);
+const TUint WBWA=(EMapAttrCachedWBWA>>12);
+const TUint AWTR=(EMapAttrAltCacheWTRA>>12);
+const TUint AWTW=(EMapAttrAltCacheWTWA>>12);
+const TUint AWBR=(EMapAttrAltCacheWBRA>>12);
+const TUint AWBW=(EMapAttrAltCacheWBWA>>12);
+
+const TUint16 UNS=0xffffu;	// Unsupported attribute
+const TUint16 SPE=0xfffeu;	// Special processing required
+
+static const TUint16 CacheBuffAttributes[16]=
+	{0x10,0x10,0x10,0x10,0x08,0x08,0x00,0x00, UNS, UNS, UNS, UNS, UNS, UNS, UNS,0x00};
+static const TUint8 CacheBuffActual[16]=
+	{FBLK,FBLK,FBLK,FBLK,WTRA,WTRA,WBWA,WBWA,FBLK,FBLK,FBLK,FBLK,FBLK,FBLK,FBLK,WBWA};
+
+static const TUint8 ActualReadPrivilegeLevel[4]={1,1,4,4};	// RONO,RWNO,RORO,RWRW
+static const TUint8 ActualWritePrivilegeLevel[4]={0,1,0,4};	// RONO,RWNO,RORO,RWRW
+
+TInt X86Mmu::PdePtePermissions(TUint& aMapAttr, TPde& aPde, TPte& aPte)
+	{
+	__KTRACE_OPT(KMMU,Kern::Printf(">X86Mmu::PdePtePermissions, mapattr=%08x",aMapAttr));
+	TUint read=aMapAttr & EMapAttrReadMask;
+	TUint write=(aMapAttr & EMapAttrWriteMask)>>4;
+	TUint exec=(aMapAttr & EMapAttrExecMask)>>8;
+	TUint cache=(aMapAttr & EMapAttrL1CacheMask)>>12;
+	TPte pte;
+	// ignore L2 cache attributes for now - downgrade to L2 uncached
+
+	// if execute access is greater than read, adjust read (since there are no separate execute permissions on X86)
+	if (exec>read)
+		read=exec;
+	pte=0;
+	if (write==0)
+		{
+		// read-only
+		if (read>=4)
+			pte=KPdePermRORO;			// user and supervisor read-only
+		else
+			pte=KPdePermRONO;			// supervisor r/o user no access
+		}
+	else if (write<4)
+		{
+		// only supervisor can write
+		if (read>=4)
+			pte=KPdePermRWRW;			// full access since no RWRO
+		else
+			pte=KPdePermRWNO;			// sup rw user no access
+		}
+	else
+		pte=KPdePermRWRW;				// sup rw user rw
+	read=ActualReadPrivilegeLevel[pte>>1];
+	write=ActualWritePrivilegeLevel[pte>>1];
+	TUint cbatt=CacheBuffAttributes[cache];
+	TInt r=KErrNone;
+	if (cbatt==UNS)
+		r=KErrNotSupported;
+	if (r==KErrNone)
+		{
+		cache=CacheBuffActual[cache];
+		aPde=KPdePtePresent|KPdePteWrite|KPdePteUser;
+		aPte=pte|cbatt|iPteGlobal;		// HW chunks can always be global
+		aMapAttr=read|(write<<4)|(read<<8)|(cache<<12);
+		}
+	__KTRACE_OPT(KMMU,Kern::Printf("<X86Mmu::PdePtePermissions, r=%d, mapattr=%08x, pde=%08x, pte=%08x",
+								r,aMapAttr,aPde,aPte));
+	return r;
+	}
+
+THwChunkAddressAllocator* X86Mmu::MappingRegion(TUint aMapAttr)
+	{
+	TUint read=aMapAttr & EMapAttrReadMask;
+	TUint write=(aMapAttr & EMapAttrWriteMask)>>4;
+	TUint exec=(aMapAttr & EMapAttrExecMask)>>8;
+	if (read>=4 || write>=4 || exec>=4)
+		return iUserHwChunkAllocator;	// if any access in user mode, must put it in user global section
+	return iHwChunkAllocator;
+	}
+
+void X86Mmu::Map(TLinAddr aLinAddr, TPhysAddr aPhysAddr, TInt aSize, TPde aPdePerm, TPte aPtePerm, TInt aMapShift)
+//
+// Map a region of physical addresses aPhysAddr to aPhysAddr+aSize-1 to virtual address aLinAddr.
+// Use permissions specified by aPdePerm and aPtePerm. Use mapping sizes up to and including (1<<aMapShift).
+// Assume any page tables required are already assigned.
+// aLinAddr, aPhysAddr, aSize must be page-aligned.
+//
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu::Map lin=%08x phys=%08x size=%08x", aLinAddr, aPhysAddr, aSize));
+	__KTRACE_OPT(KMMU, Kern::Printf("pde=%08x pte=%08x mapshift=%d", aPdePerm, aPtePerm, aMapShift));
+	TPde lp_pde=aPtePerm|KPdeLargePage;
+	TLinAddr la=aLinAddr;
+	TPhysAddr pa=aPhysAddr;
+	TInt remain=aSize;
+	while (remain)
+		{
+		if (aMapShift>=KChunkShift && (la & KChunkMask)==0 && remain>=KChunkSize)
+			{
+			// use large pages
+			TInt npdes=remain>>KChunkShift;
+			const TBitMapAllocator& b=*iOsAsidAllocator;
+			TInt num_os_asids=b.iSize-b.iAvail;
+			TInt os_asid=0;
+			for (; num_os_asids; ++os_asid)
+				{
+				if (b.NotAllocated(os_asid,1))
+					continue;			// os_asid is not needed
+				TPde* p_pde=PageDirectory(os_asid)+(la>>KChunkShift);
+				TPde* p_pde_E=p_pde+npdes;
+				TPde pde=pa|lp_pde;
+				NKern::LockSystem();
+				for (; p_pde < p_pde_E; pde+=KChunkSize)
+					{
+					__ASSERT_DEBUG(*p_pde==0, MM::Panic(MM::EPdeAlreadyInUse));
+					__KTRACE_OPT(KMMU,Kern::Printf("Writing PDE %08x to %08x", pde, p_pde));
+					*p_pde++=pde;
+					}
+				NKern::UnlockSystem();
+				--num_os_asids;
+				}
+			npdes<<=KChunkShift;
+			la+=npdes, pa+=npdes, remain-=npdes;
+			continue;
+			}
+		// use normal pages
+		TInt block_size = Min(remain, KChunkSize-(la&KChunkMask));
+		TInt id=PageTableId(la, 0);
+		__ASSERT_DEBUG(id>=0, MM::Panic(MM::EMmuMapNoPageTable));
+		TPte* p_pte=PageTable(id)+((la&KChunkMask)>>KPageShift);
+		TPte* p_pte_E = p_pte + (block_size>>KPageShift);
+		TPte pte=pa|aPtePerm;
+		SPageTableInfo& ptinfo=iPtInfo[id];
+		NKern::LockSystem();
+		for (; p_pte < p_pte_E; pte+=KPageSize)
+			{
+			__ASSERT_DEBUG(*p_pte==0, MM::Panic(MM::EPteAlreadyInUse));
+			__KTRACE_OPT(KMMU,Kern::Printf("Writing PTE %08x to %08x", pte, p_pte));
+			*p_pte++=pte;
+			++ptinfo.iCount;
+			NKern::FlashSystem();
+			}
+		NKern::UnlockSystem();
+		la+=block_size, pa+=block_size, remain-=block_size;
+		}
+	}
+
+void X86Mmu::Unmap(TLinAddr aLinAddr, TInt aSize)
+//
+// Remove all mappings in the specified range of addresses.
+// Don't free page tables.
+// aLinAddr, aSize must be page-aligned.
+//
+	{
+	__KTRACE_OPT(KMMU, Kern::Printf("X86Mmu::Unmap lin=%08x size=%08x", aLinAddr, aSize));
+#ifdef __SMP__
+	TTLBIPI ipi;
+#endif
+	TLinAddr a=aLinAddr;
+	TLinAddr end=a+aSize;
+	__KTRACE_OPT(KMMU,Kern::Printf("a=%08x end=%08x",a,end));
+	NKern::LockSystem();
+	while(a!=end)
+		{
+		TInt pdeIndex=a>>KChunkShift;
+		TLinAddr next=(pdeIndex<<KChunkShift)+KChunkSize;
+		TInt to_do=Min(TInt(end-a), TInt(next-a))>>KPageShift;
+		__KTRACE_OPT(KMMU,Kern::Printf("a=%08x next=%08x to_do=%d",a,next,to_do));
+		TPde pde=::InitPageDirectory[pdeIndex];
+		if ( (pde&(KPdePtePresent|KPdeLargePage))==(KPdePtePresent|KPdeLargePage) )
+			{
+			__ASSERT_DEBUG(!(a&KChunkMask), MM::Panic(MM::EUnmapBadAlignment));
+			::InitPageDirectory[pdeIndex]=0;
+#ifdef __SMP__
+			ipi.AddAddress(a);
+#else
+			InvalidateTLBForPage(a);	// flush any corresponding TLB entry
+#endif
+			a=next;
+			NKern::FlashSystem();
+			continue;
+			}
+		TInt ptid=PageTableId(a,0);
+		SPageTableInfo& ptinfo=iPtInfo[ptid];
+		if (ptid>=0)
+			{
+			TPte* ppte=PageTable(ptid)+((a&KChunkMask)>>KPageShift);
+			TPte* ppte_End=ppte+to_do;
+			for (; ppte<ppte_End; ++ppte, a+=KPageSize)
+				{
+				if (*ppte & KPdePtePresent)
+					--ptinfo.iCount;
+				*ppte=0;
+#ifdef __SMP__
+				ipi.AddAddress(a);
+#else
+				InvalidateTLBForPage(a);	// flush any corresponding TLB entry
+#endif
+				NKern::FlashSystem();
+				}
+			}
+		else
+			a += (to_do<<KPageShift);
+		}
+#ifdef __SMP__
+	ipi.InvalidateList();
+#endif
+	NKern::UnlockSystem();
+	}
+
+
+void X86Mmu::ClearPages(TInt aNumPages, TPhysAddr* aPageList, TUint8 aClearByte)
+	{
+	//map the pages at a temporary address, clear them and unmap
+	__ASSERT_MUTEX(RamAllocatorMutex);
+	while (--aNumPages >= 0)
+		{
+		TPhysAddr pa;
+		if((TInt)aPageList&1)
+			{
+			pa = (TPhysAddr)aPageList&~1;
+			*(TPhysAddr*)&aPageList += iPageSize;
+			}
+		else
+			pa = *aPageList++;
+		*iTempPte = pa | KPdePtePresent | KPdePteWrite | iPteGlobal;
+		__DRAIN_WRITE_BUFFER;
+		InvalidateTLBForPage(iTempAddr);
+		memset((TAny*)iTempAddr, aClearByte, iPageSize);
+		}
+	*iTempPte=0;
+	__DRAIN_WRITE_BUFFER;
+	InvalidateTLBForPage(iTempAddr);
+	}
+
+TLinAddr X86Mmu::MapTemp(TPhysAddr aPage,TLinAddr /*aLinAddr*/,TInt aPages)
+	{
+	__ASSERT_MUTEX(RamAllocatorMutex);
+	__ASSERT_DEBUG(!*iTempPte,MM::Panic(MM::ETempMappingAlreadyInUse));
+	__ASSERT_DEBUG(aPages<=4,MM::Panic(MM::ETempMappingNoRoom));
+	iTempMapCount = aPages;
+	for (TInt i=0; i<aPages; i++)
+		{
+		iTempPte[i] = ((aPage&~KPageMask)+(i<<KPageShift)) | KPdePtePresent | KPdePteWrite | iPteGlobal
+		__DRAIN_WRITE_BUFFER;
+		InvalidateTLBForPage(iTempAddr+(i<<KPageShift));
+		}
+	return iTempAddr;
+	}
+
+TLinAddr X86Mmu::MapTemp(TPhysAddr aPage,TLinAddr aLinAddr,TInt aPages, TMemoryType)
+	{
+	return MapTemp(aPage, aLinAddr, aPages);
+	}
+
+TLinAddr X86Mmu::MapSecondTemp(TPhysAddr aPage,TLinAddr /*aLinAddr*/,TInt aPages)
+	{
+	__ASSERT_MUTEX(RamAllocatorMutex);
+	__ASSERT_DEBUG(!*iSecondTempPte,MM::Panic(MM::ETempMappingAlreadyInUse));
+	__ASSERT_DEBUG(aPages<=4,MM::Panic(MM::ETempMappingNoRoom));
+	iSecondTempMapCount = aPages;
+	for (TInt i=0; i<aPages; i++)
+		{
+		iSecondTempPte[i] = ((aPage&~KPageMask)+(i<<KPageShift)) | KPdePtePresent | KPdePteWrite | iPteGlobal
+		__DRAIN_WRITE_BUFFER;
+		InvalidateTLBForPage(iSecondTempAddr+(i<<KPageShift));
+		}
+	return iSecondTempAddr;
+	}
+
+void X86Mmu::UnmapTemp()
+	{
+	__ASSERT_MUTEX(RamAllocatorMutex);
+	for (TInt i=0; i<iTempMapCount; i++)
+		{
+		iTempPte[i] = 0;
+		__DRAIN_WRITE_BUFFER;
+		InvalidateTLBForPage(iTempAddr+(i<<KPageShift));
+		}
+	}
+
+void X86Mmu::UnmapSecondTemp()
+	{
+	__ASSERT_MUTEX(RamAllocatorMutex);
+	for (TInt i=0; i<iSecondTempMapCount; i++)
+		{
+		iSecondTempPte[i] = 0;
+		__DRAIN_WRITE_BUFFER;
+		InvalidateTLBForPage(iSecondTempAddr+(i<<KPageShift));
+		}
+	}
+
+void ExecHandler::UnlockRamDrive()
+	{
+	}
+
+EXPORT_C void TInternalRamDrive::Unlock()
+	{
+	}
+
+EXPORT_C void TInternalRamDrive::Lock()
+	{
+	}
+
+TBool X86Mmu::ValidateLocalIpcAddress(TLinAddr aAddr,TInt aSize,TBool aWrite)
+	{
+	__NK_ASSERT_DEBUG(aSize<=KChunkSize);
+	TLinAddr end = aAddr+aSize-1;
+	if(end<aAddr)
+		end = ~0u;
+
+	if(TUint(aAddr^KIPCAlias)<TUint(KChunkSize) || TUint(end^KIPCAlias)<TUint(KChunkSize))
+		{
+		// local address is in alias region.
+		// remove alias...
+		NKern::LockSystem();
+		((DMemModelThread*)TheCurrentThread)->RemoveAlias();
+		NKern::UnlockSystem();
+		// access memory, which will cause an exception...
+		if(!(TUint(aAddr^KIPCAlias)<TUint(KChunkSize)))
+			aAddr = end;
+		DoInvalidateTLBForPage(aAddr);	// only need to do this processor since alias range is owned by the thread
+		if(aWrite)
+			*(volatile TUint8*)aAddr = 0;
+		else
+			aWrite = *(volatile TUint8*)aAddr;
+		// can't get here
+		__NK_ASSERT_DEBUG(0);
+		}
+
+	TUint32 local_mask;
+	DMemModelProcess* process=(DMemModelProcess*)TheCurrentThread->iOwningProcess;
+	if(aWrite)
+		local_mask = process->iAddressCheckMaskW;
+	else
+		local_mask = process->iAddressCheckMaskR;
+	TInt mask = 2<<(end>>27);
+	mask -= 1<<(aAddr>>27);
+	if((local_mask&mask)!=mask)
+		return EFalse;
+
+	return ETrue;
+	}
+
+TInt DMemModelThread::Alias(TLinAddr aAddr, DMemModelProcess* aProcess, TInt aSize, TInt aPerm, TLinAddr& aAliasAddr, TInt& aAliasSize)
+//
+// Set up an alias mapping starting at address aAddr in specified process.
+// Check permissions aPerm.
+// Enter and return with system locked.
+// Note: Alias is removed if an exception if trapped by DThread::IpcExcHandler.
+//
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("Thread %O Alias %08x+%x Process %O perm %x",this,aAddr,aSize,aProcess,aPerm));
+	__ASSERT_SYSTEM_LOCK;
+
+	if(TUint(aAddr^KIPCAlias)<TUint(KIPCAliasAreaSize))
+		return KErrBadDescriptor; // prevent access to alias region
+
+	// check if memory is in region which is safe to access with supervisor permissions...
+	TBool okForSupervisorAccess = aPerm&(EMapAttrReadSup|EMapAttrWriteSup) ? 1 : 0;
+	if(!okForSupervisorAccess)
+		{
+		if(aAddr>=0xc0000000) // address in kernel area (top 1GB)?
+			return KErrBadDescriptor; // don't have permission
+		TUint32 local_mask;
+		if(aPerm&EMapAttrWriteUser)
+			local_mask = aProcess->iAddressCheckMaskW;
+		else
+			local_mask = aProcess->iAddressCheckMaskR;
+		okForSupervisorAccess = (local_mask>>(aAddr>>27))&1;
+		}
+
+	if(aAddr>=KUserSharedDataEnd) // if address is in global section, don't bother aliasing it...
+		{
+		if(iAliasLinAddr)
+			RemoveAlias();
+		aAliasAddr = aAddr;
+		TInt maxSize = KChunkSize-(aAddr&KChunkMask);
+		aAliasSize = aSize<maxSize ? aSize : maxSize;
+		return okForSupervisorAccess;
+		}
+
+	TInt asid = aProcess->iOsAsid;
+	TPde* pd = PageDirectory(asid);
+	TPde pde = pd[aAddr>>KChunkShift];
+#ifdef __SMP__
+	TLinAddr aliasAddr;
+#else
+	TLinAddr aliasAddr = KIPCAlias+(aAddr&(KChunkMask & ~KPageMask));
+#endif
+	if(pde==iAliasPde && iAliasLinAddr)
+		{
+		// pde already aliased, so just update linear address...
+#ifdef __SMP__
+		__NK_ASSERT_DEBUG(iCpuRestoreCookie>=0);
+		aliasAddr = iAliasLinAddr & ~KChunkMask;
+		aliasAddr |= (aAddr & (KChunkMask & ~KPageMask));
+#endif
+		iAliasLinAddr = aliasAddr;
+		}
+	else
+		{
+		// alias PDE changed...
+		if(!iAliasLinAddr)
+			{
+			::TheMmu.iAliasList.Add(&iAliasLink); // add to list if not already aliased
+#ifdef __SMP__
+			__NK_ASSERT_DEBUG(iCpuRestoreCookie==-1);
+			iCpuRestoreCookie = NKern::FreezeCpu();	// temporarily lock current thread to this processor
+#endif
+			}
+		iAliasPde = pde;
+		iAliasOsAsid = asid;
+#ifdef __SMP__
+		TSubScheduler& ss = SubScheduler();	// OK since we are locked to this CPU
+		aliasAddr = TLinAddr(ss.i_AliasLinAddr) + (aAddr & (KChunkMask & ~KPageMask));
+		iAliasPdePtr = (TPde*)(TLinAddr(ss.i_AliasPdePtr) + (((DMemModelProcess*)iOwningProcess)->iOsAsid << KPageTableShift));
+#endif
+		iAliasLinAddr = aliasAddr;
+		}
+	__KTRACE_OPT(KMMU,Kern::Printf("Writing PDE %08x to %08x", pde, iAliasPdePtr));
+		*iAliasPdePtr = pde;
+		__DRAIN_WRITE_BUFFER;
+	DoInvalidateTLBForPage(aliasAddr);	// only need to do this processor
+	TInt offset = aAddr&KPageMask;
+	aAliasAddr = aliasAddr | offset;
+	TInt maxSize = KPageSize - offset;
+	aAliasSize = aSize<maxSize ? aSize : maxSize;
+	return okForSupervisorAccess;
+	}
+
+void DMemModelThread::RemoveAlias()
+//
+// Remove alias mapping (if present)
+// Enter and return with system locked.
+//
+	{
+	__KTRACE_OPT(KMMU2,Kern::Printf("Thread %O RemoveAlias", this));
+	__ASSERT_SYSTEM_LOCK;
+	TLinAddr addr = iAliasLinAddr;
+	if(addr)
+		{
+		iAliasLinAddr = 0;
+		iAliasPde = 0;
+		__KTRACE_OPT(KMMU,Kern::Printf("Clearing PDE at %08x", iAliasPdePtr));
+		*iAliasPdePtr = 0;
+		__DRAIN_WRITE_BUFFER;
+		DoInvalidateTLBForPage(addr);	// only need to do it for this processor
+		iAliasLink.Deque();
+#ifdef __SMP__
+		__NK_ASSERT_DEBUG(iCpuRestoreCookie>=0);
+		NKern::EndFreezeCpu(iCpuRestoreCookie);
+		iCpuRestoreCookie = -1;
+#endif
+		}
+	}
+
+void X86Mmu::CacheMaintenanceOnDecommit(TPhysAddr)
+	{
+	// no cache operations required on freeing memory
+	}
+
+void X86Mmu::CacheMaintenanceOnDecommit(const TPhysAddr*, TInt)
+	{
+	// no cache operations required on freeing memory
+	}
+
+void X86Mmu::CacheMaintenanceOnPreserve(TPhysAddr, TUint)
+	{
+	// no cache operations required on freeing memory
+	}
+
+void X86Mmu::CacheMaintenanceOnPreserve(const TPhysAddr*, TInt, TUint)
+	{
+	// no cache operations required on freeing memory
+	}
+
+void X86Mmu::CacheMaintenanceOnPreserve(TPhysAddr , TInt , TLinAddr , TUint )
+	{
+	// no cache operations required on freeing memory
+	}
+
+
+TInt X86Mmu::UnlockRamCachePages(TLinAddr aLinAddr, TInt aNumPages, DProcess* aProcess)
+	{
+	TInt asid = ((DMemModelProcess*)aProcess)->iOsAsid;
+	TInt page = aLinAddr>>KPageShift;
+	NKern::LockSystem();
+	for(;;)
+		{
+		TPde* pd = PageDirectory(asid)+(page>>(KChunkShift-KPageShift));
+		TPte* pt = SafePageTableFromPde(*pd++);
+		__NK_ASSERT_DEBUG(pt);
+		TInt pteIndex = page&(KChunkMask>>KPageShift);
+		pt += pteIndex;
+		do
+			{
+			TInt pagesInPt = (KChunkSize>>KPageShift)-pteIndex;
+			if(pagesInPt>aNumPages)
+				pagesInPt = aNumPages;
+			if(pagesInPt>KMaxPages)
+				pagesInPt = KMaxPages;
+
+			aNumPages -= pagesInPt;
+			page += pagesInPt;
+
+			do
+				{
+				TPte pte = *pt++;
+				if(pte) // pte may be null if page has already been unlocked and reclaimed by system
+					iRamCache->DonateRamCachePage(SPageInfo::FromPhysAddr(pte));
+				}
+			while(--pagesInPt);
+
+			if(!aNumPages)
+				{
+				NKern::UnlockSystem();
+				return KErrNone;
+				}
+
+			pteIndex = page&(KChunkMask>>KPageShift);
+			}
+		while(!NKern::FlashSystem() && pteIndex);
+		}
+	}
+
+
+TInt X86Mmu::LockRamCachePages(TLinAddr aLinAddr, TInt aNumPages, DProcess* aProcess)
+	{
+	TInt asid = ((DMemModelProcess*)aProcess)->iOsAsid;
+	TInt page = aLinAddr>>KPageShift;
+	NKern::LockSystem();
+	for(;;)
+		{
+		TPde* pd = PageDirectory(asid)+(page>>(KChunkShift-KPageShift));
+		TPte* pt = SafePageTableFromPde(*pd++);
+		__NK_ASSERT_DEBUG(pt);
+		TInt pteIndex = page&(KChunkMask>>KPageShift);
+		pt += pteIndex;
+		do
+			{
+			TInt pagesInPt = (KChunkSize>>KPageShift)-pteIndex;
+			if(pagesInPt>aNumPages)
+				pagesInPt = aNumPages;
+			if(pagesInPt>KMaxPages)
+				pagesInPt = KMaxPages;
+
+			aNumPages -= pagesInPt;
+			page += pagesInPt;
+
+			do
+				{
+				TPte pte = *pt++;
+				if(pte==0)
+					goto not_found;
+				if(!iRamCache->ReclaimRamCachePage(SPageInfo::FromPhysAddr(pte)))
+					goto not_found;
+				}
+			while(--pagesInPt);
+
+			if(!aNumPages)
+				{
+				NKern::UnlockSystem();
+				return KErrNone;
+				}
+
+			pteIndex = page&(KChunkMask>>KPageShift);
+			}
+		while(!NKern::FlashSystem() && pteIndex);
+		}
+not_found:
+	NKern::UnlockSystem();
+	return KErrNotFound;
+	}
+
+
+void RamCache::SetFree(SPageInfo* aPageInfo)
+	{
+	// Make a page free
+	TInt type = aPageInfo->Type();
+	if(type==SPageInfo::EPagedCache)
+		{
+		TInt offset = aPageInfo->Offset()<<KPageShift;
+		DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
+		__NK_ASSERT_DEBUG(TUint(offset)<TUint(chunk->iSize));
+		TLinAddr lin = ((TLinAddr)chunk->iBase)+offset;
+		TInt asid = ((DMemModelProcess*)chunk->iOwningProcess)->iOsAsid;
+		TPte* pt = PtePtrFromLinAddr(lin,asid);
+		*pt = 0;
+		InvalidateTLBForPage(lin);
+
+		// actually decommit it from chunk...
+		TInt ptid = ((TLinAddr)pt-KPageTableBase)>>KPageTableShift;
+		SPageTableInfo& ptinfo=((X86Mmu*)iMmu)->iPtInfo[ptid];
+		if(!--ptinfo.iCount)
+			{
+			chunk->iPageTables[offset>>KChunkShift] = 0xffff;
+			NKern::UnlockSystem();
+			((X86Mmu*)iMmu)->DoUnassignPageTable(lin, (TAny*)asid);
+			((X86Mmu*)iMmu)->FreePageTable(ptid);
+			NKern::LockSystem();
+			}
+		}
+	else
+		{
+		__KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("DP: SetFree() with bad page type = %d",aPageInfo->Type()));
+		Panic(EUnexpectedPageType);
+		}
+	}
+
+// Not supported on x86 - no defrag yet
+void X86Mmu::DisablePageModification(DMemModelChunk* aChunk, TInt aOffset)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	}
+
+TInt X86Mmu::RamDefragFault(TAny* aExceptionInfo)
+	{
+	MM::Panic(MM::EOperationNotSupported);
+	return KErrAbort;
+	}