diff -r 000000000000 -r a41df078684a kernel/eka/memmodel/epoc/flexible/mmu/mmu.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mmu.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,2434 @@ +// Copyright (c) 1998-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 "kernel/cache_maintenance.inl" +#include +#include +#include +#include "mm.h" +#include "mmu.h" +#include "mpager.h" +#include "mmapping.h" +#include "mobject.h" +#include "mmanager.h" +#include "mpagearray.h" + + +// +// SPageInfo +// + +// check enough space for page infos... +__ASSERT_COMPILE((KPageInfoLinearEnd-KPageInfoLinearBase)/sizeof(SPageInfo)==(1<<(32-KPageShift))); + +// check KPageInfoShift... +__ASSERT_COMPILE(sizeof(SPageInfo)==(1<>(KPageShift+KPageShift-KPageInfoShift); + TUint flags = ((TUint8*)KPageInfoMap)[index>>3]; + TUint mask = 1<<(index&7); + if(!(flags&mask)) + return 0; // no SPageInfo for aAddress + SPageInfo* info = FromPhysAddr(aAddress); + if(info->iType==SPageInfo::EInvalid) + return 0; + return info; + } + + +#ifdef _DEBUG + +void SPageInfo::CheckAccess(const char* aMessage, TUint aFlags) + { + if(K::Initialising || NKern::Crashed()) + return; + + if((aFlags&ECheckNotAllocated) && (iType!=EUnknown)) + { + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x, iType==%d : %s",this,PhysAddr(),iType,aMessage); + __NK_ASSERT_DEBUG(0); + goto fail; + } + + if((aFlags&ECheckNotUnused) && (iType==EUnused)) + { + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x, iType==%d : %s",this,PhysAddr(),iType,aMessage); + __NK_ASSERT_DEBUG(0); + goto fail; + } + + if((aFlags&ECheckUnused) && (iType!=EUnused)) + { + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x, iType==%d : %s",this,PhysAddr(),iType,aMessage); + __NK_ASSERT_DEBUG(0); + goto fail; + } + + if((aFlags&ECheckNotPaged) && (iPagedState!=EUnpaged)) + { + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x, iPagedState=%d : %s",this,PhysAddr(),iPagedState,aMessage); + __NK_ASSERT_DEBUG(0); + goto fail; + } + + if((aFlags&ECheckRamAllocLock) && !RamAllocLock::IsHeld()) + { + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x, iType==%d : %s",this,PhysAddr(),iType,aMessage); + __NK_ASSERT_DEBUG(0); + goto fail; + } + + if((aFlags&ENoCheckMmuLock) || MmuLock::IsHeld()) + return; +fail: + Kern::Printf("SPageInfo[0x%08x]::CheckAccess failed, PhysAddr()=0x%08x : %s",this,PhysAddr(),aMessage); + Mmu::Panic(Mmu::EUnsafePageInfoAccess); + } + + +void SPageInfo::Dump() + { + Kern::Printf("SPageInfo for page %x = %d,%d,%02x,0x%08x,0x%x,%d",PhysAddr(),iType,iPagedState,iFlags,iOwner,iIndex,iPinCount); + } + +#endif + + + +// +// SPageTableInfo +// + +// check enough space for page table infos... +__ASSERT_COMPILE((KPageTableInfoEnd-KPageTableInfoBase)/sizeof(SPageTableInfo) + >=(KPageTableEnd-KPageTableBase)/KPageTableSize); + +// check KPtBlockShift... +__ASSERT_COMPILE((sizeof(SPageTableInfo)<iCleanup.iThread == &Kern::CurrentThread() && m.iRamAllocLockCount; + } + + + +// +// MmuLock +// + +#ifdef _DEBUG +TUint MmuLock::UnlockGuardNest =0; +TUint MmuLock::UnlockGuardFail =0; +#endif + +NFastMutex MmuLock::iLock; + +void MmuLock::Lock() + { + NKern::FMWait(&iLock); + } + +void MmuLock::Unlock() + { + UnlockGuardCheck(); + NKern::FMSignal(&iLock); + } + +TBool MmuLock::Flash() + { + UnlockGuardCheck(); + return NKern::FMFlash(&iLock); + } + +TBool MmuLock::IsHeld() + { + NFastMutex& m = iLock; + return m.HeldByCurrentThread(); + } + + + +// +// Initialisation +// + +Mmu TheMmu; + +void Mmu::Init1Common() + { + __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("Mmu::Init1Common")); + + // Mmu data + TUint pteType = PteType(ESupervisorReadWrite,true); + iTempPteCached = BlankPte((TMemoryAttributes)(EMemoryAttributeNormalCached|EMemoryAttributeDefaultShareable),pteType); + iTempPteUncached = BlankPte((TMemoryAttributes)(EMemoryAttributeNormalUncached|EMemoryAttributeDefaultShareable),pteType); + iTempPteCacheMaintenance = BlankPte((TMemoryAttributes)(CacheMaintenance::TemporaryMapping()|EMemoryAttributeDefaultShareable),pteType); + + // other + PP::MaxUserThreadStack=0x14000; // 80K - STDLIB asks for 64K for PosixServer!!!! + PP::UserThreadStackGuard=0x2000; // 8K + PP::MaxStackSpacePerProcess=0x200000; // 2Mb + K::SupervisorThreadStackSize=0x1000; // 4K + PP::SupervisorThreadStackGuard=0x1000; // 4K + K::MachineConfig=(TMachineConfig*)KMachineConfigLinAddr; + PP::RamDriveStartAddress=0; + PP::RamDriveRange=0; + PP::RamDriveMaxSize=0x20000000; // 512MB, probably will be reduced later + K::MemModelAttributes=EMemModelTypeFlexible|EMemModelAttrNonExProt|EMemModelAttrKernProt|EMemModelAttrWriteProt| + EMemModelAttrVA|EMemModelAttrProcessProt|EMemModelAttrSameVA|EMemModelAttrSvKernProt| + EMemModelAttrIPCKernProt|EMemModelAttrRamCodeProt; + } + + +#if 0 +void Mmu::VerifyRam() + { + Kern::Printf("Mmu::VerifyRam() pass 1"); + RamAllocLock::Lock(); + + TPhysAddr p = 0; + do + { + SPageInfo* pi = SPageInfo::SafeFromPhysAddr(p); + if(pi) + { + Kern::Printf("%08x %d",p,pi->Type()); + if(pi->Type()==SPageInfo::EUnused) + { + volatile TPhysAddr* b = (volatile TPhysAddr*)MapTemp(p,0); + b[0] = p; + b[1] = ~p; + __NK_ASSERT_DEBUG(b[0]==p); + __NK_ASSERT_DEBUG(b[1]==~p); + UnmapTemp(); + } + } + p += KPageSize; + } + while(p); + + TBool fail = false; + Kern::Printf("Mmu::VerifyRam() pass 2"); + do + { + SPageInfo* pi = SPageInfo::SafeFromPhysAddr(p); + if(pi) + { + if(pi->Type()==SPageInfo::EUnused) + { + volatile TPhysAddr* b = (volatile TPhysAddr*)MapTemp(p,0); + if(b[0]!=p || b[1]!=~p) + { + fail = true; + Kern::Printf("%08x FAILED %x %x",b[0],b[1]); + } + UnmapTemp(); + } + } + p += KPageSize; + } + while(p); + + __NK_ASSERT_DEBUG(!fail); + RamAllocLock::Unlock(); + } +#endif + + +void Mmu::Init2Common() + { + __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("Mmu::Init2Common")); + + // create allocator... + const SRamInfo& info = *(const SRamInfo*)TheSuperPage().iRamBootData; + iRamPageAllocator = DRamAllocator::New(info, iRamZones, iRamZoneCallback); + + // initialise all pages in banks as unused... + const SRamBank* bank = info.iBanks; + while(bank->iSize) + { + TUint32 base = bank->iBase; + TUint32 size = bank->iSize; + __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("Found RAM bank 0x%08x size %d",base,size)); + if(base+size<=base || ((base|size)&KPageMask)) + Panic(EInvalidRamBankAtBoot); + + SPageInfo* pi = SPageInfo::FromPhysAddr(base); + SPageInfo* piEnd = pi+(size>>KPageShift); + while(piSetUnused(); + ++bank; + } + // step over the last bank to get to the reserved banks. + ++bank; + // mark any reserved regions as allocated... + while(bank->iSize) + { + TUint32 base = bank->iBase; + TUint32 size = bank->iSize; + __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("Found reserved bank 0x%08x size %d",base,size)); + if(base+size<=base || ((base|size)&KPageMask)) + Panic(EInvalidReservedBankAtBoot); + + SPageInfo* pi = SPageInfo::FromPhysAddr(base); + SPageInfo* piEnd = pi+(size>>KPageShift); + while(piSetPhysAlloc(); + ++bank; + } + + // Clear the inital (and only so far) page table info page so all unused + // page tables infos will be marked as unused. + __ASSERT_COMPILE(SPageTableInfo::EUnused == 0); + memclr((TAny*)KPageTableInfoBase, KPageSize); + + // look for page tables - assume first page table maps page tables + TPte* pPte = (TPte*)KPageTableBase; + TInt i; + for(i=0; i Phys %08x", KPageTableBase+i*KPageSize, ptpgPhys)); + SPageInfo* pi = SPageInfo::SafeFromPhysAddr(ptpgPhys); + __ASSERT_ALWAYS(pi, Panic(EInvalidPageTableAtBoot)); + pi->SetFixed(i); // this also sets the SPageInfo::iOffset so that linear-to-physical works + } + + // look for mapped pages + TPde* pd = Mmu::PageDirectory(KKernelOsAsid); + for(i=0; i<(1<<(32-KChunkShift)); ++i) + { + TPde pde = pd[i]; + if(pde==KPdeUnallocatedEntry) + continue; + TPhysAddr pdePhys = Mmu::PdePhysAddr(pde); + TPte* pt = 0; + if(pdePhys!=KPhysAddrInvalid) + { + __KTRACE_OPT(KBOOT,Kern::Printf("Addr %08x -> Whole PDE Phys %08x", i< page table %08x", i<MarkPageAllocated(pa, EPageFixed); + // allow KErrAlreadyExists since it's possible that a page is doubly mapped + __ASSERT_ALWAYS(r==KErrNone || r==KErrAlreadyExists, Panic(EBadMappedPageAfterBoot)); + if(pi->Type()==SPageInfo::EUnused) + pi->SetFixed(); + } + } + } + __KTRACE_OPT(KBOOT,Kern::Printf("Addr: %08x #PTEs=%d",(i<Boot(np); + } + } + + TInt r = K::MutexCreate(iRamAllocatorMutex, KLitRamAlloc, NULL, EFalse, KMutexOrdRamAlloc); + if(r!=KErrNone) + Panic(ERamAllocMutexCreateFailed); + iRamAllocLockCount = 0; + iRamAllocInitialFreePages = FreeRamInPages(); + + __KTRACE_OPT2(KBOOT,KMMU,Kern::Printf("Mmu::DoInit2")); + + for(i=0; i=0x80000000/KPageSize) + { + TPhysAddr dummyPage; + TInt r = iRamPageAllocator->AllocRamPages(&dummyPage,1, EPageFixed); + __NK_ASSERT_ALWAYS(r==KErrNone); + } + // hack, reduce total RAM to <2GB... + if(TheSuperPage().iTotalRamSize<0) + TheSuperPage().iTotalRamSize = 0x80000000-KPageSize; + + // Save current free RAM size - there can never be more free RAM than this + TUint maxFreePages = FreeRamInPages(); + K::MaxFreeRam = maxFreePages*KPageSize; + if(maxFreePages < (TUint(PP::RamDriveMaxSize)>>KPageShift)) + PP::RamDriveMaxSize = maxFreePages*KPageSize; + + // update this to stop assert triggering in RamAllocLock::Lock() + iRamAllocInitialFreePages = maxFreePages; + } + + +void Mmu::Init3() + { + iDefrag = new Defrag; + if (!iDefrag) + Panic(EDefragAllocFailed); + iDefrag->Init3(TheMmu.iRamPageAllocator); + } + +// +// Utils +// + +void Mmu::Panic(TPanic aPanic) + { + Kern::Fault("MMU",aPanic); + } + + +TUint Mmu::FreeRamInPages() + { + return iRamPageAllocator->FreeRamInPages()+ThePager.NumberOfFreePages(); + } + + +TUint Mmu::TotalPhysicalRamPages() + { + return iRamPageAllocator->TotalPhysicalRamPages(); + } + + +const SRamZone* Mmu::RamZoneConfig(TRamZoneCallback& aCallback) const + { + aCallback = iRamZoneCallback; + return iRamZones; + } + + +void Mmu::SetRamZoneConfig(const SRamZone* aZones, TRamZoneCallback aCallback) + { + iRamZones = aZones; + iRamZoneCallback = aCallback; + } + + +TInt Mmu::ModifyRamZoneFlags(TUint aId, TUint aClearMask, TUint aSetMask) + { + return iRamPageAllocator->ModifyZoneFlags(aId, aClearMask, aSetMask); + } + + +TInt Mmu::GetRamZonePageCount(TUint aId, SRamZonePageCount& aPageData) + { + return iRamPageAllocator->GetZonePageCount(aId, aPageData); + } + + +TInt Mmu::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aBytes, TPhysAddr& aPhysAddr, TInt aAlign) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam(?,%d,%d,?,%d)", aZoneIdCount, aBytes, aPhysAddr, aAlign)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + + TInt r = iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aBytes, aPhysAddr, EPageFixed, aAlign); + if(r!=KErrNone) + iRamAllocFailed = ETrue; + else + { + TUint pages = MM::RoundToPageCount(aBytes); + AllocatedPhysicalRam(aPhysAddr, pages, (Mmu::TRamAllocFlags)EMemAttStronglyOrdered); + } + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam returns %d and aPhysAddr=0x%08x",r,aPhysAddr)); + return r; + } + + +TInt Mmu::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aNumPages, TPhysAddr* aPageList) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam(?,%d,%d,?)", aZoneIdCount, aNumPages)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + + TInt r = iRamPageAllocator->ZoneAllocRamPages(aZoneIdList, aZoneIdCount, aPageList, aNumPages, EPageFixed); + if(r!=KErrNone) + iRamAllocFailed = ETrue; + else + { + PagesAllocated(aPageList, aNumPages, (Mmu::TRamAllocFlags)EMemAttStronglyOrdered); + + // update page infos... + TUint flash = 0; + TPhysAddr* pageEnd = aPageList + aNumPages; + MmuLock::Lock(); + TPhysAddr* page = aPageList; + while (page < pageEnd) + { + MmuLock::Flash(flash,KMaxPageInfoUpdatesInOneGo/2); + TPhysAddr pagePhys = *page++; + __NK_ASSERT_DEBUG(pagePhys != KPhysAddrInvalid); + SPageInfo::FromPhysAddr(pagePhys)->SetPhysAlloc(); + } + MmuLock::Unlock(); + } + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam returns %d",r)); + return r; + } + + +TInt Mmu::RamHalFunction(TInt aFunction, TAny* a1, TAny* a2) + { + // This function should only be registered with hal and therefore can only + // be invoked after the ram allocator has been created. + __NK_ASSERT_DEBUG(iRamPageAllocator); + return iRamPageAllocator->HalFunction(aFunction, a1, a2); + } + + +void Mmu::ChangePageType(SPageInfo* aPageInfo, TZonePageType aOldPageType, TZonePageType aNewPageType) + { + iRamPageAllocator->ChangePageType(aPageInfo, aOldPageType, aNewPageType); + } + +TInt Mmu::HandlePageFault(TLinAddr aPc, TLinAddr aFaultAddress, TUint aAccessPermissions, TAny* aExceptionInfo) + { + TRACE(("Mmu::HandlePageFault(0x%08x,0x%08x,%d)",aPc,aFaultAddress,aAccessPermissions)); + + DMemModelThread* thread = (DMemModelThread*)TheCurrentThread; + // Get the os asid of the process taking the fault, no need to open a reference + // as it is the current thread's process so can't be freed. + TUint faultOsAsid = ((DMemModelProcess*)thread->iNThread.iAddressSpace)->OsAsid(); + + // check if any fast mutexes held... + NFastMutex* fm = NKern::HeldFastMutex(); + TPagingExcTrap* trap = thread->iPagingExcTrap; + if(fm) + { + // check there is an XTRAP_PAGING in effect... + if(!trap) + { + // oops, kill system... + __KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("Fault with FM Held! addr=0x%08x (%O pc=%x)",aFaultAddress,thread,aPc)); + Exc::Fault(aExceptionInfo); + } + + // release the fast mutex... + NKern::FMSignal(fm); + } + + NKern::ThreadEnterCS(); + + // work out address space for aFaultAddress... + TUint osAsid = faultOsAsid; + TLinAddr addr = aFaultAddress; + if(thread->iAliasLinAddr && TUint(addr - thread->iAliasLinAddr) < TUint(KPageSize)) + { + // Address in aliased memory... + addr = (addr - thread->iAliasLinAddr) + thread->iAliasTarget; + // Get the os asid of the process thread is aliasing, no need to open + // a reference on it as one was already opened when the alias was created. + osAsid = thread->iAliasProcess->OsAsid(); + } + else if(addr>=KGlobalMemoryBase) + { + // Address in global region, so look it up in kernel's address space... + osAsid = KKernelOsAsid; + } + + // NOTE, osAsid will remain valid for duration of this function because it is either + // - The current thread's address space, which can't go away whilst the thread + // is running. + // - The address space of another thread which we are aliasing memory from, + // and we would only do this if we have a reference on this other thread, + // which has a reference on it's process, which should own the address space! + +#ifdef __BROADCAST_CACHE_MAINTENANCE__ + if (thread->iAliasLinAddr) + { + // If an alias is in effect, the the thread will be locked to the current CPU, + // but we need to be able to migrate between CPUs for cache maintainance. This + // must be dealt with by removing the alias and restoring it with a paging trap + // handler. + if(!trap) + { + // oops, kill system... + __KTRACE_OPT2(KPAGING,KPANIC,Kern::Printf("Fault with thread locked to current CPU! addr=0x%08x (%O pc=%x)",aFaultAddress,thread,aPc)); + Exc::Fault(aExceptionInfo); + } + thread->RemoveAlias(); + } +#endif + + // find mapping... + TUint offsetInMapping; + TUint mapInstanceCount; + DMemoryMapping* mapping = MM::FindMappingInAddressSpace(osAsid, addr, 1, offsetInMapping, mapInstanceCount); +// TRACE(("%O mapping=0x%08x",TheCurrentThread,mapping)); + TInt r = KErrNotFound; + + if(mapping) + { + // Pinning mappings should not be found from within an address space. + __NK_ASSERT_DEBUG(!mapping->IsPinned()); + MmuLock::Lock(); + + // check if we need to process page fault... + if(!Mmu::CheckPteTypePermissions(mapping->PteType(),aAccessPermissions) || + mapInstanceCount != mapping->MapInstanceCount()) + { + // Invalid access to the page. + MmuLock::Unlock(); + r = KErrAbort; + } + else + { + // we do need to handle fault so is this a demand paging or page moving fault + DMemoryObject* memory = mapping->Memory(); + if(!memory) + MmuLock::Unlock(); + else + { + TUint faultIndex = (offsetInMapping >> KPageShift) + mapping->iStartIndex; + memory->Open(); + + // This is safe as we have the instance count so can detect the mapping + // being reused and we have a reference to the memory object so it can't + // be deleted. + MmuLock::Unlock(); + + if(memory->IsDemandPaged()) + { + // Let the pager handle the fault... + r = ThePager.HandlePageFault( aPc, aFaultAddress, faultOsAsid, faultIndex, + aAccessPermissions, memory, mapping, mapInstanceCount, + thread, aExceptionInfo); + } + else + {// The page could be being moved so verify that with its manager. + DMemoryManager* manager = memory->iManager; + r = manager->HandleFault(memory, faultIndex, mapping, mapInstanceCount, aAccessPermissions); + } + if (r == KErrNone) + {// alias PDE needs updating because page tables have changed... + thread->RefreshAlias(); + } + memory->Close(); + } + } + mapping->Close(); + } + + if (trap) + { + // restore address space (because the trap will bypass any code + // which would have done this.)... + DMemModelThread::RestoreAddressSpace(); + } + + NKern::ThreadLeaveCS(); // thread will die now if CheckRealtimeThreadFault caused a panic + + // deal with XTRAP_PAGING... + if(trap) + { + // re-acquire any fast mutex which was held before the page fault... + if(fm) + NKern::FMWait(fm); + if (r == KErrNone) + { + trap->Exception(1); // return from exception trap with result '1' (value>0) + // code doesn't continue beyond this point. + __NK_ASSERT_DEBUG(0); + } + } + + return r; + } + + +// +// Memory allocation +// + +TInt Mmu::AllocRam( TPhysAddr* aPages, TUint aCount, TRamAllocFlags aFlags, TZonePageType aZonePageType, + TUint aBlockZoneId, TBool aBlockRest) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocRam(?,%d,%x)",aCount,aFlags)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); +#ifdef _DEBUG + if(K::CheckForSimulatedAllocFail()) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocRam returns simulated OOM %d",KErrNoMemory)); + return KErrNoMemory; + } +#endif + TInt missing = iRamPageAllocator->AllocRamPages(aPages, aCount, aZonePageType, aBlockZoneId, aBlockRest); + if(missing && !(aFlags&EAllocNoPagerReclaim) && ThePager.GetFreePages(missing)) + missing = iRamPageAllocator->AllocRamPages(aPages, aCount, aZonePageType, aBlockZoneId, aBlockRest); + TInt r = missing ? KErrNoMemory : KErrNone; + if(r!=KErrNone) + iRamAllocFailed = ETrue; + else + PagesAllocated(aPages,aCount,aFlags); + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocRam returns %d",r)); + return r; + } + + +void Mmu::FreeRam(TPhysAddr* aPages, TUint aCount, TZonePageType aZonePageType) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreeRam(?,%d)",aCount)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + + // update page infos... + TPhysAddr* pages = aPages; + TPhysAddr* pagesEnd = pages+aCount; + TPhysAddr* pagesOut = aPages; + MmuLock::Lock(); + TUint flash = 0; + while(pagesPagedState() != SPageInfo::EPagedPinnedMoved || aCount == 1); + if (pi->PagedState() == SPageInfo::EPagedPinnedMoved) + aZonePageType = EPageDiscard; + + if(ThePager.PageFreed(pi)==KErrNone) + --aCount; // pager has dealt with this page, so one less for us + else + { + // All paged pages should have been dealt with by the pager above. + __NK_ASSERT_DEBUG(pi->PagedState() == SPageInfo::EUnpaged); + *pagesOut++ = pagePhys; // store page address for freeing later + } + } + MmuLock::Unlock(); + + iRamPageAllocator->FreeRamPages(aPages, aCount, aZonePageType); + } + + +TInt Mmu::AllocContiguousRam(TPhysAddr& aPhysAddr, TUint aCount, TUint aAlign, TRamAllocFlags aFlags) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocContiguousRam(?,0x%x,%d,%x)",aCount,aAlign,aFlags)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); +#ifdef _DEBUG + if(K::CheckForSimulatedAllocFail()) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocContiguousRam returns simulated OOM %d",KErrNoMemory)); + return KErrNoMemory; + } + // Only the page sets EAllocNoPagerReclaim and it shouldn't allocate contiguous ram. + __NK_ASSERT_DEBUG(!(aFlags&EAllocNoPagerReclaim)); +#endif + TInt r = iRamPageAllocator->AllocContiguousRam(aCount, aPhysAddr, EPageFixed, aAlign+KPageShift); + if(r==KErrNoMemory && aCount > KMaxFreeableContiguousPages) + { + // flush paging cache and retry... + ThePager.FlushAll(); + r = iRamPageAllocator->AllocContiguousRam(aCount, aPhysAddr, EPageFixed, aAlign+KPageShift); + } + if(r!=KErrNone) + iRamAllocFailed = ETrue; + else + PagesAllocated((TPhysAddr*)(aPhysAddr|1), aCount, aFlags); + __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguouseRam returns %d and aPhysAddr=0x%08x",r,aPhysAddr)); + return r; + } + + +void Mmu::FreeContiguousRam(TPhysAddr aPhysAddr, TUint aCount) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreeContiguousRam(0x%08x,0x%x)",aPhysAddr,aCount)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + __NK_ASSERT_DEBUG((aPhysAddr&KPageMask)==0); + + TUint pageCount = aCount; + + // update page infos... + SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr); + SPageInfo* piEnd = pi+pageCount; + TUint flash = 0; + MmuLock::Lock(); + while(piFreeRamPage(aPhysAddr, EPageFixed); + aPhysAddr += KPageSize; + --pageCount; + } + } + + +TInt Mmu::AllocPhysicalRam(TPhysAddr* aPages, TUint aCount, TRamAllocFlags aFlags) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocPhysicalRam(?,%d,%x)",aCount,aFlags)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + // Allocate fixed pages as physically allocated pages aren't movable or discardable. + TInt r = AllocRam(aPages, aCount, aFlags, EPageFixed); + if (r!=KErrNone) + return r; + + // update page infos... + TPhysAddr* pages = aPages; + TPhysAddr* pagesEnd = pages+aCount; + MmuLock::Lock(); + TUint flash = 0; + while(pagesSetPhysAlloc(); + } + MmuLock::Unlock(); + + return KErrNone; + } + + +void Mmu::FreePhysicalRam(TPhysAddr* aPages, TUint aCount) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreePhysicalRam(?,%d)",aCount)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + + // update page infos... + TPhysAddr* pages = aPages; + TPhysAddr* pagesEnd = pages+aCount; + MmuLock::Lock(); + TUint flash = 0; + while(pagesType()==SPageInfo::EPhysAlloc, Panic(EBadFreePhysicalRam)); + __ASSERT_ALWAYS(!pi->UseCount(), Panic(EBadFreePhysicalRam)); + pi->SetUnused(); + } + MmuLock::Unlock(); + + iRamPageAllocator->FreeRamPages(aPages,aCount, EPageFixed); + } + + +TInt Mmu::AllocPhysicalRam(TPhysAddr& aPhysAddr, TUint aCount, TUint aAlign, TRamAllocFlags aFlags) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocPhysicalRam(?,0x%x,d,%x)",aCount,aAlign,aFlags)); + TInt r = AllocContiguousRam(aPhysAddr,aCount,aAlign,aFlags); + if (r!=KErrNone) + return r; + + // update page infos... + SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr); + SPageInfo* piEnd = pi+aCount; + TUint flash = 0; + MmuLock::Lock(); + while(piSetPhysAlloc(); + ++pi; + } + MmuLock::Unlock(); + + return KErrNone; + } + + +void Mmu::FreePhysicalRam(TPhysAddr aPhysAddr, TUint aCount) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreePhysicalRam(0x%08x,0x%x)",aPhysAddr,aCount)); + + // update page infos... + SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr); + SPageInfo* piEnd = pi+aCount; + TUint flash = 0; + MmuLock::Lock(); + while(piType()==SPageInfo::EPhysAlloc, Panic(EBadFreePhysicalRam)); + __ASSERT_ALWAYS(!pi->UseCount(), Panic(EBadFreePhysicalRam)); + pi->SetUnused(); + ++pi; + } + MmuLock::Unlock(); + + iRamPageAllocator->FreePhysicalRam(aPhysAddr, aCount << KPageShift); + } + + +TInt Mmu::ClaimPhysicalRam(TPhysAddr aPhysAddr, TUint aCount, TRamAllocFlags aFlags) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ClaimPhysicalRam(0x%08x,0x%x,0x%08x)",aPhysAddr,aCount,aFlags)); + aPhysAddr &= ~KPageMask; + TInt r = iRamPageAllocator->ClaimPhysicalRam(aPhysAddr,aCount); + if(r!=KErrNone) + return r; + + PagesAllocated((TPhysAddr*)(aPhysAddr|1), aCount, aFlags); + + // update page infos... + SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr); + SPageInfo* piEnd = pi+aCount; + TUint flash = 0; + MmuLock::Lock(); + while(piSetPhysAlloc(); + ++pi; + } + MmuLock::Unlock(); + + return KErrNone; + } + + +void Mmu::AllocatedPhysicalRam(TPhysAddr aPhysAddr, TUint aCount, TRamAllocFlags aFlags) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocatedPhysicalRam(0x%08x,0x%x,d,%x)",aPhysAddr,aCount,aFlags)); + + PagesAllocated((TPhysAddr*)(aPhysAddr|1), aCount, aFlags); + + // update page infos... + SPageInfo* pi = SPageInfo::FromPhysAddr(aPhysAddr); + SPageInfo* piEnd = pi+aCount; + TUint flash = 0; + MmuLock::Lock(); + while(piSetPhysAlloc(); + ++pi; + } + MmuLock::Unlock(); + } + + +// +// Misc +// + +#ifdef _DEBUG +/** +Perform a page table walk to return the physical address of +the memory mapped at virtual address \a aLinAddr in the +address space \a aOsAsid. + +If the page table used was not one allocated by the kernel +then the results are unpredictable and may cause a system fault. + +@pre #MmuLock held. +*/ +TPhysAddr Mmu::LinearToPhysical(TLinAddr aLinAddr, TInt aOsAsid) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld() || K::Initialising); + return UncheckedLinearToPhysical(aLinAddr,aOsAsid); + } +#endif + + +/** +Next virtual address available for allocation by TTempMapping. +This is initialised to #KTempAddr and addresses may be allocated +until they reach #KTempAddrEnd. +*/ +TLinAddr Mmu::TTempMapping::iNextLinAddr = KTempAddr; + + +/** +Allocate virtual address space required to map a given number of memory pages. + +The actual size of allocated virtual allocated needs to accommodate \a aNumPages +number of pages of any colour. For example: if \a aNumPages == 4 and #KPageColourCount == 4, +then at least 7 pages are required. + +@param aNumPages Maximum number of pages that can be mapped into this temporary mapping. + +@pre Called in single threaded content (boot) only. + +@pre #iNextLinAddr points to virtual page with zero colour. +@post #iNextLinAddr points to virtual page with zero colour. +*/ +void Mmu::TTempMapping::Alloc(TUint aNumPages) + { + __NK_ASSERT_DEBUG(aNumPages<=(KTempAddrEnd-KTempAddr)/KPageSize); + + // This runs during the boot only (single threaded context) so the access to iNextLinAddr is not guarded by any mutex. + TLinAddr tempAddr = iNextLinAddr; + TUint numPages = (KPageColourMask+aNumPages+KPageColourMask)&~KPageColourMask; + iNextLinAddr = tempAddr+numPages*KPageSize; + + __NK_ASSERT_ALWAYS(iNextLinAddr<=KTempAddrEnd); + + __NK_ASSERT_DEBUG(iSize==0); + iLinAddr = tempAddr; + MmuLock::Lock(); + iPtePtr = Mmu::PtePtrFromLinAddr(tempAddr,KKernelOsAsid); + __NK_ASSERT_DEBUG(iPtePtr); + MmuLock::Unlock(); + iBlankPte = TheMmu.iTempPteCached; + iSize = aNumPages; + iCount = 0; + + TRACEB(("Mmu::TTempMapping::Alloc(%d) iLinAddr=0x%08x, iPtePtr=0x%08x",aNumPages,iLinAddr,iPtePtr)); + } + + +/** +Map a single physical page into this temporary mapping. + +Supervisor read/write access and EMemoryAttributeStandard memory attributes apply. + +@param aPage The physical page to map. +@param aColour The required colour for the mapping. + +@return The linear address at which the page is mapped. +*/ +TLinAddr Mmu::TTempMapping::Map(TPhysAddr aPage, TUint aColour) + { + __NK_ASSERT_DEBUG(iSize>=1); + __NK_ASSERT_DEBUG(iCount==0); + + TUint colour = aColour&KPageColourMask; + TLinAddr addr = iLinAddr+(colour<=1); + __NK_ASSERT_DEBUG(iCount==0); + + TUint colour = aColour&KPageColourMask; + TLinAddr addr = iLinAddr+(colour<=aCount); + __NK_ASSERT_DEBUG(iCount==0); + + TUint colour = aColour&KPageColourMask; + TLinAddr addr = iLinAddr+(colour<=1); + if(aIMBRequired) + CacheMaintenance::CodeChanged(iLinAddr+iColour*KPageSize,iCount*KPageSize); + Unmap(); + } + + +/** +Unmap all pages from this temporary mapping. +*/ +void Mmu::TTempMapping::Unmap() + { + __NK_ASSERT_DEBUG(iSize>=1); + + TUint colour = iColour; + TLinAddr addr = iLinAddr+(colour<>KPageTableShift; + while(!iAliasList.IsEmpty()) + { + SDblQueLink* next = iAliasList.First()->Deque(); + checkedList.Add(next); + DMemModelThread* thread = (DMemModelThread*)((TInt)next-_FOFF(DMemModelThread,iAliasLink)); + if((thread->iAliasPde>>KPageTableShift)==ptId) + { + // the page table is being aliased by the thread, so remove it... + TRACE2(("Thread %O RemoveAliasesForPageTable", this)); + thread->iAliasPde = KPdeUnallocatedEntry; +#ifdef __SMP__ // we need to also unmap the page table in case thread is running on another core... + // need Data Memory Barrier (DMB) here to make sure iAliasPde change is + // seen before we set the PDE entry, otherwise 'thread' may read old value + // and put it back + __e32_memory_barrier(); + *thread->iAliasPdePtr = KPdeUnallocatedEntry; + SinglePdeUpdated(thread->iAliasPdePtr); + __NK_ASSERT_DEBUG((thread->iAliasLinAddr&KPageMask)==0); + // Invalidate the tlb for the page using os asid of the process that created the alias + // this is safe as the os asid will be valid as thread must be running otherwise the alias + // would have been removed. + InvalidateTLBForPage(thread->iAliasLinAddr | ((DMemModelProcess*)thread->iOwningProcess)->OsAsid()); + // note, race condition with 'thread' updating its iAliasLinAddr is + // not a problem because 'thread' will not the be accessing the aliased + // region and will take care of invalidating the TLB. + // FIXME: There is still a race here. If the thread owning the alias reads the + // PDE before we clear thread->iAliasPde and writes it after we clear + // *thread->iAliasPdePtr the alias still ends up restored when it shouldn't be. +#endif + } + MmuLock::Flash(); + } + + // copy checkedList back to iAliasList + iAliasList.MoveFrom(&checkedList); + + MmuLock::Unlock(); + } + + +void DMemModelThread::RefreshAlias() + { + if(iAliasLinAddr) + { + TRACE2(("Thread %O RefreshAlias", this)); + // Get the os asid, this is the current thread so no need to open a reference. + TUint thisAsid = ((DMemModelProcess*)iOwningProcess)->OsAsid(); + MmuLock::Lock(); + TInt osAsid = iAliasProcess->OsAsid(); + TPde pde = *Mmu::PageDirectoryEntry(osAsid,iAliasTarget); + iAliasPde = pde; + *iAliasPdePtr = pde; + SinglePdeUpdated(iAliasPdePtr); + InvalidateTLBForPage(iAliasLinAddr|thisAsid); + MmuLock::Unlock(); + } + } + + + +// +// Mapping/unmapping functions +// + + +/** +Modify page table entries (PTEs) so they map the given memory pages. +Entries are only updated if the current state of the corresponding page +is RPageArray::ECommitted. + +@param aPtePtr Pointer into a page table for the PTE of the first page. +@param aCount The number of pages to modify. +@param aPages Pointer to the entry for the first page in a memory object's #RPageArray. + Each entry contains the physical address of a page together with its + current state (RPageArray::TState). +@param aBlankPte The value to use for each PTE, with the physical address component equal + to zero. + +@return False, if the page table no longer maps any entries and may be freed. + True otherwise, to indicate that the page table is still needed. + +@pre #MmuLock held. +@post #MmuLock held and has not been released by this function. +*/ +TBool Mmu::MapPages(TPte* const aPtePtr, const TUint aCount, TPhysAddr* aPages, TPte aBlankPte) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aCount); + __NK_ASSERT_DEBUG(aBlankPte!=KPteUnallocatedEntry); + + TUint count = 0; + if(aCount==1) + { + // get page to map... + TPhysAddr pagePhys = *aPages; + TPte pte = *aPtePtr; + if(!RPageArray::TargetStateIsCommitted(pagePhys)) + goto done; // page no longer needs mapping + + // clear type flags... + pagePhys &= ~KPageMask; + + // check nobody has already mapped the page... + if(pte!=KPteUnallocatedEntry) + { + // already mapped... +#ifdef _DEBUG + if((pte^pagePhys)>=TPte(KPageSize)) + { + // but different! + Kern::Printf("Mmu::MapPages already mapped %x->%x",pagePhys,pte); + __NK_ASSERT_DEBUG(0); + } +#endif + return true; // return true to keep page table (it already had at least page mapped) + } + + // map page... + pte = pagePhys|aBlankPte; + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + count = 1; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + else + { + // check we are only updating a single page table... + __NK_ASSERT_DEBUG(((TLinAddr(aPtePtr)^TLinAddr(aPtePtr+aCount-1))>>KPageTableShift)==0); + + // map pages... + TPte* pPte = aPtePtr; + TPte* pPteEnd = aPtePtr+aCount; + do + { + // map page... + TPhysAddr pagePhys = *aPages++; + TPte pte = *pPte++; + if(RPageArray::TargetStateIsCommitted(pagePhys)) + { + // clear type flags... + pagePhys &= ~KPageMask; + + // page not being freed, so try and map it... + if(pte!=KPteUnallocatedEntry) + { + // already mapped... +#ifdef _DEBUG + if((pte^pagePhys)>=TPte(KPageSize)) + { + // but different! + Kern::Printf("Mmu::MapPages already mapped %x->%x",pagePhys,pte); + __NK_ASSERT_DEBUG(0); + } +#endif + } + else + { + // map page... + pte = pagePhys|aBlankPte; + TRACE2(("!PTE %x=%x",pPte-1,pte)); + pPte[-1] = pte; + ++count; + } + } + } + while(pPte!=pPteEnd); + + // clean cache... + CacheMaintenance::MultiplePtesUpdated((TLinAddr)aPtePtr,(TLinAddr)pPte-(TLinAddr)aPtePtr); + } + +done: + // update page counts... + SPageTableInfo* pti = SPageTableInfo::FromPtPtr(aPtePtr); + count = pti->IncPageCount(count); + TRACE2(("pt %x page count=%d",TLinAddr(aPtePtr)&~KPageTableMask,pti->PageCount())); + __NK_ASSERT_DEBUG(pti->CheckPageCount()); + + // see if page table needs freeing... + TUint keepPt = count | pti->PermanenceCount(); + + __NK_ASSERT_DEBUG(!pti->IsDemandPaged()); // check not demand paged page table + + return keepPt; + } + + +/** +Modify page table entries (PTEs) so they map a new page. +Entries are only updated if the current state of the corresponding page +is RPageArray::ECommitted or RPageArray::EMoving. + +@param aPtePtr Pointer into a page table for the PTE of the page. +@param aPage Pointer to the entry for the page in a memory object's #RPageArray. + The entry contains the physical address of a page together with its + current state (RPageArray::TState). +@param aBlankPte The value to use for each PTE, with the physical address component equal + to zero. + +@pre #MmuLock held. +@post #MmuLock held and has not been released by this function. +*/ +void Mmu::RemapPage(TPte* const aPtePtr, TPhysAddr& aPage, TPte aBlankPte) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aBlankPte!=KPteUnallocatedEntry); + + // get page to remap... + TPhysAddr pagePhys = aPage; + + // Only remap the page if it is committed or it is being moved and + // no other operation has been performed on the page. + if(!RPageArray::TargetStateIsCommitted(pagePhys)) + return; // page no longer needs mapping + + // Only remap the page if it is currently mapped, i.e. doesn't have an unallocated pte. + // This will only be true if a new mapping is being added but it hasn't yet updated + // all the ptes for the pages that it maps. + TPte pte = *aPtePtr; + if (pte == KPteUnallocatedEntry) + return; + + // clear type flags... + pagePhys &= ~KPageMask; + + SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pagePhys); + if (pi) + { + SPageInfo::TPagedState pagedState = pi->PagedState(); + if (pagedState != SPageInfo::EUnpaged) + { + // The page is demand paged. Only remap the page if it is pinned or is currently + // accessible but to the old physical page. + if (pagedState != SPageInfo::EPagedPinned && + (Mmu::IsPteInaccessible(pte) || (pte^pagePhys) < TPte(KPageSize))) + return; + if (!pi->IsDirty()) + { + // Ensure that the page is mapped as read only to prevent pages being marked dirty + // by page moving despite not having been written to + Mmu::MakePteInaccessible(aBlankPte, EFalse); + } + } + } + + // Map the page in the page array entry as this is always the physical + // page that the memory object's page should be mapped to. + pte = pagePhys|aBlankPte; + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + + +/** +Modify page table entries (PTEs) so they no longer map any memory pages. + +@param aPtePtr Pointer into a page table for the PTE of the first page. +@param aCount The number of pages to modify. + +@return False, if the page table no longer maps any entries and may be freed. + True otherwise, to indicate that the page table is still needed. + +@pre #MmuLock held. +@post #MmuLock held and has not been released by this function. +*/ +TBool Mmu::UnmapPages(TPte* const aPtePtr, TUint aCount) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aCount); + + TUint count = 0; + if(aCount==1) + { + if(*aPtePtr==KPteUnallocatedEntry) + return true; // page already unmapped + + // unmap page... + ++count; + TPte pte = KPteUnallocatedEntry; + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + else + { + // check we are only updating a single page table... + __NK_ASSERT_DEBUG(((TLinAddr(aPtePtr)^TLinAddr(aPtePtr+aCount-1))>>KPageTableShift)==0); + + // unmap pages... + TPte* pPte = aPtePtr; + TPte* pPteEnd = aPtePtr+aCount; + do + { + if(*pPte!=KPteUnallocatedEntry) + { + // unmap page... + ++count; + TPte pte = KPteUnallocatedEntry; + TRACE2(("!PTE %x=%x",pPte,pte)); + *pPte = pte; + } + } + while(++pPteDecPageCount(count); + TRACE2(("pt %x page count=%d",TLinAddr(aPtePtr)&~KPageTableMask,count)); + __NK_ASSERT_DEBUG(pti->CheckPageCount()); + + // see if page table needs freeing... + TUint keepPt = count | pti->PermanenceCount(); + + return keepPt; + } + + +/** +Modify page table entries (PTEs) so they no longer map the given memory pages. +Entries are only updated if the current state of the corresponding page +is 'decommitted' i.e. RPageArray::TargetStateIsDecommitted returns true. + +@param aPtePtr Pointer into a page table for the PTE of the first page. +@param aCount The number of pages to modify. +@param aPages Pointer to the entry for the first page in a memory object's #RPageArray. + Each entry contains the physical address of a page together with its + current state (RPageArray::TState). + +@return False, if the page table no longer maps any entries and may be freed. + True otherwise, to indicate that the page table is still needed. + +@pre #MmuLock held. +@post #MmuLock held and has not been released by this function. +*/ +TBool Mmu::UnmapPages(TPte* const aPtePtr, TUint aCount, TPhysAddr* aPages) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aCount); + + TUint count = 0; + if(aCount==1) + { + if(*aPtePtr==KPteUnallocatedEntry) + return true; // page already unmapped + + if(!RPageArray::TargetStateIsDecommitted(*aPages)) + return true; // page has been reallocated + + // unmap page... + ++count; + TPte pte = KPteUnallocatedEntry; + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + else + { + // check we are only updating a single page table... + __NK_ASSERT_DEBUG(((TLinAddr(aPtePtr)^TLinAddr(aPtePtr+aCount-1))>>KPageTableShift)==0); + + // unmap pages... + TPte* pPte = aPtePtr; + TPte* pPteEnd = aPtePtr+aCount; + do + { + if(RPageArray::TargetStateIsDecommitted(*aPages++) && *pPte!=KPteUnallocatedEntry) + { + // unmap page... + ++count; + TPte pte = KPteUnallocatedEntry; + TRACE2(("!PTE %x=%x",pPte,pte)); + *pPte = pte; + } + } + while(++pPteDecPageCount(count); + TRACE2(("pt %x page count=%d",TLinAddr(aPtePtr)&~KPageTableMask,count)); + __NK_ASSERT_DEBUG(pti->CheckPageCount()); + + // see if page table needs freeing... + TUint keepPt = count | pti->PermanenceCount(); + + return keepPt; + } + + +/** +Modify page table entries (PTEs) so the given memory pages are not accessible. +Entries are only updated if the current state of the corresponding page +is RPageArray::ERestrictingNA. + +@param aPtePtr Pointer into a page table for the PTE of the first page. +@param aCount The number of pages to modify. +@param aPages Pointer to the entry for the first page in a memory object's #RPageArray. + Each entry contains the physical address of a page together with its + current state (RPageArray::TState). + +@pre #MmuLock held. +@post #MmuLock held and has not been released by this function. +*/ +void Mmu::RestrictPagesNA(TPte* const aPtePtr, TUint aCount, TPhysAddr* aPages) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aCount); + + if(aCount==1) + { + TPhysAddr page = *aPages; + TPte pte = *aPtePtr; + RPageArray::TState state = RPageArray::State(page); + if(state != RPageArray::ERestrictingNA && state != RPageArray::EMoving) + return; // page no longer needs restricting + + if(pte==KPteUnallocatedEntry) + return; // page gone + + // restrict page... + pte = Mmu::MakePteInaccessible(pte,false); + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + else + { + // check we are only updating a single page table... + __NK_ASSERT_DEBUG(((TLinAddr(aPtePtr)^TLinAddr(aPtePtr+aCount-1))>>KPageTableShift)==0); + + // restrict pages... + TPte* pPte = aPtePtr; + TPte* pPteEnd = aPtePtr+aCount; + do + { + TPhysAddr page = *aPages++; + TPte pte = *pPte++; + if(RPageArray::State(page)==RPageArray::ERestrictingNA && pte!=KPteUnallocatedEntry) + { + pte = Mmu::MakePteInaccessible(pte,false); + TRACE2(("!PTE %x=%x",pPte-1,pte)); + pPte[-1] = pte; + } + } + while(pPte=TPte(KPageSize) && !Mmu::IsPteInaccessible(pte) && + !Mmu::IsPteReadOnly(pte)) + { + // Page has been mapped before but the physical address is different + // and the page hasn't been moved as it is not inaccessible. + Kern::Printf("Mmu::PageInPages already mapped %x->%x",page,pte); + __NK_ASSERT_DEBUG(0); + } + } +#endif + if(!Mmu::IsPteMoreAccessible(aBlankPte,pte)) + return true; // return true to keep page table (it already had at least page mapped) + + // remap page with new increased permissions... + if(pte==KPteUnallocatedEntry) + count = 1; // we'll be adding a new pte entry, count it + if(!Mmu::IsPteReadOnly(aBlankPte)) + ThePager.SetWritable(*SPageInfo::FromPhysAddr(page)); + pte = (page&~KPageMask)|aBlankPte; + TRACE2(("!PTE %x=%x",aPtePtr,pte)); + *aPtePtr = pte; + + // clean cache... + CacheMaintenance::SinglePteUpdated((TLinAddr)aPtePtr); + } + else + { + // check we are only updating a single page table... + __NK_ASSERT_DEBUG(((TLinAddr(aPtePtr)^TLinAddr(aPtePtr+aCount-1))>>KPageTableShift)==0); + + // map pages... + TPte* pPte = aPtePtr; + TPte* pPteEnd = aPtePtr+aCount; + do + { + // map page... + TPhysAddr page = *aPages++; + TPte pte = *pPte++; + if(RPageArray::TargetStateIsCommitted(page)) + { +#ifdef _DEBUG + if(pte!=KPteUnallocatedEntry) + { + if ((pte^page)>=TPte(KPageSize) && !Mmu::IsPteInaccessible(pte) && + !Mmu::IsPteReadOnly(pte)) + { + // Page has been mapped before but the physical address is different + // and the page hasn't been moved as it is not inaccessible. + Kern::Printf("Mmu::PageInPages already mapped %x->%x",page,pte); + __NK_ASSERT_DEBUG(0); + } + } +#endif + if(Mmu::IsPteMoreAccessible(aBlankPte,pte)) + { + // remap page with new increased permissions... + if(pte==KPteUnallocatedEntry) + ++count; // we'll be adding a new pte entry, count it + if(!Mmu::IsPteReadOnly(aBlankPte)) + ThePager.SetWritable(*SPageInfo::FromPhysAddr(page)); + pte = (page&~KPageMask)|aBlankPte; + TRACE2(("!PTE %x=%x",pPte-1,pte)); + pPte[-1] = pte; + } + } + } + while(pPte!=pPteEnd); + + // clean cache... + CacheMaintenance::MultiplePtesUpdated((TLinAddr)aPtePtr,(TLinAddr)pPte-(TLinAddr)aPtePtr); + } + +done: + // update page counts... + SPageTableInfo* pti = SPageTableInfo::FromPtPtr(aPtePtr); + count = pti->IncPageCount(count); + TRACE2(("pt %x page count=%d",TLinAddr(aPtePtr)&~KPageTableMask,pti->PageCount())); + __NK_ASSERT_DEBUG(pti->CheckPageCount()); + + // see if page table needs freeing... + TUint keepPt = count | pti->PermanenceCount(); + + // rejuvenate demand paged page tables... + ThePager.RejuvenatePageTable(aPtePtr); + + return keepPt; + } + + +// +// CodeModifier +// + +#ifdef __DEBUGGER_SUPPORT__ + +void DoWriteCode(TUint32* aAddress, TUint32 aValue); + +#ifdef __SMP__ + +extern "C" void __e32_instruction_barrier(); + +class TCodeModifierBroadcast : public TGenericIPI + { +public: + TCodeModifierBroadcast(TUint32* aAddress, TUint32 aValue); + static void Isr(TGenericIPI*); + void Go(); +public: + TUint32* iAddress; + TUint32 iValue; + volatile TInt iFlag; + }; + +TCodeModifierBroadcast::TCodeModifierBroadcast(TUint32* aAddress, TUint32 aValue) + : iAddress(aAddress), iValue(aValue), iFlag(0) + { + } + +void TCodeModifierBroadcast::Isr(TGenericIPI* aPtr) + { + TCodeModifierBroadcast& a = *(TCodeModifierBroadcast*)aPtr; + while (!__e32_atomic_load_acq32(&a.iFlag)) + __chill(); +#ifdef __BROADCAST_CACHE_MAINTENANCE__ + CacheMaintenance::CodeChanged((TLinAddr)a.iAddress, sizeof (TInt), CacheMaintenance::ECodeModifier); // need to do separate Clean-D, Purge-I on each core +#else + __e32_instruction_barrier(); // synchronize instruction execution +#endif + } + +void TCodeModifierBroadcast::Go() + { + NKern::Lock(); + QueueAllOther(&Isr); + WaitEntry(); // wait for other cores to stop + DoWriteCode(iAddress, iValue); + iFlag = 1; + __e32_instruction_barrier(); // synchronize instruction execution + WaitCompletion(); // wait for other cores to resume + NKern::Unlock(); + } +#endif + +/** +@pre Calling thread must be in critical section +@pre CodeSeg mutex held +*/ +TInt CodeModifier::SafeWriteCode(DProcess* aProcess, TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue) + { + __ASSERT_CRITICAL; + Mmu& m=TheMmu; + RamAllocLock::Lock(); + MmuLock::Lock(); + __UNLOCK_GUARD_START(MmuLock); + + // Check aProcess is still alive by opening a reference on its os asid. + TInt osAsid = ((DMemModelProcess*)aProcess)->TryOpenOsAsid(); + if (osAsid < 0) + { + __KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - zombie process")); + __UNLOCK_GUARD_END(MmuLock); + MmuLock::Unlock(); + RamAllocLock::Unlock(); + return KErrBadDescriptor; + } + + // Find physical address of the page, the breakpoint belongs to + TPhysAddr physAddr = Mmu::LinearToPhysical(aAddress, osAsid); + __KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - PA:%x", physAddr)); + + + if (physAddr==KPhysAddrInvalid) + { + __KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - invalid VA")); + __UNLOCK_GUARD_END(MmuLock); + MmuLock::Unlock(); + RamAllocLock::Unlock(); + // The os asid is no longer required. + ((DMemModelProcess*)aProcess)->CloseOsAsid(); + return KErrBadDescriptor; + } + + // Temporary map physical page + TLinAddr tempAddr = m.MapTemp(physAddr&~KPageMask, aAddress>>KPageShift); + tempAddr |= aAddress & KPageMask; + __KTRACE_OPT(KDEBUGGER,Kern::Printf("CodeModifier::SafeWriteCode - tempAddr:%x",tempAddr)); + + TInt r = KErrBadDescriptor; + TUint32* ptr = (TUint32*)(tempAddr&~3); + TUint32 oldWord; + + if(Kern::SafeRead(ptr,&oldWord,sizeof(oldWord))==0 // safely read the original value... + && Kern::SafeWrite(ptr,&oldWord,sizeof(oldWord))==0 ) // and write it back + { + // We have successfully probed the memory by reading and writing to it + // so we assume it is now safe to access without generating exceptions. + // If this is wrong it will kill the system horribly. + + TUint32 newWord; + TUint badAlign; + TUint shift = (aAddress&3)*8; + + switch(aSize) + { + case 1: // 1 byte value + badAlign = 0; + *(TUint8*)aOldValue = oldWord>>shift; + newWord = (oldWord&~(0xff<>shift; + newWord = (oldWord&~(0xffff<CloseOsAsid(); + return r; + } + +/** +@pre Calling thread must be in critical section +@pre CodeSeg mutex held +*/ +void DoWriteCode(TUint32* aAddress, TUint32 aValue) + { + // We do not want to be interrupted by e.g. ISR that will run altered code before IMB-Range. + // Therefore, copy data and clean/invalidate caches with interrupts disabled. + TInt irq = NKern::DisableAllInterrupts(); + *aAddress = aValue; + CacheMaintenance::CodeChanged((TLinAddr)aAddress, sizeof(TUint32), CacheMaintenance::ECodeModifier); + NKern::RestoreInterrupts(irq); + } + +#endif //__DEBUGGER_SUPPORT__ + + + +// +// Virtual pinning +// + +TInt M::CreateVirtualPinObject(TVirtualPinObject*& aPinObject) + { + aPinObject = (TVirtualPinObject*)new DVirtualPinMapping; + return aPinObject != NULL ? KErrNone : KErrNoMemory; + } + +TInt M::PinVirtualMemory(TVirtualPinObject* aPinObject, TLinAddr aStart, TUint aSize, DThread* aThread) + { + NKern::ThreadEnterCS(); + TUint offsetInMapping; + TUint mapInstanceCount; + DMemoryMapping* mapping = MM::FindMappingInThread( (DMemModelThread*)aThread, + aStart, + aSize, + offsetInMapping, + mapInstanceCount); + TInt r = KErrBadDescriptor; + if (mapping) + { + TInt count = ((aStart & KPageMask) + aSize + KPageMask) >> KPageShift; + if(mapping->IsPinned()) + { + // Mapping for specified virtual address is pinned so we don't need to + // do anything. Also, we can't safely pin the memory in this case + // anyway, as pinned mappings may move between memory objects + r = KErrNone; + } + else + { + MmuLock::Lock(); + DMemoryObject* memory = mapping->Memory(); + if (mapInstanceCount != mapping->MapInstanceCount() || + !memory || !memory->IsDemandPaged()) + { + // mapping has been reused, no memory, or it's not paged, so no need to pin... + MmuLock::Unlock(); + r = KErrNone; + } + else + { + // paged memory needs pinning... + // Open a reference on the memory so it doesn't get deleted. + memory->Open(); + MmuLock::Unlock(); + + TUint startInMemory = (offsetInMapping >> KPageShift) + mapping->iStartIndex; + r = ((DVirtualPinMapping*)aPinObject)->Pin( memory, startInMemory, count, mapping->Permissions(), + mapping, mapInstanceCount); + memory->Close(); + } + } + mapping->Close(); + } + NKern::ThreadLeaveCS(); + + return r; + } + +TInt M::CreateAndPinVirtualMemory(TVirtualPinObject*& aPinObject, TLinAddr aStart, TUint aSize) + { + aPinObject = 0; + NKern::ThreadEnterCS(); + TUint offsetInMapping; + TUint mapInstanceCount; + DMemoryMapping* mapping = MM::FindMappingInThread( (DMemModelThread*)&Kern::CurrentThread(), + aStart, + aSize, + offsetInMapping, + mapInstanceCount); + TInt r = KErrBadDescriptor; + if (mapping) + { + TInt count = ((aStart & KPageMask) + aSize + KPageMask) >> KPageShift; + if(mapping->IsPinned()) + { + // Mapping for specified virtual address is pinned so we don't need to + // do anything. Also, we can't safely pin the memory in this case + // anyway, as pinned mappings may move between memory objects + r = KErrNone; + } + else + { + MmuLock::Lock(); + DMemoryObject* memory = mapping->Memory(); + if (mapInstanceCount != mapping->MapInstanceCount() || + !memory || !memory->IsDemandPaged()) + { + // mapping has been reused, no memory, or it's not paged, so no need to pin... + MmuLock::Unlock(); + r = KErrNone; + } + else + {// The memory is demand paged so create a pin object and pin it. + // Open a reference on the memory so it doesn't get deleted. + memory->Open(); + MmuLock::Unlock(); + r = CreateVirtualPinObject(aPinObject); + if (r == KErrNone) + { + TUint startInMemory = (offsetInMapping >> KPageShift) + mapping->iStartIndex; + r = ((DVirtualPinMapping*)aPinObject)->Pin( memory, startInMemory, count, mapping->Permissions(), + mapping, mapInstanceCount); + if (r != KErrNone) + {// Failed to pin the memory so pin object is not required. + DestroyVirtualPinObject(aPinObject); + } + } + memory->Close(); + } + } + mapping->Close(); + } + NKern::ThreadLeaveCS(); + + return r; + } + +void M::UnpinVirtualMemory(TVirtualPinObject* aPinObject) + { + DVirtualPinMapping* mapping = (DVirtualPinMapping*)aPinObject; + if (mapping->IsAttached()) + mapping->Unpin(); + } + +void M::DestroyVirtualPinObject(TVirtualPinObject*& aPinObject) + { + DVirtualPinMapping* mapping = (DVirtualPinMapping*)__e32_atomic_swp_ord_ptr(&aPinObject, 0); + if (mapping) + { + if (mapping->IsAttached()) + mapping->Unpin(); + mapping->AsyncClose(); + } + } + +TInt M::CreatePhysicalPinObject(TPhysicalPinObject*& aPinObject) + { + aPinObject = (TPhysicalPinObject*)new DPhysicalPinMapping; + return aPinObject != NULL ? KErrNone : KErrNoMemory; + } + +// +// Physical pinning +// + +TInt M::PinPhysicalMemory(TPhysicalPinObject* aPinObject, TLinAddr aStart, TUint aSize, TBool aReadOnly, + TPhysAddr& aAddress, TPhysAddr* aPages, TUint32& aMapAttr, TUint& aColour, DThread* aThread) + { + NKern::ThreadEnterCS(); + TUint offsetInMapping; + TUint mapInstanceCount; + DMemoryMapping* mapping = MM::FindMappingInThread( (DMemModelThread*)aThread, + aStart, + aSize, + offsetInMapping, + mapInstanceCount); + TInt r = KErrBadDescriptor; + if (mapping) + { + TInt count = ((aStart & KPageMask) + aSize + KPageMask) >> KPageShift; + + MmuLock::Lock(); + DMemoryObject* memory = mapping->Memory(); + if (mapInstanceCount == mapping->MapInstanceCount() && memory) + { + memory->Open(); + MmuLock::Unlock(); + + TUint startInMemory = (offsetInMapping >> KPageShift) + mapping->iStartIndex; + TMappingPermissions permissions = aReadOnly ? ESupervisorReadOnly : ESupervisorReadWrite; + r = ((DPhysicalPinMapping*)aPinObject)->Pin(memory, startInMemory, count, permissions); + if (r == KErrNone) + { + r = ((DPhysicalPinMapping*)aPinObject)->PhysAddr(0, count, aAddress, aPages); + if (r>=KErrNone) + { + r = KErrNone; //Do not report discontiguous memory in return value. + const TMappingAttributes2& mapAttr2 = + MM::LegacyMappingAttributes(memory->Attributes(), mapping->Permissions()); + *(TMappingAttributes2*)&aMapAttr = mapAttr2; + } + else + UnpinPhysicalMemory(aPinObject); + } + memory->Close(); + } + else // mapping has been reused or no memory... + { + MmuLock::Unlock(); + } + mapping->Close(); + } + NKern::ThreadLeaveCS(); + aColour = (aStart >>KPageShift) & KPageColourMask; + return r; + } + +void M::UnpinPhysicalMemory(TPhysicalPinObject* aPinObject) + { + DPhysicalPinMapping* mapping = (DPhysicalPinMapping*)aPinObject; + if (mapping->IsAttached()) + mapping->Unpin(); + } + +void M::DestroyPhysicalPinObject(TPhysicalPinObject*& aPinObject) + { + DPhysicalPinMapping* mapping = (DPhysicalPinMapping*)__e32_atomic_swp_ord_ptr(&aPinObject, 0); + if (mapping) + { + if (mapping->IsAttached()) + mapping->Unpin(); + mapping->AsyncClose(); + } + } + + + +// +// Cache sync operations +// + +//@pre As for MASK_THREAD_STANDARD +void Mmu::SyncPhysicalMemoryBeforeDmaWrite(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + //Jump over the pages we do not have to sync + aPages += aOffset>>KPageShift; + aOffset &=KPageMask; + aColour = (aColour + (aOffset>>KPageShift)) & KPageColourMask; + + //Calculate page table entry for the temporary mapping. + TUint pteType = PteType(ESupervisorReadWrite,true); + TMappingAttributes2 mapAttr2(aMapAttr); + TPte pte = Mmu::BlankPte((TMemoryAttributes)mapAttr2.Type(), pteType); + + while (aSize) //A single pass of loop operates within page boundaries. + { + TUint sizeInLoopPass = Min (KPageSize, aOffset+aSize) - aOffset; //The size of the region in this pass. + + NKern::ThreadEnterCS(); + Kern::MutexWait(*iPhysMemSyncMutex); + + TLinAddr linAddr = iPhysMemSyncTemp.Map(*aPages, aColour, pte); + CacheMaintenance::MakeCPUChangesVisible(linAddr+aOffset, sizeInLoopPass, aMapAttr, *aPages+aOffset); + iPhysMemSyncTemp.Unmap(); + + Kern::MutexSignal(*iPhysMemSyncMutex); + NKern::ThreadLeaveCS(); + + aSize-=sizeInLoopPass; // Remaining bytes to sync + aOffset=0; // In all the pages after the first, sync will always start with zero offset. + aPages++; // Point to the next page + aColour = (aColour+1) & KPageColourMask; + } + } + +//@pre As for MASK_THREAD_STANDARD +void Mmu::SyncPhysicalMemoryBeforeDmaRead(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + //Jump over the pages we do not have to sync + aPages += aOffset>>KPageShift; + aOffset &=KPageMask; + aColour = (aColour + (aOffset>>KPageShift)) & KPageColourMask; + + //Calculate page table entry for the temporary mapping. + TUint pteType = PteType(ESupervisorReadWrite,true); + TMappingAttributes2 mapAttr2(aMapAttr); + TPte pte = Mmu::BlankPte((TMemoryAttributes)mapAttr2.Type(), pteType); + + while (aSize) //A single pass of loop operates within page boundaries. + { + TUint sizeInLoopPass = Min (KPageSize, aOffset+aSize) - aOffset; //The size of the region in this pass. + + NKern::ThreadEnterCS(); + Kern::MutexWait(*iPhysMemSyncMutex); + + TLinAddr linAddr = iPhysMemSyncTemp.Map(*aPages, aColour, pte); + CacheMaintenance::PrepareMemoryForExternalWrites(linAddr+aOffset, sizeInLoopPass, aMapAttr, *aPages+aOffset); + iPhysMemSyncTemp.Unmap(); + + Kern::MutexSignal(*iPhysMemSyncMutex); + NKern::ThreadLeaveCS(); + + aSize-=sizeInLoopPass; // Remaining bytes to sync + aOffset=0; // In all the pages after the first, sync will always start with zero offset. + aPages++; // Point to the next page + aColour = (aColour+1) & KPageColourMask; + } + } + +//@pre As for MASK_THREAD_STANDARD +void Mmu::SyncPhysicalMemoryAfterDmaRead(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + //Jump over the pages we do not have to sync + aPages += aOffset>>KPageShift; + aOffset &=KPageMask; + aColour = (aColour + (aOffset>>KPageShift)) & KPageColourMask; + + //Calculate page table entry for the temporary mapping. + TUint pteType = PteType(ESupervisorReadWrite,true); + TMappingAttributes2 mapAttr2(aMapAttr); + TPte pte = Mmu::BlankPte((TMemoryAttributes)mapAttr2.Type(), pteType); + + while (aSize) //A single pass of loop operates within page boundaries. + { + TUint sizeInLoopPass = Min (KPageSize, aOffset+aSize) - aOffset; //The size of the region in this pass. + + NKern::ThreadEnterCS(); + Kern::MutexWait(*iPhysMemSyncMutex); + + TLinAddr linAddr = iPhysMemSyncTemp.Map(*aPages, aColour, pte); + CacheMaintenance::MakeExternalChangesVisible(linAddr+aOffset, sizeInLoopPass, aMapAttr, *aPages+aOffset); + iPhysMemSyncTemp.Unmap(); + + Kern::MutexSignal(*iPhysMemSyncMutex); + NKern::ThreadLeaveCS(); + + aSize-=sizeInLoopPass; // Remaining bytes to sync + aOffset=0; // In all the pages after the first, sync will always start with zero offset. + aPages++; // Point to the next page + aColour = (aColour+1) & KPageColourMask; + } + } + +EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaWrite(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaWrite"); + TheMmu.SyncPhysicalMemoryBeforeDmaWrite(aPages, aColour, aOffset, aSize, aMapAttr); + return KErrNone; + } + +EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaRead(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaRead"); + TheMmu.SyncPhysicalMemoryBeforeDmaRead(aPages, aColour, aOffset, aSize, aMapAttr); + return KErrNone; + } + +EXPORT_C TInt Cache::SyncPhysicalMemoryAfterDmaRead(TPhysAddr* aPages, TUint aColour, TUint aOffset, TUint aSize, TUint32 aMapAttr) + { + CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryAfterDmaRead"); + TheMmu.SyncPhysicalMemoryAfterDmaRead(aPages, aColour, aOffset, aSize, aMapAttr); + return KErrNone; + }