// 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:
//

#include <x86_mem.h>
#include "cache_maintenance.inl"
#include "execs.h"
#include "mm.h"
#include "mmu.h"
#include "mpager.h"
#include "mpdalloc.h"


TPte PteGlobal;	// =0x100 on processors which support global pages, 0 on processors which don't

#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 __DebugMsgINVLPG(int a)
	{
	__KTRACE_OPT(KMMU,Kern::Printf("INVLPG(%08x)",a));
	}
#endif



extern void DoLocalInvalidateTLB();


#ifndef __SMP__


FORCE_INLINE void LocalInvalidateTLB()
	{
	DoLocalInvalidateTLB();
	}


#else // __SMP__


const TInt KMaxPages = 1;

class TTLBIPI : public TGenericIPI
	{
public:
	TTLBIPI();
	static void InvalidateForPagesIsr(TGenericIPI*);
	static void LocalInvalidateIsr(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*)
	{
	TRACE2(("TLBLocInv"));
	DoLocalInvalidateTLB();
	}

void TTLBIPI::InvalidateIsr(TGenericIPI*)
	{
	TRACE2(("TLBInv"));
	DoInvalidateTLB();
	}

void TTLBIPI::WaitAndInvalidateIsr(TGenericIPI* aTLBIPI)
	{
	TRACE2(("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)
		{
		TRACE2(("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 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();
	}


#endif // __SMP__


void InvalidateTLBForAsid(TUint aAsid)
	{
	if(aAsid==KKernelOsAsid)
		InvalidateTLB();
	else
		LocalInvalidateTLB();
	}


void SinglePdeUpdated(TPde* aPde)
	{
	CacheMaintenance::SinglePdeUpdated((TLinAddr)aPde);
	PageDirectories.GlobalPdeChanged(aPde);
	}


//
// Functions for class Mmu
//

TPhysAddr Mmu::PtePhysAddr(TPte aPte, TUint /*aPteIndex*/)
	{
	if(aPte&KPdePtePresent)
		return aPte & KPdePtePhysAddrMask;
	return KPhysAddrInvalid;
	}


TPte* Mmu::PageTableFromPde(TPde aPde)
	{
	if((aPde&(KPdeLargePage|KPdePtePresent)) == KPdePtePresent)
		{
		SPageInfo* pi = SPageInfo::FromPhysAddr(aPde);
		TInt id = (pi->Index()<<KPtClusterShift) | ((aPde>>KPageTableShift)&KPtClusterMask);
		return (TPte*)(KPageTableBase+(id<<KPageTableShift));
		}
	return 0;
	}


TPte* Mmu::SafePageTableFromPde(TPde aPde)
	{
	if((aPde&(KPdeLargePage|KPdePtePresent)) == KPdePtePresent)
		{
		SPageInfo* pi = SPageInfo::SafeFromPhysAddr(aPde&~KPageMask);
		if(pi)
			{
			TInt id = (pi->Index()<<KPtClusterShift) | ((aPde>>KPageTableShift)&KPtClusterMask);
			return (TPte*)(KPageTableBase+(id<<KPageTableShift));
			}
		}
	return 0;
	}


/**
Return the base phsical address of the section table referenced by the given
Page Directory Entry (PDE) \a aPde. If the PDE doesn't refer to a
section then KPhysAddrInvalid is returned.

@pre #MmuLock held.
*/
TPhysAddr Mmu::SectionBaseFromPde(TPde aPde)
	{
	if(PdeMapsSection(aPde))
	   return aPde&KPdeLargePagePhysAddrMask;
	return KPhysAddrInvalid;
	}


TPte* Mmu::PtePtrFromLinAddr(TLinAddr aAddress, TInt aOsAsid)
	{
	TPde pde = PageDirectory(aOsAsid)[aAddress>>KChunkShift];
	SPageInfo* pi = SPageInfo::FromPhysAddr(pde);
	TPte* pt = (TPte*)(KPageTableBase+(pi->Index()<<KPageShift)+(pde&(KPageMask&~KPageTableMask)));
	pt += (aAddress>>KPageShift)&(KChunkMask>>KPageShift);
	return pt;
	}


TPte* Mmu::SafePtePtrFromLinAddr(TLinAddr aAddress, TInt aOsAsid)
	{
	TPde pde = PageDirectory(aOsAsid)[aAddress>>KChunkShift];
	TPte* pt = SafePageTableFromPde(pde);
	if(pt)
		pt += (aAddress>>KPageShift)&(KChunkMask>>KPageShift);
	return pt;
	}


TPhysAddr Mmu::PageTablePhysAddr(TPte* aPt)
	{
	__NK_ASSERT_DEBUG(MmuLock::IsHeld() || PageTablesLockIsHeld());

	TInt pdeIndex = ((TLinAddr)aPt)>>KChunkShift;
	TPde pde = PageDirectory(KKernelOsAsid)[pdeIndex];
	__NK_ASSERT_DEBUG((pde&(KPdePtePresent|KPdeLargePage))==KPdePtePresent);

	SPageInfo* pi = SPageInfo::FromPhysAddr(pde);
	TPte* pPte = (TPte*)(KPageTableBase+(pi->Index(true)<<KPageShift));
	TPte pte = pPte[(((TLinAddr)aPt)&KChunkMask)>>KPageShift];
	__NK_ASSERT_DEBUG(pte & KPdePtePresent);

	return pte&KPdePtePhysAddrMask;
	}


TPhysAddr Mmu::UncheckedLinearToPhysical(TLinAddr aLinAddr, TInt aOsAsid)
	{
	TRACE2(("Mmu::UncheckedLinearToPhysical(%08x,%d)",aLinAddr,aOsAsid));
	TInt pdeIndex = aLinAddr>>KChunkShift;
	TPde pde = PageDirectory(aOsAsid)[pdeIndex];
	TPhysAddr pa=KPhysAddrInvalid;
	if (pde & KPdePtePresent)
		{
		if(pde&KPdeLargePage)
			{
			pa=(pde&KPdeLargePagePhysAddrMask)+(aLinAddr&~KPdeLargePagePhysAddrMask);
			__KTRACE_OPT(KMMU,Kern::Printf("Mapped with large table - returning %08x",pa));
			}
		else
			{
			SPageInfo* pi = SPageInfo::FromPhysAddr(pde);
			TInt id = (pi->Index(true)<<KPtClusterShift) | ((pde>>KPageTableShift)&KPtClusterMask);
			TPte* pPte = (TPte*)(KPageTableBase+(id<<KPageTableShift));
			TPte pte = pPte[(aLinAddr&KChunkMask)>>KPageShift];
			if (pte & KPdePtePresent)
				{
				pa=(pte&KPdePtePhysAddrMask)+(aLinAddr&KPageMask);
				__KTRACE_OPT(KMMU,Kern::Printf("Mapped with page table - returning %08x",pa));
				}
			}
		}
	return pa;
	}


void Mmu::Init1()
	{
	TRACEB(("Mmu::Init1"));

	TUint pge = TheSuperPage().iCpuId & EX86Feat_PGE;
	PteGlobal = pge ? KPdePteGlobal : 0;
	X86_UseGlobalPTEs = pge!=0;

#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

	Init1Common();
	}

void Mmu::Init2()
	{
	TRACEB(("Mmu::Init2"));

	Init2Common();
	}

void Mmu::Init2Final()
	{
	TRACEB(("Mmu::Init2Final"));

	Init2FinalCommon();
	}


const TPde KPdeForBlankPageTable = KPdePtePresent|KPdePteWrite|KPdePteUser;

TPde Mmu::BlankPde(TMemoryAttributes aAttributes)
	{
	(void)aAttributes;
	TPde pde = KPdeForBlankPageTable;
	TRACE2(("Mmu::BlankPde(%x) returns 0x%x",aAttributes,pde));
	return pde;
	}


TPde Mmu::BlankSectionPde(TMemoryAttributes aAttributes, TUint aPteType)
	{
	return PageToSectionEntry(BlankPte(aAttributes, aPteType), KPdeForBlankPageTable);
	}


TPte Mmu::BlankPte(TMemoryAttributes aAttributes, TUint aPteType)
	{
	TPte pte = KPdePtePresent;
	if(aPteType&EPteTypeUserAccess)
		pte |= KPdePteUser;
	if(aPteType&EPteTypeWritable)
		pte |= KPdePteWrite;
	if(aPteType&EPteTypeGlobal)
		pte |= PteGlobal;

	switch((TMemoryType)(aAttributes&EMemoryAttributeTypeMask))
		{
	case EMemAttStronglyOrdered:
	case EMemAttDevice:
	case EMemAttNormalUncached:
		pte |= KPdePteUncached;
		break;
	case EMemAttNormalCached:
		break;
	default:
		__NK_ASSERT_ALWAYS(0);
		break;
		}

	TRACE2(("Mmu::BlankPte(%x,%x) returns 0x%x",aAttributes,aPteType,pte));
	return pte;
	}


TPte Mmu::SectionToPageEntry(TPde& aPde)
	{
	TPte pte = aPde&~(KPdePtePhysAddrMask|KPdeLargePage);
	aPde = KPdeForBlankPageTable;
	return pte;
	}


TPde Mmu::PageToSectionEntry(TPte aPte, TPde /*aPde*/)
	{
	TPte pde = aPte&~KPdeLargePagePhysAddrMask;
	pde |= KPdeLargePage;
	return pde;
	}


TMemoryAttributes Mmu::CanonicalMemoryAttributes(TMemoryAttributes aAttr)
	{
	TUint attr = aAttr;
	if(attr&EMemoryAttributeDefaultShareable)
		{
		// sharing not specified, use default...
#if defined	(__CPU_USE_SHARED_MEMORY)
		attr |= EMemoryAttributeShareable;
#else
		attr &= ~EMemoryAttributeShareable;
#endif
		}

	// remove invalid attributes...
	attr &= ~(EMemoryAttributeUseECC);

	return (TMemoryAttributes)(attr&EMemoryAttributeMask);
	}


void Mmu::PagesAllocated(TPhysAddr* aPageList, TUint aCount, TRamAllocFlags aFlags, TBool aReallocate)
	{
	TRACE2(("Mmu::PagesAllocated(0x%08x,%d,0x%x,%d)",aPageList, aCount, aFlags, (bool)aReallocate));
	__NK_ASSERT_DEBUG(RamAllocLock::IsHeld());

	TBool wipe = !(aFlags&EAllocNoWipe); // do we need to wipe page contents?
	TMemoryType newType = (TMemoryType)(aFlags&KMemoryTypeMask); // memory type that pages will be used for
	TUint8 wipeByte = (aFlags&EAllocUseCustomWipeByte) ? (aFlags>>EAllocWipeByteShift)&0xff : 0x03; // value to wipe memory with

	// process each page in turn...
	while(aCount--)
		{
		// get physical address of next page...
		TPhysAddr pagePhys;
		if((TPhysAddr)aPageList&1)
			{
			// aPageList is actually the physical address to use...
			pagePhys = (TPhysAddr)aPageList&~1;
			*(TPhysAddr*)&aPageList += KPageSize;
			}
		else
			pagePhys = *aPageList++;
		__NK_ASSERT_DEBUG((pagePhys&KPageMask)==0);

		// get info about page...
		SPageInfo* pi = SPageInfo::FromPhysAddr(pagePhys);
		TMemoryType oldType = (TMemoryType)(pi->Flags(true)&KMemoryTypeMask);

		TRACE2(("Mmu::PagesAllocated page=0x%08x, oldType=%d, wipe=%d",pagePhys,oldType,wipe));
		if(wipe)
			{
			// work out temporary mapping values...
			TLinAddr tempLinAddr = iTempMap[0].iLinAddr;
			TPte* tempPte = iTempMap[0].iPtePtr;

			// temporarily map page...
			*tempPte = pagePhys | iTempPteCached;
			CacheMaintenance::SinglePteUpdated((TLinAddr)tempPte);
			InvalidateTLBForPage(tempLinAddr|KKernelOsAsid);

			// wipe contents of memory...
			memset((TAny*)tempLinAddr, wipeByte, KPageSize);
			__e32_io_completion_barrier();

			// invalidate temporary mapping...
			*tempPte = KPteUnallocatedEntry;
			CacheMaintenance::SinglePteUpdated((TLinAddr)tempPte);
			InvalidateTLBForPage(tempLinAddr|KKernelOsAsid);
			}

		// indicate page has been allocated...
		if(aReallocate==false)
			pi->SetAllocated();
		}
	}


void Mmu::PageFreed(SPageInfo* aPageInfo)
	{
	__NK_ASSERT_DEBUG(MmuLock::IsHeld());

	if(aPageInfo->Type()==SPageInfo::EUnused)
		return;

	aPageInfo->SetUnused();

	TRACE2(("Mmu::PageFreed page=0x%08x type=%d colour=%d",aPageInfo->PhysAddr(),aPageInfo->Flags()&KMemoryTypeMask,aPageInfo->Index()&KPageColourMask));
	}


void Mmu::CleanAndInvalidatePages(TPhysAddr* aPages, TUint aCount, TMemoryAttributes aAttributes, TUint aColour)
	{
	TMemoryType type = (TMemoryType)(aAttributes&EMemoryAttributeTypeMask);
	if(!CacheMaintenance::IsCached(type))
		{
		TRACE2(("Mmu::CleanAndInvalidatePages - nothing to do"));
		return;
		}

	RamAllocLock::Lock();

	while(aCount--)
		{
		TPhysAddr pagePhys = *aPages++;
		TRACE2(("Mmu::CleanAndInvalidatePages 0x%08x",pagePhys));

		// work out temporary mapping values...
		TLinAddr tempLinAddr = iTempMap[0].iLinAddr;
		TPte* tempPte = iTempMap[0].iPtePtr;

		// temporarily map page...
		*tempPte = pagePhys | iTempPteCached;
		CacheMaintenance::SinglePteUpdated((TLinAddr)tempPte);
		InvalidateTLBForPage(tempLinAddr|KKernelOsAsid);

		// sort out cache for memory reuse...
		CacheMaintenance::PageToPreserveAndReuse(tempLinAddr, type, KPageSize);

		// invalidate temporary mapping...
		*tempPte = KPteUnallocatedEntry;
		CacheMaintenance::SinglePteUpdated((TLinAddr)tempPte);
		InvalidateTLBForPage(tempLinAddr|KKernelOsAsid);

		RamAllocLock::Flash();
		}
	RamAllocLock::Unlock();
	}


TInt DMemModelThread::Alias(TLinAddr aAddr, DMemModelProcess* aProcess, TInt aSize, TLinAddr& aAliasAddr, TUint& aAliasSize)
//
// Set up an alias mapping starting at address aAddr in specified process.
// Note: Alias is removed if an exception is trapped by DThread::IpcExcHandler.
//
	{
	TRACE2(("Thread %O Alias %08x+%x Process %O",this,aAddr,aSize,aProcess));
	__NK_ASSERT_DEBUG(this==TheCurrentThread); // many bad things can happen if false
	// If there is an existing alias it should be on the same process otherwise
	// the os asid reference may be leaked.
	__NK_ASSERT_DEBUG(!iAliasLinAddr || aProcess == iAliasProcess);

	if(TUint(aAddr^KIPCAlias)<TUint(KIPCAliasAreaSize))
		return KErrBadDescriptor; // prevent access to alias region

	// Grab the mmu lock before opening a reference on os asid so that this thread 
	// is in an implicit critical section and therefore can't leak the reference by
	// dying before iAliasLinAddr is set.
	MmuLock::Lock();

	TInt osAsid;
	if (!iAliasLinAddr)
		{// There isn't any existing alias.
		// Open a reference on the aProcess's os asid so that it is not freed and/or reused
		// while we are aliasing an address belonging to it.
		osAsid = aProcess->TryOpenOsAsid();
		if (osAsid < 0)
			{// Couldn't open os asid so aProcess is no longer running.
			MmuLock::Unlock();
			return KErrBadDescriptor;
			}
		}
	else
		{
		// Just read the os asid of the process being aliased we already have a reference on it.
		osAsid = aProcess->OsAsid();
		}

	// Now we have the os asid check access to kernel memory.
	if(aAddr >= KUserMemoryLimit && osAsid != (TUint)KKernelOsAsid)
		{
		if (!iAliasLinAddr)
			{// Close the new reference as RemoveAlias won't do as iAliasLinAddr is not set.
			aProcess->AsyncCloseOsAsid();
			}
		MmuLock::Unlock();
		return KErrBadDescriptor; // prevent access to supervisor only memory
		}

	// Now we know all accesses to global memory are safe so check if aAddr is global.
	if(aAddr >= KGlobalMemoryBase)
		{
		// address is in global section, don't bother aliasing it...
		if (!iAliasLinAddr)
			{// Close the new reference as not required.
			aProcess->AsyncCloseOsAsid();
			}
		else
			{// Remove the existing alias as it is not required.
			DoRemoveAlias(iAliasLinAddr);
			}
		MmuLock::Unlock();
		aAliasAddr = aAddr;
		TInt maxSize = KChunkSize-(aAddr&KChunkMask);
		aAliasSize = aSize<maxSize ? aSize : maxSize;
		TRACE2(("DMemModelThread::Alias() abandoned as memory is globally mapped"));
		return KErrNone;
		}

	TPde* pd = Mmu::PageDirectory(osAsid);
	TInt pdeIndex = aAddr>>KChunkShift;
	TPde pde = pd[pdeIndex];
#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;
		iAliasProcess = aProcess;
#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) + (osAsid << KPageTableShift));
#endif
		iAliasLinAddr = aliasAddr;
		*iAliasPdePtr = pde;
		}
	TRACE2(("DMemModelThread::Alias() PDEntry=%x, iAliasLinAddr=%x",pde, aliasAddr));
	LocalInvalidateTLBForPage(aliasAddr);
	TInt offset = aAddr&KPageMask;
	aAliasAddr = aliasAddr | offset;
	TInt maxSize = KPageSize - offset;
	aAliasSize = aSize<maxSize ? aSize : maxSize;
	iAliasTarget = aAddr & ~KPageMask;

	MmuLock::Unlock();

	return KErrNone;
	}


void DMemModelThread::RemoveAlias()
//
// Remove alias mapping (if present)
//
	{
	TRACE2(("Thread %O RemoveAlias", this));
	__NK_ASSERT_DEBUG(this==TheCurrentThread); // many bad things can happen if false

	TLinAddr addr = iAliasLinAddr;
	if(addr)
		{
		MmuLock::Lock();

		DoRemoveAlias(addr);

		MmuLock::Unlock();
		}
	}


/**
Remove the alias mapping.

@pre Mmulock held
*/
void DMemModelThread::DoRemoveAlias(TLinAddr aAddr)
	{
	iAliasLinAddr = 0;
	iAliasPde = KPdeUnallocatedEntry;
	*iAliasPdePtr = KPdeUnallocatedEntry;
	SinglePdeUpdated(iAliasPdePtr);
	__NK_ASSERT_DEBUG((aAddr&KPageMask)==0);
	LocalInvalidateTLBForPage(aAddr);
	iAliasLink.Deque();
#ifdef __SMP__
	__NK_ASSERT_DEBUG(iCpuRestoreCookie>=0);
	NKern::EndFreezeCpu(iCpuRestoreCookie);
	iCpuRestoreCookie = -1;
#endif
	// Must close the os asid while the mmu lock is held to prevent it being 
	// leaked, however this requires that it is closed asynchronously as can't
	// delete os asid with mmu lock held.
	iAliasProcess->AsyncCloseOsAsid();
	}


TInt M::DemandPagingFault(TAny* aExceptionInfo)
	{
	TX86ExcInfo& exc=*(TX86ExcInfo*)aExceptionInfo;
	if(exc.iExcId!=EX86VectorPageFault)
		return KErrAbort; // not a page fault

	/*
	Meanings of exc.iExcErrorCode when exception type is EX86VectorPageFault...

	Bit 0	0 The fault was caused by a non-present page.
			1 The fault was caused by a page-level protection violation.
	Bit 1	0 The access causing the fault was a read.
			1 The access causing the fault was a write.
	Bit 2	0 The access causing the fault originated when the processor was executing in supervisor mode.
			1 The access causing the fault originated when the processor was executing in user mode.   
	Bit 3	0 The fault was not caused by reserved bit violation.
			1 The fault was caused by reserved bits set to 1 in a page directory.
	Bit 4	0 The fault was not caused by an instruction fetch.
			1 The fault was caused by an instruction fetch.
	*/

	// check access type...
	TUint accessPermissions = EUser; // we only allow paging of user memory
	if(exc.iExcErrorCode&(1<<1))
		accessPermissions |= EReadWrite;

	// let TheMmu handle the fault...
	return TheMmu.HandlePageFault(exc.iEip, exc.iFaultAddress, accessPermissions, aExceptionInfo);
	}


