--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/maddressspace.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,389 @@
+// 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 <plat_priv.h>
+#include "mm.h"
+#include "mmu.h"
+
+#include "maddressspace.h"
+#include "mpdalloc.h"
+#include "mmapping.h"
+
+
+
+/**
+Allocator for OS Address Space IDs (OS ASIDs).
+This is a simple bitmap allocator for KNumOsAsids integers with an
+associated mutex to guard against concurrency when allocating and
+freeing.
+*/
+class OsAsidAllocator
+ {
+public:
+ void Init2()
+ {
+ iAllocator = TBitMapAllocator::New(KNumOsAsids,ETrue);
+ __NK_ASSERT_ALWAYS(iAllocator);
+ iAllocator->Alloc(KKernelOsAsid,1); // make kernel OS ASID already allocated
+ }
+
+ TInt Alloc()
+ {
+ NKern::FMWait(&iLock);
+ TInt osAsid = iAllocator->Alloc();
+ NKern::FMSignal(&iLock);
+ if(osAsid<0)
+ return KErrNoMemory;
+ return osAsid;
+ }
+
+ void Free(TInt aOsAsid)
+ {
+ NKern::FMWait(&iLock);
+ iAllocator->Free(aOsAsid);
+ NKern::FMSignal(&iLock);
+ }
+
+private:
+ TBitMapAllocator* iAllocator;
+ NFastMutex iLock;
+ }
+OsAsidAllocator;
+
+
+//
+// DAddressSpace
+//
+
+DAddressSpace KernelAddressSpace; ///< The kernel's address space object.
+
+__ASSERT_COMPILE(KKernelOsAsid==0);
+DAddressSpace* AddressSpace[KNumOsAsids] = { &KernelAddressSpace };
+
+RVirtualAllocator DAddressSpace::UserGlobalVirtualAllocator;
+RBackwardsVirtualAllocator DAddressSpace::UserCommonVirtualAllocator;
+
+/**
+The read lock used for protecting the mappings container in address spaces (DAddressSpace::iMappings).
+A single global lock is used for all processes - this isn't required but it is the simplest
+implementation if we want to avoid the memory overhead of allocating a mutex per address space.
+*/
+NFastMutex TheAddressSpaceMappingLock;
+
+
+/**
+A pool of mutexes which are used to protect an address space's virtual address allocation
+and acts as a write lock for the mappings container (DAddressSpace::iMappings).
+*/
+DMutexPool AddressSpaceMutexPool;
+
+
+void DAddressSpace::Init2()
+ {
+ // create allocator for ASIDs...
+ OsAsidAllocator.Init2();
+
+ // construct the kernel's address space...
+ TInt r = KernelAddressSpace.Construct(0, KKernelSectionBase, KKernelSectionEnd);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ // mark primary i/o region as already allocated...
+ __ASSERT_COMPILE(((KPrimaryIOBase|KPrimaryIOEnd)&KChunkMask)==0); // region must be chunk aligned to avoid PDE type conflicts with any new allocations
+ TLinAddr addr;
+ TUint size;
+ r = KernelAddressSpace.AllocateVirtualMemory(addr,size,KPrimaryIOBase,KPrimaryIOEnd-KPrimaryIOBase,0);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ // construct user global memory allocator...
+ r = UserGlobalVirtualAllocator.Construct(KGlobalMemoryBase,KUserMemoryLimit,ENumVirtualAllocTypes,AddressSpace[KKernelOsAsid]->iLock);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ // construct user common memory allocator (two slab types, one each for paged and unpaged memory)...
+ r = UserCommonVirtualAllocator.Construct(KUserLocalDataBase,KUserLocalDataEnd,ENumVirtualAllocTypes,AddressSpace[KKernelOsAsid]->iLock);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+
+ // reserve virtual memory for XIP user code...
+ TUint romDataSize = TheRomHeader().iTotalUserDataSize;
+ TLinAddr romDataBase = TheRomHeader().iUserDataAddress-romDataSize;
+ __NK_ASSERT_DEBUG(TheRomHeader().iUserDataAddress==KUserLocalDataEnd);
+ if(romDataSize)
+ {
+ r = UserCommonVirtualAllocator.Alloc(addr,size,romDataBase,romDataSize,0);
+ __NK_ASSERT_ALWAYS(r==KErrNone);
+ }
+ }
+
+
+DAddressSpace::DAddressSpace()
+ : iMappings(&TheAddressSpaceMappingLock,iLock)
+ {
+ }
+
+
+TInt DAddressSpace::New(TPhysAddr& aPageDirectory)
+ {
+ TRACE(("DAddressSpace::New(?)"));
+ TInt r;
+ TInt osAsid = OsAsidAllocator.Alloc();
+ if(osAsid<0)
+ r = KErrNoMemory;
+ else
+ {
+ r = PageDirectories.Alloc(osAsid,aPageDirectory);
+ if(r!=KErrNone)
+ OsAsidAllocator.Free(osAsid);
+ else
+ {
+ DAddressSpace*& info = AddressSpace[osAsid];
+ __NK_ASSERT_DEBUG(!info);
+ info = new DAddressSpace();
+ if(!info)
+ {
+ PageDirectories.Free(osAsid);
+ OsAsidAllocator.Free(osAsid);
+ r = KErrNoMemory;
+ }
+ else
+ {
+ r = info->Construct(osAsid,KUserLocalDataBase,KUserLocalDataEnd);
+ if(r!=KErrNone)
+ {
+ info->Close();
+ info = 0;
+ }
+ }
+ }
+ }
+
+ if(r==KErrNone)
+ r = osAsid;
+ else
+ aPageDirectory = KPhysAddrInvalid;
+
+ TRACE(("DAddressSpace::New returns %d",r));
+ return r;
+ }
+
+
+
+DAddressSpace::~DAddressSpace()
+ {
+ TRACE(("DAddressSpace[0x%08x]::~DAddressSpace() osAsid = %d",this,iOsAsid));
+#ifdef _DEBUG
+ if(iMappings.Count())
+ Dump();
+#endif
+ __NK_ASSERT_DEBUG(iMappings.Count()==0);
+
+ TInt osAsid = iOsAsid;
+ AddressSpace[osAsid] = 0;
+ PageDirectories.Free(osAsid);
+ InvalidateTLBForAsid(osAsid);
+ OsAsidAllocator.Free(osAsid);
+ }
+
+
+TInt DAddressSpace::Construct(TInt aOsAsid, TLinAddr aStart, TLinAddr aEnd)
+ {
+ TRACE(("DAddressSpace::Construct(%d,0x%08x,0x%08x)",aOsAsid,aStart,aEnd));
+ iOsAsid = aOsAsid;
+ return iVirtualAllocator.Construct(aStart,aEnd,ENumVirtualAllocTypes,iLock);
+ }
+
+
+void DAddressSpace::Lock()
+ {
+ AddressSpaceMutexPool.Wait(iLock);
+ }
+
+
+void DAddressSpace::Unlock()
+ {
+ AddressSpaceMutexPool.Signal(iLock);
+ }
+
+
+TInt DAddressSpace::AllocateVirtualMemory(TLinAddr& aAddr, TUint& aSize, TLinAddr aRequestedAddr, TUint aRequestedSize, TUint aPdeType)
+ {
+ TRACE(("DAddressSpace::AllocateVirtualMemory(?,?,0x%08x,0x%08x,%d) osAsid=%d",aRequestedAddr,aRequestedSize,aPdeType,iOsAsid));
+ __NK_ASSERT_DEBUG(aPdeType<ENumVirtualAllocTypes);
+ Lock();
+ TInt r = iVirtualAllocator.Alloc(aAddr,aSize,aRequestedAddr,aRequestedSize,aPdeType);
+ if(r==KErrNone)
+ Open();
+ Unlock();
+ TRACE(("DAddressSpace::AllocateVirtualMemory returns %d region=0x%08x+0x%08x",r,aAddr,aSize));
+ return r;
+ }
+
+
+TInt DAddressSpace::AllocateUserGlobalVirtualMemory(TLinAddr& aAddr, TUint& aSize, TLinAddr aRequestedAddr, TUint aRequestedSize, TUint aPdeType)
+ {
+ TRACE(("DAddressSpace::AllocateUserGlobalVirtualMemory(?,?,0x%08x,0x%08x,%d)",aRequestedAddr,aRequestedSize,aPdeType));
+ __NK_ASSERT_DEBUG(aPdeType<ENumVirtualAllocTypes);
+ KernelAddressSpace.Lock();
+ TInt r = UserGlobalVirtualAllocator.Alloc(aAddr,aSize,aRequestedAddr,aRequestedSize,aPdeType);
+ KernelAddressSpace.Unlock();
+ TRACE(("DAddressSpace::AllocateUserGlobalVirtualMemory returns %d region=0x%08x+0x%08x",r,aAddr,aSize));
+ return r;
+ }
+
+
+void DAddressSpace::FreeVirtualMemory(TLinAddr aAddr, TUint aSize)
+ {
+ TRACE(("DAddressSpace::FreeVirtualMemory(0x%08x,0x%08x) osAsid=%d",aAddr, aSize, iOsAsid));
+ Lock();
+ if(iOsAsid==(TInt)KKernelOsAsid && UserGlobalVirtualAllocator.InRange(aAddr,aSize))
+ UserGlobalVirtualAllocator.Free(aAddr,aSize);
+ else
+ {
+ iVirtualAllocator.Free(aAddr,aSize);
+ AsyncClose();
+ }
+ Unlock();
+ }
+
+
+TInt DAddressSpace::AllocateUserCommonVirtualMemory(TLinAddr& aAddr, TUint& aSize, TLinAddr aRequestedAddr, TUint aRequestedSize, TUint aPdeType)
+ {
+ TRACE(("DAddressSpace::AllocateUserCommonVirtualMemory(?,?,0x%08x,0x%08x,%d)",aRequestedAddr,aRequestedSize,aPdeType));
+ __NK_ASSERT_DEBUG(aPdeType<ENumVirtualAllocTypes);
+ KernelAddressSpace.Lock();
+ TInt r = UserCommonVirtualAllocator.Alloc(aAddr,aSize,aRequestedAddr,aRequestedSize,aPdeType);
+ KernelAddressSpace.Unlock();
+ TRACE(("DAddressSpace::AllocateUserCommonVirtualMemory returns %d region=0x%08x+0x%08x",r,aAddr,aSize));
+ return r;
+ }
+
+
+void DAddressSpace::FreeUserCommonVirtualMemory(TLinAddr aAddr, TUint aSize)
+ {
+ TRACE(("DAddressSpace::FreeUserCommonVirtualMemory(0x%08x,0x%08x)",aAddr,aSize));
+ KernelAddressSpace.Lock();
+ UserCommonVirtualAllocator.Free(aAddr,aSize);
+ KernelAddressSpace.Unlock();
+ }
+
+
+TInt DAddressSpace::AddMapping(TLinAddr aAddr, DMemoryMapping* aMapping)
+ {
+ Lock();
+ TRACE(("DAddressSpace::AddMapping(0x%08x,0x%08x) osAsid=%d",aAddr, aMapping, iOsAsid));
+ TInt r = iMappings.Add(aAddr,aMapping);
+ TRACE(("DAddressSpace::AddMapping osAsid=%d returns %d",iOsAsid, r));
+ Unlock();
+ return r;
+ }
+
+
+DMemoryMapping* DAddressSpace::RemoveMapping(TLinAddr aAddr)
+ {
+ Lock();
+ DMemoryMapping* removed = (DMemoryMapping*)iMappings.Remove(aAddr);
+ TRACE(("DAddressSpace::RemoveMapping(0x%08x) osAsid=%d returns 0x%08x",aAddr, iOsAsid, removed));
+ Unlock();
+ return removed;
+ }
+
+
+DMemoryMapping* DAddressSpace::GetMapping(TLinAddr aAddr)
+ {
+ iMappings.ReadLock();
+ DMemoryMapping* mapping = (DMemoryMapping*)iMappings.Find(aAddr);
+ TRACE(("DAddressSpace::GetMapping(0x%08x) osAsid=%d returns 0x%08x",aAddr, iOsAsid, mapping));
+ __NK_ASSERT_DEBUG(mapping); // caller must know there is a mapping
+ iMappings.ReadUnlock();
+ return mapping;
+ }
+
+
+DMemoryMapping* DAddressSpace::FindMapping(TLinAddr aAddr, TUint aSize, TUint& aOffsetInMapping, TUint& aInstanceCount)
+ {
+ __ASSERT_CRITICAL;
+
+ DMemoryMapping* result = NULL;
+
+ // find mapping...
+ iMappings.ReadLock();
+ TUint dummy;
+ DMemoryMapping* mapping = (DMemoryMapping*)iMappings.Find(aAddr,dummy);
+ if(mapping && mapping->IsAttached())
+ {
+ // found mapping, check addresses are in range...
+ TUint offset = aAddr-mapping->Base();
+ TUint end = offset+aSize;
+ if(offset<end && end<=mapping->iSizeInPages<<KPageShift)
+ {
+ // addresses OK, get a reference on the mapping before releasing list lock...
+ aOffsetInMapping = offset;
+ aInstanceCount = mapping->MapInstanceCount();
+ mapping->Open(); // can't fail because mapping IsAttached
+ result = mapping;
+ }
+ }
+ iMappings.ReadUnlock();
+
+ return result;
+ }
+
+
+TBool DAddressSpace::CheckPdeType(TLinAddr aAddr, TUint aSize, TUint aPdeType)
+ {
+ TRACE(("DAddressSpace::CheckPdeType(0x%08x,0x%08x,%d) osAsid=%d",aAddr, aSize, aPdeType, iOsAsid));
+ TBool r;
+ Lock();
+ if(iOsAsid==(TInt)KKernelOsAsid && UserGlobalVirtualAllocator.InRange(aAddr,aSize))
+ r = UserGlobalVirtualAllocator.CheckSlabType(aAddr,aSize,aPdeType);
+ else
+ r = iVirtualAllocator.CheckSlabType(aAddr,aSize,aPdeType);
+ TRACE(("DAddressSpace::CheckPdeType returns %d",r));
+ Unlock();
+ return r;
+ }
+
+
+
+//
+// Debug
+//
+
+#ifdef _DEBUG
+
+void DAddressSpace::Dump()
+ {
+ Kern::Printf("DAddressSpace[0x%08x]::Dump() osAsid = %d",this,iOsAsid);
+ TLinAddr virt = 0;
+ do
+ {
+ --virt;
+ iMappings.ReadLock();
+ TUint offsetInMapping = 0;
+ DMemoryMapping* mapping = (DMemoryMapping*)iMappings.Find(virt,offsetInMapping);
+ if(mapping)
+ {
+ if(!mapping->TryOpen())
+ mapping = NULL;
+ virt -= offsetInMapping;
+ }
+ iMappings.ReadUnlock();
+ if(!mapping)
+ break;
+ mapping->Dump();
+ mapping->Close();
+ }
+ while(virt);
+ }
+
+#endif // _DEBUG