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\multiple\mchunk.cpp
//
//
#include "memmodel.h"
#include "cache_maintenance.h"
#include <mmubase.inl>
#include <ramalloc.h>
DMemModelChunk::DMemModelChunk()
{
}
TLinearSection* DMemModelChunk::LinearSection()
{
Mmu& m=Mmu::Get();
TInt ar=(iAttributes&EAddressRangeMask);
switch (ar)
{
case EAddressLocal: return ((DMemModelProcess*)iOwningProcess)->iLocalSection;
case EAddressFixed: return NULL;
case EAddressShared: return m.iSharedSection;
case EAddressUserGlobal: return m.iUserGlobalSection;
case EAddressKernel: return m.iKernelSection;
}
MM::Panic(MM::EChunkBadAddressRange);
return NULL;
}
void DMemModelChunk::Destruct()
{
__KTRACE_OPT(KTHREAD,Kern::Printf("DMemModelChunk destruct %O",this));
if (iPageTables)
{
#ifdef _DEBUG
TInt r;
#define SET_R_IF_DEBUG(x) r = (x)
#else
#define SET_R_IF_DEBUG(x) (void)(x)
#endif
if (iAttributes & EDisconnected)
SET_R_IF_DEBUG(Decommit(0,iMaxSize));
else if (iAttributes & EDoubleEnded)
SET_R_IF_DEBUG(AdjustDoubleEnded(0,0));
else
SET_R_IF_DEBUG(Adjust(0));
__ASSERT_DEBUG(r==KErrNone, MM::Panic(MM::EDecommitFailed));
#ifdef _DEBUG
// check all page tables have been freed...
Mmu& m=Mmu::Get();
TInt nPdes=(iMaxSize+m.iChunkMask)>>m.iChunkShift;
for(TInt i=0; i<nPdes; i++)
{
__NK_ASSERT_DEBUG(iPageTables[i]==0xffff);
}
#endif
}
if (iBase)
{
TLinearSection* s=LinearSection();
if(s)
{
Mmu::Wait();
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::~DMemModelChunk remove region"));
Mmu& m=Mmu::Get();
s->iAllocator.Free( (TLinAddr(iBase)-s->iBase)>>m.iChunkShift, iMaxSize>>m.iChunkShift);
Mmu::Signal();
}
}
delete iOsAsids;
Kern::Free(iPageTables);
delete iPageBitMap;
delete iPermanentPageBitMap;
if(iKernelMirror)
iKernelMirror->Close(NULL);
TDfc* dfc = iDestroyedDfc;
if (dfc)
dfc->QueueOnIdle();
__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;
if ((iAttributes&EMapTypeMask)==EMapTypeLocal)
pP=(DMemModelProcess*)iOwningProcess;
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)
{
__KTRACE_OPT(KMMU,Kern::Printf("Chunk %O DoCreate att=%08x",this,iAttributes));
__ASSERT_COMPILE(!(EMMChunkAttributesMask & EChunkAttributesMask));
if (aInfo.iMaxSize<=0)
return KErrArgument;
if (iKernelMirror)
{
iKernelMirror->iAttributes |= iAttributes|EMemoryNotOwned;
TInt r=iKernelMirror->DoCreate(aInfo);
if(r!=KErrNone)
return r;
}
Mmu& m=Mmu::Get();
TInt nPdes=(aInfo.iMaxSize+m.iChunkMask)>>m.iChunkShift;
iMaxSize=nPdes<<m.iChunkShift;
iMapAttr = aInfo.iMapAttr;
SetupPermissions();
TInt mapType=iAttributes & EMapTypeMask;
if (mapType==EMapTypeShared)
{
iOsAsids=TBitMapAllocator::New(m.iNumOsAsids,ETrue);
if (!iOsAsids)
return KErrNoMemory;
}
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;
}
iPageTables=(TUint16*)Kern::Alloc(nPdes*sizeof(TUint16));
if (!iPageTables)
return KErrNoMemory;
memset(iPageTables,0xff,nPdes*sizeof(TUint16));
MmuBase::Wait();
TInt r=AllocateAddress();
__KTRACE_OPT(KMEMTRACE,Kern::Printf("MT:C %d %x %O",NTickCount(),this,this));
MmuBase::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 r;
}
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.PageTableId(TLinAddr(iBase)+offset);
__ASSERT_ALWAYS(ptid>=0,MM::Panic(MM::EClaimInitialPagesBadPageTable));
__KTRACE_OPT(KMMU,Kern::Printf("Offset %x PTID=%d",offset,ptid));
iPageTables[offset>>m.iChunkShift]=ptid;
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* info = SPageInfo::SafeFromPhysAddr(phys);
if(info)
{
info->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();
}
void DMemModelChunk::SetFixedAddress(TLinAddr aAddr, TInt aInitialSize)
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk %O SetFixedAddress %08x size %08x",this,aAddr,aInitialSize));
iBase=(TUint8*)aAddr;
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));
iSize=Mmu::RoundToPageSize(aInitialSize);
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",this,iSize,iBase));
return r;
}
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)iKernelMirror->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.
// Enter and leave with system locked.
// This is sometimes called with interrupts disabled and should leave them alone.
Mmu& m = Mmu::Get();
__ASSERT_ALWAYS(iKernelMirror==NULL,MM::Panic(MM::EChunkRemapUnsupported));
TInt ptid=iPageTables[aOffset>>m.iChunkShift];
if(ptid==0xffff)
MM::Panic(MM::EChunkRemapNoPageTable);
// Permissions for global code will have been overwritten with ApplyPermissions
// so we can't trust iPtePermissions for those chunk types
TPte perms;
if(iChunkType==EKernelCode)
perms = m.iKernelCodePtePerm;
else if(iChunkType==EDll)
perms = m.iGlobalCodePtePerm;
else
perms = iPtePermissions;
m.RemapPage(ptid, (TLinAddr)iBase+aOffset, aOldAddr, aNewAddr, perms, iOwningProcess);
}
/**
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
__ASSERT_MUTEX(MmuBase::RamAllocatorMutex);
__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; // In case of discontiguous commit it points to the list of physical pages.
TPhysAddr nextPage=0; // In case of contiguous commit, it points to the physical address to commit
SPageInfo::TType type = SPageInfo::EChunk;
// 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 && aCommitType != DChunk::ECommitVirtual)
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;
case DChunk::ECommitVirtual:
#ifndef __MARM__
return KErrNotSupported;
#endif
break;
default:
return KErrNotSupported;
}
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
TInt ptid=iPageTables[offset>>m.iChunkShift];
newPtId=-1;
if (ptid==0xffff)
{
// need to allocate a new page table
newPtId=m.AllocPageTable();
if (newPtId<0)
{
r=KErrNoMemory;
break; // Exit the loop. Below, we'll free all ram
// that is allocated in the previous loop passes.
}
ptid=newPtId;
}
if(aCommitType==DChunk::ECommitDiscontiguous)
{
pPageList = pageList;
r=m.AllocRamPages(pPageList,np, GetPageType()); // try to allocate pages
if (r!=KErrNone) //If fail, clean up what was allocated in this loop.
{
if (newPtId>=0)
m.FreePageTable(newPtId);
break; // Exit the loop. Below, we'll free all ram
// that is allocated in the previous loop passes.
}
if(clearRam)
m.ClearPages(np, pPageList, iClearByte); // clear RAM if required
}
TInt commitSize = np<<m.iPageShift;
// In shared chunks (visible to both user and kernel side), it is always kernel side
// to be mapped the first. Decommiting will go in reverse order.
if(iKernelMirror)
{
// Map the same memory into the kernel mirror chunk
if(pPageList)
r = iKernelMirror->DoCommit(offset,commitSize,ECommitDiscontiguousPhysical,pPageList);
else
r = iKernelMirror->DoCommit(offset,commitSize,ECommitContiguousPhysical,(TUint32*)nextPage);
__KTRACE_OPT(KMMU,Kern::Printf("iKernelMirror->DoCommit returns %d",r));
if(r!=KErrNone) //If fail, clean up what was allocated in this loop.
{
if(aCommitType==DChunk::ECommitDiscontiguous)
m.FreePages(pPageList,np,EPageFixed);
if (newPtId>=0)
m.FreePageTable(newPtId);
break; // Exit the loop. Below, we'll free all ram
// that is allocated in the previous loop passes.
}
}
// Commit the memory.
NKern::LockSystem(); // lock the system while we change the MMU mappings
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();
if (newPtId>=0)
{
// We have allocated a new page table, now we must assign it
iPageTables[offset>>m.iChunkShift]=ptid;
TLinAddr addr=(TLinAddr)iBase+offset; // current address
m.AssignPageTable(ptid, SPageTableInfo::EChunk, this, addr, iPdePermissions);
newPtId = -1;
}
__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.
// Free any memory we succeeded in allocating in the loops before the one that failed
if (iChunkType != ESharedKernelMirror) //Kernel mirror chunk will be decommited alongside the main chunk.
{
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
// It has to go page after page as we cannot use FreePhysicalRam here because the part of
// of original allocated contiguous memory is already partly freed (in DoDecommit).
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 system unlocked
// must hold RamAlloc mutex before calling this function
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::DoDecommit %x+%x",aOffset,aSize));
TBool ownsMemory = !(iAttributes&EMemoryNotOwned);
TInt deferred=0;
TInt offset=aOffset;
TInt endOffset=offset+aSize;
Mmu& m = Mmu::Get();
DRamAllocator& a = *m.iRamPageAllocator;
TPhysAddr pageList[KMaxPages];
TLinAddr linearPageList[KMaxPages];
const TAny* asids=GLOBAL_MAPPING;
if (iOsAsids)
asids=iOsAsids;
else if (iOwningProcess)
asids=(const TAny*)((DMemModelProcess*)iOwningProcess)->iOsAsid;
TUint size_in_pages = (TUint)(Min(aSize,iSize)>>m.iPageShift);
TBool sync_decommit = (size_in_pages<m.iDecommitThreshold);
TInt total_freed=0;
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
TLinAddr addr=(TLinAddr)iBase+offset; // current address
TInt ptid=iPageTables[offset>>m.iChunkShift]; // get page table ID if a page table is already assigned here
if (ptid!=0xffff)
{
TInt nPtes=0;
TInt nUnmapped=0;
#ifdef BTRACE_CHUNKS
TUint oldFree = m.FreeRamInBytes();
#endif
// 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
// Bit 31 of return value is set if TLB flush may be incomplete
NKern::LockSystem();
TInt remain;
if (ownsMemory)
{
if (aDecommitType == EDecommitVirtual)
remain=m.UnmapVirtual(ptid,addr,np,pageList,ETrue,nPtes,nUnmapped,iOwningProcess);
else
remain=m.UnmapPages(ptid,addr,np,pageList,ETrue,nPtes,nUnmapped,iOwningProcess);
}
else
{
if (aDecommitType == EDecommitVirtual)
remain=m.UnmapUnownedVirtual(ptid,addr,np,pageList,linearPageList,nPtes,nUnmapped,iOwningProcess);
else
remain=m.UnmapUnownedPages(ptid,addr,np,pageList,linearPageList,nPtes,nUnmapped,iOwningProcess);
}
TInt nFree = ownsMemory ? nUnmapped : 0; //The number of pages to free
deferred |= remain;
TInt decommitSize=nPtes<<m.iPageShift;
iSize-=decommitSize; // reduce the committed size
NKern::UnlockSystem();
__KTRACE_OPT(KMEMTRACE,Kern::Printf("MT:A %d %x %x %O",NTickCount(),this,iSize,this));
#ifdef BTRACE_CHUNKS
TUint reclaimed = (oldFree-m.FreeRamInBytes())>>m.iPageShift; // number of 'unlocked' pages reclaimed from ram cache
if(nFree-reclaimed)
BTraceContext12(BTrace::EChunks,ownsMemory?BTrace::EChunkMemoryDeallocated:BTrace::EChunkMemoryRemoved,this,offset,(nFree-reclaimed)<<m.iPageShift);
#endif
if (sync_decommit && (remain & KUnmapPagesTLBFlushDeferred))
{
// must ensure DTLB flushed before doing cache purge on decommit
m.GenericFlush(Mmu::EFlushDTLB);
}
// if page table is now completely empty, unassign it and update chunk PDE info
remain &= KUnmapPagesCountMask;
if (remain==0)
{
m.DoUnassignPageTable(addr, asids);
m.FreePageTable(ptid);
iPageTables[offset>>m.iChunkShift]=0xffff;
}
// Physical memory not owned by the chunk has to be preserved from cache memory.
if(!ownsMemory)
{
// If a chunk has Kernel mirror, it is sufficient to do it just once.
if (!iKernelMirror)
{
TInt i;
for (i=0;i<nUnmapped;i++)
m.CacheMaintenanceOnPreserve(pageList[i], KPageSize, linearPageList[i], iMapAttr);
}
}
else if (nFree)
{
// We can now return the decommitted pages to the free page list and sort out caching.
total_freed+=nFree;
if (sync_decommit) //Purge cache if the size is below decommit threshold
m.CacheMaintenanceOnDecommit(pageList, nFree);
a.FreeRamPages(pageList,nFree, GetPageType());
}
offset+=(np<<m.iPageShift);
}
else
{
__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 (deferred & KUnmapPagesTLBFlushDeferred)
m.GenericFlush( (iAttributes&ECode) ? Mmu::EFlushDTLB|Mmu::EFlushITLB : Mmu::EFlushDTLB );
if (total_freed && !sync_decommit) //Flash entire cache if the size exceeds decommit threshold
CacheMaintenance::SyncPhysicalCache_All(); //On ARMv6, this deals with both L1 & L2 cache
// Kernel mapped part of the chunk is removed at the end. At this point, no user side is mapped
// which ensures that evicting data from cache will surely succeed.
if(iKernelMirror)
iKernelMirror->DoDecommit(aOffset,aSize);
}
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",this,iStartPos,iSize,iBase));
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.
//
{
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Allocate %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);
TInt base=TInt(TLinAddr(iBase)>>m.iPageShift);
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,base,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));
r=DoCommit(offset+aGuard,aSize);
if (r==KErrNone)
{
iPageBitMap->Alloc(i,n);
r=offset; // if operation successful, return allocated offset
}
}
Mmu::Signal();
__KTRACE_OPT(KMMU,Kern::Printf("DMemModelChunk::Allocate returns %x",r));
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateChunk, this);
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
//
// @param aDecommitType Used to indicate whether area was originally committed with the
// ECommitVirtual type
//
{
__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;
#ifndef __MARM__
if (aDecommitType == EDecommitVirtual)
return KErrNotSupported;
#endif
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
__KTRACE_OPT(KMMU,Kern::Printf("Rounded and Clipped range %x+%x",aOffset,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=m.UnlockRamCachePages((TLinAddr)(iBase+aOffset),n,iOwningProcess);
#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=m.LockRamCachePages((TLinAddr)(iBase+aOffset),n,iOwningProcess);
#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;
}
TInt DMemModelChunk::AllocateAddress()
{
__KTRACE_OPT(KMMU,Kern::Printf("Chunk %O AllocateAddress()",this));
TLinearSection* s=LinearSection();
if (!s)
return KErrNone; // chunk has fixed preallocated address
Mmu& m=Mmu::Get();
TUint32 required=iMaxSize>>m.iChunkShift;
__KTRACE_OPT(KMMU,Kern::Printf("Searching from low to high addresses"));
TInt r=s->iAllocator.AllocConsecutive(required, EFalse);
if (r<0)
return KErrNoMemory;
s->iAllocator.Alloc(r, required);
iBase=(TUint8*)(s->iBase + (r<<m.iChunkShift));
__KTRACE_OPT(KMMU,Kern::Printf("Address %08x allocated",iBase));
return KErrNone;
}
void DMemModelChunk::ApplyPermissions(TInt aOffset, TInt aSize, TPte aPtePerm)
{
__KTRACE_OPT(KMMU,Kern::Printf("Chunk %O ApplyPermissions(%x+%x,%08x)",this,aOffset,aSize,aPtePerm));
__ASSERT_ALWAYS(aOffset>=0 && aSize>=0, MM::Panic(MM::EChunkApplyPermissions1));
if (aSize==0)
return;
Mmu& m=Mmu::Get();
aOffset &= ~m.iPageMask;
aSize=(aSize+m.iPageMask)&~m.iPageMask;
TInt endOffset=aOffset+aSize;
__ASSERT_ALWAYS(endOffset<=iMaxSize, MM::Panic(MM::EChunkApplyPermissions2));
Mmu::Wait();
while(aOffset<endOffset)
{
TInt ptid=iPageTables[aOffset>>m.iChunkShift];
TInt pdeEnd=(aOffset+m.iChunkSize)&~m.iChunkMask;
if (ptid==0xffff)
{
aOffset=pdeEnd;
continue;
}
TInt np=(endOffset-aOffset)>>m.iPageShift; // number of pages remaining to process
TInt npEnd=(pdeEnd-aOffset)>>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
m.ApplyPagePermissions(ptid, (aOffset&m.iChunkMask)>>m.iPageShift, np, aPtePerm);
aOffset+=(np<<m.iPageShift);
}
Mmu::Signal();
}
TInt DMemModelChunkHw::Close(TAny*)
{
__KTRACE_OPT(KOBJECT,Kern::Printf("DMemModelChunkHw::Close %d %O",AccessCount(),this));
TInt r=Dec();
if (r==1)
{
if (iLinAddr)
{
// Save data for cache maintenance before beind destroyed by DeallocateLinearAddress
TPhysAddr pa = iPhysAddr;
TLinAddr la = iLinAddr;
TInt size = iSize;
TUint attr = iAttribs;
MmuBase& m=*MmuBase::TheMmu;
MmuBase::Wait();
m.Unmap(iLinAddr,iSize);
MmuBase::Signal();
DeallocateLinearAddress();
// Physical memory has to be evicted from cache(s).
// Must be preserved as it can still be in use by the driver.
MmuBase::Wait();
m.CacheMaintenanceOnPreserve(pa, size ,la ,attr);
MmuBase::Signal();
}
K::ObjDelete(this);
}
return r;
}
TInt DMemModelChunk::CheckAccess()
{
DProcess* pP=TheCurrentThread->iOwningProcess;
if (iAttributes&EPrivate)
{
if (iOwningProcess && iOwningProcess!=pP && pP!=K::TheKernelProcess)
return KErrAccessDenied;
}
return KErrNone;
}
void DMemModelChunk::BTracePrime(TInt aCategory)
{
DChunk::BTracePrime(aCategory);
#ifdef BTRACE_CHUNKS
if (aCategory == BTrace::EChunks || aCategory == -1)
{
MmuBase::Wait();
TBool memoryOwned = !(iAttributes&EMemoryNotOwned);
MmuBase& m=*MmuBase::TheMmu;
TInt committedBase = -1;
// look at each page table in this chunk...
TUint chunkEndIndex = iMaxSize>>KChunkShift;
for(TUint chunkIndex=0; chunkIndex<chunkEndIndex; ++chunkIndex)
{
TInt ptid = iPageTables[chunkIndex];
if(ptid==0xffff)
{
// no page table...
if(committedBase!=-1)
{
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...
NKern::LockSystem();
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
}