--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mm.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1136 @@
+// Copyright (c) 2007-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 "mobject.h"
+#include "mmapping.h"
+#include "mmanager.h"
+#include "mpdalloc.h"
+#include "mptalloc.h"
+#include "mpager.h"
+#include "maddressspace.h"
+
+
+
+
+//
+// DMutexPool
+//
+
+DMutexPool::~DMutexPool()
+ {
+ TUint i;
+ for(i=0; i<iCount; ++i)
+ {
+ DMutex* mutex = iMembers[i].iMutex;
+ if(mutex)
+ mutex->Close(0);
+ }
+ Kern::Free(iMembers);
+ }
+
+
+TInt DMutexPool::Create(TUint aCount, const TDesC* aName, TUint aOrder)
+ {
+ if(aCount>EMaxPoolSize)
+ return KErrTooBig;
+
+ iMembers = (SMember*)Kern::AllocZ(aCount*sizeof(SMember));
+ if(!iMembers)
+ return KErrNoMemory;
+
+ iCount = aCount;
+
+ TInt r = KErrNone;
+ TUint i;
+ for(i=0; i<aCount; ++i)
+ {
+ TKName name;
+ if(aName)
+ {
+ name = *aName;
+ name.AppendNum(i);
+ }
+ K::MutexCreate(iMembers[i].iMutex, name, NULL, EFalse, aOrder);
+ if(r!=KErrNone)
+ break;
+ }
+
+ return r;
+ }
+
+
+/**
+@class DMutexPool
+@details
+
+The cookie used for dynamically assigned mutexes is broken into three bit fields:
+- Bit 0, always set. (To distinguish the cookie from a proper DMutex*).
+- Bits 1 through #KMutexPoolIndexBits, these contain the index of the assigned
+ mutex within DMutexPool::iMembers.
+- Bits (#KMutexPoolIndexBits+1) through 31, the count of the number of threads waiting
+ for this particular mutex assignment. When this reaches zero, the mutex can
+ be unassigned.
+*/
+
+/**
+Number of bits used to contain the index value of a dynamically assigned pool mutex.
+*/
+const TUint KMutexPoolIndexBits = 7;
+
+const TUint KMutexPoolIndexMask = ((1<<KMutexPoolIndexBits)-1)<<1;
+const TUint KMutexPoolWaitCountIncrement = 1<<(KMutexPoolIndexBits+1);
+
+__ASSERT_COMPILE(DMutexPool::EMaxPoolSize<=TUint(KMutexPoolIndexMask/2+1)); // required for algorithm correctness
+
+__ASSERT_COMPILE(DMutexPool::EMaxPoolSize<=64); // required to avoid excessive system lock hold time
+
+
+void DMutexPool::Wait(DMutex*& aMutexRef)
+ {
+ NKern::LockSystem();
+
+ TUintPtr poolMutex = (TUintPtr)aMutexRef;
+ if(!poolMutex)
+ {
+ // try and find a free mutex, else use the next one...
+ TUint next = iNext;
+ do
+ {
+ if(iMembers[next].iUseCount==0)
+ break;
+ if(++next>=iCount)
+ next = 0;
+ }
+ while(next!=iNext);
+ // use found mutex...
+ ++iMembers[next].iUseCount;
+ poolMutex = (next*2)+1; // mutex index*2 | 1
+ // update next...
+ if(++next>=iCount)
+ next = 0;
+ iNext = next;
+ }
+
+ DMutex* mutex = (DMutex*)poolMutex;
+ if(poolMutex&1)
+ {
+ // mutex is a pool mutex, get pointer, and update wait count...
+ SMember* member = &iMembers[(poolMutex&KMutexPoolIndexMask)>>1];
+ mutex = member->iMutex;
+ poolMutex += KMutexPoolWaitCountIncrement;
+ __NK_ASSERT_ALWAYS(poolMutex>=KMutexPoolWaitCountIncrement);
+ aMutexRef = (DMutex*)poolMutex;
+ }
+
+ mutex->Wait();
+
+ NKern::UnlockSystem();
+ }
+
+
+void DMutexPool::Signal(DMutex*& aMutexRef)
+ {
+ NKern::LockSystem();
+
+ TUintPtr poolMutex = (TUintPtr)aMutexRef;
+ __NK_ASSERT_ALWAYS(poolMutex);
+
+ DMutex* mutex = (DMutex*)poolMutex;
+
+ if(poolMutex&1)
+ {
+ // mutex is a pool mutex, get pointer, and update wait count...
+ SMember* member = &iMembers[(poolMutex&KMutexPoolIndexMask)>>1];
+ mutex = member->iMutex;
+ __NK_ASSERT_ALWAYS(poolMutex>=KMutexPoolWaitCountIncrement);
+ poolMutex -= KMutexPoolWaitCountIncrement;
+ if(poolMutex<KMutexPoolWaitCountIncrement)
+ {
+ --member->iUseCount;
+ poolMutex = 0;
+ }
+ aMutexRef = (DMutex*)poolMutex;
+ }
+
+ mutex->Signal();
+ }
+
+
+TBool DMutexPool::IsHeld(DMutex*& aMutexRef)
+ {
+ TBool held = false;
+ NKern::LockSystem();
+ TUintPtr poolMutex = (TUintPtr)aMutexRef;
+ if(poolMutex)
+ {
+ DMutex* mutex = (DMutex*)poolMutex;
+ if(poolMutex&1)
+ {
+ SMember* member = &iMembers[(poolMutex&KMutexPoolIndexMask)>>1];
+ mutex = member->iMutex;
+ }
+ held = mutex->iCleanup.iThread==&Kern::CurrentThread();
+ }
+ NKern::UnlockSystem();
+ return held;
+ }
+
+
+
+//
+// DReferenceCountedObject
+//
+
+DReferenceCountedObject::~DReferenceCountedObject()
+ {
+ __NK_ASSERT_DEBUG(iReferenceCount==0);
+ }
+
+
+void DReferenceCountedObject::Open()
+ {
+ __ASSERT_CRITICAL
+ TBool ok = __e32_atomic_tas_ord32(&iReferenceCount, 1, 1, 0);
+ __NK_ASSERT_ALWAYS(ok);
+ }
+
+
+TBool DReferenceCountedObject::TryOpen()
+ {
+ __ASSERT_CRITICAL
+ TBool ok = __e32_atomic_tas_ord32(&iReferenceCount, 1, 1, 0);
+ return ok;
+ }
+
+
+TBool DReferenceCountedObject::CheckCloseIsSafe()
+ {
+ __ASSERT_CRITICAL
+#ifdef _DEBUG
+ NFastMutex* fm = NKern::HeldFastMutex();
+ if(fm)
+ {
+ Kern::Printf("DReferenceCountedObject[0x%08x]::Close() fast mutex violation %M",this,fm);
+ return false;
+ }
+ SDblQue& ml = TheCurrentThread->iMutexList;
+ if(!ml.IsEmpty())
+ {
+ DMutex* m = _LOFF(ml.First(), DMutex, iOrderLink);
+ if(m->iOrder<KMutexOrdKernelHeap)
+ {
+ Kern::Printf("DReferenceCountedObject[0x%08x]::Close() mutex order violation holding mutex %O",this,m);
+ return false;
+ }
+ }
+#endif
+ return true;
+ }
+
+
+TBool DReferenceCountedObject::CheckAsyncCloseIsSafe()
+ {
+ __ASSERT_CRITICAL
+#ifdef _DEBUG
+ NFastMutex* fm = NKern::HeldFastMutex();
+ if(fm)
+ {
+ Kern::Printf("DReferenceCountedObject[0x%08x]::AsyncClose() fast mutex violation %M",this,fm);
+ return false;
+ }
+#endif
+ return true;
+ }
+
+
+void DReferenceCountedObject::Close()
+ {
+ __ASSERT_CRITICAL
+ __NK_ASSERT_DEBUG(CheckCloseIsSafe());
+ __NK_ASSERT_DEBUG(iReferenceCount>0);
+ if (__e32_atomic_tas_ord32(&iReferenceCount, 1, -1, 0) == 1)
+ delete this;
+ }
+
+
+void DReferenceCountedObject::AsyncClose()
+ {
+ __ASSERT_CRITICAL
+ __NK_ASSERT_DEBUG(CheckAsyncCloseIsSafe());
+ __NK_ASSERT_DEBUG(iReferenceCount>0);
+ if (__e32_atomic_tas_ord32(&iReferenceCount, 1, -1, 0) == 1)
+ AsyncDelete();
+ }
+
+
+//
+// Memory object functions
+//
+
+TInt MM::MemoryNew(DMemoryObject*& aMemory, TMemoryObjectType aType, TUint aPageCount, TMemoryCreateFlags aCreateFlags, TMemoryAttributes aAttributes)
+ {
+ TRACE(("MM::MemoryNew(?,0x%08x,0x%08x,0x%08x,0x%08x)",aType,aPageCount,aCreateFlags,*(TUint32*)&aAttributes));
+
+ DMemoryManager* manager;
+ if(aCreateFlags&EMemoryCreateCustomManager)
+ manager = (DMemoryManager*)aType;
+ else
+ {
+ switch(aType)
+ {
+ case EMemoryObjectUnpaged:
+ manager = TheUnpagedMemoryManager;
+ break;
+ case EMemoryObjectMovable:
+ manager = TheMovableMemoryManager;
+ break;
+ case EMemoryObjectPaged:
+ manager = TheDataPagedMemoryManager;
+ break;
+ case EMemoryObjectDiscardable:
+ manager = TheDiscardableMemoryManager;
+ break;
+ case EMemoryObjectHardware:
+ manager = TheHardwareMemoryManager;
+ break;
+ default:
+ manager = 0;
+ __NK_ASSERT_DEBUG(0);
+ break;
+ }
+ }
+ TMemoryCreateFlags flags = (TMemoryCreateFlags)(aCreateFlags&~(EMemoryCreateDemandPaged));
+ TInt r = manager->New(aMemory,aPageCount,aAttributes,flags);
+ TRACE(("MM::MemoryNew returns %d, aMemory=0x%08x",r,aMemory));
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+ if (r == KErrNone)
+ aMemory->BTraceCreate();
+#endif
+ return r;
+ }
+
+
+TInt MM::MemoryClaimInitialPages(DMemoryObject* aMemory, TLinAddr aBase, TUint aSize, TMappingPermissions aPermissions, TBool aAllowGaps, TBool aAllowNonRamPages)
+ {
+ TRACE(("MM::MemoryClaimInitialPages(0x%08x,0x%08x,0x%08x,0x%08x,%d,%d)",aMemory,aBase,aPermissions,aSize,aAllowGaps!=0,aAllowNonRamPages!=0));
+ TInt r = aMemory->ClaimInitialPages(aBase,aSize,aPermissions,aAllowGaps,aAllowNonRamPages);
+ TRACE(("MM::MemoryClaimInitialPages returns %d",r));
+ __NK_ASSERT_DEBUG(r==KErrNone);
+ return r;
+ }
+
+
+void MM::MemorySetLock(DMemoryObject* aMemory, DMutex* aLock)
+ {
+ aMemory->SetLock(aLock);
+ }
+
+
+void MM::MemoryLock(DMemoryObject* aMemory)
+ {
+ MemoryObjectLock::Lock(aMemory);
+ }
+
+
+void MM::MemoryUnlock(DMemoryObject* aMemory)
+ {
+ MemoryObjectLock::Unlock(aMemory);
+ }
+
+
+void MM::MemoryDestroy(DMemoryObject*& aMemory)
+ {
+ DMemoryObject* memory = (DMemoryObject*)__e32_atomic_swp_ord_ptr(&aMemory, 0);
+ if (!memory)
+ return;
+ TRACE(("MM::MemoryDestroy(0x%08x)",memory));
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+ BTraceContext4(BTrace::EFlexibleMemModel,BTrace::EMemoryObjectDestroy,memory);
+#endif
+ memory->iManager->Destruct(memory);
+ }
+
+
+TInt MM::MemoryAlloc(DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+ {
+ TRACE(("MM::MemoryAlloc(0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->Alloc(aMemory,aIndex,aCount);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryAlloc returns %d",r));
+ return r;
+ }
+
+
+TInt MM::MemoryAllocContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TUint aAlign, TPhysAddr& aPhysAddr)
+ {
+ TRACE(("MM::MemoryAllocContiguous(0x%08x,0x%08x,0x%08x,%d,?)",aMemory,aIndex,aCount,aAlign));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->AllocContiguous(aMemory,aIndex,aCount,MM::RoundToPageShift(aAlign),aPhysAddr);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryAlloc returns %d (aPhysAddr=0x%08x)",r,aPhysAddr));
+ return r;
+ }
+
+
+void MM::MemoryFree(DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+ {
+ TRACE(("MM::MemoryFree(0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ aMemory->ClipRegion(aIndex,aCount);
+ aMemory->iManager->Free(aMemory,aIndex,aCount);
+ MemoryObjectLock::Unlock(aMemory);
+ }
+
+
+TInt MM::MemoryAddPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages)
+ {
+ TRACE(("MM::MemoryAddPages(0x%08x,0x%08x,0x%08x,?)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->AddPages(aMemory,aIndex,aCount,aPages);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryAddPages returns %d",r));
+ return r;
+ }
+
+
+TInt MM::MemoryAddContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr aPhysAddr)
+ {
+ TRACE(("MM::MemoryAddContiguous(0x%08x,0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount,aPhysAddr));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->AddContiguous(aMemory,aIndex,aCount,aPhysAddr);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryAddContiguous returns %d",r));
+ return r;
+ }
+
+
+TUint MM::MemoryRemovePages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages)
+ {
+ TRACE(("MM::MemoryRemovePages(0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ aMemory->ClipRegion(aIndex,aCount);
+ TInt r = aMemory->iManager->RemovePages(aMemory,aIndex,aCount,aPages);
+ if(r<0)
+ r = 0;
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryRemovePages returns %d",r));
+ return r;
+ }
+
+
+TInt MM::MemoryAllowDiscard(DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+ {
+ TRACE(("MM::MemoryAllowDiscard(0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->AllowDiscard(aMemory,aIndex,aCount);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryAllowDiscard returns %d",r));
+ return r;
+ }
+
+
+TInt MM::MemoryDisallowDiscard(DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+ {
+ TRACE(("MM::MemoryDisallowDiscard(0x%08x,0x%08x,0x%08x)",aMemory,aIndex,aCount));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ r = aMemory->iManager->DisallowDiscard(aMemory,aIndex,aCount);
+ MemoryObjectLock::Unlock(aMemory);
+ TRACE(("MM::MemoryDisallowDiscard returns %d",r));
+ return r;
+ }
+
+
+TInt MM::MemoryPhysAddr(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr& aPhysicalAddress, TPhysAddr* aPhysicalPageList)
+ {
+ TRACE(("MM::MemoryPhysAddr(0x%08x,0x%08x,0x%08x,?,?)",aMemory,aIndex,aCount));
+ TInt r = aMemory->PhysAddr(aIndex,aCount,aPhysicalAddress,aPhysicalPageList);
+ TRACE(("MM::MemoryPhysAddr returns %d aPhysicalAddress=0x%08x",r,aPhysicalAddress));
+ return r;
+ }
+
+
+void MM::MemoryBTracePrime(DMemoryObject* aMemory)
+ {
+ aMemory->BTraceCreate();
+ aMemory->iMappings.Lock();
+ TMappingListIter iter;
+ DMemoryMapping* mapping = (DMemoryMapping*)iter.Start(aMemory->iMappings);
+ while(mapping)
+ {
+ aMemory->iMappings.Unlock();
+ mapping->BTraceCreate();
+ aMemory->iMappings.Lock();
+ mapping = (DMemoryMapping*)iter.Next();
+ }
+ iter.Finish();
+ aMemory->iMappings.Unlock();
+ }
+
+
+void MM::MemoryClose(DMemoryObject* aMemory)
+ {
+ aMemory->Close();
+ }
+
+
+TBool MM::MemoryIsNotMapped(DMemoryObject* aMemory)
+ {
+ TBool r = aMemory->iMappings.IsEmpty();
+ TRACE2(("MM::MemoryIsNotMapped(0x%08x) returns %d",aMemory,r));
+ return r;
+ }
+
+//
+// Physical pinning
+//
+
+TInt MM::PinPhysicalMemory(DMemoryObject* aMemory, DPhysicalPinMapping* aPinObject, TUint aIndex, TUint aCount, TBool aReadOnly, TPhysAddr& aAddress, TPhysAddr* aPages, TUint32& aMapAttr, TUint& aColour)
+ {
+
+ if (!aMemory->CheckRegion(aIndex,aCount))
+ return KErrArgument;
+
+ TMappingPermissions permissions = aReadOnly ? ESupervisorReadOnly : ESupervisorReadWrite;
+ TInt r = aPinObject->Pin(aMemory, aIndex, aCount, permissions);
+ if (r == KErrNone)
+ {
+ r = aPinObject->PhysAddr(aIndex, aCount, aAddress, aPages);
+ if (r>=KErrNone)
+ {
+ r = KErrNone; //Do not report discontigious memory in return value.
+ const TMappingAttributes2& mapAttr2 =
+ MM::LegacyMappingAttributes(aMemory->Attributes(), permissions);
+ *(TMappingAttributes2*)&aMapAttr = mapAttr2;
+ }
+ else
+ {
+ aPinObject->Unpin();
+ }
+ }
+
+ aColour = 0;
+ return r;
+ }
+
+
+TInt MM::MemoryWipe(DMemoryObject* aMemory)
+ {
+ __NK_ASSERT_ALWAYS(aMemory->iMappings.IsEmpty()); // can't be mapped otherwise confidentiality can't be guaranteed
+ TRACE2(("MM::MemoryWipe(0x%08x)",aMemory));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r = aMemory->iManager->Wipe(aMemory);
+ MemoryObjectLock::Unlock(aMemory);
+ return r;
+ }
+
+
+TInt MM::MemorySetReadOnly(DMemoryObject* aMemory)
+ {
+ TRACE2(("MM::MemorySetReadOnly(0x%08x)",aMemory));
+ MemoryObjectLock::Lock(aMemory);
+ TInt r = aMemory->SetReadOnly();
+ MemoryObjectLock::Unlock(aMemory);
+ return r;
+ }
+
+//
+// Mapping functions
+//
+
+TInt MM::MappingNew(DMemoryMapping*& aMapping, DMemoryObject* aMemory, TMappingPermissions aPermissions, TInt aOsAsid, TMappingCreateFlags aFlags, TLinAddr aAddr, TUint aIndex, TUint aCount)
+ {
+ TRACE(("MM::MappingNew(?,0x%08x,0x%08x,%d,0x%08x,0x%08x,0x%08x,0x%08x)",aMemory, aPermissions, aOsAsid, aFlags, aAddr, aIndex, aCount));
+
+ /**
+ @todo Make mappings created with this function fail (panic?) if the are reused to map
+ another object.
+ */
+ if(aCount==~0u)
+ aCount = aMemory->iSizeInPages-aIndex;
+
+ // if memory object reserves all resources, make mappings also do so...
+ if(aMemory->iFlags&DMemoryObject::EReserveResources)
+ FlagSet(aFlags,EMappingCreateReserveAllResources);
+
+ // check if mapping is for global user data...
+ if(aOsAsid==(TInt)KKernelOsAsid && aPermissions&EUser)
+ FlagSet(aFlags,EMappingCreateUserGlobalVirtual);
+ else
+ FlagClear(aFlags,EMappingCreateUserGlobalVirtual);
+
+ // set paged attribute for mapping...
+ if(aMemory->IsDemandPaged())
+ FlagSet(aFlags,EMappingCreateDemandPaged);
+ else
+ FlagClear(aFlags,EMappingCreateDemandPaged);
+
+ DMemoryMapping* mapping = 0;
+ TInt r = KErrNone;
+ if(!aMemory->CheckRegion(aIndex,aCount))
+ r = KErrArgument;
+ else
+ {
+ mapping = aMemory->CreateMapping(aIndex, aCount);
+ if(!mapping)
+ r = KErrNoMemory;
+ }
+
+ if(!mapping)
+ {
+ // free any virtual address the mapping should have adopted...
+ if(aFlags&EMappingCreateAdoptVirtual)
+ MM::VirtualFree(aOsAsid, aAddr, aCount<<KPageShift);
+ }
+ else
+ {
+ r = mapping->Construct(aMemory->Attributes(), aFlags, aOsAsid, aAddr, aCount<<KPageShift, aIndex<<KPageShift);
+ if(r==KErrNone)
+ r = mapping->Map(aMemory, aIndex, aCount, aPermissions);
+ if(r!=KErrNone)
+ {
+ mapping->Close();
+ mapping = 0;
+ }
+ }
+
+ aMapping = mapping;
+ TRACE(("MM::MappingNew returns %d (aMapping=0x%0x)",r,aMapping));
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+ if (r == KErrNone)
+ aMapping->BTraceCreate();
+#endif
+ return r;
+ }
+
+
+TInt MM::MappingNew(DMemoryMapping*& aMapping, TUint aCount, TInt aOsAsid, TMappingCreateFlags aFlags, TLinAddr aAddr, TLinAddr aColourOffset)
+ {
+ TRACE2(("MM::MappingNew(?,0x%08x,%d,0x%08x,0x%08x,0x%08x)",aCount, aOsAsid, aFlags, aAddr, aColourOffset));
+
+ FlagClear(aFlags,EMappingCreateDemandPaged); // mapping can't use demand paged page tables
+
+ TInt r = KErrNone;
+ DMemoryMapping* mapping = new DFineMapping();
+ if(!mapping)
+ r = KErrNoMemory;
+
+ if(!mapping)
+ {
+ // free any virtual address the mapping should have adopted...
+ if(aFlags&EMappingCreateAdoptVirtual)
+ MM::VirtualFree(aOsAsid, aAddr, aCount<<KPageShift);
+ }
+ else
+ {
+ r = mapping->Construct(EMemoryAttributeStandard, aFlags, aOsAsid, aAddr, aCount<<KPageShift, aColourOffset);
+ if(r!=KErrNone)
+ {
+ mapping->Close();
+ mapping = 0;
+ }
+ }
+
+ aMapping = mapping;
+ TRACE2(("MM::MappingNew returns %d (aMapping=0x%0x)",r,aMapping));
+
+ return r;
+ }
+
+
+TInt MM::MappingMap(DMemoryMapping* aMapping, TMappingPermissions aPermissions, DMemoryObject* aMemory, TUint aIndex, TUint aCount)
+ {
+ TRACE2(("MM::MappingMap(0x%08x,0x%08x,0x%08x,0x%x,0x%x)",aMapping,aPermissions,aMemory,aIndex,aCount));
+ if(aCount==~0u)
+ aCount = aMemory->iSizeInPages-aIndex;
+ TInt r = aMapping->Map(aMemory, aIndex, aCount, aPermissions);
+ TRACE2(("MM::MappingMap returns %d",r));
+ return r;
+ }
+
+
+void MM::MappingUnmap(DMemoryMapping* aMapping)
+ {
+ if(aMapping->IsAttached())
+ {
+ TRACE2(("MM::MappingUnmap(0x%08x)",aMapping));
+ aMapping->Unmap();
+ }
+ }
+
+
+void MM::MappingDestroy(DMemoryMapping*& aMapping)
+ {
+ DMemoryMapping* mapping = (DMemoryMapping*)__e32_atomic_swp_ord_ptr(&aMapping, 0);
+ if (!mapping)
+ return;
+ TRACE(("MM::MappingDestroy(0x%08x)",mapping));
+#ifdef BTRACE_FLEXIBLE_MEM_MODEL
+ BTraceContext4(BTrace::EFlexibleMemModel,BTrace::EMemoryMappingDestroy,mapping);
+#endif
+ if(mapping->IsAttached())
+ mapping->Unmap();
+ mapping->Close();
+ }
+
+
+void MM::MappingDestroy(TLinAddr aAddr, TInt aOsAsid)
+ {
+ DMemoryMapping* mapping = AddressSpace[aOsAsid]->GetMapping(aAddr);
+ MM::MappingDestroy(mapping);
+ }
+
+
+void MM::MappingAndMemoryDestroy(DMemoryMapping*& aMapping)
+ {
+ DMemoryMapping* mapping = (DMemoryMapping*)__e32_atomic_swp_ord_ptr(&aMapping, 0);
+ TRACE(("MM::MappingAndMemoryDestroy(0x%08x)",mapping));
+ if (!mapping)
+ return;
+ DMemoryObject* memory = mapping->Memory(true); // safe because we assume owner hasn't unmapped mapping
+ MM::MappingDestroy(mapping);
+ MM::MemoryDestroy(memory);
+ }
+
+
+void MM::MappingAndMemoryDestroy(TLinAddr aAddr, TInt aOsAsid)
+ {
+ DMemoryMapping* mapping = AddressSpace[aOsAsid]->GetMapping(aAddr);
+ MM::MappingAndMemoryDestroy(mapping);
+ }
+
+
+TLinAddr MM::MappingBase(DMemoryMapping* aMapping)
+ {
+ TLinAddr base = aMapping->Base();
+ TRACE2(("MM::MappingBase(0x%08x) returns 0x%08x",aMapping,base));
+ return base;
+ }
+
+
+TInt MM::MappingOsAsid(DMemoryMapping* aMapping)
+ {
+ return aMapping->OsAsid();
+ }
+
+
+DMemoryObject* MM::MappingGetAndOpenMemory(DMemoryMapping* aMapping)
+ {
+ MmuLock::Lock();
+ DMemoryObject* memory = aMapping->Memory();
+ if (memory)
+ memory->Open();
+ MmuLock::Unlock();
+ TRACE2(("MM::MappingGetAndOpenMemory(0x%08x) returns 0x%08x",aMapping,memory));
+ return memory;
+ }
+
+
+void MM::MappingClose(DMemoryMapping* aMapping)
+ {
+ TRACE2(("MM::MappingClose(0x%08x)",aMapping));
+ aMapping->Close();
+ }
+
+
+DMemoryMapping* MM::FindMappingInThread(DMemModelThread* aThread, TLinAddr aAddr, TUint aSize,
+ TUint& aOffsetInMapping, TUint& aInstanceCount)
+ {
+ if(aAddr>=KGlobalMemoryBase)
+ {
+ // Address in global region, so look it up in kernel's address space...
+ return FindMappingInAddressSpace(KKernelOsAsid, aAddr, aSize, aOffsetInMapping, aInstanceCount);
+ }
+
+ // Address in thread's process address space so open a reference to its os asid
+ // so that it remains valid for FindMappingInAddressSpace() call.
+ DMemModelProcess* process = (DMemModelProcess*)aThread->iOwningProcess;
+ TInt osAsid = process->TryOpenOsAsid();
+ if (osAsid < 0)
+ {// The process no longer owns an address space so can't have any mappings.
+ return NULL;
+ }
+
+ DMemoryMapping* r = FindMappingInAddressSpace(osAsid, aAddr, aSize, aOffsetInMapping, aInstanceCount);
+
+ process->CloseOsAsid();
+ return r;
+ }
+
+
+DMemoryMapping* MM::FindMappingInAddressSpace( TUint aOsAsid, TLinAddr aAddr, TUint aSize,
+ TUint& aOffsetInMapping, TUint& aInstanceCount)
+ {
+ return AddressSpace[aOsAsid]->FindMapping(aAddr, aSize, aOffsetInMapping, aInstanceCount);
+ }
+
+
+
+//
+// Address space
+//
+
+TInt MM::AddressSpaceAlloc(TPhysAddr& aPageDirectory)
+ {
+ return DAddressSpace::New(aPageDirectory);
+ }
+
+
+void MM::AddressSpaceFree(TUint aOsAsid)
+ {
+ AddressSpace[aOsAsid]->Close();
+ }
+
+
+void MM::AsyncAddressSpaceFree(TUint aOsAsid)
+ {
+ AddressSpace[aOsAsid]->AsyncClose();
+ }
+
+
+TInt MM::VirtualAllocCommon(TLinAddr& aLinAddr, TUint aSize, TBool aDemandPaged)
+ {
+ TRACE(("MM::VirtualAllocCommon(?,0x%08x,%d)",aSize,aDemandPaged));
+ TUint pdeType = aDemandPaged ? EVirtualSlabTypeDemandPaged : 0;
+ TInt r = DAddressSpace::AllocateUserCommonVirtualMemory(aLinAddr, aSize, 0, aSize, pdeType);
+ TRACE(("MM::VirtualAllocCommon returns %d region=0x%08x+0x%08x",r,aLinAddr,aSize));
+ return r;
+ }
+
+
+void MM::VirtualFreeCommon(TLinAddr aLinAddr, TUint aSize)
+ {
+ TRACE(("MM::VirtualFreeCommon(0x%08x,0x%08x)",aLinAddr,aSize));
+ DAddressSpace::FreeUserCommonVirtualMemory(aLinAddr, aSize);
+ }
+
+
+TInt MM::VirtualAlloc(TInt aOsAsid, TLinAddr& aLinAddr, TUint aSize, TBool aDemandPaged)
+ {
+ TRACE(("MM::VirtualAlloc(?,%d,0x%08x,%d)",aOsAsid,aSize,aDemandPaged));
+ TUint pdeType = aDemandPaged ? EVirtualSlabTypeDemandPaged : 0;
+ TInt r = AddressSpace[aOsAsid]->AllocateVirtualMemory(aLinAddr, aSize, 0, aSize, pdeType);
+ TRACE(("MM::VirtualAlloc returns %d region=0x%08x+0x%08x",r,aLinAddr,aSize));
+ return r;
+ }
+
+
+void MM::VirtualFree(TInt aOsAsid, TLinAddr aLinAddr, TUint aSize)
+ {
+ TRACE(("MM::VirtualFree(%d,0x%08x,0x%08x)",aOsAsid,aLinAddr,aSize));
+ AddressSpace[aOsAsid]->FreeVirtualMemory(aLinAddr, aSize);
+ }
+
+
+
+//
+// Init
+//
+
+void MM::Init1()
+ {
+ TheMmu.Init1();
+ }
+
+
+extern DMutexPool MemoryObjectMutexPool;
+extern DMutexPool AddressSpaceMutexPool;
+
+void MM::Init2()
+ {
+ TInt r;
+
+ TheMmu.Init2();
+
+ // create mutex pools before calling any functions which require them...
+ _LIT(KAddressSpaceMutexName,"AddressSpaceMutex");
+ r = AddressSpaceMutexPool.Create(4, &KAddressSpaceMutexName, KMutexOrdAddresSpace);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+ _LIT(KMemoryObjectMutexName,"MemoryObjectMutex");
+ r = MemoryObjectMutexPool.Create(8, &KMemoryObjectMutexName, KMutexOrdMemoryObject);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ // use the Ram Allocator mutex for low-level memory functions...
+ DMutex* mmuAllocMutex = TheMmu.iRamAllocatorMutex;
+
+ // memory cleanup needs initialising before any memory is freed...
+ TMemoryCleanup::Init2();
+
+ // initialise allocators used for MMU operations...
+ RPageArray::Init2A();
+ PageTables.Init2(mmuAllocMutex); // must come before any other code which allocates memory objects
+ RPageArray::Init2B(mmuAllocMutex);
+ PageTables.Init2B();
+ PageDirectories.Init2();
+
+ // initialise address spaces...
+ DAddressSpace::Init2();
+
+ // init pager...
+ ThePager.Init2();
+
+ TheMmu.Init2Final();
+ }
+
+
+/** HAL Function wrapper for the RAM allocator.
+*/
+TInt RamHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2)
+ {
+ return TheMmu.RamHalFunction(aFunction, a1, a2);
+ }
+
+
+void MM::Init3()
+ {
+ __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("MM::Init3"));
+ ThePager.Init3();
+
+ // Register a HAL Function for the Ram allocator.
+ TInt r = Kern::AddHalEntry(EHalGroupRam, RamHalFunction, 0);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ TheMmu.Init3();
+ }
+
+
+TInt MM::InitFixedKernelMemory(DMemoryObject*& aMemory,
+ TLinAddr aStart,
+ TLinAddr aEnd,
+ TUint aInitSize,
+ TMemoryObjectType aType,
+ TMemoryCreateFlags aMemoryCreateFlags,
+ TMemoryAttributes aMemoryAttributes,
+ TMappingCreateFlags aMappingCreateFlags
+ )
+ {
+ TUint maxSize = aEnd-aStart;
+ TInt r = MM::MemoryNew(aMemory, aType, MM::BytesToPages(maxSize), aMemoryCreateFlags, aMemoryAttributes);
+ if(r==KErrNone)
+ {
+ TBool allowGaps = aInitSize&1; // lower bit of size is set if region to be claimed contains gaps
+ aInitSize &= ~1;
+ r = MM::MemoryClaimInitialPages(aMemory,aStart,aInitSize,ESupervisorReadWrite,allowGaps);
+ if(r==KErrNone)
+ {
+ DMemoryMapping* mapping;
+ r = MM::MappingNew(mapping,aMemory,ESupervisorReadWrite,KKernelOsAsid,aMappingCreateFlags,aStart);
+ // prevent any further mappings of this memory,
+ // this is needed for realtime and OOM guarantees...
+ aMemory->DenyMappings();
+ }
+ }
+ // Note, no cleanup is done if an error occurs because this function is only
+ // used at boot time and the system can't recover from an error
+ return r;
+ }
+
+
+void MM::Panic(MM::TMemModelPanic aPanic)
+ {
+ Kern::Fault("MemModel", aPanic);
+ }
+
+
+//
+//
+//
+
+TUint MM::BytesToPages(TUint aBytes)
+ {
+ if(aBytes&KPageMask)
+ Panic(EBadBytesToPages);
+ return aBytes>>KPageShift;
+ }
+
+
+TUint MM::RoundToPageSize(TUint aSize)
+ {
+ return (aSize+KPageMask)&~KPageMask;
+ }
+
+
+TUint MM::RoundToPageCount(TUint aSize)
+ {
+ return (aSize+KPageMask)>>KPageShift;
+ }
+
+
+TUint MM::RoundToPageShift(TUint aShift)
+ {
+ return aShift>(TUint)KPageShift ? aShift-KPageShift : 0;
+ }
+
+
+//
+//
+//
+
+void MM::ValidateLocalIpcAddress(TLinAddr aAddr, TUint aSize, TBool aWrite)
+ {
+ __NK_ASSERT_DEBUG(aSize);
+
+ TLinAddr end = aAddr+aSize-1;
+ if(end<aAddr)
+ end = ~(TLinAddr)0; // clip to end of memory
+
+ // if IPC region is in process local data area then it's OK...
+ if(end<KUserLocalDataEnd && aAddr>=KUserLocalDataBase)
+ return;
+
+ // if region overlaps alias region...
+ if(end>=KIPCAlias && aAddr<KIPCAlias+KIPCAliasAreaSize)
+ {
+ // remove alias...
+ ((DMemModelThread*)TheCurrentThread)->RemoveAlias();
+ // make sure start address is in alias region...
+ if(aAddr<KIPCAlias)
+ aAddr = KIPCAlias;
+ // then cause fault now...
+ MM::UserPermissionFault(aAddr,aWrite);
+ }
+
+ if(end<(TLinAddr)KUserMemoryLimit)
+ return; // user memory is safe
+
+ // Compare the current thread's process os asid to kernel asid, no need to
+ // open a reference on the os asid as it is the current thread.
+ if(((DMemModelProcess*)TheCurrentThread->iOwningProcess)->OsAsid()==(TInt)KKernelOsAsid)
+ return; // kernel can access everything
+
+ // make sure address is in supervisor only region...
+ if(aAddr<KUserMemoryLimit)
+ aAddr = KUserMemoryLimit;
+ // then cause fault now...
+ MM::UserPermissionFault(aAddr,aWrite);
+ }
+
+
+void MM::UserPermissionFault(TLinAddr aAddr, TBool aWrite)
+ {
+ // Access aAddr with user permissions to generate an exception...
+ if(aWrite)
+ UserWriteFault(aAddr);
+ else
+ UserReadFault(aAddr);
+ __NK_ASSERT_ALWAYS(0); // shouldn't get here
+ }
+
+
+#ifndef __SMP__
+void MM::IpcAliasPde(TPde*& aPdePtr, TUint aOsAsid)
+ {
+ aPdePtr = &Mmu::PageDirectory(aOsAsid)[KIPCAlias>>KChunkShift];
+ }
+#endif
+
+
+TMappingPermissions MM::MappingPermissions(TBool aUser, TBool aWrite, TBool aExecute)
+ {
+ TUint perm = 0;
+ if(aUser)
+ perm |= EUser;
+ if(aWrite)
+ perm |= EReadWrite;
+ if(aExecute)
+ perm |= EExecute;
+ return (TMappingPermissions)perm;
+ }
+
+
+TInt MM::MappingPermissions(TMappingPermissions& aPermissions, TMappingAttributes2 aLegacyAttributes)
+ {
+ TUint attr2 = *(TUint32*)&aLegacyAttributes;
+
+ TUint read = attr2&EMapAttrReadMask;
+ TUint write = (attr2&EMapAttrWriteMask)>>4;
+ TUint execute = (attr2&EMapAttrExecMask)>>8;
+
+ read |= execute; // execute access requires read access
+
+ if(write==0) // no write required
+ {
+ if((read&5)==0)
+ return KErrNotSupported; // neither supervisor nor user read specified
+ }
+ else if(write<4) // supervisor write required
+ {
+ if(read>=4)
+ return KErrNotSupported; // user read requested (but no user write)
+ }
+
+ read |= write; // write access implies read access
+
+ TUint user = read&4;
+ aPermissions = MappingPermissions(user,write,execute);
+
+ return KErrNone;
+ }
+
+
+TInt MM::MemoryAttributes(TMemoryAttributes& aAttributes, TMappingAttributes2 aLegacyAttributes)
+ {
+ TUint attr = aLegacyAttributes.Type();
+ if (aLegacyAttributes.Shared())
+ attr |= EMemoryAttributeShareable;
+ if (aLegacyAttributes.Parity())
+ attr |= EMemoryAttributeUseECC;
+ aAttributes = Mmu::CanonicalMemoryAttributes((TMemoryAttributes)attr);
+ return KErrNone;
+ }
+
+
+TMappingAttributes2 MM::LegacyMappingAttributes(TMemoryAttributes aAttributes, TMappingPermissions aPermissions)
+ {
+ TUint attr = Mmu::CanonicalMemoryAttributes(aAttributes);
+ return TMappingAttributes2
+ (
+ (TMemoryType)(attr&EMemoryAttributeTypeMask),
+ aPermissions&EUser,
+ aPermissions&EReadWrite,
+ aPermissions&EExecute,
+ attr&EMemoryAttributeShareable,
+ attr&EMemoryAttributeUseECC
+ );
+ }
+
+