kernel/eka/memmodel/epoc/flexible/mmu/mpdalloc.cpp
changeset 0 a41df078684a
child 87 2f92ad2dc5db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpdalloc.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,211 @@
+// 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 "mpdalloc.h"
+#include "mobject.h"
+#include "cache_maintenance.inl"
+
+
+// check enough space for page directories...
+__ASSERT_COMPILE(KNumOsAsids <= (KPageDirectoryEnd-KPageDirectoryBase)/KPageDirectorySize);
+
+
+PageDirectoryAllocator PageDirectories;
+
+
+const TUint KLocalPdShift = KPageDirectoryShift > KPageShift ? KPageDirectoryShift-1 : KPageShift;
+const TUint KLocalPdSize = 1<<KLocalPdShift;
+const TUint KLocalPdPages = 1<<(KLocalPdShift-KPageShift);
+
+
+__ASSERT_COMPILE((KPageDirectoryBase&(31*KPageDirectorySize))==0); // following code assumes this alignment
+
+void PageDirectoryAllocator::GlobalPdeChanged(TPde* aPde)
+	{
+	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
+	__NK_ASSERT_DEBUG(TLinAddr(aPde)>=KPageDirectoryBase);
+	__NK_ASSERT_DEBUG(TLinAddr(aPde)<KPageDirectoryEnd);
+	__NK_ASSERT_DEBUG(KLocalPdSize==(TUint)KPageDirectorySize); // shouldn't be called if we have separate global PDs
+
+	TLinAddr addr = (TLinAddr(aPde)&KPageDirectoryMask)*(KChunkSize/sizeof(TPde));
+	if(addr<KGlobalMemoryBase)
+		return; // change was in local part of PD, so nothing to do
+	if(addr-KIPCAlias<KIPCAliasAreaSize)
+		return; // change was in IPC alias area, so nothing to do
+	if(!iAllocator)
+		return; // not yet initialised
+
+	TRACE2(("PageDirectoryAllocator::GlobalPdeChanged(0x%08x)",aPde));
+	TPde pde = *aPde;
+	TLinAddr pPde = KPageDirectoryBase+(TLinAddr(aPde)&KPageDirectoryMask); // first page directory
+
+	// copy PDE to all allocated page directories
+	pPde -= KPageDirectorySize; // start off at PD minus one
+	TLinAddr lastPd = KPageDirectoryBase+(KNumOsAsids-1)*KPageDirectorySize;
+	TUint32* ptr = iAllocator->iMap;
+	do
+		{
+		TUint32 bits = ~*ptr++;
+		do
+			{
+			pPde += KPageDirectorySize; // step to next page directory
+			if(bits&0x80000000u)
+				{
+				TRACE2(("!PDE %x=%x",pPde,pde));
+				*(TPde*)pPde = pde;
+				CacheMaintenance::SinglePdeUpdated(pPde);
+				}
+			}
+		while(bits<<=1);
+		pPde |= 31*KPageDirectorySize; // step to next group of 32 PDs
+		}
+	while(pPde<lastPd);
+	}
+
+
+void PageDirectoryAllocator::Init2()
+	{
+	TRACEB(("PageDirectoryAllocator::Init2()"));
+
+	// construct memory object for page directories...
+#if defined(__CPU_PAGE_TABLES_FULLY_CACHED)
+	TMemoryAttributes memAttr = EMemoryAttributeStandard;
+#else
+	TMemoryAttributes memAttr = (TMemoryAttributes)(EMemoryAttributeNormalUncached|EMemoryAttributeDefaultShareable);
+#endif
+	TInt r = MM::InitFixedKernelMemory(iPageDirectoryMemory, KPageDirectoryBase, KPageDirectoryEnd, KPageDirectorySize, EMemoryObjectHardware, EMemoryCreateNoWipe, memAttr, EMappingCreateFixedVirtual);
+	__NK_ASSERT_ALWAYS(r==KErrNone);
+
+	// initialise kernel page directory...
+	TPhysAddr kernelPd = Mmu::LinearToPhysical((TLinAddr)Mmu::PageDirectory(KKernelOsAsid));
+	iKernelPageDirectory = kernelPd;
+	((DMemModelProcess*)K::TheKernelProcess)->iPageDir = kernelPd;
+	AssignPages(KKernelOsAsid*(KPageDirectorySize>>KPageShift),KPageDirectorySize>>KPageShift,kernelPd);
+
+	// construct allocator...
+	iAllocator = TBitMapAllocator::New(KNumOsAsids,ETrue);
+	__NK_ASSERT_ALWAYS(iAllocator);
+	iAllocator->Alloc(KKernelOsAsid,1); // kernel page directory already allocated
+
+	TRACEB(("PageDirectoryAllocator::Init2 done"));
+	}
+
+
+void PageDirectoryAllocator::AssignPages(TUint aIndex, TUint aCount, TPhysAddr aPhysAddr)
+	{
+	__NK_ASSERT_DEBUG(aCount<=KMaxPageInfoUpdatesInOneGo);
+	MmuLock::Lock();
+	SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr);
+	SPageInfo* piEnd = pi+aCount;
+	while(pi<piEnd)
+		{
+		pi->SetPhysAlloc(iPageDirectoryMemory,aIndex);
+		++pi;
+		++aIndex;
+		}
+	MmuLock::Unlock();
+	}
+
+
+TInt PageDirectoryAllocator::Alloc(TUint aOsAsid, TPhysAddr& aPageDirectory)
+	{
+	TRACE(("PageDirectoryAllocator::Alloc(%d)",aOsAsid));
+
+	// get memory for local page directory...
+	Mmu& m = TheMmu;
+	TUint offset = aOsAsid*KPageDirectorySize;
+	TPhysAddr pdPhys;
+	RamAllocLock::Lock();
+	TInt r = m.AllocContiguousRam(pdPhys, KLocalPdPages, KLocalPdShift-KPageShift, iPageDirectoryMemory->RamAllocFlags());
+	if(r==KErrNone)
+		AssignPages(offset>>KPageShift,KLocalPdPages,pdPhys);
+	RamAllocLock::Unlock();
+
+	if(r==KErrNone)
+		{
+		TRACE(("PageDirectoryAllocator::Alloc pdPhys = 0x%08x",pdPhys));
+
+		// map local page directory...
+		r = MM::MemoryAddContiguous(iPageDirectoryMemory,MM::BytesToPages(offset),KLocalPdPages,pdPhys);
+		if(r!=KErrNone)
+			{
+			RamAllocLock::Lock();
+			m.FreeContiguousRam(pdPhys,KLocalPdPages);
+			RamAllocLock::Unlock();
+			}
+		else
+			{
+			aPageDirectory = pdPhys;
+
+			TPde* pd = Mmu::PageDirectory(aOsAsid);
+			const TUint globalOffset = (KGlobalMemoryBase>>KChunkShift)*sizeof(TPde); // start of global part
+
+			// clear local entries in page directory...
+			memclr(pd,globalOffset);
+			CacheMaintenance::PdesInitialised((TLinAddr)pd,globalOffset);
+
+			if(KLocalPdSize<(TUint)KPageDirectorySize)
+				{
+				// map global page directory after local part...
+				__NK_ASSERT_DEBUG(KLocalPdSize==globalOffset);
+				r = MM::MemoryAddContiguous(iPageDirectoryMemory, MM::BytesToPages(offset+KLocalPdSize), 
+						(KPageDirectorySize-KLocalPdSize)/KPageSize, iKernelPageDirectory+KLocalPdSize);
+				__NK_ASSERT_DEBUG(r==KErrNone); // can't fail
+				MmuLock::Lock(); // need lock because allocator not otherwise atomic
+				iAllocator->Alloc(aOsAsid,1);
+				MmuLock::Unlock();
+				}
+			else
+				{
+				// copy global entries to local page directory...
+				TPde* globalPd = Mmu::PageDirectory(KKernelOsAsid);
+				MmuLock::Lock(); // need lock because allocator not otherwise atomic, also  to make sure GlobalPdeChanged() only accesses extant PDs
+				memcpy((TUint8*)pd+globalOffset,(TUint8*)globalPd+globalOffset,KPageDirectorySize-globalOffset);
+				iAllocator->Alloc(aOsAsid,1);
+				MmuLock::Unlock();
+				CacheMaintenance::PdesInitialised((TLinAddr)((TUint8*)pd+globalOffset),KPageDirectorySize-globalOffset);
+				}
+			}
+		}
+	TRACE(("PageDirectoryAllocator::Alloc returns %d",r));
+	return r;
+	}
+
+
+void PageDirectoryAllocator::Free(TUint aOsAsid)
+	{
+	TRACE(("PageDirectoryAllocator::Free(%d)",aOsAsid));
+
+	MmuLock::Lock(); // need lock because allocator not otherwise atomic, also to make sure GlobalPdeChanged() only accesses extant PDs
+	iAllocator->Free(aOsAsid, 1);
+	MmuLock::Unlock();
+
+	const TUint KPageDirectoryPageCount = KPageDirectorySize>>KPageShift;
+	TPhysAddr pages[KPageDirectoryPageCount];
+	TUint n = MM::MemoryRemovePages(iPageDirectoryMemory,aOsAsid*KPageDirectoryPageCount,KPageDirectoryPageCount,pages);
+	(void)n;
+	__NK_ASSERT_DEBUG(n==KPageDirectoryPageCount);
+
+	RamAllocLock::Lock();
+	Mmu& m = TheMmu;
+	// Page directories are fixed.
+	m.FreeRam(pages, KLocalPdPages, EPageFixed);
+	RamAllocLock::Unlock();
+	}
+