diff -r 000000000000 -r 96e5fb8b040d kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp --- /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>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<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() + { + }