--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/moving/mprocess.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,960 @@
+// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32\memmodel\epoc\moving\mprocess.cpp
+//
+//
+
+#include "memmodel.h"
+#include "cache_maintenance.h"
+#include "mmboot.h"
+
+#define iMState iWaitLink.iSpare1
+
+_LIT(KDollarDat,"$DAT");
+_LIT(KLitDllDollarData,"DLL$DATA");
+
+/********************************************
+ * Process
+ ********************************************/
+void DMemModelProcess::Destruct()
+ {
+ NKern::LockSystem();
+ if (this==TheCurrentAddressSpace)
+ TheCurrentAddressSpace=NULL;
+ if (this==TheCurrentVMProcess)
+ TheCurrentVMProcess=NULL;
+ if (this==TheCurrentDataSectionProcess)
+ TheCurrentDataSectionProcess=NULL;
+ if (this==TheCompleteDataSectionProcess)
+ TheCompleteDataSectionProcess=NULL;
+ NKern::UnlockSystem();
+ 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;
+ }
+ if (aInfo.iForceFixed || iAttributes & DMemModelProcess::EFixedAddress)
+ pC->iAttributes |= DMemModelChunk::EFixedAddress;
+ if (!aInfo.iGlobal && (iAttributes & DMemModelProcess::EPrivate)!=0)
+ pC->iAttributes |= DMemModelChunk::EPrivate;
+ if (pC->iChunkType==EDll || pC->iChunkType==EUserCode || pC->iChunkType==EUserSelfModCode || pC->iChunkType==EKernelCode)
+ pC->iAttributes |= (DMemModelChunk::EFixedAddress|DMemModelChunk::ECode);
+ pC->iOwningProcess=(aInfo.iGlobal)?NULL:this;
+ 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)
+ {
+ 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 && pC->iHomeRegionBase==0 && (pC->iAttributes&DMemModelChunk::EFixedAddress)!=0)
+ {
+ r=pC->Reserve(0);
+ aRunAddr=(TLinAddr)pC->Base();
+ }
+ }
+ if (r==KErrNone && (aInfo.iOperations & SChunkCreateInfo::EAdd))
+ {
+ if (pC->iAttributes & DMemModelChunk::ECode)
+ Mmu::Get().SyncCodeMappings();
+ if (pC->iChunkType!=EUserCode)
+ {
+ r=WaitProcessLock();
+ if (r==KErrNone)
+ {
+ r=AddChunk(pC,aRunAddr,EFalse);
+ SignalProcessLock();
+ }
+ }
+ else
+ aRunAddr=(TLinAddr)pC->Base(); // code chunks always fixed address
+ }
+ if (r==KErrNone)
+ {
+ 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));
+
+ if (aKernelProcess)
+ iAttributes=ESupervisor|EFixedAddress|EPrivate;
+ else if (aInfo.iAttr & ECodeSegAttFixed)
+ iAttributes=EFixedAddress|EPrivate;
+ else
+ iAttributes=0;
+ if ((iAttributes & ESupervisor)==0 && (iAttributes & EFixedAddress)!=0)
+ {
+ CheckForFixedAccess();
+ }
+ return KErrNone;
+ }
+
+TInt DMemModelProcess::CreateDataBssStackArea(TProcessCreateInfo& aInfo)
+ {
+ __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::CreateDataBssStackArea %O",this));
+ TInt dataBssSize=Mmu::RoundToPageSize(aInfo.iTotalDataSize);
+ TInt maxSize=dataBssSize+PP::MaxStackSpacePerProcess;
+ TBool fixed=(iAttributes & EFixedAddress);
+
+ __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;
+ if (fixed && dataBssSize!=0 && aInfo.iCodeLoadAddress)
+ {
+ const TRomImageHeader& rih=*(const TRomImageHeader*)aInfo.iCodeLoadAddress;
+ cinfo.iRunAddress=rih.iDataBssLinearBase;
+ }
+ else
+ cinfo.iRunAddress=0;
+ TInt r=NewChunk((DChunk*&)iDataBssStackChunk,cinfo,iDataBssRunAddress);
+ return r;
+ }
+
+TInt DMemModelProcess::AddChunk(DChunk* aChunk,TBool isReadOnly)
+ {
+ DMemModelChunk* pC=(DMemModelChunk*)aChunk;
+ 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 FlushBeforeChunkMove(DMemModelChunk* aChunk)
+ {
+ Mmu& m = Mmu::Get();
+ TUint32 ff=Mmu::EFlushDMove|Mmu::EFlushDPermChg;
+ if (aChunk->iAttributes & DMemModelChunk::ECode) // assumption here that code chunks don't move
+ ff |= Mmu::EFlushIPermChg;
+ m.GenericFlush(ff);
+ }
+
+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 %08x to %08x (for first time)",aChunk,this));
+ TInt r=AllocateDataSectionBase(*((DMemModelChunk*)aChunk),(TUint&)aDataSectionBase);
+ if(r!=KErrNone)
+ return r;
+
+ if (iNumChunks==KMaxChunksInProcess)
+ return KErrOverflow; // too many chunks in the process
+
+ SChunkInfo *pC=iChunks;
+ SChunkInfo *pE=pC+iNumChunks-1;
+ NKern::LockSystem();
+ while(pE>=pC && TUint(pE->iDataSectionBase)>TUint(aDataSectionBase))
+ {
+ pE[1]=pE[0];
+ pE--;
+ }
+ pC=pE+1;
+ pC->iDataSectionBase=aDataSectionBase;
+ pC->isReadOnly=isReadOnly;
+ pC->iAccessCount=1;
+ pC->iChunk=aChunk;
+ iNumChunks++;
+
+ if(!(iAttributes&ESupervisor))
+ {
+ TInt attribs=aChunk->iAttributes;
+ if (!(attribs&DMemModelChunk::EFixedAddress))
+ {
+ iNumMovingChunks++;
+ iAttributes |= EMoving;
+ }
+
+ if (attribs&DMemModelChunk::EFixedAccess)
+ {
+ NKern::UnlockSystem();
+ AddFixedAccessChunk(aChunk);
+ goto done; // FINISHED
+ }
+
+ iAttributes |= EVariableAccess;
+ if (attribs & DMemModelChunk::ECode)
+ {
+ iNumNonFixedAccessCodeChunks++;
+ iAttributes |= EVariableCode;
+ }
+ if (++iNumNonFixedAccessChunks==1)
+ {
+ NKern::UnlockSystem();
+ DoAttributeChange(); // change process from fixed to variable access
+ NKern::LockSystem();
+ }
+
+ if (this!=TheCurrentThread->iOwningProcess)
+ {
+ // Adding chunk to another process
+ if (this==TheCurrentDataSectionProcess && !(attribs&DMemModelChunk::EFixedAddress))
+ TheCompleteDataSectionProcess=NULL; // just set partial state change flag and leave chunk alone
+ if (this==TheCurrentAddressSpace)
+ TheCurrentAddressSpace=NULL;
+ NKern::UnlockSystem();
+ goto done; // FINISHED
+ }
+
+ // Adding chunk to currently active user process
+ {
+ TheCurrentAddressSpace=NULL;
+ Mmu& m = Mmu::Get();
+ TUint32 ff=0; // flush flags
+ DMemModelChunk::TChunkState state=isReadOnly?DMemModelChunk::ERunningRO:DMemModelChunk::ERunningRW;
+ if (attribs&DMemModelChunk::EFixedAddress)
+ {
+ // Fixed address chunk, just change permissions
+ ff|=aChunk->ApplyTopLevelPermissions(state);
+ }
+ else if (this==TheCurrentDataSectionProcess)
+ {
+ // Moving chunk.
+ // This process is already in the data section, so just move the chunk down.
+ // Must do flushing first
+ TheCompleteDataSectionProcess=NULL;
+ FlushBeforeChunkMove(aChunk);
+ aChunk->MoveToRunAddress(aDataSectionBase,state); // idempotent
+ TheCompleteDataSectionProcess=this;
+ }
+ else if (iNumMovingChunks==1)
+ {
+ // The first moving chunk being added to a process with the data section occupied by another process.
+ // This is the problematic case - we must displace the other process from the data section.
+ // However we must allow preemption after each chunk is moved. Note that if a reschedule does
+ // occur the necessary chunk moves will have been done by the scheduler, so we can finish
+ // immediately.
+ // Must do cache flushing first
+ m.GenericFlush(Mmu::EFlushDMove);
+ if (TheCurrentDataSectionProcess)
+ {
+ if (TheCurrentDataSectionProcess->iAttributes & EVariableCode)
+ ff |= Mmu::EFlushIPermChg;
+ SChunkInfo* pOtherProcChunks=TheCurrentDataSectionProcess->iChunks;
+ SChunkInfo* pEndOtherProcChunks=pOtherProcChunks+TheCurrentDataSectionProcess->iNumChunks;
+ NKern::FlashSystem();
+ // if a reschedule occurs, TheCompleteDataSectionProcess will become equal to this
+ while (TheCompleteDataSectionProcess!=this && pOtherProcChunks<pEndOtherProcChunks)
+ {
+ DMemModelChunk *pChunk=pOtherProcChunks->iChunk;
+ pChunk->MoveToHomeSection();
+ ++pOtherProcChunks;
+ TheCompleteDataSectionProcess=NULL;
+ NKern::FlashSystem();
+ }
+ }
+ if (TheCompleteDataSectionProcess!=this)
+ {
+ if (attribs & DMemModelChunk::ECode)
+ ff |= Mmu::EFlushIPermChg;
+ aChunk->MoveToRunAddress(aDataSectionBase,state);
+ TheCurrentDataSectionProcess=this;
+ TheCompleteDataSectionProcess=this;
+ }
+ }
+ TheCurrentAddressSpace=this;
+ TheCurrentVMProcess=this;
+ if (ff)
+ m.GenericFlush(ff);
+ }
+ }
+ NKern::UnlockSystem();
+done:
+ __KTRACE_OPT(KPROC,Kern::Printf("Added array entry for %x",aDataSectionBase));
+ __KTRACE_OPT(KPROC,Kern::Printf("Chunks maxsize %x",pC->iChunk->MaxSize()));
+ __DEBUG_EVENT(EEventUpdateProcess, this);
+ return KErrNone;
+ }
+
+TInt DMemModelProcess::AllocateDataSectionBase(DMemModelChunk& aChunk, TUint& aBase)
+ {
+ __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AllocateDataSectionBase"));
+ aBase=0;
+ if ((aChunk.iAttributes & DMemModelChunk::EPrivate) && this!=aChunk.iOwningProcess)
+ return KErrAccessDenied;
+ if (aChunk.iAttributes & DMemModelChunk::EFixedAddress)
+ {
+ aBase=aChunk.iHomeRegionBase;
+ return KErrNone;
+ }
+ Mmu& m = Mmu::Get();
+ TLinAddr base=0;
+ TLinAddr maxBase=0;
+ switch (aChunk.iChunkType)
+ {
+ case EUserData:
+ base=m.iDataSectionBase;
+ maxBase=m.iDllDataBase;
+ break;
+ case EUserCode:
+ case EUserSelfModCode:
+ MM::Panic(MM::EUserCodeNotFixed);
+ break;
+ case EDllData:
+ aBase=m.iDllDataBase;
+ return KErrNone;
+ default:
+ __KTRACE_OPT(KPANIC,Kern::Printf("DMemModelProcess::AllocateDataSectionBase BadChunkType %d",aChunk.iChunkType));
+ return KErrAccessDenied;
+ }
+
+ TLinAddr lastBase=base;
+ SChunkInfo *pS=iChunks;
+ SChunkInfo *pE=pS+iNumChunks;
+ while (pS<pE)
+ {
+ TLinAddr thisBase=pS->iDataSectionBase;
+ __KTRACE_OPT(KPROC,Kern::Printf("Chunk already at %x",thisBase));
+ if (thisBase>=maxBase)
+ break;
+ if (thisBase>=base) // Within the range we are allocating
+ {
+ TInt gap=thisBase-lastBase;
+ if (gap>=aChunk.MaxSize())
+ break;
+ lastBase=thisBase+pS->iChunk->MaxSize();
+ }
+ pS++;
+ }
+ if (lastBase+aChunk.MaxSize()>maxBase)
+ {
+ __KTRACE_OPT(KPROC,Kern::Printf("ERROR - none allocated, out of memory"));
+ return KErrNoMemory;
+ }
+ aBase=lastBase;
+ __KTRACE_OPT(KPROC,Kern::Printf("User allocated %x",aBase));
+ return KErrNone;
+ }
+
+TUint8* DMemModelProcess::DataSectionBase(DMemModelChunk* aChunk)
+ {
+ // this can't be called after $LOCK is deleted
+ Kern::MutexWait(*iProcessLock);
+ TInt pos=0;
+ TInt r=ChunkIndex(aChunk,pos);
+ if (r==0) // Found the chunk
+ {
+ TUint8* answer=((TUint8*)iChunks[pos].iDataSectionBase);
+ __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::DataSectionBase %x",answer));
+ Kern::MutexSignal(*iProcessLock);
+ return answer;
+ }
+ __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::DataSectionBase chunk %08x not present in %08x",aChunk,this));
+ Kern::MutexSignal(*iProcessLock);
+ return(NULL);
+ }
+
+void DMemModelProcess::DoRemoveChunk(TInt aIndex)
+ {
+ // Must be called with process $LOCK mutex held
+ __DEBUG_EVENT(EEventUpdateProcess, this);
+ DMemModelChunk* chunk = iChunks[aIndex].iChunk;
+ Mmu& m = Mmu::Get();
+ NKern::LockSystem();
+ TInt attribs=chunk->iAttributes;
+ __KTRACE_OPT(KPROC,Kern::Printf("Removing Chunk attribs=%08x, Process attribs=%08x",attribs,iAttributes));
+ if (!(attribs&DMemModelChunk::EFixedAccess))
+ {
+ // Must leave chunk in process chunk list until we have flushed the cache if necessary
+ if (this==TheCurrentVMProcess && (attribs&DMemModelChunk::EFixedAddress))
+ {
+ TUint32 ff=chunk->ApplyTopLevelPermissions(DMemModelChunk::ENotRunning);
+ m.GenericFlush(ff);
+ // the system must now remain locked until the chunk is removed from the process chunk list
+ }
+ if (this==TheCurrentDataSectionProcess && !(attribs&DMemModelChunk::EFixedAddress))
+ {
+ // must do cache flush first
+ FlushBeforeChunkMove(chunk); // preemptible, but on return cache is free of chunk data
+ chunk->MoveToHomeSection();
+ // the system must now remain locked until the chunk is removed from the process chunk list
+ }
+ }
+
+ // Remove the chunk from the process chunk list
+ SChunkInfo *pD=iChunks+aIndex;
+ SChunkInfo *pS=iChunks+aIndex+1;
+ SChunkInfo *pE=iChunks+iNumChunks;
+ while(pS<pE)
+ *pD++=*pS++;
+ iNumChunks--;
+
+ // Update the process attribute flags
+ if (!(attribs&DMemModelChunk::EFixedAddress))
+ {
+ if (--iNumMovingChunks==0)
+ iAttributes &= ~EMoving;
+ }
+ if (!(attribs&DMemModelChunk::EFixedAccess))
+ {
+ if ((attribs&DMemModelChunk::ECode) && --iNumNonFixedAccessCodeChunks==0)
+ iAttributes &= ~EVariableCode;
+ if (this==TheCurrentDataSectionProcess && !(iAttributes&EMoving))
+ {
+ TheCurrentDataSectionProcess=NULL;
+ TheCompleteDataSectionProcess=NULL;
+ }
+ if (--iNumNonFixedAccessChunks==0)
+ {
+ iAttributes &= ~EVariableAccess;
+ if (this==TheCurrentVMProcess)
+ {
+ TheCurrentVMProcess=NULL;
+ TheCurrentAddressSpace=NULL;
+ }
+ NKern::UnlockSystem();
+ DoAttributeChange(); // change process from variable to fixed access
+ }
+ else
+ NKern::UnlockSystem();
+ }
+ else
+ {
+ NKern::UnlockSystem();
+ RemoveFixedAccessChunk(chunk);
+ }
+ }
+
+/**
+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)
+ while(iNumChunks)
+ 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::RemoveChunk %08x from %08x",aChunk,this));
+ Kern::MutexWait(*iProcessLock);
+ TInt pos=0;
+ TInt r=ChunkIndex(aChunk,pos);
+ __KTRACE_OPT(KPROC,if(r) Kern::Printf("Chunk lookup failed with %d",r));
+ if (r==0) // Found the chunk
+ {
+ __KTRACE_OPT(KPROC,Kern::Printf("Chunk access count %d",iChunks[pos].iAccessCount));
+ if (--iChunks[pos].iAccessCount==0)
+ DoRemoveChunk(pos);
+ }
+ Kern::MutexSignal(*iProcessLock);
+ }
+
+TInt DMemModelProcess::ChunkIndex(DMemModelChunk* aChunk,TInt& aPos)
+ {
+ if (aChunk==NULL)
+ return(KErrNotFound);
+ TInt i=0;
+ SChunkInfo *pC=iChunks;
+ SChunkInfo *pE=pC+iNumChunks;
+ while(pC<pE && (pC->iChunk!=aChunk))
+ {
+ pC++;
+ i++;
+ }
+ if (pC==pE)
+ return KErrNotFound;
+ aPos=i;
+ return KErrNone;
+ }
+
+void DMemModelProcess::RemoveDllData()
+//
+// Call with CodeSegLock held
+//
+ {
+ Kern::SafeClose((DObject*&)iDllDataChunk, this);
+ }
+
+TInt DMemModelProcess::CreateDllDataChunk()
+//
+// Call with CodeSegLock held
+//
+ {
+ __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=0;
+ c.iPreallocated=0;
+ c.iType=EDllData;
+ c.iMaxSize=(iAttributes&EFixedAddress) ? 1 : m.iMaxDllDataSize; // minimal size for fixed processes
+ c.iName.Set(KLitDllDollarData);
+ c.iOwner=this;
+ c.iInitialBottom=0;
+ 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)
+ {
+ Mmu& m = Mmu::Get();
+ TLinAddr dll_data_base=(iAttributes & EFixedAddress) ? (TLinAddr)iDllDataChunk->Base()
+ : TLinAddr(m.iDllDataBase);
+ TInt offset=aBase-dll_data_base;
+ __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));
+ Mmu& m = Mmu::Get();
+ TLinAddr dll_data_base=(iAttributes & EFixedAddress) ? (TLinAddr)iDllDataChunk->Base()
+ : TLinAddr(m.iDllDataBase);
+ TInt offset=aBase-dll_data_base;
+ TInt r=iDllDataChunk->Decommit(offset, aSize);
+ __ASSERT_ALWAYS(r==KErrNone,MM::Panic(MM::EDecommitInvalidDllDataAddress));
+ if (iDllDataChunk->iSize==0)
+ FreeDllDataChunk();
+ }
+
+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 || seg.iDataAllocBase==-1)
+ return KErrNone; // no extra mappings needed for kernel code or code with fixed data address
+ TInt r=KErrNone;
+ 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);
+ }
+ }
+ 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 || seg.iDataAllocBase==-1)
+ return; // no extra mappings needed for kernel code or code with fixed data address
+ 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));
+ }
+ }
+
+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.
+// aSrc is run address of memory to read. The memory is in aThread's address space.
+// aDest is the address of destination. The memory is in the current process's address space.
+// aExcTrap, exception trap object to be updated if the actual memory access is performed on another memory area. It happens
+// when reading is performed in chunks or if home adress is read instead of the provided run address.
+// Enter and return with system locked.
+ {
+ const TUint8* pS=(const TUint8*)aSrc;
+ TUint8* pD=(TUint8*)aDest;
+ const TUint8* pC=NULL;
+ TBool check=ETrue;
+ TBool suspect=EFalse;
+ DThread* pT=TheCurrentThread;
+ while (aLength)
+ {
+ if (check)
+ {
+ suspect=((aFlags & KCheckLocalAddress) && !MM::CurrentAddress(pT,pD,aLength,ETrue));
+ if (iMState==EDead)
+ return KErrDied;
+ pC=(const TUint8*)MM::CurrentAddress(this,pS,aLength,EFalse);
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead %08x<-[%08x::%08x]%08x+%x",pD,this,pS,pC,aLength));
+ if (!pC)
+ return KErrBadDescriptor;
+ }
+ TInt len=Min(aLength,K::MaxMemCopyInOneGo);
+ if (aExcTrap)
+ {
+ aExcTrap->iSize = (len + 2*(sizeof(TInt32)-1));//+6 is for the worst case. We do not have to be precise here.
+ aExcTrap->iRemoteBase = (TLinAddr)pC & ~(sizeof(TInt32)-1);
+ if (aExcTrap->iLocalBase)
+ aExcTrap->iLocalBase = (TLinAddr)pD & ~(sizeof(TInt32)-1);
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead exc. update: %08x %08x %08x",aExcTrap->iLocalBase,aExcTrap->iRemoteBase,aExcTrap->iSize));
+ }
+
+#ifdef __DEMAND_PAGING__
+ XTRAP_PAGING_START(check);
+ CHECK_PAGING_SAFE;
+#endif
+
+ suspect?(void)umemput(pD,pC,len):(void)memcpy(pD,pC,len);
+
+#ifdef __DEMAND_PAGING__
+ XTRAP_PAGING_END;
+ if(check<0)
+ return check; // paging error caused by bad client (I.e. 'this' thread was bad)
+ if(check)
+ {
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead paging trap, suspect %d, dest %08x, source %08x, length %d\n", suspect, pD, pC, len));
+ continue;
+ }
+#endif
+
+ pD+=len;
+ pS+=len;
+ pC+=len;
+ aLength-=len;
+ if (aLength)
+ check=NKern::FlashSystem();
+ }
+ return KErrNone;
+ }
+
+TInt DThread::RawWrite(const TAny* aDest, const TAny* aSrc, TInt aLength, TInt aFlags, DThread* aOriginatingThread, TIpcExcTrap* aExcTrap)
+//
+// Write to the thread's process.
+// aDest is run address of memory to write. It resides in this thread's address space.
+// aSrc is address of the source buffer. It resides in the current process's address space.
+// aOriginatingThread is the thread on behalf of which this operation is performed (eg client of device driver).
+// Enter and return with system locked
+// aExcTrap, exception trap object to be updated if the actual memory access is performed on another memory area. It happens
+// when reading is performed in chunks or if home adress is read instead of the provided run address.
+//
+ {
+ TUint8* pD=(TUint8*)aDest;
+ const TUint8* pS=(const TUint8*)aSrc;
+ TUint8* pC=NULL;
+ TBool check=ETrue;
+ TBool suspect=EFalse;
+ DThread* pT=TheCurrentThread;
+ DThread* pO=aOriginatingThread;
+ if (!pO)
+ pO=pT;
+ DProcess* pF=K::TheFileServerProcess;
+ TBool special=(iOwningProcess==pF && pO->iOwningProcess==pF);
+ while (aLength)
+ {
+ if (check)
+ {
+ suspect=((aFlags & KCheckLocalAddress) && !MM::CurrentAddress(pT,pS,aLength,EFalse));
+ if (iMState==EDead)
+ return KErrDied;
+ pC=(TUint8*)MM::CurrentAddress(this,pD,aLength,ETrue);
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead [%08x::%08x]%08x<-%08x+%x",this,pD,pC,pS,aLength));
+ if (!pC)
+ {
+ if (special)
+ pC=pD;
+ else
+ return KErrBadDescriptor;
+ }
+ }
+ TInt len=Min(aLength,K::MaxMemCopyInOneGo);
+ if (aExcTrap)
+ {
+ aExcTrap->iSize = (len + 2*(sizeof(TInt32)-1));//+6 is for the worst case. We do not have to be precise here.
+ aExcTrap->iRemoteBase = (TLinAddr)pC & ~(sizeof(TInt32)-1);
+ if (aExcTrap->iLocalBase)
+ aExcTrap->iLocalBase = (TLinAddr)pS & ~(sizeof(TInt32)-1);
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawWrite exc. update %08x %08x %08x",aExcTrap->iLocalBase,aExcTrap->iRemoteBase,aExcTrap->iSize));
+ }
+
+#ifdef __DEMAND_PAGING__
+ XTRAP_PAGING_START(check);
+ // Must check that it is safe to page, unless we are reading from unpaged ROM in which case
+ // we allow it. umemget does this anyway, so we just need to check if suspect is not set.
+ if (!suspect)
+ {
+ CHECK_PAGING_SAFE_RANGE((TLinAddr)aSrc, aLength);
+ CHECK_DATA_PAGING_SAFE_RANGE((TLinAddr)aDest, aLength);
+ }
+#endif
+
+ suspect?(void)umemget(pC,pS,len):(void)memcpy(pC,pS,len);
+
+#ifdef __DEMAND_PAGING__
+ XTRAP_PAGING_END
+ if(check<0)
+ return check; // paging error caused by bad client (I.e. 'this' thread was bad)
+ if(check)
+ {
+ __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawWrite paging trap, suspect %d, dest %08x, src %08x, length %d\n", suspect, pC, pD, len));
+ continue;
+ }
+#endif
+
+ pD+=len;
+ pS+=len;
+ pC+=len;
+ aLength-=len;
+ if (aLength)
+ check=NKern::FlashSystem();
+ }
+ return KErrNone;
+ }
+
+#ifdef __DEBUGGER_SUPPORT__
+
+TInt CodeModifier::SafeWriteCode(DProcess* aProcess, TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue)
+ {
+ //Set exception handler. Make sure the boundaries cover the worst case (aSize = 4)
+ TIpcExcTrap xt;
+ xt.iLocalBase=0;
+ xt.iRemoteBase=(TLinAddr)aAddress&~3; //word aligned.
+ xt.iSize=sizeof(TInt);
+ xt.iDir=1;
+ NKern::LockSystem();
+ TInt r=xt.Trap(NULL);
+ if (r==0)
+ {
+ r = WriteCode(aAddress, aSize, aValue, aOldValue);
+ xt.UnTrap();
+ }
+ NKern::UnlockSystem();
+ return r;
+ }
+
+TInt CodeModifier::WriteCode(TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue)
+ {
+ TUint userChunkBase = (TUint)MM::UserCodeChunk->Base();
+ TRomHeader romHeader = Epoc::RomHeader();
+
+ if (!((aAddress >= romHeader.iRomBase ) && (aAddress < (romHeader.iRomBase + romHeader.iUncompressedSize)))) //if not in ROM
+ if ( (aAddress<userChunkBase) || (aAddress) > (userChunkBase+MM::UserCodeChunk->MaxSize()) ) //and not in non-XIP code
+ return KErrBadDescriptor;
+
+ // if page was moved by defrag there may be a cache line with the
+ // wrong, old physical address, so we must invalidate this first.
+ InternalCache::Invalidate(KCacheSelectD, (TLinAddr)aAddress, 4);
+
+ //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__
+
+TInt DThread::ReadAndParseDesHeader(const TAny* aSrc, TDesHeader& aDest)
+//
+// Read the header of a remote descriptor.
+// Enter and return with system locked
+//
+ {
+ TInt r=KErrBadDescriptor;
+ DThread* thread = TheCurrentThread;
+ TRawDesHeader& header = (TRawDesHeader&)aDest;
+
+#ifdef __DEMAND_PAGING__
+retry:
+ TInt pagingFault;
+ XTRAP_PAGING_START(pagingFault);
+ CHECK_PAGING_SAFE;
+ thread->iIpcClient = this;
+#endif
+
+ const TUint32* pS=(const TUint32*)MM::CurrentAddress(this,aSrc,sizeof(TDesC8),EFalse);
+ if (pS && KErrNone==Kern::SafeRead(pS,&header[0],sizeof(TUint32)))
+ {
+ TInt type=header[0]>>KShiftDesType8;
+ static const TUint8 LengthLookup[16]={4,8,12,8,12,0,0,0,0,0,0,0,0,0,0,0};
+ TInt len=LengthLookup[type];
+ if(len>(TInt)sizeof(TUint32))
+ {
+ if(KErrNone==Kern::SafeRead(pS+1,&header[1],len-sizeof(TUint32)))
+ r = type;
+ // else, bad descriptor
+ }
+ else if(len)
+ r = type;
+ // else, bad descriptor
+ }
+
+#ifdef __DEMAND_PAGING__
+ thread->iIpcClient = NULL;
+ XTRAP_PAGING_END;
+ if(pagingFault<0)
+ return pagingFault; // paging error caused by bad client (I.e. 'this' thread was bad)
+ if(pagingFault)
+ goto retry;
+#endif
+
+ return K::ParseDesHeader(aSrc, header, aDest);
+ }
+
+DMemModelChunk* ChunkFromAddress(DThread* aThread, const TAny* aAddress)
+ {
+ DMemModelProcess* pP = (DMemModelProcess*)aThread->iOwningProcess;
+ DMemModelProcess::SChunkInfo* pS=pP->iChunks;
+ DMemModelProcess::SChunkInfo* pC=pS+pP->iNumChunks;
+ while(--pC>=pS && TUint(pC->iDataSectionBase)>TUint(aAddress)) {};
+ if(pC<pS)
+ return 0;
+ return pC->iChunk;
+ }
+
+/**
+ Open a shared chunk in which a remote address range is located.
+*/
+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->iNumChunks;
+ while(--pC>=pS && TUint(pC->iDataSectionBase)>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)
+ {
+ if ((iOwningProcess->iAttributes & DMemModelProcess::EFixedAddress )==0)
+ return KErrNotSupported;
+ Mmu& m=(Mmu&)*MmuBase::TheMmu;
+ return m.PreparePagesForDMA((TLinAddr)aLinAddr, aSize, aPhysicalPageList);
+ }
+
+TInt DThread::ReleaseMemoryFromDMA(const TAny* aLinAddr, TInt aSize, TPhysAddr* aPhysicalPageList)
+ {
+ if ((iOwningProcess->iAttributes & DMemModelProcess::EFixedAddress )==0)
+ return KErrNotSupported;
+ TInt pageCount = (((TInt)aLinAddr & KPageMask) + aSize + KPageMask) >> KPageShift;
+ Mmu& m=(Mmu&)*MmuBase::TheMmu;
+ return m.ReleasePagesFromDMA(aPhysicalPageList, pageCount);
+ }