Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// Copyright (c) 1994-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\moving\mchunk.cpp
//
//
#include "memmodel.h"
#include "cache_maintenance.h"
#include <mmubase.inl>
#include <ramalloc.h>
DMemModelChunk::DMemModelChunk()
{
}
void DMemModelChunk::Destruct()
{
__KTRACE_OPT(KTHREAD,Kern::Printf("DMemModelChunk destruct %O",this));
Mmu& m = Mmu::Get();
TInt nPdes=iMaxSize>>m.iChunkShift;
if (nPdes<=32 || iPdeBitMap!=NULL)
{
if ((iAttributes & EDisconnected) && iPageBitMap!=NULL)
Decommit(0,iMaxSize);
else if (iAttributes & EDoubleEnded)
AdjustDoubleEnded(0,0);
else
Adjust(0);
}
if ((iAttributes&EFixedAddress) && iHomeRegionBase>=m.iKernelSection->iBase)
{
Mmu::Wait();
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::~DMemModelChunk remove region"));
if (TLinAddr(iBase)==iHomeBase)
iBase=NULL;
DeallocateHomeAddress(); // unlink from home section queue
iHomeRegionBase=0;
iHomeBase=0;
Mmu::Signal();
}
if ((iMaxSize>>m.iChunkShift) > 32)
{
TAny* pM = __e32_atomic_swp_ord_ptr(&iPdeBitMap, 0);
Kern::Free(pM);
}
TBitMapAllocator* pM = (TBitMapAllocator*)__e32_atomic_swp_ord_ptr(&iPageBitMap, 0);
delete pM;
pM = (TBitMapAllocator*)__e32_atomic_swp_ord_ptr(&iPermanentPageBitMap, 0);
delete pM;
TDfc* dfc = (TDfc*)__e32_atomic_swp_ord_ptr(&iDestroyedDfc, 0);
if(dfc)
dfc->Enque();
__KTRACE_OPT(KMEMTRACE, {Mmu::Wait(); Kern::Printf("MT:D %d %x %O",NTickCount(),this,this);Mmu::Signal();});
#ifdef BTRACE_CHUNKS
BTraceContext4(BTrace::EChunks,BTrace::EChunkDestroyed,this);
#endif
}
TInt DMemModelChunk::Close(TAny* aPtr)
{
if (aPtr)
{
DMemModelProcess* pP=(DMemModelProcess*)aPtr;
pP->RemoveChunk(this);
}
TInt r=Dec();
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Close %d %O",r,this));
__NK_ASSERT_DEBUG(r > 0); // Should never be negative.
if (r==1)
{
K::ObjDelete(this);
return EObjectDeleted;
}
return 0;
}
TUint8* DMemModelChunk::Base(DProcess* aProcess)
{
return iBase;
}
TInt DMemModelChunk::DoCreate(SChunkCreateInfo& aInfo)
{
__ASSERT_COMPILE(!(EMMChunkAttributesMask & EChunkAttributesMask));
if (aInfo.iMaxSize<=0)
return KErrArgument;
Mmu& m=Mmu::Get();
TInt nPdes=(aInfo.iMaxSize+m.iChunkMask)>>m.iChunkShift;
iMaxSize=nPdes<<m.iChunkShift;
iMapAttr = aInfo.iMapAttr;
SetupPermissions();
if (nPdes>32)
{
TInt words=(nPdes+31)>>5;
iPdeBitMap=(TUint32*)Kern::Alloc(words*sizeof(TUint32));
if (!iPdeBitMap)
return KErrNoMemory;
memclr(iPdeBitMap, words*sizeof(TUint32));
}
else
iPdeBitMap=NULL;
TInt maxpages=iMaxSize>>m.iPageShift;
if (iAttributes & EDisconnected)
{
TBitMapAllocator* pM=TBitMapAllocator::New(maxpages,ETrue);
if (!pM)
return KErrNoMemory;
iPageBitMap=pM;
__KTRACE_OPT(KMMU,Kern::Printf("PageBitMap at %08x, MaxPages %d",pM,maxpages));
}
if(iChunkType==ESharedKernelSingle || iChunkType==ESharedKernelMultiple)
{
TBitMapAllocator* pM=TBitMapAllocator::New(maxpages,ETrue);
if (!pM)
return KErrNoMemory;
iPermanentPageBitMap = pM;
}
__KTRACE_OPT(KMEMTRACE, {Mmu::Wait();Kern::Printf("MT:C %d %x %O",NTickCount(),this,this);Mmu::Signal();});
#ifdef BTRACE_CHUNKS
TKName nameBuf;
Name(nameBuf);
BTraceContextN(BTrace::EChunks,BTrace::EChunkCreated,this,iMaxSize,nameBuf.Ptr(),nameBuf.Size());
if(iOwningProcess)
BTrace8(BTrace::EChunks,BTrace::EChunkOwner,this,iOwningProcess);
BTraceContext12(BTrace::EChunks,BTrace::EChunkInfo,this,iChunkType,iAttributes);
#endif
return KErrNone;
}
void DMemModelChunk::ClaimInitialPages()
{
__KTRACE_OPT(KMMU,Kern::Printf("Chunk %O ClaimInitialPages()",this));
Mmu& m=Mmu::Get();
TInt offset=0;
TUint32 ccp=K::CompressKHeapPtr(this);
NKern::LockSystem();
while(offset<iSize)
{
TInt ptid=m.GetPageTableId(TLinAddr(iBase)+offset);
__ASSERT_ALWAYS(ptid>=0,MM::Panic(MM::EClaimInitialPagesBadPageTable));
__KTRACE_OPT(KMMU,Kern::Printf("Offset %x PTID=%d",offset,ptid));
AddPde(offset);
SPageTableInfo& ptinfo = m.PtInfo(ptid);
ptinfo.SetChunk(ccp,offset>>m.iChunkShift);
TPte* pPte=(TPte*)m.PageTableLinAddr(ptid);
TInt i;
TInt np = 0;
TInt flashCount = MM::MaxPagesInOneGo;
for (i=0; i<m.iChunkSize>>m.iPageShift; ++i, offset+=m.iPageSize)
{
if(--flashCount<=0)
{
flashCount = MM::MaxPagesInOneGo;
NKern::FlashSystem();
}
TPte pte=pPte[i];
if (m.PteIsPresent(pte))
{
++np;
TPhysAddr phys=m.PtePhysAddr(pte, i);
__KTRACE_OPT(KMMU,Kern::Printf("Offset %x phys %08x",offset,phys));
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(phys);
if (pi)
{
pi->SetChunk(this,offset>>m.iPageShift);
#ifdef BTRACE_KERNEL_MEMORY
--Epoc::KernelMiscPages; // page now owned by chunk, and is not 'miscelaneous'
#endif
}
}
}
ptinfo.iCount = np;
__KTRACE_OPT(KMMU,Kern::Printf("Offset %x PTID %d NP %d", offset, ptid, np));
}
NKern::UnlockSystem();
__KTRACE_OPT(KMMU,Kern::Printf("nPdes=%d, Pdes=%08x, HomePdes=%08x",iNumPdes,iPdes,iHomePdes));
}
void DMemModelChunk::SetFixedAddress(TLinAddr aAddr, TInt aInitialSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O SetFixedAddress %08X size %08X",this,aAddr,aInitialSize));
iHomeRegionOffset=0;
iHomeRegionBase=aAddr;
iHomeBase=aAddr;
iBase=(TUint8*)aAddr;
iHomeRegionSize=iMaxSize;
iAttributes|=EFixedAddress;
iSize=Mmu::RoundToPageSize(aInitialSize);
ClaimInitialPages();
}
TInt DMemModelChunk::Reserve(TInt aInitialSize)
//
// Reserve home section address space for a chunk
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O Reserve() size %08x",this,aInitialSize));
iHomeRegionOffset=0;
if (!K::Initialising)
Mmu::Wait();
iHomeRegionBase=AllocateHomeAddress(iMaxSize);
if (!K::Initialising)
Mmu::Signal();
iHomeBase=iHomeRegionBase;
iBase=(TUint8*)iHomeRegionBase;
if (iHomeRegionBase==0)
return KErrNoMemory;
iSize=Mmu::RoundToPageSize(aInitialSize);
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O address %08x",this,iHomeRegionBase));
ClaimInitialPages();
return KErrNone;
}
TInt DMemModelChunk::Adjust(TInt aNewSize)
//
// Adjust a standard chunk.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Adjust %08x",aNewSize));
if (iAttributes & (EDoubleEnded|EDisconnected))
return KErrGeneral;
if (aNewSize<0 || aNewSize>iMaxSize)
return KErrArgument;
TInt r=KErrNone;
TInt newSize=Mmu::RoundToPageSize(aNewSize);
if (newSize!=iSize)
{
Mmu::Wait();
if (newSize>iSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Adjust growing"));
r=DoCommit(iSize,newSize-iSize);
}
else if (newSize<iSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Adjust shrinking"));
DoDecommit(newSize,iSize-newSize);
}
Mmu::Signal();
}
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O adjusted to %x base %08x home %08x",this,iSize,iBase,iHomeRegionBase));
return r;
}
TInt DMemModelChunk::ExpandHomeRegion(TInt aOffset, TInt aSize)
{
// Ensure that the chunk's home region is big enough to accommodate extra RAM being committed
__KTRACE_OPT(KMMU,Kern::Printf("Chunk %O ExpandHomeRegion(%x,%x)",this,aOffset,aSize));
Mmu& m = Mmu::Get();
TBool lowerLimitOk=(aOffset>=iHomeRegionOffset && aOffset<=iHomeRegionOffset+iHomeRegionSize);
TBool upperLimitOk=(aOffset+aSize>=iHomeRegionOffset && aOffset+aSize<=iHomeRegionOffset+iHomeRegionSize);
if (lowerLimitOk && upperLimitOk)
return KErrNone; // no change required
TInt newLowerLimit;
TInt newUpperLimit;
if (iHomeRegionSize)
{
newLowerLimit=Min(iHomeRegionOffset,aOffset);
newUpperLimit=Max(iHomeRegionOffset+iHomeRegionSize,aOffset+aSize);
}
else
{
newLowerLimit=aOffset;
newUpperLimit=aOffset+aSize;
}
newLowerLimit &= ~m.iChunkMask;
newUpperLimit = (newUpperLimit+m.iChunkMask)&~m.iChunkMask;
TInt newHomeRegionSize=newUpperLimit-newLowerLimit;
__KTRACE_OPT(KMMU,Kern::Printf("newLowerLimit=%x, newUpperLimit=%x",newLowerLimit,newUpperLimit));
if (newHomeRegionSize>iMaxSize)
return KErrArgument;
TLinAddr newHomeRegionBase;
if (iHomeRegionSize==0)
newHomeRegionBase=AllocateHomeAddress(newHomeRegionSize);
else
newHomeRegionBase=ReallocateHomeAddress(newHomeRegionSize);
__KTRACE_OPT(KMMU,Kern::Printf("newHomeRegionBase=%08x",newHomeRegionBase));
if (newHomeRegionBase==0)
return KErrNoMemory;
TInt deltaOffset=iHomeRegionOffset-newLowerLimit;
TLinAddr newHomeBase=newHomeRegionBase-newLowerLimit;
TLinAddr translatedHomeBase=newHomeRegionBase+deltaOffset;
// lock the kernel while we change the chunk's home region
// Note: The new home region always contains the original home region, so
// if we reach here, it must be strictly larger.
NKern::LockSystem();
if (iNumPdes && iHomeRegionBase!=translatedHomeBase)
{
TLinAddr oldBase=TLinAddr(iBase);
if (oldBase==iHomeBase)
{
// chunk is currently at home, so must move it
// Note: this operation must cope with overlapping initial and final regions
m.GenericFlush(Mmu::EFlushDMove); // preemption could occur here...
if (TLinAddr(iBase)==iHomeBase) // ...so need to check chunk is still at home address
{
m.MoveChunk(iHomeRegionBase,translatedHomeBase,iNumPdes);
iBase=(TUint8*)newHomeBase;
MoveCurrentPdes(iHomeRegionBase,translatedHomeBase);
MoveHomePdes(iHomeRegionBase,translatedHomeBase);
}
}
else
{
MoveHomePdes(iHomeRegionBase,translatedHomeBase);
}
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::ExpandHomeRegion moved home base from %08x to %08x",
iHomeRegionBase,newHomeRegionBase));
}
if (!iBase)
iBase=(TUint8*)newHomeBase;
iHomeRegionBase=newHomeRegionBase;
iHomeRegionOffset=newLowerLimit;
iHomeBase=newHomeBase;
__KTRACE_OPT(KMMU,Kern::Printf("Final iHomeRegionBase=%08x, iHomeRegionOffset=%08x",iHomeRegionBase,iHomeRegionOffset));
__KTRACE_OPT(KMMU,Kern::Printf("Final iHomeRegionSize=%08x, iBase=%08x, iHomeBase=%08x",iHomeRegionSize,iBase,iHomeBase));
__KTRACE_OPT(KMMU,Kern::Printf("nPdes=%d, Pdes=%08x, HomePdes=%08x",iNumPdes,iPdes,iHomePdes));
NKern::UnlockSystem();
return KErrNone;
}
TInt DMemModelChunk::Address(TInt aOffset, TInt aSize, TLinAddr& aKernelAddress)
{
if(!iPermanentPageBitMap)
return KErrAccessDenied;
if(TUint(aOffset)>=TUint(iMaxSize))
return KErrArgument;
if(TUint(aOffset+aSize)>TUint(iMaxSize))
return KErrArgument;
if(aSize<=0)
return KErrArgument;
TInt pageShift = Mmu::Get().iPageShift;
TInt start = aOffset>>pageShift;
TInt size = ((aOffset+aSize-1)>>pageShift)-start+1;
if(iPermanentPageBitMap->NotAllocated(start,size))
return KErrNotFound;
aKernelAddress = (TLinAddr)iBase+aOffset;
return KErrNone;
}
TInt DMemModelChunk::PhysicalAddress(TInt aOffset, TInt aSize, TLinAddr& aKernelAddress, TUint32& aPhysicalAddress, TUint32* aPhysicalPageList)
{
TInt r=Address(aOffset,aSize,aKernelAddress);
if(r!=KErrNone)
return r;
return Mmu::Get().LinearToPhysical(aKernelAddress,aSize,aPhysicalAddress,aPhysicalPageList);
}
void DMemModelChunk::Substitute(TInt aOffset, TPhysAddr aOldAddr, TPhysAddr aNewAddr)
{
// Substitute the page mapping at aOffset with aNewAddr.
// Called with the system lock held.
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Substitute %x %08x %08x",aOffset,aOldAddr,aNewAddr));
Mmu& m = Mmu::Get();
TLinAddr addr=(TLinAddr)iBase+aOffset;
TInt ptid=m.GetPageTableId(addr);
if(ptid<0)
MM::Panic(MM::EChunkRemapNoPageTable);
m.RemapPage(ptid, addr, aOldAddr, aNewAddr, iPtePermissions, iOwningProcess);
if(iChunkType==EKernelCode || iChunkType==EDll || iChunkType==EUserSelfModCode)
m.SyncCodeMappings();
}
/**
Get the movability type of the chunk's pages
@return How movable the chunk's pages are
*/
TZonePageType DMemModelChunk::GetPageType()
{
// Shared chunks have their physical addresses available
if (iChunkType == ESharedKernelSingle ||
iChunkType == ESharedKernelMultiple ||
iChunkType == ESharedIo ||
iChunkType == ESharedKernelMirror ||
iChunkType == EKernelMessage ||
iChunkType == EKernelData) // Don't move kernel heap pages as DMA may be accessing them.
{
return EPageFixed;
}
// All other types of chunk are movable
return EPageMovable;
}
TInt DMemModelChunk::DoCommit(TInt aOffset, TInt aSize, TCommitType aCommitType, TUint32* aExtraArg)
{
// Commit more RAM to a chunk at a specified offset
// enter and leave with system unlocked
// must hold RamAlloc mutex before calling this function
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoCommit %x+%x type=%d extra=%08x",aOffset,aSize,aCommitType,aExtraArg));
TInt offset=aOffset;
TInt endOffset=offset+aSize;
TInt newPtId=-1;
Mmu& m = Mmu::Get();
DRamAllocator& a = *m.iRamPageAllocator;
TInt r=KErrNone;
TPhysAddr pageList[KMaxPages];
TPhysAddr* pPageList=0;
TPhysAddr nextPage=0;
TUint32 ccp=K::CompressKHeapPtr(this);
SPageInfo::TType type = SPageInfo::EChunk;
if (iHomeRegionSize==0 || (iAttributes&EFixedAddress)==0)
{
r=ExpandHomeRegion(aOffset,aSize);
if (r!=KErrNone)
return r;
}
// Set flag to indicate if RAM should be cleared before being committed.
// Note, EDll, EUserCode are covered in the code segment, in order not to clear
// the region overwritten by the loader
TBool clearRam = iChunkType==EUserData
|| iChunkType==EDllData
|| iChunkType==EUserSelfModCode
|| iChunkType==ESharedKernelSingle
|| iChunkType==ESharedKernelMultiple
|| iChunkType==ESharedIo
|| iChunkType==ERamDrive;
TBool ownsMemory = !(iAttributes&EMemoryNotOwned);
TBool physicalCommit = aCommitType&DChunk::ECommitPhysicalMask;
if(ownsMemory)
{
if(physicalCommit)
return KErrNotSupported;
}
else
{
if(!physicalCommit)
return KErrNotSupported;
type = SPageInfo::EInvalid; // to indicate page info not to be updated
}
switch(aCommitType)
{
case DChunk::ECommitDiscontiguous:
// No setup to do
break;
case DChunk::ECommitContiguous:
{
// Allocate a block of contiguous RAM from the free pool
TInt numPages=(endOffset-offset)>>m.iPageShift;
r=m.AllocContiguousRam(numPages<<m.iPageShift, nextPage, GetPageType(), 0);
if (r!=KErrNone)
return r;
if(clearRam)
m.ClearPages(numPages, (TPhysAddr*)(nextPage|1), iClearByte); // clear RAM if required
*aExtraArg = nextPage; // store physical address of RAM as return argument
}
break;
case DChunk::ECommitDiscontiguousPhysical:
{
pPageList = aExtraArg; // use pages given given to us
// Check address of pages are multiples of page size...
TInt numPages=(endOffset-offset)>>m.iPageShift;
TUint32* ptr = aExtraArg;
TUint32* endPtr = aExtraArg+numPages;
if(ptr>=endPtr)
return KErrNone; // Zero size commit is OK
TPhysAddr pageBits = 0;
do
pageBits |= *ptr++;
while(ptr<endPtr);
if(pageBits&(m.iPageSize-1))
return KErrArgument; // all addresses must be multiple of page size
}
break;
case DChunk::ECommitContiguousPhysical:
nextPage = (TPhysAddr)aExtraArg; // we have been given the physical address to use
if(nextPage&(m.iPageSize-1))
return KErrArgument; // address must be multiple of page size
break;
#ifdef __MARM__
case DChunk::ECommitVirtual:
break;
#endif
default:
return KErrNotSupported;
}
// Commit memory a bit at a time (so system lock is only needs to be held for limited time)
while(offset<endOffset)
{
TInt np=(endOffset-offset)>>m.iPageShift; // pages remaining to satisfy request
TInt npEnd=(m.iChunkSize-(offset&m.iChunkMask))>>m.iPageShift; // number of pages to end of page table
if (np>npEnd)
np=npEnd; // limit to single page table
if (np>MM::MaxPagesInOneGo)
np=MM::MaxPagesInOneGo; // limit
NKern::LockSystem(); // lock the system while we look at the page directory
TLinAddr addr=(TLinAddr)iBase+offset; // current address
TInt ptid=m.GetPageTableId(addr); // get page table ID if a page table is already assigned here
NKern::UnlockSystem(); // we can now unlock the system
newPtId=-1;
if (ptid<0)
{
// need to allocate a new page table
newPtId=m.AllocPageTable();
if (newPtId<0)
{
// out of memory, so break out and revert
r=KErrNoMemory;
break;
}
ptid=newPtId;
}
if(aCommitType==DChunk::ECommitDiscontiguous)
{
pPageList = pageList;
r=m.AllocRamPages(pPageList,np, GetPageType()); // try to allocate pages
if (r!=KErrNone)
break; // if we fail, break out and revert
if(clearRam)
m.ClearPages(np, pPageList, iClearByte); // clear RAM if required
}
// lock the system while we change the MMU mappings
NKern::LockSystem();
TInt commitSize = np<<m.iPageShift;
iSize += commitSize; // update committed size
if (aCommitType==DChunk::ECommitVirtual)
m.MapVirtual(ptid, np);
else if(pPageList)
{
m.MapRamPages(ptid, type, this, offset, pPageList, np, iPtePermissions);
pPageList += np;
}
else
{
m.MapPhysicalPages(ptid, type, this, offset, nextPage, np, iPtePermissions);
nextPage += commitSize;
}
NKern::UnlockSystem();
NKern::LockSystem();
if (newPtId>=0)
{
// We have allocated a new page table, now we must assign it and update PDE info
SPageTableInfo& pti=m.PtInfo(ptid);
pti.SetChunk(ccp, offset>>m.iChunkShift);
TLinAddr addr=(TLinAddr)iBase+offset; // current address
m.DoAssignPageTable(ptid, addr, iPdePermissions[iChunkState]);
AddPde(offset); // update PDE info
}
__KTRACE_OPT(KMMU,Kern::Printf("nPdes=%d, Pdes=%08x, HomePdes=%08x",iNumPdes,iPdes,iHomePdes));
NKern::UnlockSystem();
__KTRACE_OPT(KMEMTRACE,Kern::Printf("MT:A %d %x %x %O",NTickCount(),this,iSize,this));
#ifdef BTRACE_CHUNKS
BTraceContext12(BTrace::EChunks,ownsMemory?BTrace::EChunkMemoryAllocated:BTrace::EChunkMemoryAdded,this,offset,commitSize);
#endif
offset += commitSize; // update offset
}
if (r==KErrNone)
{
if(iPermanentPageBitMap)
iPermanentPageBitMap->Alloc(aOffset>>m.iPageShift,aSize>>m.iPageShift);
}
else
{
// we ran out of memory somewhere
// first check if we have an unassigned page table
if (newPtId>=0)
m.FreePageTable(newPtId); // free the unassigned page table
// now free any memory we succeeded in allocating and return the chunk to its initial state
DChunk::TDecommitType decommitType = aCommitType==DChunk::ECommitVirtual ?
DChunk::EDecommitVirtual : DChunk::EDecommitNormal;
DoDecommit(aOffset,offset-aOffset,decommitType);
if(aCommitType==DChunk::ECommitContiguous)
{
// Free the pages we allocated but didn't get around to commiting
TPhysAddr last = nextPage + ((endOffset-offset)>>m.iPageShift<<m.iPageShift);
while(nextPage<last)
{
a.FreeRamPage(nextPage, GetPageType());
nextPage += m.iPageSize;
}
*aExtraArg = KPhysAddrInvalid; // return invalid physical address
}
m.iAllocFailed=ETrue;
}
return r;
}
void DMemModelChunk::DoDecommit(TInt aOffset, TInt aSize, TDecommitType aDecommitType)
{
// Decommit RAM from a chunk at a specified offset
// enter and leave with kernel unlocked
// must hold RamAlloc mutex before calling this function
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoDecommit %x+%x",aOffset,aSize));
if (iHomeRegionBase==0)
return;
TBool ownsMemory = !(iAttributes&EMemoryNotOwned);
if (!ownsMemory)
{
// Physical memory not owned by the chunk also has to be evicted from cache(s).
// We cannot just purge, as it can still be in use by the driver. Therefore, we'll flush it.
// Purging physical memory from cache(s) that is owned by the chunk is done below.
CacheMaintenance::MemoryToPreserveAndReuse((TLinAddr)(iBase+aOffset), aSize, iMapAttr);
}
TInt offset=aOffset;
TInt endOffset=offset+aSize;
Mmu& m = Mmu::Get();
DRamAllocator& a = *m.iRamPageAllocator;
TPhysAddr pageList[KMaxPages];
#ifdef __CPU_WRITE_BACK_CACHE
TInt size_reduction = Min(aSize,iSize);
TBool selectiveFlush=((TUint)size_reduction<=(CacheMaintenance::SyncAllPerformanceThresholdPages()<<KPageShift));
#endif
while(offset<endOffset)
{
TInt np=(endOffset-offset)>>m.iPageShift; // number of pages remaining to decommit
TInt pdeEnd=(offset+m.iChunkSize)&~m.iChunkMask;
TInt npEnd=(pdeEnd-offset)>>m.iPageShift; // number of pages to end of page table
if (np>npEnd)
np=npEnd; // limit to single page table
if (np>MM::MaxPagesInOneGo)
np=MM::MaxPagesInOneGo; // limit
NKern::LockSystem(); // lock the system while we look at the page directory
TUint8* base=iBase; // save base address
TLinAddr addr=(TLinAddr)base+offset; // current address
TInt ptid=m.GetPageTableId(addr); // get page table ID if a page table is already assigned here
if (ptid>=0)
{
TInt nPtes=0;
TInt nFree=0;
// Unmap the pages, clear the PTEs and place the physical addresses of the now-free RAM pages in
// pageList. Return nPtes=number of pages placed in list, remain=number of PTEs remaining in page table
// This also invalidates any TLB entries for the unmapped pages.
// NB for WriteBack cache, we must also invalidate any cached entries for these pages - this might be done
// by invalidating entry-by-entry or by a complete cache flush at the end.
// NB For split TLB, ITLB may not be invalidated. In that case it will be invalidated by
// Mmu::SyncCodeMappings() at the end of the function.
TInt remain;
if (aDecommitType == EDecommitVirtual)
remain=m.UnmapVirtual(ptid,addr,np,pageList,ownsMemory,nPtes,nFree,iOwningProcess);
else
remain=m.UnmapPages(ptid,addr,np,pageList,ownsMemory,nPtes,nFree,iOwningProcess);
TInt decommitSize=nPtes<<m.iPageShift;
iSize-=decommitSize; // reduce the committed size
// if page table is now completely empty, unassign it and update chunk PDE info
remain &= KUnmapPagesCountMask;
if (remain==0)
{
m.DoUnassignPageTable(addr);
RemovePde(offset);
NKern::UnlockSystem();
m.FreePageTable(ptid);
NKern::LockSystem();
}
__KTRACE_OPT(KMMU,Kern::Printf("nPdes=%d, Pdes=%08x, HomePdes=%08x",iNumPdes,iPdes,iHomePdes));
#ifdef __CPU_WRITE_BACK_CACHE
if (selectiveFlush)
{
TInt n=np;
while(n && iBase==base) // reschedule may move base, but then cache will have been flushed so we can stop purging L1
{
CacheMaintenance::PageToReuseVirtualCache(addr);
addr+=m.iPageSize;
--n;
NKern::FlashSystem();
}
Mmu::Get().CacheMaintenanceOnDecommit(pageList, nFree); //On ARMv5, this deals with L2 cache only
}
#endif
NKern::UnlockSystem(); // we can now unlock the system
__KTRACE_OPT(KMEMTRACE,Kern::Printf("MT:A %d %x %x %O",NTickCount(),this,iSize,this));
#ifdef BTRACE_CHUNKS
if(nFree)
BTraceContext12(BTrace::EChunks,ownsMemory?BTrace::EChunkMemoryDeallocated:BTrace::EChunkMemoryRemoved,this,offset,nFree<<m.iPageShift);
#endif
// We can now return the decommitted pages to the free page list
if (nFree)
a.FreeRamPages(pageList,nFree, GetPageType());
offset+=(np<<m.iPageShift);
}
else
{
NKern::UnlockSystem();
__KTRACE_OPT(KMMU,Kern::Printf("No page table at %08x",addr));
if ((iAttributes&EDisconnected)==0)
MM::Panic(MM::EChunkDecommitNoPageTable);
offset=pdeEnd; // disconnected chunk - step on to next PDE
}
}
if (iSize==0 && (iAttributes&EFixedAddress)==0)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoDecommit remove region"));
NKern::LockSystem();
if (TLinAddr(iBase)==iHomeBase)
iBase=NULL;
DeallocateHomeAddress();
NKern::UnlockSystem();
}
#ifdef __CPU_WRITE_BACK_CACHE
if (!selectiveFlush)
{
NKern::LockSystem();
m.GenericFlush((TUint)Mmu::EFlushDDecommit); //Flush virtual DCache
CacheMaintenance::SyncPhysicalCache_All();
NKern::UnlockSystem();
}
#endif
if (iAttributes & ECode)
m.SyncCodeMappings(); // flush ITLB if necessary
}
TInt DMemModelChunk::AdjustDoubleEnded(TInt aBottom, TInt aTop)
//
// Adjust a double-ended chunk.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::AdjustDoubleEnded %x-%x",aBottom,aTop));
if ((iAttributes & (EDoubleEnded|EDisconnected))!=EDoubleEnded)
return KErrGeneral;
if (aTop<0 || aBottom<0 || aTop<aBottom || aTop>iMaxSize)
return KErrArgument;
Mmu& m = Mmu::Get();
aBottom &= ~m.iPageMask;
aTop=(aTop+m.iPageMask)&~m.iPageMask;
TInt newSize=aTop-aBottom;
if (newSize>iMaxSize)
return KErrArgument;
Mmu::Wait();
TInt initBottom=iStartPos;
TInt initTop=iStartPos+iSize;
TInt nBottom=Max(aBottom,iStartPos); // intersection bottom
TInt nTop=Min(aTop,iStartPos+iSize); // intersection top
TInt r=KErrNone;
if (nBottom<nTop)
{
__KTRACE_OPT(KMMU,Kern::Printf("Initial and final regions intersect"));
if (initBottom<nBottom)
{
iStartPos=aBottom;
DoDecommit(initBottom,nBottom-initBottom);
}
if (initTop>nTop)
DoDecommit(nTop,initTop-nTop); // this changes iSize
if (aBottom<nBottom)
{
r=DoCommit(aBottom,nBottom-aBottom);
if (r==KErrNone)
{
if (aTop>nTop)
r=DoCommit(nTop,aTop-nTop);
if (r==KErrNone)
iStartPos=aBottom;
else
DoDecommit(aBottom,nBottom-aBottom);
}
}
else if (aTop>nTop)
r=DoCommit(nTop,aTop-nTop);
}
else
{
__KTRACE_OPT(KMMU,Kern::Printf("Initial and final regions disjoint"));
if (iSize)
DoDecommit(initBottom,iSize);
iStartPos=aBottom;
if (newSize)
r=DoCommit(iStartPos,newSize);
}
Mmu::Signal();
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O adjusted to %x+%x base %08x home %08x",this,iStartPos,iSize,iBase,iHomeRegionBase));
return r;
}
TInt DMemModelChunk::Commit(TInt aOffset, TInt aSize, TCommitType aCommitType, TUint32* aExtraArg)
//
// Commit to a disconnected chunk.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Commit %x+%x type=%d extra=%08x",aOffset,aSize,aCommitType,aExtraArg));
if ((iAttributes & (EDoubleEnded|EDisconnected))!=EDisconnected)
return KErrGeneral;
if (aOffset<0 || aSize<0)
return KErrArgument;
if (aSize==0)
return KErrNone;
Mmu& m = Mmu::Get();
aSize+=(aOffset & m.iPageMask);
aOffset &= ~m.iPageMask;
aSize=(aSize+m.iPageMask)&~m.iPageMask;
if ((aOffset+aSize)>iMaxSize)
return KErrArgument;
Mmu::Wait();
TInt r=KErrNone;
TInt i=aOffset>>m.iPageShift;
TInt n=aSize>>m.iPageShift;
if (iPageBitMap->NotFree(i,n))
r=KErrAlreadyExists;
else
{
r=DoCommit(aOffset,aSize,aCommitType,aExtraArg);
if (r==KErrNone)
iPageBitMap->Alloc(i,n);
}
Mmu::Signal();
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
return r;
}
TInt DMemModelChunk::Allocate(TInt aSize, TInt aGuard, TInt aAlign)
//
// Allocate offset and commit to a disconnected chunk.
//
{
TInt r = DoAllocate(aSize, aGuard, aAlign, ETrue);
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
return r;
}
TInt DMemModelChunk::FindFree(TInt aSize, TInt aGuard, TInt aAlign)
//
// Find free offset but don't commit any memory.
//
{
return DoAllocate(aSize, aGuard, aAlign, EFalse);
}
TInt DMemModelChunk::DoAllocate(TInt aSize, TInt aGuard, TInt aAlign, TBool aCommit)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoAllocate %x %x %d",aSize,aGuard,aAlign));
// Only allow this to be called on disconnected chunks and not disconnected
// cache chunks as when guards pages exist the bit map can't be used to determine
// the size of disconnected cache chunks as is required by Decommit().
if ((iAttributes & (EDoubleEnded|EDisconnected|ECache))!=EDisconnected)
return KErrGeneral;
if (aSize<=0 || aGuard<0)
return KErrArgument;
Mmu& m = Mmu::Get();
aAlign=Max(aAlign-m.iPageShift,0);
aSize=(aSize+m.iPageMask)&~m.iPageMask;
aGuard=(aGuard+m.iPageMask)&~m.iPageMask;
if ((aSize+aGuard)>iMaxSize)
return KErrArgument;
Mmu::Wait();
TInt r=KErrNone;
TInt n=(aSize+aGuard)>>m.iPageShift;
TInt i=iPageBitMap->AllocAligned(n,aAlign,0,EFalse); // allocate the offset
if (i<0)
r=KErrNoMemory; // run out of reserved space for this chunk
else
{
TInt offset=i<<m.iPageShift;
__KTRACE_OPT(KMMU,Kern::Printf("Offset %x allocated",offset));
if (aCommit)
{
r=DoCommit(offset+aGuard,aSize,ECommitDiscontiguous);
if (r==KErrNone)
iPageBitMap->Alloc(i,n);
}
if (r==KErrNone)
r=offset; // if operation successful, return allocated offset
}
Mmu::Signal();
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoAllocate returns %x",r));
return r;
}
TInt DMemModelChunk::Decommit(TInt aOffset, TInt aSize)
//
// Decommit from a disconnected chunk.
//
{
return Decommit(aOffset, aSize, EDecommitNormal);
}
TInt DMemModelChunk::Decommit(TInt aOffset, TInt aSize, TDecommitType aDecommitType)
//
// Decommit from a disconnected chunk.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Decommit %x+%x",aOffset,aSize));
if ((iAttributes & (EDoubleEnded|EDisconnected))!=EDisconnected)
return KErrGeneral;
if (aOffset<0 || aSize<0)
return KErrArgument;
if (aSize==0)
return KErrNone;
Mmu& m = Mmu::Get();
aSize+=(aOffset & m.iPageMask);
aOffset &= ~m.iPageMask;
aSize=(aSize+m.iPageMask)&~m.iPageMask;
if ((aOffset+aSize)>iMaxSize)
return KErrArgument;
Mmu::Wait();
// limit the range to the home region range
TInt end = aOffset+aSize;
if (aOffset<iHomeRegionOffset)
aOffset=iHomeRegionOffset;
if (end>iHomeRegionOffset+iHomeRegionSize)
end=iHomeRegionOffset+iHomeRegionSize;
aSize = end-aOffset;
if(aSize<0)
aSize=0;
__KTRACE_OPT(KMMU,Kern::Printf("Rounded and Clipped range %x+%x",aOffset,aSize));
if (aSize)
{
TInt i=aOffset>>m.iPageShift;
TInt n=aSize>>m.iPageShift;
__KTRACE_OPT(KMMU,Kern::Printf("Calling SelectiveFree(%d,%d)",i,n));
TUint oldAvail = iPageBitMap->iAvail;
TUint oldSize = iSize;
// Free those positions which are still commited and also any guard pages,
// i.e. pages that are reserved in this chunk but which are not commited.
iPageBitMap->SelectiveFree(i,n);
DoDecommit(aOffset,aSize,aDecommitType);
if (iAttributes & ECache)
{// If this is the file server cache chunk then adjust the size based
// on the bit map size because:-
// - Unlocked and reclaimed pages will be unmapped without updating
// iSize or the bit map.
// - DoDecommit() only decommits the mapped pages.
// For all other chunks what is mapped is what is committed to the
// chunk so iSize is accurate.
TUint actualFreedPages = iPageBitMap->iAvail - oldAvail;
iSize = oldSize - (actualFreedPages << KPageShift);
}
}
Mmu::Signal();
__DEBUG_EVENT(EEventUpdateChunk, this);
return KErrNone;
}
TInt DMemModelChunk::Unlock(TInt aOffset, TInt aSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Unlock %x+%x",aOffset,aSize));
if (!(iAttributes&ECache))
return KErrGeneral;
if ((iAttributes & (EDoubleEnded|EDisconnected))!=EDisconnected)
return KErrGeneral;
// Mark this as the file server cache chunk. This is safe as it is only the
// file server that can invoke this function.
iAttributes |= ECache;
if (aOffset<0 || aSize<0)
return KErrArgument;
if (aSize==0)
return KErrNone;
Mmu& m = Mmu::Get();
aSize+=(aOffset & m.iPageMask);
aOffset &= ~m.iPageMask;
aSize=(aSize+m.iPageMask)&~m.iPageMask;
if ((aOffset+aSize)>iMaxSize)
return KErrArgument;
Mmu::Wait();
TInt r=KErrNone;
TInt i=aOffset>>m.iPageShift;
TInt n=aSize>>m.iPageShift;
if (iPageBitMap->NotAllocated(i,n))
r=KErrNotFound;
else
{
#ifdef BTRACE_CHUNKS
TUint oldFree = m.FreeRamInBytes();
#endif
r=Mmu::Get().UnlockRamCachePages(iBase,i,n);
#ifdef BTRACE_CHUNKS
if(r==KErrNone)
{
TUint unlocked = m.FreeRamInBytes()-oldFree; // size of memory unlocked
if(unlocked)
BTraceContext12(BTrace::EChunks,BTrace::EChunkMemoryDeallocated,this,aOffset,unlocked);
}
#endif
}
Mmu::Signal();
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
return r;
}
TInt DMemModelChunk::Lock(TInt aOffset, TInt aSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Lock %x+%x",aOffset,aSize));
if (!(iAttributes&ECache))
return KErrGeneral;
if ((iAttributes & (EDoubleEnded|EDisconnected))!=EDisconnected)
return KErrGeneral;
if (aOffset<0 || aSize<0)
return KErrArgument;
if (aSize==0)
return KErrNone;
Mmu& m = Mmu::Get();
aSize+=(aOffset & m.iPageMask);
aOffset &= ~m.iPageMask;
aSize=(aSize+m.iPageMask)&~m.iPageMask;
if ((aOffset+aSize)>iMaxSize)
return KErrArgument;
Mmu::Wait();
TInt r=KErrNone;
TInt i=aOffset>>m.iPageShift;
TInt n=aSize>>m.iPageShift;
if (iPageBitMap->NotAllocated(i,n))
r=KErrNotFound;
else
{
#ifdef BTRACE_CHUNKS
TUint oldFree = m.FreeRamInBytes();
#endif
r=Mmu::Get().LockRamCachePages(iBase,i,n);
#ifdef BTRACE_CHUNKS
if(r==KErrNone)
{
TUint locked = oldFree-m.FreeRamInBytes();
if(locked)
BTraceContext12(BTrace::EChunks,BTrace::EChunkMemoryAllocated,this,aOffset,locked);
}
#endif
}
if(r!=KErrNone)
{
// decommit memory on error...
__KTRACE_OPT(KMMU,Kern::Printf("Calling SelectiveFree(%d,%d)",i,n));
TUint oldAvail = iPageBitMap->iAvail;
iPageBitMap->SelectiveFree(i,n); // free those positions which are actually allocated
TUint oldSize = iSize;
DoDecommit(aOffset,aSize);
// Use the bit map to adjust the size of the chunk as unlocked and reclaimed pages
// will have been unmapped but not removed from the bit map as DoDecommit() only
// decommits the mapped pages.
TUint actualFreedPages = iPageBitMap->iAvail - oldAvail;
iSize = oldSize - (actualFreedPages << KPageShift);
}
Mmu::Signal();
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
return r;
}
#ifndef __SCHEDULER_MACHINE_CODED__
// System locked in this function for a time proportional to chunk size.
// This is unavoidable since the chunk state must always be well defined
// whenever the system is unlocked.
TUint32 DMemModelChunk::ApplyTopLevelPermissions(TChunkState aChunkState)
{
__KTRACE_OPT(KMMU,Kern::Printf("ApplyTopLevelPermissions ChunkState=%d",aChunkState));
if (!(iAttributes&EFixedAccess))
{
iChunkState=aChunkState;
if (iSize)
{
Mmu& m = Mmu::Get();
TLinAddr base=(TLinAddr)iBase;
TInt size=iSize;
TUint32 mask=m.iChunkMask;
if (iAttributes & EDoubleEnded)
{
base+=(iStartPos & ~mask);
size=((iStartPos&mask)+size+mask)&~mask;
}
m.ApplyTopLevelPermissions(base,size,iPdePermissions[aChunkState]);
}
return (iAttributes&ECode)?Mmu::EFlushDPermChg|Mmu::EFlushIPermChg:Mmu::EFlushDPermChg;
}
return 0;
}
// System locked in this function for a time proportional to chunk size.
// This is unavoidable since the chunk state must always be well defined
// whenever the system is unlocked.
TUint32 DMemModelChunk::MoveToRunAddress(TLinAddr aLinearAddr, TChunkState aChunkState)
{
iChunkState=aChunkState;
if (iSize)
{
TLinAddr base=(TLinAddr)iBase;
TLinAddr dest=aLinearAddr;
TInt size=iSize;
if (iAttributes & EDoubleEnded)
{
Mmu& m = Mmu::Get();
TUint32 mask=m.iChunkMask;
base+=(iStartPos & ~mask);
dest+=(iStartPos & ~mask);
size=((iStartPos&mask)+size+mask)&~mask;
}
m.MoveChunk(base,size,dest,iPdePermissions[aChunkState]);
}
MoveCurrentPdes((TLinAddr)iBase,aLinearAddr);
iBase=(TUint8 *)aLinearAddr;
return Mmu::EFlushDMove; // chunk can't contain code
}
// System locked in this function for a time proportional to chunk size.
// This is unavoidable since the chunk state must always be well defined
// whenever the system is unlocked.
TUint32 DMemModelChunk::MoveToHomeSection()
{
iChunkState=ENotRunning;
if (iSize)
{
TLinAddr base=TLinAddr(iBase);
TLinAddr home=iHomeRegionBase;
TInt size=iSize;
if (iAttributes & EDoubleEnded)
{
Mmu& m = Mmu::Get();
TUint32 mask=m.iChunkMask;
base+=(iStartPos & ~mask);
home+=(iStartPos & ~mask);
size=((iStartPos&mask)+size+mask)&~mask;
}
m.MoveChunk(base,size,home,iPdePermissions[0]);
}
iBase=(TUint8 *)iHomeRegionBase;
iHomePdes=iPdes;
return Mmu::EFlushDMove; // chunk can't contain code
}
#endif
TLinAddr DMemModelChunk::AllocateHomeAddress(TInt aSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::AllocateHomeAddress size %08x",aSize));
Mmu& m = Mmu::Get();
TLinearSection* s = m.iKernelSection;
TUint required;
if (iAttributes&EFixedAddress)
required=Mmu::RoundToChunkSize(iMaxSize);
else
required=Mmu::RoundToChunkSize(aSize);
required >>= m.iChunkShift;
TInt r = s->iAllocator.AllocConsecutive(required, EFalse);
if (r<0)
return 0;
s->iAllocator.Alloc(r, required);
TLinAddr addr = s->iBase + (r<<m.iChunkShift);
__KTRACE_OPT(KMMU,Kern::Printf("Address %08x allocated",addr));
iHomeRegionSize = required << m.iChunkShift;
return addr;
}
void DMemModelChunk::DeallocateHomeAddress()
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DeallocateHomeAddress %08x+%x", iHomeRegionBase, iHomeRegionSize));
if (iHomeRegionSize)
{
Mmu& m = Mmu::Get();
TLinearSection* s = m.iKernelSection;
TInt first = (TInt)((iHomeRegionBase - s->iBase)>>m.iChunkShift);
TInt count = (TInt)(iHomeRegionSize >> m.iChunkShift);
s->iAllocator.Free(first, count);
iHomeRegionBase=0;
iHomeRegionSize=0;
}
}
TLinAddr DMemModelChunk::ReallocateHomeAddress(TInt aNewSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::ReallocateHomeAddress(%08x) for chunk %O",aNewSize,this));
// can never be called for a fixed address chunk
__ASSERT_ALWAYS((iAttributes&(EFixedAddress))==0,MM::Panic(MM::EFixedChunkMoving));
Mmu& m = Mmu::Get();
TLinearSection* s = m.iKernelSection;
TUint required=Mmu::RoundToChunkSize(aNewSize);
TInt next = (TInt)((iHomeRegionBase + iHomeRegionSize - s->iBase)>>m.iChunkShift);
TInt count = (TInt)((required - iHomeRegionSize) >> m.iChunkShift);
if (!s->iAllocator.NotFree(next, count))
{
// we can expand in place
s->iAllocator.Alloc(next, count);
iHomeRegionSize = required;
return iHomeRegionBase;
}
TUint oldHomeSize = iHomeRegionSize;
TLinAddr addr = AllocateHomeAddress(required); // try to get a new home address
if (addr && oldHomeSize)
{
// succeeded - free old region
next = (TInt)((iHomeRegionBase - s->iBase)>>m.iChunkShift);
count = (TInt)(oldHomeSize >> m.iChunkShift);
s->iAllocator.Free(next, count);
}
// if it fails, keep our current home region
return addr;
}
TInt DMemModelChunk::CheckAccess()
{
DProcess* pP=TheCurrentThread->iOwningProcess;
if (iAttributes&EPrivate)
{
if (iOwningProcess && iOwningProcess!=pP && pP!=K::TheKernelProcess)
return KErrAccessDenied;
}
return KErrNone;
}
TInt DMemModelChunkHw::Close(TAny*)
{
__KTRACE_OPT(KOBJECT,Kern::Printf("DMemModelChunkHw::Close %d %O",AccessCount(),this));
TInt r=Dec();
if (r==1)
{
if (iLinAddr)
{
// Physical memory has to be evicted from cache(s).
// Must be preserved as well, as it can still be in use by the driver.
CacheMaintenance::MemoryToPreserveAndReuse(iLinAddr, iSize, iAttribs);
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
m.Unmap(iLinAddr,iSize);
MmuBase::Signal();
DeallocateLinearAddress();
}
K::ObjDelete(this);
}
return r;
}
void DMemModelChunk::BTracePrime(TInt aCategory)
{
DChunk::BTracePrime(aCategory);
#ifdef BTRACE_CHUNKS
if (aCategory == BTrace::EChunks || aCategory == -1)
{
MmuBase::Wait();
TBool memoryOwned = !(iAttributes&EMemoryNotOwned);
Mmu& m=Mmu::Get();
TInt committedBase = -1;
// look at each page table in this chunk...
TUint chunkEndIndex = iMaxSize>>KChunkShift;
NKern::LockSystem();
for(TUint chunkIndex=0; chunkIndex<chunkEndIndex; ++chunkIndex)
{
TLinAddr addr=(TLinAddr)iBase+chunkIndex*KChunkSize; // current address
TInt ptid = m.GetPageTableId(addr);
if(ptid<0)
{
// no page table...
if(committedBase!=-1)
{
NKern::FlashSystem();
TUint committedEnd = chunkIndex*KChunkSize;
BTrace12(BTrace::EChunks, memoryOwned?BTrace::EChunkMemoryAllocated:BTrace::EChunkMemoryAdded,this,committedBase,committedEnd-committedBase);
committedBase = -1;
}
continue;
}
TPte* pPte=(TPte*)m.PageTableLinAddr(ptid);
// look at each page in page table...
for(TUint pageIndex=0; pageIndex<KChunkSize/KPageSize; ++pageIndex)
{
TBool committed = false;
TPhysAddr phys = m.PtePhysAddr(pPte[pageIndex], pageIndex);
if(phys!=KPhysAddrInvalid)
{
// we have a page...
if(!memoryOwned)
committed = true;
else
{
// make sure we own the page...
SPageInfo* pi = SPageInfo::SafeFromPhysAddr(phys);
if(pi && pi->Type()==SPageInfo::EChunk && pi->Owner()==this)
committed = true;
}
}
if(committed)
{
if(committedBase==-1)
committedBase = chunkIndex*KChunkSize+pageIndex*KPageSize; // start of new region
}
else
{
if(committedBase!=-1)
{
// generate trace for region...
NKern::FlashSystem();
TUint committedEnd = chunkIndex*KChunkSize+pageIndex*KPageSize;
BTrace12(BTrace::EChunks, memoryOwned?BTrace::EChunkMemoryAllocated:BTrace::EChunkMemoryAdded,this,committedBase,committedEnd-committedBase);
committedBase = -1;
}
}
if((pageIndex&15)==0)
NKern::FlashSystem();
}
}
NKern::UnlockSystem();
if(committedBase!=-1)
{
TUint committedEnd = chunkEndIndex*KChunkSize;
BTrace12(BTrace::EChunks, memoryOwned?BTrace::EChunkMemoryAllocated:BTrace::EChunkMemoryAdded,this,committedBase,committedEnd-committedBase);
}
MmuBase::Signal();
}
#endif
}