Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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;
}