kernel/eka/memmodel/epoc/flexible/mmu/mlargemappings.cpp
changeset 0 a41df078684a
child 15 4122176ea935
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mlargemappings.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,367 @@
+// 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 "mlargemappings.h"
+#include <kernel/cache_maintenance.inl>
+
+
+//
+// DLargeMappedMemory
+//
+
+
+DLargeMappedMemory::DLargeMappedMemory(DMemoryManager* aManager, TUint aSizeInPages, TMemoryAttributes aAttributes, TMemoryCreateFlags aCreateFlags)
+	: DCoarseMemory(aManager, aSizeInPages, aAttributes, aCreateFlags)
+	{
+	}
+
+
+DLargeMappedMemory* DLargeMappedMemory::New(DMemoryManager* aManager, TUint aSizeInPages, TMemoryAttributes aAttributes, TMemoryCreateFlags aCreateFlags)
+	{
+	TRACE(("DLargeMappedMemory::New()"));
+	TUint chunkCount = (aSizeInPages + KPagesInPDE - 1) >> KPagesInPDEShift;
+	TUint wordCount = (chunkCount  + 31) >> 5;
+	TUint size = sizeof(DLargeMappedMemory) + sizeof(TUint) * (wordCount - 1);
+	DLargeMappedMemory* self = (DLargeMappedMemory*)Kern::AllocZ(size);
+	if(self)
+		{
+		new (self) DLargeMappedMemory(aManager, aSizeInPages, aAttributes, aCreateFlags);
+		if(self->Construct()!=KErrNone)
+			{
+			self->Close();
+			self = NULL;
+			}
+		}
+	TRACE(("DLargeMappedMemory::New() returns 0x%08x", self));
+	return self;
+	}
+
+
+DLargeMappedMemory::~DLargeMappedMemory()
+	{
+	TRACE2(("DLargeMappedMemory[0x%08x]::~DLargeMappedMemory()",this));
+	}
+
+
+DMemoryMapping* DLargeMappedMemory::CreateMapping(TUint aIndex, TUint aCount)
+	{
+	TRACE(("DLargeMappedMemory[0x%08x]::CreateMapping()",this));	
+	if (((aIndex|aCount)&(KChunkMask>>KPageShift))==0)
+		return new DLargeMapping();
+	else
+		return new DFineMapping();
+	}
+
+
+TInt DLargeMappedMemory::ClaimInitialPages(TLinAddr aBase,
+										   TUint aSize,
+										   TMappingPermissions aPermissions,
+										   TBool aAllowGaps,
+										   TBool aAllowNonRamPages)
+	{
+	TRACE(("DLargeMappedMemory[0x%08x]::ClaimInitialPages(0x%08x,0x%08x,0x%08x,%d,%d)",
+		   this,aBase,aSize,aPermissions,aAllowGaps,aAllowNonRamPages));
+	TInt r = DCoarseMemory::ClaimInitialPages(aBase,aSize,aPermissions,aAllowGaps,
+											  aAllowNonRamPages);
+	if (r != KErrNone)
+		return r;
+
+	// set initial contiguous state by checking which pages were section mapped by the bootstrap
+	MmuLock::Lock();
+	TPde* pPde = Mmu::PageDirectoryEntry(KKernelOsAsid,aBase);	
+	TUint endChunk = aSize >> KChunkShift;
+	for (TUint chunk = 0 ; chunk < endChunk ; ++chunk)
+		{	  
+		SetChunkContiguous(chunk, Mmu::PdeMapsSection(*pPde++));
+		TRACE(("  chunk %d contiguous state is %d", chunk, IsChunkContiguous(chunk)));
+		}
+	MmuLock::Unlock();
+	
+	return KErrNone;
+	}
+
+
+TInt DLargeMappedMemory::MapPages(RPageArray::TIter aPages)
+	{
+	TRACE2(("DLargeMappedMemory[0x%08x]::MapPages(?) index=0x%x count=0x%x",this,aPages.Index(),aPages.Count()));
+
+	// for now: assert pages do not overlapped a contiguous area
+	// todo: update contiguous state, update page tables and call MapPages on large mappings
+#ifdef _DEBUG
+	for (TUint index = aPages.Index() ; index < aPages.IndexEnd() ; index += KPagesInPDE)
+		{
+		MmuLock::Lock();
+		__NK_ASSERT_DEBUG(!IsChunkContiguous(index >> KPagesInPDEShift));
+		MmuLock::Unlock();
+		}
+#endif
+
+	// map pages in all page tables and fine mappings
+	return DCoarseMemory::MapPages(aPages);
+	}
+
+
+void DLargeMappedMemory::RemapPage(TPhysAddr& aPageArray, TUint aIndex, TBool aInvalidateTLB)
+	{
+	TRACE2(("DLargeMappedMemory[0x%08x]::RemapPage() index=0x%x",this, aIndex));
+
+	// update contiguous state...
+	// todo: for now we will assume that remapping a page makes it non-contiguous
+	MmuLock::Lock();
+	SetChunkContiguous(aIndex >> KPagesInPDEShift, EFalse);
+	MmuLock::Unlock();
+
+	// remap pages in all page tables and call RemapPage on large mappings...
+	MmuLock::Lock();
+	TUint pteType = 0;
+	do
+		{
+		DPageTables* tables = iPageTables[pteType];
+		if(tables)
+			{
+			tables->Open();
+			MmuLock::Unlock();
+			tables->RemapPage(aPageArray, aIndex, aInvalidateTLB);
+			tables->iMappings.RemapPage(aPageArray, aIndex, aInvalidateTLB);
+			tables->AsyncClose();
+			MmuLock::Lock();
+			}
+		}
+	while(++pteType<ENumPteTypes);
+	MmuLock::Unlock();
+
+	// remap page in all fine mappings...
+	DMemoryObject::RemapPage(aPageArray, aIndex, aInvalidateTLB);
+	}
+
+
+void DLargeMappedMemory::UnmapPages(RPageArray::TIter aPages, TBool aDecommitting)
+	{
+	TRACE2(("DLargeMappedMemory[0x%08x]::UnmapPages(?,%d) index=0x%x count=0x%x",this,(bool)aDecommitting,aPages.Index(),aPages.Count()));
+
+	// for now: assert pages do not overlapped a contiguous area
+	// todo: update contiguous state, update page tables and call UnmapPages on large mappings
+#ifdef _DEBUG
+	for (TUint index = aPages.Index() ; index < aPages.IndexEnd() ; index += KPagesInPDE)
+		{
+		MmuLock::Lock();
+		__NK_ASSERT_DEBUG(!IsChunkContiguous(index >> KPagesInPDEShift));
+		MmuLock::Unlock();
+		}
+#endif
+
+	// unmap pages in all page tables and fine mappings
+	DCoarseMemory::UnmapPages(aPages, aDecommitting);
+	}
+
+
+void DLargeMappedMemory::RestrictPages(RPageArray::TIter aPages, TRestrictPagesType aRestriction)
+	{
+	TRACE2(("DLargeMappedMemory[0x%08x]::RestrictPages(?,%d) index=0x%x count=0x%x",this,aRestriction,aPages.Index(),aPages.Count()));
+
+	// assert pages do not overlapped a contiguous area...
+#ifdef _DEBUG
+	for (TUint index = aPages.Index() ; index < aPages.IndexEnd() ; index += KPagesInPDE)
+		{
+		MmuLock::Lock();
+		__NK_ASSERT_DEBUG(!IsChunkContiguous(index >> KPagesInPDEShift));
+		MmuLock::Unlock();
+		}
+#endif
+
+	DCoarseMemory::RestrictPages(aPages, aRestriction);
+	}
+
+
+TBool DLargeMappedMemory::IsChunkContiguous(TInt aChunkIndex)
+	{
+	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
+	TUint index = aChunkIndex >> 5;
+	TUint mask = 1 << (aChunkIndex & 31);
+	return (iContiguousState[index] & mask) != 0;
+	}
+
+
+void DLargeMappedMemory::SetChunkContiguous(TInt aChunkIndex, TBool aIsContiguous)
+	{
+	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
+	TUint index = aChunkIndex >> 5;
+	TUint mask = 1 << (aChunkIndex & 31);
+	iContiguousState[index] = (iContiguousState[index] & ~mask) | (aIsContiguous ? mask : 0);
+	}
+
+
+//
+// DLargeMapping
+//
+
+
+DLargeMapping::DLargeMapping() : DCoarseMapping(ELargeMapping)
+	{
+	}
+
+
+TInt DLargeMapping::DoMap()
+	{
+	TRACE(("DLargeMapping[0x%08x]::DoMap()", this));
+	__NK_ASSERT_DEBUG(((iStartIndex|iSizeInPages)&(KChunkMask>>KPageShift))==0); // be extra paranoid about alignment
+
+	MmuLock::Lock();
+
+	TPde* pPde = Mmu::PageDirectoryEntry(OsAsid(),Base());
+	DLargeMappedMemory* memory = (DLargeMappedMemory*)Memory(ETrue); // safe because we're called from code which has added mapping to memory
+	
+	TUint flash = 0;
+	TUint chunk = iStartIndex >> KPagesInPDEShift;
+	TUint endChunk = (iStartIndex + iSizeInPages) >> KPagesInPDEShift;
+	
+	while(chunk < endChunk)
+		{
+		MmuLock::Flash(flash,KMaxPdesInOneGo*2);
+		TPde pde = KPdeUnallocatedEntry;
+		TPte* pt = memory->GetPageTable(PteType(), chunk);
+		if (memory->IsChunkContiguous(chunk))
+			pde = Mmu::PageToSectionEntry(pt[0],iBlankPde); // todo: use get phys addr?
+		else if (pt)
+			pde = Mmu::PageTablePhysAddr(pt)|iBlankPde;
+
+		if (pde == KPdeUnallocatedEntry)
+			{
+			TRACE2(("!PDE %x=%x (was %x)",pPde,KPdeUnallocatedEntry,*pPde));
+			__NK_ASSERT_DEBUG(*pPde==KPdeUnallocatedEntry);
+			}
+		else
+			{
+			TRACE2(("!PDE %x=%x (was %x)",pPde,pde,*pPde));
+			__NK_ASSERT_DEBUG(*pPde==KPdeUnallocatedEntry || ((*pPde^pde)&~KPdeMatchMask)==0);
+			*pPde = pde;
+			SinglePdeUpdated(pPde);
+			flash += 3; // increase flash rate because we've done quite a bit more work
+			}
+
+		++pPde;
+		++chunk;
+		}
+	MmuLock::Unlock();
+
+	return KErrNone;
+	}
+
+
+void DLargeMapping::RemapPage(TPhysAddr& aPageArray, TUint aIndex, TUint aMapInstanceCount, TBool aInvalidateTLB)
+	{
+	TRACE(("DLargeMapping[0x%08x]::RemapPage(%08x, %d, %d, %d)", this, aPageArray, aIndex, aMapInstanceCount, aInvalidateTLB));
+
+	TInt chunkIndex = aIndex >> KPagesInPDEShift;
+
+	MmuLock::Lock();
+	DLargeMappedMemory* memory = (DLargeMappedMemory*)Memory(); // safe because we're called from code which has reference on tables, which has reference on memory
+	TPte* pt = memory->GetPageTable(PteType(), chunkIndex);
+	
+	// check the page is still mapped and mapping isn't being detached 
+	// or hasn't been reused for another purpose...
+	if(!pt || BeingDetached() || aMapInstanceCount != MapInstanceCount())
+		{
+		// can't map pages to this mapping any more so just exit.
+		TRACE(("  page no longer mapped"));
+		MmuLock::Unlock();
+		return;
+		}
+	
+	TPde* pPde = Mmu::PageDirectoryEntry(OsAsid(),Base() + (chunkIndex << KChunkShift));
+	TPde currentPde = *pPde;
+	
+	if (!memory->IsChunkContiguous(chunkIndex) && Mmu::PdeMapsSection(currentPde))
+		{
+		// break section mapping and replace with page table...
+		TRACE2(("  breaking section mapping"));
+		TPde pde = Mmu::PageTablePhysAddr(pt)|iBlankPde;
+		TRACE2(("!PDE %x=%x (was %x)",pPde,pde,*pPde));
+		// can't assert old value if the first page has been remapped
+		__NK_ASSERT_DEBUG((aIndex & (KPagesInPDE - 1)) == 0 ||
+						  *pPde == Mmu::PageToSectionEntry(pt[0],iBlankPde));
+		*pPde = pde;
+		SinglePdeUpdated(pPde);
+		MmuLock::Unlock();
+#ifndef COARSE_GRAINED_TLB_MAINTENANCE
+		if (aInvalidateTLB) 
+			{
+			// invalidate chunk...
+			TUint start = (chunkIndex << KPagesInPDEShift) - iStartIndex;
+			TLinAddr addr = LinAddrAndOsAsid() + (start << KPageShift);
+			TLinAddr endAddr = addr + KChunkSize;
+			do InvalidateTLBForPage(addr);
+			while((addr+=KPageSize)<endAddr);
+			InvalidateTLBForPage(addr);
+			}
+#endif
+		}
+	else if (memory->IsChunkContiguous(chunkIndex) && Mmu::PdeMapsPageTable(currentPde))
+		{
+		// reform section mapping...
+		TRACE2(("  reforming section mapping"));
+		__NK_ASSERT_ALWAYS(0); // todo: not yet implemented
+		}
+	else
+		{
+		// remap already handled by page table update in DPageTables...
+		MmuLock::Unlock();
+#ifndef COARSE_GRAINED_TLB_MAINTENANCE
+		if (aInvalidateTLB)
+			{
+			// invalidate page...
+			TUint start = aIndex - iStartIndex;
+			TLinAddr addr = LinAddrAndOsAsid() + (start << KPageShift);
+			InvalidateTLBForPage(addr);
+			}
+#endif
+		}
+	
+	}
+
+
+TInt DLargeMapping::PageIn(RPageArray::TIter aPages, TPinArgs& aPinArgs, TUint aMapInstanceCount)
+	{
+	TRACE(("DLargeMapping[0x%08x]::PageIn(%d, %d, ?, %d)", this, aPages.Index(), aPages.Count(), aMapInstanceCount));
+#ifdef _DEBUG
+	// assert that we're not trying to page in any section mapped pages
+	TUint startIndex = aPages.Index();
+	TUint endIndex = startIndex + aPages.Count();
+	for (TUint index = startIndex ; index < endIndex ; index += KPagesInPDE)
+		{
+		TLinAddr addr = Base() + ((index - iStartIndex) << KPageShift);
+		TRACE2(("  checking page %d at %08x", index, addr));
+		TPde* pPde = Mmu::PageDirectoryEntry(OsAsid(),addr);
+		__NK_ASSERT_DEBUG(!Mmu::PdeMapsSection(*pPde));
+		}
+#endif	
+	return DCoarseMapping::PageIn(aPages, aPinArgs, aMapInstanceCount);
+	}
+
+
+TBool DLargeMapping::MovingPageIn(TPhysAddr& aPageArrayPtr, TUint aIndex)
+	{
+	// this shouldn't ever be called as it's only used by ram defrag
+	__NK_ASSERT_DEBUG(EFalse);
+	return EFalse;
+	}
+
+
+TPte* DLargeMapping::FindPageTable(TLinAddr aLinAddr, TUint aMemoryIndex)
+	{
+	// this shouldn't ever be called as it's only used by ram defrag
+	__NK_ASSERT_DEBUG(EFalse);
+	return NULL;
+	}