| /********************************************************************** |
| * |
| * Copyright (C) Imagination Technologies Ltd. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful but, except |
| * as otherwise stated in writing, without any warranty; without even the |
| * implied warranty of merchantability or fitness for a particular purpose. |
| * See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * The full GNU General Public License is included in this distribution in |
| * the file called "COPYING". |
| * |
| * Contact Information: |
| * Imagination Technologies Ltd. <gpl-support@imgtec.com> |
| * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK |
| * |
| ******************************************************************************/ |
| |
| #include "img_defs.h" |
| #include "services.h" |
| #include "pvr_bridge.h" |
| #include "perproc.h" |
| #include "mutex.h" |
| #include "syscommon.h" |
| #include "pvr_debug.h" |
| #include "proc.h" |
| #include "private_data.h" |
| #include "linkage.h" |
| #include "pvr_bridge_km.h" |
| #include "pvr_uaccess.h" |
| #include "refcount.h" |
| |
| #if defined(SUPPORT_DRI_DRM) |
| #include <drm/drmP.h> |
| #include "pvr_drm.h" |
| #if defined(PVR_SECURE_DRM_AUTH_EXPORT) |
| #include "env_perproc.h" |
| #endif |
| #endif |
| |
| #if defined(SUPPORT_VGX) |
| #include "vgx_bridge.h" |
| #endif |
| |
| #if defined(SUPPORT_SGX) |
| #include "sgx_bridge.h" |
| #endif |
| |
| #include "bridged_pvr_bridge.h" |
| |
| #if defined(SUPPORT_DRI_DRM) |
| #define PRIVATE_DATA(pFile) ((pFile)->driver_priv) |
| #else |
| #define PRIVATE_DATA(pFile) ((pFile)->private_data) |
| #endif |
| |
| #if defined(DEBUG_BRIDGE_KM) |
| |
| static struct proc_dir_entry *g_ProcBridgeStats =0; |
| static void* ProcSeqNextBridgeStats(struct seq_file *sfile,void* el,loff_t off); |
| static void ProcSeqShowBridgeStats(struct seq_file *sfile,void* el); |
| static void* ProcSeqOff2ElementBridgeStats(struct seq_file * sfile, loff_t off); |
| static void ProcSeqStartstopBridgeStats(struct seq_file *sfile,IMG_BOOL start); |
| |
| #endif |
| |
| extern PVRSRV_LINUX_MUTEX gPVRSRVLock; |
| |
| #if defined(SUPPORT_MEMINFO_IDS) |
| static IMG_UINT64 ui64Stamp; |
| #endif |
| |
| PVRSRV_ERROR |
| LinuxBridgeInit(IMG_VOID) |
| { |
| #if defined(DEBUG_BRIDGE_KM) |
| { |
| g_ProcBridgeStats = CreateProcReadEntrySeq( |
| "bridge_stats", |
| NULL, |
| ProcSeqNextBridgeStats, |
| ProcSeqShowBridgeStats, |
| ProcSeqOff2ElementBridgeStats, |
| ProcSeqStartstopBridgeStats |
| ); |
| if(!g_ProcBridgeStats) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| } |
| #endif |
| return CommonBridgeInit(); |
| } |
| |
| IMG_VOID |
| LinuxBridgeDeInit(IMG_VOID) |
| { |
| #if defined(DEBUG_BRIDGE_KM) |
| RemoveProcEntrySeq(g_ProcBridgeStats); |
| #endif |
| } |
| |
| #if defined(DEBUG_BRIDGE_KM) |
| |
| static void ProcSeqStartstopBridgeStats(struct seq_file *sfile,IMG_BOOL start) |
| { |
| if(start) |
| { |
| LinuxLockMutex(&gPVRSRVLock); |
| } |
| else |
| { |
| LinuxUnLockMutex(&gPVRSRVLock); |
| } |
| } |
| |
| |
| static void* ProcSeqOff2ElementBridgeStats(struct seq_file *sfile, loff_t off) |
| { |
| if(!off) |
| { |
| return PVR_PROC_SEQ_START_TOKEN; |
| } |
| |
| if(off > BRIDGE_DISPATCH_TABLE_ENTRY_COUNT) |
| { |
| return (void*)0; |
| } |
| |
| |
| return (void*)&g_BridgeDispatchTable[off-1]; |
| } |
| |
| static void* ProcSeqNextBridgeStats(struct seq_file *sfile,void* el,loff_t off) |
| { |
| return ProcSeqOff2ElementBridgeStats(sfile,off); |
| } |
| |
| |
| static void ProcSeqShowBridgeStats(struct seq_file *sfile,void* el) |
| { |
| PVRSRV_BRIDGE_DISPATCH_TABLE_ENTRY *psEntry = ( PVRSRV_BRIDGE_DISPATCH_TABLE_ENTRY*)el; |
| |
| if(el == PVR_PROC_SEQ_START_TOKEN) |
| { |
| seq_printf(sfile, |
| "Total ioctl call count = %u\n" |
| "Total number of bytes copied via copy_from_user = %u\n" |
| "Total number of bytes copied via copy_to_user = %u\n" |
| "Total number of bytes copied via copy_*_user = %u\n\n" |
| "%-45s | %-40s | %10s | %20s | %10s\n", |
| g_BridgeGlobalStats.ui32IOCTLCount, |
| g_BridgeGlobalStats.ui32TotalCopyFromUserBytes, |
| g_BridgeGlobalStats.ui32TotalCopyToUserBytes, |
| g_BridgeGlobalStats.ui32TotalCopyFromUserBytes+g_BridgeGlobalStats.ui32TotalCopyToUserBytes, |
| "Bridge Name", |
| "Wrapper Function", |
| "Call Count", |
| "copy_from_user Bytes", |
| "copy_to_user Bytes" |
| ); |
| return; |
| } |
| |
| seq_printf(sfile, |
| "%-45s %-40s %-10u %-20u %-10u\n", |
| psEntry->pszIOCName, |
| psEntry->pszFunctionName, |
| psEntry->ui32CallCount, |
| psEntry->ui32CopyFromUserTotalBytes, |
| psEntry->ui32CopyToUserTotalBytes); |
| } |
| |
| #endif |
| |
| |
| #if defined(SUPPORT_DRI_DRM) |
| int |
| PVRSRV_BridgeDispatchKM(struct drm_device unref__ *dev, void *arg, struct drm_file *pFile) |
| #else |
| long |
| PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsigned long arg) |
| #endif |
| { |
| IMG_UINT32 cmd; |
| #if !defined(SUPPORT_DRI_DRM) |
| PVRSRV_BRIDGE_PACKAGE *psBridgePackageUM = (PVRSRV_BRIDGE_PACKAGE *)arg; |
| PVRSRV_BRIDGE_PACKAGE sBridgePackageKM; |
| #endif |
| PVRSRV_BRIDGE_PACKAGE *psBridgePackageKM; |
| IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); |
| PVRSRV_PER_PROCESS_DATA *psPerProc; |
| IMG_INT err = -EFAULT; |
| |
| LinuxLockMutex(&gPVRSRVLock); |
| |
| #if defined(SUPPORT_DRI_DRM) |
| psBridgePackageKM = (PVRSRV_BRIDGE_PACKAGE *)arg; |
| PVR_ASSERT(psBridgePackageKM != IMG_NULL); |
| #else |
| psBridgePackageKM = &sBridgePackageKM; |
| |
| if(!OSAccessOK(PVR_VERIFY_WRITE, |
| psBridgePackageUM, |
| sizeof(PVRSRV_BRIDGE_PACKAGE))) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Received invalid pointer to function arguments", |
| __FUNCTION__)); |
| |
| goto unlock_and_return; |
| } |
| |
| |
| if(OSCopyFromUser(IMG_NULL, |
| psBridgePackageKM, |
| psBridgePackageUM, |
| sizeof(PVRSRV_BRIDGE_PACKAGE)) |
| != PVRSRV_OK) |
| { |
| goto unlock_and_return; |
| } |
| #endif |
| |
| cmd = psBridgePackageKM->ui32BridgeID; |
| |
| if(cmd != PVRSRV_BRIDGE_CONNECT_SERVICES) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = PVRSRVLookupHandle(KERNEL_HANDLE_BASE, |
| (IMG_PVOID *)&psPerProc, |
| psBridgePackageKM->hKernelServices, |
| PVRSRV_HANDLE_TYPE_PERPROC_DATA); |
| if(eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Invalid kernel services handle (%d)", |
| __FUNCTION__, eError)); |
| goto unlock_and_return; |
| } |
| |
| if(psPerProc->ui32PID != ui32PID) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Process %d tried to access data " |
| "belonging to process %d", __FUNCTION__, ui32PID, |
| psPerProc->ui32PID)); |
| goto unlock_and_return; |
| } |
| } |
| else |
| { |
| |
| psPerProc = PVRSRVPerProcessData(ui32PID); |
| if(psPerProc == IMG_NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "PVRSRV_BridgeDispatchKM: " |
| "Couldn't create per-process data area")); |
| goto unlock_and_return; |
| } |
| } |
| |
| psBridgePackageKM->ui32BridgeID = PVRSRV_GET_BRIDGE_ID(psBridgePackageKM->ui32BridgeID); |
| |
| switch(cmd) |
| { |
| case PVRSRV_BRIDGE_EXPORT_DEVICEMEM_2: |
| { |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); |
| |
| if(psPrivateData->hKernelMemInfo) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Can only export one MemInfo " |
| "per file descriptor", __FUNCTION__)); |
| err = -EINVAL; |
| goto unlock_and_return; |
| } |
| break; |
| } |
| |
| case PVRSRV_BRIDGE_MAP_DEV_MEMORY_2: |
| { |
| PVRSRV_BRIDGE_IN_MAP_DEV_MEMORY *psMapDevMemIN = |
| (PVRSRV_BRIDGE_IN_MAP_DEV_MEMORY *)psBridgePackageKM->pvParamIn; |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); |
| |
| if(!psPrivateData->hKernelMemInfo) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: File descriptor has no " |
| "associated MemInfo handle", __FUNCTION__)); |
| err = -EINVAL; |
| goto unlock_and_return; |
| } |
| |
| if (pvr_put_user(psPrivateData->hKernelMemInfo, &psMapDevMemIN->hKernelMemInfo) != 0) |
| { |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| break; |
| } |
| |
| default: |
| { |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); |
| |
| if(psPrivateData->hKernelMemInfo) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Import/Export handle tried " |
| "to use privileged service", __FUNCTION__)); |
| goto unlock_and_return; |
| } |
| break; |
| } |
| } |
| |
| #if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) |
| switch(cmd) |
| { |
| case PVRSRV_BRIDGE_MAP_DEV_MEMORY: |
| case PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY: |
| { |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData; |
| int authenticated = pFile->authenticated; |
| PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc; |
| |
| if (authenticated) |
| { |
| break; |
| } |
| |
| |
| psEnvPerProc = (PVRSRV_ENV_PER_PROCESS_DATA *)PVRSRVProcessPrivateData(psPerProc); |
| if (psEnvPerProc == IMG_NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Process private data not allocated", __FUNCTION__)); |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| |
| list_for_each_entry(psPrivateData, &psEnvPerProc->sDRMAuthListHead, sDRMAuthListItem) |
| { |
| struct drm_file *psDRMFile = psPrivateData->psDRMFile; |
| |
| if (pFile->master == psDRMFile->master) |
| { |
| authenticated |= psDRMFile->authenticated; |
| if (authenticated) |
| { |
| break; |
| } |
| } |
| } |
| |
| if (!authenticated) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Not authenticated for mapping device or device class memory", __FUNCTION__)); |
| err = -EPERM; |
| goto unlock_and_return; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| #endif |
| |
| err = BridgedDispatchKM(psPerProc, psBridgePackageKM); |
| if(err != PVRSRV_OK) |
| goto unlock_and_return; |
| |
| switch(cmd) |
| { |
| case PVRSRV_BRIDGE_EXPORT_DEVICEMEM_2: |
| { |
| PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *psExportDeviceMemOUT = |
| (PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *)psBridgePackageKM->pvParamOut; |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); |
| PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; |
| |
| |
| if(PVRSRVLookupHandle(KERNEL_HANDLE_BASE, |
| (IMG_PVOID *)&psKernelMemInfo, |
| psExportDeviceMemOUT->hMemInfo, |
| PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up export handle", __FUNCTION__)); |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| |
| |
| PVRSRVKernelMemInfoIncRef(psKernelMemInfo); |
| |
| psPrivateData->hKernelMemInfo = psExportDeviceMemOUT->hMemInfo; |
| #if defined(SUPPORT_MEMINFO_IDS) |
| psPrivateData->ui64Stamp = ++ui64Stamp; |
| |
| psKernelMemInfo->ui64Stamp = psPrivateData->ui64Stamp; |
| if (pvr_put_user(psPrivateData->ui64Stamp, &psExportDeviceMemOUT->ui64Stamp) != 0) |
| { |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| #endif |
| break; |
| } |
| |
| #if defined(SUPPORT_MEMINFO_IDS) |
| case PVRSRV_BRIDGE_MAP_DEV_MEMORY: |
| case PVRSRV_BRIDGE_MAP_DEV_MEMORY_2: |
| { |
| PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *psMapDeviceMemoryOUT = |
| (PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *)psBridgePackageKM->pvParamOut; |
| PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); |
| if (pvr_put_user(psPrivateData->ui64Stamp, &psMapDeviceMemoryOUT->sDstClientMemInfo.ui64Stamp) != 0) |
| { |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| break; |
| } |
| |
| case PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY: |
| { |
| PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *psDeviceClassMemoryOUT = |
| (PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *)psBridgePackageKM->pvParamOut; |
| if (pvr_put_user(++ui64Stamp, &psDeviceClassMemoryOUT->sClientMemInfo.ui64Stamp) != 0) |
| { |
| err = -EFAULT; |
| goto unlock_and_return; |
| } |
| break; |
| } |
| #endif |
| |
| default: |
| break; |
| } |
| |
| unlock_and_return: |
| LinuxUnLockMutex(&gPVRSRVLock); |
| return err; |
| } |