sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\drivers\locmedia\dmasupport.cpp sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include "locmedia.h" sl@0: #include "dmasupport.h" sl@0: #include "dmasupport.inl" sl@0: sl@0: #include "OstTraceDefinitions.h" sl@0: #ifdef OST_TRACE_COMPILER_IN_USE sl@0: #include "locmedia_ost.h" sl@0: #ifdef __VC32__ sl@0: #pragma warning(disable: 4127) // disabling warning "conditional expression is constant" sl@0: #endif sl@0: #include "dmasupportTraces.h" sl@0: #endif sl@0: sl@0: #define PHYSADDR_FAULT() Kern::Fault("TLOCDRV-PHYS-ADDR",__LINE__) sl@0: sl@0: //#define __DEBUG_DMASUP__ sl@0: #ifdef __DEBUG_DMASUP__ sl@0: #define __KTRACE_DMA(p) {p;} sl@0: #else sl@0: #define __KTRACE_DMA(p) sl@0: #endif sl@0: sl@0: TInt DDmaHelper::iPageSize; sl@0: TInt DDmaHelper::iPageSizeLog2; sl@0: TInt DDmaHelper::iPageSizeMsk; sl@0: sl@0: /****************************************************************************** sl@0: DDmaHelper sl@0: ******************************************************************************/ sl@0: const TPhysAddr KPhysMemFragmented = KPhysAddrInvalid; sl@0: sl@0: TUint32 Log2(TUint32 aVal) sl@0: { sl@0: __ASSERT_COMPILE(sizeof(TUint32) == 4); sl@0: sl@0: TUint32 bitPos=31; sl@0: sl@0: if(!(aVal >> 16)) {bitPos-=16; aVal<<=16;} sl@0: if(!(aVal >> 24)) {bitPos-=8; aVal<<=8 ;} sl@0: if(!(aVal >> 28)) {bitPos-=4; aVal<<=4 ;} sl@0: if(!(aVal >> 30)) {bitPos-=2; aVal<<=2 ;} sl@0: if(!(aVal >> 31)) {bitPos-=1;} sl@0: sl@0: return bitPos; sl@0: } sl@0: sl@0: TBool IsPowerOfTwo(TInt aNum) sl@0: // sl@0: // Returns ETrue if aNum is a power of two sl@0: // sl@0: { sl@0: return (aNum != 0 && (aNum & -aNum) == aNum); sl@0: } sl@0: sl@0: void DDmaHelper::ResetPageLists() sl@0: { sl@0: iFragLen = 0; sl@0: iFragLenRemaining = 0; sl@0: } sl@0: sl@0: DDmaHelper::DDmaHelper() sl@0: { sl@0: OstTraceFunctionEntry0( DDMAHELPER_DDMAHELPER_ENTRY ); sl@0: iPageSize = Kern::RoundToPageSize(1); sl@0: __ASSERT_ALWAYS(IsPowerOfTwo(iPageSize), PHYSADDR_FAULT()); sl@0: iPageSizeLog2 = Log2(iPageSize); sl@0: iPageSizeMsk = iPageSize-1; sl@0: OstTraceFunctionExit0( DDMAHELPER_DDMAHELPER_EXIT ); sl@0: } sl@0: sl@0: DDmaHelper::~DDmaHelper() sl@0: { sl@0: OstTraceFunctionEntry0( DESTRUCTOR_DDMAHELPER_ENTRY ); sl@0: delete [] iPageArray; sl@0: delete [] iPageList; sl@0: if (iPhysicalPinObject) sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: Kern::DestroyPhysicalPinObject(iPhysicalPinObject); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: OstTraceFunctionExit0( DESTRUCTOR_DDMAHELPER_EXIT ); sl@0: } sl@0: sl@0: /** sl@0: Constructs the DDmaHelper object sl@0: sl@0: @param aLength The maximum length of data mapped by this object. sl@0: Should be a multiple of the page size sl@0: @param aMediaBlockSize The minimum amount data that the media can transfer in read / write operations sl@0: @param aDmaAlignment The memory alignment required by the media devices DMA controller. (i.e. word aligned = 2) sl@0: sl@0: @return KErrNone,if successful; sl@0: KErrNoMemory, if unable to create Page Array's. sl@0: */ sl@0: TInt DDmaHelper::Construct(TInt aLength, TInt aMediaBlockSize, TInt aDmaAlignment) sl@0: { sl@0: OstTraceFunctionEntry1( DDMAHELPER_CONSTRUCT_ENTRY, this ); sl@0: __ASSERT_ALWAYS(aMediaBlockSize > 0, PHYSADDR_FAULT()); sl@0: __ASSERT_ALWAYS(IsPowerOfTwo(aMediaBlockSize), PHYSADDR_FAULT()); sl@0: __ASSERT_ALWAYS(aLength > 0, PHYSADDR_FAULT()); sl@0: __ASSERT_ALWAYS(aLength > iPageSize, PHYSADDR_FAULT()); sl@0: sl@0: // This code assumes that the media block size (normally 512) is >= the processor's sl@0: // cache-line size (typically 32 bytes). This may not be true for future processors. sl@0: // If the cache-line size was 1024, for example, reading 512 bytes into a client's sl@0: // buffer & then calling Cache::SyncMemoryAfterDmaRead would invalidate an entire 1024 sl@0: // bytes in the user's address space. sl@0: TUint cacheLineSize = Cache::DmaBufferAlignment(); sl@0: __ASSERT_ALWAYS(IsPowerOfTwo(cacheLineSize), PHYSADDR_FAULT()); sl@0: if (cacheLineSize > (TUint) aMediaBlockSize) sl@0: { sl@0: OstTraceFunctionExitExt( DDMAHELPER_CONSTRUCT_EXIT1, this, KErrNotSupported ); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: //Check whether Kernel supports physical memory pinning: sl@0: TInt mm = Kern::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask; sl@0: if (mm >= EMemModelTypeFlexible) sl@0: { sl@0: // Flexible memory model supports physical pinning for user (and Kernel) memory that sl@0: // is the subject of DMA transfer. sl@0: // Physical memory pinning ensures that: sl@0: // - physical memory is not moved by RAM defragmentation. sl@0: // - it is safe to to DMA against it or do sync cache (using new interface) even if/when sl@0: // the owner of the memory (e.g. untrusted user aplication) decomits memory or panics. sl@0: // For details @see Kern::PinPhysicalMemory. sl@0: // Cache Sync of physically pinned memory on flexible memory model is done by: sl@0: // - Cache::SyncPhysicalMemoryBeforeDmaWrite sl@0: // - Cache::SyncPhysicalMemoryBeforeDmaRead sl@0: // - Cache::SyncPhysicalMemoryAfterDmaRead sl@0: iPhysPinningAvailable = ETrue; sl@0: __KTRACE_DMA(Kern::Printf("Memory model (%d) supports physical pinning\n",mm)); sl@0: NKern::ThreadEnterCS(); sl@0: TInt r=Kern::CreatePhysicalPinObject(iPhysicalPinObject); sl@0: OstTraceExt2(TRACE_DMASUPPORT, DDMAHELPER_CONSTRUCT1, "Memory model=%d supports physical pinning; created Physical Pin Object with return value=%d",mm, r); sl@0: NKern::ThreadLeaveCS(); sl@0: if (r) return r; sl@0: } sl@0: else sl@0: { sl@0: // Memory models before flexible do not support memory pinning. sl@0: // The driver has to use PrepareMemoryForDMA/ReleaseMemoryFromDMA Kernel interface sl@0: // that ensures that physical memory won't be moved by RAM defragmentation module. sl@0: // However, Kernel relies on assumption that the user memory won't dissapear (e.g. by sl@0: // user client closing the chunk or panics), as it would lead to Kernel crash. sl@0: // For that reason, the only use case for DMA transfer into user memory is File System's sl@0: // read/write buffer - as it is assumed that File System is trusted component. sl@0: // To mark its buffers(s) for DMA transfer, File Sytem must call UserSvr::RegisterTrustedChunk sl@0: // before DMA transfer starts. sl@0: // Cache sync. operations before/after DMA transfer must be done by using the old Cache interface: sl@0: // - Cache::SyncMemoryBeforeDmaWrite sl@0: // - Cache::SyncMemoryBeforeDmaRead sl@0: // - Cache::SyncMemoryAfterDmaRead sl@0: // As they all require linear address as input, these methods also rely on File System buffers sl@0: // to be in valid state during sync calls. sl@0: iPhysPinningAvailable = EFalse; sl@0: __KTRACE_DMA(Kern::Printf("Memory model (%d) doesn't support physical pining",mm)); sl@0: OstTrace1(TRACE_DMASUPPORT, DDMAHELPER_CONSTRUCT2, "Memory model=%d doesn't support physical pinning",mm); sl@0: iPhysicalPinObject = NULL; sl@0: } sl@0: sl@0: iMaxPages = (aLength >> iPageSizeLog2)-1; sl@0: sl@0: // 2 Additional pages for page straddling sl@0: iPageArray = new TPhysAddr[iMaxPages+2]; sl@0: if (iPageArray != NULL) sl@0: { sl@0: iPageList = new TPageList[iMaxPages]; sl@0: if (iPageList != NULL) sl@0: { sl@0: iMediaBlockSize = aMediaBlockSize; sl@0: iMediaBlockSizeMask = TInt64(iMediaBlockSize - 1); sl@0: sl@0: iDmaAlignment = aDmaAlignment; sl@0: __KTRACE_DMA(Kern::Printf("-PHYSADDR: Construct iMaxPages(%d), MediaBlocks(%d), DMAalign(%d)",iMaxPages,iMediaBlockSize,iDmaAlignment)); sl@0: OstTraceExt3(TRACE_FLOW, DDMAHELPER_CONSTRUCT_EXIT2, "< KErrNone PHYSADDR: Construct iMaxPages %d MediaBlocks %d DMAalign %d", iMaxPages,iMediaBlockSize,iDmaAlignment ); sl@0: return KErrNone; sl@0: } sl@0: delete [] iPageArray; iPageArray = NULL; sl@0: } sl@0: sl@0: iMaxPages = 0; sl@0: OstTraceFunctionExitExt( DDMAHELPER_CONSTRUCT_EXIT3, this, KErrNoMemory ); sl@0: return KErrNoMemory; sl@0: } sl@0: sl@0: /** sl@0: * Each Read/Write request is examined to determine if the descriptor that sl@0: * is referenced is mapped to a physical memory object; sl@0: * if so it prepares the memory, updates the request with physical memory information sl@0: * and issues the request. sl@0: * If a request does not make use of physical memory or is not configured correctly the sl@0: * request is passed through without modification. sl@0: */ sl@0: TInt DDmaHelper::SendReceive(TLocDrvRequest& aReq, TLinAddr aLinAddress) sl@0: { sl@0: OstTraceFunctionEntry0( DDMAHELPER_SENDRECEIVE_ENTRY ); sl@0: DPrimaryMediaBase& primaryMedia = *aReq.Drive()->iPrimaryMedia; sl@0: sl@0: TInt reqId = aReq.Id(); sl@0: if (reqId != DLocalDrive::ERead && reqId != DLocalDrive::EWrite) sl@0: { sl@0: OstTrace0(TRACE_FLOW, DDMAHELPER_SENDRECEIVE_EXIT1, "< Request is not ERead or EWrite, cannot perform Direct Memory Access"); sl@0: return aReq.SendReceive(&primaryMedia.iMsgQ); sl@0: } sl@0: sl@0: if ((I64HIGH(aReq.Length()) > 0) || (aReq.Length() < iMediaBlockSize)) sl@0: { sl@0: OstTrace0(TRACE_FLOW, DDMAHELPER_SENDRECEIVE_EXIT2, "< Invalid request length, cannot perform Direct Memory Access"); sl@0: return aReq.SendReceive(&primaryMedia.iMsgQ); sl@0: } sl@0: sl@0: // If more than one user thread tries to access the drive, then bail out as there is sl@0: // only one DDmaHelper object per TLocDrv. Normally this shouldn't ever happen unless sl@0: // a client app accesses the drive directly using TBusLOcalDrive or the file system is sl@0: // asynchronous (i.e. there is a separate drive thread) but the file server message is sl@0: // flagged as synchronous - e.g. EFsDrive sl@0: if (TInt(__e32_atomic_add_ord32(&iLockCount, 1)) > 0) // busy ? sl@0: { sl@0: __KTRACE_DMA(Kern::Printf("-PHYSADDR: BUSY")); sl@0: __e32_atomic_add_ord32(&iLockCount, TUint32(-1)); sl@0: OstTrace0(TRACE_FLOW, DDMAHELPER_SENDRECEIVE_EXIT3, "< DMA Busy"); sl@0: return aReq.SendReceive(&primaryMedia.iMsgQ); sl@0: } sl@0: sl@0: // make a copy of the request sl@0: iMemoryType = EUnknown; sl@0: iReq = &aReq; sl@0: iReqId = reqId; sl@0: sl@0: iReqPosClient = iReq->Pos(); sl@0: sl@0: iReqLenClient = I64LOW(iReq->Length()); sl@0: sl@0: iReqRemoteDesOffset = iReq->RemoteDesOffset(); sl@0: iReqFlags = iReq->Flags(); sl@0: sl@0: iRemoteThread = iReq->RemoteThread(); sl@0: iCurrentThread = &Kern::CurrentThread(); sl@0: iOwningThread = iRemoteThread ? iRemoteThread : iCurrentThread; sl@0: sl@0: iChunk = NULL; sl@0: iChunkOffset = 0; sl@0: iLinAddressUser = NULL; sl@0: iLenConsumed = 0; sl@0: sl@0: // point to the start of the descriptor sl@0: iLinAddressUser = aLinAddress - iReqRemoteDesOffset; sl@0: sl@0: // Need to check descriptors from both direct Clients (i.e. file cache, RemoteThread == NULL ) sl@0: // and Remote Server Clients (file server clients, RemoteThread != NULL) sl@0: // Shared Memory can potentially be used by both remote server and direct clients sl@0: NKern::ThreadEnterCS(); sl@0: iChunk = Kern::OpenSharedChunk(iOwningThread, (const TAny*) iLinAddressUser, ETrue, iChunkOffset); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: TInt fragments = 0; sl@0: TInt r; sl@0: do sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:SendReceive() iReqLen %d; iLenConsumed %d; fragments %d",iReqLen, iLenConsumed, fragments)); sl@0: OstTraceExt2( TRACE_DMASUPPORT, DDMAHELPER_SENDRECEIVE1, "PHYSADDR:SendReceive() iLenConsumed=%d; fragments=%d", iLenConsumed, fragments); sl@0: r = RequestStart(); sl@0: if (r != KErrNone) sl@0: { sl@0: if (iChunk) sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: Kern::ChunkClose(iChunk); sl@0: iChunk = NULL; sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: __KTRACE_DMA(Kern::Printf("SendReceive(&primaryMedia.iMsgQ); sl@0: } sl@0: else sl@0: { sl@0: iReq->Flags() |= TLocDrvRequest::EPhysAddr; sl@0: } sl@0: sl@0: __KTRACE_DMA(Kern::Printf("-PHYSADDR:SendReceive() rThread %08X pos %08lX, len %d addr %08X off %08X", sl@0: iRemoteThread, iReq->Pos(), I64LOW(iReq->Length()), iLinAddressUser, iReqRemoteDesOffset)); sl@0: OstTraceExt4(TRACE_DMASUPPORT, DDMAHELPER_SENDRECEIVE2, "PHYSADDR:SendReceive() position=%Ld; length=%d; address=0x%x; offset=0x%x", iReq->Pos(), (TInt) I64LOW(iReq->Length()), (TUint) iLinAddressUser, (TUint) iReqRemoteDesOffset ); sl@0: sl@0: __ASSERT_DEBUG(iReq->Length() == FragLength(), PHYSADDR_FAULT()); sl@0: __ASSERT_DEBUG(iReq->Length() != 0, PHYSADDR_FAULT()); sl@0: sl@0: // reinstate iValue in case overwritten by DMediaPagingDevice::CompleteRequest() sl@0: iReq->iValue = iReqId; sl@0: sl@0: OstTrace1(TRACE_DMASUPPORT, DDMAHELPER_SENDRECEIVE3, "Dma SendReceive Start iReq=%d", iReq); sl@0: r = iReq->SendReceive(&primaryMedia.iMsgQ); sl@0: OstTrace1(TRACE_DMASUPPORT, DDMAHELPER_SENDRECEIVE4, "Dma SendReceive Return iReq=%d", iReq); sl@0: sl@0: // The media driver could potentially choose to deal with the request sl@0: // without accessing physical memory (e.g. if the data is already cached). sl@0: iLenConsumed += iFragLenRemaining; sl@0: sl@0: RequestEnd(); sl@0: sl@0: ResetPageLists(); sl@0: sl@0: fragments++; sl@0: sl@0: } sl@0: while(r == KErrNone && LengthRemaining() > 0); sl@0: sl@0: if (iChunk) sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: Kern::ChunkClose(iChunk); sl@0: iChunk = NULL; sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: sl@0: // Set remote descriptor length to iReqLenClient sl@0: if (iReqId == DLocalDrive::ERead && r == KErrNone) sl@0: r = UpdateRemoteDescriptorLength(iReqLenClient); sl@0: sl@0: __KTRACE_DMA(Kern::Printf(" sl@0: * sl@0: * The pages may not be physically contiguous; if they are not, sl@0: * then they are supplied to the media driver one contiguous sl@0: * sequent at a time by GetPhysicalAddress() sl@0: **/ sl@0: TInt DDmaHelper::RequestStart() sl@0: { sl@0: OstTraceFunctionEntry1( DDMAHELPER_REQUESTSTART_ENTRY, this ); sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:RequestStart()")); sl@0: sl@0: iIndex = 0; sl@0: sl@0: TLinAddr startAddr = LinAddress(); sl@0: TInt64 startPos = iReqPosClient + iLenConsumed; sl@0: TInt mediaBlockOffset = BlockOffset(startPos); sl@0: TInt addrBlockOffset = BlockOffset(startAddr); sl@0: TInt length = Min(LengthRemaining(), MaxFragLength()); sl@0: sl@0: iPageArrayCount = iPageListCount = 0; sl@0: sl@0: TLinAddr firstPageStart = PageAlign(startAddr); sl@0: TLinAddr lastPageStart = PageAlign(startAddr + length + iPageSize - 1); sl@0: iPageArrayCount = (lastPageStart - firstPageStart + 1) >> iPageSizeLog2; sl@0: sl@0: iMemoryType = EUnknown; sl@0: iPhysAddr = KPhysMemFragmented; // Default - Mark memory as fragmented sl@0: sl@0: //************************************* sl@0: // Check Physical Page Alignment!! sl@0: //************************************* sl@0: if (!IsBlockAligned(startPos)) sl@0: { sl@0: // Will DMA align at next block alignment? such that DMA can be used sl@0: TInt ofset = I64LOW((startPos + iMediaBlockSize) & (iMediaBlockSize-1)); sl@0: ofset = iMediaBlockSize - ofset; sl@0: sl@0: if (!IsDmaAligned(startAddr)) sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(" 0) ) sl@0: { sl@0: TLinAddr firstPageAddr = PageAlign(startAddr); //ensure that it is page aligned. sl@0: sl@0: TInt r = KErrNone; sl@0: if (iPhysPinningAvailable) sl@0: { sl@0: TBool readOnlyMem = (iReqId == DLocalDrive::EWrite); sl@0: r = Kern::PinPhysicalMemory(iPhysicalPinObject, firstPageAddr, iPageArrayCount << iPageSizeLog2, sl@0: readOnlyMem, iPhysAddr, iPageArray, iMapAttr, iPageColour, iCurrentThread); sl@0: } sl@0: else sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: r = Kern::PrepareMemoryForDMA(iCurrentThread, (void*)firstPageAddr, iPageArrayCount << iPageSizeLog2, iPageArray); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: if (r != KErrNone) sl@0: { sl@0: OstTraceFunctionExitExt( DDMAHELPER_REQUESTSTART_EXIT4, this, r ); sl@0: return r; sl@0: } sl@0: sl@0: iMemoryType = EFileServerChunk; sl@0: sl@0: __KTRACE_DMA(Kern::Printf("-PHYSADDR:RequestStart() - EFileServerChunk")); sl@0: OstTrace0( TRACE_DMASUPPORT, DDMAHELPER_REQUESTSTART1, "EFileServerChunk"); sl@0: } sl@0: //**************************** sl@0: // Is it shared chunk ? sl@0: //**************************** sl@0: else if (iChunk) sl@0: { sl@0: // calculate chunk offset of start of first page sl@0: TInt offset = iChunkOffset + iReqRemoteDesOffset+ iLenConsumed; sl@0: sl@0: TInt r = Kern::ChunkPhysicalAddress(iChunk, offset, length, iLinAddressKernel, iMapAttr, iPhysAddr, iPageArray); sl@0: sl@0: if (r < KErrNone) sl@0: { sl@0: OstTraceFunctionExitExt( DDMAHELPER_REQUESTSTART_EXIT5, this, r ); sl@0: return r; // 0 = Contiguous Memory, 1 = Fragmented/Dis-Contiguous Memory sl@0: } sl@0: sl@0: iMemoryType = ESharedChunk; sl@0: sl@0: __KTRACE_DMA(Kern::Printf("-PHYSADDR:RequestStart() - ESharedChunk")); sl@0: OstTrace0( TRACE_DMASUPPORT, DDMAHELPER_REQUESTSTART2, "ESharedChunk"); sl@0: } sl@0: else sl@0: { sl@0: __KTRACE_DMA(Kern::Printf("Length() = MAKE_TINT64(0, length); sl@0: iReq->Pos() = iReqPosClient + iLenConsumed; sl@0: iReq->RemoteDesOffset() = iReqRemoteDesOffset + iLenConsumed; sl@0: // restore EAdjusted flag to ensure iReq->Pos() is adjusted correctly sl@0: iReq->Flags()&= ~TLocDrvRequest::EAdjusted; sl@0: iReq->Flags()|= (iReqFlags & TLocDrvRequest::EAdjusted); sl@0: sl@0: //************************************************ sl@0: // Sync memory sl@0: //************************************************ sl@0: __KTRACE_DMA(Kern::Printf(">SYNC-PHYSADDR:addr 0x%x len %d", startAddr, length)); sl@0: OstTraceExt2(TRACE_DMASUPPORT, DDMAHELPER_REQUESTSTART3, "startAddr=0x%x length=%d", (TUint) startAddr, length ); sl@0: sl@0: // Only sync whole blocks: it is assumed that the media driver will transfer sl@0: // partial start and end blocks without DMA sl@0: sl@0: TInt startBlockPartialLen = IsBlockAligned(startPos) ? 0 : iMediaBlockSize - BlockOffset(startPos); sl@0: TInt blockLen = (TInt) BlockAlign(length - startBlockPartialLen); sl@0: sl@0: if (iReqId == DLocalDrive::EWrite) sl@0: { sl@0: if (iMemoryType == ESharedChunk) sl@0: { sl@0: Cache::SyncMemoryBeforeDmaWrite(iLinAddressKernel+startBlockPartialLen, blockLen, iMapAttr); sl@0: } sl@0: else // (iMemoryType == EFileServerChunk) sl@0: { sl@0: if (iPhysPinningAvailable) sl@0: Cache::SyncPhysicalMemoryBeforeDmaWrite(iPageArray, iPageColour, startBlockPartialLen, blockLen, iMapAttr); sl@0: else sl@0: Cache::SyncMemoryBeforeDmaWrite(startAddr+startBlockPartialLen, blockLen); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: if (iMemoryType == ESharedChunk) sl@0: Cache::SyncMemoryBeforeDmaRead(iLinAddressKernel, length, iMapAttr); sl@0: else // (iMemoryType == EFileServerChunk) sl@0: { sl@0: if (iPhysPinningAvailable) sl@0: Cache::SyncPhysicalMemoryBeforeDmaRead(iPageArray, iPageColour, 0, length, iMapAttr); sl@0: else sl@0: Cache::SyncMemoryBeforeDmaRead(startAddr, length); sl@0: } sl@0: } sl@0: sl@0: __KTRACE_DMA(Kern::Printf("PHYSADDR:RequestEnd()")); sl@0: sl@0: sl@0: __ASSERT_DEBUG(iReqId == DLocalDrive::ERead || iReqId == DLocalDrive::EWrite, PHYSADDR_FAULT()); sl@0: __ASSERT_DEBUG(iMemoryType == ESharedChunk || iMemoryType == EFileServerChunk, PHYSADDR_FAULT()); sl@0: sl@0: TInt length = FragLength(); // len of data just transferred sl@0: TLinAddr startAddr = LinAddress() - length; sl@0: sl@0: // Sync the memory : but not if the media driver has decided to transfer ALL the data using IPC rather than DMA. sl@0: // It is assumed that the media driver will transfer partial start & end blocks using IPC, but it may also choose sl@0: // to use IPC for the ENTIRE fragment when read/writing at the end of the media (see medmmc.cpp) sl@0: if (iFragLenRemaining < length && iReqId == DLocalDrive::ERead) sl@0: { sl@0: TInt64 startPos = iReq->Pos(); sl@0: TInt startBlockPartialLen = IsBlockAligned(startPos) ? 0 : iMediaBlockSize - BlockOffset(startPos); sl@0: TInt blockLen = (TInt) BlockAlign(length - startBlockPartialLen); sl@0: sl@0: if (iMemoryType == ESharedChunk) sl@0: { sl@0: Cache::SyncMemoryAfterDmaRead(iLinAddressKernel + startBlockPartialLen, blockLen); sl@0: } sl@0: else // (iMemoryType == EFileServerChunk) sl@0: { sl@0: if (iPhysPinningAvailable) sl@0: Cache::SyncPhysicalMemoryAfterDmaRead(iPageArray, iPageColour, startBlockPartialLen, blockLen, iMapAttr); sl@0: else sl@0: Cache::SyncMemoryAfterDmaRead(startAddr + startBlockPartialLen, blockLen); sl@0: } sl@0: sl@0: } sl@0: ReleasePages(PageAlign(startAddr)); sl@0: OstTraceFunctionExit0( DDMAHELPER_REQUESTEND_EXIT ); sl@0: } sl@0: sl@0: /** sl@0: * For File Server chunks this method releases the current physical memory in use. sl@0: * sl@0: * @see Kern::ReleaseMemoryFromDMA() sl@0: */ sl@0: void DDmaHelper::ReleasePages(TLinAddr aAddr) sl@0: { sl@0: OstTraceFunctionEntry1( DDMAHELPER_RELEASEPAGES_ENTRY, this ); sl@0: if (iMemoryType == EFileServerChunk) sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR():ReleasePages thread (0x%x) aAddr(0x%08x) size(%d) iPageArray(0x%x)",iCurrentThread, aAddr, (iPageArrayCount << iPageSizeLog2), iPageArray)); sl@0: OstTraceExt3( TRACE_DMASUPPORT, DDMAHELPER_RELEASEPAGES, "ReleasePages aAddr=0x%x; size=%d; iPageArray-0x%x", (TUint) aAddr, (iPageArrayCount << iPageSizeLog2), (TUint) iPageArray); sl@0: sl@0: TInt r; sl@0: if (iPhysPinningAvailable) sl@0: { sl@0: r = Kern::UnpinPhysicalMemory(iPhysicalPinObject); sl@0: } sl@0: else sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: r = Kern::ReleaseMemoryFromDMA(iCurrentThread, (void*) aAddr, iPageArrayCount << iPageSizeLog2, iPageArray); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: __ASSERT_ALWAYS(r == KErrNone, PHYSADDR_FAULT()); sl@0: } sl@0: OstTraceFunctionExit1( DDMAHELPER_RELEASEPAGES_EXIT, this ); sl@0: } sl@0: sl@0: /** sl@0: * Utility method which examines the page array, compiling adjacent pages into contiguous fragments sl@0: * and populating iPageList with said fragments. sl@0: */ sl@0: void DDmaHelper::BuildPageList() sl@0: { sl@0: OstTraceFunctionEntry1( DDMAHELPER_BUILDPAGELIST_ENTRY, this ); sl@0: iPageListCount = 0; sl@0: sl@0: if (iPhysAddr != KPhysMemFragmented) sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:BuildPageList() - Contiguous Memory")); sl@0: OstTrace0( TRACE_DMASUPPORT, DDMAHELPER_BUILDPAGELIST1, "Contiguous Memory"); sl@0: // Only one entry required. sl@0: iPageList[0].iAddress = iPhysAddr; sl@0: iPageList[0].iLength = FragLength(); sl@0: iPageListCount = 1; sl@0: } sl@0: else sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:BuildPageList() - Dis-Contiguous Memory")); sl@0: OstTrace0( TRACE_DMASUPPORT, DDMAHELPER_BUILDPAGELIST2, "Dis-Contiguous Memory"); sl@0: TInt offset; sl@0: sl@0: offset = PageOffset(iChunkOffset + iReqRemoteDesOffset+ iLenConsumed); sl@0: iPageList[0].iAddress = iPageArray[0]+offset; sl@0: iPageList[0].iLength = iPageSize-offset; sl@0: sl@0: TInt lengthRemaining = FragLength() - iPageList[0].iLength; sl@0: sl@0: TInt i =1; sl@0: for( ; i < iPageArrayCount; i++) sl@0: { sl@0: //Check if RAM pages are physically adjacent sl@0: if ((iPageArray[i-1] + PageSize()) == iPageArray[i]) sl@0: { sl@0: // Adjacent pages - just add length sl@0: iPageList[iPageListCount].iLength += PageSize(); sl@0: } sl@0: else sl@0: { sl@0: // Not Adjacent, start new Memory fragment sl@0: iPageListCount++; sl@0: iPageList[iPageListCount].iAddress = iPageArray[i]; sl@0: iPageList[iPageListCount].iLength = iPageSize; sl@0: } sl@0: sl@0: lengthRemaining -= PageSize(); sl@0: if (lengthRemaining < 0) sl@0: { sl@0: // Last page, re-adjust length for odd remainder. sl@0: iPageList[iPageListCount].iLength += lengthRemaining; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: iPageListCount++; sl@0: } sl@0: sl@0: //#ifdef __DEBUG_DMASUP__ sl@0: // for (TInt m=0; m= iPageListCount) sl@0: { sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:GetPhysD() [%d], PageListCount:%d", iIndex, iPageListCount)); sl@0: OstTraceExt2(TRACE_DMASUPPORT, DDMAHELPER_GETPHYSICALADDRESS1, "GetPhysD() [%d]; iPageCountList=%d", iIndex, iPageListCount ); sl@0: aAddr = 0; sl@0: aLen = 0; sl@0: OstTraceFunctionExitExt( DUP1_DDMAHELPER_GETPHYSICALADDRESS_EXIT1, this, KErrGeneral ); sl@0: return KErrGeneral; sl@0: } sl@0: sl@0: aAddr = iPageList[iIndex].iAddress; sl@0: aLen = iPageList[iIndex].iLength; sl@0: iLenConsumed+= aLen; sl@0: iFragLenRemaining-= aLen; sl@0: sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:GetPhysD() [%d] addr:0x%08X, l:%d; Used:%d, Left:%d", iIndex, aAddr, aLen, iLenConsumed, iFragLenRemaining)); sl@0: OstTraceExt5(TRACE_DMASUPPORT, DDMAHELPER_GETPHYSICALADDRESS2, "GetPhysD() [%d]; address=0x%x; length=%d; iLenConsumed=%d; iFragLenRemaining=%d", iIndex, (TUint) aAddr, aLen, iLenConsumed, iFragLenRemaining); sl@0: __ASSERT_DEBUG(aLen >= 0, PHYSADDR_FAULT()); sl@0: sl@0: iIndex++; //Move index to next page sl@0: sl@0: OstTraceFunctionExitExt( DDMAHELPER_GETPHYSICALADDRESS_EXIT2, this, KErrNone ); sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: #ifdef __DEMAND_PAGING__ sl@0: /** sl@0: * Returns Address and Length of next contiguous Physical memory. sl@0: * Static function specifically for Demand Paging support sl@0: * sl@0: * @param aReq TLocDrvRequest from which physical sl@0: * @param aAddr Populated with the Physical Address of the Request aReq. sl@0: * @param aLen Populated with the length in bytes of the memory. sl@0: * sl@0: * @return KErrNone sl@0: */ sl@0: TInt DDmaHelper::GetPhysicalAddress(TLocDrvRequest& aReq, TPhysAddr& aAddr, TInt& aLen) sl@0: { sl@0: OstTraceFunctionEntry0( DDMAHELPER_GETPHYSICALADDRESS_ENTRY ); sl@0: __ASSERT_DEBUG( (aReq.Flags() & TLocDrvRequest::ETClientBuffer) == 0, PHYSADDR_FAULT()); sl@0: TLinAddr linAddr = (TLinAddr) aReq.RemoteDes(); sl@0: TInt& offset = aReq.RemoteDesOffset(); sl@0: TLinAddr currLinAddr = linAddr + offset; sl@0: TInt reqLen = I64LOW(aReq.Length()); sl@0: __ASSERT_DEBUG(I64HIGH(aReq.Length()) == 0, PHYSADDR_FAULT()); sl@0: sl@0: aAddr = Epoc::LinearToPhysical(currLinAddr); sl@0: sl@0: // Set the initial length to be the length remaining in this page or the request length (whichever is shorter). sl@0: // If there are subsequent pages, we then need to determine whether they are contiguous sl@0: aLen = Min( (TInt) (PageAlign(currLinAddr+iPageSize) - currLinAddr), reqLen - offset); sl@0: sl@0: __ASSERT_DEBUG(aLen > 0, PHYSADDR_FAULT()); sl@0: sl@0: TPhysAddr currPhysPageAddr = PageAlign((TLinAddr) aAddr); sl@0: sl@0: offset+= aLen; sl@0: sl@0: sl@0: while (offset < reqLen) sl@0: { sl@0: TPhysAddr nextPhysPageAddr = Epoc::LinearToPhysical(linAddr + offset); sl@0: __ASSERT_DEBUG(PageOffset((TLinAddr) nextPhysPageAddr) == 0, PHYSADDR_FAULT()); sl@0: sl@0: if (nextPhysPageAddr != currPhysPageAddr + iPageSize) sl@0: break; sl@0: sl@0: currPhysPageAddr = nextPhysPageAddr; sl@0: sl@0: TInt len = Min(iPageSize, reqLen - offset); sl@0: offset+= len; sl@0: aLen+= len; sl@0: } sl@0: sl@0: sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:DP:GetPhysS(), linAddr %08X, physAddr %08X, len %x reqLen %x", linAddr + offset, aAddr, aLen, reqLen)); sl@0: OstTraceExt4(TRACE_DEMANDPAGING, DDMAHELPER_GETPHYSICALADDRESS_DP, "linAddr=0x%x; physAddr=0x%x; length=0x%x; reqLen=0x%x", linAddr + offset, aAddr, aLen, reqLen); sl@0: OstTraceFunctionExit0( DDMAHELPER_GETPHYSICALADDRESS_EXIT ); sl@0: return KErrNone; sl@0: } sl@0: #endif // (__DEMAND_PAGING__) sl@0: sl@0: sl@0: /** sl@0: * Modifies the current requests remote descriptor length sl@0: * sl@0: * @param aLength Length in bytes to which the descriptor is to be set. sl@0: * sl@0: * @return KErrNone, if successful; sl@0: * KErrBadDescriptor, if descriptor is corrupted; sl@0: * otherwise one of the other system wide error codes. sl@0: */ sl@0: sl@0: TInt DDmaHelper::UpdateRemoteDescriptorLength(TInt aLength) sl@0: { sl@0: OstTraceFunctionEntryExt( DDMAHELPER_UPDATEREMOTEDESCRIPTORLENGTH_ENTRY, this ); sl@0: __KTRACE_DMA(Kern::Printf(">PHYSADDR:UpDesLen(%d)",aLength)); sl@0: sl@0: // Restore request Id (overwritten by KErrNone return code) to stop ASSERT in WriteRemote sl@0: iReq->Id() = DLocalDrive::ERead; sl@0: sl@0: // restore caller's descriptor offset sl@0: iReq->RemoteDesOffset() = iReqRemoteDesOffset; sl@0: sl@0: // Write a zero length descriptor at the end such that the descriptors length is correctly updated. sl@0: TPtrC8 zeroDes(NULL, 0); sl@0: TInt r = iReq->WriteRemote(&zeroDes, aLength); sl@0: sl@0: // restore return code sl@0: iReq->iValue = KErrNone; sl@0: sl@0: OstTraceFunctionExitExt( DDMAHELPER_UPDATEREMOTEDESCRIPTORLENGTH_EXIT, this, r ); sl@0: return r; sl@0: } sl@0: