kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp
changeset 43 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,833 @@
+// Copyright (c) 1998-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 "mm.h"
+#include "mmu.h"
+
+#include "mrom.h"
+
+/**	Returns the amount of free RAM currently available.
+
+@return The number of bytes of free RAM currently available.
+@pre	any context
+ */
+EXPORT_C TInt Kern::FreeRamInBytes()
+	{
+	TUint numPages = TheMmu.FreeRamInPages();
+	// hack, clip free RAM to fit into a signed integer...
+	if(numPages>(KMaxTInt>>KPageShift))
+		return KMaxTInt;
+	return numPages*KPageSize;
+	}
+
+
+/**	Rounds up the argument to the size of a MMU page.
+
+	To find out the size of a MMU page:
+	@code
+	size = Kern::RoundToPageSize(1);
+	@endcode
+
+	@param aSize Value to round up
+	@pre any context
+ */
+EXPORT_C TUint32 Kern::RoundToPageSize(TUint32 aSize)
+	{
+	return (aSize+KPageMask)&~KPageMask;
+	}
+
+
+/**	Rounds up the argument to the amount of memory mapped by a MMU page 
+	directory entry.
+
+	Chunks occupy one or more consecutive page directory entries (PDE) and
+	therefore the amount of linear and physical memory allocated to a chunk is
+	always a multiple of the amount of memory mapped by a page directory entry.
+ */
+EXPORT_C TUint32 Kern::RoundToChunkSize(TUint32 aSize)
+	{
+	return (aSize+KChunkMask)&~KChunkMask;
+	}
+
+
+//
+// Epoc class
+// 
+#ifdef BTRACE_KERNEL_MEMORY
+TInt   Epoc::DriverAllocdPhysRam = 0;
+TInt   Epoc::KernelMiscPages = 0;
+#endif
+
+
+/**
+Allows the variant to specify the details of the RAM zones. This should be invoked 
+by the variant in its implementation of the pure virtual function Asic::Init1().
+
+There are some limitations to how the RAM zones can be specified:
+- Each RAM zone's address space must be distinct and not overlap with any 
+other RAM zone's address space
+- Each RAM zone's address space must have a size that is multiples of the 
+ASIC's MMU small page size and be aligned to the ASIC's MMU small page size, 
+usually 4KB on ARM MMUs.
+- When taken together all of the RAM zones must cover the whole of the physical RAM
+address space as specified by the bootstrap in the SuperPage members iTotalRamSize
+and iRamBootData;.
+- There can be no more than KMaxRamZones RAM zones specified by the base port
+
+Note the verification of the RAM zone data is not performed here but by the ram 
+allocator later in the boot up sequence.  This is because it is only possible to
+verify the zone data once the physical RAM configuration has been read from 
+the super page. Any verification errors result in a "RAM-ALLOC" panic 
+faulting the kernel during initialisation.
+
+@param aZones Pointer to an array of SRamZone structs containing the details for all 
+the zones. The end of the array is specified by an element with an iSize of zero. The array must 
+remain in memory at least until the kernel has successfully booted.
+
+@param aCallback Pointer to a call back function that the kernel may invoke to request 
+one of the operations specified by TRamZoneOp.
+
+@return KErrNone if successful, otherwise one of the system wide error codes
+
+@see TRamZoneOp
+@see SRamZone
+@see TRamZoneCallback
+*/
+EXPORT_C TInt Epoc::SetRamZoneConfig(const SRamZone* aZones, TRamZoneCallback aCallback)
+	{
+	TRamZoneCallback dummy;
+	// Ensure this is only called once and only while we are initialising the kernel
+	if (!K::Initialising || TheMmu.RamZoneConfig(dummy) != NULL)
+		{// fault kernel, won't return
+		K::Fault(K::EBadSetRamZoneConfig);
+		}
+
+	if (NULL == aZones)
+		{
+		return KErrArgument;
+		}
+	TheMmu.SetRamZoneConfig(aZones, aCallback);
+	return KErrNone;
+	}
+
+
+/**
+Modify the specified RAM zone's flags.
+
+This allows the BSP or device driver to configure which type of pages, if any,
+can be allocated into a RAM zone by the system.
+
+Note: updating a RAM zone's flags can result in
+	1 - memory allocations failing despite there being enough free RAM in the system.
+	2 - the methods TRamDefragRequest::EmptyRamZone(), TRamDefragRequest::ClaimRamZone()
+	or TRamDefragRequest::DefragRam() never succeeding.
+
+The flag masks KRamZoneFlagDiscardOnly, KRamZoneFlagMovAndDisOnly and KRamZoneFlagNoAlloc
+are intended to be used with this method.
+
+@param aId			The ID of the RAM zone to modify.
+@param aClearMask	The bit mask to clear, each flag of which must already be set on the RAM zone.
+@param aSetMask		The bit mask to set.
+
+@return KErrNone on success, KErrArgument if the RAM zone of aId not found or if 
+aSetMask contains invalid flag bits.
+
+@see TRamDefragRequest::EmptyRamZone()
+@see TRamDefragRequest::ClaimRamZone()
+@see TRamDefragRequest::DefragRam()
+
+@see KRamZoneFlagDiscardOnly
+@see KRamZoneFlagMovAndDisOnly
+@see KRamZoneFlagNoAlloc
+*/
+EXPORT_C TInt Epoc::ModifyRamZoneFlags(TUint aId, TUint aClearMask, TUint aSetMask)
+	{
+	RamAllocLock::Lock();
+	TInt r = TheMmu.ModifyRamZoneFlags(aId, aClearMask, aSetMask);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Gets the current count of a particular RAM zone's pages by type.
+
+@param aId The ID of the RAM zone to enquire about
+@param aPageData If successful, on return this contains the page count
+
+@return KErrNone if successful, KErrArgument if a RAM zone of aId is not found or
+one of the system wide error codes 
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+
+@see SRamZonePageCount
+*/
+EXPORT_C TInt Epoc::GetRamZonePageCount(TUint aId, SRamZonePageCount& aPageData)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::GetRamZonePageCount");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.GetRamZonePageCount(aId, aPageData);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Allocate a block of physically contiguous RAM with a physical address aligned
+to a specified power of 2 boundary.
+When the RAM is no longer required it should be freed using
+Epoc::FreePhysicalRam()
+
+@param	aSize		The size in bytes of the required block. The specified size
+					is rounded up to the page size, since only whole pages of
+					physical RAM can be allocated.
+@param	aPhysAddr	Receives the physical address of the base of the block on
+					successful allocation.
+@param	aAlign		Specifies the number of least significant bits of the
+					physical address which are required to be zero. If a value
+					less than log2(page size) is specified, page alignment is
+					assumed. Pass 0 for aAlign if there are no special alignment
+					constraints (other than page alignment).
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if a sufficiently large physically contiguous block of free
+		RAM	with the specified alignment could not be found.
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::AllocPhysicalRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::AllocPhysicalRam");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.AllocPhysicalRam
+		(
+		aPhysAddr,
+		MM::RoundToPageCount(aSize),
+		MM::RoundToPageShift(aAlign),
+		(Mmu::TRamAllocFlags)EMemAttStronglyOrdered
+		);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Allocate a block of physically contiguous RAM with a physical address aligned
+to a specified power of 2 boundary from the specified zone.
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+Note that this method only respects the KRamZoneFlagNoAlloc flag and will always attempt
+to allocate regardless of whether the other flags are set for the specified RAM zones 
+or not.
+
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+@param 	aZoneId		The ID of the zone to attempt to allocate from.
+@param	aSize		The size in bytes of the required block. The specified size
+					is rounded up to the page size, since only whole pages of
+					physical RAM can be allocated.
+@param	aPhysAddr	Receives the physical address of the base of the block on
+					successful allocation.
+@param	aAlign		Specifies the number of least significant bits of the
+					physical address which are required to be zero. If a value
+					less than log2(page size) is specified, page alignment is
+					assumed. Pass 0 for aAlign if there are no special alignment
+					constraints (other than page alignment).
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if a sufficiently large physically contiguous block of free
+		RAM	with the specified alignment could not be found within the specified 
+		zone.
+		KErrArgument if a RAM zone of the specified ID can't be found or if the
+		RAM zone has a total number of physical pages which is less than those 
+		requested for the allocation.
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint aZoneId, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
+	{
+	return ZoneAllocPhysicalRam(&aZoneId, 1, aSize, aPhysAddr, aAlign);
+	}
+
+
+/**
+Allocate a block of physically contiguous RAM with a physical address aligned
+to a specified power of 2 boundary from the specified RAM zones.
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+RAM will be allocated into the RAM zones in the order they are specified in the 
+aZoneIdList parameter. If the contiguous allocations are intended to span RAM zones 
+when required then aZoneIdList should be listed with the RAM zones in ascending 
+physical address order.
+
+Note that this method only respects the KRamZoneFlagNoAlloc flag and will always attempt
+to allocate regardless of whether the other flags are set for the specified RAM zones 
+or not.
+
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+@param 	aZoneIdList	A pointer to an array of RAM zone IDs of the RAM zones to 
+					attempt to allocate from.
+@param 	aZoneIdCount The number of RAM zone IDs contained in aZoneIdList.
+@param	aSize		The size in bytes of the required block. The specified size
+					is rounded up to the page size, since only whole pages of
+					physical RAM can be allocated.
+@param	aPhysAddr	Receives the physical address of the base of the block on
+					successful allocation.
+@param	aAlign		Specifies the number of least significant bits of the
+					physical address which are required to be zero. If a value
+					less than log2(page size) is specified, page alignment is
+					assumed. Pass 0 for aAlign if there are no special alignment
+					constraints (other than page alignment).
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if a sufficiently large physically contiguous block of free
+		RAM	with the specified alignment could not be found within the specified 
+		zone.
+		KErrArgument if a RAM zone of a specified ID can't be found or if the
+		RAM zones have a total number of physical pages which is less than those 
+		requested for the allocation.
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::ZoneAllocPhysicalRam");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.ZoneAllocPhysicalRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Attempt to allocate discontiguous RAM pages.
+
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+@param	aNumPages	The number of discontiguous pages required to be allocated
+@param	aPageList	This should be a pointer to a previously allocated array of
+					aNumPages TPhysAddr elements.  On a successful allocation it 
+					will receive the physical addresses of each page allocated.
+
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if the requested number of pages can't be allocated
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::AllocPhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "Epoc::AllocPhysicalRam");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.AllocPhysicalRam(aPageList,aNumPages,(Mmu::TRamAllocFlags)EMemAttStronglyOrdered);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Attempt to allocate discontiguous RAM pages from the specified zone.
+
+Note that this method only respects the KRamZoneFlagNoAlloc flag and will always attempt
+to allocate regardless of whether the other flags are set for the specified RAM zones 
+or not.
+
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+@param 	aZoneId		The ID of the zone to attempt to allocate from.
+@param	aNumPages	The number of discontiguous pages required to be allocated 
+					from the specified zone.
+@param	aPageList	This should be a pointer to a previously allocated array of
+					aNumPages TPhysAddr elements.  On a successful 
+					allocation it will receive the physical addresses of each 
+					page allocated.
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if the requested number of pages can't be allocated from the 
+		specified zone.
+		KErrArgument if a RAM zone of the specified ID can't be found or if the
+		RAM zone has a total number of physical pages which is less than those 
+		requested for the allocation.
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint aZoneId, TInt aNumPages, TPhysAddr* aPageList)
+	{
+	return ZoneAllocPhysicalRam(&aZoneId, 1, aNumPages, aPageList);
+	}
+
+
+/**
+Attempt to allocate discontiguous RAM pages from the specified RAM zones.
+The RAM pages will be allocated into the RAM zones in the order that they are specified 
+in the aZoneIdList parameter, the RAM zone preferences will be ignored.
+
+Note that this method only respects the KRamZoneFlagNoAlloc flag and will always attempt
+to allocate regardless of whether the other flags are set for the specified RAM zones 
+or not.
+
+When the RAM is no longer required it should be freed using Epoc::FreePhysicalRam().
+
+@param 	aZoneIdList	A pointer to an array of RAM zone IDs of the RAM zones to 
+					attempt to allocate from.
+@param	aZoneIdCount The number of RAM zone IDs pointed to by aZoneIdList.
+@param	aNumPages	The number of discontiguous pages required to be allocated 
+					from the specified zone.
+@param	aPageList	This should be a pointer to a previously allocated array of
+					aNumPages TPhysAddr elements.  On a successful 
+					allocation it will receive the physical addresses of each 
+					page allocated.
+@return	KErrNone if the allocation was successful.
+		KErrNoMemory if the requested number of pages can't be allocated from the 
+		specified zone.
+		KErrArgument if a RAM zone of a specified ID can't be found or if the
+		RAM zones have a total number of physical pages which is less than those 
+		requested for the allocation.
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aNumPages, TPhysAddr* aPageList)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "Epoc::ZoneAllocPhysicalRam");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.ZoneAllocPhysicalRam(aZoneIdList, aZoneIdCount, aNumPages, aPageList);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Free a previously-allocated block of physically contiguous RAM.
+
+Specifying one of the following may cause the system to panic: 
+a) an invalid physical RAM address.
+b) valid physical RAM addresses where some had not been previously allocated.
+c) an address not aligned to a page boundary.
+
+@param	aPhysAddr	The physical address of the base of the block to be freed.
+					This must be the address returned by a previous call to
+					Epoc::AllocPhysicalRam(), Epoc::ZoneAllocPhysicalRam(), 
+					Epoc::ClaimPhysicalRam() or Epoc::ClaimRamZone().
+@param	aSize		The size in bytes of the required block. The specified size
+					is rounded up to the page size, since only whole pages of
+					physical RAM can be allocated.
+@return	KErrNone if the operation was successful.
+
+
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::FreePhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreePhysicalRam");
+	RamAllocLock::Lock();
+	TheMmu.FreePhysicalRam(aPhysAddr,MM::RoundToPageCount(aSize));
+	RamAllocLock::Unlock();
+	return KErrNone;
+	}
+
+
+/**
+Free a number of physical RAM pages that were previously allocated using
+Epoc::AllocPhysicalRam() or Epoc::ZoneAllocPhysicalRam().
+
+Specifying one of the following may cause the system to panic: 
+a) an invalid physical RAM address.
+b) valid physical RAM addresses where some had not been previously allocated.
+c) an address not aligned to a page boundary.
+
+@param	aNumPages	The number of pages to be freed.
+@param	aPageList	An array of aNumPages TPhysAddr elements.  Where each element
+					should contain the physical address of each page to be freed.
+					This must be the same set of addresses as those returned by a 
+					previous call to Epoc::AllocPhysicalRam() or 
+					Epoc::ZoneAllocPhysicalRam().
+@return	KErrNone if the operation was successful.
+  
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+		
+*/
+EXPORT_C TInt Epoc::FreePhysicalRam(TInt aNumPages, TPhysAddr* aPageList)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::FreePhysicalRam");
+	RamAllocLock::Lock();
+	TheMmu.FreePhysicalRam(aPageList,aNumPages);
+	RamAllocLock::Unlock();
+	return KErrNone;
+	}
+
+
+/**
+Allocate a specific block of physically contiguous RAM, specified by physical
+base address and size.
+If and when the RAM is no longer required it should be freed using
+Epoc::FreePhysicalRam()
+
+@param	aPhysAddr	The physical address of the base of the required block.
+@param	aSize		The size in bytes of the required block. The specified size
+					is rounded up to the page size, since only whole pages of
+					physical RAM can be allocated.
+@return	KErrNone if the operation was successful.
+		KErrArgument if the range of physical addresses specified included some
+					which are not valid physical RAM addresses.
+		KErrInUse	if the range of physical addresses specified are all valid
+					physical RAM addresses but some of them have already been
+					allocated for other purposes.
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Epoc::ClaimPhysicalRam(TPhysAddr aPhysAddr, TInt aSize)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::ClaimPhysicalRam");
+	RamAllocLock::Lock();
+	TInt r = TheMmu.ClaimPhysicalRam
+		(
+		aPhysAddr,
+		MM::RoundToPageCount(aSize),
+		(Mmu::TRamAllocFlags)EMemAttStronglyOrdered
+		);
+	RamAllocLock::Unlock();
+	return r;
+	}
+
+
+/**
+Translate a virtual address to the corresponding physical address.
+
+@param	aLinAddr	The virtual address to be translated.
+@return	The physical address corresponding to the given virtual address, or
+		KPhysAddrInvalid if the specified virtual address is unmapped.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+*/
+EXPORT_C TPhysAddr Epoc::LinearToPhysical(TLinAddr aLinAddr)
+	{
+//	This precondition is violated by various parts of the system under some conditions,
+//	e.g. when __FLUSH_PT_INTO_RAM__ is defined. This function might also be called by
+//	a higher-level RTOS for which these conditions are meaningless. Thus, it's been
+//	disabled for now.
+//	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"Epoc::LinearToPhysical");
+
+	// When called by a higher-level OS we may not be in a DThread context, so avoid looking up the
+	// current process in the DThread for a global address
+	TInt osAsid = KKernelOsAsid;
+	if (aLinAddr < KGlobalMemoryBase)
+		{
+		// Get the os asid of current thread's process so no need to open a reference on it.
+		DMemModelProcess* pP=(DMemModelProcess*)TheCurrentThread->iOwningProcess;
+		osAsid = pP->OsAsid();
+		}
+	
+#if 1
+	return Mmu::UncheckedLinearToPhysical(aLinAddr, osAsid);
+#else
+	MmuLock::Lock();
+	TPhysAddr addr =  Mmu::LinearToPhysical(aLinAddr, osAsid);
+	MmuLock::Unlock();
+	return addr;
+#endif
+	}
+
+
+//
+// Misc
+//
+
+EXPORT_C TInt TInternalRamDrive::MaxSize()
+	{
+	TUint maxPages = (TUint(TheSuperPage().iRamDriveSize)>>KPageShift)+TheMmu.FreeRamInPages(); // current size plus spare memory
+	TUint maxPages2 = TUint(PP::RamDriveMaxSize)>>KPageShift;
+	if(maxPages>maxPages2)
+		maxPages = maxPages2;
+	return maxPages*KPageSize;
+	}
+
+
+TInt M::PageSizeInBytes()
+	{
+	return KPageSize;
+	}
+
+
+#ifdef BTRACE_KERNEL_MEMORY
+void M::BTracePrime(TUint aCategory)
+	{
+	// TODO:
+	}
+#endif
+
+
+
+//
+// DPlatChunkHw
+//
+
+/**
+Create a hardware chunk object, optionally mapping a specified block of physical
+addresses with specified access permissions and cache policy.
+
+When the mapping is no longer required, close the chunk using chunk->Close(0);
+Note that closing a chunk does not free any RAM pages which were mapped by the
+chunk - these must be freed separately using Epoc::FreePhysicalRam().
+
+@param	aChunk	Upon successful completion this parameter receives a pointer to
+				the newly created chunk. Upon unsuccessful completion it is
+				written with a NULL pointer. The virtual address of the mapping
+				can subsequently be discovered using the LinearAddress()
+				function on the chunk.
+@param	aAddr	The base address of the physical region to be mapped. This will
+				be rounded down to a multiple of the hardware page size before
+				being used.
+@param	aSize	The size of the physical address region to be mapped. This will
+				be rounded up to a multiple of the hardware page size before
+				being used; the rounding is such that the entire range from
+				aAddr to aAddr+aSize-1 inclusive is mapped. For example if
+				aAddr=0xB0001FFF, aSize=2 and the hardware page size is 4KB, an
+				8KB range of physical addresses from 0xB0001000 to 0xB0002FFF
+				inclusive will be mapped.
+@param	aMapAttr Mapping attributes required for the mapping. This is formed
+				by ORing together values from the TMappingAttributes enumeration
+				to specify the access permissions and caching policy.
+
+@pre Calling thread must be in a critical section.
+@pre Interrupts must be enabled.
+@pre Kernel must be unlocked.
+@pre No fast mutex can be held.
+@pre Call in a thread context.
+@pre Can be used in a device driver.
+@see TMappingAttributes
+*/
+EXPORT_C TInt DPlatChunkHw::New(DPlatChunkHw*& aChunk, TPhysAddr aAddr, TInt aSize, TUint aMapAttr)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"DPlatChunkHw::New");
+	__KTRACE_OPT(KMMU,Kern::Printf("DPlatChunkHw::New phys=%08x, size=%x, attribs=%x",aAddr,aSize,aMapAttr));
+
+	aChunk = NULL;
+
+	// check size...
+	if(aSize<=0)
+		return KErrArgument;
+	TPhysAddr end = aAddr+aSize-1;
+	if(end<aAddr) // overflow?
+		return KErrArgument;
+	aAddr &= ~KPageMask;
+	TUint pageCount = (end>>KPageShift)-(aAddr>>KPageShift)+1;
+
+	// check attributes...
+	TMappingPermissions perm;
+	TInt r = MM::MappingPermissions(perm,*(TMappingAttributes2*)&aMapAttr);
+	if(r!=KErrNone)
+		return r;
+	TMemoryAttributes attr;
+	r = MM::MemoryAttributes(attr,*(TMappingAttributes2*)&aMapAttr);
+	if(r!=KErrNone)
+		return r;
+
+	// construct a hardware chunk...
+	DMemModelChunkHw* pC = new DMemModelChunkHw;
+	if(!pC)
+		return KErrNoMemory;
+
+	// set the executable flags based on the specified mapping permissions...
+	TMemoryCreateFlags flags = EMemoryCreateDefault;
+	if(perm&EExecute)
+		flags = (TMemoryCreateFlags)(flags|EMemoryCreateAllowExecution);
+
+	r = MM::MemoryNew(pC->iMemoryObject, EMemoryObjectHardware, pageCount, flags, attr);
+	if(r==KErrNone)
+		{
+		r = MM::MemoryAddContiguous(pC->iMemoryObject,0,pageCount,aAddr);
+		if(r==KErrNone)
+			{
+			r = MM::MappingNew(pC->iKernelMapping,pC->iMemoryObject,perm,KKernelOsAsid);
+			if(r==KErrNone)
+				{
+				pC->iPhysAddr = aAddr;
+				pC->iLinAddr = MM::MappingBase(pC->iKernelMapping);
+				pC->iSize = pageCount<<KPageShift;
+				const TMappingAttributes2& lma = MM::LegacyMappingAttributes(attr,perm); // not needed, but keep in case someone uses this internal member
+				*(TMappingAttributes2*)&pC->iAttribs = lma;
+				}
+			}
+		}
+
+	if(r==KErrNone)
+		aChunk = pC;
+	else
+		pC->Close(NULL);
+	return r;
+	}
+
+
+TInt DMemModelChunkHw::Close(TAny*)
+	{
+	__KTRACE_OPT2(KOBJECT,KMMU,Kern::Printf("DMemModelChunkHw::Close %d %O",AccessCount(),this));
+	TInt r = Dec();
+	if(r==1)
+		{
+		MM::MappingDestroy(iKernelMapping);
+		MM::MemoryDestroy(iMemoryObject);
+		DBase::Delete(this);
+		}
+	return r;
+	}
+
+
+
+//
+// Demand Paging
+//
+
+#ifdef _DEBUG
+extern "C" void ASMCheckPagingSafe(TLinAddr aPC, TLinAddr aLR, TLinAddr aStartAddres, TUint aLength)
+	{
+	if(M::CheckPagingSafe(EFalse, aStartAddres, aLength))
+		return;
+	Kern::Printf("ASM_ASSERT_PAGING_SAFE FAILED: pc=%x lr=%x",aPC,aLR);
+	__NK_ASSERT_ALWAYS(0);
+	}
+
+extern "C" void ASMCheckDataPagingSafe(TLinAddr aPC, TLinAddr aLR, TLinAddr aStartAddres, TUint aLength)
+	{
+	if(M::CheckPagingSafe(ETrue, aStartAddres, aLength))
+		return;
+	__KTRACE_OPT(KDATAPAGEWARN,Kern::Printf("Data paging: ASM_ASSERT_DATA_PAGING_SAFE FAILED: pc=%x lr=%x",aPC,aLR));
+	}
+#endif
+
+
+DMutex* CheckMutexOrder()
+	{
+#ifdef _DEBUG
+	SDblQue& ml = TheCurrentThread->iMutexList;
+	if(ml.IsEmpty())
+		return NULL;
+	DMutex* mm = _LOFF(ml.First(), DMutex, iOrderLink);
+	if (KMutexOrdPageOut >= mm->iOrder)
+		return mm;
+#endif
+	return NULL;
+	}
+
+
+TBool M::CheckPagingSafe(TBool aDataPaging, TLinAddr aStartAddr, TUint aLength)
+	{
+	if(K::Initialising)
+		return ETrue;
+	
+	NThread* nt = NCurrentThread();
+	if(!nt)
+		return ETrue; // We've not booted properly yet!
+
+	if(aStartAddr>=KUserMemoryLimit)
+		return ETrue; // kernel memory can't be paged
+
+	if(IsUnpagedRom(aStartAddr,aLength))
+		return ETrue;
+
+	TBool dataPagingEnabled = K::MemModelAttributes&EMemModelAttrDataPaging;
+
+	DThread* thread = _LOFF(nt,DThread,iNThread);
+	NFastMutex* fm = NKern::HeldFastMutex();
+	if(fm)
+		{
+		if(!thread->iPagingExcTrap || fm!=&TheScheduler.iLock)
+			{
+			if (!aDataPaging)
+				{
+				__KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("DP: CheckPagingSafe FAILED - FM Held"));
+				return EFalse;
+				}
+			else
+				{
+				__KTRACE_OPT(KDATAPAGEWARN, Kern::Printf("Data paging: CheckPagingSafe FAILED - FM Held"));
+				return !dataPagingEnabled;
+				}
+			}
+		}
+
+	DMutex* m = CheckMutexOrder();
+	if (m)
+		{
+		if (!aDataPaging)
+			{
+			__KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("DP: Mutex Order Fault %O",m));
+			return EFalse;
+			}
+		else
+			{
+			__KTRACE_OPT(KDATAPAGEWARN, Kern::Printf("Data paging: Mutex Order Fault %O mem=%x+%x",m,aStartAddr,aLength));
+			return !dataPagingEnabled;
+			}
+		}
+	
+	return ETrue;
+	}
+
+
+
+EXPORT_C void DPagingDevice::NotifyIdle()
+	{
+	}
+
+EXPORT_C void DPagingDevice::NotifyBusy()
+	{
+	}