diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kernel/eka/memmodel/epoc/flexible/mmu/mmu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kernel/eka/memmodel/epoc/flexible/mmu/mmu.h Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,2339 @@ +// 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: +// + +/** + @file + @internalComponent +*/ + +#ifndef __MMU_H__ +#define __MMU_H__ + +#define _USE_OLDEST_LISTS + +#include "mm.h" +#include "mmboot.h" +#include +#include + + +class DCoarseMemory; +class DMemoryObject; +class DMemoryMapping; + +/** +A page information structure giving the current use and state for a +RAM page being managed by the kernel. + +Any modification to the contents of any SPageInfo structure requires the +#MmuLock to be held. The exceptions to this is when a page is unused (#Type()==#EUnused), +in this case only the #RamAllocLock is required to use #SetAllocated(), #SetUncached(), +and #CacheInvalidateCounter(). + +These structures are stored in an array at the virtual address #KPageInfoLinearBase +which is indexed by the physical address of the page they are associated with, divided +by #KPageSize. The memory for this array is allocated by the bootstrap and it has +unallocated regions where no memory is required to store SPageInfo structures. +These unallocated memory regions are indicated by zeros in the bitmap stored at +#KPageInfoMap. +*/ +struct SPageInfo + { + /** + Enumeration for the usage of a RAM page. This is stored in #iType. + */ + enum TType + { + /** + No physical RAM exists for this page. + + This represents memory which doesn't exist or is not part of the physical + address range being managed by the kernel. + */ + EInvalid, + + /** + RAM fixed at boot time. + + This is for memory which was allocated by the bootstrap and which + the kernel does not actively manage. + */ + EFixed, + + /** + Page is unused. + + The page is either free memory in Mmu::iRamPageAllocator or the demand + paging 'live' list. + + To change from or to this type the #RamAllocLock must be held. + */ + EUnused, + + /** + Page is in an indeterminate state. + + A page is placed into this state by Mmu::PagesAllocated when it is + allocated (ceases to be #EUnused). Once the page + */ + EUnknown, + + /** + Page was allocated with Mmu::AllocPhysicalRam, Mmu::ClaimPhysicalRam + or is part of a reserved RAM bank set at system boot. + */ + EPhysAlloc, + + /** + Page is owned by a memory object. + + #iOwner will point to the owning memory object and #iIndex will + be the page index into its memory for this page. + */ + EManaged, + + /** + Page is being used as a shadow page. + + @see DShadowPage. + */ + EShadow + }; + + + /** + Flags stored in #iFlags. + + The least significant bits of these flags are used for the #TMemoryAttributes + value for the page. + */ + enum TFlags + { + // lower bits hold TMemoryAttribute value for this page + + /** + Flag set to indicate that the page has writable mappings. + (This is to facilitate demand paged memory.) + */ + EWritable = 1<<(EMemoryAttributeShift), + + /** + Flag set to indicate that the memory page contents may be different + to those previously saved to backing store (contents are 'dirty'). + This is set whenever a page gains a writeable mapping and only every + cleared once a demand paging memory manager 'cleans' the page. + */ + EDirty = 1<<(EMemoryAttributeShift+1) + }; + + + /** + State for the page when being used to contain demand paged content. + */ + enum TPagedState + { + /** + Page is not being managed for demand paging purposes, is has been transiently + removed from the demand paging live list. + */ + EUnpaged = 0x0, + + /** + Page is in the live list as a young page. + */ + EPagedYoung = 0x1, + + /** + Page is in the live list as an old page. + */ + EPagedOld = 0x2, + + /** + Page was pinned but it has been moved but not yet freed. + */ + EPagedPinnedMoved = 0x3, + + /** + Page has been removed from live list to prevent contents being paged-out. + */ + // NOTE - This must be the same value as EStatePagedLocked as defined in mmubase.h + EPagedPinned = 0x4, + +#ifdef _USE_OLDEST_LISTS + /** + Page is in the live list as one of oldest pages that is clean. + */ + EPagedOldestClean = 0x5, + + /** + Page is in the live list as one of oldest pages that is dirty. + */ + EPagedOldestDirty = 0x6 +#endif + }; + + + /** + Additional flags, stored in #iFlags2. + */ + enum TFlags2 + { + /** + When #iPagedState==#EPagedPinned this indicates the page is a 'reserved' page + and is does not increase free page count when returned to the live list. + */ + EPinnedReserve = 1<<0, + }; + +private: + /** + Value from enum #TType, returned by #Type(). + */ + TUint8 iType; + + /** + Bitmask of values from #TFlags, returned by #Flags(). + */ + TUint8 iFlags; + + /** + Value from enum #TPagedState, returned by #PagedState(). + */ + TUint8 iPagedState; + + /** + Bitmask of values from #TFlags2. + */ + TUint8 iFlags2; + + union + { + /** + The memory object which owns this page. + Used for always set for #EManaged pages and can be set for #PhysAlloc pages. + */ + DMemoryObject* iOwner; + + /** + A pointer to the SPageInfo of the page that is being shadowed. + For use with #EShadow pages only. + */ + SPageInfo* iOriginalPageInfo; + }; + + /** + The index for this page within within the owning object's (#iOwner) memory. + */ + TUint32 iIndex; + + /** + Pointer identifying the current modifier of the page. See #SetModifier. + */ + TAny* iModifier; + + /** + Storage location for data specific to the memory manager object handling this page. + See #SetPagingManagerData. + */ + TUint32 iPagingManagerData; + + /** + Union of values which vary depending of the current value of #iType. + */ + union + { + /** + When #iType==#EPhysAlloc, this stores a count of the number of memory objects + this page has been added to. + */ + TUint32 iUseCount; + + /** + When #iType==#EUnused, this stores the value of Mmu::iCacheInvalidateCounter + at the time the page was freed. This is used for some cache maintenance optimisations. + */ + TUint32 iCacheInvalidateCounter; + + /** + When #iType==#EManaged, this holds the count of the number of times the page was pinned. + This will only be non-zero for demand paged memory. + */ + TUint32 iPinCount; + }; + +public: + /** + Used for placing page into linked lists. E.g. the various demand paging live lists. + */ + SDblQueLink iLink; + +public: + /** + Return the SPageInfo for a given page of physical RAM. + */ + static SPageInfo* FromPhysAddr(TPhysAddr aAddress); + + /** + Return physical address of the RAM page which this SPageInfo object is associated. + If the address has no SPageInfo, then a null pointer is returned. + */ + static SPageInfo* SafeFromPhysAddr(TPhysAddr aAddress); + + /** + Return physical address of the RAM page which this SPageInfo object is associated. + */ + FORCE_INLINE TPhysAddr PhysAddr(); + + /** + Return a SPageInfo by conversion from the address of its embedded link member #iLink. + */ + FORCE_INLINE static SPageInfo* FromLink(SDblQueLink* aLink) + { + return (SPageInfo*)((TInt)aLink-_FOFF(SPageInfo,iLink)); + } + + // + // Getters... + // + + /** + Return the current #TType value stored in #iType. + @pre #MmuLock held. + */ + FORCE_INLINE TType Type() + { + CheckAccess("Type"); + return (TType)iType; + } + + /** + Return the current value of #iFlags. + @pre #MmuLock held (if \a aNoCheck false). + */ + FORCE_INLINE TUint Flags(TBool aNoCheck=false) + { + if(!aNoCheck) + CheckAccess("Flags"); + return iFlags; + } + + /** + Return the current value of #iPagedState. + @pre #MmuLock held. + */ + FORCE_INLINE TPagedState PagedState() + { + CheckAccess("PagedState"); + return (TPagedState)iPagedState; + } + + /** + Return the current value of #iOwner. + @pre #MmuLock held. + */ + FORCE_INLINE DMemoryObject* Owner() + { + CheckAccess("Owner"); + return iOwner; + } + + /** + Return the current value of #iIndex. + @pre #MmuLock held (if \a aNoCheck false). + */ + FORCE_INLINE TUint32 Index(TBool aNoCheck=false) + { + if(!aNoCheck) + CheckAccess("Index"); + return iIndex; + } + + /** + Return the current value of #iModifier. + @pre #MmuLock held (if \a aNoCheck false). + */ + FORCE_INLINE TAny* Modifier() + { + CheckAccess("Modifier"); + return iModifier; + } + + + // + // Setters.. + // + + /** + Set this page as type #EFixed. + This is only used during boot by Mmu::Init2Common. + */ + inline void SetFixed(TUint32 aIndex=0) + { + CheckAccess("SetFixed"); + Set(EFixed,0,aIndex); + } + + /** + Set this page as type #EUnused. + + @pre #MmuLock held. + @pre #RamAllocLock held if previous page type != #EUnknown. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetUnused() + { + CheckAccess("SetUnused",ECheckNotUnused|((iType!=EUnknown)?(TInt)ECheckRamAllocLock:0)); + iType = EUnused; + iModifier = 0; + // do not modify iFlags or iIndex in this function because page allocating cache cleaning operations rely on using this value + } + + /** + Set this page as type #EUnknown. + This is only used by Mmu::PagesAllocated. + + @pre #RamAllocLock held. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetAllocated() + { + CheckAccess("SetAllocated",ECheckUnused|ECheckRamAllocLock|ENoCheckMmuLock); + iType = EUnknown; + iModifier = 0; + // do not modify iFlags or iIndex in this function because cache cleaning operations rely on using this value + } + + /** + Set this page as type #EPhysAlloc. + @param aOwner Optional value for #iOwner. + @param aIndex Optional value for #iIndex. + + @pre #MmuLock held. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetPhysAlloc(DMemoryObject* aOwner=0, TUint32 aIndex=0) + { + CheckAccess("SetPhysAlloc"); + Set(EPhysAlloc,aOwner,aIndex); + iUseCount = 0; + } + + /** + Set this page as type #EManaged. + + @param aOwner Value for #iOwner. + @param aIndex Value for #iIndex. + @param aFlags Value for #iFlags (aOwner->PageInfoFlags()). + + @pre #MmuLock held. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetManaged(DMemoryObject* aOwner, TUint32 aIndex, TUint8 aFlags) + { + CheckAccess("SetManaged"); + Set(EManaged,aOwner,aIndex); + iFlags = aFlags; + iPinCount = 0; + } + + /** + Set this page as type #EShadow. + + This is for use by #DShadowPage. + + @param aIndex Value for #iIndex. + @param aFlags Value for #iFlags. + + @pre #MmuLock held. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetShadow(TUint32 aIndex, TUint8 aFlags) + { + CheckAccess("SetShadow"); + Set(EShadow,0,aIndex); + iFlags = aFlags; + } + + /** + Store a pointer to the SPageInfo of the page that this page is shadowing. + + @param aOrigPageInfo Pointer to the SPageInfo that this page is shadowing + + @pre #MmuLock held. + */ + inline void SetOriginalPage(SPageInfo* aOrigPageInfo) + { + CheckAccess("SetOriginalPage"); + __NK_ASSERT_DEBUG(iType == EShadow); + __NK_ASSERT_DEBUG(!iOriginalPageInfo); + iOriginalPageInfo = aOrigPageInfo; + } + + /** + Reutrns a pointer to the SPageInfo of the page that this page is shadowing. + + @return A pointer to the SPageInfo that this page is shadowing + + @pre #MmuLock held. + */ + inline SPageInfo* GetOriginalPage() + { + CheckAccess("GetOriginalPage"); + __NK_ASSERT_DEBUG(iType == EShadow); + __NK_ASSERT_DEBUG(iOriginalPageInfo); + return iOriginalPageInfo; + } + + +private: + /** Internal implementation factor for methods which set page type. */ + FORCE_INLINE void Set(TType aType, DMemoryObject* aOwner, TUint32 aIndex) + { + CheckAccess("Set",ECheckNotAllocated|ECheckNotPaged); + (TUint32&)iType = aType; // also clears iFlags, iFlags2 and iPagedState + iOwner = aOwner; + iIndex = aIndex; + iModifier = 0; + } + +public: + + + // + // + // + + /** + Set #iFlags to indicate that the contents of this page have been removed from + any caches. + + @pre #MmuLock held if #iType!=#EUnused, #RamAllocLock held if #iType==#EUnused. + */ + FORCE_INLINE void SetUncached() + { + CheckAccess("SetUncached",iType==EUnused ? ECheckRamAllocLock|ENoCheckMmuLock : 0); + __NK_ASSERT_DEBUG(iType==EUnused || (iType==EPhysAlloc && iUseCount==0)); + iFlags = EMemAttNormalUncached; + } + + /** + Set memory attributes and colour for a page of type #EPhysAlloc. + + This is set the first time a page of type #EPhysAlloc is added to a memory + object with DMemoryManager::AddPages or DMemoryManager::AddContiguous. + The set values are used to check constraints are met if the page is + also added to other memory objects. + + @param aIndex The page index within a memory object at which this page + has been added. This is stored in #iIndex and used to determine + the page's 'colour'. + @param aFlags Value for #iFlags. This sets the memory attributes for the page. + + @post #iModifier==0 to indicate that page usage has changed. + */ + inline void SetMapped(TUint32 aIndex, TUint aFlags) + { + CheckAccess("SetMapped"); + __NK_ASSERT_DEBUG(iType==EPhysAlloc); + __NK_ASSERT_DEBUG(iUseCount==0); // check page not already added to an object + iIndex = aIndex; + iFlags = aFlags; + iModifier = 0; + } + + /** + Set #iPagedState + + @pre #MmuLock held. + + @post #iModifier==0 to indicate that page state has changed. + */ + FORCE_INLINE void SetPagedState(TPagedState aPagedState) + { + CheckAccess("SetPagedState"); + __NK_ASSERT_DEBUG(aPagedState==iPagedState || iPagedState!=EPagedPinned || iPinCount==0); // make sure don't set an unpinned state if iPinCount!=0 + iPagedState = aPagedState; + iModifier = 0; + } + + /** + The the pages #iModifier value. + + #iModifier is cleared to zero whenever the usage or paging state of the page + changes. So if a thread sets this to a suitable unique value (e.g. the address + of a local variable) then it may perform a long running operation on the page + and later check with #CheckModified that no other thread has changed the page + state or used SetModifier in the intervening time. + Example. + + @code + TInt anyLocalVariable; // arbitrary local variable + + MmuLock::Lock(); + SPageInfo* thePageInfo = GetAPage(); + thePageInfo->SetModifier(&anyLocalVariable); // use &anyLocalVariable as value unique to this thread + MmuLock::Unlock(); + + DoOperation(thePageInfo); + + MmuLock::Lock(); + TInt r; + if(!thePageInfo->CheckModified(&anyLocalVariable)); + { + // nobody else touched the page... + OperationSucceeded(thePageInfo); + r = KErrNone; + } + else + { + // somebody else changed our page... + OperationInterrupted(thePageInfo); + r = KErrAbort; + } + MmuLock::Unlock(); + + return r; + @endcode + + @pre #MmuLock held. + */ + FORCE_INLINE void SetModifier(TAny* aModifier) + { + CheckAccess("SetModifier"); + iModifier = aModifier; + } + + /** + Return true if the #iModifier value does not match a specified value. + + @param aModifier A 'modifier' value previously set with #SetModifier. + + @pre #MmuLock held. + + @see SetModifier. + */ + FORCE_INLINE TBool CheckModified(TAny* aModifier) + { + CheckAccess("CheckModified"); + return iModifier!=aModifier; + } + + /** + Flag this page as having Page Table Entries which give writeable access permissions. + This sets flags #EWritable and #EDirty. + + @pre #MmuLock held. + */ + FORCE_INLINE void SetWritable() + { + CheckAccess("SetWritable"); + // This should only be invoked on paged pages. + __NK_ASSERT_DEBUG(PagedState() != EUnpaged); + iFlags |= EWritable; + SetDirty(); + } + + /** + Flag this page as having no longer having any Page Table Entries which give writeable + access permissions. + This clears the flag #EWritable. + + @pre #MmuLock held. + */ + FORCE_INLINE void SetReadOnly() + { + CheckAccess("SetReadOnly"); + iFlags &= ~EWritable; + } + + /** + Returns true if #SetWritable has been called without a subsequent #SetReadOnly. + This returns the flag #EWritable. + + @pre #MmuLock held. + */ + FORCE_INLINE TBool IsWritable() + { + CheckAccess("IsWritable"); + return iFlags&EWritable; + } + + /** + Flag this page as 'dirty', indicating that its contents may no longer match those saved + to a backing store. This sets the flag #EWritable. + + This is used in the management of demand paged memory. + + @pre #MmuLock held. + */ + FORCE_INLINE void SetDirty() + { + CheckAccess("SetDirty"); + iFlags |= EDirty; + } + + /** + Flag this page as 'clean', indicating that its contents now match those saved + to a backing store. This clears the flag #EWritable. + + This is used in the management of demand paged memory. + + @pre #MmuLock held. + */ + FORCE_INLINE void SetClean() + { + CheckAccess("SetClean"); + iFlags &= ~EDirty; + } + + /** + Return the #EDirty flag. See #SetDirty and #SetClean. + + This is used in the management of demand paged memory. + + @pre #MmuLock held. + */ + FORCE_INLINE TBool IsDirty() + { + CheckAccess("IsDirty"); + return iFlags&EDirty; + } + + + // + // Type specific... + // + + /** + Set #iCacheInvalidateCounter to the specified value. + + @pre #MmuLock held. + @pre #iType==#EUnused. + */ + void SetCacheInvalidateCounter(TUint32 aCacheInvalidateCounter) + { + CheckAccess("SetCacheInvalidateCounter"); + __NK_ASSERT_DEBUG(iType==EUnused); + iCacheInvalidateCounter = aCacheInvalidateCounter; + } + + /** + Return #iCacheInvalidateCounter. + + @pre #MmuLock held. + @pre #iType==#EUnused. + */ + TUint32 CacheInvalidateCounter() + { + CheckAccess("CacheInvalidateCounter",ECheckRamAllocLock|ENoCheckMmuLock); + __NK_ASSERT_DEBUG(iType==EUnused); + return iCacheInvalidateCounter; + } + + /** + Increment #iUseCount to indicate that the page has been added to a memory object. + + @return New value of #iUseCount. + + @pre #MmuLock held. + @pre #iType==#EPhysAlloc. + */ + TUint32 IncUseCount() + { + CheckAccess("IncUseCount"); + __NK_ASSERT_DEBUG(iType==EPhysAlloc); + return ++iUseCount; + } + + /** + Decrement #iUseCount to indicate that the page has been removed from a memory object. + + @return New value of #iUseCount. + + @pre #MmuLock held. + @pre #iType==#EPhysAlloc. + */ + TUint32 DecUseCount() + { + CheckAccess("DecUseCount"); + __NK_ASSERT_DEBUG(iType==EPhysAlloc); + __NK_ASSERT_DEBUG(iUseCount); + return --iUseCount; + } + + /** + Return #iUseCount, this indicates the number of times the page has been added to memory object(s). + + @return #iUseCount. + + @pre #MmuLock held. + @pre #iType==#EPhysAlloc. + */ + TUint32 UseCount() + { + CheckAccess("UseCount"); + __NK_ASSERT_DEBUG(iType==EPhysAlloc); + return iUseCount; + } + + /** + Increment #iPinCount to indicate that a mapping has pinned this page. + This is only done for demand paged memory; unpaged memory does not have + #iPinCount updated when it is pinned. + + @return New value of #iPinCount. + + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + TUint32 IncPinCount() + { + CheckAccess("IncPinCount"); + __NK_ASSERT_DEBUG(iType==EManaged); + return ++iPinCount; + } + + /** + Decrement #iPinCount to indicate that a mapping which was pinning this page has been removed. + This is only done for demand paged memory; unpaged memory does not have + #iPinCount updated when it is unpinned. + + @return New value of #iPinCount. + + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + TUint32 DecPinCount() + { + CheckAccess("DecPinCount"); + __NK_ASSERT_DEBUG(iType==EManaged); + __NK_ASSERT_DEBUG(iPinCount); + return --iPinCount; + } + + /** + Clear #iPinCount to zero as this page is no longer being used for the + pinned page. + This is only done for demand paged memory; unpaged memory does not have + #iPinCount set. + + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + void ClearPinCount() + { + CheckAccess("ClearPinCount"); + __NK_ASSERT_DEBUG(iType==EManaged); + __NK_ASSERT_DEBUG(iPinCount); + iPinCount = 0; + } + + /** + Return #iPinCount which indicates the number of mappings that have pinned this page. + This is only valid for demand paged memory; unpaged memory does not have + #iPinCount updated when it is pinned. + + @return #iPinCount. + + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + TUint32 PinCount() + { + CheckAccess("PinCount"); + __NK_ASSERT_DEBUG(iType==EManaged); + return iPinCount; + } + + /** + Set the #EPinnedReserve flag. + @pre #MmuLock held. + @see EPinnedReserve. + */ + void SetPinnedReserve() + { + CheckAccess("SetPinnedReserve"); + iFlags2 |= EPinnedReserve; + } + + /** + Clear the #EPinnedReserve flag. + @pre #MmuLock held. + @see EPinnedReserve. + */ + TBool ClearPinnedReserve() + { + CheckAccess("ClearPinnedReserve"); + TUint oldFlags2 = iFlags2; + iFlags2 = oldFlags2&~EPinnedReserve; + return oldFlags2&EPinnedReserve; + } + + /** + Set #iPagingManagerData to the specified value. + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + void SetPagingManagerData(TUint32 aPagingManagerData) + { + CheckAccess("SetPagingManagerData"); + __NK_ASSERT_DEBUG(iType==EManaged); + iPagingManagerData = aPagingManagerData; + } + + /** + Return #iPagingManagerData. + @pre #MmuLock held. + @pre #iType==#EManaged. + */ + TUint32 PagingManagerData() + { + CheckAccess("PagingManagerData"); + __NK_ASSERT_DEBUG(iType==EManaged); + return iPagingManagerData; + } + + // + // Debug... + // + +private: + enum TChecks + { + ECheckNotAllocated = 1<<0, + ECheckNotUnused = 1<<1, + ECheckUnused = 1<<2, + ECheckNotPaged = 1<<3, + ECheckRamAllocLock = 1<<4, + ENoCheckMmuLock = 1<<5 + }; +#ifdef _DEBUG + void CheckAccess(const char* aMessage, TUint aFlags=0); +#else + FORCE_INLINE void CheckAccess(const char* /*aMessage*/, TUint /*aFlags*/=0) + {} +#endif + +public: +#ifdef _DEBUG + /** + Debug function which outputs the contents of this object to the kernel debug port. + */ + void Dump(); +#else + FORCE_INLINE void Dump() + {} +#endif + }; + + +const TInt KPageInfosPerPageShift = KPageShift-KPageInfoShift; +const TInt KPageInfosPerPage = 1<>KPageShift); + } + +FORCE_INLINE TPhysAddr SPageInfo::PhysAddr() + { + return ((TPhysAddr)this)<>KPageTableShift; + return (SPageTableInfo*)KPageTableInfoBase+id; + } + +FORCE_INLINE TPte* SPageTableInfo::PageTable() + { + return (TPte*) + (KPageTableBase+ + ( + ((TLinAddr)this-(TLinAddr)KPageTableInfoBase) + <<(KPageTableShift-KPageTableInfoShift) + ) + ); + } + + + +/** +Class providing access to the mutex used to protect memory allocation operations; +this is the mutex Mmu::iRamAllocatorMutex. +In addition to providing locking, these functions monitor the system's free RAM +levels and call K::CheckFreeMemoryLevel to notify the system of changes. +*/ +class RamAllocLock + { +public: + /** + Acquire the lock. + The lock may be acquired multiple times by a thread, and will remain locked + until #Unlock has been used enough times to balance this. + */ + static void Lock(); + + /** + Release the lock. + + @pre The current thread has previously acquired the lock. + */ + static void Unlock(); + + /** + Allow another thread to acquire the lock. + This is equivalent to #Unlock followed by #Lock, but optimised + to only do this if there is another thread waiting on the lock. + + @return True if the lock was released by this function. + + @pre The current thread has previously acquired the lock. + */ + static TBool Flash(); + + /** + Return true if the current thread holds the lock. + This is used for debug checks. + */ + static TBool IsHeld(); + }; + + + +/** +Return true if the PageTableLock is held by the current thread. +This lock is the mutex used to protect page table allocation; it is acquired +with +@code + ::PageTables.Lock(); +@endcode +and released with +@code + ::PageTables.Unlock(); +@endcode +*/ +TBool PageTablesLockIsHeld(); + + + +/** +Class providing access to the fast mutex used to protect various +low level memory operations. + +This lock must only be held for a very short and bounded time. +*/ +class MmuLock + { +public: + /** + Acquire the lock. + */ + static void Lock(); + + /** + Release the lock. + + @pre The current thread has previously acquired the lock. + */ + static void Unlock(); + + /** + Allow another thread to acquire the lock. + This is equivalent to #Unlock followed by #Lock, but optimised + to only do this if there is another thread waiting on the lock. + + @return True if the lock was released by this function. + + @pre The current thread has previously acquired the lock. + */ + static TBool Flash(); + + /** + Return true if the current thread holds the lock. + This is used for debug checks. + */ + static TBool IsHeld(); + + /** + Increment a counter and perform the action of #Flash() once a given threshold + value is reached. After flashing the counter is reset. + + This is typically used in long running loops to periodically flash the lock + and so avoid holding it for too long, e.g. + + @code + MmuLock::Lock(); + TUint flash = 0; + const TUint KMaxInterationsWithLock = 10; + while(WorkToDo) + { + DoSomeWork(); + MmuLock::Flash(flash,KMaxInterationsWithLock); // flash every N loops + } + MmuLock::Unlock(); + @endcode + + @param aCounter Reference to the counter. + @param aFlashThreshold Value \a aCounter must reach before flashing the lock. + @param aStep Value to add to \a aCounter. + + @return True if the lock was released by this function. + + @pre The current thread has previously acquired the lock. + */ + static FORCE_INLINE TBool Flash(TUint& aCounter, TUint aFlashThreshold, TUint aStep=1) + { + UnlockGuardCheck(); + if((aCounter+=aStep)>Mmu::EAllocWipeByteShift)==0); // make sure flags don't run into wipe byte value + + +/** +Create a temporary mapping of a physical page. +The RamAllocatorMutex must be held before this function is called and not released +until after UnmapTemp has been called. + +@param aPage The physical address of the page to be mapped. +@param aColour The 'colour' of the page if relevant. +@param aSlot Slot number to use, must be less than Mmu::KNumTempMappingSlots. + +@return The linear address of where the page has been mapped. +*/ +FORCE_INLINE TLinAddr Mmu::MapTemp(TPhysAddr aPage, TUint aColour, TUint aSlot) + { +// Kern::Printf("Mmu::MapTemp(0x%08x,%d,%d)",aPage,aColour,aSlot); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + __NK_ASSERT_DEBUG(aSlot=aRequired; // Note, EUseReserveForPinReplacementPages will always return true. + } + + /** + Allocate replacement pages for this TPinArgs so that it has at least + \a aNumPages. + */ + TInt AllocReplacementPages(TUint aNumPages); + + /** + Free all replacement pages which this TPinArgs still owns. + */ + void FreeReplacementPages(); + +#ifdef _DEBUG + ~TPinArgs(); +#endif + + /** + Value used to indicate that replacement pages are to come + from an already allocated reserve and don't need specially + allocating. + */ + enum { EUseReserveForPinReplacementPages = 0xffffffffu }; + }; + + +#ifdef _DEBUG +inline TPinArgs::~TPinArgs() + { + __NK_ASSERT_DEBUG(!iReplacementPages); + } +#endif + + +/** +Enumeration used in various RestrictPages APIs to specify the type of restrictions to apply. +*/ +enum TRestrictPagesType + { + /** + Make all mappings of page not accessible. + Pinned mappings will veto this operation. + */ + ERestrictPagesNoAccess = 1, + + /** + Demand paged memory being made 'old'. + Specific case of ERestrictPagesNoAccess. + */ + ERestrictPagesNoAccessForOldPage = ERestrictPagesNoAccess|0x80000000, + + /** + For page moving pinned mappings always veto the moving operation. + */ + ERestrictPagesForMovingFlag = 0x40000000, + + /** + Movable memory being made no access whilst its being copied. + Special case of ERestrictPagesNoAccess where pinned mappings always veto + this operation even if they are read-only mappings. + */ + ERestrictPagesNoAccessForMoving = ERestrictPagesNoAccess|ERestrictPagesForMovingFlag, + }; + +#include "xmmu.h" + +#endif