diff -r 000000000000 -r a41df078684a kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,418 @@ +// Copyright (c) 2008-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: +// + +#include +#include "cache_maintenance.h" +#include "mm.h" +#include "mmu.h" +#include "mmanager.h" +#include "mobject.h" +#include "mpager.h" +#include "mcodepaging.h" + +/** +Manager for memory objects containing demand paged executable code. +This is the memory used by DCodeSegMemory object to store the contents of RAM loaded +EXEs and DLLs which are to be demand paged. + +This memory has associated information, supplied by the Loader, which enables +the executable's code to be located in the file system and its contents +relocated and fixed-up when demand loaded. + +@see DPagedCodeInfo +@see MM::PagedCodeNew +*/ +class DCodePagedMemoryManager : public DPagedMemoryManager + { +private: + // from DMemoryManager... + virtual TInt New(DMemoryObject*& aMemory, TUint aSizeInPages, TMemoryAttributes aAttributes, TMemoryCreateFlags aCreateFlags); + virtual void Destruct(DMemoryObject* aMemory); + virtual void Free(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + virtual TInt CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& aPageArrayEntry); + + // from DPagedMemoryManager... + virtual void Init3(); + virtual TInt InstallPagingDevice(DPagingDevice* aDevice); + virtual TInt AcquirePageReadRequest(DPageReadRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount); + virtual TInt ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest); + virtual TBool IsAllocated(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + +private: + /** + Array of paging devices used for each media drive. + This is a initialised by #InstallPagingDevice. + Drives without paging devices have the null pointer in their entry. + */ + DPagingDevice* iDevice[KMaxLocalDrives]; + +public: + /** + The single instance of this manager class. + */ + static DCodePagedMemoryManager TheManager; + + friend DPagingDevice* CodePagingDevice(TInt aDiveNum); + }; + + +/** +Reference counted object containing a #TPagedCodeInfo. +This is a structure containing the information about a demand paged code segment +which is required to load and fixup its code section. + +An instance of this object is created for each memory object being managed by +#DCodePagedMemoryManager, and a pointer to it is stored in the memory object's +DMemoryObject::iManagerData member. + +@see TPagedCodeInfo +@see MM::PagedCodeLoaded +*/ +class DPagedCodeInfo : public DReferenceCountedObject + { +public: + /** + Return a reference to the embedded #TPagedCodeInfo. + */ + inline TPagedCodeInfo& Info() + { return iInfo; } +private: + /** + @copybrief TPagedCodeInfo + */ + TPagedCodeInfo iInfo; + }; + + +DCodePagedMemoryManager DCodePagedMemoryManager::TheManager; +DPagedMemoryManager* TheCodePagedMemoryManager = &DCodePagedMemoryManager::TheManager; + + +DPagingDevice* CodePagingDevice(TInt aDriveNum) + { + __NK_ASSERT_DEBUG(aDriveNum> EKernelConfigCodePagingPolicyShift)); + if(codePolicy == EKernelConfigCodePagingPolicyNoPaging) + { + // no paging allowed so end now... + return KErrNone; + } + + TInt i; + for(i=0; iiDrivesSupported&(1<iManagerData; + if(pagedCodeInfo) + { + TPagedCodeInfo& info = pagedCodeInfo->Info(); + device = iDevice[info.iCodeLocalDrive]; + } + MmuLock::Unlock(); + + if(!device) + { + aRequest = 0; + return KErrNotFound; + } + + aRequest = device->iRequestPool->AcquirePageReadRequest(aMemory,aIndex,aCount); + return KErrNone; + } + + +TInt DCodePagedMemoryManager::New(DMemoryObject*& aMemory, TUint aSizeInPages, TMemoryAttributes aAttributes, TMemoryCreateFlags aCreateFlags) + { + DPagedCodeInfo* pagedCodeInfo = new DPagedCodeInfo; + if(!pagedCodeInfo) + return KErrNoMemory; + + TInt r = DPagedMemoryManager::New(aMemory, aSizeInPages, aAttributes, aCreateFlags); + if(r!=KErrNone) + pagedCodeInfo->Close(); + else + aMemory->iManagerData = pagedCodeInfo; + + return r; + } + + +void DCodePagedMemoryManager::Destruct(DMemoryObject* aMemory) + { + MmuLock::Lock(); + DPagedCodeInfo* pagedCodeInfo = (DPagedCodeInfo*)aMemory->iManagerData; + aMemory->iManagerData = 0; + MmuLock::Unlock(); + + if(pagedCodeInfo) + pagedCodeInfo->Close(); + + // base call to free memory and close object... + DPagedMemoryManager::Destruct(aMemory); + } + + +void DCodePagedMemoryManager::Free(DMemoryObject* aMemory, TUint aIndex, TUint aCount) + { + DoFree(aMemory,aIndex,aCount); + } + + +TInt DCodePagedMemoryManager::CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& aPageArrayEntry) + { + if(aPageInfo->IsDirty()==false) + return KErrNone; + + // shouldn't be asked to clean a page which is writable... + __NK_ASSERT_DEBUG(aPageInfo->IsWritable()==false); + + // Note, memory may have been modified by the CodeModifier class. + + // just mark page as clean as we don't try and preserve code modifications... + ThePager.SetClean(*aPageInfo); + + return KErrNone; + } + + +TInt ReadFunc(TAny* aArg1, TAny* aArg2, TLinAddr aBuffer, TInt aBlockNumber, TInt aBlockCount) + { + START_PAGING_BENCHMARK; + TInt drive = (TInt)aArg1; + TThreadMessage* msg = (TThreadMessage*)aArg2; + DPagingDevice* device = CodePagingDevice(drive); + TInt r = device->Read(msg, aBuffer, aBlockNumber, aBlockCount, drive); + __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocated memory, therefore can't fail with KErrNoMemory + END_PAGING_BENCHMARK(EPagingBmReadMedia); + return r; + } + + +TInt DCodePagedMemoryManager::ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest) + { + TRACE2(("DCodePagedMemoryManager::ReadPage(0x%08x,0x%08x,0x%08x,?,?)",aMemory,aIndex,aCount)); + + __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + + START_PAGING_BENCHMARK; + + MmuLock::Lock(); + DPagedCodeInfo* pagedCodeInfo = (DPagedCodeInfo*)aMemory->iManagerData; + if(pagedCodeInfo) + pagedCodeInfo->Open(); + MmuLock::Unlock(); + if(!pagedCodeInfo) + return KErrNotFound; + + TPagedCodeInfo& info = pagedCodeInfo->Info(); + DPagingDevice& device = *iDevice[info.iCodeLocalDrive]; + + TLinAddr linAddr = aRequest->MapPages(aIndex,aCount,aPages); + TInt r = KErrNone; + + if(!info.iCodeSize) + { + // no blockmap yet, use blank pages... + memset((TAny*)linAddr, aCount*KPageSize, 0x03); + CacheMaintenance::CodeChanged(linAddr, aCount*KPageSize); + goto done; + } + + for(; aCount; ++aIndex, --aCount, linAddr+=KPageSize) + { + // work out which bit of the file to read + TInt codeOffset = aIndex<iBuffer, + dataOffset, + dataSize, + device.iReadUnitShift, + ReadFunc, + (TAny*)info.iCodeLocalDrive, + (TAny*)&aRequest->iMessage); + + if(bufferStart<0) + { + r = bufferStart; // return error + __NK_ASSERT_DEBUG(0); + break; + } + + TLinAddr data = aRequest->iBuffer + bufferStart; + r = Decompress(info.iCompressionType, linAddr, decompressedSize, data, dataSize); + if(r>=0) + { + if(r!=decompressedSize) + { + __KTRACE_OPT(KPANIC, Kern::Printf("DCodePagedMemoryManager::ReadPage: error decompressing page at %08x + %x: %d", dataOffset, dataSize, r)); + __NK_ASSERT_DEBUG(0); + r = KErrCorrupt; + } + else + r = KErrNone; + } + else + { + __NK_ASSERT_DEBUG(0); + } + + if(r!=KErrNone) + break; + + if(decompressedSizeUnmapPages(true); + + pagedCodeInfo->AsyncClose(); + + END_PAGING_BENCHMARK(EPagingBmReadCodePage); + return r; + } + + +TBool DCodePagedMemoryManager::IsAllocated(DMemoryObject* aMemory, TUint aIndex, TUint aCount) + { + // all pages allocated if memory not destroyed (iManagerData!=0)... + return aMemory->iManagerData!=0; + } + + +TInt MM::PagedCodeNew(DMemoryObject*& aMemory, TUint aPageCount, TPagedCodeInfo*& aInfo) + { + TRACE(("MM::PagedCodeNew(?,0x%08x,0x%08x)",aPageCount,aInfo)); + TMemoryCreateFlags createFlags = (TMemoryCreateFlags)(EMemoryCreateNoWipe | EMemoryCreateAllowExecution); + TInt r = TheCodePagedMemoryManager->New(aMemory,aPageCount,EMemoryAttributeStandard,createFlags); + if(r==KErrNone) + aInfo = &((DPagedCodeInfo*)aMemory->iManagerData)->Info(); + TRACE(("MM::PagedCodeNew returns %d, aMemory=0x%08x",r,aMemory)); + return r; + } + + +void MM::PagedCodeLoaded(DMemoryObject* aMemory, TLinAddr aLoadAddress) + { + TRACE(("MM::PagedCodeLoaded(0x%08x,0x%08x)",aMemory,aLoadAddress)); + + TPagedCodeInfo& info = ((DPagedCodeInfo*)aMemory->iManagerData)->Info(); + + // we need to apply fixups for all memory already paged in. + // Note, if this memory is subsequently discarded it should not be paged-in again + // until after this function has completed, because the Loader won't touch the memory + // and it has not yet been mapped into any other process. + + // make iterator for memory... + RPageArray::TIter pageIter; + aMemory->iPages.FindStart(0,aMemory->iSizeInPages,pageIter); + + for(;;) + { + // find some pages... + RPageArray::TIter pageList; + TUint n = pageIter.Find(pageList); + if(!n) + break; + + // fix up each page found... + UNLOCK_USER_MEMORY(); + do + { + TUint i = pageList.Index(); + TLinAddr a = aLoadAddress+i*KPageSize; + info.ApplyFixups(a,i); + CacheMaintenance::CodeChanged(a, KPageSize); + // now we've finished updating the page, mark it as read only and + // clean as we don't need to save changes if it is stolen. + MmuLock::Lock(); + TPhysAddr* pages; + if(pageList.Pages(pages,1)==1) + if(RPageArray::IsPresent(*pages)) + {// The loader page still has a writable mapping but it won't + // touch the page again so this is safe. No use restricting the + // page to be read only as if the loader did write to it again + // it would just be rejuvenated as writeable and made dirty. + SPageInfo& pageInfo = *SPageInfo::FromPhysAddr(*pages); + pageInfo.SetReadOnly(); + ThePager.SetClean(pageInfo); + } + MmuLock::Unlock(); + + pageList.Skip(1); + } + while(pageList.Count()); + LOCK_USER_MEMORY(); + + // move on... + pageIter.FindRelease(n); + } + + // done... + aMemory->iPages.FindEnd(0,aMemory->iSizeInPages); + info.iLoaded = true; // allow ReadPage to start applying fixups when handling page faults + }