kernel/eka/memmodel/epoc/flexible/mcodeseg.cpp
changeset 0 a41df078684a
child 4 56f325a607ea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mcodeseg.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,750 @@
+// Copyright (c) 1995-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 <memmodel.h>
+#include "mmu/mm.h"
+#include "mmboot.h"
+#include "mmu/mcodepaging.h"
+
+#include "cache_maintenance.h"
+
+
+DCodeSeg* M::NewCodeSeg(TCodeSegCreateInfo&)
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("M::NewCodeSeg"));
+	return new DMemModelCodeSeg;
+	}
+
+
+//
+// DMemModelCodeSegMemory
+//
+
+DEpocCodeSegMemory* DEpocCodeSegMemory::New(DEpocCodeSeg* aCodeSeg)
+	{
+	return new DMemModelCodeSegMemory(aCodeSeg);
+	}
+
+
+DMemModelCodeSegMemory::DMemModelCodeSegMemory(DEpocCodeSeg* aCodeSeg)
+	: DEpocCodeSegMemory(aCodeSeg)
+	{
+	}
+
+
+TInt DMemModelCodeSegMemory::Create(TCodeSegCreateInfo& aInfo, DMemModelProcess* aProcess)
+	{
+	TInt r;
+
+	TUint codePageCount;
+	TUint dataPageCount;
+	TBool isDemandPaged;
+	if(!aInfo.iUseCodePaging)
+		{
+		isDemandPaged = 0;
+		codePageCount = MM::RoundToPageCount(iRamInfo.iCodeSize+iRamInfo.iDataSize);
+		dataPageCount = 0;
+		}
+	else
+		{
+		isDemandPaged = 1;
+		codePageCount = MM::RoundToPageCount(iRamInfo.iCodeSize);
+		dataPageCount = MM::RoundToPageCount(iRamInfo.iDataSize);
+
+		iDataSectionMemory = Kern::Alloc(iRamInfo.iDataSize);
+		if(!iDataSectionMemory)
+			return KErrNoMemory;
+		}
+
+	iCodeSeg->iSize = codePageCount<<KPageShift;
+
+	// allocate virtual address for code to run at...
+	const TUint codeSize = codePageCount<<KPageShift;
+	if(iCodeSeg->IsExe())
+		{// Get the os asid without opening a reference on it as aProcess isn't fully 
+		// created yet so won't free its os asid.
+		r = MM::VirtualAlloc(aProcess->OsAsid(),iRamInfo.iCodeRunAddr,codeSize,isDemandPaged);
+		if(r!=KErrNone)
+			return r;
+		aProcess->iCodeVirtualAllocSize = codeSize;
+		aProcess->iCodeVirtualAllocAddress = iRamInfo.iCodeRunAddr;
+		iCodeSeg->iAttr |= ECodeSegAttAddrNotUnique;
+		}
+	else
+		{
+		r = MM::VirtualAllocCommon(iRamInfo.iCodeRunAddr,codeSize,isDemandPaged);
+		if(r!=KErrNone)
+			return r;
+		iVirtualAllocCommonSize = codeSize;
+		}
+
+	// create memory object for codeseg...
+	if(isDemandPaged)
+		{
+		// create memory object...
+		r = MM::PagedCodeNew(iCodeMemoryObject, codePageCount, iPagedCodeInfo);
+		if(r!=KErrNone)
+			return r;
+
+		// get file blockmap for codeseg contents...
+		r = iPagedCodeInfo->ReadBlockMap(aInfo);
+		if (r != KErrNone)
+			return r;
+		}
+	else
+		{
+		// create memory object...
+		TMemoryCreateFlags flags = (TMemoryCreateFlags)(EMemoryCreateNoWipe | EMemoryCreateAllowExecution);
+		r = MM::MemoryNew(iCodeMemoryObject, EMemoryObjectMovable, codePageCount, flags);
+		if(r!=KErrNone)
+			return r;
+
+		// commit memory...
+		r = MM::MemoryAlloc(iCodeMemoryObject,0,codePageCount);
+		if(r!=KErrNone)
+			return r;
+		}
+
+	// create a mapping of the memory for the loader...
+	// No need to open reference on os asid it is the current thread/process's.
+	DMemModelProcess* pP = (DMemModelProcess*)TheCurrentThread->iOwningProcess;
+	r = MM::MappingNew(iCodeLoadMapping,iCodeMemoryObject,EUserReadWrite,pP->OsAsid());
+	if(r!=KErrNone)
+		return r;
+
+	iRamInfo.iCodeLoadAddr = MM::MappingBase(iCodeLoadMapping);
+
+	// work out where the loader is to put the loaded data section...
+	TInt loadSize = iRamInfo.iCodeSize; // size of memory filled by loader
+	if(iRamInfo.iDataSize)
+		{
+		if(!dataPageCount)
+			{
+			// data loaded immediately after code...
+			iRamInfo.iDataLoadAddr = iRamInfo.iCodeLoadAddr+iRamInfo.iCodeSize;
+			loadSize += iRamInfo.iDataSize;
+			}
+		else
+			{
+			// create memory object for data...
+			DMemoryObject* dataMemory;
+			r = MM::MemoryNew(dataMemory, EMemoryObjectMovable, dataPageCount, EMemoryCreateNoWipe);
+			if(r!=KErrNone)
+				return r;
+
+			// commit memory...
+			r = MM::MemoryAlloc(dataMemory,0,dataPageCount);
+			if(r==KErrNone)
+				{
+				// create a mapping of the memory for the loader...
+				// No need to open reference on os asid it is the current thread/process's.
+				r = MM::MappingNew(iDataLoadMapping,dataMemory,EUserReadWrite,pP->OsAsid());
+				}
+
+			if(r!=KErrNone)
+				{
+				MM::MemoryDestroy(dataMemory);
+				return r;
+				}
+
+			iRamInfo.iDataLoadAddr = MM::MappingBase(iDataLoadMapping);
+			}
+		}
+
+	if(!isDemandPaged)
+		{
+		// wipe memory that the loader wont fill...
+		UNLOCK_USER_MEMORY();
+		memset((TAny*)(iRamInfo.iCodeLoadAddr+loadSize), 0x03, codeSize-loadSize);
+		LOCK_USER_MEMORY();
+		}
+
+	// done...
+	iCreator = pP;
+	
+	return KErrNone;
+	}
+
+
+TInt DMemModelCodeSegMemory::Loaded(TCodeSegCreateInfo& aInfo)
+	{
+	if(iPagedCodeInfo)
+		{
+		// get information needed to fixup code for it's run address...
+		TInt r = iPagedCodeInfo->ReadFixupTables(aInfo);
+		if(r!=KErrNone)
+			return r;
+		MM::PagedCodeLoaded(iCodeMemoryObject, iRamInfo.iCodeLoadAddr);
+		}
+	else
+		{
+		// make code visible to instruction cache...
+		UNLOCK_USER_MEMORY();
+		CacheMaintenance::CodeChanged(iRamInfo.iCodeLoadAddr, iRamInfo.iCodeSize);
+		LOCK_USER_MEMORY();
+		}
+
+	// adjust iDataLoadAddr to point to address contents for initial data section
+	// in running process...
+	if(iRamInfo.iDataLoadAddr)
+		{
+		TAny* dataSection = iDataSectionMemory;
+		if(dataSection)
+			{
+			// contents for initial data section to be stored in iDataSectionMemory...
+			UNLOCK_USER_MEMORY();
+			memcpy(dataSection,(TAny*)iRamInfo.iDataLoadAddr,iRamInfo.iDataSize);
+			LOCK_USER_MEMORY();
+			iRamInfo.iDataLoadAddr = (TLinAddr)dataSection;
+			}
+		else
+			{
+			// contents for initial data section stored after code...
+			__NK_ASSERT_DEBUG(iRamInfo.iDataLoadAddr==iRamInfo.iCodeLoadAddr+iRamInfo.iCodeSize); // check data loaded at end of code
+			iRamInfo.iDataLoadAddr = iRamInfo.iCodeRunAddr+iRamInfo.iCodeSize;
+			}
+		}
+
+	// copy export directory (this will now have fixups applied)...
+	TInt exportDirSize = iRamInfo.iExportDirCount * sizeof(TLinAddr);
+	if(exportDirSize > 0 || (exportDirSize==0 && (iCodeSeg->iAttr&ECodeSegAttNmdExpData)) )
+		{
+		exportDirSize += sizeof(TLinAddr);
+		TLinAddr* expDir = (TLinAddr*)Kern::Alloc(exportDirSize);
+		if(!expDir)
+			return KErrNoMemory;
+		iCopyOfExportDir = expDir;
+		TLinAddr expDirLoad = iRamInfo.iExportDir-iRamInfo.iCodeRunAddr+iRamInfo.iCodeLoadAddr;
+		memcpy(expDir,(TAny*)(expDirLoad-sizeof(TLinAddr)),exportDirSize);
+		}
+
+	// unmap code from loading process...
+	DMemModelProcess* pP=(DMemModelProcess*)TheCurrentThread->iOwningProcess;
+	__ASSERT_ALWAYS(iCreator==pP, MM::Panic(MM::ECodeSegLoadedNotCreator));
+	MM::MappingDestroy(iCodeLoadMapping);
+	MM::MappingAndMemoryDestroy(iDataLoadMapping);
+	iCreator=NULL;
+
+	// Mark the code memory object read only to prevent malicious code modifying it.
+	TInt r = MM::MemorySetReadOnly(iCodeMemoryObject);
+	__ASSERT_ALWAYS(r == KErrNone, MM::Panic(MM::ECodeSegSetReadOnlyFailure));
+
+	return KErrNone;
+	}
+
+
+void DMemModelCodeSegMemory::Destroy()
+	{
+	MM::MappingDestroy(iCodeLoadMapping);
+	MM::MappingAndMemoryDestroy(iDataLoadMapping);
+	}
+
+
+DMemModelCodeSegMemory::~DMemModelCodeSegMemory()
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelCodeSegMemory::~DMemModelCodeSegMemory %x", this));
+	__NK_ASSERT_DEBUG(iAccessCount==0);
+
+	MM::MappingDestroy(iCodeLoadMapping);
+	MM::MappingAndMemoryDestroy(iDataLoadMapping);
+	MM::MemoryDestroy(iCodeMemoryObject);
+
+	if(iVirtualAllocCommonSize)
+		MM::VirtualFreeCommon(iRamInfo.iCodeRunAddr, iVirtualAllocCommonSize);
+
+	Kern::Free(iCopyOfExportDir);
+	Kern::Free(iDataSectionMemory);
+	}
+
+
+//
+// DMemModelCodeSeg
+//
+
+DMemModelCodeSeg::DMemModelCodeSeg()
+	{
+	}
+
+
+DMemModelCodeSeg::~DMemModelCodeSeg()
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelCodeSeg::Destruct %C", this));
+	DCodeSeg::Wait();
+
+	MM::MappingDestroy(iCodeLoadMapping);
+	MM::MappingDestroy(iCodeGlobalMapping);
+	MM::MemoryDestroy(iCodeMemoryObject);
+
+	if(Memory())
+		Memory()->Destroy();
+
+	if(iDataAllocSize)
+		MM::VirtualFreeCommon(iDataAllocBase,iDataAllocSize);
+
+	DCodeSeg::Signal();
+
+	Kern::Free(iKernelData);
+
+	DEpocCodeSeg::Destruct();
+	}
+
+
+TInt DMemModelCodeSeg::DoCreateRam(TCodeSegCreateInfo& aInfo, DProcess* aProcess)
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelCodeSeg::DoCreateRam %C", this));
+
+	SRamCodeInfo& ri = RamInfo();
+	iSize = MM::RoundToPageSize(ri.iCodeSize+ri.iDataSize);
+	if (iSize==0)
+		return KErrCorrupt;
+
+	TBool kernel = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttKernel );
+//	TBool user_global = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttGlobal );
+	TBool user_local = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == 0 );
+
+	TUint total_data_size = ri.iDataSize+ri.iBssSize;
+
+	if(user_local)
+		{
+		// setup paging attribute for code...
+		if(aInfo.iUseCodePaging)
+			iAttr |= ECodeSegAttCodePaged;
+
+		if(total_data_size && !IsExe())
+			{
+			// setup paging attribute for data section...
+			if(aInfo.iUseCodePaging)
+				if(K::MemModelAttributes & EMemModelAttrDataPaging)
+					iAttr |= ECodeSegAttDataPaged;
+
+			// allocate virtual address for data section...
+			TInt r = MM::VirtualAllocCommon(iDataAllocBase,total_data_size,iAttr&ECodeSegAttDataPaged);
+			if(r!=KErrNone)
+				return r;
+			iDataAllocSize = total_data_size;
+			ri.iDataRunAddr = iDataAllocBase;
+			}
+
+		// create DCodeSegMemory for RAM loaded user local code...
+		TInt r = Memory()->Create(aInfo,(DMemModelProcess*)aProcess);
+
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+		if (r == KErrNone)
+			{
+			BTrace8(BTrace::EFlexibleMemModel,BTrace::EMemoryObjectIsCodeSeg,Memory()->iCodeMemoryObject,this);
+			}
+#endif
+		
+		return r;
+		}
+
+	// kernel or user-global code...
+
+	// create memory object for codeseg...
+	TMemoryCreateFlags flags = EMemoryCreateAllowExecution;
+	if(kernel)
+		{
+		flags = (TMemoryCreateFlags)(flags|EMemoryCreateNoWipe);
+		}
+	TInt r = MM::MemoryNew(iCodeMemoryObject, EMemoryObjectMovable, MM::BytesToPages(iSize), flags);
+	if(r!=KErrNone)
+		return r;
+
+	// commit memory...
+	r = MM::MemoryAlloc(iCodeMemoryObject,0,MM::BytesToPages(iSize));
+	if(r!=KErrNone)
+		return r;
+
+	// create a mapping of the memory for the loader...
+	// No need to open reference on os asid it is the current thread/process's.
+	DMemModelProcess* pP = (DMemModelProcess*)TheCurrentThread->iOwningProcess;
+	r = MM::MappingNew(iCodeLoadMapping,iCodeMemoryObject,EUserReadWrite,pP->OsAsid());
+	if(r!=KErrNone)
+		return r;
+	ri.iCodeLoadAddr = MM::MappingBase(iCodeLoadMapping);
+
+	// create a global mapping of the memory for the codeseg to run at...
+	r = MM::MappingNew(iCodeGlobalMapping,iCodeMemoryObject,kernel?ESupervisorExecute:EUserExecute,KKernelOsAsid);
+	if(r!=KErrNone)
+		return r;
+	ri.iCodeRunAddr = MM::MappingBase(iCodeGlobalMapping);
+
+	if(kernel)
+		{
+		// setup data section memory...
+		if (ri.iDataSize)
+			ri.iDataLoadAddr = ri.iCodeLoadAddr+ri.iCodeSize;
+		if (total_data_size)
+			{
+			iKernelData = Kern::Alloc(total_data_size);
+			if (!iKernelData)
+				return KErrNoMemory;
+			ri.iDataRunAddr = (TLinAddr)iKernelData;
+			}
+		}
+	else
+		{
+		// we don't allow static data in global code...
+		ri.iDataLoadAddr = 0;
+		ri.iDataRunAddr = 0;
+		}
+
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+	BTrace8(BTrace::EFlexibleMemModel,BTrace::EMemoryObjectIsCodeSeg,iCodeMemoryObject,this);
+#endif
+
+	// done...
+	return KErrNone;
+	}
+
+
+TInt DMemModelCodeSeg::DoCreateXIP(DProcess* aProcess)
+	{
+//	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelCodeSeg::DoCreateXIP %C proc %O", this, aProcess));
+	return KErrNone;
+	}
+
+
+TInt DMemModelCodeSeg::Loaded(TCodeSegCreateInfo& aInfo)
+	{
+	if(iXIP)
+		return DEpocCodeSeg::Loaded(aInfo);
+
+	TBool kernel = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttKernel );
+	TBool user_global = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttGlobal );
+	TBool user_local = ( (iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == 0 );
+	if(user_local)
+		{
+		TInt r = Memory()->Loaded(aInfo);
+		if(r!=KErrNone)
+			return r;
+		}
+	else if((kernel && iExeCodeSeg!=this) || user_global)
+		{
+		// user-global or kernel code...
+		SRamCodeInfo& ri = RamInfo();
+		UNLOCK_USER_MEMORY();
+		CacheMaintenance::CodeChanged(ri.iCodeLoadAddr, ri.iCodeSize);
+		LOCK_USER_MEMORY();
+		MM::MappingDestroy(iCodeLoadMapping);
+		// adjust iDataLoadAddr to point to address contents for initial data section
+		// in running process...
+		if(ri.iDataLoadAddr)
+			ri.iDataLoadAddr = ri.iCodeRunAddr+ri.iCodeSize;
+
+		// Mark the code memory object read only to prevent malicious code modifying it.
+		TInt r = MM::MemorySetReadOnly(iCodeMemoryObject);
+		__ASSERT_ALWAYS(r == KErrNone, MM::Panic(MM::ECodeSegSetReadOnlyFailure));
+		}
+	return DEpocCodeSeg::Loaded(aInfo);
+	}
+
+
+void DMemModelCodeSeg::ReadExportDir(TUint32* aDest)
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("DMemModelCodeSeg::ReadExportDir %C %08x",this, aDest));
+
+	if(!iXIP)
+		{
+		// This is not XIP code so the loader can't access the export directory. 
+		if (Memory()->iCopyOfExportDir)
+			{// This must be local user side code.
+			__NK_ASSERT_DEBUG((iAttr & (ECodeSegAttKernel|ECodeSegAttGlobal)) == 0);
+			// Copy the kernel's copy of the export directory for this code seg to the loader's buffer.
+			SRamCodeInfo& ri = RamInfo();
+			TInt size = (ri.iExportDirCount + 1) * sizeof(TLinAddr);
+			kumemput(aDest, Memory()->iCopyOfExportDir, size);
+			}
+		else
+			{// This must be kernel side code.
+			__NK_ASSERT_DEBUG((iAttr & (ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttKernel);
+			// Copy the export directory for this code seg to the loader's buffer.
+			SRamCodeInfo& ri = RamInfo();
+			TInt size = (ri.iExportDirCount + 1) * sizeof(TLinAddr);
+			TAny* expDirLoad = (TAny*)(ri.iExportDir - sizeof(TLinAddr));
+			kumemput(aDest, expDirLoad, size);
+			}
+		}
+	}
+
+
+TBool DMemModelCodeSeg::OpenCheck(DProcess* aProcess)
+	{
+	return FindCheck(aProcess);
+	}
+
+
+TBool DMemModelCodeSeg::FindCheck(DProcess* aProcess)
+	{
+	__KTRACE_OPT(KDLL,Kern::Printf("CSEG:%08x Compat? proc=%O",this,aProcess));
+	if (aProcess)
+		{
+		DMemModelProcess& p=*(DMemModelProcess*)aProcess;
+		DCodeSeg* pPSeg=p.CodeSeg();
+		if (iAttachProcess && iAttachProcess!=aProcess)
+			return EFalse;
+		if (iExeCodeSeg && iExeCodeSeg!=pPSeg)
+			return EFalse;
+		}
+	return ETrue;
+	}
+
+
+void DMemModelCodeSeg::BTracePrime(TInt aCategory)
+	{
+	DCodeSeg::BTracePrime(aCategory);
+
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+	if (aCategory == BTrace::EFlexibleMemModel || aCategory == -1)
+		{
+		// code seg mutex is held here, so memory objects cannot be destroyed
+		DMemModelCodeSegMemory* codeSegMemory = Memory();
+		if (codeSegMemory)
+			{
+			if (codeSegMemory->iCodeMemoryObject)
+				{
+				BTrace8(BTrace::EFlexibleMemModel,BTrace::EMemoryObjectIsCodeSeg,Memory()->iCodeMemoryObject,this);
+				}
+			}
+		else
+			{
+			if (iCodeMemoryObject)
+				{
+				BTrace8(BTrace::EFlexibleMemModel,BTrace::EMemoryObjectIsCodeSeg,iCodeMemoryObject,this);
+				}
+			}
+		}
+#endif	
+	}
+
+
+//
+// TPagedCodeInfo
+//
+
+TPagedCodeInfo::~TPagedCodeInfo()
+	{
+	Kern::Free(iCodeRelocTable);
+	Kern::Free(iCodePageOffsets);
+	}
+
+
+TInt TPagedCodeInfo::ReadBlockMap(const TCodeSegCreateInfo& aInfo)
+	{
+	if(aInfo.iCodeBlockMapEntriesSize <= 0)
+		return KErrArgument;  // no block map provided
+
+	// get compression data...
+	iCompressionType = aInfo.iCompressionType;
+	switch(iCompressionType)
+		{
+	case KFormatNotCompressed:
+		__ASSERT_COMPILE(KFormatNotCompressed==0); // Decompress() assumes this
+		break;
+
+	case KUidCompressionBytePair:
+		{
+		if(!aInfo.iCodePageOffsets)
+			return KErrArgument;
+
+		TInt pageCount = MM::RoundToPageCount(aInfo.iCodeSize);
+
+		TInt size = sizeof(TInt32) * (pageCount + 1);
+		iCodePageOffsets = (TInt32*)Kern::Alloc(size);
+		if(!iCodePageOffsets)
+			return KErrNoMemory;
+		kumemget32(iCodePageOffsets, aInfo.iCodePageOffsets, size);
+
+#ifdef __DUMP_BLOCKMAP_INFO
+		Kern::Printf("CodePageOffsets:");
+		for (TInt i = 0 ; i < pageCount + 1 ; ++i)
+			Kern::Printf("  %08x", iCodePageOffsets[i]);
+#endif
+
+		TInt last = 0;
+		for(TInt j=0; j<pageCount+1; ++j)
+			{
+			if(iCodePageOffsets[j] < last ||
+				iCodePageOffsets[j] > (aInfo.iCodeLengthInFile + aInfo.iCodeStartInFile))
+				{
+				__NK_ASSERT_DEBUG(0);
+				return KErrCorrupt;
+				}
+			last = iCodePageOffsets[j];
+			}
+		}
+		break;
+
+	default:
+		return KErrNotSupported;
+		}
+
+	// Copy block map data itself...
+
+#ifdef __DUMP_BLOCKMAP_INFO
+	Kern::Printf("Original block map");
+	Kern::Printf("  block granularity: %d", aInfo.iCodeBlockMapCommon.iBlockGranularity);
+	Kern::Printf("  block start offset: %x", aInfo.iCodeBlockMapCommon.iBlockStartOffset);
+	Kern::Printf("  start block address: %016lx", aInfo.iCodeBlockMapCommon.iStartBlockAddress);
+	Kern::Printf("  local drive number: %d", aInfo.iCodeBlockMapCommon.iLocalDriveNumber);
+	Kern::Printf("  entry size: %d", aInfo.iCodeBlockMapEntriesSize);
+#endif
+
+	// Find relevant paging device
+	iCodeLocalDrive = aInfo.iCodeBlockMapCommon.iLocalDriveNumber;
+	if(TUint(iCodeLocalDrive) >= (TUint)KMaxLocalDrives)
+		{
+		__KTRACE_OPT(KPAGING,Kern::Printf("Bad local drive number"));
+		return KErrArgument;
+		}
+
+	DPagingDevice* device = CodePagingDevice(iCodeLocalDrive);
+	if(!device)
+		{
+		__KTRACE_OPT(KPAGING,Kern::Printf("No paging device installed for drive"));
+		return KErrNotSupported;
+		}
+
+	// Set code start offset
+	iCodeStartInFile = aInfo.iCodeStartInFile;
+	if(iCodeStartInFile < 0)
+		{
+		__KTRACE_OPT(KPAGING,Kern::Printf("Bad code start offset"));
+		return KErrArgument;
+		}
+
+	// Allocate buffer for block map and copy from user-side
+	TBlockMapEntryBase* buffer = (TBlockMapEntryBase*)Kern::Alloc(aInfo.iCodeBlockMapEntriesSize);
+	if(!buffer)
+		return KErrNoMemory;
+	kumemget32(buffer, aInfo.iCodeBlockMapEntries, aInfo.iCodeBlockMapEntriesSize);
+
+#ifdef __DUMP_BLOCKMAP_INFO
+	Kern::Printf("  entries:");
+	for (TInt k = 0 ; k < aInfo.iCodeBlockMapEntriesSize / sizeof(TBlockMapEntryBase) ; ++k)
+		Kern::Printf("    %d: %d blocks at %08x", k, buffer[k].iNumberOfBlocks, buffer[k].iStartBlock);
+#endif
+
+	// Initialise block map
+	TInt r = iBlockMap.Initialise(aInfo.iCodeBlockMapCommon,
+								  buffer,
+								  aInfo.iCodeBlockMapEntriesSize,
+								  device->iReadUnitShift,
+								  iCodeStartInFile + aInfo.iCodeLengthInFile);
+	if(r!=KErrNone)
+		{
+		Kern::Free(buffer);
+		return r;
+		}
+
+#if defined(__DUMP_BLOCKMAP_INFO) && defined(_DEBUG)
+	iBlockMap.Dump();
+#endif
+
+	iCodeSize = aInfo.iCodeSize;
+	return KErrNone;
+	}
+
+
+/**
+Read code relocation table and import fixup table from user side.
+*/
+TInt TPagedCodeInfo::ReadFixupTables(const TCodeSegCreateInfo& aInfo)
+	{
+	iCodeRelocTableSize = aInfo.iCodeRelocTableSize;
+	iImportFixupTableSize = aInfo.iImportFixupTableSize;
+	iCodeDelta = aInfo.iCodeDelta;
+	iDataDelta = aInfo.iDataDelta;
+
+	// round sizes up to four-byte boundaries...
+	TUint relocSize = (iCodeRelocTableSize + 3) & ~3;
+	TUint fixupSize = (iImportFixupTableSize + 3) & ~3;
+
+	// copy relocs and fixups...
+	iCodeRelocTable = (TUint8*)Kern::Alloc(relocSize+fixupSize);
+	if (!iCodeRelocTable)
+		return KErrNoMemory;
+	iImportFixupTable = iCodeRelocTable + relocSize;
+	kumemget32(iCodeRelocTable, aInfo.iCodeRelocTable, relocSize);
+	kumemget32(iImportFixupTable, aInfo.iImportFixupTable, fixupSize);
+
+	return KErrNone;
+	}
+
+
+void TPagedCodeInfo::ApplyFixups(TLinAddr aBuffer, TUint iIndex)
+	{
+//	START_PAGING_BENCHMARK;
+	
+	// relocate code...
+	if(iCodeRelocTableSize)
+		{
+		TUint8* codeRelocTable = iCodeRelocTable;
+		TUint startOffset = ((TUint32*)codeRelocTable)[iIndex];
+		TUint endOffset = ((TUint32*)codeRelocTable)[iIndex+1];
+
+		__KTRACE_OPT(KPAGING, Kern::Printf("Performing code relocation: start == %x, end == %x", startOffset, endOffset));
+		__ASSERT_ALWAYS(startOffset<=endOffset && endOffset<=iCodeRelocTableSize, K::Fault(K::ECodeSegBadFixupTables));
+
+		const TUint32 codeDelta = iCodeDelta;
+		const TUint32 dataDelta = iDataDelta;
+
+		const TUint16* ptr = (const TUint16*)(codeRelocTable + startOffset);
+		const TUint16* end = (const TUint16*)(codeRelocTable + endOffset);
+		while(ptr<end)
+			{
+			TUint16 entry = *ptr++;
+			TUint32* addr = (TUint32*)(aBuffer+(entry&0x0fff));
+			TUint32 word = *addr;
+#ifdef _DEBUG
+			TInt type = entry&0xf000;
+			__NK_ASSERT_DEBUG(type==KTextRelocType || type==KDataRelocType);
+#endif
+			if(entry<KDataRelocType)
+				word += codeDelta;
+			else
+				word += dataDelta;
+			*addr = word;
+			}
+		}
+
+	// fixup imports...
+	if(iImportFixupTableSize)
+		{
+		TUint8* importFixupTable = iImportFixupTable;
+		TUint startOffset = ((TUint32*)importFixupTable)[iIndex];
+		TUint endOffset = ((TUint32*)importFixupTable)[iIndex+1];
+
+		__KTRACE_OPT(KPAGING, Kern::Printf("Performing import fixup: start == %x, end == %x", startOffset, endOffset));
+		__ASSERT_ALWAYS(startOffset<=endOffset && endOffset<=iImportFixupTableSize, K::Fault(K::ECodeSegBadFixupTables));
+
+		const TUint16* ptr = (const TUint16*)(importFixupTable + startOffset);
+		const TUint16* end = (const TUint16*)(importFixupTable + endOffset);
+
+		while(ptr<end)
+			{
+			TUint16 offset = *ptr++;
+			TUint32 wordLow = *ptr++;
+			TUint32 wordHigh = *ptr++;
+			TUint32 word = (wordHigh << 16) | wordLow;
+//			__KTRACE_OPT(KPAGING, Kern::Printf("DP: Fixup %08x=%08x", iRamInfo.iCodeRunAddr+(page<<KPageShift)+offset, word));
+			*(TUint32*)(aBuffer+offset) = word;
+			}
+		}
+	
+//	END_PAGING_BENCHMARK(DemandPaging::ThePager, EPagingBmFixupCodePage);
+	}
+
+