diff options
Diffstat (limited to 'pvr-source/services4/srvkm/env/linux')
32 files changed, 17448 insertions, 0 deletions
diff --git a/pvr-source/services4/srvkm/env/linux/Kbuild.mk b/pvr-source/services4/srvkm/env/linux/Kbuild.mk new file mode 100644 index 0000000..25e35e9 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/Kbuild.mk @@ -0,0 +1,166 @@ +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 +# +# The contents of this file are subject to the MIT license as set out below. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 2 ("GPL") in which case the provisions +# of GPL are applicable instead of those above. +# +# If you wish to allow use of your version of this file only under the terms of +# GPL, and not to allow others to use your version of this file under the terms +# of the MIT license, indicate your decision by deleting the provisions above +# and replace them with the notice and other provisions required by GPL as set +# out in the file called "GPL-COPYING" included in this distribution. If you do +# not delete the provisions above, a recipient may use your version of this file +# under the terms of either the MIT license or GPL. +# +# This License is also included in this distribution in the file called +# "MIT-COPYING". +# +# EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +# PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +### ########################################################################### + +pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV)-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/env/linux/sysfs.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/lists.o \ + services4/srvkm/common/mem.o \ + services4/srvkm/common/mem_debug.o \ + services4/srvkm/common/metrics.o \ + services4/srvkm/common/osfunc_common.o \ + services4/srvkm/common/pdump_common.o \ + services4/srvkm/common/perproc.o \ + services4/srvkm/common/power.o \ + services4/srvkm/common/pvrsrv.o \ + services4/srvkm/common/queue.o \ + services4/srvkm/common/ra.o \ + services4/srvkm/common/refcount.o \ + services4/srvkm/common/resman.o \ + services4/srvkm/bridged/bridged_support.o \ + services4/srvkm/bridged/bridged_pvr_bridge.o \ + services4/system/$(PVR_SYSTEM)/sysconfig.o \ + services4/system/$(PVR_SYSTEM)/sysutils.o + +pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV)-$(CONFIG_ION_OMAP) += \ + services4/srvkm/env/linux/ion.o +pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV)-$(CONFIG_GCBV) += \ + services4/srvkm/env/linux/gc_bvmapping.o + +ifeq ($(SUPPORT_ION),1) +pvrsrvkm-y += \ + services4/srvkm/env/linux/ion.o +endif + +ifeq ($(TTRACE),1) +pvrsrvkm-y += \ + services4/srvkm/common/ttrace.o +endif + +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 +CFLAGS_refcount.o := -Werror +endif + +# SUPPORT_SGX==1 only + +pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV)-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) + +pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV)-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/pvr-source/services4/srvkm/env/linux/Linux.mk b/pvr-source/services4/srvkm/env/linux/Linux.mk new file mode 100644 index 0000000..7e3d0fb --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/Linux.mk @@ -0,0 +1,45 @@ +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 +# +# The contents of this file are subject to the MIT license as set out below. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 2 ("GPL") in which case the provisions +# of GPL are applicable instead of those above. +# +# If you wish to allow use of your version of this file only under the terms of +# GPL, and not to allow others to use your version of this file under the terms +# of the MIT license, indicate your decision by deleting the provisions above +# and replace them with the notice and other provisions required by GPL as set +# out in the file called "GPL-COPYING" included in this distribution. If you do +# not delete the provisions above, a recipient may use your version of this file +# under the terms of either the MIT license or GPL. +# +# This License is also included in this distribution in the file called +# "MIT-COPYING". +# +# EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +# PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +### ########################################################################### + +modules := srvkm + +srvkm_type := kernel_module +srvkm_target := pvrsrvkm_sgx$(SGXCORE)_$(SGX_CORE_REV).ko +srvkm_makefile := $(THIS_DIR)/Kbuild.mk diff --git a/pvr-source/services4/srvkm/env/linux/env_data.h b/pvr-source/services4/srvkm/env/linux/env_data.h new file mode 100644 index 0000000..b838809 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/env_data.h @@ -0,0 +1,93 @@ +/*************************************************************************/ /*! +@Title Environmental Data header file +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Linux-specific part of system data. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef _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 + +/* + * Env data specific to linux - convenient place to put this + */ + +/* Fairly arbitrary sizes - hopefully enough for all bridge calls */ +#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 +#if defined (SUPPORT_ION) + IMG_HANDLE hIonHeaps; + IMG_HANDLE hIonDev; +#endif +} ENV_DATA; + +#endif /* _ENV_DATA_ */ +/***************************************************************************** + End of file (env_data.h) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/env_perproc.h b/pvr-source/services4/srvkm/env/linux/env_perproc.h new file mode 100644 index 0000000..8a37a7f --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/env_perproc.h @@ -0,0 +1,79 @@ +/*************************************************************************/ /*! +@Title OS specific per process data interface +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Linux per process data +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __ENV_PERPROC_H__ +#define __ENV_PERPROC_H__ + +#include <linux/list.h> +#include <linux/proc_fs.h> + +#include "services.h" +#include "handle.h" + +#define ION_CLIENT_NAME_SIZE 50 +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 +#if defined (SUPPORT_ION) + struct ion_client *psIONClient; + IMG_CHAR azIonClientName[ION_CLIENT_NAME_SIZE]; +#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 /* __ENV_PERPROC_H__ */ + +/****************************************************************************** + End of file (env_perproc.h) +******************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/event.c b/pvr-source/services4/srvkm/env/linux/event.c new file mode 100644 index 0000000..b70a79d --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/event.c @@ -0,0 +1,414 @@ +/*************************************************************************/ /*! +@Title Event Object +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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; + +/*! +****************************************************************************** + + @Function LinuxEventObjectListCreate + + @Description + + Linux wait object list creation + + @Output hOSEventKM : Pointer to the event object list handle + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function LinuxEventObjectListDestroy + + @Description + + Linux wait object list destruction + + @Input hOSEventKM : Event object list handle + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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); + /*not nulling pointer, copy on stack*/ + } + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function LinuxEventObjectDelete + + @Description + + Linux wait object removal + + @Input hOSEventObjectList : Event object list handle + @Input hOSEventObject : Event object handle + @Input bResManCallback : Called from the resman + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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; + +} + +/*! +****************************************************************************** + + @Function LinuxEventObjectDeleteCallback + + @Description + + Linux wait object removal + + @Input hOSEventObject : Event object handle + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} +/*! +****************************************************************************** + + @Function LinuxEventObjectAdd + + @Description + + Linux wait object addition + + @Input hOSEventObjectList : Event object list handle + @Output phOSEventObject : Pointer to the event object handle + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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; + } + + /* allocate completion variable */ + 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; +} + +/*! +****************************************************************************** + + @Function LinuxEventObjectSignal + + @Description + + Linux wait object signaling function + + @Input hOSEventObjectList : Event object list handle + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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; + + /* + * We don't take the write lock in interrupt context, so we don't + * need to use read_lock_irqsave. + */ + 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; + +} + +/*! +****************************************************************************** + + @Function LinuxEventObjectWait + + @Description + + Linux wait object routine + + @Input hOSEventObject : Event object handle + + @Input ui32MSTimeout : Time out value in msec + + @Return PVRSRV_ERROR : Error code + +******************************************************************************/ +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/pvr-source/services4/srvkm/env/linux/event.h b/pvr-source/services4/srvkm/env/linux/event.h new file mode 100644 index 0000000..5c1451c --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/event.h @@ -0,0 +1,48 @@ +/*************************************************************************/ /*! +@Title Event Object +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + + +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/pvr-source/services4/srvkm/env/linux/gc_bvmapping.c b/pvr-source/services4/srvkm/env/linux/gc_bvmapping.c new file mode 100644 index 0000000..6c5d17a --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/gc_bvmapping.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but 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, see <http://www.gnu.org/licenses/>. + */ +#include <linux/bltsville.h> +#include <linux/bvinternal.h> +#include <linux/gcbv-iface.h> + +#include "gc_bvmapping.h" +#include "services_headers.h" + +void gc_bvmap_meminfo(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + int i; + IMG_CPU_PHYADDR phy_addr; + unsigned long *page_addrs; + struct bvbuffdesc *buffdesc; + struct bvphysdesc *physdesc; + int num_pages; + struct bventry bv_entry; + enum bverror bv_error; + + gcbv_init(&bv_entry); + if (!bv_entry.bv_map) { + psMemInfo->bvmap_handle = NULL; + return; + } + + num_pages = (psMemInfo->uAllocSize + + PAGE_SIZE - 1) >> PAGE_SHIFT; + + page_addrs = kzalloc(sizeof(*page_addrs) * num_pages, GFP_KERNEL); + if (!page_addrs) { + printk(KERN_ERR "%s: Out of memory\n", __func__); + return; + } + + physdesc = kzalloc(sizeof(*physdesc), GFP_KERNEL); + buffdesc = kzalloc(sizeof(*buffdesc), GFP_KERNEL); + if (!buffdesc || !physdesc) { + printk(KERN_ERR "%s: Out of memory\n", __func__); + kfree(page_addrs); + kfree(physdesc); + kfree(buffdesc); + return; + } + + for (i = 0; i < num_pages; i++) { + phy_addr = OSMemHandleToCpuPAddr( + psMemInfo->sMemBlk.hOSMemHandle, i << PAGE_SHIFT); + page_addrs[i] = (u32)phy_addr.uiAddr; + } + + buffdesc->structsize = sizeof(*buffdesc); + buffdesc->map = NULL; + buffdesc->length = psMemInfo->uAllocSize; + buffdesc->auxtype = BVAT_PHYSDESC; + buffdesc->auxptr = physdesc; + physdesc->structsize = sizeof(*physdesc); + physdesc->pagesize = PAGE_SIZE; + physdesc->pagearray = page_addrs; + physdesc->pagecount = num_pages; + + /* + * For ion allocated buffers let's verify how many planes this + * meminfo consist of + */ + if(psMemInfo->ui32Flags & PVRSRV_MEM_ION) { + IMG_UINT32 num_addr_offsets = 0; + OSGetMemMultiPlaneInfo(psMemInfo->sMemBlk.hOSMemHandle, + NULL, &num_addr_offsets); + + /* + * Account for this meminfo plane offset (relative to the base + * address) if necessary + */ + if(num_addr_offsets > 0) + physdesc->pageoffset = psMemInfo->planeOffsets[0]; + + /* + * In BV there is no way to specify multiple offsets, check + * all planes have the same offset and report any discrepancy + */ + for (i = 1; i < num_addr_offsets; i++) { + IMG_UINT32 plane_offset = + psMemInfo->planeOffsets[i] % PAGE_SIZE; + if (psMemInfo->planeOffsets[0] != plane_offset) { + printk(KERN_WARNING "%s: meminfo %p offset 0 %d" + " != offset %d %d, coalignment is " + "missing\n", __func__, psMemInfo, + psMemInfo->planeOffsets[0], + i, plane_offset); + } + } + } + + bv_error = bv_entry.bv_map(buffdesc); + if (bv_error) { + printk(KERN_ERR "%s: Failed to map meminfo %p, bverror %d\n", + __func__, psMemInfo, bv_error); + psMemInfo->bvmap_handle = NULL; + } else + psMemInfo->bvmap_handle = buffdesc; + +} + +void gc_bvunmap_meminfo(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + struct bvbuffdesc *buffdesc; + struct bvphysdesc *physdesc; + struct bventry bv_entry; + enum bverror bv_error; + + gcbv_init(&bv_entry); + if (!bv_entry.bv_map || !psMemInfo || !psMemInfo->bvmap_handle) + return; + + buffdesc = psMemInfo->bvmap_handle; + physdesc = (struct bvphysdesc*) buffdesc->auxptr; + bv_error = bv_entry.bv_unmap(buffdesc); + if (bv_error) { + printk(KERN_ERR "%s: Failed to unmap bvhandle %p from meminfo " + "%p, bverror %d\n", __func__, buffdesc, psMemInfo, + bv_error); + } + + kfree(physdesc->pagearray); + kfree(physdesc); + kfree(psMemInfo->bvmap_handle); + psMemInfo->bvmap_handle = NULL; +} + +IMG_VOID *gc_meminfo_to_hndl(PVRSRV_KERNEL_MEM_INFO *psMemInfo) +{ + return psMemInfo->bvmap_handle; +} diff --git a/pvr-source/services4/srvkm/env/linux/gc_bvmapping.h b/pvr-source/services4/srvkm/env/linux/gc_bvmapping.h new file mode 100644 index 0000000..6a3a2b1 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/gc_bvmapping.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef GC_BVMAPPING_H +#define GC_BVMAPPING_H + +#include "services_headers.h" + +void gc_bvunmap_meminfo(PVRSRV_KERNEL_MEM_INFO *psMemInfo); + +void gc_bvmap_meminfo(PVRSRV_KERNEL_MEM_INFO *psMemInfo); + +IMG_VOID *gc_meminfo_to_hndl(PVRSRV_KERNEL_MEM_INFO *psMemInfo); + +#endif diff --git a/pvr-source/services4/srvkm/env/linux/ion.c b/pvr-source/services4/srvkm/env/linux/ion.c new file mode 100644 index 0000000..3e772bc --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/ion.c @@ -0,0 +1,363 @@ +/*************************************************************************/ /*! +@Title Ion driver inter-operability code. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "ion.h" + +#include "services.h" +#include "servicesint.h" +#include "mutex.h" +#include "lock.h" +#include "mm.h" +#include "handle.h" +#include "perproc.h" +#include "env_perproc.h" +#include "private_data.h" +#include "pvr_debug.h" + +#include <linux/module.h> +#include <linux/file.h> +#include <linux/fs.h> + +#if defined (CONFIG_ION_OMAP) +#define MAX_HANDLES_PER_FD 2 +extern struct ion_client *gpsIONClient; + +int PVRSRVExportFDToIONHandles(int fd, struct ion_client **client, + struct ion_handle **handles, + unsigned int *num_handles) +{ + PVRSRV_FILE_PRIVATE_DATA *psPrivateData; + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + LinuxMemArea *psLinuxMemArea; + PVRSRV_ERROR eError; + struct file *psFile; + int i; + unsigned int ui32NumHandles = *num_handles; + int ret = -EINVAL; + + /* Take the bridge mutex so the handle won't be freed underneath us */ + LinuxLockMutex(&gPVRSRVLock); + + psFile = fget(fd); + if(!psFile) + goto err_unlock; + + psPrivateData = psFile->private_data; + if(!psPrivateData) + { + PVR_DPF((PVR_DBG_ERROR, "%s: struct file* has no private_data; " + "invalid export handle", __func__)); + goto err_fput; + } + + eError = PVRSRVLookupHandle(KERNEL_HANDLE_BASE, + (IMG_PVOID *)&psKernelMemInfo, + psPrivateData->hKernelMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO); + if(eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up MEM_INFO handle", + __func__)); + goto err_fput; + } + + psLinuxMemArea = (LinuxMemArea *)psKernelMemInfo->sMemBlk.hOSMemHandle; + BUG_ON(psLinuxMemArea == IMG_NULL); + + if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_ION) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Valid handle, but not an ION buffer", + __func__)); + goto err_fput; + } + + /* Client is requesting fewer handles then we have */ + if(ui32NumHandles < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes) { + + PVR_DPF((PVR_DBG_ERROR, "%s: Client requested %u handles, but we have %u", + __func__, + ui32NumHandles, + psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes)); + + /* Clear client handles */ + for (i = 0; i < ui32NumHandles; i++) + handles[i] = NULL; + + /* Return number of handles to client */ + *num_handles = psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes; + goto err_fput; + } + + for (i = 0; (i < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes) && (i < MAX_HANDLES_PER_FD); i++) + handles[i] = psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]; + + *num_handles = i; + + if(client) + *client = gpsIONClient; + + ret = 0; + +err_fput: + fput(psFile); +err_unlock: + /* Allow PVRSRV clients to communicate with srvkm again */ + LinuxUnLockMutex(&gPVRSRVLock); + + return ret; +} + +struct ion_handle * +PVRSRVExportFDToIONHandle(int fd, struct ion_client **client) +{ + unsigned int num_handles = 1; + struct ion_handle *psHandle = IMG_NULL; + PVRSRVExportFDToIONHandles(fd, client, &psHandle, &num_handles); + return psHandle; +} + +EXPORT_SYMBOL(PVRSRVExportFDToIONHandles); +EXPORT_SYMBOL(PVRSRVExportFDToIONHandle); +#endif + +#if defined (SUPPORT_ION) +#include "syscommon.h" +#include "env_data.h" +#include "../drivers/gpu/ion/ion_priv.h" +#include "linux/kernel.h" + +struct ion_heap **apsIonHeaps; +struct ion_device *psIonDev; + +static struct ion_platform_data generic_config = { + .nr = 2, + .heaps = { + { + .type = ION_HEAP_TYPE_SYSTEM_CONTIG, + .name = "System contig", + .id = ION_HEAP_TYPE_SYSTEM_CONTIG, + }, + { + .type = ION_HEAP_TYPE_SYSTEM, + .name = "System", + .id = ION_HEAP_TYPE_SYSTEM, + } + } +}; + +PVRSRV_ERROR IonInit(IMG_VOID) +{ + int uiHeapCount = generic_config.nr; + int uiError; + int i; + + apsIonHeaps = kzalloc(sizeof(struct ion_heap *) * uiHeapCount, GFP_KERNEL); + /* Create the ion devicenode */ + psIonDev = ion_device_create(NULL); + if (IS_ERR_OR_NULL(psIonDev)) { + kfree(apsIonHeaps); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* Register all the heaps */ + for (i = 0; i < generic_config.nr; i++) + { + struct ion_platform_heap *psPlatHeapData = &generic_config.heaps[i]; + + apsIonHeaps[i] = ion_heap_create(psPlatHeapData); + if (IS_ERR_OR_NULL(apsIonHeaps[i])) + { + uiError = PTR_ERR(apsIonHeaps[i]); + goto failHeapCreate; + } + ion_device_add_heap(psIonDev, apsIonHeaps[i]); + } + + return PVRSRV_OK; +failHeapCreate: + for (i = 0; i < uiHeapCount; i++) { + if (apsIonHeaps[i]) + { + ion_heap_destroy(apsIonHeaps[i]); + } + } + kfree(apsIonHeaps); + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + +IMG_VOID IonDeinit(IMG_VOID) +{ + int uiHeapCount = generic_config.nr; + int i; + + for (i = 0; i < uiHeapCount; i++) { + if (apsIonHeaps[i]) + { + ion_heap_destroy(apsIonHeaps[i]); + } + } + kfree(apsIonHeaps); + ion_device_destroy(psIonDev); +} + +typedef struct _ION_IMPORT_DATA_ +{ + struct ion_client *psIonClient; + struct ion_handle *psIonHandle; + IMG_PVOID pvKernAddr; +} ION_IMPORT_DATA; + +PVRSRV_ERROR IonImportBufferAndAquirePhysAddr(IMG_HANDLE hIonDev, + IMG_HANDLE hIonFD, + IMG_UINT32 *pui32PageCount, + IMG_SYS_PHYADDR **ppasSysPhysAddr, + IMG_PVOID *ppvKernAddr, + IMG_HANDLE *phPriv) +{ + struct ion_client *psIonClient = hIonDev; + struct ion_handle *psIonHandle; + struct scatterlist *psScatterList; + struct scatterlist *psTemp; + IMG_SYS_PHYADDR *pasSysPhysAddr = NULL; + ION_IMPORT_DATA *psImportData; + PVRSRV_ERROR eError; + IMG_UINT32 ui32PageCount = 0; + IMG_UINT32 i; + IMG_PVOID pvKernAddr; + int fd = (int) hIonFD; + + psImportData = kmalloc(sizeof(ION_IMPORT_DATA), GFP_KERNEL); + if (psImportData == NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + /* Get the buffer handle */ + psIonHandle = ion_import_fd(psIonClient, fd); + if (psIonHandle == IMG_NULL) + { + eError = PVRSRV_ERROR_BAD_MAPPING; + goto exitFailImport; + } + + /* Create data for free callback */ + psImportData->psIonClient = psIonClient; + psImportData->psIonHandle = psIonHandle; + + psScatterList = ion_map_dma(psIonClient, psIonHandle); + if (psScatterList == NULL) + { + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto exitFailMap; + } + + /* + We do a two pass process, 1st workout how many pages there + are, 2nd fill in the data. + */ + for (i=0;i<2;i++) + { + psTemp = psScatterList; + if (i == 1) + { + pasSysPhysAddr = kmalloc(sizeof(IMG_SYS_PHYADDR) * ui32PageCount, GFP_KERNEL); + if (pasSysPhysAddr == NULL) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto exitFailAlloc; + } + ui32PageCount = 0; /* Reset the page count a we use if for the index */ + } + + while(psTemp) + { + IMG_UINT32 j; + + for (j=0;j<psTemp->length;j+=PAGE_SIZE) + { + if (i == 1) + { + /* Pass 2: Get the page data */ + pasSysPhysAddr[ui32PageCount].uiAddr = sg_phys(psTemp); + } + ui32PageCount++; + } + psTemp = sg_next(psTemp); + } + } + + pvKernAddr = ion_map_kernel(psIonClient, psIonHandle); + if (IS_ERR(pvKernAddr)) + { + pvKernAddr = IMG_NULL; + } + + psImportData->pvKernAddr = pvKernAddr; + + *ppvKernAddr = pvKernAddr; + *pui32PageCount = ui32PageCount; + *ppasSysPhysAddr = pasSysPhysAddr; + *phPriv = psImportData; + return PVRSRV_OK; + +exitFailAlloc: + ion_unmap_dma(psIonClient, psIonHandle); +exitFailMap: + ion_free(psIonClient, psIonHandle); +exitFailImport: + kfree(psImportData); + return eError; +} + + +IMG_VOID IonUnimportBufferAndReleasePhysAddr(IMG_HANDLE hPriv) +{ + ION_IMPORT_DATA *psImportData = hPriv; + + ion_unmap_dma(psImportData->psIonClient, psImportData->psIonHandle); + if (psImportData->pvKernAddr) + { + ion_unmap_kernel(psImportData->psIonClient, psImportData->psIonHandle); + } + ion_free(psImportData->psIonClient, psImportData->psIonHandle); + kfree(psImportData); +} +#endif diff --git a/pvr-source/services4/srvkm/env/linux/ion.h b/pvr-source/services4/srvkm/env/linux/ion.h new file mode 100644 index 0000000..1cf385d --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/ion.h @@ -0,0 +1,74 @@ +/*************************************************************************/ /*! +@Title Ion driver inter-operability code. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#ifndef __IMG_LINUX_ION_H__ +#define __IMG_LINUX_ION_H__ + +#include <linux/ion.h> +#if defined (CONFIG_ION_OMAP) +#include <linux/omap_ion.h> +#endif +#if defined (SUPPORT_ION) +#include "img_types.h" +#include "servicesext.h" +#endif + +int PVRSRVExportFDToIONHandles(int fd, struct ion_client **client, + struct ion_handle **handles, + unsigned int *num_handles); + +struct ion_handle *PVRSRVExportFDToIONHandle(int fd, + struct ion_client **client); + +#if defined (SUPPORT_ION) +PVRSRV_ERROR IonInit(IMG_VOID); +IMG_VOID IonDeinit(IMG_VOID); + +PVRSRV_ERROR IonImportBufferAndAquirePhysAddr(IMG_HANDLE hIonDev, + IMG_HANDLE hIonFD, + IMG_UINT32 *pui32PageCount, + IMG_SYS_PHYADDR **ppasSysPhysAddr, + IMG_PVOID *ppvKernAddr, + IMG_HANDLE *phPriv); + +IMG_VOID IonUnimportBufferAndReleasePhysAddr(IMG_HANDLE hPriv); +#endif +#endif /* __IMG_LINUX_ION_H__ */ diff --git a/pvr-source/services4/srvkm/env/linux/linkage.h b/pvr-source/services4/srvkm/env/linux/linkage.h new file mode 100644 index 0000000..55cd4f0 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/linkage.h @@ -0,0 +1,72 @@ +/*************************************************************************/ /*! +@Title Linux specific Services code internal interfaces +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Interfaces between various parts of the Linux specific + Services code, that don't have any other obvious + header file to go into. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __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 /* PVR_MANUAL_POWER_CONTROL */ + +#endif /* DEBUG */ + +#endif /* __LINKAGE_H__ */ +/***************************************************************************** + End of file (linkage.h) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/lock.h b/pvr-source/services4/srvkm/env/linux/lock.h new file mode 100644 index 0000000..11adcaa --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/lock.h @@ -0,0 +1,56 @@ +/*************************************************************************/ /*! +@Title Main driver lock +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description The main driver lock, held in most places in + the driver. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __LOCK_H__ +#define __LOCK_H__ + +/* + * Main driver lock, used to ensure driver code is single threaded. + * There are some places where this lock must not be taken, such as + * in the mmap related deriver entry points. + */ +extern PVRSRV_LINUX_MUTEX gPVRSRVLock; + +#endif /* __LOCK_H__ */ +/***************************************************************************** + End of file (lock.h) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/mm.c b/pvr-source/services4/srvkm/env/linux/mm.c new file mode 100644 index 0000000..0815e46 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mm.c @@ -0,0 +1,2945 @@ +/*************************************************************************/ /*! +@Title Misc memory management utility functions for Linux +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#if !defined(PVR_LINUX_MEM_AREA_POOL_MAX_PAGES) +#define PVR_LINUX_MEM_AREA_POOL_MAX_PAGES 0 +#endif + +#include <linux/kernel.h> +#include <asm/atomic.h> +#include <linux/list.h> +#include <linux/mutex.h> +#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> + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) +#include <linux/shrinker.h> +#endif +#endif + +#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 there is no explicit definition + * for the minimum DMM alignment size, + * then set it to "0" and let ION/DMM + * set the minimum value. */ +#ifndef CONFIG_TILER_GRANULARITY +#define CONFIG_TILER_GRANULARITY 0 +#endif + +/* + * The page pool entry count is an atomic int so that the shrinker function + * can return it even when we can't take the lock that protects the page pool + * list. + */ +static atomic_t g_sPagePoolEntryCount = ATOMIC_INIT(0); + +#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_ION, +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + DEBUG_MEM_ALLOC_TYPE_VMAP, +#endif + DEBUG_MEM_ALLOC_TYPE_COUNT +} DEBUG_MEM_ALLOC_TYPE; + +typedef struct _DEBUG_MEM_ALLOC_REC +{ + DEBUG_MEM_ALLOC_TYPE eAllocType; + IMG_VOID *pvKey; /* Some unique value (private to the eAllocType) */ + 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]; + +/* vmalloc + kmalloc + alloc_pages + kmem_cache */ +static IMG_UINT32 g_SysRAMWaterMark; /* Doesn't include page pool */ +static IMG_UINT32 g_SysRAMHighWaterMark; /* *DOES* include page pool */ + +static inline IMG_UINT32 +SysRAMTrueWaterMark(void) +{ + return g_SysRAMWaterMark + PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount)); +} + +/* ioremap + io */ +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; +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; + +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 + +typedef struct +{ + /* Linkage for page pool LRU list */ + struct list_head sPagePoolItem; + + struct page *psPage; +} LinuxPagePoolEntry; + +static LinuxKMemCache *g_PsLinuxMemAreaCache; +static LinuxKMemCache *g_PsLinuxPagePoolCache; + +static LIST_HEAD(g_sPagePoolList); +static int g_iPagePoolMaxEntries; + +#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 + + +static inline IMG_BOOL +AreaIsUncached(IMG_UINT32 ui32AreaFlags) +{ + return (ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) != 0; +} + +static inline IMG_BOOL +CanFreeToPool(LinuxMemArea *psLinuxMemArea) +{ + return AreaIsUncached(psLinuxMemArea->ui32AreaFlags) && !psLinuxMemArea->bNeedsCacheInvalidate; +} + +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) + { + IMG_UINT32 ui32SysRAMTrueWaterMark; + + g_SysRAMWaterMark += ui32Bytes; + ui32SysRAMTrueWaterMark = SysRAMTrueWaterMark(); + + if (ui32SysRAMTrueWaterMark > g_SysRAMHighWaterMark) + { + g_SysRAMHighWaterMark = ui32SysRAMTrueWaterMark; + } + } + 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) +{ +/* DEBUG_MEM_ALLOC_REC **ppsCurrentRecord;*/ + + LinuxLockMutex(&g_sDebugMutex); + + /* Locate the corresponding allocation entry */ + 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", +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + "VMAP" +#endif + }; + return apszDebugMemoryRecordTypes[eAllocType]; +} +#endif + + +static IMG_BOOL +AllocFlagsToPGProt(pgprot_t *pPGProtFlags, IMG_UINT32 ui32AllocFlags) +{ + pgprot_t PGProtFlags; + + switch (ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) + { + case PVRSRV_HAP_CACHED: + 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, + "%s: Unknown mapping flags=0x%08x", + __FUNCTION__, ui32AllocFlags)); + dump_stack(); + return IMG_FALSE; + } + + *pPGProtFlags = PGProtFlags; + + return IMG_TRUE; +} + +IMG_VOID * +_VMallocWrapper(IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32AllocFlags, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + pgprot_t PGProtFlags; + IMG_VOID *pvRet; + + if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags)) + { + return NULL; + } + + /* Allocate virtually contiguous pages */ + 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); +} + + +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) +static IMG_VOID * +_VMapWrapper(struct page **ppsPageList, IMG_UINT32 ui32NumPages, IMG_UINT32 ui32AllocFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ + pgprot_t PGProtFlags; + IMG_VOID *pvRet; + + if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags)) + { + return NULL; + } + + pvRet = vmap(ppsPageList, ui32NumPages, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + if (pvRet) + { + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMAP, + pvRet, + pvRet, + 0, + NULL, + PAGES_TO_BYTES(ui32NumPages), + pszFileName, + ui32Line + ); + } +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + return pvRet; +} + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, __FILE__, __LINE__) +#else +#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, NULL, 0) +#endif + + +static IMG_VOID +_VUnmapWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_VMAP, pvCpuVAddr, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + vunmap(pvCpuVAddr); +} + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, __FILE__, __LINE__) +#else +#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, NULL, 0) +#endif + +#endif /* defined(PVR_LINUX_MEM_AREA_USE_VMAP) */ + + +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); + + /* In this case kmem_cache_t is an incomplete typedef, + * so we can't even de-reference to get the name member. It is also a GPL export symbol */ + return ""; +} + + +static LinuxPagePoolEntry * +LinuxPagePoolEntryAlloc(IMG_VOID) +{ + return KMemCacheAllocWrapper(g_PsLinuxPagePoolCache, GFP_KERNEL); +} + +static IMG_VOID +LinuxPagePoolEntryFree(LinuxPagePoolEntry *psPagePoolEntry) +{ + KMemCacheFreeWrapper(g_PsLinuxPagePoolCache, psPagePoolEntry); +} + + +static struct page * +AllocPageFromLinux(void) +{ + struct page *psPage; + + psPage = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); + if (!psPage) + { + return NULL; + + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + /* Reserve those pages to allow them to be re-mapped to user space */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + SetPageReserved(psPage); +#else + mem_map_reserve(psPage); +#endif +#endif + return psPage; +} + + +static IMG_VOID +FreePageToLinux(struct page *psPage) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + ClearPageReserved(psPage); +#else + mem_map_reserve(psPage); +#endif +#endif + __free_pages(psPage, 0); +} + + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) +static DEFINE_MUTEX(g_sPagePoolMutex); + +static inline void +PagePoolLock(void) +{ + mutex_lock(&g_sPagePoolMutex); +} + +static inline void +PagePoolUnlock(void) +{ + mutex_unlock(&g_sPagePoolMutex); +} + +static inline int +PagePoolTrylock(void) +{ + return mutex_trylock(&g_sPagePoolMutex); +} + +#else /* (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) */ +static inline void +PagePoolLock(void) +{ +} + +static inline void +PagePoolUnlock(void) +{ +} + +static inline int +PagePoolTrylock(void) +{ + return 1; +} +#endif /* (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) */ + + +static inline void +AddEntryToPool(LinuxPagePoolEntry *psPagePoolEntry) +{ + list_add_tail(&psPagePoolEntry->sPagePoolItem, &g_sPagePoolList); + atomic_inc(&g_sPagePoolEntryCount); +} + +static inline void +RemoveEntryFromPool(LinuxPagePoolEntry *psPagePoolEntry) +{ + list_del(&psPagePoolEntry->sPagePoolItem); + atomic_dec(&g_sPagePoolEntryCount); +} + +static inline LinuxPagePoolEntry * +RemoveFirstEntryFromPool(void) +{ + LinuxPagePoolEntry *psPagePoolEntry; + + if (list_empty(&g_sPagePoolList)) + { + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + + return NULL; + } + + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) > 0); + + psPagePoolEntry = list_first_entry(&g_sPagePoolList, LinuxPagePoolEntry, sPagePoolItem); + + RemoveEntryFromPool(psPagePoolEntry); + + return psPagePoolEntry; +} + +static struct page * +AllocPage(IMG_UINT32 ui32AreaFlags, IMG_BOOL *pbFromPagePool) +{ + struct page *psPage = NULL; + + /* + * Only uncached allocations can come from the page pool. + * The page pool is currently used to reduce the cost of + * invalidating the CPU cache when uncached memory is allocated. + */ + if (AreaIsUncached(ui32AreaFlags) && atomic_read(&g_sPagePoolEntryCount) != 0) + { + LinuxPagePoolEntry *psPagePoolEntry; + + PagePoolLock(); + psPagePoolEntry = RemoveFirstEntryFromPool(); + PagePoolUnlock(); + + /* List may have changed since we checked the counter */ + if (psPagePoolEntry) + { + psPage = psPagePoolEntry->psPage; + LinuxPagePoolEntryFree(psPagePoolEntry); + *pbFromPagePool = IMG_TRUE; + } + } + + if (!psPage) + { + psPage = AllocPageFromLinux(); + if (psPage) + { + *pbFromPagePool = IMG_FALSE; + } + } + + return psPage; + +} + +static IMG_VOID +FreePage(IMG_BOOL bToPagePool, struct page *psPage) +{ + /* Only uncached allocations can be freed to the page pool */ + if (bToPagePool && atomic_read(&g_sPagePoolEntryCount) < g_iPagePoolMaxEntries) + { + LinuxPagePoolEntry *psPagePoolEntry = LinuxPagePoolEntryAlloc(); + if (psPagePoolEntry) + { + psPagePoolEntry->psPage = psPage; + + PagePoolLock(); + AddEntryToPool(psPagePoolEntry); + PagePoolUnlock(); + + return; + } + } + + FreePageToLinux(psPage); +} + +static IMG_VOID +FreePagePool(IMG_VOID) +{ + LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry; + + PagePoolLock(); + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + PVR_DPF((PVR_DBG_MESSAGE,"%s: Freeing %d pages from pool", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); +#else + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + PVR_ASSERT(list_empty(&g_sPagePoolList)); +#endif + + list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem) + { + RemoveEntryFromPool(psPagePoolEntry); + + FreePageToLinux(psPagePoolEntry->psPage); + LinuxPagePoolEntryFree(psPagePoolEntry); + } + + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + + PagePoolUnlock(); +} + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +#if defined(PVRSRV_NEED_PVR_ASSERT) +static struct shrinker g_sShrinker; +#endif + +static int +ShrinkPagePool(struct shrinker *psShrinker, struct shrink_control *psShrinkControl) +{ + unsigned long uNumToScan = psShrinkControl->nr_to_scan; + + PVR_ASSERT(psShrinker == &g_sShrinker); + (void)psShrinker; + + if (uNumToScan != 0) + { + LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry; + + PVR_DPF((PVR_DBG_MESSAGE,"%s: Number to scan: %ld", __FUNCTION__, uNumToScan)); + PVR_DPF((PVR_DBG_MESSAGE,"%s: Pages in pool before scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); + + if (!PagePoolTrylock()) + { + PVR_TRACE(("%s: Couldn't get page pool lock", __FUNCTION__)); + return -1; + } + + list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem) + { + RemoveEntryFromPool(psPagePoolEntry); + + FreePageToLinux(psPagePoolEntry->psPage); + LinuxPagePoolEntryFree(psPagePoolEntry); + + if (--uNumToScan == 0) + { + break; + } + } + + if (list_empty(&g_sPagePoolList)) + { + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + } + + PagePoolUnlock(); + + PVR_DPF((PVR_DBG_MESSAGE,"%s: Pages in pool after scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); + } + + return atomic_read(&g_sPagePoolEntryCount); +} +#endif + +static IMG_BOOL +AllocPages(IMG_UINT32 ui32AreaFlags, struct page ***pppsPageList, IMG_HANDLE *phBlockPageList, IMG_UINT32 ui32NumPages, IMG_BOOL *pbFromPagePool) +{ + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; + IMG_INT32 i; /* Must be signed; see "for" loop conditions */ + PVRSRV_ERROR eError; + IMG_BOOL bFromPagePool = IMG_FALSE; + + eError = OSAllocMem(0, sizeof(*ppsPageList) * ui32NumPages, (IMG_VOID **)&ppsPageList, &hBlockPageList, + "Array of pages"); + if (eError != PVRSRV_OK) + { + goto failed_page_list_alloc; + } + + *pbFromPagePool = IMG_TRUE; + for(i = 0; i < (IMG_INT32)ui32NumPages; i++) + { + ppsPageList[i] = AllocPage(ui32AreaFlags, &bFromPagePool); + if (!ppsPageList[i]) + { + goto failed_alloc_pages; + } + *pbFromPagePool &= bFromPagePool; + } + + *pppsPageList = ppsPageList; + *phBlockPageList = hBlockPageList; + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, + ppsPageList, + 0, + 0, + NULL, + PAGES_TO_BYTES(ui32NumPages), + "unknown", + 0 + ); +#endif + + return IMG_TRUE; + +failed_alloc_pages: + for(i--; i >= 0; i--) + { + FreePage(*pbFromPagePool, ppsPageList[i]); + } + (IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList); + +failed_page_list_alloc: + return IMG_FALSE; +} + + +static IMG_VOID +FreePages(IMG_BOOL bToPagePool, struct page **ppsPageList, IMG_HANDLE hBlockPageList, IMG_UINT32 ui32NumPages) +{ + IMG_INT32 i; + + for(i = 0; i < (IMG_INT32)ui32NumPages; i++) + { + FreePage(bToPagePool, ppsPageList[i]); + } + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, ppsPageList, __FILE__, __LINE__); +#endif + + (IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList); +} + + +LinuxMemArea * +NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea = NULL; + IMG_VOID *pvCpuVAddr; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + IMG_UINT32 ui32NumPages = 0; + struct page **ppsPageList = NULL; + IMG_HANDLE hBlockPageList; +#endif + IMG_BOOL bFromPagePool = IMG_FALSE; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if (!psLinuxMemArea) + { + goto failed; + } + +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + + if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool)) + { + goto failed; + } + + pvCpuVAddr = VMapWrapper(ppsPageList, ui32NumPages, ui32AreaFlags); +#else /* defined(PVR_LINUX_MEM_AREA_USE_VMAP) */ + pvCpuVAddr = VMallocWrapper(ui32Bytes, ui32AreaFlags); + if (!pvCpuVAddr) + { + goto failed; + } +/* PG_reserved was deprecated in linux-2.6.15 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + /* Reserve those pages to allow them to be re-mapped to user space */ + ReservePages(pvCpuVAddr, ui32Bytes); +#endif +#endif /* defined(PVR_LINUX_MEM_AREA_USE_VMAP) */ + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_VMALLOC; + psLinuxMemArea->uData.sVmalloc.pvVmallocAddress = pvCpuVAddr; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + psLinuxMemArea->uData.sVmalloc.ppsPageList = ppsPageList; + psLinuxMemArea->uData.sVmalloc.hBlockPageList = hBlockPageList; +#endif + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + /* This works around a problem where Linux will not invalidate + * the cache for physical memory it frees that is direct mapped. + * + * As a result, cache entries remain that may be subsequently flushed + * to these physical pages after they have been allocated for another + * purpose. For a subsequent cached use of this memory, that is not a + * problem, but if we are allocating uncached or write-combined memory, + * and bypassing the cache, it can cause subsequent uncached writes to + * the memory to be replaced with junk from the cache. + * + * If the pages are from our page cache, no cache invalidate is needed. + * + * This just handles the __vmalloc() case (when we have a kernel virtual + * address range). The alloc_pages() path is handled in mmap.c. + */ + if (AreaIsUncached(ui32AreaFlags) && !bFromPagePool) + { + OSInvalidateCPUCacheRangeKM(psLinuxMemArea, 0, pvCpuVAddr, ui32Bytes); + } + + return psLinuxMemArea; + +failed: + PVR_DPF((PVR_DBG_ERROR, "%s: failed!", __FUNCTION__)); +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + if (ppsPageList) + { + FreePages(bFromPagePool, ppsPageList, hBlockPageList, ui32NumPages); + } +#endif + if (psLinuxMemArea) + { + LinuxMemAreaStructFree(psLinuxMemArea); + } + + return NULL; +} + + +IMG_VOID +FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; +#endif + + 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 + + PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", + __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); + +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + VUnmapWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); + + ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + ppsPageList = psLinuxMemArea->uData.sVmalloc.ppsPageList; + hBlockPageList = psLinuxMemArea->uData.sVmalloc.hBlockPageList; + + FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages); +#else +/* PG_reserved was deprecated in linux-2.6.15 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, + psLinuxMemArea->ui32ByteSize); +#endif + + VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); +#endif /* defined(PVR_LINUX_MEM_AREA_USE_VMAP) */ + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +/* Reserve pages of memory in order that they're not automatically + deallocated after the last user reference dies. */ +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 + } +} + + +/* Un-reserve pages of memory in order that they can be freed. */ +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 /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) */ + + +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: + 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) +/* + * Avoid using remap_pfn_range on RAM, if possible. On x86 systems, with + * PAT enabled, remap_pfn_range checks the page attributes requested by + * remap_pfn_range against those of the direct kernel mapping for those + * pages (if any). This is rather annoying if the pages have been obtained + * with alloc_pages, where we just ask for raw pages; we don't care about + * the direct mapping. This latter issue arises when device memory is + * exported from one process to another. Services implements this + * using memory wrapping, which ends up creating an external KV memory area. + */ +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); + + /* + * If bPhysContig is IMG_TRUE, we must assume psSysPhysAddr points + * to the address of the first page, not an array of page addresses. + */ + 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; + } + + /* Nothing to activly do. We just keep a record of the physical range. */ + 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 + + /* Nothing more to do than free the LinuxMemArea struct */ + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +LinuxMemArea * +NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) +{ + LinuxMemArea *psLinuxMemArea; + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; + IMG_BOOL bFromPagePool; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if (!psLinuxMemArea) + { + goto failed_area_alloc; + } + + ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + + if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool)) + { + goto failed_alloc_pages; + } + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES; + psLinuxMemArea->uData.sPageList.ppsPageList = ppsPageList; + psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList; + psLinuxMemArea->ui32ByteSize = ui32Bytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + + /* We defer the cache flush to the first user mapping of this memory */ + psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags) && !bFromPagePool; + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + + return psLinuxMemArea; + +failed_alloc_pages: + LinuxMemAreaStructFree(psLinuxMemArea); +failed_area_alloc: + PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__)); + + return NULL; +} + + +IMG_VOID +FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; + + PVR_ASSERT(psLinuxMemArea); + PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES); + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + + ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + ppsPageList = psLinuxMemArea->uData.sPageList.ppsPageList; + hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList; + + FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages); + + LinuxMemAreaStructFree(psLinuxMemArea); +} + +#if defined(CONFIG_ION_OMAP) + +#include "env_perproc.h" + +#include <linux/ion.h> +#include <linux/omap_ion.h> +#include <linux/scatterlist.h> + +extern struct ion_client *gpsIONClient; + +LinuxMemArea * +NewIONLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags, + IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength) +{ + const IMG_UINT32 ui32AllocDataLen = + offsetof(struct omap_ion_tiler_alloc_data, handle); + struct omap_ion_tiler_alloc_data asAllocData[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + u32 *pu32PageAddrs[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES] = { NULL, NULL, NULL}; + IMG_UINT32 i, j, ui32NumHandlesPerFd; + IMG_BYTE *pbPrivData = pvPrivData; + IMG_CPU_PHYADDR *pCPUPhysAddrs; + IMG_UINT32 iNumPages[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES] = { 0, 0, 0}; + LinuxMemArea *psLinuxMemArea; + IMG_UINT32 ui32ProcID; + IMG_UINT32 ui32TotalPagesSizeInBytes = 0, ui32TotalPages = 0; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if (!psLinuxMemArea) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate LinuxMemArea struct", __func__)); + goto err_out; + } + + /* Depending on the UM config, userspace might give us info for + * one, two or three ION allocations. Divide the total size of data we + * were given by this ui32AllocDataLen, and check it's 1 or 2. + * Otherwise abort. + */ + BUG_ON(ui32PrivDataLength != ui32AllocDataLen && + ui32PrivDataLength != ui32AllocDataLen * 2 && + ui32PrivDataLength != ui32AllocDataLen * 3); + /* This is bad !- change this logic to pass in the size or + * use uniformed API */ + ui32NumHandlesPerFd = ui32PrivDataLength / ui32AllocDataLen; + + ui32ProcID = OSGetCurrentProcessIDKM(); + + memset(asAllocData, 0x00, sizeof(asAllocData)); + + /* We do not care about what the first (Y) buffer offset would be, + * but we do care for the UV buffers to be co-aligned with Y + * This for SGX to find the UV offset solely based on the height + * and stride of the YUV buffer.This is very important for OMAP4470 + * and later chipsets, where SGX version is 544. 544 and later use + * non-shader based YUV to RGB conversion unit that require + * contiguous GPU virtual space */ + for(i = 0; i < ui32NumHandlesPerFd; i++) + { + memcpy(&asAllocData[i], &pbPrivData[i * ui32AllocDataLen], ui32AllocDataLen); + asAllocData[i].token = ui32ProcID; + +#ifndef SGX_DISABLE_DMM_OFFSET_BUFFER_ALLOCATIONS + if(i == 0) + { + /* Tiler API says: + * Allocate first buffer with the required alignment + * and an offset of 0 ... */ + asAllocData[i].out_align = CONFIG_TILER_GRANULARITY; + asAllocData[i].offset = 0; + } + else + { /* .. Then for the second buffer, use the offset from the first + * buffer with alignment of PAGE_SIZE */ + asAllocData[i].out_align = PAGE_SIZE; + asAllocData[i].offset = asAllocData[0].offset; + } +#else + asAllocData[i].offset = 0; + asAllocData[i].out_align = PAGE_SIZE; +#endif + + if(asAllocData[i].fmt == TILER_PIXEL_FMT_PAGE) + { + /* 1D DMM Buffers */ + struct scatterlist *sg, *sglist; + IMG_UINT32 ui32Num1dPages; + + asAllocData[i].handle = ion_alloc (gpsIONClient, + ui32Bytes, + PAGE_SIZE, (1 << OMAP_ION_HEAP_SYSTEM)); + + if (asAllocData[i].handle == NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_alloc", + __func__)); + goto err_free; + } + + sglist = ion_map_dma (gpsIONClient, asAllocData[i].handle); + if (sglist == NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute pages", + __func__)); + goto err_free; + } + + ui32Num1dPages = (ui32Bytes >> PAGE_SHIFT); + pu32PageAddrs[i] = kmalloc (sizeof(u32) * ui32Num1dPages, GFP_KERNEL); + if (pu32PageAddrs[i] == NULL) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate page array", + __func__)); + goto err_free; + } + + for_each_sg (sglist, sg, ui32Num1dPages, j) + { + pu32PageAddrs[i][j] = sg_phys (sg); + } + + iNumPages[i] = ui32Num1dPages; + } + else /* 2D DMM Buffers */ + { + if (omap_ion_tiler_alloc(gpsIONClient, &asAllocData[i]) < 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_tiler", + __func__)); + goto err_free; + } + + if (omap_tiler_pages(gpsIONClient, asAllocData[i].handle, &iNumPages[i], + &pu32PageAddrs[i]) < 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", + __func__)); + goto err_free; + } + } + } + + /* Basic sanity check on plane co-alignment */ + if((ui32NumHandlesPerFd > 1) && + (asAllocData[0].offset != asAllocData[1].offset)) + { + pr_err("%s: Y and UV offsets do not match for tiler handles " + "%p,%p: %d != %d \n " + "Expect issues with SGX544xx and later chipsets\n", + __func__, asAllocData[0].handle, asAllocData[1].handle, + (int)asAllocData[0].offset, (int)asAllocData[1].offset); + } + + /* Assume the user-allocator has already done the tiler math and that the + * number of tiler pages allocated matches any other allocation type. + */ + for(i = 0; i < ui32NumHandlesPerFd; i++) + { + ui32TotalPages += iNumPages[i]; + } + + BUG_ON(ui32Bytes != (ui32TotalPages * PAGE_SIZE)); + BUG_ON(sizeof(IMG_CPU_PHYADDR) != sizeof(int)); + + /* Glue the page lists together */ + pCPUPhysAddrs = vmalloc(sizeof(IMG_CPU_PHYADDR) * ui32TotalPages); + if (!pCPUPhysAddrs) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate page list", __func__)); + goto err_free; + } + + j = 0; + for(i = 0; i < ui32NumHandlesPerFd; i++) + { + IMG_UINT32 ui32PageIndx; + for(ui32PageIndx = 0; ui32PageIndx < iNumPages[i]; ui32PageIndx++) + { + pCPUPhysAddrs[j++].uiAddr = pu32PageAddrs[i][ui32PageIndx]; + } + + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = + asAllocData[i].handle; + psLinuxMemArea->uData.sIONTilerAlloc.planeOffsets[i] = + ui32TotalPagesSizeInBytes + asAllocData[i].offset; + /* Add the number of pages this plane consists of */ + ui32TotalPagesSizeInBytes += (iNumPages[i] * PAGE_SIZE); + } + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ION; + psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = pCPUPhysAddrs; + psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes = + ui32NumHandlesPerFd; + psLinuxMemArea->ui32ByteSize = ui32TotalPagesSizeInBytes; + psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; + INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); + + /* We defer the cache flush to the first user mapping of this memory */ + psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ION, + asAllocData[0].handle, + 0, + 0, + NULL, + PAGE_ALIGN(ui32Bytes), + "unknown", + 0 + ); +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); +#endif + +err_out: + return psLinuxMemArea; + +err_free: + LinuxMemAreaStructFree(psLinuxMemArea); + psLinuxMemArea = IMG_NULL; + goto err_out; +} + +IMG_INT32 +GetIONLinuxMemAreaInfo(LinuxMemArea *psLinuxMemArea, IMG_UINT32* pui32AddressOffsets, + IMG_UINT32* ui32NumAddrOffsets) +{ + IMG_UINT32 i; + + if(!ui32NumAddrOffsets) + return -1; + + if(*ui32NumAddrOffsets < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes) + { + *ui32NumAddrOffsets = psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes; + return -1; + } + + if(!pui32AddressOffsets) + return -1; + + for(i = 0; i < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes; i++) + { + if(psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]) + pui32AddressOffsets[i] = + psLinuxMemArea->uData.sIONTilerAlloc.planeOffsets[i]; + } + + *ui32NumAddrOffsets = psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes; + + return psLinuxMemArea->ui32ByteSize; +} + +IMG_VOID +FreeIONLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + IMG_UINT32 i; + +#if defined(DEBUG_LINUX_MEM_AREAS) + DebugLinuxMemAreaRecordRemove(psLinuxMemArea); +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ION, + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[0], + __FILE__, __LINE__); +#endif + + for(i = 0; i < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes; i++) + { + if (!psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]) + break; + ion_free(gpsIONClient, psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]); + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = IMG_NULL; + } + + /* free copy of page list, originals are freed by ion_free */ + vfree(psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs); + psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = IMG_NULL; + + LinuxMemAreaStructFree(psLinuxMemArea); +} + +#endif /* defined(CONFIG_ION_OMAP) */ + +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.ppsPageList[ui32PageIndex]; + + case LINUX_MEM_AREA_VMALLOC: + pui8Addr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; + pui8Addr += ui32ByteOffset; + return vmalloc_to_page(pui8Addr); + + case LINUX_MEM_AREA_SUB_ALLOC: + /* PRQA S 3670 3 */ /* ignore recursive warning */ + 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 /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) */ + ); +} + + +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; +} + + +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 + + /* Nothing more to do than free the LinuxMemArea structure */ + + LinuxMemAreaStructFree(psLinuxMemArea); +} + + +static LinuxMemArea * +LinuxMemAreaStructAlloc(IMG_VOID) +{ +/* debug */ +#if 0 + LinuxMemArea *psLinuxMemArea; + psLinuxMemArea = kmem_cache_alloc(g_PsLinuxMemAreaCache, GFP_KERNEL); + printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea); + dump_stack(); + return psLinuxMemArea; +#else + return KMemCacheAllocWrapper(g_PsLinuxMemAreaCache, GFP_KERNEL); +#endif +} + + +static IMG_VOID +LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) +{ + KMemCacheFreeWrapper(g_PsLinuxMemAreaCache, psLinuxMemArea); + /* debug */ + //printk(KERN_ERR "%s(%p)\n", __FUNCTION__, 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; + case LINUX_MEM_AREA_ION: + FreeIONLinuxMemArea(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++; + + /* Create a new memory allocation record */ + psNewRecord = kmalloc(sizeof(DEBUG_LINUX_MEM_AREA_REC), GFP_KERNEL); + if (psNewRecord) + { + /* Record the allocation */ + 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__)); + } + + /* Sanity check the flags */ + pi8FlagsString = HAPFlagsToString(ui32Flags); + if (strstr(pi8FlagsString, "UNKNOWN")) + { + PVR_DPF((PVR_DBG_ERROR, + "%s: Unexpected flags (0x%08x) associated with psLinuxMemArea @ %p", + __FUNCTION__, + ui32Flags, + psLinuxMemArea)); + //dump_stack(); + } + + 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); + +/*exit_unlock:*/ + 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--; + + /* Locate the corresponding allocation entry */ + psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, + MatchLinuxMemArea_AnyVaCb, + psLinuxMemArea); + if (psCurrentRecord) + { + /* Unlink the allocation record */ + 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); /* PRQA S 3670 */ /* ignore recursive warning */ + 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_ION: + { + IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); + CpuPAddr = psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs[ui32PageIndex]; + CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset); + break; + } + case LINUX_MEM_AREA_ALLOC_PAGES: + { + struct page *page; + IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); + page = psLinuxMemArea->uData.sPageList.ppsPageList[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_ION: + case LINUX_MEM_AREA_VMALLOC: + case LINUX_MEM_AREA_ALLOC_PAGES: + return IMG_FALSE; + + case LINUX_MEM_AREA_SUB_ALLOC: + /* PRQA S 3670 1 */ /* ignore recursive warning */ + 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) +{ + /* Note we explicitly check the types instead of e.g. + * using the type to index an array of strings so + * we remain orthogonal to enum changes */ + 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"; + case LINUX_MEM_AREA_ION: + return "LINUX_MEM_AREA_ION"; + 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 /* defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) */ + +#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; + } +} + +/* seq_file version of generating output, for reference check proc.c:CreateProcReadEntrySeq */ +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" /* (excluding SUB areas) */ + "\t<watermark key=\"mar1\" description=\"high\" bytes=\"%u\"/>\n" /* (excluding SUB areas) */ + "</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>%d</bytes>\n" + "\t<pid>%u</pid>\n" + "\t<flags>%08x</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 /* DEBUG_LINUX_MEM_AREAS */ + + +#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; + } +} + + +/* seq_file version of generating output, for reference check proc.c:CreateProcReadEntrySeq */ +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) + /* NOTE: If you update this code, please also update the XML varient below + * too! */ + + 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]); +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + seq_printf(sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes mapped via vmap", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); + seq_printf(sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes mapped via vmap", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); +#endif +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + seq_printf(sfile, "%-60s: %d pages\n", + "Number of pages in page pool", + atomic_read(&g_sPagePoolEntryCount)); +#endif + seq_printf( sfile, "\n"); + seq_printf(sfile, "%-60s: %d bytes\n", + "The Current Water Mark for memory allocated from system RAM", + SysRAMTrueWaterMark()); + 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 /* DEBUG_LINUX_XML_PROC_FILES */ + + /* Note: If you want to update the description property of a watermark + * ensure that the key property remains unchanged so that watermark data + * logged over time from different driver revisions may remain comparable + */ + 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]); +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + seq_printf(sfile, + "<watermark key=\"mr12\" description=\"vmap_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); + seq_printf(sfile, + "<watermark key=\"mr13\" description=\"vmap_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); +#endif + seq_printf(sfile, + "<watermark key=\"mr14\" description=\"system_ram_current\" bytes=\"%d\"/>\n", + SysRAMTrueWaterMark()); + 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); + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + seq_printf(sfile, + "<watermark key=\"mr18\" description=\"page_pool_current\" bytes=\"%d\"/>\n", + PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount))); +#endif + seq_printf(sfile, "</meminfo_header>\n"); + +#endif /* DEBUG_LINUX_XML_PROC_FILES */ + 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 /* defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) */ + + +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MMAP_AREAS) +/* This could be moved somewhere more general */ +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", + "WRITECOMBINE", + "UNKNOWN" + }; + IMG_CHAR *apszMapType[] = { + "KERNEL_ONLY", + "SINGLE_PROCESS", + "MULTI_PROCESS", + "FROM_EXISTING_PROCESS", + "NO_CPU_VIRTUAL", + "UNKNOWN" + }; + + /* FIXME create an enum for the cache type that we can + * cast and select so we get compiler warnings when + * when this code isn't complete due to new flags */ + if (ui32Flags & PVRSRV_HAP_UNCACHED) { + ui32CacheTypeIndex = 0; + } else if (ui32Flags & PVRSRV_HAP_CACHED) { + ui32CacheTypeIndex = 1; + } else if (ui32Flags & PVRSRV_HAP_WRITECOMBINE) { + ui32CacheTypeIndex = 2; + } else { + ui32CacheTypeIndex = 3; + PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type (%u)", + __FUNCTION__, (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK))); + } + + /* FIXME create an enum for the map type that we can + * cast and select so we get compiler warnings when + * when this code isn't complete due to new flags */ + 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 + +#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)); + /* Note this will also remove psCurrentRecord from g_LinuxMemAreaRecords + * but that's ok since we have already got a pointer to the next area. */ + LinuxMemAreaDeepFree(psLinuxMemArea); +} +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord) + +{ + +/* It's a bug if anything remains allocated at this point. We + * report an error, and simply brute force free anything we find. */ + 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: + /* Nothing needed except to free the record */ + 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; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + case DEBUG_MEM_ALLOC_TYPE_VMAP: + VUnmapWrapper(psCurrentRecord->pvCpuVAddr); + break; +#endif + default: + PVR_ASSERT(0); + } +} +#endif + + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +static struct shrinker g_sShrinker = +{ + .shrink = ShrinkPagePool, + .seeks = DEFAULT_SEEKS +}; + +static IMG_BOOL g_bShrinkerRegistered; +#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); + + if (g_SeqFileMemArea) + { + RemoveProcEntrySeq(g_SeqFileMemArea); + } + } +#endif + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) + if (g_bShrinkerRegistered) + { + unregister_shrinker(&g_sShrinker); + } +#endif + + /* + * The page pool must be freed after any remaining mem areas, but before + * the remaining memory resources. + */ + FreePagePool(); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + + /* + * It's a bug if anything remains allocated at this point. We + * report an error, and simply brute force free anything we find. + */ + List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, LinuxMMCleanup_MemRecords_ForEachVa); + + if (g_SeqFileMemoryRecords) + { + RemoveProcEntrySeq(g_SeqFileMemoryRecords); + } + } +#endif + + if (g_PsLinuxMemAreaCache) + { + KMemCacheDestroyWrapper(g_PsLinuxMemAreaCache); + } + + if (g_PsLinuxPagePoolCache) + { + KMemCacheDestroyWrapper(g_PsLinuxPagePoolCache); + } +} + +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) + { + goto failed; + } + } +#endif + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + g_SeqFileMemoryRecords = CreateProcReadEntrySeq( + "meminfo", + NULL, + ProcSeqNextMemoryRecords, + ProcSeqShowMemoryRecords, + ProcSeqOff2ElementMemoryRecords, + ProcSeqStartstopDebugMutex + ); + if (!g_SeqFileMemoryRecords) + { + goto failed; + } + } +#endif + + g_PsLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); + if (!g_PsLinuxMemAreaCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate mem area kmem_cache", __FUNCTION__)); + goto failed; + } + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + g_iPagePoolMaxEntries = PVR_LINUX_MEM_AREA_POOL_MAX_PAGES; + if (g_iPagePoolMaxEntries <= 0 || g_iPagePoolMaxEntries > INT_MAX/2) + { + g_iPagePoolMaxEntries = INT_MAX/2; + PVR_TRACE(("%s: No limit set for page pool size", __FUNCTION__)); + } + else + { + PVR_TRACE(("%s: Maximum page pool size: %d", __FUNCTION__, g_iPagePoolMaxEntries)); + } + + g_PsLinuxPagePoolCache = KMemCacheCreateWrapper("img-mm-pool", sizeof(LinuxPagePoolEntry), 0, 0); + if (!g_PsLinuxPagePoolCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate page pool kmem_cache", __FUNCTION__)); + goto failed; + } +#endif + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) + register_shrinker(&g_sShrinker); + g_bShrinkerRegistered = IMG_TRUE; +#endif + + return PVRSRV_OK; + +failed: + LinuxMMCleanup(); + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + diff --git a/pvr-source/services4/srvkm/env/linux/mm.h b/pvr-source/services4/srvkm/env/linux/mm.h new file mode 100644 index 0000000..5c01322 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mm.h @@ -0,0 +1,751 @@ +/*************************************************************************/ /*! +@Title Linux Memory Management. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Declares various memory management utility functions + for Linux. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __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)) + +#define PAGES_TO_BYTES(pages) ((pages) << PAGE_SHIFT) + +#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_ION, +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + LINUX_MEM_AREA_VMAP, +#endif + LINUX_MEM_AREA_TYPE_COUNT +}LINUX_MEM_AREA_TYPE; + +typedef struct _LinuxMemArea LinuxMemArea; + + +/* FIXME - describe this structure. */ +struct _LinuxMemArea { + LINUX_MEM_AREA_TYPE eAreaType; + union _uData + { + struct _sIORemap + { + /* Note: The memory this represents is _not_ implicitly + * page aligned, neither is its size */ + IMG_CPU_PHYADDR CPUPhysAddr; + IMG_VOID *pvIORemapCookie; + }sIORemap; + struct _sExternalKV + { + /* Note: The memory this represents is _not_ implicitly + * page aligned, neither is its size */ + IMG_BOOL bPhysContig; + union { + /* + * SYSPhysAddr is valid if bPhysContig is true, else + * pSysPhysAddr is valid + */ + IMG_SYS_PHYADDR SysPhysAddr; + IMG_SYS_PHYADDR *pSysPhysAddr; + } uPhysAddr; + IMG_VOID *pvExternalKV; + }sExternalKV; + struct _sIO + { + /* Note: The memory this represents is _not_ implicitly + * page aligned, neither is its size */ + IMG_CPU_PHYADDR CPUPhysAddr; + }sIO; + struct _sVmalloc + { + /* Note the memory this represents _is_ implicitly + * page aligned _and_ so is its size */ + IMG_VOID *pvVmallocAddress; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; +#endif + }sVmalloc; + struct _sPageList + { + /* Note the memory this represents _is_ implicitly + * page aligned _and_ so is its size */ + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; + }sPageList; + struct _sIONTilerAlloc + { + /* Note the memory this represents _is_ implicitly + * page aligned _and_ so is its size */ + IMG_CPU_PHYADDR *pCPUPhysAddrs; + IMG_UINT32 ui32NumValidPlanes; + struct ion_handle *psIONHandle[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + IMG_UINT32 planeOffsets[PVRSRV_MAX_NUMBER_OF_MM_BUFFER_PLANES]; + }sIONTilerAlloc; + struct _sSubAlloc + { + /* Note: The memory this represents is _not_ implicitly + * page aligned, neither is its size */ + LinuxMemArea *psParentLinuxMemArea; + IMG_UINT32 ui32ByteOffset; + }sSubAlloc; + }uData; + + IMG_UINT32 ui32ByteSize; /* Size of memory area */ + + IMG_UINT32 ui32AreaFlags; /* Flags passed at creation time */ + + IMG_BOOL bMMapRegistered; /* Registered with mmap code */ + + IMG_BOOL bNeedsCacheInvalidate; /* Cache should be invalidated on first map? */ + + IMG_HANDLE hBMHandle; /* Handle back to BM for this allocation */ + + /* List entry for global list of areas registered for mmap */ + struct list_head sMMapItem; + + /* + * Head of list of all mmap offset structures associated with this + * memory area. + */ + struct list_head sMMapOffsetStructList; +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)) +typedef kmem_cache_t LinuxKMemCache; +#else +typedef struct kmem_cache LinuxKMemCache; +#endif + + +/*! + ******************************************************************************* + * @Function LinuxMMInit + * + * @Description + * + * Initialise linux memory management code. + * This should be called during services initialisation. + * + * @Return none +******************************************************************************/ +PVRSRV_ERROR LinuxMMInit(IMG_VOID); + + +/*! + ******************************************************************************* + * + * @Function LinuxMMCleanup + * + * @Description + * + * Cleanup state for the linux memory management code. + * This should be called at services cleanup. + * + * @Return none +******************************************************************************/ +IMG_VOID LinuxMMCleanup(IMG_VOID); + + +/*! + ******************************************************************************* + * @brief Wrappers for kmalloc/kfree with optional /proc/pvr/km tracking + * They can also be used as more concise replacements for OSAllocMem + * in Linux specific code. + * + * @param ui32ByteSize + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief + * + * @param pvCpuVAddr + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief + * + * @param ui32Bytes + * @param ui32AllocFlags + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief + * + * @param pvCpuVAddr + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief Allocates virtually contiguous pages + * + * @param ui32Bytes number of bytes to reserve + * @param ui32AreaFlags Heap caching and mapping Flags + * + * @return Page-aligned address of virtual allocation or NULL on error + ******************************************************************************/ +LinuxMemArea *NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +/*! + ******************************************************************************* + * @brief Deallocates virtually contiguous pages + * + * @param LinuxMemArea from NewVMallocLinuxMemArea + * + ******************************************************************************/ +IMG_VOID FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +/*! + ******************************************************************************* + * @brief Reserve physical IO memory and create a CPU virtual mapping for it + * + * @param BasePAddr + * @param ui32Bytes + * @param ui32MappingFlags + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief Reserve physical IO memory and create a CPU virtual mapping for it + * + * @param BasePAddr + * @param ui32Bytes + * @param ui32AreaFlags Heap caching and mapping Flags + * + * @return + ******************************************************************************/ +LinuxMemArea *NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ********************************************************************************/ +IMG_VOID FreeIORemapLinuxMemArea(LinuxMemArea *psLinuxMemArea); + +/*! + ******************************************************************************* + * @brief Register physical memory which already has a CPU virtual mapping + * + * @param pBasePAddr + * @param pvCPUVAddr + * @param bPhysContig + * @param ui32Bytes + * @param ui32AreaFlags Heap caching and mapping Flags + * + * @return + ******************************************************************************/ +LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig, IMG_UINT32 ui32AreaFlags); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID FreeExternalKVLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +/*! + ****************************************************************************** + * @brief Unmaps an IO memory mapping created using IORemap + * + * @param pvIORemapCookie + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * @param ui32ByteOffset + * + * @return + ******************************************************************************/ +struct page *LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset); + + +/*! + ******************************************************************************* + * @brief + * + * @param pszName + * @param Size + * @param Align + * @param ui32Flags + * + * @return + ******************************************************************************/ +LinuxKMemCache *KMemCacheCreateWrapper(IMG_CHAR *pszName, size_t Size, size_t Align, IMG_UINT32 ui32Flags); + + +/*! + ******************************************************************************* + * @brief + * + * @param psCache + * + * @return + ******************************************************************************/ +IMG_VOID KMemCacheDestroyWrapper(LinuxKMemCache *psCache); + + +/*! + ******************************************************************************* + * @brief + * + * @param psCache + * @param Flags + * + * @return + ******************************************************************************/ +#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 + +/*! + ******************************************************************************* + * @brief + * + * @param psCache + * @param pvObject + * + * @return + ******************************************************************************/ +#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); + + +/*! + ******************************************************************************* + * @brief + * + * @param psCache + * + * @return + ******************************************************************************/ +const IMG_CHAR *KMemCacheNameWrapper(LinuxKMemCache *psCache); + + +/*! + ******************************************************************************* + * @brief + * + * @param BasePAddr + * @param ui32Bytes + * @param ui32AreaFlags Heap caching and mapping Flags + * + * @return + ******************************************************************************/ +LinuxMemArea *NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID FreeIOLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +/*! + ******************************************************************************* + * @brief + * + * @param ui32Bytes + * @param ui32AreaFlags E.g Heap caching and mapping Flags + * + * @return + ******************************************************************************/ +LinuxMemArea *NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea); + + +#if defined(CONFIG_ION_OMAP) + +/*! + ******************************************************************************* + * @brief + * + * @param ui32Bytes + * @param ui32AreaFlags E.g Heap caching and mapping Flags + * + * @return + ******************************************************************************/ +LinuxMemArea * +NewIONLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags, + IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID FreeIONLinuxMemArea(LinuxMemArea *psLinuxMemArea); + +IMG_INT32 +GetIONLinuxMemAreaInfo(LinuxMemArea *psLinuxMemArea, IMG_UINT32* ui32AddressOffsets, + IMG_UINT32* ui32NumAddr); + +#else /* defined(CONFIG_ION_OMAP) */ + +static inline LinuxMemArea * +NewIONLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags, + IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength) +{ + PVR_UNREFERENCED_PARAMETER(ui32Bytes); + PVR_UNREFERENCED_PARAMETER(ui32AreaFlags); + PVR_UNREFERENCED_PARAMETER(pvPrivData); + PVR_UNREFERENCED_PARAMETER(ui32PrivDataLength); + BUG(); + return IMG_NULL; +} + +static inline IMG_VOID FreeIONLinuxMemArea(LinuxMemArea *psLinuxMemArea) +{ + PVR_UNREFERENCED_PARAMETER(psLinuxMemArea); + BUG(); +} + +static inline IMG_INT32 +GetIONLinuxMemAreaInfo(LinuxMemArea *psLinuxMemArea, IMG_UINT32* ui32AddressOffsets, + IMG_UINT32* ui32NumAddr); +{ + PVR_UNREFERENCED_PARAMETER(psLinuxMemArea); + PVR_UNREFERENCED_PARAMETER(ui32AddressOffsets); + PVR_UNREFERENCED_PARAMETER(ui32NumAddr); + BUG(); + return -1; +} + +#endif /* defined(CONFIG_ION_OMAP) */ + + +/*! + ******************************************************************************* + * @brief + * + * @param psParentLinuxMemArea + * @param ui32ByteOffset + * @param ui32Bytes + * + * @return + ******************************************************************************/ +LinuxMemArea *NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, + IMG_UINT32 ui32ByteOffset, + IMG_UINT32 ui32Bytes); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea); + + +/*! + ******************************************************************************* + * @brief For debug builds, LinuxMemAreas are tracked in /proc + * + * @param psLinuxMemArea + * + ******************************************************************************/ +#if defined(LINUX_MEM_AREAS_DEBUG) +IMG_VOID LinuxMemAreaRegister(LinuxMemArea *psLinuxMemArea); +#else +#define LinuxMemAreaRegister(X) +#endif + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * + * @return + ******************************************************************************/ +IMG_VOID *LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea); + + +/*! + ******************************************************************************* + * @brief + * + * @param psLinuxMemArea + * @param ui32ByteOffset + * + * @return + ******************************************************************************/ +IMG_CPU_PHYADDR LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset); + + +#define LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32ByteOffset) PHYS_TO_PFN(LinuxMemAreaToCpuPAddr(psLinuxMemArea, ui32ByteOffset).uiAddr) + +/*! + ******************************************************************************* + * @brief Indicate whether a LinuxMemArea is physically contiguous + * + * @param psLinuxMemArea + * + * @return IMG_TRUE if the physical address range is contiguous, else IMG_FALSE + ******************************************************************************/ +IMG_BOOL LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea); + +/*! + ******************************************************************************* + * @brief Return the real underlying LinuxMemArea + * + * @param psLinuxMemArea + * + * @return The real underlying LinuxMemArea + ******************************************************************************/ +static inline LinuxMemArea * +LinuxMemAreaRoot(LinuxMemArea *psLinuxMemArea) +{ + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + return psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea; + } + else + { + return psLinuxMemArea; + } +} + + +/*! + ******************************************************************************* + * @brief Return type of real underlying LinuxMemArea + * + * @param psLinuxMemArea + * + * @return The areas eAreaType or for SUB areas; return the parents eAreaType. + ******************************************************************************/ +static inline LINUX_MEM_AREA_TYPE +LinuxMemAreaRootType(LinuxMemArea *psLinuxMemArea) +{ + return LinuxMemAreaRoot(psLinuxMemArea)->eAreaType; +} + + +/*! + ******************************************************************************* + * @brief Converts the enum type of a LinuxMemArea to a const string + * + * @param eMemAreaType + * + * @return const string representation of type + ******************************************************************************/ +const IMG_CHAR *LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType); + + +/*! + ******************************************************************************* + * @brief + * + * @param ui32Flags + * + * @return + ******************************************************************************/ +#if defined(DEBUG) || defined(DEBUG_LINUX_MEM_AREAS) +const IMG_CHAR *HAPFlagsToString(IMG_UINT32 ui32Flags); +#endif + +#endif /* __IMG_LINUX_MM_H__ */ + diff --git a/pvr-source/services4/srvkm/env/linux/mmap.c b/pvr-source/services4/srvkm/env/linux/mmap.c new file mode 100644 index 0000000..1a485c4 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mmap.c @@ -0,0 +1,1656 @@ +/*************************************************************************/ /*! +@Title Linux mmap interface +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) +#include <linux/highmem.h> +#endif +#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 "services_headers.h" + +#include "pvrmmap.h" +#include "mutils.h" +#include "mmap.h" +#include "mm.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 + +/* WARNING: + * The mmap code has its own mutex, to prevent a possible deadlock, + * when using gPVRSRVLock. + * The Linux kernel takes the mm->mmap_sem before calling the mmap + * entry points (PVRMMap, MMapVOpen, MMapVClose), but the ioctl + * entry point may take mm->mmap_sem during fault handling, or + * before calling get_user_pages. If gPVRSRVLock was used in the + * mmap entry points, a deadlock could result, due to the ioctl + * and mmap code taking the two locks in different orders. + * As a corollary to this, the mmap entry points must not call + * any driver code that relies on gPVRSRVLock is held. + */ +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 /* defined(DEBUG_LINUX_MMAP_AREAS) */ + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +/* + * Now that we are using mmap2 in srvclient, almost (*) the full 32 + * bit offset is available. The range of values is divided into two. + * The first part of the range, from FIRST_PHYSICAL_PFN to + * LAST_PHYSICAL_PFN, is for raw page mappings (VM_PFNMAP). The + * resulting 43 bit (*) physical address range should be enough for + * the current range of processors we support. + * + * NB: (*) -- the above figures assume 4KB page size. The offset + * argument to mmap2() is in units of 4,096 bytes regardless of page + * size. Thus, we lose (PAGE_SHIFT-12) bits of resolution on other + * architectures. + * + * The second part of the range, from FIRST_SPECIAL_PFN to LAST_SPECIAL_PFN, + * is used for all other mappings. These other mappings will always + * consist of pages with associated page structures, and need not + * represent a contiguous range of physical addresses. + * + */ +#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 /* !defined(PVR_MAKE_ALL_PFNS_SPECIAL) */ + +#if PAGE_SHIFT != 12 +#error This build variant has not yet been made non-4KB page-size aware +#endif + +/* + * Since we no longer have to worry about clashes with the mmap + * offsets used for pure PFN mappings (VM_PFNMAP), there is greater + * freedom in choosing the mmap handles. This is useful if the + * mmap offset space has to be shared with another driver component. + */ + +#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 /* !defined(PVR_MAKE_ALL_PFNS_SPECIAL) */ + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) +static inline IMG_BOOL +PFNIsPhysical(IMG_UINT32 pfn) +{ + /* Unsigned, no need to compare >=0 */ + return (/*(pfn >= FIRST_PHYSICAL_PFN) &&*/ (pfn <= LAST_PHYSICAL_PFN)) ? IMG_TRUE : IMG_FALSE; +} + +static inline IMG_BOOL +PFNIsSpecial(IMG_UINT32 pfn) +{ + /* Unsigned, no need to compare <=MAX_UINT */ + return ((pfn >= FIRST_SPECIAL_PFN) /*&& (pfn <= LAST_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) +/* + * Determine whether physical or special mappings will be used for + * a given memory area. At present, this decision is made on + * whether the mapping represents a contiguous range of physical + * addresses, which is a requirement for raw page mappings (VM_PFNMAP). + * In the VMA structure for such a mapping, vm_pgoff is the PFN + * (page frame number, the physical address divided by the page size) + * of the first page in the VMA. The second page is assumed to have + * PFN (vm_pgoff + 1), the third (vm_pgoff + 2) and so on. + */ +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) +{ + /* + * The PID is the thread ID, as each thread is a + * seperate process. + */ + return (IMG_UINT32)current->pid; +} +#endif + +/* + * Create an offset structure, which is used to hold per-process + * mmap data. + */ +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; + + /* + * We store the TID in case two threads within a process + * generate the same offset structure, and both end up on the + * list of structures waiting to be mapped, at the same time. + * This could happen if two sub areas within the same page are + * being mapped at the same time. + * The TID allows the mmap entry point to distinguish which + * mapping is being done by which thread. + */ +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + psOffsetStruct->ui32TID = GetCurrentThreadID(); +#endif + psOffsetStruct->ui32PID = OSGetCurrentProcessIDKM(); + +#if defined(DEBUG_LINUX_MMAP_AREAS) + /* Extra entries to support proc filesystem debug info */ + 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); +} + + +/* + * There are no alignment constraints for mapping requests made by user + * mode Services. For this, and potentially other reasons, the + * mapping created for a users request may look different to the + * original request in terms of size and alignment. + * + * This function determines an offset that the user can add to the mapping + * that is _actually_ created which will point to the memory they are + * _really_ interested in. + * + */ +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); +} + + +/*! + ******************************************************************************* + + @Function PVRMMapOSMemHandleToMMapData + + @Description + + Determine various parameters needed to mmap a memory area, and to + locate the memory within the mapped area. + + @input psPerProc : Per-process data. + @input hMHandle : Memory handle. + @input pui32MMapOffset : pointer to location for returned mmap offset. + @input pui32ByteOffset : pointer to location for returned byte offset. + @input pui32RealByteSize : pointer to location for returned real byte size. + @input pui32UserVaddr : pointer to location for returned user mode address. + + @output pui32MMapOffset : points to mmap offset to be used in mmap2 sys call. + @output pui32ByteOffset : points to byte offset of start of memory + within mapped area returned by mmap2. + @output pui32RealByteSize : points to size of area to be mapped. + @output pui32UserVAddr : points to user mode address of start of + mapping, or 0 if it hasn't been mapped yet. + + @Return PVRSRV_ERROR : PVRSRV_OK, or error code. + + ******************************************************************************/ +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; + + if (psLinuxMemArea && (psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ION)) + { + *pui32RealByteSize = psLinuxMemArea->ui32ByteSize; + *pui32ByteOffset = psLinuxMemArea->uData.sIONTilerAlloc.planeOffsets[0]; + /* The offsets for the subsequent planes must be co-aligned for user + * space mapping and sgx 544 and later. I.e. + * psLinuxMemArea->uData.sIONTilerAlloc.planeOffsets[n]; + */ + } + else + { + + /* Sparse mappings have to ask the BM for the virtual size */ + if (psLinuxMemArea->hBMHandle) + { + *pui32RealByteSize = BM_GetVirtualSize(psLinuxMemArea->hBMHandle); + *pui32ByteOffset = 0; + } + else + { + DetermineUsersSizeAndByteOffset(psLinuxMemArea, + pui32RealByteSize, + pui32ByteOffset); + } + } + + /* Check whether this memory area has already been mapped */ + list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) + { + if (psPerProc->ui32PID == psOffsetStruct->ui32PID) + { + if (!psLinuxMemArea->hBMHandle) + { + PVR_ASSERT(*pui32RealByteSize == psOffsetStruct->ui32RealByteSize); + } + /* + * User mode locking is required to stop two threads racing to + * map the same memory area. The lock should prevent a + * second thread retrieving mmap data for a given handle, + * before the first thread has done the mmap. + * Without locking, both threads may attempt the mmap, + * and one of them will fail. + */ + *pui32MMapOffset = psOffsetStruct->ui32MMapOffset; + *pui32UserVAddr = psOffsetStruct->ui32UserVAddr; + PVRSRVOffsetStructIncRef(psOffsetStruct); + + eError = PVRSRV_OK; + goto exit_unlock; + } + } + + /* Memory area won't have been mapped yet */ + *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; + } + + /* + * Offset structures representing physical mappings are added to + * a list, so that they can be located when the memory area is mapped. + */ + list_add_tail(&psOffsetStruct->sMMapItem, &g_sMMapOffsetStructList); + + psOffsetStruct->bOnMMapList = IMG_TRUE; + + PVRSRVOffsetStructIncRef(psOffsetStruct); + + eError = PVRSRV_OK; + + /* Need to scale up the offset to counter the shifting that + is done in the mmap2() syscall, as it expects the pgoff + argument to be in units of 4,096 bytes irrespective of + page size */ + *pui32MMapOffset = *pui32MMapOffset << (PAGE_SHIFT - 12); + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + + return eError; +} + + +/*! + ******************************************************************************* + + @Function PVRMMapReleaseMMapData + + @Description + + Release mmap data. + + @input psPerProc : Per-process data. + @input hMHandle : Memory handle. + @input pbMUnmap : pointer to location for munmap flag. + @input pui32UserVAddr : pointer to location for user mode address of mapping. + @input pui32ByteSize : pointer to location for size of mapping. + + @Output pbMUnmap : points to flag that indicates whether an munmap is + required. + @output pui32UserVAddr : points to user mode address to munmap. + + @Return PVRSRV_ERROR : PVRSRV_OK, or error code. + + ******************************************************************************/ +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; + + /* Find the offset structure */ + 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; + } + + PVRSRVOffsetStructDecRef(psOffsetStruct); + + *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; + } + } + + /* MMap data not found */ +#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 the offset is physical, make sure the thread IDs match, + * as different threads may be mapping different memory areas + * with the same offset. + */ + if (!PFNIsPhysical(ui32Offset) || psOffsetStruct->ui32TID == ui32TID) +#endif + { + return psOffsetStruct; + } + } + } + + return IMG_NULL; +} + + +/* + * Map a memory area into user space. + * Note, the ui32ByteOffset is _not_ implicitly page aligned since + * LINUX_MEM_AREA_SUB_ALLOC LinuxMemAreas have no alignment constraints. + */ +static IMG_BOOL +DoMapToUser(LinuxMemArea *psLinuxMemArea, + struct vm_area_struct* ps_vma, + IMG_UINT32 ui32ByteOffset) +{ + IMG_UINT32 ui32ByteSize; + + if ((psLinuxMemArea->hBMHandle) && (ui32ByteOffset != 0)) + { + /* Partial mapping of sparse allocations should never happen */ + return IMG_FALSE; + } + + if (psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + return DoMapToUser(LinuxMemAreaRoot(psLinuxMemArea), /* PRQA S 3670 */ /* allow recursion */ + ps_vma, + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + ui32ByteOffset); + } + + /* + * Note that ui32ByteSize may be larger than the size of the memory + * area being mapped, as the former is a multiple of the page size. + */ + ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start; + PVR_ASSERT(ADDR_TO_PAGE_OFFSET(ui32ByteSize) == 0); + +#if defined (__sparc__) + /* + * For LINUX_MEM_AREA_EXTERNAL_KV, we don't know where the address range + * we are being asked to map has come from, that is, whether it is memory + * or I/O. For all architectures other than SPARC, there is no distinction. + * Since we don't currently support SPARC, we won't worry about it. + */ +#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); + /* + * Since the memory is contiguous, we can map the whole range in one + * go . + */ + + PVR_ASSERT(psLinuxMemArea->hBMHandle == IMG_NULL); + + 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 + + { + /* + * Memory may be non-contiguous, so we map the range page, + * by page. Since VM_PFNMAP mappings are assumed to be physically + * contiguous, we can't legally use REMAP_PFN_RANGE (that is, we + * could, but the resulting VMA may confuse other bits of the kernel + * that attempt to interpret it). + * The only alternative is to use VM_INSERT_PAGE, which requires + * finding the page structure corresponding to each page, or + * if mixed maps are supported (VM_MIXEDMAP), vm_insert_mixed. + */ + IMG_UINT32 ulVMAPos; + IMG_UINT32 ui32ByteEnd = ui32ByteOffset + ui32ByteSize; + IMG_UINT32 ui32PA; + IMG_UINT32 ui32AdjustedPA = ui32ByteOffset; +#if defined(PVR_MAKE_ALL_PFNS_SPECIAL) + IMG_BOOL bMixedMap = IMG_FALSE; +#endif + /* First pass, validate the page frame numbers */ + for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) + { + IMG_UINT32 pfn; + IMG_BOOL bMapPage = IMG_TRUE; + + if (psLinuxMemArea->hBMHandle) + { + if (!BM_MapPageAtOffset(psLinuxMemArea->hBMHandle, ui32PA)) + { + bMapPage = IMG_FALSE; + } + } + + if (bMapPage) + { + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32AdjustedPA); + 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 + } + ui32AdjustedPA += PAGE_SIZE; + } + } + +#if defined(PVR_MAKE_ALL_PFNS_SPECIAL) + if (bMixedMap) + { + ps_vma->vm_flags |= VM_MIXEDMAP; + } +#endif + /* Second pass, get the page structures and insert the pages */ + ulVMAPos = ps_vma->vm_start; + ui32AdjustedPA = ui32ByteOffset; + for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) + { + IMG_UINT32 pfn; + IMG_INT result; + IMG_BOOL bMapPage = IMG_TRUE; + + if (psLinuxMemArea->hBMHandle) + { + /* We have a sparse allocation, check if this page should be mapped */ + if (!BM_MapPageAtOffset(psLinuxMemArea->hBMHandle, ui32PA)) + { + bMapPage = IMG_FALSE; + } + } + + if (bMapPage) + { + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32AdjustedPA); + +#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; + } + } + ui32AdjustedPA += PAGE_SIZE; + } + 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); + PVR_ASSERT(!psOffsetStruct->bOnMMapList); + + PVRSRVOffsetStructIncMapped(psOffsetStruct); + + 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 +} + + +/* + * Linux mmap open entry point. + */ +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); + PVRSRVOffsetStructDecMapped(psOffsetStruct); + 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; +} + +/* + * Linux mmap close entry point. + */ +static void +MMapVClose(struct vm_area_struct* ps_vma) +{ + LinuxLockMutex(&g_sMMapMutex); + + MMapVCloseNoLock(ps_vma); + + LinuxUnLockMutex(&g_sMMapMutex); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) +/* + * This vma operation is used to read data from mmap regions. It is called + * by access_process_vm, which is called to handle PTRACE_PEEKDATA ptrace + * requests and reads from /proc/<pid>/mem. + */ +static int MMapVAccess(struct vm_area_struct *ps_vma, unsigned long addr, + void *buf, int len, int write) +{ + PKV_OFFSET_STRUCT psOffsetStruct; + LinuxMemArea *psLinuxMemArea; + unsigned long ulOffset; + int iRetVal = -EINVAL; + IMG_VOID *pvKernelAddr; + + LinuxLockMutex(&g_sMMapMutex); + + psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; + psLinuxMemArea = psOffsetStruct->psLinuxMemArea; + ulOffset = addr - ps_vma->vm_start; + + if (ulOffset+len > psLinuxMemArea->ui32ByteSize) + /* Out of range. We shouldn't get here, because the kernel will do + the necessary checks before calling access_process_vm. */ + goto exit_unlock; + + pvKernelAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); + + if (pvKernelAddr) + { + memcpy(buf, pvKernelAddr+ulOffset, len); + iRetVal = len; + } + else + { + IMG_UINT32 pfn, ui32OffsetInPage; + struct page *page; + + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ulOffset); + + if (!pfn_valid(pfn)) + goto exit_unlock; + + page = pfn_to_page(pfn); + ui32OffsetInPage = ADDR_TO_PAGE_OFFSET(ulOffset); + + if (ui32OffsetInPage+len > PAGE_SIZE) + /* The region crosses a page boundary */ + goto exit_unlock; + + pvKernelAddr = kmap(page); + memcpy(buf, pvKernelAddr+ui32OffsetInPage, len); + kunmap(page); + + iRetVal = len; + } + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + return iRetVal; +} +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) */ + +static struct vm_operations_struct MMapIOOps = +{ + .open=MMapVOpen, + .close=MMapVClose, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) + .access=MMapVAccess, +#endif +}; + + +/*! + ******************************************************************************* + + @Function PVRMMap + + @Description + + Driver mmap entry point. + + @input pFile : unused. + @input ps_vma : pointer to linux memory area descriptor. + + @Return 0, or Linux error code. + + ******************************************************************************/ +int +PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) +{ + LinuxMemArea *psFlushMemArea = IMG_NULL; + PKV_OFFSET_STRUCT psOffsetStruct; + IMG_UINT32 ui32ByteSize; + IMG_VOID *pvBase = IMG_NULL; + int iRetVal = 0; + IMG_UINT32 ui32ByteOffset = 0; /* Keep compiler happy */ + IMG_UINT32 ui32FlushSize = 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) + /* Pass unknown requests onto the DRM module */ + return drm_mmap(pFile, ps_vma); +#else + /* + * Indicate to caller that the request is not for us. + * Do not return this error elsewhere in this function, as the + * caller may use it as a clue as to whether the mmap request + * should be passed on to another component (e.g. drm_mmap). + */ + 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; + + /* Only support shared writeable mappings */ + 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; + + /* + * Disable mremap because our nopage handler assumes all + * page requests have already been validated. + */ + ps_vma->vm_flags |= VM_DONTEXPAND; + + /* Don't allow mapping to be inherited across a process fork */ + 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: + /* This is the default, do nothing. */ + 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 defined(SGX544) && defined(SGX_FEATURE_MP) + /* In OMAP5, the A15 no longer masks an issue with the interconnect. + writecombined access to the Tiler 2D memory will encounter errors due to + interconect bus accesses. This will result in a SIGBUS error with a + "non-line fetch abort". The workaround is to use a shared device + access. */ + if (psOffsetStruct->psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ION) + ps_vma->vm_page_prot = __pgprot_modify(ps_vma->vm_page_prot, + L_PTE_MT_MASK, L_PTE_MT_DEV_SHARED); +#endif + + /* Install open and close handlers for ref-counting */ + 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; + + /* Invalidate for the ION memory is performed during the mapping */ + if(psOffsetStruct->psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ION) + psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE; + + /* Compute the flush region (if necessary) inside the mmap mutex */ + if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate) + { + psFlushMemArea = psOffsetStruct->psLinuxMemArea; + + /* Sparse mappings have to ask the BM for the virtual size */ + if (psFlushMemArea->hBMHandle) + { + pvBase = (IMG_VOID *)ps_vma->vm_start; + ui32ByteOffset = 0; + ui32FlushSize = BM_GetVirtualSize(psFlushMemArea->hBMHandle); + } + else + { + IMG_UINT32 ui32DummyByteSize; + + DetermineUsersSizeAndByteOffset(psFlushMemArea, + &ui32DummyByteSize, + &ui32ByteOffset); + + pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset; + ui32FlushSize = psFlushMemArea->ui32ByteSize; + } + + psFlushMemArea->bNeedsCacheInvalidate = IMG_FALSE; + } + + /* Call the open routine to increment the usage count */ + 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); + + if(psFlushMemArea) + { + OSInvalidateCPUCacheRangeKM(psFlushMemArea, ui32ByteOffset, pvBase, + ui32FlushSize); + } + + return iRetVal; +} + + +#if defined(DEBUG_LINUX_MMAP_AREAS) + +/* + * Lock MMap regions list (called on page start/stop while reading /proc/mmap) + + * sfile : seq_file that handles /proc file + * start : TRUE if it's start, FALSE if it's stop + * +*/ +static void ProcSeqStartstopMMapRegistations(struct seq_file *sfile,IMG_BOOL start) +{ + if(start) + { + LinuxLockMutex(&g_sMMapMutex); + } + else + { + LinuxUnLockMutex(&g_sMMapMutex); + } +} + + +/* + * Convert offset (index from KVOffsetTable) to element + * (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * off : index into the KVOffsetTable from which to print + * + * returns void* : Pointer to element that will be dumped + * +*/ +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; +} + +/* + * Gets next MMap element to show. (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * el : actual element + * off : index into the KVOffsetTable from which to print + * + * returns void* : Pointer to element to show (0 ends iteration) +*/ +static void* ProcSeqNextMMapRegistrations(struct seq_file *sfile,void* el,loff_t off) +{ + return ProcSeqOff2ElementMMapRegistrations(sfile,off); +} + + +/* + * Show MMap element (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * el : actual element + * +*/ +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 + + +/*! + ******************************************************************************* + + @Function PVRMMapRegisterArea + + @Description + + Register a memory area with the mmap code. + + @input psLinuxMemArea : pointer to memory area. + + @Return PVRSRV_OK, or PVRSRV_ERROR. + + ******************************************************************************/ +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); + + /* Check this mem area hasn't already been registered */ + 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++; + /* + * Sub memory areas are excluded from g_ui32TotalByteSize so that we + * don't count memory twice, once for the parent and again for sub + * allocationis. + */ + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + { + g_ui32TotalByteSize += psLinuxMemArea->ui32ByteSize; + } +#endif + + eError = PVRSRV_OK; + +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + + return eError; +} + + +/*! + ******************************************************************************* + + @Function PVRMMapRemoveRegisterArea + + @Description + + Unregister a memory area with the mmap code. + + @input psLinuxMemArea : pointer to memory area. + + @Return PVRSRV_OK, or PVRSRV_ERROR. + + ******************************************************************************/ +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)); + dump_stack(); + PVRSRVDumpRefCountCCB(); + eError = PVRSRV_ERROR_STILL_MAPPED; + goto exit_unlock; + } + else + { + /* + * An offset structure is created when a call is made to get + * the mmap data for a physical mapping. If the data is never + * used for mmap, we will be left with an umapped offset + * structure. + */ + 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; +} + + +/*! + ******************************************************************************* + + @Function LinuxMMapPerProcessConnect + + @Description + + Per-process mmap initialisation code. + + @input psEnvPerProc : pointer to OS specific per-process data. + + @Return PVRSRV_OK, or PVRSRV_ERROR. + + ******************************************************************************/ +PVRSRV_ERROR +LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) +{ + PVR_UNREFERENCED_PARAMETER(psEnvPerProc); + + return PVRSRV_OK; +} + +/*! + ******************************************************************************* + + @Function LinuxMMapPerProcessDisconnect + + @Description + + Per-process mmap deinitialisation code. + + @input psEnvPerProc : pointer to OS specific per-process data. + + ******************************************************************************/ +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); +} + + +/*! + ******************************************************************************* + + @Function LinuxMMapPerProcessHandleOptions + + @Description + + Set secure handle options required by mmap code. + + @input psHandleBase : pointer to handle base. + + @Return PVRSRV_OK, or PVRSRV_ERROR. + + ******************************************************************************/ +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; +} + + +/*! + ******************************************************************************* + + @Function PVRMMapInit + + @Description + + MMap initialisation code + + ******************************************************************************/ +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 /* defined(DEBUG_LINUX_MMAP_AREAS) */ + return; + +error: + PVRMMapCleanup(); + return; +} + + +/*! + ******************************************************************************* + + @Function PVRMMapCleanup + + @Description + + Mmap deinitialisation code + + ******************************************************************************/ +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 /* defined(DEBUG_LINUX_MMAP_AREAS) */ + + if(g_psMemmapCache) + { + KMemCacheDestroyWrapper(g_psMemmapCache); + g_psMemmapCache = NULL; + } +} diff --git a/pvr-source/services4/srvkm/env/linux/mmap.h b/pvr-source/services4/srvkm/env/linux/mmap.h new file mode 100644 index 0000000..7140c13 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mmap.h @@ -0,0 +1,240 @@ +/*************************************************************************/ /*! +@Title Linux mmap interface declaration +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#if !defined(__MMAP_H__) +#define __MMAP_H__ + +#include <linux/mm.h> +#include <linux/list.h> + +#if defined(VM_MIXEDMAP) +/* + * Mixed maps allow us to avoid using raw PFN mappings (VM_PFNMAP) for + * pages without pages structures ("struct page"), giving us more + * freedom in choosing the mmap offset for mappings. Mixed maps also + * allow both the mmap and the wrap code to be simplified somewhat. + */ +#define PVR_MAKE_ALL_PFNS_SPECIAL +#endif + +#include "perproc.h" +#include "mm.h" + +/* + * This structure represents the relationship between an mmap2 file + * offset and a LinuxMemArea for a given process. + */ +typedef struct KV_OFFSET_STRUCT_TAG +{ + /* + * Mapping count. Incremented when the mapping is created, and + * if the mapping is inherited across a process fork. + */ + IMG_UINT32 ui32Mapped; + + /* + * Offset to be passed to mmap2 to map the associated memory area + * into user space. The offset may represent the page frame number + * of the first page in the area (if the area is physically + * contiguous), or it may represent the secure handle associated + * with the area. + */ + IMG_UINT32 ui32MMapOffset; + + IMG_UINT32 ui32RealByteSize; + + /* Memory area associated with this offset structure */ + LinuxMemArea *psLinuxMemArea; + +#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) + /* ID of the thread that owns this structure */ + IMG_UINT32 ui32TID; +#endif + + /* ID of the process that owns this structure */ + IMG_UINT32 ui32PID; + + /* + * For offsets that represent actual page frame numbers, this structure + * is temporarily put on a list so that it can be found from the + * driver mmap entry point. This flag indicates the structure is + * on the list. + */ + IMG_BOOL bOnMMapList; + + /* Reference count for this structure */ + IMG_UINT32 ui32RefCount; + + /* + * User mode address of start of mapping. This is not necessarily the + * first user mode address of the memory area. + */ + IMG_UINT32 ui32UserVAddr; + + /* Extra entries to support proc filesystem debug info */ +#if defined(DEBUG_LINUX_MMAP_AREAS) + const IMG_CHAR *pszName; +#endif + + /* List entry field for MMap list */ + struct list_head sMMapItem; + + /* List entry field for per-memory area list */ + struct list_head sAreaItem; +}KV_OFFSET_STRUCT, *PKV_OFFSET_STRUCT; + + + +/*! + ******************************************************************************* + * @Function Mmap initialisation code + ******************************************************************************/ +IMG_VOID PVRMMapInit(IMG_VOID); + + +/*! + ******************************************************************************* + * @Function Mmap de-initialisation code + ******************************************************************************/ +IMG_VOID PVRMMapCleanup(IMG_VOID); + + +/*! + ******************************************************************************* + * @Function Registers a memory area with the mmap code + * + * @Input psLinuxMemArea + * + * @Return PVRSRV_ERROR status + ******************************************************************************/ +PVRSRV_ERROR PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea); + + +/*! + ******************************************************************************* + * @Function Unregisters a memory area from the mmap code + * + * @Input psLinuxMemArea + * + * @Return PVRSRV_ERROR status + ******************************************************************************/ +PVRSRV_ERROR PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea); + + +/*! + ****************************************************************************** + * @Function When a userspace services client, requests to map a memory + * area to userspace, this function validates the request and + * returns the details that the client must use when calling mmap(2). + * + * @Input psPerProc Per process data. + * @Input hMHandle Handle associated with the memory to map. + * This is a (secure) handle to the OS specific + * memory handle structure (hOSMemHandle), or + * a handle to a structure that contains the + * memory handle. + * @Output pui32MMapOffset The page aligned offset that the client must + * pass to the mmap2 system call. + * @Output pui32ByteOffset The real mapping that will be created for the + * services client may have a different + * size/alignment from it request. This offset + * is returned to the client and should be added + * to virtual address returned from mmap2 to get + * the first address corresponding to its request. + * @Output pui32RealByteOffset The size that the mapping will really be, + * that the client must also pass to mmap/munmap. + * + * @Output pui32UserVAddr Pointer to returned user mode address of + * mapping. + * @Return PVRSRV_ERROR + ******************************************************************************/ +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); + +/*! + ******************************************************************************* + + @Function Release mmap data. + + @Input psPerProc Per-process data. + @Input hMHandle Memory handle. + + @Output pbMUnmap Flag that indicates whether an munmap is + required. + @Output pui32RealByteSize Location for size of mapping. + @Output pui32UserVAddr User mode address to munmap. + + @Return PVRSRV_ERROR + ******************************************************************************/ +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); + +/*! + ******************************************************************************* + * @Function driver mmap entry point + * + * @Input pFile : user file structure + * + * @Input ps_vma : vm area structure + * + * @Return 0 for success, -errno for failure. + ******************************************************************************/ +int PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma); + + +#endif /* __MMAP_H__ */ + diff --git a/pvr-source/services4/srvkm/env/linux/module.c b/pvr-source/services4/srvkm/env/linux/module.c new file mode 100644 index 0000000..487069d --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/module.c @@ -0,0 +1,1214 @@ +/*************************************************************************/ /*! +@Title Linux module setup +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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_PLUGIN) +#define PVR_MOD_STATIC +#else + /* + * For LDM drivers, define PVR_LDM_MODULE to indicate generic LDM + * support is required, besides indicating the exact support + * required (e.g. platform, or PCI device). + */ + #if defined(LDM_PLATFORM) + #define PVR_LDM_PLATFORM_MODULE + #define PVR_LDM_DEVICE_CLASS + #define PVR_LDM_MODULE + #else + #if defined(LDM_PCI) + #define PVR_LDM_DEVICE_CLASS + #define PVR_LDM_PCI_MODULE + #define PVR_LDM_MODULE + #else + #if defined(SYS_SHARES_WITH_3PKM) + #define PVR_LDM_DEVICE_CLASS + #endif + #endif + #endif +#define PVR_MOD_STATIC static +#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 /* PVR_LDM_PLATFORM_MODULE */ + +#if defined(PVR_LDM_PCI_MODULE) +#include <linux/pci.h> +#endif /* PVR_LDM_PCI_MODULE */ + +#if defined(PVR_LDM_DEVICE_CLASS) +#include <linux/device.h> +#endif /* PVR_LDM_DEVICE_CLASS */ + +#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" +#include "buffer_manager.h" + +#if defined(SUPPORT_DRI_DRM) +#include "pvr_drm.h" +#endif +/* + * DRVNAME is the name we use to register our driver. + * DEVNAME is the name we use to register actual device nodes. + */ +#if defined(PVR_LDM_MODULE) +#define DRVNAME PVR_LDM_DRIVER_REGISTRATION_NAME +#endif +#define DEVNAME PVRSRV_MODNAME + +#if defined(SUPPORT_DRI_DRM) +#define PRIVATE_DATA(pFile) ((pFile)->driver_priv) +#else +#define PRIVATE_DATA(pFile) ((pFile)->private_data) +#endif + +/* + * This is all module configuration stuff required by the linux kernel. + */ +MODULE_SUPPORTED_DEVICE(DEVNAME); + +#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 /* defined(PVRSRV_NEED_PVR_DPF) */ + +#if defined(CONFIG_ION_OMAP) +#include <linux/ion.h> +#include <linux/omap_ion.h> +#include "ion.h" +extern void omap_ion_register_pvr_export(void *); +extern struct ion_device *omap_ion_device; +struct ion_client *gpsIONClient; +EXPORT_SYMBOL(gpsIONClient); +#endif /* defined(CONFIG_ION_OMAP) */ + +/* PRQA S 3207 2 */ /* ignore 'not used' warning */ +EXPORT_SYMBOL(PVRGetDisplayClassJTable); +EXPORT_SYMBOL(PVRGetBufferClassJTable); + +#if defined(PVR_LDM_DEVICE_CLASS) && !defined(SUPPORT_DRI_DRM) +/* + * Device class used for /sys entries (and udev device node creation) + */ +static struct class *psPvrClass; +#endif + +#if !defined(SUPPORT_DRI_DRM) +/* + * This is the major number we use for all nodes in /dev. + */ +static int AssignedMajorNumber; + +/* + * These are the operations that will be associated with the device node + * we create. + * + * With gcc -W, specifying only the non-null members produces "missing + * initializer" warnings. +*/ +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; + +/* PID of process being released */ +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 /*PVR_LDM_PLATFORM_MODULE */ + +#if defined(PVR_LDM_PCI_MODULE) +#define LDM_DEV struct pci_dev +#define LDM_DRV struct pci_driver +#endif /* PVR_LDM_PCI_MODULE */ +/* + * This is the driver interface we support. + */ +#if defined(PVR_LDM_PLATFORM_MODULE) +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 +static int PVRSRVDriverSuspend(LDM_DEV *device, pm_message_t state); +static void PVRSRVDriverShutdown(LDM_DEV *device); +static int PVRSRVDriverResume(LDM_DEV *device); + +#if defined(PVR_LDM_PCI_MODULE) +/* This structure is used by the Linux module code */ +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 + +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, +}; + +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 + +/*! +****************************************************************************** + + @Function PVRSRVDriverProbe + + @Description + + See whether a given device is really one we can drive. The platform bus + handler has already established that we should be able to service this device + because of the name match. We probably don't need to do anything else. + + @input pDevice - the device for which a probe is requested + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#if defined(PVR_LDM_PLATFORM_MODULE) +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 /* INTEGRATION_POINT */ + /* Some systems require device-specific system initialisation. + * E.g. this lets the OS track a device's dependencies on various + * system hardware. + * + * Note: some systems use this to enable HW that SysAcquireData + * will depend on, therefore it must be called first. + */ + if (PerDeviceSysInitialise((IMG_PVOID)pDevice) != PVRSRV_OK) + { + return -EINVAL; + } +#endif + /* SysInitialise only designed to be called once. + */ + psSysData = SysAcquireDataNoCheck(); + if (psSysData == IMG_NULL) + { + gpsPVRLDMDev = pDevice; + if (SysInitialise() != PVRSRV_OK) + { + return -ENODEV; + } + } + +#if defined(CONFIG_ION_OMAP) + gpsIONClient = ion_client_create(omap_ion_device, + 1 << ION_HEAP_TYPE_CARVEOUT | + 1 << OMAP_ION_HEAP_TYPE_TILER | + 1 << ION_HEAP_TYPE_SYSTEM, + "pvr"); + if (IS_ERR_OR_NULL(gpsIONClient)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVDriverProbe: Couldn't create ion client")); + return PTR_ERR(gpsIONClient); + } + omap_ion_register_pvr_export(&PVRSRVExportFDToIONHandles); +#endif /* defined(CONFIG_ION_OMAP) */ + + return 0; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDriverRemove + + @Description + + This call is the opposite of the probe call: it is called when the device is + being removed from the driver's control. See the file $KERNELDIR/drivers/ + base/bus.c:device_release_driver() for the call to this function. + + This is the correct place to clean up anything our driver did while it was + asoociated with the device. + + @input pDevice - the device for which driver detachment is happening + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#if defined (PVR_LDM_PLATFORM_MODULE) +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)); + +#if defined(CONFIG_ION_OMAP) + ion_client_destroy(gpsIONClient); + gpsIONClient = IMG_NULL; +#endif + + 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 /* INTEGRATION_POINT */ + /* See previous integration point for details. */ + 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 /* defined(PVR_LDM_MODULE) */ + + +#if defined(PVR_LDM_MODULE) || defined(SUPPORT_DRI_DRM) +static PVRSRV_LINUX_MUTEX gsPMMutex; +static IMG_BOOL bDriverIsSuspended; +static IMG_BOOL bDriverIsShutdown; +#endif + +#if defined(PVR_LDM_MODULE) || defined(PVR_DRI_DRM_PLATFORM_DEV) +/*! +****************************************************************************** + + @Function PVRSRVDriverShutdown + + @Description + + Suspend device operation for system shutdown. This is called as part of the + system halt/reboot process. The driver is put into a quiescent state by + setting the power state to D3. + + @input pDevice - the device for which shutdown is requested + + @Return nothing + +*****************************************************************************/ +#if defined(SUPPORT_DRI_DRM) && !defined(PVR_DRI_DRM_PLATFORM_DEV) && \ + !defined(SUPPORT_DRI_DRM_PLUGIN) +void PVRSRVDriverShutdown(struct drm_device *pDevice) +#else +PVR_MOD_STATIC void PVRSRVDriverShutdown(LDM_DEV *pDevice) +#endif +{ + PVR_TRACE(("PVRSRVDriverShutdown(pDevice=%p)", pDevice)); + + LinuxLockMutex(&gsPMMutex); + + if (!bDriverIsShutdown && !bDriverIsSuspended) + { + /* + * Take the bridge mutex, and never release it, to stop + * processes trying to use the driver after it has been + * shutdown. + */ + LinuxLockMutex(&gPVRSRVLock); + + (void) PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3); + } + + bDriverIsShutdown = IMG_TRUE; + + /* The bridge mutex is held on exit */ + LinuxUnLockMutex(&gsPMMutex); +} + +#endif /* defined(PVR_LDM_MODULE) || defined(PVR_DRI_DRM_PLATFORM_DEV) */ + + +#if defined(PVR_LDM_MODULE) || defined(SUPPORT_DRI_DRM) +/*! +****************************************************************************** + + @Function PVRSRVDriverSuspend + + @Description + + For 2.6 kernels: + Suspend device operation. We always get three calls to this regardless of + the state (D1-D3) chosen. The order is SUSPEND_DISABLE, SUSPEND_SAVE_STATE + then SUSPEND_POWER_DOWN. We take action as soon as we get the disable call, + the other states not being handled by us yet. + + For MontaVista 2.4 kernels: + This call gets made once only when someone does something like + + # echo -e -n "suspend powerdown 0" >/sys.devices/legacy/pvrsrv0/power + + The 3rd, numeric parameter (0) in the above has no relevence and is not + passed into us. The state parameter is always zero and the level parameter + is always SUSPEND_POWER_DOWN. Vive la difference! + + @input pDevice - the device for which resume is requested + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#if defined(SUPPORT_DRI_DRM) && !defined(PVR_DRI_DRM_PLATFORM_DEV) && \ + !defined(SUPPORT_DRI_DRM_PLUGIN) +#if defined(SUPPORT_DRM_MODESET) +int PVRSRVDriverSuspend(struct pci_dev *pDevice, pm_message_t state) +#else +int PVRSRVDriverSuspend(struct drm_device *pDevice, pm_message_t state) +#endif +#else +PVR_MOD_STATIC int PVRSRVDriverSuspend(LDM_DEV *pDevice, pm_message_t state) +#endif +{ + int res = 0; +#if !(defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM)) + PVR_TRACE(( "PVRSRVDriverSuspend(pDevice=%p)", pDevice)); + + LinuxLockMutex(&gsPMMutex); + + if (!bDriverIsSuspended && !bDriverIsShutdown) + { + LinuxLockMutex(&gPVRSRVLock); + + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3) == PVRSRV_OK) + { + /* The bridge mutex will be held until we resume */ + bDriverIsSuspended = IMG_TRUE; + } + else + { + LinuxUnLockMutex(&gPVRSRVLock); + res = -EINVAL; + } + } + + LinuxUnLockMutex(&gsPMMutex); +#endif + return res; +} + + +/*! +****************************************************************************** + + @Function PVRSRVDriverResume + + @Description + + Resume device operation following a lull due to earlier suspension. It is + implicit we're returning to D0 (fully operational) state. We always get three + calls to this using level thus: RESUME_POWER_ON, RESUME_RESTORE_STATE then + RESUME_ENABLE. On 2.6 kernels We don't do anything until we get the enable + call; on the MontaVista set-up we only ever get the RESUME_POWER_ON call. + + @input pDevice - the device for which resume is requested + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#if defined(SUPPORT_DRI_DRM) && !defined(PVR_DRI_DRM_PLATFORM_DEV) && \ + !defined(SUPPORT_DRI_DRM_PLUGIN) +#if defined(SUPPORT_DRM_MODESET) +int PVRSRVDriverResume(struct pci_dev *pDevice) +#else +int PVRSRVDriverResume(struct drm_device *pDevice) +#endif +#else +PVR_MOD_STATIC int PVRSRVDriverResume(LDM_DEV *pDevice) +#endif +{ + int res = 0; +#if !(defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM)) + PVR_TRACE(("PVRSRVDriverResume(pDevice=%p)", pDevice)); + + LinuxLockMutex(&gsPMMutex); + + if (bDriverIsSuspended && !bDriverIsShutdown) + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) == PVRSRV_OK) + { + bDriverIsSuspended = IMG_FALSE; + LinuxUnLockMutex(&gPVRSRVLock); + } + else + { + /* The bridge mutex is not released on failure */ + res = -EINVAL; + } + } + + LinuxUnLockMutex(&gsPMMutex); +#endif + return res; +} +#endif /* defined(PVR_LDM_MODULE) || defined(SUPPORT_DRI_DRM) */ + + +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) && !defined(SUPPORT_DRI_DRM) +/* + * If PVR_LDM_PCI_MODULE is defined (and PVR_MANUAL_POWER_CONTROL is *NOT* defined), + * the device can be suspended and resumed without suspending/resuming the + * system, by writing values into the power/state sysfs file for the device. + * To suspend: + * echo -n 2 > power/state + * To Resume: + * echo -n 0 > power/state + * + * The problem with this approach is that the device is usually left + * powered up; it is the responsibility of the bus driver to remove + * the power. + * + * Defining PVR_MANUAL_POWER_CONTROL is intended to make it easier to + * debug power management issues, especially when power is really removed + * from the device. It is easier to debug the driver if it is not being + * suspended/resumed with the rest of the system. + * + * When PVR_MANUAL_POWER_CONTROL is defined, the following proc entry is + * created: + * /proc/pvr/power_control + * The driver suspend/resume entry points defined below no longer suspend or + * resume the device. To suspend the device, type the following: + * echo 2 > /proc/pvr/power_control + * To resume the device, type: + * echo 0 > /proc/pvr/power_control + * + * The following example shows how to suspend/resume the device independently + * of the rest of the system. + * Suspend the device: + * echo 2 > /proc/pvr/power_control + * Suspend the system. Then you should be able to suspend and resume + * as normal. To resume the device type the following: + * echo 0 > /proc/pvr/power_control + */ + +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 + +/*! +****************************************************************************** + + @Function PVRSRVOpen + + @Description + + Release access the PVR services node - called when a file is closed, whether + at exit or using close(2) system call. + + @input pInode - the inode for the file being openeded + + @input pFile - the file handle data for the actual file being opened + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#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; +} + + +/*! +****************************************************************************** + + @Function PVRSRVRelease + + @Description + + Release access the PVR services node - called when a file is closed, whether + at exit or using close(2) system call. + + @input pInode - the inode for the file being released + + @input pFile - the file handle data for the actual file being released + + @Return 0 for success or <0 for an error. + +*****************************************************************************/ +#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; + int err = 0; + + 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 + + if(psPrivateData->hKernelMemInfo) + { + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + + /* Look up the meminfo we just exported */ + if(PVRSRVLookupHandle(KERNEL_HANDLE_BASE, + (IMG_PVOID *)&psKernelMemInfo, + psPrivateData->hKernelMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up export handle", __FUNCTION__)); + err = -EFAULT; + goto err_unlock; + } + + /* Tell the XProc about the export if required */ + if (psKernelMemInfo->sShareMemWorkaround.bInUse) + { + BM_XProcIndexRelease(psKernelMemInfo->sShareMemWorkaround.ui32ShareIndex); + } + + /* This drops the psMemInfo refcount bumped on export */ + if(FreeMemCallBackCommon(psKernelMemInfo, 0, + PVRSRV_FREE_CALLBACK_ORIGIN_EXTERNAL) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: FreeMemCallBackCommon failed", __FUNCTION__)); + err = -EFAULT; + goto err_unlock; + } + } + + /* Usually this is the same as OSGetCurrentProcessIDKM(), + * but not necessarily (e.g. fork(), child closes last..) + */ + 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; /*nulling shared pointer*/ +#endif + } + +err_unlock: + LinuxUnLockMutex(&gPVRSRVLock); +#if defined(SUPPORT_DRI_DRM) + return; +#else + return err; +#endif +} + + +/*! +****************************************************************************** + + @Function PVRCore_Init + + @Description + + Insert the driver into the kernel. + + The device major number is allocated by the kernel dynamically. This means + that the device node (nominally /dev/pvrsrv) will need to be re-made at boot + time if the number changes between subsequent loads of the module. While the + number often stays constant between loads this is not guaranteed. The node + is made as root on the shell with: + + mknod /dev/pvrsrv c nnn 0 + + where nnn is the major number found in /proc/devices for DEVNAME and also + reported by the PVR_DPF() - look at the boot log using dmesg' to see this). + + Currently the auto-generated script /etc/init.d/rc.pvr handles creation of + the device. In other environments the device may be created either through + devfs or sysfs. + + Readable proc-filesystem entries under /proc/pvr are created with + CreateProcEntries(). These can be read at runtime to get information about + the device (eg. 'cat /proc/pvr/vm') + + __init places the function in a special memory section that the kernel frees + once the function has been run. Refer also to module_init() macro call below. + + @input none + + @Return none + +*****************************************************************************/ +#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; +#endif +#if !defined(SUPPORT_DRI_DRM) && defined(PVR_LDM_DEVICE_CLASS) + struct device *psDev; +#endif + +#if !defined(SUPPORT_DRI_DRM) + /* + * Must come before attempting to print anything via Services. + * For DRM, the initialisation will already have been done. + */ + PVRDPFInit(); +#endif + PVR_TRACE(("PVRCore_Init")); + +#if defined(PVR_LDM_MODULE) || defined(SUPPORT_DRI_DRM) + LinuxInitMutex(&gsPMMutex); +#endif + 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) || defined(SUPPORT_DRI_DRM_PLUGIN) + 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 /* PVR_LDM_PLATFORM_MODULE */ + +#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 /* PVR_LDM_PCI_MODULE */ +#endif /* defined(PVR_LDM_MODULE) */ + +#if !defined(PVR_LDM_MODULE) + /* + * Drivers using LDM, will call SysInitialise in the probe/attach code + */ + 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 /* !defined(PVR_LDM_MODULE) */ + +#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)); + +#if defined(PVR_LDM_DEVICE_CLASS) + /* + * This code (using GPL symbols) facilitates automatic device + * node creation on platforms with udev (or similar). + */ + 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 /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)) */ + 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 /* defined(PVR_LDM_DEVICE_CLASS) */ +#endif /* !defined(SUPPORT_DRI_DRM) */ + + return 0; + +#if !defined(SUPPORT_DRI_DRM) +#if defined(PVR_LDM_DEVICE_CLASS) +destroy_class: + class_destroy(psPvrClass); +unregister_device: + unregister_chrdev((IMG_UINT)AssignedMajorNumber, DEVNAME); +#endif +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 /* defined(PVR_LDM_MODULE) */ + /* LDM drivers call SysDeinitialise during PVRSRVDriverRemove */ + { + SYS_DATA *psSysData; + + psSysData = SysAcquireDataNoCheck(); + if (psSysData != IMG_NULL) + { + (void) SysDeinitialise(psSysData); + } + } +#endif /* defined(PVR_LDM_MODULE) */ +init_failed: + PVRMMapCleanup(); + LinuxMMCleanup(); + LinuxBridgeDeInit(); + PVROSFuncDeInit(); + RemoveProcEntries(); + + return error; + +} /*PVRCore_Init*/ + + +/*! +***************************************************************************** + + @Function PVRCore_Cleanup + + @Description + + Remove the driver from the kernel. + + There's no way we can get out of being unloaded other than panicking; we + just do everything and plough on regardless of error. + + __exit places the function in a special memory section that the kernel frees + once the function has been run. Refer also to module_exit() macro call below. + + Note that the for LDM on MontaVista kernels, the positioning of the driver + de-registration is the opposite way around than would be suggested by the + registration case or the 2,6 kernel case. This is the correct way to do it + and the kernel panics if you change it. You have been warned. + + @input none + + @Return none + +*****************************************************************************/ +#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(SUPPORT_DRI_DRM) + +#if defined(PVR_LDM_DEVICE_CLASS) + device_destroy(psPvrClass, MKDEV(AssignedMajorNumber, 0)); + class_destroy(psPvrClass); +#endif + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) + if ( +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ + unregister_chrdev((IMG_UINT)AssignedMajorNumber, DEVNAME) +#if !(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) + ; +#else /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ + ) + { + PVR_DPF((PVR_DBG_ERROR," can't unregister device major %d", AssignedMajorNumber)); + } +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ +#endif /* !defined(SUPPORT_DRI_DRM) */ + +#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 /* defined(PVR_LDM_MODULE) */ +#if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) + if (gPVRPowerLevel != 0) + { + if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) == PVRSRV_OK) + { + gPVRPowerLevel = 0; + } + } +#endif + /* LDM drivers call SysDeinitialise during PVRSRVDriverRemove */ + (void) SysDeinitialise(psSysData); +#endif /* defined(PVR_LDM_MODULE) */ + + PVRMMapCleanup(); + + LinuxMMCleanup(); + + LinuxBridgeDeInit(); + + PVROSFuncDeInit(); + + RemoveProcEntries(); + + PVR_TRACE(("PVRCore_Cleanup: unloading")); +} + +/* + * These macro calls define the initialisation and removal functions of the + * driver. Although they are prefixed `module_', they apply when compiling + * statically as well; in both cases they define the function the kernel will + * run to start/stop the driver. +*/ +#if !defined(SUPPORT_DRI_DRM) +module_init(PVRCore_Init); +module_exit(PVRCore_Cleanup); +#endif diff --git a/pvr-source/services4/srvkm/env/linux/mutex.c b/pvr-source/services4/srvkm/env/linux/mutex.c new file mode 100644 index 0000000..2cd666f --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mutex.c @@ -0,0 +1,153 @@ +/*************************************************************************/ /*! +@Title Linux mutex interface +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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 /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) */ + + +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) + { + /* The process was sent a signal while waiting for the semaphore + * (e.g. a kill signal from userspace) + */ + 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 /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) */ + diff --git a/pvr-source/services4/srvkm/env/linux/mutex.h b/pvr-source/services4/srvkm/env/linux/mutex.h new file mode 100644 index 0000000..c590da1 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mutex.h @@ -0,0 +1,90 @@ +/*************************************************************************/ /*! +@Title Linux mutex interface +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + + +#ifndef __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 /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) */ + + +typedef struct { + struct semaphore sSemaphore; + /* since Linux's struct semaphore is intended to be + * opaque we don't poke inside for the count and + * instead we track it outselves. (So we can implement + * LinuxIsLockedMutex) + */ + 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 /* __INCLUDED_LINUX_MUTEX_H_ */ + diff --git a/pvr-source/services4/srvkm/env/linux/mutils.c b/pvr-source/services4/srvkm/env/linux/mutils.c new file mode 100644 index 0000000..8e57476 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mutils.c @@ -0,0 +1,166 @@ +/*************************************************************************/ /*! +@Title Linux memory interface support functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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) +{ + /* + * cpu_has_pat indicates whether PAT support is available on the CPU, + * but doesn't indicate if it has been enabled. + */ + if (cpu_has_pat) /* PRQA S 3335 */ /* ignore 'no function declared' */ + { + u64 pat; + IMG_UINT pat_index; + IMG_UINT pat_entry; + + PVR_TRACE(("%s: PAT available", __FUNCTION__)); + /* + * There is no Linux API for finding out if write combining + * is avaialable through the PAT, so we take the direct + * approach, and see if the PAT MSR contains a write combining + * entry. + */ + 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 /* defined(SUPPORT_LINUX_X86_WRITECOMBINE) */ + PVR_TRACE(("%s: Write combining disabled in driver build", __FUNCTION__)); +#endif /* defined(SUPPORT_LINUX_X86_WRITECOMBINE) */ +#endif /* DEBUG */ +} + +pgprot_t +pvr_pgprot_writecombine(pgprot_t prot) +{ + /* + * It would be worth checking from time to time to see if a + * pgprot_writecombine function (or similar) is introduced on Linux for + * x86 processors. If so, this function, and PVRLinuxX86PATProbe can be + * removed, and a macro used to select between pgprot_writecombine and + * pgprot_noncached, dpending on the value for of + * SUPPORT_LINUX_X86_WRITECOMBINE. + */ + /* PRQA S 0481,0482 2 */ /* scalar expressions */ + return (g_write_combining_available) ? + __pgprot((pgprot_val(prot) & ~_PAGE_CACHE_MASK) | _PAGE_CACHE_WC) : pgprot_noncached(prot); +} +#endif /* defined(SUPPORT_LINUX_X86_PAT) */ + +IMG_VOID +PVRLinuxMUtilsInit(IMG_VOID) +{ +#if defined(SUPPORT_LINUX_X86_PAT) + PVRLinuxX86PATProbe(); +#endif +} + diff --git a/pvr-source/services4/srvkm/env/linux/mutils.h b/pvr-source/services4/srvkm/env/linux/mutils.h new file mode 100644 index 0000000..891598c --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/mutils.h @@ -0,0 +1,119 @@ +/*************************************************************************/ /*! +@Title Memory management support utils +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Declares various memory management support functions + for Linux. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __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 /* __IMG_LINUX_MUTILS_H__ */ + diff --git a/pvr-source/services4/srvkm/env/linux/osfunc.c b/pvr-source/services4/srvkm/env/linux/osfunc.c new file mode 100644 index 0000000..ac03185 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/osfunc.c @@ -0,0 +1,4714 @@ +/*************************************************************************/ /*! +@Title Environment related functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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/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" +#include "lock.h" +#include <syslocal.h> + +#if defined (SUPPORT_ION) +#include "ion.h" +#endif + +#if defined (CONFIG_X86_PAE) +#error Physical Address Extension not supported with the driver +#endif + +#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) +/* + * Services spins at certain points waiting for events (e.g. swap + * chain destrucion). If those events rely on workqueues running, + * it needs to be possible to preempt the waiting thread. + * Removing the need for CONFIG_PREEMPT will require adding preemption + * points at various points in Services. + */ +#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 /* EMULATOR */ + +#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) + { + /* Try to allocate the memory using vmalloc */ +#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 /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)) */ + +#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_PVOID pvPrivData, + IMG_UINT32 ui32PrivDataLength, + IMG_HANDLE hBMHandle, + IMG_VOID **ppvCpuVAddr, + IMG_HANDLE *phOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + + PVR_UNREFERENCED_PARAMETER(ui32PageSize); + +#if 0 + /* For debug: force all OSAllocPages allocations to have a kernel + * virtual address */ + if(ui32AllocFlags & PVRSRV_HAP_SINGLE_PROCESS) + { + ui32AllocFlags &= ~PVRSRV_HAP_SINGLE_PROCESS; + ui32AllocFlags |= PVRSRV_HAP_MULTI_PROCESS; + } +#endif + + if(ui32AllocFlags & PVRSRV_MEM_ION) + { + /* We'll only see HAP_SINGLE_PROCESS with MEM_ION */ + BUG_ON((ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) != PVRSRV_HAP_SINGLE_PROCESS); + + psLinuxMemArea = NewIONLinuxMemArea(ui32Size, ui32AllocFlags, + pvPrivData, ui32PrivDataLength); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + PVRMMapRegisterArea(psLinuxMemArea); + goto ExitSkipSwitch; + } + + 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: + { + /* Currently PVRSRV_HAP_SINGLE_PROCESS implies that we dont need a + * kernel virtual mapping, but will need a user space virtual mapping */ + + psLinuxMemArea = NewAllocPagesLinuxMemArea(ui32Size, ui32AllocFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + + case PVRSRV_HAP_MULTI_PROCESS: + { + /* Currently PVRSRV_HAP_MULTI_PROCESS implies that we need a kernel + * virtual mapping and potentially multiple user space virtual + * mappings: Note: these eat into our limited kernel virtual + * address space. */ + +#if defined(VIVT_CACHE) || defined(__sh__) + /* ARM9 caches are tagged with virtual pages, not physical. As we are going to + * share this memory in different address spaces, we don't want it to be cached. + * ARM11 has physical tagging, so we can cache this memory without fear of virtual + * address aliasing in the TLB, as long as the kernel supports cache colouring for + * VIPT architectures. */ + ui32AllocFlags &= ~PVRSRV_HAP_CACHED; +#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; + } + + /* + In case of sparse mapping we need to handle back to the BM as it + knows the mapping info + */ + if (ui32AllocFlags & PVRSRV_MEM_SPARSE) + { + psLinuxMemArea->hBMHandle = hBMHandle; + } + +ExitSkipSwitch: + *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; +} + +IMG_INT32 +OSGetMemMultiPlaneInfo(IMG_HANDLE hOSMemHandle, IMG_UINT32* pui32AddressOffsets, + IMG_UINT32* ui32NumAddrOffsets) +{ + LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + if(!ui32NumAddrOffsets) + return -1; + + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ION) + return GetIONLinuxMemAreaInfo(psLinuxMemArea, pui32AddressOffsets, ui32NumAddrOffsets); + + if(!pui32AddressOffsets) + return -1; + + *pui32AddressOffsets = 0; + *ui32NumAddrOffsets = 1; + + return psLinuxMemArea->ui32ByteSize; +} + +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; + + /* KERNEL_ONLY areas are never mmapable. */ + 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_CPU_PHYADDR +OSMemHandleToCpuPAddr(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32ByteOffset) +{ + PVR_ASSERT(hOSMemHandle); + + return LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset); +} + + +IMG_BOOL OSMemHandleIsPhysContig(IMG_VOID *hOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; + + PVR_ASSERT(psLinuxMemArea); + + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_EXTERNAL_KV) + return psLinuxMemArea->uData.sExternalKV.bPhysContig; + + return IMG_FALSE; +} + + +/*! +****************************************************************************** + + @Function OSMemCopy + + @Description Copies memory around + + @Input pvDst - pointer to dst + @Output pvSrc - pointer to src + @Input ui32Size - bytes to copy + + @Return none + +******************************************************************************/ +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 +} + + +/*! +****************************************************************************** + + @Function OSMemSet + + @Description Function that does the same as the C memset() functions + + @Modified *pvDest : pointer to start of buffer to be set + + @Input ui8Value: value to set each byte to + + @Input ui32Size : number of bytes to set + + @Return IMG_VOID + +******************************************************************************/ +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 +} + + +/*! +****************************************************************************** + @Function OSStringCopy + @Description strcpy +******************************************************************************/ +IMG_CHAR *OSStringCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc) +{ + return (strcpy(pszDest, pszSrc)); +} + +/*! +****************************************************************************** + @Function OSSNPrintf + @Description snprintf +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSBreakResourceLock + + @Description unlocks an OS dependant resource + + @Input phResource - pointer to OS dependent resource structure + @Input ui32ID - Lock value to look for + + @Return + +******************************************************************************/ +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")); + } +} + + +/*! +****************************************************************************** + + @Function OSCreateResource + + @Description creates a OS dependant resource object + + @Input phResource - pointer to OS dependent resource + + @Return error status + +******************************************************************************/ +PVRSRV_ERROR OSCreateResource(PVRSRV_RESOURCE *psResource) +{ + psResource->ui32ID = 0; + psResource->ui32Lock = 0; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSDestroyResource + + @Description destroys an OS dependant resource object + + @Input phResource - pointer to OS dependent resource + + @Return error status + +******************************************************************************/ +PVRSRV_ERROR OSDestroyResource (PVRSRV_RESOURCE *psResource) +{ + OSBreakResourceLock (psResource, psResource->ui32ID); + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSInitEnvData + + @Description Allocates space for env specific data + + @Input ppvEnvSpecificData - pointer to pointer in which to return + allocated data. + @Input ui32MMUMode - MMU mode. + + @Return nothing + +******************************************************************************/ +PVRSRV_ERROR OSInitEnvData(IMG_PVOID *ppvEnvSpecificData) +{ + ENV_DATA *psEnvData; + PVRSRV_ERROR eError; + + /* allocate env specific data */ + 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); + /*not nulling pointer, out of scope*/ + return eError; + } + + + /* ISR installation flags */ + psEnvData->bMISRInstalled = IMG_FALSE; + psEnvData->bLISRInstalled = IMG_FALSE; + + /* copy structure back */ + *ppvEnvSpecificData = psEnvData; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSDeInitEnvData + + @Description frees env specific data memory + + @Input pvEnvSpecificData - pointer to private structure + + @Return PVRSRV_OK on success else PVRSRV_ERROR_OUT_OF_MEMORY + +******************************************************************************/ +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); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSReleaseThreadQuanta + + @Description + Releases thread quanta + + @Return nothing + +******************************************************************************/ +IMG_VOID OSReleaseThreadQuanta(IMG_VOID) +{ + schedule(); +} + + +/*! +****************************************************************************** + + @Function OSClockus + + @Description + This function returns the clock in microseconds + + @Input void + + @Return - clock (us) + +******************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function OSFuncHighResTimerCreate + + @Description + This function creates a high res timer who's handle is returned + + @Input nothing + + @Return handle + +******************************************************************************/ +IMG_HANDLE OSFuncHighResTimerCreate(IMG_VOID) +{ + /* We don't need a handle, but we must return non-NULL */ + return (IMG_HANDLE) 1; +} + +/*! +****************************************************************************** + + @Function OSFuncHighResTimerGetus + + @Description + This function returns the current timestamp in us + + @Input nothing + + @Return handle + +******************************************************************************/ +IMG_UINT32 OSFuncHighResTimerGetus(IMG_HANDLE hTimer) +{ + return (IMG_UINT32) jiffies_to_usecs(jiffies); +} + +/*! +****************************************************************************** + + @Function OSFuncHighResTimerDestroy + + @Description + This function will destroy the high res timer + + @Input nothing + + @Return handle + +******************************************************************************/ +IMG_VOID OSFuncHighResTimerDestroy(IMG_HANDLE hTimer) +{ + PVR_UNREFERENCED_PARAMETER(hTimer); +} + +/*! +****************************************************************************** + + @Function OSGetCurrentProcessIDKM + + @Description Returns handle for current process + + @Return ID of current process + +*****************************************************************************/ +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 +} + + +int OSGetProcCmdline(IMG_UINT32 ui32PID, char * buffer, int buff_size) +{ + int res = 0; + unsigned int len; + struct task_struct *task = pid_task(find_vpid(ui32PID), PIDTYPE_PID); + struct mm_struct *mm = task ? get_task_mm(task) : IMG_NULL; + if (!mm) + goto out; + if (!mm->arg_end) + goto out_mm; /* Shh! No looking before we're done */ + + len = mm->arg_end - mm->arg_start; + + if (len > buff_size) + len = buff_size; + + res = pvr_access_process_vm(task, mm->arg_start, buffer, len, 0); + + // If the nul at the end of args has been overwritten, then + // assume application is using setproctitle(3). + if (res > 0 && buffer[res-1] != '\0' && len < buff_size) { + len = strnlen(buffer, res); + if (len < res) { + res = len; + } else { + len = mm->env_end - mm->env_start; + if (len > buff_size - res) + len = buff_size - res; + res += pvr_access_process_vm(task, mm->env_start, buffer+res, len, 0); + res = strnlen(buffer, res); + } + } +out_mm: + mmput(mm); +out: + return res; +} + +const char* OSGetPathBaseName(char * buffer, int buff_size) +{ + const char *base_name = buffer; + while (1) + { + const char *next = strnchr(base_name, buff_size, '/'); + if (!next) + break; + + buff_size -= (next - base_name -1); + base_name = (next + 1); + + } + return base_name; +} + + +/*! +****************************************************************************** + + @Function OSGetPageSize + + @Description gets page size + + @Return page size + +******************************************************************************/ +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)) +/*! +****************************************************************************** + + @Function DeviceISRWrapper + + @Description wrapper for Device ISR function to conform to ISR OS interface + + @Return + +******************************************************************************/ +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 +} + + + +/*! +****************************************************************************** + + @Function SystemISRWrapper + + @Description wrapper for System ISR function to conform to ISR OS interface + + @Input Interrupt - NT interrupt object. + @Input Context - Context parameter + + @Return + +******************************************************************************/ +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 +} +/*! +****************************************************************************** + + @Function OSInstallDeviceLISR + + @Description Installs a Device ISR + + @Input pvSysData + @Input ui32Irq - IRQ number + @Input pszISRName - ISR name + @Input pvDeviceNode - device node contains ISR function and data argument + + @Return error status + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSUninstallDeviceLISR + + @Description Uninstalls a Device ISR + + @Input pvSysData - sysdata + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSInstallSystemLISR + + @Description Installs a System ISR + + @Input psSysData + @Input ui32Irq - IRQ number + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSUninstallSystemLISR + + @Description Uninstalls a System ISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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) +/*! +****************************************************************************** + + @Function MISRWrapper + + @Description OS dependent MISR wrapper + + @Input psSysData + + @Return error status + +******************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function OSInstallMISR + + @Description Installs an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSUninstallMISR + + @Description Uninstalls an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSScheduleMISR + + @Description Schedules an OS dependent MISR + + @Input pvSysData + + @Return error status + +******************************************************************************/ +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 /* defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) */ +#if defined(PVR_LINUX_MISR_USING_WORKQUEUE) +/*! +****************************************************************************** + + @Function MISRWrapper + + @Description OS dependent MISR wrapper + + @Input psSysData + + @Return error status + +******************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function OSInstallMISR + + @Description Installs an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSUninstallMISR + + @Description Uninstalls an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSScheduleMISR + + @Description Schedules an OS dependent MISR + + @Input pvSysData + + @Return error status + +******************************************************************************/ +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 /* #if defined(PVR_LINUX_MISR_USING_WORKQUEUE) */ + + +/*! +****************************************************************************** + + @Function MISRWrapper + + @Description OS dependent MISR wrapper + + @Input psSysData + + @Return error status + +******************************************************************************/ +static void MISRWrapper(unsigned long data) +{ + SYS_DATA *psSysData; + + psSysData = (SYS_DATA *)data; + + PVRSRVMISR(psSysData); +} + + +/*! +****************************************************************************** + + @Function OSInstallMISR + + @Description Installs an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSUninstallMISR + + @Description Uninstalls an OS dependent MISR + + @Input psSysData + + @Return error status + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSScheduleMISR + + @Description Schedules an OS dependent MISR + + @Input pvSysData + + @Return error status + +******************************************************************************/ +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 /* #if defined(PVR_LINUX_MISR_USING_WORKQUEUE) */ +#endif /* #if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) */ + +#endif /* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) */ + +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 +/*! +****************************************************************************** + + @Function OSLockResource + + @Description locks an OS dependant Resource + + @Input phResource - pointer to OS dependent Resource + @Input bBlock - do we want to block? + + @Return error status + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSUnlockResource + + @Description unlocks an OS dependant resource + + @Input phResource - pointer to OS dependent resource structure + + @Return + +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function OSIsResourceLocked + + @Description tests if resource is locked + + @Input phResource - pointer to OS dependent resource structure + + @Return error status + +******************************************************************************/ +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 /* SYS_CUSTOM_POWERLOCK_WRAP */ + + +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; +} + + +/*! +****************************************************************************** + + @Function OSMapPhysToLin + + @Description Maps the physical memory into linear addr range + + @Input BasePAddr : physical cpu address + + @Input ui32Bytes - bytes to map + + @Input ui32CacheType - cache type + + @Return : Linear addr of mapping on success, else NULL + + ******************************************************************************/ +IMG_VOID * +OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE *phOSMemHandle) +{ + if(ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) + { + /* + * Provide some backwards compatibility, until all callers + * have been updated to pass a non-null OSMemHandle pointer. + * Such callers must not call OSMapLinToCPUPhys. + */ + 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; +} + +/*! +****************************************************************************** + @Function OSUnMapPhysToLin + @Description Unmaps memory that was mapped with OSMapPhysToLin + @Return TRUE on success, else FALSE +******************************************************************************/ +IMG_BOOL +OSUnMapPhysToLin(IMG_VOID *pvLinAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32MappingFlags, IMG_HANDLE hOSMemHandle) +{ + 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; +} + +/*! +****************************************************************************** + @Function RegisterExternalMem + @Description Registers external memory for user mode mapping + @Return TRUE on success, else FALSE, MemHandle out +******************************************************************************/ +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: + { + /* Currently PVRSRV_HAP_MULTI_PROCESS implies that we need a kernel + * virtual mapping and potentially multiple user space virtual mappings. + * Beware that the kernel virtual address space is a limited resource. + */ +#if defined(VIVT_CACHE) || defined(__sh__) + /* + * ARM9 caches are tagged with virtual pages, not physical. As we are going to + * share this memory in different address spaces, we don't want it to be cached. + * ARM11 has physical tagging, so we can cache this memory without fear of virtual + * address aliasing in the TLB, as long as the kernel supports cache colouring for + * VIPT architectures. + */ + ui32MappingFlags &= ~PVRSRV_HAP_CACHED; +#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; +} + + +/*! +****************************************************************************** + @Function OSRegisterMem + @Description Registers external memory for user mode mapping + @Output phOSMemHandle - handle to registered memory + @Return TRUE on success, else FALSE +******************************************************************************/ +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); +} + + +/*! +****************************************************************************** + @Function OSUnRegisterMem + @Description UnRegisters external memory for user mode mapping + @Return TRUE on success, else FALSE +******************************************************************************/ +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); +} + +/*! +****************************************************************************** + @Function OSReservePhys + @Description Registers physical memory for user mode mapping + @Output ppvCpuVAddr + @Output phOsMemHandle handle to registered memory + @Return TRUE on success, else FALSE +******************************************************************************/ +PVRSRV_ERROR +OSReservePhys(IMG_CPU_PHYADDR BasePAddr, + IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32MappingFlags, + IMG_HANDLE hBMHandle, + IMG_VOID **ppvCpuVAddr, + IMG_HANDLE *phOSMemHandle) +{ + LinuxMemArea *psLinuxMemArea; + +#if 0 + /* For debug: force all OSReservePhys reservations to have a kernel + * virtual address */ + 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: + { + /* Currently PVRSRV_HAP_KERNEL_ONLY implies that a kernel virtual + * mapping is required for the allocation and no user virtual + * mappings are allowed: Note these eat into our limited kernel + * virtual address space */ + psLinuxMemArea = NewIORemapLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + break; + } + case PVRSRV_HAP_SINGLE_PROCESS: + { + /* Currently this implies that we dont need a kernel virtual + * mapping, but will need a user space virtual mapping */ + psLinuxMemArea = NewIOLinuxMemArea(BasePAddr, ui32Bytes, ui32MappingFlags); + if(!psLinuxMemArea) + { + return PVRSRV_ERROR_BAD_MAPPING; + } + PVRMMapRegisterArea(psLinuxMemArea); + break; + } + case PVRSRV_HAP_MULTI_PROCESS: + { + /* Currently PVRSRV_HAP_MULTI_PROCESS implies that we need a kernel + * virtual mapping and potentially multiple user space virtual mappings. + * Beware that the kernel virtual address space is a limited resource. + */ +#if defined(VIVT_CACHE) || defined(__sh__) + /* + * ARM9 caches are tagged with virtual pages, not physical. As we are going to + * share this memory in different address spaces, we don't want it to be cached. + * ARM11 has physical tagging, so we can cache this memory without fear of virtual + * address aliasing in the TLB, as long as the kernel supports cache colouring for + * VIPT architectures. + */ + ui32MappingFlags &= ~PVRSRV_HAP_CACHED; +#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; + } + + /* + In case of sparse mapping we need to handle back to the BM as it + knows the mapping info + */ + if (ui32MappingFlags & PVRSRV_MEM_SPARSE) + { + PVR_ASSERT(hBMHandle != IMG_NULL); + psLinuxMemArea->hBMHandle = hBMHandle; + } + + *phOSMemHandle = (IMG_HANDLE)psLinuxMemArea; + *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); + + LinuxMemAreaRegister(psLinuxMemArea); + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + @Function OSUnReservePhys + @Description UnRegisters physical memory for user mode mapping + @Return TRUE on success, else FALSE +******************************************************************************/ +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; +} + + +/*! +****************************************************************************** + @Function OSBaseAllocContigMemory + @Description Allocate a block of contiguous virtual non-paged memory. + @Input ui32Size - number of bytes to allocate + @Output ppvLinAddr - pointer to variable that will receive the linear address of buffer + @Return PVRSRV_OK if allocation successed else returns PVRSRV_ERROR_OUT_OF_MEMORY + **************************************************************************/ +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 +/* + * On Linux, the returned virtual address should be used for CPU access, + * and not be remapped into the CPU virtual address using ioremap. The fact + * that the RAM is being managed by the kernel, and already has a virtual + * address, seems to lead to problems when the attributes of the memory are + * changed in the ioremap call (such as from cached to non-cached). + */ + 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 /* !defined(NO_HARDWARE) */ +} + + +/*! +****************************************************************************** + @Function OSBaseFreeContigMemory + @Description Frees memory allocated with OSBaseAllocContigMemory + @Input LinAddr - pointer to buffer allocated with OSBaseAllocContigMemory + **************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSWriteHWReg + + @Description + + register access function + + @input pvLinRegBaseAddr : lin addr of register block base + + @input ui32Offset : + + @input ui32Value : + + @Return none + +******************************************************************************/ + +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)) + +/*! +****************************************************************************** + + @Function OSPCISetDev + + @Description + + Set a PCI device for subsequent use. + + @input pvPCICookie : Pointer to OS specific PCI structure/cookie + + @input eFlags : Flags + + @Return Pointer to PCI device handle + +******************************************************************************/ +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) /* PRQA S 3358 */ /* misuse of enums */ + { + pci_set_master(psPVRPCI->psPCIDev); + } + + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI) /* PRQA S 3358 */ /* misuse of enums */ + { +#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; /* PRQA S 1474,3358,4130 */ /* misuse of enums */ + } +#else + PVR_DPF((PVR_DBG_WARNING, "OSPCISetDev: MSI support not enabled in the kernel")); +#endif + } + + /* Initialise the PCI resource tracking array */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + { + psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE; + } + + return (PVRSRV_PCI_DEV_HANDLE)psPVRPCI; +} + +/*! +****************************************************************************** + + @Function OSPCIAcquireDev + + @Description + + Acquire a PCI device for subsequent use. + + @input ui16VendorID : Vendor PCI ID + + @input ui16VendorID : Device PCI ID + + @input eFlags : Flags + + @Return PVESRV_ERROR + +******************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function OSPCIIRQ + + @Description + + Get the interrupt number for the device. + + @input hPVRPCI : PCI device handle + + @input pui32IRQ : Pointer to where the interrupt number should be returned + + @Return PVESRV_ERROR + +******************************************************************************/ +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; +} + +/* Functions supported by OSPCIAddrRangeFunc */ +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 +}; + +/*! +****************************************************************************** + + @Function OSPCIAddrRangeFunc + + @Description + + Internal support function for various address range related functions + + @input eFunc : Function to perform + + @input hPVRPCI : PCI device handle + + @input ui32Index : Address range index + + @Return function dependent + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSPCIAddrRangeLen + + @Description + + Returns length of a given address range length + + @input hPVRPCI : PCI device handle + + @input ui32Index : Address range index + + @Return Length of address range, or 0 if no such range + +******************************************************************************/ +IMG_UINT32 OSPCIAddrRangeLen(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_LEN, hPVRPCI, ui32Index); +} + +/*! +****************************************************************************** + + @Function OSPCIAddrRangeStart + + @Description + + Returns the start of a given address range + + @input hPVRPCI : PCI device handle + + @input ui32Index : Address range index + + @Return Start of address range, or 0 if no such range + +******************************************************************************/ +IMG_UINT32 OSPCIAddrRangeStart(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_START, hPVRPCI, ui32Index); +} + +/*! +****************************************************************************** + + @Function OSPCIAddrRangeEnd + + @Description + + Returns the end of a given address range + + @input hPVRPCI : PCI device handle"ayy + + @input ui32Index : Address range index + + @Return End of address range, or 0 if no such range + +******************************************************************************/ +IMG_UINT32 OSPCIAddrRangeEnd(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index) +{ + return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_END, hPVRPCI, ui32Index); +} + +/*! +****************************************************************************** + + @Function OSPCIRequestAddrRange + + @Description + + Request a given address range index for subsequent use + + @input hPVRPCI : PCI device handle + + @input ui32Index : Address range index + + @Return PVESRV_ERROR + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSPCIReleaseAddrRange + + @Description + + Release a given address range that is no longer being used + + @input hPVRPCI : PCI device handle + + @input ui32Index : Address range index + + @Return PVESRV_ERROR + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSPCIReleaseDev + + @Description + + Release a PCI device that is no longer being used + + @input hPVRPCI : PCI device handle + + @Return PVESRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + int i; + + PVR_TRACE(("OSPCIReleaseDev")); + + /* Release all PCI regions that are currently in use */ + 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) /* PRQA S 3358 */ /* misuse of enums */ + { + pci_disable_msi(psPVRPCI->psPCIDev); + } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) /* PRQA S 3358 */ /* misuse of enums */ + { + pci_clear_master(psPVRPCI->psPCIDev); + } +#endif + pci_disable_device(psPVRPCI->psPCIDev); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psPVRPCI), (IMG_VOID *)psPVRPCI, IMG_NULL); + /*not nulling pointer, copy on stack*/ + + return PVRSRV_OK; +} + +/*! +****************************************************************************** + + @Function OSPCISuspendDev + + @Description + + Prepare PCI device to be turned off by power management + + @input hPVRPCI : PCI device handle + + @Return PVESRV_ERROR + +******************************************************************************/ +PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) +{ + PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI; + int i; + int err; + + PVR_TRACE(("OSPCISuspendDev")); + + /* Release all PCI regions that are currently in use */ + 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; +} + +/*! +****************************************************************************** + + @Function OSPCIResumeDev + + @Description + + Prepare a PCI device to be resumed by power management + + @input hPVRPCI : PCI device handle + + @input pvPCICookie : Pointer to OS specific PCI structure/cookie + + @input eFlags : Flags + + @Return PVESRV_ERROR + +******************************************************************************/ +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) /* PRQA S 3358 */ /* misuse of enums */ + pci_set_master(psPVRPCI->psPCIDev); + + /* Restore the PCI resource tracking array */ + 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 /* #if defined(CONFIG_PCI) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) */ + +#define OS_MAX_TIMERS 8 + +/* Timer callback strucure used by OSAddTimer */ +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)) +/* The lock is used to control access to sTimers */ +/* PRQA S 0671,0685 1 */ /* C99 macro not understood by QAC */ +static spinlock_t sTimerStructLock = SPIN_LOCK_UNLOCKED; +#else +static DEFINE_SPINLOCK(sTimerStructLock); +#endif +#endif + +static void OSTimerCallbackBody(TIMER_CALLBACK_DATA *psTimerCBData) +{ + if (!psTimerCBData->bActive) + return; + + /* call timer callback */ + psTimerCBData->pfnTimerFunc(psTimerCBData->pvData); + + /* reset timer */ + mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies); +} + + +/*! +****************************************************************************** + + @Function OSTimerCallbackWrapper + + @Description + + OS specific timer callback wrapper function + + @Input ui32Data : timer callback data + + @Return NONE + +******************************************************************************/ +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 + +/*! +****************************************************************************** + + @Function OSAddTimer + + @Description + + OS specific function to install a timer callback + + @Input pfnTimerFunc : timer callback + + @Input *pvData :callback data + + @Input ui32MsTimeout: callback period + + @Return IMG_HANDLE : valid handle success, NULL failure + +******************************************************************************/ +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 + + /* check callback */ + if(!pfnTimerFunc) + { + PVR_DPF((PVR_DBG_ERROR, "OSAddTimer: passed invalid callback")); + return IMG_NULL; + } + + /* Allocate timer callback data structure */ +#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; + + /* + HZ = ticks per second + ui32MsTimeout = required ms delay + ticks = (Hz * ui32MsTimeout) / 1000 + */ + psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000) + ? 1 + : ((HZ * ui32MsTimeout) / 1000); + /* initialise object */ + init_timer(&psTimerCBData->sTimer); + + /* setup timer object */ + /* PRQA S 0307,0563 1 */ /* ignore warning about inconpartible ptr casting */ + 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]; +} + +/*! +****************************************************************************** + + @Function OSRemoveTimer + + @Description + + OS specific function to remove a timer callback + + @Input hTimer : timer handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR OSRemoveTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(!psTimerCBData->bActive); + + /* free timer callback data struct */ + psTimerCBData->bInUse = IMG_FALSE; + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSEnableTimer + + @Description + + OS specific function to enable a timer callback + + @Input hTimer : timer handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR OSEnableTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(!psTimerCBData->bActive); + + /* Start timer arming */ + psTimerCBData->bActive = IMG_TRUE; + + /* set the expire time */ + psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies; + + /* Add the timer to the list */ + add_timer(&psTimerCBData->sTimer); + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSDisableTimer + + @Description + + OS specific function to disable a timer callback + + @Input hTimer : timer handle + + @Return PVRSRV_ERROR : + +******************************************************************************/ +PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer) +{ + TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer); + + PVR_ASSERT(psTimerCBData->bInUse); + PVR_ASSERT(psTimerCBData->bActive); + + /* Stop timer from arming */ + 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 + + /* remove timer */ + del_timer_sync(&psTimerCBData->sTimer); + +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + /* + * This second flush is to catch the case where the timer ran + * before we managed to delete it, in which case, it will have + * queued more work for the workqueue. Since the bActive flag + * has been cleared, this second flush won't result in the + * timer being rearmed. + */ + flush_workqueue(psTimerWorkQueue); +#endif +#if defined(PVR_LINUX_TIMERS_USING_SHARED_WORKQUEUE) + flush_scheduled_work(); +#endif + + return PVRSRV_OK; +} + + +/*! +****************************************************************************** + + @Function OSEventObjectCreateKM + + @Description + + OS specific function to create an event object + + @Input pszName : Globally unique event object name (if null name must be autogenerated) + + @Output psEventObject : OS event object info structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +#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) + { + /* copy over the event object name */ + strncpy(psEventObject->szName, pszName, EVENTOBJNAME_MAXLENGTH); + } + else + { + /* autogenerate a name */ + 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; + +} + + +/*! +****************************************************************************** + + @Function OSEventObjectDestroyKM + + @Description + + OS specific function to destroy an event object + + @Input psEventObject : OS event object info structure + + @Return PVRSRV_ERROR : + +******************************************************************************/ +#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; +} + +/*! +****************************************************************************** + + @Function OSEventObjectWaitKM + + @Description + + OS specific function to wait for an event object. Called from client + + @Input hOSEventKM : OS and kernel specific handle to event object + + @Return PVRSRV_ERROR : + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSEventObjectOpenKM + + @Description + + OS specific function to open an event object. Called from client + + @Input psEventObject : Pointer to an event object + @Output phOSEvent : OS and kernel specific handle to event object + + @Return PVRSRV_ERROR : + +******************************************************************************/ +#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; +} + +/*! +****************************************************************************** + + @Function OSEventObjectCloseKM + + @Description + + OS specific function to close an event object. Called from client + + @Input psEventObject : Pointer to an event object + @OInput hOSEventKM : OS and kernel specific handle to event object + + + @Return PVRSRV_ERROR : + +******************************************************************************/ +#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; + +} + +/*! +****************************************************************************** + + @Function OSEventObjectSignalKM + + @Description + + OS specific function to 'signal' an event object. Called from L/MISR + + @Input hOSEventKM : OS and kernel specific handle to event object + + @Return PVRSRV_ERROR : + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSProcHasPrivSrvInit + + @Description + + Does the process have sufficient privileges to initialise services? + + @Input none + + @Return IMG_BOOL : + +******************************************************************************/ +IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID) +{ + return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE; +} + +/*! +****************************************************************************** + + @Function OSCopyToUser + + @Description + + Copy a block of data into user space + + @Input pvSrc + + @Output pvDest + + @Input ui32Bytes + + @Return PVRSRV_ERROR : + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSCopyFromUser + + @Description + + Copy a block of data from the user space + + @Output pvDest + + @Input pvSrc + + @Input ui32Bytes + + @Return PVRSRV_ERROR : + +******************************************************************************/ +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; +} + +/*! +****************************************************************************** + + @Function OSAccessOK + + @Description + + Checks if a user space pointer is valide + + @Input eVerification + + @Input pvUserPtr + + @Input ui32Bytes + + @Return IMG_BOOL : + +******************************************************************************/ +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; + + +/*! +****************************************************************************** + + @Function *CPUVAddrToPFN + + @Description + + Find the PFN associated with a given CPU virtual address, and return + the associated page structure, if it exists. + The page in question must be present (i.e. no fault handling required), + and must be writable. A get_page is done on the returned page structure. + + @Input psVMArea - pointer to VM area structure + ulCPUVAddr - CPU virtual address + pulPFN - Pointer to returned PFN. + ppsPAge - Pointer to returned page structure pointer. + + @Output *pulPFN - Set to PFN + *ppsPage - Pointer to the page structure if present, else NULL. + @Return IMG_TRUE if PFN lookup was succesful. + +******************************************************************************/ +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 +} + +/*! +****************************************************************************** + + @Function OSReleasePhysPageAddr + + @Description + + Release wrapped memory. + + @Input hOSWrapMem : Driver cookie + + @Return PVRSRV_ERROR + +******************************************************************************/ +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 the number of pages mapped is not the same as + * the number of pages in the address range, then + * get_user_pages must have failed, so we are cleaning + * up after failure, and the pages can't be dirty. + */ + 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; +} + +#if defined(CONFIG_TI_TILER) || defined(CONFIG_DRM_OMAP_DMM_TILER) + +static IMG_UINT32 CPUAddrToTilerPhy(IMG_UINT32 uiAddr) +{ + IMG_UINT32 ui32PhysAddr = 0; + pte_t *ptep, pte; + pgd_t *pgd; + pmd_t *pmd; + pud_t *pud; + + pgd = pgd_offset(current->mm, uiAddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + goto err_out; + + pud = pud_offset(pgd, uiAddr); + if (pud_none(*pud) || pud_bad(*pud)) + goto err_out; + + pmd = pmd_offset(pud, uiAddr); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + goto err_out; + + ptep = pte_offset_map(pmd, uiAddr); + if (!ptep) + goto err_out; + + pte = *ptep; + if (!pte_present(pte)) + goto err_out; + + ui32PhysAddr = (pte & PAGE_MASK) | (~PAGE_MASK & uiAddr); + + /* If the physAddr is not in the TILER physical range + * then we don't proceed. + */ + if (ui32PhysAddr < 0x60000000 && ui32PhysAddr > 0x7fffffff) + { + PVR_DPF((PVR_DBG_ERROR, "CPUAddrToTilerPhy: Not in tiler range")); + ui32PhysAddr = 0; + goto err_out; + } + +err_out: + return ui32PhysAddr; +} + +#endif /* defined(CONFIG_TI_TILER) || defined(CONFIG_DRM_OMAP_DMM_TILER) */ + +/*! +****************************************************************************** + + @Function OSAcquirePhysPageAddr + + @Description + + @Return PVRSRV_ERROR + +******************************************************************************/ +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 bMMapSemHeld = IMG_FALSE; + PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY; + + /* Align start and end addresses to page boundaries */ + ulStartAddr = ulStartAddrOrig & PAGE_MASK; + ulBeyondEndAddr = PAGE_ALIGN(ulBeyondEndAddrOrig); + ulAddrRange = ulBeyondEndAddr - ulStartAddr; + + /* + * Check for address range calculation overflow, and attempts to wrap + * zero bytes. + */ + if (ulBeyondEndAddr <= ulStartAddr) + { + PVR_DPF((PVR_DBG_ERROR, + "OSAcquirePhysPageAddr: Invalid address range (start %x, length %x)", + ulStartAddrOrig, ulAddrRangeOrig)); + goto error; + } + + /* Allocate information structure */ + 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); + + /* Allocate physical address array */ + 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)); + + /* Allocate page array */ + 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)); + + /* Default error code from now on */ + eError = PVRSRV_ERROR_BAD_MAPPING; + + /* Set the mapping type to aid clean up */ + psInfo->eType = WRAP_TYPE_GET_USER_PAGES; + + /* Lock down user memory */ + down_read(¤t->mm->mmap_sem); + bMMapSemHeld = IMG_TRUE; + + /* Get page list */ + psInfo->iNumPagesMapped = get_user_pages(current, current->mm, ulStartAddr, psInfo->iNumPages, 1, 0, psInfo->ppsPages, NULL); + + if (psInfo->iNumPagesMapped >= 0) + { + /* See if we got all the pages we wanted */ + 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; + } + + /* Build list of physical page addresses */ + 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)); + + /* Reset some fields */ + psInfo->eType = WRAP_TYPE_NULL; + psInfo->iNumPagesMapped = 0; + memset(psInfo->ppsPages, 0, (size_t)psInfo->iNumPages * sizeof(*psInfo->ppsPages)); + + /* + * get_user_pages didn't work. If this is due to the address range + * representing memory mapped I/O, then we'll look for the pages + * in the appropriate memory region of the process. + */ + + /* Set the mapping type to aid clean up */ + 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 + + /* + * find_vma locates a region with an end point past a given + * virtual address. So check the address is actually in the region. + */ + 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; + } + + /* Now check the end address is in range */ + 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; + } + + /* Does the region represent memory mapped I/O? */ + 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; + } + + /* We require read and write access */ + 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) + { +#if defined(CONFIG_TI_TILER) || defined(CONFIG_DRM_OMAP_DMM_TILER) + /* This could be tiler memory.*/ + IMG_UINT32 ui32TilerAddr = CPUAddrToTilerPhy(ulAddr); + if (ui32TilerAddr) + { + bHavePageStructs = IMG_TRUE; + psInfo->iNumPagesMapped++; + psInfo->psPhysAddr[i].uiAddr = ui32TilerAddr; + psSysPAddr[i].uiAddr = ui32TilerAddr; + continue; + } +#endif /* defined(CONFIG_TI_TILER) || defined(CONFIG_DRM_OMAP_DMM_TILER) */ + + bHaveNoPageStructs = IMG_TRUE; + } + 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) + { + /* The ideal case; every page has a page structure */ + 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; + } + +exit: + PVR_ASSERT(bMMapSemHeld); + up_read(¤t->mm->mmap_sem); + + /* Return the cookie */ + *phOSWrapMem = (IMG_HANDLE)psInfo; + + if (bHaveNoPageStructs) + { + PVR_DPF((PVR_DBG_MESSAGE, + "OSAcquirePhysPageAddr: Region contains pages which can't be locked down (no page structures)")); + } + + PVR_ASSERT(psInfo->eType != 0); + + 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 defined(__arm__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) +typedef void (*OuterCacheOp_t)(phys_addr_t uStart, phys_addr_t uEnd); +#else +typedef void (*OuterCacheOp_t)(unsigned long ulStart, unsigned long ulEnd); +#endif + +#if defined(CONFIG_OUTER_CACHE) + +typedef IMG_BOOL (*MemAreaToPhys_t)(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart); + +static IMG_BOOL VMallocAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) +{ + *pulStart = vmalloc_to_pfn(pvRangeAddrStart + ui32PageNum * PAGE_SIZE) << PAGE_SHIFT; + return IMG_TRUE; +} + +static IMG_BOOL ExternalKVAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) +{ + IMG_SYS_PHYADDR SysPAddr; + IMG_CPU_PHYADDR CpuPAddr; + SysPAddr = psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr[ui32PageNumOffset + ui32PageNum]; + CpuPAddr = SysSysPAddrToCpuPAddr(SysPAddr); + *pulStart = CpuPAddr.uiAddr; + return IMG_TRUE; +} + +static IMG_BOOL AllocPagesAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) +{ + struct page *pPage; + + pPage = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageNumOffset + ui32PageNum]; + *pulStart = page_to_pfn(pPage) << PAGE_SHIFT; + return IMG_TRUE; +} + +static IMG_BOOL AllocPagesSparseAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) +{ + IMG_UINT32 ui32VirtOffset = (ui32PageNumOffset + ui32PageNum) << PAGE_SHIFT; + IMG_UINT32 ui32PhysOffset; + struct page *pPage; + + if (BM_VirtOffsetToPhysical(psLinuxMemArea->hBMHandle, ui32VirtOffset, &ui32PhysOffset)) + { + PVR_ASSERT(ui32PhysOffset <= ui32VirtOffset); + pPage = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PhysOffset >> PAGE_SHIFT]; + *pulStart = page_to_pfn(pPage) << PAGE_SHIFT; + return IMG_TRUE; + } + + return IMG_FALSE; +} + + +static IMG_BOOL IONAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) +{ + IMG_CPU_PHYADDR CpuPAddr; + CpuPAddr = psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs[ui32PageNumOffset + ui32PageNum]; + *pulStart = CpuPAddr.uiAddr; + return IMG_TRUE; +} + +#endif /* defined(CONFIG_OUTER_CACHE) */ + +/* g_sMMapMutex must be held while this function is called */ + +static +IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, + IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) +{ + PKV_OFFSET_STRUCT psOffsetStruct; + IMG_VOID *pvMinVAddr; + + /* There's no kernel-virtual for this type of allocation, so if + * we're flushing it, it must be user-virtual, and therefore + * have a mapping. + */ + list_for_each_entry(psOffsetStruct, psMMapOffsetStructList, sAreaItem) + { + if(OSGetCurrentProcessIDKM() != psOffsetStruct->ui32PID) + continue; + + pvMinVAddr = (IMG_VOID *)psOffsetStruct->ui32UserVAddr; + + /* Within permissible range */ + if(pvRangeAddrStart >= pvMinVAddr && + ui32Length <= psOffsetStruct->ui32RealByteSize) + return pvMinVAddr; + } + + return IMG_NULL; +} + +extern PVRSRV_LINUX_MUTEX g_sMMapMutex; + +static inline void DoInnerCacheOp(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length, + InnerCacheOp_t pfnInnerCacheOp) +{ + LinuxMemArea *psLinuxMemArea = hOSMemHandle; + + if (!psLinuxMemArea->hBMHandle) + { + pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + } + else + { + IMG_UINT32 ui32ByteRemain = ui32Length; + IMG_UINT32 ui32BytesToDo = PAGE_SIZE - (((IMG_UINT32) pvRangeAddrStart) & (~PAGE_MASK)); + IMG_UINT8 *pbDo = (IMG_UINT8 *) pvRangeAddrStart; + + while(ui32ByteRemain) + { + if (BM_MapPageAtOffset(psLinuxMemArea->hBMHandle, ui32ByteOffset + (ui32Length - ui32ByteRemain))) + { + pfnInnerCacheOp(pbDo, pbDo + ui32BytesToDo); + } + pbDo += ui32BytesToDo; + ui32ByteRemain -= ui32BytesToDo; + ui32BytesToDo = MIN(ui32ByteRemain, PAGE_SIZE); + } + } +} + +static +IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + 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); + + LinuxLockMutex(&g_sMMapMutex); + + psMMapOffsetStructList = &psLinuxMemArea->sMMapOffsetStructList; + ui32AreaLength = psLinuxMemArea->ui32ByteSize; + + /* + Don't check the length in the case of sparse mappings as + we only know the physical length not the virtual + */ + if (!psLinuxMemArea->hBMHandle) + { + PVR_ASSERT(ui32Length <= ui32AreaLength); + } + + if(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) + { + ui32AreaOffset = psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset; + psLinuxMemArea = psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea; + } + + /* Recursion surely isn't possible? */ + 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; + + /* Outside permissible range */ + if(pvRangeAddrStart < pvMinVAddr) + goto err_blocked; + + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); + } + else + { + /* If this isn't a vmalloc address, assume we're flushing by + * user-virtual. Compute the mmap base vaddr and use this to + * compute the offset in vmalloc space. + */ + + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); + +#if defined(CONFIG_OUTER_CACHE) + /* + * We don't need to worry about cache aliasing here because + * we have already flushed the virtually-indexed caches (L1 + * etc.) by the supplied user-virtual addresses. + * + * The vmalloc address will only be used to determine + * affected physical pages for outer cache flushing. + */ + pvRangeAddrStart = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress + + (ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr); + } + + pfnMemAreaToPhys = VMallocAreaToPhys; +#else /* defined(CONFIG_OUTER_CACHE) */ + } +#endif /* defined(CONFIG_OUTER_CACHE) */ + break; + } + + case LINUX_MEM_AREA_EXTERNAL_KV: + { + /* We'll only see bPhysContig for frame buffers, and we shouldn't + * be flushing those (they're write combined or uncached). + */ + if (psLinuxMemArea->uData.sExternalKV.bPhysContig == IMG_TRUE) + { + PVR_DPF((PVR_DBG_WARNING, "%s: Attempt to flush contiguous external memory", __func__)); + goto err_blocked; + } + + /* If it has a kernel virtual address, something odd has happened. + * We expect EXTERNAL_KV _only_ from the wrapping of ALLOC_PAGES. + */ + 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; + + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); + +#if defined(CONFIG_OUTER_CACHE) + ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; + pfnMemAreaToPhys = ExternalKVAreaToPhys; +#endif + break; + } + + case LINUX_MEM_AREA_ION: + { + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); + +#if defined(CONFIG_OUTER_CACHE) + ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; + pfnMemAreaToPhys = IONAreaToPhys; +#endif + break; + } + + case LINUX_MEM_AREA_ALLOC_PAGES: + { + pvMinVAddr = FindMMapBaseVAddr(psMMapOffsetStructList, + pvRangeAddrStart, ui32Length); + if(!pvMinVAddr) + goto err_blocked; + + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); + +#if defined(CONFIG_OUTER_CACHE) + ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; + if (psLinuxMemArea->hBMHandle) + { + pfnMemAreaToPhys = AllocPagesSparseAreaToPhys; + } + else + { + pfnMemAreaToPhys = AllocPagesAreaToPhys; + } +#endif + break; + } + + default: + PVR_DBG_BREAK; + } + + LinuxUnLockMutex(&g_sMMapMutex); + +#if defined(CONFIG_OUTER_CACHE) + PVR_ASSERT(pfnMemAreaToPhys != IMG_NULL); + + /* Outer caches need some more work, to get a list of physical addresses */ + { + unsigned long ulStart, ulEnd, ulLength, ulStartOffset, ulEndOffset; + IMG_UINT32 i, ui32NumPages; + IMG_BOOL bValidPage; + + /* Length and offsets of flush region WRT page alignment */ + ulLength = (unsigned long)ui32Length; + ulStartOffset = ((unsigned long)pvRangeAddrStart) & (PAGE_SIZE - 1); + ulEndOffset = ((unsigned long)pvRangeAddrStart + ulLength) & (PAGE_SIZE - 1); + + /* The affected pages, rounded up */ + ui32NumPages = (ulStartOffset + ulLength + PAGE_SIZE - 1) >> PAGE_SHIFT; + + for(i = 0; i < ui32NumPages; i++) + { + bValidPage = pfnMemAreaToPhys(psLinuxMemArea, pvRangeAddrStart, + ui32PageNumOffset, i, &ulStart); + if (bValidPage) + { + 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)); + LinuxUnLockMutex(&g_sMMapMutex); + return IMG_FALSE; +} + +#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) +{ + /* No clean feature on x86 */ + 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_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + /* Write-back and invalidate */ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + /* No clean feature on x86 */ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + /* No invalidate-only support */ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, pvRangeAddrStart, ui32Length, + x86_flush_cache_range, IMG_NULL); +} + +#else /* defined(__i386__) */ + +#if defined(__arm__) + +static void per_cpu_cache_flush(void *arg) +{ + PVR_UNREFERENCED_PARAMETER(arg); + flush_cache_all(); +} + +IMG_VOID OSCleanCPUCacheKM(IMG_VOID) +{ + /* No full (inner) cache clean op */ + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +#if defined(CONFIG_OUTER_CACHE) + outer_clean_range(0, ULONG_MAX); +#endif +} + +IMG_VOID OSFlushCPUCacheKM(IMG_VOID) +{ + ON_EACH_CPU(per_cpu_cache_flush, NULL, 1); +#if defined(CONFIG_OUTER_CACHE) && \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + /* To use the "deferred flush" (not clean) DDK feature you need a kernel + * implementation of outer_flush_all() for ARM CPUs with an outer cache + * controller (e.g. PL310, common with Cortex A9 and later). + * + * Reference DDKs don't require this functionality, as they will only + * clean the cache, never flush (clean+invalidate) it. + */ + outer_flush_all(); +#endif +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) +static inline size_t pvr_dmac_range_len(const void *pvStart, const void *pvEnd) +{ + return (size_t)((char *)pvEnd - (char *)pvStart); +} +#endif + +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_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + dmac_flush_range, outer_flush_range); +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dmac_clean_range, outer_clean_range); +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dmac_inv_range, outer_inv_range); +} + +#else /* defined(__arm__) */ + +#if defined(__mips__) +/* + * dmac cache functions are supposed to be used for dma + * memory which comes from dma-able memory. However examining + * the implementation of dmac cache functions and experimenting, + * can assert that dmac functions are safe to use for high-mem + * memory as well for our OS{Clean/Flush/Invalidate}Cache functions + * + */ + +IMG_VOID OSCleanCPUCacheKM(IMG_VOID) +{ + /* dmac functions flush full cache if size is larger than + * p-cache size. This is a workaround for the fact that + * __flush_cache_all is not an exported symbol. Please + * replace with custom function if available in latest + * version of linux being used. + * Arbitrary large number (1MB) which should be larger than + * mips p-cache sizes for some time in future. + * */ + dma_cache_wback(0, 0x100000); +} + +IMG_VOID OSFlushCPUCacheKM(IMG_VOID) +{ + /* dmac functions flush full cache if size is larger than + * p-cache size. This is a workaround for the fact that + * __flush_cache_all is not an exported symbol. Please + * replace with custom function if available in latest + * version of linux being used. + * Arbitrary large number (1MB) which should be larger than + * mips p-cache sizes for some time in future. + * */ + dma_cache_wback_inv(0, 0x100000); +} + +static inline IMG_UINT32 pvr_dma_range_len(const void *pvStart, const void *pvEnd) +{ + return (IMG_UINT32)((char *)pvEnd - (char *)pvStart); +} + +static void pvr_dma_cache_wback_inv(const void *pvStart, const void *pvEnd) +{ + dma_cache_wback_inv((IMG_UINTPTR_T)pvStart, pvr_dma_range_len(pvStart, pvEnd)); +} + +static void pvr_dma_cache_wback(const void *pvStart, const void *pvEnd) +{ + dma_cache_wback((IMG_UINTPTR_T)pvStart, pvr_dma_range_len(pvStart, pvEnd)); +} + +static void pvr_dma_cache_inv(const void *pvStart, const void *pvEnd) +{ + dma_cache_inv((IMG_UINTPTR_T)pvStart, pvr_dma_range_len(pvStart, pvEnd)); +} + +IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dma_cache_wback_inv, IMG_NULL); +} + +IMG_BOOL OSCleanCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dma_cache_wback, IMG_NULL); +} + +IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32Length) +{ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dma_cache_inv, IMG_NULL); +} + +#else /* defined(__mips__) */ + +#error "Implement CPU cache flush/clean/invalidate primitives for this CPU!" + +#endif /* defined(__mips__) */ + +#endif /* defined(__arm__) */ + +#endif /* defined(__i386__) */ + +typedef struct _AtomicStruct +{ + atomic_t RefCount; +} AtomicStruct; + +PVRSRV_ERROR OSAtomicAlloc(IMG_PVOID *ppvRefCount) +{ + AtomicStruct *psRefCount; + + psRefCount = kmalloc(sizeof(AtomicStruct), GFP_KERNEL); + if (psRefCount == NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + atomic_set(&psRefCount->RefCount, 0); + + *ppvRefCount = psRefCount; + return PVRSRV_OK; +} + +IMG_VOID OSAtomicFree(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + PVR_ASSERT(atomic_read(&psRefCount->RefCount) == 0); + kfree(psRefCount); +} + +IMG_VOID OSAtomicInc(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + atomic_inc(&psRefCount->RefCount); +} + +IMG_BOOL OSAtomicDecAndTest(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + return atomic_dec_and_test(&psRefCount->RefCount) ? IMG_TRUE:IMG_FALSE; +} + +IMG_UINT32 OSAtomicRead(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + return (IMG_UINT32) atomic_read(&psRefCount->RefCount); +} + +IMG_VOID OSReleaseBridgeLock(IMG_VOID) +{ + LinuxUnLockMutex(&gPVRSRVLock); +} + +IMG_VOID OSReacquireBridgeLock(IMG_VOID) +{ + LinuxLockMutex(&gPVRSRVLock); +} + +typedef struct _OSTime +{ + unsigned long ulTime; +} OSTime; + +PVRSRV_ERROR OSTimeCreateWithUSOffset(IMG_PVOID *pvRet, IMG_UINT32 ui32USOffset) +{ + OSTime *psOSTime; + + psOSTime = kmalloc(sizeof(OSTime), GFP_KERNEL); + if (psOSTime == IMG_NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + psOSTime->ulTime = usecs_to_jiffies(jiffies_to_usecs(jiffies) + ui32USOffset); + *pvRet = psOSTime; + return PVRSRV_OK; +} + + +IMG_BOOL OSTimeHasTimePassed(IMG_PVOID pvData) +{ + OSTime *psOSTime = pvData; + + if (time_is_before_jiffies(psOSTime->ulTime)) + { + return IMG_TRUE; + } + return IMG_FALSE; +} + +IMG_VOID OSTimeDestroy(IMG_PVOID pvData) +{ + kfree(pvData); +} + +IMG_VOID OSGetCurrentProcessNameKM(IMG_CHAR *pszName, IMG_UINT32 ui32Size) +{ + strncpy(pszName, current->comm, MIN(ui32Size,TASK_COMM_LEN)); +} + +/* One time osfunc initialisation */ +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 + +#if defined (SUPPORT_ION) + { + PVRSRV_ERROR eError; + + eError = IonInit(); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: IonInit failed", __FUNCTION__)); + } + } +#endif + return PVRSRV_OK; +} + +/* + * Osfunc deinitialisation. + * Note that PVROSFuncInit may not have been called + */ +IMG_VOID PVROSFuncDeInit(IMG_VOID) +{ +#if defined (SUPPORT_ION) + IonDeinit(); +#endif +#if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) + if (psTimerWorkQueue != NULL) + { + destroy_workqueue(psTimerWorkQueue); + } +#endif +} diff --git a/pvr-source/services4/srvkm/env/linux/osperproc.c b/pvr-source/services4/srvkm/env/linux/osperproc.c new file mode 100644 index 0000000..a22b461 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/osperproc.c @@ -0,0 +1,146 @@ +/*************************************************************************/ /*! +@Title Linux specific per process data functions +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "services_headers.h" +#include "osperproc.h" + +#include "env_perproc.h" +#include "proc.h" +#if defined (SUPPORT_ION) +#include "linux/ion.h" + +extern struct ion_device *psIonDev; +#endif +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; + + /* Linux specific mmap processing */ + LinuxMMapPerProcessConnect(psEnvPerProc); + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + /* Linked list of PVRSRV_FILE_PRIVATE_DATA structures */ + INIT_LIST_HEAD(&psEnvPerProc->sDRMAuthListHead); +#endif +#if defined(SUPPORT_ION) + OSSNPrintf(psEnvPerProc->azIonClientName, ION_CLIENT_NAME_SIZE, "pvr_ion_client-%d", OSGetCurrentProcessIDKM()); + psEnvPerProc->psIONClient = + ion_client_create(psIonDev, + 1 << ION_HEAP_TYPE_SYSTEM_CONTIG | + 1 << ION_HEAP_TYPE_SYSTEM, + psEnvPerProc->azIonClientName); + + if (IS_ERR_OR_NULL(psEnvPerProc->psIONClient)) + { + PVR_DPF((PVR_DBG_ERROR, "OSPerProcessPrivateDataInit: Couldn't create " + "ion client for per process data")); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } +#endif /* SUPPORT_ION */ + 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; + + /* Linux specific mmap processing */ + LinuxMMapPerProcessDisconnect(psEnvPerProc); + + /* Remove per process /proc entries */ + RemovePerProcessProcDir(psEnvPerProc); + + eError = OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, + sizeof(PVRSRV_ENV_PER_PROCESS_DATA), + hOsPrivateData, + psEnvPerProc->hBlockAlloc); + /*not nulling pointer, copy on stack*/ + + 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/pvr-source/services4/srvkm/env/linux/pdump.c b/pvr-source/services4/srvkm/env/linux/pdump.c new file mode 100644 index 0000000..0124737 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/pdump.c @@ -0,0 +1,804 @@ +/*************************************************************************/ /*! +@Title Parameter dump macro target routines +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#if defined (SUPPORT_SGX) || defined (SUPPORT_VGX) +#if defined (PDUMP) + +#include <asm/atomic.h> +#include <stdarg.h> +#if defined (SUPPORT_SGX) +#include "sgxdefs.h" /* Is this still needed? */ +#endif +#include "services_headers.h" + +#include "pvrversion.h" +#include "pvr_debug.h" + +#include "dbgdrvif.h" +#if defined (SUPPORT_SGX) +#include "sgxmmu.h"/* Is this still needed? */ +#endif +#include "mm.h" +#include "pdump_km.h" +#include "pdump_int.h" + +#include <linux/kernel.h> // sprintf +#include <linux/string.h> // strncpy, strlen + +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) + +/* + Maximum file size to split output files +*/ +#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 + + + + +static inline IMG_BOOL PDumpSuspended(IMG_VOID) +{ + return (atomic_read(&gsPDumpSuspended) != 0) ? IMG_TRUE : IMG_FALSE; +} + +/*! + * \name PDumpOSGetScriptString + */ +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; +} + +/*! + * \name PDumpOSGetMessageString + */ +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; +} + +/*! + * \name PDumpOSGetFilenameString + */ +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; +} + +/*! + * \name PDumpOSWriteString2 + */ +IMG_BOOL PDumpOSWriteString2(IMG_HANDLE hScript, IMG_UINT32 ui32Flags) +{ + return PDumpWriteString2(hScript, ui32Flags); +} + +/*! + * \name PDumpOSBufprintf + */ +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) /* glibc >= 2.1 or glibc 2.0 */ + { + 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; +} + +/*! + * \name PDumpOSVSprintf + */ +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) /* glibc >= 2.1 or glibc 2.0 */ + { + PVR_DPF((PVR_DBG_ERROR, "Buffer overflow detected, pdump output may be incomplete.")); + + return PVRSRV_ERROR_PDUMP_BUF_OVERFLOW; + } + + return PVRSRV_OK; +} + +/*! + * \name PDumpOSDebugPrintf + */ +IMG_VOID PDumpOSDebugPrintf(IMG_CHAR* pszFormat, ...) +{ + PVR_UNREFERENCED_PARAMETER(pszFormat); + + /* FIXME: Implement using services PVR_DBG or otherwise with kprintf */ +} + +/*! + * \name PDumpOSSprintf + */ +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) /* glibc >= 2.1 or glibc 2.0 */ + { + PVR_DPF((PVR_DBG_ERROR, "Buffer overflow detected, pdump output may be incomplete.")); + + return PVRSRV_ERROR_PDUMP_BUF_OVERFLOW; + } + + return PVRSRV_OK; +} + +/*! + * \name PDumpOSBuflen + */ +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); +} + +/*! + * \name PDumpOSVerifyLineEnding + */ +IMG_VOID PDumpOSVerifyLineEnding(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMax) +{ + IMG_UINT32 ui32Count; + IMG_CHAR* pszBuf = hBuffer; + + /* strlen */ + ui32Count = PDumpOSBuflen(hBuffer, ui32BufferSizeMax); + + /* Put \r \n sequence at the end if it isn't already there */ + 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'; + } +} + +/*! + * \name PDumpOSGetStream + */ +IMG_HANDLE PDumpOSGetStream(IMG_UINT32 ePDumpStream) +{ + return (IMG_HANDLE)gsDBGPdumpState.psStream[ePDumpStream]; +} + +/*! + * \name PDumpOSGetStreamOffset + */ +IMG_UINT32 PDumpOSGetStreamOffset(IMG_UINT32 ePDumpStream) +{ + PDBG_STREAM psStream = gsDBGPdumpState.psStream[ePDumpStream]; + return gpfnDbgDrv->pfnGetStreamOffset(psStream); +} + +/*! + * \name PDumpOSGetParamFileNum + */ +IMG_UINT32 PDumpOSGetParamFileNum(IMG_VOID) +{ + return gsDBGPdumpState.ui32ParamFileNum; +} + +/*! + * \name PDumpOSWriteString + */ +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); +} + +/*! + * \name PDumpOSCheckForSplitting + */ +IMG_VOID PDumpOSCheckForSplitting(IMG_HANDLE hStream, IMG_UINT32 ui32Size, IMG_UINT32 ui32Flags) +{ + /* File size limit not implemented for this OS. + */ + PVR_UNREFERENCED_PARAMETER(hStream); + PVR_UNREFERENCED_PARAMETER(ui32Size); + PVR_UNREFERENCED_PARAMETER(ui32Flags); +} + +/*! + * \name PDumpOSJTInitialised + */ +IMG_BOOL PDumpOSJTInitialised(IMG_VOID) +{ + if(gpfnDbgDrv) + { + return IMG_TRUE; + } + return IMG_FALSE; +} + +/*! + * \name PDumpOSIsSuspended + */ +inline IMG_BOOL PDumpOSIsSuspended(IMG_VOID) +{ + return (atomic_read(&gsPDumpSuspended) != 0) ? IMG_TRUE : IMG_FALSE; +} + +/*! + * \name PDumpOSCPUVAddrToDevPAddr + */ +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); /* for when no assert */ + + /* Caller must now alway supply hOSMemHandle, even though we only (presently) + use it here in the linux implementation */ + + PVR_ASSERT (hOSMemHandle != IMG_NULL); + + sCpuPAddr = OSMemHandleToCpuPAddr(hOSMemHandle, ui32Offset); + PVR_ASSERT((sCpuPAddr.uiAddr & (ui32PageSize - 1)) == 0); + + /* convert CPU physical addr to device physical */ + *psDevPAddr = SysCpuPAddrToDevPAddr(eDeviceType, sCpuPAddr); +} + +/*! + * \name PDumpOSCPUVAddrToPhysPages + */ +IMG_VOID PDumpOSCPUVAddrToPhysPages(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32Offset, + IMG_PUINT8 pui8LinAddr, + IMG_UINT32 ui32DataPageMask, + IMG_UINT32 *pui32PageOffset) +{ + if(hOSMemHandle) + { + /* + * If a Services memory handle is provided then use it. + */ + 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); + } +} + +/*! + * \name PDumpOSDebugDriverWrite + */ +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; +} + +/*! + * \name PDumpOSReleaseExecution + */ +IMG_VOID PDumpOSReleaseExecution(IMG_VOID) +{ + OSReleaseThreadQuanta(); +} + +/************************************************************************** + * Function Name : PDumpInit + * Outputs : None + * Returns : + * Description : Reset connection to vldbgdrv + * Then try to connect to PDUMP streams +**************************************************************************/ +IMG_VOID PDumpInit(IMG_VOID) +{ + IMG_UINT32 i; + DBGKM_CONNECT_NOTIFIER sConnectNotifier; + + /* If we tried this earlier, then we might have connected to the driver + * But if pdump.exe was running then the stream connected would fail + */ + if (!gpfnDbgDrv) + { + DBGDrvGetServiceTable(&gpfnDbgDrv); + + + // If something failed then no point in trying to connect streams + if (gpfnDbgDrv == IMG_NULL) + { + return; + } + + /* + * Pass the connection notify callback + */ + 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; + } + + /* + * Remove the connection notify callback + */ + 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; + } + + /* + * Remove the connection notify callback + */ + sConnectNotifier.pfnConnectNotifier = 0; + gpfnDbgDrv->pfnSetConnectNotifier(sConnectNotifier); + + gpfnDbgDrv = IMG_NULL; +} + +/************************************************************************** + * Function Name : PDumpStartInitPhaseKM + * Inputs : None + * Outputs : None + * Returns : None + * Description : Resume init phase state +**************************************************************************/ +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; +} + +/************************************************************************** + * Function Name : PDumpStopInitPhaseKM + * Inputs : None + * Outputs : None + * Returns : None + * Description : End init phase state +**************************************************************************/ +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; +} + +/************************************************************************** + * Function Name : PDumpIsLastCaptureFrameKM + * Inputs : None + * Outputs : None + * Returns : True or false + * Description : Tests whether the current frame is being pdumped +**************************************************************************/ +IMG_BOOL PDumpIsLastCaptureFrameKM(IMG_VOID) +{ + return gpfnDbgDrv->pfnIsLastCaptureFrame(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2]); +} + + +/************************************************************************** + * Function Name : PDumpIsCaptureFrameKM + * Inputs : None + * Outputs : None + * Returns : True or false + * Description : Tests whether the current frame is being pdumped +**************************************************************************/ +IMG_BOOL PDumpOSIsCaptureFrameKM(IMG_VOID) +{ + if (PDumpSuspended()) + { + return IMG_FALSE; + } + return gpfnDbgDrv->pfnIsCaptureFrame(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2], IMG_FALSE); +} + +/************************************************************************** + * Function Name : PDumpSetFrameKM + * Inputs : None + * Outputs : None + * Returns : None + * Description : Sets a frame +**************************************************************************/ +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; +} + + +/***************************************************************************** + FUNCTION : PDumpWriteString2 + + PURPOSE : + + PARAMETERS : + + RETURNS : +*****************************************************************************/ +static IMG_BOOL PDumpWriteString2(IMG_CHAR * pszString, IMG_UINT32 ui32Flags) +{ + return PDumpWriteILock(gsDBGPdumpState.psStream[PDUMP_STREAM_SCRIPT2], (IMG_UINT8 *) pszString, strlen(pszString), ui32Flags); +} + + +/***************************************************************************** + FUNCTION : PDumpWriteILock + + PURPOSE : Writes, making sure it all goes... + + PARAMETERS : + + RETURNS : +*****************************************************************************/ +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; + } + + + /* + Set the stream marker to split output files + */ + + 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; +} + +/***************************************************************************** + FUNCTION : DbgSetFrame + + PURPOSE : Sets the frame in the stream + + PARAMETERS : psStream - Stream pointer + ui32Frame - Frame number to set + + RETURNS : None +*****************************************************************************/ +static IMG_VOID DbgSetFrame(PDBG_STREAM psStream, IMG_UINT32 ui32Frame) +{ + gpfnDbgDrv->pfnSetFrame(psStream, ui32Frame); +} + +/***************************************************************************** + FUNCTION : DbgSetMarker + + PURPOSE : Sets the marker of the stream to split output files + + PARAMETERS : psStream - Stream pointer + ui32Marker - Marker number to set + + RETURNS : None +*****************************************************************************/ +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 /* #if defined (PDUMP) */ +#endif /* #if defined (SUPPORT_SGX) */ +/***************************************************************************** + End of file (PDUMP.C) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/private_data.h b/pvr-source/services4/srvkm/env/linux/private_data.h new file mode 100644 index 0000000..6b09705 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/private_data.h @@ -0,0 +1,95 @@ +/*************************************************************************/ /*! +@Title Linux private data structure +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#ifndef __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 + +/* This structure is required in the rare case that a process creates + * a connection to services, but before closing the file descriptor, + * does a fork(). This fork() will duplicate the file descriptor in the + * child process. If the parent process dies before the child, this can + * cause the PVRSRVRelease() method to be called in a different process + * context than the original PVRSRVOpen(). This is bad because we need + * to update the per-process data reference count and/or free the + * per-process data. So we must keep a record of which PID's per-process + * data to inspect during ->release(). + */ + +typedef struct +{ + /* PID that created this services connection */ + IMG_UINT32 ui32OpenPID; + + /* Global kernel MemInfo handle */ +#if defined (SUPPORT_SID_INTERFACE) + IMG_SID hKernelMemInfo; +#else + IMG_HANDLE hKernelMemInfo; +#endif + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + /* The private data is on a list in the per-process data structure */ + struct list_head sDRMAuthListItem; + + struct drm_file *psDRMFile; +#endif + +#if defined(SUPPORT_MEMINFO_IDS) + /* Globally unique "stamp" for kernel MemInfo */ + IMG_UINT64 ui64Stamp; +#endif /* defined(SUPPORT_MEMINFO_IDS) */ + + /* Accounting for OSAllocMem */ + IMG_HANDLE hBlockAlloc; + +#if defined(SUPPORT_DRI_DRM_EXT) + IMG_PVOID pPriv; /*private data for extending this struct*/ +#endif +} +PVRSRV_FILE_PRIVATE_DATA; + +#endif /* __INCLUDED_PRIVATE_DATA_H_ */ + diff --git a/pvr-source/services4/srvkm/env/linux/proc.c b/pvr-source/services4/srvkm/env/linux/proc.c new file mode 100644 index 0000000..7307257 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/proc.c @@ -0,0 +1,1414 @@ +/*************************************************************************/ /*! +@Title Proc files implementation. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Functions for creating and reading proc filesystem entries. + Proc filesystem support must be built into the kernel for + these functions to be any use. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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/mm.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/sched.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" + +// The proc entry for our /proc/pvr directory +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); + +/*! +****************************************************************************** + + @Function : printAppend + + @Description + + Print into the supplied buffer at the specified offset remaining within + the specified total buffer size. + + @Input size : the total size of the buffer + + @Input off : the offset into the buffer to start printing + + @Input format : the printf format string + + @Input ... : format args + + @Return : The number of chars now in the buffer (original value of 'off' + plus number of chars added); 'size' if full. + +*****************************************************************************/ +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); + /* According to POSIX, n is greater than or equal to the size available if + * the print would have overflowed the buffer. Other platforms may + * return -1 if printing was truncated. + */ + if (n >= (IMG_INT)space || n < 0) + { + /* Ensure final string is terminated */ + buffer[size - 1] = 0; + return (off_t)(size - 1); + } + else + { + return (off + (off_t)n); + } +} + + +/*! +****************************************************************************** + + @Function : ProcSeq1ElementOff2Element + + @Description + + Heleper Offset -> Element function for /proc files with only one entry + without header. + + @Input sfile : seq_file object related to /proc/ file + + @Input off : the offset into the buffer (id of object) + + @Return : Pointer to element to be shown. + +*****************************************************************************/ +void* ProcSeq1ElementOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + // Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL + if(!off) + return (void*)2; + return NULL; +} + + +/*! +****************************************************************************** + + @Function : ProcSeq1ElementHeaderOff2Element + + @Description + + Heleper Offset -> Element function for /proc files with only one entry + with header. + + @Input sfile : seq_file object related to /proc/ file + + @Input off : the offset into the buffer (id of object) + + @Return : Pointer to element to be shown. + +*****************************************************************************/ +void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t off) +{ + PVR_UNREFERENCED_PARAMETER(sfile); + + if(!off) + { + return PVR_PROC_SEQ_START_TOKEN; + } + + // Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL + if(off == 1) + return (void*)2; + + return NULL; +} + + +/*! +****************************************************************************** + + @Function : pvr_proc_open + + @Description + File opening function passed to proc_dir_entry->proc_fops for /proc entries + created by CreateProcReadEntrySeq. + + @Input inode : inode entry of opened /proc file + + @Input file : file entry of opened /proc file + + @Return : 0 if no errors + +*****************************************************************************/ +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); + + /* Add pointer to handlers to seq_file structure */ + seq->private = pvr_proc_entry->data; + return ret; +} + +/*! +****************************************************************************** + + @Function : pvr_proc_write + + @Description + File writing function passed to proc_dir_entry->proc_fops for /proc files. + It's exacly the same function that is used as default one (->fs/proc/generic.c), + it calls proc_dir_entry->write_proc for writing procedure. + +*****************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_start + + @Description + Seq_file start function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + This function ises off2element handler. + + @Input proc_seq_file : sequence file entry + + @Input pos : offset within file (id of entry) + + @Return : Pointer to element from we start enumeration (0 ends it) + +*****************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_stop + + @Description + Seq_file stop function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + + @Input proc_seq_file : sequence file entry + + @Input v : current element pointer + +*****************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_next + + @Description + Seq_file next element function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + It uses supplied 'next' handler for fetching next element (or 0 if there is no one) + + @Input proc_seq_file : sequence file entry + + @Input pos : offset within file (id of entry) + + @Input v : current element pointer + + @Return : next element pointer (or 0 if end) + +*****************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function : pvr_proc_seq_show + + @Description + Seq_file show element function. Detailed description of seq_file workflow can + be found here: http://tldp.org/LDP/lkmpg/2.6/html/x861.html. + It call proper 'show' handler to show (dump) current element using seq_* functions + + @Input proc_seq_file : sequence file entry + + @Input v : current element pointer + + @Return : 0 if everything is OK + +*****************************************************************************/ +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; +} + + + +/*! +****************************************************************************** + + @Function : CreateProcEntryInDirSeq + + @Description + + Create a file under the given directory. These dynamic files can be used at + runtime to get or set information about the device. Whis version uses seq_file + interface + + @Input pdir : parent directory + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + + +*****************************************************************************/ +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; + + /* Pass the handlers */ + 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; +} + + +/*! +****************************************************************************** + + @Function : CreateProcReadEntrySeq + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. This version uses seq_file structure + for handling content generation. + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function : CreateProcEntrySeq + + @Description + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. This version uses seq_file structure + for handling content generation and is fuller than CreateProcReadEntrySeq (it + supports write access); + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +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 + ); +} + + + +/*! +****************************************************************************** + + @Function : CreatePerProcessProcEntrySeq + + @Description + + Create a file under /proc/pvr/<current process ID>. Apart from the + directory where the file is created, this works the same way as + CreateProcEntry. It's seq_file version. + + + + @Input name : the name of the file to create + + @Input data : aditional data that will be passed to handlers + + @Input next_handler : the function to call to provide the next element. OPTIONAL, if not + supplied, then off2element function is used instead + + @Input show_handler : the function to call to show element + + @Input off2element_handler : the function to call when it is needed to translate offest to element + + @Input startstop_handler : the function to call when output memory page starts or stops. OPTIONAL. + + @Input whandler : the function to interpret writes from the user + + @Return Ptr to proc entry , 0 for failure + +*****************************************************************************/ +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_buffer[256]; + IMG_CHAR dirname[256]; + IMG_INT ret; + const IMG_CHAR *proc_basename = dirname_buffer; + dirname_buffer[255] = dirname[255] = '\0'; + + OSGetProcCmdline(ui32PID, dirname_buffer, sizeof(dirname_buffer)); + PVR_DPF((PVR_DBG_MESSAGE, "Command Line of the process with ID %u is %s", ui32PID, dirname_buffer)); + + proc_basename = OSGetPathBaseName(dirname_buffer, sizeof(dirname_buffer)); + PVR_DPF((PVR_DBG_MESSAGE, "Base Name of the process with ID %u is %s\n", ui32PID, proc_basename)); + + ret = snprintf(dirname, sizeof(dirname), "%u-%s", ui32PID, proc_basename); + PVR_DPF((PVR_DBG_MESSAGE, "Creating a new process entry for %s with ID %u\n", proc_basename, 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); +} + + +/*! +****************************************************************************** + + @Function : RemoveProcEntrySeq + + @Description + + Remove a single node (created using *Seq function) under /proc/pvr. + + @Input proc_entry : structure returned by Create function. + + @Return nothing + +*****************************************************************************/ +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 ); + + } +} + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcEntry Seq + + @Description + + Remove a single node under the per process proc directory (created by *Seq function). + + Remove a single node (created using *Seq function) under /proc/pvr. + + @Input proc_entry : structure returned by Create function. + + @Return nothing + +*****************************************************************************/ +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 ); + } +} + +/*! +****************************************************************************** + + @Function : pvr_read_proc_vm + + @Description + + When the user accesses the proc filesystem entry for the device, we are + called here to create the content for the 'file'. We can print anything we + want here. If the info we want to return is too big for one page ('count' + chars), we return successive chunks on each call. For a number of ways of + achieving this, refer to proc_file_read() in linux/fs/proc/generic.c. + + Here, as we are accessing lists of information, we output '1' in '*start' to + instruct proc to advance 'off' by 1 on each call. The number of chars placed + in the buffer is returned. Multiple calls are made here by the proc + filesystem until we set *eof. We can return zero without setting eof to + instruct proc to flush 'page' (causing it to be printed) if there is not + enough space left (eg for a complete line). + + @Input page : where to write the output + + @Input start : memory location into which should be written next offset + to read from. + + @Input off : the offset into the /proc file being read + + @Input count : the size of the buffer 'page' + + @Input eof : memory location into which 1 should be written when at EOF + + @Input data : data specific to this /proc file entry + + @Return : length of string written to page + +*****************************************************************************/ +static IMG_INT pvr_read_proc(IMG_CHAR *page, IMG_CHAR **start, off_t off, + IMG_INT count, IMG_INT *eof, IMG_VOID *data) +{ + /* PRQA S 0307 1 */ /* ignore warning about casting to different pointer type */ + 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) /* not enough space in the buffer */ + { + *start = (IMG_CHAR *) 0; /* don't advance the offset */ + } + else + { + *start = (IMG_CHAR *) 1; + } + + return len; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntryInDir + + @Description + + Create a file under the given directory. These dynamic files can be used at + runtime to get or set information about the device. + + @Input pdir : parent directory + + @Input name : the name of the file to create + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntry + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get or set information about the device. + + This interface is fuller than CreateProcReadEntry, and supports write access; + it is really just a wrapper for the native linux functions. + + @Input name : the name of the file to create under /proc/pvr + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function : CreatePerProcessProcEntry + + @Description + + Create a file under /proc/pvr/<current process ID>. Apart from the + directory where the file is created, this works the same way as + CreateProcEntry. + + @Input name : the name of the file to create under the per process /proc directory + + @Input rhandler : the function to supply the content + + @Input whandler : the function to interpret writes from the user + + @Return success code : 0 or -errno. + +*****************************************************************************/ +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); +} + + +/*! +****************************************************************************** + + @Function : CreateProcReadEntry + + @Description + + Create a file under /proc/pvr. These dynamic files can be used at runtime + to get information about the device. Creation WILL fail if proc support is + not compiled into the kernel. That said, the Linux kernel is not even happy + to build without /proc support these days. + + @Input name : the name of the file to create + + @Input handler : the function to call to provide the content + + @Return 0 for success, -errno for failure + +*****************************************************************************/ +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; + } + + /* PRQA S 0307 1 */ /* ignore warning about casting to different pointer type */ + 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; +} + + +/*! +****************************************************************************** + + @Function : CreateProcEntries + + @Description + + Create a directory /proc/pvr and the necessary entries within it. These + dynamic files can be used at runtime to get information about the device. + Creation might fail if proc support is not compiled into the kernel or if + there is no memory + + @Input none + + @Return nothing + +*****************************************************************************/ +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; +} + + +/*! +****************************************************************************** + + @Function : RemoveProcEntry + + @Description + + Remove a single node under /proc/pvr. + + @Input name : the name of the node to remove + + @Return nothing + +*****************************************************************************/ +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)); + } +} + + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcEntry + + @Description + + Remove a single node under the per process proc directory. + + @Input name : the name of the node to remove + + @Return nothing + +*****************************************************************************/ +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)); + } +} + + +/*! +****************************************************************************** + + @Function : RemovePerProcessProcDir + + @Description + + Remove the per process directorty under /proc/pvr. + + @Input psPerProc : environment specific per process data + + @Return nothing + +*****************************************************************************/ +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); + } +} + +/*! +****************************************************************************** + + @Function : RemoveProcEntries + + Description + + Proc filesystem entry deletion - Remove all proc filesystem entries for + the driver. + + @Input none + + @Return nothing + +*****************************************************************************/ +IMG_VOID RemoveProcEntries(IMG_VOID) +{ +#ifdef DEBUG + RemoveProcEntrySeq( g_pProcDebugLevel ); +#ifdef PVR_MANUAL_POWER_CONTROL + RemoveProcEntrySeq( g_pProcPowerLevel ); +#endif /* PVR_MANUAL_POWER_CONTROL */ +#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); +} + +/***************************************************************************** + FUNCTION : ProcSeqShowVersion + + PURPOSE : Print the content of version to /proc file + + PARAMETERS : sfile - /proc seq_file + el - Element to print +*****************************************************************************/ +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); +} + +/*! +****************************************************************************** + + @Function procDumpSysNodes (plus deviceTypeToString and deviceClassToString) + + @Description + + Format the contents of /proc/pvr/nodes + + @Input buf : where to place format contents data. + + @Input size : the size of the buffer into which to place data + + @Input off : how far into the file we are. + + @Return amount of data placed in buffer, 0, or END_OF_FILE : + +******************************************************************************/ +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; + } +} + +/***************************************************************************** + FUNCTION : ProcSeqShowSysNodes + + PURPOSE : Print the content of version to /proc file + + PARAMETERS : sfile - /proc seq_file + el - Element to print +*****************************************************************************/ +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); +} + +/***************************************************************************** + FUNCTION : ProcSeqOff2ElementSysNodes + + PURPOSE : Transale offset to element (/proc stuff) + + PARAMETERS : sfile - /proc seq_file + off - the offset into the buffer + + RETURNS : element to print +*****************************************************************************/ +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) + { + /* Find Dev Node */ + psDevNode = (PVRSRV_DEVICE_NODE*) + List_PVRSRV_DEVICE_NODE_Any_va(psSysData->psDeviceNodeList, + DecOffPsDev_AnyVaCb, + &off); + } + + /* Return anything that is not PVR_RPOC_SEQ_START_TOKEN and NULL */ + return (void*)psDevNode; +} + +/***************************************************************************** + End of file (proc.c) +*****************************************************************************/ diff --git a/pvr-source/services4/srvkm/env/linux/proc.h b/pvr-source/services4/srvkm/env/linux/proc.h new file mode 100644 index 0000000..bc2a554 --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/proc.h @@ -0,0 +1,127 @@ +/*************************************************************************/ /*! +@Title Proc interface definition. +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Functions for creating and reading proc filesystem entries. + Refer to proc.c +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#ifndef __SERVICES_PROC_H__ +#define __SERVICES_PROC_H__ + +#include <asm/system.h> // va_list etc +#include <linux/proc_fs.h> // read_proc_t etc +#include <linux/seq_file.h> // seq_file + +#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; + + +/** off2element function for elements with only ONE element (no header) */ +void* ProcSeq1ElementOff2Element(struct seq_file *sfile, loff_t off); + +/** off2element function for elements with only ONE element (+ header) */ +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/pvr-source/services4/srvkm/env/linux/pvr_bridge_k.c b/pvr-source/services4/srvkm/env/linux/pvr_bridge_k.c new file mode 100644 index 0000000..5d1ad8c --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/pvr_bridge_k.c @@ -0,0 +1,524 @@ +/*************************************************************************/ /*! +@Title PVR Bridge Module (kernel side) +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Receives calls from the user portion of services and + despatches them to functions in the kernel portion. +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include "img_defs.h" +#include "services.h" +#include "pvr_bridge.h" +#include "perproc.h" +#include "mutex.h" +#include "syscommon.h" +#include "pvr_debug.h" +#include "proc.h" +#include "private_data.h" +#include "linkage.h" +#include "pvr_bridge_km.h" +#include "pvr_uaccess.h" +#include "refcount.h" +#include "buffer_manager.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 + +/* VGX: */ +#if defined(SUPPORT_VGX) +#include "vgx_bridge.h" +#endif + +/* SGX: */ +#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 /* defined(SUPPORT_MEMINFO_IDS) */ + +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) + +/* + * Lock MMap regions list (called on page start/stop while reading /proc/mmap) + * + * sfile : seq_file that handles /proc file + * start : TRUE if it's start, FALSE if it's stop + * + */ +static void ProcSeqStartstopBridgeStats(struct seq_file *sfile,IMG_BOOL start) +{ + if(start) + { + LinuxLockMutex(&gPVRSRVLock); + } + else + { + LinuxUnLockMutex(&gPVRSRVLock); + } +} + + +/* + * Convert offset (index from KVOffsetTable) to element + * (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * off : index into the KVOffsetTable from which to print + * + * returns void* : Pointer to element that will be dumped + * +*/ +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]; +} + +/* + * Gets next MMap element to show. (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * el : actual element + * off : index into the KVOffsetTable from which to print + * + * returns void* : Pointer to element to show (0 ends iteration) +*/ +static void* ProcSeqNextBridgeStats(struct seq_file *sfile,void* el,loff_t off) +{ + return ProcSeqOff2ElementBridgeStats(sfile,off); +} + + +/* + * Show MMap element (called when reading /proc/mmap file) + + * sfile : seq_file that handles /proc file + * el : actual element + * +*/ +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 /* DEBUG_BRIDGE_KM */ + + +#if defined(SUPPORT_DRI_DRM) +int +PVRSRV_BridgeDispatchKM(struct drm_device unref__ *dev, void *arg, struct drm_file *pFile) +#else +long +PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsigned long arg) +#endif +{ + IMG_UINT32 cmd; +#if !defined(SUPPORT_DRI_DRM) + PVRSRV_BRIDGE_PACKAGE *psBridgePackageUM = (PVRSRV_BRIDGE_PACKAGE *)arg; + PVRSRV_BRIDGE_PACKAGE sBridgePackageKM; +#endif + PVRSRV_BRIDGE_PACKAGE *psBridgePackageKM; + IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); + PVRSRV_PER_PROCESS_DATA *psPerProc; + IMG_INT err = -EFAULT; + + LinuxLockMutex(&gPVRSRVLock); + +#if defined(SUPPORT_DRI_DRM) + psBridgePackageKM = (PVRSRV_BRIDGE_PACKAGE *)arg; + PVR_ASSERT(psBridgePackageKM != IMG_NULL); +#else + psBridgePackageKM = &sBridgePackageKM; + + if(!OSAccessOK(PVR_VERIFY_WRITE, + psBridgePackageUM, + sizeof(PVRSRV_BRIDGE_PACKAGE))) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Received invalid pointer to function arguments", + __FUNCTION__)); + + goto unlock_and_return; + } + + /* FIXME - Currently the CopyFromUserWrapper which collects stats about + * how much data is shifted to/from userspace isn't available to us + * here. */ + 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 + { + /* lookup per-process data for this process */ + psPerProc = PVRSRVPerProcessData(ui32PID); + if(psPerProc == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRV_BridgeDispatchKM: " + "Couldn't create per-process data area")); + goto unlock_and_return; + } + } + + psBridgePackageKM->ui32BridgeID = PVRSRV_GET_BRIDGE_ID(psBridgePackageKM->ui32BridgeID); + + switch(cmd) + { + case PVRSRV_BRIDGE_EXPORT_DEVICEMEM_2: + { + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + + if(psPrivateData->hKernelMemInfo) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Can only export one MemInfo " + "per file descriptor", __FUNCTION__)); + err = -EINVAL; + goto unlock_and_return; + } + break; + } + + case PVRSRV_BRIDGE_MAP_DEV_MEMORY_2: + { + PVRSRV_BRIDGE_IN_MAP_DEV_MEMORY *psMapDevMemIN = + (PVRSRV_BRIDGE_IN_MAP_DEV_MEMORY *)psBridgePackageKM->pvParamIn; + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + + if(!psPrivateData->hKernelMemInfo) + { + PVR_DPF((PVR_DBG_ERROR, "%s: File descriptor has no " + "associated MemInfo handle", __FUNCTION__)); + err = -EINVAL; + goto unlock_and_return; + } + + if (pvr_put_user(psPrivateData->hKernelMemInfo, &psMapDevMemIN->hKernelMemInfo) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } + break; + } + + default: + { + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + + if(psPrivateData->hKernelMemInfo) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Import/Export handle tried " + "to use privileged service", __FUNCTION__)); + goto unlock_and_return; + } + break; + } + } + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) + switch(cmd) + { + case PVRSRV_BRIDGE_MAP_DEV_MEMORY: + case PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY: + { + PVRSRV_FILE_PRIVATE_DATA *psPrivateData; + int authenticated = pFile->authenticated; + PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc; + + if (authenticated) + { + break; + } + + /* + * The DRM file structure we are using for Services + * is not one that DRI authentication was done on. + * Look for an authenticated file structure for + * this process, making sure the DRM master is the + * same as ours. + */ + 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 /* defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) */ + + err = BridgedDispatchKM(psPerProc, psBridgePackageKM); + if(err != PVRSRV_OK) + goto unlock_and_return; + + switch(cmd) + { + case PVRSRV_BRIDGE_EXPORT_DEVICEMEM_2: + { + PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *psExportDeviceMemOUT = + (PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *)psBridgePackageKM->pvParamOut; + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + IMG_HANDLE hMemInfo; + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + + if (pvr_get_user(hMemInfo, &psExportDeviceMemOUT->hMemInfo) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } + + /* Look up the meminfo we just exported */ + if(PVRSRVLookupHandle(KERNEL_HANDLE_BASE, + (IMG_PVOID *)&psKernelMemInfo, + hMemInfo, + PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up export handle", __FUNCTION__)); + err = -EFAULT; + goto unlock_and_return; + } + + /* Bump the refcount; decremented on release of the fd */ + PVRSRVKernelMemInfoIncRef(psKernelMemInfo); + + /* Tell the XProc about the export if required */ + if (psKernelMemInfo->sShareMemWorkaround.bInUse) + { + BM_XProcIndexAcquire(psKernelMemInfo->sShareMemWorkaround.ui32ShareIndex); + } + + psPrivateData->hKernelMemInfo = hMemInfo; +#if defined(SUPPORT_MEMINFO_IDS) + psPrivateData->ui64Stamp = ++ui64Stamp; + + psKernelMemInfo->ui64Stamp = psPrivateData->ui64Stamp; + if (pvr_put_user(psPrivateData->ui64Stamp, &psExportDeviceMemOUT->ui64Stamp) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } +#endif + break; + } + +#if defined(SUPPORT_MEMINFO_IDS) + case PVRSRV_BRIDGE_MAP_DEV_MEMORY: + case PVRSRV_BRIDGE_MAP_DEV_MEMORY_2: + { + PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *psMapDeviceMemoryOUT = + (PVRSRV_BRIDGE_OUT_MAP_DEV_MEMORY *)psBridgePackageKM->pvParamOut; + PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + if (pvr_put_user(psPrivateData->ui64Stamp, &psMapDeviceMemoryOUT->sDstClientMemInfo.ui64Stamp) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } + break; + } + + case PVRSRV_BRIDGE_MAP_DEVICECLASS_MEMORY: + { + PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *psDeviceClassMemoryOUT = + (PVRSRV_BRIDGE_OUT_MAP_DEVICECLASS_MEMORY *)psBridgePackageKM->pvParamOut; + if (pvr_put_user(++ui64Stamp, &psDeviceClassMemoryOUT->sClientMemInfo.ui64Stamp) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } + break; + } +#endif /* defined(SUPPORT_MEMINFO_IDS) */ + + default: + break; + } + +unlock_and_return: + LinuxUnLockMutex(&gPVRSRVLock); + return err; +} diff --git a/pvr-source/services4/srvkm/env/linux/pvr_debug.c b/pvr-source/services4/srvkm/env/linux/pvr_debug.c new file mode 100644 index 0000000..04e42ad --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/pvr_debug.c @@ -0,0 +1,506 @@ +/*************************************************************************/ /*! +@Title Debug Functionality +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description Provides kernel side Debug Functionality +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +#include <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> // strncpy, strlen +#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" + +#if !defined(CONFIG_PREEMPT) +#define PVR_DEBUG_ALWAYS_USE_SPINLOCK +#endif + +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); + +/* NOTE: Must NOT be static! Used in module.c.. */ +IMG_UINT32 gPVRDebugLevel = + (DBGPRIV_FATAL | DBGPRIV_ERROR | DBGPRIV_WARNING); + +#endif /* defined(PVRSRV_NEED_PVR_DPF) || defined(PVRSRV_NEED_PVR_TRACE) */ + +#define PVR_MAX_MSG_LEN PVR_MAX_DEBUG_MESSAGE_LEN + +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) +/* Message buffer for non-IRQ messages */ +static IMG_CHAR gszBufferNonIRQ[PVR_MAX_MSG_LEN + 1]; +#endif + +/* Message buffer for IRQ messages */ +static IMG_CHAR gszBufferIRQ[PVR_MAX_MSG_LEN + 1]; + +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) +/* The lock is used to control access to gszBufferNonIRQ */ +static PVRSRV_LINUX_MUTEX gsDebugMutexNonIRQ; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +/* The lock is used to control access to gszBufferIRQ */ +/* PRQA S 0671,0685 1 */ /* ignore warnings about C99 style initialisation */ +static spinlock_t gsDebugLockIRQ = SPIN_LOCK_UNLOCKED; +#else +static DEFINE_SPINLOCK(gsDebugLockIRQ); +#endif + +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) +#if !defined (USE_SPIN_LOCK) /* to keep QAC happy */ +#define USE_SPIN_LOCK (in_interrupt() || !preemptible()) +#endif +#endif + +static inline void GetBufferLock(unsigned long *pulLockFlags) +{ +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + if (USE_SPIN_LOCK) +#endif + { + spin_lock_irqsave(&gsDebugLockIRQ, *pulLockFlags); + } +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + else + { + LinuxLockMutex(&gsDebugMutexNonIRQ); + } +#endif +} + +static inline void ReleaseBufferLock(unsigned long ulLockFlags) +{ +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + if (USE_SPIN_LOCK) +#endif + { + spin_unlock_irqrestore(&gsDebugLockIRQ, ulLockFlags); + } +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + else + { + LinuxUnLockMutex(&gsDebugMutexNonIRQ); + } +#endif +} + +static inline void SelectBuffer(IMG_CHAR **ppszBuf, IMG_UINT32 *pui32BufSiz) +{ +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + if (USE_SPIN_LOCK) +#endif + { + *ppszBuf = gszBufferIRQ; + *pui32BufSiz = sizeof(gszBufferIRQ); + } +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + else + { + *ppszBuf = gszBufferNonIRQ; + *pui32BufSiz = sizeof(gszBufferNonIRQ); + } +#endif +} + +/* + * Append a string to a buffer using formatted conversion. + * The function takes a variable number of arguments, pointed + * to by the var args list. + */ +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 true if string was truncated */ + return (i32Len < 0 || i32Len >= (IMG_INT32)ui32Space) ? IMG_TRUE : IMG_FALSE; +} + +/* Actually required for ReleasePrintf too */ + +IMG_VOID PVRDPFInit(IMG_VOID) +{ +#if !defined(PVR_DEBUG_ALWAYS_USE_SPINLOCK) + LinuxInitMutex(&gsDebugMutexNonIRQ); +#endif +} + +/*! +****************************************************************************** + @Function PVRSRVReleasePrintf + @Description To output an important message to the user in release builds + @Input pszFormat - The message format string + @Input ... - Zero or more arguments for use by the format string + @Return None + ******************************************************************************/ +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_TRACE) + +/*! +****************************************************************************** + @Function PVRTrace + @Description To output a debug message to the user + @Input pszFormat - The message format string + @Input ... - Zero or more arguments for use by the format string + @Return None + ******************************************************************************/ +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 /* defined(PVRSRV_NEED_PVR_TRACE) */ + +#if defined(PVRSRV_NEED_PVR_DPF) + +/* + * Append a string to a buffer using formatted conversion. + * The function takes a variable number of arguments, calling + * VBAppend to do the actual work. + */ +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; +} + +/*! +****************************************************************************** + @Function PVRSRVDebugPrintf + @Description To output a debug message to the user + @Input uDebugLevel - The current debug level + @Input pszFile - The source file generating the message + @Input uLine - The line of the source file + @Input pszFormat - The message format string + @Input ... - Zero or more arguments for use by the format string + @Return None + ******************************************************************************/ +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); + + /* Add in the level of warning */ + 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 + { + /* Traces don't need a location */ + if (bTrace == IMG_FALSE) + { +#ifdef DEBUG_LOG_PATH_TRUNCATE + /* Buffer for rewriting filepath in log messages */ + static IMG_CHAR szFileNameRewrite[PVR_MAX_FILEPATH_LEN]; + + IMG_CHAR* pszTruncIter; + IMG_CHAR* pszTruncBackInter; + + /* Truncate path (DEBUG_LOG_PATH_TRUNCATE shoud be set to EURASIA env var)*/ + if (strlen(pszFullFileName) > strlen(DEBUG_LOG_PATH_TRUNCATE)+1) + pszFileName = pszFullFileName + strlen(DEBUG_LOG_PATH_TRUNCATE)+1; + + /* Try to find '/../' entries and remove it together with + previous entry. Repeat unit all removed */ + 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; + /* Find '/../' pattern */ + if( + !( ( *pszTruncIter == '/' && (pszTruncIter-4 >= szFileNameRewrite) ) && + ( *(pszTruncIter-1) == '.') && + ( *(pszTruncIter-2) == '.') && + ( *(pszTruncIter-3) == '/') ) + ) continue; + + /* Find previous '/' */ + pszTruncBackInter = pszTruncIter - 3; + while(*(--pszTruncBackInter) != '/') + { + if(pszTruncBackInter <= szFileNameRewrite) break; + } + pszNextStartPoint = pszTruncBackInter; + + /* Remove found region */ + while(*pszTruncIter != 0) + { + *pszTruncBackInter++ = *pszTruncIter++; + } + *pszTruncBackInter = 0; + + /* Start again */ + pszTruncIter = pszNextStartPoint; + } + + pszFileName = szFileNameRewrite; + /* Remove first '/' if exist (it's always relative path */ + if(*pszFileName == '/') pszFileName++; +#endif + +#if !defined(__sh__) + pszLeafName = (IMG_CHAR *)strrchr (pszFileName, '\\'); + + if (pszLeafName) + { + pszFileName = pszLeafName; + } +#endif /* __sh__ */ + + 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 /* PVRSRV_NEED_PVR_DPF */ + +#if defined(DEBUG) + +IMG_INT PVRDebugProcSetLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT32 count, IMG_VOID *data) +{ +#define _PROC_SET_BUFFER_SZ 6 + 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; + if (sscanf(data_buffer, "%i", &gPVRDebugLevel) == 0) + return -EINVAL; + gPVRDebugLevel &= (1 << DBGPRIV_DBGLEVEL_COUNT) - 1; + } + return (count); +} + +void ProcSeqShowDebugLevel(struct seq_file *sfile,void* el) +{ + seq_printf(sfile, "%u\n", gPVRDebugLevel); +} + +#endif /* defined(DEBUG) */ diff --git a/pvr-source/services4/srvkm/env/linux/pvr_uaccess.h b/pvr-source/services4/srvkm/env/linux/pvr_uaccess.h new file mode 100644 index 0000000..7583d7e --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/pvr_uaccess.h @@ -0,0 +1,88 @@ +/*************************************************************************/ /*! +@Title Utility functions for user space access +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __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)) + /* + * The compile time correctness checking introduced for copy_from_user in + * Linux 2.6.33 isn't fully comaptible with our usage of the function. + */ + 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 +} + +#define pvr_put_user put_user +#define pvr_get_user get_user + +#endif /* __PVR_UACCESS_H__ */ + diff --git a/pvr-source/services4/srvkm/env/linux/sysfs.c b/pvr-source/services4/srvkm/env/linux/sysfs.c new file mode 100644 index 0000000..63066ad --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/sysfs.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kobject.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <asm/page.h> +#include <linux/slab.h> +#include "services_headers.h" +#include "pdump_km.h" +#include "sysfs.h" + +/* sysfs structures */ +struct pvrsrv_attribute { + struct attribute attr; + int sgx_version; + int sgx_revision; +}; + +static struct pvrsrv_attribute PVRSRVAttr = { + .attr.name = "egl.cfg", + .attr.mode = S_IRUGO, + .sgx_version = SGXCORE, + .sgx_revision = SGX_CORE_REV, +}; + +/* sysfs read function */ +static ssize_t PVRSRVEglCfgShow(struct kobject *kobj, struct attribute *attr, + char *buffer) { + struct pvrsrv_attribute *pvrsrv_attr; + + pvrsrv_attr = container_of(attr, struct pvrsrv_attribute, attr); + return snprintf(buffer, PAGE_SIZE, "0 0 android\n0 1 POWERVR_SGX%d_%d", + pvrsrv_attr->sgx_version, pvrsrv_attr->sgx_revision); +} + +/* sysfs write function unsupported*/ +static ssize_t PVRSRVEglCfgStore(struct kobject *kobj, struct attribute *attr, + const char *buffer, size_t size) { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVEglCfgStore not implemented")); + return 0; +} + +static struct attribute *pvrsrv_sysfs_attrs[] = { + &PVRSRVAttr.attr, + NULL +}; + +static const struct sysfs_ops pvrsrv_sysfs_ops = { + .show = PVRSRVEglCfgShow, + .store = PVRSRVEglCfgStore, +}; + +static struct kobj_type pvrsrv_ktype = { + .sysfs_ops = &pvrsrv_sysfs_ops, + .default_attrs = pvrsrv_sysfs_attrs, +}; + +/* create sysfs entry /sys/egl/egl.cfg to determine + which gfx libraries to load */ + +int PVRSRVCreateSysfsEntry(void) +{ + struct kobject *egl_cfg_kobject; + int r; + + egl_cfg_kobject = kzalloc(sizeof(*egl_cfg_kobject), GFP_KERNEL); + r = kobject_init_and_add(egl_cfg_kobject, &pvrsrv_ktype, NULL, "egl"); + + if (r) { + PVR_DPF((PVR_DBG_ERROR, + "Failed to create egl.cfg sysfs entry")); + return PVRSRV_ERROR_INIT_FAILURE; + } + + return PVRSRV_OK; +} diff --git a/pvr-source/services4/srvkm/env/linux/sysfs.h b/pvr-source/services4/srvkm/env/linux/sysfs.h new file mode 100644 index 0000000..fb8d20f --- /dev/null +++ b/pvr-source/services4/srvkm/env/linux/sysfs.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __SYSFS_H +#define __SYSFS_H + +int PVRSRVCreateSysfsEntry(void); + +#endif |