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