blob: 6d51200e69bb1cd7337a78c3d265254cad090071 [file] [log] [blame]
/**********************************************************************
*
* 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;
}