diff -r 000000000000 -r 96e5fb8b040d kernel/eka/memmodel/epoc/multiple/mchunk.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/multiple/mchunk.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1238 @@ +// 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 +#include + +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; iiAllocator.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.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=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.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=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; + 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>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<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<iOsAsid; + TUint size_in_pages = (TUint)(Min(aSize,iSize)>>m.iPageShift); + TBool sync_decommit = (size_in_pages>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; // number of 'unlocked' pages reclaimed from ram cache + if(nFree-reclaimed) + BTraceContext12(BTrace::EChunks,ownsMemory?BTrace::EChunkMemoryDeallocated:BTrace::EChunkMemoryRemoved,this,offset,(nFree-reclaimed)<>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;iDoDecommit(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 || aTopiMaxSize) + 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 (nBottomnTop) + DoDecommit(nTop,initTop-nTop); // this changes iSize + if (aBottomnTop) + 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<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<=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>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<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; chunkIndexType()==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 + }