diff -r 000000000000 -r a41df078684a kernel/eka/memmodel/epoc/flexible/mmu/mrom.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mrom.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1100 @@ +// 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 +#include +#include "mm.h" +#include "mmu.h" +#include "mrom.h" +#include "mpager.h" +#include "mmanager.h" +#include "mobject.h" +#include "mmapping.h" +#include "maddrcont.h" +#include "mptalloc.h" +#include "mlargemappings.h" + +#include "cache_maintenance.inl" + + +/** +Class representing the resources allocated for a ROM shadow page. + +A shadow page is a page of RAM which is mapped by the MMU to replace +a prior existing page at a particular virtual address. +*/ +class DShadowPage : public DVirtualPinMapping + { +public: + /** + Create a new #DShadowPage to shadow a specified memory page. + + On success, #iOriginalPage holds the physical address of the original page + and #iNewPage the physical address of the newly allocated RAM page; the + contents of this are a copy of the original. + + No MMU entries for the shadow page are changed - it is the responsibility + of the caller to handle this. However, the new #DShadowPage object will + have pinned the page table used by \a aMapping which maps the page being + shadowed, prevent demand paging from discarding any modifications made to + this. + + @param aMemory The memory object whose memory is to be shadowed. + @param aIndex Page index, within the memory, of the page to shadow. + @param aMapping A memory mapping which currently maps the page to be + shadowed. + + @return The newly created DShadowPage or the null pointer if there was + insufficient memory. + */ + static DShadowPage* New(DMemoryObject* aMemory, TUint aIndex, DMemoryMappingBase* aMapping); + + /** + Free the allocated shadow page (#iNewPage) and unpin any pages table which + was pinned, then free this shadow page object. + + The called of this function must ensure that all references to the shadow + RAM page have been removed from any MMU mappings. + */ + void Destroy(); + +private: + DShadowPage(); + ~DShadowPage(); + + /** + Second phase constructor. For arguments, see #New. + */ + TInt Construct(DMemoryObject* aMemory, TUint aIndex, DMemoryMappingBase* aMapping); + +public: + /** + The physical address of the original page being shadowed. + */ + TPhysAddr iOriginalPage; + + /** + The physical address of the allocated shadow page. + */ + TPhysAddr iNewPage; + }; + + +/** +Specialised manager for the memory object representing the system ROM. +This handles demand paging of the ROM contents if it is not stored in a memory +device capable of execute-in-place random access. E.g. when stored in NAND +flash. +*/ +class DRomMemoryManager : public DPagedMemoryManager + { +public: + DRomMemoryManager(); + + /** + Allocate a shadow page for the specified ROM address. + + Shadow pages are pages of RAM which are mapped by the MMU so that + they replace the original ROM memory. The contents of a shadow page + are initially the same as the ROM they replace, but may be modified with + #CopyToShadowMemory. + + @param aRomAddr An virtual address which lies within the ROM. + + @return KErrNone if successful, + KErrAlreadyExists if the specified address already has a show page, + otherwise one of the system wide error codes. + */ + TInt AllocShadowPage(TLinAddr aRomAddr); + + /** + Free a shadow page previously allocated with #AllocShadowPage. + + The original ROM memory page is again mapped at the specified address. + + @param aRomAddr An virtual address which lies within the ROM. + + @return KErrNone if successful, + otherwise one of the system wide error codes. + */ + TInt FreeShadowPage(TLinAddr aRomAddr); + + /** + Copy data into a shadow page, modifying its contents. + + @param aDst An virtual address which lies within the ROM for which a shadow + page has previously been allocated with #AllocShadowPage. + @param aSrc The start address of the data to copy to \a aDst. + @param aSize The size, in bytes, of the data to copy. + + @return KErrNone if successful, + KErrNotFound if the specified address didn't have a shadow page, + otherwise one of the system wide error codes. + */ + TInt CopyToShadowMemory(TLinAddr aDst, TLinAddr aSrc, TUint32 aSize); + +protected: + + // from DPagedMemoryManager... + virtual TInt PageInPinnedDone(DMemoryObject* aMemory, TUint aIndex, SPageInfo* aPageInfo, TPhysAddr* aPageArrayEntry, TPinArgs& aPinArgs); + +private: + // from DMemoryManager... + virtual void Destruct(DMemoryObject* aMemory); + virtual TInt HandleFault( DMemoryObject* aMemory, TUint aIndex, DMemoryMapping* aMapping, + TUint aMapInstanceCount, TUint aAccessPermissions); + virtual TInt Pin(DMemoryObject* aMemory, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs); + virtual void Unpin(DMemoryObject* aMemory, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs); + + // methods inherited from DPagedMemoryManager + + /** + @copydoc DPagedMemoryManager::Init3 + This acts as a second phase constructor for the manager which + creates the memory objects and mappings to represent the ROM. + */ + virtual void Init3(); + + virtual TInt InstallPagingDevice(DPagingDevice* aDevice); + virtual TInt AcquirePageReadRequest(DPageReadRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount); + virtual TInt ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest); + virtual TBool IsAllocated(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + virtual void DoUnpin(DMemoryObject* aMemory, TUint aIndex, TUint aCount, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs); + + /** + Acquire the mutex used to protect shadow page allocation. + */ + void ShadowLock(); + + /** + Release the mutex used to protect shadow page allocation. + */ + void ShadowUnlock(); + +private: + /** + The ROM paging device which was passed to #InstallPagingDevice. + */ + DPagingDevice* iDevice; + + /** + The memory object containing the ROM. + */ + DMemoryObject* iRomMemory; + + /** + The memory mapping which maps the ROM into a global visible virtual address. + */ + DMemoryMapping* iRomMapping; + + /** + The virtual address for the start of the ROM in the global memory region. + */ + TLinAddr iBase; + + /** + The size, in bytes, of the ROM image. + This may not be an exact multiple of a page size. + */ + TUint iSize; + + /** + The size, in pages, of the ROM image. + */ + TUint iSizeInPages; + + /** + The offset from the ROM start, in bytes, for the region of the + ROM which is demand paged. + */ + TUint iPagedStart; + + /** + The size, in bytes, for the region of the ROM which is demand paged. + */ + TUint iPagedSize; + + /** + The address within the ROM for the ROM page index. + @see TRomHeader::iRomPageIndex. + */ + SRomPageInfo* iRomPageIndex; + + /** + The mutex used to protect shadow page allocation. + */ + DMutex* iShadowLock; + + /** + Container for all allocated DShadowPage objects. + */ + RAddressedContainer iShadowPages; + +#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__ + TInt iOriginalRomPageCount; + TPhysAddr* iOriginalRomPages; + friend void RomOriginalPages(TPhysAddr*& aPages, TUint& aPageCount); +#endif + + friend TBool IsUnpagedRom(TLinAddr aBase, TUint aSize); + +public: + /** + The single instance of this manager class. + */ + static DRomMemoryManager TheManager; + }; + + +DRomMemoryManager DRomMemoryManager::TheManager; +DPagedMemoryManager* TheRomMemoryManager = &DRomMemoryManager::TheManager; + + +const TInt KMutexOrdRomMemory = KMutexOrdPageIn+1; + + +#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__ +/** +For use by the emulated paging device to get the location and size of the ROM. + +@param aPages A reference to store a pointer to an array of the physical addresses of each ROM page. +@param aPageCount A reference to store the number of rom pages. +*/ +void RomOriginalPages(TPhysAddr*& aPages, TUint& aPageCount) + { + aPages = DRomMemoryManager::TheManager.iOriginalRomPages; + aPageCount = DRomMemoryManager::TheManager.iOriginalRomPageCount; + } + +#endif + + +TBool IsUnpagedRom(TLinAddr aBase, TUint aSize) + { + TUint offset = aBase-DRomMemoryManager::TheManager.iBase; + TUint limit = DRomMemoryManager::TheManager.iPagedStart; + if(offset>=limit) + return false; + offset += aSize; + if(offset>limit || offsetIncPageCount(KPageTableSize/sizeof(TPte)); + (void)count; + TRACE2(("pt %x page count=%d",pt,pti->PageCount())); + __NK_ASSERT_DEBUG(pti->CheckPageCount()); + + // swap pde entry to point to new page table... + pde |= Mmu::PageTablePhysAddr(pt); + TRACE2(("!PDE %x=%x",pPde,pde)); + *pPde = pde; + SinglePdeUpdated(pPde); + InvalidateTLB(); + + // done... + MmuLock::Unlock(); + ::PageTables.Unlock(); + TRACE(("PagifyChunk returns %d",KErrNone)); + return KErrNone; + } + + +void UnmapROM(TLinAddr aStart, TLinAddr aEnd) + { + TRACEB(("UnmapROM 0x%08x..0x%08x",aStart,aEnd)); + + TLinAddr p = aStart; + if(p>=aEnd) + return; + + PagifyChunk(p); + + MmuLock::Lock(); // hold MmuLock for long time, shouldn't matter as this is only done during boot + + TPte* pPte = Mmu::PtePtrFromLinAddr(p,KKernelOsAsid); + __NK_ASSERT_ALWAYS(pPte); + while(piPagedStart && iPagedStart+iPagedSize<=iSize); + +#ifdef __SUPPORT_DEMAND_PAGING_EMULATION__ + // get physical addresses of ROM pages... + iOriginalRomPageCount = iSizeInPages; + iOriginalRomPages = new TPhysAddr[iOriginalRomPageCount]; + __NK_ASSERT_ALWAYS(iOriginalRomPages); + MmuLock::Lock(); // hold MmuLock for long time, shouldn't matter as this is only done during boot + TInt i; + for(i=0; i>KPageShift,EMemoryAttributeStandard,flags); + __NK_ASSERT_ALWAYS(iRomMemory); + TInt r = MM::MemoryClaimInitialPages(iRomMemory,iBase,committedSize,EUserExecute,false,true); + __NK_ASSERT_ALWAYS(r==KErrNone); + r = iRomMemory->iPages.Alloc(committedSize>>KPageShift,(chunkSize-committedSize)>>KPageShift); + __NK_ASSERT_ALWAYS(r==KErrNone); + + // create mapping for ROM... + r = MM::MappingNew(iRomMapping, iRomMemory, EUserExecute, KKernelOsAsid, EMappingCreateExactVirtual, iBase); + __NK_ASSERT_ALWAYS(r==KErrNone); + __NK_ASSERT_ALWAYS(iRomMapping->IsLarge()); + + // Set the paging device to be uninstalled, i.e. NULL. + iDevice = NULL; + + _LIT(KRomMemoryLockName,"RomMemory"); + r = K::MutexCreate(iShadowLock, KRomMemoryLockName, NULL, EFalse, KMutexOrdRomMemory); + __NK_ASSERT_ALWAYS(r==KErrNone); + MM::MemorySetLock(iRomMemory,iShadowLock); + } + + +TInt DRomMemoryManager::InstallPagingDevice(DPagingDevice* aDevice) + { + TRACEB(("DRomMemoryManager::InstallPagingDevice(0x%08x)",aDevice)); + + if(!iPagedStart) + { + TRACEB(("ROM is not paged")); + return KErrNone; + } + + TAny* null = 0; + if(!__e32_atomic_cas_ord_ptr(&iDevice, &null, aDevice)) // set iDevice=aDevice if it was originally 0 + { + // ROM paging device already registered... + TRACEB(("DRomMemoryManager::InstallPagingDevice returns ALREADY EXISTS!")); + return KErrAlreadyExists; + } + + __e32_atomic_ior_ord32(&K::MemModelAttributes, (TUint32)EMemModelAttrRomPaging); + + return KErrNone; + } + + +TInt DRomMemoryManager::AcquirePageReadRequest(DPageReadRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount) + { + aRequest = iDevice->iRequestPool->AcquirePageReadRequest(aMemory,aIndex,aCount); + return KErrNone; + } + + +void DRomMemoryManager::Destruct(DMemoryObject* aMemory) + { + __NK_ASSERT_DEBUG(0); + } + + +TInt DRomMemoryManager::ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest) + { + __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + + TLinAddr linAddr = aRequest->MapPages(aIndex,aCount,aPages); + TInt r = KErrNone; + + const TInt readUnitShift = iDevice->iReadUnitShift; + + for(; aCount; ++aIndex, --aCount, linAddr+=KPageSize) + { + START_PAGING_BENCHMARK; + if(!iRomPageIndex) + { + // ROM not broken into pages, so just read it in directly. + // KPageShift > readUnitShift so page size is exact multiple of read + // units. Therefore it is ok to just shift offset and KPageSize + // by readUnitShift. + const TInt dataOffset = aIndex << KPageShift; + START_PAGING_BENCHMARK; + r = iDevice->Read( const_cast(&aRequest->iMessage), + linAddr, dataOffset >> readUnitShift, + KPageSize >> readUnitShift, DPagingDevice::EDriveRomPaging); + __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocated memory, therefore can't fail with KErrNoMemory + END_PAGING_BENCHMARK(EPagingBmReadMedia); + } + else + { + // Work out where data for page is located + SRomPageInfo* romPageInfo = iRomPageIndex + aIndex; + const TInt dataOffset = romPageInfo->iDataStart; + const TInt dataSize = romPageInfo->iDataSize; + if(!dataSize) + { + // empty page, fill it with 0xff... + memset((TAny*)linAddr, 0xff, KPageSize); + r = KErrNone; + } + else + { + __NK_ASSERT_ALWAYS(romPageInfo->iPagingAttributes & SRomPageInfo::EPageable); + + // Read data for page... + TThreadMessage* msg = const_cast(&aRequest->iMessage); + const TLinAddr buffer = aRequest->iBuffer; + const TUint readStart = dataOffset >> readUnitShift; + const TUint readSize = ((dataOffset + dataSize - 1) >> readUnitShift) - readStart + 1; + __NK_ASSERT_DEBUG((readSize << readUnitShift) <= (DPageReadRequest::EMaxPages << KPageShift)); + START_PAGING_BENCHMARK; + r = iDevice->Read(msg, buffer, readStart, readSize, DPagingDevice::EDriveRomPaging); + __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocated memory, therefore can't fail with KErrNoMemory + END_PAGING_BENCHMARK(EPagingBmReadMedia); + if(r==KErrNone) + { + // Decompress data, remembering that the data to decompress may be offset from + // the start of the data just read in, due to reads having to be aligned by + // readUnitShift. + const TLinAddr data = buffer + dataOffset - (readStart << readUnitShift); + __ASSERT_COMPILE(SRomPageInfo::ENoCompression==0); // decompress assumes this + r = Decompress(romPageInfo->iCompressionType, linAddr, KPageSize, data, dataSize); + if(r >= 0) + { + if (r != KPageSize) + __KTRACE_OPT(KPANIC, Kern::Printf("DRomMemoryManager::ReadPage: error decompressing page at %08x + %x: %d", dataOffset, dataSize, r)); + __NK_ASSERT_ALWAYS(r == KPageSize); + r = KErrNone; + } + } + else + __KTRACE_OPT(KPANIC, Kern::Printf("DRomMemoryManager::ReadPage: error reading media at %08x + %x: %d", dataOffset, dataSize, r)); + } + } + END_PAGING_BENCHMARK(EPagingBmReadRomPage); + + if(r!=KErrNone) + break; + } + + aRequest->UnmapPages(true); + + return r; + } + + +TBool DRomMemoryManager::IsAllocated(DMemoryObject* aMemory, TUint aIndex, TUint aCount) + { + // all pages in the ROM memory object are always allocated... + return true; + } + + +TInt DRomMemoryManager::HandleFault(DMemoryObject* aMemory, TUint aIndex, DMemoryMapping* aMapping, + TUint aMapInstanceCount, TUint aAccessPermissions) + { + __NK_ASSERT_DEBUG(aMemory==iRomMemory); + + TUint offset = aIndex*KPageSize; + if(offset=iPagedStart+iPagedSize) + return KErrAbort; + + return DPagedMemoryManager::HandleFault(aMemory, aIndex, aMapping, aMapInstanceCount, aAccessPermissions); + } + + +TInt DRomMemoryManager::Pin(DMemoryObject* aMemory, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs) + { + TRACE(("DRomMemoryManager::Pin %08x %08x", aMemory, aMapping)); + TUint index = aMapping->iStartIndex; + TUint endIndex = index+aMapping->iSizeInPages; + if(endIndex>iSizeInPages) + return KErrNotFound; + + TInt r = KErrNone; + TUint pagedIndex = iPagedStart>>KPageShift; + if(pagedIndex && pagedIndex= iPagedStart >> KPageShift); + + TInt r = DoPageInDone(aMemory,aIndex,aPageInfo,aPageArrayEntry,true); + + // Rom page can't be decommitted so this must succeed. + __NK_ASSERT_DEBUG(r >= 0); + + if (aPageInfo->Type() == SPageInfo::EShadow) + {// The page is being shadowed so pin the original page. + // This is safe as the original page was physically pinned when shadowed. + __NK_ASSERT_DEBUG(RPageArray::IsPresent(*aPageArrayEntry)); + aPageInfo = aPageInfo->GetOriginalPage(); + } + + ThePager.PagedInPinned(aPageInfo,aPinArgs); + + // check page assigned correctly... +#ifdef _DEBUG + if(RPageArray::IsPresent(*aPageArrayEntry)) + { + SPageInfo* pi = SPageInfo::FromPhysAddr(*aPageArrayEntry); + if (pi->Type() != SPageInfo::EShadow) + { + __NK_ASSERT_DEBUG(pi->Type() == SPageInfo::EManaged); + __NK_ASSERT_DEBUG(pi->Owner()==aMemory); + __NK_ASSERT_DEBUG(pi->Index()==aIndex); + __NK_ASSERT_DEBUG(pi->PagedState()==SPageInfo::EPagedPinned); + } + } +#endif + return r; + } + + +void DRomMemoryManager::Unpin(DMemoryObject* aMemory, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs) + { + TRACE(("DRomMemoryManager::Unpin %08x %08x", aMemory, aMapping)); + + __ASSERT_CRITICAL; + TUint index = aMapping->iStartIndex; + TUint endIndex = index+aMapping->iSizeInPages; + __NK_ASSERT_DEBUG(endIndex<=iSizeInPages); // Pin() should have already ensured this + + TUint pagedIndex = iPagedStart>>KPageShift; + if(pagedIndex && pagedIndexFlags()&DMemoryMapping::EPagesPinned) + DoUnpin(aMemory,start,endIndex-start,aMapping,aPinArgs); + } + + __NK_ASSERT_DEBUG((aMapping->Flags()&DMemoryMapping::EPageUnmapVetoed)==0); // we shouldn't have tried to Free paged ROM + } + + +void DRomMemoryManager::DoUnpin(DMemoryObject* aMemory, TUint aIndex, TUint aCount, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs) + { + TRACE(("DRomMemoryManager::DoUnpin(0x%08x,0x%08x,0x%08x,0x%08x,?)",aMemory, aIndex, aCount, aMapping)); + + // This should only be invoked on the paged part of rom. + __NK_ASSERT_DEBUG(iPagedStart && aIndex >= (iPagedStart >> KPageShift)); + + MmuLock::Lock(); + TUint endIndex = aIndex+aCount; + for(TUint i = aIndex; i < endIndex; ++i) + { + TPhysAddr page = aMemory->iPages.Page(i); + __NK_ASSERT_DEBUG(RPageArray::IsPresent(page)); + SPageInfo* pi = SPageInfo::FromPhysAddr(page); + if(pi->Type() == SPageInfo::EShadow) + { + pi = pi->GetOriginalPage(); + } + ThePager.Unpin(pi,aPinArgs); + MmuLock::Flash(); + } + + MmuLock::Unlock(); + + // clear EPagesPinned flag... + __e32_atomic_and_ord8(&aMapping->Flags(), TUint8(~DMemoryMapping::EPagesPinned)); + } + + +void DRomMemoryManager::ShadowLock() + { + MM::MemoryLock(iRomMemory); + } + + +void DRomMemoryManager::ShadowUnlock() + { + MM::MemoryUnlock(iRomMemory); + } + + +TInt DRomMemoryManager::AllocShadowPage(TLinAddr aRomAddr) + { + TRACE(("DRomMemoryManager::AllocShadowPage %08x", aRomAddr)); + + TUint index = (aRomAddr-iBase)>>KPageShift; + if (index >= iSizeInPages) + return KErrArgument; + __NK_ASSERT_DEBUG(iRomMemory->CheckRegion(index,1)); + + TInt r; + + ShadowLock(); + + DShadowPage* shadow = (DShadowPage*)iShadowPages.Find(index); + if(shadow) + r = KErrAlreadyExists; + else + { + shadow = DShadowPage::New(iRomMemory,index,iRomMapping); + if(!shadow) + r = KErrNoMemory; + else + { + r = iShadowPages.Add(index,shadow); + if(r!=KErrNone) + { + shadow->Destroy(); + } + else + { + // Remap the shadowed rom page to the shadow page. Update the + // page array entry for the page being shadowed, this ensures + // that any page moving attempts will remap the shadow page when + // they realise that the page is physically pinned. + MmuLock::Lock(); + TPhysAddr& pageEntry = *iRomMemory->iPages.PageEntry(index); + TPhysAddr newPageAddr = shadow->iNewPage; + pageEntry = (pageEntry & KPageMask) | newPageAddr; + + // Mark the SPageInfo of the shadow page with pointer to the original page's + // SPageInfo, this is safe as we've physically pinned the original page + // so it can't be freed or reused until this shadow page is destroyed. + SPageInfo* origPi = SPageInfo::FromPhysAddr(shadow->iOriginalPage); + SPageInfo* newPi = SPageInfo::FromPhysAddr(newPageAddr); + newPi->SetOriginalPage(origPi); + MmuLock::Unlock(); + + iRomMemory->RemapPage(pageEntry, index, ETrue); + } + } + } + + ShadowUnlock(); + + return r; + } + + +TInt DRomMemoryManager::FreeShadowPage(TLinAddr aRomAddr) + { + TUint index = (aRomAddr-iBase)>>KPageShift; + if(!iRomMemory->CheckRegion(index,1)) + return KErrArgument; + + TInt r; + + ShadowLock(); + + DShadowPage* shadow = (DShadowPage*)iShadowPages.Remove(index); + if(!shadow) + { + r = KErrNotFound; + } + else + { + // Remap the rom page and update the page array entry for the page + // back to the original rom page. This is safe as the page is physically + // pinned until shadow is destroyed. + MmuLock::Lock(); + TPhysAddr& pageEntry = *iRomMemory->iPages.PageEntry(index); + pageEntry = (pageEntry & KPageMask) | shadow->iOriginalPage; + MmuLock::Unlock(); + + iRomMemory->RemapPage(pageEntry, index, ETrue); + + shadow->Destroy(); + r = KErrNone; + } + + ShadowUnlock(); + + return r; + } + + +TInt DRomMemoryManager::CopyToShadowMemory(TLinAddr aDst, TLinAddr aSrc, TUint32 aSize) + { + TRACE(("DRomMemoryManager::CopyToShadowMemory(0x%08x,0x%08x,0x%x)",aDst,aSrc,aSize)); + Mmu& m = TheMmu; + TLinAddr offset = aDst-iBase; + TLinAddr end = offset+aSize; + if(endiSize) + return KErrArgument; + + while(aSize) + { + TUint size = KPageSize-(offset&KPageMask); // bytes left in page at 'offset' + if(size>aSize) + size = aSize; + + TInt r; + + ShadowLock(); + + DShadowPage* shadow = (DShadowPage*)iShadowPages.Find(offset>>KPageShift); + if(!shadow) + { + r = KErrNotFound; + } + else + { + RamAllocLock::Lock(); + TLinAddr dst = m.MapTemp(shadow->iNewPage,offset>>KPageShift); + dst += offset&KPageMask; + memcpy((TAny*)dst,(TAny*)aSrc,size); + m.UnmapTemp(); + RamAllocLock::Unlock(); + + r = KErrNone; + } + + ShadowUnlock(); + + if(r!=KErrNone) + return r; + + offset += size; + aSrc += size; + aSize -= size; + } + + return KErrNone; + } + + +// +// DShadowPage +// + +DShadowPage* DShadowPage::New(DMemoryObject* aMemory, TUint aIndex, DMemoryMappingBase* aMapping) + { + TRACE(("DShadowPage::New(0x%08x,0x%x,0x%08x)",aMemory, aIndex, aMapping)); + __NK_ASSERT_DEBUG(MemoryObjectLock::IsHeld(aMemory)); + + DShadowPage* self = new DShadowPage; + if(self) + if(self->Construct(aMemory,aIndex,aMapping)!=KErrNone) + { + self->Destroy(); + self = 0; + } + + TRACE(("DShadowPage::New(0x%08x,0x%x,0x%08x) returns 0x%08x",aMemory, aIndex, aMapping, self)); + return self; + } + + +DShadowPage::DShadowPage() + : iOriginalPage(KPhysAddrInvalid), iNewPage(KPhysAddrInvalid) + { + // Set flag so that the rom page that is being shadowed can't be moved, + // otherwise iOriginalPage will become invalid if the page is moved. + Flags() |= EPhysicalPinningMapping; + } + + + + +TInt DShadowPage::Construct(DMemoryObject* aMemory, TUint aIndex, DMemoryMappingBase* aMapping) + { + __NK_ASSERT_DEBUG(MemoryObjectLock::IsHeld(aMemory)); + + // Pin the page. It is ok to get the mapping instance count here without + // MmuLock as there is only one permenant mapping used for the ROM. + TInt r = Pin(aMemory,aIndex,1,EUserReadOnly,aMapping,aMapping->MapInstanceCount()); + if(r!=KErrNone) + return r; + + r = PhysAddr(0,1,iOriginalPage,0); + __NK_ASSERT_DEBUG(r>=0); + if(r<0) + return r; + + RamAllocLock::Lock(); + + Mmu& m = TheMmu; + r = m.AllocRam(&iNewPage, 1, aMemory->RamAllocFlags(), EPageFixed); + if(r==KErrNone) + { + TLinAddr dst = m.MapTemp(iNewPage,aIndex,0); + TLinAddr src = m.MapTemp(iOriginalPage,aIndex,1); + pagecpy((TAny*)dst,(TAny*)src); + CacheMaintenance::CodeChanged(dst,KPageSize); // IMB not needed, just clean to PoU (but we don't have a function to do that) + + m.UnmapTemp(0); + m.UnmapTemp(1); + MmuLock::Lock(); + SPageInfo::FromPhysAddr(iNewPage)->SetShadow(aIndex,aMemory->PageInfoFlags()); + MmuLock::Unlock(); + } + + RamAllocLock::Unlock(); + + if(r!=KErrNone) + return r; + + return r; + } + + +DShadowPage::~DShadowPage() + { + } + + +void DShadowPage::Destroy() + { + TRACE2(("DShadowPage[%x]::Destroy()",this)); + if(iNewPage!=KPhysAddrInvalid) + { + RamAllocLock::Lock(); + TheMmu.FreeRam(&iNewPage, 1, EPageFixed); + RamAllocLock::Unlock(); + } + if(IsAttached()) + Unpin(); + Close(); + } + + +/** +Replace a page of the system's execute-in-place (XIP) ROM image with a page of +RAM having the same contents. This RAM can subsequently be written to in order +to apply patches to the XIP ROM or to insert software breakpoints for debugging +purposes. +Call Epoc::FreeShadowPage() when you wish to revert to the original ROM page. + +@param aRomAddr The virtual address of the ROM page to be replaced. +@return KErrNone if the operation completed successfully. + KErrArgument if the specified address is not a valid XIP ROM address. + KErrNoMemory if the operation failed due to insufficient free RAM. + KErrAlreadyExists if the XIP ROM page at the specified address has + already been shadowed by a RAM page. + +@pre Calling thread must be in a critical section. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked. +@pre No fast mutex can be held. +@pre Call in a thread context. +*/ +EXPORT_C TInt Epoc::AllocShadowPage(TLinAddr aRomAddr) + { + CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::AllocShadowPage"); + return DRomMemoryManager::TheManager.AllocShadowPage(aRomAddr); + } + + +/** +Copies data into shadow memory. Source data is presumed to be in Kernel memory. + +@param aSrc Data to copy from. +@param aDest Address to copy into. +@param aLength Number of bytes to copy. Maximum of 32 bytes of data can be copied. + +@return KErrNone if the operation completed successfully. + KErrArgument if any part of destination region is not shadow page or + if aLength is greater then 32 bytes. + +@pre Calling thread must be in a critical section. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked. +@pre No fast mutex can be held. +@pre Call in a thread context. +*/ +EXPORT_C TInt Epoc::CopyToShadowMemory(TLinAddr aDest, TLinAddr aSrc, TUint32 aLength) + { + CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"Epoc::CopyToShadowMemory"); + return DRomMemoryManager::TheManager.CopyToShadowMemory(aDest,aSrc,aLength); + } + + +/** +Revert an XIP ROM address which has previously been shadowed to the original +page of ROM. + +@param aRomAddr The virtual address of the ROM page to be reverted. +@return KErrNone if the operation completed successfully. + KErrArgument if the specified address is not a valid XIP ROM address. + KErrGeneral if the specified address has not previously been shadowed + using Epoc::AllocShadowPage(). + +@pre Calling thread must be in a critical section. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked. +@pre No fast mutex can be held. +@pre Call in a thread context. +*/ +EXPORT_C TInt Epoc::FreeShadowPage(TLinAddr aRomAddr) + { + return DRomMemoryManager::TheManager.FreeShadowPage(aRomAddr); + } + + +/** +Change the permissions on an XIP ROM address which has previously been shadowed +by a RAM page so that the RAM page may no longer be written to. + +Note: Shadow page on the latest platforms (that use the reduced set of access permissions: +arm11mpcore, arm1176, cortex) is implemented with read only permissions. Therefore, calling +this function in not necessary, as shadow page is already created as 'frozen'. + +@param aRomAddr The virtual address of the shadow RAM page to be frozen. +@return KErrNone if the operation completed successfully. + KErrArgument if the specified address is not a valid XIP ROM address. + KErrGeneral if the specified address has not previously been shadowed + using Epoc::AllocShadowPage(). + +@pre Calling thread must be in a critical section. +@pre Interrupts must be enabled. +@pre Kernel must be unlocked. +@pre No fast mutex can be held. +@pre Call in a thread context. +*/ +EXPORT_C TInt Epoc::FreezeShadowPage(TLinAddr aRomAddr) + { + // Null operation for flexible memory model... + return KErrNone; + } + +