kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp
changeset 0 a41df078684a
child 22 2f92ad2dc5db
--- /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 <plat_priv.h>
+#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<KMaxLocalDrives);
+	return DCodePagedMemoryManager::TheManager.iDevice[aDriveNum];
+	}
+
+
+void DCodePagedMemoryManager::Init3()
+	{
+	TRACEB(("DCodePagedMemoryManager::Init3()"));
+	}
+
+
+TInt DCodePagedMemoryManager::InstallPagingDevice(DPagingDevice* aDevice)
+	{
+	TRACEB(("DCodePagedMemoryManager::InstallPagingDevice(0x%08x)",aDevice));
+
+	TUint codePolicy = TheSuperPage().KernelConfigFlags() & EKernelConfigCodePagingPolicyMask;
+	TRACEB(("Code Paging Policy = %d", codePolicy >> EKernelConfigCodePagingPolicyShift));
+	if(codePolicy == EKernelConfigCodePagingPolicyNoPaging)
+		{
+		// no paging allowed so end now...
+		return KErrNone;
+		}
+	
+	TInt i;
+	for(i=0; i<KMaxLocalDrives; ++i)
+		if(aDevice->iDrivesSupported&(1<<i))
+			{
+			TRACEB(("DCodePagedMemoryManager::InstallPagingDevice drive=%d",i));
+			TAny* null = 0;
+			if(!__e32_atomic_cas_ord_ptr(&iDevice[i], &null, aDevice)) // set iDevice[i]=aDevice if it was originally 0
+				{
+				// paging device already registered...
+				TRACEB(("DCodePagedMemoryManager::InstallPagingDevice returns ALREADY EXISTS!"));
+				return KErrAlreadyExists;
+				}
+			// flag code paging is supported...
+			__e32_atomic_ior_ord32(&K::MemModelAttributes, (TUint32)EMemModelAttrCodePaging);
+			}
+
+	return KErrNone;
+	}
+
+
+TInt DCodePagedMemoryManager::AcquirePageReadRequest(DPageReadRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+	{
+	DPagingDevice* device = 0;
+	MmuLock::Lock();
+	DPagedCodeInfo* pagedCodeInfo = (DPagedCodeInfo*)aMemory->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<<KPageShift;
+		TInt dataOffset;
+		TInt dataSize;
+		TInt decompressedSize = Min(KPageSize, info.iCodeSize-codeOffset);
+		if(info.iCompressionType)
+			{
+			dataOffset = info.iCodePageOffsets[aIndex];
+			dataSize = info.iCodePageOffsets[aIndex+1] - dataOffset;
+			__KTRACE_OPT(KPAGING,Kern::Printf("  compressed, file offset == %x, size == %d", dataOffset, dataSize));
+			}
+		else
+			{
+			dataOffset = codeOffset + info.iCodeStartInFile;
+			dataSize = Min(KPageSize, info.iBlockMap.DataLength()-dataOffset);
+			__NK_ASSERT_DEBUG(dataSize==decompressedSize);
+			__KTRACE_OPT(KPAGING,Kern::Printf("  uncompressed, file offset == %x, size == %d", dataOffset, dataSize));
+			}
+
+		TInt bufferStart = info.iBlockMap.Read(aRequest->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(decompressedSize<KPageSize)
+			memset((TAny*)(linAddr+decompressedSize), KPageSize-decompressedSize, 0x03);
+		if(info.iLoaded)
+			info.ApplyFixups(linAddr, aIndex);
+		}
+done:
+	aRequest->UnmapPages(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
+	}