diff -r 000000000000 -r 96e5fb8b040d kernel/eka/memmodel/epoc/multiple/mprocess.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/multiple/mprocess.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1029 @@ +// 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 + +#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("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 (pCiMaxSize>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); aiBase)+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); aiBase)+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 (; pCiChunk!=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<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<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<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<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)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)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); + } +