diff options
Diffstat (limited to 'sgx/services4/srvkm/env/linux')
28 files changed, 12383 insertions, 0 deletions
diff --git a/sgx/services4/srvkm/env/linux/Kbuild.mk b/sgx/services4/srvkm/env/linux/Kbuild.mk new file mode 100755 index 0000000..702eadf --- /dev/null +++ b/sgx/services4/srvkm/env/linux/Kbuild.mk @@ -0,0 +1,132 @@ +# +# 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 +# + +$(PVRSRV_MODNAME)-y += \ + services4/srvkm/env/linux/osfunc.o \ + services4/srvkm/env/linux/mutils.o \ + services4/srvkm/env/linux/mmap.o \ + services4/srvkm/env/linux/module.o \ + services4/srvkm/env/linux/pdump.o \ + services4/srvkm/env/linux/proc.o \ + services4/srvkm/env/linux/pvr_bridge_k.o \ + services4/srvkm/env/linux/pvr_debug.o \ + services4/srvkm/env/linux/mm.o \ + services4/srvkm/env/linux/mutex.o \ + services4/srvkm/env/linux/event.o \ + services4/srvkm/env/linux/osperproc.o \ + services4/srvkm/common/buffer_manager.o \ + services4/srvkm/common/devicemem.o \ + services4/srvkm/common/deviceclass.o \ + services4/srvkm/common/handle.o \ + services4/srvkm/common/hash.o \ + services4/srvkm/common/metrics.o \ + services4/srvkm/common/pvrsrv.o \ + services4/srvkm/common/queue.o \ + services4/srvkm/common/ra.o \ + services4/srvkm/common/resman.o \ + services4/srvkm/common/power.o \ + services4/srvkm/common/mem.o \ + services4/srvkm/common/pdump_common.o \ + services4/srvkm/bridged/bridged_support.o \ + services4/srvkm/bridged/bridged_pvr_bridge.o \ + services4/srvkm/common/perproc.o \ + services4/system/$(PVR_SYSTEM)/sysconfig.o \ + services4/system/$(PVR_SYSTEM)/sysutils.o \ + services4/srvkm/common/lists.o \ + services4/srvkm/common/mem_debug.o \ + services4/srvkm/common/osfunc_common.o + +ifneq ($(W),1) +CFLAGS_osfunc.o := -Werror +CFLAGS_mutils.o := -Werror +CFLAGS_mmap.o := -Werror +CFLAGS_module.o := -Werror +CFLAGS_pdump.o := -Werror +CFLAGS_proc.o := -Werror +CFLAGS_pvr_bridge_k.o := -Werror +CFLAGS_pvr_debug.o := -Werror +CFLAGS_mm.o := -Werror +CFLAGS_mutex.o := -Werror +CFLAGS_event.o := -Werror +CFLAGS_osperproc.o := -Werror +CFLAGS_buffer_manager.o := -Werror +CFLAGS_devicemem.o := -Werror +CFLAGS_deviceclass.o := -Werror +CFLAGS_handle.o := -Werror +CFLAGS_hash.o := -Werror +CFLAGS_metrics.o := -Werror +CFLAGS_pvrsrv.o := -Werror +CFLAGS_queue.o := -Werror +CFLAGS_ra.o := -Werror +CFLAGS_resman.o := -Werror +CFLAGS_power.o := -Werror +CFLAGS_mem.o := -Werror +CFLAGS_pdump_common.o := -Werror +CFLAGS_bridged_support.o := -Werror +CFLAGS_bridged_pvr_bridge.o := -Werror +CFLAGS_perproc.o := -Werror +CFLAGS_lists.o := -Werror +CFLAGS_mem_debug.o := -Werror +CFLAGS_osfunc_common.o := -Werror +endif + +# SUPPORT_SGX==1 only + +$(PVRSRV_MODNAME)-y += \ + services4/srvkm/bridged/sgx/bridged_sgx_bridge.o \ + services4/srvkm/devices/sgx/sgxinit.o \ + services4/srvkm/devices/sgx/sgxpower.o \ + services4/srvkm/devices/sgx/sgxreset.o \ + services4/srvkm/devices/sgx/sgxutils.o \ + services4/srvkm/devices/sgx/sgxkick.o \ + services4/srvkm/devices/sgx/sgxtransfer.o \ + services4/srvkm/devices/sgx/mmu.o \ + services4/srvkm/devices/sgx/pb.o + +ifneq ($(W),1) +CFLAGS_bridged_sgx_bridge.o := -Werror +CFLAGS_sgxinit.o := -Werror +CFLAGS_sgxpower.o := -Werror +CFLAGS_sgxreset.o := -Werror +CFLAGS_sgxutils.o := -Werror +CFLAGS_sgxkick.o := -Werror +CFLAGS_sgxtransfer.o := -Werror +CFLAGS_mmu.o := -Werror +CFLAGS_pb.o := -Werror +endif + +ifeq ($(SUPPORT_DRI_DRM),1) + +$(PVRSRV_MODNAME)-y += \ + services4/srvkm/env/linux/pvr_drm.o + +ccflags-y += \ + -I$(KERNELDIR)/include/drm \ + -I$(TOP)/services4/include/env/linux \ + +ifeq ($(PVR_DRI_DRM_NOT_PCI),1) +ccflags-y += -I$(TOP)/services4/3rdparty/linux_drm +endif + +endif # SUPPORT_DRI_DRM diff --git a/sgx/services4/srvkm/env/linux/Linux.mk b/sgx/services4/srvkm/env/linux/Linux.mk new file mode 100755 index 0000000..0ea43aa --- /dev/null +++ b/sgx/services4/srvkm/env/linux/Linux.mk @@ -0,0 +1,30 @@ +# +# 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 +# +# + +modules := srvkm + +srvkm_type := kernel_module +srvkm_target := $(PVRSRV_MODNAME).ko +srvkm_makefile := $(THIS_DIR)/Kbuild.mk diff --git a/sgx/services4/srvkm/env/linux/env_data.h b/sgx/services4/srvkm/env/linux/env_data.h new file mode 100644 index 0000000..7716529 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/env_data.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef _ENV_DATA_ +#define _ENV_DATA_ + +#include <linux/interrupt.h> +#include <linux/pci.h> + +#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) || defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) +#include <linux/workqueue.h> +#endif + +#define PVRSRV_MAX_BRIDGE_IN_SIZE 0x1000 +#define PVRSRV_MAX_BRIDGE_OUT_SIZE 0x1000 + +typedef struct _PVR_PCI_DEV_TAG +{ + struct pci_dev *psPCIDev; + HOST_PCI_INIT_FLAGS ePCIFlags; + IMG_BOOL abPCIResourceInUse[DEVICE_COUNT_RESOURCE]; +} PVR_PCI_DEV; + +typedef struct _ENV_DATA_TAG +{ + IMG_VOID *pvBridgeData; + struct pm_dev *psPowerDevice; + IMG_BOOL bLISRInstalled; + IMG_BOOL bMISRInstalled; + IMG_UINT32 ui32IRQ; + IMG_VOID *pvISRCookie; +#if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) + struct workqueue_struct *psWorkQueue; +#endif +#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) || defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) + struct work_struct sMISRWork; + IMG_VOID *pvMISRData; +#else + struct tasklet_struct sMISRTasklet; +#endif +} ENV_DATA; + +#endif diff --git a/sgx/services4/srvkm/env/linux/env_perproc.h b/sgx/services4/srvkm/env/linux/env_perproc.h new file mode 100644 index 0000000..dabf1e3 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/env_perproc.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __ENV_PERPROC_H__ +#define __ENV_PERPROC_H__ + +#include <linux/list.h> +#include <linux/proc_fs.h> + +#include "services.h" +#include "handle.h" + +typedef struct _PVRSRV_ENV_PER_PROCESS_DATA_ +{ + IMG_HANDLE hBlockAlloc; + struct proc_dir_entry *psProcDir; +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + struct list_head sDRMAuthListHead; +#endif +} PVRSRV_ENV_PER_PROCESS_DATA; + +IMG_VOID RemovePerProcessProcDir(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc); + +PVRSRV_ERROR LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc); + +IMG_VOID LinuxMMapPerProcessDisconnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc); + +PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase); + +IMG_HANDLE LinuxTerminatingProcessPrivateData(IMG_VOID); + +#endif + diff --git a/sgx/services4/srvkm/env/linux/event.c b/sgx/services4/srvkm/env/linux/event.c new file mode 100644 index 0000000..7e160c3 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/event.c @@ -0,0 +1,293 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <asm/io.h> +#include <asm/page.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) +#include <asm/system.h> +#endif +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <asm/hardirq.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/capability.h> +#include <linux/sched.h> +#include <asm/uaccess.h> + +#include "img_types.h" +#include "services_headers.h" +#include "mm.h" +#include "pvrmmap.h" +#include "mmap.h" +#include "env_data.h" +#include "proc.h" +#include "mutex.h" +#include "lock.h" +#include "event.h" + +typedef struct PVRSRV_LINUX_EVENT_OBJECT_LIST_TAG +{ + rwlock_t sLock; + struct list_head sList; + +} PVRSRV_LINUX_EVENT_OBJECT_LIST; + + +typedef struct PVRSRV_LINUX_EVENT_OBJECT_TAG +{ + atomic_t sTimeStamp; + IMG_UINT32 ui32TimeStampPrevious; +#if defined(DEBUG) + IMG_UINT ui32Stats; +#endif + wait_queue_head_t sWait; + struct list_head sList; + IMG_HANDLE hResItem; + PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList; +} PVRSRV_LINUX_EVENT_OBJECT; + +PVRSRV_ERROR LinuxEventObjectListCreate(IMG_HANDLE *phEventObjectList) +{ + PVRSRV_LINUX_EVENT_OBJECT_LIST *psEventObjectList; + + if(OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT_LIST), + (IMG_VOID **)&psEventObjectList, IMG_NULL, + "Linux Event Object List") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectCreate: failed to allocate memory for event list")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + INIT_LIST_HEAD(&psEventObjectList->sList); + + rwlock_init(&psEventObjectList->sLock); + + *phEventObjectList = (IMG_HANDLE *) psEventObjectList; + + return PVRSRV_OK; +} + +PVRSRV_ERROR LinuxEventObjectListDestroy(IMG_HANDLE hEventObjectList) +{ + + PVRSRV_LINUX_EVENT_OBJECT_LIST *psEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST *) hEventObjectList ; + + if(psEventObjectList) + { + IMG_BOOL bListEmpty; + + read_lock(&psEventObjectList->sLock); + bListEmpty = list_empty(&psEventObjectList->sList); + read_unlock(&psEventObjectList->sLock); + + if (!bListEmpty) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectListDestroy: Event List is not empty")); + return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; + } + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT_LIST), psEventObjectList, IMG_NULL); + + } + + return PVRSRV_OK; +} + + +PVRSRV_ERROR LinuxEventObjectDelete(IMG_HANDLE hOSEventObjectList, IMG_HANDLE hOSEventObject) +{ + if(hOSEventObjectList) + { + if(hOSEventObject) + { + PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *)hOSEventObject; +#if defined(DEBUG) + PVR_DPF((PVR_DBG_MESSAGE, "LinuxEventObjectListDelete: Event object waits: %u", psLinuxEventObject->ui32Stats)); +#endif + if(ResManFreeResByPtr(psLinuxEventObject->hResItem, CLEANUP_WITH_POLL) != PVRSRV_OK) + { + return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; + } + + return PVRSRV_OK; + } + } + return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; + +} + +static PVRSRV_ERROR LinuxEventObjectDeleteCallback(IMG_PVOID pvParam, IMG_UINT32 ui32Param, IMG_BOOL bForceCleanup) +{ + PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = pvParam; + PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = psLinuxEventObject->psLinuxEventObjectList; + unsigned long ulLockFlags; + + PVR_UNREFERENCED_PARAMETER(ui32Param); + PVR_UNREFERENCED_PARAMETER(bForceCleanup); + + write_lock_irqsave(&psLinuxEventObjectList->sLock, ulLockFlags); + list_del(&psLinuxEventObject->sList); + write_unlock_irqrestore(&psLinuxEventObjectList->sLock, ulLockFlags); + +#if defined(DEBUG) + PVR_DPF((PVR_DBG_MESSAGE, "LinuxEventObjectDeleteCallback: Event object waits: %u", psLinuxEventObject->ui32Stats)); +#endif + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT), psLinuxEventObject, IMG_NULL); + + + return PVRSRV_OK; +} +PVRSRV_ERROR LinuxEventObjectAdd(IMG_HANDLE hOSEventObjectList, IMG_HANDLE *phOSEventObject) + { + PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject; + PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST*)hOSEventObjectList; + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + PVRSRV_PER_PROCESS_DATA *psPerProc; + unsigned long ulLockFlags; + + psPerProc = PVRSRVPerProcessData(ui32PID); + if (psPerProc == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: Couldn't find per-process data")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + + if(OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT), + (IMG_VOID **)&psLinuxEventObject, IMG_NULL, + "Linux Event Object") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: failed to allocate memory ")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + INIT_LIST_HEAD(&psLinuxEventObject->sList); + + atomic_set(&psLinuxEventObject->sTimeStamp, 0); + psLinuxEventObject->ui32TimeStampPrevious = 0; + +#if defined(DEBUG) + psLinuxEventObject->ui32Stats = 0; +#endif + init_waitqueue_head(&psLinuxEventObject->sWait); + + psLinuxEventObject->psLinuxEventObjectList = psLinuxEventObjectList; + + psLinuxEventObject->hResItem = ResManRegisterRes(psPerProc->hResManContext, + RESMAN_TYPE_EVENT_OBJECT, + psLinuxEventObject, + 0, + &LinuxEventObjectDeleteCallback); + + write_lock_irqsave(&psLinuxEventObjectList->sLock, ulLockFlags); + list_add(&psLinuxEventObject->sList, &psLinuxEventObjectList->sList); + write_unlock_irqrestore(&psLinuxEventObjectList->sLock, ulLockFlags); + + *phOSEventObject = psLinuxEventObject; + + return PVRSRV_OK; +} + +PVRSRV_ERROR LinuxEventObjectSignal(IMG_HANDLE hOSEventObjectList) +{ + PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject; + PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST*)hOSEventObjectList; + struct list_head *psListEntry, *psList; + + psList = &psLinuxEventObjectList->sList; + + + read_lock(&psLinuxEventObjectList->sLock); + list_for_each(psListEntry, psList) + { + + psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *)list_entry(psListEntry, PVRSRV_LINUX_EVENT_OBJECT, sList); + + atomic_inc(&psLinuxEventObject->sTimeStamp); + wake_up_interruptible(&psLinuxEventObject->sWait); + } + read_unlock(&psLinuxEventObjectList->sLock); + + return PVRSRV_OK; + +} + +PVRSRV_ERROR LinuxEventObjectWait(IMG_HANDLE hOSEventObject, IMG_UINT32 ui32MSTimeout) +{ + IMG_UINT32 ui32TimeStamp; + DEFINE_WAIT(sWait); + + PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *) hOSEventObject; + + IMG_UINT32 ui32TimeOutJiffies = msecs_to_jiffies(ui32MSTimeout); + + do + { + prepare_to_wait(&psLinuxEventObject->sWait, &sWait, TASK_INTERRUPTIBLE); + ui32TimeStamp = (IMG_UINT32)atomic_read(&psLinuxEventObject->sTimeStamp); + + if(psLinuxEventObject->ui32TimeStampPrevious != ui32TimeStamp) + { + break; + } + + LinuxUnLockMutex(&gPVRSRVLock); + + ui32TimeOutJiffies = (IMG_UINT32)schedule_timeout((IMG_INT32)ui32TimeOutJiffies); + + LinuxLockMutex(&gPVRSRVLock); +#if defined(DEBUG) + psLinuxEventObject->ui32Stats++; +#endif + + + } while (ui32TimeOutJiffies); + + finish_wait(&psLinuxEventObject->sWait, &sWait); + + psLinuxEventObject->ui32TimeStampPrevious = ui32TimeStamp; + + return ui32TimeOutJiffies ? PVRSRV_OK : PVRSRV_ERROR_TIMEOUT; + +} + diff --git a/sgx/services4/srvkm/env/linux/event.h b/sgx/services4/srvkm/env/linux/event.h new file mode 100644 index 0000000..3035283 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/event.h @@ -0,0 +1,32 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +PVRSRV_ERROR LinuxEventObjectListCreate(IMG_HANDLE *phEventObjectList); +PVRSRV_ERROR LinuxEventObjectListDestroy(IMG_HANDLE hEventObjectList); +PVRSRV_ERROR LinuxEventObjectAdd(IMG_HANDLE hOSEventObjectList, IMG_HANDLE *phOSEventObject); +PVRSRV_ERROR LinuxEventObjectDelete(IMG_HANDLE hOSEventObjectList, IMG_HANDLE hOSEventObject); +PVRSRV_ERROR LinuxEventObjectSignal(IMG_HANDLE hOSEventObjectList); +PVRSRV_ERROR LinuxEventObjectWait(IMG_HANDLE hOSEventObject, IMG_UINT32 ui32MSTimeout); diff --git a/sgx/services4/srvkm/env/linux/linkage.h b/sgx/services4/srvkm/env/linux/linkage.h new file mode 100644 index 0000000..e64012c --- /dev/null +++ b/sgx/services4/srvkm/env/linux/linkage.h @@ -0,0 +1,52 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __LINKAGE_H__ +#define __LINKAGE_H__ + +#if !defined(SUPPORT_DRI_DRM) +long PVRSRV_BridgeDispatchKM(struct file *file, unsigned int cmd, unsigned long arg); +#endif + +IMG_VOID PVRDPFInit(IMG_VOID); +PVRSRV_ERROR PVROSFuncInit(IMG_VOID); +IMG_VOID PVROSFuncDeInit(IMG_VOID); + +#ifdef DEBUG + +IMG_INT PVRDebugProcSetLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT32 count, IMG_VOID *data); +void ProcSeqShowDebugLevel(struct seq_file *sfile,void* el); + +#ifdef PVR_MANUAL_POWER_CONTROL +IMG_INT PVRProcSetPowerLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT32 count, IMG_VOID *data); + +void ProcSeqShowPowerLevel(struct seq_file *sfile,void* el); + +#endif + +#endif + +#endif diff --git a/sgx/services4/srvkm/env/linux/lock.h b/sgx/services4/srvkm/env/linux/lock.h new file mode 100644 index 0000000..a0854c3 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/lock.h @@ -0,0 +1,32 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __LOCK_H__ +#define __LOCK_H__ + +extern PVRSRV_LINUX_MUTEX gPVRSRVLock; + +#endif diff --git a/sgx/services4/srvkm/env/linux/mm.c b/sgx/services4/srvkm/env/linux/mm.c new file mode 100644 index 0000000..d53d40d --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mm.c @@ -0,0 +1,2026 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <asm/io.h> +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) +#include <linux/wrapper.h> +#endif +#include <linux/slab.h> +#include <linux/highmem.h> +#include <linux/sched.h> + +#include "img_defs.h" +#include "services.h" +#include "servicesint.h" +#include "syscommon.h" +#include "mutils.h" +#include "mm.h" +#include "pvrmmap.h" +#include "mmap.h" +#include "osfunc.h" +#include "pvr_debug.h" +#include "proc.h" +#include "mutex.h" +#include "lock.h" + +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + #include "lists.h" +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +typedef enum { + DEBUG_MEM_ALLOC_TYPE_KMALLOC, + DEBUG_MEM_ALLOC_TYPE_VMALLOC, + DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, + DEBUG_MEM_ALLOC_TYPE_IOREMAP, + DEBUG_MEM_ALLOC_TYPE_IO, + DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, + DEBUG_MEM_ALLOC_TYPE_COUNT +}DEBUG_MEM_ALLOC_TYPE; + +typedef struct _DEBUG_MEM_ALLOC_REC +{ + DEBUG_MEM_ALLOC_TYPE eAllocType; + IMG_VOID *pvKey; + IMG_VOID *pvCpuVAddr; + IMG_UINT32 ulCpuPAddr; + IMG_VOID *pvPrivateData; + IMG_UINT32 ui32Bytes; + pid_t pid; + IMG_CHAR *pszFileName; + IMG_UINT32 ui32Line; + + struct _DEBUG_MEM_ALLOC_REC *psNext; + struct _DEBUG_MEM_ALLOC_REC **ppsThis; +}DEBUG_MEM_ALLOC_REC; + +static IMPLEMENT_LIST_ANY_VA_2(DEBUG_MEM_ALLOC_REC, IMG_BOOL, IMG_FALSE) +static IMPLEMENT_LIST_ANY_VA(DEBUG_MEM_ALLOC_REC) +static IMPLEMENT_LIST_FOR_EACH(DEBUG_MEM_ALLOC_REC) +static IMPLEMENT_LIST_INSERT(DEBUG_MEM_ALLOC_REC) +static IMPLEMENT_LIST_REMOVE(DEBUG_MEM_ALLOC_REC) + + +static DEBUG_MEM_ALLOC_REC *g_MemoryRecords; + +static IMG_UINT32 g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; +static IMG_UINT32 g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; + +static IMG_UINT32 g_SysRAMWaterMark; +static IMG_UINT32 g_SysRAMHighWaterMark; + +static IMG_UINT32 g_IOMemWaterMark; +static IMG_UINT32 g_IOMemHighWaterMark; + +static IMG_VOID DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType, + IMG_VOID *pvKey, + IMG_VOID *pvCpuVAddr, + IMG_UINT32 ulCpuPAddr, + IMG_VOID *pvPrivateData, + IMG_UINT32 ui32Bytes, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line); + +static IMG_VOID DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_VOID *pvKey, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + +static IMG_CHAR *DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType); + + +static struct proc_dir_entry *g_SeqFileMemoryRecords =0; +static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off); +static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el); +static void* ProcSeqOff2ElementMemoryRecords(struct seq_file * sfile, loff_t off); + +#endif + + +#if defined(DEBUG_LINUX_MEM_AREAS) +typedef struct _DEBUG_LINUX_MEM_AREA_REC +{ + LinuxMemArea *psLinuxMemArea; + IMG_UINT32 ui32Flags; + pid_t pid; + + struct _DEBUG_LINUX_MEM_AREA_REC *psNext; + struct _DEBUG_LINUX_MEM_AREA_REC **ppsThis; +}DEBUG_LINUX_MEM_AREA_REC; + + +static IMPLEMENT_LIST_ANY_VA(DEBUG_LINUX_MEM_AREA_REC) +static IMPLEMENT_LIST_FOR_EACH(DEBUG_LINUX_MEM_AREA_REC) +static IMPLEMENT_LIST_INSERT(DEBUG_LINUX_MEM_AREA_REC) +static IMPLEMENT_LIST_REMOVE(DEBUG_LINUX_MEM_AREA_REC) + + + + +static DEBUG_LINUX_MEM_AREA_REC *g_LinuxMemAreaRecords; +static IMG_UINT32 g_LinuxMemAreaCount; +static IMG_UINT32 g_LinuxMemAreaWaterMark; +static IMG_UINT32 g_LinuxMemAreaHighWaterMark; + + +static struct proc_dir_entry *g_SeqFileMemArea=0; + +static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off); +static void ProcSeqShowMemArea(struct seq_file *sfile,void* el); +static void* ProcSeqOff2ElementMemArea(struct seq_file *sfile, loff_t off); + +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static PVRSRV_LINUX_MUTEX g_sDebugMutex; +#endif + +#if (defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)) +static void ProcSeqStartstopDebugMutex(struct seq_file *sfile,IMG_BOOL start); +#endif + +static LinuxKMemCache *psLinuxMemAreaCache; + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +static IMG_VOID ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length); +static IMG_VOID UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length); +#endif + +static LinuxMemArea *LinuxMemAreaStructAlloc(IMG_VOID); +static IMG_VOID LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea); +#if defined(DEBUG_LINUX_MEM_AREAS) +static IMG_VOID DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags); +static DEBUG_LINUX_MEM_AREA_REC *DebugLinuxMemAreaRecordFind(LinuxMemArea *psLinuxMemArea); +static IMG_VOID DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea); +#endif + +PVRSRV_ERROR +LinuxMMInit(IMG_VOID) +{ +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + LinuxInitMutex(&g_sDebugMutex); +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) + { + g_SeqFileMemArea = CreateProcReadEntrySeq( + "mem_areas", + NULL, + ProcSeqNextMemArea, + ProcSeqShowMemArea, + ProcSeqOff2ElementMemArea, + ProcSeqStartstopDebugMutex + ); + if(!g_SeqFileMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } +#endif + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + g_SeqFileMemoryRecords =CreateProcReadEntrySeq( + "meminfo", + NULL, + ProcSeqNextMemoryRecords, + ProcSeqShowMemoryRecords, + ProcSeqOff2ElementMemoryRecords, + ProcSeqStartstopDebugMutex + ); + if(!g_SeqFileMemoryRecords) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + } +#endif + + psLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); + if(!psLinuxMemAreaCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + return PVRSRV_OK; +} + +#if defined(DEBUG_LINUX_MEM_AREAS) +static IMG_VOID LinuxMMCleanup_MemAreas_ForEachCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord) +{ + LinuxMemArea *psLinuxMemArea; + + psLinuxMemArea = psCurrentRecord->psLinuxMemArea; + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up Linux memory area (%p), type=%s, size=%d bytes", + __FUNCTION__, + psCurrentRecord->psLinuxMemArea, + LinuxMemAreaTypeToString(psCurrentRecord->psLinuxMemArea->eAreaType), + psCurrentRecord->psLinuxMemArea->ui32ByteSize)); + + LinuxMemAreaDeepFree(psLinuxMemArea); +} +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord) + +{ + + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up memory: " + "type=%s " + "CpuVAddr=%p " + "CpuPAddr=0x%08x, " + "allocated @ file=%s,line=%d", + __FUNCTION__, + DebugMemAllocRecordTypeToString(psCurrentRecord->eAllocType), + psCurrentRecord->pvCpuVAddr, + psCurrentRecord->ulCpuPAddr, + psCurrentRecord->pszFileName, + psCurrentRecord->ui32Line)); + switch(psCurrentRecord->eAllocType) + { + case DEBUG_MEM_ALLOC_TYPE_KMALLOC: + KFreeWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_IOREMAP: + IOUnmapWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_IO: + + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, psCurrentRecord->pvKey, __FILE__, __LINE__); + break; + case DEBUG_MEM_ALLOC_TYPE_VMALLOC: + VFreeWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES: + + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, psCurrentRecord->pvKey, __FILE__, __LINE__); + break; + case DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE: + KMemCacheFreeWrapper(psCurrentRecord->pvPrivateData, psCurrentRecord->pvCpuVAddr); + break; + default: + PVR_ASSERT(0); + } +} +#endif + + +IMG_VOID +LinuxMMCleanup(IMG_VOID) +{ + +#if defined(DEBUG_LINUX_MEM_AREAS) + { + if(g_LinuxMemAreaCount) + { + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: There are %d LinuxMemArea allocation unfreed (%d bytes)", + __FUNCTION__, g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark)); + } + + List_DEBUG_LINUX_MEM_AREA_REC_ForEach(g_LinuxMemAreaRecords, + LinuxMMCleanup_MemAreas_ForEachCb); + + RemoveProcEntrySeq( g_SeqFileMemArea ); + } +#endif + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + + + List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, + LinuxMMCleanup_MemRecords_ForEachVa); + + RemoveProcEntrySeq( g_SeqFileMemoryRecords ); + } +#endif + + if(psLinuxMemAreaCache) + { + KMemCacheDestroyWrapper(psLinuxMemAreaCache); + psLinuxMemAreaCache=NULL; + } +} + + +IMG_VOID * +_KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ + IMG_VOID *pvRet; + pvRet = kmalloc(ui32ByteSize, uFlags); +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + if(pvRet) + { + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMALLOC, + pvRet, + pvRet, + 0, + NULL, + ui32ByteSize, + pszFileName, + ui32Line + ); + } +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + return pvRet; +} + + +IMG_VOID +_KFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMALLOC, pvCpuVAddr, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + kfree(pvCpuVAddr); +} + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static IMG_VOID +DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType, + IMG_VOID *pvKey, + IMG_VOID *pvCpuVAddr, + IMG_UINT32 ulCpuPAddr, + IMG_VOID *pvPrivateData, + IMG_UINT32 ui32Bytes, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + DEBUG_MEM_ALLOC_REC *psRecord; + + LinuxLockMutex(&g_sDebugMutex); + + psRecord = kmalloc(sizeof(DEBUG_MEM_ALLOC_REC), GFP_KERNEL); + + psRecord->eAllocType = eAllocType; + psRecord->pvKey = pvKey; + psRecord->pvCpuVAddr = pvCpuVAddr; + psRecord->ulCpuPAddr = ulCpuPAddr; + psRecord->pvPrivateData = pvPrivateData; + psRecord->pid = OSGetCurrentProcessIDKM(); + psRecord->ui32Bytes = ui32Bytes; + psRecord->pszFileName = pszFileName; + psRecord->ui32Line = ui32Line; + + List_DEBUG_MEM_ALLOC_REC_Insert(&g_MemoryRecords, psRecord); + + g_WaterMarkData[eAllocType] += ui32Bytes; + if(g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType]) + { + g_HighWaterMarkData[eAllocType] = g_WaterMarkData[eAllocType]; + } + + if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + || eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC + || eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES + || eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) + { + g_SysRAMWaterMark += ui32Bytes; + if(g_SysRAMWaterMark > g_SysRAMHighWaterMark) + { + g_SysRAMHighWaterMark = g_SysRAMWaterMark; + } + } + else if(eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP + || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) + { + g_IOMemWaterMark += ui32Bytes; + if(g_IOMemWaterMark > g_IOMemHighWaterMark) + { + g_IOMemHighWaterMark = g_IOMemWaterMark; + } + } + + LinuxUnLockMutex(&g_sDebugMutex); +} + + +static IMG_BOOL DebugMemAllocRecordRemove_AnyVaCb(DEBUG_MEM_ALLOC_REC *psCurrentRecord, va_list va) +{ + DEBUG_MEM_ALLOC_TYPE eAllocType; + IMG_VOID *pvKey; + + eAllocType = va_arg(va, DEBUG_MEM_ALLOC_TYPE); + pvKey = va_arg(va, IMG_VOID*); + + if(psCurrentRecord->eAllocType == eAllocType + && psCurrentRecord->pvKey == pvKey) + { + eAllocType = psCurrentRecord->eAllocType; + g_WaterMarkData[eAllocType] -= psCurrentRecord->ui32Bytes; + + if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + || eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC + || eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES + || eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) + { + g_SysRAMWaterMark -= psCurrentRecord->ui32Bytes; + } + else if(eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP + || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) + { + g_IOMemWaterMark -= psCurrentRecord->ui32Bytes; + } + + List_DEBUG_MEM_ALLOC_REC_Remove(psCurrentRecord); + kfree(psCurrentRecord); + + return IMG_TRUE; + } + else + { + return IMG_FALSE; + } +} + + +static IMG_VOID +DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_VOID *pvKey, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ + LinuxLockMutex(&g_sDebugMutex); + + + if(!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords, + DebugMemAllocRecordRemove_AnyVaCb, + eAllocType, + pvKey)) + { + PVR_DPF((PVR_DBG_ERROR, "%s: couldn't find an entry for type=%s with pvKey=%p (called from %s, line %d\n", + __FUNCTION__, DebugMemAllocRecordTypeToString(eAllocType), pvKey, + pszFileName, ui32Line)); + } + + LinuxUnLockMutex(&g_sDebugMutex); +} + + +static IMG_CHAR * +DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType) +{ + IMG_CHAR *apszDebugMemoryRecordTypes[] = { + "KMALLOC", + "VMALLOC", + "ALLOC_PAGES", + "IOREMAP", + "IO", + "KMEM_CACHE_ALLOC" + }; + return apszDebugMemoryRecordTypes[eAllocType]; +} +#endif + + + +IMG_VOID * +_VMallocWrapper(IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32AllocFlags, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + pgprot_t PGProtFlags; + IMG_VOID *pvRet; + + switch(ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) + { + case PVRSRV_HAP_CACHED: + case PVRSRV_HAP_SMART: + PGProtFlags = PAGE_KERNEL; + break; + case PVRSRV_HAP_WRITECOMBINE: + PGProtFlags = PGPROT_WC(PAGE_KERNEL); + break; + case PVRSRV_HAP_UNCACHED: + PGProtFlags = PGPROT_UC(PAGE_KERNEL); + break; + default: + PVR_DPF((PVR_DBG_ERROR, + "VMAllocWrapper: unknown mapping flags=0x%08x", + ui32AllocFlags)); + dump_stack(); + return NULL; + } + + + pvRet = __vmalloc(ui32Bytes, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + if(pvRet) + { + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMALLOC, + pvRet, + pvRet, + 0, + NULL, + PAGE_ALIGN(ui32Bytes), + pszFileName, + ui32Line + ); + } +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + return pvRet; +} + + +IMG_VOID +_VFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_VMALLOC, pvCpuVAddr, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + vfree(pvCpuVAddr); +} + + +LinuxMemArea * +NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea; + IMG_VOID *pvCpuVAddr; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + goto failed; + } + + pvCpuVAddr = VMallocWrapper(ui32Bytes, ui32AreaFlags); + if(!pvCpuVAddr) + { + goto failed; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + + ReservePages(pvCpuVAddr, ui32Bytes); +#endif + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_VMALLOC; + psLinuxMemArea->uData.sVmalloc.pvVmallocAddress = pvCpuVAddr; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + + if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) + OSInvalidateCPUCacheRangeKM(psLinuxMemArea, pvCpuVAddr, ui32Bytes); + + return psLinuxMemArea; + +failed: + PVR_DPF((PVR_DBG_ERROR, "%s: failed!", __FUNCTION__)); + if(psLinuxMemArea) + LinuxMemAreaStructFree(psLinuxMemArea); + return NULL; +} + + +IMG_VOID +FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_ASSERT(psLinuxMemArea); + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_VMALLOC); + PVR_ASSERT(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, + psLinuxMemArea->ui32ByteSize); +#endif + + PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", + __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); + VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +static IMG_VOID +ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length) +{ + IMG_VOID *pvPage; + IMG_VOID *pvEnd = pvAddress + ui32Length; + + for(pvPage = pvAddress; pvPage < pvEnd; pvPage += PAGE_SIZE) + { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + SetPageReserved(vmalloc_to_page(pvPage)); +#else + mem_map_reserve(vmalloc_to_page(pvPage)); +#endif + } +} + + +static IMG_VOID +UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length) +{ + IMG_VOID *pvPage; + IMG_VOID *pvEnd = pvAddress + ui32Length; + + for(pvPage = pvAddress; pvPage < pvEnd; pvPage += PAGE_SIZE) + { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + ClearPageReserved(vmalloc_to_page(pvPage)); +#else + mem_map_unreserve(vmalloc_to_page(pvPage)); +#endif + } +} +#endif + + +IMG_VOID * +_IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + IMG_VOID *pvIORemapCookie; + + switch(ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK) + { + case PVRSRV_HAP_CACHED: + case PVRSRV_HAP_SMART: + pvIORemapCookie = (IMG_VOID *)IOREMAP(BasePAddr.uiAddr, ui32Bytes); + break; + case PVRSRV_HAP_WRITECOMBINE: + pvIORemapCookie = (IMG_VOID *)IOREMAP_WC(BasePAddr.uiAddr, ui32Bytes); + break; + case PVRSRV_HAP_UNCACHED: + pvIORemapCookie = (IMG_VOID *)IOREMAP_UC(BasePAddr.uiAddr, ui32Bytes); + break; + default: + PVR_DPF((PVR_DBG_ERROR, "IORemapWrapper: unknown mapping flags")); + return NULL; + } + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + if(pvIORemapCookie) + { + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IOREMAP, + pvIORemapCookie, + pvIORemapCookie, + BasePAddr.uiAddr, + NULL, + ui32Bytes, + pszFileName, + ui32Line + ); + } +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + return pvIORemapCookie; +} + + +IMG_VOID +_IOUnmapWrapper(IMG_VOID *pvIORemapCookie, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IOREMAP, pvIORemapCookie, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + iounmap(pvIORemapCookie); +} + + +LinuxMemArea * +NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea; + IMG_VOID *pvIORemapCookie; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + return NULL; + } + + pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32AreaFlags); + if(!pvIORemapCookie) + { + LinuxMemAreaStructFree(psLinuxMemArea); + return NULL; + } + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_IOREMAP; + psLinuxMemArea->uData.sIORemap.pvIORemapCookie = pvIORemapCookie; + psLinuxMemArea->uData.sIORemap.CPUPhysAddr = BasePAddr; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + return psLinuxMemArea; +} + + +IMG_VOID +FreeIORemapLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_IOREMAP); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + + IOUnmapWrapper(psLinuxMemArea->uData.sIORemap.pvIORemapCookie); + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static IMG_BOOL +TreatExternalPagesAsContiguous(IMG_SYS_PHYADDR *psSysPhysAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig) +{ + IMG_UINT32 ui32; + IMG_UINT32 ui32AddrChk; + IMG_UINT32 ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + + + for (ui32 = 0, ui32AddrChk = psSysPhysAddr[0].uiAddr; + ui32 < ui32NumPages; + ui32++, ui32AddrChk = (bPhysContig) ? (ui32AddrChk + PAGE_SIZE) : psSysPhysAddr[ui32].uiAddr) + { + if (!pfn_valid(PHYS_TO_PFN(ui32AddrChk))) + { + break; + } + } + if (ui32 == ui32NumPages) + { + return IMG_FALSE; + } + + if (!bPhysContig) + { + for (ui32 = 0, ui32AddrChk = psSysPhysAddr[0].uiAddr; + ui32 < ui32NumPages; + ui32++, ui32AddrChk += PAGE_SIZE) + { + if (psSysPhysAddr[ui32].uiAddr != ui32AddrChk) + { + return IMG_FALSE; + } + } + } + + return IMG_TRUE; +} +#endif + +LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig, IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + return NULL; + } + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_EXTERNAL_KV; + psLinuxMemArea->uData.sExternalKV.pvExternalKV = pvCPUVAddr; + psLinuxMemArea->uData.sExternalKV.bPhysContig = +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + (bPhysContig || TreatExternalPagesAsContiguous(pBasePAddr, ui32Bytes, bPhysContig)) + ? IMG_TRUE : IMG_FALSE; +#else + bPhysContig; +#endif + if (psLinuxMemArea->uData.sExternalKV.bPhysContig) + { + psLinuxMemArea->uData.sExternalKV.uPhysAddr.SysPhysAddr = *pBasePAddr; + } + else + { + psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr = pBasePAddr; + } + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + return psLinuxMemArea; +} + + +IMG_VOID +FreeExternalKVLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_EXTERNAL_KV); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +LinuxMemArea * +NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + return NULL; + } + + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_IO; + psLinuxMemArea->uData.sIO.CPUPhysAddr.uiAddr = BasePAddr.uiAddr; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IO, + (IMG_VOID *)BasePAddr.uiAddr, + 0, + BasePAddr.uiAddr, + NULL, + ui32Bytes, + "unknown", + 0 + ); +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + return psLinuxMemArea; +} + + +IMG_VOID +FreeIOLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_IO); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, + (IMG_VOID *)psLinuxMemArea->uData.sIO.CPUPhysAddr.uiAddr, __FILE__, __LINE__); +#endif + + + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +LinuxMemArea * +NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea; + IMG_UINT32 ui32PageCount; + struct page **pvPageList; + IMG_HANDLE hBlockPageList; + IMG_INT32 i; + PVRSRV_ERROR eError; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + goto failed_area_alloc; + } + + ui32PageCount = RANGE_TO_PAGES(ui32Bytes); + eError = OSAllocMem(0, sizeof(*pvPageList) * ui32PageCount, (IMG_VOID **)&pvPageList, &hBlockPageList, + "Array of pages"); + if(eError != PVRSRV_OK) + { + goto failed_page_list_alloc; + } + + for(i=0; i<(IMG_INT32)ui32PageCount; i++) + { + pvPageList[i] = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); + if(!pvPageList[i]) + { + goto failed_alloc_pages; + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + SetPageReserved(pvPageList[i]); +#else + mem_map_reserve(pvPageList[i]); +#endif +#endif + + } + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, + pvPageList, + 0, + 0, + NULL, + PAGE_ALIGN(ui32Bytes), + "unknown", + 0 + ); +#endif + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES; + psLinuxMemArea->uData.sPageList.pvPageList = pvPageList; + psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + + + if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) + { + psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; + } + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + return psLinuxMemArea; + +failed_alloc_pages: + for(i--; i >= 0; i--) + { + __free_pages(pvPageList[i], 0); + } + (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); + psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; +failed_page_list_alloc: + LinuxMemAreaStructFree(psLinuxMemArea); +failed_area_alloc: + PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__)); + + return NULL; +} + + +IMG_VOID +FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + IMG_UINT32 ui32PageCount; + struct page **pvPageList; + IMG_HANDLE hBlockPageList; + IMG_INT32 i; + + PVR_ASSERT(psLinuxMemArea); + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + + ui32PageCount = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + pvPageList = psLinuxMemArea->uData.sPageList.pvPageList; + hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList; + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, pvPageList, __FILE__, __LINE__); +#endif + + for(i=0;i<(IMG_INT32)ui32PageCount;i++) + { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + ClearPageReserved(pvPageList[i]); +#else + mem_map_reserve(pvPageList[i]); +#endif +#endif + if (psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_SMART) + { + // XXX some race here.. + clear_bit(PG_private, &pvPageList[i]->flags); + clear_bit(PG_private_2, &pvPageList[i]->flags); + } + __free_pages(pvPageList[i], 0); + } + + (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); + psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +struct page* +LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, + IMG_UINT32 ui32ByteOffset) +{ + IMG_UINT32 ui32PageIndex; + IMG_CHAR *pui8Addr; + + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_ALLOC_PAGES: + ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); + return psLinuxMemArea->uData.sPageList.pvPageList[ui32PageIndex]; + + case LINUX_MEM_AREA_VMALLOC: + pui8Addr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; + pui8Addr += ui32ByteOffset; + return vmalloc_to_page(pui8Addr); + + case LINUX_MEM_AREA_SUB_ALLOC: + + return LinuxMemAreaOffsetToPage(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea, + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + + ui32ByteOffset); + default: + PVR_DPF((PVR_DBG_ERROR, + "%s: Unsupported request for struct page from LinuxMemArea with type=%s", + __FUNCTION__, LinuxMemAreaTypeToString(psLinuxMemArea->eAreaType))); + return NULL; + } +} + + +LinuxKMemCache * +KMemCacheCreateWrapper(IMG_CHAR *pszName, + size_t Size, + size_t Align, + IMG_UINT32 ui32Flags) +{ +#if defined(DEBUG_LINUX_SLAB_ALLOCATIONS) + ui32Flags |= SLAB_POISON|SLAB_RED_ZONE; +#endif + return kmem_cache_create(pszName, Size, Align, ui32Flags, NULL +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) + , NULL +#endif + ); +} + + +IMG_VOID +KMemCacheDestroyWrapper(LinuxKMemCache *psCache) +{ + kmem_cache_destroy(psCache); +} + + +IMG_VOID * +_KMemCacheAllocWrapper(LinuxKMemCache *psCache, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)) + gfp_t Flags, +#else + IMG_INT Flags, +#endif + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + IMG_VOID *pvRet; + + pvRet = kmem_cache_zalloc(psCache, Flags); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, + pvRet, + pvRet, + 0, + psCache, + kmem_cache_size(psCache), + pszFileName, + ui32Line + ); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + return pvRet; +} + + +IMG_VOID +_KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, pvObject, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + kmem_cache_free(psCache, pvObject); +} + + +const IMG_CHAR * +KMemCacheNameWrapper(LinuxKMemCache *psCache) +{ + PVR_UNREFERENCED_PARAMETER(psCache); + + + return ""; +} + + +LinuxMemArea * +NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, + IMG_UINT32 ui32ByteOffset, + IMG_UINT32 ui32Bytes) +{ + LinuxMemArea *psLinuxMemArea; + + PVR_ASSERT((ui32ByteOffset+ui32Bytes) <= psParentLinuxMemArea->ui32ByteSize); + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if(!psLinuxMemArea) + { + return NULL; + } + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_SUB_ALLOC; + psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea = psParentLinuxMemArea; + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset = ui32ByteOffset; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = psParentLinuxMemArea->ui32AreaFlags; + psLinuxMemArea->bNeedsCacheInvalidate = psParentLinuxMemArea->bNeedsCacheInvalidate; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEM_AREAS) + { + DEBUG_LINUX_MEM_AREA_REC *psParentRecord; + psParentRecord = DebugLinuxMemAreaRecordFind(psParentLinuxMemArea); + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, psParentRecord->ui32Flags); + } +#endif + + return psLinuxMemArea; +} + + +static IMG_VOID +FreeSubLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + + + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +static LinuxMemArea * +LinuxMemAreaStructAlloc(IMG_VOID) +{ +#if 0 + LinuxMemArea *psLinuxMemArea; + psLinuxMemArea = kmem_cache_alloc(psLinuxMemAreaCache, GFP_KERNEL); + printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea); + dump_stack(); + return psLinuxMemArea; +#else + return KMemCacheAllocWrapper(psLinuxMemAreaCache, GFP_KERNEL); +#endif +} + + +static IMG_VOID +LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) +{ + KMemCacheFreeWrapper(psLinuxMemAreaCache, psLinuxMemArea); + + +} + + +IMG_VOID +LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea) +{ + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_VMALLOC: + FreeVMallocLinuxMemArea(psLinuxMemArea); + break; + case LINUX_MEM_AREA_ALLOC_PAGES: + FreeAllocPagesLinuxMemArea(psLinuxMemArea); + break; + case LINUX_MEM_AREA_IOREMAP: + FreeIORemapLinuxMemArea(psLinuxMemArea); + break; + case LINUX_MEM_AREA_EXTERNAL_KV: + FreeExternalKVLinuxMemArea(psLinuxMemArea); + break; + case LINUX_MEM_AREA_IO: + FreeIOLinuxMemArea(psLinuxMemArea); + break; + case LINUX_MEM_AREA_SUB_ALLOC: + FreeSubLinuxMemArea(psLinuxMemArea); + break; + default: + PVR_DPF((PVR_DBG_ERROR, "%s: Unknown are type (%d)\n", + __FUNCTION__, psLinuxMemArea->eAreaType)); + break; + } +} + + +#if defined(DEBUG_LINUX_MEM_AREAS) +static IMG_VOID +DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) +{ + DEBUG_LINUX_MEM_AREA_REC *psNewRecord; + const IMG_CHAR *pi8FlagsString; + + LinuxLockMutex(&g_sDebugMutex); + + if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + { + g_LinuxMemAreaWaterMark += psLinuxMemArea->ui32ByteSize; + if(g_LinuxMemAreaWaterMark > g_LinuxMemAreaHighWaterMark) + { + g_LinuxMemAreaHighWaterMark = g_LinuxMemAreaWaterMark; + } + } + g_LinuxMemAreaCount++; + + + psNewRecord = kmalloc(sizeof(DEBUG_LINUX_MEM_AREA_REC), GFP_KERNEL); + if(psNewRecord) + { + + psNewRecord->psLinuxMemArea = psLinuxMemArea; + psNewRecord->ui32Flags = ui32Flags; + psNewRecord->pid = OSGetCurrentProcessIDKM(); + + List_DEBUG_LINUX_MEM_AREA_REC_Insert(&g_LinuxMemAreaRecords, psNewRecord); + } + else + { + PVR_DPF((PVR_DBG_ERROR, + "%s: failed to allocate linux memory area record.", + __FUNCTION__)); + } + + + pi8FlagsString = HAPFlagsToString(ui32Flags); + if(strstr(pi8FlagsString, "UNKNOWN")) + { + PVR_DPF((PVR_DBG_ERROR, + "%s: Unexpected flags (0x%08x) associated with psLinuxMemArea @ %p", + __FUNCTION__, + ui32Flags, + psLinuxMemArea)); + + } + + LinuxUnLockMutex(&g_sDebugMutex); +} + + + +static IMG_VOID* MatchLinuxMemArea_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord, + va_list va) +{ + LinuxMemArea *psLinuxMemArea; + + psLinuxMemArea = va_arg(va, LinuxMemArea*); + if(psCurrentRecord->psLinuxMemArea == psLinuxMemArea) + { + return psCurrentRecord; + } + else + { + return IMG_NULL; + } +} + + +static DEBUG_LINUX_MEM_AREA_REC * +DebugLinuxMemAreaRecordFind(LinuxMemArea *psLinuxMemArea) +{ + DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord; + + LinuxLockMutex(&g_sDebugMutex); + psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, + MatchLinuxMemArea_AnyVaCb, + psLinuxMemArea); + + LinuxUnLockMutex(&g_sDebugMutex); + + return psCurrentRecord; +} + + +static IMG_VOID +DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea) +{ + DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord; + + LinuxLockMutex(&g_sDebugMutex); + + if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + { + g_LinuxMemAreaWaterMark -= psLinuxMemArea->ui32ByteSize; + } + g_LinuxMemAreaCount--; + + + psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, + MatchLinuxMemArea_AnyVaCb, + psLinuxMemArea); + if(psCurrentRecord) + { + + List_DEBUG_LINUX_MEM_AREA_REC_Remove(psCurrentRecord); + kfree(psCurrentRecord); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "%s: couldn't find an entry for psLinuxMemArea=%p\n", + __FUNCTION__, psLinuxMemArea)); + } + + LinuxUnLockMutex(&g_sDebugMutex); +} +#endif + + +IMG_VOID * +LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea) +{ + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_VMALLOC: + return psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; + case LINUX_MEM_AREA_IOREMAP: + return psLinuxMemArea->uData.sIORemap.pvIORemapCookie; + case LINUX_MEM_AREA_EXTERNAL_KV: + return psLinuxMemArea->uData.sExternalKV.pvExternalKV; + case LINUX_MEM_AREA_SUB_ALLOC: + { + IMG_CHAR *pAddr = + LinuxMemAreaToCpuVAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); + if(!pAddr) + { + return NULL; + } + return pAddr + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset; + } + default: + return NULL; + } +} + + +IMG_CPU_PHYADDR +LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) +{ + IMG_CPU_PHYADDR CpuPAddr; + + CpuPAddr.uiAddr = 0; + + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_IOREMAP: + { + CpuPAddr = psLinuxMemArea->uData.sIORemap.CPUPhysAddr; + CpuPAddr.uiAddr += ui32ByteOffset; + break; + } + case LINUX_MEM_AREA_EXTERNAL_KV: + { + if (psLinuxMemArea->uData.sExternalKV.bPhysContig) + { + CpuPAddr = SysSysPAddrToCpuPAddr(psLinuxMemArea->uData.sExternalKV.uPhysAddr.SysPhysAddr); + CpuPAddr.uiAddr += ui32ByteOffset; + } + else + { + IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); + IMG_SYS_PHYADDR SysPAddr = psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr[ui32PageIndex]; + + CpuPAddr = SysSysPAddrToCpuPAddr(SysPAddr); + CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset); + } + break; + } + case LINUX_MEM_AREA_IO: + { + CpuPAddr = psLinuxMemArea->uData.sIO.CPUPhysAddr; + CpuPAddr.uiAddr += ui32ByteOffset; + break; + } + case LINUX_MEM_AREA_VMALLOC: + { + IMG_CHAR *pCpuVAddr; + pCpuVAddr = + (IMG_CHAR *)psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; + pCpuVAddr += ui32ByteOffset; + CpuPAddr.uiAddr = VMallocToPhys(pCpuVAddr); + break; + } + case LINUX_MEM_AREA_ALLOC_PAGES: + { + struct page *page; + IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); + page = psLinuxMemArea->uData.sPageList.pvPageList[ui32PageIndex]; + CpuPAddr.uiAddr = page_to_phys(page); + CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset); + break; + } + case LINUX_MEM_AREA_SUB_ALLOC: + { + CpuPAddr = + OSMemHandleToCpuPAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea, + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + + ui32ByteOffset); + break; + } + default: + { + PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n", + __FUNCTION__, psLinuxMemArea->eAreaType)); + PVR_ASSERT(CpuPAddr.uiAddr); + break; + } + } + + return CpuPAddr; +} + + +IMG_BOOL +LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea) +{ + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_IOREMAP: + case LINUX_MEM_AREA_IO: + return IMG_TRUE; + + case LINUX_MEM_AREA_EXTERNAL_KV: + return psLinuxMemArea->uData.sExternalKV.bPhysContig; + + case LINUX_MEM_AREA_VMALLOC: + case LINUX_MEM_AREA_ALLOC_PAGES: + return IMG_FALSE; + + case LINUX_MEM_AREA_SUB_ALLOC: + + return LinuxMemAreaPhysIsContig(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); + + default: + PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n", + __FUNCTION__, psLinuxMemArea->eAreaType)); + break; + } + return IMG_FALSE; +} + + +const IMG_CHAR * +LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) +{ + + switch(eMemAreaType) + { + case LINUX_MEM_AREA_IOREMAP: + return "LINUX_MEM_AREA_IOREMAP"; + case LINUX_MEM_AREA_EXTERNAL_KV: + return "LINUX_MEM_AREA_EXTERNAL_KV"; + case LINUX_MEM_AREA_IO: + return "LINUX_MEM_AREA_IO"; + case LINUX_MEM_AREA_VMALLOC: + return "LINUX_MEM_AREA_VMALLOC"; + case LINUX_MEM_AREA_SUB_ALLOC: + return "LINUX_MEM_AREA_SUB_ALLOC"; + case LINUX_MEM_AREA_ALLOC_PAGES: + return "LINUX_MEM_AREA_ALLOC_PAGES"; + default: + PVR_ASSERT(0); + } + + return ""; +} + + +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static void ProcSeqStartstopDebugMutex(struct seq_file *sfile, IMG_BOOL start) +{ + if(start) + { + LinuxLockMutex(&g_sDebugMutex); + } + else + { + LinuxUnLockMutex(&g_sDebugMutex); + } +} +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) + +static IMG_VOID* DecOffMemAreaRec_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psNode, va_list va) +{ + off_t *pOff = va_arg(va, off_t*); + if (--(*pOff)) + { + return IMG_NULL; + } + else + { + return psNode; + } +} + + +static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off) +{ + DEBUG_LINUX_MEM_AREA_REC *psRecord; + psRecord = (DEBUG_LINUX_MEM_AREA_REC*) + List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, + DecOffMemAreaRec_AnyVaCb, + &off); + return (void*)psRecord; +} + +static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off) +{ + DEBUG_LINUX_MEM_AREA_REC *psRecord; + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + psRecord = (DEBUG_LINUX_MEM_AREA_REC*) + List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, + DecOffMemAreaRec_AnyVaCb, + &off); + return (void*)psRecord; +} + + +static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) +{ + DEBUG_LINUX_MEM_AREA_REC *psRecord = (DEBUG_LINUX_MEM_AREA_REC*)el; + if(el == PVR_PROC_SEQ_START_TOKEN) + { + +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + seq_printf( sfile, + "Number of Linux Memory Areas: %u\n" + "At the current water mark these areas correspond to %u bytes (excluding SUB areas)\n" + "At the highest water mark these areas corresponded to %u bytes (excluding SUB areas)\n" + "\nDetails for all Linux Memory Areas:\n" + "%s %-24s %s %s %-8s %-5s %s\n", + g_LinuxMemAreaCount, + g_LinuxMemAreaWaterMark, + g_LinuxMemAreaHighWaterMark, + "psLinuxMemArea", + "LinuxMemType", + "CpuVAddr", + "CpuPAddr", + "Bytes", + "Pid", + "Flags" + ); +#else + seq_printf( sfile, + "<mem_areas_header>\n" + "\t<count>%u</count>\n" + "\t<watermark key=\"mar0\" description=\"current\" bytes=\"%u\"/>\n" + "\t<watermark key=\"mar1\" description=\"high\" bytes=\"%u\"/>\n" + "</mem_areas_header>\n", + g_LinuxMemAreaCount, + g_LinuxMemAreaWaterMark, + g_LinuxMemAreaHighWaterMark + ); +#endif + return; + } + + seq_printf( sfile, +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + "%8p %-24s %8p %08x %-8d %-5u %08x=(%s)\n", +#else + "<linux_mem_area>\n" + "\t<pointer>%8p</pointer>\n" + "\t<type>%s</type>\n" + "\t<cpu_virtual>%8p</cpu_virtual>\n" + "\t<cpu_physical>%08x</cpu_physical>\n" + "\t<bytes>%ld</bytes>\n" + "\t<pid>%u</pid>\n" + "\t<flags>%08lx</flags>\n" + "\t<flags_string>%s</flags_string>\n" + "</linux_mem_area>\n", +#endif + psRecord->psLinuxMemArea, + LinuxMemAreaTypeToString(psRecord->psLinuxMemArea->eAreaType), + LinuxMemAreaToCpuVAddr(psRecord->psLinuxMemArea), + LinuxMemAreaToCpuPAddr(psRecord->psLinuxMemArea,0).uiAddr, + psRecord->psLinuxMemArea->ui32ByteSize, + psRecord->pid, + psRecord->ui32Flags, + HAPFlagsToString(psRecord->ui32Flags) + ); + +} + +#endif + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + +static IMG_VOID* DecOffMemAllocRec_AnyVaCb(DEBUG_MEM_ALLOC_REC *psNode, va_list va) +{ + off_t *pOff = va_arg(va, off_t*); + if (--(*pOff)) + { + return IMG_NULL; + } + else + { + return psNode; + } +} + + + +static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off) +{ + DEBUG_MEM_ALLOC_REC *psRecord; + psRecord = (DEBUG_MEM_ALLOC_REC*) + List_DEBUG_MEM_ALLOC_REC_Any_va(g_MemoryRecords, + DecOffMemAllocRec_AnyVaCb, + &off); +#if defined(DEBUG_LINUX_XML_PROC_FILES) + if(!psRecord) + { + seq_printf( sfile, "</meminfo>\n"); + } +#endif + + return (void*)psRecord; +} + +static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) +{ + DEBUG_MEM_ALLOC_REC *psRecord; + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + psRecord = (DEBUG_MEM_ALLOC_REC*) + List_DEBUG_MEM_ALLOC_REC_Any_va(g_MemoryRecords, + DecOffMemAllocRec_AnyVaCb, + &off); + +#if defined(DEBUG_LINUX_XML_PROC_FILES) + if(!psRecord) + { + seq_printf( sfile, "</meminfo>\n"); + } +#endif + + return (void*)psRecord; +} + +static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) +{ + DEBUG_MEM_ALLOC_REC *psRecord = (DEBUG_MEM_ALLOC_REC*)el; + if(el == PVR_PROC_SEQ_START_TOKEN) + { +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes allocated via kmalloc", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated via kmalloc", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes allocated via vmalloc", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated via vmalloc", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes allocated via alloc_pages", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated via alloc_pages", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes allocated via ioremap", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated via ioremap", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes reserved for \"IO\" memory areas", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated for \"IO\" memory areas", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes allocated via kmem_cache_alloc", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); + seq_printf( sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes allocated via kmem_cache_alloc", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); + seq_printf( sfile, "\n"); + + seq_printf( sfile, "%-60s: %d bytes\n", + "The Current Water Mark for memory allocated from system RAM", + g_SysRAMWaterMark); + seq_printf( sfile, "%-60s: %d bytes\n", + "The Highest Water Mark for memory allocated from system RAM", + g_SysRAMHighWaterMark); + seq_printf( sfile, "%-60s: %d bytes\n", + "The Current Water Mark for memory allocated from IO memory", + g_IOMemWaterMark); + seq_printf( sfile, "%-60s: %d bytes\n", + "The Highest Water Mark for memory allocated from IO memory", + g_IOMemHighWaterMark); + + seq_printf( sfile, "\n"); + + seq_printf( sfile, "Details for all known allocations:\n" + "%-16s %-8s %-8s %-10s %-5s %-10s %s\n", + "Type", + "CpuVAddr", + "CpuPAddr", + "Bytes", + "PID", + "PrivateData", + "Filename:Line"); + +#else + + + seq_printf( sfile, "<meminfo>\n<meminfo_header>\n"); + seq_printf( sfile, + "<watermark key=\"mr0\" description=\"kmalloc_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); + seq_printf( sfile, + "<watermark key=\"mr1\" description=\"kmalloc_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); + seq_printf( sfile, + "<watermark key=\"mr2\" description=\"vmalloc_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); + seq_printf( sfile, + "<watermark key=\"mr3\" description=\"vmalloc_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); + seq_printf( sfile, + "<watermark key=\"mr4\" description=\"alloc_pages_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); + seq_printf( sfile, + "<watermark key=\"mr5\" description=\"alloc_pages_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); + seq_printf( sfile, + "<watermark key=\"mr6\" description=\"ioremap_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); + seq_printf( sfile, + "<watermark key=\"mr7\" description=\"ioremap_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); + seq_printf( sfile, + "<watermark key=\"mr8\" description=\"io_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); + seq_printf( sfile, + "<watermark key=\"mr9\" description=\"io_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); + seq_printf( sfile, + "<watermark key=\"mr10\" description=\"kmem_cache_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); + seq_printf( sfile, + "<watermark key=\"mr11\" description=\"kmem_cache_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); + seq_printf( sfile,"\n" ); + + seq_printf( sfile, + "<watermark key=\"mr14\" description=\"system_ram_current\" bytes=\"%d\"/>\n", + g_SysRAMWaterMark); + seq_printf( sfile, + "<watermark key=\"mr15\" description=\"system_ram_high\" bytes=\"%d\"/>\n", + g_SysRAMHighWaterMark); + seq_printf( sfile, + "<watermark key=\"mr16\" description=\"system_io_current\" bytes=\"%d\"/>\n", + g_IOMemWaterMark); + seq_printf( sfile, + "<watermark key=\"mr17\" description=\"system_io_high\" bytes=\"%d\"/>\n", + g_IOMemHighWaterMark); + + seq_printf( sfile, "</meminfo_header>\n"); + +#endif + return; + } + + if(psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) + { + seq_printf( sfile, +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", +#else + "<allocation>\n" + "\t<type>%s</type>\n" + "\t<cpu_virtual>%-8p</cpu_virtual>\n" + "\t<cpu_physical>%08x</cpu_physical>\n" + "\t<bytes>%d</bytes>\n" + "\t<pid>%d</pid>\n" + "\t<private>%s</private>\n" + "\t<filename>%s</filename>\n" + "\t<line>%d</line>\n" + "</allocation>\n", +#endif + DebugMemAllocRecordTypeToString(psRecord->eAllocType), + psRecord->pvCpuVAddr, + psRecord->ulCpuPAddr, + psRecord->ui32Bytes, + psRecord->pid, + "NULL", + psRecord->pszFileName, + psRecord->ui32Line); + } + else + { + seq_printf( sfile, +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", +#else + "<allocation>\n" + "\t<type>%s</type>\n" + "\t<cpu_virtual>%-8p</cpu_virtual>\n" + "\t<cpu_physical>%08x</cpu_physical>\n" + "\t<bytes>%d</bytes>\n" + "\t<pid>%d</pid>\n" + "\t<private>%s</private>\n" + "\t<filename>%s</filename>\n" + "\t<line>%d</line>\n" + "</allocation>\n", +#endif + DebugMemAllocRecordTypeToString(psRecord->eAllocType), + psRecord->pvCpuVAddr, + psRecord->ulCpuPAddr, + psRecord->ui32Bytes, + psRecord->pid, + KMemCacheNameWrapper(psRecord->pvPrivateData), + psRecord->pszFileName, + psRecord->ui32Line); + } +} + +#endif + + +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MMAP_AREAS) +const IMG_CHAR * +HAPFlagsToString(IMG_UINT32 ui32Flags) +{ + static IMG_CHAR szFlags[50]; + IMG_INT32 i32Pos = 0; + IMG_UINT32 ui32CacheTypeIndex, ui32MapTypeIndex; + IMG_CHAR *apszCacheTypes[] = { + "UNCACHED", + "CACHED", + "SMART", + "WRITECOMBINE", + "UNKNOWN" + }; + IMG_CHAR *apszMapType[] = { + "KERNEL_ONLY", + "SINGLE_PROCESS", + "MULTI_PROCESS", + "FROM_EXISTING_PROCESS", + "NO_CPU_VIRTUAL", + "UNKNOWN" + }; + + + if(ui32Flags & PVRSRV_HAP_UNCACHED){ + ui32CacheTypeIndex=0; + }else if(ui32Flags & PVRSRV_HAP_CACHED){ + ui32CacheTypeIndex=1; + }else if(ui32Flags & PVRSRV_HAP_SMART){ + ui32CacheTypeIndex=2; + }else if(ui32Flags & PVRSRV_HAP_WRITECOMBINE){ + ui32CacheTypeIndex=3; + }else{ + ui32CacheTypeIndex=4; + PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type (%u)", + __FUNCTION__, (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK))); + } + + + if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY){ + ui32MapTypeIndex = 0; + }else if(ui32Flags & PVRSRV_HAP_SINGLE_PROCESS){ + ui32MapTypeIndex = 1; + }else if(ui32Flags & PVRSRV_HAP_MULTI_PROCESS){ + ui32MapTypeIndex = 2; + }else if(ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS){ + ui32MapTypeIndex = 3; + }else if(ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL){ + ui32MapTypeIndex = 4; + }else{ + ui32MapTypeIndex = 5; + PVR_DPF((PVR_DBG_ERROR, "%s: unknown map type (%u)", + __FUNCTION__, (ui32Flags & PVRSRV_HAP_MAPTYPE_MASK))); + } + + i32Pos = sprintf(szFlags, "%s|", apszCacheTypes[ui32CacheTypeIndex]); + if (i32Pos <= 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: sprintf for cache type %u failed (%d)", + __FUNCTION__, ui32CacheTypeIndex, i32Pos)); + szFlags[0] = 0; + } + else + { + sprintf(szFlags + i32Pos, "%s", apszMapType[ui32MapTypeIndex]); + } + + return szFlags; +} +#endif + diff --git a/sgx/services4/srvkm/env/linux/mm.h b/sgx/services4/srvkm/env/linux/mm.h new file mode 100644 index 0000000..abd97b8 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mm.h @@ -0,0 +1,338 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __IMG_LINUX_MM_H__ +#define __IMG_LINUX_MM_H__ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/list.h> + +#include <asm/io.h> + +#define PHYS_TO_PFN(phys) ((phys) >> PAGE_SHIFT) +#define PFN_TO_PHYS(pfn) ((pfn) << PAGE_SHIFT) + +#define RANGE_TO_PAGES(range) (((range) + (PAGE_SIZE - 1)) >> PAGE_SHIFT) + +#define ADDR_TO_PAGE_OFFSET(addr) (((unsigned long)(addr)) & (PAGE_SIZE - 1)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)) +#define REMAP_PFN_RANGE(vma, addr, pfn, size, prot) remap_pfn_range(vma, addr, pfn, size, prot) +#else +#define REMAP_PFN_RANGE(vma, addr, pfn, size, prot) remap_page_range(vma, addr, PFN_TO_PHYS(pfn), size, prot) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)) +#define IO_REMAP_PFN_RANGE(vma, addr, pfn, size, prot) io_remap_pfn_range(vma, addr, pfn, size, prot) +#else +#define IO_REMAP_PFN_RANGE(vma, addr, pfn, size, prot) io_remap_page_range(vma, addr, PFN_TO_PHYS(pfn), size, prot) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) +#define VM_INSERT_PAGE(vma, addr, page) vm_insert_page(vma, addr, page) +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)) +#define VM_INSERT_PAGE(vma, addr, page) remap_pfn_range(vma, addr, page_to_pfn(page), PAGE_SIZE, vma->vm_page_prot); +#else +#define VM_INSERT_PAGE(vma, addr, page) remap_page_range(vma, addr, page_to_phys(page), PAGE_SIZE, vma->vm_page_prot); +#endif +#endif + +static inline IMG_UINT32 VMallocToPhys(IMG_VOID *pCpuVAddr) +{ + return (page_to_phys(vmalloc_to_page(pCpuVAddr)) + ADDR_TO_PAGE_OFFSET(pCpuVAddr)); + +} + +typedef enum { + LINUX_MEM_AREA_IOREMAP, + LINUX_MEM_AREA_EXTERNAL_KV, + LINUX_MEM_AREA_IO, + LINUX_MEM_AREA_VMALLOC, + LINUX_MEM_AREA_ALLOC_PAGES, + LINUX_MEM_AREA_SUB_ALLOC, + LINUX_MEM_AREA_TYPE_COUNT +}LINUX_MEM_AREA_TYPE; + +typedef struct _LinuxMemArea LinuxMemArea; + + +struct _LinuxMemArea { + LINUX_MEM_AREA_TYPE eAreaType; + union _uData + { + struct _sIORemap + { + + IMG_CPU_PHYADDR CPUPhysAddr; + IMG_VOID *pvIORemapCookie; + }sIORemap; + struct _sExternalKV + { + + IMG_BOOL bPhysContig; + union { + + IMG_SYS_PHYADDR SysPhysAddr; + IMG_SYS_PHYADDR *pSysPhysAddr; + } uPhysAddr; + IMG_VOID *pvExternalKV; + }sExternalKV; + struct _sIO + { + + IMG_CPU_PHYADDR CPUPhysAddr; + }sIO; + struct _sVmalloc + { + + IMG_VOID *pvVmallocAddress; + }sVmalloc; + struct _sPageList + { + + struct page **pvPageList; + IMG_HANDLE hBlockPageList; + }sPageList; + struct _sSubAlloc + { + + LinuxMemArea *psParentLinuxMemArea; + IMG_UINT32 ui32ByteOffset; + }sSubAlloc; + }uData; + + IMG_UINT32 ui32ByteSize; + + IMG_UINT32 ui32AreaFlags; + + IMG_BOOL bMMapRegistered; + + IMG_BOOL bNeedsCacheInvalidate; + + + struct list_head sMMapItem; + + + struct list_head sMMapOffsetStructList; + + IMG_HANDLE hSmartCache; +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)) +typedef kmem_cache_t LinuxKMemCache; +#else +typedef struct kmem_cache LinuxKMemCache; +#endif + + +PVRSRV_ERROR LinuxMMInit(IMG_VOID); + + +IMG_VOID LinuxMMCleanup(IMG_VOID); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define KMallocWrapper(ui32ByteSize, uFlags) _KMallocWrapper(ui32ByteSize, uFlags, __FILE__, __LINE__) +#else +#define KMallocWrapper(ui32ByteSize, uFlags) _KMallocWrapper(ui32ByteSize, uFlags, NULL, 0) +#endif +IMG_VOID *_KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *szFileName, IMG_UINT32 ui32Line); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define KFreeWrapper(pvCpuVAddr) _KFreeWrapper(pvCpuVAddr, __FILE__, __LINE__) +#else +#define KFreeWrapper(pvCpuVAddr) _KFreeWrapper(pvCpuVAddr, NULL, 0) +#endif +IMG_VOID _KFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VMallocWrapper(ui32Bytes, ui32AllocFlags) _VMallocWrapper(ui32Bytes, ui32AllocFlags, __FILE__, __LINE__) +#else +#define VMallocWrapper(ui32Bytes, ui32AllocFlags) _VMallocWrapper(ui32Bytes, ui32AllocFlags, NULL, 0) +#endif +IMG_VOID *_VMallocWrapper(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AllocFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VFreeWrapper(pvCpuVAddr) _VFreeWrapper(pvCpuVAddr, __FILE__, __LINE__) +#else +#define VFreeWrapper(pvCpuVAddr) _VFreeWrapper(pvCpuVAddr, NULL, 0) +#endif +IMG_VOID _VFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + + +LinuxMemArea *NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +IMG_VOID FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags) \ + _IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags, __FILE__, __LINE__) +#else +#define IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags) \ + _IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags, NULL, 0) +#endif +IMG_VOID *_IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line); + + +LinuxMemArea *NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +IMG_VOID FreeIORemapLinuxMemArea(LinuxMemArea *psLinuxMemArea); + +LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig, IMG_UINT32 ui32AreaFlags); + + +IMG_VOID FreeExternalKVLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define IOUnmapWrapper(pvIORemapCookie) \ + _IOUnmapWrapper(pvIORemapCookie, __FILE__, __LINE__) +#else +#define IOUnmapWrapper(pvIORemapCookie) \ + _IOUnmapWrapper(pvIORemapCookie, NULL, 0) +#endif +IMG_VOID _IOUnmapWrapper(IMG_VOID *pvIORemapCookie, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + + +struct page *LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset); + + +LinuxKMemCache *KMemCacheCreateWrapper(IMG_CHAR *pszName, size_t Size, size_t Align, IMG_UINT32 ui32Flags); + + +IMG_VOID KMemCacheDestroyWrapper(LinuxKMemCache *psCache); + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define KMemCacheAllocWrapper(psCache, Flags) _KMemCacheAllocWrapper(psCache, Flags, __FILE__, __LINE__) +#else +#define KMemCacheAllocWrapper(psCache, Flags) _KMemCacheAllocWrapper(psCache, Flags, NULL, 0) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)) +IMG_VOID *_KMemCacheAllocWrapper(LinuxKMemCache *psCache, gfp_t Flags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); +#else +IMG_VOID *_KMemCacheAllocWrapper(LinuxKMemCache *psCache, int Flags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define KMemCacheFreeWrapper(psCache, pvObject) _KMemCacheFreeWrapper(psCache, pvObject, __FILE__, __LINE__) +#else +#define KMemCacheFreeWrapper(psCache, pvObject) _KMemCacheFreeWrapper(psCache, pvObject, NULL, 0) +#endif +IMG_VOID _KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line); + + +const IMG_CHAR *KMemCacheNameWrapper(LinuxKMemCache *psCache); + + +LinuxMemArea *NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +IMG_VOID FreeIOLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +LinuxMemArea *NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +IMG_VOID FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +LinuxMemArea *NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, + IMG_UINT32 ui32ByteOffset, + IMG_UINT32 ui32Bytes); + + +IMG_VOID LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea); + + +#if defined(LINUX_MEM_AREAS_DEBUG) +IMG_VOID LinuxMemAreaRegister(LinuxMemArea *psLinuxMemArea); +#else +#define LinuxMemAreaRegister(X) +#endif + + +IMG_VOID *LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea); + + +IMG_CPU_PHYADDR LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset); + + +#define LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32ByteOffset) PHYS_TO_PFN(LinuxMemAreaToCpuPAddr(psLinuxMemArea, ui32ByteOffset).uiAddr) + +IMG_BOOL LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea); + +static inline LinuxMemArea * +LinuxMemAreaRoot(LinuxMemArea *psLinuxMemArea) +{ + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + return psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea; + } + else + { + return psLinuxMemArea; + } +} + + +static inline LINUX_MEM_AREA_TYPE +LinuxMemAreaRootType(LinuxMemArea *psLinuxMemArea) +{ + return LinuxMemAreaRoot(psLinuxMemArea)->eAreaType; +} + + +const IMG_CHAR *LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType); + + +#if defined(DEBUG) || defined(DEBUG_LINUX_MEM_AREAS) +const IMG_CHAR *HAPFlagsToString(IMG_UINT32 ui32Flags); +#endif + +#endif + diff --git a/sgx/services4/srvkm/env/linux/mmap.c b/sgx/services4/srvkm/env/linux/mmap.c new file mode 100644 index 0000000..b2794b7 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mmap.c @@ -0,0 +1,1509 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) +#include <linux/wrapper.h> +#endif +#include <linux/slab.h> +#include <asm/io.h> +#include <asm/page.h> +#include <asm/shmparam.h> +#include <asm/pgtable.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) +#include <linux/sched.h> +#include <asm/current.h> +#endif +#if defined(SUPPORT_DRI_DRM) +#include <drm/drmP.h> +#endif + +#include "img_defs.h" +#include "services.h" +#include "servicesint.h" +#include "pvrmmap.h" +#include "mutils.h" +#include "mmap.h" +#include "mm.h" +#include "pvr_debug.h" +#include "osfunc.h" +#include "proc.h" +#include "mutex.h" +#include "handle.h" +#include "perproc.h" +#include "env_perproc.h" +#include "bridged_support.h" +#if defined(SUPPORT_DRI_DRM) +#include "pvr_drm.h" +#endif + +#if !defined(PVR_SECURE_HANDLES) && !defined (SUPPORT_SID_INTERFACE) +#error "The mmap code requires PVR_SECURE_HANDLES" +#endif + +static PVRSRV_LINUX_MUTEX g_sMMapMutex; + +static LinuxKMemCache *g_psMemmapCache = NULL; +static LIST_HEAD(g_sMMapAreaList); +static LIST_HEAD(g_sMMapOffsetStructList); +#if defined(DEBUG_LINUX_MMAP_AREAS) +static IMG_UINT32 g_ui32RegisteredAreas = 0; +static IMG_UINT32 g_ui32TotalByteSize = 0; +#endif + + +#if defined(DEBUG_LINUX_MMAP_AREAS) +static struct proc_dir_entry *g_ProcMMap; +#endif + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +#define MMAP2_PGOFF_RESOLUTION (32-PAGE_SHIFT+12) +#define RESERVED_PGOFF_BITS 1 +#define MAX_MMAP_HANDLE ((1UL<<(MMAP2_PGOFF_RESOLUTION-RESERVED_PGOFF_BITS))-1) + +#define FIRST_PHYSICAL_PFN 0 +#define LAST_PHYSICAL_PFN (FIRST_PHYSICAL_PFN + MAX_MMAP_HANDLE) +#define FIRST_SPECIAL_PFN (LAST_PHYSICAL_PFN + 1) +#define LAST_SPECIAL_PFN (FIRST_SPECIAL_PFN + MAX_MMAP_HANDLE) + +#else + +#if PAGE_SHIFT != 12 +#error This build variant has not yet been made non-4KB page-size aware +#endif + +#if defined(PVR_MMAP_OFFSET_BASE) +#define FIRST_SPECIAL_PFN PVR_MMAP_OFFSET_BASE +#else +#define FIRST_SPECIAL_PFN 0x80000000UL +#endif + +#if defined(PVR_NUM_MMAP_HANDLES) +#define MAX_MMAP_HANDLE PVR_NUM_MMAP_HANDLES +#else +#define MAX_MMAP_HANDLE 0x7fffffffUL +#endif + +#endif + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static inline IMG_BOOL +PFNIsPhysical(IMG_UINT32 pfn) +{ + + return ( (pfn <= LAST_PHYSICAL_PFN)) ? IMG_TRUE : IMG_FALSE; +} + +static inline IMG_BOOL +PFNIsSpecial(IMG_UINT32 pfn) +{ + + return ((pfn >= FIRST_SPECIAL_PFN) ) ? IMG_TRUE : IMG_FALSE; +} +#endif + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static inline IMG_HANDLE +MMapOffsetToHandle(IMG_UINT32 pfn) +{ + if (PFNIsPhysical(pfn)) + { + PVR_ASSERT(PFNIsPhysical(pfn)); + return IMG_NULL; + } + return (IMG_HANDLE)(pfn - FIRST_SPECIAL_PFN); +} +#endif + +static inline IMG_UINT32 +#if defined (SUPPORT_SID_INTERFACE) +HandleToMMapOffset(IMG_SID hHandle) +#else +HandleToMMapOffset(IMG_HANDLE hHandle) +#endif +{ + IMG_UINT32 ulHandle = (IMG_UINT32)hHandle; + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (PFNIsSpecial(ulHandle)) + { + PVR_ASSERT(PFNIsSpecial(ulHandle)); + return 0; + } +#endif + return ulHandle + FIRST_SPECIAL_PFN; +} + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static inline IMG_BOOL +LinuxMemAreaUsesPhysicalMap(LinuxMemArea *psLinuxMemArea) +{ + return LinuxMemAreaPhysIsContig(psLinuxMemArea); +} +#endif + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static inline IMG_UINT32 +GetCurrentThreadID(IMG_VOID) +{ + + return (IMG_UINT32)current->pid; +} +#endif + +static PKV_OFFSET_STRUCT +CreateOffsetStruct(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize) +{ + PKV_OFFSET_STRUCT psOffsetStruct; +#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) + const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea)); +#endif + +#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) + PVR_DPF((PVR_DBG_MESSAGE, + "%s(%s, psLinuxMemArea: 0x%p, ui32AllocFlags: 0x%8x)", + __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags)); +#endif + + PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); + + PVR_ASSERT(psLinuxMemArea->bMMapRegistered); + + psOffsetStruct = KMemCacheAllocWrapper(g_psMemmapCache, GFP_KERNEL); + if(psOffsetStruct == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR,"PVRMMapRegisterArea: Couldn't alloc another mapping record from cache")); + return IMG_NULL; + } + + psOffsetStruct->ui32MMapOffset = ui32Offset; + + psOffsetStruct->psLinuxMemArea = psLinuxMemArea; + + psOffsetStruct->ui32RealByteSize = ui32RealByteSize; + + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + psOffsetStruct->ui32TID = GetCurrentThreadID(); +#endif + psOffsetStruct->ui32PID = OSGetCurrentProcessIDKM(); + +#if defined(DEBUG_LINUX_MMAP_AREAS) + + psOffsetStruct->pszName = pszName; +#endif + + list_add_tail(&psOffsetStruct->sAreaItem, &psLinuxMemArea->sMMapOffsetStructList); + + return psOffsetStruct; +} + + +static IMG_VOID +DestroyOffsetStruct(PKV_OFFSET_STRUCT psOffsetStruct) +{ +#ifdef DEBUG + IMG_CPU_PHYADDR CpuPAddr; + CpuPAddr = LinuxMemAreaToCpuPAddr(psOffsetStruct->psLinuxMemArea, 0); +#endif + + list_del(&psOffsetStruct->sAreaItem); + + if (psOffsetStruct->bOnMMapList) + { + list_del(&psOffsetStruct->sMMapItem); + } + +#ifdef DEBUG + PVR_DPF((PVR_DBG_MESSAGE, "%s: Table entry: " + "psLinuxMemArea=%p, CpuPAddr=0x%08X", __FUNCTION__, + psOffsetStruct->psLinuxMemArea, + CpuPAddr.uiAddr)); +#endif + + KMemCacheFreeWrapper(g_psMemmapCache, psOffsetStruct); +} + + +static inline IMG_VOID +DetermineUsersSizeAndByteOffset(LinuxMemArea *psLinuxMemArea, + IMG_UINT32 *pui32RealByteSize, + IMG_UINT32 *pui32ByteOffset) +{ + IMG_UINT32 ui32PageAlignmentOffset; + IMG_CPU_PHYADDR CpuPAddr; + + CpuPAddr = LinuxMemAreaToCpuPAddr(psLinuxMemArea, 0); + ui32PageAlignmentOffset = ADDR_TO_PAGE_OFFSET(CpuPAddr.uiAddr); + + *pui32ByteOffset = ui32PageAlignmentOffset; + + *pui32RealByteSize = PAGE_ALIGN(psLinuxMemArea->ui32ByteSize + ui32PageAlignmentOffset); +} + + +PVRSRV_ERROR +PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hMHandle, +#else + IMG_HANDLE hMHandle, +#endif + IMG_UINT32 *pui32MMapOffset, + IMG_UINT32 *pui32ByteOffset, + IMG_UINT32 *pui32RealByteSize, + IMG_UINT32 *pui32UserVAddr) +{ + LinuxMemArea *psLinuxMemArea; + PKV_OFFSET_STRUCT psOffsetStruct; + IMG_HANDLE hOSMemHandle; + PVRSRV_ERROR eError; + + LinuxLockMutex(&g_sMMapMutex); + + PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE); + + eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle); + if (eError != PVRSRV_OK) + { +#if defined (SUPPORT_SID_INTERFACE) + PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %x failed", __FUNCTION__, hMHandle)); +#else + PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle)); +#endif + + goto exit_unlock; + } + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + DetermineUsersSizeAndByteOffset(psLinuxMemArea, + pui32RealByteSize, + pui32ByteOffset); + + + list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) + { + if (psPerProc->ui32PID == psOffsetStruct->ui32PID) + { + + PVR_ASSERT(*pui32RealByteSize == psOffsetStruct->ui32RealByteSize); + + *pui32MMapOffset = psOffsetStruct->ui32MMapOffset; + *pui32UserVAddr = psOffsetStruct->ui32UserVAddr; + psOffsetStruct->ui32RefCount++; + + eError = PVRSRV_OK; + goto exit_unlock; + } + } + + + *pui32UserVAddr = 0; + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (LinuxMemAreaUsesPhysicalMap(psLinuxMemArea)) + { + *pui32MMapOffset = LinuxMemAreaToCpuPFN(psLinuxMemArea, 0); + PVR_ASSERT(PFNIsPhysical(*pui32MMapOffset)); + } + else +#endif + { + *pui32MMapOffset = HandleToMMapOffset(hMHandle); +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + PVR_ASSERT(PFNIsSpecial(*pui32MMapOffset)); +#endif + } + + psOffsetStruct = CreateOffsetStruct(psLinuxMemArea, *pui32MMapOffset, *pui32RealByteSize); + if (psOffsetStruct == IMG_NULL) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto exit_unlock; + } + + + list_add_tail(&psOffsetStruct->sMMapItem, &g_sMMapOffsetStructList); + + psOffsetStruct->bOnMMapList = IMG_TRUE; + + psOffsetStruct->ui32RefCount++; + + eError = PVRSRV_OK; + + + + + *pui32MMapOffset = *pui32MMapOffset << (PAGE_SHIFT - 12); + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + + return eError; +} + + +PVRSRV_ERROR +PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hMHandle, +#else + IMG_HANDLE hMHandle, +#endif + IMG_BOOL *pbMUnmap, + IMG_UINT32 *pui32RealByteSize, + IMG_UINT32 *pui32UserVAddr) +{ + LinuxMemArea *psLinuxMemArea; + PKV_OFFSET_STRUCT psOffsetStruct; + IMG_HANDLE hOSMemHandle; + PVRSRV_ERROR eError; + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + + LinuxLockMutex(&g_sMMapMutex); + + PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE); + + eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle); + if (eError != PVRSRV_OK) + { +#if defined (SUPPORT_SID_INTERFACE) + PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %x failed", __FUNCTION__, hMHandle)); +#else + PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle)); +#endif + + goto exit_unlock; + } + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + + list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) + { + if (psOffsetStruct->ui32PID == ui32PID) + { + if (psOffsetStruct->ui32RefCount == 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Attempt to release mmap data with zero reference count for offset struct 0x%p, memory area %p", __FUNCTION__, psOffsetStruct, psLinuxMemArea)); + eError = PVRSRV_ERROR_STILL_MAPPED; + goto exit_unlock; + } + + psOffsetStruct->ui32RefCount--; + + *pbMUnmap = (IMG_BOOL)((psOffsetStruct->ui32RefCount == 0) && (psOffsetStruct->ui32UserVAddr != 0)); + + *pui32UserVAddr = (*pbMUnmap) ? psOffsetStruct->ui32UserVAddr : 0; + *pui32RealByteSize = (*pbMUnmap) ? psOffsetStruct->ui32RealByteSize : 0; + + eError = PVRSRV_OK; + goto exit_unlock; + } + } + + +#if defined (SUPPORT_SID_INTERFACE) + PVR_DPF((PVR_DBG_ERROR, "%s: Mapping data not found for handle %x (memory area %p)", __FUNCTION__, hMHandle, psLinuxMemArea)); +#else + PVR_DPF((PVR_DBG_ERROR, "%s: Mapping data not found for handle %p (memory area %p)", __FUNCTION__, hMHandle, psLinuxMemArea)); +#endif + + eError = PVRSRV_ERROR_MAPPING_NOT_FOUND; + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + + return eError; +} + +static inline PKV_OFFSET_STRUCT +FindOffsetStructByOffset(IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize) +{ + PKV_OFFSET_STRUCT psOffsetStruct; +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + IMG_UINT32 ui32TID = GetCurrentThreadID(); +#endif + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + + list_for_each_entry(psOffsetStruct, &g_sMMapOffsetStructList, sMMapItem) + { + if (ui32Offset == psOffsetStruct->ui32MMapOffset && ui32RealByteSize == psOffsetStruct->ui32RealByteSize && psOffsetStruct->ui32PID == ui32PID) + { +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + + if (!PFNIsPhysical(ui32Offset) || psOffsetStruct->ui32TID == ui32TID) +#endif + { + return psOffsetStruct; + } + } + } + + return IMG_NULL; +} + + +static IMG_BOOL +DoMapToUser(LinuxMemArea *psLinuxMemArea, + struct vm_area_struct* ps_vma, + IMG_UINT32 ui32ByteOffset) +{ + IMG_UINT32 ui32ByteSize; + + if (psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + return DoMapToUser(LinuxMemAreaRoot(psLinuxMemArea), + ps_vma, + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + ui32ByteOffset); + } + + + ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start; + PVR_ASSERT(ADDR_TO_PAGE_OFFSET(ui32ByteSize) == 0); + +#if defined (__sparc__) + +#error "SPARC not supported" +#endif + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (PFNIsPhysical(ps_vma->vm_pgoff)) + { + IMG_INT result; + + PVR_ASSERT(LinuxMemAreaPhysIsContig(psLinuxMemArea)); + PVR_ASSERT(LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32ByteOffset) == ps_vma->vm_pgoff); + + result = IO_REMAP_PFN_RANGE(ps_vma, ps_vma->vm_start, ps_vma->vm_pgoff, ui32ByteSize, ps_vma->vm_page_prot); + + if(result == 0) + { + return IMG_TRUE; + } + + PVR_DPF((PVR_DBG_MESSAGE, "%s: Failed to map contiguous physical address range (%d), trying non-contiguous path", __FUNCTION__, result)); + } +#endif + + { + + IMG_UINT32 ulVMAPos; + IMG_UINT32 ui32ByteEnd = ui32ByteOffset + ui32ByteSize; + IMG_UINT32 ui32PA; +#if defined(PVR_MAKE_ALL_PFNS_SPECIAL) + IMG_BOOL bMixedMap = IMG_FALSE; +#endif + + for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) + { + IMG_UINT32 pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); + + if (!pfn_valid(pfn)) + { +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + PVR_DPF((PVR_DBG_ERROR,"%s: Error - PFN invalid: 0x%x", __FUNCTION__, pfn)); + return IMG_FALSE; +#else + bMixedMap = IMG_TRUE; +#endif + } + } + +#if defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (bMixedMap) + { + ps_vma->vm_flags |= VM_MIXEDMAP; + } +#endif + + ulVMAPos = ps_vma->vm_start; + for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) + { + IMG_UINT32 pfn; + IMG_INT result; + + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); + +#if defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (bMixedMap) + { + result = vm_insert_mixed(ps_vma, ulVMAPos, pfn); + if(result != 0) + { + PVR_DPF((PVR_DBG_ERROR,"%s: Error - vm_insert_mixed failed (%d)", __FUNCTION__, result)); + return IMG_FALSE; + } + } + else +#endif + { + struct page *psPage; + + PVR_ASSERT(pfn_valid(pfn)); + + psPage = pfn_to_page(pfn); + + result = VM_INSERT_PAGE(ps_vma, ulVMAPos, psPage); + if(result != 0) + { + PVR_DPF((PVR_DBG_ERROR,"%s: Error - VM_INSERT_PAGE failed (%d)", __FUNCTION__, result)); + return IMG_FALSE; + } + } + ulVMAPos += PAGE_SIZE; + } + } + + return IMG_TRUE; +} + + +static IMG_VOID +MMapVOpenNoLock(struct vm_area_struct* ps_vma) +{ + PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; + PVR_ASSERT(psOffsetStruct != IMG_NULL) + psOffsetStruct->ui32Mapped++; + PVR_ASSERT(!psOffsetStruct->bOnMMapList); + + if (psOffsetStruct->ui32Mapped > 1) + { + PVR_DPF((PVR_DBG_WARNING, "%s: Offset structure 0x%p is being shared across processes (psOffsetStruct->ui32Mapped: %u)", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32Mapped)); + PVR_ASSERT((ps_vma->vm_flags & VM_DONTCOPY) == 0); + } + +#if defined(DEBUG_LINUX_MMAP_AREAS) + + PVR_DPF((PVR_DBG_MESSAGE, + "%s: psLinuxMemArea 0x%p, KVAddress 0x%p MMapOffset %d, ui32Mapped %d", + __FUNCTION__, + psOffsetStruct->psLinuxMemArea, + LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea), + psOffsetStruct->ui32MMapOffset, + psOffsetStruct->ui32Mapped)); +#endif +} + + +static void +MMapVOpen(struct vm_area_struct* ps_vma) +{ + LinuxLockMutex(&g_sMMapMutex); + + MMapVOpenNoLock(ps_vma); + + LinuxUnLockMutex(&g_sMMapMutex); +} + + +static IMG_VOID +MMapVCloseNoLock(struct vm_area_struct* ps_vma) +{ + PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; + PVR_ASSERT(psOffsetStruct != IMG_NULL) + +#if defined(DEBUG_LINUX_MMAP_AREAS) + PVR_DPF((PVR_DBG_MESSAGE, + "%s: psLinuxMemArea %p, CpuVAddr %p ui32MMapOffset %d, ui32Mapped %d", + __FUNCTION__, + psOffsetStruct->psLinuxMemArea, + LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea), + psOffsetStruct->ui32MMapOffset, + psOffsetStruct->ui32Mapped)); +#endif + + PVR_ASSERT(!psOffsetStruct->bOnMMapList); + psOffsetStruct->ui32Mapped--; + if (psOffsetStruct->ui32Mapped == 0) + { + if (psOffsetStruct->ui32RefCount != 0) + { + PVR_DPF((PVR_DBG_MESSAGE, "%s: psOffsetStruct %p has non-zero reference count (ui32RefCount = %u). User mode address of start of mapping: 0x%x", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32RefCount, psOffsetStruct->ui32UserVAddr)); + } + + DestroyOffsetStruct(psOffsetStruct); + } + + ps_vma->vm_private_data = NULL; +} + +static void +MMapVClose(struct vm_area_struct* ps_vma) +{ + LinuxLockMutex(&g_sMMapMutex); + + MMapVCloseNoLock(ps_vma); + + LinuxUnLockMutex(&g_sMMapMutex); +} + + +static struct vm_operations_struct MMapIOOps = +{ + .open=MMapVOpen, + .close=MMapVClose +}; + +/*****************************************************************************/ +/* "Smart" cached buffer support.. + */ + +#include <linux/rmap.h> +#include <linux/pagemap.h> + +typedef struct { + struct mutex lock; /* mutex that protects the page list */ + struct list_head faulted; /* list of touched pages */ + int npages; /* number of pages in buffer */ + struct vm_area_struct *vma; /* vma of initial creator of buffer */ +} PVRMMapSmartCache; + +enum { + PG_touched = PG_private, + PG_written = PG_private_2 +}; + +static IMG_VOID PVRMMapUnmapInv(IMG_HANDLE hSmartCache, bool inv); + +#define DBG(fmt, ...) do {} while (0) +//#define DBG(fmt, ...) printk(KERN_INFO"[%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define VERB(fmt, ...) do {} while (0) +//#define VERB(fmt, ...) printk(KERN_INFO"[%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define ERR(fmt, ...) printk(KERN_ERR"ERR: [%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) + +static int +MMapVFault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + PKV_OFFSET_STRUCT psOffsetStruct = vma->vm_private_data; + LinuxMemArea *psLinuxMemArea = psOffsetStruct->psLinuxMemArea; + PVRMMapSmartCache *smart = psLinuxMemArea->hSmartCache; + unsigned long offset, pfn; + struct page *page; + pgoff_t pgoff; + int ret = VM_FAULT_NOPAGE; + + if (!smart) + { + ERR("uhh oh, I'm not smart..\n"); + return VM_FAULT_SIGBUS; + } + + /* We don't use vmf->pgoff since that has the fake offset */ + pgoff = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; + offset = pgoff << PAGE_SHIFT; + if (offset >= psOffsetStruct->psLinuxMemArea->ui32ByteSize) + { + ERR("%p: offset too big: %lu vs %u, %p", smart, offset, + psOffsetStruct->psLinuxMemArea->ui32ByteSize, + psOffsetStruct->psLinuxMemArea); + return VM_FAULT_SIGBUS; + } + + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, offset); + page = pfn_to_page(pfn); + if (!page) + { + ERR("%p: can't find page: %lu, %p", smart, offset, psLinuxMemArea); + return VM_FAULT_SIGBUS; + } + + + /* *** BEGIN CRITICAL SECTION ********************************************/ + mutex_lock(&smart->lock); + + /* if we already know of this page the we are done */ + if (test_and_set_bit(PG_touched, &page->flags)) + { + VERB("%p: (already touched) get_page(%p) (idx=%08lx, flg=%08x, cnt=%d)", + smart, page, page->index, vmf->flags, atomic_read(&page->_count)); + goto unlock; + } + + page->index = pgoff + vma->vm_pgoff; + + VERB("%p: get_page(%p) (idx=%08lx, flg=%08x, cnt=%d)", + smart, page, page->index, vmf->flags, atomic_read(&page->_count)); + + if (vma->vm_file) + { + page->mapping = vma->vm_file->f_mapping; + } + else + { + ERR("%p: no mapping available\n", smart); + } + + BUG_ON(!page->mapping); + + vmf->page = page; + + get_page(page); + ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); + if (ret) + { + ERR("%p: error inserting page: %d", smart, ret); + goto unlock; + } + ret = VM_FAULT_NOPAGE; + + /* Add the page to the list of pages that have been touched + */ + list_add_tail(&page->lru, &smart->faulted); + +unlock: + mutex_unlock(&smart->lock); + /* *** END CRITICAL SECTION **********************************************/ + + return ret; +} + +static int +MMapVMkWrite(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)vma->vm_private_data; + PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; + struct page *page = vmf->page; + + VERB("%p: page=%p", smart, page); + + /* *** BEGIN CRITICAL SECTION ********************************************/ + mutex_lock(&smart->lock); + + /* We want the page to remain locked from ->page_mkwrite until + * the PTE is marked dirty to avoid page_mkclean() being called + * before the PTE is updated, which would leave the page ignored. + * + * Do this by locking the page here and informing the caller + * about it with VM_FAULT_LOCKED. + */ + lock_page(page); + + set_bit(PG_written, &page->flags); + + mutex_unlock(&smart->lock); + /* *** END CRITICAL SECTION **********************************************/ + + return VM_FAULT_LOCKED; +} + +static void +MMapVClose2(struct vm_area_struct* vma) +{ + PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)vma->vm_private_data; + PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; + DBG("%p", smart); + PVRMMapUnmapInv(smart, false); + MMapVClose(vma); +} + +static struct vm_operations_struct MMapSmartOps = { + .open=MMapVOpen, + .close=MMapVClose2, + .fault=MMapVFault, + .page_mkwrite=MMapVMkWrite, +}; + +static int +MMapSetPageDirty(struct page *page) +{ + if (!PageDirty(page)) + SetPageDirty(page); + return 0; +} + +static const struct address_space_operations MMapSmartAOps = { + .set_page_dirty = MMapSetPageDirty, +}; + +/* prepare buffer transition CPU -> GPU */ +IMG_VOID +PVRMMapPrepareCpuToGpu(IMG_HANDLE hSmartCache) +{ +#if 0 + PVRMMapSmartCache *smart = hSmartCache; + struct page *page; + int cnt = 0; + + /* hopefully this is the common-path.. */ + if (list_empty(&smart->faulted)) + { + return; + } + + /* *** BEGIN CRITICAL SECTION ********************************************/ + mutex_lock(&smart->lock); + + list_for_each_entry(page, &smart->faulted, lru) { + if (test_and_clear_bit(PG_written, &page->flags)) + { + void *va = (void *)(smart->vma->vm_start + + ((page->index - smart->vma->vm_pgoff) << PAGE_SHIFT)); + unsigned long pa = page_to_phys(page); + + lock_page(page); + page_mkclean(page); + dmac_clean_range(va, va + PAGE_SIZE); + outer_clean_range(pa, pa + PAGE_SIZE); + unlock_page(page); + + cnt++; + } + } + + mutex_unlock(&smart->lock); + /* *** END CRITICAL SECTION **********************************************/ + + DBG("%p: cleaned %d (of %d)", smart, cnt, smart->npages); +#else + PVRMMapUnmapInv(hSmartCache, true); +#endif +} +/* prepare buffer transition GPU -> CPU */ +IMG_VOID +PVRMMapPrepareGpuToCpu(IMG_HANDLE hSmartCache) +{ + PVRMMapUnmapInv(hSmartCache, true); +} + +/* remove faulted pages from user's vm, and optionally invalidate.. */ +static IMG_VOID +PVRMMapUnmapInv(IMG_HANDLE hSmartCache, bool inv) +{ + PVRMMapSmartCache *smart = hSmartCache; + struct page *page, *next; + pgoff_t min = ULONG_MAX, max = 0; + struct address_space *mapping = NULL; + int cnt = 0; + + /* hopefully this is the common-path.. */ + if (list_empty(&smart->faulted)) + { + return; + } + + VERB("%p", smart); + + /* *** BEGIN CRITICAL SECTION ********************************************/ + mutex_lock(&smart->lock); + + list_for_each_entry(page, &smart->faulted, lru) { + + if (inv) + { + void *va = (void *)(smart->vma->vm_start + + ((page->index - smart->vma->vm_pgoff) << PAGE_SHIFT)); + +#if 0 + dmac_inv_range(va, va + PAGE_SIZE); +#else + dmac_flush_range(va, va + PAGE_SIZE); +#endif + } + + clear_bit(PG_touched, &page->flags); + clear_bit(PG_written, &page->flags); + + min = min(min, page->index); + max = max(max, page->index); + + mapping = page->mapping; + + cnt++; + } + + /* clear out the mapping that we setup.. do this before + * invalidating to avoid a window where the cache is + * clean, but access to it is not protected by a fault + */ + if (mapping) + { + VERB("unmap_mapping_range: max=%08lx, min=%08lx", max, min); + unmap_mapping_range(mapping, min << PAGE_SHIFT, + (max - min + 1) << PAGE_SHIFT, 1); + } + + list_for_each_entry_safe(page, next, &smart->faulted, lru) { + + if (inv) + { + unsigned long pa = page_to_phys(page); + +#if 0 + outer_inv_range(pa, pa + PAGE_SIZE); +#else + outer_flush_range(pa, pa + PAGE_SIZE); +#endif + } + + VERB("%p: put_page(%p) (idx=%08lx, cnt=%d)", + smart, page, page->index, atomic_read(&page->_count)); + + page->index = 0; + page->mapping = NULL; + + put_page(page); + + list_del(&page->lru); + } + + mutex_unlock(&smart->lock); + /* *** END CRITICAL SECTION **********************************************/ + + DBG("%p: put %d (of %d)", smart, cnt, smart->npages); +} + +/* setup smart cache buffer */ +IMG_HANDLE +PVRMMapAllocateSmart(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) +{ + PVRMMapSmartCache *smart = kzalloc(sizeof(*smart), GFP_KERNEL); + + DBG("%p", smart); + + mutex_init(&smart->lock); + INIT_LIST_HEAD(&smart->faulted); + + return smart; +} + +IMG_VOID +PVRMMapFreeSmart(IMG_HANDLE hSmartCache) +{ + PVRMMapSmartCache *smart = hSmartCache; + + DBG("%p", smart); + + PVRMMapUnmapInv(smart, false); + + mutex_destroy(&smart->lock); + + kfree(smart); +} +/*****************************************************************************/ + +int +PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) +{ + IMG_UINT32 ui32ByteSize; + PKV_OFFSET_STRUCT psOffsetStruct; + int iRetVal = 0; + + PVR_UNREFERENCED_PARAMETER(pFile); + + LinuxLockMutex(&g_sMMapMutex); + + ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start; + + PVR_DPF((PVR_DBG_MESSAGE, "%s: Received mmap(2) request with ui32MMapOffset 0x%08lx," + " and ui32ByteSize %d(0x%08x)", + __FUNCTION__, + ps_vma->vm_pgoff, + ui32ByteSize, ui32ByteSize)); + + psOffsetStruct = FindOffsetStructByOffset(ps_vma->vm_pgoff, ui32ByteSize); + if (psOffsetStruct == IMG_NULL) + { +#if defined(SUPPORT_DRI_DRM) + LinuxUnLockMutex(&g_sMMapMutex); + +#if !defined(SUPPORT_DRI_DRM_EXT) + + return drm_mmap(pFile, ps_vma); +#else + + return -ENOENT; +#endif +#else + PVR_UNREFERENCED_PARAMETER(pFile); + + PVR_DPF((PVR_DBG_ERROR, + "%s: Attempted to mmap unregistered area at vm_pgoff 0x%lx", + __FUNCTION__, ps_vma->vm_pgoff)); + iRetVal = -EINVAL; +#endif + goto unlock_and_return; + } + list_del(&psOffsetStruct->sMMapItem); + psOffsetStruct->bOnMMapList = IMG_FALSE; + + + if (((ps_vma->vm_flags & VM_WRITE) != 0) && + ((ps_vma->vm_flags & VM_SHARED) == 0)) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Cannot mmap non-shareable writable areas", __FUNCTION__)); + iRetVal = -EINVAL; + goto unlock_and_return; + } + + PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped psLinuxMemArea 0x%p\n", + __FUNCTION__, psOffsetStruct->psLinuxMemArea)); + + ps_vma->vm_flags |= VM_RESERVED; + ps_vma->vm_flags |= VM_IO; + + + ps_vma->vm_flags |= VM_DONTEXPAND; + + + ps_vma->vm_flags |= VM_DONTCOPY; + + ps_vma->vm_private_data = (void *)psOffsetStruct; + + switch(psOffsetStruct->psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_CACHETYPE_MASK) + { + case PVRSRV_HAP_CACHED: + case PVRSRV_HAP_SMART: + break; + case PVRSRV_HAP_WRITECOMBINE: + ps_vma->vm_page_prot = PGPROT_WC(ps_vma->vm_page_prot); + break; + case PVRSRV_HAP_UNCACHED: + ps_vma->vm_page_prot = PGPROT_UC(ps_vma->vm_page_prot); + break; + default: + PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type", __FUNCTION__)); + iRetVal = -EINVAL; + goto unlock_and_return; + } + + if (psOffsetStruct->psLinuxMemArea->hSmartCache) + { + PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; + DBG("using smart cache, smart=%p, psLinuxMemArea=%p (%d, %d)", + psOffsetStruct->psLinuxMemArea->hSmartCache, + psOffsetStruct->psLinuxMemArea, + psOffsetStruct->ui32RealByteSize, + psOffsetStruct->psLinuxMemArea->ui32ByteSize); + smart->npages = (psOffsetStruct->ui32RealByteSize + PAGE_SIZE - 1) / PAGE_SIZE; + /* abuse pgoff a bit less.. in unmap_mapping_range() it is assumed + * that the offset is something sane, and I think it probably + * shouldn't intersect with other page->index's.. otherwise I + * suspect the prio_tree stuff won't work out.. + */ + ps_vma->vm_pgoff = ps_vma->vm_start >> PAGE_SHIFT; + smart->vma = ps_vma; + ps_vma->vm_ops = &MMapSmartOps; + pFile->f_mapping->a_ops = &MMapSmartAOps; + + ps_vma->vm_flags |= VM_MIXEDMAP; + } + else + { + ps_vma->vm_ops = &MMapIOOps; + if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0)) + { + iRetVal = -EAGAIN; + goto unlock_and_return; + } + } + + PVR_ASSERT(psOffsetStruct->ui32UserVAddr == 0); + + psOffsetStruct->ui32UserVAddr = ps_vma->vm_start; + + + if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate) + { + IMG_UINT32 ui32RealByteSize, ui32ByteOffset; + IMG_VOID *pvBase; + + DetermineUsersSizeAndByteOffset(psOffsetStruct->psLinuxMemArea, + &ui32RealByteSize, + &ui32ByteOffset); + + ui32RealByteSize = psOffsetStruct->psLinuxMemArea->ui32ByteSize; + pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset; + + OSInvalidateCPUCacheRangeKM(psOffsetStruct->psLinuxMemArea, + pvBase, ui32RealByteSize); + psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE; + } + + + MMapVOpenNoLock(ps_vma); + + PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped area at offset 0x%08lx\n", + __FUNCTION__, ps_vma->vm_pgoff)); + +unlock_and_return: + if (iRetVal != 0 && psOffsetStruct != IMG_NULL) + { + DestroyOffsetStruct(psOffsetStruct); + } + + LinuxUnLockMutex(&g_sMMapMutex); + + return iRetVal; +} + + +#if defined(DEBUG_LINUX_MMAP_AREAS) + +static void ProcSeqStartstopMMapRegistations(struct seq_file *sfile,IMG_BOOL start) +{ + if(start) + { + LinuxLockMutex(&g_sMMapMutex); + } + else + { + LinuxUnLockMutex(&g_sMMapMutex); + } +} + + +static void* ProcSeqOff2ElementMMapRegistrations(struct seq_file *sfile, loff_t off) +{ + LinuxMemArea *psLinuxMemArea; + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + list_for_each_entry(psLinuxMemArea, &g_sMMapAreaList, sMMapItem) + { + PKV_OFFSET_STRUCT psOffsetStruct; + + list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) + { + off--; + if (off == 0) + { + PVR_ASSERT(psOffsetStruct->psLinuxMemArea == psLinuxMemArea); + return (void*)psOffsetStruct; + } + } + } + return (void*)0; +} + +static void* ProcSeqNextMMapRegistrations(struct seq_file *sfile,void* el,loff_t off) +{ + return ProcSeqOff2ElementMMapRegistrations(sfile,off); +} + + +static void ProcSeqShowMMapRegistrations(struct seq_file *sfile, void *el) +{ + KV_OFFSET_STRUCT *psOffsetStruct = (KV_OFFSET_STRUCT*)el; + LinuxMemArea *psLinuxMemArea; + IMG_UINT32 ui32RealByteSize; + IMG_UINT32 ui32ByteOffset; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf( sfile, +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + "Allocations registered for mmap: %u\n" + "In total these areas correspond to %u bytes\n" + "psLinuxMemArea " + "UserVAddr " + "KernelVAddr " + "CpuPAddr " + "MMapOffset " + "ByteLength " + "LinuxMemType " + "Pid Name Flags\n", +#else + "<mmap_header>\n" + "\t<count>%u</count>\n" + "\t<bytes>%u</bytes>\n" + "</mmap_header>\n", +#endif + g_ui32RegisteredAreas, + g_ui32TotalByteSize + ); + return; + } + + psLinuxMemArea = psOffsetStruct->psLinuxMemArea; + + DetermineUsersSizeAndByteOffset(psLinuxMemArea, + &ui32RealByteSize, + &ui32ByteOffset); + + seq_printf( sfile, +#if !defined(DEBUG_LINUX_XML_PROC_FILES) + "%-8p %08x %-8p %08x %08x %-8d %-24s %-5u %-8s %08x(%s)\n", +#else + "<mmap_record>\n" + "\t<pointer>%-8p</pointer>\n" + "\t<user_virtual>%-8x</user_virtual>\n" + "\t<kernel_virtual>%-8p</kernel_virtual>\n" + "\t<cpu_physical>%08x</cpu_physical>\n" + "\t<mmap_offset>%08x</mmap_offset>\n" + "\t<bytes>%-8d</bytes>\n" + "\t<linux_mem_area_type>%-24s</linux_mem_area_type>\n" + "\t<pid>%-5u</pid>\n" + "\t<name>%-8s</name>\n" + "\t<flags>%08x</flags>\n" + "\t<flags_string>%s</flags_string>\n" + "</mmap_record>\n", +#endif + psLinuxMemArea, + psOffsetStruct->ui32UserVAddr + ui32ByteOffset, + LinuxMemAreaToCpuVAddr(psLinuxMemArea), + LinuxMemAreaToCpuPAddr(psLinuxMemArea,0).uiAddr, + psOffsetStruct->ui32MMapOffset, + psLinuxMemArea->ui32ByteSize, + LinuxMemAreaTypeToString(psLinuxMemArea->eAreaType), + psOffsetStruct->ui32PID, + psOffsetStruct->pszName, + psLinuxMemArea->ui32AreaFlags, + HAPFlagsToString(psLinuxMemArea->ui32AreaFlags)); +} + +#endif + + +PVRSRV_ERROR +PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea) +{ + PVRSRV_ERROR eError; +#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) + const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea)); +#endif + + LinuxLockMutex(&g_sMMapMutex); + +#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) + PVR_DPF((PVR_DBG_MESSAGE, + "%s(%s, psLinuxMemArea 0x%p, ui32AllocFlags 0x%8x)", + __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags)); +#endif + + PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); + + + if(psLinuxMemArea->bMMapRegistered) + { + PVR_DPF((PVR_DBG_ERROR, "%s: psLinuxMemArea 0x%p is already registered", + __FUNCTION__, psLinuxMemArea)); + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto exit_unlock; + } + + list_add_tail(&psLinuxMemArea->sMMapItem, &g_sMMapAreaList); + + psLinuxMemArea->bMMapRegistered = IMG_TRUE; + +#if defined(DEBUG_LINUX_MMAP_AREAS) + g_ui32RegisteredAreas++; + + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + { + g_ui32TotalByteSize += psLinuxMemArea->ui32ByteSize; + } +#endif + + eError = PVRSRV_OK; + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + + return eError; +} + + +PVRSRV_ERROR +PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea) +{ + PVRSRV_ERROR eError; + PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct; + + LinuxLockMutex(&g_sMMapMutex); + + PVR_ASSERT(psLinuxMemArea->bMMapRegistered); + + list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) + { + if (psOffsetStruct->ui32Mapped != 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: psOffsetStruct 0x%p for memory area 0x0x%p is still mapped; psOffsetStruct->ui32Mapped %u", __FUNCTION__, psOffsetStruct, psLinuxMemArea, psOffsetStruct->ui32Mapped)); + eError = PVRSRV_ERROR_STILL_MAPPED; + goto exit_unlock; + } + else + { + + PVR_DPF((PVR_DBG_WARNING, "%s: psOffsetStruct 0x%p was never mapped", __FUNCTION__, psOffsetStruct)); + } + + PVR_ASSERT((psOffsetStruct->ui32Mapped == 0) && psOffsetStruct->bOnMMapList); + + DestroyOffsetStruct(psOffsetStruct); + } + + list_del(&psLinuxMemArea->sMMapItem); + + psLinuxMemArea->bMMapRegistered = IMG_FALSE; + +#if defined(DEBUG_LINUX_MMAP_AREAS) + g_ui32RegisteredAreas--; + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + { + g_ui32TotalByteSize -= psLinuxMemArea->ui32ByteSize; + } +#endif + + eError = PVRSRV_OK; + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + return eError; +} + + +PVRSRV_ERROR +LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) +{ + PVR_UNREFERENCED_PARAMETER(psEnvPerProc); + + return PVRSRV_OK; +} + +IMG_VOID +LinuxMMapPerProcessDisconnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) +{ + PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct; + IMG_BOOL bWarn = IMG_FALSE; + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + + PVR_UNREFERENCED_PARAMETER(psEnvPerProc); + + LinuxLockMutex(&g_sMMapMutex); + + list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &g_sMMapOffsetStructList, sMMapItem) + { + if (psOffsetStruct->ui32PID == ui32PID) + { + if (!bWarn) + { + PVR_DPF((PVR_DBG_WARNING, "%s: process has unmapped offset structures. Removing them", __FUNCTION__)); + bWarn = IMG_TRUE; + } + PVR_ASSERT(psOffsetStruct->ui32Mapped == 0); + PVR_ASSERT(psOffsetStruct->bOnMMapList); + + DestroyOffsetStruct(psOffsetStruct); + } + } + + LinuxUnLockMutex(&g_sMMapMutex); +} + + +PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase) +{ + PVRSRV_ERROR eError; + + eError = PVRSRVSetMaxHandle(psHandleBase, MAX_MMAP_HANDLE); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to set handle limit (%d)", __FUNCTION__, eError)); + return eError; + } + + return eError; +} + + +IMG_VOID +PVRMMapInit(IMG_VOID) +{ + LinuxInitMutex(&g_sMMapMutex); + + g_psMemmapCache = KMemCacheCreateWrapper("img-mmap", sizeof(KV_OFFSET_STRUCT), 0, 0); + if (!g_psMemmapCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); + goto error; + } + +#if defined(DEBUG_LINUX_MMAP_AREAS) + g_ProcMMap = CreateProcReadEntrySeq("mmap", NULL, + ProcSeqNextMMapRegistrations, + ProcSeqShowMMapRegistrations, + ProcSeqOff2ElementMMapRegistrations, + ProcSeqStartstopMMapRegistations + ); +#endif + return; + +error: + PVRMMapCleanup(); + return; +} + + +IMG_VOID +PVRMMapCleanup(IMG_VOID) +{ + PVRSRV_ERROR eError; + + if (!list_empty(&g_sMMapAreaList)) + { + LinuxMemArea *psLinuxMemArea, *psTmpMemArea; + + PVR_DPF((PVR_DBG_ERROR, "%s: Memory areas are still registered with MMap", __FUNCTION__)); + + PVR_TRACE(("%s: Unregistering memory areas", __FUNCTION__)); + list_for_each_entry_safe(psLinuxMemArea, psTmpMemArea, &g_sMMapAreaList, sMMapItem) + { + eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: PVRMMapRemoveRegisteredArea failed (%d)", __FUNCTION__, eError)); + } + PVR_ASSERT(eError == PVRSRV_OK); + + LinuxMemAreaDeepFree(psLinuxMemArea); + } + } + PVR_ASSERT(list_empty((&g_sMMapAreaList))); + +#if defined(DEBUG_LINUX_MMAP_AREAS) + RemoveProcEntrySeq(g_ProcMMap); +#endif + + if(g_psMemmapCache) + { + KMemCacheDestroyWrapper(g_psMemmapCache); + g_psMemmapCache = NULL; + } +} diff --git a/sgx/services4/srvkm/env/linux/mmap.h b/sgx/services4/srvkm/env/linux/mmap.h new file mode 100644 index 0000000..07d521b --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mmap.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#if !defined(__MMAP_H__) +#define __MMAP_H__ + +#include <linux/mm.h> +#include <linux/list.h> + +#if defined(VM_MIXEDMAP) +#define PVR_MAKE_ALL_PFNS_SPECIAL +#endif + +#include "perproc.h" +#include "mm.h" + +typedef struct KV_OFFSET_STRUCT_TAG +{ + + IMG_UINT32 ui32Mapped; + + + IMG_UINT32 ui32MMapOffset; + + IMG_UINT32 ui32RealByteSize; + + + LinuxMemArea *psLinuxMemArea; + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + + IMG_UINT32 ui32TID; +#endif + + + IMG_UINT32 ui32PID; + + + IMG_BOOL bOnMMapList; + + + IMG_UINT32 ui32RefCount; + + + IMG_UINT32 ui32UserVAddr; + + +#if defined(DEBUG_LINUX_MMAP_AREAS) + const IMG_CHAR *pszName; +#endif + + + struct list_head sMMapItem; + + + struct list_head sAreaItem; +}KV_OFFSET_STRUCT, *PKV_OFFSET_STRUCT; + + + +IMG_VOID PVRMMapInit(IMG_VOID); + + +IMG_VOID PVRMMapCleanup(IMG_VOID); + + +PVRSRV_ERROR PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea); + + +PVRSRV_ERROR PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea); + + +PVRSRV_ERROR PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hMHandle, +#else + IMG_HANDLE hMHandle, +#endif + IMG_UINT32 *pui32MMapOffset, + IMG_UINT32 *pui32ByteOffset, + IMG_UINT32 *pui32RealByteSize, + IMG_UINT32 *pui32UserVAddr); + +PVRSRV_ERROR +PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hMHandle, +#else + IMG_HANDLE hMHandle, +#endif + IMG_BOOL *pbMUnmap, + IMG_UINT32 *pui32RealByteSize, + IMG_UINT32 *pui32UserVAddr); + +int PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma); + +IMG_VOID PVRMMapPrepareCpuToGpu(IMG_HANDLE hSmartCache); +IMG_VOID PVRMMapPrepareGpuToCpu(IMG_HANDLE hSmartCache); +IMG_HANDLE PVRMMapAllocateSmart(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo); +IMG_VOID PVRMMapFreeSmart(IMG_HANDLE hSmartCache); + +#endif + diff --git a/sgx/services4/srvkm/env/linux/module.c b/sgx/services4/srvkm/env/linux/module.c new file mode 100755 index 0000000..596fe98 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/module.c @@ -0,0 +1,785 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#if defined(SUPPORT_DRI_DRM) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +#define PVR_MOD_STATIC +#else + #if defined(LDM_PLATFORM) + #define PVR_LDM_PLATFORM_MODULE + #define PVR_LDM_MODULE + #else + #if defined(LDM_PCI) + #define PVR_LDM_PCI_MODULE + #define PVR_LDM_MODULE + #endif + #endif +#if defined(SUPPORT_DRI_DRM_EXTERNAL) +#define PVR_MOD_STATIC +#else +#define PVR_MOD_STATIC static +#endif +#endif + +#if defined(PVR_LDM_PLATFORM_PRE_REGISTERED) +#if !defined(NO_HARDWARE) +#define PVR_USE_PRE_REGISTERED_PLATFORM_DEV +#endif +#endif + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> + +#if defined(SUPPORT_DRI_DRM) +#include <drm/drmP.h> +#if defined(PVR_SECURE_DRM_AUTH_EXPORT) +#include "env_perproc.h" +#endif +#endif + +#if defined(PVR_LDM_PLATFORM_MODULE) +#include <linux/platform_device.h> +#endif + +#if defined(PVR_LDM_PCI_MODULE) +#include <linux/pci.h> +#endif + +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) +#include <asm/uaccess.h> +#endif + +#include "img_defs.h" +#include "services.h" +#include "kerneldisplay.h" +#include "kernelbuffer.h" +#include "syscommon.h" +#include "pvrmmap.h" +#include "mutils.h" +#include "mm.h" +#include "mmap.h" +#include "mutex.h" +#include "pvr_debug.h" +#include "srvkm.h" +#include "perproc.h" +#include "handle.h" +#include "pvr_bridge_km.h" +#include "proc.h" +#include "pvrmodule.h" +#include "private_data.h" +#include "lock.h" +#include "linkage.h" + +#if defined(SUPPORT_DRI_DRM) +#include "pvr_drm.h" +#endif +#if defined(SUPPORT_DRI_DRM_EXTERNAL) +#define DRVNAME PVRSRV_MODNAME +#define DEVNAME PVRSRV_MODNAME +#else +#define DRVNAME PVRSRV_MODNAME +#define DEVNAME PVRSRV_MODNAME +MODULE_SUPPORTED_DEVICE(DEVNAME); +#endif + +#if defined(SUPPORT_DRI_DRM) +#define PRIVATE_DATA(pFile) ((pFile)->driver_priv) +#else +#define PRIVATE_DATA(pFile) ((pFile)->private_data) +#endif + +#if defined(PVRSRV_NEED_PVR_DPF) +#include <linux/moduleparam.h> +extern IMG_UINT32 gPVRDebugLevel; +module_param(gPVRDebugLevel, uint, 0644); +MODULE_PARM_DESC(gPVRDebugLevel, "Sets the level of debug output (default 0x7)"); +#endif + + +EXPORT_SYMBOL(PVRGetDisplayClassJTable); +EXPORT_SYMBOL(PVRGetBufferClassJTable); + +#if defined(PVR_LDM_MODULE) +static struct class *psPvrClass; +#endif + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) || !defined(SUPPORT_DRI_DRM) +static int AssignedMajorNumber; +#endif + +#if !defined(SUPPORT_DRI_DRM) +static int PVRSRVOpen(struct inode* pInode, struct file* pFile); +static int PVRSRVRelease(struct inode* pInode, struct file* pFile); + +static struct file_operations pvrsrv_fops = +{ + .owner=THIS_MODULE, + .unlocked_ioctl = PVRSRV_BridgeDispatchKM, + .open=PVRSRVOpen, + .release=PVRSRVRelease, + .mmap=PVRMMap, +}; +#endif + +PVRSRV_LINUX_MUTEX gPVRSRVLock; + +IMG_UINT32 gui32ReleasePID; + +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) +static IMG_UINT32 gPVRPowerLevel; +#endif + +#if defined(PVR_LDM_MODULE) + +#if defined(PVR_LDM_PLATFORM_MODULE) +#define LDM_DEV struct platform_device +#define LDM_DRV struct platform_driver +#endif + +#if defined(PVR_LDM_PCI_MODULE) +#define LDM_DEV struct pci_dev +#define LDM_DRV struct pci_driver +#endif +#if defined(PVR_LDM_PLATFORM_MODULE) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +static int PVRSRVDriverRemove(LDM_DEV *device); +static int PVRSRVDriverProbe(LDM_DEV *device); +#endif +#if defined(PVR_LDM_PCI_MODULE) +static void PVRSRVDriverRemove(LDM_DEV *device); +static int PVRSRVDriverProbe(LDM_DEV *device, const struct pci_device_id *id); +#endif + +#if defined(PVR_LDM_PCI_MODULE) +struct pci_device_id powervr_id_table[] __devinitdata = { + {PCI_DEVICE(SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV_DEVICE_ID)}, +#if defined (SYS_SGX_DEV1_DEVICE_ID) + {PCI_DEVICE(SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV1_DEVICE_ID)}, +#endif + {0} +}; + +MODULE_DEVICE_TABLE(pci, powervr_id_table); +#endif + +#if defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) +static struct platform_device_id powervr_id_table[] __devinitdata = { + {SYS_SGX_DEV_NAME, 0}, + {} +}; +#endif + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) || !defined(SUPPORT_DRI_DRM) +static LDM_DRV powervr_driver = { +#if defined(PVR_LDM_PLATFORM_MODULE) + .driver = { + .name = DRVNAME, + }, +#endif +#if defined(PVR_LDM_PCI_MODULE) + .name = DRVNAME, +#endif +#if defined(PVR_LDM_PCI_MODULE) || defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) + .id_table = powervr_id_table, +#endif + .probe = PVRSRVDriverProbe, +#if defined(PVR_LDM_PLATFORM_MODULE) + .remove = PVRSRVDriverRemove, +#endif +#if defined(PVR_LDM_PCI_MODULE) + .remove = __devexit_p(PVRSRVDriverRemove), +#endif + .suspend = PVRSRVDriverSuspend, + .resume = PVRSRVDriverResume, + .shutdown = PVRSRVDriverShutdown, +}; +#endif + +LDM_DEV *gpsPVRLDMDev; + +#if defined(MODULE) && defined(PVR_LDM_PLATFORM_MODULE) && \ + !defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) +static void PVRSRVDeviceRelease(struct device unref__ *pDevice) +{ +} + +static struct platform_device powervr_device = { + .name = DEVNAME, + .id = -1, + .dev = { + .release = PVRSRVDeviceRelease + } +}; +#endif + +#if defined(PVR_LDM_PLATFORM_MODULE) +PVR_MOD_STATIC int PVRSRVDriverProbe(LDM_DEV *pDevice) +#endif +#if defined(PVR_LDM_PCI_MODULE) +static int __devinit PVRSRVDriverProbe(LDM_DEV *pDevice, const struct pci_device_id *id) +#endif +{ + SYS_DATA *psSysData; + + PVR_TRACE(("PVRSRVDriverProbe(pDevice=%p)", pDevice)); + +#if 0 + + if (PerDeviceSysInitialise((IMG_PVOID)pDevice) != PVRSRV_OK) + { + return -EINVAL; + } +#endif + +#if defined(PVR_LDM_PLATFORM_MODULE) + if (!pDevice->dev.platform_data) + { + PVR_TRACE(("No SGX platform device data.")); + } +#endif + + psSysData = SysAcquireDataNoCheck(); + if ( psSysData == IMG_NULL) + { + gpsPVRLDMDev = pDevice; + + if (SysInitialise() != PVRSRV_OK) + { + return -ENODEV; + } + } + + return 0; +} + + +#if defined (PVR_LDM_PLATFORM_MODULE) +PVR_MOD_STATIC int PVRSRVDriverRemove(LDM_DEV *pDevice) +#endif +#if defined(PVR_LDM_PCI_MODULE) +static void __devexit PVRSRVDriverRemove(LDM_DEV *pDevice) +#endif +{ + SYS_DATA *psSysData; + + PVR_TRACE(("PVRSRVDriverRemove(pDevice=%p)", pDevice)); + + SysAcquireData(&psSysData); + +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) + if (gPVRPowerLevel != 0) + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) == PVRSRV_OK) + { + gPVRPowerLevel = 0; + } + } +#endif + (void) SysDeinitialise(psSysData); + + gpsPVRLDMDev = IMG_NULL; + +#if 0 + if (PerDeviceSysDeInitialise((IMG_PVOID)pDevice) != PVRSRV_OK) + { + return -EINVAL; + } +#endif + +#if defined (PVR_LDM_PLATFORM_MODULE) + return 0; +#endif +#if defined (PVR_LDM_PCI_MODULE) + return; +#endif +} +#endif + + +#if defined(PVR_LDM_MODULE) || defined(PVR_DRI_DRM_PLATFORM_DEV) +PVR_MOD_STATIC void PVRSRVDriverShutdown(LDM_DEV *pDevice) +{ + PVR_TRACE(("PVRSRVDriverShutdown(pDevice=%p)", pDevice)); + + (void) PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3); +} + +#endif + + +#if defined(PVR_LDM_MODULE) || defined(SUPPORT_DRI_DRM) +#if defined(SUPPORT_DRI_DRM) && !defined(PVR_DRI_DRM_PLATFORM_DEV) +int PVRSRVDriverSuspend(struct drm_device *pDevice, pm_message_t state) +#else +PVR_MOD_STATIC int PVRSRVDriverSuspend(LDM_DEV *pDevice, pm_message_t state) +#endif +{ +#if !(defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM)) + PVR_TRACE(( "PVRSRVDriverSuspend(pDevice=%p)", pDevice)); + + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3) != PVRSRV_OK) + { + return -EINVAL; + } +#endif + return 0; +} + + +#if defined(SUPPORT_DRI_DRM) && !defined(PVR_DRI_DRM_PLATFORM_DEV) +int PVRSRVDriverResume(struct drm_device *pDevice) +#else +PVR_MOD_STATIC int PVRSRVDriverResume(LDM_DEV *pDevice) +#endif +{ +#if !(defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM)) + PVR_TRACE(("PVRSRVDriverResume(pDevice=%p)", pDevice)); + + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) != PVRSRV_OK) + { + return -EINVAL; + } +#endif + return 0; +} +#endif + + +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM) +IMG_INT PVRProcSetPowerLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT32 count, IMG_VOID *data) +{ + IMG_CHAR data_buffer[2]; + IMG_UINT32 PVRPowerLevel; + + if (count != sizeof(data_buffer)) + { + return -EINVAL; + } + else + { + if (copy_from_user(data_buffer, buffer, count)) + return -EINVAL; + if (data_buffer[count - 1] != '\n') + return -EINVAL; + PVRPowerLevel = data_buffer[0] - '0'; + if (PVRPowerLevel != gPVRPowerLevel) + { + if (PVRPowerLevel != 0) + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3) != PVRSRV_OK) + { + return -EINVAL; + } + } + else + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) != PVRSRV_OK) + { + return -EINVAL; + } + } + + gPVRPowerLevel = PVRPowerLevel; + } + } + return (count); +} + +void ProcSeqShowPowerLevel(struct seq_file *sfile,void* el) +{ + seq_printf(sfile, "%lu\n", gPVRPowerLevel); +} + +#endif + +#if defined(SUPPORT_DRI_DRM) +int PVRSRVOpen(struct drm_device unref__ *dev, struct drm_file *pFile) +#else +static int PVRSRVOpen(struct inode unref__ * pInode, struct file *pFile) +#endif +{ + PVRSRV_FILE_PRIVATE_DATA *psPrivateData; + IMG_HANDLE hBlockAlloc; + int iRet = -ENOMEM; + PVRSRV_ERROR eError; + IMG_UINT32 ui32PID; +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc; +#endif + + LinuxLockMutex(&gPVRSRVLock); + + ui32PID = OSGetCurrentProcessIDKM(); + + if (PVRSRVProcessConnect(ui32PID, 0) != PVRSRV_OK) + goto err_unlock; + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + psEnvPerProc = PVRSRVPerProcessPrivateData(ui32PID); + if (psEnvPerProc == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: No per-process private data", __FUNCTION__)); + goto err_unlock; + } +#endif + + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_FILE_PRIVATE_DATA), + (IMG_PVOID *)&psPrivateData, + &hBlockAlloc, + "File Private Data"); + + if(eError != PVRSRV_OK) + goto err_unlock; + +#if defined (SUPPORT_SID_INTERFACE) + psPrivateData->hKernelMemInfo = 0; +#else + psPrivateData->hKernelMemInfo = NULL; +#endif +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + psPrivateData->psDRMFile = pFile; + + list_add_tail(&psPrivateData->sDRMAuthListItem, &psEnvPerProc->sDRMAuthListHead); +#endif + psPrivateData->ui32OpenPID = ui32PID; + psPrivateData->hBlockAlloc = hBlockAlloc; + PRIVATE_DATA(pFile) = psPrivateData; + iRet = 0; +err_unlock: + LinuxUnLockMutex(&gPVRSRVLock); + return iRet; +} + + +#if defined(SUPPORT_DRI_DRM) +void PVRSRVRelease(void *pvPrivData) +#else +static int PVRSRVRelease(struct inode unref__ * pInode, struct file *pFile) +#endif +{ + PVRSRV_FILE_PRIVATE_DATA *psPrivateData; + + LinuxLockMutex(&gPVRSRVLock); + +#if defined(SUPPORT_DRI_DRM) + psPrivateData = (PVRSRV_FILE_PRIVATE_DATA *)pvPrivData; +#else + psPrivateData = PRIVATE_DATA(pFile); +#endif + if (psPrivateData != IMG_NULL) + { +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + list_del(&psPrivateData->sDRMAuthListItem); +#endif + + + gui32ReleasePID = psPrivateData->ui32OpenPID; + PVRSRVProcessDisconnect(psPrivateData->ui32OpenPID); + gui32ReleasePID = 0; + + OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_FILE_PRIVATE_DATA), + psPrivateData, psPrivateData->hBlockAlloc); + +#if !defined(SUPPORT_DRI_DRM) + PRIVATE_DATA(pFile) = IMG_NULL; +#endif + } + + LinuxUnLockMutex(&gPVRSRVLock); + +#if !defined(SUPPORT_DRI_DRM) + return 0; +#endif +} + + +#if defined(SUPPORT_DRI_DRM) +int PVRCore_Init(void) +#else +static int __init PVRCore_Init(void) +#endif +{ + int error; +#if !defined(PVR_LDM_MODULE) + PVRSRV_ERROR eError; +#else + struct device *psDev; +#endif + +#if !defined(SUPPORT_DRI_DRM) + + PVRDPFInit(); +#endif + PVR_TRACE(("PVRCore_Init")); + + LinuxInitMutex(&gPVRSRVLock); + + if (CreateProcEntries ()) + { + error = -ENOMEM; + return error; + } + + if (PVROSFuncInit() != PVRSRV_OK) + { + error = -ENOMEM; + goto init_failed; + } + + PVRLinuxMUtilsInit(); + + if(LinuxMMInit() != PVRSRV_OK) + { + error = -ENOMEM; + goto init_failed; + } + + LinuxBridgeInit(); + + PVRMMapInit(); + +#if defined(PVR_LDM_MODULE) + +#if defined(PVR_LDM_PLATFORM_MODULE) + if ((error = platform_driver_register(&powervr_driver)) != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to register platform driver (%d)", error)); + + goto init_failed; + } + +#if defined(MODULE) && !defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) + if ((error = platform_device_register(&powervr_device)) != 0) + { + platform_driver_unregister(&powervr_driver); + + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to register platform device (%d)", error)); + + goto init_failed; + } +#endif +#endif + +#if defined(PVR_LDM_PCI_MODULE) + if ((error = pci_register_driver(&powervr_driver)) != 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to register PCI driver (%d)", error)); + + goto init_failed; + } +#endif + +#else + + if ((eError = SysInitialise()) != PVRSRV_OK) + { + error = -ENODEV; +#if defined(TCF_REV) && (TCF_REV == 110) + if(eError == PVRSRV_ERROR_NOT_SUPPORTED) + { + printk("\nAtlas wrapper (FPGA image) version mismatch"); + error = -ENODEV; + } +#endif + goto init_failed; + } +#endif + +#if !defined(SUPPORT_DRI_DRM) + AssignedMajorNumber = register_chrdev(0, DEVNAME, &pvrsrv_fops); + + if (AssignedMajorNumber <= 0) + { + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to get major number")); + + error = -EBUSY; + goto sys_deinit; + } + + PVR_TRACE(("PVRCore_Init: major device %d", AssignedMajorNumber)); +#endif + +#if defined(PVR_LDM_MODULE) + + psPvrClass = class_create(THIS_MODULE, "pvr"); + + if (IS_ERR(psPvrClass)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to create class (%ld)", PTR_ERR(psPvrClass))); + error = -EBUSY; + goto unregister_device; + } + + psDev = device_create(psPvrClass, NULL, MKDEV(AssignedMajorNumber, 0), +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)) + NULL, +#endif + DEVNAME); + if (IS_ERR(psDev)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRCore_Init: unable to create device (%ld)", PTR_ERR(psDev))); + error = -EBUSY; + goto destroy_class; + } +#endif + + return 0; + +#if defined(PVR_LDM_MODULE) +destroy_class: + class_destroy(psPvrClass); +unregister_device: + unregister_chrdev((IMG_UINT)AssignedMajorNumber, DRVNAME); +#endif +#if !defined(SUPPORT_DRI_DRM) +sys_deinit: +#endif +#if defined(PVR_LDM_MODULE) +#if defined(PVR_LDM_PCI_MODULE) + pci_unregister_driver(&powervr_driver); +#endif + +#if defined (PVR_LDM_PLATFORM_MODULE) +#if defined(MODULE) && !defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) + platform_device_unregister(&powervr_device); +#endif + platform_driver_unregister(&powervr_driver); +#endif + +#else + + { + SYS_DATA *psSysData; + + psSysData = SysAcquireDataNoCheck(); + if (psSysData != IMG_NULL) + { + (void) SysDeinitialise(psSysData); + } + } +#endif +init_failed: + PVRMMapCleanup(); + LinuxMMCleanup(); + LinuxBridgeDeInit(); + PVROSFuncDeInit(); + RemoveProcEntries(); + + return error; + +} + + +#if defined(SUPPORT_DRI_DRM) +void PVRCore_Cleanup(void) +#else +static void __exit PVRCore_Cleanup(void) +#endif +{ +#if !defined(PVR_LDM_MODULE) + SYS_DATA *psSysData; +#endif + PVR_TRACE(("PVRCore_Cleanup")); + +#if !defined(PVR_LDM_MODULE) + SysAcquireData(&psSysData); +#endif + +#if defined(PVR_LDM_MODULE) + device_destroy(psPvrClass, MKDEV(AssignedMajorNumber, 0)); + class_destroy(psPvrClass); +#endif + +#if !defined(SUPPORT_DRI_DRM) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) + if ( +#endif + unregister_chrdev((IMG_UINT)AssignedMajorNumber, DRVNAME) +#if !(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) + ; +#else + ) + { + PVR_DPF((PVR_DBG_ERROR," can't unregister device major %d", AssignedMajorNumber)); + } +#endif +#endif + +#if defined(PVR_LDM_MODULE) + +#if defined(PVR_LDM_PCI_MODULE) + pci_unregister_driver(&powervr_driver); +#endif + +#if defined (PVR_LDM_PLATFORM_MODULE) +#if defined(MODULE) && !defined(PVR_USE_PRE_REGISTERED_PLATFORM_DEV) + platform_device_unregister(&powervr_device); +#endif + platform_driver_unregister(&powervr_driver); +#endif + +#else +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) + if (gPVRPowerLevel != 0) + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) == PVRSRV_OK) + { + gPVRPowerLevel = 0; + } + } +#endif + + (void) SysDeinitialise(psSysData); +#endif + + PVRMMapCleanup(); + + LinuxMMCleanup(); + + LinuxBridgeDeInit(); + + PVROSFuncDeInit(); + + RemoveProcEntries(); + + PVR_TRACE(("PVRCore_Cleanup: unloading")); +} + +#if !defined(SUPPORT_DRI_DRM) +module_init(PVRCore_Init); +module_exit(PVRCore_Cleanup); +#endif diff --git a/sgx/services4/srvkm/env/linux/mutex.c b/sgx/services4/srvkm/env/linux/mutex.c new file mode 100644 index 0000000..742fa03 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mutex.c @@ -0,0 +1,136 @@ +/********************************************************************** + * + * 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 <linux/version.h> +#include <linux/errno.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) +#include <linux/mutex.h> +#else +#include <asm/semaphore.h> +#endif +#include <linux/module.h> + +#include <img_defs.h> +#include <services.h> + +#include "mutex.h" + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) + +IMG_VOID LinuxInitMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + mutex_init(psPVRSRVMutex); +} + +IMG_VOID LinuxLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + mutex_lock(psPVRSRVMutex); +} + +PVRSRV_ERROR LinuxLockMutexInterruptible(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + if(mutex_lock_interruptible(psPVRSRVMutex) == -EINTR) + { + return PVRSRV_ERROR_MUTEX_INTERRUPTIBLE_ERROR; + } + else + { + return PVRSRV_OK; + } +} + +IMG_INT32 LinuxTryLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + return mutex_trylock(psPVRSRVMutex); +} + +IMG_VOID LinuxUnLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + mutex_unlock(psPVRSRVMutex); +} + +IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + return (mutex_is_locked(psPVRSRVMutex)) ? IMG_TRUE : IMG_FALSE; +} + + +#else + + +IMG_VOID LinuxInitMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + init_MUTEX(&psPVRSRVMutex->sSemaphore); + atomic_set(&psPVRSRVMutex->Count, 0); +} + +IMG_VOID LinuxLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + down(&psPVRSRVMutex->sSemaphore); + atomic_dec(&psPVRSRVMutex->Count); +} + +PVRSRV_ERROR LinuxLockMutexInterruptible(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + if(down_interruptible(&psPVRSRVMutex->sSemaphore) == -EINTR) + { + + return PVRSRV_ERROR_MUTEX_INTERRUPTIBLE_ERROR; + }else{ + atomic_dec(&psPVRSRVMutex->Count); + return PVRSRV_OK; + } +} + +IMG_INT32 LinuxTryLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + IMG_INT32 Status = down_trylock(&psPVRSRVMutex->sSemaphore); + if(Status == 0) + { + atomic_dec(&psPVRSRVMutex->Count); + } + + return Status; +} + +IMG_VOID LinuxUnLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + atomic_inc(&psPVRSRVMutex->Count); + up(&psPVRSRVMutex->sSemaphore); +} + +IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) +{ + IMG_INT32 iCount; + + iCount = atomic_read(&psPVRSRVMutex->Count); + + return (IMG_BOOL)iCount; +} + +#endif + diff --git a/sgx/services4/srvkm/env/linux/mutex.h b/sgx/services4/srvkm/env/linux/mutex.h new file mode 100644 index 0000000..5e787b7 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mutex.h @@ -0,0 +1,70 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __INCLUDED_LINUX_MUTEX_H_ +#define __INCLUDED_LINUX_MUTEX_H_ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) +#include <linux/mutex.h> +#else +#include <asm/semaphore.h> +#endif + + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) + +typedef struct mutex PVRSRV_LINUX_MUTEX; + +#else + + +typedef struct { + struct semaphore sSemaphore; + + atomic_t Count; +}PVRSRV_LINUX_MUTEX; + +#endif + + +extern IMG_VOID LinuxInitMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + +extern IMG_VOID LinuxLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + +extern PVRSRV_ERROR LinuxLockMutexInterruptible(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + +extern IMG_INT32 LinuxTryLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + +extern IMG_VOID LinuxUnLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + +extern IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); + + +#endif + diff --git a/sgx/services4/srvkm/env/linux/mutils.c b/sgx/services4/srvkm/env/linux/mutils.c new file mode 100644 index 0000000..a012cf5 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mutils.c @@ -0,0 +1,136 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +#include "img_defs.h" +#include "pvr_debug.h" +#include "mutils.h" + +#if defined(SUPPORT_LINUX_X86_PAT) +#define PAT_LINUX_X86_WC 1 + +#define PAT_X86_ENTRY_BITS 8 + +#define PAT_X86_BIT_PWT 1U +#define PAT_X86_BIT_PCD 2U +#define PAT_X86_BIT_PAT 4U +#define PAT_X86_BIT_MASK (PAT_X86_BIT_PAT | PAT_X86_BIT_PCD | PAT_X86_BIT_PWT) + +static IMG_BOOL g_write_combining_available = IMG_FALSE; + +#define PROT_TO_PAT_INDEX(v, B) ((v & _PAGE_ ## B) ? PAT_X86_BIT_ ## B : 0) + +static inline IMG_UINT +pvr_pat_index(pgprotval_t prot_val) +{ + IMG_UINT ret = 0; + pgprotval_t val = prot_val & _PAGE_CACHE_MASK; + + ret |= PROT_TO_PAT_INDEX(val, PAT); + ret |= PROT_TO_PAT_INDEX(val, PCD); + ret |= PROT_TO_PAT_INDEX(val, PWT); + + return ret; +} + +static inline IMG_UINT +pvr_pat_entry(u64 pat, IMG_UINT index) +{ + return (IMG_UINT)(pat >> (index * PAT_X86_ENTRY_BITS)) & PAT_X86_BIT_MASK; +} + +static IMG_VOID +PVRLinuxX86PATProbe(IMG_VOID) +{ + + if (cpu_has_pat) + { + u64 pat; + IMG_UINT pat_index; + IMG_UINT pat_entry; + + PVR_TRACE(("%s: PAT available", __FUNCTION__)); + + rdmsrl(MSR_IA32_CR_PAT, pat); + PVR_TRACE(("%s: Top 32 bits of PAT: 0x%.8x", __FUNCTION__, (IMG_UINT)(pat >> 32))); + PVR_TRACE(("%s: Bottom 32 bits of PAT: 0x%.8x", __FUNCTION__, (IMG_UINT)(pat))); + + pat_index = pvr_pat_index(_PAGE_CACHE_WC); + PVR_TRACE(("%s: PAT index for write combining: %u", __FUNCTION__, pat_index)); + + pat_entry = pvr_pat_entry(pat, pat_index); + PVR_TRACE(("%s: PAT entry for write combining: 0x%.2x (should be 0x%.2x)", __FUNCTION__, pat_entry, PAT_LINUX_X86_WC)); + +#if defined(SUPPORT_LINUX_X86_WRITECOMBINE) + g_write_combining_available = (IMG_BOOL)(pat_entry == PAT_LINUX_X86_WC); +#endif + } +#if defined(DEBUG) +#if defined(SUPPORT_LINUX_X86_WRITECOMBINE) + if (g_write_combining_available) + { + PVR_TRACE(("%s: Write combining available via PAT", __FUNCTION__)); + } + else + { + PVR_TRACE(("%s: Write combining not available", __FUNCTION__)); + } +#else + PVR_TRACE(("%s: Write combining disabled in driver build", __FUNCTION__)); +#endif +#endif +} + +pgprot_t +pvr_pgprot_writecombine(pgprot_t prot) +{ + + + return (g_write_combining_available) ? + __pgprot((pgprot_val(prot) & ~_PAGE_CACHE_MASK) | _PAGE_CACHE_WC) : pgprot_noncached(prot); +} +#endif + +IMG_VOID +PVRLinuxMUtilsInit(IMG_VOID) +{ +#if defined(SUPPORT_LINUX_X86_PAT) + PVRLinuxX86PATProbe(); +#endif +} + diff --git a/sgx/services4/srvkm/env/linux/mutils.h b/sgx/services4/srvkm/env/linux/mutils.h new file mode 100644 index 0000000..b2a8ba0 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/mutils.h @@ -0,0 +1,103 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __IMG_LINUX_MUTILS_H__ +#define __IMG_LINUX_MUTILS_H__ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#if !(defined(__i386__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))) +#if defined(SUPPORT_LINUX_X86_PAT) +#undef SUPPORT_LINUX_X86_PAT +#endif +#endif + +#if defined(SUPPORT_LINUX_X86_PAT) + pgprot_t pvr_pgprot_writecombine(pgprot_t prot); + #define PGPROT_WC(pv) pvr_pgprot_writecombine(pv) +#else + #if defined(__arm__) || defined(__sh__) + #define PGPROT_WC(pv) pgprot_writecombine(pv) + #else + #if defined(__i386__) || defined(__mips__) + #define PGPROT_WC(pv) pgprot_noncached(pv) + #else + #define PGPROT_WC(pv) pgprot_noncached(pv) + #error Unsupported architecture! + #endif + #endif +#endif + +#define PGPROT_UC(pv) pgprot_noncached(pv) + +#if defined(__i386__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) + #define IOREMAP(pa, bytes) ioremap_cache(pa, bytes) +#else + #if defined(__arm__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + #define IOREMAP(pa, bytes) ioremap_cached(pa, bytes) + #else + #define IOREMAP(pa, bytes) ioremap(pa, bytes) + #endif +#endif + +#if defined(SUPPORT_LINUX_X86_PAT) + #if defined(SUPPORT_LINUX_X86_WRITECOMBINE) + #define IOREMAP_WC(pa, bytes) ioremap_wc(pa, bytes) + #else + #define IOREMAP_WC(pa, bytes) ioremap_nocache(pa, bytes) + #endif +#else + #if defined(__arm__) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) + #define IOREMAP_WC(pa, bytes) ioremap_wc(pa, bytes) + #else + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) + #define IOREMAP_WC(pa, bytes) ioremap_nocache(pa, bytes) + #else + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)) + #define IOREMAP_WC(pa, bytes) __ioremap(pa, bytes, L_PTE_BUFFERABLE) + #else + #define IOREMAP_WC(pa, bytes) __ioremap(pa, bytes, , L_PTE_BUFFERABLE, 1) + #endif + #endif + #endif + #else + #define IOREMAP_WC(pa, bytes) ioremap_nocache(pa, bytes) + #endif +#endif + +#define IOREMAP_UC(pa, bytes) ioremap_nocache(pa, bytes) + +IMG_VOID PVRLinuxMUtilsInit(IMG_VOID); + +#endif + diff --git a/sgx/services4/srvkm/env/linux/osfunc.c b/sgx/services4/srvkm/env/linux/osfunc.c new file mode 100644 index 0000000..42ecb05 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/osfunc.c @@ -0,0 +1,3149 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <asm/io.h> +#include <asm/page.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) +#include <asm/system.h> +#endif +#include <asm/cacheflush.h> +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/hugetlb.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <asm/hardirq.h> +#include <linux/timer.h> +#include <linux/capability.h> +#include <asm/uaccess.h> +#include <linux/spinlock.h> +#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) || \ + defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) || \ + defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || \ + defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) || \ + defined(PVR_LINUX_USING_WORKQUEUES) +#include <linux/workqueue.h> +#endif + +#include "img_types.h" +#include "services_headers.h" +#include "mm.h" +#include "pvrmmap.h" +#include "mmap.h" +#include "env_data.h" +#include "proc.h" +#include "mutex.h" +#include "event.h" +#include "linkage.h" +#include "pvr_uaccess.h" + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) +#define ON_EACH_CPU(func, info, wait) on_each_cpu(func, info, wait) +#else +#define ON_EACH_CPU(func, info, wait) on_each_cpu(func, info, 0, wait) +#endif + +#if defined(PVR_LINUX_USING_WORKQUEUES) && !defined(CONFIG_PREEMPT) +#error "A preemptible Linux kernel is required when using workqueues" +#endif + +#if defined(EMULATOR) +#define EVENT_OBJECT_TIMEOUT_MS (2000) +#else +#define EVENT_OBJECT_TIMEOUT_MS (100) +#endif + +#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID *ppvCpuVAddr, IMG_HANDLE *phBlockAlloc) +#else +PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID *ppvCpuVAddr, IMG_HANDLE *phBlockAlloc, IMG_CHAR *pszFilename, IMG_UINT32 ui32Line) +#endif +{ + PVR_UNREFERENCED_PARAMETER(ui32Flags); + PVR_UNREFERENCED_PARAMETER(phBlockAlloc); + + if (ui32Size > PAGE_SIZE) + { + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + *ppvCpuVAddr = _VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED, pszFilename, ui32Line); +#else + *ppvCpuVAddr = VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED); +#endif + if (*ppvCpuVAddr) + { + return PVRSRV_OK; + } + } + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + *ppvCpuVAddr = _KMallocWrapper(ui32Size, GFP_KERNEL | __GFP_NOWARN, pszFilename, ui32Line); +#else + *ppvCpuVAddr = KMallocWrapper(ui32Size, GFP_KERNEL | __GFP_NOWARN); +#endif + if (!*ppvCpuVAddr) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + return PVRSRV_OK; +} + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)) + +static inline int is_vmalloc_addr(const void *pvCpuVAddr) +{ + unsigned long lAddr = (unsigned long)pvCpuVAddr; + return lAddr >= VMALLOC_START && lAddr < VMALLOC_END; +} + +#endif + +#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +PVRSRV_ERROR OSFreeMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID pvCpuVAddr, IMG_HANDLE hBlockAlloc) +#else +PVRSRV_ERROR OSFreeMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID pvCpuVAddr, IMG_HANDLE hBlockAlloc, IMG_CHAR *pszFilename, IMG_UINT32 ui32Line) +#endif +{ + PVR_UNREFERENCED_PARAMETER(ui32Flags); + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(hBlockAlloc); + + if (is_vmalloc_addr(pvCpuVAddr)) + { +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + _VFreeWrapper(pvCpuVAddr, pszFilename, ui32Line); +#else + VFreeWrapper(pvCpuVAddr); +#endif + } + else + { +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + _KFreeWrapper(pvCpuVAddr, pszFilename, ui32Line); +#else + KFreeWrapper(pvCpuVAddr); +#endif + } + + return PVRSRV_OK; +} + + +PVRSRV_ERROR +OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32PageSize, + IMG_VOID **ppvCpuVAddr, + IMG_HANDLE *phOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + + PVR_UNREFERENCED_PARAMETER(ui32PageSize); + +#if 0 + + if(ui32AllocFlags & PVRSRV_HAP_SINGLE_PROCESS) + { + ui32AllocFlags &= ~PVRSRV_HAP_SINGLE_PROCESS; + ui32AllocFlags |= PVRSRV_HAP_MULTI_PROCESS; + } +#endif + + switch(ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + { + psLinuxMemArea = NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + break; + } + case PVRSRV_HAP_SINGLE_PROCESS: + { + + + psLinuxMemArea = NewAllocPagesLinuxMemArea(ui32Size, ui32AllocFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + + case PVRSRV_HAP_MULTI_PROCESS: + { + +#if defined(VIVT_CACHE) || defined(__sh__) + + ui32AllocFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); +#endif + psLinuxMemArea = NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + default: + PVR_DPF((PVR_DBG_ERROR, "OSAllocPages: invalid flags 0x%x\n", ui32AllocFlags)); + *ppvCpuVAddr = NULL; + *phOSMemHandle = (IMG_HANDLE)0; + return PVRSRV_ERROR_INVALID_PARAMS; + } + + *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); + *phOSMemHandle = psLinuxMemArea; + + LinuxMemAreaRegister(psLinuxMemArea); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR +OSFreePages(IMG_UINT32 ui32AllocFlags, IMG_UINT32 ui32Bytes, IMG_VOID *pvCpuVAddr, IMG_HANDLE hOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + PVRSRV_ERROR eError; + + PVR_UNREFERENCED_PARAMETER(ui32Bytes); + PVR_UNREFERENCED_PARAMETER(pvCpuVAddr); + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + switch(ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + break; + case PVRSRV_HAP_SINGLE_PROCESS: + case PVRSRV_HAP_MULTI_PROCESS: + eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, + "OSFreePages(ui32AllocFlags=0x%08X, ui32Bytes=%d, " + "pvCpuVAddr=%p, hOSMemHandle=%p) FAILED!", + ui32AllocFlags, ui32Bytes, pvCpuVAddr, hOSMemHandle)); + return eError; + } + break; + default: + PVR_DPF((PVR_DBG_ERROR,"%s: invalid flags 0x%x\n", + __FUNCTION__, ui32AllocFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + LinuxMemAreaDeepFree(psLinuxMemArea); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR +OSGetSubMemHandle(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32Flags, + IMG_HANDLE *phOSMemHandleRet) +{ + LinuxMemArea *psParentLinuxMemArea, *psLinuxMemArea; + PVRSRV_ERROR eError; + + psParentLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + psLinuxMemArea = NewSubLinuxMemArea(psParentLinuxMemArea, ui32ByteOffset, ui32Bytes); + if(!psLinuxMemArea) + { + *phOSMemHandleRet = NULL; + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + *phOSMemHandleRet = psLinuxMemArea; + + + if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY) + { + return PVRSRV_OK; + } + + eError = PVRMMapRegisterArea(psLinuxMemArea); + if(eError != PVRSRV_OK) + { + goto failed_register_area; + } + + return PVRSRV_OK; + +failed_register_area: + *phOSMemHandleRet = NULL; + LinuxMemAreaDeepFree(psLinuxMemArea); + return eError; +} + +PVRSRV_ERROR +OSReleaseSubMemHandle(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32Flags) +{ + LinuxMemArea *psLinuxMemArea; + PVRSRV_ERROR eError; + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC); + + if((ui32Flags & PVRSRV_HAP_KERNEL_ONLY) == 0) + { + eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); + if(eError != PVRSRV_OK) + { + return eError; + } + } + LinuxMemAreaDeepFree(psLinuxMemArea); + + return PVRSRV_OK; +} + +IMG_VOID +OSMemHandleRegisterSmart(IMG_VOID *hOSMemHandle, IMG_HANDLE hSmartCache) +{ + LinuxMemArea *psLinuxMemArea = hOSMemHandle; + psLinuxMemArea->hSmartCache = hSmartCache; +} + +IMG_VOID +OSMemHandleUnegisterSmart(IMG_VOID *hOSMemHandle, IMG_HANDLE hSmartCache) +{ + LinuxMemArea *psLinuxMemArea = hOSMemHandle; + if (psLinuxMemArea->hSmartCache == hSmartCache) + { + psLinuxMemArea->hSmartCache = NULL; + } +} + + +IMG_CPU_PHYADDR +OSMemHandleToCpuPAddr(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32ByteOffset) +{ + PVR_ASSERT(hOSMemHandle); + + return LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset); +} + + + +IMG_VOID OSMemCopy(IMG_VOID *pvDst, IMG_VOID *pvSrc, IMG_UINT32 ui32Size) +{ +#if defined(USE_UNOPTIMISED_MEMCPY) + IMG_UINT8 *Src,*Dst; + IMG_INT i; + + Src=(IMG_UINT8 *)pvSrc; + Dst=(IMG_UINT8 *)pvDst; + for(i=0;i<ui32Size;i++) + { + Dst[i]=Src[i]; + } +#else + memcpy(pvDst, pvSrc, ui32Size); +#endif +} + + +IMG_VOID OSMemSet(IMG_VOID *pvDest, IMG_UINT8 ui8Value, IMG_UINT32 ui32Size) +{ +#if defined(USE_UNOPTIMISED_MEMSET) + IMG_UINT8 *Buff; + IMG_INT i; + + Buff=(IMG_UINT8 *)pvDest; + for(i=0;i<ui32Size;i++) + { + Buff[i]=ui8Value; + } +#else + memset(pvDest, (IMG_INT) ui8Value, (size_t) ui32Size); +#endif +} + + +IMG_CHAR *OSStringCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc) +{ + return (strcpy(pszDest, pszSrc)); +} + +IMG_INT32 OSSNPrintf(IMG_CHAR *pStr, IMG_UINT32 ui32Size, const IMG_CHAR *pszFormat, ...) +{ + va_list argList; + IMG_INT32 iCount; + + va_start(argList, pszFormat); + iCount = vsnprintf(pStr, (size_t)ui32Size, pszFormat, argList); + va_end(argList); + + return iCount; +} + +IMG_VOID OSBreakResourceLock (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID) +{ + volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock; + + if(*pui32Access) + { + if(psResource->ui32ID == ui32ID) + { + psResource->ui32ID = 0; + *pui32Access = 0; + } + else + { + PVR_DPF((PVR_DBG_MESSAGE,"OSBreakResourceLock: Resource is not locked for this process.")); + } + } + else + { + PVR_DPF((PVR_DBG_MESSAGE,"OSBreakResourceLock: Resource is not locked")); + } +} + + +PVRSRV_ERROR OSCreateResource(PVRSRV_RESOURCE *psResource) +{ + psResource->ui32ID = 0; + psResource->ui32Lock = 0; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSDestroyResource (PVRSRV_RESOURCE *psResource) +{ + OSBreakResourceLock (psResource, psResource->ui32ID); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSInitEnvData(IMG_PVOID *ppvEnvSpecificData) +{ + ENV_DATA *psEnvData; + PVRSRV_ERROR eError; + + + eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), (IMG_VOID **)&psEnvData, IMG_NULL, + "Environment Data"); + if (eError != PVRSRV_OK) + { + return eError; + } + + eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE, + &psEnvData->pvBridgeData, IMG_NULL, + "Bridge Data"); + if (eError != PVRSRV_OK) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), psEnvData, IMG_NULL); + + return eError; + } + + + + psEnvData->bMISRInstalled = IMG_FALSE; + psEnvData->bLISRInstalled = IMG_FALSE; + + + *ppvEnvSpecificData = psEnvData; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSDeInitEnvData(IMG_PVOID pvEnvSpecificData) +{ + ENV_DATA *psEnvData = (ENV_DATA*)pvEnvSpecificData; + + PVR_ASSERT(!psEnvData->bMISRInstalled); + PVR_ASSERT(!psEnvData->bLISRInstalled); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE, psEnvData->pvBridgeData, IMG_NULL); + psEnvData->pvBridgeData = IMG_NULL; + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(ENV_DATA), pvEnvSpecificData, IMG_NULL); + + + return PVRSRV_OK; +} + + + +IMG_VOID OSReleaseThreadQuanta(IMG_VOID) +{ + schedule(); +} + + + +IMG_UINT32 OSClockus(IMG_VOID) +{ + IMG_UINT32 time, j = jiffies; + + time = j * (1000000 / HZ); + + return time; +} + + +IMG_VOID OSWaitus(IMG_UINT32 ui32Timeus) +{ + udelay(ui32Timeus); +} + + +IMG_VOID OSSleepms(IMG_UINT32 ui32Timems) +{ + msleep(ui32Timems); +} + + + +IMG_HANDLE OSFuncHighResTimerCreate(IMG_VOID) +{ + + return (IMG_HANDLE) 1; +} + + +IMG_UINT32 OSFuncHighResTimerGetus(IMG_HANDLE hTimer) +{ + return (IMG_UINT32) jiffies_to_usecs(jiffies); +} + + +IMG_VOID OSFuncHighResTimerDestroy(IMG_HANDLE hTimer) +{ + PVR_UNREFERENCED_PARAMETER(hTimer); +} + +IMG_UINT32 OSGetCurrentProcessIDKM(IMG_VOID) +{ + if (in_interrupt()) + { + return KERNEL_ID; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) + return (IMG_UINT32)current->pgrp; +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + return (IMG_UINT32)task_tgid_nr(current); +#else + return (IMG_UINT32)current->tgid; +#endif +#endif +} + + +IMG_UINT32 OSGetPageSize(IMG_VOID) +{ +#if defined(__sh__) + IMG_UINT32 ui32ReturnValue = PAGE_SIZE; + + return (ui32ReturnValue); +#else + return PAGE_SIZE; +#endif +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) +static irqreturn_t DeviceISRWrapper(int irq, void *dev_id +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif + ) +{ + PVRSRV_DEVICE_NODE *psDeviceNode; + IMG_BOOL bStatus = IMG_FALSE; + + PVR_UNREFERENCED_PARAMETER(irq); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + PVR_UNREFERENCED_PARAMETER(regs); +#endif + psDeviceNode = (PVRSRV_DEVICE_NODE*)dev_id; + if(!psDeviceNode) + { + PVR_DPF((PVR_DBG_ERROR, "DeviceISRWrapper: invalid params\n")); + goto out; + } + + bStatus = PVRSRVDeviceLISR(psDeviceNode); + + if (bStatus) + { + OSScheduleMISR((IMG_VOID *)psDeviceNode->psSysData); + } + +out: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + return bStatus ? IRQ_HANDLED : IRQ_NONE; +#endif +} + + + +static irqreturn_t SystemISRWrapper(int irq, void *dev_id +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif + ) +{ + SYS_DATA *psSysData; + IMG_BOOL bStatus = IMG_FALSE; + + PVR_UNREFERENCED_PARAMETER(irq); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + PVR_UNREFERENCED_PARAMETER(regs); +#endif + psSysData = (SYS_DATA *)dev_id; + if(!psSysData) + { + PVR_DPF((PVR_DBG_ERROR, "SystemISRWrapper: invalid params\n")); + goto out; + } + + bStatus = PVRSRVSystemLISR(psSysData); + + if (bStatus) + { + OSScheduleMISR((IMG_VOID *)psSysData); + } + +out: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + return bStatus ? IRQ_HANDLED : IRQ_NONE; +#endif +} +PVRSRV_ERROR OSInstallDeviceLISR(IMG_VOID *pvSysData, + IMG_UINT32 ui32Irq, + IMG_CHAR *pszISRName, + IMG_VOID *pvDeviceNode) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (psEnvData->bLISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallDeviceLISR: An ISR has already been installed: IRQ %d cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie)); + return PVRSRV_ERROR_ISR_ALREADY_INSTALLED; + } + + PVR_TRACE(("Installing device LISR %s on IRQ %d with cookie %p", pszISRName, ui32Irq, pvDeviceNode)); + + if(request_irq(ui32Irq, DeviceISRWrapper, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) + SA_SHIRQ +#else + IRQF_SHARED +#endif + , pszISRName, pvDeviceNode)) + { + PVR_DPF((PVR_DBG_ERROR,"OSInstallDeviceLISR: Couldn't install device LISR on IRQ %d", ui32Irq)); + + return PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR; + } + + psEnvData->ui32IRQ = ui32Irq; + psEnvData->pvISRCookie = pvDeviceNode; + psEnvData->bLISRInstalled = IMG_TRUE; + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSUninstallDeviceLISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (!psEnvData->bLISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSUninstallDeviceLISR: No LISR has been installed")); + return PVRSRV_ERROR_ISR_NOT_INSTALLED; + } + + PVR_TRACE(("Uninstalling device LISR on IRQ %d with cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie)); + + free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie); + + psEnvData->bLISRInstalled = IMG_FALSE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSInstallSystemLISR(IMG_VOID *pvSysData, IMG_UINT32 ui32Irq) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (psEnvData->bLISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallSystemLISR: An LISR has already been installed: IRQ %d cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie)); + return PVRSRV_ERROR_ISR_ALREADY_INSTALLED; + } + + PVR_TRACE(("Installing system LISR on IRQ %d with cookie %p", ui32Irq, pvSysData)); + + if(request_irq(ui32Irq, SystemISRWrapper, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) + SA_SHIRQ +#else + IRQF_SHARED +#endif + , PVRSRV_MODNAME, pvSysData)) + { + PVR_DPF((PVR_DBG_ERROR,"OSInstallSystemLISR: Couldn't install system LISR on IRQ %d", ui32Irq)); + + return PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR; + } + + psEnvData->ui32IRQ = ui32Irq; + psEnvData->pvISRCookie = pvSysData; + psEnvData->bLISRInstalled = IMG_TRUE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSUninstallSystemLISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (!psEnvData->bLISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSUninstallSystemLISR: No LISR has been installed")); + return PVRSRV_ERROR_ISR_NOT_INSTALLED; + } + + PVR_TRACE(("Uninstalling system LISR on IRQ %d with cookie %p", psEnvData->ui32IRQ, psEnvData->pvISRCookie)); + + free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie); + + psEnvData->bLISRInstalled = IMG_FALSE; + + return PVRSRV_OK; +} + +#if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) +static void MISRWrapper( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + void *data +#else + struct work_struct *data +#endif +) +{ + ENV_DATA *psEnvData = container_of(data, ENV_DATA, sMISRWork); + SYS_DATA *psSysData = (SYS_DATA *)psEnvData->pvMISRData; + + PVRSRVMISR(psSysData); +} + + +PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed")); + return PVRSRV_ERROR_ISR_ALREADY_INSTALLED; + } + + PVR_TRACE(("Installing MISR with cookie %p", pvSysData)); + + psEnvData->psWorkQueue = create_singlethread_workqueue("pvr_workqueue"); + + if (psEnvData->psWorkQueue == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: create_singlethreaded_workqueue failed")); + return PVRSRV_ERROR_UNABLE_TO_CREATE_THREAD; + } + + INIT_WORK(&psEnvData->sMISRWork, MISRWrapper +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + , (void *)&psEnvData->sMISRWork +#endif + ); + + psEnvData->pvMISRData = pvSysData; + psEnvData->bMISRInstalled = IMG_TRUE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (!psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed")); + return PVRSRV_ERROR_ISR_NOT_INSTALLED; + } + + PVR_TRACE(("Uninstalling MISR")); + + destroy_workqueue(psEnvData->psWorkQueue); + + psEnvData->bMISRInstalled = IMG_FALSE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + queue_work(psEnvData->psWorkQueue, &psEnvData->sMISRWork); + } + + return PVRSRV_OK; +} +#else +#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) +static void MISRWrapper( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + void *data +#else + struct work_struct *data +#endif +) +{ + ENV_DATA *psEnvData = container_of(data, ENV_DATA, sMISRWork); + SYS_DATA *psSysData = (SYS_DATA *)psEnvData->pvMISRData; + + PVRSRVMISR(psSysData); +} + + +PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed")); + return PVRSRV_ERROR_ISR_ALREADY_INSTALLED; + } + + PVR_TRACE(("Installing MISR with cookie %p", pvSysData)); + + INIT_WORK(&psEnvData->sMISRWork, MISRWrapper +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + , (void *)&psEnvData->sMISRWork +#endif + ); + + psEnvData->pvMISRData = pvSysData; + psEnvData->bMISRInstalled = IMG_TRUE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (!psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed")); + return PVRSRV_ERROR_ISR_NOT_INSTALLED; + } + + PVR_TRACE(("Uninstalling MISR")); + + flush_scheduled_work(); + + psEnvData->bMISRInstalled = IMG_FALSE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + schedule_work(&psEnvData->sMISRWork); + } + + return PVRSRV_OK; +} + +#else + + +static void MISRWrapper(unsigned long data) +{ + SYS_DATA *psSysData; + + psSysData = (SYS_DATA *)data; + + PVRSRVMISR(psSysData); +} + + +PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSInstallMISR: An MISR has already been installed")); + return PVRSRV_ERROR_ISR_ALREADY_INSTALLED; + } + + PVR_TRACE(("Installing MISR with cookie %p", pvSysData)); + + tasklet_init(&psEnvData->sMISRTasklet, MISRWrapper, (unsigned long)pvSysData); + + psEnvData->bMISRInstalled = IMG_TRUE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA *)psSysData->pvEnvSpecificData; + + if (!psEnvData->bMISRInstalled) + { + PVR_DPF((PVR_DBG_ERROR, "OSUninstallMISR: No MISR has been installed")); + return PVRSRV_ERROR_ISR_NOT_INSTALLED; + } + + PVR_TRACE(("Uninstalling MISR")); + + tasklet_kill(&psEnvData->sMISRTasklet); + + psEnvData->bMISRInstalled = IMG_FALSE; + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) +{ + SYS_DATA *psSysData = (SYS_DATA*)pvSysData; + ENV_DATA *psEnvData = (ENV_DATA*)psSysData->pvEnvSpecificData; + + if (psEnvData->bMISRInstalled) + { + tasklet_schedule(&psEnvData->sMISRTasklet); + } + + return PVRSRV_OK; +} + +#endif +#endif + +#endif + +IMG_VOID OSPanic(IMG_VOID) +{ + BUG(); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) +#define OS_TAS(p) xchg((p), 1) +#else +#define OS_TAS(p) tas(p) +#endif +PVRSRV_ERROR OSLockResource ( PVRSRV_RESOURCE *psResource, + IMG_UINT32 ui32ID) + +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if(!OS_TAS(&psResource->ui32Lock)) + psResource->ui32ID = ui32ID; + else + eError = PVRSRV_ERROR_UNABLE_TO_LOCK_RESOURCE; + + return eError; +} + + +PVRSRV_ERROR OSUnlockResource (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID) +{ + volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock; + PVRSRV_ERROR eError = PVRSRV_OK; + + if(*pui32Access) + { + if(psResource->ui32ID == ui32ID) + { + psResource->ui32ID = 0; + smp_mb(); + *pui32Access = 0; + } + else + { + PVR_DPF((PVR_DBG_ERROR,"OSUnlockResource: Resource %p is not locked with expected value.", psResource)); + PVR_DPF((PVR_DBG_MESSAGE,"Should be %x is actually %x", ui32ID, psResource->ui32ID)); + eError = PVRSRV_ERROR_INVALID_LOCK_ID; + } + } + else + { + PVR_DPF((PVR_DBG_ERROR,"OSUnlockResource: Resource %p is not locked", psResource)); + eError = PVRSRV_ERROR_RESOURCE_NOT_LOCKED; + } + + return eError; +} + + +IMG_BOOL OSIsResourceLocked (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID) +{ + volatile IMG_UINT32 *pui32Access = (volatile IMG_UINT32 *)&psResource->ui32Lock; + + return (*(volatile IMG_UINT32 *)pui32Access == 1) && (psResource->ui32ID == ui32ID) + ? IMG_TRUE + : IMG_FALSE; +} + + +#if !defined(SYS_CUSTOM_POWERLOCK_WRAP) +PVRSRV_ERROR OSPowerLockWrap(IMG_BOOL bTryLock) +{ + PVR_UNREFERENCED_PARAMETER(bTryLock); + + return PVRSRV_OK; +} + +IMG_VOID OSPowerLockUnwrap (IMG_VOID) +{ +} +#endif + + +IMG_CPU_PHYADDR OSMapLinToCPUPhys(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvLinAddr) +{ + IMG_CPU_PHYADDR CpuPAddr; + LinuxMemArea *psLinuxMemArea; + IMG_UINTPTR_T uiByteOffset; + IMG_UINT32 ui32ByteOffset; + + PVR_ASSERT(hOSMemHandle != IMG_NULL); + + + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + uiByteOffset = (IMG_UINTPTR_T)pvLinAddr - (IMG_UINTPTR_T)LinuxMemAreaToCpuVAddr(psLinuxMemArea); + ui32ByteOffset = (IMG_UINT32)uiByteOffset; + + CpuPAddr = LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset); + + return CpuPAddr; +} + + +IMG_VOID * +OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE *phOSMemHandle) +{ + if(ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) + { + + if(phOSMemHandle == IMG_NULL) + { + IMG_VOID *pvIORemapCookie; + pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags); + if(pvIORemapCookie == IMG_NULL) + { + return IMG_NULL; + } + return pvIORemapCookie; + } + else + { + LinuxMemArea *psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + + if(psLinuxMemArea == IMG_NULL) + { + return IMG_NULL; + } + + *phOSMemHandle = (IMG_HANDLE)psLinuxMemArea; + return LinuxMemAreaToCpuVAddr(psLinuxMemArea); + } + } + + PVR_DPF((PVR_DBG_ERROR, + "OSMapPhysToLin should only be used with PVRSRV_HAP_KERNEL_ONLY " + " (Use OSReservePhys otherwise)")); + + return IMG_NULL; +} + +IMG_BOOL +OSUnMapPhysToLin(IMG_VOID *pvLinAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32MappingFlags, IMG_HANDLE hOSMemHandle) +{ + PVR_TRACE(("%s: unmapping %d bytes from %p", __FUNCTION__, ui32Bytes, pvLinAddr)); + + PVR_UNREFERENCED_PARAMETER(ui32Bytes); + + if(ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) + { + if (hOSMemHandle == IMG_NULL) + { + IOUnmapWrapper(pvLinAddr); + } + else + { + LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + PVR_ASSERT(LinuxMemAreaToCpuVAddr(psLinuxMemArea) == pvLinAddr); + + FreeIORemapLinuxMemArea(psLinuxMemArea); + } + + return IMG_TRUE; + } + + PVR_DPF((PVR_DBG_ERROR, + "OSUnMapPhysToLin should only be used with PVRSRV_HAP_KERNEL_ONLY " + " (Use OSUnReservePhys otherwise)")); + return IMG_FALSE; +} + +static PVRSRV_ERROR +RegisterExternalMem(IMG_SYS_PHYADDR *pBasePAddr, + IMG_VOID *pvCPUVAddr, + IMG_UINT32 ui32Bytes, + IMG_BOOL bPhysContig, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE *phOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + + switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + { + psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags); + + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + break; + } + case PVRSRV_HAP_SINGLE_PROCESS: + { + psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags); + + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + case PVRSRV_HAP_MULTI_PROCESS: + { + +#if defined(VIVT_CACHE) || defined(__sh__) + + ui32MappingFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); +#endif + psLinuxMemArea = NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr, ui32Bytes, bPhysContig, ui32MappingFlags); + + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + default: + PVR_DPF((PVR_DBG_ERROR,"OSRegisterMem : invalid flags 0x%x\n", ui32MappingFlags)); + *phOSMemHandle = (IMG_HANDLE)0; + return PVRSRV_ERROR_INVALID_FLAGS; + } + + *phOSMemHandle = (IMG_HANDLE)psLinuxMemArea; + + LinuxMemAreaRegister(psLinuxMemArea); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR +OSRegisterMem(IMG_CPU_PHYADDR BasePAddr, + IMG_VOID *pvCPUVAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE *phOSMemHandle) +{ + IMG_SYS_PHYADDR SysPAddr = SysCpuPAddrToSysPAddr(BasePAddr); + + return RegisterExternalMem(&SysPAddr, pvCPUVAddr, ui32Bytes, IMG_TRUE, ui32MappingFlags, phOSMemHandle); +} + + +PVRSRV_ERROR OSRegisterDiscontigMem(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32MappingFlags, IMG_HANDLE *phOSMemHandle) +{ + return RegisterExternalMem(pBasePAddr, pvCPUVAddr, ui32Bytes, IMG_FALSE, ui32MappingFlags, phOSMemHandle); +} + + +PVRSRV_ERROR +OSUnRegisterMem (IMG_VOID *pvCpuVAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE hOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + PVRSRV_ERROR eError; + + PVR_UNREFERENCED_PARAMETER(pvCpuVAddr); + PVR_UNREFERENCED_PARAMETER(ui32Bytes); + + switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + break; + case PVRSRV_HAP_SINGLE_PROCESS: + case PVRSRV_HAP_MULTI_PROCESS: + { + eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s(%p, %d, 0x%08X, %p) FAILED!", + __FUNCTION__, pvCpuVAddr, ui32Bytes, + ui32MappingFlags, hOSMemHandle)); + return eError; + } + break; + } + default: + { + PVR_DPF((PVR_DBG_ERROR, "OSUnRegisterMem : invalid flags 0x%x", ui32MappingFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + } + + LinuxMemAreaDeepFree(psLinuxMemArea); + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSUnRegisterDiscontigMem(IMG_VOID *pvCpuVAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32Flags, IMG_HANDLE hOSMemHandle) +{ + return OSUnRegisterMem(pvCpuVAddr, ui32Bytes, ui32Flags, hOSMemHandle); +} + +PVRSRV_ERROR +OSReservePhys(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_VOID **ppvCpuVAddr, + IMG_HANDLE *phOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + +#if 0 + + if(ui32MappingFlags & PVRSRV_HAP_SINGLE_PROCESS) + { + ui32MappingFlags &= ~PVRSRV_HAP_SINGLE_PROCESS; + ui32MappingFlags |= PVRSRV_HAP_MULTI_PROCESS; + } +#endif + + switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + { + + psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + break; + } + case PVRSRV_HAP_SINGLE_PROCESS: + { + + psLinuxMemArea = NewIOLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + case PVRSRV_HAP_MULTI_PROCESS: + { + +#if defined(VIVT_CACHE) || defined(__sh__) + + ui32MappingFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); +#endif + psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + default: + PVR_DPF((PVR_DBG_ERROR,"OSMapPhysToLin : invalid flags 0x%x\n", ui32MappingFlags)); + *ppvCpuVAddr = NULL; + *phOSMemHandle = (IMG_HANDLE)0; + return PVRSRV_ERROR_INVALID_FLAGS; + } + + *phOSMemHandle = (IMG_HANDLE)psLinuxMemArea; + *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); + + LinuxMemAreaRegister(psLinuxMemArea); + + return PVRSRV_OK; +} + +PVRSRV_ERROR +OSUnReservePhys(IMG_VOID *pvCpuVAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE hOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + PVRSRV_ERROR eError; + + PVR_UNREFERENCED_PARAMETER(pvCpuVAddr); + PVR_UNREFERENCED_PARAMETER(ui32Bytes); + + psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + switch(ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) + { + case PVRSRV_HAP_KERNEL_ONLY: + break; + case PVRSRV_HAP_SINGLE_PROCESS: + case PVRSRV_HAP_MULTI_PROCESS: + { + eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s(%p, %d, 0x%08X, %p) FAILED!", + __FUNCTION__, pvCpuVAddr, ui32Bytes, + ui32MappingFlags, hOSMemHandle)); + return eError; + } + break; + } + default: + { + PVR_DPF((PVR_DBG_ERROR, "OSUnMapPhysToLin : invalid flags 0x%x", ui32MappingFlags)); + return PVRSRV_ERROR_INVALID_PARAMS; + } + } + + LinuxMemAreaDeepFree(psLinuxMemArea); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSBaseAllocContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR *pvLinAddr, IMG_CPU_PHYADDR *psPhysAddr) +{ +#if !defined(NO_HARDWARE) + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(pvLinAddr); + PVR_UNREFERENCED_PARAMETER(psPhysAddr); + PVR_DPF((PVR_DBG_ERROR, "%s: Not available", __FUNCTION__)); + + return PVRSRV_ERROR_OUT_OF_MEMORY; +#else + IMG_VOID *pvKernLinAddr; + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + pvKernLinAddr = _KMallocWrapper(ui32Size, GFP_KERNEL, __FILE__, __LINE__); +#else + pvKernLinAddr = KMallocWrapper(ui32Size, GFP_KERNEL); +#endif + if (!pvKernLinAddr) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + *pvLinAddr = pvKernLinAddr; + + psPhysAddr->uiAddr = virt_to_phys(pvKernLinAddr); + + return PVRSRV_OK; +#endif +} + + +PVRSRV_ERROR OSBaseFreeContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR pvLinAddr, IMG_CPU_PHYADDR psPhysAddr) +{ +#if !defined(NO_HARDWARE) + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(pvLinAddr); + PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr); + + PVR_DPF((PVR_DBG_WARNING, "%s: Not available", __FUNCTION__)); +#else + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr); + + KFreeWrapper(pvLinAddr); +#endif + return PVRSRV_OK; +} + +IMG_UINT32 OSReadHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset) +{ +#if !defined(NO_HARDWARE) + return (IMG_UINT32) readl((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset); +#else + return *(IMG_UINT32 *)((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset); +#endif +} + +IMG_VOID OSWriteHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UINT32 ui32Value) +{ +#if !defined(NO_HARDWARE) + writel(ui32Value, (IMG_PBYTE)pvLinRegBaseAddr+ui32Offset); +#else + *(IMG_UINT32 *)((IMG_PBYTE)pvLinRegBaseAddr+ui32Offset) = ui32Value; +#endif +} + +#if defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)) + +PVRSRV_PCI_DEV_HANDLE OSPCISetDev(IMG_VOID *pvPCICookie, HOST_PCI_INIT_FLAGS eFlags) +{ + int err; + IMG_UINT32 i; + PVR_PCI_DEV *psPVRPCI; + + PVR_TRACE(("OSPCISetDev")); + + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psPVRPCI), (IMG_VOID **)&psPVRPCI, IMG_NULL, + "PCI Device") != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCISetDev: Couldn't allocate PVR PCI structure")); + return IMG_NULL; + } + + psPVRPCI->psPCIDev = (struct pci_dev *)pvPCICookie; + psPVRPCI->ePCIFlags = eFlags; + + err = pci_enable_device(psPVRPCI->psPCIDev); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCISetDev: Couldn't enable device (%d)", err)); + return IMG_NULL; + } + + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) + { + pci_set_master(psPVRPCI->psPCIDev); + } + + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI) + { +#if defined(CONFIG_PCI_MSI) + err = pci_enable_msi(psPVRPCI->psPCIDev); + if (err != 0) + { + PVR_DPF((PVR_DBG_WARNING, "OSPCISetDev: Couldn't enable MSI (%d)", err)); + psPVRPCI->ePCIFlags &= ~HOST_PCI_INIT_FLAG_MSI; + } +#else + PVR_DPF((PVR_DBG_WARNING, "OSPCISetDev: MSI support not enabled in the kernel")); +#endif + } + + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + { + psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE; + } + + return (PVRSRV_PCI_DEV_HANDLE)psPVRPCI; +} + +PVRSRV_PCI_DEV_HANDLE OSPCIAcquireDev(IMG_UINT16 ui16VendorID, IMG_UINT16 ui16DeviceID, HOST_PCI_INIT_FLAGS eFlags) +{ + struct pci_dev *psPCIDev; + + psPCIDev = pci_get_device(ui16VendorID, ui16DeviceID, NULL); + if (psPCIDev == NULL) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIAcquireDev: Couldn't acquire device")); + return IMG_NULL; + } + + return OSPCISetDev((IMG_VOID *)psPCIDev, eFlags); +} + +PVRSRV_ERROR OSPCIIRQ(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 *pui32IRQ) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + + *pui32IRQ = psPVRPCI->psPCIDev->irq; + + return PVRSRV_OK; +} + +enum HOST_PCI_ADDR_RANGE_FUNC +{ + HOST_PCI_ADDR_RANGE_FUNC_LEN, + HOST_PCI_ADDR_RANGE_FUNC_START, + HOST_PCI_ADDR_RANGE_FUNC_END, + HOST_PCI_ADDR_RANGE_FUNC_REQUEST, + HOST_PCI_ADDR_RANGE_FUNC_RELEASE +}; + +static IMG_UINT32 OSPCIAddrRangeFunc(enum HOST_PCI_ADDR_RANGE_FUNC eFunc, + PVRSRV_PCI_DEV_HANDLE hPVRPCI, + IMG_UINT32 ui32Index) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + + if (ui32Index >= DEVICE_COUNT_RESOURCE) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: Index out of range")); + return 0; + + } + + switch (eFunc) + { + case HOST_PCI_ADDR_RANGE_FUNC_LEN: + return pci_resource_len(psPVRPCI->psPCIDev, ui32Index); + case HOST_PCI_ADDR_RANGE_FUNC_START: + return pci_resource_start(psPVRPCI->psPCIDev, ui32Index); + case HOST_PCI_ADDR_RANGE_FUNC_END: + return pci_resource_end(psPVRPCI->psPCIDev, ui32Index); + case HOST_PCI_ADDR_RANGE_FUNC_REQUEST: + { + int err; + + err = pci_request_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index, PVRSRV_MODNAME); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: pci_request_region_failed (%d)", err)); + return 0; + } + psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_TRUE; + return 1; + } + case HOST_PCI_ADDR_RANGE_FUNC_RELEASE: + if (psPVRPCI->abPCIResourceInUse[ui32Index]) + { + pci_release_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index); + psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_FALSE; + } + return 1; + default: + PVR_DPF((PVR_DBG_ERROR, "OSPCIAddrRangeFunc: Unknown function")); + break; + } + + return 0; +} + +IMG_UINT32 OSPCIAddrRangeLen(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_LEN, hPVRPCI, ui32Index); +} + +IMG_UINT32 OSPCIAddrRangeStart(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_START, hPVRPCI, ui32Index); +} + +IMG_UINT32 OSPCIAddrRangeEnd(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_END, hPVRPCI, ui32Index); +} + +PVRSRV_ERROR OSPCIRequestAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI, + IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_REQUEST, hPVRPCI, ui32Index) == 0 ? PVRSRV_ERROR_PCI_CALL_FAILED : PVRSRV_OK; +} + +PVRSRV_ERROR OSPCIReleaseAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_RELEASE, hPVRPCI, ui32Index) == 0 ? PVRSRV_ERROR_PCI_CALL_FAILED : PVRSRV_OK; +} + +PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + int i; + + PVR_TRACE(("OSPCIReleaseDev")); + + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + { + if (psPVRPCI->abPCIResourceInUse[i]) + { + PVR_TRACE(("OSPCIReleaseDev: Releasing Address range %d", i)); + pci_release_region(psPVRPCI->psPCIDev, i); + psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE; + } + } + +#if defined(CONFIG_PCI_MSI) + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI) + { + pci_disable_msi(psPVRPCI->psPCIDev); + } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) + { + pci_clear_master(psPVRPCI->psPCIDev); + } +#endif + pci_disable_device(psPVRPCI->psPCIDev); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psPVRPCI), (IMG_VOID *)psPVRPCI, IMG_NULL); + + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + int i; + int err; + + PVR_TRACE(("OSPCISuspendDev")); + + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + { + if (psPVRPCI->abPCIResourceInUse[i]) + { + pci_release_region(psPVRPCI->psPCIDev, i); + } + } + + err = pci_save_state(psPVRPCI->psPCIDev); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: pci_save_state_failed (%d)", err)); + return PVRSRV_ERROR_PCI_CALL_FAILED; + } + + pci_disable_device(psPVRPCI->psPCIDev); + + err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_SUSPEND)); + switch(err) + { + case 0: + break; + case -EIO: + PVR_DPF((PVR_DBG_WARNING, "OSPCISuspendDev: device doesn't support PCI PM")); + break; + case -EINVAL: + PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: can't enter requested power state")); + break; + default: + PVR_DPF((PVR_DBG_ERROR, "OSPCISuspendDev: pci_set_power_state failed (%d)", err)); + break; + } + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSPCIResumeDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + int err; + int i; + + PVR_TRACE(("OSPCIResumeDev")); + + err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_ON)); + switch(err) + { + case 0: + break; + case -EIO: + PVR_DPF((PVR_DBG_WARNING, "OSPCIResumeDev: device doesn't support PCI PM")); + break; + case -EINVAL: + PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: can't enter requested power state")); + return PVRSRV_ERROR_UNKNOWN_POWER_STATE; + default: + PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_set_power_state failed (%d)", err)); + return PVRSRV_ERROR_UNKNOWN_POWER_STATE; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)) + pci_restore_state(psPVRPCI->psPCIDev); +#else + err = pci_restore_state(psPVRPCI->psPCIDev); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_restore_state failed (%d)", err)); + return PVRSRV_ERROR_PCI_CALL_FAILED; + } +#endif + + err = pci_enable_device(psPVRPCI->psPCIDev); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: Couldn't enable device (%d)", err)); + return PVRSRV_ERROR_PCI_CALL_FAILED; + } + + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) + pci_set_master(psPVRPCI->psPCIDev); + + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + { + if (psPVRPCI->abPCIResourceInUse[i]) + { + err = pci_request_region(psPVRPCI->psPCIDev, i, PVRSRV_MODNAME); + if (err != 0) + { + PVR_DPF((PVR_DBG_ERROR, "OSPCIResumeDev: pci_request_region_failed (region %d, error %d)", i, err)); + } + } + + } + + return PVRSRV_OK; +} + +#endif + +#define OS_MAX_TIMERS 8 + +typedef struct TIMER_CALLBACK_DATA_TAG +{ + IMG_BOOL bInUse; + PFN_TIMER_FUNC pfnTimerFunc; + IMG_VOID *pvData; + struct timer_list sTimer; + IMG_UINT32 ui32Delay; + IMG_BOOL bActive; +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + struct work_struct sWork; +#endif +}TIMER_CALLBACK_DATA; + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) +static struct workqueue_struct *psTimerWorkQueue; +#endif + +static TIMER_CALLBACK_DATA sTimers[OS_MAX_TIMERS]; + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) +DEFINE_MUTEX(sTimerStructLock); +#else +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +static spinlock_t sTimerStructLock = SPIN_LOCK_UNLOCKED; +#else +static spinlock_t sTimerStructLock = __SPIN_LOCK_UNLOCKED(sTimerStructLock); +#endif +#endif + +static void OSTimerCallbackBody(TIMER_CALLBACK_DATA *psTimerCBData) +{ + if (!psTimerCBData->bActive) + return; + + + psTimerCBData->pfnTimerFunc(psTimerCBData->pvData); + + + mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies); +} + + +static IMG_VOID OSTimerCallbackWrapper(IMG_UINT32 ui32Data) +{ + TIMER_CALLBACK_DATA *psTimerCBData = (TIMER_CALLBACK_DATA*)ui32Data; + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + int res; + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + res = queue_work(psTimerWorkQueue, &psTimerCBData->sWork); +#else + res = schedule_work(&psTimerCBData->sWork); +#endif + if (res == 0) + { + PVR_DPF((PVR_DBG_WARNING, "OSTimerCallbackWrapper: work already queued")); + } +#else + OSTimerCallbackBody(psTimerCBData); +#endif +} + + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) +static void OSTimerWorkQueueCallBack(struct work_struct *psWork) +{ + TIMER_CALLBACK_DATA *psTimerCBData = container_of(psWork, TIMER_CALLBACK_DATA, sWork); + + OSTimerCallbackBody(psTimerCBData); +} +#endif + +IMG_HANDLE OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, IMG_VOID *pvData, IMG_UINT32 ui32MsTimeout) +{ + TIMER_CALLBACK_DATA *psTimerCBData; + IMG_UINT32 ui32i; +#if !(defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE)) + unsigned long ulLockFlags; +#endif + + + if(!pfnTimerFunc) + { + PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: passed invalid callback")); + return IMG_NULL; + } + + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + mutex_lock(&sTimerStructLock); +#else + spin_lock_irqsave(&sTimerStructLock, ulLockFlags); +#endif + for (ui32i = 0; ui32i < OS_MAX_TIMERS; ui32i++) + { + psTimerCBData = &sTimers[ui32i]; + if (!psTimerCBData->bInUse) + { + psTimerCBData->bInUse = IMG_TRUE; + break; + } + } +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + mutex_unlock(&sTimerStructLock); +#else + spin_unlock_irqrestore(&sTimerStructLock, ulLockFlags); +#endif + if (ui32i >= OS_MAX_TIMERS) + { + PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: all timers are in use")); + return IMG_NULL; + } + + psTimerCBData->pfnTimerFunc = pfnTimerFunc; + psTimerCBData->pvData = pvData; + psTimerCBData->bActive = IMG_FALSE; + + + + + psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000) + ? 1 + : ((HZ * ui32MsTimeout) / 1000); + + init_timer(&psTimerCBData->sTimer); + + + + psTimerCBData->sTimer.function = (IMG_VOID *)OSTimerCallbackWrapper; + psTimerCBData->sTimer.data = (IMG_UINT32)psTimerCBData; + + return (IMG_HANDLE)(ui32i + 1); +} + + +static inline TIMER_CALLBACK_DATA *GetTimerStructure(IMG_HANDLE hTimer) +{ + IMG_UINT32 ui32i = ((IMG_UINT32)hTimer) - 1; + + PVR_ASSERT(ui32i < OS_MAX_TIMERS); + + return &sTimers[ui32i]; +} + +PVRSRV_ERROR OSRemoveTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(!psTimerCBData->bActive); + + + psTimerCBData->bInUse = IMG_FALSE; + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSEnableTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(!psTimerCBData->bActive); + + + psTimerCBData->bActive = IMG_TRUE; + + + psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies; + + + add_timer(&psTimerCBData->sTimer); + + return PVRSRV_OK; +} + + +PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(psTimerCBData->bActive); + + + psTimerCBData->bActive = IMG_FALSE; + smp_mb(); + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + flush_workqueue(psTimerWorkQueue); +#endif +#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + flush_scheduled_work(); +#endif + + + del_timer_sync(&psTimerCBData->sTimer); + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + + flush_workqueue(psTimerWorkQueue); +#endif +#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + flush_scheduled_work(); +#endif + + return PVRSRV_OK; +} + + +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT_KM *psEventObject) +#else +PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT *psEventObject) +#endif +{ + + PVRSRV_ERROR eError = PVRSRV_OK; + + if(psEventObject) + { + if(pszName) + { + + strncpy(psEventObject->szName, pszName, EVENTOBJNAME_MAXLENGTH); + } + else + { + + static IMG_UINT16 ui16NameIndex = 0; +#if defined (SUPPORT_SID_INTERFACE) + snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH, "PVRSRV_EVENTOBJECT_KM_%d", ui16NameIndex++); +#else + snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH, "PVRSRV_EVENTOBJECT_%d", ui16NameIndex++); +#endif + } + + if(LinuxEventObjectListCreate(&psEventObject->hOSEventKM) != PVRSRV_OK) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + } + + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectCreateKM: psEventObject is not a valid pointer")); + eError = PVRSRV_ERROR_UNABLE_TO_CREATE_EVENT; + } + + return eError; + +} + + +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR OSEventObjectDestroyKM(PVRSRV_EVENTOBJECT_KM *psEventObject) +#else +PVRSRV_ERROR OSEventObjectDestroyKM(PVRSRV_EVENTOBJECT *psEventObject) +#endif +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if(psEventObject) + { + if(psEventObject->hOSEventKM) + { + LinuxEventObjectListDestroy(psEventObject->hOSEventKM); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: hOSEventKM is not a valid pointer")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: psEventObject is not a valid pointer")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + return eError; +} + +PVRSRV_ERROR OSEventObjectWaitKM(IMG_HANDLE hOSEventKM) +{ + PVRSRV_ERROR eError; + + if(hOSEventKM) + { + eError = LinuxEventObjectWait(hOSEventKM, EVENT_OBJECT_TIMEOUT_MS); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectWaitKM: hOSEventKM is not a valid handle")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + return eError; +} + +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR OSEventObjectOpenKM(PVRSRV_EVENTOBJECT_KM *psEventObject, +#else +PVRSRV_ERROR OSEventObjectOpenKM(PVRSRV_EVENTOBJECT *psEventObject, +#endif + IMG_HANDLE *phOSEvent) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if(psEventObject) + { + if(LinuxEventObjectAdd(psEventObject->hOSEventKM, phOSEvent) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: failed")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectCreateKM: psEventObject is not a valid pointer")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + return eError; +} + +#if defined (SUPPORT_SID_INTERFACE) +PVRSRV_ERROR OSEventObjectCloseKM(PVRSRV_EVENTOBJECT_KM *psEventObject, +#else +PVRSRV_ERROR OSEventObjectCloseKM(PVRSRV_EVENTOBJECT *psEventObject, +#endif + IMG_HANDLE hOSEventKM) +{ + PVRSRV_ERROR eError = PVRSRV_OK; + + if(psEventObject) + { + if(LinuxEventObjectDelete(psEventObject->hOSEventKM, hOSEventKM) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectDelete: failed")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectDestroyKM: psEventObject is not a valid pointer")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + return eError; + +} + +PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM) +{ + PVRSRV_ERROR eError; + + if(hOSEventKM) + { + eError = LinuxEventObjectSignal(hOSEventKM); + } + else + { + PVR_DPF((PVR_DBG_ERROR, "OSEventObjectSignalKM: hOSEventKM is not a valid handle")); + eError = PVRSRV_ERROR_INVALID_PARAMS; + } + + return eError; +} + +IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID) +{ + return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE; +} + +PVRSRV_ERROR OSCopyToUser(IMG_PVOID pvProcess, + IMG_VOID *pvDest, + IMG_VOID *pvSrc, + IMG_UINT32 ui32Bytes) +{ + PVR_UNREFERENCED_PARAMETER(pvProcess); + + if(pvr_copy_to_user(pvDest, pvSrc, ui32Bytes)==0) + return PVRSRV_OK; + else + return PVRSRV_ERROR_FAILED_TO_COPY_VIRT_MEMORY; +} + +PVRSRV_ERROR OSCopyFromUser( IMG_PVOID pvProcess, + IMG_VOID *pvDest, + IMG_VOID *pvSrc, + IMG_UINT32 ui32Bytes) +{ + PVR_UNREFERENCED_PARAMETER(pvProcess); + + if(pvr_copy_from_user(pvDest, pvSrc, ui32Bytes)==0) + return PVRSRV_OK; + else + return PVRSRV_ERROR_FAILED_TO_COPY_VIRT_MEMORY; +} + +IMG_BOOL OSAccessOK(IMG_VERIFY_TEST eVerification, IMG_VOID *pvUserPtr, IMG_UINT32 ui32Bytes) +{ + IMG_INT linuxType; + + if (eVerification == PVR_VERIFY_READ) + { + linuxType = VERIFY_READ; + } + else + { + PVR_ASSERT(eVerification == PVR_VERIFY_WRITE); + linuxType = VERIFY_WRITE; + } + + return access_ok(linuxType, pvUserPtr, ui32Bytes); +} + +typedef enum _eWrapMemType_ +{ + WRAP_TYPE_NULL = 0, + WRAP_TYPE_GET_USER_PAGES, + WRAP_TYPE_FIND_VMA +} eWrapMemType; + +typedef struct _sWrapMemInfo_ +{ + eWrapMemType eType; + IMG_INT iNumPages; + IMG_INT iNumPagesMapped; + struct page **ppsPages; + IMG_SYS_PHYADDR *psPhysAddr; + IMG_INT iPageOffset; +#if defined(DEBUG) + IMG_UINT32 ulStartAddr; + IMG_UINT32 ulBeyondEndAddr; + struct vm_area_struct *psVMArea; +#endif +} sWrapMemInfo; + + +static IMG_BOOL CPUVAddrToPFN(struct vm_area_struct *psVMArea, IMG_UINT32 ulCPUVAddr, IMG_UINT32 *pulPFN, struct page **ppsPage) +{ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10)) + pgd_t *psPGD; + pud_t *psPUD; + pmd_t *psPMD; + pte_t *psPTE; + struct mm_struct *psMM = psVMArea->vm_mm; + spinlock_t *psPTLock; + IMG_BOOL bRet = IMG_FALSE; + + *pulPFN = 0; + *ppsPage = NULL; + + psPGD = pgd_offset(psMM, ulCPUVAddr); + if (pgd_none(*psPGD) || pgd_bad(*psPGD)) + return bRet; + + psPUD = pud_offset(psPGD, ulCPUVAddr); + if (pud_none(*psPUD) || pud_bad(*psPUD)) + return bRet; + + psPMD = pmd_offset(psPUD, ulCPUVAddr); + if (pmd_none(*psPMD) || pmd_bad(*psPMD)) + return bRet; + + psPTE = (pte_t *)pte_offset_map_lock(psMM, psPMD, ulCPUVAddr, &psPTLock); + + if ((pte_none(*psPTE) == 0) && (pte_present(*psPTE) != 0) && (pte_write(*psPTE) != 0)) + { + *pulPFN = pte_pfn(*psPTE); + bRet = IMG_TRUE; + + if (pfn_valid(*pulPFN)) + { + *ppsPage = pfn_to_page(*pulPFN); + + get_page(*ppsPage); + } + } + + pte_unmap_unlock(psPTE, psPTLock); + + return bRet; +#else + return IMG_FALSE; +#endif +} + +PVRSRV_ERROR OSReleasePhysPageAddr(IMG_HANDLE hOSWrapMem) +{ + sWrapMemInfo *psInfo = (sWrapMemInfo *)hOSWrapMem; + IMG_INT i; + + if (psInfo == IMG_NULL) + { + PVR_DPF((PVR_DBG_WARNING, + "OSReleasePhysPageAddr: called with null wrap handle")); + return PVRSRV_OK; + } + + switch (psInfo->eType) + { + case WRAP_TYPE_NULL: + { + PVR_DPF((PVR_DBG_WARNING, + "OSReleasePhysPageAddr: called with wrap type WRAP_TYPE_NULL")); + break; + } + case WRAP_TYPE_GET_USER_PAGES: + { + for (i = 0; i < psInfo->iNumPagesMapped; i++) + { + struct page *psPage = psInfo->ppsPages[i]; + + PVR_ASSERT(psPage != NULL); + + + if (psInfo->iNumPagesMapped == psInfo->iNumPages) + { + if (!PageReserved(psPage)) + { + SetPageDirty(psPage); + } + } + page_cache_release(psPage); + } + break; + } + case WRAP_TYPE_FIND_VMA: + { + for (i = 0; i < psInfo->iNumPages; i++) + { + if (psInfo->ppsPages[i] != IMG_NULL) + { + put_page(psInfo->ppsPages[i]); + } + } + break; + } + default: + { + PVR_DPF((PVR_DBG_ERROR, + "OSReleasePhysPageAddr: Unknown wrap type (%d)", psInfo->eType)); + return PVRSRV_ERROR_INVALID_WRAP_TYPE; + } + } + + if (psInfo->ppsPages != IMG_NULL) + { + kfree(psInfo->ppsPages); + } + + if (psInfo->psPhysAddr != IMG_NULL) + { + kfree(psInfo->psPhysAddr); + } + + kfree(psInfo); + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, + IMG_UINT32 ui32Bytes, + IMG_SYS_PHYADDR *psSysPAddr, + IMG_HANDLE *phOSWrapMem) +{ + IMG_UINT32 ulStartAddrOrig = (IMG_UINT32) pvCPUVAddr; + IMG_UINT32 ulAddrRangeOrig = (IMG_UINT32) ui32Bytes; + IMG_UINT32 ulBeyondEndAddrOrig = ulStartAddrOrig + ulAddrRangeOrig; + IMG_UINT32 ulStartAddr; + IMG_UINT32 ulAddrRange; + IMG_UINT32 ulBeyondEndAddr; + IMG_UINT32 ulAddr; + IMG_INT i; + struct vm_area_struct *psVMArea; + sWrapMemInfo *psInfo = NULL; + IMG_BOOL bHavePageStructs = IMG_FALSE; + IMG_BOOL bHaveNoPageStructs = IMG_FALSE; + IMG_BOOL bPFNMismatch = IMG_FALSE; + IMG_BOOL bMMapSemHeld = IMG_FALSE; + PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY; + + + ulStartAddr = ulStartAddrOrig & PAGE_MASK; + ulBeyondEndAddr = PAGE_ALIGN(ulBeyondEndAddrOrig); + ulAddrRange = ulBeyondEndAddr - ulStartAddr; + + + if (ulBeyondEndAddr <= ulStartAddr) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Invalid address range (start %x, length %x)", + ulStartAddrOrig, ulAddrRangeOrig)); + goto error; + } + + + psInfo = kmalloc(sizeof(*psInfo), GFP_KERNEL); + if (psInfo == NULL) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Couldn't allocate information structure")); + goto error; + } + memset(psInfo, 0, sizeof(*psInfo)); + +#if defined(DEBUG) + psInfo->ulStartAddr = ulStartAddrOrig; + psInfo->ulBeyondEndAddr = ulBeyondEndAddrOrig; +#endif + + psInfo->iNumPages = (IMG_INT)(ulAddrRange >> PAGE_SHIFT); + psInfo->iPageOffset = (IMG_INT)(ulStartAddrOrig & ~PAGE_MASK); + + + psInfo->psPhysAddr = kmalloc((size_t)psInfo->iNumPages * sizeof(*psInfo->psPhysAddr), GFP_KERNEL); + if (psInfo->psPhysAddr == NULL) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Couldn't allocate page array")); + goto error; + } + memset(psInfo->psPhysAddr, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->psPhysAddr)); + + + psInfo->ppsPages = kmalloc((size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages), GFP_KERNEL); + if (psInfo->ppsPages == NULL) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Couldn't allocate page array")); + goto error; + } + memset(psInfo->ppsPages, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages)); + + + eError = PVRSRV_ERROR_BAD_MAPPING; + + + psInfo->eType = WRAP_TYPE_GET_USER_PAGES; + + + down_read(¤t->mm->mmap_sem); + bMMapSemHeld = IMG_TRUE; + + + psInfo->iNumPagesMapped = get_user_pages(current, current->mm, ulStartAddr, psInfo->iNumPages, 1, 0, psInfo->ppsPages, NULL); + + if (psInfo->iNumPagesMapped >= 0) + { + + if (psInfo->iNumPagesMapped != psInfo->iNumPages) + { + PVR_TRACE(("OSAcquirePhysPageAddr: Couldn't map all the pages needed (wanted: %d, got %d)", psInfo->iNumPages, psInfo->iNumPagesMapped)); + + goto error; + } + + + for (i = 0; i < psInfo->iNumPages; i++) + { + IMG_CPU_PHYADDR CPUPhysAddr; + IMG_UINT32 ulPFN; + + ulPFN = page_to_pfn(psInfo->ppsPages[i]); + CPUPhysAddr.uiAddr = ulPFN << PAGE_SHIFT; + if ((CPUPhysAddr.uiAddr >> PAGE_SHIFT) != ulPFN) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Page frame number out of range (%x)", ulPFN)); + + goto error; + } + psInfo->psPhysAddr[i] = SysCpuPAddrToSysPAddr(CPUPhysAddr); + psSysPAddr[i] = psInfo->psPhysAddr[i]; + + } + + goto exit; + } + + PVR_DPF((PVR_DBG_MESSAGE, "OSAcquirePhysPageAddr: get_user_pages failed (%d), using CPU page table", psInfo->iNumPagesMapped)); + + + psInfo->eType = WRAP_TYPE_NULL; + psInfo->iNumPagesMapped = 0; + memset(psInfo->ppsPages, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages)); + + + + psInfo->eType = WRAP_TYPE_FIND_VMA; + + psVMArea = find_vma(current->mm, ulStartAddrOrig); + if (psVMArea == NULL) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Couldn't find memory region containing start address %x", ulStartAddrOrig)); + + goto error; + } +#if defined(DEBUG) + psInfo->psVMArea = psVMArea; +#endif + + + if (ulStartAddrOrig < psVMArea->vm_start) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Start address %x is outside of the region returned by find_vma", ulStartAddrOrig)); + goto error; + } + + + if (ulBeyondEndAddrOrig > psVMArea->vm_end) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: End address %x is outside of the region returned by find_vma", ulBeyondEndAddrOrig)); + goto error; + } + + + if ((psVMArea->vm_flags & (VM_IO | VM_RESERVED)) != (VM_IO | VM_RESERVED)) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Memory region does not represent memory mapped I/O (VMA flags: 0x%lx)", psVMArea->vm_flags)); + goto error; + } + + + if ((psVMArea->vm_flags & (VM_READ | VM_WRITE)) != (VM_READ | VM_WRITE)) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: No read/write access to memory region (VMA flags: 0x%lx)", psVMArea->vm_flags)); + goto error; + } + + for (ulAddr = ulStartAddrOrig, i = 0; ulAddr < ulBeyondEndAddrOrig; ulAddr += PAGE_SIZE, i++) + { + IMG_CPU_PHYADDR CPUPhysAddr; + IMG_UINT32 ulPFN = 0; + + PVR_ASSERT(i < psInfo->iNumPages); + + if (!CPUVAddrToPFN(psVMArea, ulAddr, &ulPFN, &psInfo->ppsPages[i])) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Invalid CPU virtual address")); + + goto error; + } + if (psInfo->ppsPages[i] == NULL) + { + + bHaveNoPageStructs = IMG_TRUE; + +#if defined(VM_PFNMAP) + if ((psVMArea->vm_flags & VM_PFNMAP) != 0) + { + IMG_UINT32 ulPFNRaw = ((ulAddr - psVMArea->vm_start) >> PAGE_SHIFT) + psVMArea->vm_pgoff; + + if (ulPFNRaw != ulPFN) + { + bPFNMismatch = IMG_TRUE; + } + } +#endif + } + else + { + bHavePageStructs = IMG_TRUE; + + psInfo->iNumPagesMapped++; + + PVR_ASSERT(ulPFN == page_to_pfn(psInfo->ppsPages[i])); + } + + CPUPhysAddr.uiAddr = ulPFN << PAGE_SHIFT; + if ((CPUPhysAddr.uiAddr >> PAGE_SHIFT) != ulPFN) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Page frame number out of range (%x)", ulPFN)); + + goto error; + } + + psInfo->psPhysAddr[i] = SysCpuPAddrToSysPAddr(CPUPhysAddr); + psSysPAddr[i] = psInfo->psPhysAddr[i]; + } + PVR_ASSERT(i == psInfo->iNumPages); + +#if defined(VM_MIXEDMAP) + if ((psVMArea->vm_flags & VM_MIXEDMAP) != 0) + { + goto exit; + } +#endif + + if (bHavePageStructs && bHaveNoPageStructs) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Region is VM_MIXEDMAP, but isn't marked as such")); + goto error; + } + + if (!bHaveNoPageStructs) + { + + goto exit; + } + +#if defined(VM_PFNMAP) + if ((psVMArea->vm_flags & VM_PFNMAP) == 0) +#endif + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Region is VM_PFNMAP, but isn't marked as such")); + goto error; + } + + if (bPFNMismatch) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: PFN calculation mismatch for VM_PFNMAP region")); + goto error; + } + +exit: + PVR_ASSERT(bMMapSemHeld); + up_read(¤t->mm->mmap_sem); + + + *phOSWrapMem = (IMG_HANDLE)psInfo; + + if (bHaveNoPageStructs) + { + PVR_DPF((PVR_DBG_WARNING, + "OSAcquirePhysPageAddr: Region contains pages which can't be locked down (no page structures)")); + } + + PVR_ASSERT(psInfo->eType != 0); + +#if 0 + + + OSCleanCPUCacheRangeKM(pvCPUVAddr, (IMG_VOID *)((IMG_CHAR *)pvCPUVAddr + ui32Bytes)); +#endif + + return PVRSRV_OK; + +error: + if (bMMapSemHeld) + { + up_read(¤t->mm->mmap_sem); + } + OSReleasePhysPageAddr((IMG_HANDLE)psInfo); + + PVR_ASSERT(eError != PVRSRV_OK); + + return eError; +} + +typedef void (*InnerCacheOp_t)(const void *pvStart, const void *pvEnd); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +typedef void (*OuterCacheOp_t)(unsigned long ulStart, unsigned long ulEnd); +#else +typedef void (*OuterCacheOp_t)(phys_addr_t ulStart, phys_addr_t ulEnd); +#endif + +#if defined(CONFIG_OUTER_CACHE) + +typedef unsigned long (*MemAreaToPhys_t)(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum); + +static unsigned long VMallocAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum) +{ + return vmalloc_to_pfn(pvRangeAddrStart + ui32PageNum * PAGE_SIZE) << PAGE_SHIFT; +} + +static unsigned long ExternalKVAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum) +{ + IMG_SYS_PHYADDR SysPAddr; + IMG_CPU_PHYADDR CpuPAddr; + SysPAddr = psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr[ui32PageNumOffset + ui32PageNum]; + CpuPAddr = SysSysPAddrToCpuPAddr(SysPAddr); + return CpuPAddr.uiAddr; +} + +static unsigned long AllocPagesAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum) +{ + struct page *pPage; + pPage = psLinuxMemArea->uData.sPageList.pvPageList[ui32PageNumOffset + ui32PageNum]; + return page_to_pfn(pPage) << PAGE_SHIFT; +} + +#endif + +#ifndef __mips__ +static +IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, + IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) +{ + PKV_OFFSET_STRUCT psOffsetStruct; + IMG_VOID *pvMinVAddr; + + + list_for_each_entry(psOffsetStruct, psMMapOffsetStructList, sAreaItem) + { + if(OSGetCurrentProcessIDKM() != psOffsetStruct->ui32PID) + continue; + + pvMinVAddr = (IMG_VOID *)psOffsetStruct->ui32UserVAddr; + + + if(pvRangeAddrStart >= pvMinVAddr && + ui32Length <= psOffsetStruct->ui32RealByteSize) + return pvMinVAddr; + } + + return IMG_NULL; +} + +static +IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length, + InnerCacheOp_t pfnInnerCacheOp, + OuterCacheOp_t pfnOuterCacheOp) +{ + LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + IMG_UINT32 ui32AreaLength, ui32AreaOffset = 0; + struct list_head *psMMapOffsetStructList; + IMG_VOID *pvMinVAddr; + +#if defined(CONFIG_OUTER_CACHE) + MemAreaToPhys_t pfnMemAreaToPhys = IMG_NULL; + IMG_UINT32 ui32PageNumOffset = 0; +#endif + + PVR_ASSERT(psLinuxMemArea != IMG_NULL); + + ui32AreaLength = psLinuxMemArea->ui32ByteSize; + psMMapOffsetStructList = &psLinuxMemArea->sMMapOffsetStructList; + + PVR_ASSERT(ui32Length <= ui32AreaLength); + + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + ui32AreaOffset = psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset; + psLinuxMemArea = psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea; + } + + + PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); + + switch(psLinuxMemArea->eAreaType) + { + case LINUX_MEM_AREA_VMALLOC: + { + if(is_vmalloc_addr(pvRangeAddrStart)) + { + pvMinVAddr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress + ui32AreaOffset; + + + if(pvRangeAddrStart < pvMinVAddr) + goto err_blocked; + + pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + } + else + { + + + + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + +#if defined(CONFIG_OUTER_CACHE) + + pvRangeAddrStart = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress + + (ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr); + } + + pfnMemAreaToPhys = VMallocAreaToPhys; +#else + } +#endif + break; + } + + case LINUX_MEM_AREA_EXTERNAL_KV: + { + + if (psLinuxMemArea->uData.sExternalKV.bPhysContig == IMG_TRUE) + { + PVR_DPF((PVR_DBG_WARNING, "%s: Attempt to flush contiguous external memory", __func__)); + + goto err_blocked; + } + + + if (psLinuxMemArea->uData.sExternalKV.pvExternalKV != IMG_NULL) + { + PVR_DPF((PVR_DBG_WARNING, "%s: Attempt to flush external memory with a kernel virtual address", __func__)); + + goto err_blocked; + } + + + + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + +#if defined(CONFIG_OUTER_CACHE) + ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; + pfnMemAreaToPhys = ExternalKVAreaToPhys; +#endif + break; + } + + case LINUX_MEM_AREA_ALLOC_PAGES: + { + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + +#if defined(CONFIG_OUTER_CACHE) + ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; + pfnMemAreaToPhys = AllocPagesAreaToPhys; +#endif + break; + } + + default: + PVR_DBG_BREAK; + } + +#if defined(CONFIG_OUTER_CACHE) + PVR_ASSERT(pfnMemAreaToPhys != IMG_NULL); + + + { + unsigned long ulStart, ulEnd, ulLength, ulStartOffset, ulEndOffset; + IMG_UINT32 i, ui32NumPages; + + + ulLength = (unsigned long)ui32Length; + ulStartOffset = ((unsigned long)pvRangeAddrStart) & (PAGE_SIZE - 1); + ulEndOffset = ((unsigned long)pvRangeAddrStart + ulLength) & (PAGE_SIZE - 1); + + + ui32NumPages = (ulStartOffset + ulLength + PAGE_SIZE - 1) >> PAGE_SHIFT; + + for(i = 0; i < ui32NumPages; i++) + { + ulStart = pfnMemAreaToPhys(psLinuxMemArea, pvRangeAddrStart, + ui32PageNumOffset, i); + ulEnd = ulStart + PAGE_SIZE; + + if(i == ui32NumPages - 1 && ulEndOffset != 0) + ulEnd = ulStart + ulEndOffset; + + if(i == 0) + ulStart += ulStartOffset; + + pfnOuterCacheOp(ulStart, ulEnd); + } + } +#endif + + return IMG_TRUE; + +err_blocked: + PVR_DPF((PVR_DBG_WARNING, "%s: Blocked cache op on virtual range " + "%p-%p (type %d)", __func__, + pvRangeAddrStart, pvRangeAddrStart + ui32Length, + psLinuxMemArea->eAreaType)); + return IMG_FALSE; +} + +#endif + +#if defined(__i386__) + +#define ROUND_UP(x,a) (((x) + (a) - 1) & ~((a) - 1)) + +static void per_cpu_cache_flush(void *arg) +{ + PVR_UNREFERENCED_PARAMETER(arg); + wbinvd(); +} + +static void x86_flush_cache_range(const void *pvStart, const void *pvEnd) +{ + IMG_BYTE *pbStart = (IMG_BYTE *)pvStart; + IMG_BYTE *pbEnd = (IMG_BYTE *)pvEnd; + IMG_BYTE *pbBase; + + pbEnd = (IMG_BYTE *)ROUND_UP((IMG_UINTPTR_T)pbEnd, + boot_cpu_data.x86_clflush_size); + + mb(); + for(pbBase = pbStart; pbBase < pbEnd; pbBase += boot_cpu_data.x86_clflush_size) + clflush(pbBase); + mb(); +} + +IMG_VOID OSCleanCPUCacheKM(IMG_VOID) +{ + + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +} + +IMG_VOID OSFlushCPUCacheKM(IMG_VOID) +{ + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +} + +IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + + return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + + return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + + return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +#else + +#if defined(__arm__) + +static void per_cpu_cache_flush(void *arg) +{ + PVR_UNREFERENCED_PARAMETER(arg); + flush_cache_all(); +} + +IMG_VOID OSCleanCPUCacheKM(IMG_VOID) +{ + + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +#if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS) + outer_clean_all(); +#endif +} + +IMG_VOID OSFlushCPUCacheKM(IMG_VOID) +{ + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +#if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS) + outer_flush_all(); +#endif +} + +static inline size_t pvr_dmac_range_len(const void *pvStart, const void *pvEnd) +{ + return (size_t)((char *)pvEnd - (char *)pvStart); +} + +static void pvr_dmac_inv_range(const void *pvStart, const void *pvEnd) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) + dmac_inv_range(pvStart, pvEnd); +#else + dmac_map_area(pvStart, pvr_dmac_range_len(pvStart, pvEnd), DMA_FROM_DEVICE); +#endif +} + +static void pvr_dmac_clean_range(const void *pvStart, const void *pvEnd) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) + dmac_clean_range(pvStart, pvEnd); +#else + dmac_map_area(pvStart, pvr_dmac_range_len(pvStart, pvEnd), DMA_TO_DEVICE); +#endif +} + +IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + dmac_flush_range, outer_flush_range); +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + IMG_BOOL retval = IMG_TRUE; +#if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS) + if (ui32Length > PVR_FULL_CACHE_OP_THRESHOLD) + OSCleanCPUCacheKM(); + else +#endif + { + retval = CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + pvr_dmac_clean_range, outer_clean_range); + } + return retval; +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + pvr_dmac_inv_range, outer_inv_range); +} + +#else + +#if defined(__mips__) +IMG_VOID OSCleanCPUCacheKM(IMG_VOID) +{ + + dma_cache_wback(0, 0x100000); +} + +IMG_VOID OSFlushCPUCacheKM(IMG_VOID) +{ + + dma_cache_wback_inv(0, 0x100000); +} + +IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + if (ui32Length) + dma_cache_wback_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); + return IMG_TRUE; +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + if (ui32Length) + dma_cache_wback((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); + return IMG_TRUE; +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + if (ui32Length) + dma_cache_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); + return IMG_TRUE; +} + +#else + +#error "Implement CPU cache flush/clean/invalidate primitives for this CPU!" + +#endif + +#endif + +#endif + +PVRSRV_ERROR PVROSFuncInit(IMG_VOID) +{ +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + { + psTimerWorkQueue = create_workqueue("pvr_timer"); + if (psTimerWorkQueue == NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: couldn't create timer workqueue", __FUNCTION__)); + return PVRSRV_ERROR_UNABLE_TO_CREATE_THREAD; + + } + } +#endif + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) || defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + { + IMG_UINT32 ui32i; + + for (ui32i = 0; ui32i < OS_MAX_TIMERS; ui32i++) + { + TIMER_CALLBACK_DATA *psTimerCBData = &sTimers[ui32i]; + + INIT_WORK(&psTimerCBData->sWork, OSTimerWorkQueueCallBack); + } + } +#endif + return PVRSRV_OK; +} + +IMG_VOID PVROSFuncDeInit(IMG_VOID) +{ +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + if (psTimerWorkQueue != NULL) + { + destroy_workqueue(psTimerWorkQueue); + } +#endif +} diff --git a/sgx/services4/srvkm/env/linux/osperproc.c b/sgx/services4/srvkm/env/linux/osperproc.c new file mode 100644 index 0000000..6b57dfc --- /dev/null +++ b/sgx/services4/srvkm/env/linux/osperproc.c @@ -0,0 +1,113 @@ +/********************************************************************** + * + * 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 "services_headers.h" +#include "osperproc.h" + +#include "env_perproc.h" +#include "proc.h" + +extern IMG_UINT32 gui32ReleasePID; + +PVRSRV_ERROR OSPerProcessPrivateDataInit(IMG_HANDLE *phOsPrivateData) +{ + PVRSRV_ERROR eError; + IMG_HANDLE hBlockAlloc; + PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc; + + eError = OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_ENV_PER_PROCESS_DATA), + phOsPrivateData, + &hBlockAlloc, + "Environment per Process Data"); + + if (eError != PVRSRV_OK) + { + *phOsPrivateData = IMG_NULL; + + PVR_DPF((PVR_DBG_ERROR, "%s: OSAllocMem failed (%d)", __FUNCTION__, eError)); + return eError; + } + + psEnvPerProc = (PVRSRV_ENV_PER_PROCESS_DATA *)*phOsPrivateData; + OSMemSet(psEnvPerProc, 0, sizeof(*psEnvPerProc)); + + psEnvPerProc->hBlockAlloc = hBlockAlloc; + + + LinuxMMapPerProcessConnect(psEnvPerProc); + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + + INIT_LIST_HEAD(&psEnvPerProc->sDRMAuthListHead); +#endif + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSPerProcessPrivateDataDeInit(IMG_HANDLE hOsPrivateData) +{ + PVRSRV_ERROR eError; + PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc; + + if (hOsPrivateData == IMG_NULL) + { + return PVRSRV_OK; + } + + psEnvPerProc = (PVRSRV_ENV_PER_PROCESS_DATA *)hOsPrivateData; + + + LinuxMMapPerProcessDisconnect(psEnvPerProc); + + + RemovePerProcessProcDir(psEnvPerProc); + + eError = OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_ENV_PER_PROCESS_DATA), + hOsPrivateData, + psEnvPerProc->hBlockAlloc); + + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: OSFreeMem failed (%d)", __FUNCTION__, eError)); + } + + return PVRSRV_OK; +} + +PVRSRV_ERROR OSPerProcessSetHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase) +{ + return LinuxMMapPerProcessHandleOptions(psHandleBase); +} + +IMG_HANDLE LinuxTerminatingProcessPrivateData(IMG_VOID) +{ + if(!gui32ReleasePID) + return NULL; + return PVRSRVPerProcessPrivateData(gui32ReleasePID); +} diff --git a/sgx/services4/srvkm/env/linux/pdump.c b/sgx/services4/srvkm/env/linux/pdump.c new file mode 100644 index 0000000..13d9b0d --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pdump.c @@ -0,0 +1,628 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#if defined (SUPPORT_SGX) || defined (SUPPORT_VGX) +#if defined (PDUMP) + +#include <asm/atomic.h> +#include <stdarg.h> +#if defined (SUPPORT_SGX) +#include "sgxdefs.h" +#endif +#include "services_headers.h" + +#include "pvrversion.h" +#include "pvr_debug.h" + +#include "dbgdrvif.h" +#if defined (SUPPORT_SGX) +#include "sgxmmu.h" +#endif +#include "mm.h" +#include "pdump_km.h" +#include "pdump_int.h" + +#include <linux/kernel.h> +#include <linux/string.h> + +static IMG_BOOL PDumpWriteString2 (IMG_CHAR * pszString, IMG_UINT32 ui32Flags); +static IMG_BOOL PDumpWriteILock (PDBG_STREAM psStream, IMG_UINT8 *pui8Data, IMG_UINT32 ui32Count, IMG_UINT32 ui32Flags); +static IMG_VOID DbgSetFrame (PDBG_STREAM psStream, IMG_UINT32 ui32Frame); +static IMG_VOID DbgSetMarker (PDBG_STREAM psStream, IMG_UINT32 ui32Marker); + +#define PDUMP_DATAMASTER_PIXEL (1) +#define PDUMP_DATAMASTER_EDM (3) + +#define MAX_FILE_SIZE 0x40000000 + +static atomic_t gsPDumpSuspended = ATOMIC_INIT(0); + +static PDBGKM_SERVICE_TABLE gpfnDbgDrv = IMG_NULL; + + + +IMG_CHAR *pszStreamName[PDUMP_NUM_STREAMS] = { "ParamStream2", + "ScriptStream2", + "DriverInfoStream"}; +typedef struct PDBG_PDUMP_STATE_TAG +{ + PDBG_STREAM psStream[PDUMP_NUM_STREAMS]; + IMG_UINT32 ui32ParamFileNum; + + IMG_CHAR *pszMsg; + IMG_CHAR *pszScript; + IMG_CHAR *pszFile; + +} PDBG_PDUMP_STATE; + +static PDBG_PDUMP_STATE gsDBGPdumpState = {{IMG_NULL}, 0, IMG_NULL, IMG_NULL, IMG_NULL}; + +#define SZ_MSG_SIZE_MAX PVRSRV_PDUMP_MAX_COMMENT_SIZE-1 +#define SZ_SCRIPT_SIZE_MAX PVRSRV_PDUMP_MAX_COMMENT_SIZE-1 +#define SZ_FILENAME_SIZE_MAX PVRSRV_PDUMP_MAX_COMMENT_SIZE-1 + + + + +IMG_VOID DBGDrvGetServiceTable(IMG_VOID **fn_table); + +static inline IMG_BOOL PDumpSuspended(IMG_VOID) +{ + return (atomic_read(&gsPDumpSuspended) != 0) ? IMG_TRUE : IMG_FALSE; +} + +PVRSRV_ERROR PDumpOSGetScriptString(IMG_HANDLE *phScript, + IMG_UINT32 *pui32MaxLen) +{ + *phScript = (IMG_HANDLE)gsDBGPdumpState.pszScript; + *pui32MaxLen = SZ_SCRIPT_SIZE_MAX; + if ((!*phScript) || PDumpSuspended()) + { + return PVRSRV_ERROR_PDUMP_NOT_ACTIVE; + } + return PVRSRV_OK; +} + +PVRSRV_ERROR PDumpOSGetMessageString(IMG_CHAR **ppszMsg, + IMG_UINT32 *pui32MaxLen) +{ + *ppszMsg = gsDBGPdumpState.pszMsg; + *pui32MaxLen = SZ_MSG_SIZE_MAX; + if ((!*ppszMsg) || PDumpSuspended()) + { + return PVRSRV_ERROR_PDUMP_NOT_ACTIVE; + } + return PVRSRV_OK; +} + +PVRSRV_ERROR PDumpOSGetFilenameString(IMG_CHAR **ppszFile, + IMG_UINT32 *pui32MaxLen) +{ + *ppszFile = gsDBGPdumpState.pszFile; + *pui32MaxLen = SZ_FILENAME_SIZE_MAX; + if ((!*ppszFile) || PDumpSuspended()) + { + return PVRSRV_ERROR_PDUMP_NOT_ACTIVE; + } + return PVRSRV_OK; +} + +IMG_BOOL PDumpOSWriteString2(IMG_HANDLE hScript, IMG_UINT32 ui32Flags) +{ + return PDumpWriteString2(hScript, ui32Flags); +} + +PVRSRV_ERROR PDumpOSBufprintf(IMG_HANDLE hBuf, IMG_UINT32 ui32ScriptSizeMax, IMG_CHAR* pszFormat, ...) +{ + IMG_CHAR* pszBuf = hBuf; + IMG_INT32 n; + va_list vaArgs; + + va_start(vaArgs, pszFormat); + + n = vsnprintf(pszBuf, ui32ScriptSizeMax, pszFormat, vaArgs); + + va_end(vaArgs); + + if (n>=(IMG_INT32)ui32ScriptSizeMax || n==-1) + { + PVR_DPF((PVR_DBG_ERROR, "Buffer overflow detected, pdump output may be incomplete.")); + + return PVRSRV_ERROR_PDUMP_BUF_OVERFLOW; + } + +#if defined(PDUMP_DEBUG_OUTFILES) + g_ui32EveryLineCounter++; +#endif + return PVRSRV_OK; +} + +PVRSRV_ERROR PDumpOSVSprintf(IMG_CHAR *pszComment, IMG_UINT32 ui32ScriptSizeMax, IMG_CHAR* pszFormat, PDUMP_va_list vaArgs) +{ + IMG_INT32 n; + + n = vsnprintf(pszComment, ui32ScriptSizeMax, pszFormat, vaArgs); + + if (n>=(IMG_INT32)ui32ScriptSizeMax || n==-1) + { + PVR_DPF((PVR_DBG_ERROR, "Buffer overflow detected, pdump output may be incomplete.")); + + return PVRSRV_ERROR_PDUMP_BUF_OVERFLOW; + } + + return PVRSRV_OK; +} + +IMG_VOID PDumpOSDebugPrintf(IMG_CHAR* pszFormat, ...) +{ + PVR_UNREFERENCED_PARAMETER(pszFormat); + + +} + +PVRSRV_ERROR PDumpOSSprintf(IMG_CHAR *pszComment, IMG_UINT32 ui32ScriptSizeMax, IMG_CHAR *pszFormat, ...) +{ + IMG_INT32 n; + va_list vaArgs; + + va_start(vaArgs, pszFormat); + + n = vsnprintf(pszComment, ui32ScriptSizeMax, pszFormat, vaArgs); + + va_end(vaArgs); + + if (n>=(IMG_INT32)ui32ScriptSizeMax || n==-1) + { + PVR_DPF((PVR_DBG_ERROR, "Buffer overflow detected, pdump output may be incomplete.")); + + return PVRSRV_ERROR_PDUMP_BUF_OVERFLOW; + } + + return PVRSRV_OK; +} + +IMG_UINT32 PDumpOSBuflen(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMax) +{ + IMG_CHAR* pszBuf = hBuffer; + IMG_UINT32 ui32Count = 0; + + while ((pszBuf[ui32Count]!=0) && (ui32Count<ui32BufferSizeMax) ) + { + ui32Count++; + } + return(ui32Count); +} + +IMG_VOID PDumpOSVerifyLineEnding(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMax) +{ + IMG_UINT32 ui32Count; + IMG_CHAR* pszBuf = hBuffer; + + + ui32Count = PDumpOSBuflen(hBuffer, ui32BufferSizeMax); + + + if ((ui32Count >= 1) && (pszBuf[ui32Count-1] != '\n') && (ui32Count<ui32BufferSizeMax)) + { + pszBuf[ui32Count] = '\n'; + ui32Count++; + pszBuf[ui32Count] = '\0'; + } + if ((ui32Count >= 2) && (pszBuf[ui32Count-2] != '\r') && (ui32Count<ui32BufferSizeMax)) + { + pszBuf[ui32Count-1] = '\r'; + pszBuf[ui32Count] = '\n'; + ui32Count++; + pszBuf[ui32Count] = '\0'; + } +} + +IMG_HANDLE PDumpOSGetStream(IMG_UINT32 ePDumpStream) +{ + return (IMG_HANDLE)gsDBGPdumpState.psStream[ePDumpStream]; +} + +IMG_UINT32 PDumpOSGetStreamOffset(IMG_UINT32 ePDumpStream) +{ + PDBG_STREAM psStream = gsDBGPdumpState.psStream[ePDumpStream]; + return gpfnDbgDrv->pfnGetStreamOffset(psStream); +} + +IMG_UINT32 PDumpOSGetParamFileNum(IMG_VOID) +{ + return gsDBGPdumpState.ui32ParamFileNum; +} + +IMG_BOOL PDumpOSWriteString(IMG_HANDLE hStream, + IMG_UINT8 *psui8Data, + IMG_UINT32 ui32Size, + IMG_UINT32 ui32Flags) +{ + PDBG_STREAM psStream = (PDBG_STREAM)hStream; + return PDumpWriteILock(psStream, + psui8Data, + ui32Size, + ui32Flags); +} + +IMG_VOID PDumpOSCheckForSplitting(IMG_HANDLE hStream, IMG_UINT32 ui32Size, IMG_UINT32 ui32Flags) +{ + + PVR_UNREFERENCED_PARAMETER(hStream); + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(ui32Flags); +} + +IMG_BOOL PDumpOSJTInitialised(IMG_VOID) +{ + if(gpfnDbgDrv) + { + return IMG_TRUE; + } + return IMG_FALSE; +} + +inline IMG_BOOL PDumpOSIsSuspended(IMG_VOID) +{ + return (atomic_read(&gsPDumpSuspended) != 0) ? IMG_TRUE : IMG_FALSE; +} + +IMG_VOID PDumpOSCPUVAddrToDevPAddr(PVRSRV_DEVICE_TYPE eDeviceType, + IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32Offset, + IMG_UINT8 *pui8LinAddr, + IMG_UINT32 ui32PageSize, + IMG_DEV_PHYADDR *psDevPAddr) +{ + IMG_CPU_PHYADDR sCpuPAddr; + + PVR_UNREFERENCED_PARAMETER(pui8LinAddr); + PVR_UNREFERENCED_PARAMETER(ui32PageSize); + + + + PVR_ASSERT (hOSMemHandle != IMG_NULL); + + sCpuPAddr = OSMemHandleToCpuPAddr(hOSMemHandle, ui32Offset); + PVR_ASSERT((sCpuPAddr.uiAddr & (ui32PageSize - 1)) == 0); + + + *psDevPAddr = SysCpuPAddrToDevPAddr(eDeviceType, sCpuPAddr); +} + +IMG_VOID PDumpOSCPUVAddrToPhysPages(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32Offset, + IMG_PUINT8 pui8LinAddr, + IMG_UINT32 ui32DataPageMask, + IMG_UINT32 *pui32PageOffset) +{ + if(hOSMemHandle) + { + + IMG_CPU_PHYADDR sCpuPAddr; + + PVR_UNREFERENCED_PARAMETER(pui8LinAddr); + + sCpuPAddr = OSMemHandleToCpuPAddr(hOSMemHandle, ui32Offset); + *pui32PageOffset = sCpuPAddr.uiAddr & ui32DataPageMask; + } + else + { + PVR_UNREFERENCED_PARAMETER(hOSMemHandle); + PVR_UNREFERENCED_PARAMETER(ui32Offset); + + *pui32PageOffset = ((IMG_UINT32)pui8LinAddr & ui32DataPageMask); + } +} + +IMG_UINT32 PDumpOSDebugDriverWrite( PDBG_STREAM psStream, + PDUMP_DDWMODE eDbgDrvWriteMode, + IMG_UINT8 *pui8Data, + IMG_UINT32 ui32BCount, + IMG_UINT32 ui32Level, + IMG_UINT32 ui32DbgDrvFlags) +{ + switch(eDbgDrvWriteMode) + { + case PDUMP_WRITE_MODE_CONTINUOUS: + PVR_UNREFERENCED_PARAMETER(ui32DbgDrvFlags); + return gpfnDbgDrv->pfnDBGDrivWrite2(psStream, pui8Data, ui32BCount, ui32Level); + case PDUMP_WRITE_MODE_LASTFRAME: + return gpfnDbgDrv->pfnWriteLF(psStream, pui8Data, ui32BCount, ui32Level, ui32DbgDrvFlags); + case PDUMP_WRITE_MODE_BINCM: + PVR_UNREFERENCED_PARAMETER(ui32DbgDrvFlags); + return gpfnDbgDrv->pfnWriteBINCM(psStream, pui8Data, ui32BCount, ui32Level); + case PDUMP_WRITE_MODE_PERSISTENT: + PVR_UNREFERENCED_PARAMETER(ui32DbgDrvFlags); + return gpfnDbgDrv->pfnWritePersist(psStream, pui8Data, ui32BCount, ui32Level); + default: + PVR_UNREFERENCED_PARAMETER(ui32DbgDrvFlags); + break; + } + return 0xFFFFFFFFU; +} + +IMG_VOID PDumpOSReleaseExecution(IMG_VOID) +{ + OSReleaseThreadQuanta(); +} + +IMG_VOID PDumpInit(IMG_VOID) +{ + IMG_UINT32 i; + DBGKM_CONNECT_NOTIFIER sConnectNotifier; + + + if (!gpfnDbgDrv) + { + DBGDrvGetServiceTable((IMG_VOID **)&gpfnDbgDrv); + + + + if (gpfnDbgDrv == IMG_NULL) + { + return; + } + + + sConnectNotifier.pfnConnectNotifier = &PDumpConnectionNotify; + gpfnDbgDrv->pfnSetConnectNotifier(sConnectNotifier); + + if(!gsDBGPdumpState.pszFile) + { + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_FILENAME_SIZE_MAX, (IMG_PVOID *)&gsDBGPdumpState.pszFile, 0, + "Filename string") != PVRSRV_OK) + { + goto init_failed; + } + } + + if(!gsDBGPdumpState.pszMsg) + { + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_MSG_SIZE_MAX, (IMG_PVOID *)&gsDBGPdumpState.pszMsg, 0, + "Message string") != PVRSRV_OK) + { + goto init_failed; + } + } + + if(!gsDBGPdumpState.pszScript) + { + if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_SCRIPT_SIZE_MAX, (IMG_PVOID *)&gsDBGPdumpState.pszScript, 0, + "Script string") != PVRSRV_OK) + { + goto init_failed; + } + } + + for(i=0; i < PDUMP_NUM_STREAMS; i++) + { + gsDBGPdumpState.psStream[i] = gpfnDbgDrv->pfnCreateStream(pszStreamName[i], + DEBUG_CAPMODE_FRAMED, + DEBUG_OUTMODE_STREAMENABLE, + 0, + 10); + + gpfnDbgDrv->pfnSetCaptureMode(gsDBGPdumpState.psStream[i],DEBUG_CAPMODE_FRAMED,0xFFFFFFFF, 0xFFFFFFFF, 1); + gpfnDbgDrv->pfnSetFrame(gsDBGPdumpState.psStream[i],0); + } + + PDUMPCOMMENT("Driver Product Name: %s", VS_PRODUCT_NAME); + PDUMPCOMMENT("Driver Product Version: %s (%s)", PVRVERSION_STRING, PVRVERSION_FAMILY); + PDUMPCOMMENT("Start of Init Phase"); + } + + return; + +init_failed: + + if(gsDBGPdumpState.pszFile) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_FILENAME_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszFile, 0); + gsDBGPdumpState.pszFile = IMG_NULL; + } + + if(gsDBGPdumpState.pszScript) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_SCRIPT_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszScript, 0); + gsDBGPdumpState.pszScript = IMG_NULL; + } + + if(gsDBGPdumpState.pszMsg) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_MSG_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszMsg, 0); + gsDBGPdumpState.pszMsg = IMG_NULL; + } + + + sConnectNotifier.pfnConnectNotifier = 0; + gpfnDbgDrv->pfnSetConnectNotifier(sConnectNotifier); + + gpfnDbgDrv = IMG_NULL; +} + + +IMG_VOID PDumpDeInit(IMG_VOID) +{ + IMG_UINT32 i; + DBGKM_CONNECT_NOTIFIER sConnectNotifier; + + for(i=0; i < PDUMP_NUM_STREAMS; i++) + { + gpfnDbgDrv->pfnDestroyStream(gsDBGPdumpState.psStream[i]); + } + + if(gsDBGPdumpState.pszFile) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_FILENAME_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszFile, 0); + gsDBGPdumpState.pszFile = IMG_NULL; + } + + if(gsDBGPdumpState.pszScript) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_SCRIPT_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszScript, 0); + gsDBGPdumpState.pszScript = IMG_NULL; + } + + if(gsDBGPdumpState.pszMsg) + { + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, SZ_MSG_SIZE_MAX, (IMG_PVOID) gsDBGPdumpState.pszMsg, 0); + gsDBGPdumpState.pszMsg = IMG_NULL; + } + + + sConnectNotifier.pfnConnectNotifier = 0; + gpfnDbgDrv->pfnSetConnectNotifier(sConnectNotifier); + + gpfnDbgDrv = IMG_NULL; +} + +PVRSRV_ERROR PDumpStartInitPhaseKM(IMG_VOID) +{ + IMG_UINT32 i; + + if (gpfnDbgDrv) + { + PDUMPCOMMENT("Start Init Phase"); + for(i=0; i < PDUMP_NUM_STREAMS; i++) + { + gpfnDbgDrv->pfnStartInitPhase(gsDBGPdumpState.psStream[i]); + } + } + return PVRSRV_OK; +} + +PVRSRV_ERROR PDumpStopInitPhaseKM(IMG_VOID) +{ + IMG_UINT32 i; + + if (gpfnDbgDrv) + { + PDUMPCOMMENT("Stop Init Phase"); + + for(i=0; i < PDUMP_NUM_STREAMS; i++) + { + gpfnDbgDrv->pfnStopInitPhase(gsDBGPdumpState.psStream[i]); + } + } + return PVRSRV_OK; +} + +IMG_BOOL PDumpIsLastCaptureFrameKM(IMG_VOID) +{ + return gpfnDbgDrv->pfnIsLastCaptureFrame(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2]); +} + + +IMG_BOOL PDumpOSIsCaptureFrameKM(IMG_VOID) +{ + if (PDumpSuspended()) + { + return IMG_FALSE; + } + return gpfnDbgDrv->pfnIsCaptureFrame(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2], IMG_FALSE); +} + +PVRSRV_ERROR PDumpOSSetFrameKM(IMG_UINT32 ui32Frame) +{ + IMG_UINT32 ui32Stream; + + for (ui32Stream = 0; ui32Stream < PDUMP_NUM_STREAMS; ui32Stream++) + { + if (gsDBGPdumpState.psStream[ui32Stream]) + { + DbgSetFrame(gsDBGPdumpState.psStream[ui32Stream], ui32Frame); + } + } + + return PVRSRV_OK; +} + + +static IMG_BOOL PDumpWriteString2(IMG_CHAR * pszString, IMG_UINT32 ui32Flags) +{ + return PDumpWriteILock(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2], (IMG_UINT8 *) pszString, strlen(pszString), ui32Flags); +} + + +static IMG_BOOL PDumpWriteILock(PDBG_STREAM psStream, IMG_UINT8 *pui8Data, IMG_UINT32 ui32Count, IMG_UINT32 ui32Flags) +{ + IMG_UINT32 ui32Written = 0; + if ((psStream == IMG_NULL) || PDumpSuspended() || ((ui32Flags & PDUMP_FLAGS_NEVER) != 0)) + { + PVR_DPF((PVR_DBG_MESSAGE, "PDumpWriteILock: Failed to write 0x%x bytes to stream 0x%x", ui32Count, (IMG_UINT32)psStream)); + return IMG_TRUE; + } + + + + + if (psStream == gsDBGPdumpState.psStream[PDUMP_STREAM_PARAM2]) + { + IMG_UINT32 ui32ParamOutPos = gpfnDbgDrv->pfnGetStreamOffset(gsDBGPdumpState.psStream[PDUMP_STREAM_PARAM2]); + + if (ui32ParamOutPos + ui32Count > MAX_FILE_SIZE) + { + if ((gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2] && PDumpWriteString2("\r\n-- Splitting pdump output file\r\n\r\n", ui32Flags))) + { + DbgSetMarker(gsDBGPdumpState.psStream[PDUMP_STREAM_PARAM2], ui32ParamOutPos); + gsDBGPdumpState.ui32ParamFileNum++; + } + } + } + + ui32Written = DbgWrite(psStream, pui8Data, ui32Count, ui32Flags); + + if (ui32Written == 0xFFFFFFFF) + { + return IMG_FALSE; + } + + return IMG_TRUE; +} + +static IMG_VOID DbgSetFrame(PDBG_STREAM psStream, IMG_UINT32 ui32Frame) +{ + gpfnDbgDrv->pfnSetFrame(psStream, ui32Frame); +} + +static IMG_VOID DbgSetMarker(PDBG_STREAM psStream, IMG_UINT32 ui32Marker) +{ + gpfnDbgDrv->pfnSetMarker(psStream, ui32Marker); +} + +IMG_VOID PDumpSuspendKM(IMG_VOID) +{ + atomic_inc(&gsPDumpSuspended); +} + +IMG_VOID PDumpResumeKM(IMG_VOID) +{ + atomic_dec(&gsPDumpSuspended); +} + +#endif +#endif diff --git a/sgx/services4/srvkm/env/linux/private_data.h b/sgx/services4/srvkm/env/linux/private_data.h new file mode 100644 index 0000000..b8751d3 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/private_data.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __INCLUDED_PRIVATE_DATA_H_ +#define __INCLUDED_PRIVATE_DATA_H_ + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) +#include <linux/list.h> +#include <drm/drmP.h> +#endif + +typedef struct +{ + + IMG_UINT32 ui32OpenPID; + + +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hKernelMemInfo; +#else + IMG_HANDLE hKernelMemInfo; +#endif + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + + struct list_head sDRMAuthListItem; + + struct drm_file *psDRMFile; +#endif + +#if defined(SUPPORT_MEMINFO_IDS) + + IMG_UINT64 ui64Stamp; +#endif + + + IMG_HANDLE hBlockAlloc; + +#if defined(SUPPORT_DRI_DRM_EXT) + IMG_PVOID pPriv; +#endif +} +PVRSRV_FILE_PRIVATE_DATA; + +#endif + diff --git a/sgx/services4/srvkm/env/linux/proc.c b/sgx/services4/srvkm/env/linux/proc.c new file mode 100644 index 0000000..1df8aff --- /dev/null +++ b/sgx/services4/srvkm/env/linux/proc.c @@ -0,0 +1,835 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include "services_headers.h" + +#include "queue.h" +#include "resman.h" +#include "pvrmmap.h" +#include "pvr_debug.h" +#include "pvrversion.h" +#include "proc.h" +#include "perproc.h" +#include "env_perproc.h" +#include "linkage.h" + +#include "lists.h" + +static struct proc_dir_entry * dir; + +static const IMG_CHAR PVRProcDirRoot[] = "pvr"; + +static IMG_INT pvr_proc_open(struct inode *inode,struct file *file); +static void *pvr_proc_seq_start (struct seq_file *m, loff_t *pos); +static void pvr_proc_seq_stop (struct seq_file *m, void *v); +static void *pvr_proc_seq_next (struct seq_file *m, void *v, loff_t *pos); +static int pvr_proc_seq_show (struct seq_file *m, void *v); +static ssize_t pvr_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); + +static struct file_operations pvr_proc_operations = +{ + .open = pvr_proc_open, + .read = seq_read, + .write = pvr_proc_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct seq_operations pvr_proc_seq_operations = +{ + .start = pvr_proc_seq_start, + .next = pvr_proc_seq_next, + .stop = pvr_proc_seq_stop, + .show = pvr_proc_seq_show, +}; + +static struct proc_dir_entry* g_pProcQueue; +static struct proc_dir_entry* g_pProcVersion; +static struct proc_dir_entry* g_pProcSysNodes; + +#ifdef DEBUG +static struct proc_dir_entry* g_pProcDebugLevel; +#endif + +#ifdef PVR_MANUAL_POWER_CONTROL +static struct proc_dir_entry* g_pProcPowerLevel; +#endif + + +static void ProcSeqShowVersion(struct seq_file *sfile,void* el); + +static void ProcSeqShowSysNodes(struct seq_file *sfile,void* el); +static void* ProcSeqOff2ElementSysNodes(struct seq_file * sfile, loff_t off); + +off_t printAppend(IMG_CHAR * buffer, size_t size, off_t off, const IMG_CHAR * format, ...) +{ + IMG_INT n; + size_t space = size - (size_t)off; + va_list ap; + + va_start (ap, format); + + n = vsnprintf (buffer+off, space, format, ap); + + va_end (ap); + + if (n >= (IMG_INT)space || n < 0) + { + + buffer[size - 1] = 0; + return (off_t)(size - 1); + } + else + { + return (off + (off_t)n); + } +} + + +void* ProcSeq1ElementOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + return (void*)2; + return NULL; +} + + +void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + + if(off == 1) + return (void*)2; + + return NULL; +} + + +static IMG_INT pvr_proc_open(struct inode *inode,struct file *file) +{ + IMG_INT ret = seq_open(file, &pvr_proc_seq_operations); + + struct seq_file *seq = (struct seq_file*)file->private_data; + struct proc_dir_entry* pvr_proc_entry = PDE(inode); + + + seq->private = pvr_proc_entry->data; + return ret; +} + +static ssize_t pvr_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct proc_dir_entry * dp; + + PVR_UNREFERENCED_PARAMETER(ppos); + dp = PDE(inode); + + if (!dp->write_proc) + return -EIO; + + return dp->write_proc(file, buffer, count, dp->data); +} + + +static void *pvr_proc_seq_start (struct seq_file *proc_seq_file, loff_t *pos) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + if(handlers->startstop != NULL) + handlers->startstop(proc_seq_file, IMG_TRUE); + return handlers->off2element(proc_seq_file, *pos); +} + +static void pvr_proc_seq_stop (struct seq_file *proc_seq_file, void *v) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + PVR_UNREFERENCED_PARAMETER(v); + + if(handlers->startstop != NULL) + handlers->startstop(proc_seq_file, IMG_FALSE); +} + +static void *pvr_proc_seq_next (struct seq_file *proc_seq_file, void *v, loff_t *pos) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + (*pos)++; + if( handlers->next != NULL) + return handlers->next( proc_seq_file, v, *pos ); + return handlers->off2element(proc_seq_file, *pos); +} + +static int pvr_proc_seq_show (struct seq_file *proc_seq_file, void *v) +{ + PVR_PROC_SEQ_HANDLERS *handlers = (PVR_PROC_SEQ_HANDLERS*)proc_seq_file->private; + handlers->show( proc_seq_file,v ); + return 0; +} + + + +static struct proc_dir_entry* CreateProcEntryInDirSeq( + struct proc_dir_entry *pdir, + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + + struct proc_dir_entry * file; + mode_t mode; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDirSeq: cannot make proc entry /proc/%s/%s: no parent", PVRProcDirRoot, name)); + return NULL; + } + + mode = S_IFREG; + + if (show_handler) + { + mode |= S_IRUGO; + } + + if (whandler) + { + mode |= S_IWUSR; + } + + file=create_proc_entry(name, mode, pdir); + + if (file) + { + PVR_PROC_SEQ_HANDLERS *seq_handlers; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + + file->proc_fops = &pvr_proc_operations; + file->write_proc = whandler; + + + file->data = kmalloc(sizeof(PVR_PROC_SEQ_HANDLERS), GFP_KERNEL); + if(file->data) + { + seq_handlers = (PVR_PROC_SEQ_HANDLERS*)file->data; + seq_handlers->next = next_handler; + seq_handlers->show = show_handler; + seq_handlers->off2element = off2element_handler; + seq_handlers->startstop = startstop_handler; + seq_handlers->data = data; + + return file; + } + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDirSeq: cannot make proc entry /proc/%s/%s: no memory", PVRProcDirRoot, name)); + return NULL; +} + + +struct proc_dir_entry* CreateProcReadEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler + ) +{ + return CreateProcEntrySeq(name, + data, + next_handler, + show_handler, + off2element_handler, + startstop_handler, + NULL); +} + +struct proc_dir_entry* CreateProcEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + return CreateProcEntryInDirSeq( + dir, + name, + data, + next_handler, + show_handler, + off2element_handler, + startstop_handler, + whandler + ); +} + + + +struct proc_dir_entry* CreatePerProcessProcEntrySeq ( + const IMG_CHAR * name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + IMG_UINT32 ui32PID; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntrySeq: /proc/%s doesn't exist", PVRProcDirRoot)); + return NULL; + } + + ui32PID = OSGetCurrentProcessIDKM(); + + psPerProc = PVRSRVPerProcessPrivateData(ui32PID); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntrySeq: no per process data")); + + return NULL; + } + + if (!psPerProc->psProcDir) + { + IMG_CHAR dirname[16]; + IMG_INT ret; + + ret = snprintf(dirname, sizeof(dirname), "%u", ui32PID); + + if (ret <=0 || ret >= (IMG_INT)sizeof(dirname)) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't generate per process proc directory name \"%u\"", ui32PID)); + return NULL; + } + else + { + psPerProc->psProcDir = proc_mkdir(dirname, dir); + if (!psPerProc->psProcDir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't create per process proc directory /proc/%s/%u", + PVRProcDirRoot, ui32PID)); + return NULL; + } + } + } + + return CreateProcEntryInDirSeq(psPerProc->psProcDir, name, data, next_handler, + show_handler,off2element_handler,startstop_handler,whandler); +} + + +IMG_VOID RemoveProcEntrySeq( struct proc_dir_entry* proc_entry ) +{ + if (dir) + { + void* data = proc_entry->data ; + PVR_DPF((PVR_DBG_MESSAGE, "Removing /proc/%s/%s", PVRProcDirRoot, proc_entry->name)); + + remove_proc_entry(proc_entry->name, dir); + if( data) + kfree( data ); + + } +} + +IMG_VOID RemovePerProcessProcEntrySeq(struct proc_dir_entry* proc_entry) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + + psPerProc = LinuxTerminatingProcessPrivateData(); + if (!psPerProc) + { + psPerProc = PVRSRVFindPerProcessPrivateData(); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: can't " + "remove %s, no per process data", proc_entry->name)); + return; + } + } + + if (psPerProc->psProcDir) + { + void* data = proc_entry->data ; + PVR_DPF((PVR_DBG_MESSAGE, "Removing proc entry %s from %s", proc_entry->name, psPerProc->psProcDir->name)); + + remove_proc_entry(proc_entry->name, psPerProc->psProcDir); + if(data) + kfree( data ); + } +} + +static IMG_INT pvr_read_proc(IMG_CHAR *page, IMG_CHAR **start, off_t off, + IMG_INT count, IMG_INT *eof, IMG_VOID *data) +{ + + pvr_read_proc_t *pprn = (pvr_read_proc_t *)data; + + off_t len = pprn (page, (size_t)count, off); + + if (len == END_OF_FILE) + { + len = 0; + *eof = 1; + } + else if (!len) + { + *start = (IMG_CHAR *) 0; + } + else + { + *start = (IMG_CHAR *) 1; + } + + return len; +} + + +static IMG_INT CreateProcEntryInDir(struct proc_dir_entry *pdir, const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + struct proc_dir_entry * file; + mode_t mode; + + if (!pdir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntryInDir: parent directory doesn't exist")); + + return -ENOMEM; + } + + mode = S_IFREG; + + if (rhandler) + { + mode |= S_IRUGO; + } + + if (whandler) + { + mode |= S_IWUSR; + } + + file = create_proc_entry(name, mode, pdir); + + if (file) + { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + file->read_proc = rhandler; + file->write_proc = whandler; + file->data = data; + + PVR_DPF((PVR_DBG_MESSAGE, "Created proc entry %s in %s", name, pdir->name)); + + return 0; + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntry: cannot create proc entry %s in %s", name, pdir->name)); + + return -ENOMEM; +} + + +IMG_INT CreateProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + return CreateProcEntryInDir(dir, name, rhandler, whandler, data); +} + + +IMG_INT CreatePerProcessProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + IMG_UINT32 ui32PID; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: /proc/%s doesn't exist", PVRProcDirRoot)); + + return -ENOMEM; + } + + ui32PID = OSGetCurrentProcessIDKM(); + + psPerProc = PVRSRVPerProcessPrivateData(ui32PID); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: no per process data")); + + return -ENOMEM; + } + + if (!psPerProc->psProcDir) + { + IMG_CHAR dirname[16]; + IMG_INT ret; + + ret = snprintf(dirname, sizeof(dirname), "%u", ui32PID); + + if (ret <=0 || ret >= (IMG_INT)sizeof(dirname)) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't generate per process proc directory name \"%u\"", ui32PID)); + + return -ENOMEM; + } + else + { + psPerProc->psProcDir = proc_mkdir(dirname, dir); + if (!psPerProc->psProcDir) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: couldn't create per process proc directory /proc/%s/%u", PVRProcDirRoot, ui32PID)); + + return -ENOMEM; + } + } + } + + return CreateProcEntryInDir(psPerProc->psProcDir, name, rhandler, whandler, data); +} + + +IMG_INT CreateProcReadEntry(const IMG_CHAR * name, pvr_read_proc_t handler) +{ + struct proc_dir_entry * file; + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcReadEntry: cannot make proc entry /proc/%s/%s: no parent", PVRProcDirRoot, name)); + + return -ENOMEM; + } + + + file = create_proc_read_entry (name, S_IFREG | S_IRUGO, dir, pvr_read_proc, (IMG_VOID *)handler); + + if (file) + { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + file->owner = THIS_MODULE; +#endif + return 0; + } + + PVR_DPF((PVR_DBG_ERROR, "CreateProcReadEntry: cannot make proc entry /proc/%s/%s: no memory", PVRProcDirRoot, name)); + + return -ENOMEM; +} + + +IMG_INT CreateProcEntries(IMG_VOID) +{ + dir = proc_mkdir (PVRProcDirRoot, NULL); + + if (!dir) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: cannot make /proc/%s directory", PVRProcDirRoot)); + + return -ENOMEM; + } + + g_pProcQueue = CreateProcReadEntrySeq("queue", NULL, NULL, ProcSeqShowQueue, ProcSeqOff2ElementQueue, NULL); + g_pProcVersion = CreateProcReadEntrySeq("version", NULL, NULL, ProcSeqShowVersion, ProcSeq1ElementHeaderOff2Element, NULL); + g_pProcSysNodes = CreateProcReadEntrySeq("nodes", NULL, NULL, ProcSeqShowSysNodes, ProcSeqOff2ElementSysNodes, NULL); + + if(!g_pProcQueue || !g_pProcVersion || !g_pProcSysNodes) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s files", PVRProcDirRoot)); + + return -ENOMEM; + } + + +#ifdef DEBUG + + g_pProcDebugLevel = CreateProcEntrySeq("debug_level", NULL, NULL, + ProcSeqShowDebugLevel, + ProcSeq1ElementOff2Element, NULL, + (IMG_VOID*)PVRDebugProcSetLevel); + if(!g_pProcDebugLevel) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s/debug_level", PVRProcDirRoot)); + + return -ENOMEM; + } + +#ifdef PVR_MANUAL_POWER_CONTROL + g_pProcPowerLevel = CreateProcEntrySeq("power_control", NULL, NULL, + ProcSeqShowPowerLevel, + ProcSeq1ElementOff2Element, NULL, + PVRProcSetPowerLevel); + if(!g_pProcPowerLevel) + { + PVR_DPF((PVR_DBG_ERROR, "CreateProcEntries: couldn't make /proc/%s/power_control", PVRProcDirRoot)); + + return -ENOMEM; + } +#endif +#endif + + return 0; +} + + +IMG_VOID RemoveProcEntry(const IMG_CHAR * name) +{ + if (dir) + { + remove_proc_entry(name, dir); + PVR_DPF((PVR_DBG_MESSAGE, "Removing /proc/%s/%s", PVRProcDirRoot, name)); + } +} + + +IMG_VOID RemovePerProcessProcEntry(const IMG_CHAR *name) +{ + PVRSRV_ENV_PER_PROCESS_DATA *psPerProc; + + psPerProc = LinuxTerminatingProcessPrivateData(); + if (!psPerProc) + { + psPerProc = PVRSRVFindPerProcessPrivateData(); + if (!psPerProc) + { + PVR_DPF((PVR_DBG_ERROR, "CreatePerProcessProcEntries: can't " + "remove %s, no per process data", name)); + return; + } + } + + if (psPerProc->psProcDir) + { + remove_proc_entry(name, psPerProc->psProcDir); + + PVR_DPF((PVR_DBG_MESSAGE, "Removing proc entry %s from %s", name, psPerProc->psProcDir->name)); + } +} + + +IMG_VOID RemovePerProcessProcDir(PVRSRV_ENV_PER_PROCESS_DATA *psPerProc) +{ + if (psPerProc->psProcDir) + { + while (psPerProc->psProcDir->subdir) + { + PVR_DPF((PVR_DBG_WARNING, "Belatedly removing /proc/%s/%s/%s", PVRProcDirRoot, psPerProc->psProcDir->name, psPerProc->psProcDir->subdir->name)); + + RemoveProcEntry(psPerProc->psProcDir->subdir->name); + } + RemoveProcEntry(psPerProc->psProcDir->name); + } +} + +IMG_VOID RemoveProcEntries(IMG_VOID) +{ +#ifdef DEBUG + RemoveProcEntrySeq( g_pProcDebugLevel ); +#ifdef PVR_MANUAL_POWER_CONTROL + RemoveProcEntrySeq( g_pProcPowerLevel ); +#endif +#endif + + RemoveProcEntrySeq(g_pProcQueue); + RemoveProcEntrySeq(g_pProcVersion); + RemoveProcEntrySeq(g_pProcSysNodes); + + while (dir->subdir) + { + PVR_DPF((PVR_DBG_WARNING, "Belatedly removing /proc/%s/%s", PVRProcDirRoot, dir->subdir->name)); + + RemoveProcEntry(dir->subdir->name); + } + + remove_proc_entry(PVRProcDirRoot, NULL); +} + +static void ProcSeqShowVersion(struct seq_file *sfile,void* el) +{ + SYS_DATA *psSysData; + IMG_CHAR *pszSystemVersionString = "None"; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf(sfile, + "Version %s (%s) %s\n", + PVRVERSION_STRING, + PVR_BUILD_TYPE, PVR_BUILD_DIR); + return; + } + + psSysData = SysAcquireDataNoCheck(); + if(psSysData != IMG_NULL && psSysData->pszVersionString != IMG_NULL) + { + pszSystemVersionString = psSysData->pszVersionString; + } + + seq_printf( sfile, "System Version String: %s\n", pszSystemVersionString); +} + +static const IMG_CHAR *deviceTypeToString(PVRSRV_DEVICE_TYPE deviceType) +{ + switch (deviceType) + { + default: + { + static IMG_CHAR text[10]; + + sprintf(text, "?%x", (IMG_UINT)deviceType); + + return text; + } + } +} + + +static const IMG_CHAR *deviceClassToString(PVRSRV_DEVICE_CLASS deviceClass) +{ + switch (deviceClass) + { + case PVRSRV_DEVICE_CLASS_3D: + { + return "3D"; + } + case PVRSRV_DEVICE_CLASS_DISPLAY: + { + return "display"; + } + case PVRSRV_DEVICE_CLASS_BUFFER: + { + return "buffer"; + } + default: + { + static IMG_CHAR text[10]; + + sprintf(text, "?%x", (IMG_UINT)deviceClass); + return text; + } + } +} + +static IMG_VOID* DecOffPsDev_AnyVaCb(PVRSRV_DEVICE_NODE *psNode, va_list va) +{ + off_t *pOff = va_arg(va, off_t*); + if (--(*pOff)) + { + return IMG_NULL; + } + else + { + return psNode; + } +} + +static void ProcSeqShowSysNodes(struct seq_file *sfile,void* el) +{ + PVRSRV_DEVICE_NODE *psDevNode; + + if(el == PVR_PROC_SEQ_START_TOKEN) + { + seq_printf( sfile, + "Registered nodes\n" + "Addr Type Class Index Ref pvDev Size Res\n"); + return; + } + + psDevNode = (PVRSRV_DEVICE_NODE*)el; + + seq_printf( sfile, + "%p %-8s %-8s %4d %2u %p %3u %p\n", + psDevNode, + deviceTypeToString(psDevNode->sDevId.eDeviceType), + deviceClassToString(psDevNode->sDevId.eDeviceClass), + psDevNode->sDevId.eDeviceClass, + psDevNode->ui32RefCount, + psDevNode->pvDevice, + psDevNode->ui32pvDeviceSize, + psDevNode->hResManContext); +} + +static void* ProcSeqOff2ElementSysNodes(struct seq_file * sfile, loff_t off) +{ + SYS_DATA *psSysData; + PVRSRV_DEVICE_NODE*psDevNode = IMG_NULL; + + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + psSysData = SysAcquireDataNoCheck(); + if (psSysData != IMG_NULL) + { + + psDevNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + DecOffPsDev_AnyVaCb, + &off); + } + + + return (void*)psDevNode; +} + diff --git a/sgx/services4/srvkm/env/linux/proc.h b/sgx/services4/srvkm/env/linux/proc.h new file mode 100644 index 0000000..2066d71 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/proc.h @@ -0,0 +1,108 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __SERVICES_PROC_H__ +#define __SERVICES_PROC_H__ + +#include <asm/system.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#define END_OF_FILE (off_t) -1 + +typedef off_t (pvr_read_proc_t)(IMG_CHAR *, size_t, off_t); + + +#define PVR_PROC_SEQ_START_TOKEN (void*)1 +typedef void* (pvr_next_proc_seq_t)(struct seq_file *,void*,loff_t); +typedef void* (pvr_off2element_proc_seq_t)(struct seq_file *, loff_t); +typedef void (pvr_show_proc_seq_t)(struct seq_file *,void*); +typedef void (pvr_startstop_proc_seq_t)(struct seq_file *, IMG_BOOL start); + +typedef struct _PVR_PROC_SEQ_HANDLERS_ { + pvr_next_proc_seq_t *next; + pvr_show_proc_seq_t *show; + pvr_off2element_proc_seq_t *off2element; + pvr_startstop_proc_seq_t *startstop; + IMG_VOID *data; +} PVR_PROC_SEQ_HANDLERS; + + +void* ProcSeq1ElementOff2Element(struct seq_file *sfile, loff_t off); + +void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t off); + +off_t printAppend(IMG_CHAR * buffer, size_t size, off_t off, const IMG_CHAR * format, ...) + __attribute__((format(printf, 4, 5))); + +IMG_INT CreateProcEntries(IMG_VOID); + +IMG_INT CreateProcReadEntry (const IMG_CHAR * name, pvr_read_proc_t handler); + +IMG_INT CreateProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data); + +IMG_INT CreatePerProcessProcEntry(const IMG_CHAR * name, read_proc_t rhandler, write_proc_t whandler, IMG_VOID *data); + +IMG_VOID RemoveProcEntry(const IMG_CHAR * name); + +IMG_VOID RemovePerProcessProcEntry(const IMG_CHAR * name); + +IMG_VOID RemoveProcEntries(IMG_VOID); + +struct proc_dir_entry* CreateProcReadEntrySeq ( + const IMG_CHAR* name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler + ); + +struct proc_dir_entry* CreateProcEntrySeq ( + const IMG_CHAR* name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ); + +struct proc_dir_entry* CreatePerProcessProcEntrySeq ( + const IMG_CHAR* name, + IMG_VOID* data, + pvr_next_proc_seq_t next_handler, + pvr_show_proc_seq_t show_handler, + pvr_off2element_proc_seq_t off2element_handler, + pvr_startstop_proc_seq_t startstop_handler, + write_proc_t whandler + ); + + +IMG_VOID RemoveProcEntrySeq(struct proc_dir_entry* proc_entry); +IMG_VOID RemovePerProcessProcEntrySeq(struct proc_dir_entry* proc_entry); + +#endif diff --git a/sgx/services4/srvkm/env/linux/pvr_bridge_k.c b/sgx/services4/srvkm/env/linux/pvr_bridge_k.c new file mode 100755 index 0000000..b1c1124 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pvr_bridge_k.c @@ -0,0 +1,417 @@ +/********************************************************************** + * + * 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" + +#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 *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; + IMG_VOID *handle = NULL; +#else + IMG_VOID * omap_drm_get_default_fb(struct drm_device *dev); + IMG_VOID *handle = omap_drm_get_default_fb(dev); // XXX handle should come from omaplfb somehow, because it is omaplfb that decided to use the fb_info* as the unique handle.. +#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; + } + + psMapDevMemIN->hKernelMemInfo = psPrivateData->hKernelMemInfo; + 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, handle); + 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); + + psPrivateData->hKernelMemInfo = psExportDeviceMemOUT->hMemInfo; +#if defined(SUPPORT_MEMINFO_IDS) + psExportDeviceMemOUT->ui64Stamp = psPrivateData->ui64Stamp = ++ui64Stamp; +#endif + break; + } + +#if defined(SUPPORT_MEMINFO_IDS) + case PVRSRV_BRIDGE_MAP_DEV_MEMORY: + { + PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *psMapDeviceMemoryOUT = + (PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *)psBridgePackageKM->pvParamOut; + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + psMapDeviceMemoryOUT->sDstClientMemInfo.ui64Stamp = psPrivateData->ui64Stamp; + break; + } + + case PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY: + { + PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *psDeviceClassMemoryOUT = + (PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *)psBridgePackageKM->pvParamOut; + psDeviceClassMemoryOUT->sClientMemInfo.ui64Stamp = ++ui64Stamp; + break; + } +#endif + + default: + break; + } + +unlock_and_return: + LinuxUnLockMutex(&gPVRSRVLock); + return err; +} diff --git a/sgx/services4/srvkm/env/linux/pvr_debug.c b/sgx/services4/srvkm/env/linux/pvr_debug.c new file mode 100644 index 0000000..7690875 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pvr_debug.c @@ -0,0 +1,428 @@ +/********************************************************************** + * + * 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 <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/kernel.h> +#include <linux/hardirq.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <stdarg.h> +#include "img_types.h" +#include "servicesext.h" +#include "pvr_debug.h" +#include "srvkm.h" +#include "proc.h" +#include "mutex.h" +#include "linkage.h" +#include "pvr_uaccess.h" + +static IMG_BOOL VBAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, + const IMG_CHAR* pszFormat, va_list VArgs) + IMG_FORMAT_PRINTF(3, 0); + + +#if defined(PVRSRV_NEED_PVR_DPF) + +#define PVR_MAX_FILEPATH_LEN 256 + +static IMG_BOOL BAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, + const IMG_CHAR *pszFormat, ...) + IMG_FORMAT_PRINTF(3, 4); + +IMG_UINT32 gPVRDebugLevel = + (DBGPRIV_FATAL | DBGPRIV_ERROR | DBGPRIV_WARNING); + +#endif + +#define PVR_MAX_MSG_LEN PVR_MAX_DEBUG_MESSAGE_LEN + +static IMG_CHAR gszBufferNonIRQ[PVR_MAX_MSG_LEN + 1]; + +static IMG_CHAR gszBufferIRQ[PVR_MAX_MSG_LEN + 1]; + +static PVRSRV_LINUX_MUTEX gsDebugMutexNonIRQ; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +static spinlock_t gsDebugLockIRQ = SPIN_LOCK_UNLOCKED; +#else +static spinlock_t gsDebugLockIRQ = __SPIN_LOCK_UNLOCKED(gsDebugLockIRQ); +#endif + +#if !defined (USE_SPIN_LOCK) +#define USE_SPIN_LOCK (in_interrupt() || !preemptible()) +#endif + +static inline void GetBufferLock(unsigned long *pulLockFlags) +{ + if (USE_SPIN_LOCK) + { + spin_lock_irqsave(&gsDebugLockIRQ, *pulLockFlags); + } + else + { + LinuxLockMutex(&gsDebugMutexNonIRQ); + } +} + +static inline void ReleaseBufferLock(unsigned long ulLockFlags) +{ + if (USE_SPIN_LOCK) + { + spin_unlock_irqrestore(&gsDebugLockIRQ, ulLockFlags); + } + else + { + LinuxUnLockMutex(&gsDebugMutexNonIRQ); + } +} + +static inline void SelectBuffer(IMG_CHAR **ppszBuf, IMG_UINT32 *pui32BufSiz) +{ + if (USE_SPIN_LOCK) + { + *ppszBuf = gszBufferIRQ; + *pui32BufSiz = sizeof(gszBufferIRQ); + } + else + { + *ppszBuf = gszBufferNonIRQ; + *pui32BufSiz = sizeof(gszBufferNonIRQ); + } +} + +static IMG_BOOL VBAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, const IMG_CHAR* pszFormat, va_list VArgs) +{ + IMG_UINT32 ui32Used; + IMG_UINT32 ui32Space; + IMG_INT32 i32Len; + + ui32Used = strlen(pszBuf); + BUG_ON(ui32Used >= ui32BufSiz); + ui32Space = ui32BufSiz - ui32Used; + + i32Len = vsnprintf(&pszBuf[ui32Used], ui32Space, pszFormat, VArgs); + pszBuf[ui32BufSiz - 1] = 0; + + + return (i32Len < 0 || i32Len >= (IMG_INT32)ui32Space) ? IMG_TRUE : IMG_FALSE; +} + +IMG_VOID PVRDPFInit(IMG_VOID) +{ + LinuxInitMutex(&gsDebugMutexNonIRQ); +} + +IMG_VOID PVRSRVReleasePrintf(const IMG_CHAR *pszFormat, ...) +{ + va_list vaArgs; + unsigned long ulLockFlags = 0; + IMG_CHAR *pszBuf; + IMG_UINT32 ui32BufSiz; + + SelectBuffer(&pszBuf, &ui32BufSiz); + + va_start(vaArgs, pszFormat); + + GetBufferLock(&ulLockFlags); + strncpy (pszBuf, "PVR_K: ", (ui32BufSiz -1)); + + if (VBAppend(pszBuf, ui32BufSiz, pszFormat, vaArgs)) + { + printk(KERN_INFO "PVR_K:(Message Truncated): %s\n", pszBuf); + } + else + { + printk(KERN_INFO "%s\n", pszBuf); + } + + ReleaseBufferLock(ulLockFlags); + va_end(vaArgs); + +} + +#if defined(PVRSRV_NEED_PVR_ASSERT) + +IMG_VOID PVRSRVDebugAssertFail(const IMG_CHAR* pszFile, IMG_UINT32 uLine) +{ + PVRSRVDebugPrintf(DBGPRIV_FATAL, pszFile, uLine, "Debug assertion failed!"); + BUG(); +} + +#endif + +#if defined(PVRSRV_NEED_PVR_TRACE) + +IMG_VOID PVRSRVTrace(const IMG_CHAR* pszFormat, ...) +{ + va_list VArgs; + unsigned long ulLockFlags = 0; + IMG_CHAR *pszBuf; + IMG_UINT32 ui32BufSiz; + + SelectBuffer(&pszBuf, &ui32BufSiz); + + va_start(VArgs, pszFormat); + + GetBufferLock(&ulLockFlags); + + strncpy(pszBuf, "PVR: ", (ui32BufSiz -1)); + + if (VBAppend(pszBuf, ui32BufSiz, pszFormat, VArgs)) + { + printk(KERN_INFO "PVR_K:(Message Truncated): %s\n", pszBuf); + } + else + { + printk(KERN_INFO "%s\n", pszBuf); + } + + ReleaseBufferLock(ulLockFlags); + + va_end(VArgs); +} + +#endif + +#if defined(PVRSRV_NEED_PVR_DPF) + +static IMG_BOOL BAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, const IMG_CHAR *pszFormat, ...) +{ + va_list VArgs; + IMG_BOOL bTrunc; + + va_start (VArgs, pszFormat); + + bTrunc = VBAppend(pszBuf, ui32BufSiz, pszFormat, VArgs); + + va_end (VArgs); + + return bTrunc; +} + +IMG_VOID PVRSRVDebugPrintf ( + IMG_UINT32 ui32DebugLevel, + const IMG_CHAR* pszFullFileName, + IMG_UINT32 ui32Line, + const IMG_CHAR* pszFormat, + ... + ) +{ + IMG_BOOL bTrace; + const IMG_CHAR *pszFileName = pszFullFileName; + IMG_CHAR *pszLeafName; + + + bTrace = (IMG_BOOL)(ui32DebugLevel & DBGPRIV_CALLTRACE) ? IMG_TRUE : IMG_FALSE; + + if (gPVRDebugLevel & ui32DebugLevel) + { + va_list vaArgs; + unsigned long ulLockFlags = 0; + IMG_CHAR *pszBuf; + IMG_UINT32 ui32BufSiz; + + SelectBuffer(&pszBuf, &ui32BufSiz); + + va_start(vaArgs, pszFormat); + + GetBufferLock(&ulLockFlags); + + + if (bTrace == IMG_FALSE) + { + switch(ui32DebugLevel) + { + case DBGPRIV_FATAL: + { + strncpy (pszBuf, "PVR_K:(Fatal): ", (ui32BufSiz -1)); + break; + } + case DBGPRIV_ERROR: + { + strncpy (pszBuf, "PVR_K:(Error): ", (ui32BufSiz -1)); + break; + } + case DBGPRIV_WARNING: + { + strncpy (pszBuf, "PVR_K:(Warning): ", (ui32BufSiz -1)); + break; + } + case DBGPRIV_MESSAGE: + { + strncpy (pszBuf, "PVR_K:(Message): ", (ui32BufSiz -1)); + break; + } + case DBGPRIV_VERBOSE: + { + strncpy (pszBuf, "PVR_K:(Verbose): ", (ui32BufSiz -1)); + break; + } + default: + { + strncpy (pszBuf, "PVR_K:(Unknown message level)", (ui32BufSiz -1)); + break; + } + } + } + else + { + strncpy (pszBuf, "PVR_K: ", (ui32BufSiz -1)); + } + + if (VBAppend(pszBuf, ui32BufSiz, pszFormat, vaArgs)) + { + printk(KERN_INFO "PVR_K:(Message Truncated): %s\n", pszBuf); + } + else + { + + if (bTrace == IMG_FALSE) + { +#ifdef DEBUG_LOG_PATH_TRUNCATE + + static IMG_CHAR szFileNameRewrite[PVR_MAX_FILEPATH_LEN]; + + IMG_CHAR* pszTruncIter; + IMG_CHAR* pszTruncBackInter; + + + if (strlen(pszFullFileName) > strlen(DEBUG_LOG_PATH_TRUNCATE)+1) + pszFileName = pszFullFileName + strlen(DEBUG_LOG_PATH_TRUNCATE)+1; + + + strncpy(szFileNameRewrite, pszFileName,PVR_MAX_FILEPATH_LEN); + + if(strlen(szFileNameRewrite) == PVR_MAX_FILEPATH_LEN-1) { + IMG_CHAR szTruncateMassage[] = "FILENAME TRUNCATED"; + strcpy(szFileNameRewrite + (PVR_MAX_FILEPATH_LEN - 1 - strlen(szTruncateMassage)), szTruncateMassage); + } + + pszTruncIter = szFileNameRewrite; + while(*pszTruncIter++ != 0) + { + IMG_CHAR* pszNextStartPoint; + + if( + !( ( *pszTruncIter == '/' && (pszTruncIter-4 >= szFileNameRewrite) ) && + ( *(pszTruncIter-1) == '.') && + ( *(pszTruncIter-2) == '.') && + ( *(pszTruncIter-3) == '/') ) + ) continue; + + + pszTruncBackInter = pszTruncIter - 3; + while(*(--pszTruncBackInter) != '/') + { + if(pszTruncBackInter <= szFileNameRewrite) break; + } + pszNextStartPoint = pszTruncBackInter; + + + while(*pszTruncIter != 0) + { + *pszTruncBackInter++ = *pszTruncIter++; + } + *pszTruncBackInter = 0; + + + pszTruncIter = pszNextStartPoint; + } + + pszFileName = szFileNameRewrite; + + if(*pszFileName == '/') pszFileName++; +#endif + +#if !defined(__sh__) + pszLeafName = (IMG_CHAR *)strrchr (pszFileName, '\\'); + + if (pszLeafName) + { + pszFileName = pszLeafName; + } +#endif + + if (BAppend(pszBuf, ui32BufSiz, " [%u, %s]", ui32Line, pszFileName)) + { + printk(KERN_INFO "PVR_K:(Message Truncated): %s\n", pszBuf); + } + else + { + printk(KERN_INFO "%s\n", pszBuf); + } + } + else + { + printk(KERN_INFO "%s\n", pszBuf); + } + } + + ReleaseBufferLock(ulLockFlags); + + va_end (vaArgs); + } +} + +#endif + +#if defined(DEBUG) + +IMG_INT PVRDebugProcSetLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT32 count, IMG_VOID *data) +{ +#define _PROC_SET_BUFFER_SZ 2 + IMG_CHAR data_buffer[_PROC_SET_BUFFER_SZ]; + + if (count != _PROC_SET_BUFFER_SZ) + { + return -EINVAL; + } + else + { + if (pvr_copy_from_user(data_buffer, buffer, count)) + return -EINVAL; + if (data_buffer[count - 1] != '\n') + return -EINVAL; + gPVRDebugLevel = data_buffer[0] - '0'; + } + return (count); +} + +void ProcSeqShowDebugLevel(struct seq_file *sfile,void* el) +{ + seq_printf(sfile, "%u\n", gPVRDebugLevel); +} + +#endif diff --git a/sgx/services4/srvkm/env/linux/pvr_drm.c b/sgx/services4/srvkm/env/linux/pvr_drm.c new file mode 100755 index 0000000..bf593d7 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pvr_drm.c @@ -0,0 +1,535 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#if defined(SUPPORT_DRI_DRM) + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <asm/ioctl.h> +#include <drm/drmP.h> +#include <drm/drm.h> + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) +# include <linux/omap_drm.h> +#endif + +#include "img_defs.h" +#include "services.h" +#include "kerneldisplay.h" +#include "kernelbuffer.h" +#include "syscommon.h" +#include "pvrmmap.h" +#include "mm.h" +#include "mmap.h" +#include "mutex.h" +#include "pvr_debug.h" +#include "srvkm.h" +#include "perproc.h" +#include "handle.h" +#include "pvr_bridge_km.h" +#include "pvr_bridge.h" +#include "proc.h" +#include "pvrmodule.h" +#include "pvrversion.h" +#include "lock.h" +#include "linkage.h" +#include "pvr_drm.h" + +#if defined(PVR_DRI_DRM_NOT_PCI) +#include "pvr_drm_mod.h" +#endif + +#define PVR_DRM_NAME PVRSRV_MODNAME +#define PVR_DRM_DESC "Imagination Technologies PVR DRM" + +DECLARE_WAIT_QUEUE_HEAD(sWaitForInit); + +IMG_BOOL bInitComplete; +IMG_BOOL bInitFailed; + +#if !defined(PVR_DRI_DRM_NOT_PCI) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +#if defined(PVR_DRI_DRM_PLATFORM_DEV) +struct platform_device *gpsPVRLDMDev; +#else +struct pci_dev *gpsPVRLDMDev; +#endif +#endif + +struct drm_device *gpsPVRDRMDev; + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)) +#error "Linux kernel version 2.6.25 or later required for PVR DRM support" +#endif + +#define PVR_DRM_FILE struct drm_file * + +#if !defined(SUPPORT_DRI_DRM_EXT) +#if defined(PVR_DRI_DRM_PLATFORM_DEV) +#if defined(PVR_LDM_PLATFORM_PRE_REGISTERED) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +static struct platform_device_id asPlatIdList[] = { + {SYS_SGX_DEV_NAME, 0}, + {} +}; +#endif +#else +static struct pci_device_id asPciIdList[] = { +#if defined(PVR_DRI_DRM_NOT_PCI) + {1, 1, 1, 1, 0, 0, 0}, +#else + {SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#if defined(SYS_SGX_DEV1_DEVICE_ID) + {SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV1_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#endif +#endif + {0} +}; +#endif +#endif + +DRI_DRM_STATIC int +PVRSRVDrmLoad(struct drm_device *dev, unsigned long flags) +{ + int iRes = 0; + + PVR_TRACE(("PVRSRVDrmLoad")); + + gpsPVRDRMDev = dev; +#if !defined(PVR_DRI_DRM_NOT_PCI) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +#if defined(PVR_DRI_DRM_PLATFORM_DEV) + gpsPVRLDMDev = dev->platformdev; +#else + gpsPVRLDMDev = dev->pdev; +#endif +#endif + +#if defined(PDUMP) + iRes = dbgdrv_init(); + if (iRes != 0) + { + goto exit; + } +#endif + + iRes = PVRCore_Init(); + if (iRes != 0) + { + goto exit_dbgdrv_cleanup; + } + +#if defined(DISPLAY_CONTROLLER) + iRes = PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Init)(dev); + if (iRes != 0) + { + goto exit_pvrcore_cleanup; + } +#endif + goto exit; + +#if defined(DISPLAY_CONTROLLER) +exit_pvrcore_cleanup: + PVRCore_Cleanup(); +#endif +exit_dbgdrv_cleanup: +#if defined(PDUMP) + dbgdrv_cleanup(); +#endif +exit: + if (iRes != 0) + { + bInitFailed = IMG_TRUE; + } + bInitComplete = IMG_TRUE; + + wake_up_interruptible(&sWaitForInit); + + return iRes; +} + +DRI_DRM_STATIC int +PVRSRVDrmUnload(struct drm_device *dev) +{ + PVR_TRACE(("PVRSRVDrmUnload")); + +#if defined(DISPLAY_CONTROLLER) + PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Cleanup)(dev); +#endif + + PVRCore_Cleanup(); + +#if defined(PDUMP) + dbgdrv_cleanup(); +#endif + + return 0; +} + +DRI_DRM_STATIC int +PVRSRVDrmOpen(struct drm_device *dev, struct drm_file *file) +{ + while (!bInitComplete) + { + DEFINE_WAIT(sWait); + + prepare_to_wait(&sWaitForInit, &sWait, TASK_INTERRUPTIBLE); + + if (!bInitComplete) + { + PVR_TRACE(("%s: Waiting for module initialisation to complete", __FUNCTION__)); + + schedule(); + } + + finish_wait(&sWaitForInit, &sWait); + + if (signal_pending(current)) + { + return -ERESTARTSYS; + } + } + + if (bInitFailed) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Module initialisation failed", __FUNCTION__)); + return -EINVAL; + } + + return PVRSRVOpen(dev, file); +} + +#if defined(SUPPORT_DRI_DRM_EXT) && !defined(PVR_LINUX_USING_WORKQUEUES) +DRI_DRM_STATIC void +PVRSRVDrmPostClose(struct drm_device *dev, struct drm_file *file) +{ + PVRSRVRelease(file->driver_priv); + + file->driver_priv = NULL; +} +#elif defined(SUPPORT_DRI_DRM_EXTERNAL) +DRI_DRM_STATIC int +PVRSRVDrmRelease(struct drm_device *dev, struct drm_file *file) +{ + void *psDriverPriv = file->driver_priv; + + PVR_TRACE(("PVRSRVDrmRelease: psDriverPriv=%p", psDriverPriv)); + + if (psDriverPriv) + { + PVRSRVRelease(psDriverPriv); + } + + return 0; +} +#else +DRI_DRM_STATIC int +PVRSRVDrmRelease(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + void *psDriverPriv = file_priv->driver_priv; + int ret; + + ret = drm_release(inode, filp); + + if (ret != 0) + { + + PVR_DPF((PVR_DBG_ERROR, "%s : drm_release failed: %d", + __FUNCTION__, ret)); + } + + PVRSRVRelease(psDriverPriv); + + return 0; +} +#endif + +DRI_DRM_STATIC int +PVRDRMIsMaster(struct drm_device *dev, void *arg, struct drm_file *pFile) +{ + return 0; +} + +#if defined(SUPPORT_DRI_DRM_EXT) +int +PVRDRM_Dummy_ioctl(struct drm_device *dev, void *arg, struct drm_file *pFile) +{ + return 0; +} +#endif + +DRI_DRM_STATIC int +PVRDRMUnprivCmd(struct drm_device *dev, void *arg, struct drm_file *pFile) +{ + int ret = 0; + + LinuxLockMutex(&gPVRSRVLock); + + if (arg == NULL) + { + ret = -EFAULT; + } + else + { + IMG_UINT32 *pui32Args = (IMG_UINT32 *)arg; + IMG_UINT32 ui32Cmd = pui32Args[0]; + IMG_UINT32 *pui32OutArg = (IMG_UINT32 *)arg; + + switch (ui32Cmd) + { + case PVR_DRM_UNPRIV_INIT_SUCCESFUL: + *pui32OutArg = PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_SUCCESSFUL) ? 1 : 0; + break; + + default: + ret = -EFAULT; + } + + } + + LinuxUnLockMutex(&gPVRSRVLock); + + return ret; +} + +#if defined(DISPLAY_CONTROLLER) && defined(PVR_DISPLAY_CONTROLLER_DRM_IOCTL) +static int +PVRDRM_Display_ioctl(struct drm_device *dev, void *arg, struct drm_file *pFile) +{ + int res; + + LinuxLockMutex(&gPVRSRVLock); + + res = PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Ioctl)(dev, arg, pFile); + + LinuxUnLockMutex(&gPVRSRVLock); + + return res; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) +#define PVR_DRM_FOPS_IOCTL .unlocked_ioctl +#define PVR_DRM_UNLOCKED DRM_UNLOCKED +#else +#define PVR_DRM_FOPS_IOCTL .ioctl +#define PVR_DRM_UNLOCKED 0 +#endif + +#if !defined(SUPPORT_DRI_DRM_EXT) + +#if defined(DRM_IOCTL_DEF) +#define PVR_DRM_IOCTL_DEF(ioctl, _func, _flags) DRM_IOCTL_DEF(DRM_##ioctl, _func, _flags) +#else +#define PVR_DRM_IOCTL_DEF(ioctl, _func, _flags) DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) +#endif + +struct drm_ioctl_desc sPVRDrmIoctls[] = { +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)) + DRM_IOCTL_DEF(PVR_DRM_SRVKM_IOCTL, PVRSRV_BridgeDispatchKM, PVR_DRM_UNLOCKED), + DRM_IOCTL_DEF(PVR_DRM_IS_MASTER_IOCTL, PVRDRMIsMaster, DRM_MASTER | PVR_DRM_UNLOCKED), + DRM_IOCTL_DEF(PVR_DRM_UNPRIV_IOCTL, PVRDRMUnprivCmd, PVR_DRM_UNLOCKED), +#if defined(PDUMP) + DRM_IOCTL_DEF(PVR_DRM_DBGDRV_IOCTL, dbgdrv_ioctl, PVR_DRM_UNLOCKED), +#endif +#if defined(DISPLAY_CONTROLLER) && defined(PVR_DISPLAY_CONTROLLER_DRM_IOCTL) + DRM_IOCTL_DEF(PVR_DRM_DISP_IOCTL, PVRDRM_Display_ioctl, DRM_MASTER | PVR_DRM_UNLOCKED) +#endif +#else + DRM_IOCTL_DEF_DRV(PVR_SRVKM, PVRSRV_BridgeDispatchKM, PVR_DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(PVR_IS_MASTER, PVRDRMIsMaster, DRM_MASTER | PVR_DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(PVR_UNPRIV, PVRDRMUnprivCmd, PVR_DRM_UNLOCKED), +#if defined(PDUMP) + DRM_IOCTL_DEF_DRV(PVR_DBGDRV, dbgdrv_ioctl, PVR_DRM_UNLOCKED), +#endif +#if defined(DISPLAY_CONTROLLER) && defined(PVR_DISPLAY_CONTROLLER_DRM_IOCTL) + DRM_IOCTL_DEF_DRV(PVR_DISP, PVRDRM_Display_ioctl, DRM_MASTER | PVR_DRM_UNLOCKED) +#endif +#endif +}; + +#if !defined(SUPPORT_DRI_DRM_EXTERNAL) +static int pvr_max_ioctl = DRM_ARRAY_SIZE(sPVRDrmIoctls); +#endif + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) +static struct omap_drm_plugin plugin = { + .name = PVR_DRM_NAME, + + .open = PVRSRVDrmOpen, + .load = PVRSRVDrmLoad, + .unload = PVRSRVDrmUnload, + + .release = PVRSRVDrmRelease, + .mmap = PVRMMap, + + .ioctls = sPVRDrmIoctls, + .num_ioctls = ARRAY_SIZE(sPVRDrmIoctls), + .ioctl_start = 0, +}; +#else +static struct drm_driver sPVRDrmDriver = +{ +#if defined(PVR_DRI_DRM_PLATFORM_DEV) + .driver_features = DRIVER_USE_PLATFORM_DEVICE, +#else + .driver_features = 0, +#endif + .dev_priv_size = 0, + .load = PVRSRVDrmLoad, + .unload = PVRSRVDrmUnload, + .open = PVRSRVDrmOpen, +#if !defined(PVR_DRI_DRM_PLATFORM_DEV) + .suspend = PVRSRVDriverSuspend, + .resume = PVRSRVDriverResume, +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, +#endif + .ioctls = sPVRDrmIoctls, + .fops = + { + .owner = THIS_MODULE, + .open = drm_open, + .release = PVRSRVDrmRelease, + PVR_DRM_FOPS_IOCTL = drm_ioctl, + .mmap = PVRMMap, + .poll = drm_poll, + .fasync = drm_fasync, + }, +#if defined(PVR_DRI_DRM_PLATFORM_DEV) + .platform_driver = + { + .id_table = asPlatIdList, + .driver = + { + .name = PVR_DRM_NAME, + }, + .probe = PVRSRVDrmProbe, + .remove = PVRSRVDrmRemove, + .suspend = PVRSRVDriverSuspend, + .resume = PVRSRVDriverResume, + .shutdown = PVRSRVDriverShutdown, + }, +#else + .pci_driver = + { + .name = PVR_DRM_NAME, + .id_table = asPciIdList, + }, +#endif + .name = PVR_DRM_NAME, + .desc = PVR_DRM_DESC, + .date = PVR_BUILD_DATE, + .major = PVRVERSION_MAJ, + .minor = PVRVERSION_MIN, + .patchlevel = PVRVERSION_BUILD, +}; +#endif + +#if defined(PVR_DRI_DRM_PLATFORM_DEV) && !defined(SUPPORT_DRI_DRM_EXT) && !defined(SUPPORT_DRI_DRM_EXTERNAL) +static int +PVRSRVDrmProbe(struct platform_device *pDevice) +{ + PVR_TRACE(("PVRSRVDrmProbe")); + + return drm_get_platform_dev(pDevice, &sPVRDrmDriver); +} + +static int +PVRSRVDrmRemove(struct platform_device *pDevice) +{ + PVR_TRACE(("PVRSRVDrmRemove")); + + drm_put_dev(gpsPVRDRMDev); + + return 0; +} +#endif + +static int __init PVRSRVDrmInit(void) +{ + int iRes; + +#if !defined(SUPPORT_DRI_DRM_EXTERNAL) + sPVRDrmDriver.num_ioctls = pvr_max_ioctl; +#endif + + PVRDPFInit(); + +#if defined(PVR_DRI_DRM_NOT_PCI) + iRes = drm_pvr_dev_add(); + if (iRes != 0) + { + return iRes; + } +#endif + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) + iRes = omap_drm_register_plugin(&plugin); +#else + iRes = drm_init(&sPVRDrmDriver); +#endif + +#if defined(PVR_DRI_DRM_NOT_PCI) + if (iRes != 0) + { + drm_pvr_dev_remove(); + } +#endif + return iRes; +} + +static void __exit PVRSRVDrmExit(void) +{ +#if defined(SUPPORT_DRI_DRM_EXTERNAL) + omap_drm_unregister_plugin(&plugin); +#else + drm_exit(&sPVRDrmDriver); + +#if defined(PVR_DRI_DRM_NOT_PCI) + drm_pvr_dev_remove(); +#endif +#endif +} + +module_init(PVRSRVDrmInit); +module_exit(PVRSRVDrmExit); +#endif +#endif + + diff --git a/sgx/services4/srvkm/env/linux/pvr_drm.h b/sgx/services4/srvkm/env/linux/pvr_drm.h new file mode 100755 index 0000000..9d79602 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pvr_drm.h @@ -0,0 +1,111 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#if !defined(__PVR_DRM_H__) +#define __PVR_DRM_H__ + +#include "pvr_drm_shared.h" + +#if defined(SUPPORT_DRI_DRM) +#define PVR_DRM_MAKENAME_HELPER(x, y) x ## y +#define PVR_DRM_MAKENAME(x, y) PVR_DRM_MAKENAME_HELPER(x, y) + +#if defined(PVR_DRI_DRM_PLATFORM_DEV) +#define LDM_DEV struct platform_device +#endif + +int PVRCore_Init(void); +void PVRCore_Cleanup(void); +int PVRSRVOpen(struct drm_device *dev, struct drm_file *pFile); +void PVRSRVRelease(void *pvPrivData); + +#if defined(SUPPORT_DRI_DRM_EXTERNAL) || defined(PVR_DRI_DRM_PLATFORM_DEV) +int PVRSRVDriverProbe(LDM_DEV *pDevice); +int PVRSRVDriverRemove(LDM_DEV *pDevice); +int PVRSRVDriverSuspend(LDM_DEV *pDevice, pm_message_t state); +int PVRSRVDriverResume(LDM_DEV *pDevice); +void PVRSRVDriverShutdown(LDM_DEV *pDevice); +#else +int PVRSRVDriverSuspend(struct drm_device *pDevice, pm_message_t state); +int PVRSRVDriverResume(struct drm_device *pDevice); +#endif + +int PVRSRV_BridgeDispatchKM(struct drm_device *dev, void *arg, struct drm_file *pFile); + +#if defined(SUPPORT_DRI_DRM_EXT) +#define DRI_DRM_STATIC +int PVRSRVDrmLoad(struct drm_device *dev, unsigned long flags); +int PVRSRVDrmUnload(struct drm_device *dev); +int PVRSRVDrmOpen(struct drm_device *dev, struct drm_file *file); +#if defined(PVR_LINUX_USING_WORKQUEUES) +DRI_DRM_STATIC int PVRSRVDrmRelease(struct inode *inode, struct file *filp); +#else +void PVRSRVDrmPostClose(struct drm_device *dev, struct drm_file *file); +#endif +int PVRDRMIsMaster(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFile); +int PVRDRMUnprivCmd(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFile); +int PVRDRM_Dummy_ioctl(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFile); +#else +#define DRI_DRM_STATIC static +#endif + +#if defined(DISPLAY_CONTROLLER) +extern int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Init)(struct drm_device *); +extern void PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Cleanup)(struct drm_device *); +extern int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Suspend)(struct drm_device *); +extern int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Resume)(struct drm_device *); +#if defined(PVR_DISPLAY_CONTROLLER_DRM_IOCTL) +extern int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Ioctl)(struct drm_device *dev, void *arg, struct drm_file *pFile); +#endif +#endif + +#if defined(PDUMP) +int dbgdrv_init(void); +void dbgdrv_cleanup(void); +IMG_INT dbgdrv_ioctl(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFile); +#endif + +#if !defined(SUPPORT_DRI_DRM_EXT) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)) +#define PVR_DRM_SRVKM_IOCTL _IO(0, DRM_PVR_SRVKM) +#define PVR_DRM_IS_MASTER_IOCTL _IO(0, DRM_PVR_IS_MASTER) +#define PVR_DRM_UNPRIV_IOCTL _IO(0, DRM_PVR_UNPRIV) +#define PVR_DRM_DBGDRV_IOCTL _IO(0, DRM_PVR_DBGDRV) +#define PVR_DRM_DISP_IOCTL _IO(0, DRM_PVR_DISP) +#else +#define DRM_IOCTL_PVR_SRVKM DRM_IO(DRM_COMMAND_BASE + DRM_PVR_SRVKM) +#define DRM_IOCTL_PVR_DISP DRM_IO(DRM_COMMAND_BASE + DRM_PVR_DISP) +#define DRM_IOCTL_PVR_IS_MASTER DRM_IO(DRM_COMMAND_BASE + DRM_PVR_IS_MASTER) +#define DRM_IOCTL_PVR_UNPRIV DRM_IO(DRM_COMMAND_BASE + DRM_PVR_UNPRIV) +#define DRM_IOCTL_PVR_DBGDRV DRM_IO(DRM_COMMAND_BASE + DRM_PVR_DBGDRV) +#endif +#endif + +#endif + +#endif + + diff --git a/sgx/services4/srvkm/env/linux/pvr_uaccess.h b/sgx/services4/srvkm/env/linux/pvr_uaccess.h new file mode 100644 index 0000000..bacafe9 --- /dev/null +++ b/sgx/services4/srvkm/env/linux/pvr_uaccess.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * 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 + * + ******************************************************************************/ + +#ifndef __PVR_UACCESS_H__ +#define __PVR_UACCESS_H__ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <asm/uaccess.h> + +static inline unsigned long pvr_copy_to_user(void __user *pvTo, const void *pvFrom, unsigned long ulBytes) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) + if (access_ok(VERIFY_WRITE, pvTo, ulBytes)) + { + return __copy_to_user(pvTo, pvFrom, ulBytes); + } + return ulBytes; +#else + return copy_to_user(pvTo, pvFrom, ulBytes); +#endif +} + +static inline unsigned long pvr_copy_from_user(void *pvTo, const void __user *pvFrom, unsigned long ulBytes) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) + + if (access_ok(VERIFY_READ, pvFrom, ulBytes)) + { + return __copy_from_user(pvTo, pvFrom, ulBytes); + } + return ulBytes; +#else + return copy_from_user(pvTo, pvFrom, ulBytes); +#endif +} + +#endif + |