diff options
Diffstat (limited to 'pvr-source/services4/srvkm/common')
20 files changed, 25658 insertions, 0 deletions
diff --git a/pvr-source/services4/srvkm/common/buffer_manager.c b/pvr-source/services4/srvkm/common/buffer_manager.c new file mode 100644 index 0000000..9ce7a11 --- /dev/null +++ b/pvr-source/services4/srvkm/common/buffer_manager.c @@ -0,0 +1,3573 @@ +/*************************************************************************/ /*! +@Title Buffer management functions for Linux +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Manages buffers mapped into two memory spaces - cpu and device, + either of which can be virtual or physical. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" + +#include "sysconfig.h" +#include "hash.h" +#include "ra.h" +#include "pdump_km.h" +#include "lists.h" + +static IMG_BOOL +ZeroBuf(BM_BUF *pBuf, BM_MAPPING *pMapping, IMG_SIZE_T ui32Bytes, IMG_UINT32 ui32Flags); +static IMG_VOID +BM_FreeMemory (IMG_VOID *pH, IMG_UINTPTR_T base, BM_MAPPING *psMapping); +static IMG_BOOL +BM_ImportMemory(IMG_VOID *pH, IMG_SIZE_T uSize, + IMG_SIZE_T *pActualSize, BM_MAPPING **ppsMapping, + IMG_UINT32 uFlags, IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, IMG_UINTPTR_T *pBase); + +static IMG_INT32 +DevMemoryAlloc (BM_CONTEXT *pBMContext, + BM_MAPPING *pMapping, + IMG_SIZE_T *pActualSize, + IMG_UINT32 uFlags, + IMG_UINT32 dev_vaddr_alignment, + IMG_DEV_VIRTADDR *pDevVAddr); +static IMG_INT32 +DevMemoryFree (BM_MAPPING *pMapping); + +/*! +****************************************************************************** + + @Function AllocMemory + + @Description Allocate a buffer mapped into both cpu and device virtual + address spaces. This is now quite simple: + + 1. Choose whence to get the memory; + 2. Obtain memory from that source; + 3. Work out the actual buffer addresses in other spaces. + + In choosing whence to get the memory we work like this: + + 1. If an import arena exists, use unless BP_CONTIGUOUS is set; + 2. Use a contiguous pool. + + @Input pBMContext - BM context + @Input psBMHeap - BM heap + @Input psDevVAddr - device virtual address (optional) + @Input uSize - requested buffer size in bytes. + @Input uFlags - property flags for the buffer. + @Input uDevVAddrAlignment - required device virtual address + alignment, or 0. + @Input pvPrivData - opaque private data passed through to allocator + @Input ui32PrivDataLength - length of opaque private data + + @Output pBuf - receives a pointer to a descriptor of the allocated + buffer. + @Return IMG_TRUE - Success + IMG_FALSE - Failed. + + *****************************************************************************/ +static IMG_BOOL +AllocMemory (BM_CONTEXT *pBMContext, + BM_HEAP *psBMHeap, + IMG_DEV_VIRTADDR *psDevVAddr, + IMG_SIZE_T uSize, + IMG_UINT32 uFlags, + IMG_UINT32 uDevVAddrAlignment, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINT32 ui32ChunkSize, + IMG_UINT32 ui32NumVirtChunks, + IMG_UINT32 ui32NumPhysChunks, + IMG_BOOL *pabMapChunk, + BM_BUF *pBuf) +{ + BM_MAPPING *pMapping; + IMG_UINTPTR_T uOffset; + RA_ARENA *pArena = IMG_NULL; + + PVR_DPF ((PVR_DBG_MESSAGE, + "AllocMemory (uSize=0x%x, uFlags=0x%x, align=0x%x)", + uSize, uFlags, uDevVAddrAlignment)); + + /* + what to do depends on combination of DevVaddr generation + and backing RAM requirement + */ + if(uFlags & PVRSRV_MEM_RAM_BACKED_ALLOCATION) + { + if(uFlags & PVRSRV_MEM_USER_SUPPLIED_DEVVADDR) + { + /* user supplied DevVAddr, RAM backing */ + PVR_DPF ((PVR_DBG_ERROR, "AllocMemory: combination of DevVAddr management and RAM backing mode unsupported")); + return IMG_FALSE; + } + + /* BM supplied DevVAddr, RAM Backing */ + + /* check heap attributes */ + if(psBMHeap->ui32Attribs + & (PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG + |PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG)) + { + /* specify arena (VM+RAM)*/ + pArena = psBMHeap->pImportArena; + PVR_ASSERT(psBMHeap->sDevArena.psDeviceMemoryHeapInfo->ui32Attribs & PVRSRV_MEM_RAM_BACKED_ALLOCATION); + } + else + { + PVR_DPF ((PVR_DBG_ERROR, "AllocMemory: backing store type doesn't match heap")); + return IMG_FALSE; + } + + /* Now allocate from the arena we chose above. */ + /* in case of a pageable buffer, we must bypass RA which could + * combine/split individual mappings between buffers: + */ + if (uFlags & (PVRSRV_MEM_SPARSE | PVRSRV_HAP_GPU_PAGEABLE)) + { + IMG_BOOL bSuccess; + IMG_SIZE_T puiActualSize; + IMG_SIZE_T uRequestSize = uSize; + + if(uFlags & PVRSRV_MEM_SPARSE) + { + uRequestSize = ui32ChunkSize * ui32NumPhysChunks; + uSize = ui32ChunkSize * ui32NumVirtChunks; + } + + /* Allocate physical memory */ + if (!BM_ImportMemory(psBMHeap, + uRequestSize, + &puiActualSize, + &pMapping, + uFlags, + pvPrivData, + ui32PrivDataLength, + (IMG_UINTPTR_T *)&(pBuf->DevVAddr.uiAddr))) + { + PVR_DPF((PVR_DBG_ERROR, + "BM_ImportMemory: Failed to allocate device memory")); + return IMG_FALSE; + } + pBuf->hOSMemHandle = pMapping->hOSMemHandle; + + /* We allocate VM space for sparse area */ + if(uFlags & PVRSRV_MEM_SPARSE) + { + if (puiActualSize != ui32ChunkSize * ui32NumPhysChunks) + { + /* + * Most likely the chunk size was not host page multiple, + * so return with an error + */ + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: Failed to allocate" + "memory for sparse allocation")); + BM_FreeMemory(pArena, IMG_NULL, pMapping); + return IMG_FALSE; + } + + pMapping->uSizeVM = uSize; + pMapping->ui32ChunkSize = ui32ChunkSize; + pMapping->ui32NumVirtChunks = ui32NumVirtChunks; + pMapping->ui32NumPhysChunks = ui32NumPhysChunks; + pMapping->pabMapChunk = pabMapChunk; + + if (!(uFlags & PVRSRV_HAP_NO_GPU_VIRTUAL_ON_ALLOC)) + { + /* Allocate VA space and map in the physical memory */ + bSuccess = DevMemoryAlloc (pBMContext, + pMapping, + IMG_NULL, + uFlags, + (IMG_UINT32)uDevVAddrAlignment, + &pMapping->DevVAddr); + if (!bSuccess) + { + PVR_DPF((PVR_DBG_ERROR, + "AllocMemory: Failed to allocate device memory")); + BM_FreeMemory(pArena, IMG_NULL, pMapping); + return IMG_FALSE; + } + + /* uDevVAddrAlignment is currently set to zero so QAC + * generates warning which we override */ + /* PRQA S 3356,3358 1 */ + PVR_ASSERT (uDevVAddrAlignment>1?(pMapping->DevVAddr.uiAddr%uDevVAddrAlignment)==0:1); + pBuf->DevVAddr.uiAddr = pMapping->DevVAddr.uiAddr; + } + } + } + else + { + if (!RA_Alloc(pArena, + uSize, + IMG_NULL, + (IMG_VOID*) &pMapping, + uFlags, + uDevVAddrAlignment, + 0, + pvPrivData, + ui32PrivDataLength, + (IMG_UINTPTR_T *)&(pBuf->DevVAddr.uiAddr))) + { + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: RA_Alloc(0x%x) hOSMemHandle %p, flags 0x%08x FAILED", + uSize, pMapping->hOSMemHandle, uFlags)); + return IMG_FALSE; + } + } + + uOffset = pBuf->DevVAddr.uiAddr - pMapping->DevVAddr.uiAddr; + if(pMapping->CpuVAddr) + { + pBuf->CpuVAddr = (IMG_VOID*) ((IMG_UINTPTR_T)pMapping->CpuVAddr + uOffset); + } + else + { + pBuf->CpuVAddr = IMG_NULL; + } + + if(uSize == pMapping->uSizeVM) + { + pBuf->hOSMemHandle = pMapping->hOSMemHandle; + } + else + { + if(OSGetSubMemHandle(pMapping->hOSMemHandle, + uOffset, + uSize, + psBMHeap->ui32Attribs, + &pBuf->hOSMemHandle)!=PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: OSGetSubMemHandle FAILED")); + return IMG_FALSE; + } + } + + /* for hm_contiguous and hm_wrapped memory, the pMapping + * will have a physical address, else 0 */ + pBuf->CpuPAddr.uiAddr = pMapping->CpuPAddr.uiAddr + uOffset; + + if(uFlags & PVRSRV_MEM_ZERO) + { + if(!ZeroBuf(pBuf, pMapping, uSize, psBMHeap->ui32Attribs | uFlags)) + { + return IMG_FALSE; + } + } + } + else + { + if(uFlags & PVRSRV_MEM_USER_SUPPLIED_DEVVADDR) + { + /* user supplied DevVAddr, no RAM backing */ + PVR_ASSERT(psDevVAddr != IMG_NULL); + + if (psDevVAddr == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: invalid parameter - psDevVAddr")); + return IMG_FALSE; + } + + /* just make space in the pagetables */ + pBMContext->psDeviceNode->pfnMMUAlloc (psBMHeap->pMMUHeap, + uSize, + IMG_NULL, + PVRSRV_MEM_USER_SUPPLIED_DEVVADDR, + uDevVAddrAlignment, + psDevVAddr); + + /* setup buf */ + pBuf->DevVAddr = *psDevVAddr; + } + else + { + IMG_BOOL bResult; + /* BM supplied DevVAddr, no RAM Backing */ + + /* just make space in the pagetables */ + bResult = pBMContext->psDeviceNode->pfnMMUAlloc (psBMHeap->pMMUHeap, + uSize, + IMG_NULL, + 0, + uDevVAddrAlignment, + &pBuf->DevVAddr); + + if(!bResult) + { + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: MMUAlloc failed")); + return IMG_FALSE; + } + } + + /* allocate a mocked-up mapping */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (struct _BM_MAPPING_), + (IMG_PVOID *)&pMapping, IMG_NULL, + "Buffer Manager Mapping") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "AllocMemory: OSAllocMem(0x%x) FAILED", sizeof(*pMapping))); + return IMG_FALSE; + } + + /* setup buf */ + pBuf->CpuVAddr = IMG_NULL; + pBuf->hOSMemHandle = 0; + pBuf->CpuPAddr.uiAddr = 0; + + /* setup mapping */ + pMapping->CpuVAddr = IMG_NULL; + pMapping->CpuPAddr.uiAddr = 0; + pMapping->DevVAddr = pBuf->DevVAddr; + pMapping->ui32MappingCount = 1; + pMapping->psSysAddr = IMG_NULL; + pMapping->uSize = uSize; + pMapping->hOSMemHandle = 0; + } + + /* Record the arena pointer in the mapping. */ + pMapping->pArena = pArena; + pMapping->ui32DevVAddrAlignment = uDevVAddrAlignment; + + /* record the heap */ + pMapping->pBMHeap = psBMHeap; + pBuf->pMapping = pMapping; + + /* output some stats */ + PVR_DPF ((PVR_DBG_MESSAGE, + "AllocMemory: pMapping=%08x: DevV=%08X CpuV=%08x CpuP=%08X uSize=0x%x", + (IMG_UINTPTR_T)pMapping, + pMapping->DevVAddr.uiAddr, + (IMG_UINTPTR_T)pMapping->CpuVAddr, + pMapping->CpuPAddr.uiAddr, + pMapping->uSize)); + + PVR_DPF ((PVR_DBG_MESSAGE, + "AllocMemory: pBuf=%08x: DevV=%08X CpuV=%08x CpuP=%08X uSize=0x%x", + (IMG_UINTPTR_T)pBuf, + pBuf->DevVAddr.uiAddr, + (IMG_UINTPTR_T)pBuf->CpuVAddr, + pBuf->CpuPAddr.uiAddr, + uSize)); + + /* Verify virtual device address alignment */ + PVR_ASSERT(((pBuf->DevVAddr.uiAddr) & (uDevVAddrAlignment - 1)) == 0); + + return IMG_TRUE; +} + + +/*! +****************************************************************************** + + @Function WrapMemory + + @Description Allocate a buffer mapped into both cpu and device virtual + address spaces. + + @Input psBMHeap - BM heap + @Input uSize - requested buffer size in bytes. + @Input ui32BaseOffset - Offset from page of wrap. + @Input bPhysContig - Is the wrap physically contiguous. + @Input psAddr - List of pages to wrap. + @Input pvCPUVAddr - Optional CPU Kernel virtual address (page aligned) of memory to wrap + @Input uFlags - property flags for the buffer. + @Output Buf - receives a pointer to a descriptor of the allocated + buffer. + @Return IMG_TRUE - Success + IMG_FALSE - Failed. + + *****************************************************************************/ +static IMG_BOOL +WrapMemory (BM_HEAP *psBMHeap, + IMG_SIZE_T uSize, + IMG_SIZE_T ui32BaseOffset, + IMG_BOOL bPhysContig, + IMG_SYS_PHYADDR *psAddr, + IMG_VOID *pvCPUVAddr, + IMG_UINT32 uFlags, + BM_BUF *pBuf) +{ + IMG_DEV_VIRTADDR DevVAddr = {0}; + BM_MAPPING *pMapping; + IMG_INT32 bResult; + IMG_SIZE_T const ui32PageSize = HOST_PAGESIZE(); + + PVR_DPF ((PVR_DBG_MESSAGE, + "WrapMemory(psBMHeap=%08X, size=0x%x, offset=0x%x, bPhysContig=0x%x, pvCPUVAddr = 0x%08x, flags=0x%x)", + (IMG_UINTPTR_T)psBMHeap, uSize, ui32BaseOffset, bPhysContig, (IMG_UINTPTR_T)pvCPUVAddr, uFlags)); + + PVR_ASSERT((psAddr->uiAddr & (ui32PageSize - 1)) == 0); + /* Only need lower 12 bits of the cpu addr - don't care what size a void* is */ + PVR_ASSERT(((IMG_UINTPTR_T)pvCPUVAddr & (ui32PageSize - 1)) == 0); + + uSize += ui32BaseOffset; + uSize = HOST_PAGEALIGN (uSize); + + /* allocate a mocked-up mapping */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*pMapping), + (IMG_PVOID *)&pMapping, IMG_NULL, + "Mocked-up mapping") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSAllocMem(0x%x) FAILED",sizeof(*pMapping))); + return IMG_FALSE; + } + + OSMemSet(pMapping, 0, sizeof (*pMapping)); + + pMapping->uSize = uSize; + pMapping->uSizeVM = uSize; + pMapping->pBMHeap = psBMHeap; + + if(pvCPUVAddr) + { + pMapping->CpuVAddr = pvCPUVAddr; + + if (bPhysContig) + { + pMapping->eCpuMemoryOrigin = hm_wrapped_virtaddr; + pMapping->CpuPAddr = SysSysPAddrToCpuPAddr(psAddr[0]); + + if(OSRegisterMem(pMapping->CpuPAddr, + pMapping->CpuVAddr, + pMapping->uSize, + uFlags, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSRegisterMem Phys=0x%08X, Size=%d) failed", + pMapping->CpuPAddr.uiAddr, pMapping->uSize)); + goto fail_cleanup; + } + } + else + { + pMapping->eCpuMemoryOrigin = hm_wrapped_scatter_virtaddr; + pMapping->psSysAddr = psAddr; + + if(OSRegisterDiscontigMem(pMapping->psSysAddr, + pMapping->CpuVAddr, + pMapping->uSize, + uFlags, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSRegisterDiscontigMem Size=%d) failed", + pMapping->uSize)); + goto fail_cleanup; + } + } + } + else + { + if (bPhysContig) + { + pMapping->eCpuMemoryOrigin = hm_wrapped; + pMapping->CpuPAddr = SysSysPAddrToCpuPAddr(psAddr[0]); + + if(OSReservePhys(pMapping->CpuPAddr, + pMapping->uSize, + uFlags, + IMG_NULL, + &pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSReservePhys Phys=0x%08X, Size=%d) failed", + pMapping->CpuPAddr.uiAddr, pMapping->uSize)); + goto fail_cleanup; + } + } + else + { + pMapping->eCpuMemoryOrigin = hm_wrapped_scatter; + pMapping->psSysAddr = psAddr; + + if(OSReserveDiscontigPhys(pMapping->psSysAddr, + pMapping->uSize, + uFlags, + &pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSReserveDiscontigPhys Size=%d) failed", + pMapping->uSize)); + goto fail_cleanup; + } + } + } + + /* + * Allocate device memory for this buffer. Map wrapped pages as read/write + */ + bResult = DevMemoryAlloc(psBMHeap->pBMContext, + pMapping, + IMG_NULL, + uFlags | PVRSRV_MEM_READ | PVRSRV_MEM_WRITE, + IMG_CAST_TO_DEVVADDR_UINT(ui32PageSize), + &DevVAddr); + if (bResult <= 0) + { + PVR_DPF((PVR_DBG_ERROR, + "WrapMemory: DevMemoryAlloc(0x%x) failed", + pMapping->uSize)); + goto fail_cleanup; + } + + /* + * Determine the offset of this allocation within the underlying + * dual mapped chunk of memory, we can assume that all three + * addresses associated with this allocation are placed at the same + * offset within the underlying chunk. + */ + pBuf->CpuPAddr.uiAddr = pMapping->CpuPAddr.uiAddr + ui32BaseOffset; + if(!ui32BaseOffset) + { + pBuf->hOSMemHandle = pMapping->hOSMemHandle; + } + else + { + if(OSGetSubMemHandle(pMapping->hOSMemHandle, + ui32BaseOffset, + (pMapping->uSize-ui32BaseOffset), + uFlags, + &pBuf->hOSMemHandle)!=PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "WrapMemory: OSGetSubMemHandle failed")); + goto fail_cleanup; + } + } + if(pMapping->CpuVAddr) + { + pBuf->CpuVAddr = (IMG_VOID*) ((IMG_UINTPTR_T)pMapping->CpuVAddr + ui32BaseOffset); + } + pBuf->DevVAddr.uiAddr = pMapping->DevVAddr.uiAddr + IMG_CAST_TO_DEVVADDR_UINT(ui32BaseOffset); + + if(uFlags & PVRSRV_MEM_ZERO) + { + if(!ZeroBuf(pBuf, pMapping, uSize, uFlags)) + { + return IMG_FALSE; + } + } + + PVR_DPF ((PVR_DBG_MESSAGE, "DevVaddr.uiAddr=%08X", DevVAddr.uiAddr)); + PVR_DPF ((PVR_DBG_MESSAGE, + "WrapMemory: DevV=%08X CpuP=%08X uSize=0x%x", + pMapping->DevVAddr.uiAddr, pMapping->CpuPAddr.uiAddr, pMapping->uSize)); + PVR_DPF ((PVR_DBG_MESSAGE, + "WrapMemory: DevV=%08X CpuP=%08X uSize=0x%x", + pBuf->DevVAddr.uiAddr, pBuf->CpuPAddr.uiAddr, uSize)); + + pBuf->pMapping = pMapping; + return IMG_TRUE; + +fail_cleanup: + if(ui32BaseOffset && pBuf->hOSMemHandle) + { + OSReleaseSubMemHandle(pBuf->hOSMemHandle, uFlags); + } + + if(pMapping && (pMapping->CpuVAddr || pMapping->hOSMemHandle)) + { + switch(pMapping->eCpuMemoryOrigin) + { + case hm_wrapped: + OSUnReservePhys(pMapping->CpuVAddr, pMapping->uSize, uFlags, pMapping->hOSMemHandle); + break; + case hm_wrapped_virtaddr: + OSUnRegisterMem(pMapping->CpuVAddr, pMapping->uSize, uFlags, pMapping->hOSMemHandle); + break; + case hm_wrapped_scatter: + OSUnReserveDiscontigPhys(pMapping->CpuVAddr, pMapping->uSize, uFlags, pMapping->hOSMemHandle); + break; + case hm_wrapped_scatter_virtaddr: + OSUnRegisterDiscontigMem(pMapping->CpuVAddr, pMapping->uSize, uFlags, pMapping->hOSMemHandle); + break; + default: + break; + } + + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_MAPPING), pMapping, IMG_NULL); + /*not nulling pointer, out of scope*/ + + return IMG_FALSE; +} + + +static IMG_BOOL +ZeroBuf(BM_BUF *pBuf, BM_MAPPING *pMapping, IMG_SIZE_T ui32Bytes, IMG_UINT32 ui32Flags) +{ + IMG_VOID *pvCpuVAddr; + + if(pBuf->CpuVAddr) + { + OSMemSet(pBuf->CpuVAddr, 0, ui32Bytes); + } + else if(pMapping->eCpuMemoryOrigin == hm_contiguous + || pMapping->eCpuMemoryOrigin == hm_wrapped) + { + pvCpuVAddr = OSMapPhysToLin(pBuf->CpuPAddr, + ui32Bytes, + PVRSRV_HAP_KERNEL_ONLY + | (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK), + IMG_NULL); + if(!pvCpuVAddr) + { + PVR_DPF((PVR_DBG_ERROR, "ZeroBuf: OSMapPhysToLin for contiguous buffer failed")); + return IMG_FALSE; + } + OSMemSet(pvCpuVAddr, 0, ui32Bytes); + OSUnMapPhysToLin(pvCpuVAddr, + ui32Bytes, + PVRSRV_HAP_KERNEL_ONLY + | (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK), + IMG_NULL); + } + else + { + IMG_SIZE_T ui32BytesRemaining = ui32Bytes; + IMG_SIZE_T ui32CurrentOffset = 0; + IMG_CPU_PHYADDR CpuPAddr; + + /* Walk through the pBuf one page at a time and use + * transient mappings to zero the memory */ + + PVR_ASSERT(pBuf->hOSMemHandle); + + while(ui32BytesRemaining > 0) + { + IMG_SIZE_T ui32BlockBytes = MIN(ui32BytesRemaining, HOST_PAGESIZE()); + CpuPAddr = OSMemHandleToCpuPAddr(pBuf->hOSMemHandle, ui32CurrentOffset); + /* If the CpuPAddr isn't page aligned then start by writing up to the next page + * boundary (or ui32BytesRemaining if less), so that subsequent iterations can + * copy full physical pages. */ + if(CpuPAddr.uiAddr & (HOST_PAGESIZE() -1)) + { + ui32BlockBytes = + MIN(ui32BytesRemaining, (IMG_UINT32)(HOST_PAGEALIGN(CpuPAddr.uiAddr) - CpuPAddr.uiAddr)); + } + + pvCpuVAddr = OSMapPhysToLin(CpuPAddr, + ui32BlockBytes, + PVRSRV_HAP_KERNEL_ONLY + | (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK), + IMG_NULL); + if(!pvCpuVAddr) + { + PVR_DPF((PVR_DBG_ERROR, "ZeroBuf: OSMapPhysToLin while zeroing non-contiguous memory FAILED")); + return IMG_FALSE; + } + OSMemSet(pvCpuVAddr, 0, ui32BlockBytes); + OSUnMapPhysToLin(pvCpuVAddr, + ui32BlockBytes, + PVRSRV_HAP_KERNEL_ONLY + | (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK), + IMG_NULL); + + ui32BytesRemaining -= ui32BlockBytes; + ui32CurrentOffset += ui32BlockBytes; + } + } + + return IMG_TRUE; +} + +/*! +****************************************************************************** + + @Function FreeBuf + + @Description Free a buffer previously allocated with BM_Alloc() or unwrap + one previous wrapped with BM_Wrap(). + The buffer is identified by the buffer descriptor pBuf + returned at allocation. Note the double indirection when + passing the buffer. + + + @Input pBuf - buffer descriptor to free. + @Input ui32Flags - flags + @Input bFromAllocator - Is this being called by the + allocator? + + @Return None. + + *****************************************************************************/ +static IMG_VOID +FreeBuf (BM_BUF *pBuf, IMG_UINT32 ui32Flags, IMG_BOOL bFromAllocator) +{ + BM_MAPPING *pMapping; + PVRSRV_DEVICE_NODE *psDeviceNode; + + PVR_DPF ((PVR_DBG_MESSAGE, + "FreeBuf: pBuf=0x%x: DevVAddr=%08X CpuVAddr=0x%x CpuPAddr=%08X", + (IMG_UINTPTR_T)pBuf, pBuf->DevVAddr.uiAddr, + (IMG_UINTPTR_T)pBuf->CpuVAddr, pBuf->CpuPAddr.uiAddr)); + + /* record mapping */ + pMapping = pBuf->pMapping; + + psDeviceNode = pMapping->pBMHeap->pBMContext->psDeviceNode; + if (psDeviceNode->pfnCacheInvalidate) + { + psDeviceNode->pfnCacheInvalidate(psDeviceNode); + } + + if(ui32Flags & PVRSRV_MEM_USER_SUPPLIED_DEVVADDR) + { + /* Submemhandle is required by exported mappings */ + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + /* user supplied Device Virtual Address */ + if(ui32Flags & PVRSRV_MEM_RAM_BACKED_ALLOCATION) + { + /* RAM backed allocation */ + PVR_DPF ((PVR_DBG_ERROR, "FreeBuf: combination of DevVAddr management and RAM backing mode unsupported")); + } + else + { + /* free the mocked-up mapping */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_MAPPING), pMapping, IMG_NULL); + pBuf->pMapping = IMG_NULL; /*nulling pointer alias*/ + } + } + } + else + { + /* BM supplied Device Virtual Address */ + if(pBuf->hOSMemHandle != pMapping->hOSMemHandle) + { + /* Submemhandle is required by exported mappings */ + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + OSReleaseSubMemHandle(pBuf->hOSMemHandle, ui32Flags); + } + } + + if(ui32Flags & PVRSRV_MEM_RAM_BACKED_ALLOCATION) + { + /* Submemhandle is required by exported mappings */ + + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + /* + RAM backed allocation + Note: currently no need to distinguish between hm_env and hm_contiguous + */ + PVR_ASSERT(pBuf->ui32ExportCount == 0); + if (pBuf->pMapping->ui32Flags & (PVRSRV_MEM_SPARSE | PVRSRV_HAP_GPU_PAGEABLE)) + { + IMG_UINT32 ui32FreeSize = 0; + IMG_PVOID pvFreePtr = IMG_NULL; + + if(pBuf->pMapping->ui32Flags & PVRSRV_MEM_SPARSE) + { + ui32FreeSize = sizeof(IMG_BOOL) * pBuf->pMapping->ui32NumVirtChunks; + pvFreePtr = pBuf->pMapping->pabMapChunk; + } + + /* With sparse and page-able allocations we don't go through the sub-alloc RA */ + BM_FreeMemory(pBuf->pMapping->pBMHeap, pBuf->DevVAddr.uiAddr, pBuf->pMapping); + + if(pvFreePtr) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + ui32FreeSize, + pvFreePtr, + IMG_NULL); + } + } + else + { + RA_Free (pBuf->pMapping->pArena, pBuf->DevVAddr.uiAddr, IMG_FALSE); + } + } + } + else + { + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + switch (pMapping->eCpuMemoryOrigin) + { + case hm_wrapped: + OSUnReservePhys(pMapping->CpuVAddr, pMapping->uSize, ui32Flags, pMapping->hOSMemHandle); + break; + case hm_wrapped_virtaddr: + OSUnRegisterMem(pMapping->CpuVAddr, pMapping->uSize, ui32Flags, pMapping->hOSMemHandle); + break; + case hm_wrapped_scatter: + OSUnReserveDiscontigPhys(pMapping->CpuVAddr, pMapping->uSize, ui32Flags, pMapping->hOSMemHandle); + break; + case hm_wrapped_scatter_virtaddr: + OSUnRegisterDiscontigMem(pMapping->CpuVAddr, pMapping->uSize, ui32Flags, pMapping->hOSMemHandle); + break; + default: + break; + } + } + if (bFromAllocator) + DevMemoryFree (pMapping); + + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + /* free the mocked-up mapping */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_MAPPING), pMapping, IMG_NULL); + pBuf->pMapping = IMG_NULL; /*nulling pointer alias*/ + } + } + } + + + if ((pBuf->ui32ExportCount == 0) && (pBuf->ui32RefCount == 0)) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_BUF), pBuf, IMG_NULL); + /*not nulling pointer, copy on stack*/ + } +} + +/*! +****************************************************************************** + + @Function BM_DestroyContext_AnyCb + + @Description Destroy a buffer manager heap. + + @Input psBMHeap + + @Return PVRSRV_ERROR + + *****************************************************************************/ +static PVRSRV_ERROR BM_DestroyContext_AnyCb(BM_HEAP *psBMHeap) +{ + if(psBMHeap->ui32Attribs + & (PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG + |PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG)) + { + if (psBMHeap->pImportArena) + { + IMG_BOOL bTestDelete = RA_TestDelete(psBMHeap->pImportArena); + if (!bTestDelete) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_DestroyContext_AnyCb: RA_TestDelete failed")); + return PVRSRV_ERROR_UNABLE_TO_DESTROY_BM_HEAP; + } + } + } + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function BM_DestroyContext + + @Description Destroy a buffer manager context. All allocated buffers must be + free'd before calling this function. This function is called + also to perform cleanup during aborted initialisations so it's + fairly careful not to assume any given resource has really been + created/allocated. + + @Return PVRSRV_ERROR + + *****************************************************************************/ +PVRSRV_ERROR +BM_DestroyContext(IMG_HANDLE hBMContext, + IMG_BOOL *pbDestroyed) +{ + PVRSRV_ERROR eError; + BM_CONTEXT *pBMContext = (BM_CONTEXT*)hBMContext; + + PVR_DPF ((PVR_DBG_MESSAGE, "BM_DestroyContext")); + + if (pbDestroyed != IMG_NULL) + { + *pbDestroyed = IMG_FALSE; + } + + /* + Exit straight away if it's an invalid context handle + */ + if (pBMContext == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_DestroyContext: Invalid handle")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + pBMContext->ui32RefCount--; + + if (pBMContext->ui32RefCount > 0) + { + /* Just return if there are more references to this context */ + return PVRSRV_OK; + } + + /* + Check whether there is a bug in the client which brought it here before + all the allocations have been freed. + */ + eError = List_BM_HEAP_PVRSRV_ERROR_Any(pBMContext->psBMHeap, &BM_DestroyContext_AnyCb); + if(eError != PVRSRV_OK) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_DestroyContext: List_BM_HEAP_PVRSRV_ERROR_Any failed")); + return eError; + } + else + { + /* free the device memory context */ + eError = ResManFreeResByPtr(pBMContext->hResItem, CLEANUP_WITH_POLL); + if(eError != PVRSRV_OK) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_DestroyContext: ResManFreeResByPtr failed %d",eError)); + return eError; + } + + /* mark context as destroyed */ + if (pbDestroyed != IMG_NULL) + { + *pbDestroyed = IMG_TRUE; + } + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function BM_DestroyContextCallBack_AnyVaCb + + @Description Destroy Device memory context + + @Input psBMHeap - heap to be freed. + @Input va - list of variable arguments with the following contents: + - psDeviceNode + @Return PVRSRV_ERROR + + *****************************************************************************/ +static PVRSRV_ERROR BM_DestroyContextCallBack_AnyVaCb(BM_HEAP *psBMHeap, va_list va) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + psDeviceNode = va_arg(va, PVRSRV_DEVICE_NODE*); + + /* Free up the import arenas */ + if(psBMHeap->ui32Attribs + & (PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG + |PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG)) + { + if (psBMHeap->pImportArena) + { + RA_Delete (psBMHeap->pImportArena); + } + } + else + { + PVR_DPF((PVR_DBG_ERROR, "BM_DestroyContext: backing store type unsupported")); + return PVRSRV_ERROR_UNSUPPORTED_BACKING_STORE; + } + + /* Free up the MMU Heaps */ + psDeviceNode->pfnMMUDelete(psBMHeap->pMMUHeap); + + /* Free Heap memory */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_HEAP), psBMHeap, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function BM_DestroyContextCallBack + + @Description Destroy Device memory context + + @Input pvParam - opaque void ptr param + @Input ui32Param - opaque unsigned long param + + @Return PVRSRV_ERROR + + *****************************************************************************/ +static PVRSRV_ERROR BM_DestroyContextCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + BM_CONTEXT *pBMContext = pvParam; + PVRSRV_DEVICE_NODE *psDeviceNode; + PVRSRV_ERROR eError; +/* BM_CONTEXT **ppBMContext; + BM_HEAP *psBMHeap, *psTmpBMHeap;*/ + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + /* + Get DeviceNode from BMcontext + */ + psDeviceNode = pBMContext->psDeviceNode; + + /* + Free the import arenas and heaps + */ + eError = List_BM_HEAP_PVRSRV_ERROR_Any_va(pBMContext->psBMHeap, + &BM_DestroyContextCallBack_AnyVaCb, + psDeviceNode); + if (eError != PVRSRV_OK) + { + return eError; + } + /* + 'Finalise' the MMU + */ + if (pBMContext->psMMUContext) + { + psDeviceNode->pfnMMUFinalise(pBMContext->psMMUContext); + } + + /* + Free up generic, useful resources - if they were allocated. + */ + if (pBMContext->pBufferHash) + { + HASH_Delete(pBMContext->pBufferHash); + } + + if (pBMContext == psDeviceNode->sDevMemoryInfo.pBMKernelContext) + { + /* Freeing the kernel context */ + psDeviceNode->sDevMemoryInfo.pBMKernelContext = IMG_NULL; + } + else + { + if (pBMContext->ppsThis != IMG_NULL) + { + /* + * Remove context from the linked list + */ + List_BM_CONTEXT_Remove(pBMContext); + } + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_CONTEXT), pBMContext, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +static IMG_HANDLE BM_CreateContext_IncRefCount_AnyVaCb(BM_CONTEXT *pBMContext, va_list va) +{ + PRESMAN_CONTEXT hResManContext; + hResManContext = va_arg(va, PRESMAN_CONTEXT); + if(ResManFindResourceByPtr(hResManContext, pBMContext->hResItem) == PVRSRV_OK) + { + /* just increment the refcount and return the memory context found for this process */ + pBMContext->ui32RefCount++; + return pBMContext; + } + return IMG_NULL; +} + +static IMG_VOID BM_CreateContext_InsertHeap_ForEachVaCb(BM_HEAP *psBMHeap, va_list va) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + BM_CONTEXT *pBMContext; + psDeviceNode = va_arg(va, PVRSRV_DEVICE_NODE*); + pBMContext = va_arg(va, BM_CONTEXT*); + switch(psBMHeap->sDevArena.DevMemHeapType) + { + case DEVICE_MEMORY_HEAP_SHARED: + case DEVICE_MEMORY_HEAP_SHARED_EXPORTED: + { + /* insert the heap into the device's MMU page directory/table */ + psDeviceNode->pfnMMUInsertHeap(pBMContext->psMMUContext, psBMHeap->pMMUHeap); + break; + } + } +} + +/*! +****************************************************************************** + + @Function BM_CreateContext + + @Description Creates and initialises a buffer manager context. This function must be called + before any other buffer manager functions. + + @Return valid BM context handle - Success + IMG_NULL - Failed + + *****************************************************************************/ +IMG_HANDLE +BM_CreateContext(PVRSRV_DEVICE_NODE *psDeviceNode, + IMG_DEV_PHYADDR *psPDDevPAddr, + PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_BOOL *pbCreated) +{ + BM_CONTEXT *pBMContext; +/* BM_HEAP *psBMHeap;*/ + DEVICE_MEMORY_INFO *psDevMemoryInfo; + IMG_BOOL bKernelContext; + PRESMAN_CONTEXT hResManContext; + + PVR_DPF((PVR_DBG_MESSAGE, "BM_CreateContext")); + + if (psPerProc == IMG_NULL) + { + bKernelContext = IMG_TRUE; + hResManContext = psDeviceNode->hResManContext; + } + else + { + bKernelContext = IMG_FALSE; + hResManContext = psPerProc->hResManContext; + } + + if (pbCreated != IMG_NULL) + { + *pbCreated = IMG_FALSE; + } + + /* setup the device memory info. */ + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + + if (bKernelContext == IMG_FALSE) + { + IMG_HANDLE res = (IMG_HANDLE) List_BM_CONTEXT_Any_va(psDevMemoryInfo->pBMContext, + &BM_CreateContext_IncRefCount_AnyVaCb, + hResManContext); + if (res) + { + return res; + } + } + + /* allocate a BM context */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (struct _BM_CONTEXT_), + (IMG_PVOID *)&pBMContext, IMG_NULL, + "Buffer Manager Context") != PVRSRV_OK) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_CreateContext: Alloc failed")); + return IMG_NULL; + } + OSMemSet(pBMContext, 0, sizeof (BM_CONTEXT)); + + /* store the associated devicenode */ + pBMContext->psDeviceNode = psDeviceNode; + + /* This hash table is used to store BM_Wraps in a global way */ + /* INTEGRATION_POINT: 32 is an abitrary limit on the number of hashed BM_wraps */ + pBMContext->pBufferHash = HASH_Create(32); + if (pBMContext->pBufferHash==IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_CreateContext: HASH_Create failed")); + goto cleanup; + } + + if((IMG_NULL == psDeviceNode->pfnMMUInitialise) || (psDeviceNode->pfnMMUInitialise(psDeviceNode, + &pBMContext->psMMUContext, + psPDDevPAddr) != PVRSRV_OK)) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateContext: MMUInitialise failed")); + goto cleanup; + } + + if(bKernelContext) + { + /* just save the kernel context */ + PVR_ASSERT(psDevMemoryInfo->pBMKernelContext == IMG_NULL); + psDevMemoryInfo->pBMKernelContext = pBMContext; + } + else + { + /* + On the creation of each new context we must + insert the kernel context's 'shared' and 'shared_exported' + heaps into the new context + - check the kernel context and heaps exist + */ + PVR_ASSERT(psDevMemoryInfo->pBMKernelContext); + + if (psDevMemoryInfo->pBMKernelContext == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateContext: psDevMemoryInfo->pBMKernelContext invalid")); + goto cleanup; + } + + PVR_ASSERT(psDevMemoryInfo->pBMKernelContext->psBMHeap); + + /* + insert the kernel heaps structures into the new context's shared heap list + Note. this will include the kernel only heaps but these will not actually + be imported into the context nor returned to the client + */ + pBMContext->psBMSharedHeap = psDevMemoryInfo->pBMKernelContext->psBMHeap; + + /* + insert the shared heaps into the MMU page directory/table + for the new context + */ + List_BM_HEAP_ForEach_va(pBMContext->psBMSharedHeap, + &BM_CreateContext_InsertHeap_ForEachVaCb, + psDeviceNode, + pBMContext); + + /* Finally, insert the new context into the list of BM contexts */ + List_BM_CONTEXT_Insert(&psDevMemoryInfo->pBMContext, pBMContext); + } + + /* Increment the refcount, as creation is successful */ + pBMContext->ui32RefCount++; + + /* register with resman */ + pBMContext->hResItem = ResManRegisterRes(hResManContext, + RESMAN_TYPE_DEVICEMEM_CONTEXT, + pBMContext, + 0, + &BM_DestroyContextCallBack); + if (pBMContext->hResItem == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_CreateContext: ResManRegisterRes failed")); + goto cleanup; + } + + if (pbCreated != IMG_NULL) + { + *pbCreated = IMG_TRUE; + } + return (IMG_HANDLE)pBMContext; + +cleanup: + (IMG_VOID)BM_DestroyContextCallBack(pBMContext, 0, CLEANUP_WITH_POLL); + + return IMG_NULL; +} + + +static IMG_VOID *BM_CreateHeap_AnyVaCb(BM_HEAP *psBMHeap, va_list va) +{ + DEVICE_MEMORY_HEAP_INFO *psDevMemHeapInfo; + psDevMemHeapInfo = va_arg(va, DEVICE_MEMORY_HEAP_INFO*); + if (psBMHeap->sDevArena.ui32HeapID == psDevMemHeapInfo->ui32HeapID) + { + /* Match - just return already created heap */ + return psBMHeap; + } + else + { + return IMG_NULL; + } +} + +/*! +****************************************************************************** + + @Function BM_CreateHeap + + @Description Creates and initialises a BM heap for a given BM context. + + @Return + valid heap handle - success + IMG_NULL - failure + + + *****************************************************************************/ +IMG_HANDLE +BM_CreateHeap (IMG_HANDLE hBMContext, + DEVICE_MEMORY_HEAP_INFO *psDevMemHeapInfo) +{ + BM_CONTEXT *pBMContext = (BM_CONTEXT*)hBMContext; + PVRSRV_DEVICE_NODE *psDeviceNode; + BM_HEAP *psBMHeap; + + PVR_DPF((PVR_DBG_MESSAGE, "BM_CreateHeap")); + + if(!pBMContext) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateHeap: BM_CONTEXT null")); + return IMG_NULL; + } + + psDeviceNode = pBMContext->psDeviceNode; + + /* + * Ensure that the heap size is a multiple of the data page size. + */ + PVR_ASSERT((psDevMemHeapInfo->ui32HeapSize & (psDevMemHeapInfo->ui32DataPageSize - 1)) == 0); + PVR_ASSERT(psDevMemHeapInfo->ui32HeapSize > 0); + + /* + We may be being asked to create a heap in a context which already has one. + Test for refcount > 0 because PVRSRVGetDeviceMemHeapInfoKM doesn't increment the refcount. + This does mean that the first call to PVRSRVCreateDeviceMemContextKM will first try to find + heaps that we already know don't exist + */ + if(pBMContext->ui32RefCount > 0) + { + psBMHeap = (BM_HEAP*)List_BM_HEAP_Any_va(pBMContext->psBMHeap, + &BM_CreateHeap_AnyVaCb, + psDevMemHeapInfo); + + if (psBMHeap) + { + return psBMHeap; + } + } + + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (BM_HEAP), + (IMG_PVOID *)&psBMHeap, IMG_NULL, + "Buffer Manager Heap") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateHeap: Alloc failed")); + return IMG_NULL; + } + + OSMemSet (psBMHeap, 0, sizeof (BM_HEAP)); + + psBMHeap->sDevArena.ui32HeapID = psDevMemHeapInfo->ui32HeapID; + psBMHeap->sDevArena.pszName = psDevMemHeapInfo->pszName; + psBMHeap->sDevArena.BaseDevVAddr = psDevMemHeapInfo->sDevVAddrBase; + psBMHeap->sDevArena.ui32Size = psDevMemHeapInfo->ui32HeapSize; + psBMHeap->sDevArena.DevMemHeapType = psDevMemHeapInfo->DevMemHeapType; + psBMHeap->sDevArena.ui32DataPageSize = psDevMemHeapInfo->ui32DataPageSize; + psBMHeap->sDevArena.psDeviceMemoryHeapInfo = psDevMemHeapInfo; + psBMHeap->ui32Attribs = psDevMemHeapInfo->ui32Attribs; +#if defined(SUPPORT_MEMORY_TILING) + psBMHeap->ui32XTileStride = psDevMemHeapInfo->ui32XTileStride; +#endif + + /* tie the heap to the context */ + psBMHeap->pBMContext = pBMContext; + + psBMHeap->pMMUHeap = psDeviceNode->pfnMMUCreate (pBMContext->psMMUContext, + &psBMHeap->sDevArena, + &psBMHeap->pVMArena, + &psBMHeap->psMMUAttrib); + if (!psBMHeap->pMMUHeap) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateHeap: MMUCreate failed")); + goto ErrorExit; + } + + /* memory is allocated from the OS as required */ + psBMHeap->pImportArena = RA_Create (psDevMemHeapInfo->pszBSName, + 0, 0, IMG_NULL, + MAX(HOST_PAGESIZE(), psBMHeap->sDevArena.ui32DataPageSize), + &BM_ImportMemory, + &BM_FreeMemory, + IMG_NULL, + psBMHeap); + if(psBMHeap->pImportArena == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateHeap: RA_Create failed")); + goto ErrorExit; + } + + if(psBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG) + { + /* + memory comes from a device memory contiguous allocator (ra) + Note: these arenas are shared across the system so don't delete + as part of heap destroy + */ + psBMHeap->pLocalDevMemArena = psDevMemHeapInfo->psLocalDevMemArena; + if(psBMHeap->pLocalDevMemArena == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_CreateHeap: LocalDevMemArena null")); + goto ErrorExit; + } + } + + /* insert heap into head of the heap list */ + List_BM_HEAP_Insert(&pBMContext->psBMHeap, psBMHeap); + + return (IMG_HANDLE)psBMHeap; + + /* handle error case */ +ErrorExit: + + /* Free up the MMU if we created one */ + if (psBMHeap->pMMUHeap != IMG_NULL) + { + psDeviceNode->pfnMMUDelete (psBMHeap->pMMUHeap); + /* don't finalise psMMUContext as we don't own it */ + } + + /* Free the Heap memory */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_HEAP), psBMHeap, IMG_NULL); + /*not nulling pointer, out of scope*/ + + return IMG_NULL; +} + +/*! +****************************************************************************** + + @Function BM_DestroyHeap + + @Description Destroys a BM heap + + @Return + valid heap handle - success + IMG_NULL - failure + + + *****************************************************************************/ +IMG_VOID +BM_DestroyHeap (IMG_HANDLE hDevMemHeap) +{ + BM_HEAP* psBMHeap = (BM_HEAP*)hDevMemHeap; + PVRSRV_DEVICE_NODE *psDeviceNode = psBMHeap->pBMContext->psDeviceNode; + + PVR_DPF((PVR_DBG_MESSAGE, "BM_DestroyHeap")); + + if(psBMHeap) + { + /* Free up the import arenas */ + if(psBMHeap->ui32Attribs + & (PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG + |PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG)) + { + if (psBMHeap->pImportArena) + { + RA_Delete (psBMHeap->pImportArena); + } + } + else + { + PVR_DPF((PVR_DBG_ERROR, "BM_DestroyHeap: backing store type unsupported")); + return; + } + + /* Free up the MMU Heap */ + psDeviceNode->pfnMMUDelete (psBMHeap->pMMUHeap); + + /* remove from the heap list */ + List_BM_HEAP_Remove(psBMHeap); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_HEAP), psBMHeap, IMG_NULL); + } + else + { + PVR_DPF ((PVR_DBG_ERROR, "BM_DestroyHeap: invalid heap handle")); + } +} + + +/*! +****************************************************************************** + + @Function BM_Reinitialise + + @Description Reinitialise the buffer manager after a power down event. + + @Return IMG_TRUE - Success + IMG_FALSE - Failed + + *****************************************************************************/ +IMG_BOOL +BM_Reinitialise (PVRSRV_DEVICE_NODE *psDeviceNode) +{ + + PVR_DPF((PVR_DBG_MESSAGE, "BM_Reinitialise")); + PVR_UNREFERENCED_PARAMETER(psDeviceNode); + + /* FIXME: Need to reenable all contexts + List_BM_CONTEXT_ForEach(psDeviceNode->sDevMemoryInfo.pBMContext, MMU_Enable); + */ + + return IMG_TRUE; +} + +/*! +****************************************************************************** + + @Function BM_Alloc + + @Description Allocate a buffer mapped into both cpu and device virtual + memory maps. + + @Input hDevMemHeap + @Input psDevVAddr - device virtual address specified by caller (optional) + @Input uSize - require size in bytes of the buffer. + @Input pui32Flags - bit mask of buffer property flags. + @Input uDevVAddrAlignment - required alignment in bytes, or 0. + @Input pvPrivData - opaque private data passed through to allocator + @Input ui32PrivDataLength - length of opaque private data + + @Output phBuf - receives buffer handle + @Output pui32Flags - bit mask of heap property flags. + + @Return IMG_TRUE - Success + IMG_FALSE - Failure + + *****************************************************************************/ +IMG_BOOL +BM_Alloc ( IMG_HANDLE hDevMemHeap, + IMG_DEV_VIRTADDR *psDevVAddr, + IMG_SIZE_T uSize, + IMG_UINT32 *pui32Flags, + IMG_UINT32 uDevVAddrAlignment, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINT32 ui32ChunkSize, + IMG_UINT32 ui32NumVirtChunks, + IMG_UINT32 ui32NumPhysChunks, + IMG_BOOL *pabMapChunk, + BM_HANDLE *phBuf) +{ + BM_BUF *pBuf; + BM_CONTEXT *pBMContext; + BM_HEAP *psBMHeap; + SYS_DATA *psSysData; + IMG_UINT32 uFlags; + + if (pui32Flags == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Alloc: invalid parameter")); + PVR_DBG_BREAK; + return IMG_FALSE; + } + + uFlags = *pui32Flags; + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_Alloc (uSize=0x%x, uFlags=0x%x, uDevVAddrAlignment=0x%x)", + uSize, uFlags, uDevVAddrAlignment)); + + SysAcquireData(&psSysData); + + psBMHeap = (BM_HEAP*)hDevMemHeap; + pBMContext = psBMHeap->pBMContext; + + if(uDevVAddrAlignment == 0) + { + uDevVAddrAlignment = 1; + } + + /* + * Allocate something in which to record the allocation's details. + */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (BM_BUF), + (IMG_PVOID *)&pBuf, IMG_NULL, + "Buffer Manager buffer") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Alloc: BM_Buf alloc FAILED")); + return IMG_FALSE; + } + OSMemSet(pBuf, 0, sizeof (BM_BUF)); + + /* + * Allocate the memory itself now. + */ + if (AllocMemory(pBMContext, + psBMHeap, + psDevVAddr, + uSize, + uFlags, + uDevVAddrAlignment, + pvPrivData, + ui32PrivDataLength, + ui32ChunkSize, + ui32NumVirtChunks, + ui32NumPhysChunks, + pabMapChunk, + pBuf) != IMG_TRUE) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof (BM_BUF), pBuf, IMG_NULL); + /* not nulling pointer, out of scope */ + PVR_DPF((PVR_DBG_ERROR, "BM_Alloc: AllocMemory FAILED")); + return IMG_FALSE; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_Alloc (uSize=0x%x, uFlags=0x%x)", + uSize, uFlags)); + + /* + * Assign the handle and return. + */ + pBuf->ui32RefCount = 1; + *phBuf = (BM_HANDLE)pBuf; + *pui32Flags = uFlags | psBMHeap->ui32Attribs; + + /* + * If the user has specified heap CACHETYPE flags themselves, + * override any CACHETYPE flags inherited from the heap. + */ + if(uFlags & PVRSRV_HAP_CACHETYPE_MASK) + { + *pui32Flags &= ~PVRSRV_HAP_CACHETYPE_MASK; + *pui32Flags |= (uFlags & PVRSRV_HAP_CACHETYPE_MASK); + } + + return IMG_TRUE; +} + + + +#if defined(PVR_LMA) +/*! +****************************************************************************** + + @Function ValidSysPAddrArrayForDev + + @Description Verify the array of system address is accessible + by the given device. + + @Input psDeviceNode + @Input psSysPAddr - system address array + @Input ui32PageSize - size of address array + + @Return IMG_BOOL + + *****************************************************************************/ +static IMG_BOOL +ValidSysPAddrArrayForDev(PVRSRV_DEVICE_NODE *psDeviceNode, IMG_SYS_PHYADDR *psSysPAddr, IMG_UINT32 ui32PageCount, IMG_SIZE_T ui32PageSize) +{ + IMG_UINT32 i; + + for (i = 0; i < ui32PageCount; i++) + { + IMG_SYS_PHYADDR sStartSysPAddr = psSysPAddr[i]; + IMG_SYS_PHYADDR sEndSysPAddr; + + if (!SysVerifySysPAddrToDevPAddr(psDeviceNode->sDevId.eDeviceType, sStartSysPAddr)) + { + return IMG_FALSE; + } + + sEndSysPAddr.uiAddr = sStartSysPAddr.uiAddr + ui32PageSize; + + if (!SysVerifySysPAddrToDevPAddr(psDeviceNode->sDevId.eDeviceType, sEndSysPAddr)) + { + return IMG_FALSE; + } + } + + return IMG_TRUE; +} + +/*! +****************************************************************************** + + @Function ValidSysPAddrRangeForDev + + @Description Verify a system address range is accessible + by the given device. + + @Input psDeviceNode + @Input sStartSysPAddr - starting system address + @Input ui32Range - length of address range + + @Return IMG_BOOL + + *****************************************************************************/ +static IMG_BOOL +ValidSysPAddrRangeForDev(PVRSRV_DEVICE_NODE *psDeviceNode, IMG_SYS_PHYADDR sStartSysPAddr, IMG_SIZE_T ui32Range) +{ + IMG_SYS_PHYADDR sEndSysPAddr; + + if (!SysVerifySysPAddrToDevPAddr(psDeviceNode->sDevId.eDeviceType, sStartSysPAddr)) + { + return IMG_FALSE; + } + + sEndSysPAddr.uiAddr = sStartSysPAddr.uiAddr + ui32Range; + + if (!SysVerifySysPAddrToDevPAddr(psDeviceNode->sDevId.eDeviceType, sEndSysPAddr)) + { + return IMG_FALSE; + } + + return IMG_TRUE; +} + +#define WRAP_MAPPING_SIZE(ui32ByteSize, ui32PageOffset) HOST_PAGEALIGN((ui32ByteSize) + (ui32PageOffset)) + +#define WRAP_PAGE_COUNT(ui32ByteSize, ui32PageOffset, ui32HostPageSize) (WRAP_MAPPING_SIZE(ui32ByteSize, ui32PageOffset) / (ui32HostPageSize)) + +#endif + + +/*! +****************************************************************************** + + @Function BM_Wrap + + @Description Create a buffer which wraps user provided system physical + memory. + The wrapped memory must be page aligned. BM_Wrap will + roundup the size to a multiple of cpu pages. + + @Input ui32Size - size of memory to wrap. + @Input ui32Offset - Offset into page of memory to wrap. + @Input bPhysContig - Is the wrap physically contiguous. + @Input psSysAddr - list of system physical page addresses of memory to wrap. + @Input pvCPUVAddr - optional CPU kernel virtual address (Page aligned) of memory to wrap. + @Input uFlags - bit mask of buffer property flags. + @output phBuf - receives the buffer handle. + + @Return IMG_TRUE - Success. + IMG_FALSE - Failed + + *****************************************************************************/ +IMG_BOOL +BM_Wrap ( IMG_HANDLE hDevMemHeap, + IMG_SIZE_T ui32Size, + IMG_SIZE_T ui32Offset, + IMG_BOOL bPhysContig, + IMG_SYS_PHYADDR *psSysAddr, + IMG_VOID *pvCPUVAddr, + IMG_UINT32 *pui32Flags, + BM_HANDLE *phBuf) +{ + BM_BUF *pBuf; + BM_CONTEXT *psBMContext; + BM_HEAP *psBMHeap; + SYS_DATA *psSysData; + IMG_SYS_PHYADDR sHashAddress; + IMG_UINT32 uFlags; + + psBMHeap = (BM_HEAP*)hDevMemHeap; + psBMContext = psBMHeap->pBMContext; + + uFlags = psBMHeap->ui32Attribs & (PVRSRV_HAP_CACHETYPE_MASK | PVRSRV_HAP_MAPTYPE_MASK | PVRSRV_HAP_MAPPING_CTRL_MASK); + + if ((pui32Flags != IMG_NULL) && ((*pui32Flags & PVRSRV_HAP_CACHETYPE_MASK) != 0)) + { + uFlags &= ~PVRSRV_HAP_CACHETYPE_MASK; + uFlags |= *pui32Flags & PVRSRV_HAP_CACHETYPE_MASK; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_Wrap (uSize=0x%x, uOffset=0x%x, bPhysContig=0x%x, pvCPUVAddr=0x%x, uFlags=0x%x)", + ui32Size, ui32Offset, bPhysContig, (IMG_UINTPTR_T)pvCPUVAddr, uFlags)); + + SysAcquireData(&psSysData); + +#if defined(PVR_LMA) + if (bPhysContig) + { + if (!ValidSysPAddrRangeForDev(psBMContext->psDeviceNode, *psSysAddr, WRAP_MAPPING_SIZE(ui32Size, ui32Offset))) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Wrap: System address range invalid for device")); + return IMG_FALSE; + } + } + else + { + IMG_SIZE_T ui32HostPageSize = HOST_PAGESIZE(); + + if (!ValidSysPAddrArrayForDev(psBMContext->psDeviceNode, psSysAddr, WRAP_PAGE_COUNT(ui32Size, ui32Offset, ui32HostPageSize), ui32HostPageSize)) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Wrap: Array of system addresses invalid for device")); + return IMG_FALSE; + } + } +#endif + /* + * Insert the System Physical Address of the first page into the hash so we can optimise multiple wraps of the + * same memory. + */ + sHashAddress = psSysAddr[0]; + + /* Add the in-page offset to ensure a unique hash */ + sHashAddress.uiAddr += ui32Offset; + + /* See if this address has already been wrapped */ + pBuf = (BM_BUF *)HASH_Retrieve(psBMContext->pBufferHash, sHashAddress.uiAddr); + + if(pBuf) + { + IMG_SIZE_T ui32MappingSize = HOST_PAGEALIGN (ui32Size + ui32Offset); + + /* Check base address, size and contiguity type match */ + if(pBuf->pMapping->uSize == ui32MappingSize && (pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped || + pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped_virtaddr)) + { + PVR_DPF((PVR_DBG_MESSAGE, + "BM_Wrap (Matched previous Wrap! uSize=0x%x, uOffset=0x%x, SysAddr=%08X)", + ui32Size, ui32Offset, sHashAddress.uiAddr)); + + PVRSRVBMBufIncRef(pBuf); + *phBuf = (BM_HANDLE)pBuf; + if(pui32Flags) + *pui32Flags = uFlags; + + return IMG_TRUE; + } + else + { + /* Otherwise removed that item from the hash table + (a workaround for buffer device class) */ + HASH_Remove(psBMContext->pBufferHash, (IMG_UINTPTR_T)sHashAddress.uiAddr); + } + } + + /* + * Allocate something in which to record the allocation's details. + */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (BM_BUF), + (IMG_PVOID *)&pBuf, IMG_NULL, + "Buffer Manager buffer") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Wrap: BM_Buf alloc FAILED")); + return IMG_FALSE; + } + OSMemSet(pBuf, 0, sizeof (BM_BUF)); + + /* + * Actually perform the memory wrap. + */ + if (WrapMemory (psBMHeap, ui32Size, ui32Offset, bPhysContig, psSysAddr, pvCPUVAddr, uFlags, pBuf) != IMG_TRUE) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Wrap: WrapMemory FAILED")); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof (BM_BUF), pBuf, IMG_NULL); + /*not nulling pointer, out of scope*/ + return IMG_FALSE; + } + + /* Only insert the buffer in the hash table if it is contiguous - allows for optimisation of multiple wraps + * of the same contiguous buffer. + */ + if(pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped || pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped_virtaddr) + { + /* Have we calculated the right Hash key ? */ + PVR_ASSERT(SysSysPAddrToCpuPAddr(sHashAddress).uiAddr == pBuf->CpuPAddr.uiAddr); + + if (!HASH_Insert (psBMContext->pBufferHash, sHashAddress.uiAddr, (IMG_UINTPTR_T)pBuf)) + { + FreeBuf (pBuf, uFlags, IMG_TRUE); + PVR_DPF((PVR_DBG_ERROR, "BM_Wrap: HASH_Insert FAILED")); + return IMG_FALSE; + } + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_Wrap (uSize=0x%x, uFlags=0x%x, devVAddr=%08X)", + ui32Size, uFlags, pBuf->DevVAddr.uiAddr)); + + /* + * Assign the handle and return. + */ + pBuf->ui32RefCount = 1; + *phBuf = (BM_HANDLE)pBuf; + if(pui32Flags) + { + /* need to override the heap attributes SINGLE PROC to MULT_PROC. */ + *pui32Flags = (uFlags & ~PVRSRV_HAP_MAPTYPE_MASK) | PVRSRV_HAP_MULTI_PROCESS; + } + + return IMG_TRUE; +} + +/*! +****************************************************************************** + + @Function BM_Export + + @Description Export a buffer previously allocated via BM_Alloc. + + @Input hBuf - buffer handle. + @Input ui32Flags - flags + + @Return None. + + *****************************************************************************/ + +IMG_VOID +BM_Export (BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVRSRVBMBufIncExport(pBuf); +} + +/*! +****************************************************************************** + @Function BM_Export + + @Description Export a buffer previously allocated via BM_Alloc. + + @Input hBuf - buffer handle. + + @Return None. +**************************************************************************/ +IMG_VOID +BM_FreeExport(BM_HANDLE hBuf, + IMG_UINT32 ui32Flags) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVRSRVBMBufDecExport(pBuf); + FreeBuf (pBuf, ui32Flags, IMG_FALSE); +} + +/*! +****************************************************************************** + @Function BM_FreeExport + + @Description Free a buffer previously exported via BM_Export. + + @Input hBuf - buffer handle. + @Input ui32Flags - flags + + @Return None. +**************************************************************************/ +IMG_VOID +BM_Free (BM_HANDLE hBuf, + IMG_UINT32 ui32Flags) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + SYS_DATA *psSysData; + IMG_SYS_PHYADDR sHashAddr; + + PVR_DPF ((PVR_DBG_MESSAGE, "BM_Free (h=0x%x)", (IMG_UINTPTR_T)hBuf)); + PVR_ASSERT (pBuf!=IMG_NULL); + + if (pBuf == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_Free: invalid parameter")); + return; + } + + SysAcquireData(&psSysData); + + PVRSRVBMBufDecRef(pBuf); + if(pBuf->ui32RefCount == 0) + { + if(pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped || pBuf->pMapping->eCpuMemoryOrigin == hm_wrapped_virtaddr) + { + sHashAddr = SysCpuPAddrToSysPAddr(pBuf->CpuPAddr); + + HASH_Remove (pBuf->pMapping->pBMHeap->pBMContext->pBufferHash, (IMG_UINTPTR_T)sHashAddr.uiAddr); + } + FreeBuf (pBuf, ui32Flags, IMG_TRUE); + } +} + + +/*! +****************************************************************************** + + @Function BM_HandleToCpuVaddr + + @Description Retreive the cpu virtual address associated with a buffer. + + @Input buffer handle. + + @Return buffers cpu virtual address, or NULL if none exists + + *****************************************************************************/ +IMG_CPU_VIRTADDR +BM_HandleToCpuVaddr (BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVR_ASSERT (pBuf != IMG_NULL); + if (pBuf == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_HandleToCpuVaddr: invalid parameter")); + return IMG_NULL; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_HandleToCpuVaddr(h=0x%x)=0x%x", + (IMG_UINTPTR_T)hBuf, (IMG_UINTPTR_T)pBuf->CpuVAddr)); + return pBuf->CpuVAddr; +} + + +/*! +****************************************************************************** + + @Function BM_HandleToDevVaddr + + @Description Retreive the device virtual address associated with a buffer. + + @Input hBuf - buffer handle. + + @Return buffers device virtual address. + + *****************************************************************************/ +IMG_DEV_VIRTADDR +BM_HandleToDevVaddr (BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVR_ASSERT (pBuf != IMG_NULL); + if (pBuf == IMG_NULL) + { + IMG_DEV_VIRTADDR DevVAddr = {0}; + PVR_DPF((PVR_DBG_ERROR, "BM_HandleToDevVaddr: invalid parameter")); + return DevVAddr; + } + + PVR_DPF ((PVR_DBG_MESSAGE, "BM_HandleToDevVaddr(h=0x%x)=%08X", (IMG_UINTPTR_T)hBuf, pBuf->DevVAddr.uiAddr)); + return pBuf->DevVAddr; +} + + +/*! +****************************************************************************** + + @Function BM_HandleToSysPaddr + + @Description Retreive the system physical address associated with a buffer. + + @Input hBuf - buffer handle. + + @Return buffers device virtual address. + + *****************************************************************************/ +IMG_SYS_PHYADDR +BM_HandleToSysPaddr (BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVR_ASSERT (pBuf != IMG_NULL); + + if (pBuf == IMG_NULL) + { + IMG_SYS_PHYADDR PhysAddr = {0}; + PVR_DPF((PVR_DBG_ERROR, "BM_HandleToSysPaddr: invalid parameter")); + return PhysAddr; + } + + PVR_DPF ((PVR_DBG_MESSAGE, "BM_HandleToSysPaddr(h=0x%x)=%08X", (IMG_UINTPTR_T)hBuf, pBuf->CpuPAddr.uiAddr)); + return SysCpuPAddrToSysPAddr (pBuf->CpuPAddr); +} + +/*! +****************************************************************************** + + @Function BM_HandleToMemOSHandle + + @Description Retreive the underlying memory handle associated with a buffer. + + @Input hBuf - buffer handle. + + @Return OS Specific memory handle. + + *****************************************************************************/ +IMG_HANDLE +BM_HandleToOSMemHandle(BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + + PVR_ASSERT (pBuf != IMG_NULL); + + if (pBuf == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_HandleToOSMemHandle: invalid parameter")); + return IMG_NULL; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_HandleToOSMemHandle(h=0x%x)=0x%x", + (IMG_UINTPTR_T)hBuf, (IMG_UINTPTR_T)pBuf->hOSMemHandle)); + return pBuf->hOSMemHandle; +} + +/*---------------------------------------------------------------------------- +<function> + FUNCTION: BM_UnmapFromDev + + PURPOSE: Unmaps a buffer from GPU virtual address space, but otherwise + leaves buffer intact (ie. not changing any CPU virtual space + mappings, etc). This in conjunction with BM_RemapToDev() can + be used to migrate buffers in and out of GPU virtual address + space to deal with fragmentation and/or limited size of GPU + MMU. + + PARAMETERS: In: hBuf - buffer handle. + RETURNS: IMG_TRUE - Success + IMG_FALSE - Failure +</function> +-----------------------------------------------------------------------------*/ +IMG_INT32 +BM_UnmapFromDev(BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + BM_MAPPING *pMapping; + IMG_INT32 result; + + PVR_ASSERT (pBuf != IMG_NULL); + + if (pBuf == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_UnmapFromDev: invalid parameter")); + return -(PVRSRV_ERROR_INVALID_PARAMS); + } + + pMapping = pBuf->pMapping; + + if ((pMapping->ui32Flags & PVRSRV_HAP_GPU_PAGEABLE) == 0) + { + PVR_DPF((PVR_DBG_ERROR, "BM_UnmapFromDev: cannot unmap non-pageable buffer")); + return -(PVRSRV_ERROR_STILL_MAPPED); + } + + result = DevMemoryFree(pMapping); + + if(result == 0) + pBuf->DevVAddr.uiAddr = PVRSRV_BAD_DEVICE_ADDRESS; + + return result; +} + +/*---------------------------------------------------------------------------- +<function> + FUNCTION: BM_RemapToDev + + PURPOSE: Maps a buffer back into GPU virtual address space, after it + has been BM_UnmapFromDev()'d. After this operation, the GPU + virtual address may have changed, so BM_HandleToDevVaddr() + should be called to get the new address. + + PARAMETERS: In: hBuf - buffer handle. + RETURNS: IMG_TRUE - Success + IMG_FALSE - Failure +</function> +-----------------------------------------------------------------------------*/ +IMG_INT32 +BM_RemapToDev(BM_HANDLE hBuf) +{ + BM_BUF *pBuf = (BM_BUF *)hBuf; + BM_MAPPING *pMapping; + IMG_INT32 mapCount; + + PVR_ASSERT (pBuf != IMG_NULL); + + if (pBuf == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_RemapToDev: invalid parameter")); + return -PVRSRV_ERROR_INVALID_PARAMS; + } + + pMapping = pBuf->pMapping; + + if ((pMapping->ui32Flags & PVRSRV_HAP_GPU_PAGEABLE) == 0) + { + PVR_DPF((PVR_DBG_ERROR, "BM_RemapToDev: cannot remap non-pageable buffer")); + return -PVRSRV_ERROR_BAD_MAPPING; + } + + mapCount = DevMemoryAlloc(pMapping->pBMHeap->pBMContext, pMapping, IMG_NULL, + pMapping->ui32Flags, pMapping->ui32DevVAddrAlignment, &pBuf->DevVAddr); + + if(mapCount <= 0) + { + PVR_DPF((PVR_DBG_WARNING, "BM_RemapToDev: failed to allocate device memory")); + } + + return mapCount; +} + +/*! +****************************************************************************** + + @Function DevMemoryAlloc + + @Description Allocate device memory for a given physical/virtual memory + mapping. We handle the main cases where device MMU mappings + are required - these are the dynamic cases: all wrappings of + host OS memory and host OS imports for SYS_MMU_NORMAL mode. + + If no MMU support is required then we simply map device virtual + space as device physical space. + + @Input pBMContext - the pager to allocate from. + @Output pMapping - the mapping descriptor to be filled in for this + allocation. + @Output pActualSize - the actual size of the block allocated in + bytes. + @Input uFlags - allocation flags + @Input dev_vaddr_alignment - required device virtual address + alignment, or 0. + @Output pDevVAddr - receives the device virtual base address of the + allocated block. + @Return IMG_INT32 - Reference count + -1 - Failed. + + *****************************************************************************/ +static IMG_INT32 +DevMemoryAlloc (BM_CONTEXT *pBMContext, + BM_MAPPING *pMapping, + IMG_SIZE_T *pActualSize, + IMG_UINT32 uFlags, + IMG_UINT32 dev_vaddr_alignment, + IMG_DEV_VIRTADDR *pDevVAddr) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; +#ifdef PDUMP + IMG_UINT32 ui32PDumpSize = (IMG_UINT32)pMapping->uSize; +#endif + + if(pMapping->ui32MappingCount > 0) + { + pMapping->ui32MappingCount++; + *pDevVAddr = pMapping->DevVAddr; + return pMapping->ui32MappingCount; + } + + psDeviceNode = pBMContext->psDeviceNode; + + pMapping->ui32DevVAddrAlignment = dev_vaddr_alignment; + + if(uFlags & PVRSRV_MEM_INTERLEAVED) + { + /* double the size */ + /* don't continue to alter the size each time a buffer is remapped.. + * we only want to do this the first time + */ + /* TODO: FIXME: There is something wrong with this logic */ + if (pMapping->ui32MappingCount == 0) + pMapping->uSize *= 2; + } + +#ifdef PDUMP + if(uFlags & PVRSRV_MEM_DUMMY) + { + /* only one page behind a dummy allocation */ + ui32PDumpSize = pMapping->pBMHeap->sDevArena.ui32DataPageSize; + } +#endif + + /* Check we haven't fall through a gap */ + PVR_ASSERT(pMapping->uSizeVM != 0); + /* allocate device linear space */ + if (!psDeviceNode->pfnMMUAlloc (pMapping->pBMHeap->pMMUHeap, + pMapping->uSizeVM, + pActualSize, + 0, + dev_vaddr_alignment, + &(pMapping->DevVAddr))) + { + PVR_DPF((PVR_DBG_ERROR, "DevMemoryAlloc ERROR MMU_Alloc")); + pDevVAddr->uiAddr = PVRSRV_BAD_DEVICE_ADDRESS; + return -(PVRSRV_ERROR_FAILED_TO_ALLOC_VIRT_MEMORY); + } + +#ifdef SUPPORT_SGX_MMU_BYPASS + EnableHostAccess(pBMContext->psMMUContext); +#endif + +#if defined(PDUMP) + /* pdump the memory allocate */ + PDUMPMALLOCPAGES(&psDeviceNode->sDevId, + pMapping->DevVAddr.uiAddr, + pMapping->CpuVAddr, + pMapping->hOSMemHandle, + ui32PDumpSize, + pMapping->pBMHeap->sDevArena.ui32DataPageSize, +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + psDeviceNode->pfnMMUIsHeapShared(pMapping->pBMHeap->pMMUHeap), +#else + IMG_FALSE, // unused +#endif /* SUPPORT_PDUMP_MULTI_PROCESS */ + (IMG_HANDLE)pMapping); +#endif + + switch (pMapping->eCpuMemoryOrigin) + { + case hm_wrapped: + case hm_wrapped_virtaddr: + case hm_contiguous: + { + if (uFlags & PVRSRV_MEM_SPARSE) + { + /* Check if this device supports sparse mappings */ + PVR_ASSERT(psDeviceNode->pfnMMUMapPagesSparse != IMG_NULL); + psDeviceNode->pfnMMUMapPagesSparse(pMapping->pBMHeap->pMMUHeap, + pMapping->DevVAddr, + SysCpuPAddrToSysPAddr (pMapping->CpuPAddr), + pMapping->ui32ChunkSize, + pMapping->ui32NumVirtChunks, + pMapping->ui32NumPhysChunks, + pMapping->pabMapChunk, + uFlags, + (IMG_HANDLE)pMapping); + } + else + { + psDeviceNode->pfnMMUMapPages ( pMapping->pBMHeap->pMMUHeap, + pMapping->DevVAddr, + SysCpuPAddrToSysPAddr (pMapping->CpuPAddr), + pMapping->uSize, + uFlags, + (IMG_HANDLE)pMapping); + } + *pDevVAddr = pMapping->DevVAddr; + break; + } + case hm_env: + { + if (uFlags & PVRSRV_MEM_SPARSE) + { + /* Check if this device supports sparse mappings */ + PVR_ASSERT(psDeviceNode->pfnMMUMapShadowSparse != IMG_NULL); + psDeviceNode->pfnMMUMapShadowSparse(pMapping->pBMHeap->pMMUHeap, + pMapping->DevVAddr, + pMapping->ui32ChunkSize, + pMapping->ui32NumVirtChunks, + pMapping->ui32NumPhysChunks, + pMapping->pabMapChunk, + pMapping->CpuVAddr, + pMapping->hOSMemHandle, + pDevVAddr, + uFlags, + (IMG_HANDLE)pMapping); + } + else + { + psDeviceNode->pfnMMUMapShadow ( pMapping->pBMHeap->pMMUHeap, + pMapping->DevVAddr, + pMapping->uSize, + pMapping->CpuVAddr, + pMapping->hOSMemHandle, + pDevVAddr, + uFlags, + (IMG_HANDLE)pMapping); + } + break; + } + case hm_wrapped_scatter: + case hm_wrapped_scatter_virtaddr: + { + psDeviceNode->pfnMMUMapScatter (pMapping->pBMHeap->pMMUHeap, + pMapping->DevVAddr, + pMapping->psSysAddr, + pMapping->uSize, + uFlags, + (IMG_HANDLE)pMapping); + + *pDevVAddr = pMapping->DevVAddr; + break; + } + default: + PVR_DPF((PVR_DBG_ERROR, + "Illegal value %d for pMapping->eCpuMemoryOrigin", + pMapping->eCpuMemoryOrigin)); + return -(PVRSRV_ERROR_FAILED_TO_MAP_PAGE_TABLE); + } + +#ifdef SUPPORT_SGX_MMU_BYPASS + DisableHostAccess(pBMContext->psMMUContext); +#endif + + pMapping->ui32MappingCount = 1; + + return pMapping->ui32MappingCount; +} + +static IMG_INT32 +DevMemoryFree (BM_MAPPING *pMapping) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_DEV_PHYADDR sDevPAddr; +#ifdef PDUMP + IMG_UINT32 ui32PSize; +#endif + + if(pMapping->ui32MappingCount > 1) + { + pMapping->ui32MappingCount--; + + /* Nothing else to do for now */ + return pMapping->ui32MappingCount; + } + + if (pMapping->ui32MappingCount == 0) + { + /* already unmapped from GPU.. bail */ + return -(PVRSRV_ERROR_MAPPING_NOT_FOUND); + } + + /* Then pMapping->ui32MappingCount is 1 + * ready to release GPU mapping */ + + psDeviceNode = pMapping->pBMHeap->pBMContext->psDeviceNode; + sDevPAddr = psDeviceNode->pfnMMUGetPhysPageAddr(pMapping->pBMHeap->pMMUHeap, pMapping->DevVAddr); + + if (sDevPAddr.uiAddr != 0) + { +#ifdef PDUMP + /* pdump the memory free */ + if(pMapping->ui32Flags & PVRSRV_MEM_DUMMY) + { + /* physical memory size differs in the case of Dummy allocations */ + ui32PSize = pMapping->pBMHeap->sDevArena.ui32DataPageSize; + } + else + { + ui32PSize = (IMG_UINT32)pMapping->uSize; + } + + PDUMPFREEPAGES(pMapping->pBMHeap, + pMapping->DevVAddr, + ui32PSize, + pMapping->pBMHeap->sDevArena.ui32DataPageSize, + (IMG_HANDLE)pMapping, + (pMapping->ui32Flags & PVRSRV_MEM_INTERLEAVED) ? IMG_TRUE : IMG_FALSE, + (pMapping->ui32Flags & PVRSRV_MEM_SPARSE) ? IMG_TRUE : IMG_FALSE); +#endif + } + PVR_ASSERT(pMapping->uSizeVM != 0); + psDeviceNode->pfnMMUFree (pMapping->pBMHeap->pMMUHeap, pMapping->DevVAddr, IMG_CAST_TO_DEVVADDR_UINT(pMapping->uSizeVM)); + + pMapping->ui32MappingCount = 0; + + return pMapping->ui32MappingCount; +} + +/* If this array grows larger, it might be preferable to use a hashtable rather than an array. */ +#ifndef XPROC_WORKAROUND_NUM_SHAREABLES +#define XPROC_WORKAROUND_NUM_SHAREABLES 500 +#endif + +#define XPROC_WORKAROUND_BAD_SHAREINDEX 0773407734 + +#define XPROC_WORKAROUND_UNKNOWN 0 +#define XPROC_WORKAROUND_ALLOC 1 +#define XPROC_WORKAROUND_MAP 2 + +static IMG_UINT32 gXProcWorkaroundShareIndex = XPROC_WORKAROUND_BAD_SHAREINDEX; +static IMG_UINT32 gXProcWorkaroundState = XPROC_WORKAROUND_UNKNOWN; + +/* PRQA S 0686 10 */ /* force compiler to init structure */ +XPROC_DATA gXProcWorkaroundShareData[XPROC_WORKAROUND_NUM_SHAREABLES] = {{0}}; + +IMG_INT32 BM_XProcGetShareDataRefCount(IMG_UINT32 ui32Index) +{ + if(ui32Index >= XPROC_WORKAROUND_NUM_SHAREABLES) + return -1; + + return gXProcWorkaroundShareData[ui32Index].ui32RefCount; +} + +PVRSRV_ERROR BM_XProcWorkaroundSetShareIndex(IMG_UINT32 ui32Index) +{ + /* if you fail this assertion - did you acquire the mutex? + did you call "set" exactly once? + did you call "unset" exactly once per set? + */ + if (gXProcWorkaroundShareIndex != XPROC_WORKAROUND_BAD_SHAREINDEX) + { + PVR_DPF((PVR_DBG_ERROR, "No, it's already set!")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + gXProcWorkaroundShareIndex = ui32Index; + gXProcWorkaroundState = XPROC_WORKAROUND_MAP; + + return PVRSRV_OK; +} + +PVRSRV_ERROR BM_XProcWorkaroundUnsetShareIndex(IMG_UINT32 ui32Index) +{ + /* if you fail this assertion - did you acquire the mutex? + did you call "set" exactly once? + did you call "unset" exactly once per set? + */ + if (gXProcWorkaroundShareIndex == XPROC_WORKAROUND_BAD_SHAREINDEX) + { + PVR_DPF((PVR_DBG_ERROR, "huh? how can it be bad??")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + if (gXProcWorkaroundShareIndex != ui32Index) + { + PVR_DPF((PVR_DBG_ERROR, "gXProcWorkaroundShareIndex == 0x%08x != 0x%08x == ui32Index", gXProcWorkaroundShareIndex, ui32Index)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + gXProcWorkaroundShareIndex = XPROC_WORKAROUND_BAD_SHAREINDEX; + gXProcWorkaroundState = XPROC_WORKAROUND_UNKNOWN; + + return PVRSRV_OK; +} + +PVRSRV_ERROR BM_XProcWorkaroundFindNewBufferAndSetShareIndex(IMG_UINT32 *pui32Index) +{ + /* if you fail this assertion - did you acquire the mutex? + did you call "set" exactly once? + did you call "unset" exactly once per set? + */ + if (gXProcWorkaroundShareIndex != XPROC_WORKAROUND_BAD_SHAREINDEX) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + for (*pui32Index = 0; *pui32Index < XPROC_WORKAROUND_NUM_SHAREABLES; (*pui32Index)++) + { + if (gXProcWorkaroundShareData[*pui32Index].ui32RefCount == 0) + { + gXProcWorkaroundShareIndex = *pui32Index; + gXProcWorkaroundState = XPROC_WORKAROUND_ALLOC; + return PVRSRV_OK; + } + } + + PVR_DPF((PVR_DBG_ERROR, "ran out of shared buffers")); + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + +static PVRSRV_ERROR +XProcWorkaroundAllocShareable(RA_ARENA *psArena, + IMG_UINT32 ui32AllocFlags, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32PageSize, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_VOID **ppvCpuVAddr, + IMG_HANDLE *phOSMemHandle) +{ + if ((ui32AllocFlags & PVRSRV_MEM_XPROC) == 0) + { + PVR_DPF((PVR_DBG_VERBOSE, "XProcWorkaroundAllocShareable: bad flags")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32RefCount > 0) + { + PVR_DPF((PVR_DBG_VERBOSE, + "XProcWorkaroundAllocShareable: re-using previously allocated pages")); + + ui32AllocFlags &= ~PVRSRV_HAP_MAPTYPE_MASK; + ui32AllocFlags |= PVRSRV_HAP_SINGLE_PROCESS; + + if (ui32AllocFlags != gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32AllocFlags) + { + PVR_DPF((PVR_DBG_ERROR, + "%s ERROR: Flags don't match (Shared 0x%08x, Requested 0x%08x)!", + __FUNCTION__, + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32AllocFlags, + ui32AllocFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (ui32Size != gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32Size) + { + PVR_DPF((PVR_DBG_ERROR, + "%s ERROR: Size doesn't match (Shared %d, Requested %d) with flags 0x%08x - 0x%08x!", + __FUNCTION__, + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32Size, + ui32Size, + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32AllocFlags, + ui32AllocFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (ui32PageSize != gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32PageSize) + { + PVR_DPF((PVR_DBG_ERROR, + "%s ERROR: Page Size doesn't match (Shared %d, Requested %d) with flags 0x%08x - 0x%08x!", + __FUNCTION__, + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32PageSize, + ui32PageSize, + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32AllocFlags, + ui32AllocFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + *ppvCpuVAddr = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr; + *phOSMemHandle = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle; + + BM_XProcIndexAcquire(gXProcWorkaroundShareIndex); + + return PVRSRV_OK; + } + else + { + if (gXProcWorkaroundState != XPROC_WORKAROUND_ALLOC) + { + PVR_DPF((PVR_DBG_ERROR, + "XPROC workaround in bad state! About to allocate memory from non-alloc state! (%d)", + gXProcWorkaroundState)); + } + PVR_ASSERT(gXProcWorkaroundState == XPROC_WORKAROUND_ALLOC); + + if (psArena != IMG_NULL) + { + IMG_CPU_PHYADDR sCpuPAddr; + IMG_SYS_PHYADDR sSysPAddr; + + PVR_DPF((PVR_DBG_VERBOSE, + "XProcWorkaroundAllocShareable: making a NEW allocation from local mem")); + + if (!RA_Alloc (psArena, + ui32Size, + IMG_NULL, + IMG_NULL, + 0, + ui32PageSize, + 0, + pvPrivData, + ui32PrivDataLength, + (IMG_UINTPTR_T *)&sSysPAddr.uiAddr)) + { + PVR_DPF((PVR_DBG_ERROR, "XProcWorkaroundAllocShareable: RA_Alloc(0x%x) FAILED", ui32Size)); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + sCpuPAddr = SysSysPAddrToCpuPAddr(sSysPAddr); + if(OSReservePhys(sCpuPAddr, + ui32Size, + ui32AllocFlags, + IMG_NULL, + (IMG_VOID **)&gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr, + &gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "XProcWorkaroundAllocShareable: OSReservePhys failed")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].sSysPAddr = sSysPAddr; + } + else + { + PVR_DPF((PVR_DBG_VERBOSE, + "XProcWorkaroundAllocShareable: making a NEW allocation from OS")); + + ui32AllocFlags &= ~PVRSRV_HAP_MAPTYPE_MASK; + ui32AllocFlags |= PVRSRV_HAP_SINGLE_PROCESS; + + /* allocate pages from the OS RAM */ + if (OSAllocPages(ui32AllocFlags, + ui32Size, + ui32PageSize, + pvPrivData, + ui32PrivDataLength, + IMG_NULL, /* FIXME: to support cross process sparse allocations */ + (IMG_VOID **)&gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr, + &gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "XProcWorkaroundAllocShareable: OSAllocPages(0x%x) failed", + ui32PageSize)); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } + + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].psArena = psArena; + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32AllocFlags = ui32AllocFlags; + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32Size = ui32Size; + gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32PageSize = ui32PageSize; + + *ppvCpuVAddr = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr; + *phOSMemHandle = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle; + + BM_XProcIndexAcquire(gXProcWorkaroundShareIndex); + + return PVRSRV_OK; + } +} + +static PVRSRV_ERROR XProcWorkaroundHandleToSI(IMG_HANDLE hOSMemHandle, IMG_UINT32 *pui32SI) +{ + IMG_UINT32 ui32SI; + IMG_BOOL bFound; + IMG_BOOL bErrorDups; + + bFound = IMG_FALSE; + bErrorDups = IMG_FALSE; + + for (ui32SI = 0; ui32SI < XPROC_WORKAROUND_NUM_SHAREABLES; ui32SI++) + { + if (gXProcWorkaroundShareData[ui32SI].ui32RefCount>0 && gXProcWorkaroundShareData[ui32SI].hOSMemHandle == hOSMemHandle) + { + if (bFound) + { + bErrorDups = IMG_TRUE; + } + else + { + *pui32SI = ui32SI; + bFound = IMG_TRUE; + } + } + } + + if (bErrorDups || !bFound) + { + return PVRSRV_ERROR_BM_BAD_SHAREMEM_HANDLE; + } + + return PVRSRV_OK; +} + +#if defined(PVRSRV_REFCOUNT_DEBUG) +IMG_VOID _BM_XProcIndexAcquireDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +#else +IMG_VOID _BM_XProcIndexAcquire(IMG_UINT32 ui32Index) +#endif +{ +#if defined(PVRSRV_REFCOUNT_DEBUG) + PVRSRVBMXProcIncRef2(pszFile, iLine, ui32Index); +#else + PVRSRVBMXProcIncRef(ui32Index); +#endif +} + +#if defined(PVRSRV_REFCOUNT_DEBUG) +IMG_VOID _BM_XProcIndexReleaseDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +#else +IMG_VOID _BM_XProcIndexRelease(IMG_UINT32 ui32Index) +#endif +{ +#if defined(PVRSRV_REFCOUNT_DEBUG) + PVRSRVBMXProcDecRef2(pszFile, iLine, ui32Index); +#else + PVRSRVBMXProcDecRef(ui32Index); +#endif + + PVR_DPF((PVR_DBG_VERBOSE, "Reduced refcount of SI[%d] from %d to %d", + ui32Index, gXProcWorkaroundShareData[ui32Index].ui32RefCount+1, gXProcWorkaroundShareData[ui32Index].ui32RefCount)); + + if (gXProcWorkaroundShareData[ui32Index].ui32RefCount == 0) + { + if (gXProcWorkaroundShareData[ui32Index].psArena != IMG_NULL) + { + IMG_SYS_PHYADDR sSysPAddr; + + if (gXProcWorkaroundShareData[ui32Index].pvCpuVAddr != IMG_NULL) + { + OSUnReservePhys(gXProcWorkaroundShareData[ui32Index].pvCpuVAddr, + gXProcWorkaroundShareData[ui32Index].ui32Size, + gXProcWorkaroundShareData[ui32Index].ui32AllocFlags, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle); + } + sSysPAddr = gXProcWorkaroundShareData[ui32Index].sSysPAddr; + RA_Free (gXProcWorkaroundShareData[ui32Index].psArena, + sSysPAddr.uiAddr, + IMG_FALSE); + } + else + { + PVR_DPF((PVR_DBG_VERBOSE, "freeing OS memory")); + OSFreePages(gXProcWorkaroundShareData[ui32Index].ui32AllocFlags, + gXProcWorkaroundShareData[ui32Index].ui32PageSize, + gXProcWorkaroundShareData[ui32Index].pvCpuVAddr, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle); + } + } +} + +static IMG_VOID XProcWorkaroundFreeShareable(IMG_HANDLE hOSMemHandle) +{ + IMG_UINT32 ui32SI = (IMG_UINT32)((IMG_UINTPTR_T)hOSMemHandle & 0xffffU); + PVRSRV_ERROR eError; + + eError = XProcWorkaroundHandleToSI(hOSMemHandle, &ui32SI); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "bad handle")); + return; + } + + BM_XProcIndexRelease(ui32SI); +} + + +/*! +****************************************************************************** + + @Function BM_ImportMemory + + @Description Provide a resource allocator with a source of pages of memory + from the Host OS's own allocation. Allocates a block of pages + larger than requested, allowing the resource allocator to + operate a small cache of pre allocated pages. + + @Input pH - buffer manager handle, not the void type is dictated + by the generic nature of the resource allocator interface. + @Input uRequestSize - requested size in bytes + @Output pActualSize - receives the actual size allocated in bytes + which may be >= requested size + @Output ppsMapping - receives the arbitrary user reference + associated with the underlying storage. + @Input uFlags - bit mask of allocation flags + @Input pvPrivData - opaque private data passed through to allocator + @Input ui32PrivDataLength - length of opaque private data + @Output pBase - receives a pointer to the allocated storage. + + @Return IMG_TRUE - success + IMG_FALSE - failed + + *****************************************************************************/ +static IMG_BOOL +BM_ImportMemory (IMG_VOID *pH, + IMG_SIZE_T uRequestSize, + IMG_SIZE_T *pActualSize, + BM_MAPPING **ppsMapping, + IMG_UINT32 uFlags, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINTPTR_T *pBase) +{ + BM_MAPPING *pMapping; + BM_HEAP *pBMHeap = pH; + BM_CONTEXT *pBMContext = pBMHeap->pBMContext; + IMG_INT32 uResult; + IMG_SIZE_T uSize; + IMG_SIZE_T uPSize; + IMG_SIZE_T uDevVAddrAlignment = 0; /* ? */ + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_ImportMemory (pBMContext=0x%x, uRequestSize=0x%x, uFlags=0x%x, uAlign=0x%x)", + (IMG_UINTPTR_T)pBMContext, uRequestSize, uFlags, uDevVAddrAlignment)); + + PVR_ASSERT (ppsMapping != IMG_NULL); + PVR_ASSERT (pBMContext != IMG_NULL); + + if (ppsMapping == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_ImportMemory: invalid parameter")); + goto fail_exit; + } + + uSize = HOST_PAGEALIGN (uRequestSize); + PVR_ASSERT (uSize >= uRequestSize); + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (BM_MAPPING), + (IMG_PVOID *)&pMapping, IMG_NULL, + "Buffer Manager Mapping") != PVRSRV_OK) + { + PVR_DPF ((PVR_DBG_ERROR, "BM_ImportMemory: failed BM_MAPPING alloc")); + goto fail_exit; + } + + pMapping->hOSMemHandle = 0; + pMapping->CpuVAddr = 0; + pMapping->DevVAddr.uiAddr = 0; + pMapping->ui32MappingCount = 0; + pMapping->CpuPAddr.uiAddr = 0; + pMapping->uSize = uSize; + if ((uFlags & PVRSRV_MEM_SPARSE) == 0) + { + pMapping->uSizeVM = uSize; + } + pMapping->pBMHeap = pBMHeap; + pMapping->ui32Flags = uFlags; + + /* + * If anyone want's to know, pass back the actual size of our allocation. + * There could be up to an extra page's worth of memory which will be marked + * as free in the RA. + */ + if (pActualSize) + { + *pActualSize = uSize; + } + + /* if it's a dummy allocation only use one physical page */ + if(pMapping->ui32Flags & PVRSRV_MEM_DUMMY) + { + uPSize = pBMHeap->sDevArena.ui32DataPageSize; + } + else + { + uPSize = pMapping->uSize; + } + + if (uFlags & PVRSRV_MEM_XPROC) + { + IMG_UINT32 ui32Attribs = pBMHeap->ui32Attribs | PVRSRV_MEM_XPROC; + IMG_BOOL bBadBackingStoreType; + + if(uFlags & PVRSRV_MEM_ION) + { + ui32Attribs |= PVRSRV_MEM_ION; + } + + bBadBackingStoreType = IMG_TRUE; + + if ((ui32Attribs & PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG) != 0) + { + uDevVAddrAlignment = MAX(pBMHeap->sDevArena.ui32DataPageSize, HOST_PAGESIZE()); + + + if (uPSize % uDevVAddrAlignment != 0) + { + PVR_DPF((PVR_DBG_ERROR, "Cannot use use this memory sharing workaround with allocations that might be suballocated")); + goto fail_mapping_alloc; + } + uDevVAddrAlignment = 0; /* FIXME: find out why it doesn't work if alignment is specified */ + + /* If the user has specified heap CACHETYPE flags, use them to + * override the flags inherited from the heap. + */ + if (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK) + { + ui32Attribs &= ~PVRSRV_HAP_CACHETYPE_MASK; + ui32Attribs |= (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK); + } + + /* allocate "shared" pages. */ + if (XProcWorkaroundAllocShareable(IMG_NULL, + ui32Attribs, + (IMG_UINT32)uPSize, + pBMHeap->sDevArena.ui32DataPageSize, + pvPrivData, + ui32PrivDataLength, + (IMG_VOID **)&pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "BM_ImportMemory: XProcWorkaroundAllocShareable(0x%x) failed", + uPSize)); + goto fail_mapping_alloc; + } + + /* specify how page addresses are derived */ + /* it works just like "env" now - no need to record + it as shareable, as we use the actual hOSMemHandle + and only divert to our wrapper layer based on Attribs */ + pMapping->eCpuMemoryOrigin = hm_env; + bBadBackingStoreType = IMG_FALSE; + } + + if ((ui32Attribs & PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG) != 0) + { + uDevVAddrAlignment = pBMHeap->sDevArena.ui32DataPageSize; + + if (uPSize % uDevVAddrAlignment != 0) + { + PVR_DPF((PVR_DBG_ERROR, "Cannot use use this memory sharing workaround with allocations that might be suballocated")); + goto fail_mapping_alloc; + } + uDevVAddrAlignment = 0; /* FIXME: find out why it doesn't work if alignment is specified */ + + /* If the user has specified heap CACHETYPE flags, use them to + * override the flags inherited from the heap. + */ + if (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK) + { + ui32Attribs &= ~PVRSRV_HAP_CACHETYPE_MASK; + ui32Attribs |= (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK); + } + + /* allocate "shared" pages. */ + if (XProcWorkaroundAllocShareable(pBMHeap->pLocalDevMemArena, + ui32Attribs, + (IMG_UINT32)uPSize, + pBMHeap->sDevArena.ui32DataPageSize, + pvPrivData, + ui32PrivDataLength, + (IMG_VOID **)&pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "BM_ImportMemory: XProcWorkaroundAllocShareable(0x%x) failed", + uPSize)); + goto fail_mapping_alloc; + } + + /* specify how page addresses are derived */ + /* it works just like "env" now - no need to record + it as shareable, as we use the actual hOSMemHandle + and only divert to our wrapper layer based on Attribs */ + pMapping->eCpuMemoryOrigin = hm_env; + bBadBackingStoreType = IMG_FALSE; + } + + if (bBadBackingStoreType) + { + PVR_DPF((PVR_DBG_ERROR, "Cannot use this memory sharing workaround with this type of backing store")); + goto fail_mapping_alloc; + } + } + else + + /* + What type of backing store do we have? + */ + if(pBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG) + { + IMG_UINT32 ui32Attribs = pBMHeap->ui32Attribs; + + /* The allocation code needs to know this is a sparse mapping */ + if (pMapping->ui32Flags & PVRSRV_MEM_SPARSE) + { + ui32Attribs |= PVRSRV_MEM_SPARSE; + } + + /* If the user has specified heap CACHETYPE flags, use them to + * override the flags inherited from the heap. + */ + if (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK) + { + ui32Attribs &= ~PVRSRV_HAP_CACHETYPE_MASK; + ui32Attribs |= (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK); + } + + if (pMapping->ui32Flags & PVRSRV_MEM_ALLOCATENONCACHEDMEM) + { + ui32Attribs &= ~PVRSRV_MEM_ALLOCATENONCACHEDMEM; + ui32Attribs |= (pMapping->ui32Flags & PVRSRV_MEM_ALLOCATENONCACHEDMEM); + } + + /* allocate pages from the OS RAM */ + if (OSAllocPages(ui32Attribs, + uPSize, + pBMHeap->sDevArena.ui32DataPageSize, + pvPrivData, + ui32PrivDataLength, + pMapping, + (IMG_VOID **)&pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "BM_ImportMemory: OSAllocPages(0x%x) failed", + uPSize)); + goto fail_mapping_alloc; + } + + /* specify how page addresses are derived */ + pMapping->eCpuMemoryOrigin = hm_env; + } + else if(pBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG) + { + IMG_SYS_PHYADDR sSysPAddr; + IMG_UINT32 ui32Attribs = pBMHeap->ui32Attribs; + + /* The allocation code needs to know this is a sparse mapping */ + if (pMapping->ui32Flags & PVRSRV_MEM_SPARSE) + { + ui32Attribs |= PVRSRV_MEM_SPARSE; + } + + /* allocate pages from the local device memory allocator */ + PVR_ASSERT(pBMHeap->pLocalDevMemArena != IMG_NULL); + + /* If the user has specified heap CACHETYPE flags, use them to + * override the flags inherited from the heap. + */ + if (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK) + { + ui32Attribs &= ~PVRSRV_HAP_CACHETYPE_MASK; + ui32Attribs |= (pMapping->ui32Flags & PVRSRV_HAP_CACHETYPE_MASK); + } + + if (!RA_Alloc (pBMHeap->pLocalDevMemArena, + uPSize, + IMG_NULL, + IMG_NULL, + 0, + pBMHeap->sDevArena.ui32DataPageSize, + 0, + pvPrivData, + ui32PrivDataLength, + (IMG_UINTPTR_T *)&sSysPAddr.uiAddr)) + { + PVR_DPF((PVR_DBG_ERROR, "BM_ImportMemory: RA_Alloc(0x%x) FAILED", uPSize)); + goto fail_mapping_alloc; + } + + /* derive the CPU virtual address */ + pMapping->CpuPAddr = SysSysPAddrToCpuPAddr(sSysPAddr); + if(OSReservePhys(pMapping->CpuPAddr, + uPSize, + ui32Attribs, + pMapping, + &pMapping->CpuVAddr, + &pMapping->hOSMemHandle) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "BM_ImportMemory: OSReservePhys failed")); + goto fail_dev_mem_alloc; + } + + /* specify how page addresses are derived */ + pMapping->eCpuMemoryOrigin = hm_contiguous; + } + else + { + PVR_DPF((PVR_DBG_ERROR, "BM_ImportMemory: Invalid backing store type")); + goto fail_mapping_alloc; + } + + if(uFlags & PVRSRV_MEM_ION) + { + IMG_UINT32 ui32AddressOffsets[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + IMG_UINT32 ui32NumAddrOffsets = PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES; + + IMG_INT32 retSize = OSGetMemMultiPlaneInfo(pMapping->hOSMemHandle, + ui32AddressOffsets, &ui32NumAddrOffsets); + + if(retSize > 0 && pActualSize) + { + *pActualSize = pMapping->uSize = retSize; + } + } + + /* + * Allocate some device memory for what we just allocated. + */ + /* + * Do not allocate GPU mapping if NO_GPU_VIRTUAL_ON_ALLOC is requested. + * In the case where CBI is enabled, this allows for late + * GPU mapping. This flag is, otherwise, used in cases where only + * the memory management feature of the driver is utilized, without + * a need for GPU rendering + */ + if ((uFlags & (PVRSRV_MEM_SPARSE | PVRSRV_HAP_NO_GPU_VIRTUAL_ON_ALLOC)) == 0) + { + uResult = DevMemoryAlloc (pBMContext, + pMapping, + IMG_NULL, + uFlags, + (IMG_UINT32)uDevVAddrAlignment, + &pMapping->DevVAddr); + if (uResult <= 0) + { + PVR_DPF((PVR_DBG_ERROR, + "BM_ImportMemory: DevMemoryAlloc(0x%x) failed", + pMapping->uSize)); + goto fail_dev_mem_alloc; + } + + /* uDevVAddrAlignment is currently set to zero so QAC generates warning which we override */ + /* PRQA S 3356,3358 1 */ + PVR_ASSERT (uDevVAddrAlignment>1?(pMapping->DevVAddr.uiAddr%uDevVAddrAlignment)==0:1); + PVR_ASSERT(pBase); + } + + if(pBase) + *pBase = pMapping->DevVAddr.uiAddr; + *ppsMapping = pMapping; + + PVR_DPF ((PVR_DBG_MESSAGE, "BM_ImportMemory: IMG_TRUE")); + return IMG_TRUE; + +fail_dev_mem_alloc: + if (pMapping && (pMapping->CpuVAddr || pMapping->hOSMemHandle)) + { + /* the size is double the actual size for interleaved allocations */ + if(pMapping->ui32Flags & PVRSRV_MEM_INTERLEAVED) + { + pMapping->uSize /= 2; + } + + if(pMapping->ui32Flags & PVRSRV_MEM_DUMMY) + { + uPSize = pBMHeap->sDevArena.ui32DataPageSize; + } + else + { + uPSize = pMapping->uSize; + } + + if (uFlags & PVRSRV_MEM_XPROC) + { + XProcWorkaroundFreeShareable(pMapping->hOSMemHandle); + } + else + if(pBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG) + { + OSFreePages(pBMHeap->ui32Attribs, + uPSize, + (IMG_VOID *)pMapping->CpuVAddr, + pMapping->hOSMemHandle); + } + else + { + IMG_SYS_PHYADDR sSysPAddr; + + if(pMapping->CpuVAddr) + { + OSUnReservePhys(pMapping->CpuVAddr, + uPSize, + pBMHeap->ui32Attribs, + pMapping->hOSMemHandle); + } + sSysPAddr = SysCpuPAddrToSysPAddr(pMapping->CpuPAddr); + RA_Free (pBMHeap->pLocalDevMemArena, sSysPAddr.uiAddr, IMG_FALSE); + } + } +fail_mapping_alloc: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_MAPPING), pMapping, IMG_NULL); + /*not nulling pointer, out of scope*/ +fail_exit: + return IMG_FALSE; +} + + +/*! +****************************************************************************** + + @Function BM_FreeMemory + + @Description Free a block of pages previously allocated via + BM_ImportMemory. + + @Input h - buffer manager handle, not the void type as dictated by + the generic nature of the resource allocator interface. + @Input _base - base address of blocks to free. + @Input psMapping - arbitrary user reference associated with the + underlying storage provided by BM_ImportMemory + @Return None + + *****************************************************************************/ +static IMG_VOID +BM_FreeMemory (IMG_VOID *h, IMG_UINTPTR_T _base, BM_MAPPING *psMapping) +{ + BM_HEAP *pBMHeap = h; + IMG_SIZE_T uPSize; + + PVR_UNREFERENCED_PARAMETER (_base); + + PVR_DPF ((PVR_DBG_MESSAGE, + "BM_FreeMemory (h=0x%x, base=0x%x, psMapping=0x%x)", + (IMG_UINTPTR_T)h, _base, (IMG_UINTPTR_T)psMapping)); + + PVR_ASSERT (psMapping != IMG_NULL); + + if (psMapping == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "BM_FreeMemory: invalid parameter")); + return; + } + + /* + Only free the virtual memory if we got as far a allocating it. + This NULL check should be safe as we always have a guard page + at virtual address 0x00000000 + */ + if (psMapping->DevVAddr.uiAddr) + { + DevMemoryFree (psMapping); + } + + /* the size is double the actual for interleaved */ + if((psMapping->ui32Flags & PVRSRV_MEM_INTERLEAVED) != 0) + { + psMapping->uSize /= 2; + } + + if(psMapping->ui32Flags & PVRSRV_MEM_DUMMY) + { + uPSize = psMapping->pBMHeap->sDevArena.ui32DataPageSize; + } + else + { + uPSize = psMapping->uSize; + } + + if (psMapping->ui32Flags & PVRSRV_MEM_XPROC) + { + XProcWorkaroundFreeShareable(psMapping->hOSMemHandle); + } + else + if(pBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_SYSMEM_NONCONTIG) + { + OSFreePages(pBMHeap->ui32Attribs, + uPSize, + (IMG_VOID *) psMapping->CpuVAddr, + psMapping->hOSMemHandle); + } + else if(pBMHeap->ui32Attribs & PVRSRV_BACKINGSTORE_LOCALMEM_CONTIG) + { + IMG_SYS_PHYADDR sSysPAddr; + + OSUnReservePhys(psMapping->CpuVAddr, uPSize, pBMHeap->ui32Attribs, psMapping->hOSMemHandle); + + sSysPAddr = SysCpuPAddrToSysPAddr(psMapping->CpuPAddr); + + RA_Free (pBMHeap->pLocalDevMemArena, sSysPAddr.uiAddr, IMG_FALSE); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "BM_FreeMemory: Invalid backing store type")); + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BM_MAPPING), psMapping, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + PVR_DPF((PVR_DBG_MESSAGE, + "..BM_FreeMemory (h=0x%x, base=0x%x)", + (IMG_UINTPTR_T)h, _base)); +} + +/*! +****************************************************************************** + + @Function BM_GetPhysPageAddr + + @Description + + @Input psMemInfo + + @Input sDevVPageAddr + + @Output psDevPAddr + + @Return IMG_VOID + +******************************************************************************/ + +IMG_VOID BM_GetPhysPageAddr(PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_DEV_VIRTADDR sDevVPageAddr, + IMG_DEV_PHYADDR *psDevPAddr) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + + PVR_DPF((PVR_DBG_MESSAGE, "BM_GetPhysPageAddr")); + + PVR_ASSERT(psMemInfo && psDevPAddr); + + /* check it's a page address */ + PVR_ASSERT((sDevVPageAddr.uiAddr & 0xFFF) == 0); + + /* PRQA S 0505 4 */ /* PVR_ASSERT should catch NULL ptrs */ + psDeviceNode = ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->pBMContext->psDeviceNode; + + *psDevPAddr = psDeviceNode->pfnMMUGetPhysPageAddr(((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->pMMUHeap, + sDevVPageAddr); +} + + +/*! +****************************************************************************** + @Function BM_GetMMUContext + + @Description utility function to return the MMU context + + @Input hDevMemHeap - the Dev mem heap handle + + @Return MMU context, else NULL +**************************************************************************/ +MMU_CONTEXT* BM_GetMMUContext(IMG_HANDLE hDevMemHeap) +{ + BM_HEAP *pBMHeap = (BM_HEAP*)hDevMemHeap; + + PVR_DPF((PVR_DBG_VERBOSE, "BM_GetMMUContext")); + + return pBMHeap->pBMContext->psMMUContext; +} + +/*! +****************************************************************************** + @Function BM_GetMMUContextFromMemContext + + @Description utility function to return the MMU context + + @Input hDevMemContext - the Dev mem context handle + + @Return MMU context, else NULL +**************************************************************************/ +MMU_CONTEXT* BM_GetMMUContextFromMemContext(IMG_HANDLE hDevMemContext) +{ + BM_CONTEXT *pBMContext = (BM_CONTEXT*)hDevMemContext; + + PVR_DPF ((PVR_DBG_VERBOSE, "BM_GetMMUContextFromMemContext")); + + return pBMContext->psMMUContext; +} + +/*! +****************************************************************************** + @Function BM_GetMMUHeap + + @Description utility function to return the MMU heap handle + + @Input hDevMemHeap - the Dev mem heap handle + + @Return MMU heap handle, else NULL +**************************************************************************/ +IMG_HANDLE BM_GetMMUHeap(IMG_HANDLE hDevMemHeap) +{ + PVR_DPF((PVR_DBG_VERBOSE, "BM_GetMMUHeap")); + + return (IMG_HANDLE)((BM_HEAP*)hDevMemHeap)->pMMUHeap; +} + + +/*! +****************************************************************************** + @Function BM_GetDeviceNode + + @Description utility function to return the devicenode from the BM Context + + @Input hDevMemContext - the Dev Mem Context + + @Return MMU heap handle, else NULL +**************************************************************************/ +PVRSRV_DEVICE_NODE* BM_GetDeviceNode(IMG_HANDLE hDevMemContext) +{ + PVR_DPF((PVR_DBG_VERBOSE, "BM_GetDeviceNode")); + + return ((BM_CONTEXT*)hDevMemContext)->psDeviceNode; +} + + +/*! +****************************************************************************** + @Function BM_GetMappingHandle + + @Description utility function to return the mapping handle from a meminfo + + @Input psMemInfo - kernel meminfo + + @Return mapping handle, else NULL +**************************************************************************/ +IMG_HANDLE BM_GetMappingHandle(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + PVR_DPF((PVR_DBG_VERBOSE, "BM_GetMappingHandle")); + + return ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->hOSMemHandle; +} + +/*! +****************************************************************************** + @Function BM_MappingHandleFromBuffer + + @Description utility function to get the BM mapping handle from a BM buffer + + @Input hBuffer - Handle to BM buffer + + @Return BM mapping handle +**************************************************************************/ +IMG_HANDLE BM_MappingHandleFromBuffer(IMG_HANDLE hBuffer) +{ + BM_BUF *psBuffer; + + PVR_ASSERT(hBuffer != IMG_NULL); + psBuffer = hBuffer; + return psBuffer->pMapping; +} + +/*! +****************************************************************************** + @Function BM_GetVirtualSize + + @Description utility function to get the VM size of a BM mapping + + @Input hBMHandle - Handle to BM mapping + + @Return VM size of mapping +**************************************************************************/ +IMG_UINT32 BM_GetVirtualSize(IMG_HANDLE hBMHandle) +{ + BM_MAPPING *psMapping; + + PVR_ASSERT(hBMHandle != IMG_NULL); + psMapping = hBMHandle; + return psMapping->ui32ChunkSize * psMapping->ui32NumVirtChunks; +} + +/*! +****************************************************************************** + @Function BM_MapPageAtOffset + + @Description utility function check if the specificed offset in a BM mapping + is a page that needs tp be mapped + + @Input hBMHandle - Handle to BM mapping + + @Input ui32Offset - Offset into allocation + + @Return IMG_TRUE if the page should be mapped +**************************************************************************/ +IMG_BOOL BM_MapPageAtOffset(IMG_HANDLE hBMHandle, IMG_UINT32 ui32Offset) +{ + BM_MAPPING *psMapping; + IMG_UINT32 ui32ChunkIndex; + + PVR_ASSERT(hBMHandle != IMG_NULL); + psMapping = hBMHandle; + + ui32ChunkIndex = ui32Offset / psMapping->ui32ChunkSize; + /* Check for overrun */ + PVR_ASSERT(ui32ChunkIndex <= psMapping->ui32NumVirtChunks); + return psMapping->pabMapChunk[ui32ChunkIndex]; +} + +/*! +****************************************************************************** + @Function BM_VirtOffsetToPhyscial + + @Description utility function find of physical offset of a sparse allocation + from it's virtual offset. + + @Input hBMHandle - Handle to BM mapping + + @Input ui32VirtOffset - Virtual offset into allocation + + @Output pui32PhysOffset - Physical offset + + @Return IMG_TRUE if the virtual offset is physically backed +**************************************************************************/ +IMG_BOOL BM_VirtOffsetToPhysical(IMG_HANDLE hBMHandle, + IMG_UINT32 ui32VirtOffset, + IMG_UINT32 *pui32PhysOffset) +{ + BM_MAPPING *psMapping; + IMG_UINT32 ui32ChunkOffset; + IMG_UINT32 ui32PhysOffset = 0; + IMG_UINT32 i; + + PVR_ASSERT(hBMHandle != IMG_NULL); + psMapping = hBMHandle; + + ui32ChunkOffset = ui32VirtOffset / psMapping->ui32ChunkSize; + if (!psMapping->pabMapChunk[ui32ChunkOffset]) + { + return IMG_FALSE; + } + + for (i=0;i<ui32ChunkOffset;i++) + { + if (psMapping->pabMapChunk[i]) + { + ui32PhysOffset += psMapping->ui32ChunkSize; + } + } + *pui32PhysOffset = ui32PhysOffset; + + return IMG_TRUE; +} +/****************************************************************************** + End of file (buffer_manager.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/deviceclass.c b/pvr-source/services4/srvkm/common/deviceclass.c new file mode 100644 index 0000000..d047c78 --- /dev/null +++ b/pvr-source/services4/srvkm/common/deviceclass.c @@ -0,0 +1,2863 @@ +/*************************************************************************/ /*! +@Title Device class services functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Kernel services functions for device class devices +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "buffer_manager.h" +#include "kernelbuffer.h" +#include "kerneldisplay.h" +#include "pvr_bridge_km.h" +#include "pdump_km.h" +#include "deviceid.h" + +#include "lists.h" +#if defined(CONFIG_GCBV) +#include "gc_bvmapping.h" +#endif + +PVRSRV_ERROR AllocateDeviceID(SYS_DATA *psSysData, IMG_UINT32 *pui32DevID); +PVRSRV_ERROR FreeDeviceID(SYS_DATA *psSysData, IMG_UINT32 ui32DevID); + +#if defined(SUPPORT_MISR_IN_THREAD) +void OSVSyncMISR(IMG_HANDLE, IMG_BOOL); +#endif + +#if defined(SUPPORT_CUSTOM_SWAP_OPERATIONS) +IMG_VOID PVRSRVFreeCommandCompletePacketKM(IMG_HANDLE hCmdCookie, + IMG_BOOL bScheduleMISR); +#endif +/*********************************************************************** + Local Display Class Structures +************************************************************************/ +typedef struct PVRSRV_DC_SRV2DISP_KMJTABLE_TAG *PPVRSRV_DC_SRV2DISP_KMJTABLE; + +/* + Display Class Buffer Info +*/ +typedef struct PVRSRV_DC_BUFFER_TAG +{ + /* BC/DC common details - THIS MUST BE THE FIRST MEMBER */ + PVRSRV_DEVICECLASS_BUFFER sDeviceClassBuffer; + + struct PVRSRV_DISPLAYCLASS_INFO_TAG *psDCInfo; + struct PVRSRV_DC_SWAPCHAIN_TAG *psSwapChain; +} PVRSRV_DC_BUFFER; + +/* + Display Device Class kernel swapchain information structure +*/ +typedef struct PVRSRV_DC_SWAPCHAIN_TAG +{ + IMG_HANDLE hExtSwapChain; + IMG_UINT32 ui32SwapChainID; + IMG_UINT32 ui32RefCount; + IMG_UINT32 ui32Flags; + PVRSRV_QUEUE_INFO *psQueue; + PVRSRV_DC_BUFFER asBuffer[PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS]; + IMG_UINT32 ui32BufferCount; + PVRSRV_DC_BUFFER *psLastFlipBuffer; + IMG_UINT32 ui32MinSwapInterval; + IMG_UINT32 ui32MaxSwapInterval; +#if !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) + PVRSRV_KERNEL_SYNC_INFO **ppsLastSyncInfos; + IMG_UINT32 ui32LastNumSyncInfos; +#endif /* !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) */ + struct PVRSRV_DISPLAYCLASS_INFO_TAG *psDCInfo; + struct PVRSRV_DC_SWAPCHAIN_TAG *psNext; +} PVRSRV_DC_SWAPCHAIN; + + +/* + Display Device Class kernel swapchain referecne structure +*/ +typedef struct PVRSRV_DC_SWAPCHAIN_REF_TAG +{ + struct PVRSRV_DC_SWAPCHAIN_TAG *psSwapChain; + IMG_HANDLE hResItem; +} PVRSRV_DC_SWAPCHAIN_REF; + + +/* + Display Device Class kernel services information structure +*/ +typedef struct PVRSRV_DISPLAYCLASS_INFO_TAG +{ + IMG_UINT32 ui32RefCount; + IMG_UINT32 ui32DeviceID; + IMG_HANDLE hExtDevice; + PPVRSRV_DC_SRV2DISP_KMJTABLE psFuncTable; + IMG_HANDLE hDevMemContext; + PVRSRV_DC_BUFFER sSystemBuffer; + struct PVRSRV_DC_SWAPCHAIN_TAG *psDCSwapChainShared; +} PVRSRV_DISPLAYCLASS_INFO; + + +/* + Per-context Display Device Class kernel services information structure +*/ +typedef struct PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO_TAG +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PRESMAN_ITEM hResItem; +} PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO; + + +/*********************************************************************** + Local Buffer Class Structures +************************************************************************/ +typedef struct PVRSRV_BC_SRV2BUFFER_KMJTABLE_TAG *PPVRSRV_BC_SRV2BUFFER_KMJTABLE; + +/* + Buffer Class Buffer Info +*/ +typedef struct PVRSRV_BC_BUFFER_TAG +{ + /* BC/DC common details - THIS MUST BE THE FIRST MEMBER */ + PVRSRV_DEVICECLASS_BUFFER sDeviceClassBuffer; + + struct PVRSRV_BUFFERCLASS_INFO_TAG *psBCInfo; +} PVRSRV_BC_BUFFER; + + +/* + Buffer Device Class kernel services information structure +*/ +typedef struct PVRSRV_BUFFERCLASS_INFO_TAG +{ + IMG_UINT32 ui32RefCount; + IMG_UINT32 ui32DeviceID; + IMG_HANDLE hExtDevice; + PPVRSRV_BC_SRV2BUFFER_KMJTABLE psFuncTable; + IMG_HANDLE hDevMemContext; + /* buffer info returned from 3rd party driver */ + IMG_UINT32 ui32BufferCount; + PVRSRV_BC_BUFFER *psBuffer; + +} PVRSRV_BUFFERCLASS_INFO; + + +/* + Per-context Buffer Device Class kernel services information structure +*/ +typedef struct PVRSRV_BUFFERCLASS_PERCONTEXT_INFO_TAG +{ + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + IMG_HANDLE hResItem; +} PVRSRV_BUFFERCLASS_PERCONTEXT_INFO; + + +/*! +****************************************************************************** + @Function DCDeviceHandleToDCInfo + + @Description + + Convert a client-visible 3rd party device class handle to an internal + PVRSRV_DISPLAYCLASS_INFO pointer. + + @Input hDeviceKM - handle to display class device, returned from OpenDCDevice + + @Return + success: pointer to PVRSRV_DISPLAYCLASS_INFO + failure: IMG_NULL +******************************************************************************/ +static PVRSRV_DISPLAYCLASS_INFO* DCDeviceHandleToDCInfo (IMG_HANDLE hDeviceKM) +{ + PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *psDCPerContextInfo; + + psDCPerContextInfo = (PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *)hDeviceKM; + + return psDCPerContextInfo->psDCInfo; +} + + +/*! +****************************************************************************** + @Function BCDeviceHandleToBCInfo + + @Description + + Convert a client-visible 3rd party buffer class handle to an internal + PVRSRV_BUFFERCLASS_INFO pointer. + + @Input hDeviceKM - handle to buffer class device, returned from OpenBCDevice + + @Return + success: pointer to PVRSRV_BUFFERCLASS_INFO + failure: IMG_NULL +******************************************************************************/ +static PVRSRV_BUFFERCLASS_INFO* BCDeviceHandleToBCInfo (IMG_HANDLE hDeviceKM) +{ + PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *psBCPerContextInfo; + + psBCPerContextInfo = (PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *)hDeviceKM; + + return psBCPerContextInfo->psBCInfo; +} + +/*! +****************************************************************************** + @Function PVRSRVEnumerateDCKM_ForEachVaCb + + @Description + + Enumerates the device node (if is of the same class as given). + + @Input psDeviceNode - The device node to be enumerated + va - variable arguments list, with: + pui32DevCount - The device count pointer (to be increased) + ppui32DevID - The pointer to the device IDs pointer (to be updated and increased) + peDeviceClass - The pointer to the device class of the psDeviceNode's to be enumerated. +******************************************************************************/ +static IMG_VOID PVRSRVEnumerateDCKM_ForEachVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + IMG_UINT *pui32DevCount; + IMG_UINT32 **ppui32DevID; + PVRSRV_DEVICE_CLASS peDeviceClass; + + pui32DevCount = va_arg(va, IMG_UINT*); + ppui32DevID = va_arg(va, IMG_UINT32**); + peDeviceClass = va_arg(va, PVRSRV_DEVICE_CLASS); + + if ((psDeviceNode->sDevId.eDeviceClass == peDeviceClass) + && (psDeviceNode->sDevId.eDeviceType == PVRSRV_DEVICE_TYPE_EXT)) + { + (*pui32DevCount)++; + if(*ppui32DevID) + { + *(*ppui32DevID)++ = psDeviceNode->sDevId.ui32DeviceIndex; + } + } +} + + +/*! +****************************************************************************** + + @Function PVRSRVEnumerateDCKM + + @Description + + Enumerates devices available in a given class. + On first call, pass valid ptr for pui32DevCount and IMG_NULL for pui32DevID, + On second call, pass same ptr for pui32DevCount and client allocated ptr + for pui32DevID device id list + + @Input hServices - handle for services connection + @Input ui32DevClass - device class identifier + @Output pui32DevCount - number of devices available in class + @Output pui32DevID - list of device ids in the device class + + @Return + success: handle to matching display class device + failure: IMG_NULL + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVEnumerateDCKM (PVRSRV_DEVICE_CLASS DeviceClass, + IMG_UINT32 *pui32DevCount, + IMG_UINT32 *pui32DevID ) +{ + /*PVRSRV_DEVICE_NODE *psDeviceNode;*/ + IMG_UINT ui32DevCount = 0; + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + /* search devonode list for devices in specified class and return the device ids */ + List_PVRSRV_DEVICE_NODE_ForEach_va(psSysData->psDeviceNodeList, + &PVRSRVEnumerateDCKM_ForEachVaCb, + &ui32DevCount, + &pui32DevID, + DeviceClass); + + if(pui32DevCount) + { + *pui32DevCount = ui32DevCount; + } + else if(pui32DevID == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVEnumerateDCKM: Invalid parameters")); + return (PVRSRV_ERROR_INVALID_PARAMS); + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterDCDeviceKM + + @Description + + registers an external device with the system + + @Input psFuncTable : device function table + + @Output pui32DeviceID : unique device key (for case of multiple identical devices) + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVRegisterDCDeviceKM (PVRSRV_DC_SRV2DISP_KMJTABLE *psFuncTable, + IMG_UINT32 *pui32DeviceID) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo = IMG_NULL; + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + + /* + IN: + - name of client side ext. device driver library for subsequent loading + - predefined list of callbacks into kernel ext. device driver (based on class type) + + FUNCTION TASKS: + - allocate display device class info structure + - hang ext.device kernel callbacks on this structure (pfnKSwapToSystem) + + OUT: + - DEVICE_ID + - pass back devinfo? no + + Q&A: + - DEVICE_ID passed in or allocated - assume allocate + */ + + SysAcquireData(&psSysData); + + /* + If we got this far we're doing dynamic enumeration + or first time static registration + */ + + /* Allocate device control block */ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*psDCInfo), + (IMG_VOID **)&psDCInfo, IMG_NULL, + "Display Class Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterDCDeviceKM: Failed psDCInfo alloc")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet (psDCInfo, 0, sizeof(*psDCInfo)); + + /* setup the display device information structure */ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE), + (IMG_VOID **)&psDCInfo->psFuncTable, IMG_NULL, + "Function table for SRVKM->DISPLAY") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterDCDeviceKM: Failed psFuncTable alloc")); + goto ErrorExit; + } + OSMemSet (psDCInfo->psFuncTable, 0, sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE)); + + /* copy the jump table */ + *psDCInfo->psFuncTable = *psFuncTable; + + /* Allocate device node */ + if(OSAllocMem( PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_DEVICE_NODE), + (IMG_VOID **)&psDeviceNode, IMG_NULL, + "Device Node") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterDCDeviceKM: Failed psDeviceNode alloc")); + goto ErrorExit; + } + OSMemSet (psDeviceNode, 0, sizeof(PVRSRV_DEVICE_NODE)); + + psDeviceNode->pvDevice = (IMG_VOID*)psDCInfo; + psDeviceNode->ui32pvDeviceSize = sizeof(*psDCInfo); + psDeviceNode->ui32RefCount = 1; + psDeviceNode->sDevId.eDeviceType = PVRSRV_DEVICE_TYPE_EXT; + psDeviceNode->sDevId.eDeviceClass = PVRSRV_DEVICE_CLASS_DISPLAY; + psDeviceNode->psSysData = psSysData; + + /* allocate a unique device id */ + if (AllocateDeviceID(psSysData, &psDeviceNode->sDevId.ui32DeviceIndex) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterBCDeviceKM: Failed to allocate Device ID")); + goto ErrorExit; + } + psDCInfo->ui32DeviceID = psDeviceNode->sDevId.ui32DeviceIndex; + if (pui32DeviceID) + { + *pui32DeviceID = psDeviceNode->sDevId.ui32DeviceIndex; + } + + /* Register the device with the system */ + SysRegisterExternalDevice(psDeviceNode); + + /* and finally insert the device into the dev-list */ + List_PVRSRV_DEVICE_NODE_Insert(&psSysData->psDeviceNodeList, psDeviceNode); + + return PVRSRV_OK; + +ErrorExit: + + if(psDCInfo->psFuncTable) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE), psDCInfo->psFuncTable, IMG_NULL); + psDCInfo->psFuncTable = IMG_NULL; + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DISPLAYCLASS_INFO), psDCInfo, IMG_NULL); + /*not nulling pointer, out of scope*/ + + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + +/*! +****************************************************************************** + + @Function PVRSRVRemoveDCDeviceKM + + @Description + + Removes external device from services system record + + @Input ui32DeviceIndex : unique device key (for case of multiple identical devices) + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVRemoveDCDeviceKM(IMG_UINT32 ui32DevIndex) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE *psDeviceNode; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + + SysAcquireData(&psSysData); + + /*search the node matching the devindex and display class*/ + psDeviceNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DevIndex, + IMG_FALSE, + PVRSRV_DEVICE_CLASS_DISPLAY); + if (!psDeviceNode) + { + /*device not found*/ + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemoveDCDeviceKM: requested device %d not present", ui32DevIndex)); + return PVRSRV_ERROR_NO_DEVICENODE_FOUND; + } + + /* setup DCInfo ptr */ + psDCInfo = (PVRSRV_DISPLAYCLASS_INFO*)psDeviceNode->pvDevice; + + /* + The device can only be removed if there are + no open connections in the Services interface + */ + if(psDCInfo->ui32RefCount == 0) + { + /* + Remove from the device list. + */ + List_PVRSRV_DEVICE_NODE_Remove(psDeviceNode); + + /* Unregister the device with the system */ + SysRemoveExternalDevice(psDeviceNode); + + /* + OK found a device with a matching devindex + remove registration information + */ + PVR_ASSERT(psDCInfo->ui32RefCount == 0); + (IMG_VOID)FreeDeviceID(psSysData, ui32DevIndex); + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE), psDCInfo->psFuncTable, IMG_NULL); + psDCInfo->psFuncTable = IMG_NULL; + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DISPLAYCLASS_INFO), psDCInfo, IMG_NULL); + /*not nulling original pointer, overwritten*/ + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DEVICE_NODE), psDeviceNode, IMG_NULL); + /*not nulling pointer, out of scope*/ + } + else + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemoveDCDeviceKM: failed as %d Services DC API connections are still open", psDCInfo->ui32RefCount)); + return PVRSRV_ERROR_UNABLE_TO_REMOVE_DEVICE; + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterBCDeviceKM + + @Description + + registers an external device with the system + + @Input psFuncTable : device function table + @Input ui32DeviceIndex : unique device key (for case of multiple identical devices) + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVRegisterBCDeviceKM (PVRSRV_BC_SRV2BUFFER_KMJTABLE *psFuncTable, + IMG_UINT32 *pui32DeviceID) +{ + PVRSRV_BUFFERCLASS_INFO *psBCInfo = IMG_NULL; + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + /* + IN: + - name of client side ext. device driver library for subsequent loading + - predefined list of callbacks into kernel ext. device driver (based on class type) + + FUNCTION TASKS: + - allocate buffer device class info structure + + OUT: + - DEVICE_ID + - pass back devinfo? no + + Q&A: + - DEVICE_ID passed in or allocated - assume allcoate + */ + + SysAcquireData(&psSysData); + + /* + If we got this far we're doing dynamic enumeration + or first time static registration + */ + + /* Allocate device control block */ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*psBCInfo), + (IMG_VOID **)&psBCInfo, IMG_NULL, + "Buffer Class Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterBCDeviceKM: Failed psBCInfo alloc")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet (psBCInfo, 0, sizeof(*psBCInfo)); + + /* setup the buffer device information structure */ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_BC_SRV2BUFFER_KMJTABLE), + (IMG_VOID **)&psBCInfo->psFuncTable, IMG_NULL, + "Function table for SRVKM->BUFFER") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterBCDeviceKM: Failed psFuncTable alloc")); + goto ErrorExit; + } + OSMemSet (psBCInfo->psFuncTable, 0, sizeof(PVRSRV_BC_SRV2BUFFER_KMJTABLE)); + + /* copy the jump table */ + *psBCInfo->psFuncTable = *psFuncTable; + + /* Allocate device node */ + if(OSAllocMem( PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_DEVICE_NODE), + (IMG_VOID **)&psDeviceNode, IMG_NULL, + "Device Node") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterBCDeviceKM: Failed psDeviceNode alloc")); + goto ErrorExit; + } + OSMemSet (psDeviceNode, 0, sizeof(PVRSRV_DEVICE_NODE)); + + psDeviceNode->pvDevice = (IMG_VOID*)psBCInfo; + psDeviceNode->ui32pvDeviceSize = sizeof(*psBCInfo); + psDeviceNode->ui32RefCount = 1; + psDeviceNode->sDevId.eDeviceType = PVRSRV_DEVICE_TYPE_EXT; + psDeviceNode->sDevId.eDeviceClass = PVRSRV_DEVICE_CLASS_BUFFER; + psDeviceNode->psSysData = psSysData; + + /* allocate a unique device id */ + if (AllocateDeviceID(psSysData, &psDeviceNode->sDevId.ui32DeviceIndex) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterBCDeviceKM: Failed to allocate Device ID")); + goto ErrorExit; + } + psBCInfo->ui32DeviceID = psDeviceNode->sDevId.ui32DeviceIndex; + if (pui32DeviceID) + { + *pui32DeviceID = psDeviceNode->sDevId.ui32DeviceIndex; + } + + /* and finally insert the device into the dev-list */ + List_PVRSRV_DEVICE_NODE_Insert(&psSysData->psDeviceNodeList, psDeviceNode); + + return PVRSRV_OK; + +ErrorExit: + + if(psBCInfo->psFuncTable) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PPVRSRV_BC_SRV2BUFFER_KMJTABLE), psBCInfo->psFuncTable, IMG_NULL); + psBCInfo->psFuncTable = IMG_NULL; + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BUFFERCLASS_INFO), psBCInfo, IMG_NULL); + /*not nulling shared pointer, wasn't allocated to this point*/ + + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRemoveBCDeviceKM + + @Description + + Removes external device from services system record + + @Input ui32DeviceIndex : unique device key (for case of multiple identical devices) + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVRemoveBCDeviceKM(IMG_UINT32 ui32DevIndex) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE *psDevNode; + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + + SysAcquireData(&psSysData); + + /*search the device node with the devindex and buffer class*/ + psDevNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DevIndex, + IMG_FALSE, + PVRSRV_DEVICE_CLASS_BUFFER); + + if (!psDevNode) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemoveBCDeviceKM: requested device %d not present", ui32DevIndex)); + return PVRSRV_ERROR_NO_DEVICENODE_FOUND; + } + + /* set-up devnode ptr */ +/* psDevNode = *(ppsDevNode); */ + /* setup BCInfo ptr */ + psBCInfo = (PVRSRV_BUFFERCLASS_INFO*)psDevNode->pvDevice; + + /* + The device can only be removed if there are + no open connections in the Services interface + */ + if(psBCInfo->ui32RefCount == 0) + { + /* + Remove from the device list. + */ + List_PVRSRV_DEVICE_NODE_Remove(psDevNode); + + /* + OK found a device with a matching devindex + remove registration information + */ + (IMG_VOID)FreeDeviceID(psSysData, ui32DevIndex); + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BC_SRV2BUFFER_KMJTABLE), psBCInfo->psFuncTable, IMG_NULL); + psBCInfo->psFuncTable = IMG_NULL; + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BUFFERCLASS_INFO), psBCInfo, IMG_NULL); + /*not nulling pointer, copy on stack*/ + (IMG_VOID)OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DEVICE_NODE), psDevNode, IMG_NULL); + /*not nulling pointer, out of scope*/ + } + else + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemoveBCDeviceKM: failed as %d Services BC API connections are still open", psBCInfo->ui32RefCount)); + return PVRSRV_ERROR_UNABLE_TO_REMOVE_DEVICE; + } + + return PVRSRV_OK; +} + + + +/*! +****************************************************************************** + + @Function PVRSRVCloseDCDeviceKM + + @Description + + Closes a connection to the Display Class device + + @Input hDeviceKM : device handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVCloseDCDeviceKM (IMG_HANDLE hDeviceKM) +{ + PVRSRV_ERROR eError; + PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *psDCPerContextInfo; + + psDCPerContextInfo = (PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *)hDeviceKM; + + /* Remove the item from the resman list and trigger the callback. */ + eError = ResManFreeResByPtr(psDCPerContextInfo->hResItem, CLEANUP_WITH_POLL); + + return eError; +} + + +static PVRSRV_ERROR CloseDCDeviceCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *psDCPerContextInfo; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + psDCPerContextInfo = (PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *)pvParam; + psDCInfo = psDCPerContextInfo->psDCInfo; + + if(psDCInfo->sSystemBuffer.sDeviceClassBuffer.ui32MemMapRefCount != 0) + { + PVR_DPF((PVR_DBG_MESSAGE,"CloseDCDeviceCallBack: system buffer (0x%p) still mapped (refcount = %d)", + &psDCInfo->sSystemBuffer.sDeviceClassBuffer, + psDCInfo->sSystemBuffer.sDeviceClassBuffer.ui32MemMapRefCount)); + } + + psDCInfo->ui32RefCount--; + if(psDCInfo->ui32RefCount == 0) + { + /* close the external device */ + psDCInfo->psFuncTable->pfnCloseDCDevice(psDCInfo->hExtDevice); + + PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + + psDCInfo->hDevMemContext = IMG_NULL; + psDCInfo->hExtDevice = IMG_NULL; + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO), psDCPerContextInfo, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVOpenDCDeviceKM + + @Description + + Opens a connection to the Display Class device, associating the connection + with a Device Memory Context for a services managed device + + @Input psPerProc : Per-process data + @Input ui32DeviceID : unique device index + @Input hDevCookie : devcookie used to derive the Device Memory + Context into BC surfaces will be mapped into + @Outut phDeviceKM : handle to the DC device + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVOpenDCDeviceKM (PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_UINT32 ui32DeviceID, + IMG_HANDLE hDevCookie, + IMG_HANDLE *phDeviceKM) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DISPLAYCLASS_PERCONTEXT_INFO *psDCPerContextInfo; + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + + if(!phDeviceKM || !hDevCookie) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Invalid params")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + SysAcquireData(&psSysData); + + /* find the matching devicenode */ + psDeviceNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DeviceID, + IMG_FALSE, + PVRSRV_DEVICE_CLASS_DISPLAY); + if (!psDeviceNode) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: no devnode matching index %d", ui32DeviceID)); + return PVRSRV_ERROR_NO_DEVICENODE_FOUND; + } + psDCInfo = (PVRSRV_DISPLAYCLASS_INFO*)psDeviceNode->pvDevice; + + /* + Allocate the per-context DC Info before calling the external device, + to make error handling easier. + */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*psDCPerContextInfo), + (IMG_VOID **)&psDCPerContextInfo, IMG_NULL, + "Display Class per Context Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed psDCPerContextInfo alloc")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet(psDCPerContextInfo, 0, sizeof(*psDCPerContextInfo)); + + if(psDCInfo->ui32RefCount++ == 0) + { + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + /* store the device kernel context to map into */ + psDCInfo->hDevMemContext = (IMG_HANDLE)psDeviceNode->sDevMemoryInfo.pBMKernelContext; + + /* create a syncinfo for the device's system surface */ + eError = PVRSRVAllocSyncInfoKM(IMG_NULL, + (IMG_HANDLE)psDeviceNode->sDevMemoryInfo.pBMKernelContext, + &psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed sync info alloc")); + psDCInfo->ui32RefCount--; + return eError; + } + + /* open the external device */ + eError = psDCInfo->psFuncTable->pfnOpenDCDevice(ui32DeviceID, + &psDCInfo->hExtDevice, + (PVRSRV_SYNC_DATA*)psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo->psSyncDataMemInfoKM->pvLinAddrKM); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed to open external DC device")); + psDCInfo->ui32RefCount--; + PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + return eError; + } + + psDCPerContextInfo->psDCInfo = psDCInfo; + eError = PVRSRVGetDCSystemBufferKM(psDCPerContextInfo, IMG_NULL); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed to get system buffer")); + psDCInfo->ui32RefCount--; + PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + return eError; + } + psDCInfo->sSystemBuffer.sDeviceClassBuffer.ui32MemMapRefCount = 0; + } + else + { + psDCPerContextInfo->psDCInfo = psDCInfo; + } + + psDCPerContextInfo->hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DISPLAYCLASS_DEVICE, + psDCPerContextInfo, + 0, + &CloseDCDeviceCallBack); + + /* return a reference to the DCPerContextInfo */ + *phDeviceKM = (IMG_HANDLE)psDCPerContextInfo; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVEnumDCFormatsKM + + @Description + + Enumerates the devices pixel formats + + @Input hDeviceKM : device handle + @Output pui32Count : number of pixel formats + @Output psFormat : format list + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVEnumDCFormatsKM (IMG_HANDLE hDeviceKM, + IMG_UINT32 *pui32Count, + DISPLAY_FORMAT *psFormat) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + + if(!hDeviceKM || !pui32Count || !psFormat) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVEnumDCFormatsKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* call into the display device driver to get info */ + return psDCInfo->psFuncTable->pfnEnumDCFormats(psDCInfo->hExtDevice, pui32Count, psFormat); +} + + + +/*! +****************************************************************************** + + @Function PVRSRVEnumDCDimsKM + + @Description + + Enumerates the devices mode dimensions for a given pixel format + + @Input hDeviceKM : device handle + @Input psFormat : pixel format + @Output pui32Count : number of dimensions + @Output psDim : dimensions list + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVEnumDCDimsKM (IMG_HANDLE hDeviceKM, + DISPLAY_FORMAT *psFormat, + IMG_UINT32 *pui32Count, + DISPLAY_DIMS *psDim) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + + if(!hDeviceKM || !pui32Count || !psFormat) // psDim==NULL to query number of dims + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVEnumDCDimsKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* call into the display device driver to get info */ + return psDCInfo->psFuncTable->pfnEnumDCDims(psDCInfo->hExtDevice, psFormat, pui32Count, psDim); +} + + +/*! +****************************************************************************** + + @Function PVRSRVGetDCSystemBufferKM + + @Description + + Get the primary surface and optionally return its buffer handle + + @Input hDeviceKM : device handle + @Output phBuffer : Optional buffer handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVGetDCSystemBufferKM (IMG_HANDLE hDeviceKM, + IMG_HANDLE *phBuffer) +{ + PVRSRV_ERROR eError; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + IMG_HANDLE hExtBuffer; + + if(!hDeviceKM) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetDCSystemBufferKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* call into the display device driver to get info */ + eError = psDCInfo->psFuncTable->pfnGetDCSystemBuffer(psDCInfo->hExtDevice, &hExtBuffer); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetDCSystemBufferKM: Failed to get valid buffer handle from external driver")); + return eError; + } + + /* save the new info */ + psDCInfo->sSystemBuffer.sDeviceClassBuffer.pfnGetBufferAddr = psDCInfo->psFuncTable->pfnGetBufferAddr; + psDCInfo->sSystemBuffer.sDeviceClassBuffer.hDevMemContext = psDCInfo->hDevMemContext; + psDCInfo->sSystemBuffer.sDeviceClassBuffer.hExtDevice = psDCInfo->hExtDevice; + psDCInfo->sSystemBuffer.sDeviceClassBuffer.hExtBuffer = hExtBuffer; + + psDCInfo->sSystemBuffer.psDCInfo = psDCInfo; + + /* return handle */ + if (phBuffer) + { + *phBuffer = (IMG_HANDLE)&(psDCInfo->sSystemBuffer); + } + + return PVRSRV_OK; +} + + +/****************************************************************************** + + @Function PVRSRVGetDCInfoKM + + @Description + + Gets Display Class device Info + + @Input hDeviceKM : device handle + @Output psDisplayInfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVGetDCInfoKM (IMG_HANDLE hDeviceKM, + DISPLAY_INFO *psDisplayInfo) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_ERROR eError; + + if(!hDeviceKM || !psDisplayInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetDCInfoKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* call into the display device driver to get info */ + eError = psDCInfo->psFuncTable->pfnGetDCInfo(psDCInfo->hExtDevice, psDisplayInfo); + if (eError != PVRSRV_OK) + { + return eError; + } + + if (psDisplayInfo->ui32MaxSwapChainBuffers > PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS) + { + psDisplayInfo->ui32MaxSwapChainBuffers = PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS; + } + + return PVRSRV_OK; +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVDestroyDCSwapChainKM(IMG_HANDLE hSwapChainRef) +{ + PVRSRV_ERROR eError; + PVRSRV_DC_SWAPCHAIN_REF *psSwapChainRef; + + if(!hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDestroyDCSwapChainKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psSwapChainRef = hSwapChainRef; + + eError = ResManFreeResByPtr(psSwapChainRef->hResItem, CLEANUP_WITH_POLL); + + return eError; +} + + +static PVRSRV_ERROR DestroyDCSwapChain(PVRSRV_DC_SWAPCHAIN *psSwapChain) +{ + PVRSRV_ERROR eError; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo = psSwapChain->psDCInfo; + IMG_UINT32 i; + + /* Update shared swapchains list */ + if( psDCInfo->psDCSwapChainShared ) + { + if( psDCInfo->psDCSwapChainShared == psSwapChain ) + { + psDCInfo->psDCSwapChainShared = psSwapChain->psNext; + } + else + { + PVRSRV_DC_SWAPCHAIN *psCurrentSwapChain; + psCurrentSwapChain = psDCInfo->psDCSwapChainShared; + while( psCurrentSwapChain->psNext ) + { + if( psCurrentSwapChain->psNext != psSwapChain ) + { + psCurrentSwapChain = psCurrentSwapChain->psNext; + continue; + } + psCurrentSwapChain->psNext = psSwapChain->psNext; + break; + } + } + } + + /* Destroy command queue before swapchain - it may use the swapchain when commands are flushed. */ + PVRSRVDestroyCommandQueueKM(psSwapChain->psQueue); + + /* call into the display device driver to destroy a swapchain */ + eError = psDCInfo->psFuncTable->pfnDestroyDCSwapChain(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"DestroyDCSwapChainCallBack: Failed to destroy DC swap chain")); + return eError; + } + + /* free the resources */ + for(i=0; i<psSwapChain->ui32BufferCount; i++) + { + if(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + } + } + +#if !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) + if (psSwapChain->ppsLastSyncInfos) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_KERNEL_SYNC_INFO *) * psSwapChain->ui32LastNumSyncInfos, + psSwapChain->ppsLastSyncInfos, IMG_NULL); + } +#endif /* !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) */ + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_SWAPCHAIN), psSwapChain, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return eError; +} + + +static PVRSRV_ERROR DestroyDCSwapChainRefCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_DC_SWAPCHAIN_REF *psSwapChainRef = (PVRSRV_DC_SWAPCHAIN_REF *) pvParam; + PVRSRV_ERROR eError = PVRSRV_OK; + IMG_UINT32 i; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + for (i = 0; i < psSwapChainRef->psSwapChain->ui32BufferCount; i++) + { + if (psSwapChainRef->psSwapChain->asBuffer[i].sDeviceClassBuffer.ui32MemMapRefCount != 0) + { + PVR_DPF((PVR_DBG_ERROR, "DestroyDCSwapChainRefCallBack: swapchain (0x%p) still mapped (ui32MemMapRefCount = %d)", + &psSwapChainRef->psSwapChain->asBuffer[i].sDeviceClassBuffer, + psSwapChainRef->psSwapChain->asBuffer[i].sDeviceClassBuffer.ui32MemMapRefCount)); + } + } + + if(--psSwapChainRef->psSwapChain->ui32RefCount == 0) + { + eError = DestroyDCSwapChain(psSwapChainRef->psSwapChain); + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_SWAPCHAIN_REF), psSwapChainRef, IMG_NULL); + return eError; +} + +static PVRSRV_DC_SWAPCHAIN* PVRSRVFindSharedDCSwapChainKM(PVRSRV_DISPLAYCLASS_INFO *psDCInfo, + IMG_UINT32 ui32SwapChainID) +{ + PVRSRV_DC_SWAPCHAIN *psCurrentSwapChain; + + for(psCurrentSwapChain = psDCInfo->psDCSwapChainShared; + psCurrentSwapChain; + psCurrentSwapChain = psCurrentSwapChain->psNext) + { + if(psCurrentSwapChain->ui32SwapChainID == ui32SwapChainID) + return psCurrentSwapChain; + } + return IMG_NULL; +} + +static PVRSRV_ERROR PVRSRVCreateDCSwapChainRefKM(PVRSRV_PER_PROCESS_DATA *psPerProc, + PVRSRV_DC_SWAPCHAIN *psSwapChain, + PVRSRV_DC_SWAPCHAIN_REF **ppsSwapChainRef) +{ + PVRSRV_DC_SWAPCHAIN_REF *psSwapChainRef = IMG_NULL; + + /* Allocate swapchain reference structre*/ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_DC_SWAPCHAIN_REF), + (IMG_VOID **)&psSwapChainRef, IMG_NULL, + "Display Class Swapchain Reference") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainRefKM: Failed psSwapChainRef alloc")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet (psSwapChainRef, 0, sizeof(PVRSRV_DC_SWAPCHAIN_REF)); + + /* Bump refcount */ + psSwapChain->ui32RefCount++; + + /* Create reference resource */ + psSwapChainRef->psSwapChain = psSwapChain; + psSwapChainRef->hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DISPLAYCLASS_SWAPCHAIN_REF, + psSwapChainRef, + 0, + &DestroyDCSwapChainRefCallBack); + *ppsSwapChainRef = psSwapChainRef; + + return PVRSRV_OK; +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVCreateDCSwapChainKM (PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE hDeviceKM, + IMG_UINT32 ui32Flags, + DISPLAY_SURF_ATTRIBUTES *psDstSurfAttrib, + DISPLAY_SURF_ATTRIBUTES *psSrcSurfAttrib, + IMG_UINT32 ui32BufferCount, + IMG_UINT32 ui32OEMFlags, + IMG_HANDLE *phSwapChainRef, + IMG_UINT32 *pui32SwapChainID) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain = IMG_NULL; + PVRSRV_DC_SWAPCHAIN_REF *psSwapChainRef = IMG_NULL; + PVRSRV_SYNC_DATA *apsSyncData[PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS]; + PVRSRV_QUEUE_INFO *psQueue = IMG_NULL; + PVRSRV_ERROR eError; + IMG_UINT32 i; + DISPLAY_INFO sDisplayInfo; + + + if(!hDeviceKM + || !psDstSurfAttrib + || !psSrcSurfAttrib + || !phSwapChainRef + || !pui32SwapChainID) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (ui32BufferCount > PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Too many buffers")); + return PVRSRV_ERROR_TOOMANYBUFFERS; + } + + if (ui32BufferCount < 2) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Too few buffers")); + return PVRSRV_ERROR_TOO_FEW_BUFFERS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + if( ui32Flags & PVRSRV_CREATE_SWAPCHAIN_QUERY ) + { + /* Query - use pui32SwapChainID as input */ + psSwapChain = PVRSRVFindSharedDCSwapChainKM(psDCInfo, *pui32SwapChainID ); + if( psSwapChain ) + { + /* Create new reference */ + eError = PVRSRVCreateDCSwapChainRefKM(psPerProc, + psSwapChain, + &psSwapChainRef); + if( eError != PVRSRV_OK ) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Couldn't create swap chain reference")); + return eError; + } + + *phSwapChainRef = (IMG_HANDLE)psSwapChainRef; + return PVRSRV_OK; + } + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: No shared SwapChain found for query")); + return PVRSRV_ERROR_FLIP_CHAIN_EXISTS; + } + + /* Allocate swapchain control structure for srvkm */ + if(OSAllocMem( PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_DC_SWAPCHAIN), + (IMG_VOID **)&psSwapChain, IMG_NULL, + "Display Class Swapchain") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Failed psSwapChain alloc")); + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ErrorExit; + } + OSMemSet (psSwapChain, 0, sizeof(PVRSRV_DC_SWAPCHAIN)); + + /* Create a command queue for the swapchain */ + eError = PVRSRVCreateCommandQueueKM(1024, &psQueue); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Failed to create CmdQueue")); + goto ErrorExit; + } + + /* store the Queue */ + psSwapChain->psQueue = psQueue; + + /* Create a Sync Object for each surface in the swapchain */ + for(i=0; i<ui32BufferCount; i++) + { + eError = PVRSRVAllocSyncInfoKM(IMG_NULL, + psDCInfo->hDevMemContext, + &psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Failed to alloc syninfo for psSwapChain")); + goto ErrorExit; + } + + /* setup common device class info */ + psSwapChain->asBuffer[i].sDeviceClassBuffer.pfnGetBufferAddr = psDCInfo->psFuncTable->pfnGetBufferAddr; + psSwapChain->asBuffer[i].sDeviceClassBuffer.hDevMemContext = psDCInfo->hDevMemContext; + psSwapChain->asBuffer[i].sDeviceClassBuffer.hExtDevice = psDCInfo->hExtDevice; + + /* save off useful ptrs */ + psSwapChain->asBuffer[i].psDCInfo = psDCInfo; + psSwapChain->asBuffer[i].psSwapChain = psSwapChain; + + /* syncinfos must be passed as array of syncdata ptrs to the 3rd party driver */ + apsSyncData[i] = (PVRSRV_SYNC_DATA*)psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->psSyncDataMemInfoKM->pvLinAddrKM; + } + + psSwapChain->ui32BufferCount = ui32BufferCount; + psSwapChain->psDCInfo = psDCInfo; + +#if defined(PDUMP) + PDUMPCOMMENT("Allocate DC swap chain (SwapChainID == %u, BufferCount == %u)", + *pui32SwapChainID, + ui32BufferCount); + PDUMPCOMMENT(" Src surface dimensions == %u x %u", + psSrcSurfAttrib->sDims.ui32Width, + psSrcSurfAttrib->sDims.ui32Height); + PDUMPCOMMENT(" Dst surface dimensions == %u x %u", + psDstSurfAttrib->sDims.ui32Width, + psDstSurfAttrib->sDims.ui32Height); +#endif + + eError = psDCInfo->psFuncTable->pfnGetDCInfo(psDCInfo->hExtDevice, &sDisplayInfo); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Failed to get DC info")); + return eError; + } + + psSwapChain->ui32MinSwapInterval = sDisplayInfo.ui32MinSwapInterval; + psSwapChain->ui32MaxSwapInterval = sDisplayInfo.ui32MaxSwapInterval; + + /* call into the display device driver to create a swapchain */ + eError = psDCInfo->psFuncTable->pfnCreateDCSwapChain(psDCInfo->hExtDevice, + ui32Flags, + psDstSurfAttrib, + psSrcSurfAttrib, + ui32BufferCount, + apsSyncData, + ui32OEMFlags, + &psSwapChain->hExtSwapChain, + &psSwapChain->ui32SwapChainID); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Failed to create 3rd party SwapChain")); + PDUMPCOMMENT("Swapchain allocation failed."); + goto ErrorExit; + } + + /* Create new reference */ + eError = PVRSRVCreateDCSwapChainRefKM(psPerProc, + psSwapChain, + &psSwapChainRef); + if( eError != PVRSRV_OK ) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDCSwapChainKM: Couldn't create swap chain reference")); + PDUMPCOMMENT("Swapchain allocation failed."); + goto ErrorExit; + } + + psSwapChain->ui32RefCount = 1; + psSwapChain->ui32Flags = ui32Flags; + + /* Save pointer in DC structure if ti's shared struct */ + if( ui32Flags & PVRSRV_CREATE_SWAPCHAIN_SHARED ) + { + if(! psDCInfo->psDCSwapChainShared ) + { + psDCInfo->psDCSwapChainShared = psSwapChain; + } + else + { + PVRSRV_DC_SWAPCHAIN *psOldHead = psDCInfo->psDCSwapChainShared; + psDCInfo->psDCSwapChainShared = psSwapChain; + psSwapChain->psNext = psOldHead; + } + } + + /* We create swapchain - pui32SwapChainID is output */ + *pui32SwapChainID = psSwapChain->ui32SwapChainID; + + /* return the swapchain reference handle */ + *phSwapChainRef= (IMG_HANDLE)psSwapChainRef; + + return eError; + +ErrorExit: + + for(i=0; i<ui32BufferCount; i++) + { + if(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + } + } + + if(psQueue) + { + PVRSRVDestroyCommandQueueKM(psQueue); + } + + if(psSwapChain) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_SWAPCHAIN), psSwapChain, IMG_NULL); + /*not nulling pointer, out of scope*/ + } + + return eError; +} + + + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetDCDstRectKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef, + IMG_RECT *psRect) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + + if(!hDeviceKM || !hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSetDCDstRectKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef)->psSwapChain; + + return psDCInfo->psFuncTable->pfnSetDCDstRect(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + psRect); +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetDCSrcRectKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef, + IMG_RECT *psRect) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + + if(!hDeviceKM || !hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSetDCSrcRectKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef)->psSwapChain; + + return psDCInfo->psFuncTable->pfnSetDCSrcRect(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + psRect); +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetDCDstColourKeyKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef, + IMG_UINT32 ui32CKColour) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + + if(!hDeviceKM || !hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSetDCDstColourKeyKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef)->psSwapChain; + + return psDCInfo->psFuncTable->pfnSetDCDstColourKey(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + ui32CKColour); +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetDCSrcColourKeyKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef, + IMG_UINT32 ui32CKColour) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + + if(!hDeviceKM || !hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSetDCSrcColourKeyKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef)->psSwapChain; + + return psDCInfo->psFuncTable->pfnSetDCSrcColourKey(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + ui32CKColour); +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVGetDCBuffersKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef, + IMG_UINT32 *pui32BufferCount, + IMG_HANDLE *phBuffer, + IMG_SYS_PHYADDR *psPhyAddr) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + IMG_HANDLE ahExtBuffer[PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS]; + PVRSRV_ERROR eError; + IMG_UINT32 i; + + if(!hDeviceKM || !hSwapChainRef || !phBuffer || !psPhyAddr) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetDCBuffersKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef)->psSwapChain; + + /* call into the display device driver to get info */ + eError = psDCInfo->psFuncTable->pfnGetDCBuffers(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + pui32BufferCount, + ahExtBuffer); + + PVR_ASSERT(*pui32BufferCount <= PVRSRV_MAX_DC_SWAPCHAIN_BUFFERS); + + /* + populate the srvkm's buffer structure with the 3rd party buffer handles + and return the services buffer handles + */ + for(i=0; i<*pui32BufferCount; i++) + { + psSwapChain->asBuffer[i].sDeviceClassBuffer.hExtBuffer = ahExtBuffer[i]; + phBuffer[i] = (IMG_HANDLE)&psSwapChain->asBuffer[i]; + } + +#if defined(SUPPORT_GET_DC_BUFFERS_SYS_PHYADDRS) + for(i = 0; i < *pui32BufferCount; i++) + { + IMG_UINT32 ui32ByteSize, ui32TilingStride; + IMG_SYS_PHYADDR *pPhyAddr; + IMG_BOOL bIsContiguous; + IMG_HANDLE hOSMapInfo; + IMG_VOID *pvVAddr; + + eError = psDCInfo->psFuncTable->pfnGetBufferAddr(psDCInfo->hExtDevice, + ahExtBuffer[i], + &pPhyAddr, + &ui32ByteSize, + &pvVAddr, + &hOSMapInfo, + &bIsContiguous, + &ui32TilingStride); + if(eError != PVRSRV_OK) + { + break; + } + + psPhyAddr[i] = *pPhyAddr; + } +#endif /* defined(SUPPORT_GET_DC_BUFFERS_SYS_PHYADDRS) */ + + return eError; +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSwapToDCBufferKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hBuffer, + IMG_UINT32 ui32SwapInterval, + IMG_HANDLE hPrivateTag, + IMG_UINT32 ui32ClipRectCount, + IMG_RECT *psClipRect) +{ + PVRSRV_ERROR eError; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_BUFFER *psBuffer; + PVRSRV_QUEUE_INFO *psQueue; + DISPLAYCLASS_FLIP_COMMAND *psFlipCmd; + IMG_UINT32 i; + IMG_BOOL bAddReferenceToLast = IMG_TRUE; + IMG_UINT16 ui16SwapCommandID = DC_FLIP_COMMAND; + IMG_UINT32 ui32NumSrcSyncs = 1; + PVRSRV_KERNEL_SYNC_INFO *apsSrcSync[2]; + PVRSRV_COMMAND *psCommand; + SYS_DATA *psSysData; + + if(!hDeviceKM || !hBuffer || !psClipRect) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBufferKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psBuffer = (PVRSRV_DC_BUFFER*)hBuffer; + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* Validate swap interval against limits */ + if(ui32SwapInterval < psBuffer->psSwapChain->ui32MinSwapInterval || + ui32SwapInterval > psBuffer->psSwapChain->ui32MaxSwapInterval) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBufferKM: Invalid swap interval. Requested %u, Allowed range %u-%u", + ui32SwapInterval, psBuffer->psSwapChain->ui32MinSwapInterval, psBuffer->psSwapChain->ui32MaxSwapInterval)); + return PVRSRV_ERROR_INVALID_SWAPINTERVAL; + } + +#if defined(SUPPORT_CUSTOM_SWAP_OPERATIONS) + + if(psDCInfo->psFuncTable->pfnQuerySwapCommandID != IMG_NULL) + { + psDCInfo->psFuncTable->pfnQuerySwapCommandID(psDCInfo->hExtDevice, + psBuffer->psSwapChain->hExtSwapChain, + psBuffer->sDeviceClassBuffer.hExtBuffer, + hPrivateTag, + &ui16SwapCommandID, + &bAddReferenceToLast); + + } + +#endif + + /* get the queue from the buffer structure */ + psQueue = psBuffer->psSwapChain->psQueue; + + /* specify the syncs */ + apsSrcSync[0] = psBuffer->sDeviceClassBuffer.psKernelSyncInfo; + if(bAddReferenceToLast && psBuffer->psSwapChain->psLastFlipBuffer && + psBuffer != psBuffer->psSwapChain->psLastFlipBuffer) + { + apsSrcSync[1] = psBuffer->psSwapChain->psLastFlipBuffer->sDeviceClassBuffer.psKernelSyncInfo; + ui32NumSrcSyncs++; + } + + /* insert the command (header) */ + eError = PVRSRVInsertCommandKM (psQueue, + &psCommand, + psDCInfo->ui32DeviceID, + ui16SwapCommandID, + 0, + IMG_NULL, + ui32NumSrcSyncs, + apsSrcSync, + sizeof(DISPLAYCLASS_FLIP_COMMAND) + (sizeof(IMG_RECT) * ui32ClipRectCount), + IMG_NULL, + IMG_NULL); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBufferKM: Failed to get space in queue")); + goto Exit; + } + + /* setup the flip command */ + psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND*)psCommand->pvData; + + /* Ext Device Handle */ + psFlipCmd->hExtDevice = psDCInfo->hExtDevice; + + /* Ext SwapChain Handle */ + psFlipCmd->hExtSwapChain = psBuffer->psSwapChain->hExtSwapChain; + + /* Ext Buffer Handle (Buffer to Flip to) */ + psFlipCmd->hExtBuffer = psBuffer->sDeviceClassBuffer.hExtBuffer; + + /* private tag */ + psFlipCmd->hPrivateTag = hPrivateTag; + + /* setup the clip rects */ + psFlipCmd->ui32ClipRectCount = ui32ClipRectCount; + /* cliprect memory appends the command structure */ + psFlipCmd->psClipRect = (IMG_RECT*)((IMG_UINT8*)psFlipCmd + sizeof(DISPLAYCLASS_FLIP_COMMAND)); // PRQA S 3305 + /* copy the clip rects */ + for(i=0; i<ui32ClipRectCount; i++) + { + psFlipCmd->psClipRect[i] = psClipRect[i]; + } + + /* number of vsyncs between successive flips */ + psFlipCmd->ui32SwapInterval = ui32SwapInterval; + + SysAcquireData(&psSysData); + + /* Because we might be composing just software surfaces, without + * any SGX renders since the last frame, we won't necessarily + * have cleaned/flushed the CPU caches before the buffers need + * to be displayed. + * + * Doing so now is safe because InsertCommand bumped ROP2 on the + * affected buffers (preventing more SW renders starting) but the + * display won't start to process the buffers until SubmitCommand. + */ + { + if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_FLUSH) + { + OSFlushCPUCacheKM(); + } + else if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_CLEAN) + { + OSCleanCPUCacheKM(); + } + + psSysData->ePendingCacheOpType = PVRSRV_MISC_INFO_CPUCACHEOP_NONE; + } + + /* submit the command */ + eError = PVRSRVSubmitCommandKM (psQueue, psCommand); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBufferKM: Failed to submit command")); + goto Exit; + } + + /* + Schedule an MISR to process it + */ + eError = OSScheduleMISR(psSysData); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBufferKM: Failed to schedule MISR")); + goto Exit; + } + + /* update the last flip buffer */ + psBuffer->psSwapChain->psLastFlipBuffer = psBuffer; + +Exit: + + if(eError == PVRSRV_ERROR_CANNOT_GET_QUEUE_SPACE) + { + eError = PVRSRV_ERROR_RETRY; + } + + return eError; +} + +typedef struct _CALLBACK_DATA_ +{ + IMG_PVOID pvPrivData; + IMG_UINT32 ui32PrivDataLength; + IMG_PVOID ppvMemInfos; + IMG_UINT32 ui32NumMemInfos; +} CALLBACK_DATA; + +static IMG_VOID FreePrivateData(IMG_HANDLE hCallbackData) +{ + CALLBACK_DATA *psCallbackData = hCallbackData; + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, psCallbackData->ui32PrivDataLength, + psCallbackData->pvPrivData, IMG_NULL); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(IMG_VOID *) * psCallbackData->ui32NumMemInfos, + psCallbackData->ppvMemInfos, IMG_NULL); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(CALLBACK_DATA), hCallbackData, IMG_NULL); +} + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSwapToDCBuffer2KM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChain, + IMG_UINT32 ui32SwapInterval, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfos, + PVRSRV_KERNEL_SYNC_INFO **ppsSyncInfos, + IMG_UINT32 ui32NumMemSyncInfos, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength) +{ + PVRSRV_KERNEL_SYNC_INFO **ppsCompiledSyncInfos; + IMG_UINT32 i, ui32NumCompiledSyncInfos; + DISPLAYCLASS_FLIP_COMMAND2 *psFlipCmd; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + PVRSRV_ERROR eError = PVRSRV_OK; + CALLBACK_DATA *psCallbackData; + PVRSRV_QUEUE_INFO *psQueue; + PVRSRV_COMMAND *psCommand; + IMG_PVOID *ppvMemInfos; + SYS_DATA *psSysData; + + if(!hDeviceKM || !hSwapChain || !ppsMemInfos || !ppsSyncInfos || ui32NumMemSyncInfos < 1) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psSwapChain = ((PVRSRV_DC_SWAPCHAIN_REF*)hSwapChain)->psSwapChain; + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + + /* Validate swap interval against limits */ + if(ui32SwapInterval < psSwapChain->ui32MinSwapInterval || + ui32SwapInterval > psSwapChain->ui32MaxSwapInterval) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Invalid swap interval. Requested %u, Allowed range %u-%u", + ui32SwapInterval, psSwapChain->ui32MinSwapInterval, psSwapChain->ui32MaxSwapInterval)); + return PVRSRV_ERROR_INVALID_SWAPINTERVAL; + } + + eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(CALLBACK_DATA), + (IMG_VOID **)&psCallbackData, IMG_NULL, + "PVRSRVSwapToDCBuffer2KM callback data"); + if (eError != PVRSRV_OK) + { + return eError; + } + + psCallbackData->pvPrivData = pvPrivData; + psCallbackData->ui32PrivDataLength = ui32PrivDataLength; + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(IMG_VOID *) * ui32NumMemSyncInfos, + (IMG_VOID **)&ppvMemInfos, IMG_NULL, + "Swap Command Meminfos") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to allocate space for meminfo list")); + psCallbackData->ppvMemInfos = IMG_NULL; + goto Exit; + } + + for(i = 0; i < ui32NumMemSyncInfos; i++) + { + ppvMemInfos[i] = ppsMemInfos[i]; + } + + psCallbackData->ppvMemInfos = ppvMemInfos; + psCallbackData->ui32NumMemInfos = ui32NumMemSyncInfos; + + /* get the queue from the buffer structure */ + psQueue = psSwapChain->psQueue; + +#if !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) + if(psSwapChain->ppsLastSyncInfos) + { + IMG_UINT32 ui32NumUniqueSyncInfos = psSwapChain->ui32LastNumSyncInfos; + IMG_UINT32 j; + + for(j = 0; j < psSwapChain->ui32LastNumSyncInfos; j++) + { + for(i = 0; i < ui32NumMemSyncInfos; i++) + { + if(psSwapChain->ppsLastSyncInfos[j] == ppsSyncInfos[i]) + { + psSwapChain->ppsLastSyncInfos[j] = IMG_NULL; + ui32NumUniqueSyncInfos--; + } + } + } + + ui32NumCompiledSyncInfos = ui32NumMemSyncInfos + ui32NumUniqueSyncInfos; + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_SYNC_INFO *) * ui32NumCompiledSyncInfos, + (IMG_VOID **)&ppsCompiledSyncInfos, IMG_NULL, + "Compiled syncinfos") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to allocate space for meminfo list")); + goto Exit; + } + + OSMemCopy(ppsCompiledSyncInfos, ppsSyncInfos, sizeof(PVRSRV_KERNEL_SYNC_INFO *) * ui32NumMemSyncInfos); + for(j = 0, i = ui32NumMemSyncInfos; j < psSwapChain->ui32LastNumSyncInfos; j++) + { + if(psSwapChain->ppsLastSyncInfos[j]) + { + ppsCompiledSyncInfos[i] = psSwapChain->ppsLastSyncInfos[j]; + i++; + } + } + } + else +#endif /* !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) */ + { + ppsCompiledSyncInfos = ppsSyncInfos; + ui32NumCompiledSyncInfos = ui32NumMemSyncInfos; + } + + /* insert the command (header) */ + eError = PVRSRVInsertCommandKM (psQueue, + &psCommand, + psDCInfo->ui32DeviceID, + DC_FLIP_COMMAND, + 0, + IMG_NULL, + ui32NumCompiledSyncInfos, + ppsCompiledSyncInfos, + sizeof(DISPLAYCLASS_FLIP_COMMAND2), + FreePrivateData, + psCallbackData); + + if (ppsCompiledSyncInfos != ppsSyncInfos) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_SYNC_INFO *) * ui32NumCompiledSyncInfos, + (IMG_VOID *)ppsCompiledSyncInfos, + IMG_NULL); + } + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to get space in queue")); + goto Exit; + } + + /* setup the flip command */ + psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND2*)psCommand->pvData; + + /* Ext Device Handle */ + psFlipCmd->hExtDevice = psDCInfo->hExtDevice; + + /* Ext SwapChain Handle */ + psFlipCmd->hExtSwapChain = psSwapChain->hExtSwapChain; + + /* number of vsyncs between successive flips */ + psFlipCmd->ui32SwapInterval = ui32SwapInterval; + + /* Opaque private data, if supplied */ + psFlipCmd->pvPrivData = pvPrivData; + psFlipCmd->ui32PrivDataLength = ui32PrivDataLength; + + psFlipCmd->ppsMemInfos = (PDC_MEM_INFO *)ppvMemInfos; + psFlipCmd->ui32NumMemInfos = ui32NumMemSyncInfos; + + /* Even though this is "unused", we have to initialize it, + * as the display controller might NULL-test it. + */ + psFlipCmd->hUnused = IMG_NULL; + + SysAcquireData(&psSysData); + + /* Because we might be composing just software surfaces, without + * any SGX renders since the last frame, we won't necessarily + * have cleaned/flushed the CPU caches before the buffers need + * to be displayed. + * + * Doing so now is safe because InsertCommand bumped ROP2 on the + * affected buffers (preventing more SW renders starting) but the + * display won't start to process the buffers until SubmitCommand. + */ + { + if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_FLUSH) + { + OSFlushCPUCacheKM(); + } + else if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_CLEAN) + { + OSCleanCPUCacheKM(); + } + + psSysData->ePendingCacheOpType = PVRSRV_MISC_INFO_CPUCACHEOP_NONE; + } + + /* submit the command */ + eError = PVRSRVSubmitCommandKM (psQueue, psCommand); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to submit command")); + goto Exit; + } + + /* The command has been submitted and so psCallbackData will be freed by the callback */ + psCallbackData = IMG_NULL; + + /* + Schedule an MISR to process it + */ + eError = OSScheduleMISR(psSysData); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to schedule MISR")); + goto Exit; + } + +#if !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) + /* Reallocate the syncinfo list if it was too small */ + if (psSwapChain->ui32LastNumSyncInfos < ui32NumMemSyncInfos) + { + if (psSwapChain->ppsLastSyncInfos) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_KERNEL_SYNC_INFO *) * psSwapChain->ui32LastNumSyncInfos, + psSwapChain->ppsLastSyncInfos, IMG_NULL); + } + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_SYNC_INFO *) * ui32NumMemSyncInfos, + (IMG_VOID **)&psSwapChain->ppsLastSyncInfos, IMG_NULL, + "Last syncinfos") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCBuffer2KM: Failed to allocate space for meminfo list")); + goto Exit; + } + } + + psSwapChain->ui32LastNumSyncInfos = ui32NumMemSyncInfos; + + for(i = 0; i < ui32NumMemSyncInfos; i++) + { + psSwapChain->ppsLastSyncInfos[i] = ppsSyncInfos[i]; + } +#endif /* !defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) */ + +Exit: + if (psCallbackData) + { + if(psCallbackData->ppvMemInfos) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(IMG_VOID *) * psCallbackData->ui32NumMemInfos, + psCallbackData->ppvMemInfos, IMG_NULL); + } + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(CALLBACK_DATA), psCallbackData, IMG_NULL); + } + if(eError == PVRSRV_ERROR_CANNOT_GET_QUEUE_SPACE) + { + eError = PVRSRV_ERROR_RETRY; + } + + return eError; +} + + +IMG_EXPORT +PVRSRV_ERROR PVRSRVSwapToDCSystemKM(IMG_HANDLE hDeviceKM, + IMG_HANDLE hSwapChainRef) +{ + PVRSRV_ERROR eError; + PVRSRV_QUEUE_INFO *psQueue; + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DC_SWAPCHAIN *psSwapChain; + PVRSRV_DC_SWAPCHAIN_REF *psSwapChainRef; + DISPLAYCLASS_FLIP_COMMAND *psFlipCmd; + IMG_UINT32 ui32NumSrcSyncs = 1; + PVRSRV_KERNEL_SYNC_INFO *apsSrcSync[2]; + PVRSRV_COMMAND *psCommand; + IMG_BOOL bAddReferenceToLast = IMG_TRUE; + IMG_UINT16 ui16SwapCommandID = DC_FLIP_COMMAND; + SYS_DATA *psSysData; + + if(!hDeviceKM || !hSwapChainRef) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCSystemKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDCInfo = DCDeviceHandleToDCInfo(hDeviceKM); + psSwapChainRef = (PVRSRV_DC_SWAPCHAIN_REF*)hSwapChainRef; + psSwapChain = psSwapChainRef->psSwapChain; + + /* + If more then 1 reference to the swapchain exist then + ignore any request to swap to the system buffer + */ + if (psSwapChain->ui32RefCount > 1) + { + return PVRSRV_OK; + } + + /* get the queue from the buffer structure */ + psQueue = psSwapChain->psQueue; + +#if defined(SUPPORT_CUSTOM_SWAP_OPERATIONS) + + if(psDCInfo->psFuncTable->pfnQuerySwapCommandID != IMG_NULL) + { + psDCInfo->psFuncTable->pfnQuerySwapCommandID(psDCInfo->hExtDevice, + psSwapChain->hExtSwapChain, + psDCInfo->sSystemBuffer.sDeviceClassBuffer.hExtBuffer, + 0, + &ui16SwapCommandID, + &bAddReferenceToLast); + + } + +#endif + + /* specify the syncs */ + apsSrcSync[0] = psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo; + if(bAddReferenceToLast && psSwapChain->psLastFlipBuffer) + { + /* Make sure we don't make a double dependency on the same server */ + if (apsSrcSync[0] != psSwapChain->psLastFlipBuffer->sDeviceClassBuffer.psKernelSyncInfo) + { + apsSrcSync[1] = psSwapChain->psLastFlipBuffer->sDeviceClassBuffer.psKernelSyncInfo; + ui32NumSrcSyncs++; + } + } + + /* insert the command (header) */ + eError = PVRSRVInsertCommandKM (psQueue, + &psCommand, + psDCInfo->ui32DeviceID, + ui16SwapCommandID, + 0, + IMG_NULL, + ui32NumSrcSyncs, + apsSrcSync, + sizeof(DISPLAYCLASS_FLIP_COMMAND), + IMG_NULL, + IMG_NULL); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCSystemKM: Failed to get space in queue")); + goto Exit; + } + + /* setup the flip command */ + psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND*)psCommand->pvData; + + /* Ext Device Handle */ + psFlipCmd->hExtDevice = psDCInfo->hExtDevice; + + /* Ext SwapChain Handle */ + psFlipCmd->hExtSwapChain = psSwapChain->hExtSwapChain; + + /* Ext Buffer Handle (Buffer to Flip to) */ + psFlipCmd->hExtBuffer = psDCInfo->sSystemBuffer.sDeviceClassBuffer.hExtBuffer; + + /* private tag */ + psFlipCmd->hPrivateTag = IMG_NULL; + + /* setup the clip rects */ + psFlipCmd->ui32ClipRectCount = 0; + + psFlipCmd->ui32SwapInterval = 1; + + /* submit the command */ + eError = PVRSRVSubmitCommandKM (psQueue, psCommand); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCSystemKM: Failed to submit command")); + goto Exit; + } + + /* Schedule an MISR to process it */ + SysAcquireData(&psSysData); + eError = OSScheduleMISR(psSysData); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVSwapToDCSystemKM: Failed to schedule MISR")); + goto Exit; + } + + /* update the last flip buffer */ + psSwapChain->psLastFlipBuffer = &psDCInfo->sSystemBuffer; + + eError = PVRSRV_OK; + +Exit: + + if(eError == PVRSRV_ERROR_CANNOT_GET_QUEUE_SPACE) + { + eError = PVRSRV_ERROR_RETRY; + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterSystemISRHandler + + @Description + + registers an external ISR to be called of the back of a system ISR + + @Input ppfnISRHandler : ISR pointer + + @Input hISRHandlerData : Callback data + + @Input ui32ISRSourceMask : ISR Mask + + @Input ui32DeviceID : unique device key + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVRegisterSystemISRHandler (PFN_ISR_HANDLER pfnISRHandler, + IMG_VOID *pvISRHandlerData, + IMG_UINT32 ui32ISRSourceMask, + IMG_UINT32 ui32DeviceID) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE *psDevNode; + + PVR_UNREFERENCED_PARAMETER(ui32ISRSourceMask); + + SysAcquireData(&psSysData); + + /* Find Dev Node (just using the device id, ignore the class) */ + psDevNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DeviceID, + IMG_TRUE); + + if (psDevNode == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterSystemISRHandler: Failed to get psDevNode")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_NO_DEVICENODE_FOUND; + } + + /* set up data before enabling the ISR */ + psDevNode->pvISRData = (IMG_VOID*) pvISRHandlerData; + + /* enable the ISR */ + psDevNode->pfnDeviceISR = pfnISRHandler; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVSetDCState_ForEachVaCb + + @Description + + If the device node is a display, calls its set state function. + + @Input psDeviceNode - the device node + va - variable argument list with: + ui32State - the state to be set. + +******************************************************************************/ +static +IMG_VOID PVRSRVSetDCState_ForEachVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + IMG_UINT32 ui32State; + ui32State = va_arg(va, IMG_UINT32); + + if (psDeviceNode->sDevId.eDeviceClass == PVRSRV_DEVICE_CLASS_DISPLAY) + { + psDCInfo = (PVRSRV_DISPLAYCLASS_INFO *)psDeviceNode->pvDevice; + if (psDCInfo->psFuncTable->pfnSetDCState && psDCInfo->hExtDevice) + { + psDCInfo->psFuncTable->pfnSetDCState(psDCInfo->hExtDevice, ui32State); + } + } +} + + +/*! +****************************************************************************** + + @Function PVRSRVSetDCState + + @Description + + Calls the display driver(s) to put them into the specified state. + + @Input ui32State: new DC state - one of DC_STATE_* + +******************************************************************************/ +IMG_VOID IMG_CALLCONV PVRSRVSetDCState(IMG_UINT32 ui32State) +{ +/* PVRSRV_DISPLAYCLASS_INFO *psDCInfo; + PVRSRV_DEVICE_NODE *psDeviceNode; */ + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + List_PVRSRV_DEVICE_NODE_ForEach_va(psSysData->psDeviceNodeList, + &PVRSRVSetDCState_ForEachVaCb, + ui32State); +} + +static PVRSRV_ERROR +PVRSRVDCMemInfoGetCpuVAddr(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo, + IMG_CPU_VIRTADDR *pVAddr) +{ + *pVAddr = psKernelMemInfo->pvLinAddrKM; + return PVRSRV_OK; +} + +static PVRSRV_ERROR +PVRSRVDCMemInfoGetCpuPAddr(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo, + IMG_SIZE_T uByteOffset, IMG_CPU_PHYADDR *pPAddr) +{ + *pPAddr = OSMemHandleToCpuPAddr(psKernelMemInfo->sMemBlk.hOSMemHandle, uByteOffset); + return PVRSRV_OK; +} + +static PVRSRV_ERROR +PVRSRVDCMemInfoGetByteSize(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo, + IMG_SIZE_T *uByteSize) +{ + *uByteSize = psKernelMemInfo->uAllocSize; + return PVRSRV_OK; +} + +static IMG_BOOL +PVRSRVDCMemInfoIsPhysContig(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + return OSMemHandleIsPhysContig(psKernelMemInfo->sMemBlk.hOSMemHandle); +} + +static PVRSRV_ERROR PVRSRVDCMemInfoGetBvHandle(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo, IMG_VOID **handle) +{ +#if !defined(CONFIG_GCBV) + *handle = NULL; + return PVRSRV_ERROR_NOT_SUPPORTED; +#else + *handle = gc_meminfo_to_hndl(psKernelMemInfo); + return PVRSRV_OK; +#endif +} + +/*! +****************************************************************************** + + @Function PVRSRVDCMemInfoGetCpuMultiPlanePAddr + + @Description returns physical addresses of a multi-plane buffer + + + @Input psKernelMemInfo - Pointer to Kernel Memory Info structure + puPlaneByteOffsets - requested offset inside the plane. + If the array is a NULL pointer, 0 requested offsets + are assumed for all planes; + pui32NumAddrOffsets - specifying the size of the user array. + If the array is smaller than the number of the planes + for this buffer, the correct size will be set and an + error returned back; + +@Output pPlanePAddrs - array of plane physical addresses of the returned size + in pui32NumAddrOffsets; + pui32NumAddrOffsets - contains the real number of planes for the buffer; + +@Return IMG_INT32 : size of the entire buffer or negative number on ERROR + +******************************************************************************/ +static IMG_INT32 +PVRSRVDCMemInfoGetCpuMultiPlanePAddr(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo, + IMG_SIZE_T* puPlaneByteOffsets, IMG_CPU_PHYADDR* pPlanePAddrs, + IMG_UINT32* pui32NumAddrOffsets) +{ + IMG_UINT32 aui32PlaneAddressOffsets[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + IMG_INT32 i32Ret; + IMG_UINT32 i; + + i32Ret = OSGetMemMultiPlaneInfo(psKernelMemInfo->sMemBlk.hOSMemHandle, + aui32PlaneAddressOffsets, + pui32NumAddrOffsets); + + if((i32Ret < 0) || (pPlanePAddrs == IMG_NULL)) + return i32Ret; + + for (i = 0; i < *pui32NumAddrOffsets; i++) + { + IMG_SIZE_T uiReqByteOffsets = puPlaneByteOffsets ? puPlaneByteOffsets[i] : 0; + + uiReqByteOffsets += aui32PlaneAddressOffsets[i]; + + pPlanePAddrs[i] = OSMemHandleToCpuPAddr(psKernelMemInfo->sMemBlk.hOSMemHandle, uiReqByteOffsets); + } + + return i32Ret; +} + +/*! +****************************************************************************** + + @Function PVRGetDisplayClassJTable + + @Description + + Sets up function table for 3rd party Display Class Device to call through + + @Input psJTable : pointer to function pointer table memory + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +IMG_BOOL PVRGetDisplayClassJTable(PVRSRV_DC_DISP2SRV_KMJTABLE *psJTable) +{ + psJTable->ui32TableSize = sizeof(PVRSRV_DC_DISP2SRV_KMJTABLE); + psJTable->pfnPVRSRVRegisterDCDevice = &PVRSRVRegisterDCDeviceKM; + psJTable->pfnPVRSRVRemoveDCDevice = &PVRSRVRemoveDCDeviceKM; + psJTable->pfnPVRSRVOEMFunction = &SysOEMFunction; + psJTable->pfnPVRSRVRegisterCmdProcList = &PVRSRVRegisterCmdProcListKM; + psJTable->pfnPVRSRVRemoveCmdProcList = &PVRSRVRemoveCmdProcListKM; +#if defined(SUPPORT_MISR_IN_THREAD) + psJTable->pfnPVRSRVCmdComplete = &OSVSyncMISR; +#else + psJTable->pfnPVRSRVCmdComplete = &PVRSRVCommandCompleteKM; +#endif + psJTable->pfnPVRSRVRegisterSystemISRHandler = &PVRSRVRegisterSystemISRHandler; + psJTable->pfnPVRSRVRegisterPowerDevice = &PVRSRVRegisterPowerDevice; +#if defined(SUPPORT_CUSTOM_SWAP_OPERATIONS) + psJTable->pfnPVRSRVFreeCmdCompletePacket = &PVRSRVFreeCommandCompletePacketKM; +#endif + psJTable->pfnPVRSRVDCMemInfoGetCpuVAddr = &PVRSRVDCMemInfoGetCpuVAddr; + psJTable->pfnPVRSRVDCMemInfoGetCpuPAddr = &PVRSRVDCMemInfoGetCpuPAddr; + psJTable->pfnPVRSRVDCMemInfoGetByteSize = &PVRSRVDCMemInfoGetByteSize; + psJTable->pfnPVRSRVDCMemInfoIsPhysContig = &PVRSRVDCMemInfoIsPhysContig; + psJTable->pfnPVRSRVDCMemInfoGetBvHandle = &PVRSRVDCMemInfoGetBvHandle; + psJTable->pfnPVRSRVDCMemInfoGetCpuMultiPlanePAddr = PVRSRVDCMemInfoGetCpuMultiPlanePAddr; + return IMG_TRUE; +} + + + +/****************************************************************************** + + @Function PVRSRVCloseBCDeviceKM + + @Description + + Closes a connection to the Buffer Class device + + @Input hDeviceKM : device handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVCloseBCDeviceKM (IMG_HANDLE hDeviceKM) +{ + PVRSRV_ERROR eError; + PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *psBCPerContextInfo; + + psBCPerContextInfo = (PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *)hDeviceKM; + + /* Remove the item from the resman list and trigger the callback. */ + eError = ResManFreeResByPtr(psBCPerContextInfo->hResItem, CLEANUP_WITH_POLL); + + return eError; +} + + +static PVRSRV_ERROR CloseBCDeviceCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *psBCPerContextInfo; + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + IMG_UINT32 i; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + psBCPerContextInfo = (PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *)pvParam; + + psBCInfo = psBCPerContextInfo->psBCInfo; + + for (i = 0; i < psBCInfo->ui32BufferCount; i++) + { + if (psBCInfo->psBuffer[i].sDeviceClassBuffer.ui32MemMapRefCount != 0) + { + PVR_DPF((PVR_DBG_ERROR, "CloseBCDeviceCallBack: buffer %d (0x%p) still mapped (ui32MemMapRefCount = %d)", + i, + &psBCInfo->psBuffer[i].sDeviceClassBuffer, + psBCInfo->psBuffer[i].sDeviceClassBuffer.ui32MemMapRefCount)); + return PVRSRV_ERROR_STILL_MAPPED; + } + } + + psBCInfo->ui32RefCount--; + if(psBCInfo->ui32RefCount == 0) + { + /* close the external device */ + psBCInfo->psFuncTable->pfnCloseBCDevice(psBCInfo->ui32DeviceID, psBCInfo->hExtDevice); + + /* free syncinfos */ + for(i=0; i<psBCInfo->ui32BufferCount; i++) + { + if(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + } + } + + /* free buffers */ + if(psBCInfo->psBuffer) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BC_BUFFER) * psBCInfo->ui32BufferCount, psBCInfo->psBuffer, IMG_NULL); + psBCInfo->psBuffer = IMG_NULL; + } + } + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BUFFERCLASS_PERCONTEXT_INFO), psBCPerContextInfo, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVOpenBCDeviceKM + + @Description + + Opens a connection to the Buffer Class device, associating the connection + with a Device Memory Context for a services managed device + + @Input psPerProc : Per-process data + @Input ui32DeviceID : unique device index + @Input hDevCookie : devcookie used to derive the Device Memory + Context into BC surfaces will be mapped into + @Outut phDeviceKM : handle to the DC device + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVOpenBCDeviceKM (PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_UINT32 ui32DeviceID, + IMG_HANDLE hDevCookie, + IMG_HANDLE *phDeviceKM) +{ + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + PVRSRV_BUFFERCLASS_PERCONTEXT_INFO *psBCPerContextInfo; + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + IMG_UINT32 i; + PVRSRV_ERROR eError; + + if(!phDeviceKM || !hDevCookie) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Invalid params")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + SysAcquireData(&psSysData); + + /* find the matching devicenode */ + psDeviceNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DeviceID, + IMG_FALSE, + PVRSRV_DEVICE_CLASS_BUFFER); + if (!psDeviceNode) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: No devnode matching index %d", ui32DeviceID)); + return PVRSRV_ERROR_NO_DEVICENODE_FOUND; + } + psBCInfo = (PVRSRV_BUFFERCLASS_INFO*)psDeviceNode->pvDevice; + +/* +FoundDevice: +*/ + /* + Allocate the per-context BC Info before calling the external device, + to make error handling easier. + */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*psBCPerContextInfo), + (IMG_VOID **)&psBCPerContextInfo, IMG_NULL, + "Buffer Class per Context Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed psBCPerContextInfo alloc")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet(psBCPerContextInfo, 0, sizeof(*psBCPerContextInfo)); + + if(psBCInfo->ui32RefCount++ == 0) + { + BUFFER_INFO sBufferInfo; + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + /* store the device kernel context to map into */ + psBCInfo->hDevMemContext = (IMG_HANDLE)psDeviceNode->sDevMemoryInfo.pBMKernelContext; + + /* open the external device */ + eError = psBCInfo->psFuncTable->pfnOpenBCDevice(ui32DeviceID, &psBCInfo->hExtDevice); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed to open external BC device")); + return eError; + } + + /* get information about the buffers */ + eError = psBCInfo->psFuncTable->pfnGetBCInfo(psBCInfo->hExtDevice, &sBufferInfo); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM : Failed to get BC Info")); + return eError; + } + + /* interpret and store info */ + psBCInfo->ui32BufferCount = sBufferInfo.ui32BufferCount; + + /* allocate BC buffers */ + eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_BC_BUFFER) * sBufferInfo.ui32BufferCount, + (IMG_VOID **)&psBCInfo->psBuffer, + IMG_NULL, + "Array of Buffer Class Buffer"); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed to allocate BC buffers")); + return eError; + } + OSMemSet (psBCInfo->psBuffer, + 0, + sizeof(PVRSRV_BC_BUFFER) * sBufferInfo.ui32BufferCount); + + for(i=0; i<psBCInfo->ui32BufferCount; i++) + { + /* create a syncinfo for the device's system surface */ + eError = PVRSRVAllocSyncInfoKM(IMG_NULL, + psBCInfo->hDevMemContext, + &psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed sync info alloc")); + goto ErrorExit; + } + + /* + get the buffers from the buffer class + drivers by index, passing-in the syncdata objects + */ + eError = psBCInfo->psFuncTable->pfnGetBCBuffer(psBCInfo->hExtDevice, + i, + psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->psSyncData, + &psBCInfo->psBuffer[i].sDeviceClassBuffer.hExtBuffer); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed to get BC buffers")); + goto ErrorExit; + } + + /* setup common device class info */ + psBCInfo->psBuffer[i].sDeviceClassBuffer.pfnGetBufferAddr = psBCInfo->psFuncTable->pfnGetBufferAddr; + psBCInfo->psBuffer[i].sDeviceClassBuffer.hDevMemContext = psBCInfo->hDevMemContext; + psBCInfo->psBuffer[i].sDeviceClassBuffer.hExtDevice = psBCInfo->hExtDevice; + psBCInfo->psBuffer[i].sDeviceClassBuffer.ui32MemMapRefCount = 0; + } + } + + psBCPerContextInfo->psBCInfo = psBCInfo; + psBCPerContextInfo->hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_BUFFERCLASS_DEVICE, + psBCPerContextInfo, + 0, + &CloseBCDeviceCallBack); + + /* return a reference to the BCPerContextInfo */ + *phDeviceKM = (IMG_HANDLE)psBCPerContextInfo; + + return PVRSRV_OK; + +ErrorExit: + + /* free syncinfos */ + for(i=0; i<psBCInfo->ui32BufferCount; i++) + { + if(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); + } + } + + /* free buffers */ + if(psBCInfo->psBuffer) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_BC_BUFFER), psBCInfo->psBuffer, IMG_NULL); + psBCInfo->psBuffer = IMG_NULL; + } + + return eError; +} + + + + +/****************************************************************************** + + @Function PVRSRVGetBCInfoKM + + @Description + + Gets Buffer Class device Info + + @Input hDeviceKM : device handle + @Output psBufferInfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVGetBCInfoKM (IMG_HANDLE hDeviceKM, + BUFFER_INFO *psBufferInfo) +{ + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + PVRSRV_ERROR eError; + + if(!hDeviceKM || !psBufferInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetBCInfoKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psBCInfo = BCDeviceHandleToBCInfo(hDeviceKM); + + eError = psBCInfo->psFuncTable->pfnGetBCInfo(psBCInfo->hExtDevice, psBufferInfo); + + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetBCInfoKM : Failed to get BC Info")); + return eError; + } + + return PVRSRV_OK; +} + + +/****************************************************************************** + + @Function PVRSRVGetBCBufferKM + + @Description + + Gets Buffer Class Buffer Handle + + @Input hDeviceKM : device handle + @Output psBufferInfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVGetBCBufferKM (IMG_HANDLE hDeviceKM, + IMG_UINT32 ui32BufferIndex, + IMG_HANDLE *phBuffer) +{ + PVRSRV_BUFFERCLASS_INFO *psBCInfo; + + if(!hDeviceKM || !phBuffer) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetBCBufferKM: Invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psBCInfo = BCDeviceHandleToBCInfo(hDeviceKM); + + if(ui32BufferIndex < psBCInfo->ui32BufferCount) + { + *phBuffer = (IMG_HANDLE)&psBCInfo->psBuffer[ui32BufferIndex]; + } + else + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetBCBufferKM: Buffer index %d out of range (%d)", ui32BufferIndex,psBCInfo->ui32BufferCount)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRGetBufferClassJTable + + @Description + + Sets up function table for 3rd party Buffer Class Device to call through + + @Input psJTable : pointer to function pointer table memory + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +IMG_BOOL PVRGetBufferClassJTable(PVRSRV_BC_BUFFER2SRV_KMJTABLE *psJTable) +{ + psJTable->ui32TableSize = sizeof(PVRSRV_BC_BUFFER2SRV_KMJTABLE); + + psJTable->pfnPVRSRVRegisterBCDevice = &PVRSRVRegisterBCDeviceKM; + psJTable->pfnPVRSRVScheduleDevices = &PVRSRVScheduleDevicesKM; + psJTable->pfnPVRSRVRemoveBCDevice = &PVRSRVRemoveBCDeviceKM; + + return IMG_TRUE; +} + +/****************************************************************************** + End of file (deviceclass.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/deviceid.h b/pvr-source/services4/srvkm/common/deviceid.h new file mode 100644 index 0000000..1cf9f0f --- /dev/null +++ b/pvr-source/services4/srvkm/common/deviceid.h @@ -0,0 +1,51 @@ +/*************************************************************************/ /*! +@Title Device ID helpers +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#ifndef __DEVICEID_H__ +#define __DEVICEID_H__ + +#include "services.h" +#include "syscommon.h" + +PVRSRV_ERROR AllocateDeviceID(SYS_DATA *psSysData, IMG_UINT32 *pui32DevID); +PVRSRV_ERROR FreeDeviceID(SYS_DATA *psSysData, IMG_UINT32 ui32DevID); + +#endif /* __DEVICEID_H__ */ diff --git a/pvr-source/services4/srvkm/common/devicemem.c b/pvr-source/services4/srvkm/common/devicemem.c new file mode 100644 index 0000000..872c0ba --- /dev/null +++ b/pvr-source/services4/srvkm/common/devicemem.c @@ -0,0 +1,2580 @@ +/*************************************************************************/ /*! +@Title Device addressable memory functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Device addressable memory APIs +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <stddef.h> + +#include "services_headers.h" +#include "buffer_manager.h" +#include "pdump_km.h" +#include "pvr_bridge_km.h" +#include "osfunc.h" +#if defined(CONFIG_GCBV) +#include "gc_bvmapping.h" +#endif + +#if defined(SUPPORT_ION) +#include "ion.h" +#include "env_perproc.h" +#endif + +/* local function prototypes */ +static PVRSRV_ERROR AllocDeviceMem(IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemHeap, + IMG_UINT32 ui32Flags, + IMG_SIZE_T ui32Size, + IMG_SIZE_T ui32Alignment, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINT32 ui32ChunkSize, + IMG_UINT32 ui32NumVirtChunks, + IMG_UINT32 ui32NumPhysChunks, + IMG_BOOL *pabMapChunk, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfo); + +/* local structures */ + +/* + structure stored in resman to store references + to the SRC and DST meminfo +*/ +typedef struct _RESMAN_MAP_DEVICE_MEM_DATA_ +{ + /* the DST meminfo created by the map */ + PVRSRV_KERNEL_MEM_INFO *psMemInfo; + /* SRC meminfo */ + PVRSRV_KERNEL_MEM_INFO *psSrcMemInfo; +} RESMAN_MAP_DEVICE_MEM_DATA; + +/* + map device class resman memory storage structure +*/ +typedef struct _PVRSRV_DC_MAPINFO_ +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo; + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_UINT32 ui32RangeIndex; + IMG_UINT32 ui32TilingStride; + PVRSRV_DEVICECLASS_BUFFER *psDeviceClassBuffer; +} PVRSRV_DC_MAPINFO; + +static IMG_UINT32 g_ui32SyncUID = 0; + +/*! +****************************************************************************** + + @Function PVRSRVGetDeviceMemHeapsKM + + @Description + + Gets the device shared memory heaps + + @Input hDevCookie : + @Output phDevMemContext : ptr to handle to memory context + @Output psHeapInfo : ptr to array of heap info + + @Return PVRSRV_DEVICE_NODE, valid devnode or IMG_NULL + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetDeviceMemHeapsKM(IMG_HANDLE hDevCookie, +#if defined (SUPPORT_SID_INTERFACE) + PVRSRV_HEAP_INFO_KM *psHeapInfo) +#else + PVRSRV_HEAP_INFO *psHeapInfo) +#endif +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_UINT32 ui32HeapCount; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_UINT32 i; + + if (hDevCookie == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetDeviceMemHeapsKM: hDevCookie invalid")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + /* Setup useful pointers */ + ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount; + psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap; + + /* check we don't exceed the max number of heaps */ + PVR_ASSERT(ui32HeapCount <= PVRSRV_MAX_CLIENT_HEAPS); + + /* retrieve heap information */ + for(i=0; i<ui32HeapCount; i++) + { + /* return information about the heap */ + psHeapInfo[i].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID; + psHeapInfo[i].hDevMemHeap = psDeviceMemoryHeap[i].hDevMemHeap; + psHeapInfo[i].sDevVAddrBase = psDeviceMemoryHeap[i].sDevVAddrBase; + psHeapInfo[i].ui32HeapByteSize = psDeviceMemoryHeap[i].ui32HeapSize; + psHeapInfo[i].ui32Attribs = psDeviceMemoryHeap[i].ui32Attribs; + /* (XTileStride > 0) denotes a tiled heap */ + psHeapInfo[i].ui32XTileStride = psDeviceMemoryHeap[i].ui32XTileStride; + } + + for(; i < PVRSRV_MAX_CLIENT_HEAPS; i++) + { + OSMemSet(psHeapInfo + i, 0, sizeof(*psHeapInfo)); + psHeapInfo[i].ui32HeapID = (IMG_UINT32)PVRSRV_UNDEFINED_HEAP_ID; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVCreateDeviceMemContextKM + + @Description + + Creates a device memory context + + @Input hDevCookie : + @Input psPerProc : Per-process data + @Output phDevMemContext : ptr to handle to memory context + @Output pui32ClientHeapCount : ptr to heap count + @Output psHeapInfo : ptr to array of heap info + + @Return PVRSRV_DEVICE_NODE, valid devnode or IMG_NULL + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVCreateDeviceMemContextKM(IMG_HANDLE hDevCookie, + PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE *phDevMemContext, + IMG_UINT32 *pui32ClientHeapCount, +#if defined (SUPPORT_SID_INTERFACE) + PVRSRV_HEAP_INFO_KM *psHeapInfo, +#else + PVRSRV_HEAP_INFO *psHeapInfo, +#endif + IMG_BOOL *pbCreated, + IMG_BOOL *pbShared) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_UINT32 ui32HeapCount, ui32ClientHeapCount=0; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_HANDLE hDevMemContext; + IMG_HANDLE hDevMemHeap; + IMG_DEV_PHYADDR sPDDevPAddr; + IMG_UINT32 i; + +#if !defined(PVR_SECURE_HANDLES) && !defined (SUPPORT_SID_INTERFACE) + PVR_UNREFERENCED_PARAMETER(pbShared); +#endif + + if (hDevCookie == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVCreateDeviceMemContextKM: hDevCookie invalid")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + /* + Setup useful pointers + */ + ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount; + psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap; + + /* + check we don't exceed the max number of heaps + */ + PVR_ASSERT(ui32HeapCount <= PVRSRV_MAX_CLIENT_HEAPS); + + /* + Create a memory context for the caller + */ + hDevMemContext = BM_CreateContext(psDeviceNode, + &sPDDevPAddr, + psPerProc, + pbCreated); + if (hDevMemContext == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateDeviceMemContextKM: Failed BM_CreateContext")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* create the per context heaps */ + for(i=0; i<ui32HeapCount; i++) + { + switch(psDeviceMemoryHeap[i].DevMemHeapType) + { + case DEVICE_MEMORY_HEAP_SHARED_EXPORTED: + { + /* return information about the heap */ + psHeapInfo[ui32ClientHeapCount].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID; + psHeapInfo[ui32ClientHeapCount].hDevMemHeap = psDeviceMemoryHeap[i].hDevMemHeap; + psHeapInfo[ui32ClientHeapCount].sDevVAddrBase = psDeviceMemoryHeap[i].sDevVAddrBase; + psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize = psDeviceMemoryHeap[i].ui32HeapSize; + psHeapInfo[ui32ClientHeapCount].ui32Attribs = psDeviceMemoryHeap[i].ui32Attribs; + #if defined(SUPPORT_MEMORY_TILING) + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = psDeviceMemoryHeap[i].ui32XTileStride; + #else + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = 0; + #endif + +#if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) + pbShared[ui32ClientHeapCount] = IMG_TRUE; +#endif + ui32ClientHeapCount++; + break; + } + case DEVICE_MEMORY_HEAP_PERCONTEXT: + { + if (psDeviceMemoryHeap[i].ui32HeapSize > 0) + { + hDevMemHeap = BM_CreateHeap(hDevMemContext, + &psDeviceMemoryHeap[i]); + if (hDevMemHeap == IMG_NULL) + { + BM_DestroyContext(hDevMemContext, IMG_NULL); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } + else + { + hDevMemHeap = IMG_NULL; + } + + /* return information about the heap */ + psHeapInfo[ui32ClientHeapCount].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID; + psHeapInfo[ui32ClientHeapCount].hDevMemHeap = hDevMemHeap; + psHeapInfo[ui32ClientHeapCount].sDevVAddrBase = psDeviceMemoryHeap[i].sDevVAddrBase; + psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize = psDeviceMemoryHeap[i].ui32HeapSize; + psHeapInfo[ui32ClientHeapCount].ui32Attribs = psDeviceMemoryHeap[i].ui32Attribs; + #if defined(SUPPORT_MEMORY_TILING) + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = psDeviceMemoryHeap[i].ui32XTileStride; + #else + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = 0; + #endif +#if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) + pbShared[ui32ClientHeapCount] = IMG_FALSE; +#endif + + ui32ClientHeapCount++; + break; + } + } + } + + /* return shared_exported and per context heap information to the caller */ + *pui32ClientHeapCount = ui32ClientHeapCount; + *phDevMemContext = hDevMemContext; + + return PVRSRV_OK; +} + +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVDestroyDeviceMemContextKM(IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemContext, + IMG_BOOL *pbDestroyed) +{ + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + return BM_DestroyContext(hDevMemContext, pbDestroyed); +} + + + + +/*! +****************************************************************************** + + @Function PVRSRVGetDeviceMemHeapInfoKM + + @Description + + gets heap info + + @Input hDevCookie : + @Input hDevMemContext : ptr to handle to memory context + @Output pui32ClientHeapCount : ptr to heap count + @Output psHeapInfo : ptr to array of heap info + + @Return + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetDeviceMemHeapInfoKM(IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemContext, + IMG_UINT32 *pui32ClientHeapCount, +#if defined (SUPPORT_SID_INTERFACE) + PVRSRV_HEAP_INFO_KM *psHeapInfo, +#else + PVRSRV_HEAP_INFO *psHeapInfo, +#endif + IMG_BOOL *pbShared) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_UINT32 ui32HeapCount, ui32ClientHeapCount=0; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_HANDLE hDevMemHeap; + IMG_UINT32 i; + +#if !defined(PVR_SECURE_HANDLES) && !defined (SUPPORT_SID_INTERFACE) + PVR_UNREFERENCED_PARAMETER(pbShared); +#endif + + if (hDevCookie == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetDeviceMemHeapInfoKM: hDevCookie invalid")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + /* + Setup useful pointers + */ + ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount; + psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap; + + /* + check we don't exceed the max number of heaps + */ + PVR_ASSERT(ui32HeapCount <= PVRSRV_MAX_CLIENT_HEAPS); + + /* create the per context heaps */ + for(i=0; i<ui32HeapCount; i++) + { + switch(psDeviceMemoryHeap[i].DevMemHeapType) + { + case DEVICE_MEMORY_HEAP_SHARED_EXPORTED: + { + /* return information about the heap */ + psHeapInfo[ui32ClientHeapCount].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID; + psHeapInfo[ui32ClientHeapCount].hDevMemHeap = psDeviceMemoryHeap[i].hDevMemHeap; + psHeapInfo[ui32ClientHeapCount].sDevVAddrBase = psDeviceMemoryHeap[i].sDevVAddrBase; + psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize = psDeviceMemoryHeap[i].ui32HeapSize; + psHeapInfo[ui32ClientHeapCount].ui32Attribs = psDeviceMemoryHeap[i].ui32Attribs; + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = psDeviceMemoryHeap[i].ui32XTileStride; +#if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) + pbShared[ui32ClientHeapCount] = IMG_TRUE; +#endif + ui32ClientHeapCount++; + break; + } + case DEVICE_MEMORY_HEAP_PERCONTEXT: + { + if (psDeviceMemoryHeap[i].ui32HeapSize > 0) + { + hDevMemHeap = BM_CreateHeap(hDevMemContext, + &psDeviceMemoryHeap[i]); + + if (hDevMemHeap == IMG_NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } + else + { + hDevMemHeap = IMG_NULL; + } + + /* return information about the heap */ + psHeapInfo[ui32ClientHeapCount].ui32HeapID = psDeviceMemoryHeap[i].ui32HeapID; + psHeapInfo[ui32ClientHeapCount].hDevMemHeap = hDevMemHeap; + psHeapInfo[ui32ClientHeapCount].sDevVAddrBase = psDeviceMemoryHeap[i].sDevVAddrBase; + psHeapInfo[ui32ClientHeapCount].ui32HeapByteSize = psDeviceMemoryHeap[i].ui32HeapSize; + psHeapInfo[ui32ClientHeapCount].ui32Attribs = psDeviceMemoryHeap[i].ui32Attribs; + psHeapInfo[ui32ClientHeapCount].ui32XTileStride = psDeviceMemoryHeap[i].ui32XTileStride; +#if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) + pbShared[ui32ClientHeapCount] = IMG_FALSE; +#endif + + ui32ClientHeapCount++; + break; + } + } + } + + /* return shared_exported and per context heap information to the caller */ + *pui32ClientHeapCount = ui32ClientHeapCount; + + return PVRSRV_OK; +} + +static PVRSRV_ERROR UpdateDeviceMemoryPlaneOffsets(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + if(psMemInfo->ui32Flags & PVRSRV_MEM_ION) + { + + PVRSRV_MEMBLK *psMemBlock = &(psMemInfo->sMemBlk); + IMG_UINT32 ui32AddressOffsets[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + IMG_UINT32 ui32NumAddrOffsets = PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES; + + IMG_INT32 retSize = OSGetMemMultiPlaneInfo(psMemBlock->hOSMemHandle, + ui32AddressOffsets, &ui32NumAddrOffsets); + + if((retSize > 0) && ui32NumAddrOffsets) + { + int i; + for(i = 0; i < PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES; i++) + { + if(i < ui32NumAddrOffsets) + psMemInfo->planeOffsets[i] = ui32AddressOffsets[i]; + else + psMemInfo->planeOffsets[i] = (IMG_INT32)-1; + } + } + } + + return PVRSRV_OK; + +} + +/*! +****************************************************************************** + + @Function AllocDeviceMem + + @Description + + Allocates device memory + + @Input hDevCookie : + + @Input hDevMemHeap + + @Input ui32Flags : Some combination of PVRSRV_MEM_ flags + + @Input ui32Size : Number of bytes to allocate + + @Input ui32Alignment : Alignment of allocation + + @Input pvPrivData : Opaque private data passed through to allocator + + @Input ui32PrivDataLength : Length of opaque private data + + @Output **ppsMemInfo : On success, receives a pointer to the created MEM_INFO structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static PVRSRV_ERROR AllocDeviceMem(IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemHeap, + IMG_UINT32 ui32Flags, + IMG_SIZE_T ui32Size, + IMG_SIZE_T ui32Alignment, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINT32 ui32ChunkSize, + IMG_UINT32 ui32NumVirtChunks, + IMG_UINT32 ui32NumPhysChunks, + IMG_BOOL *pabMapChunk, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfo) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo; + BM_HANDLE hBuffer; + /* Pointer to implementation details within the mem_info */ + PVRSRV_MEMBLK *psMemBlock; + IMG_BOOL bBMError; + + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + *ppsMemInfo = IMG_NULL; + + if(OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psMemInfo, IMG_NULL, + "Kernel Memory Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"AllocDeviceMem: Failed to alloc memory for block")); + return (PVRSRV_ERROR_OUT_OF_MEMORY); + } + + OSMemSet(psMemInfo, 0, sizeof(*psMemInfo)); + + psMemBlock = &(psMemInfo->sMemBlk); + + /* ION and DYNAMIC re-mapping + * require the PAGEABLE FLAG set + */ + if (ui32Flags & (PVRSRV_MEM_ION | + PVRSRV_HAP_NO_GPU_VIRTUAL_ON_ALLOC)) + { + ui32Flags |= PVRSRV_HAP_GPU_PAGEABLE; + } + + /* BM supplied Device Virtual Address with physical backing RAM */ + psMemInfo->ui32Flags = ui32Flags | PVRSRV_MEM_RAM_BACKED_ALLOCATION; + + bBMError = BM_Alloc (hDevMemHeap, + IMG_NULL, + ui32Size, + &psMemInfo->ui32Flags, + IMG_CAST_TO_DEVVADDR_UINT(ui32Alignment), + pvPrivData, + ui32PrivDataLength, + ui32ChunkSize, + ui32NumVirtChunks, + ui32NumPhysChunks, + pabMapChunk, + &hBuffer); + + if (!bBMError) + { + PVR_DPF((PVR_DBG_ERROR,"AllocDeviceMem: BM_Alloc Failed")); + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + /*not nulling pointer, out of scope*/ + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* Fill in "Implementation dependant" section of mem info */ + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer); + psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer); + + /* Convert from BM_HANDLE to external IMG_HANDLE */ + psMemBlock->hBuffer = (IMG_HANDLE)hBuffer; + + /* Fill in the public fields of the MEM_INFO structure */ + + psMemInfo->pvLinAddrKM = BM_HandleToCpuVaddr(hBuffer); + + psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr; + + if (ui32Flags & PVRSRV_MEM_SPARSE) + { + psMemInfo->uAllocSize = ui32ChunkSize * ui32NumVirtChunks; + } + else + { + psMemInfo->uAllocSize = ui32Size; + } + + /* Clear the Backup buffer pointer as we do not have one at this point. We only allocate this as we are going up/down */ + psMemInfo->pvSysBackupBuffer = IMG_NULL; + + /* Update the Multimedia plane offsets */ + UpdateDeviceMemoryPlaneOffsets(psMemInfo); + + /* + * Setup the output. + */ + *ppsMemInfo = psMemInfo; + + /* + * And I think we're done for now.... + */ + return (PVRSRV_OK); +} + +static PVRSRV_ERROR FreeDeviceMem2(PVRSRV_KERNEL_MEM_INFO *psMemInfo, PVRSRV_FREE_CALLBACK_ORIGIN eCallbackOrigin) +{ + BM_HANDLE hBuffer; + + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + hBuffer = psMemInfo->sMemBlk.hBuffer; + + switch(eCallbackOrigin) + { + case PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR: + BM_Free(hBuffer, psMemInfo->ui32Flags); + break; + case PVRSRV_FREE_CALLBACK_ORIGIN_IMPORTER: + BM_FreeExport(hBuffer, psMemInfo->ui32Flags); + break; + default: + break; + } + + if (psMemInfo->pvSysBackupBuffer && + eCallbackOrigin == PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, psMemInfo->uAllocSize, psMemInfo->pvSysBackupBuffer, IMG_NULL); + psMemInfo->pvSysBackupBuffer = IMG_NULL; + } + + if (psMemInfo->ui32RefCount == 0) + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + + return(PVRSRV_OK); +} + +static PVRSRV_ERROR FreeDeviceMem(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + BM_HANDLE hBuffer; + + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + hBuffer = psMemInfo->sMemBlk.hBuffer; + + BM_Free(hBuffer, psMemInfo->ui32Flags); + + if(psMemInfo->pvSysBackupBuffer) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, psMemInfo->uAllocSize, psMemInfo->pvSysBackupBuffer, IMG_NULL); + psMemInfo->pvSysBackupBuffer = IMG_NULL; + } + + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + + return(PVRSRV_OK); +} + + +/*! +****************************************************************************** + + @Function PVRSRVAllocSyncInfoKM + + @Description + + Allocates a sync info + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemContext, + PVRSRV_KERNEL_SYNC_INFO **ppsKernelSyncInfo) +{ + IMG_HANDLE hSyncDevMemHeap; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + BM_CONTEXT *pBMContext; + PVRSRV_ERROR eError; + PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo; + PVRSRV_SYNC_DATA *psSyncData; + + eError = OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_SYNC_INFO), + (IMG_VOID **)&psKernelSyncInfo, IMG_NULL, + "Kernel Synchronization Info"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSyncInfoKM: Failed to alloc memory")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + eError = OSAtomicAlloc(&psKernelSyncInfo->pvRefCount); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSyncInfoKM: Failed to allocate atomic")); + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + + /* Get the devnode from the devheap */ + pBMContext = (BM_CONTEXT*)hDevMemContext; + psDevMemoryInfo = &pBMContext->psDeviceNode->sDevMemoryInfo; + + /* and choose a heap for the syncinfo */ + hSyncDevMemHeap = psDevMemoryInfo->psDeviceMemoryHeap[psDevMemoryInfo->ui32SyncHeapID].hDevMemHeap; + + /* + Cache consistent flag would be unnecessary if the heap attributes were + changed to specify it. + */ + eError = AllocDeviceMem(hDevCookie, + hSyncDevMemHeap, + PVRSRV_MEM_CACHE_CONSISTENT, + sizeof(PVRSRV_SYNC_DATA), + sizeof(IMG_UINT32), + IMG_NULL, + 0, + 0, 0, 0, IMG_NULL, /* Sparse mapping args, not required */ + &psKernelSyncInfo->psSyncDataMemInfoKM); + + if (eError != PVRSRV_OK) + { + + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSyncInfoKM: Failed to alloc memory")); + OSAtomicFree(psKernelSyncInfo->pvRefCount); + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); + /*not nulling pointer, out of scope*/ + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* init sync data */ + psKernelSyncInfo->psSyncData = psKernelSyncInfo->psSyncDataMemInfoKM->pvLinAddrKM; + psSyncData = psKernelSyncInfo->psSyncData; + + psSyncData->ui32WriteOpsPending = 0; + psSyncData->ui32WriteOpsComplete = 0; + psSyncData->ui32ReadOpsPending = 0; + psSyncData->ui32ReadOpsComplete = 0; + psSyncData->ui32ReadOps2Pending = 0; + psSyncData->ui32ReadOps2Complete = 0; + psSyncData->ui32LastOpDumpVal = 0; + psSyncData->ui32LastReadOpDumpVal = 0; + psSyncData->ui64LastWrite = 0; + +#if defined(PDUMP) + PDUMPCOMMENT("Allocating kernel sync object"); + PDUMPMEM(psKernelSyncInfo->psSyncDataMemInfoKM->pvLinAddrKM, + psKernelSyncInfo->psSyncDataMemInfoKM, + 0, + (IMG_UINT32)psKernelSyncInfo->psSyncDataMemInfoKM->uAllocSize, + PDUMP_FLAGS_CONTINUOUS, + MAKEUNIQUETAG(psKernelSyncInfo->psSyncDataMemInfoKM)); +#endif + + psKernelSyncInfo->sWriteOpsCompleteDevVAddr.uiAddr = psKernelSyncInfo->psSyncDataMemInfoKM->sDevVAddr.uiAddr + offsetof(PVRSRV_SYNC_DATA, ui32WriteOpsComplete); + psKernelSyncInfo->sReadOpsCompleteDevVAddr.uiAddr = psKernelSyncInfo->psSyncDataMemInfoKM->sDevVAddr.uiAddr + offsetof(PVRSRV_SYNC_DATA, ui32ReadOpsComplete); + psKernelSyncInfo->sReadOps2CompleteDevVAddr.uiAddr = psKernelSyncInfo->psSyncDataMemInfoKM->sDevVAddr.uiAddr + offsetof(PVRSRV_SYNC_DATA, ui32ReadOps2Complete); + psKernelSyncInfo->ui32UID = g_ui32SyncUID++; + + /* syncinfo meminfo has no syncinfo! */ + psKernelSyncInfo->psSyncDataMemInfoKM->psKernelSyncInfo = IMG_NULL; + + OSAtomicInc(psKernelSyncInfo->pvRefCount); + + /* return result */ + *ppsKernelSyncInfo = psKernelSyncInfo; + + return PVRSRV_OK; +} + +IMG_EXPORT +IMG_VOID PVRSRVAcquireSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) +{ + OSAtomicInc(psKernelSyncInfo->pvRefCount); +} + +/*! +****************************************************************************** + + @Function PVRSRVFreeSyncInfoKM + + @Description + + Frees a sync info + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +IMG_VOID IMG_CALLCONV PVRSRVReleaseSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) +{ + if (OSAtomicDecAndTest(psKernelSyncInfo->pvRefCount)) + { + FreeDeviceMem(psKernelSyncInfo->psSyncDataMemInfoKM); + + /* Catch anyone who is trying to access the freed structure */ + psKernelSyncInfo->psSyncDataMemInfoKM = IMG_NULL; + psKernelSyncInfo->psSyncData = IMG_NULL; + OSAtomicFree(psKernelSyncInfo->pvRefCount); + (IMG_VOID)OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); + /*not nulling pointer, copy on stack*/ + } +} + +/*! +****************************************************************************** + + @Function freeExternal + + @Description + + Code for freeing meminfo elements that are specific to external types memory + + @Input psMemInfo : Kernel meminfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ + +static IMG_VOID freeExternal(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + IMG_HANDLE hOSWrapMem = psMemInfo->sMemBlk.hOSWrapMem; + + /* free the page addr array if req'd */ + if(psMemInfo->sMemBlk.psIntSysPAddr) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(IMG_SYS_PHYADDR), psMemInfo->sMemBlk.psIntSysPAddr, IMG_NULL); + psMemInfo->sMemBlk.psIntSysPAddr = IMG_NULL; + } + + /* Mem type dependent stuff */ + if (psMemInfo->memType == PVRSRV_MEMTYPE_WRAPPED) + { + if(hOSWrapMem) + { + OSReleasePhysPageAddr(hOSWrapMem); + } + } +#if defined(SUPPORT_ION) + else if (psMemInfo->memType == PVRSRV_MEMTYPE_ION) + { + if (hOSWrapMem) + { + IonUnimportBufferAndReleasePhysAddr(hOSWrapMem); + } + } +#endif +} + +/*! +****************************************************************************** + + @Function FreeMemCallBackCommon + + @Description + + Common code for freeing device mem (called for freeing, unwrapping and unmapping) + + @Input psMemInfo : Kernel meminfo + @Input ui32Param : packet size + @Input uibFromAllocatorParam : Are we being called by the original allocator? + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR FreeMemCallBackCommon(PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Param, + PVRSRV_FREE_CALLBACK_ORIGIN eCallbackOrigin) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + + /* decrement the refcount */ + PVRSRVKernelMemInfoDecRef(psMemInfo); + + /* check no other processes has this meminfo mapped */ + if (psMemInfo->ui32RefCount == 0) + { + if((psMemInfo->ui32Flags & PVRSRV_MEM_EXPORTED) != 0) + { +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hMemInfo = 0; +#else + IMG_HANDLE hMemInfo = IMG_NULL; +#endif + + /* find the handle */ + eError = PVRSRVFindHandle(KERNEL_HANDLE_BASE, + &hMemInfo, + psMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeMemCallBackCommon: can't find exported meminfo in the global handle list")); + return eError; + } + + /* release the handle */ + eError = PVRSRVReleaseHandle(KERNEL_HANDLE_BASE, + hMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeMemCallBackCommon: PVRSRVReleaseHandle failed for exported meminfo")); + return eError; + } + } + + switch(psMemInfo->memType) + { + /* Fall through: Free only what we should for each memory type */ + case PVRSRV_MEMTYPE_WRAPPED: + case PVRSRV_MEMTYPE_ION: + freeExternal(psMemInfo); + case PVRSRV_MEMTYPE_DEVICE: + case PVRSRV_MEMTYPE_DEVICECLASS: + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + break; + default: + PVR_DPF((PVR_DBG_ERROR, "FreeMemCallBackCommon: Unknown memType")); + eError = PVRSRV_ERROR_INVALID_MEMINFO; + } + } + +#if defined(CONFIG_GCBV) + if (psMemInfo->ui32Flags & PVRSRV_MAP_GC_MMU) + gc_bvunmap_meminfo(psMemInfo); +#endif + + /* + * FreeDeviceMem2 will do the right thing, freeing + * the virtual memory info when the allocator calls + * but only releaseing the physical pages when everyone + * is done. + */ + + if (eError == PVRSRV_OK) + { + eError = FreeDeviceMem2(psMemInfo, eCallbackOrigin); + } + + return eError; +} + +/*! +****************************************************************************** + + @Function FreeDeviceMemCallBack + + @Description + + ResMan call back to free device memory + + @Input pvParam : data packet + @Input ui32Param : packet size + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static PVRSRV_ERROR FreeDeviceMemCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo = (PVRSRV_KERNEL_MEM_INFO *)pvParam; + + PVR_UNREFERENCED_PARAMETER(bDummy); + + return FreeMemCallBackCommon(psMemInfo, ui32Param, + PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR); +} + + +/*! +****************************************************************************** + + @Function PVRSRVFreeDeviceMemKM + + @Description + + Frees memory allocated with PVRAllocDeviceMem, including the mem_info structure + + @Input psMemInfo : + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVFreeDeviceMemKM(IMG_HANDLE hDevCookie, + PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + PVRSRV_ERROR eError; + + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (psMemInfo->sMemBlk.hResItem != IMG_NULL) + { + eError = ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); + } + else + { + /* PVRSRV_MEM_NO_RESMAN */ + eError = FreeDeviceMemCallBack(psMemInfo, 0, CLEANUP_WITH_POLL); + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRemapToDevKM + + @Description + + Remaps buffer to GPU virtual address space + + @Input psMemInfo + + @Return PVRSRV_ERROR : 0 means the memory is still unmapped - ERROR, + * bigger than 0 (mapping reference count) - success mapping + * smaller than 0 - PVRSRV error +******************************************************************************/ +IMG_EXPORT +IMG_INT32 IMG_CALLCONV PVRSRVRemapToDevKM(IMG_HANDLE hDevCookie, + PVRSRV_KERNEL_MEM_INFO *psMemInfo, IMG_DEV_VIRTADDR *psDevVAddr) +{ + PVRSRV_MEMBLK *psMemBlock; + IMG_INT32 result; + + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + if (!psMemInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemapToDevKM: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psMemBlock = &(psMemInfo->sMemBlk); + + result = BM_RemapToDev(psMemBlock->hBuffer); + + if(result <= 0) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRemapToDevKM: could not remap")); + } + + *psDevVAddr = psMemInfo->sDevVAddr = + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(psMemBlock->hBuffer); + + UpdateDeviceMemoryPlaneOffsets(psMemInfo); + + return result; +} + + +/*! +****************************************************************************** + + @Function PVRSRVUnmapFromDevKM + + @Description + + Unmaps buffer from GPU virtual address space + + @Input psMemInfo + + @Return PVRSRV_ERROR : 0 means the memory is unmapped, + * bigger than 0 (mapping reference count) still mapped + * smaller than 0 - PVRSRV error +******************************************************************************/ +IMG_EXPORT +IMG_INT32 IMG_CALLCONV PVRSRVUnmapFromDevKM(IMG_HANDLE hDevCookie, + PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + PVRSRV_MEMBLK *psMemBlock; + IMG_INT32 result; + + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + if (!psMemInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVUnmapFromDevKM: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psMemBlock = &(psMemInfo->sMemBlk); + + result = BM_UnmapFromDev(psMemBlock->hBuffer); + /* 0 means the memory is unmapped, + * bigger than 0 (mapping ref count) still mapped + * smaller than 0 PVRSRV error + */ + if(result < 0) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVUnmapFromDevKM: could not unmap")); + } + + psMemInfo->sDevVAddr = + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(psMemBlock->hBuffer); + + return result; +} + + +/*! +****************************************************************************** + + @Function PVRSRVAllocDeviceMemKM + + @Description + + Allocates device memory + + @Input hDevCookie : + @Input psPerProc : Per-process data + @Input hDevMemHeap + @Input ui32Flags : Some combination of PVRSRV_MEM_ flags + @Input ui32Size : Number of bytes to allocate + @Input ui32Alignment : + @Output **ppsMemInfo : On success, receives a pointer to the created MEM_INFO structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV _PVRSRVAllocDeviceMemKM(IMG_HANDLE hDevCookie, + PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE hDevMemHeap, + IMG_UINT32 ui32Flags, + IMG_SIZE_T ui32Size, + IMG_SIZE_T ui32Alignment, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINT32 ui32ChunkSize, + IMG_UINT32 ui32NumVirtChunks, + IMG_UINT32 ui32NumPhysChunks, + IMG_BOOL *pabMapChunk, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfo) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo; + PVRSRV_ERROR eError; + BM_HEAP *psBMHeap; + IMG_HANDLE hDevMemContext; + + if (!hDevMemHeap || + ((ui32Size == 0) && ((ui32Flags & PVRSRV_MEM_SPARSE) == 0)) || + (((ui32ChunkSize == 0) || (ui32NumVirtChunks == 0) || (ui32NumPhysChunks == 0) || + (pabMapChunk == IMG_NULL )) && (ui32Flags & PVRSRV_MEM_SPARSE))) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* Sprase alloc input validation */ + if (ui32Flags & PVRSRV_MEM_SPARSE) + { + IMG_UINT32 i; + IMG_UINT32 ui32Check = 0; + + if (ui32NumVirtChunks < ui32NumPhysChunks) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + for (i=0;i<ui32NumVirtChunks;i++) + { + if (pabMapChunk[i]) + { + ui32Check++; + } + } + if (ui32NumPhysChunks != ui32Check) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + } + + /* FIXME: At the moment we force CACHETYPE override allocations to + * be multiples of PAGE_SIZE and page aligned. If the RA/BM + * is fixed, this limitation can be removed. + * + * INTEGRATION_POINT: HOST_PAGESIZE() is not correct, should be device-specific. + */ + if (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK) + { + /* PRQA S 3415 1 */ /* order of evaluation is not important */ + if (((ui32Size % HOST_PAGESIZE()) != 0) || + ((ui32Alignment % HOST_PAGESIZE()) != 0)) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + } + + eError = AllocDeviceMem(hDevCookie, + hDevMemHeap, + ui32Flags, + ui32Size, + ui32Alignment, + pvPrivData, + ui32PrivDataLength, + ui32ChunkSize, + ui32NumVirtChunks, + ui32NumPhysChunks, + pabMapChunk, + &psMemInfo); + + if (eError != PVRSRV_OK) + { + return eError; + } + +#if defined(CONFIG_GCBV) + if (ui32Flags & PVRSRV_MAP_GC_MMU) + gc_bvmap_meminfo(psMemInfo); +#endif + + if (ui32Flags & PVRSRV_MEM_NO_SYNCOBJ) + { + psMemInfo->psKernelSyncInfo = IMG_NULL; + } + else + { + /* + allocate a syncinfo but don't register with resman + because the holding devicemem will handle the syncinfo + */ + psBMHeap = (BM_HEAP*)hDevMemHeap; + hDevMemContext = (IMG_HANDLE)psBMHeap->pBMContext; + eError = PVRSRVAllocSyncInfoKM(hDevCookie, + hDevMemContext, + &psMemInfo->psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + goto free_mainalloc; + } + } + + /* + * Setup the output. + */ + *ppsMemInfo = psMemInfo; + + if (ui32Flags & PVRSRV_MEM_NO_RESMAN) + { + psMemInfo->sMemBlk.hResItem = IMG_NULL; + } + else + { + /* register with the resman */ + psMemInfo->sMemBlk.hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DEVICEMEM_ALLOCATION, + psMemInfo, + 0, + &FreeDeviceMemCallBack); + if (psMemInfo->sMemBlk.hResItem == IMG_NULL) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto free_mainalloc; + } + } + + /* increment the refcount */ + PVRSRVKernelMemInfoIncRef(psMemInfo); + + psMemInfo->memType = PVRSRV_MEMTYPE_DEVICE; + + /* + * And I think we're done for now.... + */ + return (PVRSRV_OK); + +free_mainalloc: + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + FreeDeviceMem(psMemInfo); + + return eError; +} + +#if defined(SUPPORT_ION) +static PVRSRV_ERROR IonUnmapCallback(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo = (PVRSRV_KERNEL_MEM_INFO *)pvParam; + + PVR_UNREFERENCED_PARAMETER(bDummy); + + return FreeMemCallBackCommon(psMemInfo, ui32Param, PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR); +} + +/*! +****************************************************************************** + + @Function PVRSRVMapIonHandleKM + + @Description + + Map an ION buffer into the specified device memory context + + @Input psPerProc : PerProcess data + @Input hDevCookie : Device node cookie + @Input hDevMemContext : Device memory context cookie + @Input hIon : Handle to ION buffer + @Input ui32Flags : Mapping flags + @Input ui32Size : Mapping size + @Output ppsKernelMemInfo: Output kernel meminfo if successful + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVMapIonHandleKM(PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE hDevCookie, + IMG_HANDLE hDevMemContext, + IMG_HANDLE hIon, + IMG_UINT32 ui32Flags, + IMG_UINT32 ui32Size, + PVRSRV_KERNEL_MEM_INFO **ppsKernelMemInfo) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProcEnv = PVRSRVProcessPrivateData(psPerProc); + PVRSRV_DEVICE_NODE *psDeviceNode; + PVRSRV_KERNEL_MEM_INFO *psNewKernelMemInfo; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_SYS_PHYADDR *pasSysPhysAddr; + PVRSRV_MEMBLK *psMemBlock; + PVRSRV_ERROR eError; + IMG_HANDLE hDevMemHeap = IMG_NULL; + IMG_HANDLE hPriv; + BM_HANDLE hBuffer; + IMG_UINT32 ui32HeapCount; + IMG_UINT32 ui32PageCount; + IMG_UINT32 i; + IMG_BOOL bAllocSync = (ui32Flags & PVRSRV_MEM_NO_SYNCOBJ)?IMG_FALSE:IMG_TRUE; + + if ((hDevCookie == IMG_NULL) || (ui32Size == 0) + || (hDevMemContext == IMG_NULL) || (ppsKernelMemInfo == IMG_NULL)) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Invalid params", __FUNCTION__)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDeviceNode = (PVRSRV_DEVICE_NODE *)hDevCookie; + + if(OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psNewKernelMemInfo, IMG_NULL, + "Kernel Memory Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"%s: Failed to alloc memory for block", __FUNCTION__)); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet(psNewKernelMemInfo, 0, sizeof(PVRSRV_KERNEL_MEM_INFO)); + + /* Choose the heap to map to */ + ui32HeapCount = psDeviceNode->sDevMemoryInfo.ui32HeapCount; + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + psDeviceMemoryHeap = psDeviceNode->sDevMemoryInfo.psDeviceMemoryHeap; + for(i=0; i<PVRSRV_MAX_CLIENT_HEAPS; i++) + { + if(HEAP_IDX(psDeviceMemoryHeap[i].ui32HeapID) == psDevMemoryInfo->ui32IonHeapID) + { + if(psDeviceMemoryHeap[i].DevMemHeapType == DEVICE_MEMORY_HEAP_PERCONTEXT) + { + if (psDeviceMemoryHeap[i].ui32HeapSize > 0) + { + hDevMemHeap = BM_CreateHeap(hDevMemContext, &psDeviceMemoryHeap[i]); + } + else + { + hDevMemHeap = IMG_NULL; + } + } + else + { + hDevMemHeap = psDevMemoryInfo->psDeviceMemoryHeap[i].hDevMemHeap; + } + break; + } + } + + if (hDevMemHeap == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to get ION heap", __FUNCTION__)); + eError = PVRSRV_ERROR_FAILED_TO_RETRIEVE_HEAPINFO; + goto exitFailedHeap; + } + + /* Import the ION buffer into our ion_client and DMA map it */ + eError = IonImportBufferAndAquirePhysAddr(psPerProcEnv->psIONClient, + hIon, + &ui32PageCount, + &pasSysPhysAddr, + &psNewKernelMemInfo->pvLinAddrKM, + &hPriv); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to get ion buffer/buffer phys addr", __FUNCTION__)); + goto exitFailedHeap; + } + + /* Wrap the returned addresses into our memory context */ + if (!BM_Wrap(hDevMemHeap, + ui32Size, + 0, + IMG_FALSE, + pasSysPhysAddr, + IMG_NULL, + &ui32Flags, /* This function clobbers our bits in ui32Flags */ + &hBuffer)) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to wrap ion buffer", __FUNCTION__)); + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto exitFailedWrap; + } + + /* Fill in "Implementation dependant" section of mem info */ + psMemBlock = &psNewKernelMemInfo->sMemBlk; + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer); + psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer); + psMemBlock->hBuffer = (IMG_HANDLE) hBuffer; + psMemBlock->hOSWrapMem = hPriv; /* Saves creating a new element as we know hOSWrapMem will not be used */ + psMemBlock->psIntSysPAddr = pasSysPhysAddr; + + psNewKernelMemInfo->ui32Flags = ui32Flags; + psNewKernelMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr; + psNewKernelMemInfo->uAllocSize = ui32Size; + psNewKernelMemInfo->memType = PVRSRV_MEMTYPE_ION; + PVRSRVKernelMemInfoIncRef(psNewKernelMemInfo); + + /* Clear the Backup buffer pointer as we do not have one at this point. We only allocate this as we are going up/down */ + psNewKernelMemInfo->pvSysBackupBuffer = IMG_NULL; + + if (!bAllocSync) + { + psNewKernelMemInfo->psKernelSyncInfo = IMG_NULL; + } + else + { + eError = PVRSRVAllocSyncInfoKM(hDevCookie, + hDevMemContext, + &psNewKernelMemInfo->psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + goto exitFailedSync; + } + } + + /* register with the resman */ + psNewKernelMemInfo->sMemBlk.hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DEVICEMEM_ION, + psNewKernelMemInfo, + 0, + &IonUnmapCallback); + if (psNewKernelMemInfo->sMemBlk.hResItem == IMG_NULL) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto exitFailedResman; + } + + psNewKernelMemInfo->memType = PVRSRV_MEMTYPE_ION; + + *ppsKernelMemInfo = psNewKernelMemInfo; + return PVRSRV_OK; + +exitFailedResman: + if (psNewKernelMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psNewKernelMemInfo->psKernelSyncInfo, psNewKernelMemInfo); + } +exitFailedSync: + BM_Free(hBuffer, ui32Flags); +exitFailedWrap: + IonUnimportBufferAndReleasePhysAddr(hPriv); + OSFreeMem(PVRSRV_PAGEABLE_SELECT, + sizeof(IMG_SYS_PHYADDR) * ui32PageCount, + pasSysPhysAddr, + IMG_NULL); +exitFailedHeap: + OSFreeMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_MEM_INFO), + psNewKernelMemInfo, + IMG_NULL); + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVUnmapIonHandleKM + + @Description + + Frees an ion buffer mapped with PVRSRVMapIonHandleKM, including the mem_info structure + + @Input psMemInfo : + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVUnmapIonHandleKM(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); +} +#endif /* SUPPORT_ION */ + +/*! +****************************************************************************** + + @Function PVRSRVDissociateDeviceMemKM + + @Description + + Dissociates memory from the process that allocates it. Intended for + transfering the ownership of device memory from a particular process + to the kernel. + + @Input psMemInfo : + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVDissociateDeviceMemKM(IMG_HANDLE hDevCookie, + PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + PVRSRV_ERROR eError; + PVRSRV_DEVICE_NODE *psDeviceNode = hDevCookie; + + PVR_UNREFERENCED_PARAMETER(hDevCookie); + + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + eError = ResManDissociateRes(psMemInfo->sMemBlk.hResItem, psDeviceNode->hResManContext); + + PVR_ASSERT(eError == PVRSRV_OK); + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVGetFreeDeviceMemKM + + @Description + + Determines how much memory remains available in the system with the specified + capabilities. + + @Input ui32Flags : + + @Output pui32Total : + + @Output pui32Free : + + @Output pui32LargestBlock : + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetFreeDeviceMemKM(IMG_UINT32 ui32Flags, + IMG_SIZE_T *pui32Total, + IMG_SIZE_T *pui32Free, + IMG_SIZE_T *pui32LargestBlock) +{ + /* TO BE IMPLEMENTED */ + + PVR_UNREFERENCED_PARAMETER(ui32Flags); + PVR_UNREFERENCED_PARAMETER(pui32Total); + PVR_UNREFERENCED_PARAMETER(pui32Free); + PVR_UNREFERENCED_PARAMETER(pui32LargestBlock); + + return PVRSRV_OK; +} + + + + +/*! +****************************************************************************** + @Function PVRSRVUnwrapExtMemoryKM + + @Description On last unwrap of a given meminfo, unmaps physical pages from a + wrapped allocation, and frees the associated device address space. + Note: this can only unmap memory mapped by PVRSRVWrapExtMemory + + @Input psMemInfo - mem info describing the wrapped allocation + @Return None +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVUnwrapExtMemoryKM (PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); +} + + +/*! +****************************************************************************** + @Function UnwrapExtMemoryCallBack + + @Description Resman callback to unwrap memory + + @Input pvParam - opaque void ptr param + @Input ui32Param - opaque unsigned long param + @Return PVRSRV_ERROR +******************************************************************************/ +static PVRSRV_ERROR UnwrapExtMemoryCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo = (PVRSRV_KERNEL_MEM_INFO *)pvParam; + + PVR_UNREFERENCED_PARAMETER(bDummy); + + return FreeMemCallBackCommon(psMemInfo, ui32Param, + PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR); +} + + +/*! +****************************************************************************** + @Function PVRSRVWrapExtMemoryKM + + @Description Allocates a Device Virtual Address in the shared mapping heap + and maps physical pages into that allocation. Note, if the pages are + already mapped into the heap, the existing allocation is returned. + + @Input hDevCookie - Device cookie + @Input psPerProc - Per-process data + @Input hDevMemContext - device memory context + @Input uByteSize - Size of allocation + @Input uPageOffset - Offset into the first page of the memory to be wrapped + @Input bPhysContig - whether the underlying memory is physically contiguous + @Input psExtSysPAddr - The list of Device Physical page addresses + @Input pvLinAddr - ptr to buffer to wrap + @Output ppsMemInfo - mem info describing the wrapped allocation + @Return None +******************************************************************************/ + +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVWrapExtMemoryKM(IMG_HANDLE hDevCookie, + PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE hDevMemContext, + IMG_SIZE_T uByteSize, + IMG_SIZE_T uPageOffset, + IMG_BOOL bPhysContig, + IMG_SYS_PHYADDR *psExtSysPAddr, + IMG_VOID *pvLinAddr, + IMG_UINT32 ui32Flags, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfo) +{ + PVRSRV_KERNEL_MEM_INFO *psMemInfo = IMG_NULL; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + IMG_SIZE_T ui32HostPageSize = HOST_PAGESIZE(); + IMG_HANDLE hDevMemHeap = IMG_NULL; + PVRSRV_DEVICE_NODE* psDeviceNode; + BM_HANDLE hBuffer; + PVRSRV_MEMBLK *psMemBlock; + IMG_BOOL bBMError; + BM_HEAP *psBMHeap; + PVRSRV_ERROR eError; + IMG_VOID *pvPageAlignedCPUVAddr; + IMG_SYS_PHYADDR *psIntSysPAddr = IMG_NULL; + IMG_HANDLE hOSWrapMem = IMG_NULL; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_UINT32 i; + IMG_SIZE_T uPageCount = 0; + + + psDeviceNode = (PVRSRV_DEVICE_NODE*)hDevCookie; + PVR_ASSERT(psDeviceNode != IMG_NULL); + + if (psDeviceNode == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVWrapExtMemoryKM: invalid parameter")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if(pvLinAddr) + { + /* derive the page offset from the cpu ptr (in case it's not supplied) */ + uPageOffset = (IMG_UINTPTR_T)pvLinAddr & (ui32HostPageSize - 1); + + /* get the pagecount and the page aligned base ptr */ + uPageCount = HOST_PAGEALIGN(uByteSize + uPageOffset) / ui32HostPageSize; + pvPageAlignedCPUVAddr = (IMG_VOID *)((IMG_UINTPTR_T)pvLinAddr - uPageOffset); + + /* allocate array of SysPAddr to hold page addresses */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + uPageCount * sizeof(IMG_SYS_PHYADDR), + (IMG_VOID **)&psIntSysPAddr, IMG_NULL, + "Array of Page Addresses") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVWrapExtMemoryKM: Failed to alloc memory for block")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + eError = OSAcquirePhysPageAddr(pvPageAlignedCPUVAddr, + uPageCount * ui32HostPageSize, + psIntSysPAddr, + &hOSWrapMem); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVWrapExtMemoryKM: Failed to alloc memory for block")); + eError = PVRSRV_ERROR_OUT_OF_MEMORY;//FIXME: need better error code + goto ErrorExitPhase1; + } + + /* replace the supplied page address list */ + psExtSysPAddr = psIntSysPAddr; + + /* assume memory is not physically contiguous; + we shouldn't trust what the user says here + */ + bPhysContig = IMG_FALSE; + } + + /* Choose the heap to map to */ + psDevMemoryInfo = &((BM_CONTEXT*)hDevMemContext)->psDeviceNode->sDevMemoryInfo; + psDeviceMemoryHeap = psDevMemoryInfo->psDeviceMemoryHeap; + for(i=0; i<PVRSRV_MAX_CLIENT_HEAPS; i++) + { + if(HEAP_IDX(psDeviceMemoryHeap[i].ui32HeapID) == psDevMemoryInfo->ui32MappingHeapID) + { + if(psDeviceMemoryHeap[i].DevMemHeapType == DEVICE_MEMORY_HEAP_PERCONTEXT) + { + if (psDeviceMemoryHeap[i].ui32HeapSize > 0) + { + hDevMemHeap = BM_CreateHeap(hDevMemContext, &psDeviceMemoryHeap[i]); + } + else + { + hDevMemHeap = IMG_NULL; + } + } + else + { + hDevMemHeap = psDevMemoryInfo->psDeviceMemoryHeap[i].hDevMemHeap; + } + break; + } + } + + if(hDevMemHeap == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVWrapExtMemoryKM: unable to find mapping heap")); + eError = PVRSRV_ERROR_UNABLE_TO_FIND_MAPPING_HEAP; + goto ErrorExitPhase2; + } + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psMemInfo, IMG_NULL, + "Kernel Memory Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVWrapExtMemoryKM: Failed to alloc memory for block")); + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ErrorExitPhase2; + } + + OSMemSet(psMemInfo, 0, sizeof(*psMemInfo)); + psMemInfo->ui32Flags = ui32Flags; + + psMemBlock = &(psMemInfo->sMemBlk); + + bBMError = BM_Wrap(hDevMemHeap, + uByteSize, + uPageOffset, + bPhysContig, + psExtSysPAddr, + IMG_NULL, + &psMemInfo->ui32Flags, + &hBuffer); + if (!bBMError) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVWrapExtMemoryKM: BM_Wrap Failed")); + eError = PVRSRV_ERROR_BAD_MAPPING; + goto ErrorExitPhase3; + } + + /* Fill in "Implementation dependant" section of mem info */ + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer); + psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer); + psMemBlock->hOSWrapMem = hOSWrapMem; + psMemBlock->psIntSysPAddr = psIntSysPAddr; + + /* Convert from BM_HANDLE to external IMG_HANDLE */ + psMemBlock->hBuffer = (IMG_HANDLE)hBuffer; + + /* Fill in the public fields of the MEM_INFO structure */ + psMemInfo->pvLinAddrKM = BM_HandleToCpuVaddr(hBuffer); + psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr; + psMemInfo->uAllocSize = uByteSize; + + /* Clear the Backup buffer pointer as we do not have one at this point. + We only allocate this as we are going up/down + */ + psMemInfo->pvSysBackupBuffer = IMG_NULL; + + /* + allocate a syncinfo but don't register with resman + because the holding devicemem will handle the syncinfo + */ + psBMHeap = (BM_HEAP*)hDevMemHeap; + hDevMemContext = (IMG_HANDLE)psBMHeap->pBMContext; + eError = PVRSRVAllocSyncInfoKM(hDevCookie, + hDevMemContext, + &psMemInfo->psKernelSyncInfo); + if(eError != PVRSRV_OK) + { + goto ErrorExitPhase4; + } + + /* increment the refcount */ + PVRSRVKernelMemInfoIncRef(psMemInfo); + + psMemInfo->memType = PVRSRV_MEMTYPE_WRAPPED; + + /* Register Resource */ + psMemInfo->sMemBlk.hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DEVICEMEM_WRAP, + psMemInfo, + 0, + &UnwrapExtMemoryCallBack); + + /* return the meminfo */ + *ppsMemInfo = psMemInfo; + + return PVRSRV_OK; + + /* error handling: */ + +ErrorExitPhase4: + if(psMemInfo) + { + FreeDeviceMem(psMemInfo); + /* + FreeDeviceMem will free the meminfo so set + it to NULL to avoid double free below + */ + psMemInfo = IMG_NULL; + } + +ErrorExitPhase3: + if(psMemInfo) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + /*not nulling pointer, out of scope*/ + } + +ErrorExitPhase2: + if(psIntSysPAddr) + { + OSReleasePhysPageAddr(hOSWrapMem); + } + +ErrorExitPhase1: + if(psIntSysPAddr) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, uPageCount * sizeof(IMG_SYS_PHYADDR), psIntSysPAddr, IMG_NULL); + /*not nulling shared pointer, uninitialized to this point*/ + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVUnmapDeviceMemoryKM + + @Description + Unmaps an existing allocation previously mapped by PVRSRVMapDeviceMemory + + @Input psMemInfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVUnmapDeviceMemoryKM (PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); +} + + +/*! +****************************************************************************** + @Function UnmapDeviceMemoryCallBack + + @Description Resman callback to unmap memory memory previously mapped + from one allocation to another + + @Input pvParam - opaque void ptr param + @Input ui32Param - opaque unsigned long param + @Return PVRSRV_ERROR +******************************************************************************/ +static PVRSRV_ERROR UnmapDeviceMemoryCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_ERROR eError; + RESMAN_MAP_DEVICE_MEM_DATA *psMapData = pvParam; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + if(psMapData->psMemInfo->sMemBlk.psIntSysPAddr) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(IMG_SYS_PHYADDR), psMapData->psMemInfo->sMemBlk.psIntSysPAddr, IMG_NULL); + psMapData->psMemInfo->sMemBlk.psIntSysPAddr = IMG_NULL; + } + + if( psMapData->psMemInfo->psKernelSyncInfo ) + { + PVRSRVKernelSyncInfoDecRef(psMapData->psMemInfo->psKernelSyncInfo, psMapData->psMemInfo); + } + + eError = FreeDeviceMem(psMapData->psMemInfo); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"UnmapDeviceMemoryCallBack: Failed to free DST meminfo")); + return eError; + } + + /* This will only free the src psMemInfo if we hold the last reference */ + eError = FreeMemCallBackCommon(psMapData->psSrcMemInfo, 0, + PVRSRV_FREE_CALLBACK_ORIGIN_IMPORTER); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RESMAN_MAP_DEVICE_MEM_DATA), psMapData, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVMapDeviceMemoryKM + + @Description + Maps an existing allocation to a specific device address space and heap + Note: it's valid to map from one physical device to another + + @Input psPerProc : Per-process data + @Input psSrcMemInfo + @Input hDstDevMemHeap + @Input ppsDstMemInfo + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVMapDeviceMemoryKM(PVRSRV_PER_PROCESS_DATA *psPerProc, + PVRSRV_KERNEL_MEM_INFO *psSrcMemInfo, + IMG_HANDLE hDstDevMemHeap, + PVRSRV_KERNEL_MEM_INFO **ppsDstMemInfo) +{ + PVRSRV_ERROR eError; + IMG_UINT32 i; + IMG_SIZE_T uPageCount, uPageOffset; + IMG_SIZE_T ui32HostPageSize = HOST_PAGESIZE(); + IMG_SYS_PHYADDR *psSysPAddr = IMG_NULL; + IMG_DEV_PHYADDR sDevPAddr; + BM_BUF *psBuf; + IMG_DEV_VIRTADDR sDevVAddr; + PVRSRV_KERNEL_MEM_INFO *psMemInfo = IMG_NULL; + BM_HANDLE hBuffer; + PVRSRV_MEMBLK *psMemBlock; + IMG_BOOL bBMError; + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_VOID *pvPageAlignedCPUVAddr; + RESMAN_MAP_DEVICE_MEM_DATA *psMapData = IMG_NULL; + + /* check params */ + if(!psSrcMemInfo || !hDstDevMemHeap || !ppsDstMemInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceMemoryKM: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* initialise the Dst Meminfo to NULL*/ + *ppsDstMemInfo = IMG_NULL; + + uPageOffset = psSrcMemInfo->sDevVAddr.uiAddr & (ui32HostPageSize - 1); + uPageCount = HOST_PAGEALIGN(psSrcMemInfo->uAllocSize + uPageOffset) / ui32HostPageSize; + pvPageAlignedCPUVAddr = (IMG_VOID *)((IMG_UINTPTR_T)psSrcMemInfo->pvLinAddrKM - uPageOffset); + + /* + allocate array of SysPAddr to hold SRC allocation page addresses + */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + uPageCount*sizeof(IMG_SYS_PHYADDR), + (IMG_VOID **)&psSysPAddr, IMG_NULL, + "Array of Page Addresses") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceMemoryKM: Failed to alloc memory for block")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + psBuf = psSrcMemInfo->sMemBlk.hBuffer; + + /* get the device node */ + psDeviceNode = psBuf->pMapping->pBMHeap->pBMContext->psDeviceNode; + + /* build a list of physical page addresses */ + sDevVAddr.uiAddr = psSrcMemInfo->sDevVAddr.uiAddr - IMG_CAST_TO_DEVVADDR_UINT(uPageOffset); + for(i=0; i<uPageCount; i++) + { + BM_GetPhysPageAddr(psSrcMemInfo, sDevVAddr, &sDevPAddr); + + /* save the address */ + psSysPAddr[i] = SysDevPAddrToSysPAddr (psDeviceNode->sDevId.eDeviceType, sDevPAddr); + + /* advance the DevVaddr one page */ + sDevVAddr.uiAddr += IMG_CAST_TO_DEVVADDR_UINT(ui32HostPageSize); + } + + /* allocate the resman map data */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(RESMAN_MAP_DEVICE_MEM_DATA), + (IMG_VOID **)&psMapData, IMG_NULL, + "Resource Manager Map Data") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceMemoryKM: Failed to alloc resman map data")); + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ErrorExit; + } + + if(OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psMemInfo, IMG_NULL, + "Kernel Memory Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceMemoryKM: Failed to alloc memory for block")); + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ErrorExit; + } + + OSMemSet(psMemInfo, 0, sizeof(*psMemInfo)); + psMemInfo->ui32Flags = psSrcMemInfo->ui32Flags; + + psMemBlock = &(psMemInfo->sMemBlk); + + bBMError = BM_Wrap(hDstDevMemHeap, + psSrcMemInfo->uAllocSize, + uPageOffset, + IMG_FALSE, + psSysPAddr, + pvPageAlignedCPUVAddr, + &psMemInfo->ui32Flags, + &hBuffer); + + if (!bBMError) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceMemoryKM: BM_Wrap Failed")); + eError = PVRSRV_ERROR_BAD_MAPPING; + goto ErrorExit; + } + + /* Fill in "Implementation dependant" section of mem info */ + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer); + psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer); + + /* Convert from BM_HANDLE to external IMG_HANDLE */ + psMemBlock->hBuffer = (IMG_HANDLE)hBuffer; + + /* Store page list */ + psMemBlock->psIntSysPAddr = psSysPAddr; + + /* patch up the CPU VAddr into the meminfo */ + psMemInfo->pvLinAddrKM = psSrcMemInfo->pvLinAddrKM; + + /* Fill in the public fields of the MEM_INFO structure */ + psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr; + psMemInfo->uAllocSize = psSrcMemInfo->uAllocSize; + psMemInfo->psKernelSyncInfo = psSrcMemInfo->psKernelSyncInfo; + + /* reference the same ksi that the original meminfo referenced */ + if(psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoIncRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + + /* Clear the Backup buffer pointer as we do not have one at this point. + We only allocate this as we are going up/down + */ + psMemInfo->pvSysBackupBuffer = IMG_NULL; + + /* increment our refcount */ + PVRSRVKernelMemInfoIncRef(psMemInfo); + + /* increment the src refcount */ + PVRSRVKernelMemInfoIncRef(psSrcMemInfo); + + /* Tell the buffer manager about the export */ + BM_Export(psSrcMemInfo->sMemBlk.hBuffer); + + psMemInfo->memType = PVRSRV_MEMTYPE_MAPPED; + + /* setup the resman map data */ + psMapData->psMemInfo = psMemInfo; + psMapData->psSrcMemInfo = psSrcMemInfo; + + /* Register Resource */ + psMemInfo->sMemBlk.hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DEVICEMEM_MAPPING, + psMapData, + 0, + &UnmapDeviceMemoryCallBack); + + *ppsDstMemInfo = psMemInfo; + + return PVRSRV_OK; + + /* error handling: */ + +ErrorExit: + + if(psSysPAddr) + { + /* Free the page address list */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(IMG_SYS_PHYADDR), psSysPAddr, IMG_NULL); + /*not nulling shared pointer, holding structure could be not initialized*/ + } + + if(psMemInfo) + { + /* Free the page address list */ + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + /*not nulling shared pointer, holding structure could be not initialized*/ + } + + if(psMapData) + { + /* Free the resman map data */ + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(RESMAN_MAP_DEVICE_MEM_DATA), psMapData, IMG_NULL); + /*not nulling pointer, out of scope*/ + } + + return eError; +} + + +/*! +****************************************************************************** + @Function PVRSRVUnmapDeviceClassMemoryKM + + @Description unmaps physical pages from devices address space at a specified + Device Virtual Address. + Note: this can only unmap memory mapped by + PVRSRVMapDeviceClassMemoryKM + + @Input psMemInfo - mem info describing the device virtual address + to unmap RAM from + @Return None +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVUnmapDeviceClassMemoryKM(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + if (!psMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return ResManFreeResByPtr(psMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); +} + + +/*! +****************************************************************************** + @Function UnmapDeviceClassMemoryCallBack + + @Description Resman callback to unmap device class memory + + @Input pvParam - opaque void ptr param + @Input ui32Param - opaque unsigned long param + @Return PVRSRV_ERROR +******************************************************************************/ +static PVRSRV_ERROR UnmapDeviceClassMemoryCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_DC_MAPINFO *psDCMapInfo = pvParam; + PVRSRV_KERNEL_MEM_INFO *psMemInfo; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + psMemInfo = psDCMapInfo->psMemInfo; + +#if defined(SUPPORT_MEMORY_TILING) + if(psDCMapInfo->ui32TilingStride > 0) + { + PVRSRV_DEVICE_NODE *psDeviceNode = psDCMapInfo->psDeviceNode; + + if (psDeviceNode->pfnFreeMemTilingRange(psDeviceNode, + psDCMapInfo->ui32RangeIndex) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"UnmapDeviceClassMemoryCallBack: FreeMemTilingRange failed")); + } + } +#endif + + (psDCMapInfo->psDeviceClassBuffer->ui32MemMapRefCount)--; + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_DC_MAPINFO), psDCMapInfo, IMG_NULL); + + return FreeMemCallBackCommon(psMemInfo, ui32Param, + PVRSRV_FREE_CALLBACK_ORIGIN_ALLOCATOR); +} + + +/*! +****************************************************************************** + @Function PVRSRVMapDeviceClassMemoryKM + + @Description maps physical pages for DeviceClass buffers into a devices + address space at a specified and pre-allocated Device + Virtual Address + + @Input psPerProc - Per-process data + @Input hDevMemContext - Device memory context + @Input hDeviceClassBuffer - Device Class Buffer (Surface) handle + @Input hDevMemContext - device memory context to which mapping + is made + @Output ppsMemInfo - mem info describing the mapped memory + @Output phOSMapInfo - OS specific mapping information + @Return None +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVMapDeviceClassMemoryKM(PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_HANDLE hDevMemContext, + IMG_HANDLE hDeviceClassBuffer, + PVRSRV_KERNEL_MEM_INFO **ppsMemInfo, + IMG_HANDLE *phOSMapInfo) +{ + PVRSRV_ERROR eError; + PVRSRV_DEVICE_NODE* psDeviceNode; + PVRSRV_KERNEL_MEM_INFO *psMemInfo = IMG_NULL; + PVRSRV_DEVICECLASS_BUFFER *psDeviceClassBuffer; + IMG_SYS_PHYADDR *psSysPAddr; + IMG_VOID *pvCPUVAddr, *pvPageAlignedCPUVAddr; + IMG_BOOL bPhysContig; + BM_CONTEXT *psBMContext; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + DEVICE_MEMORY_HEAP_INFO *psDeviceMemoryHeap; + IMG_HANDLE hDevMemHeap = IMG_NULL; + IMG_SIZE_T uByteSize; + IMG_SIZE_T ui32Offset; + IMG_SIZE_T ui32PageSize = HOST_PAGESIZE(); + BM_HANDLE hBuffer; + PVRSRV_MEMBLK *psMemBlock; + IMG_BOOL bBMError; + IMG_UINT32 i; + PVRSRV_DC_MAPINFO *psDCMapInfo = IMG_NULL; + + if(!hDeviceClassBuffer || !ppsMemInfo || !phOSMapInfo || !hDevMemContext) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* allocate resman storage structure */ + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_DC_MAPINFO), + (IMG_VOID **)&psDCMapInfo, IMG_NULL, + "PVRSRV_DC_MAPINFO") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: Failed to alloc memory for psDCMapInfo")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + OSMemSet(psDCMapInfo, 0, sizeof(PVRSRV_DC_MAPINFO)); + + psDeviceClassBuffer = (PVRSRV_DEVICECLASS_BUFFER*)hDeviceClassBuffer; + + /* + call into external driver to get info so we can map a meminfo + Notes: + It's expected that third party displays will only support + physically contiguous display surfaces. However, it's possible + a given display may have an MMU and therefore support non-contig' + display surfaces. + + If surfaces are contiguous, ext driver should return: + - a CPU virtual address, or IMG_NULL where the surface is not mapped to CPU + - (optional) an OS Mapping handle for KM->UM surface mapping + - the size in bytes + - a single system physical address + + If surfaces are non-contiguous, ext driver should return: + - a CPU virtual address + - (optional) an OS Mapping handle for KM->UM surface mapping + - the size in bytes (must be multiple of 4kB) + - a list of system physical addresses (at 4kB intervals) + */ + eError = psDeviceClassBuffer->pfnGetBufferAddr(psDeviceClassBuffer->hExtDevice, + psDeviceClassBuffer->hExtBuffer, + &psSysPAddr, + &uByteSize, + &pvCPUVAddr, + phOSMapInfo, + &bPhysContig, + &psDCMapInfo->ui32TilingStride); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: unable to get buffer address")); + goto ErrorExitPhase1; + } + + /* Choose the heap to map to */ + psBMContext = (BM_CONTEXT*)psDeviceClassBuffer->hDevMemContext; + psDeviceNode = psBMContext->psDeviceNode; + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + psDeviceMemoryHeap = psDevMemoryInfo->psDeviceMemoryHeap; + for(i=0; i<PVRSRV_MAX_CLIENT_HEAPS; i++) + { + if(HEAP_IDX(psDeviceMemoryHeap[i].ui32HeapID) == psDevMemoryInfo->ui32MappingHeapID) + { + if(psDeviceMemoryHeap[i].DevMemHeapType == DEVICE_MEMORY_HEAP_PERCONTEXT) + { + if (psDeviceMemoryHeap[i].ui32HeapSize > 0) + { + hDevMemHeap = BM_CreateHeap(hDevMemContext, &psDeviceMemoryHeap[i]); + } + else + { + hDevMemHeap = IMG_NULL; + } + } + else + { + hDevMemHeap = psDevMemoryInfo->psDeviceMemoryHeap[i].hDevMemHeap; + } + break; + } + } + + if(hDevMemHeap == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: unable to find mapping heap")); + eError = PVRSRV_ERROR_UNABLE_TO_FIND_RESOURCE; + goto ErrorExitPhase1; + } + + /* Only need lower 12 bits of the cpu addr - don't care what size a void* is */ + ui32Offset = ((IMG_UINTPTR_T)pvCPUVAddr) & (ui32PageSize - 1); + pvPageAlignedCPUVAddr = (IMG_VOID *)((IMG_UINTPTR_T)pvCPUVAddr - ui32Offset); + + eError = OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psMemInfo, IMG_NULL, + "Kernel Memory Info"); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: Failed to alloc memory for block")); + goto ErrorExitPhase1; + } + + OSMemSet(psMemInfo, 0, sizeof(*psMemInfo)); + + psMemBlock = &(psMemInfo->sMemBlk); + + bBMError = BM_Wrap(hDevMemHeap, + uByteSize, + ui32Offset, + bPhysContig, + psSysPAddr, + pvPageAlignedCPUVAddr, + &psMemInfo->ui32Flags, + &hBuffer); + + if (!bBMError) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: BM_Wrap Failed")); + /*not nulling pointer, out of scope*/ + eError = PVRSRV_ERROR_BAD_MAPPING; + goto ErrorExitPhase2; + } + + /* Fill in "Implementation dependant" section of mem info */ + psMemBlock->sDevVirtAddr = BM_HandleToDevVaddr(hBuffer); + psMemBlock->hOSMemHandle = BM_HandleToOSMemHandle(hBuffer); + + /* Convert from BM_HANDLE to external IMG_HANDLE */ + psMemBlock->hBuffer = (IMG_HANDLE)hBuffer; + + /* patch up the CPU VAddr into the meminfo - use the address from the BM, not the one from the deviceclass + api, to ensure user mode mapping is possible + */ + psMemInfo->pvLinAddrKM = BM_HandleToCpuVaddr(hBuffer); + + /* Fill in the public fields of the MEM_INFO structure */ + psMemInfo->sDevVAddr = psMemBlock->sDevVirtAddr; + psMemInfo->uAllocSize = uByteSize; + psMemInfo->psKernelSyncInfo = psDeviceClassBuffer->psKernelSyncInfo; + + PVR_ASSERT(psMemInfo->psKernelSyncInfo != IMG_NULL); + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoIncRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + + /* Clear the Backup buffer pointer as we do not have one at this point. + We only allocate this as we are going up/down + */ + psMemInfo->pvSysBackupBuffer = IMG_NULL; + + /* setup DCMapInfo */ + psDCMapInfo->psMemInfo = psMemInfo; + psDCMapInfo->psDeviceClassBuffer = psDeviceClassBuffer; + +#if defined(SUPPORT_MEMORY_TILING) + psDCMapInfo->psDeviceNode = psDeviceNode; + + if(psDCMapInfo->ui32TilingStride > 0) + { + /* try to acquire a tiling range on this device */ + eError = psDeviceNode->pfnAllocMemTilingRange(psDeviceNode, + psMemInfo, + psDCMapInfo->ui32TilingStride, + &psDCMapInfo->ui32RangeIndex); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVMapDeviceClassMemoryKM: AllocMemTilingRange failed")); + goto ErrorExitPhase3; + } + } +#endif + + /* Register Resource */ + psMemInfo->sMemBlk.hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_DEVICECLASSMEM_MAPPING, + psDCMapInfo, + 0, + &UnmapDeviceClassMemoryCallBack); + + (psDeviceClassBuffer->ui32MemMapRefCount)++; + PVRSRVKernelMemInfoIncRef(psMemInfo); + + psMemInfo->memType = PVRSRV_MEMTYPE_DEVICECLASS; + + /* return the meminfo */ + *ppsMemInfo = psMemInfo; + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + /* If the 3PDD supplies a kernel virtual address, we can PDUMP it */ + if(psMemInfo->pvLinAddrKM) + { + /* FIXME: + * Initialise the display surface here when it is mapped into Services. + * Otherwise there is a risk that pdump toolchain will assign previously + * used physical pages, leading to visual artefacts on the unrendered surface + * (e.g. during LLS rendering). + * + * A better method is to pdump the allocation from the DC driver, so the + * BM_Wrap pdumps only the virtual memory which better represents the driver + * behaviour. + */ + PDUMPCOMMENT("Dump display surface"); + PDUMPMEM(IMG_NULL, psMemInfo, ui32Offset, psMemInfo->uAllocSize, PDUMP_FLAGS_CONTINUOUS, ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping); + } +#endif + return PVRSRV_OK; + +#if defined(SUPPORT_MEMORY_TILING) +ErrorExitPhase3: + if(psMemInfo) + { + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + + FreeDeviceMem(psMemInfo); + /* + FreeDeviceMem will free the meminfo so set + it to NULL to avoid double free below + */ + psMemInfo = IMG_NULL; + } +#endif + +ErrorExitPhase2: + if(psMemInfo) + { + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_MEM_INFO), psMemInfo, IMG_NULL); + } + +ErrorExitPhase1: + if(psDCMapInfo) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_KERNEL_MEM_INFO), psDCMapInfo, IMG_NULL); + } + + return eError; +} + + +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVChangeDeviceMemoryAttributesKM(IMG_HANDLE hKernelMemInfo, IMG_UINT32 ui32Attribs) +{ + PVRSRV_KERNEL_MEM_INFO *psKMMemInfo; + + if (hKernelMemInfo == IMG_NULL) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psKMMemInfo = (PVRSRV_KERNEL_MEM_INFO *)hKernelMemInfo; + + if (ui32Attribs & PVRSRV_CHANGEDEVMEM_ATTRIBS_CACHECOHERENT) + { + psKMMemInfo->ui32Flags |= PVRSRV_MEM_CACHE_CONSISTENT; + } + else + { + psKMMemInfo->ui32Flags &= ~PVRSRV_MEM_CACHE_CONSISTENT; + } + + return PVRSRV_OK; +} + + +/****************************************************************************** + End of file (devicemem.c) +******************************************************************************/ + diff --git a/pvr-source/services4/srvkm/common/handle.c b/pvr-source/services4/srvkm/common/handle.c new file mode 100644 index 0000000..1e26047 --- /dev/null +++ b/pvr-source/services4/srvkm/common/handle.c @@ -0,0 +1,2689 @@ +/*************************************************************************/ /*! +@Title Resource Handle Manager +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Provide resource handle management +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) +/* See handle.h for a description of the handle API. */ + +/* + * There is no locking here. It is assumed the code is used in a single + * threaded environment. In particular, it is assumed that the code will + * never be called from an interrupt handler. + * + * The implmentation supports movable handle structures, allowing the address + * of a handle structure to change without having to fix up pointers in + * any of the handle structures. For example, the linked list mechanism + * used to link subhandles together uses handle array indices rather than + * pointers to the structures themselves. + */ + +#include <stddef.h> + +#include "services_headers.h" +#include "handle.h" + +#ifdef DEBUG +#define HANDLE_BLOCK_SHIFT 2 +#else +#define HANDLE_BLOCK_SHIFT 8 +#endif + +#define DIVIDE_BY_BLOCK_SIZE(i) (((IMG_UINT32)(i)) >> HANDLE_BLOCK_SHIFT) +#define MULTIPLY_BY_BLOCK_SIZE(i) (((IMG_UINT32)(i)) << HANDLE_BLOCK_SHIFT) + +#define HANDLE_BLOCK_SIZE MULTIPLY_BY_BLOCK_SIZE(1) +#define HANDLE_SUB_BLOCK_MASK (HANDLE_BLOCK_SIZE - 1) +#define HANDLE_BLOCK_MASK (~(HANDLE_SUB_BLOCK_MASK)) + +#define HANDLE_HASH_TAB_INIT_SIZE 32 + +#define INDEX_IS_VALID(psBase, i) ((i) < (psBase)->ui32TotalHandCount) + +/* Valid handles are never NULL, but handle array indices are based from 0 */ +#if defined (SUPPORT_SID_INTERFACE) +#define INDEX_TO_HANDLE(i) ((IMG_SID)((i) + 1)) +#define HANDLE_TO_INDEX(h) ((IMG_UINT32)(h) - 1) +#else +#define INDEX_TO_HANDLE(i) ((IMG_HANDLE)((IMG_UINTPTR_T)(i) + 1)) +#define HANDLE_TO_INDEX(h) ((IMG_UINT32)(IMG_UINTPTR_T)(h) - 1) + +#endif + +#define INDEX_TO_BLOCK_INDEX(i) DIVIDE_BY_BLOCK_SIZE(i) +#define BLOCK_INDEX_TO_INDEX(i) MULTIPLY_BY_BLOCK_SIZE(i) +#define INDEX_TO_SUB_BLOCK_INDEX(i) ((i) & HANDLE_SUB_BLOCK_MASK) + +#define INDEX_TO_INDEX_STRUCT_PTR(psArray, i) (&((psArray)[INDEX_TO_BLOCK_INDEX(i)])) +#define BASE_AND_INDEX_TO_INDEX_STRUCT_PTR(psBase, i) INDEX_TO_INDEX_STRUCT_PTR((psBase)->psHandleArray, i) + +#define INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, i) (BASE_AND_INDEX_TO_INDEX_STRUCT_PTR(psBase, i)->ui32FreeHandBlockCount) + +#define INDEX_TO_HANDLE_STRUCT_PTR(psBase, i) (BASE_AND_INDEX_TO_INDEX_STRUCT_PTR(psBase, i)->psHandle + INDEX_TO_SUB_BLOCK_INDEX(i)) + +#define HANDLE_TO_HANDLE_STRUCT_PTR(psBase, h) (INDEX_TO_HANDLE_STRUCT_PTR(psBase, HANDLE_TO_INDEX(h))) + +#define HANDLE_PTR_TO_INDEX(psHandle) ((psHandle)->ui32Index) +#define HANDLE_PTR_TO_HANDLE(psHandle) INDEX_TO_HANDLE(HANDLE_PTR_TO_INDEX(psHandle)) + +#define ROUND_DOWN_TO_MULTIPLE_OF_BLOCK_SIZE(a) (HANDLE_BLOCK_MASK & (a)) +#define ROUND_UP_TO_MULTIPLE_OF_BLOCK_SIZE(a) ROUND_DOWN_TO_MULTIPLE_OF_BLOCK_SIZE((a) + HANDLE_BLOCK_SIZE - 1) + +#define DEFAULT_MAX_HANDLE 0x7fffffffu +#define DEFAULT_MAX_INDEX_PLUS_ONE ROUND_DOWN_TO_MULTIPLE_OF_BLOCK_SIZE(DEFAULT_MAX_HANDLE) + +#define HANDLES_BATCHED(psBase) ((psBase)->ui32HandBatchSize != 0) + +#define HANDLE_ARRAY_SIZE(handleCount) DIVIDE_BY_BLOCK_SIZE(ROUND_UP_TO_MULTIPLE_OF_BLOCK_SIZE(handleCount)) + +#define SET_FLAG(v, f) ((IMG_VOID)((v) |= (f))) +#define CLEAR_FLAG(v, f) ((IMG_VOID)((v) &= ~(f))) +#define TEST_FLAG(v, f) ((IMG_BOOL)(((v) & (f)) != 0)) + +#define TEST_ALLOC_FLAG(psHandle, f) TEST_FLAG((psHandle)->eFlag, f) + +#define SET_INTERNAL_FLAG(psHandle, f) SET_FLAG((psHandle)->eInternalFlag, f) +#define CLEAR_INTERNAL_FLAG(psHandle, f) CLEAR_FLAG((psHandle)->eInternalFlag, f) +#define TEST_INTERNAL_FLAG(psHandle, f) TEST_FLAG((psHandle)->eInternalFlag, f) + +#define BATCHED_HANDLE(psHandle) TEST_INTERNAL_FLAG(psHandle, INTERNAL_HANDLE_FLAG_BATCHED) + +#define SET_BATCHED_HANDLE(psHandle) SET_INTERNAL_FLAG(psHandle, INTERNAL_HANDLE_FLAG_BATCHED) + +#define SET_UNBATCHED_HANDLE(psHandle) CLEAR_INTERNAL_FLAG(psHandle, INTERNAL_HANDLE_FLAG_BATCHED) + +#define BATCHED_HANDLE_PARTIALLY_FREE(psHandle) TEST_INTERNAL_FLAG(psHandle, INTERNAL_HANDLE_FLAG_BATCHED_PARTIALLY_FREE) + +#define SET_BATCHED_HANDLE_PARTIALLY_FREE(psHandle) SET_INTERNAL_FLAG(psHandle, INTERNAL_HANDLE_FLAG_BATCHED_PARTIALLY_FREE) + +#define HANDLE_STRUCT_IS_FREE(psHandle) ((psHandle)->eType == PVRSRV_HANDLE_TYPE_NONE && (psHandle)->eInternalFlag == INTERNAL_HANDLE_FLAG_NONE) + +#ifdef MIN +#undef MIN +#endif + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +/* + * Linked list structure. Used for both the list head and list items. + * Array indices, rather than pointers, are used to point to the next and + * previous items on the list. + */ +struct sHandleList +{ + IMG_UINT32 ui32Prev; + IMG_UINT32 ui32Next; +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hParent; +#else + IMG_HANDLE hParent; +#endif +}; + +enum ePVRSRVInternalHandleFlag +{ + INTERNAL_HANDLE_FLAG_NONE = 0x00, + INTERNAL_HANDLE_FLAG_BATCHED = 0x01, + INTERNAL_HANDLE_FLAG_BATCHED_PARTIALLY_FREE = 0x02, +}; + +/* Handle structure */ +struct sHandle +{ + /* Handle type */ + PVRSRV_HANDLE_TYPE eType; + + /* Pointer to the data that the handle represents */ + IMG_VOID *pvData; + + /* + * When handles are on the free list, the value of the "next index + * plus one field" has the following meaning: + * zero - next handle is the one that follows this one, + * nonzero - the index of the next handle is the value minus one. + * This scheme means handle space can be initialised to all zeros. + * + * When this field is used to link together handles on a list + * other than the free list, zero indicates the end of the + * list, with nonzero the same as above. + */ + IMG_UINT32 ui32NextIndexPlusOne; + + /* Internal flags */ + enum ePVRSRVInternalHandleFlag eInternalFlag; + + /* Flags specified when the handle was allocated */ + PVRSRV_HANDLE_ALLOC_FLAG eFlag; + + /* Index of this handle in the handle array */ + IMG_UINT32 ui32Index; + + /* List head for subhandles of this handle */ + struct sHandleList sChildren; + + /* List entry for sibling subhandles */ + struct sHandleList sSiblings; +}; + +/* Handle array index structure. + * The handle array is an array of index structures, reallocated as the number of + * handles increases. + * NOTE: There is one index structure per block of handles. + */ +struct sHandleIndex +{ + /* Pointer to first handle structure in the block */ + struct sHandle *psHandle; + + /* Block allocation cookie returned from OSAllocMem for the block of handles */ + IMG_HANDLE hBlockAlloc; + + /* Number of free handles in block */ + IMG_UINT32 ui32FreeHandBlockCount; +}; + +struct _PVRSRV_HANDLE_BASE_ +{ + /* Handle returned from OSAllocMem for handle base allocation */ + IMG_HANDLE hBaseBlockAlloc; + + /* Handle returned from OSAllocMem for handle array allocation */ + IMG_HANDLE hArrayBlockAlloc; + + /* Pointer to array of pointers to handle structures */ + struct sHandleIndex *psHandleArray; + + /* + * Pointer to handle hash table. + * The hash table is used to do reverse lookups, converting data + * pointers to handles. + */ + HASH_TABLE *psHashTab; + + /* Number of free handles */ + IMG_UINT32 ui32FreeHandCount; + + /* + * If purging is not enabled, this is the array index of first free + * handle. + * If purging is enabled, this is the index to start searching for + * a free handle from. In this case it is usually zero, unless + * the handle array size has been increased due to lack of + * handles. + */ + IMG_UINT32 ui32FirstFreeIndex; + + /* Maximum handle index, plus one */ + IMG_UINT32 ui32MaxIndexPlusOne; + + /* Total number of handles, free and allocated */ + IMG_UINT32 ui32TotalHandCount; + + /* + * Index of the last free index, plus one. Not used if purging + * is enabled. + */ + IMG_UINT32 ui32LastFreeIndexPlusOne; + + /* Size of current handle batch, or zero if batching not enabled */ + IMG_UINT32 ui32HandBatchSize; + + /* Number of handles prior to start of current batch */ + IMG_UINT32 ui32TotalHandCountPreBatch; + + /* Index of first handle in batch, plus one */ + IMG_UINT32 ui32FirstBatchIndexPlusOne; + + /* Number of handle allocation failures in batch */ + IMG_UINT32 ui32BatchHandAllocFailures; + + /* Purging enabled. + * If purging is enabled, the size of the table can be reduced + * by removing free space at the end of the table. To make + * purging more likely to succeed, handles are allocated as + * far to the front of the table as possible. The first free + * handle is found by a linear search from the start of the table, + * and so no free handle list management is done. + */ + IMG_BOOL bPurgingEnabled; +}; + +/* + * The key for the handle hash table is an array of three elements, the + * pointer to the resource, the resource type, and the process ID. The + * eHandKey enumeration gives the array indices of the elements making + * up the key. + */ +enum eHandKey { + HAND_KEY_DATA = 0, + HAND_KEY_TYPE, + HAND_KEY_PARENT, + HAND_KEY_LEN /* Must be last item in list */ +}; + +/* + * Kernel handle base structure. For handles that are not allocated on + * behalf of a particular process + */ +PVRSRV_HANDLE_BASE *gpsKernelHandleBase = IMG_NULL; + +/* HAND_KEY is the type of the hash table key */ +typedef IMG_UINTPTR_T HAND_KEY[HAND_KEY_LEN]; + +/*! +****************************************************************************** + + @Function HandleListInit + + @Description Initialise a linked list structure embedded in a handle + structure. + + @Input ui32Index - index of handle in the handle array + psList - pointer to linked list structure + hParent - parent handle, or IMG_NULL + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(HandleListInit) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +IMG_VOID HandleListInit(IMG_UINT32 ui32Index, struct sHandleList *psList, IMG_SID hParent) +#else +IMG_VOID HandleListInit(IMG_UINT32 ui32Index, struct sHandleList *psList, IMG_HANDLE hParent) +#endif +{ + psList->ui32Next = ui32Index; + psList->ui32Prev = ui32Index; + psList->hParent = hParent; +} + +/*! +****************************************************************************** + + @Function InitParentList + + @Description Initialise the children list head in a handle structure. + The children are the subhandles of this handle. + + @Input psHandle - pointer to handle structure + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(InitParentList) +#endif +static INLINE +IMG_VOID InitParentList(struct sHandle *psHandle) +{ + IMG_UINT32 ui32Parent = HANDLE_PTR_TO_INDEX(psHandle); + + HandleListInit(ui32Parent, &psHandle->sChildren, INDEX_TO_HANDLE(ui32Parent)); +} + +/*! +****************************************************************************** + + @Function InitChildEntry + + @Description Initialise the child list entry in a handle structure. + The list entry is used to link together subhandles of + a given handle. + + @Input psHandle - pointer to handle structure + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(InitChildEntry) +#endif +static INLINE +IMG_VOID InitChildEntry(struct sHandle *psHandle) +{ + HandleListInit(HANDLE_PTR_TO_INDEX(psHandle), &psHandle->sSiblings, IMG_NULL); +} + +/*! +****************************************************************************** + + @Function HandleListIsEmpty + + @Description Determine whether a given linked list is empty. + + @Input ui32Index - index of the handle containing the list head + psList - pointer to the list head + + @Return IMG_TRUE if the list is empty, IMG_FALSE if it isn't. + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(HandleListIsEmpty) +#endif +static INLINE +IMG_BOOL HandleListIsEmpty(IMG_UINT32 ui32Index, struct sHandleList *psList) +{ + IMG_BOOL bIsEmpty; + + bIsEmpty = (IMG_BOOL)(psList->ui32Next == ui32Index); + +#ifdef DEBUG + { + IMG_BOOL bIsEmpty2; + + bIsEmpty2 = (IMG_BOOL)(psList->ui32Prev == ui32Index); + PVR_ASSERT(bIsEmpty == bIsEmpty2); + } +#endif + + return bIsEmpty; +} + +#ifdef DEBUG +/*! +****************************************************************************** + + @Function NoChildren + + @Description Determine whether a handle has any subhandles + + @Input psHandle - pointer to handle structure + + @Return IMG_TRUE if the handle has no subhandles, IMG_FALSE if it does. + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(NoChildren) +#endif +static INLINE +IMG_BOOL NoChildren(struct sHandle *psHandle) +{ + PVR_ASSERT(psHandle->sChildren.hParent == HANDLE_PTR_TO_HANDLE(psHandle)); + + return HandleListIsEmpty(HANDLE_PTR_TO_INDEX(psHandle), &psHandle->sChildren); +} + +/*! +****************************************************************************** + + @Function NoParent + + @Description Determine whether a handle is a subhandle + + @Input psHandle - pointer to handle structure + + @Return IMG_TRUE if the handle is not a subhandle, IMG_FALSE if it is. + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(NoParent) +#endif +static INLINE +IMG_BOOL NoParent(struct sHandle *psHandle) +{ + if (HandleListIsEmpty(HANDLE_PTR_TO_INDEX(psHandle), &psHandle->sSiblings)) + { + PVR_ASSERT(psHandle->sSiblings.hParent == IMG_NULL); + + return IMG_TRUE; + } + else + { + PVR_ASSERT(psHandle->sSiblings.hParent != IMG_NULL); + } + return IMG_FALSE; +} +#endif /*DEBUG*/ +/*! +****************************************************************************** + + @Function ParentHandle + + @Description Determine the parent of a handle + + @Input psHandle - pointer to handle structure + + @Return Parent handle, or IMG_NULL if the handle is not a subhandle. + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(ParentHandle) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +IMG_SID ParentHandle(struct sHandle *psHandle) +#else +IMG_HANDLE ParentHandle(struct sHandle *psHandle) +#endif +{ + return psHandle->sSiblings.hParent; +} + +/* + * The LIST_PTR_FROM_INDEX_AND_OFFSET macro is used to generate either a + * pointer to the subhandle list head, or a pointer to the linked list + * structure of an item on a subhandle list. + * The list head is itself on the list, but is at a different offset + * in the handle structure to the linked list structure for items on + * the list. The two linked list structures are differentiated by + * the third parameter, containing the parent index. The parent field + * in the list head structure references the handle structure that contains + * it. For items on the list, the parent field in the linked list structure + * references the parent handle, which will be different from the handle + * containing the linked list structure. + */ +#define LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, i, p, po, eo) \ + ((struct sHandleList *)((IMG_CHAR *)(INDEX_TO_HANDLE_STRUCT_PTR(psBase, i)) + (((i) == (p)) ? (po) : (eo)))) + +/*! +****************************************************************************** + + @Function HandleListInsertBefore + + @Description Insert a handle before a handle currently on the list. + + @Input ui32InsIndex - index of handle to be inserted after + psIns - pointer to handle structure to be inserted after + uiParentOffset - offset to list head struct in handle structure + ui32EntryIndex - index of handle to be inserted + psEntry - pointer to handle structure of item to be inserted + uiEntryOffset - offset of list item struct in handle structure + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(HandleListInsertBefore) +#endif +static INLINE +IMG_VOID HandleListInsertBefore(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32InsIndex, struct sHandleList *psIns, IMG_SIZE_T uiParentOffset, IMG_UINT32 ui32EntryIndex, struct sHandleList *psEntry, IMG_SIZE_T uiEntryOffset, IMG_UINT32 ui32ParentIndex) +{ + /* PRQA S 3305 7 */ /*override stricter alignment warning */ + struct sHandleList *psPrevIns = LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, psIns->ui32Prev, ui32ParentIndex, uiParentOffset, uiEntryOffset); + + PVR_ASSERT(psEntry->hParent == IMG_NULL); + PVR_ASSERT(ui32InsIndex == psPrevIns->ui32Next); + PVR_ASSERT(LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, ui32ParentIndex, ui32ParentIndex, uiParentOffset, uiParentOffset)->hParent == INDEX_TO_HANDLE(ui32ParentIndex)); + + psEntry->ui32Prev = psIns->ui32Prev; + psIns->ui32Prev = ui32EntryIndex; + psEntry->ui32Next = ui32InsIndex; + psPrevIns->ui32Next = ui32EntryIndex; + + psEntry->hParent = INDEX_TO_HANDLE(ui32ParentIndex); +} + +/*! +****************************************************************************** + + @Function AdoptChild + + @Description Assign a subhandle to a handle + + @Input psParent - pointer to handle structure of parent handle + psChild - pointer to handle structure of child subhandle + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(AdoptChild) +#endif +static INLINE +IMG_VOID AdoptChild(PVRSRV_HANDLE_BASE *psBase, struct sHandle *psParent, struct sHandle *psChild) +{ + IMG_UINT32 ui32Parent = HANDLE_TO_INDEX(psParent->sChildren.hParent); + + PVR_ASSERT(ui32Parent == HANDLE_PTR_TO_INDEX(psParent)); + + HandleListInsertBefore(psBase, ui32Parent, &psParent->sChildren, offsetof(struct sHandle, sChildren), HANDLE_PTR_TO_INDEX(psChild), &psChild->sSiblings, offsetof(struct sHandle, sSiblings), ui32Parent); + +} + +/*! +****************************************************************************** + + @Function HandleListRemove + + @Description Remove a handle from a list + + @Input ui32EntryIndex - index of handle to be removed + psEntry - pointer to handle structure of item to be removed + uiEntryOffset - offset of list item struct in handle structure + uiParentOffset - offset to list head struct in handle structure + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(HandleListRemove) +#endif +static INLINE +IMG_VOID HandleListRemove(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32EntryIndex, struct sHandleList *psEntry, IMG_SIZE_T uiEntryOffset, IMG_SIZE_T uiParentOffset) +{ + if (!HandleListIsEmpty(ui32EntryIndex, psEntry)) + { + /* PRQA S 3305 3 */ /*override stricter alignment warning */ + struct sHandleList *psPrev = LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, psEntry->ui32Prev, HANDLE_TO_INDEX(psEntry->hParent), uiParentOffset, uiEntryOffset); + struct sHandleList *psNext = LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, psEntry->ui32Next, HANDLE_TO_INDEX(psEntry->hParent), uiParentOffset, uiEntryOffset); + + /* + * The list head is on the list, and we don't want to + * remove it. + */ + PVR_ASSERT(psEntry->hParent != IMG_NULL); + + psPrev->ui32Next = psEntry->ui32Next; + psNext->ui32Prev = psEntry->ui32Prev; + + HandleListInit(ui32EntryIndex, psEntry, IMG_NULL); + } +} + +/*! +****************************************************************************** + + @Function UnlinkFromParent + + @Description Remove a subhandle from its parents list + + @Input psHandle - pointer to handle structure of child subhandle + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(UnlinkFromParent) +#endif +static INLINE +IMG_VOID UnlinkFromParent(PVRSRV_HANDLE_BASE *psBase, struct sHandle *psHandle) +{ + HandleListRemove(psBase, HANDLE_PTR_TO_INDEX(psHandle), &psHandle->sSiblings, offsetof(struct sHandle, sSiblings), offsetof(struct sHandle, sChildren)); +} + +/*! +****************************************************************************** + + @Function HandleListIterate + + @Description Iterate over the items in a list + + @Input psHead - pointer to list head + uiParentOffset - offset to list head struct in handle structure + uiEntryOffset - offset of list item struct in handle structure + pfnIterFunc - function to be called for each handle in the list + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(HandleListIterate) +#endif +static INLINE +PVRSRV_ERROR HandleListIterate(PVRSRV_HANDLE_BASE *psBase, struct sHandleList *psHead, IMG_SIZE_T uiParentOffset, IMG_SIZE_T uiEntryOffset, PVRSRV_ERROR (*pfnIterFunc)(PVRSRV_HANDLE_BASE *, struct sHandle *)) +{ + IMG_UINT32 ui32Index; + IMG_UINT32 ui32Parent = HANDLE_TO_INDEX(psHead->hParent); + + PVR_ASSERT(psHead->hParent != IMG_NULL); + + /* + * Follow the next chain from the list head until we reach + * the list head again, which signifies the end of the list. + */ + for(ui32Index = psHead->ui32Next; ui32Index != ui32Parent; ) + { + struct sHandle *psHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, ui32Index); + /* PRQA S 3305 2 */ /*override stricter alignment warning */ + struct sHandleList *psEntry = LIST_PTR_FROM_INDEX_AND_OFFSET(psBase, ui32Index, ui32Parent, uiParentOffset, uiEntryOffset); + PVRSRV_ERROR eError; + + PVR_ASSERT(psEntry->hParent == psHead->hParent); + /* + * Get the next index now, in case the list item is + * modified by the iteration function. + */ + ui32Index = psEntry->ui32Next; + + eError = (*pfnIterFunc)(psBase, psHandle); + if (eError != PVRSRV_OK) + { + return eError; + } + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function IterateOverChildren + + @Description Iterate over the subhandles of a parent handle + + @Input psParent - pointer to parent handle structure + pfnIterFunc - function to be called for each subhandle + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(IterateOverChildren) +#endif +static INLINE +PVRSRV_ERROR IterateOverChildren(PVRSRV_HANDLE_BASE *psBase, struct sHandle *psParent, PVRSRV_ERROR (*pfnIterFunc)(PVRSRV_HANDLE_BASE *, struct sHandle *)) +{ + return HandleListIterate(psBase, &psParent->sChildren, offsetof(struct sHandle, sChildren), offsetof(struct sHandle, sSiblings), pfnIterFunc); +} + +/*! +****************************************************************************** + + @Function GetHandleStructure + + @Description Get the handle structure for a given handle + + @Input psBase - pointer to handle base structure + ppsHandle - location to return pointer to handle structure + hHandle - handle from client + eType - handle type or PVRSRV_HANDLE_TYPE_NONE if the + handle type is not to be checked. + + @Output ppsHandle - points to a pointer to the handle structure + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(GetHandleStructure) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR GetHandleStructure(PVRSRV_HANDLE_BASE *psBase, struct sHandle **ppsHandle, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR GetHandleStructure(PVRSRV_HANDLE_BASE *psBase, struct sHandle **ppsHandle, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType) +#endif +{ + IMG_UINT32 ui32Index = HANDLE_TO_INDEX(hHandle); + struct sHandle *psHandle; + + /* Check handle index is in range */ + if (!INDEX_IS_VALID(psBase, ui32Index)) + { + PVR_DPF((PVR_DBG_ERROR, "GetHandleStructure: Handle index out of range (%u >= %u)", ui32Index, psBase->ui32TotalHandCount)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE; + } + + psHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, ui32Index); + if (psHandle->eType == PVRSRV_HANDLE_TYPE_NONE) + { + PVR_DPF((PVR_DBG_ERROR, "GetHandleStructure: Handle not allocated (index: %u)", ui32Index)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return PVRSRV_ERROR_HANDLE_NOT_ALLOCATED; + } + + /* + * Unless PVRSRV_HANDLE_TYPE_NONE was passed in to this function, + * check handle is of the correct type. + */ + if (eType != PVRSRV_HANDLE_TYPE_NONE && eType != psHandle->eType) + { + PVR_DPF((PVR_DBG_ERROR, "GetHandleStructure: Handle type mismatch (%d != %d)", eType, psHandle->eType)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return PVRSRV_ERROR_HANDLE_TYPE_MISMATCH; + } + + /* Return the handle structure */ + *ppsHandle = psHandle; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function ParentIfPrivate + + @Description Return the parent handle if the handle was allocated + with PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE, else return + IMG_NULL + + @Input psHandle - pointer to handle + + @Return Parent handle, or IMG_NULL + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(ParentIfPrivate) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +IMG_SID ParentIfPrivate(struct sHandle *psHandle) +#else +IMG_HANDLE ParentIfPrivate(struct sHandle *psHandle) +#endif +{ + return TEST_ALLOC_FLAG(psHandle, PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE) ? + ParentHandle(psHandle) : IMG_NULL; +} + +/*! +****************************************************************************** + + @Function InitKey + + @Description Initialise a hash table key for the current process + + @Input psBase - pointer to handle base structure + aKey - pointer to key + pvData - pointer to the resource the handle represents + eType - type of resource + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(InitKey) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +IMG_VOID InitKey(HAND_KEY aKey, PVRSRV_HANDLE_BASE *psBase, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, IMG_SID hParent) +#else +IMG_VOID InitKey(HAND_KEY aKey, PVRSRV_HANDLE_BASE *psBase, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, IMG_HANDLE hParent) +#endif +{ + PVR_UNREFERENCED_PARAMETER(psBase); + + aKey[HAND_KEY_DATA] = (IMG_UINTPTR_T)pvData; + aKey[HAND_KEY_TYPE] = (IMG_UINTPTR_T)eType; + aKey[HAND_KEY_PARENT] = (IMG_UINTPTR_T)hParent; +} + +/*! +****************************************************************************** + + @Function ReallocHandleArray + + @Description Reallocate the handle array + + @Input psBase - handle base. + phBlockAlloc - pointer to block allocation handle. + ui32NewCount - new handle count + ui32OldCount - old handle count + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +static +PVRSRV_ERROR ReallocHandleArray(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32NewCount) +{ + struct sHandleIndex *psOldArray = psBase->psHandleArray; + IMG_HANDLE hOldArrayBlockAlloc = psBase->hArrayBlockAlloc; + IMG_UINT32 ui32OldCount = psBase->ui32TotalHandCount; + struct sHandleIndex *psNewArray = IMG_NULL; + IMG_HANDLE hNewArrayBlockAlloc = IMG_NULL; + PVRSRV_ERROR eError; + PVRSRV_ERROR eReturn = PVRSRV_OK; + IMG_UINT32 ui32Index; + + if (ui32NewCount == ui32OldCount) + { + return PVRSRV_OK; + } + + if (ui32NewCount != 0 && !psBase->bPurgingEnabled && + ui32NewCount < ui32OldCount) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (((ui32OldCount % HANDLE_BLOCK_SIZE) != 0) || + ((ui32NewCount % HANDLE_BLOCK_SIZE) != 0)) + { + PVR_ASSERT((ui32OldCount % HANDLE_BLOCK_SIZE) == 0); + PVR_ASSERT((ui32NewCount % HANDLE_BLOCK_SIZE) == 0); + + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (ui32NewCount != 0) + { + /* Allocate new handle array */ + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + HANDLE_ARRAY_SIZE(ui32NewCount) * sizeof(struct sHandleIndex), + (IMG_VOID **)&psNewArray, + &hNewArrayBlockAlloc, + "Memory Area"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't allocate new handle array (%d)", eError)); + eReturn = eError; + goto error; + } + + if (ui32OldCount != 0) + { + OSMemCopy(psNewArray, psOldArray, HANDLE_ARRAY_SIZE(MIN(ui32NewCount, ui32OldCount)) * sizeof(struct sHandleIndex)); + } + } + + /* + * If the new handle array is smaller than the old one, free + * unused handle structures + */ + for(ui32Index = ui32NewCount; ui32Index < ui32OldCount; ui32Index += HANDLE_BLOCK_SIZE) + { + struct sHandleIndex *psIndex = INDEX_TO_INDEX_STRUCT_PTR(psOldArray, ui32Index); + + eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(struct sHandle) * HANDLE_BLOCK_SIZE, + psIndex->psHandle, + psIndex->hBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't free handle structures (%d)", eError)); + } + } + + /* + * If the new handle array is bigger than the old one, allocate + * new handle structures + */ + for(ui32Index = ui32OldCount; ui32Index < ui32NewCount; ui32Index += HANDLE_BLOCK_SIZE) + { + /* PRQA S 0505 1 */ /* psNewArray is never NULL, see assert earlier */ + struct sHandleIndex *psIndex = INDEX_TO_INDEX_STRUCT_PTR(psNewArray, ui32Index); + + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(struct sHandle) * HANDLE_BLOCK_SIZE, + (IMG_VOID **)&psIndex->psHandle, + &psIndex->hBlockAlloc, + "Memory Area"); + if (eError != PVRSRV_OK) + { + psIndex->psHandle = IMG_NULL; + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't allocate handle structures (%d)", eError)); + eReturn = eError; + } + else + { + IMG_UINT32 ui32SubIndex; + + psIndex->ui32FreeHandBlockCount = HANDLE_BLOCK_SIZE; + + for(ui32SubIndex = 0; ui32SubIndex < HANDLE_BLOCK_SIZE; ui32SubIndex++) + { + struct sHandle *psHandle = psIndex->psHandle + ui32SubIndex; + + + psHandle->ui32Index = ui32SubIndex + ui32Index; + psHandle->eType = PVRSRV_HANDLE_TYPE_NONE; + psHandle->eInternalFlag = INTERNAL_HANDLE_FLAG_NONE; + psHandle->ui32NextIndexPlusOne = 0; + } + } + } + if (eReturn != PVRSRV_OK) + { + goto error; + } + +#ifdef DEBUG_MAX_HANDLE_COUNT + /* Force handle failure to test error exit code */ + if (ui32NewCount > DEBUG_MAX_HANDLE_COUNT) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Max handle count (%u) reached", DEBUG_MAX_HANDLE_COUNT)); + eReturn = PVRSRV_ERROR_OUT_OF_MEMORY; + goto error; + } +#endif + + if (psOldArray != IMG_NULL) + { + /* Free old handle array */ + eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + HANDLE_ARRAY_SIZE(ui32OldCount) * sizeof(struct sHandleIndex), + psOldArray, + hOldArrayBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't free old handle array (%d)", eError)); + } + } + + psBase->psHandleArray = psNewArray; + psBase->hArrayBlockAlloc = hNewArrayBlockAlloc; + psBase->ui32TotalHandCount = ui32NewCount; + + if (ui32NewCount > ui32OldCount) + { + /* Check for wraparound */ + PVR_ASSERT(psBase->ui32FreeHandCount + (ui32NewCount - ui32OldCount) > psBase->ui32FreeHandCount); + + /* PRQA S 3382 1 */ /* ui32NewCount always > ui32OldCount */ + psBase->ui32FreeHandCount += (ui32NewCount - ui32OldCount); + + /* + * If purging is enabled, there is no free handle list + * management, but as an optimization, when allocating + * new handles, we use ui32FirstFreeIndex to point to + * the first handle in a newly allocated block. + */ + if (psBase->ui32FirstFreeIndex == 0) + { + PVR_ASSERT(psBase->ui32LastFreeIndexPlusOne == 0); + + psBase->ui32FirstFreeIndex = ui32OldCount; + } + else + { + if (!psBase->bPurgingEnabled) + { + PVR_ASSERT(psBase->ui32LastFreeIndexPlusOne != 0); + PVR_ASSERT(INDEX_TO_HANDLE_STRUCT_PTR(psBase, psBase->ui32LastFreeIndexPlusOne - 1)->ui32NextIndexPlusOne == 0); + + INDEX_TO_HANDLE_STRUCT_PTR(psBase, psBase->ui32LastFreeIndexPlusOne - 1)->ui32NextIndexPlusOne = ui32OldCount + 1; + } + } + + if (!psBase->bPurgingEnabled) + { + psBase->ui32LastFreeIndexPlusOne = ui32NewCount; + } + } + else + { + PVR_ASSERT(ui32NewCount == 0 || psBase->bPurgingEnabled); + PVR_ASSERT(ui32NewCount == 0 || psBase->ui32FirstFreeIndex <= ui32NewCount); + PVR_ASSERT(psBase->ui32FreeHandCount - (ui32OldCount - ui32NewCount) < psBase->ui32FreeHandCount); + + /* PRQA S 3382 1 */ /* ui32OldCount always >= ui32NewCount */ + psBase->ui32FreeHandCount -= (ui32OldCount - ui32NewCount); + + if (ui32NewCount == 0) + { + psBase->ui32FirstFreeIndex = 0; + psBase->ui32LastFreeIndexPlusOne = 0; + } + } + + PVR_ASSERT(psBase->ui32FirstFreeIndex <= psBase->ui32TotalHandCount); + + return PVRSRV_OK; + +error: + PVR_ASSERT(eReturn != PVRSRV_OK); + + if (psNewArray != IMG_NULL) + { + /* Free any new handle structures that were allocated */ + for(ui32Index = ui32OldCount; ui32Index < ui32NewCount; ui32Index += HANDLE_BLOCK_SIZE) + { + struct sHandleIndex *psIndex = INDEX_TO_INDEX_STRUCT_PTR(psNewArray, ui32Index); + if (psIndex->psHandle != IMG_NULL) + { + eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(struct sHandle) * HANDLE_BLOCK_SIZE, + psIndex->psHandle, + psIndex->hBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't free handle structures (%d)", eError)); + } + } + } + + /* Free new handle array */ + eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + HANDLE_ARRAY_SIZE(ui32NewCount) * sizeof(struct sHandleIndex), + psNewArray, + hNewArrayBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ReallocHandleArray: Couldn't free new handle array (%d)", eError)); + } + } + + return eReturn; +} + +/*! +****************************************************************************** + + @Function FreeHandleArray + + @Description Frees the handle array. + The memory containing the array of handle structure + pointers is deallocated. + + @Input psBase - pointer to handle base structure + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +static PVRSRV_ERROR FreeHandleArray(PVRSRV_HANDLE_BASE *psBase) +{ + return ReallocHandleArray(psBase, 0); +} + +/*! +****************************************************************************** + + @Function FreeHandle + + @Description Free a handle structure. + + @Input psBase - pointer to handle base structure + psHandle - pointer to handle structure + + @Return PVRSRV_OK or PVRSRV_ERROR + +******************************************************************************/ +static PVRSRV_ERROR FreeHandle(PVRSRV_HANDLE_BASE *psBase, struct sHandle *psHandle) +{ + HAND_KEY aKey; + IMG_UINT32 ui32Index = HANDLE_PTR_TO_INDEX(psHandle); + PVRSRV_ERROR eError; + + /* + * If a handle allocated in batch mode is freed whilst still + * in batch mode, the type is set to PVRSRV_HANDLE_TYPE_NONE further + * down, to indicate the handle will not be used, but not actually + * freed. The Free is completed when this function is called a + * second time as part of the batch commit or release. + */ + + InitKey(aKey, psBase, psHandle->pvData, psHandle->eType, ParentIfPrivate(psHandle)); + + if (!TEST_ALLOC_FLAG(psHandle, PVRSRV_HANDLE_ALLOC_FLAG_MULTI) && !BATCHED_HANDLE_PARTIALLY_FREE(psHandle)) + { +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hHandle; + hHandle = (IMG_SID) HASH_Remove_Extended(psBase->psHashTab, aKey); +#else + IMG_HANDLE hHandle; + hHandle = (IMG_HANDLE) HASH_Remove_Extended(psBase->psHashTab, aKey); + +#endif + + PVR_ASSERT(hHandle != IMG_NULL); + PVR_ASSERT(hHandle == INDEX_TO_HANDLE(ui32Index)); + PVR_UNREFERENCED_PARAMETER(hHandle); + } + + /* Unlink handle from parent */ + UnlinkFromParent(psBase, psHandle); + + /* Free children */ + eError = IterateOverChildren(psBase, psHandle, FreeHandle); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeHandle: Error whilst freeing subhandles (%d)", eError)); + return eError; + } + + /* + * Clear the type here, so that a handle can no longer be looked + * up if it is only partially freed. + */ + psHandle->eType = PVRSRV_HANDLE_TYPE_NONE; + + if (BATCHED_HANDLE(psHandle) && !BATCHED_HANDLE_PARTIALLY_FREE(psHandle)) + { + /* PRQA S 1474,4130 1 */ /* ignore warnings about enum types being modified */ + SET_BATCHED_HANDLE_PARTIALLY_FREE(psHandle); + /* + * If the handle was allocated in batch mode, delay the free + * until the batch commit or release. + */ + return PVRSRV_OK; + } + + /* No free list management if purging is enabled */ + if (!psBase->bPurgingEnabled) + { + if (psBase->ui32FreeHandCount == 0) + { + PVR_ASSERT(psBase->ui32FirstFreeIndex == 0); + PVR_ASSERT(psBase->ui32LastFreeIndexPlusOne == 0); + + psBase->ui32FirstFreeIndex = ui32Index; + } + else + { + /* + * Put the handle pointer on the end of the the free + * handle pointer linked list. + */ + PVR_ASSERT(psBase->ui32LastFreeIndexPlusOne != 0); + PVR_ASSERT(INDEX_TO_HANDLE_STRUCT_PTR(psBase, psBase->ui32LastFreeIndexPlusOne - 1)->ui32NextIndexPlusOne == 0); + INDEX_TO_HANDLE_STRUCT_PTR(psBase, psBase->ui32LastFreeIndexPlusOne - 1)->ui32NextIndexPlusOne = ui32Index + 1; + } + + PVR_ASSERT(psHandle->ui32NextIndexPlusOne == 0); + + /* Update the end of the free handle linked list */ + psBase->ui32LastFreeIndexPlusOne = ui32Index + 1; + } + + psBase->ui32FreeHandCount++; + INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32Index)++; + + PVR_ASSERT(INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32Index)<= HANDLE_BLOCK_SIZE); + +#ifdef DEBUG + { + IMG_UINT32 ui32BlockedIndex; + IMG_UINT32 ui32FreeHandCount = 0; + + for (ui32BlockedIndex = 0; ui32BlockedIndex < psBase->ui32TotalHandCount; ui32BlockedIndex += HANDLE_BLOCK_SIZE) + { + ui32FreeHandCount += INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32BlockedIndex); + } + + PVR_ASSERT(ui32FreeHandCount == psBase->ui32FreeHandCount); + } +#endif + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function FreeAllHandles + + @Description Free all handles for a given handle base + + @Input psBase - pointer to handle base structure + + @Return PVRSRV_OK or PVRSRV_ERROR + +******************************************************************************/ +static PVRSRV_ERROR FreeAllHandles(PVRSRV_HANDLE_BASE *psBase) +{ + IMG_UINT32 i; + PVRSRV_ERROR eError = PVRSRV_OK; + + if (psBase->ui32FreeHandCount == psBase->ui32TotalHandCount) + { + return eError; + } + + for (i = 0; i < psBase->ui32TotalHandCount; i++) + { + struct sHandle *psHandle; + + psHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, i); + + if (psHandle->eType != PVRSRV_HANDLE_TYPE_NONE) + { + eError = FreeHandle(psBase, psHandle); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeAllHandles: FreeHandle failed (%d)", eError)); + break; + } + + /* Break out of loop if all the handles free */ + if (psBase->ui32FreeHandCount == psBase->ui32TotalHandCount) + { + break; + } + } + } + + return eError; +} + +/*! +****************************************************************************** + + @Function FreeHandleBase + + @Description Free a handle base. + + @Input psHandleBase - pointer to handle base + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +static PVRSRV_ERROR FreeHandleBase(PVRSRV_HANDLE_BASE *psBase) +{ + PVRSRV_ERROR eError; + + if (HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_WARNING, "FreeHandleBase: Uncommitted/Unreleased handle batch")); + PVRSRVReleaseHandleBatch(psBase); + } + + /* Free the handle array */ + eError = FreeAllHandles(psBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeHandleBase: Couldn't free handles (%d)", eError)); + return eError; + } + + /* Free the handle array */ + eError = FreeHandleArray(psBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeHandleBase: Couldn't free handle array (%d)", eError)); + return eError; + } + + if (psBase->psHashTab != IMG_NULL) + { + /* Free the hash table */ + HASH_Delete(psBase->psHashTab); + } + + eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*psBase), + psBase, + psBase->hBaseBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeHandleBase: Couldn't free handle base (%d)", eError)); + return eError; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function FindHandle + + @Description Find handle corresponding to a resource pointer + + @Input psBase - pointer to handle base structure + pvData - pointer to resource to be associated with the handle + eType - the type of resource + + @Return the handle, or IMG_NULL if not found + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(FindHandle) +#endif +static INLINE +#if defined (SUPPORT_SID_INTERFACE) +IMG_SID FindHandle(PVRSRV_HANDLE_BASE *psBase, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, IMG_SID hParent) +#else +IMG_HANDLE FindHandle(PVRSRV_HANDLE_BASE *psBase, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, IMG_HANDLE hParent) +#endif +{ + HAND_KEY aKey; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + InitKey(aKey, psBase, pvData, eType, hParent); + +#if defined (SUPPORT_SID_INTERFACE) + return (IMG_SID) HASH_Retrieve_Extended(psBase->psHashTab, aKey); +#else + return (IMG_HANDLE) HASH_Retrieve_Extended(psBase->psHashTab, aKey); +#endif +} + +/*! +****************************************************************************** + + @Function IncreaseHandleArraySize + + @Description Allocate some more free handles + + @Input psBase - pointer to handle base structure + ui32Delta - number of new handles required + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +static PVRSRV_ERROR IncreaseHandleArraySize(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32Delta) +{ + PVRSRV_ERROR eError; + IMG_UINT32 ui32DeltaAdjusted = ROUND_UP_TO_MULTIPLE_OF_BLOCK_SIZE(ui32Delta); + IMG_UINT32 ui32NewTotalHandCount = psBase->ui32TotalHandCount + ui32DeltaAdjusted; + + PVR_ASSERT(ui32Delta != 0); + + /* + * Check new count against max handle index, and check for wrap around. + */ + if (ui32NewTotalHandCount > psBase->ui32MaxIndexPlusOne || ui32NewTotalHandCount <= psBase->ui32TotalHandCount) + { + ui32NewTotalHandCount = psBase->ui32MaxIndexPlusOne; + + ui32DeltaAdjusted = ui32NewTotalHandCount - psBase->ui32TotalHandCount; + + if (ui32DeltaAdjusted < ui32Delta) + { + PVR_DPF((PVR_DBG_ERROR, "IncreaseHandleArraySize: Maximum handle limit reached (%d)", psBase->ui32MaxIndexPlusOne)); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } + + PVR_ASSERT(ui32DeltaAdjusted >= ui32Delta); + + /* Realloc handle pointer array */ + eError = ReallocHandleArray(psBase, ui32NewTotalHandCount); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "IncreaseHandleArraySize: ReallocHandleArray failed (%d)", eError)); + return eError; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function EnsureFreeHandles + + @Description Ensure there are enough free handles + + @Input psBase - pointer to handle base structure + ui32Free - number of free handles required + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +static PVRSRV_ERROR EnsureFreeHandles(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32Free) +{ + PVRSRV_ERROR eError; + + if (ui32Free > psBase->ui32FreeHandCount) + { + IMG_UINT32 ui32FreeHandDelta = ui32Free - psBase->ui32FreeHandCount; + eError = IncreaseHandleArraySize(psBase, ui32FreeHandDelta); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "EnsureFreeHandles: Couldn't allocate %u handles to ensure %u free handles (IncreaseHandleArraySize failed with error %d)", ui32FreeHandDelta, ui32Free, eError)); + + return eError; + } + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function AllocHandle + + @Description Allocate a new handle + + @Input phHandle - location for new handle + pvData - pointer to resource to be associated with the handle + eType - the type of resource + hParent - parent handle or IMG_NULL + + @Output phHandle - points to new handle + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +static PVRSRV_ERROR AllocHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag, IMG_SID hParent) +#else +static PVRSRV_ERROR AllocHandle(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag, IMG_HANDLE hParent) +#endif +{ + IMG_UINT32 ui32NewIndex = DEFAULT_MAX_INDEX_PLUS_ONE; + struct sHandle *psNewHandle = IMG_NULL; +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hHandle; +#else + IMG_HANDLE hHandle; +#endif + HAND_KEY aKey; + PVRSRV_ERROR eError; + + /* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */ + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + PVR_ASSERT(psBase != IMG_NULL); + PVR_ASSERT(psBase->psHashTab != IMG_NULL); + + if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI)) + { + /* Handle must not already exist */ + PVR_ASSERT(FindHandle(psBase, pvData, eType, hParent) == IMG_NULL); + } + + if (psBase->ui32FreeHandCount == 0 && HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_WARNING, "AllocHandle: Handle batch size (%u) was too small, allocating additional space", psBase->ui32HandBatchSize)); + } + + /* Ensure there is a free handle */ + eError = EnsureFreeHandles(psBase, 1); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "AllocHandle: EnsureFreeHandles failed (%d)", eError)); + return eError; + } + PVR_ASSERT(psBase->ui32FreeHandCount != 0); + + if (!psBase->bPurgingEnabled) + { + /* Array index of first free handle */ + ui32NewIndex = psBase->ui32FirstFreeIndex; + + /* Get handle array entry */ + psNewHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, ui32NewIndex); + } + else + { + IMG_UINT32 ui32BlockedIndex; + + /* + * If purging is enabled, we always try to allocate handles + * at the front of the array, to increase the chances that + * the size of the handle array can be reduced by a purge. + * No linked list of free handles is kept; we search for + * free handles as required. + */ + + /* + * ui32FirstFreeIndex should only be set when a new batch of + * handle structures is allocated, and should always be a + * multiple of the block size. + */ + PVR_ASSERT((psBase->ui32FirstFreeIndex % HANDLE_BLOCK_SIZE) == 0); + + for (ui32BlockedIndex = ROUND_DOWN_TO_MULTIPLE_OF_BLOCK_SIZE(psBase->ui32FirstFreeIndex); ui32BlockedIndex < psBase->ui32TotalHandCount; ui32BlockedIndex += HANDLE_BLOCK_SIZE) + { + struct sHandleIndex *psIndex = BASE_AND_INDEX_TO_INDEX_STRUCT_PTR(psBase, ui32BlockedIndex); + + if (psIndex->ui32FreeHandBlockCount == 0) + { + continue; + } + + for (ui32NewIndex = ui32BlockedIndex; ui32NewIndex < ui32BlockedIndex + HANDLE_BLOCK_SIZE; ui32NewIndex++) + { + psNewHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, ui32NewIndex); + if (HANDLE_STRUCT_IS_FREE(psNewHandle)) + { + break; + } + } + } + psBase->ui32FirstFreeIndex = 0; + PVR_ASSERT(ui32NewIndex < psBase->ui32TotalHandCount); + } + PVR_ASSERT(psNewHandle != IMG_NULL); + + /* Handle to be returned to client */ + hHandle = INDEX_TO_HANDLE(ui32NewIndex); + + /* + * If a data pointer can be associated with multiple handles, we + * don't put the handle in the hash table, as the data pointer + * may not map to a unique handle + */ + if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI)) + { + /* Initialise hash key */ + InitKey(aKey, psBase, pvData, eType, hParent); + + /* Put the new handle in the hash table */ + if (!HASH_Insert_Extended(psBase->psHashTab, aKey, (IMG_UINTPTR_T)hHandle)) + { + PVR_DPF((PVR_DBG_ERROR, "AllocHandle: Couldn't add handle to hash table")); + + return PVRSRV_ERROR_UNABLE_TO_ADD_HANDLE; + } + } + + psBase->ui32FreeHandCount--; + + PVR_ASSERT(INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32NewIndex) <= HANDLE_BLOCK_SIZE); + PVR_ASSERT(INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32NewIndex) > 0); + + INDEX_TO_FREE_HAND_BLOCK_COUNT(psBase, ui32NewIndex)--; + + /* No free list management if purging is enabled */ + if (!psBase->bPurgingEnabled) + { + /* Check whether the last free handle has been allocated */ + if (psBase->ui32FreeHandCount == 0) + { + PVR_ASSERT(psBase->ui32FirstFreeIndex == ui32NewIndex); + PVR_ASSERT(psBase->ui32LastFreeIndexPlusOne == (ui32NewIndex + 1)); + + psBase->ui32LastFreeIndexPlusOne = 0; + psBase->ui32FirstFreeIndex = 0; + } + else + { + /* + * Update the first free handle index. + * If the "next free index plus one" field in the new + * handle structure is zero, the next free index is + * the index of the new handle plus one. This + * convention has been adopted to simplify the + * initialisation of freshly allocated handle + * space. + */ + psBase->ui32FirstFreeIndex = (psNewHandle->ui32NextIndexPlusOne == 0) ? + ui32NewIndex + 1 : + psNewHandle->ui32NextIndexPlusOne - 1; + } + } + + /* Initialise the newly allocated handle */ + PVR_ASSERT(psNewHandle->ui32Index == ui32NewIndex); + + /* PRQA S 0505 1 */ /* psNewHandle is never NULL, see assert earlier */ + psNewHandle->eType = eType; + psNewHandle->pvData = pvData; + psNewHandle->eInternalFlag = INTERNAL_HANDLE_FLAG_NONE; + psNewHandle->eFlag = eFlag; + + InitParentList(psNewHandle); +#if defined(DEBUG) + PVR_ASSERT(NoChildren(psNewHandle)); +#endif + + InitChildEntry(psNewHandle); +#if defined(DEBUG) + PVR_ASSERT(NoParent(psNewHandle)); +#endif + + if (HANDLES_BATCHED(psBase)) + { + /* Add handle to batch list */ + psNewHandle->ui32NextIndexPlusOne = psBase->ui32FirstBatchIndexPlusOne; + + psBase->ui32FirstBatchIndexPlusOne = ui32NewIndex + 1; + + /* PRQA S 1474 1 */ /* ignore warnings about enum types being modified */ + SET_BATCHED_HANDLE(psNewHandle); + } + else + { + psNewHandle->ui32NextIndexPlusOne = 0; + } + + /* Return the new handle to the client */ + *phHandle = hHandle; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVAllocHandle + + @Description Allocate a handle + + @Input phHandle - location for new handle + pvData - pointer to resource to be associated with the handle + eType - the type of resource + + @Output phHandle - points to new handle + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVAllocHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag) +#else +PVRSRV_ERROR PVRSRVAllocHandle(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag) +#endif +{ +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hHandle; +#else + IMG_HANDLE hHandle; +#endif + PVRSRV_ERROR eError; + +#if defined (SUPPORT_SID_INTERFACE) + *phHandle = 0; +#else + *phHandle = IMG_NULL; +#endif + + if (HANDLES_BATCHED(psBase)) + { + /* + * Increment the counter in case of failure. It will be + * decremented on success. + */ + psBase->ui32BatchHandAllocFailures++; + } + + /* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */ + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI)) + { + /* See if there is already a handle for this data pointer */ + hHandle = FindHandle(psBase, pvData, eType, IMG_NULL); +#if defined (SUPPORT_SID_INTERFACE) + if (hHandle != 0) +#else + if (hHandle != IMG_NULL) +#endif + { + struct sHandle *psHandle; + + eError = GetHandleStructure(psBase, &psHandle, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandle: Lookup of existing handle failed")); + return eError; + } + + /* + * If the client is willing to share a handle, and the + * existing handle is marked as shareable, return the + * existing handle. + */ + if (TEST_FLAG(psHandle->eFlag & eFlag, PVRSRV_HANDLE_ALLOC_FLAG_SHARED)) + { + *phHandle = hHandle; + eError = PVRSRV_OK; + goto exit_ok; + } + +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return PVRSRV_ERROR_HANDLE_NOT_SHAREABLE; + } + } + + eError = AllocHandle(psBase, phHandle, pvData, eType, eFlag, IMG_NULL); + +exit_ok: + if (HANDLES_BATCHED(psBase) && (eError == PVRSRV_OK)) + { + psBase->ui32BatchHandAllocFailures--; + } + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVAllocSubHandle + + @Description Allocate a subhandle + + @Input phHandle - location for new subhandle + pvData - pointer to resource to be associated with the subhandle + eType - the type of resource + hParent - parent handle + + @Output phHandle - points to new subhandle + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVAllocSubHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag, IMG_SID hParent) +#else +PVRSRV_ERROR PVRSRVAllocSubHandle(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType, PVRSRV_HANDLE_ALLOC_FLAG eFlag, IMG_HANDLE hParent) +#endif +{ + struct sHandle *psPHand; + struct sHandle *psCHand; + PVRSRV_ERROR eError; +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hParentKey; + IMG_SID hHandle; + + *phHandle = 0; +#else + IMG_HANDLE hParentKey; + IMG_HANDLE hHandle; + + *phHandle = IMG_NULL; +#endif + + if (HANDLES_BATCHED(psBase)) + { + /* + * Increment the counter in case of failure. It will be + * decremented on success. + */ + psBase->ui32BatchHandAllocFailures++; + } + + /* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */ + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + hParentKey = TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE) ? + hParent : IMG_NULL; + + /* Lookup the parent handle */ + eError = GetHandleStructure(psBase, &psPHand, hParent, PVRSRV_HANDLE_TYPE_NONE); + if (eError != PVRSRV_OK) + { + return eError; + } + + if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI)) + { + /* See if there is already a handle for this data pointer */ + hHandle = FindHandle(psBase, pvData, eType, hParentKey); +#if defined (SUPPORT_SID_INTERFACE) + if (hHandle != 0) +#else + if (hHandle != IMG_NULL) +#endif + { + struct sHandle *psCHandle; + PVRSRV_ERROR eErr; + + eErr = GetHandleStructure(psBase, &psCHandle, hHandle, eType); + if (eErr != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Lookup of existing handle failed")); + return eErr; + } + + PVR_ASSERT(hParentKey != IMG_NULL && ParentHandle(HANDLE_TO_HANDLE_STRUCT_PTR(psBase, hHandle)) == hParent); + + /* + * If the client is willing to share a handle, the + * existing handle is marked as shareable, and the + * existing handle has the same parent, return the + * existing handle. + */ + if (TEST_FLAG(psCHandle->eFlag & eFlag, PVRSRV_HANDLE_ALLOC_FLAG_SHARED) && ParentHandle(HANDLE_TO_HANDLE_STRUCT_PTR(psBase, hHandle)) == hParent) + { + *phHandle = hHandle; + goto exit_ok; + } +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return PVRSRV_ERROR_HANDLE_NOT_SHAREABLE; + } + } + + eError = AllocHandle(psBase, &hHandle, pvData, eType, eFlag, hParentKey); + if (eError != PVRSRV_OK) + { + return eError; + } + + /* + * Get the parent handle structure again, in case the handle + * structure has moved (depending on the implementation + * of AllocHandle). + */ + psPHand = HANDLE_TO_HANDLE_STRUCT_PTR(psBase, hParent); + + psCHand = HANDLE_TO_HANDLE_STRUCT_PTR(psBase, hHandle); + + AdoptChild(psBase, psPHand, psCHand); + + *phHandle = hHandle; + +exit_ok: + if (HANDLES_BATCHED(psBase)) + { + psBase->ui32BatchHandAllocFailures--; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVFindHandle + + @Description Find handle corresponding to a resource pointer + + @Input phHandle - location for returned handle + pvData - pointer to resource to be associated with the handle + eType - the type of resource + + @Output phHandle - points to handle + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVFindHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR PVRSRVFindHandle(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE *phHandle, IMG_VOID *pvData, PVRSRV_HANDLE_TYPE eType) +#endif +{ +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hHandle; +#else + IMG_HANDLE hHandle; +#endif + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + /* See if there is a handle for this data pointer */ +#if defined (SUPPORT_SID_INTERFACE) + hHandle = (IMG_SID) FindHandle(psBase, pvData, eType, IMG_NULL); +#else + hHandle = (IMG_HANDLE) FindHandle(psBase, pvData, eType, IMG_NULL); +#endif + if (hHandle == IMG_NULL) + { + return PVRSRV_ERROR_HANDLE_NOT_FOUND; + } + + *phHandle = hHandle; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVLookupHandleAnyType + + @Description Lookup the data pointer and type corresponding to a handle + + @Input ppvData - location to return data pointer + peType - location to return handle type + hHandle - handle from client + + @Output ppvData - points to the data pointer + peType - points to handle type + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVLookupHandleAnyType(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, PVRSRV_HANDLE_TYPE *peType, IMG_SID hHandle) +#else +PVRSRV_ERROR PVRSRVLookupHandleAnyType(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, PVRSRV_HANDLE_TYPE *peType, IMG_HANDLE hHandle) +#endif +{ + struct sHandle *psHandle; + PVRSRV_ERROR eError; + + eError = GetHandleStructure(psBase, &psHandle, hHandle, PVRSRV_HANDLE_TYPE_NONE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupHandleAnyType: Error looking up handle (%d)", eError)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return eError; + } + + *ppvData = psHandle->pvData; + *peType = psHandle->eType; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVLookupHandle + + @Description Lookup the data pointer corresponding to a handle + + @Input ppvData - location to return data pointer + hHandle - handle from client + eType - handle type + + @Output ppvData - points to the data pointer + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVLookupHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR PVRSRVLookupHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType) +#endif +{ + struct sHandle *psHandle; + PVRSRV_ERROR eError; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); +#if defined (SUPPORT_SID_INTERFACE) + PVR_ASSERT(hHandle != 0); +#endif + + eError = GetHandleStructure(psBase, &psHandle, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupHandle: Error looking up handle (%d)", eError)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return eError; + } + + *ppvData = psHandle->pvData; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVLookupSubHandle + + @Description Lookup the data pointer corresponding to a subhandle + + @Input ppvData - location to return data pointer + hHandle - handle from client + eType - handle type + hAncestor - ancestor handle + + @Output ppvData - points to the data pointer + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVLookupSubHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType, IMG_SID hAncestor) +#else +PVRSRV_ERROR PVRSRVLookupSubHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType, IMG_HANDLE hAncestor) +#endif +{ + struct sHandle *psPHand; + struct sHandle *psCHand; + PVRSRV_ERROR eError; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); +#if defined (SUPPORT_SID_INTERFACE) + PVR_ASSERT(hHandle != 0); +#endif + + eError = GetHandleStructure(psBase, &psCHand, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupSubHandle: Error looking up subhandle (%d)", eError)); + return eError; + } + + /* Look for hAncestor among the handle's ancestors */ + for (psPHand = psCHand; ParentHandle(psPHand) != hAncestor; ) + { + eError = GetHandleStructure(psBase, &psPHand, ParentHandle(psPHand), PVRSRV_HANDLE_TYPE_NONE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupSubHandle: Subhandle doesn't belong to given ancestor")); + return PVRSRV_ERROR_INVALID_SUBHANDLE; + } + } + + *ppvData = psCHand->pvData; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVGetParentHandle + + @Description Lookup the parent of a handle + + @Input phParent - location for returning parent handle + hHandle - handle for which the parent handle is required + eType - handle type + hParent - parent handle + + @Output *phParent - parent handle, or IMG_NULL if there is no parent + + @Return Error code or PVRSRV_OK. Note that not having a parent is + not regarded as an error. + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVGetParentHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID *phParent, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR PVRSRVGetParentHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *phParent, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType) +#endif +{ + struct sHandle *psHandle; + PVRSRV_ERROR eError; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + eError = GetHandleStructure(psBase, &psHandle, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetParentHandle: Error looking up subhandle (%d)", eError)); + return eError; + } + + *phParent = ParentHandle(psHandle); + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVLookupAndReleaseHandle + + @Description Lookup the data pointer corresponding to a handle + + @Input ppvData - location to return data pointer + hHandle - handle from client + eType - handle type + eFlag - lookup flags + + @Output ppvData - points to the data pointer + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVLookupAndReleaseHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR PVRSRVLookupAndReleaseHandle(PVRSRV_HANDLE_BASE *psBase, IMG_PVOID *ppvData, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType) +#endif +{ + struct sHandle *psHandle; + PVRSRV_ERROR eError; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + eError = GetHandleStructure(psBase, &psHandle, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupAndReleaseHandle: Error looking up handle (%d)", eError)); +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#endif + return eError; + } + + *ppvData = psHandle->pvData; + + eError = FreeHandle(psBase, psHandle); + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVReleaseHandle + + @Description Release a handle that is no longer needed + + @Input hHandle - handle from client + eType - handle type + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR PVRSRVReleaseHandle(PVRSRV_HANDLE_BASE *psBase, IMG_SID hHandle, PVRSRV_HANDLE_TYPE eType) +#else +PVRSRV_ERROR PVRSRVReleaseHandle(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE hHandle, PVRSRV_HANDLE_TYPE eType) +#endif +{ + struct sHandle *psHandle; + PVRSRV_ERROR eError; + + PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE); + + eError = GetHandleStructure(psBase, &psHandle, hHandle, eType); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVReleaseHandle: Error looking up handle (%d)", eError)); + return eError; + } + + eError = FreeHandle(psBase, psHandle); + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVNewHandleBatch + + @Description Start a new handle batch + + @Input psBase - handle base + @Input ui32BatchSize - handle batch size + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVNewHandleBatch(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32BatchSize) +{ + PVRSRV_ERROR eError; + + if (HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVNewHandleBatch: There is a handle batch already in use (size %u)", psBase->ui32HandBatchSize)); + return PVRSRV_ERROR_HANDLE_BATCH_IN_USE; + } + + if (ui32BatchSize == 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVNewHandleBatch: Invalid batch size (%u)", ui32BatchSize)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + eError = EnsureFreeHandles(psBase, ui32BatchSize); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVNewHandleBatch: EnsureFreeHandles failed (error %d)", eError)); + return eError; + } + + psBase->ui32HandBatchSize = ui32BatchSize; + + /* Record current number of handles */ + psBase->ui32TotalHandCountPreBatch = psBase->ui32TotalHandCount; + + PVR_ASSERT(psBase->ui32BatchHandAllocFailures == 0); + + PVR_ASSERT(psBase->ui32FirstBatchIndexPlusOne == 0); + + PVR_ASSERT(HANDLES_BATCHED(psBase)); + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVHandleBatchCommitOrRelease + + @Description Release a handle batch + + @Input psBase - handle base + bCommit - commit handles + + @Return none + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVHandleBatchCommitOrRelease(PVRSRV_HANDLE_BASE *psBase, IMG_BOOL bCommit) +{ + + IMG_UINT32 ui32IndexPlusOne; + IMG_BOOL bCommitBatch = bCommit; + + if (!HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleBatchCommitOrRelease: There is no handle batch")); + return PVRSRV_ERROR_INVALID_PARAMS; + + } + + if (psBase->ui32BatchHandAllocFailures != 0) + { + if (bCommit) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleBatchCommitOrRelease: Attempting to commit batch with handle allocation failures.")); + } + bCommitBatch = IMG_FALSE; + } + /* + * The whole point of batched handles is to avoid handle allocation + * failures. + */ + PVR_ASSERT(psBase->ui32BatchHandAllocFailures == 0 || !bCommit); + + ui32IndexPlusOne = psBase->ui32FirstBatchIndexPlusOne; + while(ui32IndexPlusOne != 0) + { + struct sHandle *psHandle = INDEX_TO_HANDLE_STRUCT_PTR(psBase, ui32IndexPlusOne - 1); + IMG_UINT32 ui32NextIndexPlusOne = psHandle->ui32NextIndexPlusOne; + PVR_ASSERT(BATCHED_HANDLE(psHandle)); + + psHandle->ui32NextIndexPlusOne = 0; + + if (!bCommitBatch || BATCHED_HANDLE_PARTIALLY_FREE(psHandle)) + { + PVRSRV_ERROR eError; + + /* + * We need a complete free here. If the handle + * is not partially free, set the handle as + * unbatched to avoid a partial free. + */ + if (!BATCHED_HANDLE_PARTIALLY_FREE(psHandle)) + { + /* PRQA S 1474,4130 1 */ /* ignore warnings about enum types being modified */ + SET_UNBATCHED_HANDLE(psHandle); /* PRQA S 4130 */ /* mis-use of enums FIXME*/ + } + + eError = FreeHandle(psBase, psHandle); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleBatchCommitOrRelease: Error freeing handle (%d)", eError)); + } + PVR_ASSERT(eError == PVRSRV_OK); + } + else + { + /* PRQA S 1474,4130 1 */ /* ignore warnings about enum types being modified */ + SET_UNBATCHED_HANDLE(psHandle); + } + + ui32IndexPlusOne = ui32NextIndexPlusOne; + } + +#ifdef DEBUG + if (psBase->ui32TotalHandCountPreBatch != psBase->ui32TotalHandCount) + { + IMG_UINT32 ui32Delta = psBase->ui32TotalHandCount - psBase->ui32TotalHandCountPreBatch; + + PVR_ASSERT(psBase->ui32TotalHandCount > psBase->ui32TotalHandCountPreBatch); + + PVR_DPF((PVR_DBG_WARNING, "PVRSRVHandleBatchCommitOrRelease: The batch size was too small. Batch size was %u, but needs to be %u", psBase->ui32HandBatchSize, psBase->ui32HandBatchSize + ui32Delta)); + + } +#endif + + psBase->ui32HandBatchSize = 0; + psBase->ui32FirstBatchIndexPlusOne = 0; + psBase->ui32TotalHandCountPreBatch = 0; + psBase->ui32BatchHandAllocFailures = 0; + + if (psBase->ui32BatchHandAllocFailures != 0 && bCommit) + { + PVR_ASSERT(!bCommitBatch); + + return PVRSRV_ERROR_HANDLE_BATCH_COMMIT_FAILURE; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVCommitHandleBatch + + @Description Commit a handle batch + + @Input psBase - handle base + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVCommitHandleBatch(PVRSRV_HANDLE_BASE *psBase) +{ + return PVRSRVHandleBatchCommitOrRelease(psBase, IMG_TRUE); +} + +/*! +****************************************************************************** + + @Function PVRSRReleaseHandleBatch + + @Description Release a handle batch + + @Input psBase - handle base + + @Return none + +******************************************************************************/ +IMG_VOID PVRSRVReleaseHandleBatch(PVRSRV_HANDLE_BASE *psBase) +{ + (IMG_VOID) PVRSRVHandleBatchCommitOrRelease(psBase, IMG_FALSE); +} + +/*! +****************************************************************************** + + @Function PVRSRVSetMaxHandle + + @Description Set maximum handle number for given handle base + + @Input psBase - pointer to handle base structure + ui32MaxHandle - Maximum handle number + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVSetMaxHandle(PVRSRV_HANDLE_BASE *psBase, IMG_UINT32 ui32MaxHandle) +{ + IMG_UINT32 ui32MaxHandleRounded; + + if (HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSetMaxHandle: Limit cannot be set whilst in batch mode")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* Validate the limit */ + if (ui32MaxHandle == 0 || ui32MaxHandle > DEFAULT_MAX_HANDLE) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSetMaxHandle: Limit must be between %u and %u, inclusive", 0, DEFAULT_MAX_HANDLE)); + + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* The limit can only be set if no handles have been allocated */ + if (psBase->ui32TotalHandCount != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSetMaxHandle: Limit cannot be set because handles have already been allocated")); + + return PVRSRV_ERROR_INVALID_PARAMS; + } + + ui32MaxHandleRounded = ROUND_DOWN_TO_MULTIPLE_OF_BLOCK_SIZE(ui32MaxHandle); + + /* + * Allow the maximum number of handles to be reduced, but never to + * zero. + */ + if (ui32MaxHandleRounded != 0 && ui32MaxHandleRounded < psBase->ui32MaxIndexPlusOne) + { + psBase->ui32MaxIndexPlusOne = ui32MaxHandleRounded; + } + + PVR_ASSERT(psBase->ui32MaxIndexPlusOne != 0); + PVR_ASSERT(psBase->ui32MaxIndexPlusOne <= DEFAULT_MAX_INDEX_PLUS_ONE); + PVR_ASSERT((psBase->ui32MaxIndexPlusOne % HANDLE_BLOCK_SIZE) == 0); + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVGetMaxHandle + + @Description Get maximum handle number for given handle base + + @Input psBase - pointer to handle base structure + + @Output Maximum handle number, or 0 if handle limits not + supported. + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +IMG_UINT32 PVRSRVGetMaxHandle(PVRSRV_HANDLE_BASE *psBase) +{ + return psBase->ui32MaxIndexPlusOne; +} + +/*! +****************************************************************************** + + @Function PVRSRVEnableHandlePurging + + @Description Enable purging for a given handle base + + @Input psBase - pointer to handle base structure + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVEnableHandlePurging(PVRSRV_HANDLE_BASE *psBase) +{ + if (psBase->bPurgingEnabled) + { + PVR_DPF((PVR_DBG_WARNING, "PVRSRVEnableHandlePurging: Purging already enabled")); + return PVRSRV_OK; + } + + /* Purging can only be enabled if no handles have been allocated */ + if (psBase->ui32TotalHandCount != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVEnableHandlePurging: Handles have already been allocated")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psBase->bPurgingEnabled = IMG_TRUE; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVPurgeHandles + + @Description Purge handles for a given handle base + + @Input psBase - pointer to handle base structure + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVPurgeHandles(PVRSRV_HANDLE_BASE *psBase) +{ + IMG_UINT32 ui32BlockIndex; + IMG_UINT32 ui32NewHandCount; + + if (!psBase->bPurgingEnabled) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPurgeHandles: Purging not enabled for this handle base")); + return PVRSRV_ERROR_NOT_SUPPORTED; + } + + if (HANDLES_BATCHED(psBase)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPurgeHandles: Purging not allowed whilst in batch mode")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + PVR_ASSERT((psBase->ui32TotalHandCount % HANDLE_BLOCK_SIZE) == 0); + + for (ui32BlockIndex = INDEX_TO_BLOCK_INDEX(psBase->ui32TotalHandCount); ui32BlockIndex != 0; ui32BlockIndex--) + { + if (psBase->psHandleArray[ui32BlockIndex - 1].ui32FreeHandBlockCount != HANDLE_BLOCK_SIZE) + { + break; + } + } + ui32NewHandCount = BLOCK_INDEX_TO_INDEX(ui32BlockIndex); + + /* + * Check for a suitable decrease in the handle count. + */ + if (ui32NewHandCount <= (psBase->ui32TotalHandCount/2)) + { + PVRSRV_ERROR eError; + + // PVR_TRACE((" PVRSRVPurgeHandles: reducing number of handles from %u to %u", psBase->ui32TotalHandCount, ui32NewHandCount)); + + eError = ReallocHandleArray(psBase, ui32NewHandCount); + if (eError != PVRSRV_OK) + { + return eError; + } + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVAllocHandleBase + + @Description Allocate a handle base structure for a process + + @Input ppsBase - pointer to handle base structure pointer + + @Output ppsBase - points to handle base structure pointer + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVAllocHandleBase(PVRSRV_HANDLE_BASE **ppsBase) +{ + PVRSRV_HANDLE_BASE *psBase; + IMG_HANDLE hBlockAlloc; + PVRSRV_ERROR eError; + + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(*psBase), + (IMG_PVOID *)&psBase, + &hBlockAlloc, + "Handle Base"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandleBase: Couldn't allocate handle base (%d)", eError)); + return eError; + } + OSMemSet(psBase, 0, sizeof(*psBase)); + + /* Create hash table */ + psBase->psHashTab = HASH_Create_Extended(HANDLE_HASH_TAB_INIT_SIZE, sizeof(HAND_KEY), HASH_Func_Default, HASH_Key_Comp_Default); + if (psBase->psHashTab == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandleBase: Couldn't create data pointer hash table\n")); + (IMG_VOID)PVRSRVFreeHandleBase(psBase); + return PVRSRV_ERROR_UNABLE_TO_CREATE_HASH_TABLE; + } + + psBase->hBaseBlockAlloc = hBlockAlloc; + + psBase->ui32MaxIndexPlusOne = DEFAULT_MAX_INDEX_PLUS_ONE; + + *ppsBase = psBase; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVFreeHandleBase + + @Description Free a handle base structure + + @Input psBase - pointer to handle base structure + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVFreeHandleBase(PVRSRV_HANDLE_BASE *psBase) +{ + PVRSRV_ERROR eError; + + PVR_ASSERT(psBase != gpsKernelHandleBase); + + eError = FreeHandleBase(psBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVFreeHandleBase: FreeHandleBase failed (%d)", eError)); + } + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVHandleInit + + @Description Initialise handle management + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVHandleInit(IMG_VOID) +{ + PVRSRV_ERROR eError; + + PVR_ASSERT(gpsKernelHandleBase == IMG_NULL); + + eError = PVRSRVAllocHandleBase(&gpsKernelHandleBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleInit: PVRSRVAllocHandleBase failed (%d)", eError)); + goto error; + } + + eError = PVRSRVEnableHandlePurging(gpsKernelHandleBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleInit: PVRSRVEnableHandlePurging failed (%d)", eError)); + goto error; + } + + return PVRSRV_OK; +error: + (IMG_VOID) PVRSRVHandleDeInit(); + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVHandleDeInit + + @Description De-initialise handle management + + @Return Error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVHandleDeInit(IMG_VOID) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if (gpsKernelHandleBase != IMG_NULL) + { + eError = FreeHandleBase(gpsKernelHandleBase); + if (eError == PVRSRV_OK) + { + gpsKernelHandleBase = IMG_NULL; + } + else + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVHandleDeInit: FreeHandleBase failed (%d)", eError)); + } + } + + return eError; +} +#else +/* disable warning about empty module */ +#endif /* #if defined(PVR_SECURE_HANDLES) || defined (SUPPORT_SID_INTERFACE) */ +/****************************************************************************** + End of file (handle.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/hash.c b/pvr-source/services4/srvkm/common/hash.c new file mode 100644 index 0000000..1569425 --- /dev/null +++ b/pvr-source/services4/srvkm/common/hash.c @@ -0,0 +1,738 @@ +/*************************************************************************/ /*! +@Title Self scaling hash tables. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description + Implements simple self scaling hash tables. Hash collisions are + handled by chaining entries together. Hash tables are increased in + size when they become more than (50%?) full and decreased in size + when less than (25%?) full. Hash tables are never decreased below + their initial size. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "pvr_debug.h" +#include "img_defs.h" +#include "services.h" +#include "servicesint.h" +#include "hash.h" +#include "osfunc.h" + +#define PRIVATE_MAX(a,b) ((a)>(b)?(a):(b)) + +#define KEY_TO_INDEX(pHash, key, uSize) \ + ((pHash)->pfnHashFunc((pHash)->uKeySize, (key), (uSize)) % (uSize)) + +#define KEY_COMPARE(pHash, pKey1, pKey2) \ + ((pHash)->pfnKeyComp((pHash)->uKeySize, (pKey1), (pKey2))) + +/* Each entry in a hash table is placed into a bucket */ +struct _BUCKET_ +{ + /* the next bucket on the same chain */ + struct _BUCKET_ *pNext; + + /* entry value */ + IMG_UINTPTR_T v; + + /* entry key */ + IMG_UINTPTR_T k[]; /* PRQA S 0642 */ /* override dynamic array declaration warning */ +}; +typedef struct _BUCKET_ BUCKET; + +struct _HASH_TABLE_ +{ + /* the hash table array */ + BUCKET **ppBucketTable; + + /* current size of the hash table */ + IMG_UINT32 uSize; + + /* number of entries currently in the hash table */ + IMG_UINT32 uCount; + + /* the minimum size that the hash table should be re-sized to */ + IMG_UINT32 uMinimumSize; + + /* size of key in bytes */ + IMG_UINT32 uKeySize; + + /* hash function */ + HASH_FUNC *pfnHashFunc; + + /* key comparison function */ + HASH_KEY_COMP *pfnKeyComp; +}; + +/*! +****************************************************************************** + @Function HASH_Func_Default + + @Description Hash function intended for hashing keys composed of + IMG_UINTPTR_T arrays. + + @Input uKeySize - the size of the hash key, in bytes. + @Input pKey - a pointer to the key to hash. + @Input uHashTabLen - the length of the hash table. + + @Return the hash value. +******************************************************************************/ +IMG_UINT32 +HASH_Func_Default (IMG_SIZE_T uKeySize, IMG_VOID *pKey, IMG_UINT32 uHashTabLen) +{ + IMG_UINTPTR_T *p = (IMG_UINTPTR_T *)pKey; + IMG_UINT32 uKeyLen = (IMG_UINT32)(uKeySize / sizeof(IMG_UINTPTR_T)); + IMG_UINT32 ui; + IMG_UINT32 uHashKey = 0; + + PVR_UNREFERENCED_PARAMETER(uHashTabLen); + + PVR_ASSERT((uKeySize % sizeof(IMG_UINTPTR_T)) == 0); + + for (ui = 0; ui < uKeyLen; ui++) + { + IMG_UINT32 uHashPart = (IMG_UINT32)*p++; + + uHashPart += (uHashPart << 12); + uHashPart ^= (uHashPart >> 22); + uHashPart += (uHashPart << 4); + uHashPart ^= (uHashPart >> 9); + uHashPart += (uHashPart << 10); + uHashPart ^= (uHashPart >> 2); + uHashPart += (uHashPart << 7); + uHashPart ^= (uHashPart >> 12); + + uHashKey += uHashPart; + } + + return uHashKey; +} + +/*! +****************************************************************************** + @Function HASH_Key_Comp_Default + + @Description Compares keys composed of IMG_UINTPTR_T arrays. + + @Input uKeySize - the size of the hash key, in bytes. + @Input pKey1 - pointer to first hash key to compare. + @Input pKey2 - pointer to second hash key to compare. + @Return IMG_TRUE - the keys match. + IMG_FALSE - the keys don't match. +******************************************************************************/ +IMG_BOOL +HASH_Key_Comp_Default (IMG_SIZE_T uKeySize, IMG_VOID *pKey1, IMG_VOID *pKey2) +{ + IMG_UINTPTR_T *p1 = (IMG_UINTPTR_T *)pKey1; + IMG_UINTPTR_T *p2 = (IMG_UINTPTR_T *)pKey2; + IMG_UINT32 uKeyLen = (IMG_UINT32)(uKeySize / sizeof(IMG_UINTPTR_T)); + IMG_UINT32 ui; + + PVR_ASSERT((uKeySize % sizeof(IMG_UINTPTR_T)) == 0); + + for (ui = 0; ui < uKeyLen; ui++) + { + if (*p1++ != *p2++) + return IMG_FALSE; + } + + return IMG_TRUE; +} + +/*! +****************************************************************************** + @Function _ChainInsert + + @Description Insert a bucket into the appropriate hash table chain. + + @Input pBucket - the bucket + @Input ppBucketTable - the hash table + @Input uSize - the size of the hash table + + @Return PVRSRV_ERROR +******************************************************************************/ +static PVRSRV_ERROR +_ChainInsert (HASH_TABLE *pHash, BUCKET *pBucket, BUCKET **ppBucketTable, IMG_UINT32 uSize) +{ + IMG_UINT32 uIndex; + + PVR_ASSERT (pBucket != IMG_NULL); + PVR_ASSERT (ppBucketTable != IMG_NULL); + PVR_ASSERT (uSize != 0); + + if ((pBucket == IMG_NULL) || (ppBucketTable == IMG_NULL) || (uSize == 0)) + { + PVR_DPF((PVR_DBG_ERROR, "_ChainInsert: invalid parameter")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + uIndex = KEY_TO_INDEX(pHash, pBucket->k, uSize); /* PRQA S 0432,0541 */ /* ignore dynamic array warning */ + pBucket->pNext = ppBucketTable[uIndex]; + ppBucketTable[uIndex] = pBucket; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + @Function _Rehash + + @Description Iterate over every entry in an old hash table and + rehash into the new table. + + @Input ppOldTable - the old hash table + @Input uOldSize - the size of the old hash table + @Input ppNewTable - the new hash table + @Input uNewSize - the size of the new hash table + + @Return None +******************************************************************************/ +static PVRSRV_ERROR +_Rehash (HASH_TABLE *pHash, + BUCKET **ppOldTable, IMG_UINT32 uOldSize, + BUCKET **ppNewTable, IMG_UINT32 uNewSize) +{ + IMG_UINT32 uIndex; + for (uIndex=0; uIndex< uOldSize; uIndex++) + { + BUCKET *pBucket; + pBucket = ppOldTable[uIndex]; + while (pBucket != IMG_NULL) + { + PVRSRV_ERROR eError; + BUCKET *pNextBucket = pBucket->pNext; + eError = _ChainInsert (pHash, pBucket, ppNewTable, uNewSize); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "_Rehash: call to _ChainInsert failed")); + return eError; + } + pBucket = pNextBucket; + } + } + return PVRSRV_OK; +} + +/*! +****************************************************************************** + @Function _Resize + + @Description Attempt to resize a hash table, failure to allocate a + new larger hash table is not considered a hard failure. + We simply continue and allow the table to fill up, the + effect is to allow hash chains to become longer. + + @Input pHash - Hash table to resize. + @Input uNewSize - Required table size. + @Return IMG_TRUE Success + IMG_FALSE Failed +******************************************************************************/ +static IMG_BOOL +_Resize (HASH_TABLE *pHash, IMG_UINT32 uNewSize) +{ + if (uNewSize != pHash->uSize) + { + BUCKET **ppNewTable; + IMG_UINT32 uIndex; + + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Resize: oldsize=0x%x newsize=0x%x count=0x%x", + pHash->uSize, uNewSize, pHash->uCount)); + + OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof (BUCKET *) * uNewSize, + (IMG_PVOID*)&ppNewTable, IMG_NULL, + "Hash Table Buckets"); + if (ppNewTable == IMG_NULL) + return IMG_FALSE; + + for (uIndex=0; uIndex<uNewSize; uIndex++) + ppNewTable[uIndex] = IMG_NULL; + + if (_Rehash (pHash, pHash->ppBucketTable, pHash->uSize, ppNewTable, uNewSize) != PVRSRV_OK) + { + return IMG_FALSE; + } + + OSFreeMem (PVRSRV_PAGEABLE_SELECT, sizeof(BUCKET *)*pHash->uSize, pHash->ppBucketTable, IMG_NULL); + /*not nulling pointer, being reassigned just below*/ + pHash->ppBucketTable = ppNewTable; + pHash->uSize = uNewSize; + } + return IMG_TRUE; +} + + +/*! +****************************************************************************** + @Function HASH_Create_Extended + + @Description Create a self scaling hash table, using the supplied + key size, and the supplied hash and key comparsion + functions. + + @Input uInitialLen - initial and minimum length of the + hash table, where the length refers to the number + of entries in the hash table, not its size in + bytes. + @Input uKeySize - the size of the key, in bytes. + @Input pfnHashFunc - pointer to hash function. + @Input pfnKeyComp - pointer to key comparsion function. + @Return IMG_NULL or hash table handle. +******************************************************************************/ +HASH_TABLE * HASH_Create_Extended (IMG_UINT32 uInitialLen, IMG_SIZE_T uKeySize, HASH_FUNC *pfnHashFunc, HASH_KEY_COMP *pfnKeyComp) +{ + HASH_TABLE *pHash; + IMG_UINT32 uIndex; + + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Create_Extended: InitialSize=0x%x", uInitialLen)); + + if(OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(HASH_TABLE), + (IMG_VOID **)&pHash, IMG_NULL, + "Hash Table") != PVRSRV_OK) + { + return IMG_NULL; + } + + pHash->uCount = 0; + pHash->uSize = uInitialLen; + pHash->uMinimumSize = uInitialLen; + pHash->uKeySize = (IMG_UINT32)uKeySize; + pHash->pfnHashFunc = pfnHashFunc; + pHash->pfnKeyComp = pfnKeyComp; + + OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof (BUCKET *) * pHash->uSize, + (IMG_PVOID*)&pHash->ppBucketTable, IMG_NULL, + "Hash Table Buckets"); + + if (pHash->ppBucketTable == IMG_NULL) + { + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(HASH_TABLE), pHash, IMG_NULL); + /*not nulling pointer, out of scope*/ + return IMG_NULL; + } + + for (uIndex=0; uIndex<pHash->uSize; uIndex++) + pHash->ppBucketTable[uIndex] = IMG_NULL; + return pHash; +} + +/*! +****************************************************************************** + @Function HASH_Create + + @Description Create a self scaling hash table with a key + consisting of a single IMG_UINTPTR_T, and using + the default hash and key comparison functions. + + @Input uInitialLen - initial and minimum length of the + hash table, where the length refers to the + number of entries in the hash table, not its size + in bytes. + @Return IMG_NULL or hash table handle. +******************************************************************************/ +HASH_TABLE * HASH_Create (IMG_UINT32 uInitialLen) +{ + return HASH_Create_Extended(uInitialLen, sizeof(IMG_UINTPTR_T), + &HASH_Func_Default, &HASH_Key_Comp_Default); +} + +/*! +****************************************************************************** + @Function HASH_Delete + + @Description Delete a hash table created by HASH_Create_Extended or + HASH_Create. All entries in the table must have been + removed before calling this function. + + @Input pHash - hash table + + @Return None +******************************************************************************/ +IMG_VOID +HASH_Delete (HASH_TABLE *pHash) +{ + if (pHash != IMG_NULL) + { + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Delete")); + + PVR_ASSERT (pHash->uCount==0); + if(pHash->uCount != 0) + { + PVR_DPF ((PVR_DBG_ERROR, "HASH_Delete: leak detected in hash table!")); + PVR_DPF ((PVR_DBG_ERROR, "Likely Cause: client drivers not freeing alocations before destroying devmemcontext")); + } + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(BUCKET *)*pHash->uSize, pHash->ppBucketTable, IMG_NULL); + pHash->ppBucketTable = IMG_NULL; + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(HASH_TABLE), pHash, IMG_NULL); + /*not nulling pointer, copy on stack*/ + } +} + +/*! +****************************************************************************** + @Function HASH_Insert_Extended + + @Description Insert a key value pair into a hash table created + with HASH_Create_Extended. + + @Input pHash - the hash table. + @Input pKey - pointer to the key. + @Input v - the value associated with the key. + + @Return IMG_TRUE - success + IMG_FALSE - failure +******************************************************************************/ +IMG_BOOL +HASH_Insert_Extended (HASH_TABLE *pHash, IMG_VOID *pKey, IMG_UINTPTR_T v) +{ + BUCKET *pBucket; + + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Insert_Extended: Hash=0x%08x, pKey=0x%08x, v=0x%x", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey, v)); + + PVR_ASSERT (pHash != IMG_NULL); + + if (pHash == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "HASH_Insert_Extended: invalid parameter")); + return IMG_FALSE; + } + + if(OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(BUCKET) + pHash->uKeySize, + (IMG_VOID **)&pBucket, IMG_NULL, + "Hash Table entry") != PVRSRV_OK) + { + return IMG_FALSE; + } + + pBucket->v = v; + /* PRQA S 0432,0541 1 */ /* ignore warning about dynamic array k (linux)*/ + OSMemCopy(pBucket->k, pKey, pHash->uKeySize); + if (_ChainInsert (pHash, pBucket, pHash->ppBucketTable, pHash->uSize) != PVRSRV_OK) + { + OSFreeMem(PVRSRV_PAGEABLE_SELECT, + sizeof(BUCKET) + pHash->uKeySize, + pBucket, IMG_NULL); + return IMG_FALSE; + } + + pHash->uCount++; + + /* check if we need to think about re-balencing */ + if (pHash->uCount << 1 > pHash->uSize) + { + /* Ignore the return code from _Resize because the hash table is + still in a valid state and although not ideally sized, it is still + functional */ + _Resize (pHash, pHash->uSize << 1); + } + + + return IMG_TRUE; +} + +/*! +****************************************************************************** + @Function HASH_Insert + + @Description Insert a key value pair into a hash table created with + HASH_Create. + + @Input pHash - the hash table. + @Input k - the key value. + @Input v - the value associated with the key. + + @Return IMG_TRUE - success. + IMG_FALSE - failure. +******************************************************************************/ +IMG_BOOL +HASH_Insert (HASH_TABLE *pHash, IMG_UINTPTR_T k, IMG_UINTPTR_T v) +{ + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Insert: Hash=0x%x, k=0x%x, v=0x%x", + (IMG_UINTPTR_T)pHash, k, v)); + + return HASH_Insert_Extended(pHash, &k, v); +} + +/*! +****************************************************************************** + @Function HASH_Remove_Extended + + @Description Remove a key from a hash table created with + HASH_Create_Extended. + + @Input pHash - the hash table. + @Input pKey - pointer to key. + + @Return 0 if the key is missing, or the value associated + with the key. +******************************************************************************/ +IMG_UINTPTR_T +HASH_Remove_Extended(HASH_TABLE *pHash, IMG_VOID *pKey) +{ + BUCKET **ppBucket; + IMG_UINT32 uIndex; + + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Remove_Extended: Hash=0x%x, pKey=0x%x", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey)); + + PVR_ASSERT (pHash != IMG_NULL); + + if (pHash == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "HASH_Remove_Extended: Null hash table")); + return 0; + } + + uIndex = KEY_TO_INDEX(pHash, pKey, pHash->uSize); + + for (ppBucket = &(pHash->ppBucketTable[uIndex]); *ppBucket != IMG_NULL; ppBucket = &((*ppBucket)->pNext)) + { + /* PRQA S 0432,0541 1 */ /* ignore warning about dynamic array k */ + if (KEY_COMPARE(pHash, (*ppBucket)->k, pKey)) + { + BUCKET *pBucket = *ppBucket; + IMG_UINTPTR_T v = pBucket->v; + (*ppBucket) = pBucket->pNext; + + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(BUCKET) + pHash->uKeySize, pBucket, IMG_NULL); + /*not nulling original pointer, already overwritten*/ + + pHash->uCount--; + + /* check if we need to think about re-balencing */ + if (pHash->uSize > (pHash->uCount << 2) && + pHash->uSize > pHash->uMinimumSize) + { + /* Ignore the return code from _Resize because the + hash table is still in a valid state and although + not ideally sized, it is still functional */ + _Resize (pHash, + PRIVATE_MAX (pHash->uSize >> 1, + pHash->uMinimumSize)); + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Remove_Extended: Hash=0x%x, pKey=0x%x = 0x%x", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey, v)); + return v; + } + } + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Remove_Extended: Hash=0x%x, pKey=0x%x = 0x0 !!!!", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey)); + return 0; +} + +/*! +****************************************************************************** + @Function HASH_Remove + + @Description Remove a key value pair from a hash table created + with HASH_Create. + + @Input pHash - the hash table + @Input k - the key + + @Return 0 if the key is missing, or the value associated + with the key. +******************************************************************************/ +IMG_UINTPTR_T +HASH_Remove (HASH_TABLE *pHash, IMG_UINTPTR_T k) +{ + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Remove: Hash=0x%x, k=0x%x", + (IMG_UINTPTR_T)pHash, k)); + + return HASH_Remove_Extended(pHash, &k); +} + +/*! +****************************************************************************** + @Function HASH_Retrieve_Extended + + @Description Retrieve a value from a hash table created with + HASH_Create_Extended. + + @Input pHash - the hash table. + @Input pKey - pointer to the key. + + @Return 0 if the key is missing, or the value associated with + the key. +******************************************************************************/ +IMG_UINTPTR_T +HASH_Retrieve_Extended (HASH_TABLE *pHash, IMG_VOID *pKey) +{ + BUCKET **ppBucket; + IMG_UINT32 uIndex; + + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Retrieve_Extended: Hash=0x%x, pKey=0x%x", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey)); + + PVR_ASSERT (pHash != IMG_NULL); + + if (pHash == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "HASH_Retrieve_Extended: Null hash table")); + return 0; + } + + uIndex = KEY_TO_INDEX(pHash, pKey, pHash->uSize); + + for (ppBucket = &(pHash->ppBucketTable[uIndex]); *ppBucket != IMG_NULL; ppBucket = &((*ppBucket)->pNext)) + { + /* PRQA S 0432,0541 1 */ /* ignore warning about dynamic array k */ + if (KEY_COMPARE(pHash, (*ppBucket)->k, pKey)) + { + BUCKET *pBucket = *ppBucket; + IMG_UINTPTR_T v = pBucket->v; + + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Retrieve: Hash=0x%x, pKey=0x%x = 0x%x", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey, v)); + return v; + } + } + PVR_DPF ((PVR_DBG_MESSAGE, + "HASH_Retrieve: Hash=0x%x, pKey=0x%x = 0x0 !!!!", + (IMG_UINTPTR_T)pHash, (IMG_UINTPTR_T)pKey)); + return 0; +} + +/*! +****************************************************************************** + @Function HASH_Retrieve + + @Description Retrieve a value from a hash table created with + HASH_Create. + + @Input pHash - the hash table + @Input k - the key + @Return 0 if the key is missing, or the value associated with + the key. +******************************************************************************/ +IMG_UINTPTR_T +HASH_Retrieve (HASH_TABLE *pHash, IMG_UINTPTR_T k) +{ + PVR_DPF ((PVR_DBG_MESSAGE, "HASH_Retrieve: Hash=0x%x, k=0x%x", + (IMG_UINTPTR_T)pHash, k)); + return HASH_Retrieve_Extended(pHash, &k); +} + +/*! +****************************************************************************** + @Function HASH_Iterate + + @Description Iterate over every entry in the hash table + + @Input pHash - the old hash table + @Input pfnCallback - the size of the old hash table + + @Return Callback error if any, otherwise PVRSRV_OK +******************************************************************************/ +PVRSRV_ERROR +HASH_Iterate(HASH_TABLE *pHash, HASH_pfnCallback pfnCallback) +{ + IMG_UINT32 uIndex; + for (uIndex=0; uIndex < pHash->uSize; uIndex++) + { + BUCKET *pBucket; + pBucket = pHash->ppBucketTable[uIndex]; + while (pBucket != IMG_NULL) + { + PVRSRV_ERROR eError; + BUCKET *pNextBucket = pBucket->pNext; + + eError = pfnCallback((IMG_UINTPTR_T) ((IMG_VOID *) *(pBucket->k)), (IMG_UINTPTR_T) pBucket->v); + + /* The callback might want us to break out early */ + if (eError != PVRSRV_OK) + return eError; + + pBucket = pNextBucket; + } + } + return PVRSRV_OK; +} + +#ifdef HASH_TRACE +/*! +****************************************************************************** + @Function HASH_Dump + + @Description To dump the contents of a hash table in human readable + form. + + @Input pHash - the hash table + + @Return None +******************************************************************************/ +IMG_VOID +HASH_Dump (HASH_TABLE *pHash) +{ + IMG_UINT32 uIndex; + IMG_UINT32 uMaxLength=0; + IMG_UINT32 uEmptyCount=0; + + PVR_ASSERT (pHash != IMG_NULL); + for (uIndex=0; uIndex<pHash->uSize; uIndex++) + { + BUCKET *pBucket; + IMG_UINT32 uLength = 0; + if (pHash->ppBucketTable[uIndex] == IMG_NULL) + { + uEmptyCount++; + } + for (pBucket=pHash->ppBucketTable[uIndex]; + pBucket != IMG_NULL; + pBucket = pBucket->pNext) + { + uLength++; + } + uMaxLength = PRIVATE_MAX (uMaxLength, uLength); + } + + PVR_TRACE(("hash table: uMinimumSize=%d size=%d count=%d", + pHash->uMinimumSize, pHash->uSize, pHash->uCount)); + PVR_TRACE((" empty=%d max=%d", uEmptyCount, uMaxLength)); +} +#endif diff --git a/pvr-source/services4/srvkm/common/lists.c b/pvr-source/services4/srvkm/common/lists.c new file mode 100644 index 0000000..c6e1ee8 --- /dev/null +++ b/pvr-source/services4/srvkm/common/lists.c @@ -0,0 +1,156 @@ +/*************************************************************************/ /*! +@Title Linked list shared functions implementation +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Implementation of the list iterators for types shared among + more than one file in the services code. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#include "lists.h" +#include "services_headers.h" + +/*=================================================================== + LIST ITERATOR FUNCTIONS USED IN MORE THAN ONE FILE (those used just + once are implemented locally). + ===================================================================*/ + +IMPLEMENT_LIST_ANY_VA(BM_HEAP) +IMPLEMENT_LIST_ANY_2(BM_HEAP, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_ANY_VA_2(BM_HEAP, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_FOR_EACH_VA(BM_HEAP) +IMPLEMENT_LIST_REMOVE(BM_HEAP) +IMPLEMENT_LIST_INSERT(BM_HEAP) + +IMPLEMENT_LIST_ANY_VA(BM_CONTEXT) +IMPLEMENT_LIST_ANY_VA_2(BM_CONTEXT, IMG_HANDLE, IMG_NULL) +IMPLEMENT_LIST_ANY_VA_2(BM_CONTEXT, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_FOR_EACH(BM_CONTEXT) +IMPLEMENT_LIST_REMOVE(BM_CONTEXT) +IMPLEMENT_LIST_INSERT(BM_CONTEXT) + +IMPLEMENT_LIST_ANY_2(PVRSRV_DEVICE_NODE, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_ANY_VA(PVRSRV_DEVICE_NODE) +IMPLEMENT_LIST_ANY_VA_2(PVRSRV_DEVICE_NODE, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_FOR_EACH(PVRSRV_DEVICE_NODE) +IMPLEMENT_LIST_FOR_EACH_VA(PVRSRV_DEVICE_NODE) +IMPLEMENT_LIST_INSERT(PVRSRV_DEVICE_NODE) +IMPLEMENT_LIST_REMOVE(PVRSRV_DEVICE_NODE) + +IMPLEMENT_LIST_ANY_VA(PVRSRV_POWER_DEV) +IMPLEMENT_LIST_ANY_VA_2(PVRSRV_POWER_DEV, PVRSRV_ERROR, PVRSRV_OK) +IMPLEMENT_LIST_INSERT(PVRSRV_POWER_DEV) +IMPLEMENT_LIST_REMOVE(PVRSRV_POWER_DEV) + + +/*=================================================================== + BELOW ARE IMPLEMENTED SOME COMMON CALLBACKS USED IN DIFFERENT FILES + ===================================================================*/ + + +/*! +****************************************************************************** + @Function MatchDeviceKM_AnyVaCb + @Description Matchs a device node with an id and optionally a class. + + @Input psDeviceNode - Pointer to the device node. + @Input va - Variable argument list, with te following values: + # ui32DevIndex - Index of de device to match. + # bIgnoreClass - Flag indicating if there's + no need to check the device class. + # eDevClass - Device class, ONLY present if + bIgnoreClass was IMG_FALSE. + + @Return The pointer to the device node if it matchs, IMG_NULL + otherwise. +******************************************************************************/ +IMG_VOID* MatchDeviceKM_AnyVaCb(PVRSRV_DEVICE_NODE* psDeviceNode, va_list va) +{ + IMG_UINT32 ui32DevIndex; + IMG_BOOL bIgnoreClass; + PVRSRV_DEVICE_CLASS eDevClass; + + ui32DevIndex = va_arg(va, IMG_UINT32); + bIgnoreClass = va_arg(va, IMG_BOOL); + if (!bIgnoreClass) + { + eDevClass = va_arg(va, PVRSRV_DEVICE_CLASS); + } + else + { + /*this value will never be used, since the short circuit evaluation + of the first clause will stop because bIgnoreClass is true, but the + compiler complains if it's not initialized.*/ + eDevClass = PVRSRV_DEVICE_CLASS_FORCE_I32; + } + + if ((bIgnoreClass || psDeviceNode->sDevId.eDeviceClass == eDevClass) && + psDeviceNode->sDevId.ui32DeviceIndex == ui32DevIndex) + { + return psDeviceNode; + } + return IMG_NULL; +} + +/*! +****************************************************************************** + + @Function MatchPowerDeviceIndex_AnyVaCb + + @Description + Matches a power device with its device index. + + @Input va : variable argument list with: + ui32DeviceIndex : device index + + @Return the pointer to the device it matched, IMG_NULL otherwise. + +******************************************************************************/ +IMG_VOID* MatchPowerDeviceIndex_AnyVaCb(PVRSRV_POWER_DEV *psPowerDev, va_list va) +{ + IMG_UINT32 ui32DeviceIndex; + + ui32DeviceIndex = va_arg(va, IMG_UINT32); + + if (psPowerDev->ui32DeviceIndex == ui32DeviceIndex) + { + return psPowerDev; + } + else + { + return IMG_NULL; + } +} diff --git a/pvr-source/services4/srvkm/common/mem.c b/pvr-source/services4/srvkm/common/mem.c new file mode 100644 index 0000000..cccdd24 --- /dev/null +++ b/pvr-source/services4/srvkm/common/mem.c @@ -0,0 +1,175 @@ +/*************************************************************************/ /*! +@Title System memory functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description System memory allocation APIs +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "pvr_bridge_km.h" + + +static PVRSRV_ERROR +FreeSharedSysMemCallBack(IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bDummy) +{ + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo = pvParam; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bDummy); + + OSFreePages(psKernelMemInfo->ui32Flags, + psKernelMemInfo->uAllocSize, + psKernelMemInfo->pvLinAddrKM, + psKernelMemInfo->sMemBlk.hOSMemHandle); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_MEM_INFO), + psKernelMemInfo, + IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +IMG_EXPORT PVRSRV_ERROR +PVRSRVAllocSharedSysMemoryKM(PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_UINT32 ui32Flags, + IMG_SIZE_T uSize, + PVRSRV_KERNEL_MEM_INFO **ppsKernelMemInfo) +{ + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_MEM_INFO), + (IMG_VOID **)&psKernelMemInfo, IMG_NULL, + "Kernel Memory Info") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSharedSysMemoryKM: Failed to alloc memory for meminfo")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + OSMemSet(psKernelMemInfo, 0, sizeof(*psKernelMemInfo)); + + ui32Flags &= ~PVRSRV_HAP_MAPTYPE_MASK; + ui32Flags |= PVRSRV_HAP_MULTI_PROCESS; + psKernelMemInfo->ui32Flags = ui32Flags; + psKernelMemInfo->uAllocSize = uSize; + + if(OSAllocPages(psKernelMemInfo->ui32Flags, + psKernelMemInfo->uAllocSize, + (IMG_UINT32)HOST_PAGESIZE(), + IMG_NULL, + 0, + IMG_NULL, + &psKernelMemInfo->pvLinAddrKM, + &psKernelMemInfo->sMemBlk.hOSMemHandle) + != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSharedSysMemoryKM: Failed to alloc memory for block")); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(PVRSRV_KERNEL_MEM_INFO), + psKernelMemInfo, + 0); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* register with the resman */ + psKernelMemInfo->sMemBlk.hResItem = + ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_SHARED_MEM_INFO, + psKernelMemInfo, + 0, + &FreeSharedSysMemCallBack); + + *ppsKernelMemInfo = psKernelMemInfo; + + return PVRSRV_OK; +} + + +IMG_EXPORT PVRSRV_ERROR +PVRSRVFreeSharedSysMemoryKM(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + PVRSRV_ERROR eError; + + if(psKernelMemInfo->sMemBlk.hResItem) + { + eError = ResManFreeResByPtr(psKernelMemInfo->sMemBlk.hResItem, CLEANUP_WITH_POLL); + } + else + { + eError = FreeSharedSysMemCallBack(psKernelMemInfo, 0, CLEANUP_WITH_POLL); + } + + return eError; +} + + +IMG_EXPORT PVRSRV_ERROR +PVRSRVDissociateMemFromResmanKM(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if(!psKernelMemInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if(psKernelMemInfo->sMemBlk.hResItem) + { + eError = ResManDissociateRes(psKernelMemInfo->sMemBlk.hResItem, IMG_NULL); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDissociateMemFromResmanKM: ResManDissociateRes failed")); + PVR_DBG_BREAK; + return eError; + } + + psKernelMemInfo->sMemBlk.hResItem = IMG_NULL; + } + + return eError; +} + +/****************************************************************************** + End of file (mem.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/mem_debug.c b/pvr-source/services4/srvkm/common/mem_debug.c new file mode 100644 index 0000000..04432b1 --- /dev/null +++ b/pvr-source/services4/srvkm/common/mem_debug.c @@ -0,0 +1,272 @@ +/*************************************************************************/ /*! +@Title Memory debugging routines. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Adds extra memory to the allocations to trace the memory bounds + and other runtime information. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#ifndef MEM_DEBUG_C +#define MEM_DEBUG_C + +#if defined(PVRSRV_DEBUG_OS_MEMORY) + +#include "img_types.h" +#include "services_headers.h" + +#if defined (__cplusplus) +extern "C" +{ +#endif + +#define STOP_ON_ERROR 0 + + /* + Allocated Memory Layout: + + --------- \ + Status [OSMEM_DEBUG_INFO] |- TEST_BUFFER_PADDING_STATUS + --------- < + [0xBB]* [raw bytes] |- ui32Size + --------- < + [0xB2]* [raw bytes] |- TEST_BUFFER_PADDING_AFTER + --------- / + */ + + IMG_BOOL MemCheck(const IMG_PVOID pvAddr, const IMG_UINT8 ui8Pattern, IMG_SIZE_T uSize) + { + IMG_UINT8 *pui8Addr; + for (pui8Addr = (IMG_UINT8*)pvAddr; uSize > 0; uSize--, pui8Addr++) + { + if (*pui8Addr != ui8Pattern) + { + return IMG_FALSE; + } + } + return IMG_TRUE; + } + + /* + This function expects the pointer to the user data, not the debug data. + */ + IMG_VOID OSCheckMemDebug(IMG_PVOID pvCpuVAddr, IMG_SIZE_T uSize, const IMG_CHAR *pszFileName, const IMG_UINT32 uLine) + { + OSMEM_DEBUG_INFO const *psInfo = (OSMEM_DEBUG_INFO *)((IMG_UINT32)pvCpuVAddr - TEST_BUFFER_PADDING_STATUS); + + /* invalid pointer */ + if (pvCpuVAddr == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "Pointer 0x%X : null pointer" + " - referenced %s:%d - allocated %s:%d", + pvCpuVAddr, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + + /* align */ + if (((IMG_UINT32)pvCpuVAddr&3) != 0) + { + PVR_DPF((PVR_DBG_ERROR, "Pointer 0x%X : invalid alignment" + " - referenced %s:%d - allocated %s:%d", + pvCpuVAddr, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + + /*check guard region before*/ + if (!MemCheck((IMG_PVOID)psInfo->sGuardRegionBefore, 0xB1, sizeof(psInfo->sGuardRegionBefore))) + { + PVR_DPF((PVR_DBG_ERROR, "Pointer 0x%X : guard region before overwritten" + " - referenced %s:%d - allocated %s:%d", + pvCpuVAddr, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + + /*check size*/ + if (uSize != psInfo->uSize) + { + PVR_DPF((PVR_DBG_WARNING, "Pointer 0x%X : supplied size was different to stored size (0x%X != 0x%X)" + " - referenced %s:%d - allocated %s:%d", + pvCpuVAddr, uSize, psInfo->uSize, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + + /*check size parity*/ + if ((0x01234567 ^ psInfo->uSizeParityCheck) != psInfo->uSize) + { + PVR_DPF((PVR_DBG_WARNING, "Pointer 0x%X : stored size parity error (0x%X != 0x%X)" + " - referenced %s:%d - allocated %s:%d", + pvCpuVAddr, psInfo->uSize, 0x01234567 ^ psInfo->uSizeParityCheck, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + else + { + /*the stored size is ok, so we use it instead the supplied uSize*/ + uSize = psInfo->uSize; + } + + /*check padding after*/ + if (uSize) + { + if (!MemCheck((IMG_VOID*)((IMG_UINT32)pvCpuVAddr + uSize), 0xB2, TEST_BUFFER_PADDING_AFTER)) + { + PVR_DPF((PVR_DBG_ERROR, "Pointer 0x%X : guard region after overwritten" + " - referenced from %s:%d - allocated from %s:%d", + pvCpuVAddr, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + } + } + + /* allocated... */ + if (psInfo->eValid != isAllocated) + { + PVR_DPF((PVR_DBG_ERROR, "Pointer 0x%X : not allocated (freed? %d)" + " - referenced %s:%d - freed %s:%d", + pvCpuVAddr, psInfo->eValid == isFree, + pszFileName, uLine, + psInfo->sFileName, psInfo->uLineNo)); + while (STOP_ON_ERROR); + } + } + + IMG_VOID debug_strcpy(IMG_CHAR *pDest, const IMG_CHAR *pSrc) + { + IMG_SIZE_T i = 0; + + for (; i < 128; i++) /*changed to 128 to match the filename array size*/ + { + *pDest = *pSrc; + if (*pSrc == '\0') break; + pDest++; + pSrc++; + } + } + + PVRSRV_ERROR OSAllocMem_Debug_Wrapper(IMG_UINT32 ui32Flags, + IMG_UINT32 ui32Size, + IMG_PVOID *ppvCpuVAddr, + IMG_HANDLE *phBlockAlloc, + IMG_CHAR *pszFilename, + IMG_UINT32 ui32Line) + { + OSMEM_DEBUG_INFO *psInfo; + + PVRSRV_ERROR eError; + + eError = OSAllocMem_Debug_Linux_Memory_Allocations(ui32Flags, + ui32Size + TEST_BUFFER_PADDING, + ppvCpuVAddr, + phBlockAlloc, + pszFilename, + ui32Line); + + if (eError != PVRSRV_OK) + { + return eError; + } + + OSMemSet((IMG_CHAR *)(*ppvCpuVAddr) + TEST_BUFFER_PADDING_STATUS, 0xBB, ui32Size); + OSMemSet((IMG_CHAR *)(*ppvCpuVAddr) + ui32Size + TEST_BUFFER_PADDING_STATUS, 0xB2, TEST_BUFFER_PADDING_AFTER); + + /*fill the dbg info struct*/ + psInfo = (OSMEM_DEBUG_INFO *)(*ppvCpuVAddr); + + OSMemSet(psInfo->sGuardRegionBefore, 0xB1, sizeof(psInfo->sGuardRegionBefore)); + debug_strcpy(psInfo->sFileName, pszFilename); + psInfo->uLineNo = ui32Line; + psInfo->eValid = isAllocated; + psInfo->uSize = ui32Size; + psInfo->uSizeParityCheck = 0x01234567 ^ ui32Size; + + /*point to the user data section*/ + *ppvCpuVAddr = (IMG_PVOID) ((IMG_UINT32)*ppvCpuVAddr)+TEST_BUFFER_PADDING_STATUS; + +#ifdef PVRSRV_LOG_MEMORY_ALLOCS + /*this is here to simplify the surounding logging macro, that is a expression + maybe the macro should be an expression */ + PVR_TRACE(("Allocated pointer (after debug info): 0x%X from %s:%d", *ppvCpuVAddr, pszFilename, ui32Line)); +#endif + + return PVRSRV_OK; + } + + PVRSRV_ERROR OSFreeMem_Debug_Wrapper(IMG_UINT32 ui32Flags, + IMG_UINT32 ui32Size, + IMG_PVOID pvCpuVAddr, + IMG_HANDLE hBlockAlloc, + IMG_CHAR *pszFilename, + IMG_UINT32 ui32Line) + { + OSMEM_DEBUG_INFO *psInfo; + + /*check dbginfo (arg pointing to user memory)*/ + OSCheckMemDebug(pvCpuVAddr, ui32Size, pszFilename, ui32Line); + + /*mark memory as freed*/ + OSMemSet(pvCpuVAddr, 0xBF, ui32Size + TEST_BUFFER_PADDING_AFTER); + + /*point to the starting address of the total allocated memory*/ + psInfo = (OSMEM_DEBUG_INFO *)((IMG_UINT32) pvCpuVAddr - TEST_BUFFER_PADDING_STATUS); + + /*update dbg info struct*/ + psInfo->uSize = 0; + psInfo->uSizeParityCheck = 0; + psInfo->eValid = isFree; + psInfo->uLineNo = ui32Line; + debug_strcpy(psInfo->sFileName, pszFilename); + + return OSFreeMem_Debug_Linux_Memory_Allocations(ui32Flags, ui32Size + TEST_BUFFER_PADDING, psInfo, hBlockAlloc, pszFilename, ui32Line); + } + +#if defined (__cplusplus) + +} +#endif + +#endif /* PVRSRV_DEBUG_OS_MEMORY */ + +#endif /* MEM_DEBUG_C */ diff --git a/pvr-source/services4/srvkm/common/metrics.c b/pvr-source/services4/srvkm/common/metrics.c new file mode 100644 index 0000000..7370ec1 --- /dev/null +++ b/pvr-source/services4/srvkm/common/metrics.c @@ -0,0 +1,209 @@ +/*************************************************************************/ /*! +@Title Time measuring functions. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "metrics.h" + +/* VGX: */ +#if defined(SUPPORT_VGX) +#include "vgxapi_km.h" +#endif + +/* SGX: */ +#if defined(SUPPORT_SGX) +#include "sgxapi_km.h" +#endif + +#if defined(DEBUG) || defined(TIMING) + +static volatile IMG_UINT32 *pui32TimerRegister = 0; + +#define PVRSRV_TIMER_TOTAL_IN_TICKS(X) asTimers[X].ui32Total +#define PVRSRV_TIMER_TOTAL_IN_MS(X) ((1000*asTimers[X].ui32Total)/ui32TicksPerMS) +#define PVRSRV_TIMER_COUNT(X) asTimers[X].ui32Count + + +Temporal_Data asTimers[PVRSRV_NUM_TIMERS]; + + +/*********************************************************************************** + Function Name : PVRSRVTimeNow + Inputs : None + Outputs : None + Returns : Current timer register value + Description : Returns the current timer register value +************************************************************************************/ +IMG_UINT32 PVRSRVTimeNow(IMG_VOID) +{ + if (!pui32TimerRegister) + { + static IMG_BOOL bFirstTime = IMG_TRUE; + + if (bFirstTime) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVTimeNow: No timer register set up")); + + bFirstTime = IMG_FALSE; + } + + return 0; + } + +#if defined(__sh__) + + return (0xffffffff-*pui32TimerRegister); + +#else /* defined(__sh__) */ + + return 0; + +#endif /* defined(__sh__) */ +} + + +/*********************************************************************************** + Function Name : PVRSRVGetCPUFreq + Inputs : None + Outputs : None + Returns : CPU timer frequency + Description : Returns the CPU timer frequency +************************************************************************************/ +static IMG_UINT32 PVRSRVGetCPUFreq(IMG_VOID) +{ + IMG_UINT32 ui32Time1, ui32Time2; + + ui32Time1 = PVRSRVTimeNow(); + + OSWaitus(1000000); + + ui32Time2 = PVRSRVTimeNow(); + + PVR_DPF((PVR_DBG_WARNING, "PVRSRVGetCPUFreq: timer frequency = %d Hz", ui32Time2 - ui32Time1)); + + return (ui32Time2 - ui32Time1); +} + + +/*********************************************************************************** + Function Name : PVRSRVSetupMetricTimers + Inputs : pvDevInfo + Outputs : None + Returns : None + Description : Resets metric timers and sets up the timer register +************************************************************************************/ +IMG_VOID PVRSRVSetupMetricTimers(IMG_VOID *pvDevInfo) +{ + IMG_UINT32 ui32Loop; + + PVR_UNREFERENCED_PARAMETER(pvDevInfo); + + for(ui32Loop=0; ui32Loop < (PVRSRV_NUM_TIMERS); ui32Loop++) + { + asTimers[ui32Loop].ui32Total = 0; + asTimers[ui32Loop].ui32Count = 0; + } + + #if defined(__sh__) + + /* timer control register */ + // clock / 1024 when TIMER_DIVISOR = 4 + // underflow int disabled + // we get approx 38 uS per timer tick + *TCR_2 = TIMER_DIVISOR; + + /* reset the timer counter to 0 */ + *TCOR_2 = *TCNT_2 = (IMG_UINT)0xffffffff; + + /* start timer 2 */ + *TST_REG |= (IMG_UINT8)0x04; + + pui32TimerRegister = (IMG_UINT32 *)TCNT_2; + + #else /* defined(__sh__) */ + + pui32TimerRegister = 0; + + #endif /* defined(__sh__) */ +} + + +/*********************************************************************************** + Function Name : PVRSRVOutputMetricTotals + Inputs : None + Outputs : None + Returns : None + Description : Displays final metric data +************************************************************************************/ +IMG_VOID PVRSRVOutputMetricTotals(IMG_VOID) +{ + IMG_UINT32 ui32TicksPerMS, ui32Loop; + + ui32TicksPerMS = PVRSRVGetCPUFreq(); + + if (!ui32TicksPerMS) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVOutputMetricTotals: Failed to get CPU Freq")); + return; + } + + for(ui32Loop=0; ui32Loop < (PVRSRV_NUM_TIMERS); ui32Loop++) + { + if (asTimers[ui32Loop].ui32Count & 0x80000000L) + { + PVR_DPF((PVR_DBG_WARNING,"PVRSRVOutputMetricTotals: Timer %u is still ON", ui32Loop)); + } + } +#if 0 + /* + ** EXAMPLE TIMER OUTPUT + */ + PVR_DPF((PVR_DBG_ERROR," Timer(%u): Total = %u",PVRSRV_TIMER_EXAMPLE_1, PVRSRV_TIMER_TOTAL_IN_TICKS(PVRSRV_TIMER_EXAMPLE_1))); + PVR_DPF((PVR_DBG_ERROR," Timer(%u): Time = %ums",PVRSRV_TIMER_EXAMPLE_1, PVRSRV_TIMER_TOTAL_IN_MS(PVRSRV_TIMER_EXAMPLE_1))); + PVR_DPF((PVR_DBG_ERROR," Timer(%u): Count = %u",PVRSRV_TIMER_EXAMPLE_1, PVRSRV_TIMER_COUNT(PVRSRV_TIMER_EXAMPLE_1))); +#endif +} + +#endif /* defined(DEBUG) || defined(TIMING) */ + +/****************************************************************************** + End of file (metrics.c) +******************************************************************************/ + diff --git a/pvr-source/services4/srvkm/common/osfunc_common.c b/pvr-source/services4/srvkm/common/osfunc_common.c new file mode 100644 index 0000000..19ba9ea --- /dev/null +++ b/pvr-source/services4/srvkm/common/osfunc_common.c @@ -0,0 +1,48 @@ +/*************************************************************************/ /*! +@Title Wrapper layer for osfunc routines that have common code. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Adds extra memory to the allocations to trace the memory bounds + and other runtime information. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "img_types.h" +#include "services_headers.h" +#include "osfunc.h" + + diff --git a/pvr-source/services4/srvkm/common/pdump_common.c b/pvr-source/services4/srvkm/common/pdump_common.c new file mode 100644 index 0000000..2d96dc3 --- /dev/null +++ b/pvr-source/services4/srvkm/common/pdump_common.c @@ -0,0 +1,2967 @@ +/*************************************************************************/ /*! +@Title Common PDump functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#if defined(PDUMP) +#include <stdarg.h> + +#include "services_headers.h" +#include "perproc.h" + +/* pdump headers */ +#include "pdump_km.h" +#include "pdump_int.h" + +/* Allow temporary buffer size override */ +#if !defined(PDUMP_TEMP_BUFFER_SIZE) +#define PDUMP_TEMP_BUFFER_SIZE (64 * 1024U) +#endif + +/* DEBUG */ +#if 1 +#define PDUMP_DBG(a) PDumpOSDebugPrintf (a) +#else +#define PDUMP_DBG(a) +#endif + + +#define PTR_PLUS(t, p, x) ((t)(((IMG_CHAR *)(p)) + (x))) +#define VPTR_PLUS(p, x) PTR_PLUS(IMG_VOID *, p, x) +#define VPTR_INC(p, x) ((p) = VPTR_PLUS(p, x)) +#define MAX_PDUMP_MMU_CONTEXTS (32) +static IMG_VOID *gpvTempBuffer = IMG_NULL; +static IMG_HANDLE ghTempBufferBlockAlloc; +static IMG_UINT16 gui16MMUContextUsage = 0; + +#if defined(PDUMP_DEBUG_OUTFILES) +/* counter increments each time debug write is called */ +IMG_UINT32 g_ui32EveryLineCounter = 1U; +#endif + +#ifdef INLINE_IS_PRAGMA +#pragma inline(_PDumpIsPersistent) +#endif +static INLINE +IMG_BOOL _PDumpIsPersistent(IMG_VOID) +{ + PVRSRV_PER_PROCESS_DATA* psPerProc = PVRSRVFindPerProcessData(); + + if(psPerProc == IMG_NULL) + { + /* only occurs early in driver init, and init phase is already persistent */ + return IMG_FALSE; + } + return psPerProc->bPDumpPersistent; +} + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + + +static INLINE +IMG_BOOL _PDumpIsProcessActive(IMG_VOID) +{ + PVRSRV_PER_PROCESS_DATA* psPerProc = PVRSRVFindPerProcessData(); + if(psPerProc == IMG_NULL) + { + /* FIXME: kernel process logs some comments when kernel module is + * loaded, want to keep those. + */ + return IMG_TRUE; + } + return psPerProc->bPDumpActive; +} + +#endif /* SUPPORT_PDUMP_MULTI_PROCESS */ + +#if defined(PDUMP_DEBUG_OUTFILES) +static INLINE +IMG_UINT32 _PDumpGetPID(IMG_VOID) +{ + PVRSRV_PER_PROCESS_DATA* psPerProc = PVRSRVFindPerProcessData(); + if(psPerProc == IMG_NULL) + { + /* Kernel PID */ + return 0; + } + return psPerProc->ui32PID; +} +#endif /* PDUMP_DEBUG_OUTFILES */ + +/************************************************************************** + * Function Name : GetTempBuffer + * Inputs : None + * Outputs : None + * Returns : Temporary buffer address, or IMG_NULL + * Description : Get temporary buffer address. +**************************************************************************/ +static IMG_VOID *GetTempBuffer(IMG_VOID) +{ + /* + * Allocate the temporary buffer, it it hasn't been allocated already. + * Return the address of the temporary buffer, or IMG_NULL if it + * couldn't be allocated. + * It is expected that the buffer will be allocated once, at driver + * load time, and left in place until the driver unloads. + */ + + if (gpvTempBuffer == IMG_NULL) + { + PVRSRV_ERROR eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + PDUMP_TEMP_BUFFER_SIZE, + &gpvTempBuffer, + &ghTempBufferBlockAlloc, + "PDUMP Temporary Buffer"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "GetTempBuffer: OSAllocMem failed: %d", eError)); + } + } + + return gpvTempBuffer; +} + +static IMG_VOID FreeTempBuffer(IMG_VOID) +{ + + if (gpvTempBuffer != IMG_NULL) + { + PVRSRV_ERROR eError = OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + PDUMP_TEMP_BUFFER_SIZE, + gpvTempBuffer, + ghTempBufferBlockAlloc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreeTempBuffer: OSFreeMem failed: %d", eError)); + } + else + { + gpvTempBuffer = IMG_NULL; + } + } +} + +IMG_VOID PDumpInitCommon(IMG_VOID) +{ + /* Allocate temporary buffer for copying from user space */ + (IMG_VOID) GetTempBuffer(); + + /* Call environment specific PDump initialisation */ + PDumpInit(); +} + +IMG_VOID PDumpDeInitCommon(IMG_VOID) +{ + /* Free temporary buffer */ + FreeTempBuffer(); + + /* Call environment specific PDump Deinitialisation */ + PDumpDeInit(); +} + +IMG_BOOL PDumpIsSuspended(IMG_VOID) +{ + return PDumpOSIsSuspended(); +} + +IMG_BOOL PDumpIsCaptureFrameKM(IMG_VOID) +{ +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + if( _PDumpIsProcessActive() ) + { + return PDumpOSIsCaptureFrameKM(); + } + return IMG_FALSE; +#else + return PDumpOSIsCaptureFrameKM(); +#endif +} + +PVRSRV_ERROR PDumpSetFrameKM(IMG_UINT32 ui32Frame) +{ +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + if( _PDumpIsProcessActive() ) + { + return PDumpOSSetFrameKM(ui32Frame); + } + return PVRSRV_OK; +#else + return PDumpOSSetFrameKM(ui32Frame); +#endif +} + +/************************************************************************** + * Function Name : PDumpRegWithFlagsKM + * Inputs : pszPDumpDevName, Register offset, and value to write + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Create a PDUMP string, which represents a register write +**************************************************************************/ +PVRSRV_ERROR PDumpRegWithFlagsKM(IMG_CHAR *pszPDumpRegName, + IMG_UINT32 ui32Reg, + IMG_UINT32 ui32Data, + IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING() + PDUMP_DBG(("PDumpRegWithFlagsKM")); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "WRW :%s:0x%08X 0x%08X\r\n", + pszPDumpRegName, ui32Reg, ui32Data); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpRegKM + * Inputs : Register offset, and value to write + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Create a PDUMP string, which represents a register write +**************************************************************************/ +PVRSRV_ERROR PDumpRegKM(IMG_CHAR *pszPDumpRegName, + IMG_UINT32 ui32Reg, + IMG_UINT32 ui32Data) +{ + return PDumpRegWithFlagsKM(pszPDumpRegName, ui32Reg, ui32Data, PDUMP_FLAGS_CONTINUOUS); +} + +/************************************************************************** + * Function Name : PDumpRegPolWithFlagsKM + * Inputs : Description of what this register read is trying to do + * pszPDumpDevName + * Register offset + * expected value + * mask for that value + * Outputs : None + * Returns : None + * Description : Create a PDUMP string which represents a register read + * with the expected value +**************************************************************************/ +PVRSRV_ERROR PDumpRegPolWithFlagsKM(IMG_CHAR *pszPDumpRegName, + IMG_UINT32 ui32RegAddr, + IMG_UINT32 ui32RegValue, + IMG_UINT32 ui32Mask, + IMG_UINT32 ui32Flags, + PDUMP_POLL_OPERATOR eOperator) +{ + /* Timings correct for linux and XP */ + #define POLL_DELAY 1000U + #define POLL_COUNT_LONG (2000000000U / POLL_DELAY) + #define POLL_COUNT_SHORT (1000000U / POLL_DELAY) + + PVRSRV_ERROR eErr; + IMG_UINT32 ui32PollCount; + + PDUMP_GET_SCRIPT_STRING(); + PDUMP_DBG(("PDumpRegPolWithFlagsKM")); + if ( _PDumpIsPersistent() ) + { + /* Don't pdump-poll if the process is persistent */ + return PVRSRV_OK; + } + + ui32PollCount = POLL_COUNT_LONG; + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "POL :%s:0x%08X 0x%08X 0x%08X %d %u %d\r\n", + pszPDumpRegName, ui32RegAddr, ui32RegValue, + ui32Mask, eOperator, ui32PollCount, POLL_DELAY); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : PDumpRegPol + * Inputs : Description of what this register read is trying to do + * pszPDumpDevName + Register offset + * expected value + * mask for that value + * Outputs : None + * Returns : None + * Description : Create a PDUMP string which represents a register read + * with the expected value +**************************************************************************/ +PVRSRV_ERROR PDumpRegPolKM(IMG_CHAR *pszPDumpRegName, IMG_UINT32 ui32RegAddr, IMG_UINT32 ui32RegValue, IMG_UINT32 ui32Mask, PDUMP_POLL_OPERATOR eOperator) +{ + return PDumpRegPolWithFlagsKM(pszPDumpRegName, ui32RegAddr, ui32RegValue, ui32Mask, PDUMP_FLAGS_CONTINUOUS, eOperator); +} + +/************************************************************************** + * Function Name : PDumpMallocPages + * Inputs : psDevID, ui32DevVAddr, pvLinAddr, ui32NumBytes, hOSMemHandle + * : hUniqueTag + * Outputs : None + * Returns : None + * Description : Malloc memory pages + +FIXME: This function assumes pvLinAddr is the address of the start of the +block for this hOSMemHandle. +If this isn't true, the call to PDumpOSCPUVAddrToDevPAddr below will be +incorrect. (Consider using OSMemHandleToCPUPAddr() instead?) +The only caller at the moment is in buffer_manager.c, which does the right +thing. +**************************************************************************/ +PVRSRV_ERROR PDumpMallocPages (PVRSRV_DEVICE_IDENTIFIER *psDevID, + IMG_UINT32 ui32DevVAddr, + IMG_CPU_VIRTADDR pvLinAddr, + IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32NumBytes, + IMG_UINT32 ui32PageSize, + IMG_BOOL bShared, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_PUINT8 pui8LinAddr; + IMG_UINT32 ui32Offset; + IMG_UINT32 ui32NumPages; + IMG_DEV_PHYADDR sDevPAddr; + IMG_UINT32 ui32Page; + IMG_UINT32 ui32Flags = PDUMP_FLAGS_CONTINUOUS; + + PDUMP_GET_SCRIPT_STRING(); +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + /* Always dump physical pages backing a shared allocation */ + ui32Flags |= ( _PDumpIsPersistent() || bShared ) ? PDUMP_FLAGS_PERSISTENT : 0; +#else + PVR_UNREFERENCED_PARAMETER(bShared); + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; +#endif + + /* However, lin addr is only required in non-linux OSes */ +#if !defined(LINUX) + PVR_ASSERT(((IMG_UINTPTR_T)pvLinAddr & HOST_PAGEMASK) == 0); +#endif + + PVR_ASSERT(((IMG_UINT32) ui32DevVAddr & HOST_PAGEMASK) == 0); + PVR_ASSERT(((IMG_UINT32) ui32NumBytes & HOST_PAGEMASK) == 0); + + /* + Write a comment to the PDump2 script streams indicating the memory allocation + */ + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "-- MALLOC :%s:VA_%08X 0x%08X %u\r\n", + psDevID->pszPDumpDevName, ui32DevVAddr, ui32NumBytes, ui32PageSize); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + /* + Write to the MMU script stream indicating the memory allocation + */ + pui8LinAddr = (IMG_PUINT8) pvLinAddr; + ui32Offset = 0; + ui32NumPages = ui32NumBytes / ui32PageSize; + while (ui32NumPages) + { + ui32NumPages--; + + /* See FIXME in function header. + * Currently: linux pdump uses OSMemHandle and Offset + * other OSes use the LinAddr. + */ + /* Calculate the device physical address for this page */ + PDumpOSCPUVAddrToDevPAddr(psDevID->eDeviceType, + hOSMemHandle, + ui32Offset, + pui8LinAddr, + ui32PageSize, + &sDevPAddr); + ui32Page = (IMG_UINT32)(sDevPAddr.uiAddr / ui32PageSize); + /* increment kernel virtual address */ + pui8LinAddr += ui32PageSize; + ui32Offset += ui32PageSize; + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "MALLOC :%s:PA_%08X%08X %u %u 0x%08X\r\n", + psDevID->pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + ui32Page * ui32PageSize, + ui32PageSize, + ui32PageSize, + ui32Page * ui32PageSize); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + } + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : PDumpMallocPageTable + * Inputs : psDevId, pvLinAddr, ui32NumBytes, hUniqueTag + * Outputs : None + * Returns : None + * Description : Malloc memory page table +**************************************************************************/ +PVRSRV_ERROR PDumpMallocPageTable (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32Offset, + IMG_CPU_VIRTADDR pvLinAddr, + IMG_UINT32 ui32PTSize, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_DEV_PHYADDR sDevPAddr; + + PDUMP_GET_SCRIPT_STRING(); + + PVR_ASSERT(((IMG_UINTPTR_T)pvLinAddr & (ui32PTSize - 1)) == 0); + ui32Flags |= PDUMP_FLAGS_CONTINUOUS; + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; + + /* + Write a comment to the PDump2 script streams indicating the memory allocation + */ + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "-- MALLOC :%s:PAGE_TABLE 0x%08X %u\r\n", + psDevId->pszPDumpDevName, + ui32PTSize, + ui32PTSize); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + /* + Write to the MMU script stream indicating the memory allocation + */ + // FIXME: we'll never need more than a 4k page for a pagetable + // fixing to 1 page for now. + // note: when the mmu code supports packed pagetables the PTs + // will be as small as 16bytes + + PDumpOSCPUVAddrToDevPAddr(psDevId->eDeviceType, + hOSMemHandle, /* um - does this mean the pvLinAddr would be ignored? Is that safe? */ + ui32Offset, + (IMG_PUINT8) pvLinAddr, + ui32PTSize, + &sDevPAddr); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "MALLOC :%s:PA_%08X%08X 0x%X %u 0x%08X\r\n", + psDevId->pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr, + ui32PTSize,//size + ui32PTSize,//alignment + sDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpFreePages + * Inputs : psBMHeap, sDevVAddr, ui32NumBytes, hUniqueTag, + bInterLeaved + * Outputs : None + * Returns : None + * Description : Free memory pages +**************************************************************************/ +PVRSRV_ERROR PDumpFreePages (BM_HEAP *psBMHeap, + IMG_DEV_VIRTADDR sDevVAddr, + IMG_UINT32 ui32NumBytes, + IMG_UINT32 ui32PageSize, + IMG_HANDLE hUniqueTag, + IMG_BOOL bInterleaved, + IMG_BOOL bSparse) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32NumPages, ui32PageCounter; + IMG_DEV_PHYADDR sDevPAddr; + IMG_UINT32 ui32Flags = PDUMP_FLAGS_CONTINUOUS; + PVRSRV_DEVICE_NODE *psDeviceNode; + + PDUMP_GET_SCRIPT_STRING(); + + PVR_ASSERT(((IMG_UINT32) sDevVAddr.uiAddr & (ui32PageSize - 1)) == 0); + PVR_ASSERT(((IMG_UINT32) ui32NumBytes & (ui32PageSize - 1)) == 0); + + psDeviceNode = psBMHeap->pBMContext->psDeviceNode; + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; + + /* + Write a comment to the PDUMP2 script streams indicating the memory free + */ + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "-- FREE :%s:VA_%08X\r\n", + psDeviceNode->sDevId.pszPDumpDevName, sDevVAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + /* if we're dumping a shared heap, need to ensure phys allocation + * is freed even if this app isn't the one marked for pdumping + */ + { + PVRSRV_DEVICE_NODE *psDeviceNode = psBMHeap->pBMContext->psDeviceNode; + + if( psDeviceNode->pfnMMUIsHeapShared(psBMHeap->pMMUHeap) ) + { + ui32Flags |= PDUMP_FLAGS_PERSISTENT; + } + } +#endif + PDumpOSWriteString2(hScript, ui32Flags); + + /* + Write to the MMU script stream indicating the memory free + */ + ui32NumPages = ui32NumBytes / ui32PageSize; + for (ui32PageCounter = 0; ui32PageCounter < ui32NumPages; ui32PageCounter++) + { + if (!bInterleaved || (ui32PageCounter % 2) == 0) + { + sDevPAddr = psDeviceNode->pfnMMUGetPhysPageAddr(psBMHeap->pMMUHeap, sDevVAddr); + + /* With sparse mappings we expect spaces */ + if (bSparse && (sDevPAddr.uiAddr == 0)) + { + continue; + } + + PVR_ASSERT(sDevPAddr.uiAddr != 0); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "FREE :%s:PA_%08X%08X\r\n", + psDeviceNode->sDevId.pszPDumpDevName, (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, sDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + } + else + { + /* Gap pages in an interleaved allocation should be ignored. */ + } + + sDevVAddr.uiAddr += ui32PageSize; + } + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpFreePageTable + * Inputs : psDevID, pvLinAddr, ui32NumBytes, hUniqueTag + * Outputs : None + * Returns : None + * Description : Free memory page table +**************************************************************************/ +PVRSRV_ERROR PDumpFreePageTable (PVRSRV_DEVICE_IDENTIFIER *psDevID, + IMG_HANDLE hOSMemHandle, + IMG_CPU_VIRTADDR pvLinAddr, + IMG_UINT32 ui32PTSize, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_DEV_PHYADDR sDevPAddr; + + PDUMP_GET_SCRIPT_STRING(); + + PVR_UNREFERENCED_PARAMETER(ui32PTSize); + ui32Flags |= PDUMP_FLAGS_CONTINUOUS; + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; + + /* override QAC warning about wrap around */ + PVR_ASSERT(((IMG_UINTPTR_T)pvLinAddr & (ui32PTSize-1UL)) == 0); /* PRQA S 3382 */ + + /* + Write a comment to the PDUMP2 script streams indicating the memory free + */ + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "-- FREE :%s:PAGE_TABLE\r\n", psDevID->pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + /* + Write to the MMU script stream indicating the memory free + */ + // FIXME: we'll never need more than a 4k page for a pagetable + // fixing to 1 page for now. + // note: when the mmu code supports packed pagetables the PTs + // will be as small as 16bytes + + PDumpOSCPUVAddrToDevPAddr(psDevID->eDeviceType, + hOSMemHandle, /* um - does this mean the pvLinAddr would be ignored? Is that safe? */ + 0, + (IMG_PUINT8) pvLinAddr, + ui32PTSize, + &sDevPAddr); + + { + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "FREE :%s:PA_%08X%08X\r\n", + psDevID->pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + } + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpPDRegWithFlags + * Inputs : psMMUAttrib + * : ui32Reg + * : ui32Data + * : hUniqueTag + * Outputs : None + * Returns : None + * Description : Kernel Services internal pdump memory API + * Used for registers specifying physical addresses + e.g. MMU page directory register +**************************************************************************/ +PVRSRV_ERROR PDumpPDRegWithFlags(PDUMP_MMU_ATTRIB *psMMUAttrib, + IMG_UINT32 ui32Reg, + IMG_UINT32 ui32Data, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_CHAR *pszRegString; + PDUMP_GET_SCRIPT_STRING() + + if(psMMUAttrib->pszPDRegRegion != IMG_NULL) + { + pszRegString = psMMUAttrib->pszPDRegRegion; + } + else + { + pszRegString = psMMUAttrib->sDevId.pszPDumpRegName; + } + + /* + Write to the MMU script stream indicating the physical page directory + */ +#if defined(SGX_FEATURE_36BIT_MMU) + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, + "WRW :%s:$1 :%s:PA_%08X%08X:0x0\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag, + (ui32Data & psMMUAttrib->ui32PDEMask) << psMMUAttrib->ui32PDEAlignShift); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "SHR :%s:$1 :%s:$1 0x4\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, + "WRW :%s:0x%08X: %s:$1\r\n", + pszRegString, + ui32Reg, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); +#else + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "WRW :%s:0x%08X :%s:PA_%08X%08X:0x%08X\r\n", + pszRegString, + ui32Reg, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + (ui32Data & psMMUAttrib->ui32PDEMask) << psMMUAttrib->ui32PDEAlignShift, + ui32Data & ~psMMUAttrib->ui32PDEMask); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); +#endif + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpPDReg + * Inputs : psMMUAttrib + : ui32Reg + * : ui32Data + * : hUniqueTag + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Kernel Services internal pdump memory API + * Used for registers specifying physical addresses + e.g. MMU page directory register +**************************************************************************/ +PVRSRV_ERROR PDumpPDReg (PDUMP_MMU_ATTRIB *psMMUAttrib, + IMG_UINT32 ui32Reg, + IMG_UINT32 ui32Data, + IMG_HANDLE hUniqueTag) +{ + return PDumpPDRegWithFlags(psMMUAttrib, ui32Reg, ui32Data, PDUMP_FLAGS_CONTINUOUS, hUniqueTag); +} + +/************************************************************************** + * Function Name : PDumpMemPolKM + * Inputs : psMemInfo + * : ui32Offset + * : ui32Value + * : ui32Mask + * : eOperator + * : ui32Flags + * : hUniqueTag + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Implements Client pdump memory poll API +**************************************************************************/ +PVRSRV_ERROR PDumpMemPolKM(PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Offset, + IMG_UINT32 ui32Value, + IMG_UINT32 ui32Mask, + PDUMP_POLL_OPERATOR eOperator, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + #define MEMPOLL_DELAY (1000) + #define MEMPOLL_COUNT (2000000000 / MEMPOLL_DELAY) + + PVRSRV_ERROR eErr; + IMG_UINT32 ui32PageOffset; + IMG_UINT8 *pui8LinAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_DEV_VIRTADDR sDevVPageAddr; + PDUMP_MMU_ATTRIB *psMMUAttrib; + + PDUMP_GET_SCRIPT_STRING(); + + if (PDumpOSIsSuspended()) + { + return PVRSRV_OK; + } + + if ( _PDumpIsPersistent() ) + { + /* Don't pdump-poll if the process is persistent */ + return PVRSRV_OK; + } + + /* Check the offset and size don't exceed the bounds of the allocation */ + PVR_ASSERT((ui32Offset + sizeof(IMG_UINT32)) <= psMemInfo->uAllocSize); + + psMMUAttrib = ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->psMMUAttrib; + + /* + Write a comment to the PDump2 script streams indicating the virtual memory pol + */ + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "-- POL :%s:VA_%08X 0x%08X 0x%08X %d %d %d\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMemInfo->sDevVAddr.uiAddr + ui32Offset, + ui32Value, + ui32Mask, + eOperator, + MEMPOLL_COUNT, + MEMPOLL_DELAY); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + + pui8LinAddr = psMemInfo->pvLinAddrKM; + + /* Advance address by offset */ + pui8LinAddr += ui32Offset; + + /* + query the buffer manager for the physical pages that back the + virtual address + */ + PDumpOSCPUVAddrToPhysPages(psMemInfo->sMemBlk.hOSMemHandle, + ui32Offset, + pui8LinAddr, + psMMUAttrib->ui32DataPageMask, + &ui32PageOffset); + + /* calculate the DevV page address */ + sDevVPageAddr.uiAddr = psMemInfo->sDevVAddr.uiAddr + ui32Offset - ui32PageOffset; + + PVR_ASSERT((sDevVPageAddr.uiAddr & psMMUAttrib->ui32DataPageMask) == 0); + + /* get the physical page address based on the device virtual address */ + BM_GetPhysPageAddr(psMemInfo, sDevVPageAddr, &sDevPAddr); + + /* convert DevP page address to byte address */ + sDevPAddr.uiAddr += ui32PageOffset; + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "POL :%s:PA_%08X%08X:0x%08X 0x%08X 0x%08X %d %d %d\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr & ~(psMMUAttrib->ui32DataPageMask), + sDevPAddr.uiAddr & (psMMUAttrib->ui32DataPageMask), + ui32Value, + ui32Mask, + eOperator, + MEMPOLL_COUNT, + MEMPOLL_DELAY); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : _PDumpMemIntKM + * Inputs : psMemInfo + * : ui32Offset + * : ui32Bytes + * : ui32Flags + * : hUniqueTag + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Implements Client pdump mem API +**************************************************************************/ +static PVRSRV_ERROR _PDumpMemIntKM(IMG_PVOID pvAltLinAddr, + PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Offset, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32NumPages; + IMG_UINT32 ui32PageByteOffset; + IMG_UINT32 ui32BlockBytes; + IMG_UINT8* pui8LinAddr; + IMG_UINT8* pui8DataLinAddr = IMG_NULL; + IMG_DEV_VIRTADDR sDevVPageAddr; + IMG_DEV_VIRTADDR sDevVAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_UINT32 ui32ParamOutPos; + PDUMP_MMU_ATTRIB *psMMUAttrib; + IMG_UINT32 ui32DataPageSize; + + PDUMP_GET_SCRIPT_AND_FILE_STRING(); + + /* PRQA S 3415 1 */ /* side effects desired */ + if (ui32Bytes == 0 || PDumpOSIsSuspended()) + { + return PVRSRV_OK; + } + + psMMUAttrib = ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->psMMUAttrib; + + /* + check the offset and size don't exceed the bounds of the allocation + */ + PVR_ASSERT((ui32Offset + ui32Bytes) <= psMemInfo->uAllocSize); + + if (!PDumpOSJTInitialised()) + { + return PVRSRV_ERROR_PDUMP_NOT_AVAILABLE; + } + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + /* if we're dumping a shared heap, need to ensure phys allocation + * is initialised even if this app isn't the one marked for pdumping + */ + { + BM_HEAP *pHeap = ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap; + PVRSRV_DEVICE_NODE *psDeviceNode = pHeap->pBMContext->psDeviceNode; + + if( psDeviceNode->pfnMMUIsHeapShared(pHeap->pMMUHeap) ) + { + ui32Flags |= PDUMP_FLAGS_PERSISTENT; + } + } +#endif + + /* setup memory addresses */ + if(pvAltLinAddr) + { + pui8DataLinAddr = pvAltLinAddr; + } + else if(psMemInfo->pvLinAddrKM) + { + pui8DataLinAddr = (IMG_UINT8 *)psMemInfo->pvLinAddrKM + ui32Offset; + } + pui8LinAddr = (IMG_UINT8 *)psMemInfo->pvLinAddrKM; + sDevVAddr = psMemInfo->sDevVAddr; + + /* advance address by offset */ + sDevVAddr.uiAddr += ui32Offset; + pui8LinAddr += ui32Offset; + + PVR_ASSERT(pui8DataLinAddr); + + PDumpOSCheckForSplitting(PDumpOSGetStream(PDUMP_STREAM_PARAM2), ui32Bytes, ui32Flags); + + ui32ParamOutPos = PDumpOSGetStreamOffset(PDUMP_STREAM_PARAM2); + + /* + write the binary data up-front. + */ + if(!PDumpOSWriteString(PDumpOSGetStream(PDUMP_STREAM_PARAM2), + pui8DataLinAddr, + ui32Bytes, + ui32Flags)) + { + return PVRSRV_ERROR_PDUMP_BUFFER_FULL; + } + + if (PDumpOSGetParamFileNum() == 0) + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%.prm"); + } + else + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%_%u.prm", PDumpOSGetParamFileNum()); + } + if(eErr != PVRSRV_OK) + { + return eErr; + } + + /* + Write a comment to the PDump2 script streams indicating the virtual memory load + */ + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "-- LDB :%s:VA_%08X%08X:0x%08X 0x%08X 0x%08X %s\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + psMemInfo->sDevVAddr.uiAddr, + ui32Offset, + ui32Bytes, + ui32ParamOutPos, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + /* + query the buffer manager for the physical pages that back the + virtual address + */ + PDumpOSCPUVAddrToPhysPages(psMemInfo->sMemBlk.hOSMemHandle, + ui32Offset, + pui8LinAddr, + psMMUAttrib->ui32DataPageMask, + &ui32PageByteOffset); + ui32DataPageSize = psMMUAttrib->ui32DataPageMask + 1; + ui32NumPages = (ui32PageByteOffset + ui32Bytes + psMMUAttrib->ui32DataPageMask) / ui32DataPageSize; + + while(ui32NumPages) + { + ui32NumPages--; + + /* calculate the DevV page address */ + sDevVPageAddr.uiAddr = sDevVAddr.uiAddr - ui32PageByteOffset; + + if (ui32DataPageSize <= PDUMP_TEMP_BUFFER_SIZE) + { + /* if a page fits within temp buffer, we should dump in page-aligned chunks. */ + PVR_ASSERT((sDevVPageAddr.uiAddr & psMMUAttrib->ui32DataPageMask) == 0); + } + + /* get the physical page address based on the device virtual address */ + BM_GetPhysPageAddr(psMemInfo, sDevVPageAddr, &sDevPAddr); + + /* convert DevP page address to byte address */ + sDevPAddr.uiAddr += ui32PageByteOffset; + + /* how many bytes to dump from this page */ + if (ui32PageByteOffset + ui32Bytes > ui32DataPageSize) + { + /* dump up to the page boundary */ + ui32BlockBytes = ui32DataPageSize - ui32PageByteOffset; + } + else + { + /* dump what's left */ + ui32BlockBytes = ui32Bytes; + } + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "LDB :%s:PA_%08X%08X:0x%08X 0x%08X 0x%08X %s\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr & ~(psMMUAttrib->ui32DataPageMask), + sDevPAddr.uiAddr & (psMMUAttrib->ui32DataPageMask), + ui32BlockBytes, + ui32ParamOutPos, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + /* update details for next page */ + +#if defined(SGX_FEATURE_VARIABLE_MMU_PAGE_SIZE) + /* page may be larger than pdump temporary buffer */ + ui32PageByteOffset = (ui32PageByteOffset + ui32BlockBytes) % ui32DataPageSize; +#else + /* page offset 0 after first page dump */ + ui32PageByteOffset = 0; +#endif + /* bytes left over */ + ui32Bytes -= ui32BlockBytes; /* PRQA S 3382 */ /* QAC missed MIN test */ + /* advance devVaddr */ + sDevVAddr.uiAddr += ui32BlockBytes; + /* advance the cpuVaddr */ + pui8LinAddr += ui32BlockBytes; + /* update the file write offset */ + ui32ParamOutPos += ui32BlockBytes; + } + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpMemKM + * Inputs : psMemInfo + * : ui32Offset + * : ui32Bytes + * : ui32Flags + * : hUniqueTag + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Implements Client pdump mem API +**************************************************************************/ +PVRSRV_ERROR PDumpMemKM(IMG_PVOID pvAltLinAddr, + PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Offset, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + /* + For now we don't support dumping sparse allocations that + are from within the kernel, or are from UM but without a + alternative linear address + */ + PVR_ASSERT((psMemInfo->ui32Flags & PVRSRV_MEM_SPARSE) == 0); + + if (psMemInfo->ui32Flags & PVRSRV_MEM_SPARSE) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + else + { + return _PDumpMemIntKM(pvAltLinAddr, + psMemInfo, + ui32Offset, + ui32Bytes, + ui32Flags, + hUniqueTag); + } +} + +PVRSRV_ERROR PDumpMemPDEntriesKM(PDUMP_MMU_ATTRIB *psMMUAttrib, + IMG_HANDLE hOSMemHandle, + IMG_CPU_VIRTADDR pvLinAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_BOOL bInitialisePages, + IMG_HANDLE hUniqueTag1, + IMG_HANDLE hUniqueTag2) +{ + PDUMP_MMU_ATTRIB sMMUAttrib; + + /* Override the (variable) PT size since PDs are always 4K in size */ + sMMUAttrib = *psMMUAttrib; + sMMUAttrib.ui32PTSize = (IMG_UINT32)HOST_PAGESIZE(); + return PDumpMemPTEntriesKM( &sMMUAttrib, + hOSMemHandle, + pvLinAddr, + ui32Bytes, + ui32Flags, + bInitialisePages, + hUniqueTag1, + hUniqueTag2); +} + +/************************************************************************** + * Function Name : PDumpMemPTEntriesKM + * Inputs : psMMUAttrib - MMU attributes for pdump + * : pvLinAddr - CPU address of PT base + * : ui32Bytes - size + * : ui32Flags - pdump flags + * : bInitialisePages - whether to initialise pages from file + * : hUniqueTag1 - ID for PT physical page + * : hUniqueTag2 - ID for target physical page (if !bInitialisePages) + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Kernel Services internal pdump memory API + * Used for memory without DevVAddress mappings + e.g. MMU page tables + FIXME: This function doesn't support non-4k data pages, + e.g. dummy data page +**************************************************************************/ +PVRSRV_ERROR PDumpMemPTEntriesKM(PDUMP_MMU_ATTRIB *psMMUAttrib, + IMG_HANDLE hOSMemHandle, + IMG_CPU_VIRTADDR pvLinAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_BOOL bInitialisePages, + IMG_HANDLE hUniqueTag1, + IMG_HANDLE hUniqueTag2) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32NumPages; + IMG_UINT32 ui32PageOffset; + IMG_UINT32 ui32BlockBytes; + IMG_UINT8* pui8LinAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_CPU_PHYADDR sCpuPAddr; + IMG_UINT32 ui32Offset; + IMG_UINT32 ui32ParamOutPos; + IMG_UINT32 ui32PageMask; /* mask for the physical page backing the PT */ + + PDUMP_GET_SCRIPT_AND_FILE_STRING(); + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; + + if (PDumpOSIsSuspended()) + { + return PVRSRV_OK; + } + + if (!PDumpOSJTInitialised()) + { + return PVRSRV_ERROR_PDUMP_NOT_AVAILABLE; + } + + if (!pvLinAddr) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + PDumpOSCheckForSplitting(PDumpOSGetStream(PDUMP_STREAM_PARAM2), ui32Bytes, ui32Flags); + + ui32ParamOutPos = PDumpOSGetStreamOffset(PDUMP_STREAM_PARAM2); + + if (bInitialisePages) + { + /* + write the binary data up-front + Use the 'continuous' memory stream + */ + if (!PDumpOSWriteString(PDumpOSGetStream(PDUMP_STREAM_PARAM2), + pvLinAddr, + ui32Bytes, + ui32Flags | PDUMP_FLAGS_CONTINUOUS)) + { + return PVRSRV_ERROR_PDUMP_BUFFER_FULL; + } + + if (PDumpOSGetParamFileNum() == 0) + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%.prm"); + } + else + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%_%u.prm", PDumpOSGetParamFileNum()); + } + if(eErr != PVRSRV_OK) + { + return eErr; + } + } + + /* + Mask for the physical page address backing the PT + The PT size can be less than 4k with variable page size support + The PD size is always 4k + FIXME: This won't work for dumping the dummy data page + */ + ui32PageMask = psMMUAttrib->ui32PTSize - 1; + + /* + Write to the MMU script stream indicating the physical page table entries + */ + /* physical pages that back the virtual address */ + ui32PageOffset = (IMG_UINT32)((IMG_UINTPTR_T)pvLinAddr & (psMMUAttrib->ui32PTSize - 1)); + ui32NumPages = (ui32PageOffset + ui32Bytes + psMMUAttrib->ui32PTSize - 1) / psMMUAttrib->ui32PTSize; + pui8LinAddr = (IMG_UINT8*) pvLinAddr; + + while (ui32NumPages) + { + ui32NumPages--; + /* FIXME: if we used OSMemHandleToCPUPAddr() here, we might be + able to lose the lin addr arg. At least one thing that + would need to be done here is to pass in an offset, as the + calling function doesn't necessarily give us the lin addr + of the start of the mem area. Probably best to keep the + lin addr arg for now - but would be nice to remove the + redundancy */ + sCpuPAddr = OSMapLinToCPUPhys(hOSMemHandle, pui8LinAddr); + sDevPAddr = SysCpuPAddrToDevPAddr(psMMUAttrib->sDevId.eDeviceType, sCpuPAddr); + + /* how many bytes to dump from this page */ + if (ui32PageOffset + ui32Bytes > psMMUAttrib->ui32PTSize) + { + /* dump up to the page boundary */ + ui32BlockBytes = psMMUAttrib->ui32PTSize - ui32PageOffset; + } + else + { + /* dump what's left */ + ui32BlockBytes = ui32Bytes; + } + + /* + Write a comment to the MMU script stream indicating the page table load + */ + + if (bInitialisePages) + { + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "LDB :%s:PA_%08X%08X:0x%08X 0x%08X 0x%08X %s\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + sDevPAddr.uiAddr & ~ui32PageMask, + sDevPAddr.uiAddr & ui32PageMask, + ui32BlockBytes, + ui32ParamOutPos, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); + } + else + { + for (ui32Offset = 0; ui32Offset < ui32BlockBytes; ui32Offset += sizeof(IMG_UINT32)) + { + IMG_UINT32 ui32PTE = *((IMG_UINT32 *)(IMG_UINTPTR_T)(pui8LinAddr + ui32Offset)); /* PRQA S 3305 */ /* strict pointer */ + + if ((ui32PTE & psMMUAttrib->ui32PDEMask) != 0) + { + /* PT entry points to non-null page */ +#if defined(SGX_FEATURE_36BIT_MMU) + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:$1 :%s:PA_%08X%08X:0x0\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag2, + (ui32PTE & psMMUAttrib->ui32PDEMask) << psMMUAttrib->ui32PTEAlignShift); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); + eErr = PDumpOSBufprintf(hScript, ui32MaxLenScript, "SHR :%s:$1 :%s:$1 0x4\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); + eErr = PDumpOSBufprintf(hScript, ui32MaxLenScript, "OR :%s:$1 :%s:$1 0x%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName, + ui32PTE & ~psMMUAttrib->ui32PDEMask); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X :%s:$1\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag1, + (sDevPAddr.uiAddr + ui32Offset) & ~ui32PageMask, + (sDevPAddr.uiAddr + ui32Offset) & ui32PageMask, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); +#else + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X :%s:PA_%08X%08X:0x%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + (sDevPAddr.uiAddr + ui32Offset) & ~ui32PageMask, + (sDevPAddr.uiAddr + ui32Offset) & ui32PageMask, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag2, + (ui32PTE & psMMUAttrib->ui32PDEMask) << psMMUAttrib->ui32PTEAlignShift, + ui32PTE & ~psMMUAttrib->ui32PDEMask); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); +#endif + } + else + { +#if !defined(FIX_HW_BRN_31620) + PVR_ASSERT((ui32PTE & psMMUAttrib->ui32PTEValid) == 0UL); +#endif + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X 0x%08X%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + (sDevPAddr.uiAddr + ui32Offset) & ~ui32PageMask, + (sDevPAddr.uiAddr + ui32Offset) & ui32PageMask, + (ui32PTE << psMMUAttrib->ui32PTEAlignShift), + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag2); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags | PDUMP_FLAGS_CONTINUOUS); + } + } + } + + /* update details for next page */ + + /* page offset 0 after first page dump */ + ui32PageOffset = 0; + /* bytes left over */ + ui32Bytes -= ui32BlockBytes; + /* advance the cpuVaddr */ + pui8LinAddr += ui32BlockBytes; + /* update the file write offset */ + ui32ParamOutPos += ui32BlockBytes; + } + + return PVRSRV_OK; +} + +PVRSRV_ERROR PDumpPDDevPAddrKM(PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Offset, + IMG_DEV_PHYADDR sPDDevPAddr, + IMG_HANDLE hUniqueTag1, + IMG_HANDLE hUniqueTag2) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32PageByteOffset; + IMG_DEV_VIRTADDR sDevVAddr; + IMG_DEV_VIRTADDR sDevVPageAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_UINT32 ui32Flags = PDUMP_FLAGS_CONTINUOUS; + IMG_UINT32 ui32ParamOutPos; + PDUMP_MMU_ATTRIB *psMMUAttrib; + IMG_UINT32 ui32PageMask; /* mask for the physical page backing the PT */ + + PDUMP_GET_SCRIPT_AND_FILE_STRING(); + + if (!PDumpOSJTInitialised()) + { + return PVRSRV_ERROR_PDUMP_NOT_AVAILABLE; + } + + psMMUAttrib = ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->psMMUAttrib; + ui32PageMask = psMMUAttrib->ui32PTSize - 1; + + ui32ParamOutPos = PDumpOSGetStreamOffset(PDUMP_STREAM_PARAM2); + + /* Write the PD phys addr to the param stream up front */ + if(!PDumpOSWriteString(PDumpOSGetStream(PDUMP_STREAM_PARAM2), + (IMG_UINT8 *)&sPDDevPAddr, + sizeof(IMG_DEV_PHYADDR), + ui32Flags)) + { + return PVRSRV_ERROR_PDUMP_BUFFER_FULL; + } + + if (PDumpOSGetParamFileNum() == 0) + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%.prm"); + } + else + { + eErr = PDumpOSSprintf(pszFileName, ui32MaxLenFileName, "%%0%%_%u.prm", PDumpOSGetParamFileNum()); + } + if(eErr != PVRSRV_OK) + { + return eErr; + } + + /* Write a comment indicating the PD phys addr write, so that the offsets + * into the param stream increase in correspondence with the number of bytes + * written. */ + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "-- LDB :%s:PA_0x%08X%08X:0x%08X 0x%08X 0x%08X %s\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + sPDDevPAddr.uiAddr & ~ui32PageMask, + sPDDevPAddr.uiAddr & ui32PageMask, + sizeof(IMG_DEV_PHYADDR), + ui32ParamOutPos, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + sDevVAddr = psMemInfo->sDevVAddr; + ui32PageByteOffset = sDevVAddr.uiAddr & ui32PageMask; + + sDevVPageAddr.uiAddr = sDevVAddr.uiAddr - ui32PageByteOffset; + PVR_ASSERT((sDevVPageAddr.uiAddr & 0xFFF) == 0); + + BM_GetPhysPageAddr(psMemInfo, sDevVPageAddr, &sDevPAddr); + sDevPAddr.uiAddr += ui32PageByteOffset + ui32Offset; + + if ((sPDDevPAddr.uiAddr & psMMUAttrib->ui32PDEMask) != 0UL) + { +#if defined(SGX_FEATURE_36BIT_MMU) + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:$1 :%s:PA_%08X%08X:0x0\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag2, + sPDDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLenScript, "AND :%s:$2 :%s:$1 0xFFFFFFFF\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X :%s:$2\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag1, + (sDevPAddr.uiAddr) & ~(psMMUAttrib->ui32DataPageMask), + (sDevPAddr.uiAddr) & (psMMUAttrib->ui32DataPageMask), + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLenScript, "SHR :%s:$2 :%s:$1 0x20\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X :%s:$2\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)hUniqueTag1, + (sDevPAddr.uiAddr + 4) & ~(psMMUAttrib->ui32DataPageMask), + (sDevPAddr.uiAddr + 4) & (psMMUAttrib->ui32DataPageMask), + psMMUAttrib->sDevId.pszPDumpDevName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); +#else + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X :%s:PA_%08X%08X:0x%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + sDevPAddr.uiAddr & ~ui32PageMask, + sDevPAddr.uiAddr & ui32PageMask, + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag2, + sPDDevPAddr.uiAddr & psMMUAttrib->ui32PDEMask, + sPDDevPAddr.uiAddr & ~psMMUAttrib->ui32PDEMask); + if(eErr != PVRSRV_OK) + { + return eErr; + } +#endif + } + else + { + PVR_ASSERT(!(sDevPAddr.uiAddr & psMMUAttrib->ui32PTEValid)); + eErr = PDumpOSBufprintf(hScript, + ui32MaxLenScript, + "WRW :%s:PA_%08X%08X:0x%08X 0x%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + sDevPAddr.uiAddr & ~ui32PageMask, + sDevPAddr.uiAddr & ui32PageMask, + sPDDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + } + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpCommentKM + * Inputs : pszComment, ui32Flags + * Outputs : None + * Returns : None + * Description : Dumps a comment +**************************************************************************/ +PVRSRV_ERROR PDumpCommentKM(IMG_CHAR *pszComment, IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + IMG_CHAR pszCommentPrefix[] = "-- "; /* prefix for comments */ +#if defined(PDUMP_DEBUG_OUTFILES) + IMG_CHAR pszTemp[256]; +#endif + IMG_UINT32 ui32LenCommentPrefix; + PDUMP_GET_SCRIPT_STRING(); + PDUMP_DBG(("PDumpCommentKM")); +#if defined(PDUMP_DEBUG_OUTFILES) + /* include comments in the "extended" init phase. + * default is to ignore them. + */ + ui32Flags |= ( _PDumpIsPersistent() ) ? PDUMP_FLAGS_PERSISTENT : 0; +#endif + /* Put \r \n sequence at the end if it isn't already there */ + PDumpOSVerifyLineEnding(pszComment, ui32MaxLen); + + /* Length of string excluding terminating NULL character */ + ui32LenCommentPrefix = PDumpOSBuflen(pszCommentPrefix, sizeof(pszCommentPrefix)); + + /* Ensure output file is available for writing */ + /* FIXME: is this necessary? */ + if (!PDumpOSWriteString(PDumpOSGetStream(PDUMP_STREAM_SCRIPT2), + (IMG_UINT8*)pszCommentPrefix, + ui32LenCommentPrefix, + ui32Flags)) + { +#if defined(PDUMP_DEBUG_OUTFILES) + if(ui32Flags & PDUMP_FLAGS_CONTINUOUS) + { + PVR_DPF((PVR_DBG_WARNING, "Incomplete comment, %d: %s (continuous set)", + g_ui32EveryLineCounter, pszComment)); + return PVRSRV_ERROR_PDUMP_BUFFER_FULL; + } + else if(ui32Flags & PDUMP_FLAGS_PERSISTENT) + { + PVR_DPF((PVR_DBG_WARNING, "Incomplete comment, %d: %s (persistent set)", + g_ui32EveryLineCounter, pszComment)); + return PVRSRV_ERROR_CMD_NOT_PROCESSED; + } + else + { + PVR_DPF((PVR_DBG_WARNING, "Incomplete comment, %d: %s", + g_ui32EveryLineCounter, pszComment)); + return PVRSRV_ERROR_CMD_NOT_PROCESSED; + } +#else + PVR_DPF((PVR_DBG_WARNING, "Incomplete comment, %s", + pszComment)); + return PVRSRV_ERROR_CMD_NOT_PROCESSED; +#endif + } + +#if defined(PDUMP_DEBUG_OUTFILES) + /* Prefix comment with PID and line number */ + eErr = PDumpOSSprintf(pszTemp, 256, "%d-%d %s", + _PDumpGetPID(), + g_ui32EveryLineCounter, + pszComment); + + /* Append the comment to the script stream */ + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "%s", + pszTemp); +#else + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "%s", + pszComment); +#endif + if( (eErr != PVRSRV_OK) && + (eErr != PVRSRV_ERROR_PDUMP_BUF_OVERFLOW)) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + return PVRSRV_OK; +} + +/************************************************************************** + * Function Name : PDumpCommentWithFlags + * Inputs : psPDev - PDev for PDump device + * : pszFormat - format string for comment + * : ... - args for format string + * Outputs : None + * Returns : None + * Description : PDumps a comments +**************************************************************************/ +PVRSRV_ERROR PDumpCommentWithFlags(IMG_UINT32 ui32Flags, IMG_CHAR * pszFormat, ...) +{ + PVRSRV_ERROR eErr; + PDUMP_va_list ap; + PDUMP_GET_MSG_STRING(); + + /* Construct the string */ + PDUMP_va_start(ap, pszFormat); + eErr = PDumpOSVSprintf(pszMsg, ui32MaxLen, pszFormat, ap); + PDUMP_va_end(ap); + + if(eErr != PVRSRV_OK) + { + return eErr; + } + return PDumpCommentKM(pszMsg, ui32Flags); +} + +/************************************************************************** + * Function Name : PDumpComment + * Inputs : psPDev - PDev for PDump device + * : pszFormat - format string for comment + * : ... - args for format string + * Outputs : None + * Returns : None + * Description : PDumps a comments +**************************************************************************/ +PVRSRV_ERROR PDumpComment(IMG_CHAR *pszFormat, ...) +{ + PVRSRV_ERROR eErr; + PDUMP_va_list ap; + PDUMP_GET_MSG_STRING(); + + /* Construct the string */ + PDUMP_va_start(ap, pszFormat); + eErr = PDumpOSVSprintf(pszMsg, ui32MaxLen, pszFormat, ap); + PDUMP_va_end(ap); + + if(eErr != PVRSRV_OK) + { + return eErr; + } + return PDumpCommentKM(pszMsg, PDUMP_FLAGS_CONTINUOUS); +} + +/************************************************************************** + * Function Name : PDumpDriverInfoKM + * Inputs : pszString, ui32Flags + * Outputs : None + * Returns : None + * Description : Dumps a comment +**************************************************************************/ +PVRSRV_ERROR PDumpDriverInfoKM(IMG_CHAR *pszString, IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32MsgLen; + PDUMP_GET_MSG_STRING(); + + /* Construct the string */ + eErr = PDumpOSSprintf(pszMsg, ui32MaxLen, "%s", pszString); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + /* Put \r \n sequence at the end if it isn't already there */ + PDumpOSVerifyLineEnding(pszMsg, ui32MaxLen); + ui32MsgLen = PDumpOSBuflen(pszMsg, ui32MaxLen); + + if (!PDumpOSWriteString(PDumpOSGetStream(PDUMP_STREAM_DRIVERINFO), + (IMG_UINT8*)pszMsg, + ui32MsgLen, + ui32Flags)) + { + if (ui32Flags & PDUMP_FLAGS_CONTINUOUS) + { + return PVRSRV_ERROR_PDUMP_BUFFER_FULL; + } + else + { + return PVRSRV_ERROR_CMD_NOT_PROCESSED; + } + } + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PDumpBitmapKM + + @Description + + Dumps a bitmap from device memory to a file + + @Input psDevId + @Input pszFileName + @Input ui32FileOffset + @Input ui32Width + @Input ui32Height + @Input ui32StrideInBytes + @Input sDevBaseAddr + @Input ui32Size + @Input ePixelFormat + @Input eMemFormat + @Input ui32PDumpFlags + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR PDumpBitmapKM( PVRSRV_DEVICE_NODE *psDeviceNode, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32FileOffset, + IMG_UINT32 ui32Width, + IMG_UINT32 ui32Height, + IMG_UINT32 ui32StrideInBytes, + IMG_DEV_VIRTADDR sDevBaseAddr, + IMG_HANDLE hDevMemContext, + IMG_UINT32 ui32Size, + PDUMP_PIXEL_FORMAT ePixelFormat, + PDUMP_MEM_FORMAT eMemFormat, + IMG_UINT32 ui32PDumpFlags) +{ + PVRSRV_DEVICE_IDENTIFIER *psDevId = &psDeviceNode->sDevId; + IMG_UINT32 ui32MMUContextID; + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + if ( _PDumpIsPersistent() ) + { + return PVRSRV_OK; + } + + PDumpCommentWithFlags(ui32PDumpFlags, "\r\n-- Dump bitmap of render\r\n"); + + /* find MMU context ID */ + ui32MMUContextID = psDeviceNode->pfnMMUGetContextID( hDevMemContext ); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "SII %s %s.bin :%s:v%x:0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\r\n", + pszFileName, + pszFileName, + psDevId->pszPDumpDevName, + ui32MMUContextID, + sDevBaseAddr.uiAddr, + ui32Size, + ui32FileOffset, + ePixelFormat, + ui32Width, + ui32Height, + ui32StrideInBytes, + eMemFormat); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpOSWriteString2( hScript, ui32PDumpFlags); + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PDumpReadRegKM + + @Description + + Dumps a read from a device register to a file + + @Input psConnection : connection info + @Input pszFileName + @Input ui32FileOffset + @Input ui32Address + @Input ui32Size + @Input ui32PDumpFlags + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR PDumpReadRegKM ( IMG_CHAR *pszPDumpRegName, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32FileOffset, + IMG_UINT32 ui32Address, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32PDumpFlags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + PVR_UNREFERENCED_PARAMETER(ui32Size); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "SAB :%s:0x%08X 0x%08X %s\r\n", + pszPDumpRegName, + ui32Address, + ui32FileOffset, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpOSWriteString2( hScript, ui32PDumpFlags); + + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpTestNextFrame + @brief Tests whether the next frame will be pdumped + @param ui32CurrentFrame + @return bFrameDumped +*****************************************************************************/ +IMG_BOOL PDumpTestNextFrame(IMG_UINT32 ui32CurrentFrame) +{ + IMG_BOOL bFrameDumped; + + /* + Try dumping a string + */ + (IMG_VOID) PDumpSetFrameKM(ui32CurrentFrame + 1); + bFrameDumped = PDumpIsCaptureFrameKM(); + (IMG_VOID) PDumpSetFrameKM(ui32CurrentFrame); + + return bFrameDumped; +} + +/***************************************************************************** + @name PDumpSignatureRegister + @brief Dumps a single signature register + @param psDevId - device ID + @param ui32Address - The register address + @param ui32Size - The amount of data to be dumped in bytes + @param pui32FileOffset - Offset of dump in output file + @param ui32Flags - Flags + @return none +*****************************************************************************/ +static PVRSRV_ERROR PDumpSignatureRegister (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Address, + IMG_UINT32 ui32Size, + IMG_UINT32 *pui32FileOffset, + IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "SAB :%s:0x%08X 0x%08X %s\r\n", + psDevId->pszPDumpRegName, + ui32Address, + *pui32FileOffset, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpOSWriteString2(hScript, ui32Flags); + *pui32FileOffset += ui32Size; + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpRegisterRange + @brief Dumps a list of signature registers to a file + @param psDevId - device ID + @param pszFileName - target filename for dump + @param pui32Registers - register list + @param ui32NumRegisters - number of regs to dump + @param pui32FileOffset - file offset + @param ui32Size - size of write in bytes + @param ui32Flags - pdump flags + @return none + *****************************************************************************/ +static IMG_VOID PDumpRegisterRange(PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_CHAR *pszFileName, + IMG_UINT32 *pui32Registers, + IMG_UINT32 ui32NumRegisters, + IMG_UINT32 *pui32FileOffset, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32Flags) +{ + IMG_UINT32 i; + for (i = 0; i < ui32NumRegisters; i++) + { + PDumpSignatureRegister(psDevId, pszFileName, pui32Registers[i], ui32Size, pui32FileOffset, ui32Flags); + } +} + +/***************************************************************************** + @name PDump3DSignatureRegisters + @brief Dumps the signature registers for 3D modules... + @param psDevId - device ID info + @param pui32Registers - register list + @param ui32NumRegisters - number of regs to dump + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDump3DSignatureRegisters(PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_UINT32 ui32DumpFrameNum, + IMG_BOOL bLastFrame, + IMG_UINT32 *pui32Registers, + IMG_UINT32 ui32NumRegisters) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32FileOffset, ui32Flags; + + PDUMP_GET_FILE_STRING(); + + ui32Flags = bLastFrame ? PDUMP_FLAGS_LASTFRAME : 0; + ui32FileOffset = 0; + + PDumpCommentWithFlags(ui32Flags, "\r\n-- Dump 3D signature registers\r\n"); + eErr = PDumpOSSprintf(pszFileName, ui32MaxLen, "out%u_3d.sig", ui32DumpFrameNum); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpRegisterRange(psDevId, + pszFileName, + pui32Registers, + ui32NumRegisters, + &ui32FileOffset, + sizeof(IMG_UINT32), + ui32Flags); + + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpTASignatureRegisters + @brief Dumps the TA signature registers + @param psDevId - device id info + @param ui32DumpFrameNum - frame number + @param ui32TAKickCount - TA kick counter + @param bLastFrame + @param pui32Registers - register list + @param ui32NumRegisters - number of regs to dump + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDumpTASignatureRegisters (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_UINT32 ui32DumpFrameNum, + IMG_UINT32 ui32TAKickCount, + IMG_BOOL bLastFrame, + IMG_UINT32 *pui32Registers, + IMG_UINT32 ui32NumRegisters) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32FileOffset, ui32Flags; + + PDUMP_GET_FILE_STRING(); + + ui32Flags = bLastFrame ? PDUMP_FLAGS_LASTFRAME : 0; + ui32FileOffset = ui32TAKickCount * ui32NumRegisters * sizeof(IMG_UINT32); + + PDumpCommentWithFlags(ui32Flags, "\r\n-- Dump TA signature registers\r\n"); + eErr = PDumpOSSprintf(pszFileName, ui32MaxLen, "out%u_ta.sig", ui32DumpFrameNum); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpRegisterRange(psDevId, + pszFileName, + pui32Registers, + ui32NumRegisters, + &ui32FileOffset, + sizeof(IMG_UINT32), + ui32Flags); + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpCounterRegisters + @brief Dumps the performance counters + @param psDevId - device id info + @param ui32DumpFrameNum - frame number + @param bLastFrame + @param pui32Registers - register list + @param ui32NumRegisters - number of regs to dump + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDumpCounterRegisters (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_UINT32 ui32DumpFrameNum, + IMG_BOOL bLastFrame, + IMG_UINT32 *pui32Registers, + IMG_UINT32 ui32NumRegisters) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32FileOffset, ui32Flags; + + PDUMP_GET_FILE_STRING(); + + ui32Flags = bLastFrame ? PDUMP_FLAGS_LASTFRAME : 0UL; + ui32FileOffset = 0UL; + + PDumpCommentWithFlags(ui32Flags, "\r\n-- Dump counter registers\r\n"); + eErr = PDumpOSSprintf(pszFileName, ui32MaxLen, "out%u.perf", ui32DumpFrameNum); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpRegisterRange(psDevId, + pszFileName, + pui32Registers, + ui32NumRegisters, + &ui32FileOffset, + sizeof(IMG_UINT32), + ui32Flags); + + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpRegRead + @brief Dump signature register read to script + @param pszPDumpDevName - pdump device name + @param ui32RegOffset - register offset + @param ui32Flags - pdump flags + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDumpRegRead(IMG_CHAR *pszPDumpRegName, + const IMG_UINT32 ui32RegOffset, + IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "RDW :%s:0x%X\r\n", + pszPDumpRegName, + ui32RegOffset); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpSaveMemKM + @brief Save device memory to a file + @param psDevId + @param pszFileName + @param ui32FileOffset + @param sDevBaseAddr + @param ui32Size + @param ui32PDumpFlags + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDumpSaveMemKM (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32FileOffset, + IMG_DEV_VIRTADDR sDevBaseAddr, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32MMUContextID, + IMG_UINT32 ui32PDumpFlags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "SAB :%s:v%x:0x%08X 0x%08X 0x%08X %s.bin\r\n", + psDevId->pszPDumpDevName, + ui32MMUContextID, + sDevBaseAddr.uiAddr, + ui32Size, + ui32FileOffset, + pszFileName); + if(eErr != PVRSRV_OK) + { + return eErr; + } + + PDumpOSWriteString2(hScript, ui32PDumpFlags); + return PVRSRV_OK; +} + +/***************************************************************************** + @name PDumpCycleCountRegRead + @brief Dump counter register read to script + @param ui32RegOffset - register offset + @param bLastFrame + @return Error +*****************************************************************************/ +PVRSRV_ERROR PDumpCycleCountRegRead(PVRSRV_DEVICE_IDENTIFIER *psDevId, + const IMG_UINT32 ui32RegOffset, + IMG_BOOL bLastFrame) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "RDW :%s:0x%X\r\n", + psDevId->pszPDumpRegName, + ui32RegOffset); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, bLastFrame ? PDUMP_FLAGS_LASTFRAME : 0); + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PDumpSignatureBuffer + + @Description + + Dumps a signature registers buffer + + @Return PVRSRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR PDumpSignatureBuffer (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_CHAR *pszFileName, + IMG_CHAR *pszBufferType, + IMG_UINT32 ui32FileOffset, + IMG_DEV_VIRTADDR sDevBaseAddr, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32MMUContextID, + IMG_UINT32 ui32PDumpFlags) +{ + PDumpCommentWithFlags(ui32PDumpFlags, "\r\n-- Dump microkernel %s signature Buffer\r\n", + pszBufferType); + PDumpCommentWithFlags(ui32PDumpFlags, "Buffer format (sizes in 32-bit words):\r\n"); + PDumpCommentWithFlags(ui32PDumpFlags, "\tNumber of signatures per sample (1)\r\n"); + PDumpCommentWithFlags(ui32PDumpFlags, "\tNumber of samples (1)\r\n"); + PDumpCommentWithFlags(ui32PDumpFlags, "\tSignature register offsets (1 * number of signatures)\r\n"); + PDumpCommentWithFlags(ui32PDumpFlags, "\tSignature sample values (number of samples * number of signatures)\r\n"); + PDumpCommentWithFlags(ui32PDumpFlags, "Note: If buffer is full, last sample is final state after test completed\r\n"); + return PDumpSaveMemKM(psDevId, pszFileName, ui32FileOffset, sDevBaseAddr, ui32Size, + ui32MMUContextID, ui32PDumpFlags); +} + + +/*! +****************************************************************************** + + @Function PDumpHWPerfCBKM + + @Description + + Dumps the HW Perf Circular Buffer + + @Return PVRSRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR PDumpHWPerfCBKM (PVRSRV_DEVICE_IDENTIFIER *psDevId, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32FileOffset, + IMG_DEV_VIRTADDR sDevBaseAddr, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32MMUContextID, + IMG_UINT32 ui32PDumpFlags) +{ + PDumpCommentWithFlags(ui32PDumpFlags, "\r\n-- Dump Hardware Performance Circular Buffer\r\n"); + return PDumpSaveMemKM(psDevId, pszFileName, ui32FileOffset, sDevBaseAddr, ui32Size, + ui32MMUContextID, ui32PDumpFlags); +} + + +/***************************************************************************** + FUNCTION : PDumpCBP + + PURPOSE : Dump CBP command to script + + PARAMETERS : + + RETURNS : None +*****************************************************************************/ +PVRSRV_ERROR PDumpCBP(PPVRSRV_KERNEL_MEM_INFO psROffMemInfo, + IMG_UINT32 ui32ROffOffset, + IMG_UINT32 ui32WPosVal, + IMG_UINT32 ui32PacketSize, + IMG_UINT32 ui32BufferSize, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + PVRSRV_ERROR eErr; + IMG_UINT32 ui32PageOffset; + IMG_UINT8 *pui8LinAddr; + IMG_DEV_VIRTADDR sDevVAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_DEV_VIRTADDR sDevVPageAddr; + //IMG_CPU_PHYADDR CpuPAddr; + PDUMP_MMU_ATTRIB *psMMUAttrib; + + PDUMP_GET_SCRIPT_STRING(); + + psMMUAttrib = ((BM_BUF*)psROffMemInfo->sMemBlk.hBuffer)->pMapping->pBMHeap->psMMUAttrib; + + /* Check the offset and size don't exceed the bounds of the allocation */ + PVR_ASSERT((ui32ROffOffset + sizeof(IMG_UINT32)) <= psROffMemInfo->uAllocSize); + + pui8LinAddr = psROffMemInfo->pvLinAddrKM; + sDevVAddr = psROffMemInfo->sDevVAddr; + + /* Advance addresses by offset */ + pui8LinAddr += ui32ROffOffset; + sDevVAddr.uiAddr += ui32ROffOffset; + + /* + query the buffer manager for the physical pages that back the + virtual address + */ + PDumpOSCPUVAddrToPhysPages(psROffMemInfo->sMemBlk.hOSMemHandle, + ui32ROffOffset, + pui8LinAddr, + psMMUAttrib->ui32DataPageMask, + &ui32PageOffset); + + /* calculate the DevV page address */ + sDevVPageAddr.uiAddr = sDevVAddr.uiAddr - ui32PageOffset; + + PVR_ASSERT((sDevVPageAddr.uiAddr & 0xFFF) == 0); + + /* get the physical page address based on the device virtual address */ + BM_GetPhysPageAddr(psROffMemInfo, sDevVPageAddr, &sDevPAddr); + + /* convert DevP page address to byte address */ + sDevPAddr.uiAddr += ui32PageOffset; + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "CBP :%s:PA_%08X%08X:0x%08X 0x%08X 0x%08X 0x%08X\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr & ~(psMMUAttrib->ui32DataPageMask), + sDevPAddr.uiAddr & (psMMUAttrib->ui32DataPageMask), + ui32WPosVal, + ui32PacketSize, + ui32BufferSize); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : PDumpIDLWithFlags + * Inputs : Idle time in clocks + * Outputs : None + * Returns : Error + * Description : Dump IDL command to script +**************************************************************************/ +PVRSRV_ERROR PDumpIDLWithFlags(IMG_UINT32 ui32Clocks, IMG_UINT32 ui32Flags) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + PDUMP_DBG(("PDumpIDLWithFlags")); + + eErr = PDumpOSBufprintf(hScript, ui32MaxLen, "IDL %u\r\n", ui32Clocks); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, ui32Flags); + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : PDumpIDL + * Inputs : Idle time in clocks + * Outputs : None + * Returns : Error + * Description : Dump IDL command to script +**************************************************************************/ +PVRSRV_ERROR PDumpIDL(IMG_UINT32 ui32Clocks) +{ + return PDumpIDLWithFlags(ui32Clocks, PDUMP_FLAGS_CONTINUOUS); +} + +/************************************************************************** + * Function Name : PDumpMemUM + * Inputs : pvAltLinAddrUM + * : pvLinAddrUM + * : psMemInfo + * : ui32Offset + * : ui32Bytes + * : ui32Flags + * : hUniqueTag + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Dump user mode memory +**************************************************************************/ +PVRSRV_ERROR PDumpMemUM(PVRSRV_PER_PROCESS_DATA *psPerProc, + IMG_PVOID pvAltLinAddrUM, + IMG_PVOID pvLinAddrUM, + PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 ui32Offset, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_HANDLE hUniqueTag) +{ + IMG_VOID *pvAddrUM; + IMG_VOID *pvAddrKM; + PVRSRV_ERROR eError; + + if (psMemInfo->pvLinAddrKM != IMG_NULL && pvAltLinAddrUM == IMG_NULL) + { + /* + * There is a kernel virtual address for the memory that is + * being dumped, and no alternate user mode linear address. + */ + return PDumpMemKM(IMG_NULL, + psMemInfo, + ui32Offset, + ui32Bytes, + ui32Flags, + hUniqueTag); + } + + pvAddrUM = (pvAltLinAddrUM != IMG_NULL) ? pvAltLinAddrUM : ((pvLinAddrUM != IMG_NULL) ? VPTR_PLUS(pvLinAddrUM, ui32Offset) : IMG_NULL); + + pvAddrKM = GetTempBuffer(); + + /* + * The memory to be dumped needs to be copied in from + * the client. Dump the memory, a buffer at a time. + */ + PVR_ASSERT(pvAddrUM != IMG_NULL && pvAddrKM != IMG_NULL); + if (pvAddrUM == IMG_NULL || pvAddrKM == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpMemUM: Nothing to dump")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (ui32Bytes > PDUMP_TEMP_BUFFER_SIZE) + { + PDumpCommentWithFlags(ui32Flags, "Dumping 0x%08x bytes of memory, in blocks of 0x%08x bytes", ui32Bytes, (IMG_UINT32)PDUMP_TEMP_BUFFER_SIZE); + } + + if (psMemInfo->ui32Flags & PVRSRV_MEM_SPARSE) + { + /* + In case of sparse mappings we can't just copy the full range as not + all pages are valid, instead we walk a page at a time only dumping + if the a page exists at that address + */ + IMG_UINT32 ui32BytesRemain = ui32Bytes; + IMG_UINT32 ui32InPageStart = ui32Offset & (~HOST_PAGEMASK); + IMG_UINT32 ui32PageOffset = ui32Offset & (HOST_PAGEMASK); + IMG_UINT32 ui32BytesToCopy = MIN(HOST_PAGESIZE() - ui32InPageStart, ui32BytesRemain); + + do + { + if (BM_MapPageAtOffset(BM_MappingHandleFromBuffer(psMemInfo->sMemBlk.hBuffer), ui32PageOffset)) + { + eError = OSCopyFromUser(psPerProc, + pvAddrKM, + pvAddrUM, + ui32BytesToCopy); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpMemUM: OSCopyFromUser failed (%d)", eError)); + return eError; + } + + /* + At this point we know we're dumping a valid page so call + the internal function + */ + eError = _PDumpMemIntKM(pvAddrKM, + psMemInfo, + ui32PageOffset + ui32InPageStart, + ui32BytesToCopy, + ui32Flags, + hUniqueTag); + + if (eError != PVRSRV_OK) + { + /* + * If writing fails part way through, then some + * investigation is needed. + */ + if (ui32BytesToCopy != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpMemUM: PDumpMemKM failed (%d)", eError)); + } + PVR_ASSERT(ui32BytesToCopy == 0); + return eError; + } + } + + VPTR_INC(pvAddrUM, ui32BytesToCopy); + ui32BytesRemain -= ui32BytesToCopy; + ui32InPageStart = 0; + ui32PageOffset += HOST_PAGESIZE(); + } while(ui32BytesRemain); + } + else + { + IMG_UINT32 ui32CurrentOffset = ui32Offset; + IMG_UINT32 ui32BytesDumped; + + for (ui32BytesDumped = 0; ui32BytesDumped < ui32Bytes;) + { + IMG_UINT32 ui32BytesToDump = MIN(PDUMP_TEMP_BUFFER_SIZE, ui32Bytes - ui32BytesDumped); + + eError = OSCopyFromUser(psPerProc, + pvAddrKM, + pvAddrUM, + ui32BytesToDump); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpMemUM: OSCopyFromUser failed (%d)", eError)); + return eError; + } + + eError = PDumpMemKM(pvAddrKM, + psMemInfo, + ui32CurrentOffset, + ui32BytesToDump, + ui32Flags, + hUniqueTag); + + if (eError != PVRSRV_OK) + { + /* + * If writing fails part way through, then some + * investigation is needed. + */ + if (ui32BytesDumped != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpMemUM: PDumpMemKM failed (%d)", eError)); + } + PVR_ASSERT(ui32BytesDumped == 0); + return eError; + } + + VPTR_INC(pvAddrUM, ui32BytesToDump); + ui32CurrentOffset += ui32BytesToDump; + ui32BytesDumped += ui32BytesToDump; + } + } + + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : _PdumpAllocMMUContext + * Inputs : pui32MMUContextID + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : pdump util to allocate MMU contexts +**************************************************************************/ +static PVRSRV_ERROR _PdumpAllocMMUContext(IMG_UINT32 *pui32MMUContextID) +{ + IMG_UINT32 i; + + /* there are MAX_PDUMP_MMU_CONTEXTS contexts available, find one */ + for(i=0; i<MAX_PDUMP_MMU_CONTEXTS; i++) + { + if((gui16MMUContextUsage & (1U << i)) == 0) + { + /* mark in use */ + gui16MMUContextUsage |= 1U << i; + *pui32MMUContextID = i; + return PVRSRV_OK; + } + } + + PVR_DPF((PVR_DBG_ERROR, "_PdumpAllocMMUContext: no free MMU context ids")); + + return PVRSRV_ERROR_MMU_CONTEXT_NOT_FOUND; +} + + +/************************************************************************** + * Function Name : _PdumpFreeMMUContext + * Inputs : ui32MMUContextID + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : pdump util to free MMU contexts +**************************************************************************/ +static PVRSRV_ERROR _PdumpFreeMMUContext(IMG_UINT32 ui32MMUContextID) +{ + if(ui32MMUContextID < MAX_PDUMP_MMU_CONTEXTS) + { + /* free the id */ + gui16MMUContextUsage &= ~(1U << ui32MMUContextID); + return PVRSRV_OK; + } + + PVR_DPF((PVR_DBG_ERROR, "_PdumpFreeMMUContext: MMU context ids invalid")); + + return PVRSRV_ERROR_MMU_CONTEXT_NOT_FOUND; +} + + +/************************************************************************** + * Function Name : PDumpSetMMUContext + * Inputs : + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Set MMU Context +**************************************************************************/ +PVRSRV_ERROR PDumpSetMMUContext(PVRSRV_DEVICE_TYPE eDeviceType, + IMG_CHAR *pszMemSpace, + IMG_UINT32 *pui32MMUContextID, + IMG_UINT32 ui32MMUType, + IMG_HANDLE hUniqueTag1, + IMG_HANDLE hOSMemHandle, + IMG_VOID *pvPDCPUAddr) +{ + IMG_UINT8 *pui8LinAddr = (IMG_UINT8 *)pvPDCPUAddr; + IMG_CPU_PHYADDR sCpuPAddr; + IMG_DEV_PHYADDR sDevPAddr; + IMG_UINT32 ui32MMUContextID; + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + + eErr = _PdumpAllocMMUContext(&ui32MMUContextID); + if(eErr != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpSetMMUContext: _PdumpAllocMMUContext failed: %d", eErr)); + return eErr; + } + + /* derive the DevPAddr */ + /* FIXME: if we used OSMemHandleToCPUPAddr() here, we could lose the lin addr arg */ + sCpuPAddr = OSMapLinToCPUPhys(hOSMemHandle, pui8LinAddr); + sDevPAddr = SysCpuPAddrToDevPAddr(eDeviceType, sCpuPAddr); + /* and round to 4k page */ + sDevPAddr.uiAddr &= ~((PVRSRV_4K_PAGE_SIZE) -1); + + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "MMU :%s:v%d %d :%s:PA_%08X%08X\r\n", + pszMemSpace, + ui32MMUContextID, + ui32MMUType, + pszMemSpace, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag1, + sDevPAddr.uiAddr); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, PDUMP_FLAGS_CONTINUOUS); + + /* return the MMU Context ID */ + *pui32MMUContextID = ui32MMUContextID; + + return PVRSRV_OK; +} + + +/************************************************************************** + * Function Name : PDumpClearMMUContext + * Inputs : + * Outputs : None + * Returns : PVRSRV_ERROR + * Description : Clear MMU Context +**************************************************************************/ +PVRSRV_ERROR PDumpClearMMUContext(PVRSRV_DEVICE_TYPE eDeviceType, + IMG_CHAR *pszMemSpace, + IMG_UINT32 ui32MMUContextID, + IMG_UINT32 ui32MMUType) +{ + PVRSRV_ERROR eErr; + PDUMP_GET_SCRIPT_STRING(); + PVR_UNREFERENCED_PARAMETER(eDeviceType); + PVR_UNREFERENCED_PARAMETER(ui32MMUType); + + /* FIXME: Propagate error from PDumpComment once it's supported on + * all OSes and platforms + */ + PDumpComment("Clear MMU Context for memory space %s\r\n", pszMemSpace); + eErr = PDumpOSBufprintf(hScript, + ui32MaxLen, + "MMU :%s:v%d\r\n", + pszMemSpace, + ui32MMUContextID); + if(eErr != PVRSRV_OK) + { + return eErr; + } + PDumpOSWriteString2(hScript, PDUMP_FLAGS_CONTINUOUS); + + eErr = _PdumpFreeMMUContext(ui32MMUContextID); + if(eErr != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PDumpClearMMUContext: _PdumpFreeMMUContext failed: %d", eErr)); + return eErr; + } + + return PVRSRV_OK; +} + +/***************************************************************************** + FUNCTION : PDumpStoreMemToFile + + PURPOSE : Dumps a given addr:size to a file + + PARAMETERS : + + RETURNS : +*****************************************************************************/ +PVRSRV_ERROR PDumpStoreMemToFile(PDUMP_MMU_ATTRIB *psMMUAttrib, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32FileOffset, + PVRSRV_KERNEL_MEM_INFO *psMemInfo, + IMG_UINT32 uiAddr, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32PDumpFlags, + IMG_HANDLE hUniqueTag) +{ + IMG_DEV_PHYADDR sDevPAddr; + IMG_DEV_VIRTADDR sDevVPageAddr; + IMG_UINT32 ui32PageOffset; + + PDUMP_GET_SCRIPT_STRING(); + + /* + query the buffer manager for the physical pages that back the + virtual address + */ + ui32PageOffset = (IMG_UINT32)((IMG_UINTPTR_T)psMemInfo->pvLinAddrKM & psMMUAttrib->ui32DataPageMask); + + /* calculate the DevV page address */ + sDevVPageAddr.uiAddr = uiAddr - ui32PageOffset; + + /* get the physical page address based on the device virtual address */ + BM_GetPhysPageAddr(psMemInfo, sDevVPageAddr, &sDevPAddr); + + /* convert DevP page address to byte address */ + sDevPAddr.uiAddr += ui32PageOffset; + + PDumpOSBufprintf(hScript, + ui32MaxLen, + "SAB :%s:PA_%08X%08X:0x%08X 0x%08X 0x%08X %s\r\n", + psMMUAttrib->sDevId.pszPDumpDevName, + (IMG_UINT32)(IMG_UINTPTR_T)hUniqueTag, + sDevPAddr.uiAddr & ~psMMUAttrib->ui32DataPageMask, + sDevPAddr.uiAddr & psMMUAttrib->ui32DataPageMask, + ui32Size, + ui32FileOffset, + pszFileName); + + PDumpOSWriteString2(hScript, ui32PDumpFlags); + + return PVRSRV_OK; +} + +/***************************************************************************** + FUNCTION : PDumpRegBasedCBP + + PURPOSE : Dump CBP command to script + + PARAMETERS : + + RETURNS : None +*****************************************************************************/ +PVRSRV_ERROR PDumpRegBasedCBP(IMG_CHAR *pszPDumpRegName, + IMG_UINT32 ui32RegOffset, + IMG_UINT32 ui32WPosVal, + IMG_UINT32 ui32PacketSize, + IMG_UINT32 ui32BufferSize, + IMG_UINT32 ui32Flags) +{ + PDUMP_GET_SCRIPT_STRING(); + + PDumpOSBufprintf(hScript, + ui32MaxLen, + "CBP :%s:0x%08X 0x%08X 0x%08X 0x%08X\r\n", + pszPDumpRegName, + ui32RegOffset, + ui32WPosVal, + ui32PacketSize, + ui32BufferSize); + PDumpOSWriteString2(hScript, ui32Flags); + + return PVRSRV_OK; +} + + +/**************************************************** + * Non-uitron code here. + * For example, code communicating with dbg driver. + ***************************************************/ +/* PRQA S 5087 1 */ /* include file needed here */ +#include "syscommon.h" + +/************************************************************************** + * Function Name : PDumpConnectionNotify + * Description : Called by the debugdrv to tell Services that pdump has + * connected + * NOTE: No debugdrv on uitron. + **************************************************************************/ +IMG_EXPORT IMG_VOID PDumpConnectionNotify(IMG_VOID) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE *psThis; + PVR_DPF((PVR_DBG_WARNING, "PDump has connected.")); + + /* Loop over all known devices */ + SysAcquireData(&psSysData); + + psThis = psSysData->psDeviceNodeList; + while (psThis) + { + if (psThis->pfnPDumpInitDevice) + { + /* Reset pdump according to connected device */ + psThis->pfnPDumpInitDevice(psThis); + } + psThis = psThis->psNext; + } +} + +/***************************************************************************** + * Function Name : DbgWrite + * Inputs : psStream - debug stream to write to + pui8Data - buffer + ui32BCount - buffer length + ui32Flags - flags, e.g. continuous, LF + * Outputs : None + * Returns : Bytes written + * Description : Write a block of data to a debug stream + * NOTE: No debugdrv on uitron. + *****************************************************************************/ +IMG_UINT32 DbgWrite(PDBG_STREAM psStream, IMG_UINT8 *pui8Data, IMG_UINT32 ui32BCount, IMG_UINT32 ui32Flags) +{ + IMG_UINT32 ui32BytesWritten = 0; + IMG_UINT32 ui32Off = 0; + PDBG_STREAM_CONTROL psCtrl = psStream->psCtrl; + + /* Return immediately if marked as "never" */ + if ((ui32Flags & PDUMP_FLAGS_NEVER) != 0) + { + return ui32BCount; + } + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + /* Return if process is not marked for pdumping, unless it's persistent. + */ + if ( (_PDumpIsProcessActive() == IMG_FALSE ) && + ((ui32Flags & PDUMP_FLAGS_PERSISTENT) == 0) ) + { + return ui32BCount; + } +#endif + + /* Send persistent data first ... + * If we're still initialising the params will be captured to the + * init stream in the call to pfnDBGDrivWrite2 below. + */ + if ( ((ui32Flags & PDUMP_FLAGS_PERSISTENT) != 0) && (psCtrl->bInitPhaseComplete) ) + { + while (ui32BCount > 0) + { + /* + Params marked as persistent should be appended to the init phase. + For example window system mem mapping of the primary surface. + */ + ui32BytesWritten = PDumpOSDebugDriverWrite( psStream, + PDUMP_WRITE_MODE_PERSISTENT, + &pui8Data[ui32Off], ui32BCount, 1, 0); + + if (ui32BytesWritten == 0) + { + PDumpOSReleaseExecution(); + } + + if (ui32BytesWritten != 0xFFFFFFFFU) + { + ui32Off += ui32BytesWritten; + ui32BCount -= ui32BytesWritten; + } + else + { + PVR_DPF((PVR_DBG_ERROR, "DbgWrite: Failed to send persistent data")); + if( (psCtrl->ui32Flags & DEBUG_FLAGS_READONLY) != 0) + { + /* suspend pdump to prevent flooding kernel log buffer */ + PDumpSuspendKM(); + } + return 0xFFFFFFFFU; + } + } + + /* reset buffer counters */ + ui32BCount = ui32Off; ui32Off = 0; ui32BytesWritten = 0; + } + + while (((IMG_UINT32) ui32BCount > 0) && (ui32BytesWritten != 0xFFFFFFFFU)) + { + if ((ui32Flags & PDUMP_FLAGS_CONTINUOUS) != 0) + { + /* + If pdump client (or its equivalent) isn't running then throw continuous data away. + */ + if (((psCtrl->ui32CapMode & DEBUG_CAPMODE_FRAMED) != 0) && + (psCtrl->ui32Start == 0xFFFFFFFFU) && + (psCtrl->ui32End == 0xFFFFFFFFU) && + psCtrl->bInitPhaseComplete) + { + ui32BytesWritten = ui32BCount; + } + else + { + ui32BytesWritten = PDumpOSDebugDriverWrite( psStream, + PDUMP_WRITE_MODE_CONTINUOUS, + &pui8Data[ui32Off], ui32BCount, 1, 0); + } + } + else + { + if (ui32Flags & PDUMP_FLAGS_LASTFRAME) + { + IMG_UINT32 ui32DbgFlags; + + ui32DbgFlags = 0; + if (ui32Flags & PDUMP_FLAGS_RESETLFBUFFER) + { + ui32DbgFlags |= WRITELF_FLAGS_RESETBUF; + } + + ui32BytesWritten = PDumpOSDebugDriverWrite( psStream, + PDUMP_WRITE_MODE_LASTFRAME, + &pui8Data[ui32Off], ui32BCount, 1, ui32DbgFlags); + } + else + { + ui32BytesWritten = PDumpOSDebugDriverWrite( psStream, + PDUMP_WRITE_MODE_BINCM, + &pui8Data[ui32Off], ui32BCount, 1, 0); + } + } + + /* + If the debug driver's buffers are full so no data could be written then yield + execution so pdump can run and empty them. + */ + if (ui32BytesWritten == 0) + { + PDumpOSReleaseExecution(); + } + + if (ui32BytesWritten != 0xFFFFFFFFU) + { + ui32Off += ui32BytesWritten; + ui32BCount -= ui32BytesWritten; + } + + /* loop exits when i) all data is written, or ii) an unrecoverable error occurs */ + } + + return ui32BytesWritten; +} + + + +#else /* defined(PDUMP) */ +/* disable warning about empty module */ +#endif /* defined(PDUMP) */ +/***************************************************************************** + End of file (pdump_common.c) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/perproc.c b/pvr-source/services4/srvkm/common/perproc.c new file mode 100644 index 0000000..3918bb2 --- /dev/null +++ b/pvr-source/services4/srvkm/common/perproc.c @@ -0,0 +1,398 @@ +/*************************************************************************/ /*! +@Title Per-process storage +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Manage per-process storage +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "resman.h" +#include "handle.h" +#include "perproc.h" +#include "osperproc.h" +#if defined(TTRACE) +#include "ttrace.h" +#endif + +#define HASH_TAB_INIT_SIZE 32 + +static HASH_TABLE *psHashTab = IMG_NULL; + +/*! +****************************************************************************** + + @Function FreePerProcData + + @Description Free a per-process data area + + @Input psPerProc - pointer to per-process data area + + @Return Error code, or PVRSRV_OK + +******************************************************************************/ +static PVRSRV_ERROR FreePerProcessData(PVRSRV_PER_PROCESS_DATA *psPerProc) +{ + PVRSRV_ERROR eError; + IMG_UINTPTR_T uiPerProc; + + PVR_ASSERT(psPerProc != IMG_NULL); + + if (psPerProc == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: invalid parameter")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + uiPerProc = HASH_Remove(psHashTab, (IMG_UINTPTR_T)psPerProc->ui32PID); + if (uiPerProc == 0) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: Couldn't find process in per-process data hash table")); + /* + * We must have failed early in the per-process data area + * creation, before the process ID was set. + */ + PVR_ASSERT(psPerProc->ui32PID == 0); + } + else + { + PVR_ASSERT((PVRSRV_PER_PROCESS_DATA *)uiPerProc == psPerProc); + PVR_ASSERT(((PVRSRV_PER_PROCESS_DATA *)uiPerProc)->ui32PID == psPerProc->ui32PID); + } + + /* Free handle base for this process */ + if (psPerProc->psHandleBase != IMG_NULL) + { + eError = PVRSRVFreeHandleBase(psPerProc->psHandleBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: Couldn't free handle base for process (%d)", eError)); + return eError; + } + } + + /* Release handle for per-process data area */ + if (psPerProc->hPerProcData != IMG_NULL) + { + eError = PVRSRVReleaseHandle(KERNEL_HANDLE_BASE, psPerProc->hPerProcData, PVRSRV_HANDLE_TYPE_PERPROC_DATA); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: Couldn't release per-process data handle (%d)", eError)); + return eError; + } + } + + /* Call environment specific per process deinit function */ + eError = OSPerProcessPrivateDataDeInit(psPerProc->hOsPrivateData); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: OSPerProcessPrivateDataDeInit failed (%d)", eError)); + return eError; + } + + eError = OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(*psPerProc), + psPerProc, + psPerProc->hBlockAlloc); + /*not nulling pointer, copy on stack*/ + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "FreePerProcessData: Couldn't free per-process data (%d)", eError)); + return eError; + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVPerProcessData + + @Description Return per-process data area + + @Input ui32PID - process ID + + @Return Pointer to per-process data area, or IMG_NULL on error. + +******************************************************************************/ +PVRSRV_PER_PROCESS_DATA *PVRSRVPerProcessData(IMG_UINT32 ui32PID) +{ + PVRSRV_PER_PROCESS_DATA *psPerProc; + + PVR_ASSERT(psHashTab != IMG_NULL); + + /* Look for existing per-process data area */ + psPerProc = (PVRSRV_PER_PROCESS_DATA *)HASH_Retrieve(psHashTab, (IMG_UINTPTR_T)ui32PID); + return psPerProc; +} + + +/*! +****************************************************************************** + + @Function PVRSRVPerProcessDataConnect + + @Description Allocate per-process data area, or increment refcount if one + already exists for this PID. + + @Input ui32PID - process ID + ppsPerProc - Pointer to per-process data area + + @Return PVRSRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR PVRSRVPerProcessDataConnect(IMG_UINT32 ui32PID, IMG_UINT32 ui32Flags) +{ + PVRSRV_PER_PROCESS_DATA *psPerProc; + IMG_HANDLE hBlockAlloc; + PVRSRV_ERROR eError = PVRSRV_OK; + + if (psHashTab == IMG_NULL) + { + return PVRSRV_ERROR_INIT_FAILURE; + } + + /* Look for existing per-process data area */ + psPerProc = (PVRSRV_PER_PROCESS_DATA *)HASH_Retrieve(psHashTab, (IMG_UINTPTR_T)ui32PID); + + if (psPerProc == IMG_NULL) + { + /* Allocate per-process data area */ + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(*psPerProc), + (IMG_PVOID *)&psPerProc, + &hBlockAlloc, + "Per Process Data"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't allocate per-process data (%d)", eError)); + return eError; + } + OSMemSet(psPerProc, 0, sizeof(*psPerProc)); + psPerProc->hBlockAlloc = hBlockAlloc; + + if (!HASH_Insert(psHashTab, (IMG_UINTPTR_T)ui32PID, (IMG_UINTPTR_T)psPerProc)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't insert per-process data into hash table")); + eError = PVRSRV_ERROR_INSERT_HASH_TABLE_DATA_FAILED; + goto failure; + } + + psPerProc->ui32PID = ui32PID; + psPerProc->ui32RefCount = 0; + +#if defined(SUPPORT_PDUMP_MULTI_PROCESS) + if (ui32Flags == SRV_FLAGS_PDUMP_ACTIVE) + { + psPerProc->bPDumpActive = IMG_TRUE; + } +#else + PVR_UNREFERENCED_PARAMETER(ui32Flags); +#endif + + /* Call environment specific per process init function */ + eError = OSPerProcessPrivateDataInit(&psPerProc->hOsPrivateData); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: OSPerProcessPrivateDataInit failed (%d)", eError)); + goto failure; + } + + /* Allocate a handle for the per-process data area */ + eError = PVRSRVAllocHandle(KERNEL_HANDLE_BASE, + &psPerProc->hPerProcData, + psPerProc, + PVRSRV_HANDLE_TYPE_PERPROC_DATA, + PVRSRV_HANDLE_ALLOC_FLAG_NONE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't allocate handle for per-process data (%d)", eError)); + goto failure; + } + + /* Allocate handle base for this process */ + eError = PVRSRVAllocHandleBase(&psPerProc->psHandleBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't allocate handle base for process (%d)", eError)); + goto failure; + } + + /* Set per-process handle options */ + eError = OSPerProcessSetHandleOptions(psPerProc->psHandleBase); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't set handle options (%d)", eError)); + goto failure; + } + + /* Create a resource manager context for the process */ + eError = PVRSRVResManConnect(psPerProc, &psPerProc->hResManContext); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataConnect: Couldn't register with the resource manager")); + goto failure; + } +#if defined (TTRACE) + PVRSRVTimeTraceBufferCreate(ui32PID); +#endif + } + + psPerProc->ui32RefCount++; + PVR_DPF((PVR_DBG_MESSAGE, + "PVRSRVPerProcessDataConnect: Process 0x%x has ref-count %d", + ui32PID, psPerProc->ui32RefCount)); + + return eError; + +failure: + (IMG_VOID)FreePerProcessData(psPerProc); + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVPerProcessDataDisconnect + + @Description Decrement refcount for per-process data area, + and free the resources if necessary. + + @Input ui32PID - process ID + + @Return IMG_VOID + +******************************************************************************/ +IMG_VOID PVRSRVPerProcessDataDisconnect(IMG_UINT32 ui32PID) +{ + PVRSRV_ERROR eError; + PVRSRV_PER_PROCESS_DATA *psPerProc; + + PVR_ASSERT(psHashTab != IMG_NULL); + + psPerProc = (PVRSRV_PER_PROCESS_DATA *)HASH_Retrieve(psHashTab, (IMG_UINTPTR_T)ui32PID); + if (psPerProc == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataDealloc: Couldn't locate per-process data for PID %u", ui32PID)); + } + else + { + psPerProc->ui32RefCount--; + if (psPerProc->ui32RefCount == 0) + { + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVPerProcessDataDisconnect: " + "Last close from process 0x%x received", ui32PID)); + + /* Close the Resource Manager connection */ + PVRSRVResManDisconnect(psPerProc->hResManContext, IMG_FALSE); + +#if defined (TTRACE) + PVRSRVTimeTraceBufferDestroy(ui32PID); +#endif + + /* Free the per-process data */ + eError = FreePerProcessData(psPerProc); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataDisconnect: Error freeing per-process data")); + } + } + } + + eError = PVRSRVPurgeHandles(KERNEL_HANDLE_BASE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataDisconnect: Purge of global handle pool failed (%d)", eError)); + } +} + + +/*! +****************************************************************************** + + @Function PVRSRVPerProcessDataInit + + @Description Initialise per-process data management + + @Return Error code, or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVPerProcessDataInit(IMG_VOID) +{ + PVR_ASSERT(psHashTab == IMG_NULL); + + /* Create hash table */ + psHashTab = HASH_Create(HASH_TAB_INIT_SIZE); + if (psHashTab == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVPerProcessDataInit: Couldn't create per-process data hash table")); + return PVRSRV_ERROR_UNABLE_TO_CREATE_HASH_TABLE; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVPerProcessDataDeInit + + @Description De-initialise per-process data management + + @Return Error code, or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVPerProcessDataDeInit(IMG_VOID) +{ + /* Destroy per-process data area hash table */ + if (psHashTab != IMG_NULL) + { + /* Free the hash table */ + HASH_Delete(psHashTab); + psHashTab = IMG_NULL; + } + + return PVRSRV_OK; +} + +/****************************************************************************** + End of file (perproc.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/power.c b/pvr-source/services4/srvkm/common/power.c new file mode 100644 index 0000000..511a690 --- /dev/null +++ b/pvr-source/services4/srvkm/common/power.c @@ -0,0 +1,996 @@ +/*************************************************************************/ /*! +@Title Power management functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Main APIs for power management functions +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "pdump_km.h" + +#include "lists.h" + +static IMG_BOOL gbInitServerRunning = IMG_FALSE; +static IMG_BOOL gbInitServerRan = IMG_FALSE; +static IMG_BOOL gbInitSuccessful = IMG_FALSE; + +/*! +****************************************************************************** + + @Function PVRSRVSetInitServerState + + @Description Sets given services init state. + + @Input eInitServerState : a services init state + @Input bState : a state to set + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetInitServerState(PVRSRV_INIT_SERVER_STATE eInitServerState, IMG_BOOL bState) +{ + + switch(eInitServerState) + { + case PVRSRV_INIT_SERVER_RUNNING: + gbInitServerRunning = bState; + break; + case PVRSRV_INIT_SERVER_RAN: + gbInitServerRan = bState; + break; + case PVRSRV_INIT_SERVER_SUCCESSFUL: + gbInitSuccessful = bState; + break; + default: + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVSetInitServerState : Unknown state %x", eInitServerState)); + return PVRSRV_ERROR_UNKNOWN_INIT_SERVER_STATE; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVGetInitServerState + + @Description Tests whether a given services init state was run. + + @Input eInitServerState : a services init state + + @Return IMG_BOOL + +******************************************************************************/ +IMG_EXPORT +IMG_BOOL PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_STATE eInitServerState) +{ + IMG_BOOL bReturnVal; + + switch(eInitServerState) + { + case PVRSRV_INIT_SERVER_RUNNING: + bReturnVal = gbInitServerRunning; + break; + case PVRSRV_INIT_SERVER_RAN: + bReturnVal = gbInitServerRan; + break; + case PVRSRV_INIT_SERVER_SUCCESSFUL: + bReturnVal = gbInitSuccessful; + break; + default: + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVGetInitServerState : Unknown state %x", eInitServerState)); + bReturnVal = IMG_FALSE; + } + + return bReturnVal; +} + +/*! +****************************************************************************** + + @Function _IsSystemStatePowered + + @Description Tests whether a given system state represents powered-up. + + @Input eSystemPowerState : a system power state + + @Return IMG_BOOL + +******************************************************************************/ +static IMG_BOOL _IsSystemStatePowered(PVRSRV_SYS_POWER_STATE eSystemPowerState) +{ + return (IMG_BOOL)(eSystemPowerState < PVRSRV_SYS_POWER_STATE_D2); +} + + +/*! +****************************************************************************** + + @Function PVRSRVPowerLock + + @Description Obtain the mutex for power transitions + + @Input ui32CallerID : KERNEL_ID or ISR_ID + @Input bSystemPowerEvent : Only pass IMG_TRUE if the lock is for a + system power state change + + @Return PVRSRV_ERROR IMG_CALLCONV + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVPowerLock(IMG_UINT32 ui32CallerID, + IMG_BOOL bSystemPowerEvent) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + IMG_UINT32 ui32Timeout = 1000000; + IMG_BOOL bTryLock = (ui32CallerID == ISR_ID); + + SysAcquireData(&psSysData); + + eError = OSPowerLockWrap(bTryLock); + if (eError != PVRSRV_OK) + { + return eError; + } + + do + { + eError = OSLockResource(&psSysData->sPowerStateChangeResource, + ui32CallerID); + if (eError == PVRSRV_OK) + { + break; + } + else if (bTryLock) + { + /* + ISR failed to acquire lock so it must be held by a kernel thread. + */ + eError = PVRSRV_ERROR_RETRY; + break; + } + + OSWaitus(1); + ui32Timeout--; + } while (ui32Timeout > 0); + + if (eError != PVRSRV_OK) + { + OSPowerLockUnwrap(); + } + + /* PRQA S 3415 3 */ /* side effects desired */ + if ((eError == PVRSRV_OK) && + !bSystemPowerEvent && + !_IsSystemStatePowered(psSysData->eCurrentPowerState)) + { + /* Reject device power state change due to system power state. */ + PVRSRVPowerUnlock(ui32CallerID); + eError = PVRSRV_ERROR_RETRY; + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVPowerUnlock + + @Description Release the mutex for power transitions + + @Input ui32CallerID : KERNEL_ID or ISR_ID + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +IMG_VOID PVRSRVPowerUnlock(IMG_UINT32 ui32CallerID) +{ + OSUnlockResource(&gpsSysData->sPowerStateChangeResource, ui32CallerID); + OSPowerLockUnwrap(); +} + + +/*! +****************************************************************************** + + @Function PVRSRVDevicePrePowerStateKM_AnyVaCb + + @Description + + Perform device-specific processing required before a power transition + + @Input psPowerDevice : the device + @Input va : variable argument list with: + bAllDevices : IMG_TRUE - All devices + IMG_FALSE - Use ui32DeviceIndex + ui32DeviceIndex : device index + eNewPowerState : New power state + + @Return PVRSRV_ERROR + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVDevicePrePowerStateKM_AnyVaCb(PVRSRV_POWER_DEV *psPowerDevice, va_list va) +{ + PVRSRV_DEV_POWER_STATE eNewDevicePowerState; + PVRSRV_ERROR eError; + + /*Variable Argument variables*/ + IMG_BOOL bAllDevices; + IMG_UINT32 ui32DeviceIndex; + PVRSRV_DEV_POWER_STATE eNewPowerState; + + /* WARNING: if types were not aligned to 4 bytes, this could be dangerous. */ + bAllDevices = va_arg(va, IMG_BOOL); + ui32DeviceIndex = va_arg(va, IMG_UINT32); + eNewPowerState = va_arg(va, PVRSRV_DEV_POWER_STATE); + + if (bAllDevices || (ui32DeviceIndex == psPowerDevice->ui32DeviceIndex)) + { + eNewDevicePowerState = (eNewPowerState == PVRSRV_DEV_POWER_STATE_DEFAULT) ? + psPowerDevice->eDefaultPowerState : eNewPowerState; + + if (psPowerDevice->eCurrentPowerState != eNewDevicePowerState) + { + if (psPowerDevice->pfnPrePower != IMG_NULL) + { + /* Call the device's power callback. */ + eError = psPowerDevice->pfnPrePower(psPowerDevice->hDevCookie, + eNewDevicePowerState, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + return eError; + } + } + + /* Do any required system-layer processing. */ + eError = SysDevicePrePowerState(psPowerDevice->ui32DeviceIndex, + eNewDevicePowerState, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + return eError; + } + } + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVDevicePrePowerStateKM + + @Description + + Perform device-specific processing required before a power transition + + @Input bAllDevices : IMG_TRUE - All devices + IMG_FALSE - Use ui32DeviceIndex + @Input ui32DeviceIndex : device index + @Input eNewPowerState : New power state + + @Return PVRSRV_ERROR + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVDevicePrePowerStateKM(IMG_BOOL bAllDevices, + IMG_UINT32 ui32DeviceIndex, + PVRSRV_DEV_POWER_STATE eNewPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + /* Loop through the power devices. */ + eError = List_PVRSRV_POWER_DEV_PVRSRV_ERROR_Any_va(psSysData->psPowerDeviceList, + &PVRSRVDevicePrePowerStateKM_AnyVaCb, + bAllDevices, + ui32DeviceIndex, + eNewPowerState); + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVDevicePostPowerStateKM_AnyVaCb + + @Description + + Perform device-specific processing required after a power transition + + @Input psPowerDevice : the device + @Input va : variable argument list with: + bAllDevices : IMG_TRUE - All devices + IMG_FALSE - Use ui32DeviceIndex + ui32DeviceIndex : device index + eNewPowerState : New power state + + @Return PVRSRV_ERROR + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVDevicePostPowerStateKM_AnyVaCb(PVRSRV_POWER_DEV *psPowerDevice, va_list va) +{ + PVRSRV_DEV_POWER_STATE eNewDevicePowerState; + PVRSRV_ERROR eError; + + /*Variable Argument variables*/ + IMG_BOOL bAllDevices; + IMG_UINT32 ui32DeviceIndex; + PVRSRV_DEV_POWER_STATE eNewPowerState; + + /* WARNING: if types were not aligned to 4 bytes, this could be dangerous. */ + bAllDevices = va_arg(va, IMG_BOOL); + ui32DeviceIndex = va_arg(va, IMG_UINT32); + eNewPowerState = va_arg(va, PVRSRV_DEV_POWER_STATE); + + if (bAllDevices || (ui32DeviceIndex == psPowerDevice->ui32DeviceIndex)) + { + eNewDevicePowerState = (eNewPowerState == PVRSRV_DEV_POWER_STATE_DEFAULT) ? + psPowerDevice->eDefaultPowerState : eNewPowerState; + + if (psPowerDevice->eCurrentPowerState != eNewDevicePowerState) + { + /* Do any required system-layer processing. */ + eError = SysDevicePostPowerState(psPowerDevice->ui32DeviceIndex, + eNewDevicePowerState, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + return eError; + } + + if (psPowerDevice->pfnPostPower != IMG_NULL) + { + /* Call the device's power callback. */ + eError = psPowerDevice->pfnPostPower(psPowerDevice->hDevCookie, + eNewDevicePowerState, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + return eError; + } + } + + psPowerDevice->eCurrentPowerState = eNewDevicePowerState; + } + } + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVDevicePostPowerStateKM + + @Description + + Perform device-specific processing required after a power transition + + @Input bAllDevices : IMG_TRUE - All devices + IMG_FALSE - Use ui32DeviceIndex + @Input ui32DeviceIndex : device index + @Input eNewPowerState : New power state + + @Return PVRSRV_ERROR + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVDevicePostPowerStateKM(IMG_BOOL bAllDevices, + IMG_UINT32 ui32DeviceIndex, + PVRSRV_DEV_POWER_STATE eNewPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + /* Loop through the power devices. */ + eError = List_PVRSRV_POWER_DEV_PVRSRV_ERROR_Any_va(psSysData->psPowerDeviceList, + &PVRSRVDevicePostPowerStateKM_AnyVaCb, + bAllDevices, + ui32DeviceIndex, + eNewPowerState); + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVSetDevicePowerStateKM + + @Description Set the Device into a new state + + @Input ui32DeviceIndex : device index + @Input eNewPowerState : New power state + @Input ui32CallerID : KERNEL_ID or ISR_ID + @Input bRetainMutex : If true, the power mutex is retained on exit + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetDevicePowerStateKM(IMG_UINT32 ui32DeviceIndex, + PVRSRV_DEV_POWER_STATE eNewPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + #if defined(PDUMP) + if (eNewPowerState == PVRSRV_DEV_POWER_STATE_DEFAULT) + { + /* + Pdump a power-up regardless of the default state. + Then disable pdump and transition to the default power state. + This ensures that a power-up is always present in the pdump when necessary. + */ + eError = PVRSRVDevicePrePowerStateKM(IMG_FALSE, ui32DeviceIndex, PVRSRV_DEV_POWER_STATE_ON); + if(eError != PVRSRV_OK) + { + goto Exit; + } + + eError = PVRSRVDevicePostPowerStateKM(IMG_FALSE, ui32DeviceIndex, PVRSRV_DEV_POWER_STATE_ON); + + if (eError != PVRSRV_OK) + { + goto Exit; + } + + PDUMPSUSPEND(); + } + #endif /* PDUMP */ + + eError = PVRSRVDevicePrePowerStateKM(IMG_FALSE, ui32DeviceIndex, eNewPowerState); + if(eError != PVRSRV_OK) + { + if (eNewPowerState == PVRSRV_DEV_POWER_STATE_DEFAULT) + { + PDUMPRESUME(); + } + goto Exit; + } + + eError = PVRSRVDevicePostPowerStateKM(IMG_FALSE, ui32DeviceIndex, eNewPowerState); + + if (eNewPowerState == PVRSRV_DEV_POWER_STATE_DEFAULT) + { + PDUMPRESUME(); + } + +Exit: + + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVSetDevicePowerStateKM : Transition to %d FAILED 0x%x", eNewPowerState, eError)); + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVSystemPrePowerStateKM + + @Description Perform processing required before a system power transition + + @Input eNewSysPowerState : + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVSystemPrePowerStateKM(PVRSRV_SYS_POWER_STATE eNewSysPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + PVRSRV_DEV_POWER_STATE eNewDevicePowerState; + + SysAcquireData(&psSysData); + + /* This mutex is unlocked in PVRSRVSystemPostPowerStateKM() */ + eError = PVRSRVPowerLock(KERNEL_ID, IMG_TRUE); + if(eError != PVRSRV_OK) + { + return eError; + } + + if (_IsSystemStatePowered(eNewSysPowerState) != + _IsSystemStatePowered(psSysData->eCurrentPowerState)) + { + if (_IsSystemStatePowered(eNewSysPowerState)) + { + /* Return device back to its default state. */ + eNewDevicePowerState = PVRSRV_DEV_POWER_STATE_DEFAULT; + } + else + { + eNewDevicePowerState = PVRSRV_DEV_POWER_STATE_OFF; + } + + /* Perform device-specific transitions. */ + eError = PVRSRVDevicePrePowerStateKM(IMG_TRUE, 0, eNewDevicePowerState); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + } + + if (eNewSysPowerState != psSysData->eCurrentPowerState) + { + /* Perform system-specific power transitions. */ + eError = SysSystemPrePowerState(eNewSysPowerState); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + } + + return eError; + +ErrorExit: + + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVSystemPrePowerStateKM: Transition from %d to %d FAILED 0x%x", + psSysData->eCurrentPowerState, eNewSysPowerState, eError)); + + /* save the power state for the re-attempt */ + psSysData->eFailedPowerState = eNewSysPowerState; + + PVRSRVPowerUnlock(KERNEL_ID); + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVSystemPostPowerStateKM + + @Description Perform processing required after a system power transition + + @Input eNewSysPowerState : + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVSystemPostPowerStateKM(PVRSRV_SYS_POWER_STATE eNewSysPowerState) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + SYS_DATA *psSysData; + PVRSRV_DEV_POWER_STATE eNewDevicePowerState; + + SysAcquireData(&psSysData); + + if (eNewSysPowerState != psSysData->eCurrentPowerState) + { + /* Perform system-specific power transitions. */ + eError = SysSystemPostPowerState(eNewSysPowerState); + if (eError != PVRSRV_OK) + { + goto Exit; + } + } + + if (_IsSystemStatePowered(eNewSysPowerState) != + _IsSystemStatePowered(psSysData->eCurrentPowerState)) + { + if (_IsSystemStatePowered(eNewSysPowerState)) + { + /* Return device back to its default state. */ + eNewDevicePowerState = PVRSRV_DEV_POWER_STATE_DEFAULT; + } + else + { + eNewDevicePowerState = PVRSRV_DEV_POWER_STATE_OFF; + } + + /* Perform device-specific power transitions. */ + eError = PVRSRVDevicePostPowerStateKM(IMG_TRUE, 0, eNewDevicePowerState); + if (eError != PVRSRV_OK) + { + goto Exit; + } + } + + PVR_DPF((PVR_DBG_MESSAGE, + "PVRSRVSystemPostPowerStateKM: System Power Transition from %d to %d OK", + psSysData->eCurrentPowerState, eNewSysPowerState)); + + psSysData->eCurrentPowerState = eNewSysPowerState; + +Exit: + + PVRSRVPowerUnlock(KERNEL_ID); + + /* PRQA S 3415 2 */ /* side effects desired */ + if (_IsSystemStatePowered(eNewSysPowerState) && + PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_SUCCESSFUL)) + { + /* + Reprocess the devices' queues in case commands were blocked during + the power transition. + */ + PVRSRVScheduleDeviceCallbacks(); + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVSetPowerStateKM + + @Description Set the system into a new state + + @Input eNewPowerState : + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE eNewSysPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + + SysAcquireData(&psSysData); + + eError = PVRSRVSystemPrePowerStateKM(eNewSysPowerState); + if(eError != PVRSRV_OK) + { + goto ErrorExit; + } + + eError = PVRSRVSystemPostPowerStateKM(eNewSysPowerState); + if(eError != PVRSRV_OK) + { + goto ErrorExit; + } + + /* save new power state */ + psSysData->eFailedPowerState = PVRSRV_SYS_POWER_STATE_Unspecified; + + return PVRSRV_OK; + +ErrorExit: + + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVSetPowerStateKM: Transition from %d to %d FAILED 0x%x", + psSysData->eCurrentPowerState, eNewSysPowerState, eError)); + + /* save the power state for the re-attempt */ + psSysData->eFailedPowerState = eNewSysPowerState; + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterPowerDevice + + @Description + + Registers a device with the power manager. Passes Pre/Post Power handlers + and private device handle to be passed to power handlers + + @Input ui32DeviceIndex : device index + @Input pfnPrePower : Pre power transition handler + @Input pfnPostPower : Post power transition handler + @Input pfnPreClockSpeedChange : Pre clock speed transition handler (if required) + @Input pfnPostClockSpeedChange : Post clock speed transition handler (if required) + @Input hDevCookie : Dev cookie for dev power handlers + @Input eCurrentPowerState : Current power state of the device + @Input eDefaultPowerState : Default power state of the device + + @Return PVRSRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR PVRSRVRegisterPowerDevice(IMG_UINT32 ui32DeviceIndex, + PFN_PRE_POWER pfnPrePower, + PFN_POST_POWER pfnPostPower, + PFN_PRE_CLOCKSPEED_CHANGE pfnPreClockSpeedChange, + PFN_POST_CLOCKSPEED_CHANGE pfnPostClockSpeedChange, + IMG_HANDLE hDevCookie, + PVRSRV_DEV_POWER_STATE eCurrentPowerState, + PVRSRV_DEV_POWER_STATE eDefaultPowerState) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + PVRSRV_POWER_DEV *psPowerDevice; + + if (pfnPrePower == IMG_NULL && + pfnPostPower == IMG_NULL) + { + return PVRSRVRemovePowerDevice(ui32DeviceIndex); + } + + SysAcquireData(&psSysData); + + eError = OSAllocMem( PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_POWER_DEV), + (IMG_VOID **)&psPowerDevice, IMG_NULL, + "Power Device"); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterPowerDevice: Failed to alloc PVRSRV_POWER_DEV")); + return eError; + } + + /* setup device for power manager */ + psPowerDevice->pfnPrePower = pfnPrePower; + psPowerDevice->pfnPostPower = pfnPostPower; + psPowerDevice->pfnPreClockSpeedChange = pfnPreClockSpeedChange; + psPowerDevice->pfnPostClockSpeedChange = pfnPostClockSpeedChange; + psPowerDevice->hDevCookie = hDevCookie; + psPowerDevice->ui32DeviceIndex = ui32DeviceIndex; + psPowerDevice->eCurrentPowerState = eCurrentPowerState; + psPowerDevice->eDefaultPowerState = eDefaultPowerState; + + /* insert into power device list */ + List_PVRSRV_POWER_DEV_Insert(&(psSysData->psPowerDeviceList), psPowerDevice); + + return (PVRSRV_OK); +} + + +/*! +****************************************************************************** + + @Function PVRSRVRemovePowerDevice + + @Description + + Removes device from power management register. Device is located by Device Index + + @Input ui32DeviceIndex : device index + + @Return PVRSRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR PVRSRVRemovePowerDevice (IMG_UINT32 ui32DeviceIndex) +{ + SYS_DATA *psSysData; + PVRSRV_POWER_DEV *psPowerDev; + + SysAcquireData(&psSysData); + + /* find device in list and remove it */ + psPowerDev = (PVRSRV_POWER_DEV*) + List_PVRSRV_POWER_DEV_Any_va(psSysData->psPowerDeviceList, + &MatchPowerDeviceIndex_AnyVaCb, + ui32DeviceIndex); + + if (psPowerDev) + { + List_PVRSRV_POWER_DEV_Remove(psPowerDev); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_POWER_DEV), psPowerDev, IMG_NULL); + /*not nulling pointer, copy on stack*/ + } + + return (PVRSRV_OK); +} + + +/*! +****************************************************************************** + + @Function PVRSRVIsDevicePowered + + @Description + + Whether the device is powered, for the purposes of lockup detection. + + @Input ui32DeviceIndex : device index + + @Return IMG_BOOL + +******************************************************************************/ +IMG_EXPORT +IMG_BOOL PVRSRVIsDevicePowered(IMG_UINT32 ui32DeviceIndex) +{ + SYS_DATA *psSysData; + PVRSRV_POWER_DEV *psPowerDevice; + + SysAcquireData(&psSysData); + + /* PRQA S 3415 2 */ /* order not important */ + if (OSIsResourceLocked(&psSysData->sPowerStateChangeResource, KERNEL_ID) || + OSIsResourceLocked(&psSysData->sPowerStateChangeResource, ISR_ID)) + { + return IMG_FALSE; + } + + psPowerDevice = (PVRSRV_POWER_DEV*) + List_PVRSRV_POWER_DEV_Any_va(psSysData->psPowerDeviceList, + &MatchPowerDeviceIndex_AnyVaCb, + ui32DeviceIndex); + return (psPowerDevice && (psPowerDevice->eCurrentPowerState == PVRSRV_DEV_POWER_STATE_ON)) + ? IMG_TRUE : IMG_FALSE; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDevicePreClockSpeedChange + + @Description + + Notification from system layer that a device clock speed change is about to happen. + + @Input ui32DeviceIndex : device index + @Input bIdleDevice : whether the device should be idled + @Input pvInfo + + @Return IMG_VOID + +******************************************************************************/ +PVRSRV_ERROR PVRSRVDevicePreClockSpeedChange(IMG_UINT32 ui32DeviceIndex, + IMG_BOOL bIdleDevice, + IMG_VOID *pvInfo) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + SYS_DATA *psSysData; + PVRSRV_POWER_DEV *psPowerDevice; + + PVR_UNREFERENCED_PARAMETER(pvInfo); + + SysAcquireData(&psSysData); + + if (bIdleDevice) + { + /* This mutex is released in PVRSRVDevicePostClockSpeedChange. */ + eError = PVRSRVPowerLock(KERNEL_ID, IMG_FALSE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVDevicePreClockSpeedChange : failed to acquire lock, error:0x%x", eError)); + return eError; + } + } + + /*search the device and then do the pre clock speed change*/ + psPowerDevice = (PVRSRV_POWER_DEV*) + List_PVRSRV_POWER_DEV_Any_va(psSysData->psPowerDeviceList, + &MatchPowerDeviceIndex_AnyVaCb, + ui32DeviceIndex); + + if (psPowerDevice && psPowerDevice->pfnPostClockSpeedChange) + { + eError = psPowerDevice->pfnPreClockSpeedChange(psPowerDevice->hDevCookie, + bIdleDevice, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVDevicePreClockSpeedChange : Device %u failed, error:0x%x", + ui32DeviceIndex, eError)); + } + } + + if (bIdleDevice && eError != PVRSRV_OK) + { + PVRSRVPowerUnlock(KERNEL_ID); + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDevicePostClockSpeedChange + + @Description + + Notification from system layer that a device clock speed change has just happened. + + @Input ui32DeviceIndex : device index + @Input bIdleDevice : whether the device had been idled + @Input pvInfo + + @Return IMG_VOID + +******************************************************************************/ +IMG_VOID PVRSRVDevicePostClockSpeedChange(IMG_UINT32 ui32DeviceIndex, + IMG_BOOL bIdleDevice, + IMG_VOID *pvInfo) +{ + PVRSRV_ERROR eError; + SYS_DATA *psSysData; + PVRSRV_POWER_DEV *psPowerDevice; + + PVR_UNREFERENCED_PARAMETER(pvInfo); + + SysAcquireData(&psSysData); + + /*search the device and then do the post clock speed change*/ + psPowerDevice = (PVRSRV_POWER_DEV*) + List_PVRSRV_POWER_DEV_Any_va(psSysData->psPowerDeviceList, + &MatchPowerDeviceIndex_AnyVaCb, + ui32DeviceIndex); + + if (psPowerDevice && psPowerDevice->pfnPostClockSpeedChange) + { + eError = psPowerDevice->pfnPostClockSpeedChange(psPowerDevice->hDevCookie, + bIdleDevice, + psPowerDevice->eCurrentPowerState); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVDevicePostClockSpeedChange : Device %u failed, error:0x%x", + ui32DeviceIndex, eError)); + } + } + + + if (bIdleDevice) + { + /* This mutex was acquired in PVRSRVDevicePreClockSpeedChange. */ + PVRSRVPowerUnlock(KERNEL_ID); + } +} + +/****************************************************************************** + End of file (power.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/pvrsrv.c b/pvr-source/services4/srvkm/common/pvrsrv.c new file mode 100644 index 0000000..1b5312c --- /dev/null +++ b/pvr-source/services4/srvkm/common/pvrsrv.c @@ -0,0 +1,1846 @@ +/*************************************************************************/ /*! +@Title core services functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Main APIs for core services functions +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "buffer_manager.h" +#include "pvr_bridge_km.h" +#include "handle.h" +#include "perproc.h" +#include "pdump_km.h" +#include "deviceid.h" +#include "ra.h" +#if defined(__linux__) +#include "sysfs.h" +#endif +#if defined(TTRACE) +#include "ttrace.h" +#endif +#include "perfkm.h" + +#include "pvrversion.h" + +#include "lists.h" + +IMG_UINT32 g_ui32InitFlags; +extern int powering_down; + +/* mark which parts of Services were initialised */ +#define INIT_DATA_ENABLE_PDUMPINIT 0x1U +#define INIT_DATA_ENABLE_TTARCE 0x2U + +/*! +****************************************************************************** + + @Function AllocateDeviceID + + @Description + + allocates a device id from the pool of valid ids + + @input psSysData : system data + + @input pui32DevID : device id to return + + @Return device id + +******************************************************************************/ +PVRSRV_ERROR AllocateDeviceID(SYS_DATA *psSysData, IMG_UINT32 *pui32DevID) +{ + SYS_DEVICE_ID* psDeviceWalker; + SYS_DEVICE_ID* psDeviceEnd; + + psDeviceWalker = &psSysData->sDeviceID[0]; + psDeviceEnd = psDeviceWalker + psSysData->ui32NumDevices; + + /* find a free ID */ + while (psDeviceWalker < psDeviceEnd) + { + if (!psDeviceWalker->bInUse) + { + psDeviceWalker->bInUse = IMG_TRUE; + *pui32DevID = psDeviceWalker->uiID; + return PVRSRV_OK; + } + psDeviceWalker++; + } + + PVR_DPF((PVR_DBG_ERROR,"AllocateDeviceID: No free and valid device IDs available!")); + + /* Should never get here: sDeviceID[] may have been setup too small */ + PVR_ASSERT(psDeviceWalker < psDeviceEnd); + + return PVRSRV_ERROR_NO_FREE_DEVICEIDS_AVALIABLE; +} + + +/*! +****************************************************************************** + + @Function FreeDeviceID + + @Description + + frees a device id from the pool of valid ids + + @input psSysData : system data + + @input ui32DevID : device id to free + + @Return device id + +******************************************************************************/ +PVRSRV_ERROR FreeDeviceID(SYS_DATA *psSysData, IMG_UINT32 ui32DevID) +{ + SYS_DEVICE_ID* psDeviceWalker; + SYS_DEVICE_ID* psDeviceEnd; + + psDeviceWalker = &psSysData->sDeviceID[0]; + psDeviceEnd = psDeviceWalker + psSysData->ui32NumDevices; + + /* find the ID to free */ + while (psDeviceWalker < psDeviceEnd) + { + /* if matching id and in use, free */ + if ( + (psDeviceWalker->uiID == ui32DevID) && + (psDeviceWalker->bInUse) + ) + { + psDeviceWalker->bInUse = IMG_FALSE; + return PVRSRV_OK; + } + psDeviceWalker++; + } + + PVR_DPF((PVR_DBG_ERROR,"FreeDeviceID: no matching dev ID that is in use!")); + + /* should never get here */ + PVR_ASSERT(psDeviceWalker < psDeviceEnd); + + return PVRSRV_ERROR_INVALID_DEVICEID; +} + + +/*! +****************************************************************************** + + @Function ReadHWReg + + @Description + + register access function + + @input pvLinRegBaseAddr : lin addr of register block base + + @input ui32Offset : byte offset from register base + + @Return register value + +******************************************************************************/ +#ifndef ReadHWReg +IMG_EXPORT +IMG_UINT32 ReadHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset) +{ + return *(volatile IMG_UINT32*)((IMG_UINTPTR_T)pvLinRegBaseAddr+ui32Offset); +} +#endif + + +/*! +****************************************************************************** + + @Function WriteHWReg + + @Description + + register access function + + @input pvLinRegBaseAddr : lin addr of register block base + + @input ui32Offset : byte offset from register base + + @input ui32Value : value to write to register + + @Return register value : original reg. value + +******************************************************************************/ +#ifndef WriteHWReg +IMG_EXPORT +IMG_VOID WriteHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT32 ui32Value) +{ + PVR_DPF((PVR_DBG_MESSAGE,"WriteHWReg Base:%x, Offset: %x, Value %x", + (IMG_UINTPTR_T)pvLinRegBaseAddr,ui32Offset,ui32Value)); + + *(IMG_UINT32*)((IMG_UINTPTR_T)pvLinRegBaseAddr+ui32Offset) = ui32Value; +} +#endif + + +/*! +****************************************************************************** + + @Function WriteHWRegs + + @Description + + register access function + + @input pvLinRegBaseAddr : lin addr of register block base + + @input ui32Count : register count + + @input psHWRegs : address/value register list + + @Return none + +******************************************************************************/ +#ifndef WriteHWRegs +IMG_EXPORT +IMG_VOID WriteHWRegs(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Count, PVRSRV_HWREG *psHWRegs) +{ + while (ui32Count) + { + WriteHWReg (pvLinRegBaseAddr, psHWRegs->ui32RegAddr, psHWRegs->ui32RegVal); + psHWRegs++; + ui32Count--; + } +} +#endif + +/*! +****************************************************************************** + @Function PVRSRVEnumerateDCKM_ForEachVaCb + + @Description + + Enumerates the device node (if is of the same class as given). + + @Input psDeviceNode - The device node to be enumerated + va - variable arguments list, with: + pui32DevCount - The device count pointer (to be increased) + ppui32DevID - The pointer to the device IDs pointer (to be updated and increased) +******************************************************************************/ +static IMG_VOID PVRSRVEnumerateDevicesKM_ForEachVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + IMG_UINT *pui32DevCount; + PVRSRV_DEVICE_IDENTIFIER **ppsDevIdList; + + pui32DevCount = va_arg(va, IMG_UINT*); + ppsDevIdList = va_arg(va, PVRSRV_DEVICE_IDENTIFIER**); + + if (psDeviceNode->sDevId.eDeviceType != PVRSRV_DEVICE_TYPE_EXT) + { + *(*ppsDevIdList) = psDeviceNode->sDevId; + (*ppsDevIdList)++; + (*pui32DevCount)++; + } +} + + + +/*! +****************************************************************************** + + @Function PVRSRVEnumerateDevicesKM + + @Description + This function will enumerate all the devices supported by the + PowerVR services within the target system. + The function returns a list of the device ID strcutres stored either in + the services or constructed in the user mode glue component in certain + environments. The number of devices in the list is also returned. + + In a binary layered component which does not support dynamic runtime selection, + the glue code should compile to return the supported devices statically, + e.g. multiple instances of the same device if multiple devices are supported, + or the target combination of MBX and display device. + + In the case of an environment (for instance) where one MBX1 may connect to two + display devices this code would enumerate all three devices and even + non-dynamic MBX1 selection code should retain the facility to parse the list + to find the index of the MBX device + + @output pui32NumDevices : On success, contains the number of devices present + in the system + + @output psDevIdList : Pointer to called supplied buffer to receive the + list of PVRSRV_DEVICE_IDENTIFIER + + @return PVRSRV_ERROR : PVRSRV_NO_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVEnumerateDevicesKM(IMG_UINT32 *pui32NumDevices, + PVRSRV_DEVICE_IDENTIFIER *psDevIdList) +{ + SYS_DATA *psSysData; +/* PVRSRV_DEVICE_NODE *psDeviceNode; */ + IMG_UINT32 i; + + if (!pui32NumDevices || !psDevIdList) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVEnumerateDevicesKM: Invalid params")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + SysAcquireData(&psSysData); + + /* + setup input buffer to be `empty' + */ + for (i=0; i<PVRSRV_MAX_DEVICES; i++) + { + psDevIdList[i].eDeviceType = PVRSRV_DEVICE_TYPE_UNKNOWN; + } + + /* and zero device count */ + *pui32NumDevices = 0; + + /* + Search through the device list for services managed devices + return id info for each device and the number of devices + available + */ + List_PVRSRV_DEVICE_NODE_ForEach_va(psSysData->psDeviceNodeList, + &PVRSRVEnumerateDevicesKM_ForEachVaCb, + pui32NumDevices, + &psDevIdList); + + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVInit + + @Description Initialise services + + @Input psSysData : sysdata structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVInit(PSYS_DATA psSysData) +{ + PVRSRV_ERROR eError; + +#if defined(__linux__) + eError = PVRSRVCreateSysfsEntry(); + if (eError != PVRSRV_OK) + { + goto Error; + } +#endif + + /* Initialise Resource Manager */ + eError = ResManInit(); + if (eError != PVRSRV_OK) + { + goto Error; + } + + eError = PVRSRVPerProcessDataInit(); + if(eError != PVRSRV_OK) + { + goto Error; + } + + /* Initialise handles */ + eError = PVRSRVHandleInit(); + if(eError != PVRSRV_OK) + { + goto Error; + } + + /* Initialise Power Manager Lock */ + eError = OSCreateResource(&psSysData->sPowerStateChangeResource); + if (eError != PVRSRV_OK) + { + goto Error; + } + + /* Initialise system power state */ + psSysData->eCurrentPowerState = PVRSRV_SYS_POWER_STATE_D0; + psSysData->eFailedPowerState = PVRSRV_SYS_POWER_STATE_Unspecified; + + /* Create an event object */ + if(OSAllocMem( PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_EVENTOBJECT) , + (IMG_VOID **)&psSysData->psGlobalEventObject, 0, + "Event Object") != PVRSRV_OK) + { + + goto Error; + } + + if(OSEventObjectCreateKM("PVRSRV_GLOBAL_EVENTOBJECT", psSysData->psGlobalEventObject) != PVRSRV_OK) + { + goto Error; + } + + /* Store OS high res timer fallbacks, the system is free to overide these */ + psSysData->pfnHighResTimerCreate = OSFuncHighResTimerCreate; + psSysData->pfnHighResTimerGetus = OSFuncHighResTimerGetus; + psSysData->pfnHighResTimerDestroy = OSFuncHighResTimerDestroy; + +#if defined(TTRACE) + eError = PVRSRVTimeTraceInit(); + if (eError != PVRSRV_OK) + goto Error; + g_ui32InitFlags |= INIT_DATA_ENABLE_TTARCE; +#endif + + /* Initialise pdump */ + PDUMPINIT(); + g_ui32InitFlags |= INIT_DATA_ENABLE_PDUMPINIT; + + PERFINIT(); + return eError; + +Error: + PVRSRVDeInit(psSysData); + return eError; +} + + + +/*! +****************************************************************************** + + @Function PVRSRVDeInit + + @Description De-Initialise services + + @Input psSysData : sysdata structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_VOID IMG_CALLCONV PVRSRVDeInit(PSYS_DATA psSysData) +{ + PVRSRV_ERROR eError; + + PVR_UNREFERENCED_PARAMETER(psSysData); + + if (psSysData == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeInit: PVRSRVHandleDeInit failed - invalid param")); + return; + } + + PERFDEINIT(); + +#if defined(TTRACE) + /* deinitialise ttrace */ + if ((g_ui32InitFlags & INIT_DATA_ENABLE_TTARCE) > 0) + { + PVRSRVTimeTraceDeinit(); + } +#endif + /* deinitialise pdump */ + if( (g_ui32InitFlags & INIT_DATA_ENABLE_PDUMPINIT) > 0) + { + PDUMPDEINIT(); + } + + /* destroy event object */ + if(psSysData->psGlobalEventObject) + { + OSEventObjectDestroyKM(psSysData->psGlobalEventObject); + OSFreeMem( PVRSRV_PAGEABLE_SELECT, + sizeof(PVRSRV_EVENTOBJECT), + psSysData->psGlobalEventObject, + 0); + psSysData->psGlobalEventObject = IMG_NULL; + } + + eError = PVRSRVHandleDeInit(); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeInit: PVRSRVHandleDeInit failed")); + } + + eError = PVRSRVPerProcessDataDeInit(); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeInit: PVRSRVPerProcessDataDeInit failed")); + } + + ResManDeInit(); +} + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterDevice + + @Description + + registers a device with the system + + @Input psSysData : sysdata structure + + @Input pfnRegisterDevice : device registration function + + @Input ui32SOCInterruptBit : SoC interrupt bit for this device + + @Output pui32DeviceIndex : unique device key (for case of multiple identical devices) + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVRegisterDevice(PSYS_DATA psSysData, + PVRSRV_ERROR (*pfnRegisterDevice)(PVRSRV_DEVICE_NODE*), + IMG_UINT32 ui32SOCInterruptBit, + IMG_UINT32 *pui32DeviceIndex) +{ + PVRSRV_ERROR eError; + PVRSRV_DEVICE_NODE *psDeviceNode; + + /* Allocate device node */ + if(OSAllocMem( PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_DEVICE_NODE), + (IMG_VOID **)&psDeviceNode, IMG_NULL, + "Device Node") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterDevice : Failed to alloc memory for psDeviceNode")); + return (PVRSRV_ERROR_OUT_OF_MEMORY); + } + OSMemSet (psDeviceNode, 0, sizeof(PVRSRV_DEVICE_NODE)); + + eError = pfnRegisterDevice(psDeviceNode); + if (eError != PVRSRV_OK) + { + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_DEVICE_NODE), psDeviceNode, IMG_NULL); + /*not nulling pointer, out of scope*/ + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterDevice : Failed to register device")); + return (PVRSRV_ERROR_DEVICE_REGISTER_FAILED); + } + + /* + make the refcount 1 and test on this to initialise device + at acquiredevinfo. On release if refcount is 1, deinitialise + and when refcount is 0 (sysdata de-alloc) deallocate the device + structures + */ + psDeviceNode->ui32RefCount = 1; + psDeviceNode->psSysData = psSysData; + psDeviceNode->ui32SOCInterruptBit = ui32SOCInterruptBit; + + /* all devices need a unique identifier */ + AllocateDeviceID(psSysData, &psDeviceNode->sDevId.ui32DeviceIndex); + + /* and finally insert the device into the dev-list */ + List_PVRSRV_DEVICE_NODE_Insert(&psSysData->psDeviceNodeList, psDeviceNode); + + /* and copy back index */ + *pui32DeviceIndex = psDeviceNode->sDevId.ui32DeviceIndex; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVInitialiseDevice + + @Description + + initialises device by index + + @Input ui32DevIndex : Index to the required device + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVInitialiseDevice (IMG_UINT32 ui32DevIndex) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVInitialiseDevice")); + + SysAcquireData(&psSysData); + + /* Find device in the list */ + psDeviceNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DevIndex, + IMG_TRUE); + if(!psDeviceNode) + { + /* Devinfo not in the list */ + PVR_DPF((PVR_DBG_ERROR,"PVRSRVInitialiseDevice: requested device is not present")); + return PVRSRV_ERROR_INIT_FAILURE; + } +/* +FoundDevice: +*/ + + PVR_ASSERT (psDeviceNode->ui32RefCount > 0); + + /* + Create the device's resource manager context. + */ + eError = PVRSRVResManConnect(IMG_NULL, &psDeviceNode->hResManContext); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVInitialiseDevice: Failed PVRSRVResManConnect call")); + return eError; + } + + /* Initialise the device */ + if(psDeviceNode->pfnInitDevice != IMG_NULL) + { + eError = psDeviceNode->pfnInitDevice(psDeviceNode); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVInitialiseDevice: Failed InitDevice call")); + return eError; + } + } + + return PVRSRV_OK; +} + + +static PVRSRV_ERROR PVRSRVFinaliseSystem_SetPowerState_AnyCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + PVRSRV_ERROR eError; + + eError = PVRSRVPowerLock(KERNEL_ID, IMG_FALSE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVFinaliseSystem: Failed PVRSRVPowerLock call (device index: %d)", psDeviceNode->sDevId.ui32DeviceIndex)); + return eError; + } + + eError = PVRSRVSetDevicePowerStateKM(psDeviceNode->sDevId.ui32DeviceIndex, + PVRSRV_DEV_POWER_STATE_DEFAULT); + PVRSRVPowerUnlock(KERNEL_ID); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVFinaliseSystem: Failed PVRSRVSetDevicePowerStateKM call (device index: %d)", psDeviceNode->sDevId.ui32DeviceIndex)); + } + return eError; +} + +/*wraps the PVRSRVDevInitCompatCheck call and prints a debugging message if failed*/ +static PVRSRV_ERROR PVRSRVFinaliseSystem_CompatCheck_AnyCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + PVRSRV_ERROR eError; + eError = PVRSRVDevInitCompatCheck(psDeviceNode); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVFinaliseSystem: Failed PVRSRVDevInitCompatCheck call (device index: %d)", psDeviceNode->sDevId.ui32DeviceIndex)); + } + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVFinaliseSystem + + @Description + + Final part of system initialisation. + + @Input ui32DevIndex : Index to the required device + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVFinaliseSystem(IMG_BOOL bInitSuccessful) +{ +/* PVRSRV_DEVICE_NODE *psDeviceNode;*/ + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVFinaliseSystem")); + + SysAcquireData(&psSysData); + + if (bInitSuccessful) + { + eError = SysFinalise(); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVFinaliseSystem: SysFinalise failed (%d)", eError)); + return eError; + } + + /* Place all devices into their default power state. */ + eError = List_PVRSRV_DEVICE_NODE_PVRSRV_ERROR_Any(psSysData->psDeviceNodeList, + &PVRSRVFinaliseSystem_SetPowerState_AnyCb); + if (eError != PVRSRV_OK) + { + return eError; + } + + /* Verify microkernel compatibility for devices */ + eError = List_PVRSRV_DEVICE_NODE_PVRSRV_ERROR_Any(psSysData->psDeviceNodeList, + &PVRSRVFinaliseSystem_CompatCheck_AnyCb); + if (eError != PVRSRV_OK) + { + return eError; + } + } + + /* Some platforms call this too early in the boot phase. */ + PDUMPENDINITPHASE(); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR PVRSRVDevInitCompatCheck(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + /* Only check devices which specify a compatibility check callback */ + if (psDeviceNode->pfnInitDeviceCompatCheck) + return psDeviceNode->pfnInitDeviceCompatCheck(psDeviceNode); + else + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVAcquireDeviceDataKM + + @Description + + Matchs a device given a device type and a device index. + + @input psDeviceNode :The device node to be matched. + + @Input va : Variable argument list with: + eDeviceType : Required device type. If type is unknown use ui32DevIndex + to locate device data + + ui32DevIndex : Index to the required device obtained from the + PVRSRVEnumerateDevice function + + @Return PVRSRV_ERROR : + +******************************************************************************/ +static IMG_VOID * PVRSRVAcquireDeviceDataKM_Match_AnyVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + PVRSRV_DEVICE_TYPE eDeviceType; + IMG_UINT32 ui32DevIndex; + + eDeviceType = va_arg(va, PVRSRV_DEVICE_TYPE); + ui32DevIndex = va_arg(va, IMG_UINT32); + + if ((eDeviceType != PVRSRV_DEVICE_TYPE_UNKNOWN && + psDeviceNode->sDevId.eDeviceType == eDeviceType) || + (eDeviceType == PVRSRV_DEVICE_TYPE_UNKNOWN && + psDeviceNode->sDevId.ui32DeviceIndex == ui32DevIndex)) + { + return psDeviceNode; + } + else + { + return IMG_NULL; + } +} + +/*! +****************************************************************************** + + @Function PVRSRVAcquireDeviceDataKM + + @Description + + Returns device information + + @Input ui32DevIndex : Index to the required device obtained from the + PVRSRVEnumerateDevice function + + @Input eDeviceType : Required device type. If type is unknown use ui32DevIndex + to locate device data + + @Output *phDevCookie : Dev Cookie + + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVAcquireDeviceDataKM (IMG_UINT32 ui32DevIndex, + PVRSRV_DEVICE_TYPE eDeviceType, + IMG_HANDLE *phDevCookie) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVAcquireDeviceDataKM")); + + SysAcquireData(&psSysData); + + /* Find device in the list */ + psDeviceNode = List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &PVRSRVAcquireDeviceDataKM_Match_AnyVaCb, + eDeviceType, + ui32DevIndex); + + + if (!psDeviceNode) + { + /* device can't be found in the list so it isn't in the system */ + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAcquireDeviceDataKM: requested device is not present")); + return PVRSRV_ERROR_INIT_FAILURE; + } + +/*FoundDevice:*/ + + PVR_ASSERT (psDeviceNode->ui32RefCount > 0); + + /* return the dev cookie? */ + if (phDevCookie) + { + *phDevCookie = (IMG_HANDLE)psDeviceNode; + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDeinitialiseDevice + + @Description + + This De-inits device + + @Input ui32DevIndex : Index to the required device + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVDeinitialiseDevice(IMG_UINT32 ui32DevIndex) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + + SysAcquireData(&psSysData); + + psDeviceNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + &MatchDeviceKM_AnyVaCb, + ui32DevIndex, + IMG_TRUE); + + if (!psDeviceNode) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeinitialiseDevice: requested device %d is not present", ui32DevIndex)); + return PVRSRV_ERROR_DEVICEID_NOT_FOUND; + } + + eError = PVRSRVPowerLock(KERNEL_ID, IMG_FALSE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeinitialiseDevice: Failed PVRSRVPowerLock call")); + return eError; + } + + /* + Power down the device if necessary. + */ + eError = PVRSRVSetDevicePowerStateKM(ui32DevIndex, + PVRSRV_DEV_POWER_STATE_OFF); + PVRSRVPowerUnlock(KERNEL_ID); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeinitialiseDevice: Failed PVRSRVSetDevicePowerStateKM call")); + return eError; + } + + /* + Free the dissociated device memory. + */ + eError = ResManFreeResByCriteria(psDeviceNode->hResManContext, + RESMAN_CRITERIA_RESTYPE, + RESMAN_TYPE_DEVICEMEM_ALLOCATION, + IMG_NULL, 0); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeinitialiseDevice: Failed ResManFreeResByCriteria call")); + return eError; + } + + /* + De-init the device. + */ + if(psDeviceNode->pfnDeInitDevice != IMG_NULL) + { + eError = psDeviceNode->pfnDeInitDevice(psDeviceNode); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDeinitialiseDevice: Failed DeInitDevice call")); + return eError; + } + } + + /* + Close the device's resource manager context. + */ + PVRSRVResManDisconnect(psDeviceNode->hResManContext, IMG_TRUE); + psDeviceNode->hResManContext = IMG_NULL; + + /* remove node from list */ + List_PVRSRV_DEVICE_NODE_Remove(psDeviceNode); + + /* deallocate id and memory */ + (IMG_VOID)FreeDeviceID(psSysData, ui32DevIndex); + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_DEVICE_NODE), psDeviceNode, IMG_NULL); + /*not nulling pointer, out of scope*/ + + return (PVRSRV_OK); +} + + +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PollForValueKM (volatile IMG_UINT32* pui32LinMemAddr, + IMG_UINT32 ui32Value, + IMG_UINT32 ui32Mask, + IMG_UINT32 ui32Timeoutus, + IMG_UINT32 ui32PollPeriodus, + IMG_BOOL bAllowPreemption) +{ +#if defined (EMULATOR) + { + PVR_UNREFERENCED_PARAMETER(bAllowPreemption); + #if !defined(__linux__) + PVR_UNREFERENCED_PARAMETER(ui32PollPeriodus); + #endif + + /* For the Emulator we want the system to stop when a lock-up is detected so the state can be analysed. + * Also the Emulator is much slower than real silicon so timeouts are not valid. + */ + do + { + if((*pui32LinMemAddr & ui32Mask) == ui32Value) + { + return PVRSRV_OK; + } + + #if defined(__linux__) + OSWaitus(ui32PollPeriodus); + #else + OSReleaseThreadQuanta(); + #endif + + } while (ui32Timeoutus); /* Endless loop only for the Emulator */ + } +#else + { + IMG_UINT32 ui32ActualValue = 0xFFFFFFFFU; /* Initialiser only required to prevent incorrect warning */ + + if (bAllowPreemption) + { + PVR_ASSERT(ui32PollPeriodus >= 1000); + } + + /* PRQA S 3415,4109 1 */ /* macro format critical - leave alone */ + LOOP_UNTIL_TIMEOUT(ui32Timeoutus) + { + ui32ActualValue = (*pui32LinMemAddr & ui32Mask); + if(ui32ActualValue == ui32Value) + { + return PVRSRV_OK; + } + + if (bAllowPreemption) + { + OSSleepms(ui32PollPeriodus / 1000); + } + else + { + OSWaitus(ui32PollPeriodus); + } + } END_LOOP_UNTIL_TIMEOUT(); + + PVR_DPF((PVR_DBG_ERROR,"PollForValueKM: Timeout. Expected 0x%x but found 0x%x (mask 0x%x).", + ui32Value, ui32ActualValue, ui32Mask)); + } +#endif /* #if defined (EMULATOR) */ + + return PVRSRV_ERROR_TIMEOUT; +} + + +/*Level 3 of the loop nesting*/ +static IMG_VOID PVRSRVGetMiscInfoKM_RA_GetStats_ForEachVaCb(BM_HEAP *psBMHeap, va_list va) +{ + IMG_CHAR **ppszStr; + IMG_UINT32 *pui32StrLen; + IMG_UINT32 ui32Mode; + PVRSRV_ERROR (*pfnGetStats)(RA_ARENA *, IMG_CHAR **, IMG_UINT32 *); + + ppszStr = va_arg(va, IMG_CHAR**); + pui32StrLen = va_arg(va, IMG_UINT32*); + ui32Mode = va_arg(va, IMG_UINT32); + + /* Would be better to pass fn pointer in the variable args list + * but MS C compiler complains with error C2066: In ANSI C, + * it is not legal to cast between a function pointer and a data pointer. + */ + switch(ui32Mode) + { + case PVRSRV_MISC_INFO_MEMSTATS_PRESENT: + pfnGetStats = &RA_GetStats; + break; + case PVRSRV_MISC_INFO_FREEMEM_PRESENT: + pfnGetStats = &RA_GetStatsFreeMem; + break; + default: + return; + } + + if(psBMHeap->pImportArena) + { + pfnGetStats(psBMHeap->pImportArena, + ppszStr, + pui32StrLen); + } + + if(psBMHeap->pVMArena) + { + pfnGetStats(psBMHeap->pVMArena, + ppszStr, + pui32StrLen); + } +} + +/*Level 2 of the loop nesting*/ +static PVRSRV_ERROR PVRSRVGetMiscInfoKM_BMContext_AnyVaCb(BM_CONTEXT *psBMContext, va_list va) +{ + + IMG_UINT32 *pui32StrLen; + IMG_INT32 *pi32Count; + IMG_CHAR **ppszStr; + IMG_UINT32 ui32Mode; + + pui32StrLen = va_arg(va, IMG_UINT32*); + pi32Count = va_arg(va, IMG_INT32*); + ppszStr = va_arg(va, IMG_CHAR**); + ui32Mode = va_arg(va, IMG_UINT32); + + CHECK_SPACE(*pui32StrLen); + *pi32Count = OSSNPrintf(*ppszStr, 100, "\nApplication Context (hDevMemContext) %p:\n", + (IMG_HANDLE)psBMContext); + UPDATE_SPACE(*ppszStr, *pi32Count, *pui32StrLen); + + List_BM_HEAP_ForEach_va(psBMContext->psBMHeap, + &PVRSRVGetMiscInfoKM_RA_GetStats_ForEachVaCb, + ppszStr, + pui32StrLen, + ui32Mode); + return PVRSRV_OK; +} + + +/*level 1 of the loop nesting*/ +static PVRSRV_ERROR PVRSRVGetMiscInfoKM_Device_AnyVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + IMG_UINT32 *pui32StrLen; + IMG_INT32 *pi32Count; + IMG_CHAR **ppszStr; + IMG_UINT32 ui32Mode; + + pui32StrLen = va_arg(va, IMG_UINT32*); + pi32Count = va_arg(va, IMG_INT32*); + ppszStr = va_arg(va, IMG_CHAR**); + ui32Mode = va_arg(va, IMG_UINT32); + + CHECK_SPACE(*pui32StrLen); + *pi32Count = OSSNPrintf(*ppszStr, 100, "\n\nDevice Type %d:\n", psDeviceNode->sDevId.eDeviceType); + UPDATE_SPACE(*ppszStr, *pi32Count, *pui32StrLen); + + /* kernel context: */ + if(psDeviceNode->sDevMemoryInfo.pBMKernelContext) + { + CHECK_SPACE(*pui32StrLen); + *pi32Count = OSSNPrintf(*ppszStr, 100, "\nKernel Context:\n"); + UPDATE_SPACE(*ppszStr, *pi32Count, *pui32StrLen); + + List_BM_HEAP_ForEach_va(psDeviceNode->sDevMemoryInfo.pBMKernelContext->psBMHeap, + &PVRSRVGetMiscInfoKM_RA_GetStats_ForEachVaCb, + ppszStr, + pui32StrLen, + ui32Mode); + } + + /* double loop app contexts:heaps */ + return List_BM_CONTEXT_PVRSRV_ERROR_Any_va(psDeviceNode->sDevMemoryInfo.pBMContext, + &PVRSRVGetMiscInfoKM_BMContext_AnyVaCb, + pui32StrLen, + pi32Count, + ppszStr, + ui32Mode); +} + + +/*! +****************************************************************************** + + @Function PVRSRVGetMiscInfoKM + + @Description + Retrieves misc. info. + + @Output PVRSRV_MISC_INFO + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetMiscInfoKM(PVRSRV_MISC_INFO_KM *psMiscInfo) +#else +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetMiscInfoKM(PVRSRV_MISC_INFO *psMiscInfo) +#endif +{ + SYS_DATA *psSysData; + + if(!psMiscInfo) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetMiscInfoKM: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psMiscInfo->ui32StatePresent = 0; + + /* do a basic check for uninitialised request flag */ + if(psMiscInfo->ui32StateRequest & ~(PVRSRV_MISC_INFO_TIMER_PRESENT + |PVRSRV_MISC_INFO_CLOCKGATE_PRESENT + |PVRSRV_MISC_INFO_MEMSTATS_PRESENT + |PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT + |PVRSRV_MISC_INFO_DDKVERSION_PRESENT + |PVRSRV_MISC_INFO_CPUCACHEOP_PRESENT + |PVRSRV_MISC_INFO_RESET_PRESENT + |PVRSRV_MISC_INFO_FREEMEM_PRESENT + |PVRSRV_MISC_INFO_GET_REF_COUNT_PRESENT + |PVRSRV_MISC_INFO_GET_PAGE_SIZE_PRESENT + |PVRSRV_MISC_INFO_FORCE_SWAP_TO_SYSTEM_PRESENT)) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVGetMiscInfoKM: invalid state request flags")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + SysAcquireData(&psSysData); + + /* return SOC Timer registers */ + if(((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_TIMER_PRESENT) != 0UL) && + (psSysData->pvSOCTimerRegisterKM != IMG_NULL)) + { + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_TIMER_PRESENT; + psMiscInfo->pvSOCTimerRegisterKM = psSysData->pvSOCTimerRegisterKM; + psMiscInfo->hSOCTimerRegisterOSMemHandle = psSysData->hSOCTimerRegisterOSMemHandle; + } + else + { + psMiscInfo->pvSOCTimerRegisterKM = IMG_NULL; + psMiscInfo->hSOCTimerRegisterOSMemHandle = IMG_NULL; + } + + /* return SOC Clock Gating registers */ + if(((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_CLOCKGATE_PRESENT) != 0UL) && + (psSysData->pvSOCClockGateRegsBase != IMG_NULL)) + { + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_CLOCKGATE_PRESENT; + psMiscInfo->pvSOCClockGateRegs = psSysData->pvSOCClockGateRegsBase; + psMiscInfo->ui32SOCClockGateRegsSize = psSysData->ui32SOCClockGateRegsSize; + } + + /* memory stats */ + if(((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_MEMSTATS_PRESENT) != 0UL) && + (psMiscInfo->pszMemoryStr != IMG_NULL)) + { + RA_ARENA **ppArena; +/* BM_HEAP *psBMHeap; + BM_CONTEXT *psBMContext; + PVRSRV_DEVICE_NODE *psDeviceNode;*/ + IMG_CHAR *pszStr; + IMG_UINT32 ui32StrLen; + IMG_INT32 i32Count; + + pszStr = psMiscInfo->pszMemoryStr; + ui32StrLen = psMiscInfo->ui32MemoryStrLen; + + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_MEMSTATS_PRESENT; + + /* Local backing stores */ + ppArena = &psSysData->apsLocalDevMemArena[0]; + while(*ppArena) + { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "\nLocal Backing Store:\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + RA_GetStats(*ppArena, + &pszStr, + &ui32StrLen); + /* advance through the array */ + ppArena++; + } + + /* per device */ +/* psDeviceNode = psSysData->psDeviceNodeList;*/ + + /*triple loop; devices:contexts:heaps*/ + List_PVRSRV_DEVICE_NODE_PVRSRV_ERROR_Any_va(psSysData->psDeviceNodeList, + &PVRSRVGetMiscInfoKM_Device_AnyVaCb, + &ui32StrLen, + &i32Count, + &pszStr, + PVRSRV_MISC_INFO_MEMSTATS_PRESENT); + + /* attach a new line and string terminate */ + i32Count = OSSNPrintf(pszStr, 100, "\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + /* Lean version of mem stats: only show free mem on each RA */ + if(((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_FREEMEM_PRESENT) != 0) + && psMiscInfo->pszMemoryStr) + { + IMG_CHAR *pszStr; + IMG_UINT32 ui32StrLen; + IMG_INT32 i32Count; + + pszStr = psMiscInfo->pszMemoryStr; + ui32StrLen = psMiscInfo->ui32MemoryStrLen; + + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_FREEMEM_PRESENT; + + /* triple loop over devices:contexts:heaps */ + List_PVRSRV_DEVICE_NODE_PVRSRV_ERROR_Any_va(psSysData->psDeviceNodeList, + &PVRSRVGetMiscInfoKM_Device_AnyVaCb, + &ui32StrLen, + &i32Count, + &pszStr, + PVRSRV_MISC_INFO_FREEMEM_PRESENT); + + i32Count = OSSNPrintf(pszStr, 100, "\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + if(((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT) != 0UL) && + (psSysData->psGlobalEventObject != IMG_NULL)) + { + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_GLOBALEVENTOBJECT_PRESENT; + psMiscInfo->sGlobalEventObject = *psSysData->psGlobalEventObject; + } + + /* DDK version and memstats not supported in same call to GetMiscInfo */ + + if (((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_DDKVERSION_PRESENT) != 0UL) + && ((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_MEMSTATS_PRESENT) == 0UL) + && (psMiscInfo->pszMemoryStr != IMG_NULL)) + { + IMG_CHAR *pszStr; + IMG_UINT32 ui32StrLen; + IMG_UINT32 ui32LenStrPerNum = 12; /* string length per UI32: 10 digits + '.' + '\0' = 12 bytes */ + IMG_INT32 i32Count; + IMG_INT i; + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_DDKVERSION_PRESENT; + + /* construct DDK string */ + psMiscInfo->aui32DDKVersion[0] = PVRVERSION_MAJ; + psMiscInfo->aui32DDKVersion[1] = PVRVERSION_MIN; + psMiscInfo->aui32DDKVersion[2] = PVRVERSION_BUILD_HI; + psMiscInfo->aui32DDKVersion[3] = PVRVERSION_BUILD_LO; + + pszStr = psMiscInfo->pszMemoryStr; + ui32StrLen = psMiscInfo->ui32MemoryStrLen; + + for (i=0; i<4; i++) + { + if (ui32StrLen < ui32LenStrPerNum) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + i32Count = OSSNPrintf(pszStr, ui32LenStrPerNum, "%u", psMiscInfo->aui32DDKVersion[i]); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + if (i != 3) + { + i32Count = OSSNPrintf(pszStr, 2, "."); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + } + } + + if((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_CPUCACHEOP_PRESENT) != 0UL) + { + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_CPUCACHEOP_PRESENT; + + if(psMiscInfo->sCacheOpCtl.bDeferOp) + { + /* For now, assume deferred ops are "full" cache ops, + * and we don't need (or expect) a meminfo. + */ + psSysData->ePendingCacheOpType = psMiscInfo->sCacheOpCtl.eCacheOpType; + } + else + { +#if defined (SUPPORT_SID_INTERFACE) + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo = psMiscInfo->sCacheOpCtl.psKernelMemInfo; + + if(!psMiscInfo->sCacheOpCtl.psKernelMemInfo) +#else + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + PVRSRV_PER_PROCESS_DATA *psPerProc; + + if(!psMiscInfo->sCacheOpCtl.u.psKernelMemInfo) +#endif + { + PVR_DPF((PVR_DBG_WARNING, "PVRSRVGetMiscInfoKM: " + "Ignoring non-deferred cache op with no meminfo")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if(psSysData->ePendingCacheOpType != PVRSRV_MISC_INFO_CPUCACHEOP_NONE) + { + PVR_DPF((PVR_DBG_WARNING, "PVRSRVGetMiscInfoKM: " + "Deferred cache op is pending. It is unlikely you want " + "to combine deferred cache ops with immediate ones")); + } + +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#else + psPerProc = PVRSRVFindPerProcessData(); + + if(PVRSRVLookupHandle(psPerProc->psHandleBase, + (IMG_PVOID *)&psKernelMemInfo, + psMiscInfo->sCacheOpCtl.u.psKernelMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetMiscInfoKM: " + "Can't find kernel meminfo")); + return PVRSRV_ERROR_INVALID_PARAMS; + } +#endif + + if(psMiscInfo->sCacheOpCtl.eCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_FLUSH) + { + if(!OSFlushCPUCacheRangeKM(psKernelMemInfo->sMemBlk.hOSMemHandle, + 0, + psMiscInfo->sCacheOpCtl.pvBaseVAddr, + psMiscInfo->sCacheOpCtl.ui32Length)) + { + return PVRSRV_ERROR_CACHEOP_FAILED; + } + } + else if(psMiscInfo->sCacheOpCtl.eCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_CLEAN) + { + if(!OSCleanCPUCacheRangeKM(psKernelMemInfo->sMemBlk.hOSMemHandle, + 0, + psMiscInfo->sCacheOpCtl.pvBaseVAddr, + psMiscInfo->sCacheOpCtl.ui32Length)) + { + return PVRSRV_ERROR_CACHEOP_FAILED; + } + } + } + } + + if((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_GET_REF_COUNT_PRESENT) != 0UL) + { +#if !defined (SUPPORT_SID_INTERFACE) + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + PVRSRV_PER_PROCESS_DATA *psPerProc; +#endif + + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_GET_REF_COUNT_PRESENT; + +#if defined (SUPPORT_SID_INTERFACE) + PVR_DBG_BREAK +#else + psPerProc = PVRSRVFindPerProcessData(); + + if(PVRSRVLookupHandle(psPerProc->psHandleBase, + (IMG_PVOID *)&psKernelMemInfo, + psMiscInfo->sGetRefCountCtl.u.psKernelMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetMiscInfoKM: " + "Can't find kernel meminfo")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psMiscInfo->sGetRefCountCtl.ui32RefCount = psKernelMemInfo->ui32RefCount; +#endif + } + + if ((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_GET_PAGE_SIZE_PRESENT) != 0UL) + { + psMiscInfo->ui32PageSize = HOST_PAGESIZE(); + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_GET_PAGE_SIZE_PRESENT; + } + +#if defined(PVRSRV_RESET_ON_HWTIMEOUT) + if((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_RESET_PRESENT) != 0UL) + { + PVR_LOG(("User requested OS reset")); + OSPanic(); + } +#endif /* #if defined(PVRSRV_RESET_ON_HWTIMEOUT) */ + + if ((psMiscInfo->ui32StateRequest & PVRSRV_MISC_INFO_FORCE_SWAP_TO_SYSTEM_PRESENT) != 0UL) + { + PVRSRVSetDCState(DC_STATE_FORCE_SWAP_TO_SYSTEM); + psMiscInfo->ui32StatePresent |= PVRSRV_MISC_INFO_FORCE_SWAP_TO_SYSTEM_PRESENT; + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDeviceLISR + + @Description + OS-independent Device Low-level Interrupt Service Routine + + @Input psDeviceNode + + @Return IMG_BOOL : Whether any interrupts were serviced + +******************************************************************************/ +IMG_BOOL IMG_CALLCONV PVRSRVDeviceLISR(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + SYS_DATA *psSysData; + IMG_BOOL bStatus = IMG_FALSE; + IMG_UINT32 ui32InterruptSource; + + if(!psDeviceNode) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVDeviceLISR: Invalid params\n")); + goto out; + } + psSysData = psDeviceNode->psSysData; + + /* query the SOC/system to see whether this device was the source of the interrupt */ + ui32InterruptSource = SysGetInterruptSource(psSysData, psDeviceNode); + if(ui32InterruptSource & psDeviceNode->ui32SOCInterruptBit) + { + if(psDeviceNode->pfnDeviceISR != IMG_NULL) + { + bStatus = (*psDeviceNode->pfnDeviceISR)(psDeviceNode->pvISRData); + } + if(!powering_down) { + SysClearInterrupts(psSysData, psDeviceNode->ui32SOCInterruptBit); + } + } + +out: + return bStatus; +} + +static IMG_VOID PVRSRVSystemLISR_ForEachVaCb(PVRSRV_DEVICE_NODE *psDeviceNode, va_list va) +{ + + IMG_BOOL *pbStatus; + IMG_UINT32 *pui32InterruptSource; + IMG_UINT32 *pui32ClearInterrupts; + + pbStatus = va_arg(va, IMG_BOOL*); + pui32InterruptSource = va_arg(va, IMG_UINT32*); + pui32ClearInterrupts = va_arg(va, IMG_UINT32*); + + + if(psDeviceNode->pfnDeviceISR != IMG_NULL) + { + if(*pui32InterruptSource & psDeviceNode->ui32SOCInterruptBit) + { + if((*psDeviceNode->pfnDeviceISR)(psDeviceNode->pvISRData)) + { + /* Record if serviced any interrupts. */ + *pbStatus = IMG_TRUE; + } + /* Combine the SOC clear bits. */ + *pui32ClearInterrupts |= psDeviceNode->ui32SOCInterruptBit; + } + } +} + +/*! +****************************************************************************** + + @Function PVRSRVSystemLISR + + @Description + OS-independent System Low-level Interrupt Service Routine + + @Input pvSysData + + @Return IMG_BOOL : Whether any interrupts were serviced + +******************************************************************************/ +IMG_BOOL IMG_CALLCONV PVRSRVSystemLISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = pvSysData; + IMG_BOOL bStatus = IMG_FALSE; + IMG_UINT32 ui32InterruptSource; + IMG_UINT32 ui32ClearInterrupts = 0; +/* PVRSRV_DEVICE_NODE *psDeviceNode;*/ + + if(!psSysData) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSystemLISR: Invalid params\n")); +/* goto out; */ + } + else + { + /* query SOC for source of interrupts */ + ui32InterruptSource = SysGetInterruptSource(psSysData, IMG_NULL); + + /* only proceed if PVR interrupts */ + if(ui32InterruptSource) + { + /* traverse the devices' ISR handlers */ + List_PVRSRV_DEVICE_NODE_ForEach_va(psSysData->psDeviceNodeList, + &PVRSRVSystemLISR_ForEachVaCb, + &bStatus, + &ui32InterruptSource, + &ui32ClearInterrupts); + + SysClearInterrupts(psSysData, ui32ClearInterrupts); + } +/*out:*/ + } + return bStatus; +} + + +static IMG_VOID PVRSRVMISR_ForEachCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + if(psDeviceNode->pfnDeviceMISR != IMG_NULL) + { + (*psDeviceNode->pfnDeviceMISR)(psDeviceNode->pvISRData); + } +} + +/*! +****************************************************************************** + + @Function PVRSRVMISR + + @Input pvSysData + + @Description + OS-independent Medium-level Interrupt Service Routine + +******************************************************************************/ +IMG_VOID IMG_CALLCONV PVRSRVMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = pvSysData; +/* PVRSRV_DEVICE_NODE *psDeviceNode; */ + + if(!psSysData) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVMISR: Invalid params\n")); + return; + } + + /* Traverse the devices' MISR handlers. */ + List_PVRSRV_DEVICE_NODE_ForEach(psSysData->psDeviceNodeList, + &PVRSRVMISR_ForEachCb); + + /* Process the queues. */ + if (PVRSRVProcessQueues(IMG_FALSE) == PVRSRV_ERROR_PROCESSING_BLOCKED) + { + PVRSRVProcessQueues(IMG_FALSE); + } + + /* signal global event object */ + if (psSysData->psGlobalEventObject) + { + IMG_HANDLE hOSEventKM = psSysData->psGlobalEventObject->hOSEventKM; + if(hOSEventKM) + { + OSEventObjectSignalKM(hOSEventKM); + } + } +} + + +/*! +****************************************************************************** + + @Function PVRSRVProcessConnect + + @Description Inform services that a process has connected. + + @Input ui32PID - process ID + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVProcessConnect(IMG_UINT32 ui32PID, IMG_UINT32 ui32Flags) +{ + return PVRSRVPerProcessDataConnect(ui32PID, ui32Flags); +} + + +/*! +****************************************************************************** + + @Function PVRSRVProcessDisconnect + + @Description Inform services that a process has disconnected. + + @Input ui32PID - process ID + + @Return IMG_VOID + +******************************************************************************/ +IMG_EXPORT +IMG_VOID IMG_CALLCONV PVRSRVProcessDisconnect(IMG_UINT32 ui32PID) +{ + PVRSRVPerProcessDataDisconnect(ui32PID); +} + + +/*! +****************************************************************************** + + @Function PVRSRVSaveRestoreLiveSegments + + @Input pArena - the arena the segment was originally allocated from. + pbyBuffer - the system memory buffer set to null to get the size needed. + puiBufSize - size of system memory buffer. + bSave - IMG_TRUE if a save is required + + @Description + Function to save or restore Resources Live segments + +******************************************************************************/ +PVRSRV_ERROR IMG_CALLCONV PVRSRVSaveRestoreLiveSegments(IMG_HANDLE hArena, IMG_PBYTE pbyBuffer, + IMG_SIZE_T *puiBufSize, IMG_BOOL bSave) +{ + IMG_SIZE_T uiBytesSaved = 0; + IMG_PVOID pvLocalMemCPUVAddr; + RA_SEGMENT_DETAILS sSegDetails; + + if (hArena == IMG_NULL) + { + return (PVRSRV_ERROR_INVALID_PARAMS); + } + + sSegDetails.uiSize = 0; + sSegDetails.sCpuPhyAddr.uiAddr = 0; + sSegDetails.hSegment = 0; + + /* walk the arena segments and write live one to the buffer */ + while (RA_GetNextLiveSegment(hArena, &sSegDetails)) + { + if (pbyBuffer == IMG_NULL) + { + /* calc buffer required */ + uiBytesSaved += sizeof(sSegDetails.uiSize) + sSegDetails.uiSize; + } + else + { + if ((uiBytesSaved + sizeof(sSegDetails.uiSize) + sSegDetails.uiSize) > *puiBufSize) + { + return (PVRSRV_ERROR_OUT_OF_MEMORY); + } + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVSaveRestoreLiveSegments: Base %08x size %08x", sSegDetails.sCpuPhyAddr.uiAddr, sSegDetails.uiSize)); + + /* Map the device's local memory area onto the host. */ + pvLocalMemCPUVAddr = OSMapPhysToLin(sSegDetails.sCpuPhyAddr, + sSegDetails.uiSize, + PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED, + IMG_NULL); + if (pvLocalMemCPUVAddr == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSaveRestoreLiveSegments: Failed to map local memory to host")); + return (PVRSRV_ERROR_OUT_OF_MEMORY); + } + + if (bSave) + { + /* write segment size then segment data */ + OSMemCopy(pbyBuffer, &sSegDetails.uiSize, sizeof(sSegDetails.uiSize)); + pbyBuffer += sizeof(sSegDetails.uiSize); + + OSMemCopy(pbyBuffer, pvLocalMemCPUVAddr, sSegDetails.uiSize); + pbyBuffer += sSegDetails.uiSize; + } + else + { + IMG_UINT32 uiSize; + /* reag segment size and validate */ + OSMemCopy(&uiSize, pbyBuffer, sizeof(sSegDetails.uiSize)); + + if (uiSize != sSegDetails.uiSize) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVSaveRestoreLiveSegments: Segment size error")); + } + else + { + pbyBuffer += sizeof(sSegDetails.uiSize); + + OSMemCopy(pvLocalMemCPUVAddr, pbyBuffer, sSegDetails.uiSize); + pbyBuffer += sSegDetails.uiSize; + } + } + + + uiBytesSaved += sizeof(sSegDetails.uiSize) + sSegDetails.uiSize; + + OSUnMapPhysToLin(pvLocalMemCPUVAddr, + sSegDetails.uiSize, + PVRSRV_HAP_KERNEL_ONLY|PVRSRV_HAP_UNCACHED, + IMG_NULL); + } + } + + if (pbyBuffer == IMG_NULL) + { + *puiBufSize = uiBytesSaved; + } + + return (PVRSRV_OK); +} + + +/*! + ****************************************************************************** + + @Function PVRSRVGetErrorStringKM + + @Description Returns a text string relating to the PVRSRV_ERROR enum. + + @Note case statement used rather than an indexed arrary to ensure text is + synchronised with the correct enum + + @Input eError : PVRSRV_ERROR enum + + @Return const IMG_CHAR * : Text string + + @Note Must be kept in sync with servicesext.h + +******************************************************************************/ + +IMG_EXPORT +const IMG_CHAR *PVRSRVGetErrorStringKM(PVRSRV_ERROR eError) +{ +/* PRQA S 5087 1 */ /* include file required here */ +#include "pvrsrv_errors.h" +} + +static IMG_VOID PVRSRVCommandCompleteCallbacks_ForEachCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + if(psDeviceNode->pfnDeviceCommandComplete != IMG_NULL) + { + /* Call the device's callback function. */ + (*psDeviceNode->pfnDeviceCommandComplete)(psDeviceNode); + } +} + +/*! +****************************************************************************** + + @Function PVRSRVScheduleDeviceCallbacks + + @Description Schedule all device callbacks + + @Return IMG_VOID + +******************************************************************************/ +IMG_VOID PVRSRVScheduleDeviceCallbacks(IMG_VOID) +{ + SYS_DATA *psSysData; +/* PVRSRV_DEVICE_NODE *psDeviceNode;*/ + + SysAcquireData(&psSysData); + + /*for all the device, invoke the callback function*/ + List_PVRSRV_DEVICE_NODE_ForEach(psSysData->psDeviceNodeList, + &PVRSRVCommandCompleteCallbacks_ForEachCb); +} + +/*! +****************************************************************************** + + @Function PVRSRVScheduleDevices + + @Description Schedules all Services-Managed Devices to check their pending + command queues. The intention is that ScheduleDevices be called by the + 3rd party BC driver after it has finished writing new data to its output + texture. + + @Return IMG_VOID + +******************************************************************************/ +IMG_EXPORT +IMG_VOID PVRSRVScheduleDevicesKM(IMG_VOID) +{ + PVRSRVScheduleDeviceCallbacks(); +} + +/***************************************************************************** + End of file (pvrsrv.c) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/queue.c b/pvr-source/services4/srvkm/common/queue.c new file mode 100644 index 0000000..88b05a4 --- /dev/null +++ b/pvr-source/services4/srvkm/common/queue.c @@ -0,0 +1,1500 @@ +/*************************************************************************/ /*! +@Title Kernel side command queue functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "pvr_bridge_km.h" + +#include "lists.h" +#include "ttrace.h" + +/* + * The number of commands of each type which can be in flight at once. + */ +#if defined(SUPPORT_DC_CMDCOMPLETE_WHEN_NO_LONGER_DISPLAYED) +#define DC_NUM_COMMANDS_PER_TYPE 2 +#else +#define DC_NUM_COMMANDS_PER_TYPE 1 +#endif + +/* + * List of private command processing function pointer tables and command + * complete tables for a device in the system. + * Each table is allocated when the device registers its private command + * processing functions. + */ +typedef struct _DEVICE_COMMAND_DATA_ +{ + PFN_CMD_PROC pfnCmdProc; + PCOMMAND_COMPLETE_DATA apsCmdCompleteData[DC_NUM_COMMANDS_PER_TYPE]; + IMG_UINT32 ui32CCBOffset; + IMG_UINT32 ui32MaxDstSyncCount; /*!< Maximum number of dest syncs */ + IMG_UINT32 ui32MaxSrcSyncCount; /*!< Maximum number of source syncs */ +} DEVICE_COMMAND_DATA; + + +#if defined(__linux__) && defined(__KERNEL__) + +#include "proc.h" + +/***************************************************************************** + FUNCTION : ProcSeqShowQueue + + PURPOSE : Print the content of queue element to /proc file + (See env/linux/proc.c:CreateProcReadEntrySeq) + + PARAMETERS : sfile - /proc seq_file + el - Element to print +*****************************************************************************/ +void ProcSeqShowQueue(struct seq_file *sfile,void* el) +{ + PVRSRV_QUEUE_INFO *psQueue = (PVRSRV_QUEUE_INFO*)el; + IMG_INT cmds = 0; + IMG_SIZE_T ui32ReadOffset; + IMG_SIZE_T ui32WriteOffset; + PVRSRV_COMMAND *psCmd; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf( sfile, + "Command Queues\n" + "Queue CmdPtr Pid Command Size DevInd DSC SSC #Data ...\n"); + return; + } + + ui32ReadOffset = psQueue->ui32ReadOffset; + ui32WriteOffset = psQueue->ui32WriteOffset; + + while (ui32ReadOffset != ui32WriteOffset) + { + psCmd= (PVRSRV_COMMAND *)((IMG_UINTPTR_T)psQueue->pvLinQueueKM + ui32ReadOffset); + + seq_printf(sfile, "%x %x %5u %6u %3u %5u %2u %2u %3u \n", + (IMG_UINTPTR_T)psQueue, + (IMG_UINTPTR_T)psCmd, + psCmd->ui32ProcessID, + psCmd->CommandType, + psCmd->uCmdSize, + psCmd->ui32DevIndex, + psCmd->ui32DstSyncCount, + psCmd->ui32SrcSyncCount, + psCmd->uDataSize); + { + IMG_UINT32 i; + for (i = 0; i < psCmd->ui32SrcSyncCount; i++) + { + PVRSRV_SYNC_DATA *psSyncData = psCmd->psSrcSync[i].psKernelSyncInfoKM->psSyncData; + seq_printf(sfile, " Sync %u: ROP/ROC: 0x%x/0x%x WOP/WOC: 0x%x/0x%x ROC-VA: 0x%x WOC-VA: 0x%x\n", + i, + psCmd->psSrcSync[i].ui32ReadOps2Pending, + psSyncData->ui32ReadOps2Complete, + psCmd->psSrcSync[i].ui32WriteOpsPending, + psSyncData->ui32WriteOpsComplete, + psCmd->psSrcSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCmd->psSrcSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr); + } + } + + /* taken from UPDATE_QUEUE_ROFF in queue.h */ + ui32ReadOffset += psCmd->uCmdSize; + ui32ReadOffset &= psQueue->ui32QueueSize - 1; + cmds++; + } + + if (cmds == 0) + { + seq_printf(sfile, "%x <empty>\n", (IMG_UINTPTR_T)psQueue); + } +} + +/***************************************************************************** + FUNCTION : ProcSeqOff2ElementQueue + + PURPOSE : Transale offset to element (/proc stuff) + + PARAMETERS : sfile - /proc seq_file + off - the offset into the buffer + + RETURNS : element to print +*****************************************************************************/ +void* ProcSeqOff2ElementQueue(struct seq_file * sfile, loff_t off) +{ + PVRSRV_QUEUE_INFO *psQueue = IMG_NULL; + SYS_DATA *psSysData; + + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + + psSysData = SysAcquireDataNoCheck(); + if (psSysData != IMG_NULL) + { + for (psQueue = psSysData->psQueueList; (((--off) > 0) && (psQueue != IMG_NULL)); psQueue = psQueue->psNextKM); + } + + return psQueue; +} +#endif /* __linux__ && __KERNEL__ */ + +/*! + * Macro to return space in given command queue + */ +#define GET_SPACE_IN_CMDQ(psQueue) \ + ((((psQueue)->ui32ReadOffset - (psQueue)->ui32WriteOffset) \ + + ((psQueue)->ui32QueueSize - 1)) & ((psQueue)->ui32QueueSize - 1)) + +/*! + * Macro to Write Offset in given command queue + */ +#define UPDATE_QUEUE_WOFF(psQueue, ui32Size) \ + (psQueue)->ui32WriteOffset = ((psQueue)->ui32WriteOffset + (ui32Size)) \ + & ((psQueue)->ui32QueueSize - 1); + +/*! + * Check if an ops complete value has gone past the pending value. + * This can happen when dummy processing multiple operations, e.g. hardware recovery. + */ +#define SYNCOPS_STALE(ui32OpsComplete, ui32OpsPending) \ + ((ui32OpsComplete) >= (ui32OpsPending)) + +/*! +**************************************************************************** + @Function : PVRSRVGetWriteOpsPending + + @Description : Gets the next operation to wait for in a sync object + + @Input : psSyncInfo - pointer to sync information struct + @Input : bIsReadOp - Is this a read or write op + + @Return : Next op value +*****************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(PVRSRVGetWriteOpsPending) +#endif +static INLINE +IMG_UINT32 PVRSRVGetWriteOpsPending(PVRSRV_KERNEL_SYNC_INFO *psSyncInfo, IMG_BOOL bIsReadOp) +{ + IMG_UINT32 ui32WriteOpsPending; + + if(bIsReadOp) + { + ui32WriteOpsPending = psSyncInfo->psSyncData->ui32WriteOpsPending; + } + else + { + /* + Note: This needs to be atomic and is provided the + kernel driver is single threaded (non-rentrant) + */ + ui32WriteOpsPending = psSyncInfo->psSyncData->ui32WriteOpsPending++; + } + + return ui32WriteOpsPending; +} + +/*! +***************************************************************************** + @Function : PVRSRVGetReadOpsPending + + @Description : Gets the number of pending read ops + + @Input : psSyncInfo - pointer to sync information struct + @Input : bIsReadOp - Is this a read or write op + + @Return : Next op value +*****************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(PVRSRVGetReadOpsPending) +#endif +static INLINE +IMG_UINT32 PVRSRVGetReadOpsPending(PVRSRV_KERNEL_SYNC_INFO *psSyncInfo, IMG_BOOL bIsReadOp) +{ + IMG_UINT32 ui32ReadOpsPending; + + if(bIsReadOp) + { + ui32ReadOpsPending = psSyncInfo->psSyncData->ui32ReadOps2Pending++; + } + else + { + ui32ReadOpsPending = psSyncInfo->psSyncData->ui32ReadOps2Pending; + } + + return ui32ReadOpsPending; +} + +static IMG_VOID QueueDumpCmdComplete(COMMAND_COMPLETE_DATA *psCmdCompleteData, + IMG_UINT32 i, + IMG_BOOL bIsSrc) +{ + PVRSRV_SYNC_OBJECT *psSyncObject; + + psSyncObject = bIsSrc ? psCmdCompleteData->psSrcSync : psCmdCompleteData->psDstSync; + + if (psCmdCompleteData->bInUse) + { + PVR_LOG(("\t%s %u: ROC DevVAddr:0x%X ROP:0x%x ROC:0x%x, WOC DevVAddr:0x%X WOP:0x%x WOC:0x%x", + bIsSrc ? "SRC" : "DEST", i, + psSyncObject[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psSyncObject[i].psKernelSyncInfoKM->psSyncData->ui32ReadOps2Pending, + psSyncObject[i].psKernelSyncInfoKM->psSyncData->ui32ReadOps2Complete, + psSyncObject[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psSyncObject[i].psKernelSyncInfoKM->psSyncData->ui32WriteOpsPending, + psSyncObject[i].psKernelSyncInfoKM->psSyncData->ui32WriteOpsComplete)) + } + else + { + PVR_LOG(("\t%s %u: (Not in use)", bIsSrc ? "SRC" : "DEST", i)) + } +} + + +static IMG_VOID QueueDumpDebugInfo_ForEachCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + if (psDeviceNode->sDevId.eDeviceClass == PVRSRV_DEVICE_CLASS_DISPLAY) + { + IMG_UINT32 ui32CmdCounter, ui32SyncCounter; + SYS_DATA *psSysData; + DEVICE_COMMAND_DATA *psDeviceCommandData; + PCOMMAND_COMPLETE_DATA psCmdCompleteData; + + SysAcquireData(&psSysData); + + psDeviceCommandData = psSysData->apsDeviceCommandData[psDeviceNode->sDevId.ui32DeviceIndex]; + + if (psDeviceCommandData != IMG_NULL) + { + for (ui32CmdCounter = 0; ui32CmdCounter < DC_NUM_COMMANDS_PER_TYPE; ui32CmdCounter++) + { + psCmdCompleteData = psDeviceCommandData[DC_FLIP_COMMAND].apsCmdCompleteData[ui32CmdCounter]; + + PVR_LOG(("Flip Command Complete Data %u for display device %u:", + ui32CmdCounter, psDeviceNode->sDevId.ui32DeviceIndex)) + + for (ui32SyncCounter = 0; + ui32SyncCounter < psCmdCompleteData->ui32SrcSyncCount; + ui32SyncCounter++) + { + QueueDumpCmdComplete(psCmdCompleteData, ui32SyncCounter, IMG_TRUE); + } + + for (ui32SyncCounter = 0; + ui32SyncCounter < psCmdCompleteData->ui32DstSyncCount; + ui32SyncCounter++) + { + QueueDumpCmdComplete(psCmdCompleteData, ui32SyncCounter, IMG_FALSE); + } + } + } + else + { + PVR_LOG(("There is no Command Complete Data for display device %u", psDeviceNode->sDevId.ui32DeviceIndex)) + } + } +} + + +IMG_VOID QueueDumpDebugInfo(IMG_VOID) +{ + SYS_DATA *psSysData; + SysAcquireData(&psSysData); + List_PVRSRV_DEVICE_NODE_ForEach(psSysData->psDeviceNodeList, &QueueDumpDebugInfo_ForEachCb); +} + + +/***************************************************************************** + Kernel-side functions of User->Kernel transitions +******************************************************************************/ + +static IMG_SIZE_T NearestPower2(IMG_SIZE_T ui32Value) +{ + IMG_SIZE_T ui32Temp, ui32Result = 1; + + if(!ui32Value) + return 0; + + ui32Temp = ui32Value - 1; + while(ui32Temp) + { + ui32Result <<= 1; + ui32Temp >>= 1; + } + + return ui32Result; +} + + +/*! +****************************************************************************** + + @Function PVRSRVCreateCommandQueueKM + + @Description + Creates a new command queue into which render/blt commands etc can be + inserted. + + @Input ui32QueueSize : + + @Output ppsQueueInfo : + + @Return PVRSRV_ERROR : + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVCreateCommandQueueKM(IMG_SIZE_T ui32QueueSize, + PVRSRV_QUEUE_INFO **ppsQueueInfo) +{ + PVRSRV_QUEUE_INFO *psQueueInfo; + IMG_SIZE_T ui32Power2QueueSize = NearestPower2(ui32QueueSize); + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + IMG_HANDLE hMemBlock; + + SysAcquireData(&psSysData); + + /* allocate an internal queue info structure */ + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_QUEUE_INFO), + (IMG_VOID **)&psQueueInfo, &hMemBlock, + "Queue Info"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateCommandQueueKM: Failed to alloc queue struct")); + goto ErrorExit; + } + OSMemSet(psQueueInfo, 0, sizeof(PVRSRV_QUEUE_INFO)); + + psQueueInfo->hMemBlock[0] = hMemBlock; + psQueueInfo->ui32ProcessID = OSGetCurrentProcessIDKM(); + + /* allocate the command queue buffer - allow for overrun */ + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + ui32Power2QueueSize + PVRSRV_MAX_CMD_SIZE, + &psQueueInfo->pvLinQueueKM, &hMemBlock, + "Command Queue"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVCreateCommandQueueKM: Failed to alloc queue buffer")); + goto ErrorExit; + } + + psQueueInfo->hMemBlock[1] = hMemBlock; + psQueueInfo->pvLinQueueUM = psQueueInfo->pvLinQueueKM; + + /* Sanity check: Should be zeroed by OSMemSet */ + PVR_ASSERT(psQueueInfo->ui32ReadOffset == 0); + PVR_ASSERT(psQueueInfo->ui32WriteOffset == 0); + + psQueueInfo->ui32QueueSize = ui32Power2QueueSize; + + /* if this is the first q, create a lock resource for the q list */ + if (psSysData->psQueueList == IMG_NULL) + { + eError = OSCreateResource(&psSysData->sQProcessResource); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + } + + /* Ensure we don't corrupt queue list, by blocking access */ + eError = OSLockResource(&psSysData->sQProcessResource, + KERNEL_ID); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + + psQueueInfo->psNextKM = psSysData->psQueueList; + psSysData->psQueueList = psQueueInfo; + + eError = OSUnlockResource(&psSysData->sQProcessResource, KERNEL_ID); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + + *ppsQueueInfo = psQueueInfo; + + return PVRSRV_OK; + +ErrorExit: + + if(psQueueInfo) + { + if(psQueueInfo->pvLinQueueKM) + { + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + psQueueInfo->ui32QueueSize, + psQueueInfo->pvLinQueueKM, + psQueueInfo->hMemBlock[1]); + psQueueInfo->pvLinQueueKM = IMG_NULL; + } + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_QUEUE_INFO), + psQueueInfo, + psQueueInfo->hMemBlock[0]); + /*not nulling pointer, out of scope*/ + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDestroyCommandQueueKM + + @Description Destroys a command queue + + @Input psQueueInfo : + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVDestroyCommandQueueKM(PVRSRV_QUEUE_INFO *psQueueInfo) +{ + PVRSRV_QUEUE_INFO *psQueue; + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + IMG_BOOL bTimeout = IMG_TRUE; + + SysAcquireData(&psSysData); + + psQueue = psSysData->psQueueList; + + /* PRQA S 3415,4109 1 */ /* macro format critical - leave alone */ + LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) + { + if(psQueueInfo->ui32ReadOffset == psQueueInfo->ui32WriteOffset) + { + bTimeout = IMG_FALSE; + break; + } + OSSleepms(1); + } END_LOOP_UNTIL_TIMEOUT(); + + if (bTimeout) + { + /* The command queue could not be flushed within the timeout period. + Allow the queue to be destroyed before returning the error code. */ + PVR_DPF((PVR_DBG_ERROR,"PVRSRVDestroyCommandQueueKM : Failed to empty queue")); + eError = PVRSRV_ERROR_CANNOT_FLUSH_QUEUE; + goto ErrorExit; + } + + /* Ensure we don't corrupt queue list, by blocking access */ + eError = OSLockResource(&psSysData->sQProcessResource, + KERNEL_ID); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + + if(psQueue == psQueueInfo) + { + psSysData->psQueueList = psQueueInfo->psNextKM; + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + NearestPower2(psQueueInfo->ui32QueueSize) + PVRSRV_MAX_CMD_SIZE, + psQueueInfo->pvLinQueueKM, + psQueueInfo->hMemBlock[1]); + psQueueInfo->pvLinQueueKM = IMG_NULL; + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_QUEUE_INFO), + psQueueInfo, + psQueueInfo->hMemBlock[0]); + /* PRQA S 3199 1 */ /* see note */ + psQueueInfo = IMG_NULL; /*it's a copy on stack, but null it because the function doesn't end right here*/ + } + else + { + while(psQueue) + { + if(psQueue->psNextKM == psQueueInfo) + { + psQueue->psNextKM = psQueueInfo->psNextKM; + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + psQueueInfo->ui32QueueSize, + psQueueInfo->pvLinQueueKM, + psQueueInfo->hMemBlock[1]); + psQueueInfo->pvLinQueueKM = IMG_NULL; + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_QUEUE_INFO), + psQueueInfo, + psQueueInfo->hMemBlock[0]); + /* PRQA S 3199 1 */ /* see note */ + psQueueInfo = IMG_NULL; /*it's a copy on stack, but null it because the function doesn't end right here*/ + break; + } + psQueue = psQueue->psNextKM; + } + + if(!psQueue) + { + eError = OSUnlockResource(&psSysData->sQProcessResource, KERNEL_ID); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto ErrorExit; + } + } + + /* unlock the Q list lock resource */ + eError = OSUnlockResource(&psSysData->sQProcessResource, KERNEL_ID); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + + /* if the Q list is now empty, destroy the Q list lock resource */ + if (psSysData->psQueueList == IMG_NULL) + { + eError = OSDestroyResource(&psSysData->sQProcessResource); + if (eError != PVRSRV_OK) + { + goto ErrorExit; + } + } + +ErrorExit: + + return eError; +} + + +/*! +***************************************************************************** + + @Function : PVRSRVGetQueueSpaceKM + + @Description : Waits for queue access rights and checks for available space in + queue for task param structure + + @Input : psQueue - pointer to queue information struct + @Input : ui32ParamSize - size of task data structure + @Output : ppvSpace + + @Return : PVRSRV_ERROR +*****************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVGetQueueSpaceKM(PVRSRV_QUEUE_INFO *psQueue, + IMG_SIZE_T ui32ParamSize, + IMG_VOID **ppvSpace) +{ + IMG_BOOL bTimeout = IMG_TRUE; + + /* round to 4byte units */ + ui32ParamSize = (ui32ParamSize+3) & 0xFFFFFFFC; + + if (ui32ParamSize > PVRSRV_MAX_CMD_SIZE) + { + PVR_DPF((PVR_DBG_WARNING,"PVRSRVGetQueueSpace: max command size is %d bytes", PVRSRV_MAX_CMD_SIZE)); + return PVRSRV_ERROR_CMD_TOO_BIG; + } + + /* PRQA S 3415,4109 1 */ /* macro format critical - leave alone */ + LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) + { + if (GET_SPACE_IN_CMDQ(psQueue) > ui32ParamSize) + { + bTimeout = IMG_FALSE; + break; + } + OSSleepms(1); + } END_LOOP_UNTIL_TIMEOUT(); + + if (bTimeout == IMG_TRUE) + { + *ppvSpace = IMG_NULL; + + return PVRSRV_ERROR_CANNOT_GET_QUEUE_SPACE; + } + else + { + *ppvSpace = (IMG_VOID *)((IMG_UINTPTR_T)psQueue->pvLinQueueUM + psQueue->ui32WriteOffset); + } + + return PVRSRV_OK; +} + + +/*! +***************************************************************************** + @Function PVRSRVInsertCommandKM + + @Description : + command insertion utility + - waits for space in the queue for a new command + - fills in generic command information + - returns a pointer to the caller who's expected to then fill + in the private data. + The caller should follow PVRSRVInsertCommand with PVRSRVSubmitCommand + which will update the queue's write offset so the command can be + executed. + + @Input psQueue : pointer to queue information struct + + @Output ppvCmdData : holds pointer to space in queue for private cmd data + + @Return PVRSRV_ERROR +*****************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVInsertCommandKM(PVRSRV_QUEUE_INFO *psQueue, + PVRSRV_COMMAND **ppsCommand, + IMG_UINT32 ui32DevIndex, + IMG_UINT16 CommandType, + IMG_UINT32 ui32DstSyncCount, + PVRSRV_KERNEL_SYNC_INFO *apsDstSync[], + IMG_UINT32 ui32SrcSyncCount, + PVRSRV_KERNEL_SYNC_INFO *apsSrcSync[], + IMG_SIZE_T ui32DataByteSize, + PFN_QUEUE_COMMAND_COMPLETE pfnCommandComplete, + IMG_HANDLE hCallbackData) +{ + PVRSRV_ERROR eError; + PVRSRV_COMMAND *psCommand; + IMG_SIZE_T ui32CommandSize; + IMG_UINT32 i; + SYS_DATA *psSysData; + DEVICE_COMMAND_DATA *psDeviceCommandData; + + /* Check that we've got enough space in our command complete data for this command */ + SysAcquireData(&psSysData); + psDeviceCommandData = psSysData->apsDeviceCommandData[ui32DevIndex]; + + if ((psDeviceCommandData[CommandType].ui32MaxDstSyncCount < ui32DstSyncCount) || + (psDeviceCommandData[CommandType].ui32MaxSrcSyncCount < ui32SrcSyncCount)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVInsertCommandKM: Too many syncs")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* Round up to nearest 32 bit size so pointer arithmetic works */ + ui32DataByteSize = (ui32DataByteSize + 3UL) & ~3UL; + + /* calc. command size */ + ui32CommandSize = sizeof(PVRSRV_COMMAND) + + ((ui32DstSyncCount + ui32SrcSyncCount) * sizeof(PVRSRV_SYNC_OBJECT)) + + ui32DataByteSize; + + /* wait for space in queue */ + eError = PVRSRVGetQueueSpaceKM (psQueue, ui32CommandSize, (IMG_VOID**)&psCommand); + if(eError != PVRSRV_OK) + { + return eError; + } + + psCommand->ui32ProcessID = OSGetCurrentProcessIDKM(); + + /* setup the command */ + psCommand->uCmdSize = ui32CommandSize; /* this may change if cmd shrinks */ + psCommand->ui32DevIndex = ui32DevIndex; + psCommand->CommandType = CommandType; + psCommand->ui32DstSyncCount = ui32DstSyncCount; + psCommand->ui32SrcSyncCount = ui32SrcSyncCount; + /* override QAC warning about stricter pointers */ + /* PRQA S 3305 END_PTR_ASSIGNMENTS */ + psCommand->psDstSync = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psCommand) + sizeof(PVRSRV_COMMAND)); + + + psCommand->psSrcSync = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psCommand->psDstSync) + + (ui32DstSyncCount * sizeof(PVRSRV_SYNC_OBJECT))); + + psCommand->pvData = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psCommand->psSrcSync) + + (ui32SrcSyncCount * sizeof(PVRSRV_SYNC_OBJECT))); +/* PRQA L:END_PTR_ASSIGNMENTS */ + + psCommand->uDataSize = ui32DataByteSize;/* this may change if cmd shrinks */ + + psCommand->pfnCommandComplete = pfnCommandComplete; + psCommand->hCallbackData = hCallbackData; + + PVR_TTRACE(PVRSRV_TRACE_GROUP_QUEUE, PVRSRV_TRACE_CLASS_CMD_START, QUEUE_TOKEN_INSERTKM); + PVR_TTRACE_UI32(PVRSRV_TRACE_GROUP_QUEUE, PVRSRV_TRACE_CLASS_NONE, + QUEUE_TOKEN_COMMAND_TYPE, CommandType); + + /* setup dst sync objects and their sync dependencies */ + for (i=0; i<ui32DstSyncCount; i++) + { + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_DST_SYNC, + apsDstSync[i], PVRSRV_SYNCOP_SAMPLE); + + psCommand->psDstSync[i].psKernelSyncInfoKM = apsDstSync[i]; + psCommand->psDstSync[i].ui32WriteOpsPending = PVRSRVGetWriteOpsPending(apsDstSync[i], IMG_FALSE); + psCommand->psDstSync[i].ui32ReadOps2Pending = PVRSRVGetReadOpsPending(apsDstSync[i], IMG_FALSE); + + PVRSRVKernelSyncInfoIncRef(apsDstSync[i], IMG_NULL); + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVInsertCommandKM: Dst %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", + i, psCommand->psDstSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCommand->psDstSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCommand->psDstSync[i].ui32ReadOps2Pending, + psCommand->psDstSync[i].ui32WriteOpsPending)); + } + + /* setup src sync objects and their sync dependencies */ + for (i=0; i<ui32SrcSyncCount; i++) + { + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_DST_SYNC, + apsSrcSync[i], PVRSRV_SYNCOP_SAMPLE); + + psCommand->psSrcSync[i].psKernelSyncInfoKM = apsSrcSync[i]; + psCommand->psSrcSync[i].ui32WriteOpsPending = PVRSRVGetWriteOpsPending(apsSrcSync[i], IMG_TRUE); + psCommand->psSrcSync[i].ui32ReadOps2Pending = PVRSRVGetReadOpsPending(apsSrcSync[i], IMG_TRUE); + + PVRSRVKernelSyncInfoIncRef(apsSrcSync[i], IMG_NULL); + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVInsertCommandKM: Src %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", + i, psCommand->psSrcSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCommand->psSrcSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCommand->psSrcSync[i].ui32ReadOps2Pending, + psCommand->psSrcSync[i].ui32WriteOpsPending)); + } + PVR_TTRACE(PVRSRV_TRACE_GROUP_QUEUE, PVRSRV_TRACE_CLASS_CMD_END, QUEUE_TOKEN_INSERTKM); + + /* return pointer to caller to fill out private data */ + *ppsCommand = psCommand; + + return PVRSRV_OK; +} + + +/*! +******************************************************************************* + @Function : PVRSRVSubmitCommandKM + + @Description : + updates the queue's write offset so the command can be executed. + + @Input : psQueue - queue command is in + @Input : psCommand + + @Return : PVRSRV_ERROR +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR IMG_CALLCONV PVRSRVSubmitCommandKM(PVRSRV_QUEUE_INFO *psQueue, + PVRSRV_COMMAND *psCommand) +{ + /* override QAC warnings about stricter pointers */ + /* PRQA S 3305 END_PTR_ASSIGNMENTS2 */ + /* patch pointers in the command to be kernel pointers */ + if (psCommand->ui32DstSyncCount > 0) + { + psCommand->psDstSync = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psQueue->pvLinQueueKM) + + psQueue->ui32WriteOffset + sizeof(PVRSRV_COMMAND)); + } + + if (psCommand->ui32SrcSyncCount > 0) + { + psCommand->psSrcSync = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psQueue->pvLinQueueKM) + + psQueue->ui32WriteOffset + sizeof(PVRSRV_COMMAND) + + (psCommand->ui32DstSyncCount * sizeof(PVRSRV_SYNC_OBJECT))); + } + + psCommand->pvData = (PVRSRV_SYNC_OBJECT*)(((IMG_UINTPTR_T)psQueue->pvLinQueueKM) + + psQueue->ui32WriteOffset + sizeof(PVRSRV_COMMAND) + + (psCommand->ui32DstSyncCount * sizeof(PVRSRV_SYNC_OBJECT)) + + (psCommand->ui32SrcSyncCount * sizeof(PVRSRV_SYNC_OBJECT))); + +/* PRQA L:END_PTR_ASSIGNMENTS2 */ + + /* update write offset before releasing access lock */ + UPDATE_QUEUE_WOFF(psQueue, psCommand->uCmdSize); + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function CheckIfSyncIsQueued + + @Description Check if the specificed sync object is already queued and + can safely be given to the display controller. + This check is required as a 3rd party displayclass device can + have several flips "in flight" and we need to ensure that we + keep their pipeline full and don't deadlock waiting for them + to complete an operation on a surface. + + @Input psSysData : system data + @Input psCmdData : COMMAND_COMPLETE_DATA structure + + @Return PVRSRV_ERROR + +******************************************************************************/ +static +PVRSRV_ERROR CheckIfSyncIsQueued(PVRSRV_SYNC_OBJECT *psSync, COMMAND_COMPLETE_DATA *psCmdData) +{ + IMG_UINT32 k; + + if (psCmdData->bInUse) + { + for (k=0;k<psCmdData->ui32SrcSyncCount;k++) + { + if (psSync->psKernelSyncInfoKM == psCmdData->psSrcSync[k].psKernelSyncInfoKM) + { + PVRSRV_SYNC_DATA *psSyncData = psSync->psKernelSyncInfoKM->psSyncData; + IMG_UINT32 ui32WriteOpsComplete = psSyncData->ui32WriteOpsComplete; + + /* + We still need to ensure that we don't we don't give a command + to the display controller if writes are outstanding on it + */ + if (ui32WriteOpsComplete == psSync->ui32WriteOpsPending) + { + return PVRSRV_OK; + } + else + { + if (SYNCOPS_STALE(ui32WriteOpsComplete, psSync->ui32WriteOpsPending)) + { + PVR_DPF((PVR_DBG_WARNING, + "CheckIfSyncIsQueued: Stale syncops psSyncData:0x%x ui32WriteOpsComplete:0x%x ui32WriteOpsPending:0x%x", + (IMG_UINTPTR_T)psSyncData, ui32WriteOpsComplete, psSync->ui32WriteOpsPending)); + return PVRSRV_OK; + } + } + } + } + } + return PVRSRV_ERROR_FAILED_DEPENDENCIES; +} + +/*! +****************************************************************************** + + @Function PVRSRVProcessCommand + + @Description Tries to process a command + + @Input psSysData : system data + @Input psCommand : PVRSRV_COMMAND structure + @Input bFlush : Check for stale dependencies (only used for HW recovery) + + @Return PVRSRV_ERROR + +******************************************************************************/ +static +PVRSRV_ERROR PVRSRVProcessCommand(SYS_DATA *psSysData, + PVRSRV_COMMAND *psCommand, + IMG_BOOL bFlush) +{ + PVRSRV_SYNC_OBJECT *psWalkerObj; + PVRSRV_SYNC_OBJECT *psEndObj; + IMG_UINT32 i; + COMMAND_COMPLETE_DATA *psCmdCompleteData; + PVRSRV_ERROR eError = PVRSRV_OK; + IMG_UINT32 ui32WriteOpsComplete; + IMG_UINT32 ui32ReadOpsComplete; + DEVICE_COMMAND_DATA *psDeviceCommandData; + IMG_UINT32 ui32CCBOffset; + + /* satisfy sync dependencies on the DST(s) */ + psWalkerObj = psCommand->psDstSync; + psEndObj = psWalkerObj + psCommand->ui32DstSyncCount; + while (psWalkerObj < psEndObj) + { + PVRSRV_SYNC_DATA *psSyncData = psWalkerObj->psKernelSyncInfoKM->psSyncData; + + ui32WriteOpsComplete = psSyncData->ui32WriteOpsComplete; + ui32ReadOpsComplete = psSyncData->ui32ReadOps2Complete; + /* fail if reads or writes are not up to date */ + if ((ui32WriteOpsComplete != psWalkerObj->ui32WriteOpsPending) + || (ui32ReadOpsComplete != psWalkerObj->ui32ReadOps2Pending)) + { + if (!bFlush || + !SYNCOPS_STALE(ui32WriteOpsComplete, psWalkerObj->ui32WriteOpsPending) || + !SYNCOPS_STALE(ui32ReadOpsComplete, psWalkerObj->ui32ReadOps2Pending)) + { + return PVRSRV_ERROR_FAILED_DEPENDENCIES; + } + } + + psWalkerObj++; + } + + /* satisfy sync dependencies on the SRC(s) */ + psWalkerObj = psCommand->psSrcSync; + psEndObj = psWalkerObj + psCommand->ui32SrcSyncCount; + while (psWalkerObj < psEndObj) + { + PVRSRV_SYNC_DATA *psSyncData = psWalkerObj->psKernelSyncInfoKM->psSyncData; + + ui32ReadOpsComplete = psSyncData->ui32ReadOps2Complete; + ui32WriteOpsComplete = psSyncData->ui32WriteOpsComplete; + /* fail if writes are not up to date */ + if ((ui32WriteOpsComplete != psWalkerObj->ui32WriteOpsPending) + || (ui32ReadOpsComplete != psWalkerObj->ui32ReadOps2Pending)) + { + if (!bFlush && + SYNCOPS_STALE(ui32WriteOpsComplete, psWalkerObj->ui32WriteOpsPending) && + SYNCOPS_STALE(ui32ReadOpsComplete, psWalkerObj->ui32ReadOps2Pending)) + { + PVR_DPF((PVR_DBG_WARNING, + "PVRSRVProcessCommand: Stale syncops psSyncData:0x%x ui32WriteOpsComplete:0x%x ui32WriteOpsPending:0x%x", + (IMG_UINTPTR_T)psSyncData, ui32WriteOpsComplete, psWalkerObj->ui32WriteOpsPending)); + } + + if (!bFlush || + !SYNCOPS_STALE(ui32WriteOpsComplete, psWalkerObj->ui32WriteOpsPending) || + !SYNCOPS_STALE(ui32ReadOpsComplete, psWalkerObj->ui32ReadOps2Pending)) + { + IMG_UINT32 j; + PVRSRV_ERROR eError; + IMG_BOOL bFound = IMG_FALSE; + + psDeviceCommandData = psSysData->apsDeviceCommandData[psCommand->ui32DevIndex]; + for (j=0;j<DC_NUM_COMMANDS_PER_TYPE;j++) + { + eError = CheckIfSyncIsQueued(psWalkerObj, psDeviceCommandData[psCommand->CommandType].apsCmdCompleteData[j]); + + if (eError == PVRSRV_OK) + { + bFound = IMG_TRUE; + } + } + if (!bFound) + return PVRSRV_ERROR_FAILED_DEPENDENCIES; + } + } + psWalkerObj++; + } + + /* validate device type */ + if (psCommand->ui32DevIndex >= SYS_DEVICE_COUNT) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVProcessCommand: invalid DeviceType 0x%x", + psCommand->ui32DevIndex)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* fish out the appropriate storage structure for the duration of the command */ + psDeviceCommandData = psSysData->apsDeviceCommandData[psCommand->ui32DevIndex]; + ui32CCBOffset = psDeviceCommandData[psCommand->CommandType].ui32CCBOffset; + psCmdCompleteData = psDeviceCommandData[psCommand->CommandType].apsCmdCompleteData[ui32CCBOffset]; + if (psCmdCompleteData->bInUse) + { + /* can use this to protect against concurrent execution of same command */ + return PVRSRV_ERROR_FAILED_DEPENDENCIES; + } + + /* mark the structure as in use */ + psCmdCompleteData->bInUse = IMG_TRUE; + + /* copy src updates over */ + psCmdCompleteData->ui32DstSyncCount = psCommand->ui32DstSyncCount; + for (i=0; i<psCommand->ui32DstSyncCount; i++) + { + psCmdCompleteData->psDstSync[i] = psCommand->psDstSync[i]; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVProcessCommand: Dst %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x (CCB:%u)", + i, psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCmdCompleteData->psDstSync[i].ui32ReadOps2Pending, + psCmdCompleteData->psDstSync[i].ui32WriteOpsPending, + ui32CCBOffset)); + } + + psCmdCompleteData->pfnCommandComplete = psCommand->pfnCommandComplete; + psCmdCompleteData->hCallbackData = psCommand->hCallbackData; + + /* copy dst updates over */ + psCmdCompleteData->ui32SrcSyncCount = psCommand->ui32SrcSyncCount; + for (i=0; i<psCommand->ui32SrcSyncCount; i++) + { + psCmdCompleteData->psSrcSync[i] = psCommand->psSrcSync[i]; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVProcessCommand: Src %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x (CCB:%u)", + i, psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCmdCompleteData->psSrcSync[i].ui32ReadOps2Pending, + psCmdCompleteData->psSrcSync[i].ui32WriteOpsPending, + ui32CCBOffset)); + } + + /* + call the cmd specific handler: + it should: + - check the cmd specific dependencies + - setup private cmd complete structure + - execute cmd on HW + - store psCmdCompleteData `cookie' and later pass as + argument to Generic Command Complete Callback + + n.b. ui32DataSize (packet size) is useful for packet validation + */ + if (psDeviceCommandData[psCommand->CommandType].pfnCmdProc((IMG_HANDLE)psCmdCompleteData, + (IMG_UINT32)psCommand->uDataSize, + psCommand->pvData) == IMG_FALSE) + { + /* + clean-up: + free cmd complete structure + */ + psCmdCompleteData->bInUse = IMG_FALSE; + eError = PVRSRV_ERROR_CMD_NOT_PROCESSED; + } + + /* Increment the CCB offset */ + psDeviceCommandData[psCommand->CommandType].ui32CCBOffset = (ui32CCBOffset + 1) % DC_NUM_COMMANDS_PER_TYPE; + + return eError; +} + + +static IMG_VOID PVRSRVProcessQueues_ForEachCb(PVRSRV_DEVICE_NODE *psDeviceNode) +{ + if (psDeviceNode->bReProcessDeviceCommandComplete && + psDeviceNode->pfnDeviceCommandComplete != IMG_NULL) + { + (*psDeviceNode->pfnDeviceCommandComplete)(psDeviceNode); + } +} + +/*! +****************************************************************************** + + @Function PVRSRVProcessQueues + + @Description Tries to process a command from each Q + + @input ui32CallerID - used to distinguish between async ISR/DPC type calls + the synchronous services driver + @input bFlush - flush commands with stale dependencies (only used for HW recovery) + + @Return PVRSRV_ERROR + +******************************************************************************/ + +IMG_EXPORT +PVRSRV_ERROR PVRSRVProcessQueues(IMG_BOOL bFlush) +{ + PVRSRV_QUEUE_INFO *psQueue; + SYS_DATA *psSysData; + PVRSRV_COMMAND *psCommand; +/* PVRSRV_DEVICE_NODE *psDeviceNode;*/ + + SysAcquireData(&psSysData); + + /* Ensure we don't corrupt queue list, by blocking access. This is required for OSs where + multiple ISR threads may exist simultaneously (eg WinXP DPC routines) + */ + while (OSLockResource(&psSysData->sQProcessResource, ISR_ID) != PVRSRV_OK) + { + OSWaitus(1); + }; + + psQueue = psSysData->psQueueList; + + if(!psQueue) + { + PVR_DPF((PVR_DBG_MESSAGE,"No Queues installed - cannot process commands")); + } + + if (bFlush) + { + PVRSRVSetDCState(DC_STATE_FLUSH_COMMANDS); + } + + while (psQueue) + { + while (psQueue->ui32ReadOffset != psQueue->ui32WriteOffset) + { + psCommand = (PVRSRV_COMMAND*)((IMG_UINTPTR_T)psQueue->pvLinQueueKM + psQueue->ui32ReadOffset); + + if (PVRSRVProcessCommand(psSysData, psCommand, bFlush) == PVRSRV_OK) + { + /* processed cmd so update queue */ + UPDATE_QUEUE_ROFF(psQueue, psCommand->uCmdSize) + continue; + } + + break; + } + psQueue = psQueue->psNextKM; + } + + if (bFlush) + { + PVRSRVSetDCState(DC_STATE_NO_FLUSH_COMMANDS); + } + + /* Re-process command complete handlers if necessary. */ + List_PVRSRV_DEVICE_NODE_ForEach(psSysData->psDeviceNodeList, + &PVRSRVProcessQueues_ForEachCb); + + OSUnlockResource(&psSysData->sQProcessResource, ISR_ID); + + return PVRSRV_OK; +} + +#if defined(SUPPORT_CUSTOM_SWAP_OPERATIONS) +/*! +****************************************************************************** + + @Function PVRSRVCommandCompleteKM + + @Description Updates non-private command complete sync objects + + @Input hCmdCookie : command cookie + @Input bScheduleMISR : obsolete parameter + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_INTERNAL +IMG_VOID PVRSRVFreeCommandCompletePacketKM(IMG_HANDLE hCmdCookie, + IMG_BOOL bScheduleMISR) +{ + COMMAND_COMPLETE_DATA *psCmdCompleteData = (COMMAND_COMPLETE_DATA *)hCmdCookie; + SYS_DATA *psSysData; + + PVR_UNREFERENCED_PARAMETER(bScheduleMISR); + + SysAcquireData(&psSysData); + + /* free command complete storage */ + psCmdCompleteData->bInUse = IMG_FALSE; + + /* FIXME: This may cause unrelated devices to be woken up. */ + PVRSRVScheduleDeviceCallbacks(); + + /* the MISR is always scheduled, regardless of bScheduleMISR */ + OSScheduleMISR(psSysData); +} + +#endif /* (SUPPORT_CUSTOM_SWAP_OPERATIONS) */ + +#if defined(SYS_OMAP4_HAS_DVFS_FRAMEWORK) +extern void sgxfreq_notif_sgx_frame_done(void); +#endif /* (SYS_OMAP4_HAS_DVFS_FRAMEWORK) */ + +/*! +****************************************************************************** + + @Function PVRSRVCommandCompleteKM + + @Description Updates non-private command complete sync objects + + @Input hCmdCookie : command cookie + @Input bScheduleMISR : boolean to schedule MISR + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +IMG_VOID PVRSRVCommandCompleteKM(IMG_HANDLE hCmdCookie, + IMG_BOOL bScheduleMISR) +{ + IMG_UINT32 i; + COMMAND_COMPLETE_DATA *psCmdCompleteData = (COMMAND_COMPLETE_DATA *)hCmdCookie; + SYS_DATA *psSysData; + +#if defined(SYS_OMAP4_HAS_DVFS_FRAMEWORK) + sgxfreq_notif_sgx_frame_done(); +#endif /* (SYS_OMAP4_HAS_DVFS_FRAMEWORK) */ + + SysAcquireData(&psSysData); + + PVR_TTRACE(PVRSRV_TRACE_GROUP_QUEUE, PVRSRV_TRACE_CLASS_CMD_COMP_START, + QUEUE_TOKEN_COMMAND_COMPLETE); + + /* update DST(s) syncs */ + for (i=0; i<psCmdCompleteData->ui32DstSyncCount; i++) + { + psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->psSyncData->ui32WriteOpsComplete++; + + PVRSRVKernelSyncInfoDecRef(psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM, IMG_NULL); + + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_UPDATE_DST, + psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM, + PVRSRV_SYNCOP_COMPLETE); + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVCommandCompleteKM: Dst %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", + i, psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCmdCompleteData->psDstSync[i].ui32ReadOps2Pending, + psCmdCompleteData->psDstSync[i].ui32WriteOpsPending)); + } + + /* update SRC(s) syncs */ + for (i=0; i<psCmdCompleteData->ui32SrcSyncCount; i++) + { + psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->psSyncData->ui32ReadOps2Complete++; + + PVRSRVKernelSyncInfoDecRef(psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM, IMG_NULL); + + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_UPDATE_SRC, + psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM, + PVRSRV_SYNCOP_COMPLETE); + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVCommandCompleteKM: Src %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", + i, psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, + psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, + psCmdCompleteData->psSrcSync[i].ui32ReadOps2Pending, + psCmdCompleteData->psSrcSync[i].ui32WriteOpsPending)); + } + + PVR_TTRACE(PVRSRV_TRACE_GROUP_QUEUE, PVRSRV_TRACE_CLASS_CMD_COMP_END, + QUEUE_TOKEN_COMMAND_COMPLETE); + + if (psCmdCompleteData->pfnCommandComplete) + { + psCmdCompleteData->pfnCommandComplete(psCmdCompleteData->hCallbackData); + } + + /* free command complete storage */ + psCmdCompleteData->bInUse = IMG_FALSE; + + /* FIXME: This may cause unrelated devices to be woken up. */ + PVRSRVScheduleDeviceCallbacks(); + + if(bScheduleMISR) + { + OSScheduleMISR(psSysData); + } +} + + + + +/*! +****************************************************************************** + + @Function PVRSRVRegisterCmdProcListKM + + @Description + + registers a list of private command processing functions with the Command + Queue Manager + + @Input ui32DevIndex : device index + + @Input ppfnCmdProcList : function ptr table of private command processors + + @Input ui32MaxSyncsPerCmd : max number of syncobjects used by command + + @Input ui32CmdCount : number of entries in function ptr table + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVRegisterCmdProcListKM(IMG_UINT32 ui32DevIndex, + PFN_CMD_PROC *ppfnCmdProcList, + IMG_UINT32 ui32MaxSyncsPerCmd[][2], + IMG_UINT32 ui32CmdCount) +{ + SYS_DATA *psSysData; + PVRSRV_ERROR eError; + IMG_UINT32 ui32CmdCounter, ui32CmdTypeCounter; + IMG_SIZE_T ui32AllocSize; + DEVICE_COMMAND_DATA *psDeviceCommandData; + COMMAND_COMPLETE_DATA *psCmdCompleteData; + + /* validate device type */ + if(ui32DevIndex >= SYS_DEVICE_COUNT) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVRegisterCmdProcListKM: invalid DeviceType 0x%x", + ui32DevIndex)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* acquire system data structure */ + SysAcquireData(&psSysData); + + /* array of pointers for each command store */ + ui32AllocSize = ui32CmdCount * sizeof(*psDeviceCommandData); + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + ui32AllocSize, + (IMG_VOID **)&psDeviceCommandData, IMG_NULL, + "Array of Pointers for Command Store"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterCmdProcListKM: Failed to alloc CC data")); + goto ErrorExit; + } + + psSysData->apsDeviceCommandData[ui32DevIndex] = psDeviceCommandData; + + for (ui32CmdTypeCounter = 0; ui32CmdTypeCounter < ui32CmdCount; ui32CmdTypeCounter++) + { + psDeviceCommandData[ui32CmdTypeCounter].pfnCmdProc = ppfnCmdProcList[ui32CmdTypeCounter]; + psDeviceCommandData[ui32CmdTypeCounter].ui32CCBOffset = 0; + psDeviceCommandData[ui32CmdTypeCounter].ui32MaxDstSyncCount = ui32MaxSyncsPerCmd[ui32CmdTypeCounter][0]; + psDeviceCommandData[ui32CmdTypeCounter].ui32MaxSrcSyncCount = ui32MaxSyncsPerCmd[ui32CmdTypeCounter][1]; + for (ui32CmdCounter = 0; ui32CmdCounter < DC_NUM_COMMANDS_PER_TYPE; ui32CmdCounter++) + { + /* + allocate storage for the sync update on command complete + */ + ui32AllocSize = sizeof(COMMAND_COMPLETE_DATA) /* space for one GENERIC_CMD_COMPLETE */ + + ((ui32MaxSyncsPerCmd[ui32CmdTypeCounter][0] + + ui32MaxSyncsPerCmd[ui32CmdTypeCounter][1]) + * sizeof(PVRSRV_SYNC_OBJECT)); /* space for max sync objects */ + + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + ui32AllocSize, + (IMG_VOID **)&psCmdCompleteData, + IMG_NULL, + "Command Complete Data"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVRegisterCmdProcListKM: Failed to alloc cmd %d", ui32CmdTypeCounter)); + goto ErrorExit; + } + + psDeviceCommandData[ui32CmdTypeCounter].apsCmdCompleteData[ui32CmdCounter] = psCmdCompleteData; + + /* clear memory */ + OSMemSet(psCmdCompleteData, 0x00, ui32AllocSize); + + /* setup sync pointers */ + psCmdCompleteData->psDstSync = (PVRSRV_SYNC_OBJECT*) + (((IMG_UINTPTR_T)psCmdCompleteData) + + sizeof(COMMAND_COMPLETE_DATA)); + psCmdCompleteData->psSrcSync = (PVRSRV_SYNC_OBJECT*) + (((IMG_UINTPTR_T)psCmdCompleteData->psDstSync) + + (sizeof(PVRSRV_SYNC_OBJECT) * ui32MaxSyncsPerCmd[ui32CmdTypeCounter][0])); + + psCmdCompleteData->ui32AllocSize = (IMG_UINT32)ui32AllocSize; + } + } + + return PVRSRV_OK; + +ErrorExit: + + /* clean-up if things went wrong */ + if (PVRSRVRemoveCmdProcListKM(ui32DevIndex, ui32CmdCount) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVRegisterCmdProcListKM: Failed to clean up after error, device 0x%x", + ui32DevIndex)); + } + + return eError; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRemoveCmdProcListKM + + @Description + + removes a list of private command processing functions and data from the + Queue Manager + + @Input ui32DevIndex : device index + + @Input ui32CmdCount : number of entries in function ptr table + + @Return PVRSRV_ERROR + +******************************************************************************/ +IMG_EXPORT +PVRSRV_ERROR PVRSRVRemoveCmdProcListKM(IMG_UINT32 ui32DevIndex, + IMG_UINT32 ui32CmdCount) +{ + SYS_DATA *psSysData; + IMG_UINT32 ui32CmdTypeCounter, ui32CmdCounter; + DEVICE_COMMAND_DATA *psDeviceCommandData; + COMMAND_COMPLETE_DATA *psCmdCompleteData; + IMG_SIZE_T ui32AllocSize; + + /* validate device type */ + if(ui32DevIndex >= SYS_DEVICE_COUNT) + { + PVR_DPF((PVR_DBG_ERROR, + "PVRSRVRemoveCmdProcListKM: invalid DeviceType 0x%x", + ui32DevIndex)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + /* acquire system data structure */ + SysAcquireData(&psSysData); + + psDeviceCommandData = psSysData->apsDeviceCommandData[ui32DevIndex]; + if(psDeviceCommandData != IMG_NULL) + { + for (ui32CmdTypeCounter = 0; ui32CmdTypeCounter < ui32CmdCount; ui32CmdTypeCounter++) + { + for (ui32CmdCounter = 0; ui32CmdCounter < DC_NUM_COMMANDS_PER_TYPE; ui32CmdCounter++) + { + psCmdCompleteData = psDeviceCommandData[ui32CmdTypeCounter].apsCmdCompleteData[ui32CmdCounter]; + + /* free the cmd complete structure array entries */ + if (psCmdCompleteData != IMG_NULL) + { + PVR_ASSERT(psCmdCompleteData->bInUse == IMG_FALSE); + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, psCmdCompleteData->ui32AllocSize, + psCmdCompleteData, IMG_NULL); + psDeviceCommandData[ui32CmdTypeCounter].apsCmdCompleteData[ui32CmdCounter] = IMG_NULL; + } + } + } + + /* free the cmd complete structure array for the device */ + ui32AllocSize = ui32CmdCount * sizeof(*psDeviceCommandData); + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, ui32AllocSize, psDeviceCommandData, IMG_NULL); + psSysData->apsDeviceCommandData[ui32DevIndex] = IMG_NULL; + } + + return PVRSRV_OK; +} + +/****************************************************************************** + End of file (queue.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/ra.c b/pvr-source/services4/srvkm/common/ra.c new file mode 100644 index 0000000..da48939 --- /dev/null +++ b/pvr-source/services4/srvkm/common/ra.c @@ -0,0 +1,2427 @@ +/*************************************************************************/ /*! +@Title Resource Allocator +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description + Implements generic resource allocation. The resource + allocator was originally intended to manage address spaces in + practice the resource allocator is generic and can manages arbitrary + sets of integers. + + Resources are allocated from arenas. Arena's can be created with an + initial span of resources. Further resources spans can be added to + arenas. A call back mechanism allows an arena to request further + resource spans on demand. + + Each arena maintains an ordered list of resource segments each + described by a boundary tag. Each boundary tag describes a segment + of resources which are either 'free', available for allocation, or + 'busy' currently allocated. Adjacent 'free' segments are always + coallesced to avoid fragmentation. + + For allocation, all 'free' segments are kept on lists of 'free' + segments in a table index by pvr_log2(segment size). ie Each table index + n holds 'free' segments in the size range 2**(n-1) -> 2**n. + + Allocation policy is based on an *almost* best fit + stratedy. Choosing any segment from the appropriate table entry + guarantees that we choose a segment which is with a power of 2 of + the size we are allocating. + + Allocated segments are inserted into a self scaling hash table which + maps the base resource of the span to the relevant boundary + tag. This allows the code to get back to the bounary tag without + exporting explicit boundary tag references through the API. + + Each arena has an associated quantum size, all allocations from the + arena are made in multiples of the basic quantum. + + On resource exhaustion in an arena, a callback if provided will be + used to request further resources. Resouces spans allocated by the + callback mechanism are delimited by special boundary tag markers of + zero span, 'span' markers. Span markers are never coallesced. Span + markers are used to detect when an imported span is completely free + and can be deallocated by the callback mechanism. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +/* Issues: + * - flags, flags are passed into the resource allocator but are not currently used. + * - determination, of import size, is currently braindead. + * - debug code should be moved out to own module and #ifdef'd + */ + +#include "services_headers.h" +#include "hash.h" +#include "ra.h" +#include "buffer_manager.h" +#include "osfunc.h" + +#if defined(__linux__) && defined(__KERNEL__) +#include <linux/kernel.h> +#include "pvr_uaccess.h" +#include "proc.h" +#include <linux/sched.h> +#endif + +#ifdef USE_BM_FREESPACE_CHECK +#include <stdio.h> +#endif + +/* The initial, and minimum size of the live address -> boundary tag + structure hash table. The value 64 is a fairly arbitrary + choice. The hash table resizes on demand so the value choosen is + not critical. */ +#define MINIMUM_HASH_SIZE (64) + +#if defined(VALIDATE_ARENA_TEST) + +/* This test validates the doubly linked ordered list of boundary tags, by +checking that adjacent members of the list have compatible eResourceSpan +and eResourceType values. */ + +typedef enum RESOURCE_DESCRIPTOR_TAG { + + RESOURCE_SPAN_LIVE = 10, + RESOURCE_SPAN_FREE, + IMPORTED_RESOURCE_SPAN_START, + IMPORTED_RESOURCE_SPAN_LIVE, + IMPORTED_RESOURCE_SPAN_FREE, + IMPORTED_RESOURCE_SPAN_END, + +} RESOURCE_DESCRIPTOR; + +typedef enum RESOURCE_TYPE_TAG { + + IMPORTED_RESOURCE_TYPE = 20, + NON_IMPORTED_RESOURCE_TYPE + +} RESOURCE_TYPE; + + +static IMG_UINT32 ui32BoundaryTagID = 0; + +IMG_UINT32 ValidateArena(RA_ARENA *pArena); +#endif + +/* boundary tags, used to describe a resource segment */ +struct _BT_ +{ + enum bt_type + { + btt_span, /* span markers */ + btt_free, /* free resource segment */ + btt_live /* allocated resource segment */ + } type; + + /* The base resource and extent of this segment */ + IMG_UINTPTR_T base; + IMG_SIZE_T uSize; + + /* doubly linked ordered list of all segments within the arena */ + struct _BT_ *pNextSegment; + struct _BT_ *pPrevSegment; + /* doubly linked un-ordered list of free segments. */ + struct _BT_ *pNextFree; + struct _BT_ *pPrevFree; + /* a user reference associated with this span, user references are + * currently only provided in the callback mechanism */ + BM_MAPPING *psMapping; + +#if defined(VALIDATE_ARENA_TEST) + RESOURCE_DESCRIPTOR eResourceSpan; + RESOURCE_TYPE eResourceType; + + /* This variable provides a reference (used in debug messages) to incompatible + boundary tags within the doubly linked ordered list. */ + IMG_UINT32 ui32BoundaryTagID; +#endif + +}; +typedef struct _BT_ BT; + + +/* resource allocation arena */ +struct _RA_ARENA_ +{ + /* arena name for diagnostics output */ + IMG_CHAR *name; + + /* allocations within this arena are quantum sized */ + IMG_SIZE_T uQuantum; + + /* import interface, if provided */ + IMG_BOOL (*pImportAlloc)(IMG_VOID *, + IMG_SIZE_T uSize, + IMG_SIZE_T *pActualSize, + BM_MAPPING **ppsMapping, + IMG_UINT32 uFlags, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINTPTR_T *pBase); + IMG_VOID (*pImportFree) (IMG_VOID *, + IMG_UINTPTR_T, + BM_MAPPING *psMapping); + IMG_VOID (*pBackingStoreFree) (IMG_VOID *, IMG_SIZE_T, IMG_SIZE_T, IMG_HANDLE); + + /* arbitrary handle provided by arena owner to be passed into the + * import alloc and free hooks */ + IMG_VOID *pImportHandle; + + /* head of list of free boundary tags for indexed by pvr_log2 of the + boundary tag size */ +#define FREE_TABLE_LIMIT 32 + + /* power-of-two table of free lists */ + BT *aHeadFree [FREE_TABLE_LIMIT]; + + /* resource ordered segment list */ + BT *pHeadSegment; + BT *pTailSegment; + + /* segment address to boundary tag hash table */ + HASH_TABLE *pSegmentHash; + +#ifdef RA_STATS + RA_STATISTICS sStatistics; +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) +#define PROC_NAME_SIZE 64 + + struct proc_dir_entry* pProcInfo; + struct proc_dir_entry* pProcSegs; + + IMG_BOOL bInitProcEntry; + +#if defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + struct proc_dir_entry* pProcAllocFailThreshold; + + IMG_BOOL bFailAllocationOnce; + IMG_BOOL bFailAllocationPersist; + IMG_SIZE_T uAllocFailThreshold; + IMG_UINT32 uAllocFailMask; +#endif //defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + +#endif +}; +/* #define ENABLE_RA_DUMP 1 */ +#if defined(ENABLE_RA_DUMP) +IMG_VOID RA_Dump (RA_ARENA *pArena); +#endif + +static INLINE IMG_BOOL RA_TestAllocationFail(RA_ARENA *pArena, IMG_SIZE_T size, IMG_UINT32 buff_type) +{ + #if defined (CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + if(pArena->bFailAllocationOnce == IMG_TRUE) + { + if((size > pArena->uAllocFailThreshold) && (pArena->uAllocFailMask & buff_type)) + { + if(pArena->bFailAllocationPersist == IMG_FALSE) + pArena->bFailAllocationOnce = IMG_FALSE; + return IMG_TRUE; + } + } + #endif //CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG + return IMG_FALSE; +} + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) + +static void RA_ProcSeqShowInfo(struct seq_file *sfile, void* el); +static void* RA_ProcSeqOff2ElementInfo(struct seq_file * sfile, loff_t off); + +static void RA_ProcSeqShowRegs(struct seq_file *sfile, void* el); +static void* RA_ProcSeqOff2ElementRegs(struct seq_file * sfile, loff_t off); + +#if defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) +static int RA_ProcSetAllocFailThreshold(struct file *file, const char __user *buffer, unsigned long count, void *data); +static void* RA_ProcSeqOff2AllocFailThreshold(struct seq_file * sfile, loff_t off); +static void RA_ProcSeqShowAllocFailThreshold(struct seq_file *sfile,void* el); +#endif //defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + +#endif /* defined(CONFIG_PROC_FS) && defined(DEBUG) */ + +static PVRSRV_ERROR RA_DumpHeapInfo(RA_ARENA *pArena, IMG_UINT32 ui32DebugLevel); + +#ifdef USE_BM_FREESPACE_CHECK +IMG_VOID CheckBMFreespace(IMG_VOID); +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) +static IMG_CHAR *ReplaceSpaces(IMG_CHAR * const pS) +{ + IMG_CHAR *pT; + + for(pT = pS; *pT != 0; pT++) + { + if (*pT == ' ' || *pT == '\t') + { + *pT = '_'; + } + } + + return pS; +} +#endif + +/*! +****************************************************************************** + @Function _RequestAllocFail + + @Description Default callback allocator used if no callback is + specified, always fails to allocate further resources to the + arena. + + @Input _h - callback handle + @Input _uSize - requested allocation size + @Output _pActualSize - actual allocation size + @Input _pRef - user reference + @Input _uflags - allocation flags + @Input _pvPrivData - private data + @Input _ui32PrivDataLength - private data length + @Input _pBase - receives allocated base + + @Return IMG_FALSE, this function always fails to allocate. +******************************************************************************/ +static IMG_BOOL +_RequestAllocFail (IMG_VOID *_h, + IMG_SIZE_T _uSize, + IMG_SIZE_T *_pActualSize, + BM_MAPPING **_ppsMapping, + IMG_UINT32 _uFlags, + IMG_PVOID _pvPrivData, + IMG_UINT32 _ui32PrivDataLength, + IMG_UINTPTR_T *_pBase) +{ + PVR_UNREFERENCED_PARAMETER (_h); + PVR_UNREFERENCED_PARAMETER (_uSize); + PVR_UNREFERENCED_PARAMETER (_pActualSize); + PVR_UNREFERENCED_PARAMETER (_ppsMapping); + PVR_UNREFERENCED_PARAMETER (_uFlags); + PVR_UNREFERENCED_PARAMETER (_pBase); + PVR_UNREFERENCED_PARAMETER (_pvPrivData); + PVR_UNREFERENCED_PARAMETER (_ui32PrivDataLength); + + return IMG_FALSE; +} + +/*! +****************************************************************************** + @Function pvr_log2 + + @Description Computes the floor of the log base 2 of a unsigned integer + + @Input n - unsigned integer + + @Return Floor(Log2(n)) +******************************************************************************/ +static IMG_UINT32 +pvr_log2 (IMG_SIZE_T n) +{ + IMG_UINT32 l = 0; + n>>=1; + while (n>0) + { + n>>=1; + l++; + } + return l; +} + +/*! +****************************************************************************** + @Function _SegmentListInsertAfter + + @Description Insert a boundary tag into an arena segment list after a + specified boundary tag. + + @Input pArena - the arena. + @Input pInsertionPoint - the insertion point. + @Input pBT - the boundary tag to insert. + + @Return PVRSRV_ERROR +******************************************************************************/ +static PVRSRV_ERROR +_SegmentListInsertAfter (RA_ARENA *pArena, + BT *pInsertionPoint, + BT *pBT) +{ + PVR_ASSERT (pArena != IMG_NULL); + PVR_ASSERT (pInsertionPoint != IMG_NULL); + + if ((pInsertionPoint == IMG_NULL) || (pArena == IMG_NULL)) + { + PVR_DPF ((PVR_DBG_ERROR,"_SegmentListInsertAfter: invalid parameters")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + pBT->pNextSegment = pInsertionPoint->pNextSegment; + pBT->pPrevSegment = pInsertionPoint; + if (pInsertionPoint->pNextSegment == IMG_NULL) + pArena->pTailSegment = pBT; + else + pInsertionPoint->pNextSegment->pPrevSegment = pBT; + pInsertionPoint->pNextSegment = pBT; + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + @Function _SegmentListInsert + + @Description Insert a boundary tag into an arena segment list at the + appropriate point. + + @Input pArena - the arena. + @Input pBT - the boundary tag to insert. + + @Return None +******************************************************************************/ +static PVRSRV_ERROR +_SegmentListInsert (RA_ARENA *pArena, BT *pBT) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + /* insert into the segment chain */ + if (pArena->pHeadSegment == IMG_NULL) + { + pArena->pHeadSegment = pArena->pTailSegment = pBT; + pBT->pNextSegment = pBT->pPrevSegment = IMG_NULL; + } + else + { + BT *pBTScan; + + if (pBT->base < pArena->pHeadSegment->base) + { + /* The base address of pBT is less than the base address of the boundary tag + at the head of the list - so insert this boundary tag at the head. */ + pBT->pNextSegment = pArena->pHeadSegment; + pArena->pHeadSegment->pPrevSegment = pBT; + pArena->pHeadSegment = pBT; + pBT->pPrevSegment = IMG_NULL; + } + else + { + + /* The base address of pBT is greater than or equal to that of the boundary tag + at the head of the list. Search for the insertion point: pBT must be inserted + before the first boundary tag with a greater base value - or at the end of the list. + */ + pBTScan = pArena->pHeadSegment; + + while ((pBTScan->pNextSegment != IMG_NULL) && (pBT->base >= pBTScan->pNextSegment->base)) + { + pBTScan = pBTScan->pNextSegment; + } + + eError = _SegmentListInsertAfter (pArena, pBTScan, pBT); + if (eError != PVRSRV_OK) + { + return eError; + } + } + } + return eError; +} + +/*! +****************************************************************************** + @Function _SegmentListRemove + + @Description Remove a boundary tag from an arena segment list. + + @Input pArena - the arena. + @Input pBT - the boundary tag to remove. + + @Return None +******************************************************************************/ +static IMG_VOID +_SegmentListRemove (RA_ARENA *pArena, BT *pBT) +{ + if (pBT->pPrevSegment == IMG_NULL) + pArena->pHeadSegment = pBT->pNextSegment; + else + pBT->pPrevSegment->pNextSegment = pBT->pNextSegment; + + if (pBT->pNextSegment == IMG_NULL) + pArena->pTailSegment = pBT->pPrevSegment; + else + pBT->pNextSegment->pPrevSegment = pBT->pPrevSegment; +} + +/*! +****************************************************************************** + @Function _SegmentSplit + + @Description Split a segment into two, maintain the arena segment list. The + boundary tag should not be in the free table. Neither the + original or the new neighbour bounary tag will be in the free + table. + + @Input pArena - the arena. + @Input pBT - the boundary tag to split. + @Input uSize - the required segment size of boundary tag after + splitting. + + @Return New neighbour boundary tag. + +******************************************************************************/ +static BT * +_SegmentSplit (RA_ARENA *pArena, BT *pBT, IMG_SIZE_T uSize) +{ + BT *pNeighbour; + + PVR_ASSERT (pArena != IMG_NULL); + + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_SegmentSplit: invalid parameter - pArena")); + return IMG_NULL; + } + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(BT), + (IMG_VOID **)&pNeighbour, IMG_NULL, + "Boundary Tag") != PVRSRV_OK) + { + return IMG_NULL; + } + + OSMemSet(pNeighbour, 0, sizeof(BT)); + +#if defined(VALIDATE_ARENA_TEST) + pNeighbour->ui32BoundaryTagID = ++ui32BoundaryTagID; +#endif + + pNeighbour->pPrevSegment = pBT; + pNeighbour->pNextSegment = pBT->pNextSegment; + if (pBT->pNextSegment == IMG_NULL) + pArena->pTailSegment = pNeighbour; + else + pBT->pNextSegment->pPrevSegment = pNeighbour; + pBT->pNextSegment = pNeighbour; + + pNeighbour->type = btt_free; + pNeighbour->uSize = pBT->uSize - uSize; + pNeighbour->base = pBT->base + uSize; + pNeighbour->psMapping = pBT->psMapping; + pBT->uSize = uSize; + +#if defined(VALIDATE_ARENA_TEST) + if (pNeighbour->pPrevSegment->eResourceType == IMPORTED_RESOURCE_TYPE) + { + pNeighbour->eResourceType = IMPORTED_RESOURCE_TYPE; + pNeighbour->eResourceSpan = IMPORTED_RESOURCE_SPAN_FREE; + } + else if (pNeighbour->pPrevSegment->eResourceType == NON_IMPORTED_RESOURCE_TYPE) + { + pNeighbour->eResourceType = NON_IMPORTED_RESOURCE_TYPE; + pNeighbour->eResourceSpan = RESOURCE_SPAN_FREE; + } + else + { + PVR_DPF ((PVR_DBG_ERROR,"_SegmentSplit: pNeighbour->pPrevSegment->eResourceType unrecognized")); + PVR_DBG_BREAK; + } +#endif + + return pNeighbour; +} + +/*! +****************************************************************************** + @Function _FreeListInsert + + @Description Insert a boundary tag into an arena free table. + + @Input pArena - the arena. + @Input pBT - the boundary tag. + + @Return None + +******************************************************************************/ +static IMG_VOID +_FreeListInsert (RA_ARENA *pArena, BT *pBT) +{ + IMG_UINT32 uIndex; + uIndex = pvr_log2 (pBT->uSize); + pBT->type = btt_free; + pBT->pNextFree = pArena->aHeadFree [uIndex]; + pBT->pPrevFree = IMG_NULL; + if (pArena->aHeadFree[uIndex] != IMG_NULL) + pArena->aHeadFree[uIndex]->pPrevFree = pBT; + pArena->aHeadFree [uIndex] = pBT; +} + +/*! +****************************************************************************** + @Function _FreeListRemove + + @Description Remove a boundary tag from an arena free table. + + @Input pArena - the arena. + @Input pBT - the boundary tag. + + @Return None + +******************************************************************************/ +static IMG_VOID +_FreeListRemove (RA_ARENA *pArena, BT *pBT) +{ + IMG_UINT32 uIndex; + uIndex = pvr_log2 (pBT->uSize); + if (pBT->pNextFree != IMG_NULL) + pBT->pNextFree->pPrevFree = pBT->pPrevFree; + if (pBT->pPrevFree == IMG_NULL) + pArena->aHeadFree[uIndex] = pBT->pNextFree; + else + pBT->pPrevFree->pNextFree = pBT->pNextFree; +} + +/*! +****************************************************************************** + @Function _BuildSpanMarker + + @Description Construct a span marker boundary tag. + + @Input pArena - arena to contain span marker + @Input base - the base of the bounary tag. + + @Return span marker boundary tag + +******************************************************************************/ +static BT * +_BuildSpanMarker (IMG_UINTPTR_T base, IMG_SIZE_T uSize) +{ + BT *pBT; + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(BT), + (IMG_VOID **)&pBT, IMG_NULL, + "Boundary Tag") != PVRSRV_OK) + { + return IMG_NULL; + } + + OSMemSet(pBT, 0, sizeof(BT)); + +#if defined(VALIDATE_ARENA_TEST) + pBT->ui32BoundaryTagID = ++ui32BoundaryTagID; +#endif + + pBT->type = btt_span; + pBT->base = base; + pBT->uSize = uSize; + pBT->psMapping = IMG_NULL; + + return pBT; +} + +/*! +****************************************************************************** + @Function _BuildBT + + @Description Construct a boundary tag for a free segment. + + @Input base - the base of the resource segment. + @Input uSize - the extent of the resouce segment. + + @Return boundary tag + +******************************************************************************/ +static BT * +_BuildBT (IMG_UINTPTR_T base, IMG_SIZE_T uSize) +{ + BT *pBT; + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(BT), + (IMG_VOID **)&pBT, IMG_NULL, + "Boundary Tag") != PVRSRV_OK) + { + return IMG_NULL; + } + + OSMemSet(pBT, 0, sizeof(BT)); + +#if defined(VALIDATE_ARENA_TEST) + pBT->ui32BoundaryTagID = ++ui32BoundaryTagID; +#endif + + pBT->type = btt_free; + pBT->base = base; + pBT->uSize = uSize; + + return pBT; +} + +/*! +****************************************************************************** + @Function _InsertResource + + @Description Add a free resource segment to an arena. + + @Input pArena - the arena. + @Input base - the base of the resource segment. + @Input uSize - the extent of the resource segment. + + @Return New bucket pointer + IMG_NULL failure + +******************************************************************************/ +static BT * +_InsertResource (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) +{ + BT *pBT; + PVR_ASSERT (pArena!=IMG_NULL); + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_InsertResource: invalid parameter - pArena")); + return IMG_NULL; + } + + pBT = _BuildBT (base, uSize); + if (pBT != IMG_NULL) + { + +#if defined(VALIDATE_ARENA_TEST) + pBT->eResourceSpan = RESOURCE_SPAN_FREE; + pBT->eResourceType = NON_IMPORTED_RESOURCE_TYPE; +#endif + + if (_SegmentListInsert (pArena, pBT) != PVRSRV_OK) + { + PVR_DPF ((PVR_DBG_ERROR,"_InsertResource: call to _SegmentListInsert failed")); + return IMG_NULL; + } + _FreeListInsert (pArena, pBT); +#ifdef RA_STATS + pArena->sStatistics.uTotalResourceCount+=uSize; + pArena->sStatistics.uFreeResourceCount+=uSize; + pArena->sStatistics.uSpanCount++; +#endif + } + return pBT; +} + +/*! +****************************************************************************** + @Function _InsertResourceSpan + + @Description Add a free resource span to an arena, complete with span markers. + + @Input pArena - the arena. + @Input base - the base of the resource segment. + @Input uSize - the extent of the resource segment. + + @Return the boundary tag representing the free resource segment, + or IMG_NULL on failure. +******************************************************************************/ +static BT * +_InsertResourceSpan (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) +{ + PVRSRV_ERROR eError; + BT *pSpanStart; + BT *pSpanEnd; + BT *pBT; + + PVR_ASSERT (pArena != IMG_NULL); + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_InsertResourceSpan: invalid parameter - pArena")); + return IMG_NULL; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_InsertResourceSpan: arena='%s', base=0x%x, size=0x%x", + pArena->name, base, uSize)); + + pSpanStart = _BuildSpanMarker (base, uSize); + if (pSpanStart == IMG_NULL) + { + goto fail_start; + } + +#if defined(VALIDATE_ARENA_TEST) + pSpanStart->eResourceSpan = IMPORTED_RESOURCE_SPAN_START; + pSpanStart->eResourceType = IMPORTED_RESOURCE_TYPE; +#endif + + pSpanEnd = _BuildSpanMarker (base + uSize, 0); + if (pSpanEnd == IMG_NULL) + { + goto fail_end; + } + +#if defined(VALIDATE_ARENA_TEST) + pSpanEnd->eResourceSpan = IMPORTED_RESOURCE_SPAN_END; + pSpanEnd->eResourceType = IMPORTED_RESOURCE_TYPE; +#endif + + pBT = _BuildBT (base, uSize); + if (pBT == IMG_NULL) + { + goto fail_bt; + } + +#if defined(VALIDATE_ARENA_TEST) + pBT->eResourceSpan = IMPORTED_RESOURCE_SPAN_FREE; + pBT->eResourceType = IMPORTED_RESOURCE_TYPE; +#endif + + eError = _SegmentListInsert (pArena, pSpanStart); + if (eError != PVRSRV_OK) + { + goto fail_SegListInsert; + } + + eError = _SegmentListInsertAfter (pArena, pSpanStart, pBT); + if (eError != PVRSRV_OK) + { + goto fail_SegListInsert; + } + + _FreeListInsert (pArena, pBT); + + eError = _SegmentListInsertAfter (pArena, pBT, pSpanEnd); + if (eError != PVRSRV_OK) + { + goto fail_SegListInsert; + } + +#ifdef RA_STATS + pArena->sStatistics.uTotalResourceCount+=uSize; +/* pArena->sStatistics.uFreeResourceCount+=uSize; + This has got to be wrong as uFreeResourceCount ends + up larger than uTotalResourceCount by uTotalResourceCount + - allocated memory +*/ +#endif + return pBT; + + fail_SegListInsert: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); + /*not nulling pointer, out of scope*/ + fail_bt: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pSpanEnd, IMG_NULL); + /*not nulling pointer, out of scope*/ + fail_end: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pSpanStart, IMG_NULL); + /*not nulling pointer, out of scope*/ + fail_start: + return IMG_NULL; +} + +/*! +****************************************************************************** + @Function _FreeBT + + @Description Free a boundary tag taking care of the segment list and the + boundary tag free table. + + @Input pArena - the arena. + @Input pBT - the boundary tag to free. + @Input bFreeBackingStore - Should backing for the memory be freed + as well. + @Return None +******************************************************************************/ +static IMG_VOID +_FreeBT (RA_ARENA *pArena, BT *pBT, IMG_BOOL bFreeBackingStore) +{ + BT *pNeighbour; + IMG_UINTPTR_T uOrigBase; + IMG_SIZE_T uOrigSize; + + PVR_ASSERT (pArena!=IMG_NULL); + PVR_ASSERT (pBT!=IMG_NULL); + + if ((pArena == IMG_NULL) || (pBT == IMG_NULL)) + { + PVR_DPF ((PVR_DBG_ERROR,"_FreeBT: invalid parameter")); + return; + } + +#ifdef RA_STATS + pArena->sStatistics.uLiveSegmentCount--; + pArena->sStatistics.uFreeSegmentCount++; + pArena->sStatistics.uFreeResourceCount+=pBT->uSize; +#endif + + uOrigBase = pBT->base; + uOrigSize = pBT->uSize; + + /* try and coalesce with left neighbour */ + pNeighbour = pBT->pPrevSegment; + if (pNeighbour!=IMG_NULL + && pNeighbour->type == btt_free + && pNeighbour->base + pNeighbour->uSize == pBT->base) + { + _FreeListRemove (pArena, pNeighbour); + _SegmentListRemove (pArena, pNeighbour); + pBT->base = pNeighbour->base; + pBT->uSize += pNeighbour->uSize; + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pNeighbour, IMG_NULL); + /*not nulling original pointer, already overwritten*/ +#ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount--; +#endif + } + + /* try to coalesce with right neighbour */ + pNeighbour = pBT->pNextSegment; + if (pNeighbour!=IMG_NULL + && pNeighbour->type == btt_free + && pBT->base + pBT->uSize == pNeighbour->base) + { + _FreeListRemove (pArena, pNeighbour); + _SegmentListRemove (pArena, pNeighbour); + pBT->uSize += pNeighbour->uSize; + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pNeighbour, IMG_NULL); + /*not nulling original pointer, already overwritten*/ +#ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount--; +#endif + } + + /* try to free backing store memory. */ + if (pArena->pBackingStoreFree != IMG_NULL && bFreeBackingStore) + { + IMG_UINTPTR_T uRoundedStart, uRoundedEnd; + + /* Work out the first address we might be able to free. */ + uRoundedStart = (uOrigBase / pArena->uQuantum) * pArena->uQuantum; + /* If a span is still using that address then leave it. */ + if (uRoundedStart < pBT->base) + { + uRoundedStart += pArena->uQuantum; + } + + /* Work out the last address we might be able to free. */ + uRoundedEnd = ((uOrigBase + uOrigSize + pArena->uQuantum - 1) / pArena->uQuantum) * pArena->uQuantum; + /* If a span is still using that addres then leave it. */ + if (uRoundedEnd > (pBT->base + pBT->uSize)) + { + uRoundedEnd -= pArena->uQuantum; + } + + if (uRoundedStart < uRoundedEnd) + { + pArena->pBackingStoreFree(pArena->pImportHandle, (IMG_SIZE_T)uRoundedStart, (IMG_SIZE_T)uRoundedEnd, (IMG_HANDLE)0); + } + } + + if (pBT->pNextSegment!=IMG_NULL && pBT->pNextSegment->type == btt_span + && pBT->pPrevSegment!=IMG_NULL && pBT->pPrevSegment->type == btt_span) + { + BT *next = pBT->pNextSegment; + BT *prev = pBT->pPrevSegment; + _SegmentListRemove (pArena, next); + _SegmentListRemove (pArena, prev); + _SegmentListRemove (pArena, pBT); + pArena->pImportFree (pArena->pImportHandle, pBT->base, pBT->psMapping); +#ifdef RA_STATS + pArena->sStatistics.uSpanCount--; + pArena->sStatistics.uExportCount++; + pArena->sStatistics.uFreeSegmentCount--; + pArena->sStatistics.uFreeResourceCount-=pBT->uSize; + pArena->sStatistics.uTotalResourceCount-=pBT->uSize; +#endif + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), next, IMG_NULL); + /*not nulling original pointer, already overwritten*/ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), prev, IMG_NULL); + /*not nulling original pointer, already overwritten*/ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); + /*not nulling pointer, copy on stack*/ + } + else + _FreeListInsert (pArena, pBT); +} + + +/*! +****************************************************************************** + @Function _AttemptAllocAligned + + @Description Attempt an allocation from an arena. + + @Input pArena - the arena. + @Input uSize - the requested allocation size. + @Output ppsMapping - the user references associated with + the allocated segment. + @Input flags - allocation flags + @Input uAlignment - required uAlignment, or 0 + @Input uAlignmentOffset + @Output base - allocated resource base + + @Return IMG_FALSE failure + IMG_TRUE success +******************************************************************************/ +static IMG_BOOL +_AttemptAllocAligned (RA_ARENA *pArena, + IMG_SIZE_T uSize, + BM_MAPPING **ppsMapping, + IMG_UINT32 uFlags, + IMG_UINT32 uAlignment, + IMG_UINT32 uAlignmentOffset, + IMG_UINTPTR_T *base) +{ + IMG_UINT32 uIndex; + PVR_ASSERT (pArena!=IMG_NULL); + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: invalid parameter - pArena")); + return IMG_FALSE; + } + + if (uAlignment>1) + uAlignmentOffset %= uAlignment; + + /* search for a near fit free boundary tag, start looking at the + pvr_log2 free table for our required size and work on up the + table. */ + uIndex = pvr_log2 (uSize); + + while (uIndex < FREE_TABLE_LIMIT && pArena->aHeadFree[uIndex]==IMG_NULL) + uIndex++; + + while (uIndex < FREE_TABLE_LIMIT) + { + if (pArena->aHeadFree[uIndex]!=IMG_NULL) + { + /* we have a cached free boundary tag */ + BT *pBT; + + pBT = pArena->aHeadFree [uIndex]; + while (pBT!=IMG_NULL) + { + IMG_UINTPTR_T aligned_base; + + if (uAlignment>1) + aligned_base = (pBT->base + uAlignmentOffset + uAlignment - 1) / uAlignment * uAlignment - uAlignmentOffset; + else + aligned_base = pBT->base; + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_AttemptAllocAligned: pBT-base=0x%x " + "pBT-size=0x%x alignedbase=0x%x size=0x%x", + pBT->base, pBT->uSize, aligned_base, uSize)); + + if (pBT->base + pBT->uSize >= aligned_base + uSize) + { + if(!pBT->psMapping || pBT->psMapping->ui32Flags == uFlags) + { + _FreeListRemove (pArena, pBT); + + PVR_ASSERT (pBT->type == btt_free); + +#ifdef RA_STATS + pArena->sStatistics.uLiveSegmentCount++; + pArena->sStatistics.uFreeSegmentCount--; + pArena->sStatistics.uFreeResourceCount-=pBT->uSize; +#endif + + /* with uAlignment we might need to discard the front of this segment */ + if (aligned_base > pBT->base) + { + BT *pNeighbour; + pNeighbour = _SegmentSplit (pArena, pBT, (IMG_SIZE_T)(aligned_base - pBT->base)); + /* partition the buffer, create a new boundary tag */ + if (pNeighbour==IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: Front split failed")); + /* Put pBT back in the list */ + _FreeListInsert (pArena, pBT); + return IMG_FALSE; + } + + _FreeListInsert (pArena, pBT); + #ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount++; + pArena->sStatistics.uFreeResourceCount+=pBT->uSize; + #endif + pBT = pNeighbour; + } + + /* the segment might be too big, if so, discard the back of the segment */ + if (pBT->uSize > uSize) + { + BT *pNeighbour; + pNeighbour = _SegmentSplit (pArena, pBT, uSize); + /* partition the buffer, create a new boundary tag */ + if (pNeighbour==IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: Back split failed")); + /* Put pBT back in the list */ + _FreeListInsert (pArena, pBT); + return IMG_FALSE; + } + + _FreeListInsert (pArena, pNeighbour); + #ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount++; + pArena->sStatistics.uFreeResourceCount+=pNeighbour->uSize; + #endif + } + + pBT->type = btt_live; + +#if defined(VALIDATE_ARENA_TEST) + if (pBT->eResourceType == IMPORTED_RESOURCE_TYPE) + { + pBT->eResourceSpan = IMPORTED_RESOURCE_SPAN_LIVE; + } + else if (pBT->eResourceType == NON_IMPORTED_RESOURCE_TYPE) + { + pBT->eResourceSpan = RESOURCE_SPAN_LIVE; + } + else + { + PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned ERROR: pBT->eResourceType unrecognized")); + PVR_DBG_BREAK; + } +#endif + if (!HASH_Insert (pArena->pSegmentHash, pBT->base, (IMG_UINTPTR_T) pBT)) + { + _FreeBT (pArena, pBT, IMG_FALSE); + return IMG_FALSE; + } + + if (ppsMapping!=IMG_NULL) + *ppsMapping = pBT->psMapping; + + *base = pBT->base; + + return IMG_TRUE; + } + else + { + PVR_DPF ((PVR_DBG_MESSAGE, + "AttemptAllocAligned: mismatch in flags. Import has %x, request was %x", pBT->psMapping->ui32Flags, uFlags)); + + } + } + pBT = pBT->pNextFree; + } + + } + uIndex++; + } + + return IMG_FALSE; +} + + + +/*! +****************************************************************************** + @Function RA_Create + + @Description To create a resource arena. + + @Input name - the name of the arena for diagnostic purposes. + @Input base - the base of an initial resource span or 0. + @Input uSize - the size of an initial resource span or 0. + @Input uQuantum - the arena allocation quantum. + @Input alloc - a resource allocation callback or 0. + @Input free - a resource de-allocation callback or 0. + @Input backingstore_free - a callback to free resources for spans or 0. + @Input pImportHandle - handle passed to alloc and free or 0. + + @Return arena handle, or IMG_NULL. +******************************************************************************/ +RA_ARENA * +RA_Create (IMG_CHAR *name, + IMG_UINTPTR_T base, + IMG_SIZE_T uSize, + BM_MAPPING *psMapping, + IMG_SIZE_T uQuantum, + IMG_BOOL (*imp_alloc)(IMG_VOID *, IMG_SIZE_T uSize, IMG_SIZE_T *pActualSize, + BM_MAPPING **ppsMapping, IMG_UINT32 _flags, + IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength, + IMG_UINTPTR_T *pBase), + IMG_VOID (*imp_free) (IMG_VOID *, IMG_UINTPTR_T, BM_MAPPING *), + IMG_VOID (*backingstore_free) (IMG_VOID*, IMG_SIZE_T, IMG_SIZE_T, IMG_HANDLE), + IMG_VOID *pImportHandle) +{ + RA_ARENA *pArena; + BT *pBT; + IMG_INT i; + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Create: name='%s', base=0x%x, uSize=0x%x, alloc=0x%x, free=0x%x", + name, base, uSize, (IMG_UINTPTR_T)imp_alloc, (IMG_UINTPTR_T)imp_free)); + + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof (*pArena), + (IMG_VOID **)&pArena, IMG_NULL, + "Resource Arena") != PVRSRV_OK) + { + goto arena_fail; + } + + pArena->name = name; + pArena->pImportAlloc = (imp_alloc!=IMG_NULL) ? imp_alloc : &_RequestAllocFail; + pArena->pImportFree = imp_free; + pArena->pBackingStoreFree = backingstore_free; + pArena->pImportHandle = pImportHandle; + for (i=0; i<FREE_TABLE_LIMIT; i++) + pArena->aHeadFree[i] = IMG_NULL; + pArena->pHeadSegment = IMG_NULL; + pArena->pTailSegment = IMG_NULL; + pArena->uQuantum = uQuantum; + +#ifdef RA_STATS + OSMemSet(&pArena->sStatistics, 0x00, sizeof(pArena->sStatistics)); +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) + if(strcmp(pArena->name,"") != 0) + { + IMG_INT ret; + IMG_CHAR szProcInfoName[PROC_NAME_SIZE]; + IMG_CHAR szProcSegsName[PROC_NAME_SIZE]; + struct proc_dir_entry* (*pfnCreateProcEntrySeq)(const IMG_CHAR *, + IMG_VOID*, + pvr_next_proc_seq_t, + pvr_show_proc_seq_t, + pvr_off2element_proc_seq_t, + pvr_startstop_proc_seq_t, + write_proc_t); + + pArena->bInitProcEntry = !PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_SUCCESSFUL); + + /* Don't put shared heap info into a per process /proc subdirectory */ + pfnCreateProcEntrySeq = pArena->bInitProcEntry ? CreateProcEntrySeq : CreatePerProcessProcEntrySeq; + + ret = snprintf(szProcInfoName, sizeof(szProcInfoName), "ra_info_%s", pArena->name); + if (ret > 0 && ret < sizeof(szProcInfoName)) + { + pArena->pProcInfo = pfnCreateProcEntrySeq(ReplaceSpaces(szProcInfoName), pArena, NULL, + RA_ProcSeqShowInfo, RA_ProcSeqOff2ElementInfo, NULL, NULL); + } + else + { + pArena->pProcInfo = 0; + PVR_DPF((PVR_DBG_ERROR, "RA_Create: couldn't create ra_info proc entry for arena %s", pArena->name)); + } + + ret = snprintf(szProcSegsName, sizeof(szProcSegsName), "ra_segs_%s", pArena->name); + if (ret > 0 && ret < sizeof(szProcSegsName)) + { + pArena->pProcSegs = pfnCreateProcEntrySeq(ReplaceSpaces(szProcSegsName), pArena, NULL, + RA_ProcSeqShowRegs, RA_ProcSeqOff2ElementRegs, NULL, NULL); + } + else + { + pArena->pProcSegs = 0; + PVR_DPF((PVR_DBG_ERROR, "RA_Create: couldn't create ra_segs proc entry for arena %s", pArena->name)); + } + +#if defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + pArena->uAllocFailThreshold = ~0; + pArena->uAllocFailMask = ~0; + pArena->bFailAllocationOnce = IMG_FALSE; + pArena->bFailAllocationPersist = IMG_FALSE; + + ret = snprintf(szProcSegsName, sizeof(szProcSegsName), "ra_fail_alloc_thld_%s", pArena->name); + if (ret > 0 && ret < sizeof(szProcSegsName)) + { + pArena->pProcAllocFailThreshold = pfnCreateProcEntrySeq(ReplaceSpaces(szProcSegsName), pArena, NULL, + RA_ProcSeqShowAllocFailThreshold, RA_ProcSeqOff2AllocFailThreshold, NULL, RA_ProcSetAllocFailThreshold); + } + else + { + pArena->pProcAllocFailThreshold = 0; + PVR_DPF((PVR_DBG_ERROR, "RA_Create: couldn't create ra_fail_alloc_thld proc entry for arena %s", pArena->name)); + } +#endif //defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + } +#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) */ + + pArena->pSegmentHash = HASH_Create (MINIMUM_HASH_SIZE); + if (pArena->pSegmentHash==IMG_NULL) + { + goto hash_fail; + } + if (uSize>0) + { + uSize = (uSize + uQuantum - 1) / uQuantum * uQuantum; + pBT = _InsertResource (pArena, base, uSize); + if (pBT == IMG_NULL) + { + goto insert_fail; + } + pBT->psMapping = psMapping; + + } + return pArena; + +insert_fail: + HASH_Delete (pArena->pSegmentHash); +hash_fail: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RA_ARENA), pArena, IMG_NULL); + /*not nulling pointer, out of scope*/ +arena_fail: + return IMG_NULL; +} + +/*! +****************************************************************************** + @Function RA_Delete + + @Description To delete a resource arena. All resources allocated from + the arena must be freed before deleting the arena. + + @Input pArena - the arena to delete. + + @Return None +******************************************************************************/ +IMG_VOID +RA_Delete (RA_ARENA *pArena) +{ + IMG_UINT32 uIndex; + + PVR_ASSERT(pArena != IMG_NULL); + + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: invalid parameter - pArena")); + return; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Delete: name='%s'", pArena->name)); + + for (uIndex=0; uIndex<FREE_TABLE_LIMIT; uIndex++) + pArena->aHeadFree[uIndex] = IMG_NULL; + + while (pArena->pHeadSegment != IMG_NULL) + { + BT *pBT = pArena->pHeadSegment; + + if (pBT->type != btt_free) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: allocations still exist in the arena that is being destroyed")); + PVR_DPF ((PVR_DBG_ERROR,"Likely Cause: client drivers not freeing allocations before destroying devmemcontext")); + PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: base = 0x%x size=0x%x", pBT->base, pBT->uSize)); + } + + _SegmentListRemove (pArena, pBT); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); + /*not nulling original pointer, it has changed*/ +#ifdef RA_STATS + pArena->sStatistics.uSpanCount--; +#endif + } +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) + { + IMG_VOID (*pfnRemoveProcEntrySeq)(struct proc_dir_entry*); + + pfnRemoveProcEntrySeq = pArena->bInitProcEntry ? RemoveProcEntrySeq : RemovePerProcessProcEntrySeq; + + if (pArena->pProcInfo != 0) + { + pfnRemoveProcEntrySeq( pArena->pProcInfo ); + } + + if (pArena->pProcSegs != 0) + { + pfnRemoveProcEntrySeq( pArena->pProcSegs ); + } + +#if defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + if(pArena->pProcAllocFailThreshold != 0) + { + pfnRemoveProcEntrySeq( pArena->pProcAllocFailThreshold ); + } +#endif //defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + } +#endif + HASH_Delete (pArena->pSegmentHash); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RA_ARENA), pArena, IMG_NULL); + /*not nulling pointer, copy on stack*/ +} + +/*! +****************************************************************************** + @Function RA_TestDelete + + @Description To test whether it is safe to delete a resource arena. If any + allocations have not been freed, the RA must not be deleted. + + @Input pArena - the arena to test. + + @Return IMG_BOOL - IMG_TRUE if is safe to go on and call RA_Delete. +******************************************************************************/ +IMG_BOOL +RA_TestDelete (RA_ARENA *pArena) +{ + PVR_ASSERT(pArena != IMG_NULL); + + if (pArena != IMG_NULL) + { + while (pArena->pHeadSegment != IMG_NULL) + { + BT *pBT = pArena->pHeadSegment; + if (pBT->type != btt_free) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_TestDelete: detected resource leak!")); + PVR_DPF ((PVR_DBG_ERROR,"RA_TestDelete: base = 0x%x size=0x%x", pBT->base, pBT->uSize)); + return IMG_FALSE; + } + } + } + + return IMG_TRUE; +} + +/*! +****************************************************************************** + @Function RA_Add + + @Description To add a resource span to an arena. The span must not + overlapp with any span previously added to the arena. + + @Input pArena - the arena to add a span into. + @Input base - the base of the span. + @Input uSize - the extent of the span. + + @Return IMG_TRUE - Success + IMG_FALSE - failure +******************************************************************************/ +IMG_BOOL +RA_Add (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) +{ + PVR_ASSERT (pArena != IMG_NULL); + + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_Add: invalid parameter - pArena")); + return IMG_FALSE; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Add: name='%s', base=0x%x, size=0x%x", pArena->name, base, uSize)); + + uSize = (uSize + pArena->uQuantum - 1) / pArena->uQuantum * pArena->uQuantum; + return ((IMG_BOOL)(_InsertResource (pArena, base, uSize) != IMG_NULL)); +} + +/*! +****************************************************************************** + @Function RA_Alloc + + @Description To allocate resource from an arena. + + @Input pArena - the arena + @Input uRequestSize - the size of resource segment requested. + @Output pActualSize - the actual size of resource segment + allocated, typcially rounded up by quantum. + @Output ppsMapping - the user reference associated with allocated resource span. + @Input uFlags - flags influencing allocation policy. + @Input uAlignment - the uAlignment constraint required for the + allocated segment, use 0 if uAlignment not required. + @Input uAlignmentOffset + @Input pvPrivData - opaque private data passed through to allocator + @Input ui32PrivDataLength - length of opaque private data + + @Output base - allocated base resource + + @Return IMG_TRUE - success + IMG_FALSE - failure +******************************************************************************/ +IMG_BOOL +RA_Alloc (RA_ARENA *pArena, + IMG_SIZE_T uRequestSize, + IMG_SIZE_T *pActualSize, + BM_MAPPING **ppsMapping, + IMG_UINT32 uFlags, + IMG_UINT32 uAlignment, + IMG_UINT32 uAlignmentOffset, + IMG_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_UINTPTR_T *base) +{ + IMG_BOOL bResult = IMG_FALSE; + IMG_BOOL bTestAllocFail = IMG_FALSE; + IMG_SIZE_T uSize = uRequestSize; + + PVR_ASSERT (pArena!=IMG_NULL); + + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_Alloc: invalid parameter - pArena")); + return IMG_FALSE; + } + +#if defined(VALIDATE_ARENA_TEST) + ValidateArena(pArena); +#endif + +#ifdef USE_BM_FREESPACE_CHECK + CheckBMFreespace(); +#endif + + if (pActualSize != IMG_NULL) + { + *pActualSize = uSize; + } + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Alloc: arena='%s', size=0x%x(0x%x), alignment=0x%x, offset=0x%x", + pArena->name, uSize, uRequestSize, uAlignment, uAlignmentOffset)); + + bTestAllocFail = RA_TestAllocationFail(pArena, uSize, ~0); + if(!bTestAllocFail) + { + /* if allocation failed then we might have an import source which + can provide more resource, else we will have to fail the + allocation to the caller. */ + bResult = _AttemptAllocAligned (pArena, uSize, ppsMapping, uFlags, + uAlignment, uAlignmentOffset, base); + if (!bResult) + { + BM_MAPPING *psImportMapping; + IMG_UINTPTR_T import_base; + IMG_SIZE_T uImportSize = uSize; + + /* + Ensure that we allocate sufficient space to meet the uAlignment + constraint + */ + if (uAlignment > pArena->uQuantum) + { + uImportSize += (uAlignment - 1); + } + + /* ensure that we import according to the quanta of this arena */ + uImportSize = ((uImportSize + pArena->uQuantum - 1)/pArena->uQuantum)*pArena->uQuantum; + + bResult = + pArena->pImportAlloc (pArena->pImportHandle, uImportSize, &uImportSize, + &psImportMapping, uFlags, + pvPrivData, ui32PrivDataLength, &import_base); + if (bResult) + { + BT *pBT; + pBT = _InsertResourceSpan (pArena, import_base, uImportSize); + /* successfully import more resource, create a span to + represent it and retry the allocation attempt */ + if (pBT == IMG_NULL) + { + /* insufficient resources to insert the newly acquired span, + so free it back again */ + pArena->pImportFree(pArena->pImportHandle, import_base, + psImportMapping); + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Alloc: name='%s', size=0x%x failed!", + pArena->name, uSize)); + /* RA_Dump (arena); */ + return IMG_FALSE; + } + pBT->psMapping = psImportMapping; + #ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount++; + pArena->sStatistics.uFreeResourceCount += uImportSize; + pArena->sStatistics.uImportCount++; + pArena->sStatistics.uSpanCount++; + #endif + bResult = _AttemptAllocAligned(pArena, uSize, ppsMapping, uFlags, + uAlignment, uAlignmentOffset, + base); + if (!bResult) + { + PVR_DPF ((PVR_DBG_ERROR, + "RA_Alloc: name='%s' uAlignment failed!", + pArena->name)); + } + } + } + #ifdef RA_STATS + if (bResult) + pArena->sStatistics.uCumulativeAllocs++; + #endif + } + + PVR_DPF((PVR_DBG_MESSAGE, + "RA_Alloc: arena=%s, size=0x%x(0x%x), alignment=0x%x, "\ + "offset=0x%x, result=%d", + pArena->name, + uSize, uRequestSize, uAlignment, uAlignmentOffset, + bResult)); + + /* RA_Dump (pArena); + ra_stats (pArena); + */ + + if (!bResult) { + PVR_LOG(("RA_Alloc %s %s: arena=%s, size=0x%x(0x%x), "\ + "alignment=0x%x, offset=0x%x", + (bResult ? "SUCCESS" : "FAILED"), + (bTestAllocFail ? "in TEST_MODE!" : " "), + pArena->name, + uSize, uRequestSize, uAlignment, uAlignmentOffset)); + RA_DumpHeapInfo(pArena, ~0); + } +#if defined(VALIDATE_ARENA_TEST) + ValidateArena(pArena); +#endif + + return bResult; +} + + +#if defined(VALIDATE_ARENA_TEST) + +/*! +****************************************************************************** + @Function ValidateArena + + @Description Validate an arena by checking that adjacent members of the + double linked ordered list are compatible. PVR_DBG_BREAK and + PVR_DPF messages are used when an error is detected. + NOTE: A DEBUG build is required for PVR_DBG_BREAK and PVR_DPF + to operate. + + @Input pArena - the arena + + @Return 0 +******************************************************************************/ +IMG_UINT32 ValidateArena(RA_ARENA *pArena) +{ + BT* pSegment; + RESOURCE_DESCRIPTOR eNextSpan; + + pSegment = pArena->pHeadSegment; + + if (pSegment == IMG_NULL) + { + return 0; + } + + if (pSegment->eResourceType == IMPORTED_RESOURCE_TYPE) + { + PVR_ASSERT(pSegment->eResourceSpan == IMPORTED_RESOURCE_SPAN_START); + + while (pSegment->pNextSegment) + { + eNextSpan = pSegment->pNextSegment->eResourceSpan; + + switch (pSegment->eResourceSpan) + { + case IMPORTED_RESOURCE_SPAN_LIVE: + + if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_END))) + { + /* error - next span must be live, free or end */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + case IMPORTED_RESOURCE_SPAN_FREE: + + if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_END))) + { + /* error - next span must be live or end */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + case IMPORTED_RESOURCE_SPAN_END: + + if ((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_END)) + { + /* error - next span cannot be live, free or end */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + + case IMPORTED_RESOURCE_SPAN_START: + + if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || + (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE))) + { + /* error - next span must be live or free */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + default: + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + break; + } + pSegment = pSegment->pNextSegment; + } + } + else if (pSegment->eResourceType == NON_IMPORTED_RESOURCE_TYPE) + { + PVR_ASSERT((pSegment->eResourceSpan == RESOURCE_SPAN_FREE) || (pSegment->eResourceSpan == RESOURCE_SPAN_LIVE)); + + while (pSegment->pNextSegment) + { + eNextSpan = pSegment->pNextSegment->eResourceSpan; + + switch (pSegment->eResourceSpan) + { + case RESOURCE_SPAN_LIVE: + + if (!((eNextSpan == RESOURCE_SPAN_FREE) || + (eNextSpan == RESOURCE_SPAN_LIVE))) + { + /* error - next span must be free or live */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + case RESOURCE_SPAN_FREE: + + if (!((eNextSpan == RESOURCE_SPAN_FREE) || + (eNextSpan == RESOURCE_SPAN_LIVE))) + { + /* error - next span must be free or live */ + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + } + break; + + default: + PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x%x) and %d (base=0x%x) are incompatible (arena: %s)", + pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); + + PVR_DBG_BREAK; + break; + } + pSegment = pSegment->pNextSegment; + } + + } + else + { + PVR_DPF ((PVR_DBG_ERROR,"ValidateArena ERROR: pSegment->eResourceType unrecognized")); + + PVR_DBG_BREAK; + } + + return 0; +} + +#endif + + +/*! +****************************************************************************** + @Function RA_Free + + @Description To free a resource segment. + + @Input pArena - the arena the segment was originally allocated from. + @Input base - the base of the resource span to free. + @Input bFreeBackingStore - Should backing store memory be freed. + + @Return None +******************************************************************************/ +IMG_VOID +RA_Free (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_BOOL bFreeBackingStore) +{ + BT *pBT; + + PVR_ASSERT (pArena != IMG_NULL); + + if (pArena == IMG_NULL) + { + PVR_DPF ((PVR_DBG_ERROR,"RA_Free: invalid parameter - pArena")); + return; + } + +#ifdef USE_BM_FREESPACE_CHECK + CheckBMFreespace(); +#endif + + PVR_DPF ((PVR_DBG_MESSAGE, + "RA_Free: name='%s', base=0x%x", pArena->name, base)); + + pBT = (BT *) HASH_Remove (pArena->pSegmentHash, base); + PVR_ASSERT (pBT != IMG_NULL); + + if (pBT) + { + PVR_ASSERT (pBT->base == base); + +#ifdef RA_STATS + pArena->sStatistics.uCumulativeFrees++; +#endif + +#ifdef USE_BM_FREESPACE_CHECK +{ + IMG_BYTE* p; + IMG_BYTE* endp; + + p = (IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(); + endp = (IMG_BYTE*)((IMG_UINT32)(p + pBT->uSize)); + while ((IMG_UINT32)p & 3) + { + *p++ = 0xAA; + } + while (p < (IMG_BYTE*)((IMG_UINT32)endp & 0xfffffffc)) + { + *(IMG_UINT32*)p = 0xAAAAAAAA; + p += sizeof(IMG_UINT32); + } + while (p < endp) + { + *p++ = 0xAA; + } + PVR_DPF((PVR_DBG_MESSAGE,"BM_FREESPACE_CHECK: RA_Free Cleared %08X to %08X (size=0x%x)",(IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(),endp-1,pBT->uSize)); +} +#endif + _FreeBT (pArena, pBT, bFreeBackingStore); + } +} + + +/*! +****************************************************************************** + @Function RA_GetNextLiveSegment + + @Description Returns details of the next live resource segments + + @Input pArena - the arena the segment was originally allocated from. + @InOut psSegDetails - rtn details of segments + + @Return IMG_TRUE if operation succeeded +******************************************************************************/ +IMG_BOOL RA_GetNextLiveSegment(IMG_HANDLE hArena, RA_SEGMENT_DETAILS *psSegDetails) +{ + BT *pBT; + + if (psSegDetails->hSegment) + { + pBT = (BT *)psSegDetails->hSegment; + } + else + { + RA_ARENA *pArena = (RA_ARENA *)hArena; + + pBT = pArena->pHeadSegment; + } + /* walk the arena segments and write live one to the buffer */ + while (pBT != IMG_NULL) + { + if (pBT->type == btt_live) + { + psSegDetails->uiSize = pBT->uSize; + psSegDetails->sCpuPhyAddr.uiAddr = pBT->base; + psSegDetails->hSegment = (IMG_HANDLE)pBT->pNextSegment; + + return IMG_TRUE; + } + + pBT = pBT->pNextSegment; + } + + psSegDetails->uiSize = 0; + psSegDetails->sCpuPhyAddr.uiAddr = 0; + psSegDetails->hSegment = (IMG_HANDLE)IMG_UNDEF; + + return IMG_FALSE; +} + + +#ifdef USE_BM_FREESPACE_CHECK +RA_ARENA* pJFSavedArena = IMG_NULL; + +IMG_VOID CheckBMFreespace(IMG_VOID) +{ + BT *pBT; + IMG_BYTE* p; + IMG_BYTE* endp; + + if (pJFSavedArena != IMG_NULL) + { + for (pBT=pJFSavedArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) + { + if (pBT->type == btt_free) + { + p = (IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(); + endp = (IMG_BYTE*)((IMG_UINT32)(p + pBT->uSize) & 0xfffffffc); + + while ((IMG_UINT32)p & 3) + { + if (*p++ != 0xAA) + { + fprintf(stderr,"BM_FREESPACE_CHECK: Blank space at %08X has changed to 0x%x\n",p,*(IMG_UINT32*)p); + for (;;); + break; + } + } + while (p < endp) + { + if (*(IMG_UINT32*)p != 0xAAAAAAAA) + { + fprintf(stderr,"BM_FREESPACE_CHECK: Blank space at %08X has changed to 0x%x\n",p,*(IMG_UINT32*)p); + for (;;); + break; + } + p += 4; + } + } + } + } +} +#endif + + +#if (defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS)) || defined (RA_STATS) +static IMG_CHAR * +_BTType (IMG_INT eType) +{ + switch (eType) + { + case btt_span: return "span"; + case btt_free: return "free"; + case btt_live: return "live"; + } + return "junk"; +} +#endif /*defined(CONFIG_PROC_FS) && defined(DEBUG)*/ + +#if defined(ENABLE_RA_DUMP) +/*! +****************************************************************************** + @Function RA_Dump + + @Description To dump a readable description of an arena. Diagnostic only. + + @Input pArena - the arena to dump. + + @Return None +******************************************************************************/ +IMG_VOID +RA_Dump (RA_ARENA *pArena) +{ + BT *pBT; + PVR_ASSERT (pArena != IMG_NULL); + PVR_DPF ((PVR_DBG_MESSAGE,"Arena '%s':", pArena->name)); + PVR_DPF ((PVR_DBG_MESSAGE," alloc=%08X free=%08X handle=%08X quantum=%d", + pArena->pImportAlloc, pArena->pImportFree, pArena->pImportHandle, + pArena->uQuantum)); + PVR_DPF ((PVR_DBG_MESSAGE," segment Chain:")); + if (pArena->pHeadSegment != IMG_NULL && + pArena->pHeadSegment->pPrevSegment != IMG_NULL) + PVR_DPF ((PVR_DBG_MESSAGE," error: head boundary tag has invalid pPrevSegment")); + if (pArena->pTailSegment != IMG_NULL && + pArena->pTailSegment->pNextSegment != IMG_NULL) + PVR_DPF ((PVR_DBG_MESSAGE," error: tail boundary tag has invalid pNextSegment")); + + for (pBT=pArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) + { + PVR_DPF ((PVR_DBG_MESSAGE,"\tbase=0x%x size=0x%x type=%s", + (IMG_UINT32) pBT->base, pBT->uSize, _BTType (pBT->type))); + } + +#ifdef HASH_TRACE + HASH_Dump (pArena->pSegmentHash); +#endif +} +#endif /* #if defined(ENABLE_RA_DUMP) */ + +static PVRSRV_ERROR RA_DumpHeapInfo(RA_ARENA *pArena, IMG_UINT32 ui32DebugLevel) +{ + BT *pBT; + + { + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + IMG_CHAR dirname_buffer[256]; + IMG_CHAR dirname[256]; + const IMG_CHAR *proc_basename = dirname_buffer; + dirname_buffer[255] = dirname[255] = '\0'; + + OSGetProcCmdline(ui32PID, dirname_buffer, sizeof(dirname_buffer)); + PVR_LOG(("\nCommand Line of the current process with ID %u is %s", ui32PID, dirname_buffer)); + + proc_basename = OSGetPathBaseName(dirname_buffer, sizeof(dirname_buffer)); + PVR_LOG(("Base Name of the current process with ID %u is %s", ui32PID, proc_basename)); + + } + + PVR_LOG(("Arena '%s':", pArena->name)); + + PVR_LOG(( " allocCB=%p freeCB=%p handle=%p quantum=%d", + pArena->pImportAlloc, + pArena->pImportFree, + pArena->pImportHandle, + pArena->uQuantum)); + + PVR_LOG(( "span count\t\t%u", pArena->sStatistics.uSpanCount)); + + PVR_LOG(( "live segment count\t%u", pArena->sStatistics.uLiveSegmentCount)); + + PVR_LOG(( "free segment count\t%u", pArena->sStatistics.uFreeSegmentCount)); + + PVR_LOG(( "free resource count\t%u (0x%x)", + pArena->sStatistics.uFreeResourceCount, + (IMG_UINT)pArena->sStatistics.uFreeResourceCount)); + + PVR_LOG(( "total allocs\t\t%u", pArena->sStatistics.uCumulativeAllocs)); + + PVR_LOG(( "total failed allocs\t%u", pArena->sStatistics.uFailedAllocCount)); + + PVR_LOG(( "total frees\t\t%u", pArena->sStatistics.uCumulativeFrees)); + + PVR_LOG(( "import count\t\t%u", pArena->sStatistics.uImportCount)); + + PVR_LOG(( "export count\t\t%u", pArena->sStatistics.uExportCount)); + + PVR_LOG(( " segment Chain:")); + + if (pArena->pHeadSegment != IMG_NULL && + pArena->pHeadSegment->pPrevSegment != IMG_NULL) + { + PVR_LOG(( " error: head boundary tag has invalid pPrevSegment")); + } + + if (pArena->pTailSegment != IMG_NULL && + pArena->pTailSegment->pNextSegment != IMG_NULL) + { + PVR_LOG(( " error: tail boundary tag has invalid pNextSegment")); + } + + for (pBT=pArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) + { + PVR_LOG(( "%s base=0x%08x size=%08d(0x%08x) type=%s ref=%p", + ((pBT->type == btt_span) ? "\t\t" : "\t"), + (IMG_UINT32) pBT->base, + pBT->uSize, pBT->uSize, + _BTType(pBT->type), + pBT->psMapping)); + if(pBT->psMapping) + { + BM_MAPPING *psImportMapping = pBT->psMapping; + PVR_LOG(( "\t %p: mapping type %s, mapping count=%d, size=%08d(0x%08x), flags=0x%08x, align=0x%04x", + psImportMapping, + _BMMappingType(psImportMapping->eCpuMemoryOrigin), + psImportMapping->ui32MappingCount, + psImportMapping->uSize, psImportMapping->uSize, + psImportMapping->ui32Flags, + psImportMapping->ui32DevVAddrAlignment)); + } + } + + return PVRSRV_OK; +} + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_PROC_FS) + +#if defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) +#define _PROC_SET_ALLOC_TH_BUFFER_SZ 32 +static int RA_ProcSetAllocFailThreshold(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)data; + RA_ARENA *pArena; + IMG_CHAR data_buffer[_PROC_SET_ALLOC_TH_BUFFER_SZ]; + IMG_INT32 value = ~0; + IMG_UINT32 mask = ~0; + IMG_INT32 format_ret; + + if ((handlers == NULL) || (handlers->data == NULL) || (count > sizeof(data_buffer))) + { + return -EINVAL; + } + + pArena = (RA_ARENA *)handlers->data; + + count = MIN(count, sizeof(data_buffer)); + + if (pvr_copy_from_user(data_buffer, buffer, count)) + return -EINVAL; + + if (data_buffer[count - 1] != '\n') + return -EINVAL; + + data_buffer[(sizeof(data_buffer) - 1)] = '\0'; + if((sizeof(data_buffer) -1) <= count) + data_buffer[count] = '\0'; + + PVR_LOG(("Buffer from the user is %s\n", data_buffer)); + format_ret = sscanf(data_buffer, "%i:0x%x", &value, &mask); + PVR_LOG(("Value set is %i, type is %x, format %i\n", value, mask, format_ret)); + if(format_ret <= 0) + return -EINVAL; + +/* + Heap Allocation Buffer Threshold Setting - for testing purposes only + Causes allocation of a GFX buffer of type MASK for the respective heap to + fail. + Format is <threshold value number>:<buffer type mask hex value> + for example: 1000:0x01. + Value of -1 disables the allocation fail test + Value bigger than and eq. to 0 enables the allocation fail test for + the first buffer only. + Value smaller than -1 enables the buffer allocation failure for this + heap until the test disables it. +*/ + if(value < 0) + { + if(value == -1) + { + pArena->bFailAllocationPersist = pArena->bFailAllocationOnce = IMG_FALSE; + } + else if(value == -2) + { + RA_DumpHeapInfo(pArena, ~0); + } + else + { + pArena->bFailAllocationPersist = pArena->bFailAllocationOnce = IMG_TRUE; + pArena->uAllocFailThreshold = -value; + } + } + else + { + pArena->bFailAllocationPersist = 0; + pArena->bFailAllocationOnce = 1; + pArena->uAllocFailThreshold = value; + } + + if(format_ret > 1) + { + if((pArena->bFailAllocationOnce == IMG_TRUE) && (mask == 0)) + pArena->uAllocFailMask = ~0; + else + pArena->uAllocFailMask = mask; + } + PVR_LOG(("*************** User Fail Heap Allocation Settings for %s *******************************\n", + pArena->name)); + PVR_LOG(("Fail Heap Allocation is %s in %s mode\n", (pArena->bFailAllocationOnce ? "Enabled": "Disabled"), + (pArena->bFailAllocationPersist ? "Persistent": "One-Shot"))); + PVR_LOG(("Fail Heap Allocation Buffer Size Threshold is %u with a Mask of 0x%x\n", + pArena->uAllocFailThreshold, pArena->uAllocFailMask)); + PVR_LOG(("*******************************************************************************************\n")); + return (count); +} + +static void* RA_ProcSeqOff2AllocFailThreshold(struct seq_file * sfile, loff_t off) +{ + + if(off <= 1) + return (void*)(IMG_INT)(off+1); + + return 0; +} + +static void RA_ProcSeqShowAllocFailThreshold(struct seq_file *sfile,void* el) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)sfile->private; + RA_ARENA *pArena = (RA_ARENA *)handlers->data; + IMG_INT off = (IMG_INT)el; + + switch (off) + { + case 1: + seq_printf(sfile, "Heap Allocation Buffer Threshold Setting - for testing purposes only\n"); + seq_printf(sfile, "Format is <threshold value number>:<buffer type mask hex value> for example: 1000:0x01\n"); + seq_printf(sfile, "Value of -1 disables the allocation fail test\n"); + seq_printf(sfile, "Value of -2 dumps the heap entries to the kernel log\n"); + seq_printf(sfile, "Value => 0 enables the allocation fail test for the first buffer with the met threshold only\n"); + seq_printf(sfile, "Value < -2 enables the buffer allocation failure for this heap until the test disables it\n"); + break; + case 2: + seq_printf(sfile, "*********** Current Settings: ********************\n"); + seq_printf(sfile,"Fail Heap Allocation is %s in %s mode\n", (pArena->bFailAllocationOnce ? "Enabled": "Disabled"), + (pArena->bFailAllocationPersist ? "Persistent": "One-Shot")); + seq_printf(sfile, "Fail Heap Allocation Buffer Size Threshold is %u with a Mask of 0x%x\n", + pArena->uAllocFailThreshold, pArena->uAllocFailMask); + break; + } +} +#endif //defined(CONFIG_PVR_PROC_FS_HEAP_ALLOC_DEBUG) + +static void RA_ProcSeqShowInfo(struct seq_file *sfile, void* el) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)sfile->private; + RA_ARENA *pArena = (RA_ARENA *)handlers->data; + IMG_INT off = (IMG_INT)el; + + switch (off) + { + case 1: + seq_printf(sfile, "quantum\t\t\t%u\n", pArena->uQuantum); + break; + case 2: + seq_printf(sfile, "import_handle\t\t%08X\n", (IMG_UINT)pArena->pImportHandle); + break; +#ifdef RA_STATS + case 3: + seq_printf(sfile,"span count\t\t%u\n", pArena->sStatistics.uSpanCount); + break; + case 4: + seq_printf(sfile, "live segment count\t%u\n", pArena->sStatistics.uLiveSegmentCount); + break; + case 5: + seq_printf(sfile, "free segment count\t%u\n", pArena->sStatistics.uFreeSegmentCount); + break; + case 6: + seq_printf(sfile, "free resource count\t%u (0x%x)\n", + pArena->sStatistics.uFreeResourceCount, + (IMG_UINT)pArena->sStatistics.uFreeResourceCount); + break; + case 7: + seq_printf(sfile, "total allocs\t\t%u\n", pArena->sStatistics.uCumulativeAllocs); + break; + case 8: + seq_printf(sfile, "total frees\t\t%u\n", pArena->sStatistics.uCumulativeFrees); + break; + case 9: + seq_printf(sfile, "import count\t\t%u\n", pArena->sStatistics.uImportCount); + break; + case 10: + seq_printf(sfile, "export count\t\t%u\n", pArena->sStatistics.uExportCount); + break; +#endif + } + +} + +static void* RA_ProcSeqOff2ElementInfo(struct seq_file * sfile, loff_t off) +{ +#ifdef RA_STATS + if(off <= 9) +#else + if(off <= 1) +#endif + return (void*)(IMG_INT)(off+1); + return 0; +} + +static void RA_ProcSeqShowRegs(struct seq_file *sfile, void* el) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)sfile->private; + RA_ARENA *pArena = (RA_ARENA *)handlers->data; + BT *pBT = (BT*)el; + + if (el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf(sfile, "Arena \"%s\"\nBase Size Type Ref\n", pArena->name); + return; + } + + if (pBT) + { + seq_printf(sfile, "%08x %8x %4s %08x\n", + (IMG_UINT)pBT->base, (IMG_UINT)pBT->uSize, _BTType (pBT->type), + (IMG_UINT)pBT->psMapping); + } +} + +static void* RA_ProcSeqOff2ElementRegs(struct seq_file * sfile, loff_t off) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)sfile->private; + RA_ARENA *pArena = (RA_ARENA *)handlers->data; + BT *pBT = 0; + + if(off == 0) + return PVR_PROC_SEQ_START_TOKEN; + + for (pBT=pArena->pHeadSegment; --off && pBT; pBT=pBT->pNextSegment); + + return (void*)pBT; +} + +#endif /* defined(CONFIG_PROC_FS) && defined(DEBUG) */ + + +#ifdef RA_STATS +/*! +****************************************************************************** + @Function RA_GetStats + + @Description Gets the arena stats and places in client buffer + + @Input pArena - the arena to print statistics for. + @Input ppszStr - caller string to fill + @Input pui32StrLen - length of caller string + + @Return PVRSRV_ERROR +******************************************************************************/ +PVRSRV_ERROR RA_GetStats(RA_ARENA *pArena, + IMG_CHAR **ppszStr, + IMG_UINT32 *pui32StrLen) +{ + IMG_CHAR *pszStr = *ppszStr; + IMG_UINT32 ui32StrLen = *pui32StrLen; + IMG_INT32 i32Count; + BT *pBT; + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "\nArena '%s':\n", pArena->name); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, " allocCB=%p freeCB=%p handle=%p quantum=%d\n", + pArena->pImportAlloc, + pArena->pImportFree, + pArena->pImportHandle, + pArena->uQuantum); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "span count\t\t%u\n", pArena->sStatistics.uSpanCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "live segment count\t%u\n", pArena->sStatistics.uLiveSegmentCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "free segment count\t%u\n", pArena->sStatistics.uFreeSegmentCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "free resource count\t%u (0x%x)\n", + pArena->sStatistics.uFreeResourceCount, + (IMG_UINT)pArena->sStatistics.uFreeResourceCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "total allocs\t\t%u\n", pArena->sStatistics.uCumulativeAllocs); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "total frees\t\t%u\n", pArena->sStatistics.uCumulativeFrees); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "import count\t\t%u\n", pArena->sStatistics.uImportCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "export count\t\t%u\n", pArena->sStatistics.uExportCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, " segment Chain:\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + if (pArena->pHeadSegment != IMG_NULL && + pArena->pHeadSegment->pPrevSegment != IMG_NULL) + { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, " error: head boundary tag has invalid pPrevSegment\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + if (pArena->pTailSegment != IMG_NULL && + pArena->pTailSegment->pNextSegment != IMG_NULL) + { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, " error: tail boundary tag has invalid pNextSegment\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + for (pBT=pArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) + { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "\tbase=0x%x size=0x%x type=%s ref=%p\n", + (IMG_UINT32) pBT->base, + pBT->uSize, + _BTType(pBT->type), + pBT->psMapping); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + *ppszStr = pszStr; + *pui32StrLen = ui32StrLen; + + return PVRSRV_OK; +} + +PVRSRV_ERROR RA_GetStatsFreeMem(RA_ARENA *pArena, + IMG_CHAR **ppszStr, + IMG_UINT32 *pui32StrLen) +{ + IMG_CHAR *pszStr = *ppszStr; + IMG_UINT32 ui32StrLen = *pui32StrLen; + IMG_INT32 i32Count; + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "Bytes free: Arena %-30s: %u (0x%x)\n", pArena->name, + pArena->sStatistics.uFreeResourceCount, + pArena->sStatistics.uFreeResourceCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + *ppszStr = pszStr; + *pui32StrLen = ui32StrLen; + + return PVRSRV_OK; +} +#endif + +/****************************************************************************** + End of file (ra.c) +******************************************************************************/ + + + + diff --git a/pvr-source/services4/srvkm/common/refcount.c b/pvr-source/services4/srvkm/common/refcount.c new file mode 100644 index 0000000..fa64b23 --- /dev/null +++ b/pvr-source/services4/srvkm/common/refcount.c @@ -0,0 +1,588 @@ +/*************************************************************************/ /*! +@Title Services reference count debugging +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#if defined(PVRSRV_REFCOUNT_DEBUG) + +#include "services_headers.h" + +#ifndef __linux__ +#warning Reference count debugging is not thread-safe on this platform +#define PVRSRV_LOCK_CCB() +#define PVRSRV_UNLOCK_CCB() +#else /* __linux__ */ +#include <linux/spinlock.h> +static DEFINE_SPINLOCK(gsCCBLock); +#define PVRSRV_LOCK_CCB() \ + { \ + unsigned long flags; \ + spin_lock_irqsave(&gsCCBLock, flags); +#define PVRSRV_UNLOCK_CCB() \ + spin_unlock_irqrestore(&gsCCBLock, flags); \ + } +#endif /* __linux__ */ + +#define PVRSRV_REFCOUNT_CCB_MAX 512 +#define PVRSRV_REFCOUNT_CCB_MESG_MAX 80 + +#define PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO (1U << 0) +#define PVRSRV_REFCOUNT_CCB_DEBUG_MEMINFO (1U << 1) +#define PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF (1U << 2) +#define PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF2 (1U << 3) +#define PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC (1U << 4) + +#if defined(__linux__) +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP (1U << 16) +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2 (1U << 17) +#else +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP 0 +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2 0 +#endif + +#define PVRSRV_REFCOUNT_CCB_DEBUG_ALL ~0U + +/*static const IMG_UINT guiDebugMask = PVRSRV_REFCOUNT_CCB_DEBUG_ALL;*/ +static const IMG_UINT guiDebugMask = + PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO | + PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2; + +typedef struct +{ + const IMG_CHAR *pszFile; + IMG_INT iLine; + IMG_UINT32 ui32PID; + IMG_CHAR pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX]; +} +PVRSRV_REFCOUNT_CCB; + +static PVRSRV_REFCOUNT_CCB gsRefCountCCB[PVRSRV_REFCOUNT_CCB_MAX]; +static IMG_UINT giOffset; + +static const IMG_CHAR gszHeader[] = + /* 10 20 30 40 50 60 70 + * 345678901234567890123456789012345678901234567890123456789012345678901 + */ + "TYPE SYNCINFO MEMINFO MEMHANDLE OTHER REF REF' SIZE PID"; + /* NCINFO deadbeef deadbeef deadbeef deadbeef 1234 1234 deadbeef */ + +#define PVRSRV_REFCOUNT_CCB_FMT_STRING "%8.8s %8p %8p %8p %8p %.4d %.4d %.8x" + +IMG_INTERNAL +void PVRSRVDumpRefCountCCB(void) +{ + int i; + + PVRSRV_LOCK_CCB(); + + PVR_LOG(("%s", gszHeader)); + + for(i = 0; i < PVRSRV_REFCOUNT_CCB_MAX; i++) + { + PVRSRV_REFCOUNT_CCB *psRefCountCCBEntry = + &gsRefCountCCB[(giOffset + i) % PVRSRV_REFCOUNT_CCB_MAX]; + + /* Early on, we won't have MAX_REFCOUNT_CCB_SIZE messages */ + if(!psRefCountCCBEntry->pszFile) + break; + + PVR_LOG(("%s %d %s:%d", psRefCountCCBEntry->pcMesg, + psRefCountCCBEntry->ui32PID, + psRefCountCCBEntry->pszFile, + psRefCountCCBEntry->iLine)); + } + + PVRSRV_UNLOCK_CCB(); +} + +IMG_INTERNAL +void PVRSRVKernelSyncInfoIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo, + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + IMG_UINT32 ui32RefValue = OSAtomicRead(psKernelSyncInfo->pvRefCount); + + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "SYNCINFO", + psKernelSyncInfo, + psKernelMemInfo, + NULL, + (psKernelMemInfo) ? psKernelMemInfo->sMemBlk.hOSMemHandle : NULL, + ui32RefValue, + ui32RefValue + 1, + (psKernelMemInfo) ? psKernelMemInfo->uAllocSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + PVRSRVAcquireSyncInfoKM(psKernelSyncInfo); +} + +IMG_INTERNAL +void PVRSRVKernelSyncInfoDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo, + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + IMG_UINT32 ui32RefValue = OSAtomicRead(psKernelSyncInfo->pvRefCount); + + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "SYNCINFO", + psKernelSyncInfo, + psKernelMemInfo, + (psKernelMemInfo) ? psKernelMemInfo->sMemBlk.hOSMemHandle : NULL, + NULL, + ui32RefValue, + ui32RefValue - 1, + (psKernelMemInfo) ? psKernelMemInfo->uAllocSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + PVRSRVReleaseSyncInfoKM(psKernelSyncInfo); +} + +IMG_INTERNAL +void PVRSRVKernelMemInfoIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MEMINFO)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MEMINFO", + psKernelMemInfo->psKernelSyncInfo, + psKernelMemInfo, + psKernelMemInfo->sMemBlk.hOSMemHandle, + NULL, + psKernelMemInfo->ui32RefCount, + psKernelMemInfo->ui32RefCount + 1, + psKernelMemInfo->uAllocSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psKernelMemInfo->ui32RefCount++; +} + +IMG_INTERNAL +void PVRSRVKernelMemInfoDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MEMINFO)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MEMINFO", + psKernelMemInfo->psKernelSyncInfo, + psKernelMemInfo, + psKernelMemInfo->sMemBlk.hOSMemHandle, + NULL, + psKernelMemInfo->ui32RefCount, + psKernelMemInfo->ui32RefCount - 1, + psKernelMemInfo->uAllocSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psKernelMemInfo->ui32RefCount--; +} + +IMG_INTERNAL +void PVRSRVBMBufIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_BUF", + NULL, + NULL, + BM_HandleToOSMemHandle(pBuf), + pBuf, + pBuf->ui32RefCount, + pBuf->ui32RefCount + 1, + (pBuf->pMapping) ? pBuf->pMapping->uSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + pBuf->ui32RefCount++; +} + +IMG_INTERNAL +void PVRSRVBMBufDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_BUF", + NULL, + NULL, + BM_HandleToOSMemHandle(pBuf), + pBuf, + pBuf->ui32RefCount, + pBuf->ui32RefCount - 1, + (pBuf->pMapping) ? pBuf->pMapping->uSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + pBuf->ui32RefCount--; +} + +IMG_INTERNAL +void PVRSRVBMBufIncExport2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF2)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_BUF2", + NULL, + NULL, + BM_HandleToOSMemHandle(pBuf), + pBuf, + pBuf->ui32ExportCount, + pBuf->ui32ExportCount + 1, + (pBuf->pMapping) ? pBuf->pMapping->uSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + pBuf->ui32ExportCount++; +} + +IMG_INTERNAL +void PVRSRVBMBufDecExport2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF2)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_BUF2", + NULL, + NULL, + BM_HandleToOSMemHandle(pBuf), + pBuf, + pBuf->ui32ExportCount, + pBuf->ui32ExportCount - 1, + (pBuf->pMapping) ? pBuf->pMapping->uSize : 0); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + pBuf->ui32ExportCount--; +} + +IMG_INTERNAL +void PVRSRVBMXProcIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_XPROC", + NULL, + NULL, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle, + (IMG_VOID *) ui32Index, + gXProcWorkaroundShareData[ui32Index].ui32RefCount, + gXProcWorkaroundShareData[ui32Index].ui32RefCount + 1, + gXProcWorkaroundShareData[ui32Index].ui32Size); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + gXProcWorkaroundShareData[ui32Index].ui32RefCount++; +} + +IMG_INTERNAL +void PVRSRVBMXProcDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_XPROC", + NULL, + NULL, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle, + (IMG_VOID *) ui32Index, + gXProcWorkaroundShareData[ui32Index].ui32RefCount, + gXProcWorkaroundShareData[ui32Index].ui32RefCount - 1, + gXProcWorkaroundShareData[ui32Index].ui32Size); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + gXProcWorkaroundShareData[ui32Index].ui32RefCount--; +} + +#if defined(__linux__) + +/* mmap refcounting is Linux specific */ + +IMG_INTERNAL +void PVRSRVOffsetStructIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PKV_OFFSET_STRUCT psOffsetStruct) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MMAP)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MMAP", + NULL, + NULL, + psOffsetStruct->psLinuxMemArea, + psOffsetStruct, + psOffsetStruct->ui32RefCount, + psOffsetStruct->ui32RefCount + 1, + psOffsetStruct->ui32RealByteSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psOffsetStruct->ui32RefCount++; +} + +IMG_INTERNAL +void PVRSRVOffsetStructDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + PKV_OFFSET_STRUCT psOffsetStruct) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MMAP)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MMAP", + NULL, + NULL, + psOffsetStruct->psLinuxMemArea, + psOffsetStruct, + psOffsetStruct->ui32RefCount, + psOffsetStruct->ui32RefCount - 1, + psOffsetStruct->ui32RealByteSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psOffsetStruct->ui32RefCount--; +} + +IMG_INTERNAL +void PVRSRVOffsetStructIncMapped2(const IMG_CHAR *pszFile, IMG_INT iLine, + PKV_OFFSET_STRUCT psOffsetStruct) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MMAP2", + NULL, + NULL, + psOffsetStruct->psLinuxMemArea, + psOffsetStruct, + psOffsetStruct->ui32Mapped, + psOffsetStruct->ui32Mapped + 1, + psOffsetStruct->ui32RealByteSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psOffsetStruct->ui32Mapped++; +} + +IMG_INTERNAL +void PVRSRVOffsetStructDecMapped2(const IMG_CHAR *pszFile, IMG_INT iLine, + PKV_OFFSET_STRUCT psOffsetStruct) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "MMAP2", + NULL, + NULL, + psOffsetStruct->psLinuxMemArea, + psOffsetStruct, + psOffsetStruct->ui32Mapped, + psOffsetStruct->ui32Mapped - 1, + psOffsetStruct->ui32RealByteSize); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + psOffsetStruct->ui32Mapped--; +} + +#endif /* defined(__linux__) */ + +#endif /* defined(PVRSRV_REFCOUNT_DEBUG) */ diff --git a/pvr-source/services4/srvkm/common/resman.c b/pvr-source/services4/srvkm/common/resman.c new file mode 100644 index 0000000..aef102f --- /dev/null +++ b/pvr-source/services4/srvkm/common/resman.c @@ -0,0 +1,985 @@ +/*************************************************************************/ /*! +@Title Resource Manager +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Provide resource management +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#include "services_headers.h" +#include "resman.h" + +#ifdef __linux__ +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/sched.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) +#include <linux/hardirq.h> +#else +#include <asm/hardirq.h> +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) +#include <linux/mutex.h> +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) +#include <linux/semaphore.h> +#else +#include <asm/semaphore.h> +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) +static DEFINE_MUTEX(lock); +#define DOWN(m) mutex_lock(m) +#define UP(m) mutex_unlock(m) +#else +static DECLARE_MUTEX(lock); +#define DOWN(m) down(m) +#define UP(m) up(m) +#endif + +#define ACQUIRE_SYNC_OBJ do { \ + if (in_interrupt()) { \ + printk("ISR cannot take RESMAN mutex\n"); \ + BUG(); \ + } \ + else DOWN(&lock); \ +} while (0) +#define RELEASE_SYNC_OBJ UP(&lock) + +#else + +#define ACQUIRE_SYNC_OBJ +#define RELEASE_SYNC_OBJ + +#endif + +#define RESMAN_SIGNATURE 0x12345678 + +/****************************************************************************** + * resman structures + *****************************************************************************/ + +/* resman item structure */ +typedef struct _RESMAN_ITEM_ +{ +#ifdef DEBUG + IMG_UINT32 ui32Signature; +#endif + struct _RESMAN_ITEM_ **ppsThis; /*!< list navigation */ + struct _RESMAN_ITEM_ *psNext; /*!< list navigation */ + + IMG_UINT32 ui32Flags; /*!< flags */ + IMG_UINT32 ui32ResType;/*!< res type */ + + IMG_PVOID pvParam; /*!< param1 for callback */ + IMG_UINT32 ui32Param; /*!< param2 for callback */ + + RESMAN_FREE_FN pfnFreeResource;/*!< resman item free callback */ +} RESMAN_ITEM; + + +/* resman context structure */ +typedef struct _RESMAN_CONTEXT_ +{ +#ifdef DEBUG + IMG_UINT32 ui32Signature; +#endif + struct _RESMAN_CONTEXT_ **ppsThis;/*!< list navigation */ + struct _RESMAN_CONTEXT_ *psNext;/*!< list navigation */ + + PVRSRV_PER_PROCESS_DATA *psPerProc; /* owner of resources */ + + RESMAN_ITEM *psResItemList;/*!< res item list for context */ + +} RESMAN_CONTEXT; + + +/* resman list structure */ +typedef struct +{ + RESMAN_CONTEXT *psContextList; /*!< resman context list */ + +} RESMAN_LIST, *PRESMAN_LIST; /* PRQA S 3205 */ + + +PRESMAN_LIST gpsResList = IMG_NULL; + +#include "lists.h" /* PRQA S 5087 */ /* include lists.h required here */ + +static IMPLEMENT_LIST_ANY_VA(RESMAN_ITEM) +static IMPLEMENT_LIST_ANY_VA_2(RESMAN_ITEM, IMG_BOOL, IMG_FALSE) +static IMPLEMENT_LIST_INSERT(RESMAN_ITEM) +static IMPLEMENT_LIST_REMOVE(RESMAN_ITEM) +static IMPLEMENT_LIST_REVERSE(RESMAN_ITEM) + +static IMPLEMENT_LIST_REMOVE(RESMAN_CONTEXT) +static IMPLEMENT_LIST_INSERT(RESMAN_CONTEXT) + + +#define PRINT_RESLIST(x, y, z) + +/******************************************************** Forword references */ + +static PVRSRV_ERROR FreeResourceByPtr(RESMAN_ITEM *psItem, IMG_BOOL bExecuteCallback, IMG_BOOL bForceCleanup); + +static PVRSRV_ERROR FreeResourceByCriteria(PRESMAN_CONTEXT psContext, + IMG_UINT32 ui32SearchCriteria, + IMG_UINT32 ui32ResType, + IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bExecuteCallback); + + +#ifdef DEBUG + static IMG_VOID ValidateResList(PRESMAN_LIST psResList); + #define VALIDATERESLIST() ValidateResList(gpsResList) +#else + #define VALIDATERESLIST() +#endif + + + + + + +/*! +****************************************************************************** + + @Function ResManInit + + @Description initialises the resman + + @Return none + +******************************************************************************/ +PVRSRV_ERROR ResManInit(IMG_VOID) +{ + if (gpsResList == IMG_NULL) + { + /* If not already initialised */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*gpsResList), + (IMG_VOID **)&gpsResList, IMG_NULL, + "Resource Manager List") != PVRSRV_OK) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* Init list, the linked list has dummy entries at both ends */ + gpsResList->psContextList = IMG_NULL; + + /* Check resource list */ + VALIDATERESLIST(); + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function ResManDeInit + + @Description de-initialises the resman + + @Return none + +******************************************************************************/ +IMG_VOID ResManDeInit(IMG_VOID) +{ + if (gpsResList != IMG_NULL) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*gpsResList), gpsResList, IMG_NULL); + gpsResList = IMG_NULL; + } +} + + +/*! +****************************************************************************** + + @Function PVRSRVResManConnect + + @Description Opens a connection to the Resource Manager + + @input hPerProc - Per-process data (if applicable) + @output phResManContext - Resman context + + @Return error code or PVRSRV_OK + +******************************************************************************/ +PVRSRV_ERROR PVRSRVResManConnect(IMG_HANDLE hPerProc, + PRESMAN_CONTEXT *phResManContext) +{ + PVRSRV_ERROR eError; + PRESMAN_CONTEXT psResManContext; + + /*Acquire resource list sync object*/ + ACQUIRE_SYNC_OBJ; + + /*Check resource list*/ + VALIDATERESLIST(); + + /* Allocate memory for the new context. */ + eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psResManContext), + (IMG_VOID **)&psResManContext, IMG_NULL, + "Resource Manager Context"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVResManConnect: ERROR allocating new RESMAN context struct")); + + /* Check resource list */ + VALIDATERESLIST(); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + + return eError; + } + +#ifdef DEBUG + psResManContext->ui32Signature = RESMAN_SIGNATURE; +#endif /* DEBUG */ + psResManContext->psResItemList = IMG_NULL; + psResManContext->psPerProc = hPerProc; + + /* Insert new context struct after the dummy first entry */ + List_RESMAN_CONTEXT_Insert(&gpsResList->psContextList, psResManContext); + + /* Check resource list */ + VALIDATERESLIST(); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + + *phResManContext = psResManContext; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function PVRSRVResManDisconnect + + @Description Closes a Resource Manager connection and frees all resources + + @input hResManContext - Resman context + @input bKernelContext - IMG_TRUE for kernel contexts + + @Return IMG_VOID + +******************************************************************************/ +IMG_VOID PVRSRVResManDisconnect(PRESMAN_CONTEXT psResManContext, + IMG_BOOL bKernelContext) +{ + /* Acquire resource list sync object */ + ACQUIRE_SYNC_OBJ; + + /* Check resource list */ + VALIDATERESLIST(); + + /* Print and validate resource list */ + PRINT_RESLIST(gpsResList, psResManContext, IMG_TRUE); + + /* Free all auto-freed resources in order */ + + if (!bKernelContext) + { + /* OS specific User-mode Mappings: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_OS_USERMODE_MAPPING, 0, 0, IMG_TRUE); + + /* VGX types: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DMA_CLIENT_FIFO_DATA, 0, 0, IMG_TRUE); + + /* Event Object */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_EVENT_OBJECT, 0, 0, IMG_TRUE); + + /* syncobject state (Read/Write Complete values) */ + /* Must be FIFO, so we reverse the list, twice */ + List_RESMAN_ITEM_Reverse(&psResManContext->psResItemList); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_MODIFY_SYNC_OPS, 0, 0, IMG_TRUE); + List_RESMAN_ITEM_Reverse(&psResManContext->psResItemList); // (could survive without this - all following items would be cleared up "fifo" too) + + /* SGX types: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_HW_RENDER_CONTEXT, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_HW_TRANSFER_CONTEXT, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_HW_2D_CONTEXT, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_TRANSFER_CONTEXT, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_SHARED_PB_DESC_CREATE_LOCK, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_SHARED_PB_DESC, 0, 0, IMG_TRUE); + + /* COMMON types: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_SYNC_INFO, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICECLASSMEM_MAPPING, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICEMEM_WRAP, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICEMEM_MAPPING, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_KERNEL_DEVICEMEM_ALLOCATION, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICEMEM_ALLOCATION, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICEMEM_CONTEXT, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_SHARED_MEM_INFO, 0, 0, IMG_TRUE); +#if defined(SUPPORT_ION) + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DEVICEMEM_ION, 0, 0, IMG_TRUE); +#endif + /* DISPLAY CLASS types: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DISPLAYCLASS_SWAPCHAIN_REF, 0, 0, IMG_TRUE); + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_DISPLAYCLASS_DEVICE, 0, 0, IMG_TRUE); + + /* BUFFER CLASS types: */ + FreeResourceByCriteria(psResManContext, RESMAN_CRITERIA_RESTYPE, RESMAN_TYPE_BUFFERCLASS_DEVICE, 0, 0, IMG_TRUE); + } + + /* Ensure that there are no resources left */ + PVR_ASSERT(psResManContext->psResItemList == IMG_NULL); + + /* Remove the context struct from the list */ + List_RESMAN_CONTEXT_Remove(psResManContext); + + /* Free the context struct */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RESMAN_CONTEXT), psResManContext, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + + /* Check resource list */ + VALIDATERESLIST(); + + /* Print and validate resource list */ + PRINT_RESLIST(gpsResList, psResManContext, IMG_FALSE); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; +} + + +/*! +****************************************************************************** + @Function ResManRegisterRes + + @Description : Inform the resource manager that the given resource has + been alloacted and freeing of it will be the responsibility + of the resource manager + + @input psResManContext - resman context + @input ui32ResType - identify what kind of resource it is + @input pvParam - address of resource + @input ui32Param - size of resource + @input pfnFreeResource - pointer to function that frees this resource + + @Return On success a pointer to an opaque data structure that represents + the allocated resource, else NULL + +**************************************************************************/ +PRESMAN_ITEM ResManRegisterRes(PRESMAN_CONTEXT psResManContext, + IMG_UINT32 ui32ResType, + IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + RESMAN_FREE_FN pfnFreeResource) +{ + PRESMAN_ITEM psNewResItem; + + PVR_ASSERT(psResManContext != IMG_NULL); + PVR_ASSERT(ui32ResType != 0); + + if (psResManContext == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "ResManRegisterRes: invalid parameter - psResManContext")); + return (PRESMAN_ITEM) IMG_NULL; + } + + /* Acquire resource list sync object */ + ACQUIRE_SYNC_OBJ; + + /* Check resource list */ + VALIDATERESLIST(); + + PVR_DPF((PVR_DBG_MESSAGE, "ResManRegisterRes: register resource " + "Context 0x%x, ResType 0x%x, pvParam 0x%x, ui32Param 0x%x, " + "FreeFunc %08X", + (IMG_UINTPTR_T)psResManContext, + ui32ResType, + (IMG_UINTPTR_T)pvParam, + ui32Param, + (IMG_UINTPTR_T)pfnFreeResource)); + + /* Allocate memory for the new resource structure */ + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(RESMAN_ITEM), (IMG_VOID **)&psNewResItem, + IMG_NULL, + "Resource Manager Item") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ResManRegisterRes: " + "ERROR allocating new resource item")); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + + return((PRESMAN_ITEM)IMG_NULL); + } + + /* Fill in details about this resource */ +#ifdef DEBUG + psNewResItem->ui32Signature = RESMAN_SIGNATURE; +#endif /* DEBUG */ + psNewResItem->ui32ResType = ui32ResType; + psNewResItem->pvParam = pvParam; + psNewResItem->ui32Param = ui32Param; + psNewResItem->pfnFreeResource = pfnFreeResource; + psNewResItem->ui32Flags = 0; + + /* Insert new structure after dummy first entry */ + List_RESMAN_ITEM_Insert(&psResManContext->psResItemList, psNewResItem); + + /* Check resource list */ + VALIDATERESLIST(); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + + return(psNewResItem); +} + +/*! +****************************************************************************** + @Function ResManFreeResByPtr + + @Description frees a resource by matching on pointer type + + @inputs psResItem - pointer to resource item to free + bForceCleanup - ignored uKernel re-sync + + @Return PVRSRV_ERROR +**************************************************************************/ +PVRSRV_ERROR ResManFreeResByPtr(RESMAN_ITEM *psResItem, IMG_BOOL bForceCleanup) +{ + PVRSRV_ERROR eError; + + PVR_ASSERT(psResItem != IMG_NULL); + + if (psResItem == IMG_NULL) + { + PVR_DPF((PVR_DBG_MESSAGE, "ResManFreeResByPtr: NULL ptr - nothing to do")); + return PVRSRV_OK; + } + + PVR_DPF((PVR_DBG_MESSAGE, "ResManFreeResByPtr: freeing resource at %08X", + (IMG_UINTPTR_T)psResItem)); + + /*Acquire resource list sync object*/ + ACQUIRE_SYNC_OBJ; + + /*Check resource list*/ + VALIDATERESLIST(); + + /*Free resource*/ + eError = FreeResourceByPtr(psResItem, IMG_TRUE, bForceCleanup); + + /*Check resource list*/ + VALIDATERESLIST(); + + /*Release resource list sync object*/ + RELEASE_SYNC_OBJ; + + return(eError); +} + + +/*! +****************************************************************************** + @Function ResManFreeResByCriteria + + @Description frees a resource by matching on criteria + + @inputs hResManContext - handle for resman context + @inputs ui32SearchCriteria - indicates which parameters should be + used in search for resources to free + @inputs ui32ResType - identify what kind of resource to free + @inputs pvParam - address of resource to be free + @inputs ui32Param - size of resource to be free + + @Return PVRSRV_ERROR +**************************************************************************/ +PVRSRV_ERROR ResManFreeResByCriteria(PRESMAN_CONTEXT psResManContext, + IMG_UINT32 ui32SearchCriteria, + IMG_UINT32 ui32ResType, + IMG_PVOID pvParam, + IMG_UINT32 ui32Param) +{ + PVRSRV_ERROR eError; + + PVR_ASSERT(psResManContext != IMG_NULL); + + /* Acquire resource list sync object */ + ACQUIRE_SYNC_OBJ; + + /* Check resource list */ + VALIDATERESLIST(); + + PVR_DPF((PVR_DBG_MESSAGE, "ResManFreeResByCriteria: " + "Context 0x%x, Criteria 0x%x, Type 0x%x, Addr 0x%x, Param 0x%x", + (IMG_UINTPTR_T)psResManContext, ui32SearchCriteria, ui32ResType, + (IMG_UINTPTR_T)pvParam, ui32Param)); + + /* Free resources by criteria for this context */ + eError = FreeResourceByCriteria(psResManContext, ui32SearchCriteria, + ui32ResType, pvParam, ui32Param, + IMG_TRUE); + + /* Check resource list */ + VALIDATERESLIST(); + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + + return eError; +} + + +/*! +****************************************************************************** + @Function ResManDissociateRes + + @Description Moves a resource from one context to another. + + @inputs psResItem - pointer to resource item to dissociate + @inputs psNewResManContext - new resman context for the resource + + @Return IMG_VOID +**************************************************************************/ +PVRSRV_ERROR ResManDissociateRes(RESMAN_ITEM *psResItem, + PRESMAN_CONTEXT psNewResManContext) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + PVR_ASSERT(psResItem != IMG_NULL); + + if (psResItem == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "ResManDissociateRes: invalid parameter - psResItem")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_INVALID_PARAMS; + } + +#ifdef DEBUG /* QAC fix */ + PVR_ASSERT(psResItem->ui32Signature == RESMAN_SIGNATURE); +#endif + + if (psNewResManContext != IMG_NULL) + { + /* Remove this item from its old resource list */ + List_RESMAN_ITEM_Remove(psResItem); + + /* Re-insert into new list */ + List_RESMAN_ITEM_Insert(&psNewResManContext->psResItemList, psResItem); + + } + else + { + eError = FreeResourceByPtr(psResItem, IMG_FALSE, CLEANUP_WITH_POLL); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "ResManDissociateRes: failed to free resource by pointer")); + return eError; + } + } + + return eError; +} + +/*! +****************************************************************************** + @Function ResManFindResourceByPtr_AnyVaCb + + @Description + Compares the resman item with a given pointer. + + @inputs psCurItem - theThe item to check + @inputs va - Variable argument list with: + psItem - pointer to resource item to find + + @Return IMG_BOOL +**************************************************************************/ +static IMG_BOOL ResManFindResourceByPtr_AnyVaCb(RESMAN_ITEM *psCurItem, va_list va) +{ + RESMAN_ITEM *psItem; + + psItem = va_arg(va, RESMAN_ITEM*); + + return (IMG_BOOL)(psCurItem == psItem); +} + + +/*! +****************************************************************************** + @Function ResManFindResourceByPtr + + @Description + Attempts to find a resource in the list for this context + + @inputs hResManContext - handle for resman context + @inputs psItem - pointer to resource item to find + + @Return PVRSRV_ERROR +**************************************************************************/ +IMG_INTERNAL PVRSRV_ERROR ResManFindResourceByPtr(PRESMAN_CONTEXT psResManContext, + RESMAN_ITEM *psItem) +{ +/* RESMAN_ITEM *psCurItem;*/ + + PVRSRV_ERROR eResult; + + PVR_ASSERT(psResManContext != IMG_NULL); + PVR_ASSERT(psItem != IMG_NULL); + + if ((psItem == IMG_NULL) || (psResManContext == IMG_NULL)) + { + PVR_DPF((PVR_DBG_ERROR, "ResManFindResourceByPtr: invalid parameter")); + PVR_DBG_BREAK; + return PVRSRV_ERROR_INVALID_PARAMS; + } + +#ifdef DEBUG /* QAC fix */ + PVR_ASSERT(psItem->ui32Signature == RESMAN_SIGNATURE); +#endif + + /* Acquire resource list sync object */ + ACQUIRE_SYNC_OBJ; + + PVR_DPF((PVR_DBG_MESSAGE, + "FindResourceByPtr: psItem=%08X, psItem->psNext=%08X", + (IMG_UINTPTR_T)psItem, (IMG_UINTPTR_T)psItem->psNext)); + + PVR_DPF((PVR_DBG_MESSAGE, + "FindResourceByPtr: Resource Ctx 0x%x, Type 0x%x, Addr 0x%x, " + "Param 0x%x, FnCall %08X, Flags 0x%x", + (IMG_UINTPTR_T)psResManContext, + psItem->ui32ResType, + (IMG_UINTPTR_T)psItem->pvParam, + psItem->ui32Param, + (IMG_UINTPTR_T)psItem->pfnFreeResource, + psItem->ui32Flags)); + + /* Search resource items starting at after the first dummy item */ + if(List_RESMAN_ITEM_IMG_BOOL_Any_va(psResManContext->psResItemList, + &ResManFindResourceByPtr_AnyVaCb, + psItem)) + { + eResult = PVRSRV_OK; + } + else + { + eResult = PVRSRV_ERROR_NOT_OWNER; + } + + /* Release resource list sync object */ + RELEASE_SYNC_OBJ; + +/* return PVRSRV_ERROR_NOT_OWNER;*/ + return eResult; +} + +/*! +****************************************************************************** + @Function FreeResourceByPtr + + @Description + Frees a resource and move it from the list + NOTE : this function must be called with the resource + list sync object held + + @inputs psItem - pointer to resource item to free + bExecuteCallback - execute callback? + bForceCleanup - skips uKernel re-sync + + @Return PVRSRV_ERROR +**************************************************************************/ +static PVRSRV_ERROR FreeResourceByPtr(RESMAN_ITEM *psItem, + IMG_BOOL bExecuteCallback, + IMG_BOOL bForceCleanup) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + PVR_ASSERT(psItem != IMG_NULL); + + if (psItem == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "FreeResourceByPtr: invalid parameter")); + return PVRSRV_ERROR_INVALID_PARAMS; + } + +#ifdef DEBUG /* QAC fix */ + PVR_ASSERT(psItem->ui32Signature == RESMAN_SIGNATURE); +#endif + + PVR_DPF((PVR_DBG_MESSAGE, + "FreeResourceByPtr: psItem=%08X, psItem->psNext=%08X", + (IMG_UINTPTR_T)psItem, (IMG_UINTPTR_T)psItem->psNext)); + + PVR_DPF((PVR_DBG_MESSAGE, + "FreeResourceByPtr: Type 0x%x, Addr 0x%x, " + "Param 0x%x, FnCall %08X, Flags 0x%x", + psItem->ui32ResType, + (IMG_UINTPTR_T)psItem->pvParam, psItem->ui32Param, + (IMG_UINTPTR_T)psItem->pfnFreeResource, psItem->ui32Flags)); + + /* Release resource list sync object just in case the free routine calls the resource manager */ + RELEASE_SYNC_OBJ; + + /* Call the freeing routine */ + if (bExecuteCallback) + { + eError = psItem->pfnFreeResource(psItem->pvParam, psItem->ui32Param, bForceCleanup); + if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY)) + { + PVR_DPF((PVR_DBG_ERROR, "FreeResourceByPtr: ERROR calling FreeResource function")); + } + } + + /* Acquire resource list sync object */ + ACQUIRE_SYNC_OBJ; + + if (eError != PVRSRV_ERROR_RETRY) + { + /* Remove this item from the resource list */ + List_RESMAN_ITEM_Remove(psItem); + + /* Free memory for the resource item */ + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RESMAN_ITEM), psItem, IMG_NULL); + } + + return(eError); +} + +/*! +****************************************************************************** + @Function FreeResourceByCriteria_AnyVaCb + + @Description + Matches a resource manager item with a given criteria. + + @inputs psCuItem - the item to be matched + @inputs va - a variable argument list with:. + ui32SearchCriteria - indicates which parameters should be used + search for resources to free + ui32ResType - identify what kind of resource to free + pvParam - address of resource to be free + ui32Param - size of resource to be free + + + @Return psCurItem if matched, IMG_NULL otherwise. +**************************************************************************/ +static IMG_VOID* FreeResourceByCriteria_AnyVaCb(RESMAN_ITEM *psCurItem, va_list va) +{ + IMG_UINT32 ui32SearchCriteria; + IMG_UINT32 ui32ResType; + IMG_PVOID pvParam; + IMG_UINT32 ui32Param; + + ui32SearchCriteria = va_arg(va, IMG_UINT32); + ui32ResType = va_arg(va, IMG_UINT32); + pvParam = va_arg(va, IMG_PVOID); + ui32Param = va_arg(va, IMG_UINT32); + + /*check that for all conditions are either disabled or eval to true*/ + if( + /* Check resource type */ + (((ui32SearchCriteria & RESMAN_CRITERIA_RESTYPE) == 0UL) || + (psCurItem->ui32ResType == ui32ResType)) + && + /* Check address */ + (((ui32SearchCriteria & RESMAN_CRITERIA_PVOID_PARAM) == 0UL) || + (psCurItem->pvParam == pvParam)) + && + /* Check size */ + (((ui32SearchCriteria & RESMAN_CRITERIA_UI32_PARAM) == 0UL) || + (psCurItem->ui32Param == ui32Param)) + ) + { + return psCurItem; + } + else + { + return IMG_NULL; + } +} + +/*! +****************************************************************************** + @Function FreeResourceByCriteria + + @Description + Frees all resources that match the given criteria for the + context. + NOTE : this function must be called with the resource + list sync object held + + @inputs psResManContext - pointer to resman context + @inputs ui32SearchCriteria - indicates which parameters should be used + @inputs search for resources to free + @inputs ui32ResType - identify what kind of resource to free + @inputs pvParam - address of resource to be free + @inputs ui32Param - size of resource to be free + @inputs ui32AutoFreeLev - auto free level to free + @inputs bExecuteCallback - execute callback? + + @Return PVRSRV_ERROR +**************************************************************************/ +static PVRSRV_ERROR FreeResourceByCriteria(PRESMAN_CONTEXT psResManContext, + IMG_UINT32 ui32SearchCriteria, + IMG_UINT32 ui32ResType, + IMG_PVOID pvParam, + IMG_UINT32 ui32Param, + IMG_BOOL bExecuteCallback) +{ + PRESMAN_ITEM psCurItem; + PVRSRV_ERROR eError = PVRSRV_OK; + + /* Search resource items starting at after the first dummy item */ + /*while we get a match and not an error*/ + while((psCurItem = (PRESMAN_ITEM) + List_RESMAN_ITEM_Any_va(psResManContext->psResItemList, + &FreeResourceByCriteria_AnyVaCb, + ui32SearchCriteria, + ui32ResType, + pvParam, + ui32Param)) != IMG_NULL + && eError == PVRSRV_OK) + { + do + { + eError = FreeResourceByPtr(psCurItem, bExecuteCallback, CLEANUP_WITH_POLL); + if (eError == PVRSRV_ERROR_RETRY) + { + RELEASE_SYNC_OBJ; + OSReleaseBridgeLock(); + /* Give a chance for other threads to come in and SGX to do more work */ + OSSleepms(MAX_CLEANUP_TIME_WAIT_US/1000); + OSReacquireBridgeLock(); + ACQUIRE_SYNC_OBJ; + } + } while (eError == PVRSRV_ERROR_RETRY); + } + + return eError; +} + + +#ifdef DEBUG +/*! +****************************************************************************** + @Function ValidateResList + + @Description + Walks the resource list check the pointers + NOTE : this function must be called with the resource + list sync object held + + @Return none +**************************************************************************/ +static IMG_VOID ValidateResList(PRESMAN_LIST psResList) +{ + PRESMAN_ITEM psCurItem, *ppsThisItem; + PRESMAN_CONTEXT psCurContext, *ppsThisContext; + + /* check we're initialised */ + if (psResList == IMG_NULL) + { + PVR_DPF((PVR_DBG_MESSAGE, "ValidateResList: resman not initialised yet")); + return; + } + + psCurContext = psResList->psContextList; + ppsThisContext = &psResList->psContextList; + + /* Walk the context list */ + while(psCurContext != IMG_NULL) + { + /* Check current item */ + PVR_ASSERT(psCurContext->ui32Signature == RESMAN_SIGNATURE); + if (psCurContext->ppsThis != ppsThisContext) + { + PVR_DPF((PVR_DBG_WARNING, + "psCC=%08X psCC->ppsThis=%08X psCC->psNext=%08X ppsTC=%08X", + (IMG_UINTPTR_T)psCurContext, + (IMG_UINTPTR_T)psCurContext->ppsThis, + (IMG_UINTPTR_T)psCurContext->psNext, + (IMG_UINTPTR_T)ppsThisContext)); + PVR_ASSERT(psCurContext->ppsThis == ppsThisContext); + } + + /* Walk the list for this context */ + psCurItem = psCurContext->psResItemList; + ppsThisItem = &psCurContext->psResItemList; + while(psCurItem != IMG_NULL) + { + /* Check current item */ + PVR_ASSERT(psCurItem->ui32Signature == RESMAN_SIGNATURE); + if (psCurItem->ppsThis != ppsThisItem) + { + PVR_DPF((PVR_DBG_WARNING, + "psCurItem=%08X psCurItem->ppsThis=%08X psCurItem->psNext=%08X ppsThisItem=%08X", + (IMG_UINTPTR_T)psCurItem, + (IMG_UINTPTR_T)psCurItem->ppsThis, + (IMG_UINTPTR_T)psCurItem->psNext, + (IMG_UINTPTR_T)ppsThisItem)); + PVR_ASSERT(psCurItem->ppsThis == ppsThisItem); + } + + /* Move to next item */ + ppsThisItem = &psCurItem->psNext; + psCurItem = psCurItem->psNext; + } + + /* Move to next context */ + ppsThisContext = &psCurContext->psNext; + psCurContext = psCurContext->psNext; + } +} +#endif /* DEBUG */ + + +/****************************************************************************** + End of file (resman.c) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/common/ttrace.c b/pvr-source/services4/srvkm/common/ttrace.c new file mode 100644 index 0000000..574bf25 --- /dev/null +++ b/pvr-source/services4/srvkm/common/ttrace.c @@ -0,0 +1,597 @@ +/*************************************************************************/ /*! +@Title Timed Trace functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#if defined (TTRACE) + +#include "services_headers.h" +#include "ttrace.h" + +#if defined(PVRSRV_NEED_PVR_DPF) +#define CHECKSIZE(n,m) \ + if ((n & m) != n) \ + PVR_DPF((PVR_DBG_ERROR,"Size check failed for " #m)) +#else +#define CHECKSIZE(n,m) +#endif + +#define TIME_TRACE_HASH_TABLE_SIZE 32 + +HASH_TABLE *g_psBufferTable; +IMG_UINT32 g_ui32HostUID; +IMG_HANDLE g_psTimer; + +/* Trace buffer struct */ +typedef struct +{ + IMG_UINT32 ui32Woff; /* Offset to where next item will be written */ + IMG_UINT32 ui32Roff; /* Offset to where to start reading from */ + IMG_UINT32 ui32ByteCount; /* Number of bytes in buffer */ + IMG_UINT8 ui8Data[0]; +} sTimeTraceBuffer; + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceItemSize + + @Description + + Calculate the size of a trace item + + @Input psTraceItem : Trace item + + @Return size of trace item + +******************************************************************************/ +static IMG_UINT32 +PVRSRVTimeTraceItemSize(IMG_UINT32 *psTraceItem) +{ + IMG_UINT32 ui32Size = PVRSRV_TRACE_ITEM_SIZE; + + ui32Size += READ_HEADER(SIZE, psTraceItem[PVRSRV_TRACE_DATA_HEADER]); + + return ui32Size; +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceAllocItem + + @Description + + Allocate a trace item from the buffer of the current process + + @Output ppsTraceItem : Pointer to allocated trace item + + @Input ui32Size : Size of data packet to be allocated + + @Return none + +******************************************************************************/ +static IMG_VOID +PVRSRVTimeTraceAllocItem(IMG_UINT32 **pui32Item, IMG_UINT32 ui32Size) +{ + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + IMG_UINT32 ui32AllocOffset; + sTimeTraceBuffer *psBuffer = (sTimeTraceBuffer *) HASH_Retrieve(g_psBufferTable, (IMG_UINTPTR_T) ui32PID); + + /* The caller only asks for extra data space */ + ui32Size += PVRSRV_TRACE_ITEM_SIZE; + + /* Always round to 32-bit */ + ui32Size = ((ui32Size - 1) & (~0x3)) + 0x04; + + if (!psBuffer) + { + PVRSRV_ERROR eError; + + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVTimeTraceAllocItem: Creating buffer for PID %u", (IMG_UINT32) ui32PID)); + eError = PVRSRVTimeTraceBufferCreate(ui32PID); + if (eError != PVRSRV_OK) + { + *pui32Item = IMG_NULL; + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceAllocItem: Failed to create buffer")); + return; + } + + psBuffer = (sTimeTraceBuffer *) HASH_Retrieve(g_psBufferTable, (IMG_UINTPTR_T) ui32PID); + if (psBuffer == IMG_NULL) + { + *pui32Item = NULL; + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceAllocItem: Failed to retrieve buffer")); + return; + } + } + + /* Can't allocate more then buffer size */ + if (ui32Size >= TIME_TRACE_BUFFER_SIZE) + { + *pui32Item = NULL; + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceAllocItem: Error trace item too large (%d)", ui32Size)); + return; + } + + /* FIXME: Enter critical section? */ + + /* Always ensure we have enough space to write a padding message */ + if ((psBuffer->ui32Woff + ui32Size + PVRSRV_TRACE_ITEM_SIZE) > TIME_TRACE_BUFFER_SIZE) + { + IMG_UINT32 *ui32WriteEOB = (IMG_UINT32 *) &psBuffer->ui8Data[psBuffer->ui32Woff]; + IMG_UINT32 ui32Remain = TIME_TRACE_BUFFER_SIZE - psBuffer->ui32Woff; + + /* Not enough space at the end of the buffer, back to the start */ + *ui32WriteEOB++ = WRITE_HEADER(GROUP, PVRSRV_TRACE_GROUP_PADDING); + *ui32WriteEOB++ = 0; /* Don't need timestamp */ + *ui32WriteEOB++ = 0; /* Don't need UID */ + *ui32WriteEOB = WRITE_HEADER(SIZE, (ui32Remain - PVRSRV_TRACE_ITEM_SIZE)); + psBuffer->ui32ByteCount += ui32Remain; + psBuffer->ui32Woff = ui32AllocOffset = 0; + } + else + ui32AllocOffset = psBuffer->ui32Woff; + + psBuffer->ui32Woff = psBuffer->ui32Woff + ui32Size; + psBuffer->ui32ByteCount += ui32Size; + + /* This allocation will start overwritting past our read pointer, move the read pointer along */ + while (psBuffer->ui32ByteCount > TIME_TRACE_BUFFER_SIZE) + { + IMG_UINT32 *psReadItem = (IMG_UINT32 *) &psBuffer->ui8Data[psBuffer->ui32Roff]; + IMG_UINT32 ui32ReadSize; + + ui32ReadSize = PVRSRVTimeTraceItemSize(psReadItem); + psBuffer->ui32Roff = (psBuffer->ui32Roff + ui32ReadSize) & (TIME_TRACE_BUFFER_SIZE - 1); + psBuffer->ui32ByteCount -= ui32ReadSize; + } + + *pui32Item = (IMG_UINT32 *) &psBuffer->ui8Data[ui32AllocOffset]; + /* FIXME: Exit critical section? */ +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceBufferCreate + + @Description + + Create a trace buffer. + + Note: We assume that this will only be called once per process. + + @Input ui32PID : PID of the process that is creating the buffer + + @Return none + +******************************************************************************/ +PVRSRV_ERROR PVRSRVTimeTraceBufferCreate(IMG_UINT32 ui32PID) +{ + sTimeTraceBuffer *psBuffer; + PVRSRV_ERROR eError = PVRSRV_OK; + + eError = OSAllocMem(PVRSRV_PAGEABLE_SELECT, + sizeof(sTimeTraceBuffer) + TIME_TRACE_BUFFER_SIZE, + (IMG_VOID **)&psBuffer, IMG_NULL, + "Time Trace Buffer"); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceBufferCreate: Error allocating trace buffer")); + return eError; + } + + OSMemSet(psBuffer, 0, TIME_TRACE_BUFFER_SIZE); + + if (!HASH_Insert(g_psBufferTable, (IMG_UINTPTR_T) ui32PID, (IMG_UINTPTR_T) psBuffer)) + { + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(sTimeTraceBuffer) + TIME_TRACE_BUFFER_SIZE, + psBuffer, NULL); + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceBufferCreate: Error adding trace buffer to hash table")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + return eError; +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceBufferDestroy + + @Description + + Destroy a trace buffer. + + Note: We assume that this will only be called once per process. + + @Input ui32PID : PID of the process that is creating the buffer + + @Return none + +******************************************************************************/ +PVRSRV_ERROR PVRSRVTimeTraceBufferDestroy(IMG_UINT32 ui32PID) +{ + sTimeTraceBuffer *psBuffer; + +#if defined(DUMP_TTRACE_BUFFERS_ON_EXIT) + PVRSRVDumpTimeTraceBuffers(); +#endif + psBuffer = (sTimeTraceBuffer *) HASH_Retrieve(g_psBufferTable, (IMG_UINTPTR_T) ui32PID); + if (psBuffer) + { + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(sTimeTraceBuffer) + TIME_TRACE_BUFFER_SIZE, + psBuffer, NULL); + HASH_Remove(g_psBufferTable, (IMG_UINTPTR_T) ui32PID); + return PVRSRV_OK; + } + + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceBufferDestroy: Can't find trace buffer in hash table")); + return PVRSRV_ERROR_INVALID_PARAMS; +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceInit + + @Description + + Initialise the timed trace subsystem. + + @Return Error + +******************************************************************************/ +PVRSRV_ERROR PVRSRVTimeTraceInit(IMG_VOID) +{ + g_psBufferTable = HASH_Create(TIME_TRACE_HASH_TABLE_SIZE); + + /* Create hash table to store the per process buffers in */ + if (!g_psBufferTable) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceInit: Error creating hash table")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* Create the kernel buffer */ + PVRSRVTimeTraceBufferCreate(KERNEL_ID); + + g_psTimer = OSFuncHighResTimerCreate(); + + if (!g_psTimer) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVTimeTraceInit: Error creating timer")); + return PVRSRV_ERROR_INIT_FAILURE; + } + return PVRSRV_OK; +} + +static PVRSRV_ERROR _PVRSRVTimeTraceBufferDestroy(IMG_UINTPTR_T hKey, IMG_UINTPTR_T hData) +{ + PVR_UNREFERENCED_PARAMETER(hData); + PVR_DPF((PVR_DBG_MESSAGE, "_PVRSRVTimeTraceBufferDestroy: Destroying buffer for PID %u", (IMG_UINT32) hKey)); + + PVRSRVTimeTraceBufferDestroy(hKey); + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceDeinit + + @Description + + De-initialise the timed trace subsystem. + + @Return Error + +******************************************************************************/ +IMG_VOID PVRSRVTimeTraceDeinit(IMG_VOID) +{ + PVRSRVTimeTraceBufferDestroy(KERNEL_ID); + /* Free any buffers the where created at alloc item time */ + HASH_Iterate(g_psBufferTable, _PVRSRVTimeTraceBufferDestroy); + HASH_Delete(g_psBufferTable); + OSFuncHighResTimerDestroy(g_psTimer); +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceWriteHeader + + @Description + + Write the header for a trace item. + + @Input pui32TraceItem : Pointer to trace item + + @Input ui32Group : Trace item's group ID + + @Input ui32Class : Trace item's class ID + + @Input ui32Token : Trace item's ui32Token ID + + @Input ui32Size : Trace item's data payload size + + @Input ui32Type : Trace item's data type + + @Input ui32Count : Trace item's data count + + @Return Pointer to data payload space, or NULL if no data payload + +******************************************************************************/ +#ifdef INLINE_IS_PRAGMA +#pragma inline(PVRSRVTimeTraceWriteHeader) +#endif +static INLINE IMG_VOID *PVRSRVTimeTraceWriteHeader(IMG_UINT32 *pui32TraceItem, IMG_UINT32 ui32Group, + IMG_UINT32 ui32Class, IMG_UINT32 ui32Token, + IMG_UINT32 ui32Size, IMG_UINT32 ui32Type, + IMG_UINT32 ui32Count) +{ + /* Sanity check arg's */ + CHECKSIZE(ui32Group, PVRSRV_TRACE_GROUP_MASK); + CHECKSIZE(ui32Class, PVRSRV_TRACE_CLASS_MASK); + CHECKSIZE(ui32Token, PVRSRV_TRACE_TOKEN_MASK); + + CHECKSIZE(ui32Size, PVRSRV_TRACE_SIZE_MASK); + CHECKSIZE(ui32Type, PVRSRV_TRACE_TYPE_MASK); + CHECKSIZE(ui32Count, PVRSRV_TRACE_COUNT_MASK); + + /* Trace header */ + pui32TraceItem[PVRSRV_TRACE_HEADER] = WRITE_HEADER(GROUP, ui32Group); + pui32TraceItem[PVRSRV_TRACE_HEADER] |= WRITE_HEADER(CLASS, ui32Class); + pui32TraceItem[PVRSRV_TRACE_HEADER] |= WRITE_HEADER(TOKEN, ui32Token); + + /* Data header */ + pui32TraceItem[PVRSRV_TRACE_DATA_HEADER] = WRITE_HEADER(SIZE, ui32Size); + pui32TraceItem[PVRSRV_TRACE_DATA_HEADER] |= WRITE_HEADER(TYPE, ui32Type); + pui32TraceItem[PVRSRV_TRACE_DATA_HEADER] |= WRITE_HEADER(COUNT, ui32Count); + + pui32TraceItem[PVRSRV_TRACE_TIMESTAMP] = OSFuncHighResTimerGetus(g_psTimer); + pui32TraceItem[PVRSRV_TRACE_HOSTUID] = g_ui32HostUID++; + + return ui32Size?((IMG_VOID *) &pui32TraceItem[PVRSRV_TRACE_DATA_PAYLOAD]):NULL; +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceArray + + @Description + + Write trace item with an array of data + + @Input ui32Group : Trace item's group ID + + @Input ui32Class : Trace item's class ID + + @Input ui32Token : Trace item's ui32Token ID + + @Input ui32Size : Trace item's data payload size + + @Input ui32Type : Trace item's data type + + @Input ui32Count : Trace item's data count + + @Input pui8Data : Pointer to data array + + @Return Pointer to data payload space, or NULL if no data payload + +******************************************************************************/ +IMG_VOID PVRSRVTimeTraceArray(IMG_UINT32 ui32Group, IMG_UINT32 ui32Class, IMG_UINT32 ui32Token, + IMG_UINT32 ui32Type, IMG_UINT32 ui32Count, IMG_UINT8 *pui8Data) +{ + IMG_UINT32 *pui32TraceItem; + IMG_UINT32 ui32Size, ui32TypeSize; + IMG_UINT8 *ui8Ptr; + + /* Only the 1st 4 sizes are for ui types, others are "special" */ + switch (ui32Type) + { + case PVRSRV_TRACE_TYPE_UI8: ui32TypeSize = 1; + break; + case PVRSRV_TRACE_TYPE_UI16: ui32TypeSize = 2; + break; + case PVRSRV_TRACE_TYPE_UI32: ui32TypeSize = 4; + break; + case PVRSRV_TRACE_TYPE_UI64: ui32TypeSize = 8; + break; + default: + PVR_DPF((PVR_DBG_ERROR, "Unsupported size\n")); + return; + } + + ui32Size = ui32TypeSize * ui32Count; + + /* Allocate space from the buffer */ + PVRSRVTimeTraceAllocItem(&pui32TraceItem, ui32Size); + + if (!pui32TraceItem) + { + PVR_DPF((PVR_DBG_ERROR, "Can't find buffer\n")); + return; + } + + ui8Ptr = PVRSRVTimeTraceWriteHeader(pui32TraceItem, ui32Group, ui32Class, ui32Token, + ui32Size, ui32Type, ui32Count); + + if (ui8Ptr) + { + OSMemCopy(ui8Ptr, pui8Data, ui32Size); + } +} + +/*! +****************************************************************************** + + @Function PVRSRVTimeTraceSyncObject + + @Description + + Write trace item with a sync object + + @Input ui32Group : Trace item's group ID + + @Input ui32Token : Trace item's ui32Token ID + + @Input psSync : Sync object + + @Input ui8SyncOpp : Sync object operation + + @Return None + +******************************************************************************/ +IMG_VOID PVRSRVTimeTraceSyncObject(IMG_UINT32 ui32Group, IMG_UINT32 ui32Token, + PVRSRV_KERNEL_SYNC_INFO *psSync, IMG_UINT8 ui8SyncOp) +{ + IMG_UINT32 *pui32TraceItem; + IMG_UINT32 *ui32Ptr; + IMG_UINT32 ui32Size = PVRSRV_TRACE_TYPE_SYNC_SIZE; + + + PVRSRVTimeTraceAllocItem(&pui32TraceItem, ui32Size); + + if (!pui32TraceItem) + { + PVR_DPF((PVR_DBG_ERROR, "Can't find buffer\n")); + return; + } + + ui32Ptr = PVRSRVTimeTraceWriteHeader(pui32TraceItem, ui32Group, PVRSRV_TRACE_CLASS_SYNC, + ui32Token, ui32Size, PVRSRV_TRACE_TYPE_SYNC, 1); + + ui32Ptr[PVRSRV_TRACE_SYNC_UID] = psSync->ui32UID; + ui32Ptr[PVRSRV_TRACE_SYNC_WOP] = psSync->psSyncData->ui32WriteOpsPending; + ui32Ptr[PVRSRV_TRACE_SYNC_WOC] = psSync->psSyncData->ui32WriteOpsComplete; + ui32Ptr[PVRSRV_TRACE_SYNC_ROP] = psSync->psSyncData->ui32ReadOpsPending; + ui32Ptr[PVRSRV_TRACE_SYNC_ROC] = psSync->psSyncData->ui32ReadOpsComplete; + ui32Ptr[PVRSRV_TRACE_SYNC_RO2P] = psSync->psSyncData->ui32ReadOps2Pending; + ui32Ptr[PVRSRV_TRACE_SYNC_RO2C] = psSync->psSyncData->ui32ReadOps2Complete; + ui32Ptr[PVRSRV_TRACE_SYNC_WO_DEV_VADDR] = psSync->sWriteOpsCompleteDevVAddr.uiAddr; + ui32Ptr[PVRSRV_TRACE_SYNC_RO_DEV_VADDR] = psSync->sReadOpsCompleteDevVAddr.uiAddr; + ui32Ptr[PVRSRV_TRACE_SYNC_RO2_DEV_VADDR] = psSync->sReadOps2CompleteDevVAddr.uiAddr; + ui32Ptr[PVRSRV_TRACE_SYNC_OP] = ui8SyncOp; +} + +/*! +****************************************************************************** + + @Function PVRSRVDumpTimeTraceBuffer + + @Description + + Dump the contents of the trace buffer. + + @Input hKey : Trace item's group ID + + @Input hData : Trace item's ui32Token ID + + @Return Error + +******************************************************************************/ +static PVRSRV_ERROR PVRSRVDumpTimeTraceBuffer(IMG_UINTPTR_T hKey, IMG_UINTPTR_T hData) +{ + sTimeTraceBuffer *psBuffer = (sTimeTraceBuffer *) hData; + IMG_UINT32 ui32ByteCount = psBuffer->ui32ByteCount; + IMG_UINT32 ui32Walker = psBuffer->ui32Roff; + IMG_UINT32 ui32Read, ui32LineLen, ui32EOL, ui32MinLine; + + PVR_DPF((PVR_DBG_ERROR, "TTB for PID %u:\n", (IMG_UINT32) hKey)); + + while (ui32ByteCount) + { + IMG_UINT32 *pui32Buffer = (IMG_UINT32 *) &psBuffer->ui8Data[ui32Walker]; + + ui32LineLen = (ui32ByteCount/sizeof(IMG_UINT32)); + ui32EOL = (TIME_TRACE_BUFFER_SIZE - ui32Walker)/sizeof(IMG_UINT32); + ui32MinLine = (ui32LineLen < ui32EOL)?ui32LineLen:ui32EOL; + + if (ui32MinLine >= 4) + { + PVR_DPF((PVR_DBG_ERROR, "\t(TTB-%X) %08X %08X %08X %08X", ui32ByteCount, + pui32Buffer[0], pui32Buffer[1], pui32Buffer[2], pui32Buffer[3])); + ui32Read = 4 * sizeof(IMG_UINT32); + } + else if (ui32MinLine >= 3) + { + PVR_DPF((PVR_DBG_ERROR, "\t(TTB-%X) %08X %08X %08X", ui32ByteCount, + pui32Buffer[0], pui32Buffer[1], pui32Buffer[2])); + ui32Read = 3 * sizeof(IMG_UINT32); + } + else if (ui32MinLine >= 2) + { + PVR_DPF((PVR_DBG_ERROR, "\t(TTB-%X) %08X %08X", ui32ByteCount, + pui32Buffer[0], pui32Buffer[1])); + ui32Read = 2 * sizeof(IMG_UINT32); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "\t(TTB-%X) %08X", ui32ByteCount, + pui32Buffer[0])); + ui32Read = sizeof(IMG_UINT32); + } + + ui32Walker = (ui32Walker + ui32Read) & (TIME_TRACE_BUFFER_SIZE - 1); + ui32ByteCount -= ui32Read; + } + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function PVRSRVDumpTimeTraceBuffers + + @Description + + Dump the contents of all the trace buffers. + + @Return None + +******************************************************************************/ +IMG_VOID PVRSRVDumpTimeTraceBuffers(IMG_VOID) +{ + HASH_Iterate(g_psBufferTable, PVRSRVDumpTimeTraceBuffer); +} + +#endif /* TTRACE */ |