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

#include "memmodel.h"
#include "mmboot.h"
#include "cache_maintenance.h"
#include <demand_paging.h>

#define iMState		iWaitLink.iSpare1

// just for convenience...
#define KAmSelfMod	(DMemModelChunk::ECode | DMemModelChunk::EAddressLocal)

_LIT(KDollarDat,"$DAT");
_LIT(KLitDollarCode,"$CODE");
_LIT(KLitDllDollarData,"DLL$DATA");

#ifdef __CPU_HAS_BTB
extern void __FlushBtb();
#endif

const TInt KChunkGranularity=4;

/********************************************
 * Process
 ********************************************/
void DMemModelProcess::Destruct()
	{
	__ASSERT_ALWAYS(!iChunkCount && !iCodeChunk && !iDllDataChunk, MM::Panic(MM::EProcessDestructChunksRemaining));
	Kern::Free(iChunks);
	Kern::Free(iLocalSection);
	if (iOsAsid)
		{
		Mmu& m=Mmu::Get();
		MmuBase::Wait();
		m.FreeOsAsid(iOsAsid);
		iOsAsid=0;
		MmuBase::Signal();
#ifndef __SMP__
		LastUserSelfMod=0;  // must force a BTB flush when next selfmod chunk switched in
#endif
		}
#ifdef __CPU_HAS_BTB
	__FlushBtb();
#endif
	DProcess::Destruct();
	}

TInt DMemModelProcess::NewChunk(DChunk*& aChunk, SChunkCreateInfo& aInfo, TLinAddr& aRunAddr)
	{
	aChunk=NULL;
	DMemModelChunk* pC=NULL;
	TInt r=GetNewChunk(pC,aInfo);
	if (r!=KErrNone)
		{
		if (pC)
			pC->Close(NULL);
		return r;
		}
	TInt mapType=pC->iAttributes & DMemModelChunk::EMapTypeMask;
	pC->iOwningProcess=(mapType==DMemModelChunk::EMapTypeLocal)?this:NULL;
#ifdef __CPU_HAS_BTB
	if ((pC->iAttributes & KAmSelfMod) == KAmSelfMod)  // it's a potentially overlapping self-mod
		{
		iSelfModChunks++;
#ifndef __SMP__
		LastUserSelfMod = this;  // we become the last selfmodding process
#endif
		__FlushBtb();		// we need to do this, as there may be bad branches already in the btb
		}
#endif
	r=pC->Create(aInfo);
	if (r==KErrNone && (aInfo.iOperations & SChunkCreateInfo::EAdjust))
		{
		if (aInfo.iRunAddress!=0)
			pC->SetFixedAddress(aInfo.iRunAddress,aInfo.iPreallocated);
		if (aInfo.iPreallocated==0 && aInfo.iInitialTop!=0)
			{
			if (pC->iAttributes & DChunk::EDisconnected)
				{
				r=pC->Commit(aInfo.iInitialBottom,aInfo.iInitialTop-aInfo.iInitialBottom);
				}
			else if (pC->iAttributes & DChunk::EDoubleEnded)
				{
				r=pC->AdjustDoubleEnded(aInfo.iInitialBottom,aInfo.iInitialTop);
				}
			else
				{
				r=pC->Adjust(aInfo.iInitialTop);
				}
			}
		}
	if (r==KErrNone && (aInfo.iOperations & SChunkCreateInfo::EAdd))
		{
//			if (pC->iAttributes & DMemModelChunk::ECode)
//				MM::TheMmu->SyncCodeMappings();
		if (mapType!=DMemModelChunk::EMapTypeGlobal)
			{
			r=WaitProcessLock();
			if (r==KErrNone)
				{
				r=AddChunk(pC,aRunAddr,EFalse);
				SignalProcessLock();
				}
			}
		else
			aRunAddr=(TLinAddr)pC->Base();
		}
	if (r==KErrNone)
		{
		if(r==KErrNone)
			if(pC->iKernelMirror)
				aRunAddr = (TLinAddr)pC->iKernelMirror->Base();
		pC->iDestroyedDfc = aInfo.iDestroyedDfc;
		aChunk=(DChunk*)pC;
		}
	else
		pC->Close(NULL);	// NULL since chunk can't have been added to process
	return r;
	}

TInt DMemModelProcess::DoCreate(TBool aKernelProcess, TProcessCreateInfo& aInfo)
	{
	__KTRACE_OPT(KPROC,Kern::Printf(">DMemModelProcess::DoCreate %O",this));

	Mmu& m=Mmu::Get();
	TInt r=KErrNone;

	iSelfModChunks=0;  // we don't have any yet.

	if (aKernelProcess)
		{
		iAttributes |= ESupervisor;
		//iOsAsid=0;
//		Leave these till Mmu::Init2
//		if (m.iLocalPdSize)
//			iLocalPageDir=m.LinearToPhysical(TLinAddr(m.LocalPageDir(0)));
//		iGlobalPageDir=m.LinearToPhysical(TLinAddr(m.GlobalPageDir(0)));
		m.iAsidInfo[0]=((TUint32)this)|1;
		iAddressCheckMaskR=0xffffffff;
		iAddressCheckMaskW=0xffffffff;
		}
	else
		{
		MmuBase::Wait();
		r=m.NewOsAsid(EFalse);
		if (r>=0)
			{
			iOsAsid=r;
			if (m.iLocalPdSize)
				iLocalPageDir=m.LinearToPhysical(TLinAddr(m.LocalPageDir(r)));
			else
				iGlobalPageDir=m.LinearToPhysical(TLinAddr(m.GlobalPageDir(r)));
			m.iAsidInfo[r] |= (TUint32)this;
			r=KErrNone;
			}
		MmuBase::Signal();
		if (r==KErrNone && 0==(iLocalSection=TLinearSection::New(m.iUserLocalBase, m.iUserLocalEnd)) )
			r=KErrNoMemory;
		}

	__KTRACE_OPT(KPROC,Kern::Printf("OS ASID=%d, LPD=%08x, GPD=%08x, ASID info=%08x",iOsAsid,iLocalPageDir,
											iGlobalPageDir,m.iAsidInfo[iOsAsid]));
	__KTRACE_OPT(KPROC,Kern::Printf("<DMemModelProcess::DoCreate %d",r));
	return r;
	}

TInt DMemModelProcess::CreateDataBssStackArea(TProcessCreateInfo& aInfo)
	{
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::CreateDataBssStackArea %O",this));
	Mmu& m=Mmu::Get();
	TInt dataBssSize=Mmu::RoundToPageSize(aInfo.iTotalDataSize);
	TInt maxSize=dataBssSize+PP::MaxStackSpacePerProcess;
	TLinAddr dataRunAddress=m.iUserLocalBase;
	iDataBssRunAddress=dataRunAddress;

	__KTRACE_OPT(KPROC,Kern::Printf("DataBssSize=%x, chunk max size %x",dataBssSize,maxSize));

	SChunkCreateInfo cinfo;
	cinfo.iGlobal=EFalse;
	cinfo.iAtt=TChunkCreate::EDisconnected;
	cinfo.iForceFixed=EFalse;
	cinfo.iOperations=SChunkCreateInfo::EAdjust|SChunkCreateInfo::EAdd;
	cinfo.iType=EUserData;
	cinfo.iMaxSize=maxSize;
	cinfo.iInitialBottom=0;
	cinfo.iInitialTop=dataBssSize;
	cinfo.iPreallocated=0;
	cinfo.iName.Set(KDollarDat);
	cinfo.iOwner=this;
	cinfo.iRunAddress=0;
	TLinAddr cb;
	TInt r=NewChunk((DChunk*&)iDataBssStackChunk,cinfo,cb);
	return r;
	}

TInt DMemModelProcess::AddChunk(DChunk* aChunk, TBool isReadOnly)
	{
	DMemModelChunk* pC=(DMemModelChunk*)aChunk;
	if ((pC->iAttributes & DMemModelChunk::EPrivate) && this!=pC->iOwningProcess)
		return KErrAccessDenied;
	TInt r=WaitProcessLock();
	if (r==KErrNone)
		{
		TInt pos=0;
		r=ChunkIndex(pC,pos);
		TLinAddr dataSectionBase=0;
		if (r==0) // Found the chunk in this process, just up its count
			{
			iChunks[pos].iAccessCount++;
			__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk %08x to %08x (Access count incremented to %d)",aChunk,this,iChunks[pos].iAccessCount));
			SignalProcessLock();
			return KErrNone;
			}
		r=AddChunk(pC,dataSectionBase,isReadOnly);
		SignalProcessLock();
		}
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk returns %d",r));
	return r;
	}

void M::FsRegisterThread()
	{
	DMemModelChunk* pC=(DMemModelChunk*)PP::TheRamDriveChunk;
	TInt mapType=pC->iAttributes & DMemModelChunk::EMapTypeMask;
	if (mapType!=DMemModelChunk::EMapTypeLocal)
		{
		DMemModelProcess* pP=(DMemModelProcess*)TheCurrentThread->iOwningProcess;
		TLinAddr dataSectionBase;
		TInt r=pP->WaitProcessLock();
		if (r==KErrNone)
			r=pP->AddChunk(pC,dataSectionBase,EFalse);
		__ASSERT_ALWAYS(r==KErrNone, MM::Panic(MM::EFsRegisterThread));
		pP->SignalProcessLock();
		}
	}

TInt DMemModelProcess::AddChunk(DMemModelChunk* aChunk, TLinAddr& aDataSectionBase, TBool isReadOnly)
	{
	//
	// Must hold the process $LOCK mutex before calling this
	//
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk %O to %O",aChunk,this));
	SChunkInfo *pC=iChunks;
	SChunkInfo *pE=pC+iChunkCount-1;
	TLinAddr base=TLinAddr(aChunk->iBase);
	TInt i=0;

#ifdef __CPU_HAS_BTB
	if ((aChunk->iAttributes & KAmSelfMod)==KAmSelfMod)  // it's a potentially overlapping self-mod
		{
		iSelfModChunks++;
#ifndef __SMP__
		LastUserSelfMod = this;  // we become the last selfmodding process
#endif
		__FlushBtb();		// we need to do this, as there may be bad branches already in the btb
		}
#endif
	if (iChunkCount)
		{
		for (; pE>=pC && TLinAddr(pE->iChunk->iBase)>base; --pE);
		if (pE>=pC && TLinAddr(pE->iChunk->iBase)+pE->iChunk->iMaxSize>base)
			return KErrInUse;
		pC=pE+1;
		if (pC<iChunks+iChunkCount && base+aChunk->iMaxSize>TLinAddr(pC->iChunk->iBase))
			return KErrInUse;
		i=pC-iChunks;
		}
	if (iChunkCount==iChunkAlloc)
		{
		TInt newAlloc=iChunkAlloc+KChunkGranularity;
		TInt r=Kern::SafeReAlloc((TAny*&)iChunks,iChunkAlloc*sizeof(SChunkInfo),newAlloc*sizeof(SChunkInfo));
		if (r!=KErrNone)
			return r;
		pC=iChunks+i;
		iChunkAlloc=newAlloc;
		}
	memmove(pC+1,pC,(iChunkCount-i)*sizeof(SChunkInfo));
	++iChunkCount;
	pC->isReadOnly=isReadOnly;
	pC->iAccessCount=1;
	pC->iChunk=aChunk;
	aDataSectionBase=base;
	Mmu& m=Mmu::Get();
	if (aChunk->iOsAsids)
		{
		// only need to do address space manipulation for shared chunks
		MmuBase::Wait();
		aChunk->iOsAsids->Alloc(iOsAsid,1);
		TLinAddr a;
		TInt i=0;
		for (a=TLinAddr(aChunk->iBase); a<TLinAddr(aChunk->iBase)+aChunk->iMaxSize; a+=m.iChunkSize, ++i)
			{
			TInt ptid=aChunk->iPageTables[i];
			if (ptid!=0xffff)
				m.DoAssignPageTable(ptid,a,aChunk->iPdePermissions,(const TAny*)iOsAsid);
			}
		MmuBase::Signal();
		}
	if (aChunk->iChunkType==ERamDrive)
		{
		NKern::LockSystem();
		iAddressCheckMaskR |= m.iRamDriveMask;
		iAddressCheckMaskW |= m.iRamDriveMask;
		NKern::UnlockSystem();
		}
	__DEBUG_EVENT(EEventUpdateProcess, this);
	return KErrNone;
	}

void DMemModelProcess::DoRemoveChunk(TInt aIndex)
	{
	__DEBUG_EVENT(EEventUpdateProcess, this);
	DMemModelChunk* chunk = iChunks[aIndex].iChunk;
	memmove(iChunks+aIndex, iChunks+aIndex+1, (iChunkCount-aIndex-1)*sizeof(SChunkInfo));
	--iChunkCount;
	Mmu& m=Mmu::Get();
	if (chunk->iOsAsids)
		{
		// only need to do address space manipulation for shared chunks
		MmuBase::Wait();
		chunk->iOsAsids->Free(iOsAsid);
		TLinAddr a;
		for (a=TLinAddr(chunk->iBase); a<TLinAddr(chunk->iBase)+chunk->iMaxSize; a+=m.iChunkSize)
			m.DoUnassignPageTable(a,(const TAny*)iOsAsid);
		TUint32 mask=(chunk->iAttributes&DMemModelChunk::ECode)?Mmu::EFlushITLB:0;
		m.GenericFlush(mask|Mmu::EFlushDTLB);

		MmuBase::Signal();
		}
	if (chunk->iChunkType==ERamDrive)
		{
		NKern::LockSystem();
		iAddressCheckMaskR &= ~m.iRamDriveMask;
		iAddressCheckMaskW &= ~m.iRamDriveMask;
		NKern::UnlockSystem();
		}
	}

/**
Final chance for process to release resources during its death.

Called with process $LOCK mutex held (if it exists).
This mutex will not be released before it is deleted.
I.e. no other thread will ever hold the mutex again.
*/
void DMemModelProcess::FinalRelease()
	{
	// Clean up any left over chunks (such as SharedIo buffers)
	if(iProcessLock)
		while(iChunkCount)
			DoRemoveChunk(0);
	}

void DMemModelProcess::RemoveChunk(DMemModelChunk *aChunk)
	{
	// note that this can't be called after the process $LOCK mutex has been deleted
	// since it can only be called by a thread in this process doing a handle close or
	// dying, or by the process handles array being deleted due to the process dying,
	// all of which happen before $LOCK is deleted.
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess %O RemoveChunk %O",this,aChunk));
	Kern::MutexWait(*iProcessLock);
	TInt pos=0;
	TInt r=ChunkIndex(aChunk,pos);

	if (r==KErrNone) // Found the chunk
		{
		__KTRACE_OPT(KPROC,Kern::Printf("Chunk access count %d",iChunks[pos].iAccessCount));
		if (--iChunks[pos].iAccessCount==0)
			{
			DoRemoveChunk(pos);
#ifdef __CPU_HAS_BTB
			if ((aChunk->iAttributes & KAmSelfMod)==KAmSelfMod)  // was a self-mod code chunk
				if (iSelfModChunks)
					iSelfModChunks--;
#endif
			}
		}
	Kern::MutexSignal(*iProcessLock);
	}

TInt DMemModelProcess::ChunkIndex(DMemModelChunk* aChunk,TInt& aPos)
	{
	if (!aChunk)
		return KErrNotFound;
	SChunkInfo *pC=iChunks;
	SChunkInfo *pE=pC+iChunkCount;
	for (; pC<pE && pC->iChunk!=aChunk; ++pC);
	if (pC==pE)
		return KErrNotFound;
	aPos=pC-iChunks;
	return KErrNone;
	}

TInt DMemModelProcess::MapCodeSeg(DCodeSeg* aSeg)
	{
	DMemModelCodeSeg& seg=*(DMemModelCodeSeg*)aSeg;
	__KTRACE_OPT(KDLL,Kern::Printf("Process %O MapCodeSeg %C", this, aSeg));
	TBool kernel_only=( (seg.iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttKernel );
	if (kernel_only && !(iAttributes&ESupervisor))
		return KErrNotSupported;
	if (seg.iAttr&ECodeSegAttKernel)
		return KErrNone;	// no extra mappings needed for kernel code
	TInt r=KErrNone;
	if (seg.Pages())
		r=MapUserRamCode(seg.Memory(),EFalse);
	if (seg.IsDll())
		{
		TInt total_data_size;
		TLinAddr data_base;
		seg.GetDataSizeAndBase(total_data_size, data_base);
		if (r==KErrNone && total_data_size)
			{
			TInt size=Mmu::RoundToPageSize(total_data_size);
			r=CommitDllData(data_base, size);
			if (r!=KErrNone && seg.Pages())
				UnmapUserRamCode(seg.Memory(), EFalse);
			}
		}
	return r;
	}

void DMemModelProcess::UnmapCodeSeg(DCodeSeg* aSeg)
	{
	DMemModelCodeSeg& seg=*(DMemModelCodeSeg*)aSeg;
	__KTRACE_OPT(KDLL,Kern::Printf("Process %O UnmapCodeSeg %C", this, aSeg));
	if (seg.iAttr&ECodeSegAttKernel)
		return;	// no extra mappings needed for kernel code
	if (seg.IsDll())
		{
		TInt total_data_size;
		TLinAddr data_base;
		seg.GetDataSizeAndBase(total_data_size, data_base);
		if (total_data_size)
			DecommitDllData(data_base, Mmu::RoundToPageSize(total_data_size));
		}
	if (seg.Pages())
		UnmapUserRamCode(seg.Memory(), EFalse);
	}

void DMemModelProcess::RemoveDllData()
//
// Call with CodeSegLock held
//
	{
	}

TInt DMemModelProcess::CreateCodeChunk()
	{
	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O CreateCodeChunk",this));
	TBool kernel=iAttributes&ESupervisor;
	Mmu& m=Mmu::Get();
	SChunkCreateInfo c;
	c.iGlobal=kernel;
	c.iAtt = TChunkCreate::EDisconnected | (kernel? 0 : TChunkCreate::EMemoryNotOwned);
	c.iForceFixed=EFalse;
	c.iOperations=SChunkCreateInfo::EAdjust|SChunkCreateInfo::EAdd;
	c.iRunAddress=kernel ? 0 : m.iUserCodeBase;
	c.iPreallocated=0;
	c.iType=kernel ? EKernelCode : EUserCode;
	c.iMaxSize=m.iMaxUserCodeSize;
	c.iName.Set(KLitDollarCode);
	c.iOwner=this;
	c.iInitialTop=0;
	TLinAddr runAddr;
	TInt r = NewChunk((DChunk*&)iCodeChunk,c,runAddr);
	return r;
	}

void DMemModelProcess::FreeCodeChunk()
	{
	iCodeChunk->Close(this);
	iCodeChunk=NULL;
	}

TInt DMemModelProcess::MapUserRamCode(DMemModelCodeSegMemory* aMemory, TBool aLoading)
	{
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess %O MapUserRamCode %C %d %d %d",
									this, aMemory->iCodeSeg, aLoading, iOsAsid, aMemory->iIsDemandPaged));
	__ASSERT_MUTEX(DCodeSeg::CodeSegLock);

	TInt r;

	if (!iCodeChunk)
		{
		r=CreateCodeChunk();
		__KTRACE_OPT(KPROC,Kern::Printf("CreateCodeChunk returns %d", r));
		if (r!=KErrNone)
			return r;
		}

	MmuBase::Wait();

	Mmu& m=Mmu::Get();
	TInt offset=aMemory->iRamInfo.iCodeRunAddr-TLinAddr(iCodeChunk->iBase);
	TInt codeSize = aMemory->iPageCount<<m.iPageShift;
	TBool paged = aMemory->iIsDemandPaged;
	DChunk::TCommitType commitType = paged ? DChunk::ECommitVirtual : DChunk::ECommitDiscontiguousPhysical;
	r=iCodeChunk->Commit(offset, codeSize, commitType, aMemory->iPages);
	__KTRACE_OPT(KPROC,Kern::Printf("Commit Pages returns %d", r));
	if(r==KErrNone)
		{
		if (aLoading && !paged)
			{
			iCodeChunk->ApplyPermissions(offset, codeSize, m.iUserCodeLoadPtePerm);
			UNLOCK_USER_MEMORY();
			memset((TAny*)(aMemory->iRamInfo.iCodeLoadAddr+aMemory->iRamInfo.iCodeSize+aMemory->iRamInfo.iDataSize), 0x03, codeSize-(aMemory->iRamInfo.iCodeSize+aMemory->iRamInfo.iDataSize));
			LOCK_USER_MEMORY();
			}
		if(aLoading && aMemory->iDataPageCount)
			{
			TInt dataSize = aMemory->iDataPageCount<<m.iPageShift;
			r=iCodeChunk->Commit(offset+codeSize, dataSize, DChunk::ECommitDiscontiguousPhysical, aMemory->iPages+aMemory->iPageCount);
			if(r==KErrNone)
				{
				iCodeChunk->ApplyPermissions(offset+codeSize, dataSize, m.iUserCodeLoadPtePerm);
				UNLOCK_USER_MEMORY();
				memset((TAny*)(aMemory->iRamInfo.iDataLoadAddr+aMemory->iRamInfo.iDataSize), 0x03, dataSize-aMemory->iRamInfo.iDataSize);
				LOCK_USER_MEMORY();
				}
			}
		if(r!=KErrNone)
			{
			// error, so decommit up code pages we had already committed...
			DChunk::TDecommitType decommitType = paged ? DChunk::EDecommitVirtual : DChunk::EDecommitNormal;
			iCodeChunk->Decommit(offset, codeSize, decommitType);
			}
		else
			{
			// indicate codeseg is now successfully mapped into the process...
			NKern::LockSystem();
			aMemory->iOsAsids->Free(iOsAsid);
			NKern::UnlockSystem();
			}
		}

	MmuBase::Signal();

	if(r!=KErrNone && iCodeChunk->iSize==0)
		FreeCodeChunk(); // cleanup any unused code chunk we would otherwise leave lying around

	return r;
	}

void DMemModelProcess::UnmapUserRamCode(DMemModelCodeSegMemory* aMemory, TBool aLoading)
	{
	__KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess %O UnmapUserRamCode %C %d %d",
									this, aMemory->iCodeSeg, iOsAsid, aMemory->iIsDemandPaged != 0));

	__ASSERT_MUTEX(DCodeSeg::CodeSegLock);

	MmuBase::Wait();

	NKern::LockSystem();
	aMemory->iOsAsids->Alloc(iOsAsid, 1);
	NKern::UnlockSystem();

	Mmu& m=Mmu::Get();
	__NK_ASSERT_DEBUG(iCodeChunk);
	TInt offset=aMemory->iRamInfo.iCodeRunAddr-TLinAddr(iCodeChunk->iBase);
	TInt codeSize = aMemory->iPageCount<<m.iPageShift;
	TBool paged = aMemory->iIsDemandPaged;
	DChunk::TDecommitType decommitType = paged ? DChunk::EDecommitVirtual : DChunk::EDecommitNormal;
	TInt r=iCodeChunk->Decommit(offset, codeSize, decommitType);
	__ASSERT_DEBUG(r==KErrNone, MM::Panic(MM::EDecommitFailed));
	(void)r; //Supress the warning in urel build

	if(aLoading && aMemory->iDataPageCount)
		{
		// decommit pages used to store data section...
		TInt dataSize = aMemory->iDataPageCount<<m.iPageShift;
		r=iCodeChunk->Decommit(offset+codeSize, dataSize);
		__ASSERT_DEBUG(r==KErrNone, MM::Panic(MM::EDecommitFailed));
		(void)r; //Supress the warning in urel build
		}
	__NK_ASSERT_DEBUG(iCodeChunk->iSize >= 0);

	MmuBase::Signal();

	if (iCodeChunk->iSize==0)
		FreeCodeChunk();
	}

TInt DMemModelProcess::CreateDllDataChunk()
	{
	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O CreateDllDataChunk",this));
	Mmu& m=Mmu::Get();
	SChunkCreateInfo c;
	c.iGlobal=EFalse;
	c.iAtt=TChunkCreate::EDisconnected;
	c.iForceFixed=EFalse;
	c.iOperations=SChunkCreateInfo::EAdjust|SChunkCreateInfo::EAdd;
	c.iRunAddress=m.iDllDataBase;
	c.iPreallocated=0;
	c.iType=EDllData;
	c.iMaxSize=m.iMaxDllDataSize;
	c.iName.Set(KLitDllDollarData);
	c.iOwner=this;
	c.iInitialTop=0;
	TLinAddr runAddr;
	return NewChunk((DChunk*&)iDllDataChunk,c,runAddr);
	}

void DMemModelProcess::FreeDllDataChunk()
	{
	iDllDataChunk->Close(this);
	iDllDataChunk=NULL;
	}

TInt DMemModelProcess::CommitDllData(TLinAddr aBase, TInt aSize)
	{
	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O CommitDllData %08x+%x",this,aBase,aSize));
	TInt r=KErrNone;
	if (!iDllDataChunk)
		r=CreateDllDataChunk();
	if (r==KErrNone)
		{
		TInt offset=aBase-(TLinAddr)iDllDataChunk->iBase;
		__ASSERT_ALWAYS(TUint32(offset)<TUint32(iDllDataChunk->iMaxSize),MM::Panic(MM::ECommitInvalidDllDataAddress));
		r=iDllDataChunk->Commit(offset, aSize);
		if (r!=KErrNone && iDllDataChunk->iSize==0)
			FreeDllDataChunk();
		}
	__KTRACE_OPT(KDLL,Kern::Printf("CommitDllData returns %d",r));
	return r;
	}

void DMemModelProcess::DecommitDllData(TLinAddr aBase, TInt aSize)
	{
	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O DecommitDllData %08x+%x",this,aBase,aSize));
	TInt offset=aBase-(TLinAddr)iDllDataChunk->iBase;
	TInt r=iDllDataChunk->Decommit(offset, aSize);
	__ASSERT_ALWAYS(r==KErrNone,MM::Panic(MM::EDecommitInvalidDllDataAddress));
	if (iDllDataChunk->iSize==0)
		FreeDllDataChunk();
	}

TInt DMemModelProcess::NewShPool(DShPool*& /* aPool */, TShPoolCreateInfo& /* aInfo */)
	{
	return KErrNotSupported;
	}


TInt DThread::RawRead(const TAny* aSrc, TAny* aDest, TInt aLength, TInt aFlags, TIpcExcTrap* /*aExcTrap*/)
//
// Read from the thread's process.
// Enter and return with system locked
// aSrc      Run address of memory to read
// aDest     Current address of destination
// aExcTrap  Exception trap object to be updated if the actual memory access is performed on other memory area then specified.
//           It happens when  reading is performed on un-aligned memory area.
//
	{
	Mmu& m=Mmu::Get();
	DMemModelThread& t=*(DMemModelThread*)TheCurrentThread;
	DMemModelProcess* pP=(DMemModelProcess*)iOwningProcess;
	TLinAddr src=(TLinAddr)aSrc;
	TLinAddr dest=(TLinAddr)aDest;
	TBool localIsSafe=ETrue;
	TInt result = KErrNone;

	while (aLength)
		{
		if (iMState==EDead)
			{
			result = KErrDied;
			break;
			}
		TLinAddr alias_src;
		TInt alias_size;
		TInt alias_result=t.Alias(src, pP, aLength, EMapAttrReadUser, alias_src, alias_size);
		if (alias_result<0)
			{
			result = KErrBadDescriptor;	// bad permissions
			break;
			}
		NKern::UnlockSystem();

		__KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead %08x<-%08x+%x",dest,alias_src,alias_size));
		if(aFlags&KCheckLocalAddress)
			localIsSafe = m.ValidateLocalIpcAddress(dest,alias_size,ETrue);

		CHECK_PAGING_SAFE;

		COND_UNLOCK_USER_MEMORY(localIsSafe);

		if(alias_result)
			{
			// remote address is safe for direct access...
			if (localIsSafe)
				memcpy( (TAny*)dest, (const TAny*)alias_src, alias_size);
			else
				umemput( (TAny*)dest, (const TAny*)alias_src, alias_size);
			}
		else
			{
			// remote address is NOT safe for direct access, so use user permision checks when reading...
			if (localIsSafe)
				umemget( (TAny*)dest, (const TAny*)alias_src, alias_size);
			else
				uumemcpy( (TAny*)dest, (const TAny*)alias_src, alias_size);
			}

		LOCK_USER_MEMORY();

		src+=alias_size;
		dest+=alias_size;
		aLength-=alias_size;
		NKern::LockSystem();
		}
	t.RemoveAlias();
	return result;
	}

TInt DThread::RawWrite(const TAny* aDest, const TAny* aSrc, TInt aLength, TInt aFlags, DThread* anOriginatingThread, TIpcExcTrap* /*aExcTrap*/)
//
// Write to the thread's process.
// Enter and return with system locked
// aDest               Run address of memory to write
// aSrc                Current address of destination
// anOriginatingThread The thread on behalf of which this operation is performed (eg client of device driver).
// aExcTrap            Exception trap object to be updated if the actual memory access is performed on other memory area then specified.
//                     It happens when reading is performed on un-aligned memory area.
//
	{
	Mmu& m=Mmu::Get();
	DMemModelThread& t=*(DMemModelThread*)TheCurrentThread;
	DMemModelProcess* pP=(DMemModelProcess*)iOwningProcess;
	TLinAddr src=(TLinAddr)aSrc;
	TLinAddr dest=(TLinAddr)aDest;
	TBool localIsSafe=ETrue;
	DThread* pO=anOriginatingThread?anOriginatingThread:&t;
	DProcess* pF=K::TheFileServerProcess;
	TBool special=(iOwningProcess==pF && pO->iOwningProcess==pF);
	TUint32 perm=special ? EMapAttrWriteSup : EMapAttrWriteUser;
	TInt result = KErrNone;

	while (aLength)
		{
		if (iMState==EDead)
			{
			result = KErrDied;
			break;
			}
		TLinAddr alias_dest;
		TInt alias_size;
		TInt alias_result=t.Alias(dest, pP, aLength, perm, alias_dest, alias_size);
		if (alias_result<0)
			{
			result = KErrBadDescriptor;	// bad permissions
			break;
			}
		NKern::UnlockSystem();

		__KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawWrite %08x+%x->%08x",src,alias_size,alias_dest));
		if(aFlags&KCheckLocalAddress)
			localIsSafe = m.ValidateLocalIpcAddress(src,alias_size,EFalse);

		// Must check that it is safe to page, unless we are reading from unpaged ROM in which case
		// we allow it.  umemget and uumemcpy do this anyway, so we just need to check if
		// localIsSafe is set.
		if (localIsSafe)
			{
			CHECK_PAGING_SAFE_RANGE(src, aLength);
			CHECK_DATA_PAGING_SAFE_RANGE(dest, aLength);
			}

		COND_UNLOCK_USER_MEMORY(localIsSafe);

		if(alias_result)
			{
			// remote address is safe for direct access...
			if (localIsSafe)
				memcpy( (TAny*)alias_dest, (const TAny*)src, alias_size);
			else
				umemget( (TAny*)alias_dest, (const TAny*)src, alias_size);
			}
		else
			{
			// remote address is NOT safe for direct access, so use user permision checks when writing...
			if (localIsSafe)
				umemput( (TAny*)alias_dest, (const TAny*)src, alias_size);
			else
				uumemcpy( (TAny*)alias_dest, (const TAny*)src, alias_size);
			}

		LOCK_USER_MEMORY();

		src+=alias_size;
		dest+=alias_size;
		aLength-=alias_size;
		NKern::LockSystem();
		}
	t.RemoveAlias();
	return result;
	}

#ifdef __DEBUGGER_SUPPORT__

/**
@pre Calling thread must be in critical section
@pre CodeSeg mutex held
*/
TInt CodeModifier::SafeWriteCode(DProcess* aProcess, TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue)
	{
	Mmu& m=Mmu::Get();
	MmuBase::Wait();

	NKern::LockSystem();

	// Find physical address of the page, the breakpoint belongs to
	TPhysAddr physAddr = m.LinearToPhysical(aAddress,((DMemModelProcess*)aProcess)->iOsAsid);
	__KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - PA:%x", physAddr));
	if (physAddr==KPhysAddrInvalid)
		{
		__KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - invalid VA"));
		NKern::UnlockSystem();
		MmuBase::Signal();
		return KErrBadDescriptor;
		}

	// Temporarily map physical page
	TLinAddr tempAddr = m.MapTemp (physAddr&~m.iPageMask, aAddress);
	tempAddr |=  aAddress & m.iPageMask;
	__KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - tempAddr:%x",tempAddr));

	//Set exception handler. Make sure the boundaries cover the worst case (aSize = 4)
	TIpcExcTrap xt;
	xt.iLocalBase=0;
	xt.iRemoteBase=(TLinAddr)tempAddr&~3; //word aligned.
	xt.iSize=sizeof(TInt);
	xt.iDir=1;

	TInt r=xt.Trap(NULL);
	if (r==0)
		{
		r = WriteCode(tempAddr, aSize, aValue, aOldValue);
		xt.UnTrap();
		}

	m.UnmapTemp();
	NKern::UnlockSystem();
	MmuBase::Signal();
	return r;	
	}

/**
@pre Calling thread must be in critical section
@pre CodeSeg mutex held
*/
TInt CodeModifier::WriteCode(TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue)
	{
	// We do not want to be interrupted by e.g. ISR that will run altered code before IMB-Range.
	// Therefore, copy data and clean/invalidate caches with interrupts disabled.
	TInt irq=NKern::DisableAllInterrupts();
	switch(aSize)
		{
		case 1:
			*(TUint8*) aOldValue = *(TUint8*)aAddress;
			*(TUint8*) aAddress  = (TUint8)aValue;
			 break;
		case 2:
			*(TUint16*) aOldValue = *(TUint16*)aAddress;
			*(TUint16*) aAddress  = (TUint16)aValue;
			 break;
		default://It is 4 otherwise
			*(TUint32*) aOldValue = *(TUint32*)aAddress;
			*(TUint32*) aAddress  = (TUint32)aValue;
			 break;
		};
	CacheMaintenance::CodeChanged(aAddress, aSize, CacheMaintenance::ECodeModifier);
	NKern::RestoreInterrupts(irq);
	return KErrNone;
	}
#endif //__DEBUGGER_SUPPORT__


#ifdef __MARM__

// the body of ReadDesHeader is machine coded on ARM...
extern TInt ThreadDoReadAndParseDesHeader(DThread* aThread, const TAny* aSrc, TUint32* aDest);

TInt DThread::ReadAndParseDesHeader(const TAny* aSrc, TDesHeader& aDest)
//
// Read and parse the header of a remote descriptor.
// Enter and return with system locked
//
	{
	// todo: remove use of system lock from callers, when they have been un-exported from the kernel
	NKern::UnlockSystem();	
	TInt r = ThreadDoReadAndParseDesHeader(this,aSrc,(TUint32*)&aDest);
	NKern::LockSystem();
	return r;
	}


#else // !__MARM__


TInt DThread::ReadAndParseDesHeader(const TAny* aSrc, TDesHeader& aDest)
//
// Read and parse the header of a remote descriptor.
// Enter and return with system locked
//
	{
	static const TUint8 LengthLookup[16] = {4,8,12,8,12,0,0,0,0,0,0,0,0,0,0,0};

	DMemModelThread& t = *(DMemModelThread*)TheCurrentThread;
	TInt r = KErrBadDescriptor;

	CHECK_PAGING_SAFE;

	DMemModelProcess* pP = (DMemModelProcess*)iOwningProcess;
	TLinAddr src = (TLinAddr)aSrc;
	const TUint32* pAlias;
	TInt alias_size;
	TInt alias_result = t.Alias(src, pP, 12, EMapAttrReadUser, (TLinAddr&)pAlias, alias_size);
	if (alias_result<0)
		return KErrBadDescriptor;	// bad permissions
	NKern::UnlockSystem();
	t.iIpcClient = this;
	TUint32* dest = (TUint32*)&aDest;
	if (Kern::SafeRead(pAlias, dest, sizeof(TUint32)))
		goto fail;

	{
	TInt type=*dest>>KShiftDesType8;

	src += sizeof(TUint32);
	alias_size -=  sizeof(TUint32);
	++pAlias;
	++dest;

	TInt l=LengthLookup[type];
	if (l==0)
		goto fail;

	l -= sizeof(TUint32); // we've already read one word
	if (l>0 && alias_size)
		{
get_more:
		// more to go - get rest or as much as is currently aliased
		TInt ll = alias_size>=l ? l : alias_size;
		if(Kern::SafeRead(pAlias, dest, l))
			goto fail;
		l -= ll;
		src += TLinAddr(ll);
		dest = (TUint32*)(TLinAddr(dest) + TLinAddr(ll));
		}
	if (l>0)
		{
		// more to go - need to step alias on
		NKern::LockSystem();
		alias_result = t.Alias(src, pP, l, EMapAttrReadUser, (TLinAddr&)pAlias, alias_size);
		if (alias_result<0)
			goto fail_locked;
		NKern::UnlockSystem();
		goto get_more;
		}

	r = K::ParseDesHeader(aSrc, *(TRawDesHeader*)&aDest, aDest);
	}

fail:
	NKern::LockSystem();
fail_locked:
	t.RemoveAlias();
	t.iIpcClient = NULL;
	return r;
	}


#endif


DChunk* DThread::OpenSharedChunk(const TAny* aAddress, TBool aWrite, TInt& aOffset)
	{
	NKern::LockSystem();
	
	DMemModelProcess* pP = (DMemModelProcess*)iOwningProcess;
	DMemModelProcess::SChunkInfo* pS=pP->iChunks;
	DMemModelProcess::SChunkInfo* pC=pS+pP->iChunkCount;
	while(--pC>=pS && TUint(pC->iChunk->Base())>TUint(aAddress)) {};
	if(pC>=pS)
		{
		DMemModelChunk* chunk = pC->iChunk;
		if(chunk->iChunkType==ESharedKernelSingle || chunk->iChunkType==ESharedKernelMultiple)
			{
			TInt offset = (TInt)aAddress-(TInt)chunk->Base();
			if(TUint(offset)<TUint(chunk->iMaxSize) && chunk->Open()==KErrNone)
				{
				aOffset = offset;
				NKern::UnlockSystem();
				return chunk;
				}
			}
		}
	NKern::UnlockSystem();
	return 0;
	}

TInt DThread::PrepareMemoryForDMA(const TAny* aLinAddr, TInt aSize, TPhysAddr* aPhysicalPageList)
	{
	TInt asid = ((DMemModelProcess*)iOwningProcess)->iOsAsid;
	Mmu& m=(Mmu&)*MmuBase::TheMmu;
	return m.PreparePagesForDMA((TLinAddr)aLinAddr, aSize, asid, aPhysicalPageList);
	}

TInt DThread::ReleaseMemoryFromDMA(const TAny* aLinAddr, TInt aSize, TPhysAddr* aPhysicalPageList)
	{
	TInt pageCount = (((TInt)aLinAddr & KPageMask) + aSize + KPageMask) >> KPageShift;
	Mmu& m=(Mmu&)*MmuBase::TheMmu;
	return m.ReleasePagesFromDMA(aPhysicalPageList, pageCount);
	}