// 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