// Copyright (c) 1998-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\mmubase\mmubase.cpp
//
//
#include <memmodel/epoc/mmubase/mmubase.h>
#include <mmubase.inl>
#include <ramcache.h>
#include <demand_paging.h>
#include "cache_maintenance.h"
#include "highrestimer.h"
#include <defrag.h>
#include <ramalloc.h>
__ASSERT_COMPILE(sizeof(SPageInfo)==(1<<KPageInfoShift));
_LIT(KLitRamAlloc,"RamAlloc");
_LIT(KLitHwChunk,"HwChunk");
DMutex* MmuBase::HwChunkMutex;
DMutex* MmuBase::RamAllocatorMutex;
#ifdef BTRACE_KERNEL_MEMORY
TInt Epoc::DriverAllocdPhysRam = 0;
TInt Epoc::KernelMiscPages = 0;
#endif
/******************************************************************************
* Code common to all MMU memory models
******************************************************************************/
const TInt KFreePagesStepSize=16;
void MmuBase::Panic(TPanic aPanic)
{
Kern::Fault("MMUBASE",aPanic);
}
void SPageInfo::Lock()
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::Lock");
++iLockCount;
if(!iLockCount)
MmuBase::Panic(MmuBase::EPageLockedTooManyTimes);
}
TInt SPageInfo::Unlock()
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::Unlock");
if(!iLockCount)
MmuBase::Panic(MmuBase::EPageUnlockedTooManyTimes);
return --iLockCount;
}
#ifdef _DEBUG
void SPageInfo::Set(TType aType, TAny* aOwner, TUint32 aOffset)
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::Set");
(TUint16&)iType = aType; // also sets iState to EStateNormal
iOwner = aOwner;
iOffset = aOffset;
iModifier = 0;
}
void SPageInfo::Change(TType aType,TState aState)
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::Change");
iType = aType;
iState = aState;
iModifier = 0;
}
void SPageInfo::SetState(TState aState)
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::SetState");
iState = aState;
iModifier = 0;
}
void SPageInfo::SetModifier(TAny* aModifier)
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::SetModifier");
iModifier = aModifier;
}
TInt SPageInfo::CheckModified(TAny* aModifier)
{
CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"SPageInfo::CheckModified");
return iModifier!=aModifier;
}
void SPageInfo::SetZone(TUint8 aZoneIndex)
{
__ASSERT_ALWAYS(K::Initialising,Kern::Fault("SPageInfo::SetZone",0));
iZone = aZoneIndex;
}
#endif
MmuBase::MmuBase()
: iRamCache(NULL), iDefrag(NULL)
{
}
TUint32 MmuBase::RoundToPageSize(TUint32 aSize)
{
return (aSize+KPageMask)&~KPageMask;
}
TUint32 MmuBase::RoundToChunkSize(TUint32 aSize)
{
TUint32 mask=TheMmu->iChunkMask;
return (aSize+mask)&~mask;
}
TInt MmuBase::RoundUpRangeToPageSize(TUint32& aBase, TUint32& aSize)
{
TUint32 mask=KPageMask;
TUint32 shift=KPageShift;
TUint32 offset=aBase&mask;
aBase&=~mask;
aSize=(aSize+offset+mask)&~mask;
return TInt(aSize>>shift);
}
void MmuBase::Wait()
{
Kern::MutexWait(*RamAllocatorMutex);
if (RamAllocatorMutex->iHoldCount==1)
{
MmuBase& m=*TheMmu;
m.iInitialFreeMemory=Kern::FreeRamInBytes();
m.iAllocFailed=EFalse;
}
}
void MmuBase::Signal()
{
if (RamAllocatorMutex->iHoldCount>1)
{
Kern::MutexSignal(*RamAllocatorMutex);
return;
}
MmuBase& m=*TheMmu;
TInt initial=m.iInitialFreeMemory;
TBool failed=m.iAllocFailed;
TInt final=Kern::FreeRamInBytes();
Kern::MutexSignal(*RamAllocatorMutex);
K::CheckFreeMemoryLevel(initial,final,failed);
}
void MmuBase::WaitHwChunk()
{
Kern::MutexWait(*HwChunkMutex);
}
void MmuBase::SignalHwChunk()
{
Kern::MutexSignal(*HwChunkMutex);
}
void MmuBase::MapRamPage(TLinAddr aAddr, TPhysAddr aPage, TPte aPtePerm)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MapRamPage %08x@%08x perm %08x", aPage, aAddr, aPtePerm));
TInt ptid=PageTableId(aAddr);
NKern::LockSystem();
MapRamPages(ptid,SPageInfo::EInvalid,0,aAddr,&aPage,1,aPtePerm);
NKern::UnlockSystem();
}
//
// Unmap and free pages from a global area
//
void MmuBase::UnmapAndFree(TLinAddr aAddr, TInt aNumPages)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::UnmapAndFree(%08x,%d)",aAddr,aNumPages));
while(aNumPages)
{
TInt pt_np=(iChunkSize-(aAddr&iChunkMask))>>iPageShift;
TInt np=Min(aNumPages,pt_np);
aNumPages-=np;
TInt id=PageTableId(aAddr);
if (id>=0)
{
while(np)
{
TInt np2=Min(np,KFreePagesStepSize);
TPhysAddr phys[KFreePagesStepSize];
TInt nptes;
TInt nfree;
NKern::LockSystem();
UnmapPages(id,aAddr,np2,phys,true,nptes,nfree,NULL);
NKern::UnlockSystem();
if (nfree)
{
if (iDecommitThreshold)
CacheMaintenanceOnDecommit(phys, nfree);
iRamPageAllocator->FreeRamPages(phys,nfree,EPageFixed);
}
np-=np2;
aAddr+=(np2<<iPageShift);
}
}
else
{
aAddr+=(np<<iPageShift);
}
}
}
void MmuBase::FreePages(TPhysAddr* aPageList, TInt aCount, TZonePageType aPageType)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::FreePages(%08x,%d)",aPageList,aCount));
if (!aCount)
return;
TBool sync_decommit = (TUint(aCount)<iDecommitThreshold);
TPhysAddr* ppa=aPageList;
TPhysAddr* ppaE=ppa+aCount;
NKern::LockSystem();
while (ppa<ppaE)
{
TPhysAddr pa=*ppa++;
SPageInfo* pi=SPageInfo::SafeFromPhysAddr(pa);
if (pi)
{
pi->SetUnused();
if (pi->LockCount())
ppa[-1]=KPhysAddrInvalid; // don't free page if it's locked down
else if (sync_decommit)
{
NKern::UnlockSystem();
CacheMaintenanceOnDecommit(pa);
NKern::LockSystem();
}
}
if (!sync_decommit)
NKern::FlashSystem();
}
NKern::UnlockSystem();
if (iDecommitThreshold && !sync_decommit)
CacheMaintenance::SyncPhysicalCache_All();
iRamPageAllocator->FreeRamPages(aPageList,aCount, aPageType);
}
TInt MmuBase::InitPageTableInfo(TInt aId)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::InitPageTableInfo(%x)",aId));
TInt ptb=aId>>iPtBlockShift;
if (++iPtBlockCount[ptb]==1)
{
// expand page table info array
TPhysAddr pagePhys;
if (AllocRamPages(&pagePhys,1, EPageFixed)!=KErrNone)
{
__KTRACE_OPT(KMMU,Kern::Printf("Unable to allocate page"));
iPtBlockCount[ptb]=0;
iAllocFailed=ETrue;
return KErrNoMemory;
}
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscAlloc, 1<<KPageShift);
++Epoc::KernelMiscPages;
#endif
TLinAddr pil=PtInfoBlockLinAddr(ptb);
NKern::LockSystem();
SPageInfo::FromPhysAddr(pagePhys)->SetPtInfo(ptb);
NKern::UnlockSystem();
MapRamPage(pil, pagePhys, iPtInfoPtePerm);
memclr((TAny*)pil, iPageSize);
}
return KErrNone;
}
TInt MmuBase::DoAllocPageTable(TPhysAddr& aPhysAddr)
//
// Allocate a new page table but don't map it.
// Return page table id and page number/phys address of new page if any.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::DoAllocPageTable()"));
#ifdef _DEBUG
if(K::CheckForSimulatedAllocFail())
return KErrNoMemory;
#endif
TInt id=iPageTableAllocator?iPageTableAllocator->Alloc():-1;
if (id<0)
{
// need to allocate a new page
if (AllocRamPages(&aPhysAddr,1, EPageFixed)!=KErrNone)
{
__KTRACE_OPT(KMMU,Kern::Printf("Unable to allocate page"));
iAllocFailed=ETrue;
return KErrNoMemory;
}
// allocate an ID for the new page
id=iPageTableLinearAllocator->Alloc();
if (id>=0)
{
id<<=iPtClusterShift;
__KTRACE_OPT(KMMU,Kern::Printf("Allocated ID %04x",id));
}
if (id<0 || InitPageTableInfo(id)!=KErrNone)
{
__KTRACE_OPT(KMMU,Kern::Printf("Unable to allocate page table info"));
iPageTableLinearAllocator->Free(id>>iPtClusterShift);
if (iDecommitThreshold)
CacheMaintenanceOnDecommit(aPhysAddr);
iRamPageAllocator->FreeRamPage(aPhysAddr, EPageFixed);
iAllocFailed=ETrue;
return KErrNoMemory;
}
// Set up page info for new page
NKern::LockSystem();
SPageInfo::FromPhysAddr(aPhysAddr)->SetPageTable(id>>iPtClusterShift);
NKern::UnlockSystem();
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscAlloc, 1<<KPageShift);
++Epoc::KernelMiscPages;
#endif
// mark all subpages other than first as free for use as page tables
if (iPtClusterSize>1)
iPageTableAllocator->Free(id+1,iPtClusterSize-1);
}
else
aPhysAddr=KPhysAddrInvalid;
__KTRACE_OPT(KMMU,Kern::Printf("DoAllocPageTable returns %d (%08x)",id,aPhysAddr));
PtInfo(id).SetUnused();
return id;
}
TInt MmuBase::MapPageTable(TInt aId, TPhysAddr aPhysAddr, TBool aAllowExpand)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MapPageTable(%d,%08x)",aId,aPhysAddr));
TLinAddr ptLin=PageTableLinAddr(aId);
TInt ptg=aId>>iPtGroupShift;
if (++iPtGroupCount[ptg]==1)
{
// need to allocate a new page table
__ASSERT_ALWAYS(aAllowExpand, Panic(EMapPageTableBadExpand));
TPhysAddr xptPhys;
TInt xptid=DoAllocPageTable(xptPhys);
if (xptid<0)
{
__KTRACE_OPT(KMMU,Kern::Printf("Unable to allocate extra page table"));
iPtGroupCount[ptg]=0;
return KErrNoMemory;
}
if (xptPhys==KPhysAddrInvalid)
xptPhys=aPhysAddr + ((xptid-aId)<<iPageTableShift);
BootstrapPageTable(xptid, xptPhys, aId, aPhysAddr); // initialise XPT and map it
}
else
MapRamPage(ptLin, aPhysAddr, iPtPtePerm);
return KErrNone;
}
TInt MmuBase::AllocPageTable()
//
// Allocate a new page table, mapped at the correct linear address.
// Clear all entries to Not Present. Return page table id.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::AllocPageTable()"));
__ASSERT_MUTEX(MmuBase::RamAllocatorMutex);
TPhysAddr ptPhys;
TInt id=DoAllocPageTable(ptPhys);
if (id<0)
return KErrNoMemory;
if (ptPhys!=KPhysAddrInvalid)
{
TInt r=MapPageTable(id,ptPhys);
if (r!=KErrNone)
{
DoFreePageTable(id);
SPageInfo* pi=SPageInfo::FromPhysAddr(ptPhys);
NKern::LockSystem();
pi->SetUnused();
NKern::UnlockSystem();
if (iDecommitThreshold)
CacheMaintenanceOnDecommit(ptPhys);
iRamPageAllocator->FreeRamPage(ptPhys, EPageFixed);
return r;
}
}
ClearPageTable(id);
__KTRACE_OPT(KMMU,Kern::Printf("AllocPageTable returns %d",id));
return id;
}
TBool MmuBase::DoFreePageTable(TInt aId)
//
// Free an empty page table. We assume that all pages mapped by the page table have
// already been unmapped and freed.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::DoFreePageTable(%d)",aId));
SPageTableInfo& s=PtInfo(aId);
__NK_ASSERT_DEBUG(!s.iCount); // shouldn't have any pages mapped
s.SetUnused();
TInt id=aId &~ iPtClusterMask;
if (iPageTableAllocator)
{
iPageTableAllocator->Free(aId);
if (iPageTableAllocator->NotFree(id,iPtClusterSize))
{
// some subpages still in use
return ETrue;
}
__KTRACE_OPT(KMMU,Kern::Printf("Freeing whole page, id=%d",id));
// whole page is now free
// remove it from the page table allocator
iPageTableAllocator->Alloc(id,iPtClusterSize);
}
TInt ptb=aId>>iPtBlockShift;
if (--iPtBlockCount[ptb]==0)
{
// shrink page table info array
TLinAddr pil=PtInfoBlockLinAddr(ptb);
UnmapAndFree(pil,1); // remove PTE, null page info, free page
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscFree, 1<<KPageShift);
--Epoc::KernelMiscPages;
#endif
}
// free the page table linear address
iPageTableLinearAllocator->Free(id>>iPtClusterShift);
return EFalse;
}
void MmuBase::FreePageTable(TInt aId)
//
// Free an empty page table. We assume that all pages mapped by the page table have
// already been unmapped and freed.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::FreePageTable(%d)",aId));
if (DoFreePageTable(aId))
return;
TInt id=aId &~ iPtClusterMask;
// calculate linear address of page
TLinAddr ptLin=PageTableLinAddr(id);
__KTRACE_OPT(KMMU,Kern::Printf("Page lin %08x",ptLin));
// unmap and free the page
UnmapAndFree(ptLin,1);
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscFree, 1<<KPageShift);
--Epoc::KernelMiscPages;
#endif
TInt ptg=aId>>iPtGroupShift;
--iPtGroupCount[ptg];
// don't shrink the page table mapping for now
}
TInt MmuBase::AllocPhysicalRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocPhysicalRam() size=%x align=%d",aSize,aAlign));
TInt r=AllocContiguousRam(aSize, aPhysAddr, aAlign);
if (r!=KErrNone)
{
iAllocFailed=ETrue;
return r;
}
TInt n=TInt(TUint32(aSize+iPageMask)>>iPageShift);
SPageInfo* pI=SPageInfo::FromPhysAddr(aPhysAddr);
SPageInfo* pE=pI+n;
for (; pI<pE; ++pI)
{
NKern::LockSystem();
__NK_ASSERT_DEBUG(pI->Type()==SPageInfo::EUnused);
pI->Lock();
NKern::UnlockSystem();
}
return KErrNone;
}
/** Attempt to allocate a contiguous block of RAM from the specified zone.
@param aZoneIdList An array of the IDs of the RAM zones to allocate from.
@param aZoneIdCount The number of RAM zone IDs listed in aZoneIdList.
@param aSize The number of contiguous bytes to allocate
@param aPhysAddr The physical address of the start of the contiguous block of
memory allocated
@param aAlign Required alignment
@return KErrNone on success, KErrArgument if zone doesn't exist or aSize is larger than the
size of the RAM zone or KErrNoMemory when the RAM zone is too full.
*/
TInt MmuBase::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam() size=0x%x align=%d", aSize, aAlign));
TInt r = ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign);
if (r!=KErrNone)
{
iAllocFailed=ETrue;
return r;
}
TInt n=TInt(TUint32(aSize+iPageMask)>>iPageShift);
SPageInfo* pI=SPageInfo::FromPhysAddr(aPhysAddr);
SPageInfo* pE=pI+n;
for (; pI<pE; ++pI)
{
NKern::LockSystem();
__NK_ASSERT_DEBUG(pI->Type()==SPageInfo::EUnused);
pI->Lock();
NKern::UnlockSystem();
}
return KErrNone;
}
/** Attempt to allocate discontiguous RAM pages.
@param aNumPages The number of pages to allocate.
@param aPageList Pointer to an array where each element will be the physical
address of each page allocated.
@return KErrNone on success, KErrNoMemory otherwise
*/
TInt MmuBase::AllocPhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocPhysicalRam() numpages=%x", aNumPages));
TInt r = AllocRamPages(aPageList, aNumPages, EPageFixed);
if (r!=KErrNone)
{
iAllocFailed=ETrue;
return r;
}
TPhysAddr* pageEnd = aPageList + aNumPages;
for (TPhysAddr* page = aPageList; page < pageEnd; page++)
{
SPageInfo* pageInfo = SPageInfo::FromPhysAddr(*page);
NKern::LockSystem();
__NK_ASSERT_DEBUG(pageInfo->Type() == SPageInfo::EUnused);
pageInfo->Lock();
NKern::UnlockSystem();
}
return KErrNone;
}
/** Attempt to allocate discontiguous RAM pages from the specified RAM zones.
@param aZoneIdList An array of the IDs of the RAM zones to allocate from.
@param aZoneIdCount The number of RAM zone IDs listed in aZoneIdList.
@param aNumPages The number of pages to allocate.
@param aPageList Pointer to an array where each element will be the physical
address of each page allocated.
@return KErrNone on success, KErrArgument if zone doesn't exist or aNumPages is
larger than the total number of pages in the RAM zone or KErrNoMemory when the RAM
zone is too full.
*/
TInt MmuBase::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aNumPages, TPhysAddr* aPageList)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam() numpages 0x%x zones 0x%x", aNumPages, aZoneIdCount));
TInt r = ZoneAllocRamPages(aZoneIdList, aZoneIdCount, aPageList, aNumPages, EPageFixed);
if (r!=KErrNone)
{
iAllocFailed=ETrue;
return r;
}
TPhysAddr* pageEnd = aPageList + aNumPages;
for (TPhysAddr* page = aPageList; page < pageEnd; page++)
{
SPageInfo* pageInfo = SPageInfo::FromPhysAddr(*page);
NKern::LockSystem();
__NK_ASSERT_DEBUG(pageInfo->Type() == SPageInfo::EUnused);
pageInfo->Lock();
NKern::UnlockSystem();
}
return KErrNone;
}
TInt MmuBase::FreePhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreePhysicalRam(%08x,%x)",aPhysAddr,aSize));
TInt n=TInt(TUint32(aSize+iPageMask)>>iPageShift);
SPageInfo* pI=SPageInfo::FromPhysAddr(aPhysAddr);
SPageInfo* pE=pI+n;
for (; pI<pE; ++pI)
{
NKern::LockSystem();
__ASSERT_ALWAYS(pI->Type()==SPageInfo::EUnused && pI->Unlock()==0, Panic(EBadFreePhysicalRam));
NKern::UnlockSystem();
}
TInt r=iRamPageAllocator->FreePhysicalRam(aPhysAddr, aSize);
return r;
}
/** Free discontiguous RAM pages that were previously allocated using discontiguous
overload of MmuBase::AllocPhysicalRam() or MmuBase::ZoneAllocPhysicalRam().
Specifying one of the following may cause the system to panic:
a) an invalid physical RAM address.
b) valid physical RAM addresses where some had not been previously allocated.
c) an adrress not aligned to a page boundary.
@param aNumPages Number of pages to free
@param aPageList Array of the physical address of each page to free
@return KErrNone if the operation was successful.
*/
TInt MmuBase::FreePhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreePhysicalRam(%08x,%08x)", aNumPages, aPageList));
TPhysAddr* pageEnd = aPageList + aNumPages;
TInt r = KErrNone;
for (TPhysAddr* page = aPageList; page < pageEnd && r == KErrNone; page++)
{
SPageInfo* pageInfo = SPageInfo::FromPhysAddr(*page);
NKern::LockSystem();
__ASSERT_ALWAYS(pageInfo->Type()==SPageInfo::EUnused && pageInfo->Unlock()==0, Panic(EBadFreePhysicalRam));
NKern::UnlockSystem();
// Free the page
r = iRamPageAllocator->FreePhysicalRam(*page, KPageSize);
}
return r;
}
TInt MmuBase::FreeRamZone(TUint aZoneId, TPhysAddr& aZoneBase, TUint& aZoneBytes)
{
TUint zonePages;
TInt r = iRamPageAllocator->GetZoneAddress(aZoneId, aZoneBase, zonePages);
if (r != KErrNone)
return r;
aZoneBytes = zonePages << KPageShift;
return MmuBase::FreePhysicalRam(aZoneBase, aZoneBytes);
}
TInt MmuBase::ClaimPhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("Mmu::ClaimPhysicalRam(%08x,%x)",aPhysAddr,aSize));
TUint32 pa=aPhysAddr;
TUint32 size=aSize;
TInt n=RoundUpRangeToPageSize(pa,size);
TInt r=iRamPageAllocator->ClaimPhysicalRam(pa, size);
if (r==KErrNone)
{
SPageInfo* pI=SPageInfo::FromPhysAddr(pa);
SPageInfo* pE=pI+n;
for (; pI<pE; ++pI)
{
NKern::LockSystem();
__NK_ASSERT_DEBUG(pI->Type()==SPageInfo::EUnused && pI->LockCount()==0);
pI->Lock();
NKern::UnlockSystem();
}
}
return r;
}
/**
Allocate a set of discontiguous RAM pages from the specified zone.
@param aZoneIdList The array of IDs of the RAM zones to allocate from.
@param aZoneIdCount The number of RAM zone IDs in aZoneIdList.
@param aPageList Preallocated array of TPhysAddr elements that will receive the
physical address of each page allocated.
@param aNumPages The number of pages to allocate.
@param aPageType The type of the pages being allocated.
@return KErrNone on success, KErrArgument if a zone of aZoneIdList doesn't exist,
KErrNoMemory if there aren't enough free pages in the zone
*/
TInt MmuBase::ZoneAllocRamPages(TUint* aZoneIdList, TUint aZoneIdCount, TPhysAddr* aPageList, TInt aNumPages, TZonePageType aPageType)
{
#ifdef _DEBUG
if(K::CheckForSimulatedAllocFail())
return KErrNoMemory;
#endif
__NK_ASSERT_DEBUG(aPageType == EPageFixed);
return iRamPageAllocator->ZoneAllocRamPages(aZoneIdList, aZoneIdCount, aPageList, aNumPages, aPageType);
}
TInt MmuBase::AllocRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aPageType, TUint aBlockedZoneId, TBool aBlockRest)
{
#ifdef _DEBUG
if(K::CheckForSimulatedAllocFail())
return KErrNoMemory;
#endif
TInt missing = iRamPageAllocator->AllocRamPages(aPageList, aNumPages, aPageType, aBlockedZoneId, aBlockRest);
// If missing some pages, ask the RAM cache to donate some of its pages.
// Don't ask it for discardable pages as those are intended for itself.
if(missing && aPageType != EPageDiscard && iRamCache->GetFreePages(missing))
missing = iRamPageAllocator->AllocRamPages(aPageList, aNumPages, aPageType, aBlockedZoneId, aBlockRest);
return missing ? KErrNoMemory : KErrNone;
}
TInt MmuBase::AllocContiguousRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
#ifdef _DEBUG
if(K::CheckForSimulatedAllocFail())
return KErrNoMemory;
#endif
TUint contigPages = (aSize + KPageSize - 1) >> KPageShift;
TInt r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aAlign);
if (r == KErrNoMemory && contigPages > KMaxFreeableContiguousPages)
{// Allocation failed but as this is a large allocation flush the RAM cache
// and reattempt the allocation as large allocation wouldn't discard pages.
iRamCache->FlushAll();
r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aAlign);
}
return r;
}
/**
Allocate contiguous RAM from the specified RAM zones.
@param aZoneIdList An array of IDs of the RAM zones to allocate from
@param aZoneIdCount The number of IDs listed in aZoneIdList
@param aSize The number of bytes to allocate
@param aPhysAddr Will receive the physical base address of the allocated RAM
@param aAlign The log base 2 alginment required
*/
TInt MmuBase::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
#ifdef _DEBUG
if(K::CheckForSimulatedAllocFail())
return KErrNoMemory;
#endif
return iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign);
}
SPageInfo* SPageInfo::SafeFromPhysAddr(TPhysAddr aAddress)
{
TUint index = aAddress>>(KPageShift+KPageShift-KPageInfoShift);
TUint flags = ((TUint8*)KPageInfoMap)[index>>3];
TUint mask = 1<<(index&7);
if(!(flags&mask))
return 0; // no SPageInfo for aAddress
SPageInfo* info = FromPhysAddr(aAddress);
if(info->Type()==SPageInfo::EInvalid)
return 0;
return info;
}
/** HAL Function wrapper for the RAM allocator.
*/
TInt RamHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2)
{
DRamAllocator *pRamAlloc = MmuBase::TheMmu->iRamPageAllocator;
if (pRamAlloc)
return pRamAlloc->HalFunction(aFunction, a1, a2);
return KErrNotSupported;
}
/******************************************************************************
* Initialisation
******************************************************************************/
void MmuBase::Init1()
{
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("MmuBase::Init1"));
iInitialFreeMemory=0;
iAllocFailed=EFalse;
}
void MmuBase::Init2()
{
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("MmuBase::Init2"));
TInt total_ram=TheSuperPage().iTotalRamSize;
TInt total_ram_pages=total_ram>>iPageShift;
iNumPages = total_ram_pages;
const SRamInfo& info=*(const SRamInfo*)TheSuperPage().iRamBootData;
iRamPageAllocator=DRamAllocator::New(info, RamZoneConfig, RamZoneCallback);
TInt max_pt=total_ram>>iPageTableShift;
if (max_pt<iMaxPageTables)
iMaxPageTables=max_pt;
iMaxPageTables &= ~iPtClusterMask;
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iMaxPageTables=%d",iMaxPageTables));
TInt max_ptpg=iMaxPageTables>>iPtClusterShift;
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("max_ptpg=%d",max_ptpg));
iPageTableLinearAllocator=TBitMapAllocator::New(max_ptpg,ETrue);
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iPageTableLinearAllocator=%08x",iPageTableLinearAllocator));
__ASSERT_ALWAYS(iPageTableLinearAllocator,Panic(EPtLinAllocCreateFailed));
if (iPtClusterShift) // if more than one page table per page
{
iPageTableAllocator=TBitMapAllocator::New(iMaxPageTables,EFalse);
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iPageTableAllocator=%08x",iPageTableAllocator));
__ASSERT_ALWAYS(iPageTableAllocator,Panic(EPtAllocCreateFailed));
}
TInt max_ptb=(iMaxPageTables+iPtBlockMask)>>iPtBlockShift;
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("max_ptb=%d",max_ptb));
iPtBlockCount=(TInt*)Kern::AllocZ(max_ptb*sizeof(TInt));
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iPtBlockCount=%08x",iPtBlockCount));
__ASSERT_ALWAYS(iPtBlockCount,Panic(EPtBlockCountCreateFailed));
TInt max_ptg=(iMaxPageTables+iPtGroupMask)>>iPtGroupShift;
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("ptg_shift=%d, max_ptg=%d",iPtGroupShift,max_ptg));
iPtGroupCount=(TInt*)Kern::AllocZ(max_ptg*sizeof(TInt));
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("iPtGroupCount=%08x",iPtGroupCount));
__ASSERT_ALWAYS(iPtGroupCount,Panic(EPtGroupCountCreateFailed));
// Clear the inital (and only so far) page table info page so all unused
// page tables will be marked as unused.
memclr((TAny*)KPageTableInfoBase, KPageSize);
// look for page tables - assume first page table (id=0) maps page tables
TPte* pPte=(TPte*)iPageTableLinBase;
TInt i;
for (i=0; i<iChunkSize/iPageSize; ++i)
{
TPte pte=*pPte++;
if (!PteIsPresent(pte)) // after boot, page tables are contiguous
break;
iPageTableLinearAllocator->Alloc(i,1);
TPhysAddr ptpgPhys=PtePhysAddr(pte, i);
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(ptpgPhys);
__ASSERT_ALWAYS(pi, Panic(EInvalidPageTableAtBoot));
pi->SetPageTable(i);
pi->Lock();
TInt id=i<<iPtClusterShift;
TInt ptb=id>>iPtBlockShift;
++iPtBlockCount[ptb];
TInt ptg=id>>iPtGroupShift;
++iPtGroupCount[ptg];
}
// look for mapped pages
TInt npdes=1<<(32-iChunkShift);
TInt npt=0;
for (i=0; i<npdes; ++i)
{
TLinAddr cAddr=TLinAddr(i<<iChunkShift);
if (cAddr>=PP::RamDriveStartAddress && TUint32(cAddr-PP::RamDriveStartAddress)<TUint32(PP::RamDriveRange))
continue; // leave RAM drive for now
TInt ptid=PageTableId(cAddr);
TPhysAddr pdePhys = PdePhysAddr(cAddr); // check for whole PDE mapping
pPte = NULL;
if (ptid>=0)
{
++npt;
__KTRACE_OPT(KMMU,Kern::Printf("Addr %08x -> page table %d", cAddr, ptid));
pPte=(TPte*)PageTableLinAddr(ptid);
}
#ifdef KMMU
if (pdePhys != KPhysAddrInvalid)
{
__KTRACE_OPT(KMMU,Kern::Printf("Addr %08x -> Whole PDE Phys %08x", cAddr, pdePhys));
}
#endif
if (ptid>=0 || pdePhys != KPhysAddrInvalid)
{
TInt j;
TInt np=0;
for (j=0; j<iChunkSize/iPageSize; ++j)
{
TBool present = ETrue; // all pages present if whole PDE mapping
TPte pte = 0;
if (pPte)
{
pte = pPte[j];
present = PteIsPresent(pte);
}
if (present)
{
++np;
TPhysAddr pa = pPte ? PtePhysAddr(pte, j) : (pdePhys + (j<<iPageShift));
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pa);
__KTRACE_OPT(KMMU,Kern::Printf("Addr: %08x PA=%08x",
cAddr+(j<<iPageShift), pa));
if (pi) // ignore non-RAM mappings
{//these pages will never be freed and can't be moved
TInt r = iRamPageAllocator->MarkPageAllocated(pa, EPageFixed);
// allow KErrAlreadyExists since it's possible that a page is doubly mapped
__ASSERT_ALWAYS(r==KErrNone || r==KErrAlreadyExists, Panic(EBadMappedPageAfterBoot));
SetupInitialPageInfo(pi,cAddr,j);
#ifdef BTRACE_KERNEL_MEMORY
if(r==KErrNone)
++Epoc::KernelMiscPages;
#endif
}
}
}
__KTRACE_OPT(KMMU,Kern::Printf("Addr: %08x #PTEs=%d",cAddr,np));
if (ptid>=0)
SetupInitialPageTableInfo(ptid,cAddr,np);
}
}
TInt oddpt=npt & iPtClusterMask;
if (oddpt)
oddpt=iPtClusterSize-oddpt;
__KTRACE_OPT(KBOOT,Kern::Printf("Total page tables %d, left over subpages %d",npt,oddpt));
if (oddpt)
iPageTableAllocator->Free(npt,oddpt);
DoInit2();
// Save current free RAM size - there can never be more free RAM than this
TInt max_free = Kern::FreeRamInBytes();
K::MaxFreeRam = max_free;
if (max_free < PP::RamDriveMaxSize)
PP::RamDriveMaxSize = max_free;
if (K::ColdStart)
ClearRamDrive(PP::RamDriveStartAddress);
else
RecoverRamDrive();
TInt r=K::MutexCreate((DMutex*&)RamAllocatorMutex, KLitRamAlloc, NULL, EFalse, KMutexOrdRamAlloc);
if (r!=KErrNone)
Panic(ERamAllocMutexCreateFailed);
r=K::MutexCreate((DMutex*&)HwChunkMutex, KLitHwChunk, NULL, EFalse, KMutexOrdHwChunk);
if (r!=KErrNone)
Panic(EHwChunkMutexCreateFailed);
#ifdef __DEMAND_PAGING__
if (DemandPaging::RomPagingRequested() || DemandPaging::CodePagingRequested())
iRamCache = DemandPaging::New();
else
iRamCache = new RamCache;
#else
iRamCache = new RamCache;
#endif
if (!iRamCache)
Panic(ERamCacheAllocFailed);
iRamCache->Init2();
RamCacheBase::TheRamCache = iRamCache;
// Get the allocator to signal to the variant which RAM zones are in use so far
iRamPageAllocator->InitialCallback();
}
void MmuBase::Init3()
{
__KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("MmuBase::Init3"));
// Initialise demand paging
#ifdef __DEMAND_PAGING__
M::DemandPagingInit();
#endif
// Register a HAL Function for the Ram allocator.
TInt r = Kern::AddHalEntry(EHalGroupRam, RamHalFunction, 0);
__NK_ASSERT_ALWAYS(r==KErrNone);
//
// Perform the intialisation for page moving and RAM defrag object.
//
// allocate a page to use as an alt stack
MmuBase::Wait();
TPhysAddr stackpage;
r = AllocPhysicalRam(KPageSize, stackpage);
MmuBase::Signal();
if (r!=KErrNone)
Panic(EDefragStackAllocFailed);
// map it at a predetermined address
TInt ptid = PageTableId(KDefragAltStackAddr);
TPte perm = PtePermissions(EKernelStack);
NKern::LockSystem();
MapRamPages(ptid, SPageInfo::EFixed, NULL, KDefragAltStackAddr, &stackpage, 1, perm);
NKern::UnlockSystem();
iAltStackBase = KDefragAltStackAddr + KPageSize;
__KTRACE_OPT(KMMU,Kern::Printf("Allocated defrag alt stack page at %08x, mapped to %08x, base is now %08x", stackpage, KDefragAltStackAddr, iAltStackBase));
// Create the actual defrag object and initialise it.
iDefrag = new Defrag;
if (!iDefrag)
Panic(EDefragAllocFailed);
iDefrag->Init3(iRamPageAllocator);
}
void MmuBase::CreateKernelSection(TLinAddr aEnd, TInt aHwChunkAlign)
{
TLinAddr base=(TLinAddr)TheRomHeader().iKernelLimit;
iKernelSection=TLinearSection::New(base, aEnd);
__ASSERT_ALWAYS(iKernelSection!=NULL, Panic(ECreateKernelSectionFailed));
iHwChunkAllocator=THwChunkAddressAllocator::New(aHwChunkAlign, iKernelSection);
__ASSERT_ALWAYS(iHwChunkAllocator!=NULL, Panic(ECreateHwChunkAllocFailed));
}
// Recover RAM drive contents after a reset
TInt MmuBase::RecoverRamDrive()
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::RecoverRamDrive()"));
TLinAddr ptlin;
TLinAddr chunk = PP::RamDriveStartAddress;
TLinAddr end = chunk + (TLinAddr)PP::RamDriveRange;
TInt size = 0;
TInt limit = RoundToPageSize(TheSuperPage().iRamDriveSize);
for( ; chunk<end; chunk+=iChunkSize)
{
if (size==limit) // have reached end of ram drive
break;
TPhysAddr ptphys = 0;
TInt ptid = BootPageTableId(chunk, ptphys); // ret KErrNotFound if PDE not present, KErrUnknown if present but as yet unknown page table
__KTRACE_OPT(KMMU,Kern::Printf("Addr %08x: PTID=%d PTPHYS=%08x", chunk, ptid, ptphys));
if (ptid==KErrNotFound)
break; // no page table so stop here and clear to end of range
TPhysAddr ptpgphys = ptphys & ~iPageMask;
TInt r = iRamPageAllocator->MarkPageAllocated(ptpgphys, EPageMovable);
__KTRACE_OPT(KMMU,Kern::Printf("MPA: r=%d",r));
if (r==KErrArgument)
break; // page table address was invalid - stop here and clear to end of range
if (r==KErrNone)
{
// this page was currently unallocated
if (ptid>=0)
break; // ID has been allocated - bad news - bail here
ptid = iPageTableLinearAllocator->Alloc();
__ASSERT_ALWAYS(ptid>=0, Panic(ERecoverRamDriveAllocPTIDFailed));
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(ptpgphys);
__ASSERT_ALWAYS(pi, Panic(ERecoverRamDriveBadPageTable));
pi->SetPageTable(ptid); // id = cluster number here
ptid <<= iPtClusterShift;
MapPageTable(ptid, ptpgphys, EFalse);
if (iPageTableAllocator)
iPageTableAllocator->Free(ptid, iPtClusterSize);
ptid |= ((ptphys>>iPageTableShift)&iPtClusterMask);
ptlin = PageTableLinAddr(ptid);
__KTRACE_OPT(KMMU,Kern::Printf("Page table ID %d lin %08x", ptid, ptlin));
if (iPageTableAllocator)
iPageTableAllocator->Alloc(ptid, 1);
}
else
{
// this page was already allocated
if (ptid<0)
break; // ID not allocated - bad news - bail here
ptlin = PageTableLinAddr(ptid);
__KTRACE_OPT(KMMU,Kern::Printf("Page table lin %08x", ptlin));
if (iPageTableAllocator)
iPageTableAllocator->Alloc(ptid, 1);
}
TInt pte_index;
TBool chunk_inc = 0;
TPte* page_table = (TPte*)ptlin;
for (pte_index=0; pte_index<(iChunkSize>>iPageSize); ++pte_index)
{
if (size==limit) // have reached end of ram drive
break;
TPte pte = page_table[pte_index];
if (PteIsPresent(pte))
{
TPhysAddr pa=PtePhysAddr(pte, pte_index);
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pa);
if (!pi)
break;
TInt r = iRamPageAllocator->MarkPageAllocated(pa, EPageMovable);
__ASSERT_ALWAYS(r==KErrNone, Panic(ERecoverRamDriveBadPage));
size+=iPageSize;
chunk_inc = iChunkSize;
}
}
if (pte_index < (iChunkSize>>iPageSize) )
{
// if we recovered pages in this page table, leave it in place
chunk += chunk_inc;
// clear from here on
ClearPageTable(ptid, pte_index);
break;
}
}
if (chunk < end)
ClearRamDrive(chunk);
__KTRACE_OPT(KMMU,Kern::Printf("Recovered RAM drive size %08x",size));
if (size<TheSuperPage().iRamDriveSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("Truncating RAM drive from %08x to %08x", TheSuperPage().iRamDriveSize, size));
TheSuperPage().iRamDriveSize=size;
}
return KErrNone;
}
TInt MmuBase::AllocShadowPage(TLinAddr aRomAddr)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase:AllocShadowPage(%08x)", aRomAddr));
aRomAddr &= ~iPageMask;
TPhysAddr orig_phys = KPhysAddrInvalid;
if (aRomAddr>=iRomLinearBase && aRomAddr<=(iRomLinearEnd-iPageSize))
orig_phys = LinearToPhysical(aRomAddr);
__KTRACE_OPT(KMMU,Kern::Printf("OrigPhys = %08x",orig_phys));
if (orig_phys == KPhysAddrInvalid)
{
__KTRACE_OPT(KMMU,Kern::Printf("Invalid ROM address"));
return KErrArgument;
}
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(orig_phys);
if (pi && pi->Type()==SPageInfo::EShadow)
{
__KTRACE_OPT(KMMU,Kern::Printf("ROM address already shadowed"));
return KErrAlreadyExists;
}
TInt ptid = PageTableId(aRomAddr);
__KTRACE_OPT(KMMU, Kern::Printf("Shadow PTID %d", ptid));
TInt newptid = -1;
if (ptid<0)
{
newptid = AllocPageTable();
__KTRACE_OPT(KMMU, Kern::Printf("New shadow PTID %d", newptid));
if (newptid<0)
return KErrNoMemory;
ptid = newptid;
PtInfo(ptid).SetShadow( (aRomAddr-iRomLinearBase)>>iChunkShift );
InitShadowPageTable(ptid, aRomAddr, orig_phys);
}
TPhysAddr shadow_phys;
if (AllocRamPages(&shadow_phys, 1, EPageFixed) != KErrNone)
{
__KTRACE_OPT(KMMU,Kern::Printf("Unable to allocate page"));
iAllocFailed=ETrue;
if (newptid>=0)
{
FreePageTable(newptid);
}
return KErrNoMemory;
}
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscAlloc, 1<<KPageShift);
++Epoc::KernelMiscPages;
#endif
InitShadowPage(shadow_phys, aRomAddr); // copy original ROM contents
NKern::LockSystem();
Pagify(ptid, aRomAddr);
MapRamPages(ptid, SPageInfo::EShadow, (TAny*)orig_phys, (aRomAddr-iRomLinearBase), &shadow_phys, 1, iShadowPtePerm);
NKern::UnlockSystem();
if (newptid>=0)
{
NKern::LockSystem();
AssignShadowPageTable(newptid, aRomAddr);
NKern::UnlockSystem();
}
FlushShadow(aRomAddr);
__KTRACE_OPT(KMMU,Kern::Printf("AllocShadowPage successful"));
return KErrNone;
}
TInt MmuBase::FreeShadowPage(TLinAddr aRomAddr)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase:FreeShadowPage(%08x)", aRomAddr));
aRomAddr &= ~iPageMask;
TPhysAddr shadow_phys = KPhysAddrInvalid;
if (aRomAddr>=iRomLinearBase || aRomAddr<=(iRomLinearEnd-iPageSize))
shadow_phys = LinearToPhysical(aRomAddr);
__KTRACE_OPT(KMMU,Kern::Printf("ShadowPhys = %08x",shadow_phys));
if (shadow_phys == KPhysAddrInvalid)
{
__KTRACE_OPT(KMMU,Kern::Printf("Invalid ROM address"));
return KErrArgument;
}
TInt ptid = PageTableId(aRomAddr);
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(shadow_phys);
if (ptid<0 || !pi || pi->Type()!=SPageInfo::EShadow)
{
__KTRACE_OPT(KMMU,Kern::Printf("No shadow page at this address"));
return KErrGeneral;
}
TPhysAddr orig_phys = (TPhysAddr)pi->Owner();
DoUnmapShadowPage(ptid, aRomAddr, orig_phys);
SPageTableInfo& pti = PtInfo(ptid);
if (pti.Attribs()==SPageTableInfo::EShadow && --pti.iCount==0)
{
TInt r = UnassignShadowPageTable(aRomAddr, orig_phys);
if (r==KErrNone)
FreePageTable(ptid);
else
pti.SetGlobal(aRomAddr>>iChunkShift);
}
FreePages(&shadow_phys, 1, EPageFixed);
__KTRACE_OPT(KMMU,Kern::Printf("FreeShadowPage successful"));
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscFree, 1<<KPageShift);
--Epoc::KernelMiscPages;
#endif
return KErrNone;
}
TInt MmuBase::FreezeShadowPage(TLinAddr aRomAddr)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase:FreezeShadowPage(%08x)", aRomAddr));
aRomAddr &= ~iPageMask;
TPhysAddr shadow_phys = KPhysAddrInvalid;
if (aRomAddr>=iRomLinearBase || aRomAddr<=(iRomLinearEnd-iPageSize))
shadow_phys = LinearToPhysical(aRomAddr);
__KTRACE_OPT(KMMU,Kern::Printf("ShadowPhys = %08x",shadow_phys));
if (shadow_phys == KPhysAddrInvalid)
{
__KTRACE_OPT(KMMU,Kern::Printf("Invalid ROM address"));
return KErrArgument;
}
TInt ptid = PageTableId(aRomAddr);
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(shadow_phys);
if (ptid<0 || pi==0)
{
__KTRACE_OPT(KMMU,Kern::Printf("No shadow page at this address"));
return KErrGeneral;
}
DoFreezeShadowPage(ptid, aRomAddr);
__KTRACE_OPT(KMMU,Kern::Printf("FreezeShadowPage successful"));
return KErrNone;
}
TInt MmuBase::CopyToShadowMemory(TLinAddr aDest, TLinAddr aSrc, TUint32 aLength)
{
memcpy ((TAny*)aDest, (const TAny*)aSrc, aLength);
return KErrNone;
}
void M::BTracePrime(TUint aCategory)
{
(void)aCategory;
#ifdef BTRACE_KERNEL_MEMORY
// Must check for -1 as that is the default value of aCategory for
// BTrace::Prime() which is intended to prime all categories that are
// currently enabled via a single invocation of BTrace::Prime().
if(aCategory==BTrace::EKernelMemory || (TInt)aCategory == -1)
{
NKern::ThreadEnterCS();
Mmu::Wait();
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryInitialFree,TheSuperPage().iTotalRamSize);
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryCurrentFree,Kern::FreeRamInBytes());
BTrace4(BTrace::EKernelMemory, BTrace::EKernelMemoryMiscAlloc, Epoc::KernelMiscPages<<KPageShift);
#ifdef __DEMAND_PAGING__
if (DemandPaging::ThePager)
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryDemandPagingCache,DemandPaging::ThePager->iMinimumPageCount << KPageShift);
#endif
BTrace8(BTrace::EKernelMemory,BTrace::EKernelMemoryDrvPhysAlloc, Epoc::DriverAllocdPhysRam, -1);
Mmu::Signal();
NKern::ThreadLeaveCS();
}
#endif
#ifdef BTRACE_RAM_ALLOCATOR
// Must check for -1 as that is the default value of aCategory for
// BTrace::Prime() which is intended to prime all categories that are
// currently enabled via a single invocation of BTrace::Prime().
if(aCategory==BTrace::ERamAllocator || (TInt)aCategory == -1)
{
NKern::ThreadEnterCS();
Mmu::Wait();
Mmu::Get().iRamPageAllocator->DoBTracePrime();
Mmu::Signal();
NKern::ThreadLeaveCS();
}
#endif
}
/******************************************************************************
* Code common to all virtual memory models
******************************************************************************/
void RHeapK::Mutate(TInt aOffset, TInt aMaxLength)
//
// Used by the kernel to mutate a fixed heap into a chunk heap.
//
{
iMinLength += aOffset;
iMaxLength = aMaxLength + aOffset;
iOffset = aOffset;
iChunkHandle = (TInt)K::HeapInfo.iChunk;
iPageSize = M::PageSizeInBytes();
iGrowBy = iPageSize;
iFlags = 0;
}
TInt M::PageSizeInBytes()
{
return KPageSize;
}
TInt MmuBase::FreeRamInBytes()
{
TInt free = iRamPageAllocator->FreeRamInBytes();
if(iRamCache)
free += iRamCache->NumberOfFreePages()<<iPageShift;
return free;
}
/** Returns the amount of free RAM currently available.
@return The number of bytes of free RAM currently available.
@pre any context
*/
EXPORT_C TInt Kern::FreeRamInBytes()
{
return MmuBase::TheMmu->FreeRamInBytes();
}
/** Rounds up the argument to the size of a MMU page.
To find out the size of a MMU page:
@code
size = Kern::RoundToPageSize(1);
@endcode
@param aSize Value to round up
@pre any context
*/
EXPORT_C TUint32 Kern::RoundToPageSize(TUint32 aSize)
{
return MmuBase::RoundToPageSize(aSize);
}
/** Rounds up the argument to the amount of memory mapped by a MMU page
directory entry.
Chunks occupy one or more consecutive page directory entries (PDE) and
therefore the amount of linear and physical memory allocated to a chunk is
always a multiple of the amount of memory mapped by a page directory entry.
*/
EXPORT_C TUint32 Kern::RoundToChunkSize(TUint32 aSize)
{
return MmuBase::RoundToChunkSize(aSize);
}
/**
Allows the variant to specify the details of the RAM zones. This should be invoked
by the variant in its implementation of the pure virtual function Asic::Init1().
There are some limitations to how the RAM zones can be specified:
- Each RAM zone's address space must be distinct and not overlap with any
other RAM zone's address space
- Each RAM zone's address space must have a size that is multiples of the
ASIC's MMU small page size and be aligned to the ASIC's MMU small page size,
usually 4KB on ARM MMUs.
- When taken together all of the RAM zones must cover the whole of the physical RAM
address space as specified by the bootstrap in the SuperPage members iTotalRamSize
and iRamBootData;.
- There can be no more than KMaxRamZones RAM zones specified by the base port
Note the verification of the RAM zone data is not performed here but by the ram
allocator later in the boot up sequence. This is because it is only possible to
verify the zone data once the physical RAM configuration has been read from
the super page. Any verification errors result in a "RAM-ALLOC" panic
faulting the kernel during initialisation.
@param aZones Pointer to an array of SRamZone structs containing the details for all
the zones. The end of the array is specified by an element with an iSize of zero. The array must
remain in memory at least until the kernel has successfully booted.
@param aCallback Pointer to a call back function that the kernel may invoke to request
one of the operations specified by TRamZoneOp.
@return KErrNone if successful, otherwise one of the system wide error codes
@see TRamZoneOp
@see SRamZone
@see TRamZoneCallback
*/
EXPORT_C TInt Epoc::SetRamZoneConfig(const SRamZone* aZones, TRamZoneCallback aCallback)
{
// Ensure this is only called once and only while we are initialising the kernel
if (!K::Initialising || MmuBase::RamZoneConfig != NULL)
{// fault kernel, won't return
K::Fault(K::EBadSetRamZoneConfig);
}
if (NULL == aZones)
{
return KErrArgument;
}
MmuBase::RamZoneConfig=aZones;
MmuBase::RamZoneCallback=aCallback;
return KErrNone;
}
/**
Modify the specified RAM zone's flags.
This allows the BSP or device driver to configure which type of pages, if any,
can be allocated into a RAM zone by the system.
Note: updating a RAM zone's flags can result in
1 - memory allocations failing despite there being enough free RAM in the system.
2 - the methods TRamDefragRequest::EmptyRamZone(), TRamDefragRequest::ClaimRamZone()
or TRamDefragRequest::DefragRam() never succeeding.
The flag masks KRamZoneFlagDiscardOnly, KRamZoneFlagMovAndDisOnly and KRamZoneFlagNoAlloc
are intended to be used with this method.
@param aId The ID of the RAM zone to modify.
@param aClearMask The bit mask to clear, each flag of which must already be set on the RAM zone.
@param aSetMask The bit mask to set.
@return KErrNone on success, KErrArgument if the RAM zone of aId not found or if
aSetMask contains invalid flag bits.
@see TRamDefragRequest::EmptyRamZone()
@see TRamDefragRequest::ClaimRamZone()
@see TRamDefragRequest::DefragRam()
@see KRamZoneFlagDiscardOnly
@see KRamZoneFlagMovAndDisOnly
@see KRamZoneFlagNoAlloc
*/
EXPORT_C TInt Epoc::ModifyRamZoneFlags(TUint aId, TUint aClearMask, TUint aSetMask)
{
MmuBase& m = *MmuBase::TheMmu;
MmuBase::Wait();
TInt ret = m.ModifyRamZoneFlags(aId, aClearMask, aSetMask);
MmuBase::Signal();
return ret;
}
TInt MmuBase::ModifyRamZoneFlags(TUint aId, TUint aClearMask, TUint aSetMask)
{
return iRamPageAllocator->ModifyZoneFlags(aId, aClearMask, aSetMask);
}
/**
Gets the current count of a particular RAM zone's pages by type.
@param aId The ID of the RAM zone to enquire about
@param aPageData If successful, on return this contains the page count
@return KErrNone if successful, KErrArgument if a RAM zone of aId is not found or
one of the system wide error codes
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@see SRamZonePageCount
*/
EXPORT_C TInt Epoc::GetRamZonePageCount(TUint aId, SRamZonePageCount& aPageData)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::GetRamZonePageCount");
MmuBase& m = *MmuBase::TheMmu;
MmuBase::Wait(); // Gets RAM alloc mutex
TInt r = m.GetRamZonePageCount(aId, aPageData);
MmuBase::Signal();
return r;
}
TInt MmuBase::GetRamZonePageCount(TUint aId, SRamZonePageCount& aPageData)
{
return iRamPageAllocator->GetZonePageCount(aId, aPageData);
}
/**
Replace a page of the system's execute-in-place (XIP) ROM image with a page of
RAM having the same contents. This RAM can subsequently be written to in order
to apply patches to the XIP ROM or to insert software breakpoints for debugging
purposes.
Call Epoc::FreeShadowPage() when you wish to revert to the original ROM page.
@param aRomAddr The virtual address of the ROM page to be replaced.
@return KErrNone if the operation completed successfully.
KErrArgument if the specified address is not a valid XIP ROM address.
KErrNoMemory if the operation failed due to insufficient free RAM.
KErrAlreadyExists if the XIP ROM page at the specified address has
already been shadowed by a RAM page.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
*/
EXPORT_C TInt Epoc::AllocShadowPage(TLinAddr aRomAddr)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::AllocShadowPage");
TInt r;
r=M::LockRegion(aRomAddr,1);
if(r!=KErrNone && r!=KErrNotFound)
return r;
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
r=m.AllocShadowPage(aRomAddr);
MmuBase::Signal();
if(r!=KErrNone)
M::UnlockRegion(aRomAddr,1);
return r;
}
/**
Copies data into shadow memory. Source data is presumed to be in Kernel memory.
@param aSrc Data to copy from.
@param aDest Address to copy into.
@param aLength Number of bytes to copy. Maximum of 32 bytes of data can be copied.
.
@return KErrNone if the operation completed successfully.
KErrArgument if any part of destination region is not shadow page or
if aLength is greater then 32 bytes.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
*/
EXPORT_C TInt Epoc::CopyToShadowMemory(TLinAddr aDest, TLinAddr aSrc, TUint32 aLength)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::CopyToShadowMemory");
if (aLength>32)
return KErrArgument;
MmuBase& m=*MmuBase::TheMmu;
// This is a simple copy operation except on platforms with __CPU_MEMORY_TYPE_REMAPPING defined,
// where shadow page is read-only and it has to be remapped before it is written into.
return m.CopyToShadowMemory(aDest, aSrc, aLength);
}
/**
Revert an XIP ROM address which has previously been shadowed to the original
page of ROM.
@param aRomAddr The virtual address of the ROM page to be reverted.
@return KErrNone if the operation completed successfully.
KErrArgument if the specified address is not a valid XIP ROM address.
KErrGeneral if the specified address has not previously been shadowed
using Epoc::AllocShadowPage().
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
*/
EXPORT_C TInt Epoc::FreeShadowPage(TLinAddr aRomAddr)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreeShadowPage");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.FreeShadowPage(aRomAddr);
MmuBase::Signal();
if(r==KErrNone)
M::UnlockRegion(aRomAddr,1);
return r;
}
/**
Change the permissions on an XIP ROM address which has previously been shadowed
by a RAM page so that the RAM page may no longer be written to.
Note: Shadow page on the latest platforms (that use the reduced set of access permissions:
arm11mpcore, arm1176, cortex) is implemented with read only permissions. Therefore, calling
this function in not necessary, as shadow page is already created as 'frozen'.
@param aRomAddr The virtual address of the shadow RAM page to be frozen.
@return KErrNone if the operation completed successfully.
KErrArgument if the specified address is not a valid XIP ROM address.
KErrGeneral if the specified address has not previously been shadowed
using Epoc::AllocShadowPage().
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
*/
EXPORT_C TInt Epoc::FreezeShadowPage(TLinAddr aRomAddr)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreezeShadowPage");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.FreezeShadowPage(aRomAddr);
MmuBase::Signal();
return r;
}
/**
Allocate a block of physically contiguous RAM with a physical address aligned
to a specified power of 2 boundary.
When the RAM is no longer required it should be freed using
Epoc::FreePhysicalRam()
@param aSize The size in bytes of the required block. The specified size
is rounded up to the page size, since only whole pages of
physical RAM can be allocated.
@param aPhysAddr Receives the physical address of the base of the block on
successful allocation.
@param aAlign Specifies the number of least significant bits of the
physical address which are required to be zero. If a value
less than log2(page size) is specified, page alignment is
assumed. Pass 0 for aAlign if there are no special alignment
constraints (other than page alignment).
@return KErrNone if the allocation was successful.
KErrNoMemory if a sufficiently large physically contiguous block of free
RAM with the specified alignment could not be found.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::AllocPhysicalRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::AllocPhysicalRam");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.AllocPhysicalRam(aSize,aPhysAddr,aAlign);
if (r == KErrNone)
{
// For the sake of platform security we have to clear the memory. E.g. the driver
// could assign it to a chunk visible to user side.
m.ClearPages(Kern::RoundToPageSize(aSize)>>m.iPageShift, (TPhysAddr*)(aPhysAddr|1));
#ifdef BTRACE_KERNEL_MEMORY
TUint size = Kern::RoundToPageSize(aSize);
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, size, aPhysAddr);
Epoc::DriverAllocdPhysRam += size;
#endif
}
MmuBase::Signal();
return r;
}
/**
Allocate a block of physically contiguous RAM with a physical address aligned
to a specified power of 2 boundary from the specified zone.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
Note that this method only repsects the KRamZoneFlagNoAlloc flag and will always attempt
to allocate regardless of whether the other flags are set for the specified RAM zones
or not.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
@param aZoneId The ID of the zone to attempt to allocate from.
@param aSize The size in bytes of the required block. The specified size
is rounded up to the page size, since only whole pages of
physical RAM can be allocated.
@param aPhysAddr Receives the physical address of the base of the block on
successful allocation.
@param aAlign Specifies the number of least significant bits of the
physical address which are required to be zero. If a value
less than log2(page size) is specified, page alignment is
assumed. Pass 0 for aAlign if there are no special alignment
constraints (other than page alignment).
@return KErrNone if the allocation was successful.
KErrNoMemory if a sufficiently large physically contiguous block of free
RAM with the specified alignment could not be found within the specified
zone.
KErrArgument if a RAM zone of the specified ID can't be found or if the
RAM zone has a total number of physical pages which is less than those
requested for the allocation.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint aZoneId, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
return ZoneAllocPhysicalRam(&aZoneId, 1, aSize, aPhysAddr, aAlign);
}
/**
Allocate a block of physically contiguous RAM with a physical address aligned
to a specified power of 2 boundary from the specified RAM zones.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
RAM will be allocated into the RAM zones in the order they are specified in the
aZoneIdList parameter. If the contiguous allocations are intended to span RAM zones
when required then aZoneIdList should be listed with the RAM zones in ascending
physical address order.
Note that this method only repsects the KRamZoneFlagNoAlloc flag and will always attempt
to allocate regardless of whether the other flags are set for the specified RAM zones
or not.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
@param aZoneIdList A pointer to an array of RAM zone IDs of the RAM zones to
attempt to allocate from.
@param aZoneIdCount The number of RAM zone IDs contained in aZoneIdList.
@param aSize The size in bytes of the required block. The specified size
is rounded up to the page size, since only whole pages of
physical RAM can be allocated.
@param aPhysAddr Receives the physical address of the base of the block on
successful allocation.
@param aAlign Specifies the number of least significant bits of the
physical address which are required to be zero. If a value
less than log2(page size) is specified, page alignment is
assumed. Pass 0 for aAlign if there are no special alignment
constraints (other than page alignment).
@return KErrNone if the allocation was successful.
KErrNoMemory if a sufficiently large physically contiguous block of free
RAM with the specified alignment could not be found within the specified
zone.
KErrArgument if a RAM zone of a specified ID can't be found or if the
RAM zones have a total number of physical pages which is less than those
requested for the allocation.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::ZoneAllocPhysicalRam");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r = m.ZoneAllocPhysicalRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign);
if (r == KErrNone)
{
// For the sake of platform security we have to clear the memory. E.g. the driver
// could assign it to a chunk visible to user side.
m.ClearPages(Kern::RoundToPageSize(aSize)>>m.iPageShift, (TPhysAddr*)(aPhysAddr|1));
#ifdef BTRACE_KERNEL_MEMORY
TUint size = Kern::RoundToPageSize(aSize);
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, size, aPhysAddr);
Epoc::DriverAllocdPhysRam += size;
#endif
}
MmuBase::Signal();
return r;
}
/**
Attempt to allocate discontiguous RAM pages.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
@param aNumPages The number of discontiguous pages required to be allocated
@param aPageList This should be a pointer to a previously allocated array of
aNumPages TPhysAddr elements. On a succesful allocation it
will receive the physical addresses of each page allocated.
@return KErrNone if the allocation was successful.
KErrNoMemory if the requested number of pages can't be allocated
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::AllocPhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "Epoc::AllocPhysicalRam");
MmuBase& m = *MmuBase::TheMmu;
MmuBase::Wait();
TInt r = m.AllocPhysicalRam(aNumPages, aPageList);
if (r == KErrNone)
{
// For the sake of platform security we have to clear the memory. E.g. the driver
// could assign it to a chunk visible to user side.
m.ClearPages(aNumPages, aPageList);
#ifdef BTRACE_KERNEL_MEMORY
if (BTrace::CheckFilter(BTrace::EKernelMemory))
{// Only loop round each page if EKernelMemory tracing is enabled
TPhysAddr* pAddr = aPageList;
TPhysAddr* pAddrEnd = aPageList + aNumPages;
while (pAddr < pAddrEnd)
{
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, KPageSize, *pAddr++);
Epoc::DriverAllocdPhysRam += KPageSize;
}
}
#endif
}
MmuBase::Signal();
return r;
}
/**
Attempt to allocate discontiguous RAM pages from the specified zone.
Note that this method only repsects the KRamZoneFlagNoAlloc flag and will always attempt
to allocate regardless of whether the other flags are set for the specified RAM zones
or not.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
@param aZoneId The ID of the zone to attempt to allocate from.
@param aNumPages The number of discontiguous pages required to be allocated
from the specified zone.
@param aPageList This should be a pointer to a previously allocated array of
aNumPages TPhysAddr elements. On a succesful
allocation it will receive the physical addresses of each
page allocated.
@return KErrNone if the allocation was successful.
KErrNoMemory if the requested number of pages can't be allocated from the
specified zone.
KErrArgument if a RAM zone of the specified ID can't be found or if the
RAM zone has a total number of physical pages which is less than those
requested for the allocation.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint aZoneId, TInt aNumPages, TPhysAddr* aPageList)
{
return ZoneAllocPhysicalRam(&aZoneId, 1, aNumPages, aPageList);
}
/**
Attempt to allocate discontiguous RAM pages from the specified RAM zones.
The RAM pages will be allocated into the RAM zones in the order that they are specified
in the aZoneIdList parameter, the RAM zone preferences will be ignored.
Note that this method only repsects the KRamZoneFlagNoAlloc flag and will always attempt
to allocate regardless of whether the other flags are set for the specified RAM zones
or not.
When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
@param aZoneIdList A pointer to an array of RAM zone IDs of the RAM zones to
attempt to allocate from.
@param aZoneIdCount The number of RAM zone IDs pointed to by aZoneIdList.
@param aNumPages The number of discontiguous pages required to be allocated
from the specified zone.
@param aPageList This should be a pointer to a previously allocated array of
aNumPages TPhysAddr elements. On a succesful
allocation it will receive the physical addresses of each
page allocated.
@return KErrNone if the allocation was successful.
KErrNoMemory if the requested number of pages can't be allocated from the
specified zone.
KErrArgument if a RAM zone of a specified ID can't be found or if the
RAM zones have a total number of physical pages which is less than those
requested for the allocation.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aNumPages, TPhysAddr* aPageList)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "Epoc::ZoneAllocPhysicalRam");
MmuBase& m = *MmuBase::TheMmu;
MmuBase::Wait();
TInt r = m.ZoneAllocPhysicalRam(aZoneIdList, aZoneIdCount, aNumPages, aPageList);
if (r == KErrNone)
{
// For the sake of platform security we have to clear the memory. E.g. the driver
// could assign it to a chunk visible to user side.
m.ClearPages(aNumPages, aPageList);
#ifdef BTRACE_KERNEL_MEMORY
if (BTrace::CheckFilter(BTrace::EKernelMemory))
{// Only loop round each page if EKernelMemory tracing is enabled
TPhysAddr* pAddr = aPageList;
TPhysAddr* pAddrEnd = aPageList + aNumPages;
while (pAddr < pAddrEnd)
{
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, KPageSize, *pAddr++);
Epoc::DriverAllocdPhysRam += KPageSize;
}
}
#endif
}
MmuBase::Signal();
return r;
}
/**
Free a previously-allocated block of physically contiguous RAM.
Specifying one of the following may cause the system to panic:
a) an invalid physical RAM address.
b) valid physical RAM addresses where some had not been previously allocated.
c) an adrress not aligned to a page boundary.
@param aPhysAddr The physical address of the base of the block to be freed.
This must be the address returned by a previous call to
Epoc::AllocPhysicalRam(), Epoc::ZoneAllocPhysicalRam(),
Epoc::ClaimPhysicalRam() or Epoc::ClaimRamZone().
@param aSize The size in bytes of the required block. The specified size
is rounded up to the page size, since only whole pages of
physical RAM can be allocated.
@return KErrNone if the operation was successful.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::FreePhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreePhysicalRam");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.FreePhysicalRam(aPhysAddr,aSize);
#ifdef BTRACE_KERNEL_MEMORY
if (r == KErrNone)
{
TUint size = Kern::RoundToPageSize(aSize);
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysFree, size, aPhysAddr);
Epoc::DriverAllocdPhysRam -= size;
}
#endif
MmuBase::Signal();
return r;
}
/**
Free a number of physical RAM pages that were previously allocated using
Epoc::AllocPhysicalRam() or Epoc::ZoneAllocPhysicalRam().
Specifying one of the following may cause the system to panic:
a) an invalid physical RAM address.
b) valid physical RAM addresses where some had not been previously allocated.
c) an adrress not aligned to a page boundary.
@param aNumPages The number of pages to be freed.
@param aPhysAddr An array of aNumPages TPhysAddr elements. Where each element
should contain the physical address of each page to be freed.
This must be the same set of addresses as those returned by a
previous call to Epoc::AllocPhysicalRam() or
Epoc::ZoneAllocPhysicalRam().
@return KErrNone if the operation was successful.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::FreePhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreePhysicalRam");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.FreePhysicalRam(aNumPages, aPageList);
#ifdef BTRACE_KERNEL_MEMORY
if (r == KErrNone && BTrace::CheckFilter(BTrace::EKernelMemory))
{// Only loop round each page if EKernelMemory tracing is enabled
TPhysAddr* pAddr = aPageList;
TPhysAddr* pAddrEnd = aPageList + aNumPages;
while (pAddr < pAddrEnd)
{
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysFree, KPageSize, *pAddr++);
Epoc::DriverAllocdPhysRam -= KPageSize;
}
}
#endif
MmuBase::Signal();
return r;
}
/**
Allocate a specific block of physically contiguous RAM, specified by physical
base address and size.
If and when the RAM is no longer required it should be freed using
Epoc::FreePhysicalRam()
@param aPhysAddr The physical address of the base of the required block.
@param aSize The size in bytes of the required block. The specified size
is rounded up to the page size, since only whole pages of
physical RAM can be allocated.
@return KErrNone if the operation was successful.
KErrArgument if the range of physical addresses specified included some
which are not valid physical RAM addresses.
KErrInUse if the range of physical addresses specified are all valid
physical RAM addresses but some of them have already been
allocated for other purposes.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::ClaimPhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::ClaimPhysicalRam");
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
TInt r=m.ClaimPhysicalRam(aPhysAddr,aSize);
#ifdef BTRACE_KERNEL_MEMORY
if(r==KErrNone)
{
TUint32 pa=aPhysAddr;
TUint32 size=aSize;
m.RoundUpRangeToPageSize(pa,size);
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, size, pa);
Epoc::DriverAllocdPhysRam += size;
}
#endif
MmuBase::Signal();
return r;
}
/**
Free a RAM zone which was previously allocated by one of these methods:
Epoc::AllocPhysicalRam(), Epoc::ZoneAllocPhysicalRam() or
TRamDefragRequest::ClaimRamZone().
All of the pages in the RAM zone must be allocated and only via one of the methods
listed above, otherwise a system panic will occur.
@param aZoneId The ID of the RAM zone to free.
@return KErrNone If the operation was successful.
KErrArgument If a RAM zone with ID aZoneId was not found.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
*/
EXPORT_C TInt Epoc::FreeRamZone(TUint aZoneId)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreeRamZone");
MmuBase& m = *MmuBase::TheMmu;
MmuBase::Wait();
TPhysAddr zoneBase;
TUint zoneBytes;
TInt r = m.FreeRamZone(aZoneId, zoneBase, zoneBytes);
#ifdef BTRACE_KERNEL_MEMORY
if (r == KErrNone)
{
BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysFree, zoneBytes, zoneBase);
Epoc::DriverAllocdPhysRam -= zoneBytes;
}
#endif
MmuBase::Signal();
return r;
}
/**
Translate a virtual address to the corresponding physical address.
@param aLinAddr The virtual address to be translated.
@return The physical address corresponding to the given virtual address, or
KPhysAddrInvalid if the specified virtual address is unmapped.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre Call in a thread context.
@pre Can be used in a device driver.
@pre Hold system lock if there is any possibility that the virtual address is
unmapped, may become unmapped, or may be remapped during the operation.
This will potentially be the case unless the virtual address refers to a
hardware chunk or shared chunk under the control of the driver calling this
function.
*/
EXPORT_C TPhysAddr Epoc::LinearToPhysical(TLinAddr aLinAddr)
{
// This precondition is violated by various parts of the system under some conditions,
// e.g. when __FLUSH_PT_INTO_RAM__ is defined. This function might also be called by
// a higher-level RTOS for which these conditions are meaningless. Thus, it's been
// disabled for now.
// CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"Epoc::LinearToPhysical");
MmuBase& m=*MmuBase::TheMmu;
TPhysAddr pa=m.LinearToPhysical(aLinAddr);
return pa;
}
EXPORT_C TInt TInternalRamDrive::MaxSize()
{
return TheSuperPage().iRamDriveSize+Kern::FreeRamInBytes();
}
/******************************************************************************
* Address allocator
******************************************************************************/
TLinearSection* TLinearSection::New(TLinAddr aBase, TLinAddr aEnd)
{
__KTRACE_OPT(KMMU,Kern::Printf("TLinearSection::New(%08x,%08x)", aBase, aEnd));
MmuBase& m=*MmuBase::TheMmu;
TUint npdes=(aEnd-aBase)>>m.iChunkShift;
TInt nmapw=(npdes+31)>>5;
TInt memsz=sizeof(TLinearSection)+(nmapw-1)*sizeof(TUint32);
TLinearSection* p=(TLinearSection*)Kern::Alloc(memsz);
if (p)
{
new(&p->iAllocator) TBitMapAllocator(npdes, ETrue);
p->iBase=aBase;
p->iEnd=aEnd;
}
__KTRACE_OPT(KMMU,Kern::Printf("TLinearSection at %08x", p));
return p;
}
/******************************************************************************
* Address allocator for HW chunks
******************************************************************************/
THwChunkPageTable::THwChunkPageTable(TInt aIndex, TInt aSize, TPde aPdePerm)
: THwChunkRegion(aIndex, 0, aPdePerm),
iAllocator(aSize, ETrue)
{
}
THwChunkPageTable* THwChunkPageTable::New(TInt aIndex, TPde aPdePerm)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChunkPageTable::New(%03x,%08x)",aIndex,aPdePerm));
MmuBase& m=*MmuBase::TheMmu;
TInt pdepages=m.iChunkSize>>m.iPageShift;
TInt nmapw=(pdepages+31)>>5;
TInt memsz=sizeof(THwChunkPageTable)+(nmapw-1)*sizeof(TUint32);
THwChunkPageTable* p=(THwChunkPageTable*)Kern::Alloc(memsz);
if (p)
new (p) THwChunkPageTable(aIndex, pdepages, aPdePerm);
__KTRACE_OPT(KMMU, Kern::Printf("THwChunkPageTable at %08x",p));
return p;
}
THwChunkAddressAllocator::THwChunkAddressAllocator()
{
}
THwChunkAddressAllocator* THwChunkAddressAllocator::New(TInt aAlign, TLinearSection* aSection)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChunkAddressAllocator::New(%d,%08x)",aAlign,aSection));
THwChunkAddressAllocator* p=new THwChunkAddressAllocator;
if (p)
{
p->iAlign=aAlign;
p->iSection=aSection;
}
__KTRACE_OPT(KMMU, Kern::Printf("THwChunkAddressAllocator at %08x",p));
return p;
}
THwChunkRegion* THwChunkAddressAllocator::NewRegion(TInt aIndex, TInt aSize, TPde aPdePerm)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::NewRegion(index=%x, size=%x, pde=%08x)",aIndex,aSize,aPdePerm));
THwChunkRegion* p=new THwChunkRegion(aIndex, aSize, aPdePerm);
if (p)
{
TInt r=InsertInOrder(p, Order);
__KTRACE_OPT(KMMU, Kern::Printf("p=%08x, insert ret %d",p,r));
if (r<0)
delete p, p=NULL;
}
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::NewRegion ret %08x)",p));
return p;
}
THwChunkPageTable* THwChunkAddressAllocator::NewPageTable(TInt aIndex, TPde aPdePerm, TInt aInitB, TInt aInitC)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::NewPageTable(index=%x, pde=%08x, iB=%d, iC=%d)",aIndex,aPdePerm,aInitB,aInitC));
THwChunkPageTable* p=THwChunkPageTable::New(aIndex, aPdePerm);
if (p)
{
TInt r=InsertInOrder(p, Order);
__KTRACE_OPT(KMMU, Kern::Printf("p=%08x, insert ret %d",p,r));
if (r<0)
delete p, p=NULL;
else
p->iAllocator.Alloc(aInitB, aInitC);
}
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::NewPageTable ret %08x)",p));
return p;
}
TLinAddr THwChunkAddressAllocator::SearchExisting(TInt aNumPages, TInt aPageAlign, TInt aPageOffset, TPde aPdePerm)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::SrchEx np=%03x align=%d offset=%03x pdeperm=%08x",
aNumPages, aPageAlign, aPageOffset, aPdePerm));
TInt c=Count();
if (c==0)
return 0; // don't try to access [0] if array empty!
THwChunkPageTable** pp=(THwChunkPageTable**)&(*this)[0];
THwChunkPageTable** ppE=pp+c;
while(pp<ppE)
{
THwChunkPageTable* p=*pp++;
if (p->iRegionSize!=0 || p->iPdePerm!=aPdePerm)
continue; // if not page table or PDE permissions wrong, we can't use it
TInt r=p->iAllocator.AllocAligned(aNumPages, aPageAlign, -aPageOffset, EFalse);
__KTRACE_OPT(KMMU, Kern::Printf("r=%d", r));
if (r<0)
continue; // not enough space in this page table
// got enough space in existing page table, so use it
p->iAllocator.Alloc(r, aNumPages);
MmuBase& m=*MmuBase::TheMmu;
TLinAddr a = iSection->iBase + (TLinAddr(p->iIndex)<<m.iChunkShift) + (r<<m.iPageShift);
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::SrchEx OK, returning %08x", a));
return a;
}
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::SrchEx not found"));
return 0;
}
TLinAddr THwChunkAddressAllocator::Alloc(TInt aSize, TInt aAlign, TInt aOffset, TPde aPdePerm)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::Alloc size=%08x align=%d offset=%08x pdeperm=%08x",
aSize, aAlign, aOffset, aPdePerm));
MmuBase& m=*MmuBase::TheMmu;
TInt npages=(aSize+m.iPageMask)>>m.iPageShift;
TInt align=Max(aAlign,iAlign);
if (align>m.iChunkShift)
return 0;
TInt aligns=1<<align;
TInt alignm=aligns-1;
TInt offset=(aOffset&alignm)>>m.iPageShift;
TInt pdepages=m.iChunkSize>>m.iPageShift;
TInt pdepageshift=m.iChunkShift-m.iPageShift;
MmuBase::WaitHwChunk();
if (npages<pdepages)
{
// for small regions, first try to share an existing page table
TLinAddr a=SearchExisting(npages, align-m.iPageShift, offset, aPdePerm);
if (a)
{
MmuBase::SignalHwChunk();
return a;
}
}
// large region or no free space in existing page tables - allocate whole PDEs
TInt npdes=(npages+offset+pdepages-1)>>pdepageshift;
__KTRACE_OPT(KMMU, Kern::Printf("Allocate %d PDEs", npdes));
MmuBase::Wait();
TInt ix=iSection->iAllocator.AllocConsecutive(npdes, EFalse);
if (ix>=0)
iSection->iAllocator.Alloc(ix, npdes);
MmuBase::Signal();
TLinAddr a=0;
if (ix>=0)
a = iSection->iBase + (TLinAddr(ix)<<m.iChunkShift) + (TLinAddr(offset)<<m.iPageShift);
// Create bitmaps for each page table and placeholders for section blocks.
// We only create a bitmap for the first and last PDE and then only if they are not
// fully occupied by this request
THwChunkPageTable* first=NULL;
THwChunkRegion* middle=NULL;
TInt remain=npages;
TInt nix=ix;
if (a && (offset || npages<pdepages))
{
// first PDE is bitmap
TInt first_count = Min(remain, pdepages-offset);
first=NewPageTable(nix, aPdePerm, offset, first_count);
++nix;
remain -= first_count;
if (!first)
a=0;
}
if (a && remain>=pdepages)
{
// next need whole-PDE-block placeholder
TInt whole_pdes=remain>>pdepageshift;
middle=NewRegion(nix, whole_pdes, aPdePerm);
nix+=whole_pdes;
remain-=(whole_pdes<<pdepageshift);
if (!middle)
a=0;
}
if (a && remain)
{
// need final bitmap section
if (!NewPageTable(nix, aPdePerm, 0, remain))
a=0;
}
if (!a)
{
// alloc failed somewhere - free anything we did create
if (middle)
Discard(middle);
if (first)
Discard(first);
if (ix>=0)
{
MmuBase::Wait();
iSection->iAllocator.Free(ix, npdes);
MmuBase::Signal();
}
}
MmuBase::SignalHwChunk();
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::Alloc returns %08x", a));
return a;
}
void THwChunkAddressAllocator::Discard(THwChunkRegion* aRegion)
{
// remove a region from the array and destroy it
TInt r=FindInOrder(aRegion, Order);
if (r>=0)
Remove(r);
Kern::Free(aRegion);
}
TInt THwChunkAddressAllocator::Order(const THwChunkRegion& a1, const THwChunkRegion& a2)
{
// order two regions by address
return a1.iIndex-a2.iIndex;
}
THwChunkRegion* THwChunkAddressAllocator::Free(TLinAddr aAddr, TInt aSize)
{
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::Free addr=%08x size=%08x", aAddr, aSize));
__ASSERT_ALWAYS(aAddr>=iSection->iBase && (aAddr+aSize)<=iSection->iEnd,
MmuBase::Panic(MmuBase::EFreeHwChunkAddrInvalid));
THwChunkRegion* list=NULL;
MmuBase& m=*MmuBase::TheMmu;
TInt ix=(aAddr - iSection->iBase)>>m.iChunkShift;
TInt remain=(aSize+m.iPageMask)>>m.iPageShift;
TInt pdepageshift=m.iChunkShift-m.iPageShift;
TInt offset=(aAddr&m.iChunkMask)>>m.iPageShift;
THwChunkRegion find(ix, 0, 0);
MmuBase::WaitHwChunk();
TInt r=FindInOrder(&find, Order);
__ASSERT_ALWAYS(r>=0, MmuBase::Panic(MmuBase::EFreeHwChunkAddrInvalid));
while (remain)
{
THwChunkPageTable* p=(THwChunkPageTable*)(*this)[r];
__ASSERT_ALWAYS(p->iIndex==ix, MmuBase::Panic(MmuBase::EFreeHwChunkIndexInvalid));
if (p->iRegionSize)
{
// multiple-whole-PDE region
TInt rsz=p->iRegionSize;
remain-=(rsz<<pdepageshift);
Remove(r); // r now indexes following array entry
ix+=rsz;
}
else
{
// bitmap region
TInt n=Min(remain, (1<<pdepageshift)-offset);
p->iAllocator.Free(offset, n);
remain-=n;
++ix;
if (p->iAllocator.iAvail < p->iAllocator.iSize)
{
// bitmap still in use
offset=0;
++r; // r indexes following array entry
continue;
}
Remove(r); // r now indexes following array entry
}
offset=0;
p->iNext=list;
list=p; // chain free region descriptors together
}
MmuBase::SignalHwChunk();
__KTRACE_OPT(KMMU, Kern::Printf("THwChAA::Free returns %08x", list));
return list;
}
/********************************************
* Hardware chunk abstraction
********************************************/
THwChunkAddressAllocator* MmuBase::MappingRegion(TUint)
{
return iHwChunkAllocator;
}
TInt MmuBase::AllocateAllPageTables(TLinAddr aLinAddr, TInt aSize, TPde aPdePerm, TInt aMapShift, SPageTableInfo::TAttribs aAttrib)
{
__KTRACE_OPT(KMMU,Kern::Printf("AllocateAllPageTables lin=%08x, size=%x, pde=%08x, mapshift=%d attribs=%d",
aLinAddr, aSize, aPdePerm, aMapShift, aAttrib));
TInt offset=aLinAddr&iChunkMask;
TInt remain=aSize;
TLinAddr a=aLinAddr&~iChunkMask;
TInt newpts=0;
for (; remain>0; a+=iChunkSize)
{
// don't need page table if a whole PDE mapping is permitted here
if (aMapShift<iChunkShift || offset || remain<iChunkSize)
{
// need to check for a page table at a
TInt id=PageTableId(a);
if (id<0)
{
// no page table - must allocate one
id = AllocPageTable();
if (id<0)
break;
// got page table, assign it
// AssignPageTable(TInt aId, TInt aUsage, TAny* aObject, TLinAddr aAddr, TPde aPdePerm)
AssignPageTable(id, aAttrib, NULL, a, aPdePerm);
++newpts;
}
}
remain -= (iChunkSize-offset);
offset=0;
}
if (remain<=0)
return KErrNone; // completed OK
// ran out of memory somewhere - free page tables which were allocated
for (; newpts; --newpts)
{
a-=iChunkSize;
TInt id=UnassignPageTable(a);
FreePageTable(id);
}
return KErrNoMemory;
}
/**
Create a hardware chunk object mapping a specified block of physical addresses
with specified access permissions and cache policy.
When the mapping is no longer required, close the chunk using chunk->Close(0);
Note that closing a chunk does not free any RAM pages which were mapped by the
chunk - these must be freed separately using Epoc::FreePhysicalRam().
@param aChunk Upon successful completion this parameter receives a pointer to
the newly created chunk. Upon unsuccessful completion it is
written with a NULL pointer. The virtual address of the mapping
can subsequently be discovered using the LinearAddress()
function on the chunk.
@param aAddr The base address of the physical region to be mapped. This will
be rounded down to a multiple of the hardware page size before
being used.
@param aSize The size of the physical address region to be mapped. This will
be rounded up to a multiple of the hardware page size before
being used; the rounding is such that the entire range from
aAddr to aAddr+aSize-1 inclusive is mapped. For example if
aAddr=0xB0001FFF, aSize=2 and the hardware page size is 4KB, an
8KB range of physical addresses from 0xB0001000 to 0xB0002FFF
inclusive will be mapped.
@param aMapAttr Mapping attributes required for the mapping. This is formed
by ORing together values from the TMappingAttributes enumeration
to specify the access permissions and caching policy.
@pre Calling thread must be in a critical section.
@pre Interrupts must be enabled.
@pre Kernel must be unlocked.
@pre No fast mutex can be held.
@pre Call in a thread context.
@pre Can be used in a device driver.
@see TMappingAttributes
*/
EXPORT_C TInt DPlatChunkHw::New(DPlatChunkHw*& aChunk, TPhysAddr aAddr, TInt aSize, TUint aMapAttr)
{
if (aAddr == KPhysAddrInvalid)
return KErrNotSupported;
return DoNew(aChunk, aAddr, aSize, aMapAttr);
}
TInt DPlatChunkHw::DoNew(DPlatChunkHw*& aChunk, TPhysAddr aAddr, TInt aSize, TUint aMapAttr)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"DPlatChunkHw::New");
__KTRACE_OPT(KMMU,Kern::Printf("DPlatChunkHw::New phys=%08x, size=%x, attribs=%x",aAddr,aSize,aMapAttr));
if (aSize<=0)
return KErrArgument;
MmuBase& m=*MmuBase::TheMmu;
aChunk=NULL;
TPhysAddr pa=aAddr!=KPhysAddrInvalid ? aAddr&~m.iPageMask : 0;
TInt size=((aAddr+aSize+m.iPageMask)&~m.iPageMask)-pa;
__KTRACE_OPT(KMMU,Kern::Printf("Rounded %08x+%x", pa, size));
DMemModelChunkHw* pC=new DMemModelChunkHw;
if (!pC)
return KErrNoMemory;
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunkHw created at %08x",pC));
pC->iPhysAddr=aAddr;
pC->iSize=size;
TUint mapattr=aMapAttr;
TPde pdePerm=0;
TPte ptePerm=0;
TInt r=m.PdePtePermissions(mapattr, pdePerm, ptePerm);
if (r==KErrNone)
{
pC->iAllocator=m.MappingRegion(mapattr);
pC->iAttribs=mapattr; // save actual mapping attributes
r=pC->AllocateLinearAddress(pdePerm);
if (r>=0)
{
TInt map_shift=r;
MmuBase::Wait();
r=m.AllocateAllPageTables(pC->iLinAddr, size, pdePerm, map_shift, SPageTableInfo::EGlobal);
if (r==KErrNone && aAddr!=KPhysAddrInvalid)
m.Map(pC->iLinAddr, pa, size, pdePerm, ptePerm, map_shift);
MmuBase::Signal();
}
}
if (r==KErrNone)
aChunk=pC;
else
pC->Close(NULL);
return r;
}
TInt DMemModelChunkHw::AllocateLinearAddress(TPde aPdePerm)
{
__KTRACE_OPT(KMMU, Kern::Printf("DMemModelChunkHw::AllocateLinearAddress(%08x)", aPdePerm));
__KTRACE_OPT(KMMU, Kern::Printf("iAllocator=%08x iPhysAddr=%08x iSize=%08x", iAllocator, iPhysAddr, iSize));
MmuBase& m=*MmuBase::TheMmu;
TInt map_shift = (iPhysAddr<0xffffffffu) ? 30 : m.iPageShift;
for (; map_shift>=m.iPageShift; --map_shift)
{
TUint32 map_size = 1<<map_shift;
TUint32 map_mask = map_size-1;
if (!(m.iMapSizes & map_size))
continue; // map_size is not supported on this hardware
TPhysAddr base = (iPhysAddr+map_mask) &~ map_mask; // base rounded up
TPhysAddr end = (iPhysAddr+iSize)&~map_mask; // end rounded down
if ((base-end)<0x80000000u && map_shift>m.iPageShift)
continue; // region not big enough to use this mapping size
__KTRACE_OPT(KMMU, Kern::Printf("Try map size %08x", map_size));
iLinAddr=iAllocator->Alloc(iSize, map_shift, iPhysAddr, aPdePerm);
if (iLinAddr)
break; // done
}
TInt r=iLinAddr ? map_shift : KErrNoMemory;
__KTRACE_OPT(KMMU, Kern::Printf("iLinAddr=%08x, returning %d", iLinAddr, r));
return r;
}
void DMemModelChunkHw::DeallocateLinearAddress()
{
__KTRACE_OPT(KMMU, Kern::Printf("DMemModelChunkHw::DeallocateLinearAddress %O", this));
MmuBase& m=*MmuBase::TheMmu;
MmuBase::WaitHwChunk();
THwChunkRegion* rgn=iAllocator->Free(iLinAddr, iSize);
iLinAddr=0;
MmuBase::SignalHwChunk();
TLinAddr base = iAllocator->iSection->iBase;
TBitMapAllocator& section_allocator = iAllocator->iSection->iAllocator;
while (rgn)
{
MmuBase::Wait();
if (rgn->iRegionSize)
{
// free address range
__KTRACE_OPT(KMMU, Kern::Printf("Freeing range %03x+%03x", rgn->iIndex, rgn->iRegionSize));
section_allocator.Free(rgn->iIndex, rgn->iRegionSize);
// Though this is large region, it still can be made up of page tables (not sections).
// Check each chunk and remove tables in neccessary
TInt i = 0;
TLinAddr a = base + (TLinAddr(rgn->iIndex)<<m.iChunkShift);
for (; i<rgn->iRegionSize ; i++,a+=m.iChunkSize)
{
TInt id = m.UnassignPageTable(a);
if (id>=0)
m.FreePageTable(id);
}
}
else
{
// free address and page table if it exists
__KTRACE_OPT(KMMU, Kern::Printf("Freeing index %03x", rgn->iIndex));
section_allocator.Free(rgn->iIndex);
TLinAddr a = base + (TLinAddr(rgn->iIndex)<<m.iChunkShift);
TInt id = m.UnassignPageTable(a);
if (id>=0)
m.FreePageTable(id);
}
MmuBase::Signal();
THwChunkRegion* free=rgn;
rgn=rgn->iNext;
Kern::Free(free);
}
}
//
// RamCacheBase
//
RamCacheBase* RamCacheBase::TheRamCache = NULL;
RamCacheBase::RamCacheBase()
{
}
void RamCacheBase::Init2()
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">RamCacheBase::Init2"));
iMmu = MmuBase::TheMmu;
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("<RamCacheBase::Init2"));
}
void RamCacheBase::ReturnToSystem(SPageInfo* aPageInfo)
{
__ASSERT_MUTEX(MmuBase::RamAllocatorMutex);
__ASSERT_SYSTEM_LOCK;
aPageInfo->SetUnused();
--iNumberOfFreePages;
__NK_ASSERT_DEBUG(iNumberOfFreePages>=0);
// Release system lock before using the RAM allocator.
NKern::UnlockSystem();
iMmu->iRamPageAllocator->FreeRamPage(aPageInfo->PhysAddr(), EPageDiscard);
NKern::LockSystem();
}
SPageInfo* RamCacheBase::GetPageFromSystem(TUint aBlockedZoneId, TBool aBlockRest)
{
__ASSERT_MUTEX(MmuBase::RamAllocatorMutex);
SPageInfo* pageInfo;
TPhysAddr pagePhys;
TInt r = iMmu->iRamPageAllocator->AllocRamPages(&pagePhys,1, EPageDiscard, aBlockedZoneId, aBlockRest);
if(r==KErrNone)
{
NKern::LockSystem();
pageInfo = SPageInfo::FromPhysAddr(pagePhys);
pageInfo->Change(SPageInfo::EPagedFree,SPageInfo::EStatePagedDead);
++iNumberOfFreePages;
NKern::UnlockSystem();
}
else
pageInfo = NULL;
return pageInfo;
}
//
// RamCache
//
void RamCache::Init2()
{
__KTRACE_OPT(KBOOT,Kern::Printf(">RamCache::Init2"));
RamCacheBase::Init2();
__KTRACE_OPT(KBOOT,Kern::Printf("<RamCache::Init2"));
}
TInt RamCache::Init3()
{
return KErrNone;
}
void RamCache::RemovePage(SPageInfo& aPageInfo)
{
__NK_ASSERT_DEBUG(aPageInfo.Type() == SPageInfo::EPagedCache);
__NK_ASSERT_DEBUG(aPageInfo.State() == SPageInfo::EStatePagedYoung);
aPageInfo.iLink.Deque();
aPageInfo.SetState(SPageInfo::EStatePagedDead);
}
TBool RamCache::GetFreePages(TInt aNumPages)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: >GetFreePages %d",aNumPages));
NKern::LockSystem();
while(aNumPages>0 && NumberOfFreePages()>=aNumPages)
{
// steal a page from cache list and return it to the free pool...
SPageInfo* pageInfo = SPageInfo::FromLink(iPageList.First()->Deque());
pageInfo->SetState(SPageInfo::EStatePagedDead);
SetFree(pageInfo);
ReturnToSystem(pageInfo);
--aNumPages;
}
NKern::UnlockSystem();
__KTRACE_OPT(KPAGING,Kern::Printf("DP: <GetFreePages %d",!aNumPages));
return !aNumPages;
}
void RamCache::DonateRamCachePage(SPageInfo* aPageInfo)
{
SPageInfo::TType type = aPageInfo->Type();
if(type==SPageInfo::EChunk)
{
// Must not donate locked page. An example is DMA transferred memory.
__NK_ASSERT_DEBUG(!aPageInfo->LockCount());
aPageInfo->Change(SPageInfo::EPagedCache,SPageInfo::EStatePagedYoung);
iPageList.Add(&aPageInfo->iLink);
++iNumberOfFreePages;
// Update ram allocator counts as this page has changed its type
DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
iMmu->iRamPageAllocator->ChangePageType(aPageInfo, chunk->GetPageType(), EPageDiscard);
#ifdef BTRACE_PAGING
BTraceContext8(BTrace::EPaging, BTrace::EPagingChunkDonatePage, chunk, aPageInfo->Offset());
#endif
return;
}
// allow already donated pages...
__NK_ASSERT_DEBUG(type==SPageInfo::EPagedCache);
}
TBool RamCache::ReclaimRamCachePage(SPageInfo* aPageInfo)
{
SPageInfo::TType type = aPageInfo->Type();
// Kern::Printf("DemandPaging::ReclaimRamCachePage %x %d free=%d",aPageInfo,type,iNumberOfFreePages);
if(type==SPageInfo::EChunk)
return ETrue; // page already reclaimed
__NK_ASSERT_DEBUG(type==SPageInfo::EPagedCache);
__NK_ASSERT_DEBUG(aPageInfo->State()==SPageInfo::EStatePagedYoung);
// Update ram allocator counts as this page has changed its type
DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
iMmu->iRamPageAllocator->ChangePageType(aPageInfo, EPageDiscard, chunk->GetPageType());
aPageInfo->iLink.Deque();
--iNumberOfFreePages;
aPageInfo->Change(SPageInfo::EChunk,SPageInfo::EStateNormal);
#ifdef BTRACE_PAGING
BTraceContext8(BTrace::EPaging, BTrace::EPagingChunkReclaimPage, chunk, aPageInfo->Offset());
#endif
return ETrue;
}
/**
Discard the specified page.
Should only be called on a page if a previous call to IsPageDiscardable()
returned ETrue and the system lock hasn't been released between the calls.
@param aPageInfo The page info of the page to be discarded
@param aBlockedZoneId Not used by this overload.
@param aBlockRest Not used by this overload.
@return ETrue if page succesfully discarded
@pre System lock held.
@post System lock held.
*/
TBool RamCache::DoDiscardPage(SPageInfo& aPageInfo, TUint aBlockedZoneId, TBool aBlockRest)
{
__NK_ASSERT_DEBUG(iNumberOfFreePages > 0);
RemovePage(aPageInfo);
SetFree(&aPageInfo);
ReturnToSystem(&aPageInfo);
return ETrue;
}
/**
First stage in discarding a list of pages.
Must ensure that the pages will still be discardable even if system lock is released.
To be used in conjunction with RamCacheBase::DoDiscardPages1().
@param aPageList A NULL terminated list of the pages to be discarded
@return KErrNone on success.
@pre System lock held
@post System lock held
*/
TInt RamCache::DoDiscardPages0(SPageInfo** aPageList)
{
__ASSERT_SYSTEM_LOCK;
SPageInfo* pageInfo;
while((pageInfo = *aPageList++) != 0)
{
RemovePage(*pageInfo);
}
return KErrNone;
}
/**
Final stage in discarding a list of page
Finish discarding the pages previously removed by RamCacheBase::DoDiscardPages0().
This overload doesn't actually need to do anything.
@param aPageList A NULL terminated list of the pages to be discarded
@return KErrNone on success.
@pre System lock held
@post System lock held
*/
TInt RamCache::DoDiscardPages1(SPageInfo** aPageList)
{
__ASSERT_SYSTEM_LOCK;
SPageInfo* pageInfo;
while((pageInfo = *aPageList++) != 0)
{
SetFree(pageInfo);
ReturnToSystem(pageInfo);
}
return KErrNone;
}
/**
Check whether the specified page can be discarded by the RAM cache.
@param aPageInfo The page info of the page being queried.
@return ETrue when the page can be discarded, EFalse otherwise.
@pre System lock held.
@post System lock held.
*/
TBool RamCache::IsPageDiscardable(SPageInfo& aPageInfo)
{
SPageInfo::TType type = aPageInfo.Type();
SPageInfo::TState state = aPageInfo.State();
return (type == SPageInfo::EPagedCache && state == SPageInfo::EStatePagedYoung);
}
/**
@return ETrue when the unmapped page should be freed, EFalse otherwise
*/
TBool RamCache::PageUnmapped(SPageInfo* aPageInfo)
{
SPageInfo::TType type = aPageInfo->Type();
// Kern::Printf("DemandPaging::PageUnmapped %x %d",aPageInfo,type);
if(type!=SPageInfo::EPagedCache)
return ETrue;
SPageInfo::TState state = aPageInfo->State();
if(state==SPageInfo::EStatePagedYoung)
{
// This page will be freed by DChunk::DoDecommit as it was originally
// allocated so update page counts in ram allocator
DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
iMmu->iRamPageAllocator->ChangePageType(aPageInfo, EPageDiscard, chunk->GetPageType());
aPageInfo->iLink.Deque();
--iNumberOfFreePages;
}
return ETrue;
}
void RamCache::Panic(TFault aFault)
{
Kern::Fault("RamCache",aFault);
}
/**
Flush all cache pages.
@pre RAM allocator mutex held
@post RAM allocator mutex held
*/
void RamCache::FlushAll()
{
__ASSERT_MUTEX(MmuBase::RamAllocatorMutex);
#ifdef _DEBUG
// Should always succeed
__NK_ASSERT_DEBUG(GetFreePages(iNumberOfFreePages));
#else
GetFreePages(iNumberOfFreePages);
#endif
}
//
// Demand Paging
//
#ifdef __DEMAND_PAGING__
DemandPaging* DemandPaging::ThePager = 0;
TBool DemandPaging::PseudoRandInitialised = EFalse;
volatile TUint32 DemandPaging::PseudoRandSeed = 0;
void M::DemandPagingInit()
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">M::DemandPagingInit"));
TInt r = RamCacheBase::TheRamCache->Init3();
if (r != KErrNone)
DemandPaging::Panic(DemandPaging::EInitialiseFailed);
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("<M::DemandPagingInit"));
}
TInt M::DemandPagingFault(TAny* aExceptionInfo)
{
DemandPaging* pager = DemandPaging::ThePager;
if(pager)
return pager->Fault(aExceptionInfo);
return KErrAbort;
}
#ifdef _DEBUG
extern "C" void ASMCheckPagingSafe(TLinAddr aPC, TLinAddr aLR, TLinAddr aStartAddres, TUint aLength)
{
if(M::CheckPagingSafe(EFalse, aStartAddres, aLength))
return;
Kern::Printf("ASM_ASSERT_PAGING_SAFE FAILED: pc=%x lr=%x",aPC,aLR);
__NK_ASSERT_ALWAYS(0);
}
extern "C" void ASMCheckDataPagingSafe(TLinAddr aPC, TLinAddr aLR, TLinAddr aStartAddres, TUint aLength)
{
if(M::CheckPagingSafe(ETrue, aStartAddres, aLength))
return;
__KTRACE_OPT(KDATAPAGEWARN,Kern::Printf("Data paging: ASM_ASSERT_DATA_PAGING_SAFE FAILED: pc=%x lr=%x",aPC,aLR));
}
#endif
TBool M::CheckPagingSafe(TBool aDataPaging, TLinAddr aStartAddr, TUint aLength)
{
DemandPaging* pager = DemandPaging::ThePager;
if(!pager || K::Initialising)
return ETrue;
NThread* nt = NCurrentThread();
if(!nt)
return ETrue; // We've not booted properly yet!
if (!pager->NeedsMutexOrderCheck(aStartAddr, aLength))
return ETrue;
TBool dataPagingEnabled = EFalse; // data paging not supported on moving or multiple models
DThread* thread = _LOFF(nt,DThread,iNThread);
NFastMutex* fm = NKern::HeldFastMutex();
if(fm)
{
if(!thread->iPagingExcTrap || fm!=&TheScheduler.iLock)
{
if (!aDataPaging)
{
__KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("DP: CheckPagingSafe FAILED - FM Held"));
return EFalse;
}
else
{
__KTRACE_OPT(KDATAPAGEWARN, Kern::Printf("Data paging: CheckPagingSafe FAILED - FM Held"));
return !dataPagingEnabled;
}
}
}
DMutex* m = pager->CheckMutexOrder();
if (m)
{
if (!aDataPaging)
{
__KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("DP: Mutex Order Fault %O",m));
return EFalse;
}
else
{
__KTRACE_OPT(KDATAPAGEWARN, Kern::Printf("Data paging: Mutex Order Fault %O",m));
return !dataPagingEnabled;
}
}
return ETrue;
}
TInt M::LockRegion(TLinAddr aStart,TInt aSize)
{
DemandPaging* pager = DemandPaging::ThePager;
if(pager)
return pager->LockRegion(aStart,aSize,NULL);
return KErrNone;
}
TInt M::UnlockRegion(TLinAddr aStart,TInt aSize)
{
DemandPaging* pager = DemandPaging::ThePager;
if(pager)
return pager->UnlockRegion(aStart,aSize,NULL);
return KErrNone;
}
#else // !__DEMAND_PAGING__
TInt M::LockRegion(TLinAddr /*aStart*/,TInt /*aSize*/)
{
return KErrNone;
}
TInt M::UnlockRegion(TLinAddr /*aStart*/,TInt /*aSize*/)
{
return KErrNone;
}
#endif // __DEMAND_PAGING__
//
// DemandPaging
//
#ifdef __DEMAND_PAGING__
const TUint16 KDefaultYoungOldRatio = 3;
const TUint KDefaultMinPages = 256;
const TUint KDefaultMaxPages = KMaxTUint >> KPageShift;
/* Need at least 4 mapped pages to guarentee to be able to execute all ARM instructions.
(Worst case is a THUMB2 STM instruction with both instruction and data stradling page
boundaries.)
*/
const TUint KMinYoungPages = 4;
const TUint KMinOldPages = 1;
/* A minimum young/old ratio of 1 means that we need at least twice KMinYoungPages pages...
*/
const TUint KAbsoluteMinPageCount = 2*KMinYoungPages;
__ASSERT_COMPILE(KMinOldPages<=KAbsoluteMinPageCount/2);
class DMissingPagingDevice : public DPagingDevice
{
TInt Read(TThreadMessage* /*aReq*/,TLinAddr /*aBuffer*/,TUint /*aOffset*/,TUint /*aSize*/,TInt /*aDrvNumber*/)
{ DemandPaging::Panic(DemandPaging::EDeviceMissing); return 0; }
};
TBool DemandPaging::RomPagingRequested()
{
return TheRomHeader().iPageableRomSize != 0;
}
TBool DemandPaging::CodePagingRequested()
{
return (TheSuperPage().KernelConfigFlags() & EKernelConfigCodePagingPolicyDefaultPaged) != EKernelConfigCodePagingPolicyNoPaging;
}
DemandPaging::DemandPaging()
{
}
void DemandPaging::Init2()
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">DemandPaging::Init2"));
RamCacheBase::Init2();
// initialise live list...
SDemandPagingConfig config = TheRomHeader().iDemandPagingConfig;
iMinimumPageCount = KDefaultMinPages;
if(config.iMinPages)
iMinimumPageCount = config.iMinPages;
if(iMinimumPageCount<KAbsoluteMinPageCount)
iMinimumPageCount = KAbsoluteMinPageCount;
iInitMinimumPageCount = iMinimumPageCount;
iMaximumPageCount = KDefaultMaxPages;
if(config.iMaxPages)
iMaximumPageCount = config.iMaxPages;
iInitMaximumPageCount = iMaximumPageCount;
iYoungOldRatio = KDefaultYoungOldRatio;
if(config.iYoungOldRatio)
iYoungOldRatio = config.iYoungOldRatio;
TInt ratioLimit = (iMinimumPageCount-KMinOldPages)/KMinOldPages;
if(iYoungOldRatio>ratioLimit)
iYoungOldRatio = ratioLimit;
iMinimumPageLimit = (KMinYoungPages * (1 + iYoungOldRatio)) / iYoungOldRatio;
if(iMinimumPageLimit<KAbsoluteMinPageCount)
iMinimumPageLimit = KAbsoluteMinPageCount;
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">DemandPaging::InitialiseLiveList min=%d max=%d ratio=%d",iMinimumPageCount,iMaximumPageCount,iYoungOldRatio));
if(iMaximumPageCount<iMinimumPageCount)
Panic(EInitialiseBadArgs);
//
// This routine doesn't acuire any mutexes because it should be called before the system
// is fully up and running. I.e. called before another thread can preempt this.
//
// Calculate page counts
iOldCount = iMinimumPageCount/(1+iYoungOldRatio);
if(iOldCount<KMinOldPages)
Panic(EInitialiseBadArgs);
iYoungCount = iMinimumPageCount-iOldCount;
if(iYoungCount<KMinYoungPages)
Panic(EInitialiseBadArgs); // Need at least 4 pages mapped to execute an ARM LDM instruction in THUMB2 mode
iNumberOfFreePages = 0;
// Allocate RAM pages and put them all on the old list
iYoungCount = 0;
iOldCount = 0;
for(TUint i=0; i<iMinimumPageCount; i++)
{
// Allocate a single page
TPhysAddr pagePhys;
TInt r = iMmu->iRamPageAllocator->AllocRamPages(&pagePhys,1, EPageDiscard);
if(r!=0)
Panic(EInitialiseFailed);
AddAsFreePage(SPageInfo::FromPhysAddr(pagePhys));
}
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("<DemandPaging::Init2"));
}
TInt VMHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2);
TInt DemandPaging::Init3()
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">DemandPaging::Init3"));
TInt r;
// construct iBufferChunk
iDeviceBufferSize = 2*KPageSize;
TChunkCreateInfo info;
info.iType = TChunkCreateInfo::ESharedKernelMultiple;
info.iMaxSize = iDeviceBufferSize*KMaxPagingDevices;
info.iMapAttr = EMapAttrCachedMax;
info.iOwnsMemory = ETrue;
TUint32 mapAttr;
r = Kern::ChunkCreate(info,iDeviceBuffersChunk,iDeviceBuffers,mapAttr);
if(r!=KErrNone)
return r;
// Install 'null' paging devices which panic if used...
DMissingPagingDevice* missingPagingDevice = new DMissingPagingDevice;
for(TInt i=0; i<KMaxPagingDevices; i++)
{
iPagingDevices[i].iInstalled = EFalse;
iPagingDevices[i].iDevice = missingPagingDevice;
}
// Initialise ROM info...
const TRomHeader& romHeader = TheRomHeader();
iRomLinearBase = (TLinAddr)&romHeader;
iRomSize = iMmu->RoundToPageSize(romHeader.iUncompressedSize);
if(romHeader.iRomPageIndex)
iRomPageIndex = (SRomPageInfo*)((TInt)&romHeader+romHeader.iRomPageIndex);
TLinAddr pagedStart = romHeader.iPageableRomSize ? (TLinAddr)&romHeader+romHeader.iPageableRomStart : 0;
if(pagedStart)
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("ROM=%x+%x PagedStart=%x",iRomLinearBase,iRomSize,pagedStart));
__NK_ASSERT_ALWAYS(TUint(pagedStart-iRomLinearBase)<TUint(iRomSize));
iRomPagedLinearBase = pagedStart;
iRomPagedSize = iRomLinearBase+iRomSize-pagedStart;
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("DemandPaging::Init3, ROM Paged start(0x%x), sixe(0x%x)",iRomPagedLinearBase,iRomPagedSize));
#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__
// Get physical addresses of ROM pages
iOriginalRomPageCount = iMmu->RoundToPageSize(iRomSize)>>KPageShift;
iOriginalRomPages = new TPhysAddr[iOriginalRomPageCount];
__NK_ASSERT_ALWAYS(iOriginalRomPages);
TPhysAddr romPhysAddress;
iMmu->LinearToPhysical(iRomLinearBase,iRomSize,romPhysAddress,iOriginalRomPages);
#endif
}
r = Kern::AddHalEntry(EHalGroupVM, VMHalFunction, 0);
__NK_ASSERT_ALWAYS(r==KErrNone);
#ifdef __DEMAND_PAGING_BENCHMARKS__
for (TInt i = 0 ; i < EMaxPagingBm ; ++i)
ResetBenchmarkData((TPagingBenchmark)i);
#endif
// Initialisation now complete
ThePager = this;
return KErrNone;
}
DemandPaging::~DemandPaging()
{
#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__
delete[] iOriginalRomPages;
#endif
for (TUint i = 0 ; i < iPagingRequestCount ; ++i)
delete iPagingRequests[i];
}
TInt DemandPaging::InstallPagingDevice(DPagingDevice* aDevice)
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf(">DemandPaging::InstallPagingDevice name='%s' type=%d",aDevice->iName,aDevice->iType));
if(aDevice->iReadUnitShift>KPageShift)
Panic(EInvalidPagingDevice);
TInt i;
TInt r = KErrNone;
TBool createRequestObjects = EFalse;
if ((aDevice->iType & DPagingDevice::ERom) && RomPagingRequested())
{
r = DoInstallPagingDevice(aDevice, 0);
if (r != KErrNone)
goto done;
K::MemModelAttributes|=EMemModelAttrRomPaging;
createRequestObjects = ETrue;
}
if ((aDevice->iType & DPagingDevice::ECode) && CodePagingRequested())
{
for (i = 0 ; i < KMaxLocalDrives ; ++i)
{
if (aDevice->iDrivesSupported & (1<<i))
{
r = DoInstallPagingDevice(aDevice, i + 1);
if (r != KErrNone)
goto done;
}
}
K::MemModelAttributes|=EMemModelAttrCodePaging;
createRequestObjects = ETrue;
}
if (createRequestObjects)
{
for (i = 0 ; i < KPagingRequestsPerDevice ; ++i)
{
r = CreateRequestObject();
if (r != KErrNone)
goto done;
}
}
done:
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("<DemandPaging::InstallPagingDevice returns %d",r));
return r;
}
TInt DemandPaging::DoInstallPagingDevice(DPagingDevice* aDevice, TInt aId)
{
NKern::LockSystem();
SPagingDevice* device = &iPagingDevices[aId];
if((device->iInstalled) && !(aDevice->iType & DPagingDevice::EMediaExtension))
{
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("**** Attempt to install more than one ROM paging device !!!!!!!! ****"));
//Panic(EDeviceAlreadyExists);
NKern::UnlockSystem();
return KErrNone;
}
aDevice->iDeviceId = aId;
device->iDevice = aDevice;
device->iInstalled = ETrue;
NKern::UnlockSystem();
__KTRACE_OPT2(KPAGING,KBOOT,Kern::Printf("DemandPaging::InstallPagingDevice id=%d, device=%08x",aId,device));
return KErrNone;
}
DemandPaging::DPagingRequest::~DPagingRequest()
{
if (iMutex)
iMutex->Close(NULL);
}
TInt DemandPaging::CreateRequestObject()
{
_LIT(KLitPagingRequest,"PagingRequest-");
TInt index;
TInt id = (TInt)__e32_atomic_add_ord32(&iNextPagingRequestCount, 1);
TLinAddr offset = id * iDeviceBufferSize;
TUint32 physAddr = 0;
TInt r = Kern::ChunkCommitContiguous(iDeviceBuffersChunk,offset,iDeviceBufferSize, physAddr);
if(r != KErrNone)
return r;
DPagingRequest* req = new DPagingRequest();
if (!req)
return KErrNoMemory;
req->iBuffer = iDeviceBuffers + offset;
AllocLoadAddress(*req, id);
TBuf<16> mutexName(KLitPagingRequest);
mutexName.AppendNum(id);
r = K::MutexCreate(req->iMutex, mutexName, NULL, EFalse, KMutexOrdPageIn);
if (r!=KErrNone)
goto done;
// Ensure there are enough young pages to cope with new request object
r = ResizeLiveList(iMinimumPageCount, iMaximumPageCount);
if (r!=KErrNone)
goto done;
NKern::LockSystem();
index = iPagingRequestCount++;
__NK_ASSERT_ALWAYS(index < KMaxPagingRequests);
iPagingRequests[index] = req;
iFreeRequestPool.AddHead(req);
NKern::UnlockSystem();
done:
if (r != KErrNone)
delete req;
return r;
}
DemandPaging::DPagingRequest* DemandPaging::AcquireRequestObject()
{
__ASSERT_SYSTEM_LOCK;
__NK_ASSERT_DEBUG(iPagingRequestCount > 0);
DPagingRequest* req = NULL;
// System lock used to serialise access to our data strucures as we have to hold it anyway when
// we wait on the mutex
req = (DPagingRequest*)iFreeRequestPool.GetFirst();
if (req != NULL)
__NK_ASSERT_DEBUG(req->iUsageCount == 0);
else
{
// Pick a random request object to wait on
TUint index = (FastPseudoRand() * TUint64(iPagingRequestCount)) >> 32;
__NK_ASSERT_DEBUG(index < iPagingRequestCount);
req = iPagingRequests[index];
__NK_ASSERT_DEBUG(req->iUsageCount > 0);
}
#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__
++iWaitingCount;
if (iWaitingCount > iMaxWaitingCount)
iMaxWaitingCount = iWaitingCount;
#endif
++req->iUsageCount;
TInt r = req->iMutex->Wait();
__NK_ASSERT_ALWAYS(r == KErrNone);
#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__
--iWaitingCount;
++iPagingCount;
if (iPagingCount > iMaxPagingCount)
iMaxPagingCount = iPagingCount;
#endif
return req;
}
void DemandPaging::ReleaseRequestObject(DPagingRequest* aReq)
{
__ASSERT_SYSTEM_LOCK;
#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__
--iPagingCount;
#endif
// If there are no threads waiting on the mutex then return it to the free pool
__NK_ASSERT_DEBUG(aReq->iUsageCount > 0);
if (--aReq->iUsageCount == 0)
iFreeRequestPool.AddHead(aReq);
aReq->iMutex->Signal();
NKern::LockSystem();
}
TInt DemandPaging::ReadRomPage(const DPagingRequest* aReq, TLinAddr aRomAddress)
{
START_PAGING_BENCHMARK;
TInt pageSize = KPageSize;
TInt dataOffset = aRomAddress-iRomLinearBase;
TInt pageNumber = dataOffset>>KPageShift;
TInt readUnitShift = RomPagingDevice().iDevice->iReadUnitShift;
TInt r;
if(!iRomPageIndex)
{
// ROM not broken into pages, so just read it in directly
START_PAGING_BENCHMARK;
r = RomPagingDevice().iDevice->Read(const_cast<TThreadMessage*>(&aReq->iMessage),aReq->iLoadAddr,dataOffset>>readUnitShift,pageSize>>readUnitShift,-1/*token for ROM paging*/);
END_PAGING_BENCHMARK(DemandPaging::ThePager, EPagingBmReadMedia);
}
else
{
// Work out where data for page is located
SRomPageInfo* romPageInfo = iRomPageIndex+pageNumber;
dataOffset = romPageInfo->iDataStart;
TInt dataSize = romPageInfo->iDataSize;
if(!dataSize)
{
// empty page, fill it with 0xff...
memset((void*)aReq->iLoadAddr,-1,pageSize);
r = KErrNone;
}
else
{
__NK_ASSERT_ALWAYS(romPageInfo->iPagingAttributes&SRomPageInfo::EPageable);
// Read data for page...
TThreadMessage* msg= const_cast<TThreadMessage*>(&aReq->iMessage);
TLinAddr buffer = aReq->iBuffer;
TUint readStart = dataOffset>>readUnitShift;
TUint readSize = ((dataOffset+dataSize-1)>>readUnitShift)-readStart+1;
__NK_ASSERT_DEBUG((readSize<<readUnitShift)<=iDeviceBufferSize);
START_PAGING_BENCHMARK;
r = RomPagingDevice().iDevice->Read(msg,buffer,readStart,readSize,-1/*token for ROM paging*/);
END_PAGING_BENCHMARK(DemandPaging::ThePager, EPagingBmReadMedia);
if(r==KErrNone)
{
// Decompress data...
TLinAddr data = buffer+dataOffset-(readStart<<readUnitShift);
r = Decompress(romPageInfo->iCompressionType,aReq->iLoadAddr,data,dataSize);
if(r>=0)
{
__NK_ASSERT_ALWAYS(r==pageSize);
r = KErrNone;
}
}
}
}
END_PAGING_BENCHMARK(this, EPagingBmReadRomPage);
return r;
}
TInt ReadFunc(TAny* aArg1, TAny* aArg2, TLinAddr aBuffer, TInt aBlockNumber, TInt aBlockCount)
{
START_PAGING_BENCHMARK;
TInt drive = (TInt)aArg1;
TThreadMessage* msg= (TThreadMessage*)aArg2;
DemandPaging::SPagingDevice& device = DemandPaging::ThePager->CodePagingDevice(drive);
TInt r = device.iDevice->Read(msg, aBuffer, aBlockNumber, aBlockCount, drive);
END_PAGING_BENCHMARK(DemandPaging::ThePager, EPagingBmReadMedia);
return r;
}
TInt DemandPaging::ReadCodePage(const DPagingRequest* aReq, DMmuCodeSegMemory* aCodeSegMemory, TLinAddr aCodeAddress)
{
__KTRACE_OPT(KPAGING,Kern::Printf("ReadCodePage buffer = %08x, csm == %08x, addr == %08x", aReq->iLoadAddr, aCodeSegMemory, aCodeAddress));
START_PAGING_BENCHMARK;
// Get the paging device for this drive
SPagingDevice& device = CodePagingDevice(aCodeSegMemory->iCodeLocalDrive);
// Work out which bit of the file to read
SRamCodeInfo& ri = aCodeSegMemory->iRamInfo;
TInt codeOffset = aCodeAddress - ri.iCodeRunAddr;
TInt pageNumber = codeOffset >> KPageShift;
TBool compressed = aCodeSegMemory->iCompressionType != SRomPageInfo::ENoCompression;
TInt dataOffset, dataSize;
if (compressed)
{
dataOffset = aCodeSegMemory->iCodePageOffsets[pageNumber];
dataSize = aCodeSegMemory->iCodePageOffsets[pageNumber + 1] - dataOffset;
__KTRACE_OPT(KPAGING,Kern::Printf(" compressed, file offset == %x, size == %d", dataOffset, dataSize));
}
else
{
dataOffset = codeOffset + aCodeSegMemory->iCodeStartInFile;
dataSize = Min(KPageSize, aCodeSegMemory->iBlockMap.DataLength() - dataOffset);
__NK_ASSERT_DEBUG(dataSize >= 0);
__KTRACE_OPT(KPAGING,Kern::Printf(" uncompressed, file offset == %x, size == %d", dataOffset, dataSize));
}
TInt bufferStart = aCodeSegMemory->iBlockMap.Read(aReq->iBuffer,
dataOffset,
dataSize,
device.iDevice->iReadUnitShift,
ReadFunc,
(TAny*)aCodeSegMemory->iCodeLocalDrive,
(TAny*)&aReq->iMessage);
TInt r = KErrNone;
if(bufferStart<0)
{
r = bufferStart; // return error
__NK_ASSERT_DEBUG(0);
}
else
{
TLinAddr data = aReq->iBuffer + bufferStart;
if (compressed)
{
TInt r = Decompress(aCodeSegMemory->iCompressionType, aReq->iLoadAddr, data, dataSize);
if(r>=0)
{
dataSize = Min(KPageSize, ri.iCodeSize - codeOffset);
if(r!=dataSize)
{
__NK_ASSERT_DEBUG(0);
r = KErrCorrupt;
}
else
r = KErrNone;
}
else
{
__NK_ASSERT_DEBUG(0);
}
}
else
{
#ifdef BTRACE_PAGING_VERBOSE
BTraceContext4(BTrace::EPaging,BTrace::EPagingDecompressStart,SRomPageInfo::ENoCompression);
#endif
memcpy((TAny*)aReq->iLoadAddr, (TAny*)data, dataSize);
#ifdef BTRACE_PAGING_VERBOSE
BTraceContext0(BTrace::EPaging,BTrace::EPagingDecompressEnd);
#endif
}
}
if(r==KErrNone)
if (dataSize < KPageSize)
memset((TAny*)(aReq->iLoadAddr + dataSize), KPageSize - dataSize, 0x03);
END_PAGING_BENCHMARK(this, EPagingBmReadCodePage);
return KErrNone;
}
#include "decompress.h"
TInt DemandPaging::Decompress(TInt aCompressionType,TLinAddr aDst,TLinAddr aSrc,TUint aSrcSize)
{
#ifdef BTRACE_PAGING_VERBOSE
BTraceContext4(BTrace::EPaging,BTrace::EPagingDecompressStart,aCompressionType);
#endif
TInt r;
switch(aCompressionType)
{
case SRomPageInfo::ENoCompression:
memcpy((void*)aDst,(void*)aSrc,aSrcSize);
r = aSrcSize;
break;
case SRomPageInfo::EBytePair:
{
START_PAGING_BENCHMARK;
TUint8* srcNext=0;
r=BytePairDecompress((TUint8*)aDst,KPageSize,(TUint8*)aSrc,aSrcSize,srcNext);
if (r == KErrNone)
__NK_ASSERT_ALWAYS((TLinAddr)srcNext == aSrc + aSrcSize);
END_PAGING_BENCHMARK(this, EPagingBmDecompress);
}
break;
default:
r = KErrNotSupported;
break;
}
#ifdef BTRACE_PAGING_VERBOSE
BTraceContext0(BTrace::EPaging,BTrace::EPagingDecompressEnd);
#endif
return r;
}
void DemandPaging::BalanceAges()
{
if(iOldCount*iYoungOldRatio>=iYoungCount)
return; // We have enough old pages
// make one young page into an old page...
__NK_ASSERT_DEBUG(!iYoungList.IsEmpty());
__NK_ASSERT_DEBUG(iYoungCount);
SDblQueLink* link = iYoungList.Last()->Deque();
--iYoungCount;
SPageInfo* pageInfo = SPageInfo::FromLink(link);
pageInfo->SetState(SPageInfo::EStatePagedOld);
iOldList.AddHead(link);
++iOldCount;
SetOld(pageInfo);
#ifdef BTRACE_PAGING_VERBOSE
BTraceContext4(BTrace::EPaging,BTrace::EPagingAged,pageInfo->PhysAddr());
#endif
}
void DemandPaging::AddAsYoungest(SPageInfo* aPageInfo)
{
#ifdef _DEBUG
SPageInfo::TType type = aPageInfo->Type();
__NK_ASSERT_DEBUG(type==SPageInfo::EPagedROM || type==SPageInfo::EPagedCode || type==SPageInfo::EPagedData || type==SPageInfo::EPagedCache);
#endif
aPageInfo->SetState(SPageInfo::EStatePagedYoung);
iYoungList.AddHead(&aPageInfo->iLink);
++iYoungCount;
}
void DemandPaging::AddAsFreePage(SPageInfo* aPageInfo)
{
#ifdef BTRACE_PAGING
TPhysAddr phys = aPageInfo->PhysAddr();
BTraceContext4(BTrace::EPaging,BTrace::EPagingPageInFree,phys);
#endif
aPageInfo->Change(SPageInfo::EPagedFree,SPageInfo::EStatePagedOld);
iOldList.Add(&aPageInfo->iLink);
++iOldCount;
}
void DemandPaging::RemovePage(SPageInfo* aPageInfo)
{
switch(aPageInfo->State())
{
case SPageInfo::EStatePagedYoung:
__NK_ASSERT_DEBUG(iYoungCount);
aPageInfo->iLink.Deque();
--iYoungCount;
break;
case SPageInfo::EStatePagedOld:
__NK_ASSERT_DEBUG(iOldCount);
aPageInfo->iLink.Deque();
--iOldCount;
break;
case SPageInfo::EStatePagedLocked:
break;
default:
__NK_ASSERT_DEBUG(0);
}
aPageInfo->SetState(SPageInfo::EStatePagedDead);
}
SPageInfo* DemandPaging::GetOldestPage()
{
// remove oldest from list...
SDblQueLink* link;
if(iOldCount)
{
__NK_ASSERT_DEBUG(!iOldList.IsEmpty());
link = iOldList.Last()->Deque();
--iOldCount;
}
else
{
__NK_ASSERT_DEBUG(iYoungCount);
__NK_ASSERT_DEBUG(!iYoungList.IsEmpty());
link = iYoungList.Last()->Deque();
--iYoungCount;
}
SPageInfo* pageInfo = SPageInfo::FromLink(link);
pageInfo->SetState(SPageInfo::EStatePagedDead);
// put page in a free state...
SetFree(pageInfo);
pageInfo->Change(SPageInfo::EPagedFree,SPageInfo::EStatePagedDead);
// keep live list balanced...
BalanceAges();
return pageInfo;
}
TBool DemandPaging::GetFreePages(TInt aNumPages)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: >GetFreePages %d",aNumPages));
NKern::LockSystem();
while(aNumPages>0 && NumberOfFreePages()>=aNumPages)
{
// steal a page from live page list and return it to the free pool...
ReturnToSystem(GetOldestPage());
--aNumPages;
}
NKern::UnlockSystem();
__KTRACE_OPT(KPAGING,Kern::Printf("DP: <GetFreePages %d",!aNumPages));
return !aNumPages;
}
void DemandPaging::DonateRamCachePage(SPageInfo* aPageInfo)
{
__NK_ASSERT_DEBUG(iMinimumPageCount + iNumberOfFreePages <= iMaximumPageCount);
SPageInfo::TType type = aPageInfo->Type();
if(type==SPageInfo::EChunk)
{
// Must not donate locked page. An example is DMA transferred memory.
__NK_ASSERT_DEBUG(!aPageInfo->LockCount());
aPageInfo->Change(SPageInfo::EPagedCache,SPageInfo::EStatePagedYoung);
// Update ram allocator counts as this page has changed its type
DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
iMmu->iRamPageAllocator->ChangePageType(aPageInfo, chunk->GetPageType(), EPageDiscard);
AddAsYoungest(aPageInfo);
++iNumberOfFreePages;
if (iMinimumPageCount + iNumberOfFreePages > iMaximumPageCount)
ReturnToSystem(GetOldestPage());
BalanceAges();
return;
}
// allow already donated pages...
__NK_ASSERT_DEBUG(type==SPageInfo::EPagedCache);
}
TBool DemandPaging::ReclaimRamCachePage(SPageInfo* aPageInfo)
{
SPageInfo::TType type = aPageInfo->Type();
if(type==SPageInfo::EChunk)
return ETrue; // page already reclaimed
__NK_ASSERT_DEBUG(type==SPageInfo::EPagedCache);
if(!iNumberOfFreePages)
return EFalse;
--iNumberOfFreePages;
RemovePage(aPageInfo);
aPageInfo->Change(SPageInfo::EChunk,SPageInfo::EStateNormal);
// Update ram allocator counts as this page has changed its type
DMemModelChunk* chunk = (DMemModelChunk*)aPageInfo->Owner();
iMmu->iRamPageAllocator->ChangePageType(aPageInfo, EPageDiscard, chunk->GetPageType());
return ETrue;
}
SPageInfo* DemandPaging::AllocateNewPage()
{
__ASSERT_SYSTEM_LOCK
SPageInfo* pageInfo;
NKern::UnlockSystem();
MmuBase::Wait();
NKern::LockSystem();
// Try getting a free page from our active page list
if(iOldCount)
{
pageInfo = SPageInfo::FromLink(iOldList.Last());
if(pageInfo->Type()==SPageInfo::EPagedFree)
{
pageInfo = GetOldestPage();
goto done;
}
}
// Try getting a free page from the system pool
if(iMinimumPageCount+iNumberOfFreePages<iMaximumPageCount)
{
NKern::UnlockSystem();
pageInfo = GetPageFromSystem();
NKern::LockSystem();
if(pageInfo)
goto done;
}
// As a last resort, steal one from our list of active pages
pageInfo = GetOldestPage();
done:
NKern::UnlockSystem();
MmuBase::Signal();
NKern::LockSystem();
return pageInfo;
}
void DemandPaging::Rejuvenate(SPageInfo* aPageInfo)
{
SPageInfo::TState state = aPageInfo->State();
if(state==SPageInfo::EStatePagedOld)
{
// move page from old list to head of young list...
__NK_ASSERT_DEBUG(iOldCount);
aPageInfo->iLink.Deque();
--iOldCount;
AddAsYoungest(aPageInfo);
BalanceAges();
}
else if(state==SPageInfo::EStatePagedYoung)
{
// page was already young, move it to the start of the list (make it the youngest)
aPageInfo->iLink.Deque();
iYoungList.AddHead(&aPageInfo->iLink);
}
else
{
// leave locked pages alone
__NK_ASSERT_DEBUG(state==SPageInfo::EStatePagedLocked);
}
}
TInt DemandPaging::CheckRealtimeThreadFault(DThread* aThread, TAny* aContext)
{
TInt r = KErrNone;
DThread* client = aThread->iIpcClient;
// If iIpcClient is set then we are accessing the address space of a remote thread. If we are
// in an IPC trap, this will contain information the local and remte addresses being accessed.
// If this is not set then we assume than any fault must be the fault of a bad remote address.
TIpcExcTrap* ipcTrap = (TIpcExcTrap*)aThread->iExcTrap;
if (ipcTrap && !ipcTrap->IsTIpcExcTrap())
ipcTrap = 0;
if (client && (!ipcTrap || ipcTrap->ExcLocation(aThread, aContext) == TIpcExcTrap::EExcRemote))
{
// Kill client thread...
NKern::UnlockSystem();
if(K::IllegalFunctionForRealtimeThread(client,"Access to Paged Memory (by other thread)"))
{
// Treat memory access as bad...
r = KErrAbort;
}
// else thread is in 'warning only' state so allow paging
}
else
{
// Kill current thread...
NKern::UnlockSystem();
if(K::IllegalFunctionForRealtimeThread(NULL,"Access to Paged Memory"))
{
// If current thread is in critical section, then the above kill will be deferred
// and we will continue executing. We will handle this by returning an error
// which means that the thread will take an exception (which hopfully is XTRAPed!)
r = KErrAbort;
}
// else thread is in 'warning only' state so allow paging
}
NKern::LockSystem();
return r;
}
TInt DemandPaging::ResizeLiveList(TUint aMinimumPageCount,TUint aMaximumPageCount)
{
if(!aMaximumPageCount)
{
aMinimumPageCount = iInitMinimumPageCount;
aMaximumPageCount = iInitMaximumPageCount;
}
// Min must not be greater than max...
if(aMinimumPageCount>aMaximumPageCount)
return KErrArgument;
NKern::ThreadEnterCS();
MmuBase::Wait();
NKern::LockSystem();
// Make sure aMinimumPageCount is not less than absolute minimum we can cope with...
iMinimumPageLimit = ((KMinYoungPages + iNextPagingRequestCount) * (1 + iYoungOldRatio)) / iYoungOldRatio;
if(iMinimumPageLimit<KAbsoluteMinPageCount)
iMinimumPageLimit = KAbsoluteMinPageCount;
if(aMinimumPageCount<iMinimumPageLimit+iReservePageCount)
aMinimumPageCount = iMinimumPageLimit+iReservePageCount;
if(aMaximumPageCount<aMinimumPageCount)
aMaximumPageCount=aMinimumPageCount;
// Increase iMaximumPageCount?
TInt extra = aMaximumPageCount-iMaximumPageCount;
if(extra>0)
iMaximumPageCount += extra;
// Reduce iMinimumPageCount?
TInt spare = iMinimumPageCount-aMinimumPageCount;
if(spare>0)
{
iMinimumPageCount -= spare;
iNumberOfFreePages += spare;
}
// Increase iMinimumPageCount?
TInt r=KErrNone;
while(aMinimumPageCount>iMinimumPageCount)
{
if(iNumberOfFreePages==0) // Need more pages?
{
// get a page from the system
NKern::UnlockSystem();
SPageInfo* pageInfo = GetPageFromSystem();
NKern::LockSystem();
if(!pageInfo)
{
r=KErrNoMemory;
break;
}
AddAsFreePage(pageInfo);
}
++iMinimumPageCount;
--iNumberOfFreePages;
NKern::FlashSystem();
}
// Reduce iMaximumPageCount?
while(iMaximumPageCount>aMaximumPageCount)
{
if (iMinimumPageCount+iNumberOfFreePages==iMaximumPageCount) // Need to free pages?
{
ReturnToSystem(GetOldestPage());
}
--iMaximumPageCount;
NKern::FlashSystem();
}
#ifdef BTRACE_KERNEL_MEMORY
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryDemandPagingCache,ThePager->iMinimumPageCount << KPageShift);
#endif
__NK_ASSERT_DEBUG(iMinimumPageCount + iNumberOfFreePages <= iMaximumPageCount);
NKern::UnlockSystem();
MmuBase::Signal();
NKern::ThreadLeaveCS();
return r;
}
TInt VMHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2)
{
DemandPaging* pager = DemandPaging::ThePager;
switch(aFunction)
{
case EVMHalFlushCache:
if(!TheCurrentThread->HasCapability(ECapabilityWriteDeviceData,__PLATSEC_DIAGNOSTIC_STRING("Checked by VMHalFunction(EVMHalFlushCache)")))
K::UnlockedPlatformSecurityPanic();
pager->FlushAll();
return KErrNone;
case EVMHalSetCacheSize:
{
if(!TheCurrentThread->HasCapability(ECapabilityWriteDeviceData,__PLATSEC_DIAGNOSTIC_STRING("Checked by VMHalFunction(EVMHalSetCacheSize)")))
K::UnlockedPlatformSecurityPanic();
TUint min = (TUint)a1>>KPageShift;
if((TUint)a1&KPageMask)
++min;
TUint max = (TUint)a2>>KPageShift;
if((TUint)a2&KPageMask)
++max;
return pager->ResizeLiveList(min,max);
}
case EVMHalGetCacheSize:
{
SVMCacheInfo info;
NKern::LockSystem(); // lock system to ensure consistent set of values are read...
info.iMinSize = pager->iMinimumPageCount<<KPageShift;
info.iMaxSize = pager->iMaximumPageCount<<KPageShift;
info.iCurrentSize = (pager->iMinimumPageCount+pager->iNumberOfFreePages)<<KPageShift;
info.iMaxFreeSize = pager->iNumberOfFreePages<<KPageShift;
NKern::UnlockSystem();
kumemput32(a1,&info,sizeof(info));
}
return KErrNone;
case EVMHalGetEventInfo:
{
SVMEventInfo info;
NKern::LockSystem(); // lock system to ensure consistent set of values are read...
info = pager->iEventInfo;
NKern::UnlockSystem();
Kern::InfoCopy(*(TDes8*)a1,(TUint8*)&info,sizeof(info));
}
return KErrNone;
case EVMHalResetEventInfo:
NKern::LockSystem();
memclr(&pager->iEventInfo, sizeof(pager->iEventInfo));
NKern::UnlockSystem();
return KErrNone;
#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__
case EVMHalGetOriginalRomPages:
*(TPhysAddr**)a1 = pager->iOriginalRomPages;
*(TInt*)a2 = pager->iOriginalRomPageCount;
return KErrNone;
#endif
case EVMPageState:
return pager->PageState((TLinAddr)a1);
#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__
case EVMHalGetConcurrencyInfo:
{
NKern::LockSystem();
SPagingConcurrencyInfo info = { pager->iMaxWaitingCount, pager->iMaxPagingCount };
NKern::UnlockSystem();
kumemput32(a1,&info,sizeof(info));
}
return KErrNone;
case EVMHalResetConcurrencyInfo:
NKern::LockSystem();
pager->iMaxWaitingCount = 0;
pager->iMaxPagingCount = 0;
NKern::UnlockSystem();
return KErrNone;
#endif
#ifdef __DEMAND_PAGING_BENCHMARKS__
case EVMHalGetPagingBenchmark:
{
TUint index = (TInt) a1;
if (index >= EMaxPagingBm)
return KErrNotFound;
NKern::LockSystem();
SPagingBenchmarkInfo info = pager->iBenchmarkInfo[index];
NKern::UnlockSystem();
kumemput32(a2,&info,sizeof(info));
}
return KErrNone;
case EVMHalResetPagingBenchmark:
{
TUint index = (TInt) a1;
if (index >= EMaxPagingBm)
return KErrNotFound;
NKern::LockSystem();
pager->ResetBenchmarkData((TPagingBenchmark)index);
NKern::UnlockSystem();
}
return KErrNone;
#endif
default:
return KErrNotSupported;
}
}
void DemandPaging::Panic(TFault aFault)
{
Kern::Fault("DEMAND-PAGING",aFault);
}
DMutex* DemandPaging::CheckMutexOrder()
{
#ifdef _DEBUG
SDblQue& ml = TheCurrentThread->iMutexList;
if(ml.IsEmpty())
return NULL;
DMutex* mm = _LOFF(ml.First(), DMutex, iOrderLink);
if (KMutexOrdPageIn >= mm->iOrder)
return mm;
#endif
return NULL;
}
TBool DemandPaging::ReservePage()
{
__ASSERT_SYSTEM_LOCK;
__ASSERT_CRITICAL;
NKern::UnlockSystem();
MmuBase::Wait();
NKern::LockSystem();
__NK_ASSERT_DEBUG(iMinimumPageCount >= iMinimumPageLimit + iReservePageCount);
while (iMinimumPageCount == iMinimumPageLimit + iReservePageCount &&
iNumberOfFreePages == 0)
{
NKern::UnlockSystem();
SPageInfo* pageInfo = GetPageFromSystem();
if(!pageInfo)
{
MmuBase::Signal();
NKern::LockSystem();
return EFalse;
}
NKern::LockSystem();
AddAsFreePage(pageInfo);
}
if (iMinimumPageCount == iMinimumPageLimit + iReservePageCount)
{
++iMinimumPageCount;
--iNumberOfFreePages;
if (iMinimumPageCount > iMaximumPageCount)
iMaximumPageCount = iMinimumPageCount;
}
++iReservePageCount;
__NK_ASSERT_DEBUG(iMinimumPageCount >= iMinimumPageLimit + iReservePageCount);
__NK_ASSERT_DEBUG(iMinimumPageCount + iNumberOfFreePages <= iMaximumPageCount);
NKern::UnlockSystem();
MmuBase::Signal();
NKern::LockSystem();
return ETrue;
}
TInt DemandPaging::LockRegion(TLinAddr aStart,TInt aSize,DProcess* aProcess)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: LockRegion(%08x,%x)",aStart,aSize));
NKern::ThreadEnterCS();
// calculate the number of pages required to lock aSize bytes
TUint32 mask=KPageMask;
TUint32 offset=aStart&mask;
TInt numPages = (aSize+offset+mask)>>KPageShift;
// Lock pages...
TInt r=KErrNone;
TLinAddr page = aStart;
NKern::LockSystem();
while(--numPages>=0)
{
if (!ReservePage())
break;
TPhysAddr phys;
r = LockPage(page,aProcess,phys);
NKern::FlashSystem();
if(r!=KErrNone)
break;
page += KPageSize;
}
NKern::UnlockSystem();
// If error, unlock whatever we managed to lock...
if(r!=KErrNone)
{
while((page-=KPageSize)>=aStart)
{
NKern::LockSystem();
UnlockPage(aStart,aProcess,KPhysAddrInvalid);
--iReservePageCount;
NKern::UnlockSystem();
}
}
NKern::ThreadLeaveCS();
__KTRACE_OPT(KPAGING,Kern::Printf("DP: LockRegion returns %d",r));
return r;
}
TInt DemandPaging::UnlockRegion(TLinAddr aStart,TInt aSize,DProcess* aProcess)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: UnlockRegion(%08x,%x)",aStart,aSize));
TUint32 mask=KPageMask;
TUint32 offset=aStart&mask;
TInt numPages = (aSize+offset+mask)>>KPageShift;
NKern::LockSystem();
__NK_ASSERT_DEBUG(iReservePageCount >= (TUint)numPages);
while(--numPages>=0)
{
UnlockPage(aStart,aProcess,KPhysAddrInvalid);
--iReservePageCount;
NKern::FlashSystem();
aStart += KPageSize;
}
NKern::UnlockSystem();
return KErrNone;
}
void DemandPaging::FlushAll()
{
NKern::ThreadEnterCS();
MmuBase::Wait();
// look at all RAM pages in the system, and unmap all those used for paging
const TUint32* piMap = (TUint32*)KPageInfoMap;
const TUint32* piMapEnd = piMap+(KNumPageInfoPages>>5);
SPageInfo* pi = (SPageInfo*)KPageInfoLinearBase;
NKern::LockSystem();
do
{
SPageInfo* piNext = pi+(KPageInfosPerPage<<5);
for(TUint32 piFlags=*piMap++; piFlags; piFlags>>=1)
{
if(!(piFlags&1))
{
pi += KPageInfosPerPage;
continue;
}
SPageInfo* piEnd = pi+KPageInfosPerPage;
do
{
SPageInfo::TState state = pi->State();
if(state==SPageInfo::EStatePagedYoung || state==SPageInfo::EStatePagedOld)
{
RemovePage(pi);
SetFree(pi);
AddAsFreePage(pi);
NKern::FlashSystem();
}
++pi;
const TUint KFlashCount = 64; // flash every 64 page infos (must be a power-of-2)
__ASSERT_COMPILE((TUint)KPageInfosPerPage >= KFlashCount);
if(((TUint)pi&((KFlashCount-1)<<KPageInfoShift))==0)
NKern::FlashSystem();
}
while(pi<piEnd);
}
pi = piNext;
}
while(piMap<piMapEnd);
NKern::UnlockSystem();
// reduce live page list to a minimum
while(GetFreePages(1)) {};
MmuBase::Signal();
NKern::ThreadLeaveCS();
}
TInt DemandPaging::LockPage(TLinAddr aPage, DProcess *aProcess, TPhysAddr& aPhysAddr)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: LockPage() %08x",aPage));
__ASSERT_SYSTEM_LOCK
aPhysAddr = KPhysAddrInvalid;
TInt r = EnsurePagePresent(aPage,aProcess);
if (r != KErrNone)
return KErrArgument; // page doesn't exist
// get info about page to be locked...
TPhysAddr phys = LinearToPhysical(aPage,aProcess);
retry:
__NK_ASSERT_DEBUG(phys!=KPhysAddrInvalid);
SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(phys);
if(!pageInfo)
return KErrNotFound;
// lock it...
SPageInfo::TType type = pageInfo->Type();
if(type==SPageInfo::EShadow)
{
// get the page which is being shadowed and lock that
phys = (TPhysAddr)pageInfo->Owner();
goto retry;
}
switch(pageInfo->State())
{
case SPageInfo::EStatePagedLocked:
// already locked, so just increment lock count...
++pageInfo->PagedLock();
break;
case SPageInfo::EStatePagedYoung:
{
if(type!=SPageInfo::EPagedROM && type !=SPageInfo::EPagedCode)
{
// not implemented yet
__NK_ASSERT_ALWAYS(0);
}
// remove page to be locked from live list...
RemovePage(pageInfo);
// change to locked state...
pageInfo->SetState(SPageInfo::EStatePagedLocked);
pageInfo->PagedLock() = 1; // Start with lock count of one
// open reference on memory...
if(type==SPageInfo::EPagedCode)
{
DMemModelCodeSegMemory* codeSegMemory = (DMemModelCodeSegMemory*)pageInfo->Owner();
if(codeSegMemory->Open()!=KErrNone)
{
__NK_ASSERT_DEBUG(0);
}
}
}
break;
case SPageInfo::EStatePagedOld:
// can't happen because we forced the page to be accessible earlier
__NK_ASSERT_ALWAYS(0);
return KErrCorrupt;
default:
return KErrNotFound;
}
aPhysAddr = phys;
#ifdef BTRACE_PAGING
BTraceContext8(BTrace::EPaging,BTrace::EPagingPageLock,phys,pageInfo->PagedLock());
#endif
return KErrNone;
}
TInt DemandPaging::UnlockPage(TLinAddr aPage, DProcess* aProcess, TPhysAddr aPhysAddr)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: UnlockPage() %08x",aPage));
__ASSERT_SYSTEM_LOCK;
__ASSERT_CRITICAL;
// Get info about page to be unlocked
TPhysAddr phys = LinearToPhysical(aPage,aProcess);
if(phys==KPhysAddrInvalid)
{
phys = aPhysAddr;
if(phys==KPhysAddrInvalid)
return KErrNotFound;
}
retry:
SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(phys);
if(!pageInfo)
return KErrNotFound;
SPageInfo::TType type = pageInfo->Type();
if(type==SPageInfo::EShadow)
{
// Get the page which is being shadowed and unlock that
phys = (TPhysAddr)pageInfo->Owner();
goto retry;
}
__NK_ASSERT_DEBUG(phys==aPhysAddr || aPhysAddr==KPhysAddrInvalid);
// Unlock it...
switch(pageInfo->State())
{
case SPageInfo::EStatePagedLocked:
#ifdef BTRACE_PAGING
BTraceContext8(BTrace::EPaging,BTrace::EPagingPageUnlock,phys,pageInfo->PagedLock());
#endif
if(!(--pageInfo->PagedLock()))
{
// get pointer to memory...
DMemModelCodeSegMemory* codeSegMemory = 0;
if(type==SPageInfo::EPagedCode)
codeSegMemory = (DMemModelCodeSegMemory*)pageInfo->Owner();
// put page back on live list...
AddAsYoungest(pageInfo);
BalanceAges();
// close reference on memory...
if(codeSegMemory)
{
NKern::UnlockSystem();
codeSegMemory->Close();
NKern::LockSystem();
}
}
break;
default:
return KErrNotFound;
}
return KErrNone;
}
TInt DemandPaging::ReserveAlloc(TInt aSize, DDemandPagingLock& aLock)
{
__NK_ASSERT_DEBUG(aLock.iPages == NULL);
// calculate the number of pages required to lock aSize bytes
TInt numPages = ((aSize-1+KPageMask)>>KPageShift)+1;
__KTRACE_OPT(KPAGING,Kern::Printf("DP: ReserveAlloc() pages %d",numPages));
NKern::ThreadEnterCS();
aLock.iPages = (TPhysAddr*)Kern::Alloc(numPages*sizeof(TPhysAddr));
if(!aLock.iPages)
{
NKern::ThreadLeaveCS();
return KErrNoMemory;
}
MmuBase::Wait();
NKern::LockSystem();
// reserve pages, adding more if necessary
while (aLock.iReservedPageCount < numPages)
{
if (!ReservePage())
break;
++aLock.iReservedPageCount;
}
NKern::UnlockSystem();
MmuBase::Signal();
TBool enoughPages = aLock.iReservedPageCount == numPages;
if(!enoughPages)
ReserveFree(aLock);
NKern::ThreadLeaveCS();
return enoughPages ? KErrNone : KErrNoMemory;
}
void DemandPaging::ReserveFree(DDemandPagingLock& aLock)
{
NKern::ThreadEnterCS();
// make sure pages aren't still locked
ReserveUnlock(aLock);
NKern::LockSystem();
__NK_ASSERT_DEBUG(iReservePageCount >= (TUint)aLock.iReservedPageCount);
iReservePageCount -= aLock.iReservedPageCount;
aLock.iReservedPageCount = 0;
NKern::UnlockSystem();
// free page array...
Kern::Free(aLock.iPages);
aLock.iPages = 0;
NKern::ThreadLeaveCS();
}
TBool DemandPaging::ReserveLock(DThread* aThread, TLinAddr aStart,TInt aSize, DDemandPagingLock& aLock)
{
if(aLock.iLockedPageCount)
Panic(ELockTwice);
// calculate the number of pages that need to be locked...
TUint32 mask=KPageMask;
TUint32 offset=aStart&mask;
TInt numPages = (aSize+offset+mask)>>KPageShift;
if(numPages>aLock.iReservedPageCount)
Panic(ELockTooBig);
NKern::LockSystem();
// lock the pages
TBool locked = EFalse; // becomes true if any pages were locked
DProcess* process = aThread->iOwningProcess;
TLinAddr page=aStart;
TInt count=numPages;
TPhysAddr* physPages = aLock.iPages;
while(--count>=0)
{
if(LockPage(page,process,*physPages)==KErrNone)
locked = ETrue;
NKern::FlashSystem();
page += KPageSize;
++physPages;
}
// if any pages were locked, save the lock info...
if(locked)
{
if(aLock.iLockedPageCount)
Panic(ELockTwice);
aLock.iLockedStart = aStart;
aLock.iLockedPageCount = numPages;
aLock.iProcess = process;
aLock.iProcess->Open();
}
NKern::UnlockSystem();
return locked;
}
void DemandPaging::ReserveUnlock(DDemandPagingLock& aLock)
{
NKern::ThreadEnterCS();
DProcess* process = NULL;
NKern::LockSystem();
TInt numPages = aLock.iLockedPageCount;
TLinAddr page = aLock.iLockedStart;
TPhysAddr* physPages = aLock.iPages;
while(--numPages>=0)
{
UnlockPage(page, aLock.iProcess,*physPages);
NKern::FlashSystem();
page += KPageSize;
++physPages;
}
process = aLock.iProcess;
aLock.iProcess = NULL;
aLock.iLockedPageCount = 0;
NKern::UnlockSystem();
if (process)
process->Close(NULL);
NKern::ThreadLeaveCS();
}
/**
Check whether the specified page can be discarded by the RAM cache.
@param aPageInfo The page info of the page being queried.
@return ETrue when the page can be discarded, EFalse otherwise.
@pre System lock held.
@post System lock held.
*/
TBool DemandPaging::IsPageDiscardable(SPageInfo& aPageInfo)
{
// on live list?
SPageInfo::TState state = aPageInfo.State();
return (state == SPageInfo::EStatePagedYoung || state == SPageInfo::EStatePagedOld);
}
/**
Discard the specified page.
Should only be called on a page if a previous call to IsPageDiscardable()
returned ETrue and the system lock hasn't been released between the calls.
@param aPageInfo The page info of the page to be discarded
@param aBlockZoneId The ID of the RAM zone that shouldn't be allocated into.
@param aBlockRest Set to ETrue to stop allocation as soon as aBlockedZoneId is reached
in preference ordering. EFalse otherwise.
@return ETrue if the page could be discarded, EFalse otherwise.
@pre System lock held.
@post System lock held.
*/
TBool DemandPaging::DoDiscardPage(SPageInfo& aPageInfo, TUint aBlockedZoneId, TBool aBlockRest)
{
__ASSERT_SYSTEM_LOCK;
// Ensure that we don't reduce the cache beyond its minimum.
if (iNumberOfFreePages == 0)
{
NKern::UnlockSystem();
SPageInfo* newPage = GetPageFromSystem(aBlockedZoneId, aBlockRest);
NKern::LockSystem();
if (newPage == NULL)
{// couldn't allocate a new page
return EFalse;
}
if (IsPageDiscardable(aPageInfo))
{// page can still be discarded so use new page
// and discard old one
AddAsFreePage(newPage);
RemovePage(&aPageInfo);
SetFree(&aPageInfo);
ReturnToSystem(&aPageInfo);
BalanceAges();
return ETrue;
}
else
{// page no longer discardable so no longer require new page
ReturnToSystem(newPage);
return EFalse;
}
}
// Discard the page
RemovePage(&aPageInfo);
SetFree(&aPageInfo);
ReturnToSystem(&aPageInfo);
BalanceAges();
return ETrue;
}
/**
First stage in discarding a list of pages.
Must ensure that the pages will still be discardable even if system lock is released.
To be used in conjunction with RamCacheBase::DoDiscardPages1().
@param aPageList A NULL terminated list of the pages to be discarded
@return KErrNone on success.
@pre System lock held
@post System lock held
*/
TInt DemandPaging::DoDiscardPages0(SPageInfo** aPageList)
{
__ASSERT_SYSTEM_LOCK;
SPageInfo* pageInfo;
while((pageInfo = *aPageList++) != 0)
{
RemovePage(pageInfo);
}
return KErrNone;
}
/**
Final stage in discarding a list of page
Finish discarding the pages previously removed by RamCacheBase::DoDiscardPages0().
@param aPageList A NULL terminated list of the pages to be discarded
@return KErrNone on success.
@pre System lock held
@post System lock held
*/
TInt DemandPaging::DoDiscardPages1(SPageInfo** aPageList)
{
__ASSERT_SYSTEM_LOCK;
SPageInfo* pageInfo;
while((pageInfo = *aPageList++)!=0)
{
SetFree(pageInfo);
ReturnToSystem(pageInfo);
BalanceAges();
}
return KErrNone;
}
TBool DemandPaging::MayBePaged(TLinAddr aStartAddr, TUint aLength)
{
TLinAddr endAddr = aStartAddr + aLength;
TBool rangeTouchesPagedRom =
TUint(aStartAddr - iRomPagedLinearBase) < iRomSize ||
TUint(endAddr - iRomPagedLinearBase) < iRomSize;
TBool rangeTouchesCodeArea =
TUint(aStartAddr - iCodeLinearBase) < iCodeSize ||
TUint(endAddr - iCodeLinearBase) < iCodeSize;
return rangeTouchesPagedRom || rangeTouchesCodeArea;
}
#ifdef __DEMAND_PAGING_BENCHMARKS__
void DemandPaging::ResetBenchmarkData(TPagingBenchmark aBm)
{
SPagingBenchmarkInfo& info = iBenchmarkInfo[aBm];
info.iCount = 0;
info.iTotalTime = 0;
info.iMaxTime = 0;
info.iMinTime = KMaxTInt;
}
void DemandPaging::RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime)
{
SPagingBenchmarkInfo& info = iBenchmarkInfo[aBm];
++info.iCount;
#if !defined(HIGH_RES_TIMER) || defined(HIGH_RES_TIMER_COUNTS_UP)
TInt64 elapsed = aEndTime - aStartTime;
#else
TInt64 elapsed = aStartTime - aEndTime;
#endif
info.iTotalTime += elapsed;
if (elapsed > info.iMaxTime)
info.iMaxTime = elapsed;
if (elapsed < info.iMinTime)
info.iMinTime = elapsed;
}
#endif
//
// DDemandPagingLock
//
EXPORT_C DDemandPagingLock::DDemandPagingLock()
: iThePager(DemandPaging::ThePager), iReservedPageCount(0), iLockedPageCount(0), iPages(0)
{
}
EXPORT_C TInt DDemandPagingLock::Alloc(TInt aSize)
{
if (iThePager)
return iThePager->ReserveAlloc(aSize,*this);
else
return KErrNone;
}
EXPORT_C void DDemandPagingLock::DoUnlock()
{
if (iThePager)
iThePager->ReserveUnlock(*this);
}
EXPORT_C void DDemandPagingLock::Free()
{
if (iThePager)
iThePager->ReserveFree(*this);
}
EXPORT_C TInt Kern::InstallPagingDevice(DPagingDevice* aDevice)
{
if (DemandPaging::ThePager)
return DemandPaging::ThePager->InstallPagingDevice(aDevice);
else
return KErrNotSupported;
}
#else // !__DEMAND_PAGING__
EXPORT_C DDemandPagingLock::DDemandPagingLock()
: iLockedPageCount(0)
{
}
EXPORT_C TInt DDemandPagingLock::Alloc(TInt /*aSize*/)
{
return KErrNone;
}
EXPORT_C TBool DDemandPagingLock::Lock(DThread* /*aThread*/, TLinAddr /*aStart*/, TInt /*aSize*/)
{
return EFalse;
}
EXPORT_C void DDemandPagingLock::DoUnlock()
{
}
EXPORT_C void DDemandPagingLock::Free()
{
}
EXPORT_C TInt Kern::InstallPagingDevice(DPagingDevice* aDevice)
{
return KErrNotSupported;
}
#endif // __DEMAND_PAGING__
DMmuCodeSegMemory::DMmuCodeSegMemory(DEpocCodeSeg* aCodeSeg)
: DEpocCodeSegMemory(aCodeSeg), iCodeAllocBase(KMinTInt)
{
}
//#define __DUMP_BLOCKMAP_INFO
DMmuCodeSegMemory::~DMmuCodeSegMemory()
{
#ifdef __DEMAND_PAGING__
Kern::Free(iCodeRelocTable);
Kern::Free(iCodePageOffsets);
Kern::Free(iDataSectionMemory);
#endif
}
#ifdef __DEMAND_PAGING__
/**
Read and process the block map and related data.
*/
TInt DMmuCodeSegMemory::ReadBlockMap(const TCodeSegCreateInfo& aInfo)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: Reading block map for %C", iCodeSeg));
if (aInfo.iCodeBlockMapEntriesSize <= 0)
return KErrArgument; // no block map provided
// Get compression data
switch (aInfo.iCompressionType)
{
case KFormatNotCompressed:
iCompressionType = SRomPageInfo::ENoCompression;
break;
case KUidCompressionBytePair:
{
iCompressionType = SRomPageInfo::EBytePair;
if (!aInfo.iCodePageOffsets)
return KErrArgument;
TInt size = sizeof(TInt32) * (iPageCount + 1);
iCodePageOffsets = (TInt32*)Kern::Alloc(size);
if (!iCodePageOffsets)
return KErrNoMemory;
kumemget32(iCodePageOffsets, aInfo.iCodePageOffsets, size);
#ifdef __DUMP_BLOCKMAP_INFO
Kern::Printf("CodePageOffsets:");
for (TInt i = 0 ; i < iPageCount + 1 ; ++i)
Kern::Printf(" %08x", iCodePageOffsets[i]);
#endif
TInt last = 0;
for (TInt j = 0 ; j < iPageCount + 1 ; ++j)
{
if (iCodePageOffsets[j] < last ||
iCodePageOffsets[j] > (aInfo.iCodeLengthInFile + aInfo.iCodeStartInFile))
{
__NK_ASSERT_DEBUG(0);
return KErrCorrupt;
}
last = iCodePageOffsets[j];
}
}
break;
default:
return KErrNotSupported;
}
// Copy block map data itself...
#ifdef __DUMP_BLOCKMAP_INFO
Kern::Printf("Original block map");
Kern::Printf(" block granularity: %d", aInfo.iCodeBlockMapCommon.iBlockGranularity);
Kern::Printf(" block start offset: %x", aInfo.iCodeBlockMapCommon.iBlockStartOffset);
Kern::Printf(" start block address: %016lx", aInfo.iCodeBlockMapCommon.iStartBlockAddress);
Kern::Printf(" local drive number: %d", aInfo.iCodeBlockMapCommon.iLocalDriveNumber);
Kern::Printf(" entry size: %d", aInfo.iCodeBlockMapEntriesSize);
#endif
// Find relevant paging device
iCodeLocalDrive = aInfo.iCodeBlockMapCommon.iLocalDriveNumber;
if (TUint(iCodeLocalDrive) >= (TUint)KMaxLocalDrives)
{
__KTRACE_OPT(KPAGING,Kern::Printf("Bad local drive number"));
return KErrArgument;
}
DemandPaging* pager = DemandPaging::ThePager;
if (!pager->CodePagingDevice(iCodeLocalDrive).iInstalled)
{
__KTRACE_OPT(KPAGING,Kern::Printf("No paging device installed for drive"));
return KErrNotSupported;
}
DPagingDevice* device = pager->CodePagingDevice(iCodeLocalDrive).iDevice;
// Set code start offset
iCodeStartInFile = aInfo.iCodeStartInFile;
if (iCodeStartInFile < 0)
{
__KTRACE_OPT(KPAGING,Kern::Printf("Bad code start offset"));
return KErrArgument;
}
// Allocate buffer for block map and copy from user-side
TBlockMapEntryBase* buffer = (TBlockMapEntryBase*)Kern::Alloc(aInfo.iCodeBlockMapEntriesSize);
if (!buffer)
return KErrNoMemory;
kumemget32(buffer, aInfo.iCodeBlockMapEntries, aInfo.iCodeBlockMapEntriesSize);
#ifdef __DUMP_BLOCKMAP_INFO
Kern::Printf(" entries:");
for (TInt k = 0 ; k < aInfo.iCodeBlockMapEntriesSize / sizeof(TBlockMapEntryBase) ; ++k)
Kern::Printf(" %d: %d blocks at %08x", k, buffer[k].iNumberOfBlocks, buffer[k].iStartBlock);
#endif
// Initialise block map
TInt r = iBlockMap.Initialise(aInfo.iCodeBlockMapCommon,
buffer,
aInfo.iCodeBlockMapEntriesSize,
device->iReadUnitShift,
iCodeStartInFile + aInfo.iCodeLengthInFile);
if (r != KErrNone)
{
Kern::Free(buffer);
return r;
}
#if defined(__DUMP_BLOCKMAP_INFO) && defined(_DEBUG)
iBlockMap.Dump();
#endif
return KErrNone;
}
/**
Read code relocation table and import fixup table from user side.
*/
TInt DMmuCodeSegMemory::ReadFixupTables(const TCodeSegCreateInfo& aInfo)
{
__KTRACE_OPT(KPAGING,Kern::Printf("DP: Reading fixup tables for %C", iCodeSeg));
iCodeRelocTableSize = aInfo.iCodeRelocTableSize;
iImportFixupTableSize = aInfo.iImportFixupTableSize;
iCodeDelta = aInfo.iCodeDelta;
iDataDelta = aInfo.iDataDelta;
// round sizes to four-byte boundaris...
TInt relocSize = (iCodeRelocTableSize + 3) & ~3;
TInt fixupSize = (iImportFixupTableSize + 3) & ~3;
// copy relocs and fixups...
iCodeRelocTable = (TUint8*)Kern::Alloc(relocSize+fixupSize);
if (!iCodeRelocTable)
return KErrNoMemory;
iImportFixupTable = iCodeRelocTable + relocSize;
kumemget32(iCodeRelocTable, aInfo.iCodeRelocTable, relocSize);
kumemget32(iImportFixupTable, aInfo.iImportFixupTable, fixupSize);
return KErrNone;
}
#endif
TInt DMmuCodeSegMemory::Create(TCodeSegCreateInfo& aInfo)
{
TInt r = KErrNone;
if (!aInfo.iUseCodePaging)
iPageCount=(iRamInfo.iCodeSize+iRamInfo.iDataSize+KPageMask)>>KPageShift;
else
{
#ifdef __DEMAND_PAGING__
iDataSectionMemory = Kern::Alloc(iRamInfo.iDataSize);
if (!iDataSectionMemory)
return KErrNoMemory;
iPageCount=(iRamInfo.iCodeSize+KPageMask)>>KPageShift;
iDataPageCount=(iRamInfo.iDataSize+KPageMask)>>KPageShift;
r = ReadBlockMap(aInfo);
if (r != KErrNone)
return r;
iIsDemandPaged = ETrue;
iCodeSeg->iAttr |= ECodeSegAttCodePaged;
#endif
}
iCodeSeg->iSize = (iPageCount+iDataPageCount)<<KPageShift;
return r;
}
TInt DMmuCodeSegMemory::Loaded(TCodeSegCreateInfo& aInfo)
{
#ifdef __DEMAND_PAGING__
if(iIsDemandPaged)
{
TInt r = ReadFixupTables(aInfo);
if (r != KErrNone)
return r;
}
TAny* dataSection = iDataSectionMemory;
if(dataSection)
{
UNLOCK_USER_MEMORY();
memcpy(dataSection,(TAny*)iRamInfo.iDataLoadAddr,iRamInfo.iDataSize);
LOCK_USER_MEMORY();
iRamInfo.iDataLoadAddr = (TLinAddr)dataSection;
}
#endif
return KErrNone;
}
void DMmuCodeSegMemory::ApplyCodeFixups(TUint32* aBuffer, TLinAddr aDestAddress)
{
__NK_ASSERT_DEBUG(iRamInfo.iCodeRunAddr==iRamInfo.iCodeLoadAddr); // code doesn't work if this isn't true
START_PAGING_BENCHMARK;
TUint offset = aDestAddress - iRamInfo.iCodeRunAddr;
__ASSERT_ALWAYS(offset < (TUint)(iRamInfo.iCodeSize + iRamInfo.iDataSize), K::Fault(K::ECodeSegBadFixupAddress));
// Index tables are only valid for pages containg code
if (offset >= (TUint)iRamInfo.iCodeSize)
return;
UNLOCK_USER_MEMORY();
TInt page = offset >> KPageShift;
// Relocate code
if (iCodeRelocTableSize > 0)
{
TUint32* codeRelocTable32 = (TUint32*)iCodeRelocTable;
TUint startOffset = codeRelocTable32[page];
TUint endOffset = codeRelocTable32[page + 1];
__KTRACE_OPT(KPAGING, Kern::Printf("Performing code relocation: start == %x, end == %x", startOffset, endOffset));
__ASSERT_ALWAYS(startOffset <= endOffset && endOffset <= (TUint)iCodeRelocTableSize,
K::Fault(K::ECodeSegBadFixupTables));
TUint8* codeRelocTable8 = (TUint8*)codeRelocTable32;
const TUint16* ptr = (const TUint16*)(codeRelocTable8 + startOffset);
const TUint16* end = (const TUint16*)(codeRelocTable8 + endOffset);
const TUint32 codeDelta = iCodeDelta;
const TUint32 dataDelta = iDataDelta;
while (ptr < end)
{
TUint16 entry = *ptr++;
// address of word to fix up is sum of page start and 12-bit offset
TUint32* addr = (TUint32*)((TUint8*)aBuffer + (entry & 0x0fff));
TUint32 word = *addr;
#ifdef _DEBUG
TInt type = entry & 0xf000;
__NK_ASSERT_DEBUG(type == KTextRelocType || type == KDataRelocType);
#endif
if (entry < KDataRelocType /* => type == KTextRelocType */)
word += codeDelta;
else
word += dataDelta;
*addr = word;
}
}
// Fixup imports
if (iImportFixupTableSize > 0)
{
TUint32* importFixupTable32 = (TUint32*)iImportFixupTable;
TUint startOffset = importFixupTable32[page];
TUint endOffset = importFixupTable32[page + 1];
__KTRACE_OPT(KPAGING, Kern::Printf("Performing import fixup: start == %x, end == %x", startOffset, endOffset));
__ASSERT_ALWAYS(startOffset <= endOffset && endOffset <= (TUint)iImportFixupTableSize,
K::Fault(K::ECodeSegBadFixupTables));
TUint8* importFixupTable8 = (TUint8*)importFixupTable32;
const TUint16* ptr = (const TUint16*)(importFixupTable8 + startOffset);
const TUint16* end = (const TUint16*)(importFixupTable8 + endOffset);
while (ptr < end)
{
TUint16 offset = *ptr++;
// get word to write into that address
// (don't read as a single TUint32 because may not be word-aligned)
TUint32 wordLow = *ptr++;
TUint32 wordHigh = *ptr++;
TUint32 word = (wordHigh << 16) | wordLow;
__KTRACE_OPT(KPAGING, Kern::Printf("DP: Fixup %08x=%08x", iRamInfo.iCodeRunAddr+(page<<KPageShift)+offset, word));
*(TUint32*)((TLinAddr)aBuffer+offset) = word;
}
}
LOCK_USER_MEMORY();
END_PAGING_BENCHMARK(DemandPaging::ThePager, EPagingBmFixupCodePage);
}
TInt DMmuCodeSegMemory::ApplyCodeFixupsOnLoad(TUint32* aBuffer, TLinAddr aDestAddress)
{
#ifdef __DEMAND_PAGING__
TInt r=DemandPaging::ThePager->LockRegion((TLinAddr)aBuffer,KPageSize,&Kern::CurrentProcess());
if(r!=KErrNone)
return r;
#endif
ApplyCodeFixups(aBuffer,aDestAddress);
UNLOCK_USER_MEMORY();
CacheMaintenance::CodeChanged((TLinAddr)aBuffer, KPageSize);
LOCK_USER_MEMORY();
#ifdef __DEMAND_PAGING__
DemandPaging::ThePager->UnlockRegion((TLinAddr)aBuffer,KPageSize,&Kern::CurrentProcess());
#endif
return KErrNone;
}
#ifdef __DEMAND_PAGING__
TInt M::CreateVirtualPinObject(TVirtualPinObject*& aPinObject)
{
aPinObject = (TVirtualPinObject*) new DDemandPagingLock;
return aPinObject != NULL ? KErrNone : KErrNoMemory;
}
TInt M::PinVirtualMemory(TVirtualPinObject* aPinObject, TLinAddr aStart, TUint aSize, DThread* aThread)
{
if (!DemandPaging::ThePager)
return KErrNone;
if (!DemandPaging::ThePager->MayBePaged(aStart, aSize))
return KErrNone;
DDemandPagingLock* lock = (DDemandPagingLock*)aPinObject;
TInt r = lock->Alloc(aSize);
if (r != KErrNone)
return r;
lock->Lock(aThread, aStart, aSize);
return KErrNone;
}
TInt M::CreateAndPinVirtualMemory(TVirtualPinObject*& aPinObject, TLinAddr aStart, TUint aSize)
{
aPinObject = 0;
if (!DemandPaging::ThePager)
return KErrNone;
if (!DemandPaging::ThePager->MayBePaged(aStart, aSize))
return KErrNone;
TInt r = CreateVirtualPinObject(aPinObject);
if (r != KErrNone)
return r;
DDemandPagingLock* lock = (DDemandPagingLock*)aPinObject;
r = lock->Alloc(aSize);
if (r != KErrNone)
return r;
lock->Lock(TheCurrentThread, aStart, aSize);
return KErrNone;
}
void M::UnpinVirtualMemory(TVirtualPinObject* aPinObject)
{
DDemandPagingLock* lock = (DDemandPagingLock*)aPinObject;
if (lock)
lock->Free();
}
void M::DestroyVirtualPinObject(TVirtualPinObject*& aPinObject)
{
DDemandPagingLock* lock = (DDemandPagingLock*)__e32_atomic_swp_ord_ptr(&aPinObject, 0);
if (lock)
lock->AsyncDelete();
}
#else
class TVirtualPinObject
{
};
TInt M::CreateVirtualPinObject(TVirtualPinObject*& aPinObject)
{
aPinObject = new TVirtualPinObject;
return aPinObject != NULL ? KErrNone : KErrNoMemory;
}
TInt M::PinVirtualMemory(TVirtualPinObject* aPinObject, TLinAddr, TUint, DThread*)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EVirtualPinObjectBad));
(void)aPinObject;
return KErrNone;
}
TInt M::CreateAndPinVirtualMemory(TVirtualPinObject*& aPinObject, TLinAddr, TUint)
{
aPinObject = 0;
return KErrNone;
}
void M::UnpinVirtualMemory(TVirtualPinObject* aPinObject)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EVirtualPinObjectBad));
(void)aPinObject;
}
void M::DestroyVirtualPinObject(TVirtualPinObject*& aPinObject)
{
TVirtualPinObject* object = (TVirtualPinObject*)__e32_atomic_swp_ord_ptr(&aPinObject, 0);
if (object)
Kern::AsyncFree(object);
}
#endif
TInt M::CreatePhysicalPinObject(TPhysicalPinObject*& aPinObject)
{
return KErrNotSupported;
}
TInt M::PinPhysicalMemory(TPhysicalPinObject*, TLinAddr, TUint, TBool, TUint32&, TUint32*, TUint32&, TUint&, DThread*)
{
K::Fault(K::EPhysicalPinObjectBad);
return KErrNone;
}
void M::UnpinPhysicalMemory(TPhysicalPinObject* aPinObject)
{
K::Fault(K::EPhysicalPinObjectBad);
}
void M::DestroyPhysicalPinObject(TPhysicalPinObject*& aPinObject)
{
K::Fault(K::EPhysicalPinObjectBad);
}
//
// Kernel map and pin (Not supported on the moving or multiple memory models).
//
TInt M::CreateKernelMapObject(TKernelMapObject*&, TUint)
{
return KErrNotSupported;
}
TInt M::MapAndPinMemory(TKernelMapObject*, DThread*, TLinAddr, TUint, TUint, TLinAddr&, TPhysAddr*)
{
return KErrNotSupported;
}
void M::UnmapAndUnpinMemory(TKernelMapObject*)
{
}
void M::DestroyKernelMapObject(TKernelMapObject*&)
{
}
// Misc DPagingDevice methods
EXPORT_C NFastMutex* DPagingDevice::NotificationLock()
{
// use the system lock
return &TheScheduler.iLock;
}
EXPORT_C void DPagingDevice::NotifyIdle()
{
// Not used on this memory model
}
EXPORT_C void DPagingDevice::NotifyBusy()
{
// Not used on this memory model
}
EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaWrite(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaWrite");
return KErrNotSupported;
}
EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaRead(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaRead");
return KErrNotSupported;
}
EXPORT_C TInt Cache::SyncPhysicalMemoryAfterDmaRead(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryAfterDmaRead");
return KErrNotSupported;
}
//
// Page moving methods
//
/*
* Move a page from aOld to aNew safely, updating any references to the page
* stored elsewhere (such as page table entries). The destination page must
* already be allocated. If the move is successful, the source page will be
* freed and returned to the allocator.
*
* @pre RAM alloc mutex must be held.
* @pre Calling thread must be in a critical section.
* @pre Interrupts must be enabled.
* @pre Kernel must be unlocked.
* @pre No fast mutex can be held.
* @pre Call in a thread context.
*/
TInt MmuBase::MovePage(TPhysAddr aOld, TPhysAddr& aNew, TUint aBlockZoneId, TBool aBlockRest)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "Defrag::DoMovePage");
__ASSERT_WITH_MESSAGE_MUTEX(MmuBase::RamAllocatorMutex, "Ram allocator mutex must be held", "Defrag::DoMovePage");
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage() old=%08x",aOld));
TInt r = KErrNotSupported;
#if defined(__CPU_X86) && defined(__MEMMODEL_MULTIPLE__)
return r;
#endif
aNew = KPhysAddrInvalid;
NKern::LockSystem();
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(aOld);
if (!pi)
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage() fails: page has no PageInfo"));
r = KErrArgument;
goto fail;
}
if (pi->LockCount())
{
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage() fails: page is locked"));
goto fail;
}
switch(pi->Type())
{
case SPageInfo::EUnused:
// Nothing to do - we allow this, though, in case the caller wasn't
// actually checking the free bitmap.
r = KErrNotFound;
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage(): page unused"));
break;
case SPageInfo::EChunk:
{
// It's a chunk - we need to investigate what it's used for.
DChunk* chunk = (DChunk*)pi->Owner();
TInt offset = pi->Offset()<<KPageShift;
switch(chunk->iChunkType)
{
case EKernelData:
case EKernelMessage:
// The kernel data/bss/heap chunk pages are not moved as DMA may be accessing them.
__KTRACE_OPT(KMMU, Kern::Printf("MmuBase::MovePage() fails: kernel data"));
goto fail;
case EKernelStack:
// The kernel thread stack chunk.
r = MoveKernelStackPage(chunk, offset, aOld, aNew, aBlockZoneId, aBlockRest);
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: k stack r%d",r));
__NK_ASSERT_DEBUG(NKern::HeldFastMutex()==0);
goto released;
case EKernelCode:
case EDll:
// The kernel code chunk, or a global user code chunk.
r = MoveCodeChunkPage(chunk, offset, aOld, aNew, aBlockZoneId, aBlockRest);
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: code chk r%d",r));
__NK_ASSERT_DEBUG(NKern::HeldFastMutex()==0);
goto released;
case ERamDrive:
case EUserData:
case EDllData:
case EUserSelfModCode:
// A data chunk of some description.
r = MoveDataChunkPage(chunk, offset, aOld, aNew, aBlockZoneId, aBlockRest);
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: data chk r%d",r));
__NK_ASSERT_DEBUG(NKern::HeldFastMutex()==0);
goto released;
case ESharedKernelSingle:
case ESharedKernelMultiple:
case ESharedIo:
case ESharedKernelMirror:
// These chunk types cannot be moved
r = KErrNotSupported;
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: shared r%d",r));
break;
case EUserCode:
default:
// Unknown page type, or EUserCode.
// EUserCode is not used in moving model, and on multiple model
// it never owns any pages so shouldn't be found via SPageInfo
__KTRACE_OPT(KMMU,Kern::Printf("Defrag::DoMovePage fails: unknown chunk type %d",chunk->iChunkType));
Panic(EDefragUnknownChunkType);
}
}
break;
case SPageInfo::ECodeSegMemory:
// It's a code segment memory section (multiple model only)
r = MoveCodeSegMemoryPage((DMemModelCodeSegMemory*)pi->Owner(), pi->Offset()<<KPageShift, aOld, aNew, aBlockZoneId, aBlockRest);
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: codeseg r%d",r));
__NK_ASSERT_DEBUG(NKern::HeldFastMutex()==0);
goto released;
case SPageInfo::EPagedROM:
case SPageInfo::EPagedCode:
case SPageInfo::EPagedData:
case SPageInfo::EPagedCache:
case SPageInfo::EPagedFree:
{// DP or RamCache page so attempt to discard it. Added for testing purposes only
// In normal use ClearDiscardableFromZone will have already removed RAM cache pages
r = KErrInUse;
MmuBase& mmu = *MmuBase::TheMmu;
RamCacheBase& ramCache = *(mmu.iRamCache);
if (ramCache.IsPageDiscardable(*pi))
{
if (ramCache.DoDiscardPage(*pi, KRamZoneInvalidId, EFalse))
{// Sucessfully discarded the page.
r = KErrNone;
}
}
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: paged r%d",r));
goto fail; // Goto fail to release the system lock.
}
case SPageInfo::EPageTable:
case SPageInfo::EPageDir:
case SPageInfo::EPtInfo:
case SPageInfo::EInvalid:
case SPageInfo::EFixed:
case SPageInfo::EShadow:
// These page types cannot be moved (or don't need to be moved)
r = KErrNotSupported;
__KTRACE_OPT(KMMU,if (r!=KErrNone) Kern::Printf("MmuBase::MovePage() fails: PT etc r%d",r));
break;
default:
// Unknown page type
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage() fails: unknown page type %d",pi->Type()));
Panic(EDefragUnknownPageType);
}
fail:
NKern::UnlockSystem();
released:
__KTRACE_OPT(KMMU,Kern::Printf("MmuBase::MovePage() returns %d",r));
return r;
}
TInt MmuBase::DiscardPage(TPhysAddr aAddr, TUint aBlockZoneId, TBool aBlockRest)
{
TInt r = KErrInUse;
NKern::LockSystem();
SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(aAddr);
if (pageInfo != NULL)
{// Allocatable page at this address so is it a discardable one?
if (iRamCache->IsPageDiscardable(*pageInfo))
{
// Discard this page and return it to the ram allocator
if (!iRamCache->DoDiscardPage(*pageInfo, aBlockZoneId, aBlockRest))
{// Couldn't discard the page.
if (aBlockRest)
{
__KTRACE_OPT(KMMU, Kern::Printf("ClearDiscardableFromZone: page discard fail addr %x", aAddr));
NKern::UnlockSystem();
return KErrNoMemory;
}
}
else
{// Page discarded successfully.
r = KErrNone;
}
}
}
NKern::UnlockSystem();
return r;
}
TUint MmuBase::NumberOfFreeDpPages()
{
TUint free = 0;
if(iRamCache)
{
free = iRamCache->NumberOfFreePages();
}
return free;
}
EXPORT_C TInt Epoc::MovePhysicalPage(TPhysAddr aOld, TPhysAddr& aNew, TRamDefragPageToMove aPageToMove)
{
CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::MovePhysicalPage");
__KTRACE_OPT(KMMU,Kern::Printf("Epoc::MovePhysicalPage() old=%08x pageToMove=%d",aOld,aPageToMove));
switch(aPageToMove)
{
case ERamDefragPage_Physical:
break;
default:
return KErrNotSupported;
}
MmuBase::Wait();
TInt r=M::MovePage(aOld,aNew,KRamZoneInvalidId,EFalse);
if (r!=KErrNone)
aNew = KPhysAddrInvalid;
MmuBase::Signal();
__KTRACE_OPT(KMMU,Kern::Printf("Epoc::MovePhysicalPage() returns %d",r));
return r;
}
TInt M::RamDefragFault(TAny* aExceptionInfo)
{
// If the mmu has been initialised then let it try processing the fault.
if(MmuBase::TheMmu)
return MmuBase::TheMmu->RamDefragFault(aExceptionInfo);
return KErrAbort;
}
void M::RamZoneClaimed(SZone* aZone)
{
// Lock each page. OK to traverse SPageInfo array as we know no unknown
// pages are in the zone.
SPageInfo* pageInfo = SPageInfo::FromPhysAddr(aZone->iPhysBase);
SPageInfo* pageInfoEnd = pageInfo + aZone->iPhysPages;
for (; pageInfo < pageInfoEnd; ++pageInfo)
{
NKern::LockSystem();
__NK_ASSERT_DEBUG(pageInfo->Type()==SPageInfo::EUnused);
pageInfo->Lock();
NKern::UnlockSystem();
}
// For the sake of platform security we have to clear the memory. E.g. the driver
// could assign it to a chunk visible to user side. Set LSB so ClearPages
// knows this is a contiguous memory region.
Mmu::Get().ClearPages(aZone->iPhysPages, (TPhysAddr*)(aZone->iPhysBase|1));
}