kernel/eka/memmodel/epoc/multiple/mchunk.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 26 c734af59ce98
permissions -rw-r--r--
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
	}