diff options
author | Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> | 2012-07-18 00:30:31 -0300 |
---|---|---|
committer | Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> | 2012-07-18 00:30:31 -0300 |
commit | 0f9b9d9e1f16d454b12921d3429eced6dc1095d4 (patch) | |
tree | 21eaffbd85393a9e53889bbd868a255c7f6c24fc /sgx/services4/srvkm/env/linux | |
parent | 50fa520ba5f68fa76173493c44715d4542007120 (diff) |
Imported Upstream version 1.9.0.4.1.1 (ARMHF)upstream/1.9.0.4.1.1
Signed-off-by: Ricardo Salveti de Araujo <ricardo.salveti@linaro.org>
Diffstat (limited to 'sgx/services4/srvkm/env/linux')
28 files changed, 6801 insertions, 2042 deletions
diff --git a/sgx/services4/srvkm/env/linux/Kbuild.mk b/sgx/services4/srvkm/env/linux/Kbuild.mk index 95df225..b101a5f 100755..100644 --- a/sgx/services4/srvkm/env/linux/Kbuild.mk +++ b/sgx/services4/srvkm/env/linux/Kbuild.mk @@ -1,26 +1,43 @@ -# -# Copyright (C) Imagination Technologies Ltd. All rights reserved. +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 # -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. +# The contents of this file are subject to the MIT license as set out below. # -# This program is distributed in the hope it will be useful but, except -# as otherwise stated in writing, without any warranty; without even the -# implied warranty of merchantability or fitness for a particular purpose. -# See the GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # -# The full GNU General Public License is included in this distribution in -# the file called "COPYING". -# -# Contact Information: -# Imagination Technologies Ltd. <gpl-support@imgtec.com> -# Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK +# 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_MODNAME)-y += \ services4/srvkm/env/linux/osfunc.o \ @@ -40,22 +57,36 @@ $(PVRSRV_MODNAME)-y += \ 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/common/power.o \ - services4/srvkm/common/mem.o \ - services4/srvkm/common/pdump_common.o \ services4/srvkm/bridged/bridged_support.o \ services4/srvkm/bridged/bridged_pvr_bridge.o \ - services4/srvkm/common/perproc.o \ services4/system/$(PVR_SYSTEM)/sysconfig.o \ - services4/system/$(PVR_SYSTEM)/sysutils.o \ - services4/srvkm/common/lists.o \ - services4/srvkm/common/mem_debug.o \ - services4/srvkm/common/osfunc_common.o + services4/system/$(PVR_SYSTEM)/sysutils.o + +$(PVRSRV_MODNAME)-$(CONFIG_ION_OMAP) += \ + services4/srvkm/env/linux/ion.o + +ifeq ($(SUPPORT_ION),1) +$(PVRSRV_MODNAME)-y += \ + services4/srvkm/env/linux/ion.o +endif + +ifeq ($(TTRACE),1) +$(PVRSRV_MODNAME)-y += \ + services4/srvkm/common/ttrace.o +endif ifneq ($(W),1) CFLAGS_osfunc.o := -Werror @@ -89,6 +120,7 @@ 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 diff --git a/sgx/services4/srvkm/env/linux/Linux.mk b/sgx/services4/srvkm/env/linux/Linux.mk index 0ea43aa..b5e9de4 100755..100644 --- a/sgx/services4/srvkm/env/linux/Linux.mk +++ b/sgx/services4/srvkm/env/linux/Linux.mk @@ -1,27 +1,43 @@ -# -# Copyright (C) Imagination Technologies Ltd. All rights reserved. +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 # -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. +# The contents of this file are subject to the MIT license as set out below. # -# This program is distributed in the hope it will be useful but, except -# as otherwise stated in writing, without any warranty; without even the -# implied warranty of merchantability or fitness for a particular purpose. -# See the GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # -# The full GNU General Public License is included in this distribution in -# the file called "COPYING". -# -# Contact Information: -# Imagination Technologies Ltd. <gpl-support@imgtec.com> -# Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK +# 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 diff --git a/sgx/services4/srvkm/env/linux/env_data.h b/sgx/services4/srvkm/env/linux/env_data.h index 7716529..4a2b9b1 100644 --- a/sgx/services4/srvkm/env/linux/env_data.h +++ b/sgx/services4/srvkm/env/linux/env_data.h @@ -1,29 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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_ @@ -34,6 +50,11 @@ #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 @@ -61,6 +82,13 @@ typedef struct _ENV_DATA_TAG #else struct tasklet_struct sMISRTasklet; #endif +#if defined (SUPPORT_ION) + IMG_HANDLE hIonHeaps; + IMG_HANDLE hIonDev; +#endif } ENV_DATA; -#endif +#endif /* _ENV_DATA_ */ +/***************************************************************************** + End of file (env_data.h) +*****************************************************************************/ diff --git a/sgx/services4/srvkm/env/linux/env_perproc.h b/sgx/services4/srvkm/env/linux/env_perproc.h index 58e2c38..f434226 100644 --- a/sgx/services4/srvkm/env/linux/env_perproc.h +++ b/sgx/services4/srvkm/env/linux/env_perproc.h @@ -1,29 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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__ @@ -33,6 +49,7 @@ #include "services.h" #include "handle.h" +#define ION_CLIENT_NAME_SIZE 50 typedef struct _PVRSRV_ENV_PER_PROCESS_DATA_ { IMG_HANDLE hBlockAlloc; @@ -47,6 +64,10 @@ typedef struct _PVRSRV_ENV_PER_PROCESS_DATA_ struct drm_device *dev; #endif /* SUPPORT_DRI_DRM_EXTERNAL */ #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); @@ -59,5 +80,8 @@ PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase); IMG_HANDLE LinuxTerminatingProcessPrivateData(IMG_VOID); -#endif +#endif /* __ENV_PERPROC_H__ */ +/****************************************************************************** + End of file (env_perproc.h) +******************************************************************************/ diff --git a/sgx/services4/srvkm/env/linux/event.c b/sgx/services4/srvkm/env/linux/event.c index 7e160c3..1946e61 100644 --- a/sgx/services4/srvkm/env/linux/event.c +++ b/sgx/services4/srvkm/env/linux/event.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -85,6 +101,20 @@ typedef struct PVRSRV_LINUX_EVENT_OBJECT_TAG 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; @@ -106,6 +136,20 @@ PVRSRV_ERROR LinuxEventObjectListCreate(IMG_HANDLE *phEventObjectList) 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) { @@ -126,13 +170,29 @@ PVRSRV_ERROR LinuxEventObjectListDestroy(IMG_HANDLE hEventObjectList) } 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) @@ -155,6 +215,20 @@ PVRSRV_ERROR LinuxEventObjectDelete(IMG_HANDLE hOSEventObjectList, IMG_HANDLE hO } +/*! +****************************************************************************** + + @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; @@ -173,10 +247,25 @@ static PVRSRV_ERROR LinuxEventObjectDeleteCallback(IMG_PVOID pvParam, IMG_UINT32 #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; @@ -192,7 +281,7 @@ PVRSRV_ERROR LinuxEventObjectAdd(IMG_HANDLE hOSEventObjectList, IMG_HANDLE *phOS 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) @@ -228,6 +317,20 @@ PVRSRV_ERROR LinuxEventObjectAdd(IMG_HANDLE hOSEventObjectList, IMG_HANDLE *phOS 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; @@ -236,7 +339,10 @@ PVRSRV_ERROR LinuxEventObjectSignal(IMG_HANDLE hOSEventObjectList) 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) { @@ -252,6 +358,22 @@ PVRSRV_ERROR LinuxEventObjectSignal(IMG_HANDLE hOSEventObjectList) } +/*! +****************************************************************************** + + @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; diff --git a/sgx/services4/srvkm/env/linux/event.h b/sgx/services4/srvkm/env/linux/event.h index 3035283..88adb27 100644 --- a/sgx/services4/srvkm/env/linux/event.h +++ b/sgx/services4/srvkm/env/linux/event.h @@ -1,28 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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); diff --git a/sgx/services4/srvkm/env/linux/linkage.h b/sgx/services4/srvkm/env/linux/linkage.h index e64012c..94aae0b 100644 --- a/sgx/services4/srvkm/env/linux/linkage.h +++ b/sgx/services4/srvkm/env/linux/linkage.h @@ -1,29 +1,47 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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__ @@ -45,8 +63,11 @@ IMG_INT PVRProcSetPowerLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT void ProcSeqShowPowerLevel(struct seq_file *sfile,void* el); -#endif +#endif /* PVR_MANUAL_POWER_CONTROL */ -#endif +#endif /* DEBUG */ -#endif +#endif /* __LINKAGE_H__ */ +/***************************************************************************** + End of file (linkage.h) +*****************************************************************************/ diff --git a/sgx/services4/srvkm/env/linux/lock.h b/sgx/services4/srvkm/env/linux/lock.h index a0854c3..d6d5b50 100644 --- a/sgx/services4/srvkm/env/linux/lock.h +++ b/sgx/services4/srvkm/env/linux/lock.h @@ -1,32 +1,57 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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 +#endif /* __LOCK_H__ */ +/***************************************************************************** + End of file (lock.h) +*****************************************************************************/ diff --git a/sgx/services4/srvkm/env/linux/mm.c b/sgx/services4/srvkm/env/linux/mm.c index 3edbc49..dc2b471 100644 --- a/sgx/services4/srvkm/env/linux/mm.c +++ b/sgx/services4/srvkm/env/linux/mm.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -32,6 +48,14 @@ #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> @@ -42,6 +66,12 @@ #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" @@ -60,6 +90,13 @@ #include "lists.h" #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, @@ -68,13 +105,17 @@ typedef enum { 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; +} DEBUG_MEM_ALLOC_TYPE; typedef struct _DEBUG_MEM_ALLOC_REC { DEBUG_MEM_ALLOC_TYPE eAllocType; - IMG_VOID *pvKey; + IMG_VOID *pvKey; /* Some unique value (private to the eAllocType) */ IMG_VOID *pvCpuVAddr; IMG_UINT32 ulCpuPAddr; IMG_VOID *pvPrivateData; @@ -85,7 +126,7 @@ typedef struct _DEBUG_MEM_ALLOC_REC struct _DEBUG_MEM_ALLOC_REC *psNext; struct _DEBUG_MEM_ALLOC_REC **ppsThis; -}DEBUG_MEM_ALLOC_REC; +} 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) @@ -99,9 +140,17 @@ static DEBUG_MEM_ALLOC_REC *g_MemoryRecords; static IMG_UINT32 g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; static IMG_UINT32 g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; -static IMG_UINT32 g_SysRAMWaterMark; -static IMG_UINT32 g_SysRAMHighWaterMark; +/* 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; @@ -119,7 +168,7 @@ static IMG_VOID DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_V static IMG_CHAR *DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType); -static struct proc_dir_entry *g_SeqFileMemoryRecords =0; +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); @@ -153,7 +202,7 @@ static IMG_UINT32 g_LinuxMemAreaWaterMark; static IMG_UINT32 g_LinuxMemAreaHighWaterMark; -static struct proc_dir_entry *g_SeqFileMemArea=0; +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); @@ -169,8 +218,19 @@ static PVRSRV_LINUX_MUTEX g_sDebugMutex; static void ProcSeqStartstopDebugMutex(struct seq_file *sfile,IMG_BOOL start); #endif -static LinuxKMemCache *psLinuxMemAreaCache; +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); @@ -185,165 +245,26 @@ static DEBUG_LINUX_MEM_AREA_REC *DebugLinuxMemAreaRecordFind(LinuxMemArea *psLin static IMG_VOID DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea); #endif -PVRSRV_ERROR -LinuxMMInit(IMG_VOID) -{ -#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - LinuxInitMutex(&g_sDebugMutex); -#endif - -#if defined(DEBUG_LINUX_MEM_AREAS) - { - g_SeqFileMemArea = CreateProcReadEntrySeq( - "mem_areas", - NULL, - ProcSeqNextMemArea, - ProcSeqShowMemArea, - ProcSeqOff2ElementMemArea, - ProcSeqStartstopDebugMutex - ); - if(!g_SeqFileMemArea) - { - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - } -#endif - - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - { - g_SeqFileMemoryRecords =CreateProcReadEntrySeq( - "meminfo", - NULL, - ProcSeqNextMemoryRecords, - ProcSeqShowMemoryRecords, - ProcSeqOff2ElementMemoryRecords, - ProcSeqStartstopDebugMutex - ); - if(!g_SeqFileMemoryRecords) - { - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - } -#endif - - psLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); - if(!psLinuxMemAreaCache) - { - PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - - return PVRSRV_OK; -} - -#if defined(DEBUG_LINUX_MEM_AREAS) -static IMG_VOID LinuxMMCleanup_MemAreas_ForEachCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord) -{ - LinuxMemArea *psLinuxMemArea; - - psLinuxMemArea = psCurrentRecord->psLinuxMemArea; - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up Linux memory area (%p), type=%s, size=%d bytes", - __FUNCTION__, - psCurrentRecord->psLinuxMemArea, - LinuxMemAreaTypeToString(psCurrentRecord->psLinuxMemArea->eAreaType), - psCurrentRecord->psLinuxMemArea->ui32ByteSize)); - - LinuxMemAreaDeepFree(psLinuxMemArea); -} -#endif - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) -static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord) +static inline IMG_BOOL +AreaIsUncached(IMG_UINT32 ui32AreaFlags) { - - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up memory: " - "type=%s " - "CpuVAddr=%p " - "CpuPAddr=0x%08x, " - "allocated @ file=%s,line=%d", - __FUNCTION__, - DebugMemAllocRecordTypeToString(psCurrentRecord->eAllocType), - psCurrentRecord->pvCpuVAddr, - psCurrentRecord->ulCpuPAddr, - psCurrentRecord->pszFileName, - psCurrentRecord->ui32Line)); - switch(psCurrentRecord->eAllocType) - { - case DEBUG_MEM_ALLOC_TYPE_KMALLOC: - KFreeWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_IOREMAP: - IOUnmapWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_IO: - - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, psCurrentRecord->pvKey, __FILE__, __LINE__); - break; - case DEBUG_MEM_ALLOC_TYPE_VMALLOC: - VFreeWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES: - - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, psCurrentRecord->pvKey, __FILE__, __LINE__); - break; - case DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE: - KMemCacheFreeWrapper(psCurrentRecord->pvPrivateData, psCurrentRecord->pvCpuVAddr); - break; - default: - PVR_ASSERT(0); - } + return (ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) != 0; } -#endif - -IMG_VOID -LinuxMMCleanup(IMG_VOID) +static inline IMG_BOOL +CanFreeToPool(LinuxMemArea *psLinuxMemArea) { - -#if defined(DEBUG_LINUX_MEM_AREAS) - { - if(g_LinuxMemAreaCount) - { - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: There are %d LinuxMemArea allocation unfreed (%d bytes)", - __FUNCTION__, g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark)); - } - - List_DEBUG_LINUX_MEM_AREA_REC_ForEach(g_LinuxMemAreaRecords, - LinuxMMCleanup_MemAreas_ForEachCb); - - RemoveProcEntrySeq( g_SeqFileMemArea ); - } -#endif - - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - { - - - List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, - LinuxMMCleanup_MemRecords_ForEachVa); - - RemoveProcEntrySeq( g_SeqFileMemoryRecords ); - } -#endif - - if(psLinuxMemAreaCache) - { - KMemCacheDestroyWrapper(psLinuxMemAreaCache); - psLinuxMemAreaCache=NULL; - } + 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) + if (pvRet) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMALLOC, pvRet, @@ -353,7 +274,7 @@ _KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *pszFileName, IM ui32ByteSize, pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -406,27 +327,31 @@ DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType, List_DEBUG_MEM_ALLOC_REC_Insert(&g_MemoryRecords, psRecord); g_WaterMarkData[eAllocType] += ui32Bytes; - if(g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType]) + if (g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType]) { g_HighWaterMarkData[eAllocType] = g_WaterMarkData[eAllocType]; } - if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + 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; - if(g_SysRAMWaterMark > g_SysRAMHighWaterMark) + ui32SysRAMTrueWaterMark = SysRAMTrueWaterMark(); + + if (ui32SysRAMTrueWaterMark > g_SysRAMHighWaterMark) { - g_SysRAMHighWaterMark = g_SysRAMWaterMark; + g_SysRAMHighWaterMark = ui32SysRAMTrueWaterMark; } } - else if(eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP + else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) { g_IOMemWaterMark += ui32Bytes; - if(g_IOMemWaterMark > g_IOMemHighWaterMark) + if (g_IOMemWaterMark > g_IOMemHighWaterMark) { g_IOMemHighWaterMark = g_IOMemWaterMark; } @@ -444,20 +369,20 @@ static IMG_BOOL DebugMemAllocRecordRemove_AnyVaCb(DEBUG_MEM_ALLOC_REC *psCurrent eAllocType = va_arg(va, DEBUG_MEM_ALLOC_TYPE); pvKey = va_arg(va, IMG_VOID*); - if(psCurrentRecord->eAllocType == eAllocType + if (psCurrentRecord->eAllocType == eAllocType && psCurrentRecord->pvKey == pvKey) { eAllocType = psCurrentRecord->eAllocType; g_WaterMarkData[eAllocType] -= psCurrentRecord->ui32Bytes; - if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + 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 + else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) { g_IOMemWaterMark -= psCurrentRecord->ui32Bytes; @@ -478,10 +403,12 @@ static IMG_BOOL DebugMemAllocRecordRemove_AnyVaCb(DEBUG_MEM_ALLOC_REC *psCurrent 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); - - if(!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords, + /* Locate the corresponding allocation entry */ + if (!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords, DebugMemAllocRecordRemove_AnyVaCb, eAllocType, pvKey)) @@ -504,27 +431,24 @@ DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType) "ALLOC_PAGES", "IOREMAP", "IO", - "KMEM_CACHE_ALLOC" + "KMEM_CACHE_ALLOC", +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + "VMAP" +#endif }; return apszDebugMemoryRecordTypes[eAllocType]; } #endif - -IMG_VOID * -_VMallocWrapper(IMG_UINT32 ui32Bytes, - IMG_UINT32 ui32AllocFlags, - IMG_CHAR *pszFileName, - IMG_UINT32 ui32Line) +static IMG_BOOL +AllocFlagsToPGProt(pgprot_t *pPGProtFlags, IMG_UINT32 ui32AllocFlags) { pgprot_t PGProtFlags; - IMG_VOID *pvRet; - - switch(ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) + + switch (ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: - case PVRSRV_HAP_SMART: PGProtFlags = PAGE_KERNEL; break; case PVRSRV_HAP_WRITECOMBINE: @@ -535,17 +459,36 @@ _VMallocWrapper(IMG_UINT32 ui32Bytes, break; default: PVR_DPF((PVR_DBG_ERROR, - "VMAllocWrapper: unknown mapping flags=0x%08x", - ui32AllocFlags)); + "%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) + if (pvRet) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMALLOC, pvRet, @@ -555,7 +498,7 @@ _VMallocWrapper(IMG_UINT32 ui32Bytes, PAGE_ALIGN(ui32Bytes), pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -579,31 +522,481 @@ _VFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) } +#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_TRACE(("%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_TRACE(("%s: Number to scan: %ld", __FUNCTION__, uNumToScan)); + PVR_TRACE(("%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_TRACE(("%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; + 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) + 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) + 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); @@ -612,16 +1005,41 @@ NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); #endif - - if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) - OSInvalidateCPUCacheRangeKM(psLinuxMemArea, pvCpuVAddr, ui32Bytes); + /* 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(psLinuxMemArea) +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + if (ppsPageList) + { + FreePages(bFromPagePool, ppsPageList, hBlockPageList, ui32NumPages); + } +#endif + if (psLinuxMemArea) + { LinuxMemAreaStructFree(psLinuxMemArea); + } + return NULL; } @@ -629,6 +1047,12 @@ failed: 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); @@ -637,20 +1061,34 @@ FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) 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, + UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, psLinuxMemArea->ui32ByteSize); #endif - PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", - __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); +#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) { @@ -668,6 +1106,7 @@ ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length) } +/* Un-reserve pages of memory in order that they can be freed. */ static IMG_VOID UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length) { @@ -683,7 +1122,7 @@ UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length) #endif } } -#endif +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) */ IMG_VOID * @@ -695,10 +1134,9 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, { IMG_VOID *pvIORemapCookie; - switch(ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK) + switch (ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: - case PVRSRV_HAP_SMART: pvIORemapCookie = (IMG_VOID *)IOREMAP(BasePAddr.uiAddr, ui32Bytes); break; case PVRSRV_HAP_WRITECOMBINE: @@ -713,7 +1151,7 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, } #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - if(pvIORemapCookie) + if (pvIORemapCookie) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IOREMAP, pvIORemapCookie, @@ -723,7 +1161,7 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, ui32Bytes, pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -756,13 +1194,13 @@ NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_VOID *pvIORemapCookie; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32AreaFlags); - if(!pvIORemapCookie) + if (!pvIORemapCookie) { LinuxMemAreaStructFree(psLinuxMemArea); return NULL; @@ -799,6 +1237,16 @@ FreeIORemapLinuxMemArea(LinuxMemArea *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) { @@ -806,7 +1254,10 @@ TreatExternalPagesAsContiguous(IMG_SYS_PHYADDR *psSysPhysAddr, IMG_UINT32 ui32By 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) @@ -843,7 +1294,7 @@ LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *p LinuxMemArea *psLinuxMemArea; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } @@ -896,12 +1347,12 @@ NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32AreaFlags) { LinuxMemArea *psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + 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; @@ -917,7 +1368,7 @@ NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, ui32Bytes, "unknown", 0 - ); + ); #endif #if defined(DEBUG_LINUX_MEM_AREAS) @@ -942,7 +1393,7 @@ FreeIOLinuxMemArea(LinuxMemArea *psLinuxMemArea) (IMG_VOID *)psLinuxMemArea->uData.sIO.CPUPhysAddr.uiAddr, __FILE__, __LINE__); #endif - + /* Nothing more to do than free the LinuxMemArea struct */ LinuxMemAreaStructFree(psLinuxMemArea); } @@ -952,68 +1403,33 @@ LinuxMemArea * NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) { LinuxMemArea *psLinuxMemArea; - IMG_UINT32 ui32PageCount; - struct page **pvPageList; + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; IMG_HANDLE hBlockPageList; - IMG_INT32 i; - PVRSRV_ERROR eError; - + IMG_BOOL bFromPagePool; + psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { goto failed_area_alloc; } - ui32PageCount = RANGE_TO_PAGES(ui32Bytes); - eError = OSAllocMem(0, sizeof(*pvPageList) * ui32PageCount, (IMG_VOID **)&pvPageList, &hBlockPageList, - "Array of pages"); - if(eError != PVRSRV_OK) - { - goto failed_page_list_alloc; - } - - for(i=0; i<(IMG_INT32)ui32PageCount; i++) - { - pvPageList[i] = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); - if(!pvPageList[i]) - { - goto failed_alloc_pages; - } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) - SetPageReserved(pvPageList[i]); -#else - mem_map_reserve(pvPageList[i]); -#endif -#endif + ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool)) + { + goto failed_alloc_pages; } -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, - pvPageList, - 0, - 0, - NULL, - PAGE_ALIGN(ui32Bytes), - "unknown", - 0 - ); -#endif - psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES; - psLinuxMemArea->uData.sPageList.pvPageList = pvPageList; + psLinuxMemArea->uData.sPageList.ppsPageList = ppsPageList; psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList; psLinuxMemArea->ui32ByteSize = ui32Bytes; psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); - - if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) - { - psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; - } + /* 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); @@ -1022,13 +1438,6 @@ NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) return psLinuxMemArea; failed_alloc_pages: - for(i--; i >= 0; i--) - { - __free_pages(pvPageList[i], 0); - } - (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); - psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; -failed_page_list_alloc: LinuxMemAreaStructFree(psLinuxMemArea); failed_area_alloc: PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__)); @@ -1040,10 +1449,9 @@ failed_area_alloc: IMG_VOID FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) { - IMG_UINT32 ui32PageCount; - struct page **pvPageList; + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; IMG_HANDLE hBlockPageList; - IMG_INT32 i; PVR_ASSERT(psLinuxMemArea); PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES); @@ -1052,38 +1460,162 @@ FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) DebugLinuxMemAreaRecordRemove(psLinuxMemArea); #endif - ui32PageCount = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); - pvPageList = psLinuxMemArea->uData.sPageList.pvPageList; + ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + ppsPageList = psLinuxMemArea->uData.sPageList.ppsPageList; hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList; -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, pvPageList, __FILE__, __LINE__); -#endif + FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages); + + LinuxMemAreaStructFree(psLinuxMemArea); +} + +#if defined(CONFIG_ION_OMAP) + +#include "env_perproc.h" - for(i=0;i<(IMG_INT32)ui32PageCount;i++) +#include <linux/ion.h> +#include <linux/omap_ion.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[2] = {}; + u32 *pu32PageAddrs[2] = { NULL, NULL }; + IMG_UINT32 i, ui32NumHandlesPerFd; + IMG_BYTE *pbPrivData = pvPrivData; + IMG_CPU_PHYADDR *pCPUPhysAddrs; + int iNumPages[2] = { 0, 0 }; + LinuxMemArea *psLinuxMemArea; + + psLinuxMemArea = LinuxMemAreaStructAlloc(); + if (!psLinuxMemArea) { -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) - ClearPageReserved(pvPageList[i]); -#else - mem_map_reserve(pvPageList[i]); -#endif -#endif - if (psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_SMART) + 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 or two 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); + ui32NumHandlesPerFd = ui32PrivDataLength / ui32AllocDataLen; + + /* Shuffle the alloc data into separate Y & UV bits and + * make two separate allocations via the tiler. + */ + for(i = 0; i < ui32NumHandlesPerFd; i++) + { + memcpy(&asAllocData[i], &pbPrivData[i * ui32AllocDataLen], ui32AllocDataLen); + + 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) { - // XXX some race here.. - clear_bit(PG_private, &pvPageList[i]->flags); - clear_bit(PG_private_2, &pvPageList[i]->flags); + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", __func__)); + goto err_free; } - __free_pages(pvPageList[i], 0); } - (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); - psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; + /* Assume the user-allocator has already done the tiler math and that the + * number of tiler pages allocated matches any other allocation type. + */ + BUG_ON(ui32Bytes != (iNumPages[0] + iNumPages[1]) * PAGE_SIZE); + BUG_ON(sizeof(IMG_CPU_PHYADDR) != sizeof(int)); + + /* Glue the page lists together */ + pCPUPhysAddrs = vmalloc(sizeof(IMG_CPU_PHYADDR) * (iNumPages[0] + iNumPages[1])); + if (!pCPUPhysAddrs) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate page list", __func__)); + goto err_free; + } + for(i = 0; i < iNumPages[0]; i++) + pCPUPhysAddrs[i].uiAddr = pu32PageAddrs[0][i]; + for(i = 0; i < iNumPages[1]; i++) + pCPUPhysAddrs[iNumPages[0] + i].uiAddr = pu32PageAddrs[1][i]; + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ION, + asAllocData[0].handle, + 0, + 0, + NULL, + PAGE_ALIGN(ui32Bytes), + "unknown", + 0 + ); +#endif + + for(i = 0; i < 2; i++) + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = asAllocData[i].handle; + + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ION; + psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = pCPUPhysAddrs; + 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); + +#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_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 < 2; 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, @@ -1092,11 +1624,11 @@ LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32PageIndex; IMG_CHAR *pui8Addr; - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_ALLOC_PAGES: ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); - return psLinuxMemArea->uData.sPageList.pvPageList[ui32PageIndex]; + return psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex]; case LINUX_MEM_AREA_VMALLOC: pui8Addr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; @@ -1104,7 +1636,7 @@ LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, 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); @@ -1129,8 +1661,8 @@ KMemCacheCreateWrapper(IMG_CHAR *pszName, return kmem_cache_create(pszName, Size, Align, ui32Flags, NULL #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) , NULL -#endif - ); +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) */ + ); } @@ -1164,7 +1696,7 @@ _KMemCacheAllocWrapper(LinuxKMemCache *psCache, kmem_cache_size(psCache), pszFileName, ui32Line - ); + ); #else PVR_UNREFERENCED_PARAMETER(pszFileName); PVR_UNREFERENCED_PARAMETER(ui32Line); @@ -1174,30 +1706,6 @@ _KMemCacheAllocWrapper(LinuxKMemCache *psCache, } -IMG_VOID -_KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) -{ -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, pvObject, pszFileName, ui32Line); -#else - PVR_UNREFERENCED_PARAMETER(pszFileName); - PVR_UNREFERENCED_PARAMETER(ui32Line); -#endif - - kmem_cache_free(psCache, pvObject); -} - - -const IMG_CHAR * -KMemCacheNameWrapper(LinuxKMemCache *psCache) -{ - PVR_UNREFERENCED_PARAMETER(psCache); - - - return ""; -} - - LinuxMemArea * NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, IMG_UINT32 ui32ByteOffset, @@ -1208,7 +1716,7 @@ NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, PVR_ASSERT((ui32ByteOffset+ui32Bytes) <= psParentLinuxMemArea->ui32ByteSize); psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } @@ -1242,7 +1750,7 @@ FreeSubLinuxMemArea(LinuxMemArea *psLinuxMemArea) DebugLinuxMemAreaRecordRemove(psLinuxMemArea); #endif - + /* Nothing more to do than free the LinuxMemArea structure */ LinuxMemAreaStructFree(psLinuxMemArea); } @@ -1251,14 +1759,15 @@ FreeSubLinuxMemArea(LinuxMemArea *psLinuxMemArea) static LinuxMemArea * LinuxMemAreaStructAlloc(IMG_VOID) { +/* debug */ #if 0 LinuxMemArea *psLinuxMemArea; - psLinuxMemArea = kmem_cache_alloc(psLinuxMemAreaCache, GFP_KERNEL); + psLinuxMemArea = kmem_cache_alloc(g_PsLinuxMemAreaCache, GFP_KERNEL); printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea); dump_stack(); return psLinuxMemArea; #else - return KMemCacheAllocWrapper(psLinuxMemAreaCache, GFP_KERNEL); + return KMemCacheAllocWrapper(g_PsLinuxMemAreaCache, GFP_KERNEL); #endif } @@ -1274,16 +1783,16 @@ LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) if (psLinuxMemArea->buf) drm_gem_object_unreference_unlocked(psLinuxMemArea->buf); #endif /* SUPPORT_DRI_DRM_EXTERNAL */ - KMemCacheFreeWrapper(psLinuxMemAreaCache, psLinuxMemArea); - - + KMemCacheFreeWrapper(g_PsLinuxMemAreaCache, psLinuxMemArea); + /* debug */ + //printk(KERN_ERR "%s(%p)\n", __FUNCTION__, psLinuxMemArea); } IMG_VOID LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea) { - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_VMALLOC: FreeVMallocLinuxMemArea(psLinuxMemArea); @@ -1294,15 +1803,18 @@ LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea) case LINUX_MEM_AREA_IOREMAP: FreeIORemapLinuxMemArea(psLinuxMemArea); break; - case LINUX_MEM_AREA_EXTERNAL_KV: - FreeExternalKVLinuxMemArea(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)); @@ -1320,21 +1832,21 @@ DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) LinuxLockMutex(&g_sDebugMutex); - if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) { g_LinuxMemAreaWaterMark += psLinuxMemArea->ui32ByteSize; - if(g_LinuxMemAreaWaterMark > g_LinuxMemAreaHighWaterMark) + 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) + if (psNewRecord) { - + /* Record the allocation */ psNewRecord->psLinuxMemArea = psLinuxMemArea; psNewRecord->ui32Flags = ui32Flags; psNewRecord->pid = OSGetCurrentProcessIDKM(); @@ -1348,16 +1860,16 @@ DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) __FUNCTION__)); } - + /* Sanity check the flags */ pi8FlagsString = HAPFlagsToString(ui32Flags); - if(strstr(pi8FlagsString, "UNKNOWN")) + 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); @@ -1371,7 +1883,7 @@ static IMG_VOID* MatchLinuxMemArea_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRe LinuxMemArea *psLinuxMemArea; psLinuxMemArea = va_arg(va, LinuxMemArea*); - if(psCurrentRecord->psLinuxMemArea == psLinuxMemArea) + if (psCurrentRecord->psLinuxMemArea == psLinuxMemArea) { return psCurrentRecord; } @@ -1392,6 +1904,7 @@ DebugLinuxMemAreaRecordFind(LinuxMemArea *psLinuxMemArea) MatchLinuxMemArea_AnyVaCb, psLinuxMemArea); +/*exit_unlock:*/ LinuxUnLockMutex(&g_sDebugMutex); return psCurrentRecord; @@ -1405,19 +1918,19 @@ DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea) LinuxLockMutex(&g_sDebugMutex); - if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + 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) + if (psCurrentRecord) { - + /* Unlink the allocation record */ List_DEBUG_LINUX_MEM_AREA_REC_Remove(psCurrentRecord); kfree(psCurrentRecord); } @@ -1439,7 +1952,7 @@ LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea) { return NULL; } - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_VMALLOC: return psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; @@ -1450,8 +1963,8 @@ LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea) case LINUX_MEM_AREA_SUB_ALLOC: { IMG_CHAR *pAddr = - LinuxMemAreaToCpuVAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); - if(!pAddr) + LinuxMemAreaToCpuVAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); /* PRQA S 3670 */ /* ignore recursive warning */ + if (!pAddr) { return NULL; } @@ -1473,7 +1986,7 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) return CpuPAddr; } - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_IOREMAP: { @@ -1513,11 +2026,18 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 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.pvPageList[ui32PageIndex]; + page = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex]; CpuPAddr.uiAddr = page_to_phys(page); CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset); break; @@ -1546,22 +2066,23 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) IMG_BOOL LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea) { - switch(psLinuxMemArea->eAreaType) + 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_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; + return IMG_FALSE; case LINUX_MEM_AREA_SUB_ALLOC: - - return LinuxMemAreaPhysIsContig(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); + /* 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", @@ -1575,8 +2096,10 @@ LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea) const IMG_CHAR * LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) { - - switch(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"; @@ -1590,6 +2113,8 @@ LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) 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); } @@ -1601,7 +2126,7 @@ LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) #if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) static void ProcSeqStartstopDebugMutex(struct seq_file *sfile, IMG_BOOL start) { - if(start) + if (start) { LinuxLockMutex(&g_sDebugMutex); } @@ -1610,7 +2135,7 @@ static void ProcSeqStartstopDebugMutex(struct seq_file *sfile, IMG_BOOL start) LinuxUnLockMutex(&g_sDebugMutex); } } -#endif +#endif /* defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) */ #if defined(DEBUG_LINUX_MEM_AREAS) @@ -1627,7 +2152,7 @@ static IMG_VOID* DecOffMemAreaRec_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psNode, va_l } } - +/* 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; @@ -1641,7 +2166,7 @@ static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off) static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off) { DEBUG_LINUX_MEM_AREA_REC *psRecord; - if(!off) + if (!off) { return PVR_PROC_SEQ_START_TOKEN; } @@ -1657,11 +2182,11 @@ static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off) 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 (el == PVR_PROC_SEQ_START_TOKEN) { #if !defined(DEBUG_LINUX_XML_PROC_FILES) - seq_printf( sfile, + 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" @@ -1677,23 +2202,23 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) "Bytes", "Pid", "Flags" - ); + ); #else - seq_printf( sfile, + seq_printf(sfile, "<mem_areas_header>\n" "\t<count>%u</count>\n" - "\t<watermark key=\"mar0\" description=\"current\" bytes=\"%u\"/>\n" - "\t<watermark key=\"mar1\" description=\"high\" bytes=\"%u\"/>\n" + "\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, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%8p %-24s %8p %08x %-8d %-5u %08x=(%s)\n", #else @@ -1702,9 +2227,9 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) "\t<type>%s</type>\n" "\t<cpu_virtual>%8p</cpu_virtual>\n" "\t<cpu_physical>%08x</cpu_physical>\n" - "\t<bytes>%ld</bytes>\n" + "\t<bytes>%d</bytes>\n" "\t<pid>%u</pid>\n" - "\t<flags>%08lx</flags>\n" + "\t<flags>%08x</flags>\n" "\t<flags_string>%s</flags_string>\n" "</linux_mem_area>\n", #endif @@ -1716,11 +2241,11 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) psRecord->pid, psRecord->ui32Flags, HAPFlagsToString(psRecord->ui32Flags) - ); + ); } -#endif +#endif /* DEBUG_LINUX_MEM_AREAS */ #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) @@ -1739,7 +2264,7 @@ static IMG_VOID* DecOffMemAllocRec_AnyVaCb(DEBUG_MEM_ALLOC_REC *psNode, va_list } - +/* 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; @@ -1748,9 +2273,9 @@ static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off DecOffMemAllocRec_AnyVaCb, &off); #if defined(DEBUG_LINUX_XML_PROC_FILES) - if(!psRecord) + if (!psRecord) { - seq_printf( sfile, "</meminfo>\n"); + seq_printf(sfile, "</meminfo>\n"); } #endif @@ -1760,7 +2285,7 @@ static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) { DEBUG_MEM_ALLOC_REC *psRecord; - if(!off) + if (!off) { return PVR_PROC_SEQ_START_TOKEN; } @@ -1771,9 +2296,9 @@ static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) &off); #if defined(DEBUG_LINUX_XML_PROC_FILES) - if(!psRecord) + if (!psRecord) { - seq_printf( sfile, "</meminfo>\n"); + seq_printf(sfile, "</meminfo>\n"); } #endif @@ -1783,64 +2308,78 @@ static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) 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 (el == PVR_PROC_SEQ_START_TOKEN) { #if !defined(DEBUG_LINUX_XML_PROC_FILES) - - seq_printf( sfile, "%-60s: %d bytes\n", + /* 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", + 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", + 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", + 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", + 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", + 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", + 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", + 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", + 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", + 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", + 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", + 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", + seq_printf(sfile, "%-60s: %d bytes\n", "The Current Water Mark for memory allocated from system RAM", - g_SysRAMWaterMark); - seq_printf( sfile, "%-60s: %d bytes\n", + 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", + 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", + 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" + seq_printf(sfile, "Details for all known allocations:\n" "%-16s %-8s %-8s %-10s %-5s %-10s %s\n", "Type", "CpuVAddr", @@ -1850,70 +2389,84 @@ static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) "PrivateData", "Filename:Line"); -#else - +#else /* DEBUG_LINUX_XML_PROC_FILES */ - seq_printf( sfile, "<meminfo>\n<meminfo_header>\n"); - seq_printf( sfile, + /* 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, + seq_printf(sfile, "<watermark key=\"mr1\" description=\"kmalloc_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr2\" description=\"vmalloc_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr3\" description=\"vmalloc_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr4\" description=\"alloc_pages_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr5\" description=\"alloc_pages_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr6\" description=\"ioremap_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr7\" description=\"ioremap_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr8\" description=\"io_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr9\" description=\"io_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr10\" description=\"kmem_cache_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr11\" description=\"kmem_cache_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); - seq_printf( sfile,"\n" ); - - seq_printf( sfile, +#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", - g_SysRAMWaterMark); - seq_printf( sfile, + SysRAMTrueWaterMark()); + seq_printf(sfile, "<watermark key=\"mr15\" description=\"system_ram_high\" bytes=\"%d\"/>\n", g_SysRAMHighWaterMark); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr16\" description=\"system_io_current\" bytes=\"%d\"/>\n", g_IOMemWaterMark); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr17\" description=\"system_io_high\" bytes=\"%d\"/>\n", g_IOMemHighWaterMark); - seq_printf( sfile, "</meminfo_header>\n"); +#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 +#endif /* DEBUG_LINUX_XML_PROC_FILES */ return; } - if(psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) + if (psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) { - seq_printf( sfile, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", #else @@ -1939,7 +2492,7 @@ static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) } else { - seq_printf( sfile, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", #else @@ -1965,10 +2518,11 @@ static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) } } -#endif +#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) { @@ -1978,7 +2532,6 @@ HAPFlagsToString(IMG_UINT32 ui32Flags) IMG_CHAR *apszCacheTypes[] = { "UNCACHED", "CACHED", - "SMART", "WRITECOMBINE", "UNKNOWN" }; @@ -1991,33 +2544,35 @@ HAPFlagsToString(IMG_UINT32 ui32Flags) "UNKNOWN" }; - - if(ui32Flags & PVRSRV_HAP_UNCACHED){ - ui32CacheTypeIndex=0; - }else if(ui32Flags & PVRSRV_HAP_CACHED){ - ui32CacheTypeIndex=1; - }else if(ui32Flags & PVRSRV_HAP_SMART){ - ui32CacheTypeIndex=2; - }else if(ui32Flags & PVRSRV_HAP_WRITECOMBINE){ - ui32CacheTypeIndex=3; - }else{ - ui32CacheTypeIndex=4; + /* 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))); } - - if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY){ + /* 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){ + } else if (ui32Flags & PVRSRV_HAP_SINGLE_PROCESS) { ui32MapTypeIndex = 1; - }else if(ui32Flags & PVRSRV_HAP_MULTI_PROCESS){ + } else if (ui32Flags & PVRSRV_HAP_MULTI_PROCESS) { ui32MapTypeIndex = 2; - }else if(ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS){ + } else if (ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS) { ui32MapTypeIndex = 3; - }else if(ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL){ + } else if (ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL) { ui32MapTypeIndex = 4; - }else{ + } else { ui32MapTypeIndex = 5; PVR_DPF((PVR_DBG_ERROR, "%s: unknown map type (%u)", __FUNCTION__, (ui32Flags & PVRSRV_HAP_MAPTYPE_MASK))); @@ -2039,3 +2594,222 @@ HAPFlagsToString(IMG_UINT32 ui32Flags) } #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/sgx/services4/srvkm/env/linux/mm.h b/sgx/services4/srvkm/env/linux/mm.h index da49083..2bcf408 100644 --- a/sgx/services4/srvkm/env/linux/mm.h +++ b/sgx/services4/srvkm/env/linux/mm.h @@ -1,29 +1,46 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ - +/*************************************************************************/ /*! +@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__ @@ -48,6 +65,8 @@ #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 @@ -78,33 +97,43 @@ static inline IMG_UINT32 VMallocToPhys(IMG_VOID *pCpuVAddr) typedef enum { LINUX_MEM_AREA_IOREMAP, - LINUX_MEM_AREA_EXTERNAL_KV, + 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; @@ -112,44 +141,62 @@ struct _LinuxMemArea { }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 { - - struct page **pvPageList; + /* 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; + struct ion_handle *psIONHandle[2]; + }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; + IMG_UINT32 ui32ByteSize; /* Size of memory area */ + + IMG_UINT32 ui32AreaFlags; /* Flags passed at creation time */ - IMG_UINT32 ui32AreaFlags; + IMG_BOOL bMMapRegistered; /* Registered with mmap code */ - IMG_BOOL bMMapRegistered; + IMG_BOOL bNeedsCacheInvalidate; /* Cache should be invalidated on first map? */ - IMG_BOOL bNeedsCacheInvalidate; + 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; - IMG_HANDLE hSmartCache; - #if defined(SUPPORT_DRI_DRM_EXTERNAL) IMG_HANDLE buf; /* external buffer handle, like a GEM or ION buffer */ #endif /* SUPPORT_DRI_DRM_EXTERNAL */ @@ -162,12 +209,45 @@ 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 @@ -176,6 +256,14 @@ IMG_VOID LinuxMMCleanup(IMG_VOID); 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 @@ -184,6 +272,15 @@ IMG_VOID *_KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *szFil 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 @@ -192,6 +289,14 @@ IMG_VOID _KFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 u 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 @@ -200,12 +305,38 @@ IMG_VOID *_VMallocWrapper(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AllocFlags, IMG_C 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__) @@ -220,17 +351,63 @@ IMG_VOID *_IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, 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__) @@ -241,15 +418,52 @@ IMG_VOID FreeExternalKVLinuxMemArea(LinuxMemArea *psLinuxMemArea); 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 @@ -262,6 +476,15 @@ IMG_VOID *_KMemCacheAllocWrapper(LinuxKMemCache *psCache, gfp_t Flags, IMG_CHAR 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 @@ -270,29 +493,146 @@ IMG_VOID *_KMemCacheAllocWrapper(LinuxKMemCache *psCache, int Flags, IMG_CHAR *p 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); + +#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(); +} + +#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 @@ -300,16 +640,49 @@ IMG_VOID LinuxMemAreaRegister(LinuxMemArea *psLinuxMemArea); #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) { @@ -324,6 +697,14 @@ LinuxMemAreaRoot(LinuxMemArea *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) { @@ -331,12 +712,28 @@ LinuxMemAreaRootType(LinuxMemArea *psLinuxMemArea) } +/*! + ******************************************************************************* + * @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_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MMAP_AREAS) const IMG_CHAR *HAPFlagsToString(IMG_UINT32 ui32Flags); #endif -#endif +#endif /* __IMG_LINUX_MM_H__ */ diff --git a/sgx/services4/srvkm/env/linux/mmap.c b/sgx/services4/srvkm/env/linux/mmap.c index ca38e5d..6e032dc 100644 --- a/sgx/services4/srvkm/env/linux/mmap.c +++ b/sgx/services4/srvkm/env/linux/mmap.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -39,6 +55,9 @@ #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> @@ -51,15 +70,12 @@ #include <drm/drmP.h> #endif -#include "img_defs.h" -#include "services.h" -#include "servicesint.h" +#include "services_headers.h" + #include "pvrmmap.h" #include "mutils.h" #include "mmap.h" #include "mm.h" -#include "pvr_debug.h" -#include "osfunc.h" #include "proc.h" #include "mutex.h" #include "handle.h" @@ -74,7 +90,19 @@ #error "The mmap code requires PVR_SECURE_HANDLES" #endif -static PVRSRV_LINUX_MUTEX g_sMMapMutex; +/* 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); @@ -88,9 +116,28 @@ static inline PKV_OFFSET_STRUCT FindOffsetStructByPID(LinuxMemArea *psLinuxMemAr #if defined(DEBUG_LINUX_MMAP_AREAS) static struct proc_dir_entry *g_ProcMMap; -#endif +#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) @@ -100,12 +147,19 @@ static struct proc_dir_entry *g_ProcMMap; #define FIRST_SPECIAL_PFN (LAST_PHYSICAL_PFN + 1) #define LAST_SPECIAL_PFN (FIRST_SPECIAL_PFN + MAX_MMAP_HANDLE) -#else +#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 @@ -118,21 +172,21 @@ static struct proc_dir_entry *g_ProcMMap; #define MAX_MMAP_HANDLE 0x7fffffffUL #endif -#endif +#endif /* !defined(PVR_MAKE_ALL_PFNS_SPECIAL) */ #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) static inline IMG_BOOL PFNIsPhysical(IMG_UINT32 pfn) { - - return ( (pfn <= LAST_PHYSICAL_PFN)) ? IMG_TRUE : IMG_FALSE; + /* 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) { - - return ((pfn >= FIRST_SPECIAL_PFN) ) ? IMG_TRUE : IMG_FALSE; + /* Unsigned, no need to compare <=MAX_UINT */ + return ((pfn >= FIRST_SPECIAL_PFN) /*&& (pfn <= LAST_SPECIAL_PFN)*/) ? IMG_TRUE : IMG_FALSE; } #endif @@ -169,6 +223,16 @@ HandleToMMapOffset(IMG_HANDLE hHandle) } #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) { @@ -180,11 +244,18 @@ LinuxMemAreaUsesPhysicalMap(LinuxMemArea *psLinuxMemArea) 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) { @@ -216,14 +287,22 @@ CreateOffsetStruct(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Offset, IMG_UINT 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 @@ -259,6 +338,17 @@ DestroyOffsetStruct(PKV_OFFSET_STRUCT 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, @@ -337,7 +427,7 @@ create_gem_wrapper(struct drm_device *dev, LinuxMemArea *psLinuxMemArea, case LINUX_MEM_AREA_ALLOC_PAGES: pages = kmalloc(sizeof(pages) * npages, GFP_KERNEL); for (i = 0; i < npages; i++) { - pages[i] = psLinuxMemArea->uData.sPageList.pvPageList[i + PHYS_TO_PFN(ui32ByteOffset)]; + pages[i] = psLinuxMemArea->uData.sPageList.ppsPageList[i + PHYS_TO_PFN(ui32ByteOffset)]; } break; case LINUX_MEM_AREA_SUB_ALLOC: @@ -355,7 +445,6 @@ create_gem_wrapper(struct drm_device *dev, LinuxMemArea *psLinuxMemArea, /* map PVR cache type flags to GEM.. */ switch(psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: - case PVRSRV_HAP_SMART: flags = OMAP_BO_CACHED; break; case PVRSRV_HAP_WRITECOMBINE: @@ -380,6 +469,33 @@ create_gem_wrapper(struct drm_device *dev, LinuxMemArea *psLinuxMemArea, #endif /* SUPPORT_DRI_DRM_EXTERNAL */ +/*! + ******************************************************************************* + + @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) @@ -442,24 +558,43 @@ PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, } #endif /* SUPPORT_DRI_DRM_EXTERNAL */ - DetermineUsersSizeAndByteOffset(psLinuxMemArea, - pui32RealByteSize, - pui32ByteOffset); + /* 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); + } psOffsetStruct = FindOffsetStructByPID(psLinuxMemArea, psPerProc->ui32PID); if (psOffsetStruct) { - PVR_ASSERT(*pui32RealByteSize == psOffsetStruct->ui32RealByteSize); - + 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; - psOffsetStruct->ui32RefCount++; + 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) @@ -495,18 +630,22 @@ PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, 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; - psOffsetStruct->ui32RefCount++; + 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: @@ -517,6 +656,28 @@ exit_unlock: } +/*! + ******************************************************************************* + + @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) @@ -562,7 +723,7 @@ PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, goto exit_unlock; } - psOffsetStruct->ui32RefCount--; + PVRSRVOffsetStructDecRef(psOffsetStruct); *pbMUnmap = (IMG_BOOL)((psOffsetStruct->ui32RefCount == 0) && (psOffsetStruct->ui32UserVAddr != 0)); @@ -573,7 +734,7 @@ PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, 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 @@ -602,7 +763,11 @@ FindOffsetStructByOffset(IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize) 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 { @@ -627,7 +792,11 @@ FindOffsetStructByPID(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32PID) } return 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, @@ -635,19 +804,33 @@ DoMapToUser(LinuxMemArea *psLinuxMemArea, { 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), + 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 @@ -658,7 +841,13 @@ DoMapToUser(LinuxMemArea *psLinuxMemArea, 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) @@ -671,27 +860,51 @@ DoMapToUser(LinuxMemArea *psLinuxMemArea, #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 = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); + IMG_UINT32 pfn; + IMG_BOOL bMapPage = IMG_TRUE; - if (!pfn_valid(pfn)) - { + 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; + PVR_DPF((PVR_DBG_ERROR,"%s: Error - PFN invalid: 0x%x", __FUNCTION__, pfn)); + return IMG_FALSE; #else - bMixedMap = IMG_TRUE; + bMixedMap = IMG_TRUE; #endif - } + } + ui32AdjustedPA += PAGE_SIZE; + } } #if defined(PVR_MAKE_ALL_PFNS_SPECIAL) @@ -700,43 +913,58 @@ DoMapToUser(LinuxMemArea *psLinuxMemArea, 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; + } + } - pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); + 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 + if (bMixedMap) + { + result = vm_insert_mixed(ps_vma, ulVMAPos, pfn); + if(result != 0) + { + PVR_DPF((PVR_DBG_ERROR,"%s: Error - vm_insert_mixed failed (%d)", __FUNCTION__, result)); + return IMG_FALSE; + } + } + else #endif - { - struct page *psPage; - - PVR_ASSERT(pfn_valid(pfn)); - - psPage = pfn_to_page(pfn); - - result = VM_INSERT_PAGE(ps_vma, ulVMAPos, psPage); - if(result != 0) - { - PVR_DPF((PVR_DBG_ERROR,"%s: Error - VM_INSERT_PAGE failed (%d)", __FUNCTION__, result)); - return IMG_FALSE; - } - } - ulVMAPos += PAGE_SIZE; - } + { + 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; @@ -746,10 +974,11 @@ DoMapToUser(LinuxMemArea *psLinuxMemArea, static IMG_VOID MMapVOpenNoLock(struct vm_area_struct* ps_vma, PKV_OFFSET_STRUCT psOffsetStruct) { - PVR_ASSERT(psOffsetStruct != IMG_NULL) - psOffsetStruct->ui32Mapped++; + 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)); @@ -769,6 +998,9 @@ MMapVOpenNoLock(struct vm_area_struct* ps_vma, PKV_OFFSET_STRUCT psOffsetStruct) } +/* + * Linux mmap open entry point. + */ static void MMapVOpen(struct vm_area_struct* ps_vma) { @@ -799,7 +1031,7 @@ MMapVCloseNoLock(struct vm_area_struct* ps_vma, PKV_OFFSET_STRUCT psOffsetStruct #endif PVR_ASSERT(!psOffsetStruct->bOnMMapList); - psOffsetStruct->ui32Mapped--; + PVRSRVOffsetStructDecMapped(psOffsetStruct); if (psOffsetStruct->ui32Mapped == 0) { if (psOffsetStruct->ui32RefCount != 0) @@ -813,6 +1045,9 @@ MMapVCloseNoLock(struct vm_area_struct* ps_vma, PKV_OFFSET_STRUCT psOffsetStruct ps_vma->vm_private_data = NULL; } +/* + * Linux mmap close entry point. + */ static void MMapVClose(struct vm_area_struct* ps_vma) { @@ -823,361 +1058,103 @@ MMapVClose(struct vm_area_struct* ps_vma) LinuxUnLockMutex(&g_sMMapMutex); } - -static struct vm_operations_struct MMapIOOps = -{ - .open=MMapVOpen, - .close=MMapVClose -}; - -/*****************************************************************************/ -/* "Smart" cached buffer support.. +#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. */ - -#include <linux/rmap.h> -#include <linux/pagemap.h> - -typedef struct { - struct mutex lock; /* mutex that protects the page list */ - struct list_head faulted; /* list of touched pages */ - int npages; /* number of pages in buffer */ - struct vm_area_struct *vma; /* vma of initial creator of buffer */ -} PVRMMapSmartCache; - -enum { - PG_touched = PG_private, - PG_written = PG_private_2 -}; - -static IMG_VOID PVRMMapUnmapInv(IMG_HANDLE hSmartCache, bool inv); - - -#ifndef DBG -#define DBG(fmt, ...) do {} while (0) -//#define DBG(fmt, ...) printk(KERN_INFO"[%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) -#endif -#ifndef VERB -#define VERB(fmt, ...) do {} while (0) -//#define VERB(fmt, ...) printk(KERN_INFO"[%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) -#endif -#define ERR(fmt, ...) printk(KERN_ERR"ERR: [%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) - -static int -MMapVFault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int MMapVAccess(struct vm_area_struct *ps_vma, unsigned long addr, + void *buf, int len, int write) { - PKV_OFFSET_STRUCT psOffsetStruct = vma->vm_private_data; - LinuxMemArea *psLinuxMemArea = psOffsetStruct->psLinuxMemArea; - PVRMMapSmartCache *smart = psLinuxMemArea->hSmartCache; - unsigned long offset, pfn; - struct page *page; - pgoff_t pgoff; - int ret = VM_FAULT_NOPAGE; - - if (!smart) - { - ERR("uhh oh, I'm not smart..\n"); - return VM_FAULT_SIGBUS; - } - - /* We don't use vmf->pgoff since that has the fake offset */ - pgoff = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; - offset = pgoff << PAGE_SHIFT; - if (offset >= psOffsetStruct->psLinuxMemArea->ui32ByteSize) - { - ERR("%p: offset too big: %lu vs %u, %p", smart, offset, - psOffsetStruct->psLinuxMemArea->ui32ByteSize, - psOffsetStruct->psLinuxMemArea); - return VM_FAULT_SIGBUS; - } - - pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, offset); - page = pfn_to_page(pfn); - if (!page) - { - ERR("%p: can't find page: %lu, %p", smart, offset, psLinuxMemArea); - return VM_FAULT_SIGBUS; - } - + PKV_OFFSET_STRUCT psOffsetStruct; + LinuxMemArea *psLinuxMemArea; + unsigned long ulOffset; + int iRetVal = -EINVAL; + IMG_VOID *pvKernelAddr; - /* *** BEGIN CRITICAL SECTION ********************************************/ - mutex_lock(&smart->lock); + LinuxLockMutex(&g_sMMapMutex); - /* if we already know of this page the we are done */ - if (test_and_set_bit(PG_touched, &page->flags)) - { - VERB("%p: (already touched) get_page(%p) (idx=%08lx, flg=%08x, cnt=%d)", - smart, page, page->index, vmf->flags, atomic_read(&page->_count)); - goto unlock; - } + psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; + psLinuxMemArea = psOffsetStruct->psLinuxMemArea; + ulOffset = addr - ps_vma->vm_start; - page->index = pgoff + vma->vm_pgoff; + 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; - VERB("%p: get_page(%p) (idx=%08lx, flg=%08x, cnt=%d)", - smart, page, page->index, vmf->flags, atomic_read(&page->_count)); + pvKernelAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); - if (vma->vm_file) + if (pvKernelAddr) { - page->mapping = vma->vm_file->f_mapping; + memcpy(buf, pvKernelAddr+ulOffset, len); + iRetVal = len; } else { - ERR("%p: no mapping available\n", smart); - } + IMG_UINT32 pfn, ui32OffsetInPage; + struct page *page; - BUG_ON(!page->mapping); + pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ulOffset); - vmf->page = page; + if (!pfn_valid(pfn)) + goto exit_unlock; - get_page(page); - ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); - if (ret) - { - ERR("%p: error inserting page: %d", smart, ret); - goto unlock; - } - ret = VM_FAULT_NOPAGE; - - /* Add the page to the list of pages that have been touched - */ - list_add_tail(&page->lru, &smart->faulted); - -unlock: - mutex_unlock(&smart->lock); - /* *** END CRITICAL SECTION **********************************************/ - - return ret; -} - -static int -MMapVMkWrite(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)vma->vm_private_data; - PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; - struct page *page = vmf->page; - - VERB("%p: page=%p", smart, page); - - /* *** BEGIN CRITICAL SECTION ********************************************/ - mutex_lock(&smart->lock); - - /* We want the page to remain locked from ->page_mkwrite until - * the PTE is marked dirty to avoid page_mkclean() being called - * before the PTE is updated, which would leave the page ignored. - * - * Do this by locking the page here and informing the caller - * about it with VM_FAULT_LOCKED. - */ - lock_page(page); + page = pfn_to_page(pfn); + ui32OffsetInPage = ADDR_TO_PAGE_OFFSET(ulOffset); - set_bit(PG_written, &page->flags); + if (ui32OffsetInPage+len > PAGE_SIZE) + /* The region crosses a page boundary */ + goto exit_unlock; - mutex_unlock(&smart->lock); - /* *** END CRITICAL SECTION **********************************************/ + pvKernelAddr = kmap(page); + memcpy(buf, pvKernelAddr+ui32OffsetInPage, len); + kunmap(page); - return VM_FAULT_LOCKED; -} - -static void -MMapVClose2(struct vm_area_struct* vma) -{ - PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)vma->vm_private_data; - PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; - DBG("%p", smart); - PVRMMapUnmapInv(smart, false); - MMapVClose(vma); -} - -static struct vm_operations_struct MMapSmartOps = { - .open=MMapVOpen, - .close=MMapVClose2, - .fault=MMapVFault, - .page_mkwrite=MMapVMkWrite, -}; - -static int -MMapSetPageDirty(struct page *page) -{ - if (!PageDirty(page)) - SetPageDirty(page); - return 0; -} - -static const struct address_space_operations MMapSmartAOps = { - .set_page_dirty = MMapSetPageDirty, -}; - -/* prepare buffer transition CPU -> GPU */ -IMG_VOID -PVRMMapPrepareCpuToGpu(IMG_HANDLE hSmartCache) -{ -#if 0 - PVRMMapSmartCache *smart = hSmartCache; - struct page *page; - int cnt = 0; - - /* hopefully this is the common-path.. */ - if (list_empty(&smart->faulted)) - { - return; + iRetVal = len; } - /* *** BEGIN CRITICAL SECTION ********************************************/ - mutex_lock(&smart->lock); - - list_for_each_entry(page, &smart->faulted, lru) { - if (test_and_clear_bit(PG_written, &page->flags)) - { - void *va = (void *)(smart->vma->vm_start + - ((page->index - smart->vma->vm_pgoff) << PAGE_SHIFT)); - unsigned long pa = page_to_phys(page); - - lock_page(page); - page_mkclean(page); - dmac_clean_range(va, va + PAGE_SIZE); - outer_clean_range(pa, pa + PAGE_SIZE); - unlock_page(page); - - cnt++; - } - } - - mutex_unlock(&smart->lock); - /* *** END CRITICAL SECTION **********************************************/ - - DBG("%p: cleaned %d (of %d)", smart, cnt, smart->npages); -#else - PVRMMapUnmapInv(hSmartCache, true); -#endif -} -/* prepare buffer transition GPU -> CPU */ -IMG_VOID -PVRMMapPrepareGpuToCpu(IMG_HANDLE hSmartCache) -{ - PVRMMapUnmapInv(hSmartCache, true); +exit_unlock: + LinuxUnLockMutex(&g_sMMapMutex); + return iRetVal; } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) */ -/* remove faulted pages from user's vm, and optionally invalidate.. */ -static IMG_VOID -PVRMMapUnmapInv(IMG_HANDLE hSmartCache, bool inv) +static struct vm_operations_struct MMapIOOps = { - PVRMMapSmartCache *smart = hSmartCache; - struct page *page, *next; - pgoff_t min = ULONG_MAX, max = 0; - struct address_space *mapping = NULL; - int cnt = 0; - - /* hopefully this is the common-path.. */ - if (list_empty(&smart->faulted)) - { - return; - } - - VERB("%p", smart); - - /* *** BEGIN CRITICAL SECTION ********************************************/ - mutex_lock(&smart->lock); - - list_for_each_entry(page, &smart->faulted, lru) { - - if (inv) - { - void *va = (void *)(smart->vma->vm_start + - ((page->index - smart->vma->vm_pgoff) << PAGE_SHIFT)); - -#if 0 - dmac_inv_range(va, va + PAGE_SIZE); -#else - dmac_flush_range(va, va + PAGE_SIZE); -#endif - } - - clear_bit(PG_touched, &page->flags); - clear_bit(PG_written, &page->flags); - - min = min(min, page->index); - max = max(max, page->index); - - mapping = page->mapping; - - cnt++; - } - - /* clear out the mapping that we setup.. do this before - * invalidating to avoid a window where the cache is - * clean, but access to it is not protected by a fault - */ - if (mapping) - { - VERB("unmap_mapping_range: max=%08lx, min=%08lx", max, min); - unmap_mapping_range(mapping, min << PAGE_SHIFT, - (max - min + 1) << PAGE_SHIFT, 1); - } - - list_for_each_entry_safe(page, next, &smart->faulted, lru) { - - if (inv) - { - unsigned long pa = page_to_phys(page); - -#if 0 - outer_inv_range(pa, pa + PAGE_SIZE); -#else - outer_flush_range(pa, pa + PAGE_SIZE); + .open=MMapVOpen, + .close=MMapVClose, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) + .access=MMapVAccess, #endif - } - - VERB("%p: put_page(%p) (idx=%08lx, cnt=%d)", - smart, page, page->index, atomic_read(&page->_count)); - - page->index = 0; - page->mapping = NULL; - - put_page(page); - - list_del(&page->lru); - } - - mutex_unlock(&smart->lock); - /* *** END CRITICAL SECTION **********************************************/ - - DBG("%p: put %d (of %d)", smart, cnt, smart->npages); -} - -/* setup smart cache buffer */ -IMG_HANDLE -PVRMMapAllocateSmart(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) -{ - PVRMMapSmartCache *smart = kzalloc(sizeof(*smart), GFP_KERNEL); - - DBG("%p", smart); +}; - mutex_init(&smart->lock); - INIT_LIST_HEAD(&smart->faulted); - return smart; -} +/*! + ******************************************************************************* -IMG_VOID -PVRMMapFreeSmart(IMG_HANDLE hSmartCache) -{ - PVRMMapSmartCache *smart = hSmartCache; + @Function PVRMMap - DBG("%p", smart); + @Description - PVRMMapUnmapInv(smart, false); + Driver mmap entry point. - mutex_destroy(&smart->lock); + @input pFile : unused. + @input ps_vma : pointer to linux memory area descriptor. - kfree(smart); -} -/*****************************************************************************/ + @Return 0, or Linux error code. + ******************************************************************************/ int PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) { - IMG_UINT32 ui32ByteSize; + 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 */ PVR_UNREFERENCED_PARAMETER(pFile); @@ -1192,17 +1169,23 @@ PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) 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 - - return -ENOENT; + /* + * 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); @@ -1214,10 +1197,11 @@ PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) #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)) { @@ -1232,10 +1216,13 @@ PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) 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; @@ -1243,7 +1230,7 @@ PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) switch(psOffsetStruct->psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: - case PVRSRV_HAP_SMART: + /* This is the default, do nothing. */ break; case PVRSRV_HAP_WRITECOMBINE: ps_vma->vm_page_prot = PGPROT_WC(ps_vma->vm_page_prot); @@ -1254,76 +1241,58 @@ PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) default: PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type", __FUNCTION__)); iRetVal = -EINVAL; - goto unlock_and_return; + goto unlock_and_return; } - if (psOffsetStruct->psLinuxMemArea->hSmartCache) - { - PVRMMapSmartCache *smart = psOffsetStruct->psLinuxMemArea->hSmartCache; - DBG("using smart cache, smart=%p, psLinuxMemArea=%p (%d, %d)", - psOffsetStruct->psLinuxMemArea->hSmartCache, - psOffsetStruct->psLinuxMemArea, - psOffsetStruct->ui32RealByteSize, - psOffsetStruct->psLinuxMemArea->ui32ByteSize); - smart->npages = (psOffsetStruct->ui32RealByteSize + PAGE_SIZE - 1) / PAGE_SIZE; - /* abuse pgoff a bit less.. in unmap_mapping_range() it is assumed - * that the offset is something sane, and I think it probably - * shouldn't intersect with other page->index's.. otherwise I - * suspect the prio_tree stuff won't work out.. - */ - ps_vma->vm_pgoff = ps_vma->vm_start >> PAGE_SHIFT; - smart->vma = ps_vma; - ps_vma->vm_ops = &MMapSmartOps; - pFile->f_mapping->a_ops = &MMapSmartAOps; - - ps_vma->vm_flags |= VM_MIXEDMAP; - } - else + /* Install open and close handlers for ref-counting */ + ps_vma->vm_ops = &MMapIOOps; + + if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0)) { - ps_vma->vm_ops = &MMapIOOps; - if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0)) - { - iRetVal = -EAGAIN; - goto unlock_and_return; - } + iRetVal = -EAGAIN; + goto unlock_and_return; } PVR_ASSERT(psOffsetStruct->ui32UserVAddr == 0); psOffsetStruct->ui32UserVAddr = ps_vma->vm_start; - + /* Compute the flush region (if necessary) inside the mmap mutex */ if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate) { - IMG_UINT32 ui32RealByteSize, ui32ByteOffset; - IMG_VOID *pvBase; + IMG_UINT32 ui32DummyByteSize; DetermineUsersSizeAndByteOffset(psOffsetStruct->psLinuxMemArea, - &ui32RealByteSize, + &ui32DummyByteSize, &ui32ByteOffset); - ui32RealByteSize = psOffsetStruct->psLinuxMemArea->ui32ByteSize; pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset; + psFlushMemArea = psOffsetStruct->psLinuxMemArea; - OSInvalidateCPUCacheRangeKM(psOffsetStruct->psLinuxMemArea, - pvBase, ui32RealByteSize); psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE; } - + + /* Call the open routine to increment the usage count */ MMapVOpenNoLock(ps_vma, ps_vma->vm_private_data); 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); + DestroyOffsetStruct(psOffsetStruct); } LinuxUnLockMutex(&g_sMMapMutex); - + + if(psFlushMemArea) + { + OSInvalidateCPUCacheRangeKM(psFlushMemArea, ui32ByteOffset, pvBase, + psFlushMemArea->ui32ByteSize); + } + return iRetVal; } @@ -1372,6 +1341,11 @@ PVRMMapExt(struct file* pFile, struct vm_area_struct* ps_vma) IMG_UINT32 ui32ByteSize; PKV_OFFSET_STRUCT psOffsetStruct; + /* for cache maintenance: */ + LinuxMemArea *psFlushMemArea = IMG_NULL; + IMG_VOID *pvBase = IMG_NULL; + IMG_UINT32 ui32ByteOffset = 0; /* Keep compiler happy */ + PVR_UNREFERENCED_PARAMETER(pFile); LinuxLockMutex(&g_sMMapMutex); @@ -1390,16 +1364,44 @@ PVRMMapExt(struct file* pFile, struct vm_area_struct* ps_vma) list_del(&psOffsetStruct->sMMapItem); psOffsetStruct->bOnMMapList = IMG_FALSE; + if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0)) + { + goto unlock_and_return; + } + PVR_ASSERT(psOffsetStruct->ui32UserVAddr == 0); psOffsetStruct->ui32UserVAddr = ps_vma->vm_start; obj->driver_private = psOffsetStruct->psLinuxMemArea; + + /* Compute the flush region (if necessary) inside the mmap mutex */ + if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate) + { + IMG_UINT32 ui32DummyByteSize; + + DetermineUsersSizeAndByteOffset(psOffsetStruct->psLinuxMemArea, + &ui32DummyByteSize, + &ui32ByteOffset); + + pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset; + psFlushMemArea = psOffsetStruct->psLinuxMemArea; + + psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE; + } + + /* Call the open routine to increment the usage count */ MMapVOpenNoLock(ps_vma, psOffsetStruct); unlock_and_return: LinuxUnLockMutex(&g_sMMapMutex); + + if(psFlushMemArea) + { + OSInvalidateCPUCacheRangeKM(psFlushMemArea, ui32ByteOffset, pvBase, + psFlushMemArea->ui32ByteSize); + } } static struct omap_gem_vm_ops gem_ops = { @@ -1411,6 +1413,13 @@ static struct omap_gem_vm_ops gem_ops = { #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) @@ -1424,6 +1433,16 @@ static void ProcSeqStartstopMMapRegistations(struct seq_file *sfile,IMG_BOOL sta } +/* + * 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; @@ -1449,12 +1468,28 @@ static void* ProcSeqOff2ElementMMapRegistrations(struct seq_file *sfile, loff_t 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; @@ -1528,6 +1563,20 @@ static void ProcSeqShowMMapRegistrations(struct seq_file *sfile, void *el) #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) { @@ -1546,7 +1595,7 @@ PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea) 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", @@ -1561,7 +1610,11 @@ PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea) #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; @@ -1577,6 +1630,20 @@ exit_unlock: } +/*! + ******************************************************************************* + + @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) { @@ -1592,12 +1659,19 @@ PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea) 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)); } @@ -1626,6 +1700,20 @@ exit_unlock: } +/*! + ******************************************************************************* + + @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) { @@ -1634,6 +1722,18 @@ LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *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) { @@ -1665,6 +1765,20 @@ LinuxMMapPerProcessDisconnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) } +/*! + ******************************************************************************* + + @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; @@ -1680,6 +1794,16 @@ PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase) } +/*! + ******************************************************************************* + + @Function PVRMMapInit + + @Description + + MMap initialisation code + + ******************************************************************************/ IMG_VOID PVRMMapInit(IMG_VOID) { @@ -1699,7 +1823,7 @@ PVRMMapInit(IMG_VOID) ProcSeqOff2ElementMMapRegistrations, ProcSeqStartstopMMapRegistations ); -#endif +#endif /* defined(DEBUG_LINUX_MMAP_AREAS) */ return; error: @@ -1708,6 +1832,16 @@ error: } +/*! + ******************************************************************************* + + @Function PVRMMapCleanup + + @Description + + Mmap deinitialisation code + + ******************************************************************************/ IMG_VOID PVRMMapCleanup(IMG_VOID) { @@ -1736,7 +1870,7 @@ PVRMMapCleanup(IMG_VOID) #if defined(DEBUG_LINUX_MMAP_AREAS) RemoveProcEntrySeq(g_ProcMMap); -#endif +#endif /* defined(DEBUG_LINUX_MMAP_AREAS) */ if(g_psMemmapCache) { diff --git a/sgx/services4/srvkm/env/linux/mmap.h b/sgx/services4/srvkm/env/linux/mmap.h index 07d521b..bca8358 100644 --- a/sgx/services4/srvkm/env/linux/mmap.h +++ b/sgx/services4/srvkm/env/linux/mmap.h @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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__ @@ -31,68 +47,146 @@ #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, @@ -104,6 +198,21 @@ PVRSRV_ERROR PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, 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) @@ -115,12 +224,18 @@ PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, 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); -IMG_VOID PVRMMapPrepareCpuToGpu(IMG_HANDLE hSmartCache); -IMG_VOID PVRMMapPrepareGpuToCpu(IMG_HANDLE hSmartCache); -IMG_HANDLE PVRMMapAllocateSmart(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo); -IMG_VOID PVRMMapFreeSmart(IMG_HANDLE hSmartCache); -#endif +#endif /* __MMAP_H__ */ diff --git a/sgx/services4/srvkm/env/linux/module.c b/sgx/services4/srvkm/env/linux/module.c index cda7c21..fe31e41 100644 --- a/sgx/services4/srvkm/env/linux/module.c +++ b/sgx/services4/srvkm/env/linux/module.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -35,13 +51,24 @@ #if defined(SUPPORT_DRI_DRM) && !defined(SUPPORT_DRI_DRM_EXTERNAL) #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 #if defined(SUPPORT_DRI_DRM_EXTERNAL) @@ -72,11 +99,15 @@ #if defined(PVR_LDM_PLATFORM_MODULE) #include <linux/platform_device.h> -#endif +#endif /* PVR_LDM_PLATFORM_MODULE */ #if defined(PVR_LDM_PCI_MODULE) #include <linux/pci.h> -#endif +#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> @@ -102,38 +133,69 @@ #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(SUPPORT_DRI_DRM_EXTERNAL) #define DRVNAME PVRSRV_MODNAME #define DEVNAME PVRSRV_MODNAME #else -#define DRVNAME PVRSRV_MODNAME +#if defined(PVR_LDM_MODULE) +#define DRVNAME PVR_LDM_DRIVER_REGISTRATION_NAME +#endif #define DEVNAME PVRSRV_MODNAME MODULE_SUPPORTED_DEVICE(DEVNAME); #endif +/* + * This is all module configuration stuff required by the linux kernel. + */ #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 +#endif /* defined(PVRSRV_NEED_PVR_DPF) */ - +#if defined(CONFIG_ION_OMAP) +#include <linux/ion.h> +#include <linux/omap_ion.h> +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_MODULE) +/* + * Device class used for /sys entries (and udev device node creation) + */ static struct class *psPvrClass; #endif #if defined(SUPPORT_DRI_DRM_EXTERNAL) || !defined(SUPPORT_DRI_DRM) +/* + * This is the major number we use for all nodes in /dev. + */ static int AssignedMajorNumber; #endif +/* + * 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. +*/ #if !defined(SUPPORT_DRI_DRM) static int PVRSRVOpen(struct inode* pInode, struct file* pFile); static int PVRSRVRelease(struct inode* pInode, struct file* pFile); @@ -150,6 +212,7 @@ static struct file_operations pvrsrv_fops = PVRSRV_LINUX_MUTEX gPVRSRVLock; +/* PID of process being released */ IMG_UINT32 gui32ReleasePID; #if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) @@ -161,12 +224,15 @@ static IMG_UINT32 gPVRPowerLevel; #if defined(PVR_LDM_PLATFORM_MODULE) #define LDM_DEV struct platform_device #define LDM_DRV struct platform_driver -#endif +#endif /*PVR_LDM_PLATFORM_MODULE */ #if defined(PVR_LDM_PCI_MODULE) #define LDM_DEV struct pci_dev #define LDM_DRV struct pci_driver -#endif +#endif /* PVR_LDM_PCI_MODULE */ +/* + * This is the driver interface we support. + */ #if defined(PVR_LDM_PLATFORM_MODULE) && !defined(SUPPORT_DRI_DRM_EXTERNAL) static int PVRSRVDriverRemove(LDM_DEV *device); static int PVRSRVDriverProbe(LDM_DEV *device); @@ -177,6 +243,7 @@ static int PVRSRVDriverProbe(LDM_DEV *device, const struct pci_device_id *id); #endif #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) @@ -238,6 +305,22 @@ static struct platform_device powervr_device = { }; #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) PVR_MOD_STATIC int PVRSRVDriverProbe(LDM_DEV *pDevice) #endif @@ -249,14 +332,19 @@ static int __devinit PVRSRVDriverProbe(LDM_DEV *pDevice, const struct pci_device PVR_TRACE(("PVRSRVDriverProbe(pDevice=%p) (%s)", pDevice, pDevice->name)); -#if 0 - +#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 - #if defined(PVR_LDM_PLATFORM_MODULE) if (!pDevice->dev.platform_data) { @@ -264,21 +352,53 @@ static int __devinit PVRSRVDriverProbe(LDM_DEV *pDevice, const struct pci_device } #endif + /* SysInitialise only designed to be called once. + */ psSysData = SysAcquireDataNoCheck(); - if ( psSysData == IMG_NULL) + 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, + "pvr"); + if (IS_ERR_OR_NULL(gpsIONClient)) + { + PVR_DPF((PVR_DBG_ERROR, "PVRSRVDriverProbe: Couldn't create ion client")); + return PTR_ERR(gpsIONClient); + } +#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) PVR_MOD_STATIC int PVRSRVDriverRemove(LDM_DEV *pDevice) #endif @@ -290,6 +410,11 @@ static void __devexit PVRSRVDriverRemove(LDM_DEV *pDevice) 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) @@ -305,7 +430,8 @@ static void __devexit PVRSRVDriverRemove(LDM_DEV *pDevice) gpsPVRLDMDev = IMG_NULL; -#if 0 +#if 0 /* INTEGRATION_POINT */ + /* See previous integration point for details. */ if (PerDeviceSysDeInitialise((IMG_PVOID)pDevice) != PVRSRV_OK) { return -EINVAL; @@ -319,59 +445,216 @@ static void __devexit PVRSRVDriverRemove(LDM_DEV *pDevice) return; #endif } -#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 + +*****************************************************************************/ PVR_MOD_STATIC void PVRSRVDriverShutdown(LDM_DEV *pDevice) { PVR_TRACE(("PVRSRVDriverShutdown(pDevice=%p)", pDevice)); - (void) PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3); + 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 +#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) +#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)); - if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3) != PVRSRV_OK) + LinuxLockMutex(&gsPMMutex); + + if (!bDriverIsSuspended && !bDriverIsShutdown) { - return -EINVAL; + 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 0; + 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) +#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)); - if (PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D0) != PVRSRV_OK) + LinuxLockMutex(&gsPMMutex); + + if (bDriverIsSuspended && !bDriverIsShutdown) { - return -EINVAL; + 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 0; + return res; } -#endif +#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]; @@ -418,6 +701,23 @@ void ProcSeqShowPowerLevel(struct seq_file *sfile,void* el) #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 @@ -478,6 +778,23 @@ err_unlock: } +/*! +****************************************************************************** + + @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 @@ -485,6 +802,7 @@ static int PVRSRVRelease(struct inode unref__ * pInode, struct file *pFile) #endif { PVRSRV_FILE_PRIVATE_DATA *psPrivateData; + int err = 0; LinuxLockMutex(&gPVRSRVLock); @@ -499,7 +817,40 @@ static int PVRSRVRelease(struct inode unref__ * pInode, struct file *pFile) 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; @@ -509,18 +860,56 @@ static int PVRSRVRelease(struct inode unref__ * pInode, struct file *pFile) psPrivateData, psPrivateData->hBlockAlloc); #if !defined(SUPPORT_DRI_DRM) - set_private(pFile, IMG_NULL); + set_private(pFile, IMG_NULL); /*nulling shared pointer*/ #endif } +err_unlock: LinuxUnLockMutex(&gPVRSRVLock); - -#if !defined(SUPPORT_DRI_DRM) - return 0; +#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 @@ -535,11 +924,17 @@ static int __init PVRCore_Init(void) #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 ()) @@ -586,7 +981,7 @@ static int __init PVRCore_Init(void) goto init_failed; } #endif -#endif +#endif /* PVR_LDM_PLATFORM_MODULE */ #if defined(PVR_LDM_PCI_MODULE) if ((error = pci_register_driver(&powervr_driver)) != 0) @@ -595,10 +990,12 @@ static int __init PVRCore_Init(void) goto init_failed; } -#endif +#endif /* PVR_LDM_PCI_MODULE */ #else - + /* + * Drivers using LDM, will call SysInitialise in the probe/attach code + */ if ((eError = SysInitialise()) != PVRSRV_OK) { error = -ENODEV; @@ -611,7 +1008,7 @@ static int __init PVRCore_Init(void) #endif goto init_failed; } -#endif +#endif /* !defined(PVR_LDM_MODULE) */ #if !defined(SUPPORT_DRI_DRM) AssignedMajorNumber = register_chrdev(0, DEVNAME, &pvrsrv_fops); @@ -625,10 +1022,13 @@ static int __init PVRCore_Init(void) } PVR_TRACE(("PVRCore_Init: major device %d", AssignedMajorNumber)); -#endif +#endif /* !defined(SUPPORT_DRI_DRM) */ #if defined(PVR_LDM_MODULE) - + /* + * 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)) @@ -641,7 +1041,7 @@ static int __init PVRCore_Init(void) psDev = device_create(psPvrClass, NULL, MKDEV(AssignedMajorNumber, 0), #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)) NULL, -#endif +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)) */ DEVNAME); if (IS_ERR(psDev)) { @@ -649,7 +1049,7 @@ static int __init PVRCore_Init(void) error = -EBUSY; goto destroy_class; } -#endif +#endif /* defined(PVR_LDM_DEVICE_CLASS) */ return 0; @@ -674,8 +1074,8 @@ sys_deinit: platform_driver_unregister(&powervr_driver); #endif -#else - +#else /* defined(PVR_LDM_MODULE) */ + /* LDM drivers call SysDeinitialise during PVRSRVDriverRemove */ { SYS_DATA *psSysData; @@ -685,7 +1085,7 @@ sys_deinit: (void) SysDeinitialise(psSysData); } } -#endif +#endif /* defined(PVR_LDM_MODULE) */ init_failed: PVRMMapCleanup(); LinuxMMCleanup(); @@ -695,9 +1095,34 @@ init_failed: 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 @@ -721,17 +1146,17 @@ static void __exit PVRCore_Cleanup(void) #if !defined(SUPPORT_DRI_DRM) #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) if ( -#endif +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ unregister_chrdev((IMG_UINT)AssignedMajorNumber, DRVNAME) #if !(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) ; -#else +#else /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ ) { PVR_DPF((PVR_DBG_ERROR," can't unregister device major %d", AssignedMajorNumber)); } -#endif -#endif +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) */ +#endif /* !defined(SUPPORT_DRI_DRM) */ #if defined(PVR_LDM_MODULE) @@ -746,7 +1171,7 @@ static void __exit PVRCore_Cleanup(void) platform_driver_unregister(&powervr_driver); #endif -#else +#else /* defined(PVR_LDM_MODULE) */ #if defined(DEBUG) && defined(PVR_MANUAL_POWER_CONTROL) if (gPVRPowerLevel != 0) { @@ -756,9 +1181,9 @@ static void __exit PVRCore_Cleanup(void) } } #endif - + /* LDM drivers call SysDeinitialise during PVRSRVDriverRemove */ (void) SysDeinitialise(psSysData); -#endif +#endif /* defined(PVR_LDM_MODULE) */ PVRMMapCleanup(); @@ -773,6 +1198,12 @@ static void __exit PVRCore_Cleanup(void) 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); diff --git a/sgx/services4/srvkm/env/linux/mutex.c b/sgx/services4/srvkm/env/linux/mutex.c index 742fa03..84e2a74 100644 --- a/sgx/services4/srvkm/env/linux/mutex.c +++ b/sgx/services4/srvkm/env/linux/mutex.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -79,7 +95,7 @@ IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) } -#else +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) */ IMG_VOID LinuxInitMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) @@ -98,7 +114,9 @@ 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); @@ -132,5 +150,5 @@ IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex) return (IMG_BOOL)iCount; } -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) */ diff --git a/sgx/services4/srvkm/env/linux/mutex.h b/sgx/services4/srvkm/env/linux/mutex.h index 5e787b7..2c7d658 100644 --- a/sgx/services4/srvkm/env/linux/mutex.h +++ b/sgx/services4/srvkm/env/linux/mutex.h @@ -1,28 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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_ @@ -41,12 +58,16 @@ typedef struct mutex PVRSRV_LINUX_MUTEX; -#else +#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; @@ -66,5 +87,5 @@ extern IMG_VOID LinuxUnLockMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); extern IMG_BOOL LinuxIsLockedMutex(PVRSRV_LINUX_MUTEX *psPVRSRVMutex); -#endif +#endif /* __INCLUDED_LINUX_MUTEX_H_ */ diff --git a/sgx/services4/srvkm/env/linux/mutils.c b/sgx/services4/srvkm/env/linux/mutils.c index a012cf5..9679ed7 100644 --- a/sgx/services4/srvkm/env/linux/mutils.c +++ b/sgx/services4/srvkm/env/linux/mutils.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -77,15 +93,23 @@ pvr_pat_entry(u64 pat, IMG_UINT index) static IMG_VOID PVRLinuxX86PATProbe(IMG_VOID) { - - if (cpu_has_pat) + /* + * 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))); @@ -110,21 +134,28 @@ PVRLinuxX86PATProbe(IMG_VOID) { PVR_TRACE(("%s: Write combining not available", __FUNCTION__)); } -#else +#else /* defined(SUPPORT_LINUX_X86_WRITECOMBINE) */ PVR_TRACE(("%s: Write combining disabled in driver build", __FUNCTION__)); -#endif -#endif +#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 +#endif /* defined(SUPPORT_LINUX_X86_PAT) */ IMG_VOID PVRLinuxMUtilsInit(IMG_VOID) diff --git a/sgx/services4/srvkm/env/linux/mutils.h b/sgx/services4/srvkm/env/linux/mutils.h index b2a8ba0..4921e60 100644 --- a/sgx/services4/srvkm/env/linux/mutils.h +++ b/sgx/services4/srvkm/env/linux/mutils.h @@ -1,29 +1,46 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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__ @@ -99,5 +116,5 @@ IMG_VOID PVRLinuxMUtilsInit(IMG_VOID); -#endif +#endif /* __IMG_LINUX_MUTILS_H__ */ diff --git a/sgx/services4/srvkm/env/linux/osfunc.c b/sgx/services4/srvkm/env/linux/osfunc.c index 1fb99f2..4e4b3a0 100644 --- a/sgx/services4/srvkm/env/linux/osfunc.c +++ b/sgx/services4/srvkm/env/linux/osfunc.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -74,6 +90,15 @@ #include "event.h" #include "linkage.h" #include "pvr_uaccess.h" +#include "lock.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) @@ -82,6 +107,13 @@ #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 @@ -89,7 +121,7 @@ #define EVENT_OBJECT_TIMEOUT_MS (2000) #else #define EVENT_OBJECT_TIMEOUT_MS (100) -#endif +#endif /* EMULATOR */ #if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOID *ppvCpuVAddr, IMG_HANDLE *phBlockAlloc) @@ -102,7 +134,7 @@ PVRSRV_ERROR OSAllocMem_Impl(IMG_UINT32 ui32Flags, IMG_UINT32 ui32Size, IMG_PVOI 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 @@ -135,7 +167,7 @@ static inline int is_vmalloc_addr(const void *pvCpuVAddr) return lAddr >= VMALLOC_START && lAddr < VMALLOC_END; } -#endif +#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) @@ -172,6 +204,9 @@ 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) { @@ -180,7 +215,8 @@ OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, 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; @@ -188,6 +224,22 @@ OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, } #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: @@ -201,8 +253,9 @@ OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, } 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) { @@ -214,10 +267,18 @@ OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, 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__) - - ui32AllocFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); + /* 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) @@ -234,6 +295,16 @@ OSAllocPages_Impl(IMG_UINT32 ui32AllocFlags, 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; @@ -302,7 +373,7 @@ OSGetSubMemHandle(IMG_HANDLE hOSMemHandle, } *phOSMemHandleRet = psLinuxMemArea; - + /* KERNEL_ONLY areas are never mmapable. */ if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY) { return PVRSRV_OK; @@ -344,23 +415,6 @@ OSReleaseSubMemHandle(IMG_VOID *hOSMemHandle, IMG_UINT32 ui32Flags) return PVRSRV_OK; } -IMG_VOID -OSMemHandleRegisterSmart(IMG_VOID *hOSMemHandle, IMG_HANDLE hSmartCache) -{ - LinuxMemArea *psLinuxMemArea = hOSMemHandle; - psLinuxMemArea->hSmartCache = hSmartCache; -} - -IMG_VOID -OSMemHandleUnegisterSmart(IMG_VOID *hOSMemHandle, IMG_HANDLE hSmartCache) -{ - LinuxMemArea *psLinuxMemArea = hOSMemHandle; - if (psLinuxMemArea->hSmartCache == hSmartCache) - { - psLinuxMemArea->hSmartCache = NULL; - } -} - #if defined(SUPPORT_DRI_DRM_EXTERNAL) IMG_VOID @@ -388,7 +442,33 @@ OSMemHandleToCpuPAddr(IMG_VOID *hOSMemHandle, IMG_UINT32 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) @@ -407,6 +487,22 @@ IMG_VOID OSMemCopy(IMG_VOID *pvDst, IMG_VOID *pvSrc, IMG_UINT32 ui32Size) } +/*! +****************************************************************************** + + @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) @@ -424,11 +520,21 @@ IMG_VOID OSMemSet(IMG_VOID *pvDest, IMG_UINT8 ui8Value, IMG_UINT32 ui32Size) } +/*! +****************************************************************************** + @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; @@ -441,6 +547,19 @@ IMG_INT32 OSSNPrintf(IMG_CHAR *pStr, IMG_UINT32 ui32Size, const IMG_CHAR *pszFor 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; @@ -464,6 +583,18 @@ IMG_VOID OSBreakResourceLock (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID) } +/*! +****************************************************************************** + + @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; @@ -473,6 +604,18 @@ PVRSRV_ERROR OSCreateResource(PVRSRV_RESOURCE *psResource) } +/*! +****************************************************************************** + + @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); @@ -481,12 +624,26 @@ PVRSRV_ERROR OSDestroyResource (PVRSRV_RESOURCE *psResource) } +/*! +****************************************************************************** + + @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) @@ -500,22 +657,34 @@ PVRSRV_ERROR OSInitEnvData(IMG_PVOID *ppvEnvSpecificData) 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; @@ -527,20 +696,42 @@ PVRSRV_ERROR OSDeInitEnvData(IMG_PVOID pvEnvSpecificData) 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; @@ -563,25 +754,71 @@ IMG_VOID OSSleepms(IMG_UINT32 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()) @@ -601,6 +838,16 @@ IMG_UINT32 OSGetCurrentProcessIDKM(IMG_VOID) } +/*! +****************************************************************************** + + @Function OSGetPageSize + + @Description gets page size + + @Return page size + +******************************************************************************/ IMG_UINT32 OSGetPageSize(IMG_VOID) { #if defined(__sh__) @@ -613,6 +860,16 @@ IMG_UINT32 OSGetPageSize(IMG_VOID) } #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 @@ -649,6 +906,19 @@ out: +/*! +****************************************************************************** + + @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 @@ -682,6 +952,21 @@ out: 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, @@ -718,6 +1003,18 @@ PVRSRV_ERROR OSInstallDeviceLISR(IMG_VOID *pvSysData, 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; @@ -739,6 +1036,19 @@ PVRSRV_ERROR OSUninstallDeviceLISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -773,6 +1083,18 @@ PVRSRV_ERROR OSInstallSystemLISR(IMG_VOID *pvSysData, IMG_UINT32 ui32Irq) } +/*! +****************************************************************************** + + @Function OSUninstallSystemLISR + + @Description Uninstalls a System ISR + + @Input psSysData + + @Return error status + +******************************************************************************/ PVRSRV_ERROR OSUninstallSystemLISR(IMG_VOID *pvSysData) { SYS_DATA *psSysData = (SYS_DATA*)pvSysData; @@ -794,6 +1116,18 @@ PVRSRV_ERROR OSUninstallSystemLISR(IMG_VOID *pvSysData) } #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 @@ -809,6 +1143,18 @@ static void MISRWrapper( } +/*! +****************************************************************************** + + @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; @@ -843,6 +1189,18 @@ PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -864,6 +1222,18 @@ PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -876,8 +1246,20 @@ PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) return PVRSRV_OK; } -#else +#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 @@ -893,6 +1275,18 @@ static void MISRWrapper( } +/*! +****************************************************************************** + + @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; @@ -919,6 +1313,18 @@ PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -940,6 +1346,18 @@ PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -953,9 +1371,21 @@ PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) return PVRSRV_OK; } -#else +#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; @@ -966,6 +1396,18 @@ static void MISRWrapper(unsigned long data) } +/*! +****************************************************************************** + + @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; @@ -987,6 +1429,18 @@ PVRSRV_ERROR OSInstallMISR(IMG_VOID *pvSysData) } +/*! +****************************************************************************** + + @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; @@ -1007,6 +1461,18 @@ PVRSRV_ERROR OSUninstallMISR(IMG_VOID *pvSysData) 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; @@ -1020,10 +1486,10 @@ PVRSRV_ERROR OSScheduleMISR(IMG_VOID *pvSysData) return PVRSRV_OK; } -#endif -#endif +#endif /* #if defined(PVR_LINUX_MISR_USING_WORKQUEUE) */ +#endif /* #if defined(PVR_LINUX_MISR_USING_PRIVATE_WORKQUEUE) */ -#endif +#endif /* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) */ IMG_VOID OSPanic(IMG_VOID) { @@ -1035,6 +1501,19 @@ IMG_VOID OSPanic(IMG_VOID) #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) @@ -1050,6 +1529,18 @@ PVRSRV_ERROR OSLockResource ( PVRSRV_RESOURCE *psResource, } +/*! +****************************************************************************** + + @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; @@ -1080,6 +1571,18 @@ PVRSRV_ERROR OSUnlockResource (PVRSRV_RESOURCE *psResource, IMG_UINT32 ui32ID) } +/*! +****************************************************************************** + + @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; @@ -1101,7 +1604,7 @@ PVRSRV_ERROR OSPowerLockWrap(IMG_BOOL bTryLock) IMG_VOID OSPowerLockUnwrap (IMG_VOID) { } -#endif +#endif /* SYS_CUSTOM_POWERLOCK_WRAP */ IMG_CPU_PHYADDR OSMapLinToCPUPhys(IMG_HANDLE hOSMemHandle, @@ -1114,8 +1617,6 @@ IMG_CPU_PHYADDR OSMapLinToCPUPhys(IMG_HANDLE hOSMemHandle, PVR_ASSERT(hOSMemHandle != IMG_NULL); - - psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; uiByteOffset = (IMG_UINTPTR_T)pvLinAddr - (IMG_UINTPTR_T)LinuxMemAreaToCpuVAddr(psLinuxMemArea); @@ -1127,6 +1628,22 @@ IMG_CPU_PHYADDR OSMapLinToCPUPhys(IMG_HANDLE hOSMemHandle, } +/*! +****************************************************************************** + + @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, @@ -1135,7 +1652,11 @@ OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr, { 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; @@ -1167,6 +1688,12 @@ OSMapPhysToLin(IMG_CPU_PHYADDR BasePAddr, 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) { @@ -1198,6 +1725,12 @@ OSUnMapPhysToLin(IMG_VOID *pvLinAddr, IMG_UINT32 ui32Bytes, IMG_UINT32 ui32Mappi 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, @@ -1233,10 +1766,19 @@ RegisterExternalMem(IMG_SYS_PHYADDR *pBasePAddr, } 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__) - - ui32MappingFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); + /* + * 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); @@ -1261,6 +1803,13 @@ RegisterExternalMem(IMG_SYS_PHYADDR *pBasePAddr, } +/*! +****************************************************************************** + @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, @@ -1280,6 +1829,12 @@ PVRSRV_ERROR OSRegisterDiscontigMem(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPU } +/*! +****************************************************************************** + @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, @@ -1326,17 +1881,27 @@ PVRSRV_ERROR OSUnRegisterDiscontigMem(IMG_VOID *pvCpuVAddr, IMG_UINT32 ui32Bytes 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; @@ -1348,7 +1913,10 @@ OSReservePhys(IMG_CPU_PHYADDR BasePAddr, { 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) { @@ -1358,7 +1926,8 @@ OSReservePhys(IMG_CPU_PHYADDR BasePAddr, } 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) { @@ -1369,10 +1938,19 @@ OSReservePhys(IMG_CPU_PHYADDR BasePAddr, } 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__) - - ui32MappingFlags &= ~(PVRSRV_HAP_CACHED|PVRSRV_HAP_SMART); + /* + * 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) @@ -1389,6 +1967,16 @@ OSReservePhys(IMG_CPU_PHYADDR BasePAddr, 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); @@ -1397,6 +1985,12 @@ OSReservePhys(IMG_CPU_PHYADDR BasePAddr, 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, @@ -1441,6 +2035,14 @@ OSUnReservePhys(IMG_VOID *pvCpuVAddr, } +/*! +****************************************************************************** + @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) @@ -1451,6 +2053,13 @@ PVRSRV_ERROR OSBaseAllocContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR *pvLi 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) @@ -1468,10 +2077,16 @@ PVRSRV_ERROR OSBaseAllocContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR *pvLi psPhysAddr->uiAddr = virt_to_phys(pvKernLinAddr); return PVRSRV_OK; -#endif +#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) @@ -1489,6 +2104,25 @@ PVRSRV_ERROR OSBaseFreeContigMemory(IMG_UINT32 ui32Size, IMG_CPU_VIRTADDR pvLinA 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) @@ -1509,6 +2143,22 @@ IMG_VOID OSWriteHWReg(IMG_PVOID pvLinRegBaseAddr, IMG_UINT32 ui32Offset, IMG_UIN #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; @@ -1534,26 +2184,26 @@ PVRSRV_PCI_DEV_HANDLE OSPCISetDev(IMG_VOID *pvPCICookie, HOST_PCI_INIT_FLAGS eFl return IMG_NULL; } - if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) + 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) + 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; + 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; @@ -1562,6 +2212,24 @@ PVRSRV_PCI_DEV_HANDLE OSPCISetDev(IMG_VOID *pvPCICookie, HOST_PCI_INIT_FLAGS eFl 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; @@ -1576,6 +2244,22 @@ PVRSRV_PCI_DEV_HANDLE OSPCIAcquireDev(IMG_UINT16 ui16VendorID, IMG_UINT16 ui16De 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; @@ -1585,6 +2269,7 @@ PVRSRV_ERROR OSPCIIRQ(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 *pui32IRQ) return PVRSRV_OK; } +/* Functions supported by OSPCIAddrRangeFunc */ enum HOST_PCI_ADDR_RANGE_FUNC { HOST_PCI_ADDR_RANGE_FUNC_LEN, @@ -1594,6 +2279,24 @@ enum HOST_PCI_ADDR_RANGE_FUNC 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) @@ -1643,32 +2346,126 @@ static IMG_UINT32 OSPCIAddrRangeFunc(enum HOST_PCI_ADDR_RANGE_FUNC eFunc, 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; @@ -1676,7 +2473,7 @@ PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) PVR_TRACE(("OSPCIReleaseDev")); - + /* Release all PCI regions that are currently in use */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (psPVRPCI->abPCIResourceInUse[i]) @@ -1688,14 +2485,14 @@ PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) } #if defined(CONFIG_PCI_MSI) - if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_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) + if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) /* PRQA S 3358 */ /* misuse of enums */ { pci_clear_master(psPVRPCI->psPCIDev); } @@ -1703,11 +2500,25 @@ PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) 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; @@ -1716,7 +2527,7 @@ PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) PVR_TRACE(("OSPCISuspendDev")); - + /* Release all PCI regions that are currently in use */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (psPVRPCI->abPCIResourceInUse[i]) @@ -1753,6 +2564,24 @@ PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) 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; @@ -1795,10 +2624,10 @@ PVRSRV_ERROR OSPCIResumeDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) return PVRSRV_ERROR_PCI_CALL_FAILED; } - if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) + 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]) @@ -1815,10 +2644,11 @@ PVRSRV_ERROR OSPCIResumeDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI) return PVRSRV_OK; } -#endif +#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; @@ -1842,9 +2672,11 @@ static TIMER_CALLBACK_DATA sTimers[OS_MAX_TIMERS]; 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 spinlock_t sTimerStructLock = __SPIN_LOCK_UNLOCKED(sTimerStructLock); +static DEFINE_SPINLOCK(sTimerStructLock); #endif #endif @@ -1853,14 +2685,28 @@ 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; @@ -1892,6 +2738,24 @@ static void OSTimerWorkQueueCallBack(struct work_struct *psWork) } #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; @@ -1900,14 +2764,14 @@ IMG_HANDLE OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, IMG_VOID *pvData, IMG_UINT32 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 @@ -1937,17 +2801,19 @@ IMG_HANDLE OSAddTimer(PFN_TIMER_FUNC pfnTimerFunc, IMG_VOID *pvData, IMG_UINT32 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; @@ -1964,6 +2830,20 @@ static inline TIMER_CALLBACK_DATA *GetTimerStructure(IMG_HANDLE hTimer) 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); @@ -1971,13 +2851,27 @@ PVRSRV_ERROR OSRemoveTimer (IMG_HANDLE 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); @@ -1985,19 +2879,33 @@ PVRSRV_ERROR OSEnableTimer (IMG_HANDLE 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); @@ -2005,7 +2913,7 @@ PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer) PVR_ASSERT(psTimerCBData->bInUse); PVR_ASSERT(psTimerCBData->bActive); - + /* Stop timer from arming */ psTimerCBData->bActive = IMG_FALSE; smp_mb(); @@ -2016,11 +2924,17 @@ PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer) 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) @@ -2031,6 +2945,22 @@ PVRSRV_ERROR OSDisableTimer (IMG_HANDLE hTimer) } +/*! +****************************************************************************** + + @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 @@ -2044,12 +2974,12 @@ PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT * { 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++); @@ -2075,6 +3005,20 @@ PVRSRV_ERROR OSEventObjectCreateKM(const IMG_CHAR *pszName, PVRSRV_EVENTOBJECT * } +/*! +****************************************************************************** + + @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 @@ -2104,6 +3048,20 @@ PVRSRV_ERROR OSEventObjectDestroyKM(PVRSRV_EVENTOBJECT *psEventObject) 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; @@ -2121,6 +3079,21 @@ PVRSRV_ERROR OSEventObjectWaitKM(IMG_HANDLE hOSEventKM) 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 @@ -2148,6 +3121,22 @@ PVRSRV_ERROR OSEventObjectOpenKM(PVRSRV_EVENTOBJECT *psEventObject, 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 @@ -2176,6 +3165,20 @@ PVRSRV_ERROR OSEventObjectCloseKM(PVRSRV_EVENTOBJECT *psEventObject, } +/*! +****************************************************************************** + + @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; @@ -2193,11 +3196,43 @@ PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM) 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, @@ -2211,6 +3246,24 @@ PVRSRV_ERROR OSCopyToUser(IMG_PVOID pvProcess, 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, @@ -2224,6 +3277,24 @@ PVRSRV_ERROR OSCopyFromUser( IMG_PVOID pvProcess, 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; @@ -2264,6 +3335,28 @@ typedef struct _sWrapMemInfo_ } 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)) @@ -2313,6 +3406,20 @@ static IMG_BOOL CPUVAddrToPFN(struct vm_area_struct *psVMArea, IMG_UINT32 ulCPUV #endif } +/*! +****************************************************************************** + + @Function OSReleasePhysPageAddr + + @Description + + Release wrapped memory. + + @Input hOSWrapMem : Driver cookie + + @Return PVRSRV_ERROR + +******************************************************************************/ PVRSRV_ERROR OSReleasePhysPageAddr(IMG_HANDLE hOSWrapMem) { sWrapMemInfo *psInfo = (sWrapMemInfo *)hOSWrapMem; @@ -2341,7 +3448,12 @@ PVRSRV_ERROR OSReleasePhysPageAddr(IMG_HANDLE hOSWrapMem) 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)) @@ -2387,6 +3499,59 @@ PVRSRV_ERROR OSReleasePhysPageAddr(IMG_HANDLE hOSWrapMem) return PVRSRV_OK; } +#if defined(CONFIG_TI_TILER) + +static IMG_UINT32 CPUAddrToTilerPhy(IMG_UINT32 uiAddr) +{ + IMG_UINT32 ui32PhysAddr = 0; + pte_t *ptep, pte; + pgd_t *pgd; + pmd_t *pmd; + + pgd = pgd_offset(current->mm, uiAddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + goto err_out; + + pmd = pmd_offset(pgd, 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) */ + +/*! +****************************************************************************** + + @Function OSAcquirePhysPageAddr + + @Description + + @Return PVRSRV_ERROR + +******************************************************************************/ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_SYS_PHYADDR *psSysPAddr, @@ -2404,16 +3569,18 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, sWrapMemInfo *psInfo = NULL; IMG_BOOL bHavePageStructs = IMG_FALSE; IMG_BOOL bHaveNoPageStructs = IMG_FALSE; - IMG_BOOL bPFNMismatch = IMG_FALSE; IMG_BOOL bMMapSemHeld = IMG_FALSE; PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY; - + /* 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, @@ -2422,7 +3589,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, goto error; } - + /* Allocate information structure */ psInfo = kmalloc(sizeof(*psInfo), GFP_KERNEL); if (psInfo == NULL) { @@ -2440,7 +3607,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, 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) { @@ -2450,7 +3617,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, } 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) { @@ -2460,22 +3627,22 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, } 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)); @@ -2483,7 +3650,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, goto error; } - + /* Build list of physical page addresses */ for (i = 0; i < psInfo->iNumPages; i++) { IMG_CPU_PHYADDR CPUPhysAddr; @@ -2508,13 +3675,18 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, 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); @@ -2529,7 +3701,10 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, 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, @@ -2537,7 +3712,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, goto error; } - + /* Now check the end address is in range */ if (ulBeyondEndAddrOrig > psVMArea->vm_end) { PVR_DPF((PVR_DBG_ERROR, @@ -2545,7 +3720,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, 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, @@ -2553,7 +3728,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, 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, @@ -2577,20 +3752,20 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, } if (psInfo->ppsPages[i] == NULL) { +#if defined(CONFIG_TI_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) */ bHaveNoPageStructs = IMG_TRUE; - -#if defined(VM_PFNMAP) - if ((psVMArea->vm_flags & VM_PFNMAP) != 0) - { - IMG_UINT32 ulPFNRaw = ((ulAddr - psVMArea->vm_start) >> PAGE_SHIFT) + psVMArea->vm_pgoff; - - if (ulPFNRaw != ulPFN) - { - bPFNMismatch = IMG_TRUE; - } - } -#endif } else { @@ -2631,7 +3806,7 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, if (!bHaveNoPageStructs) { - + /* The ideal case; every page has a page structure */ goto exit; } @@ -2644,34 +3819,21 @@ PVRSRV_ERROR OSAcquirePhysPageAddr(IMG_VOID *pvCPUVAddr, goto error; } - if (bPFNMismatch) - { - PVR_DPF((PVR_DBG_ERROR, - "OSAcquirePhysPageAddr: PFN calculation mismatch for VM_PFNMAP region")); - goto error; - } - exit: PVR_ASSERT(bMMapSemHeld); up_read(¤t->mm->mmap_sem); - + /* Return the cookie */ *phOSWrapMem = (IMG_HANDLE)psInfo; if (bHaveNoPageStructs) { - PVR_DPF((PVR_DBG_WARNING, + PVR_DPF((PVR_DBG_MESSAGE, "OSAcquirePhysPageAddr: Region contains pages which can't be locked down (no page structures)")); } PVR_ASSERT(psInfo->eType != 0); -#if 0 - - - OSCleanCPUCacheRangeKM(pvCPUVAddr, (IMG_VOID *)((IMG_CHAR *)pvCPUVAddr + ui32Bytes)); -#endif - return PVRSRV_OK; error: @@ -2687,52 +3849,96 @@ error: } typedef void (*InnerCacheOp_t)(const void *pvStart, const void *pvEnd); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) -typedef void (*OuterCacheOp_t)(unsigned long ulStart, unsigned long ulEnd); + +#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)(phys_addr_t ulStart, phys_addr_t ulEnd); +typedef void (*OuterCacheOp_t)(unsigned long ulStart, unsigned long ulEnd); #endif #if defined(CONFIG_OUTER_CACHE) -typedef unsigned long (*MemAreaToPhys_t)(LinuxMemArea *psLinuxMemArea, +typedef IMG_BOOL (*MemAreaToPhys_t)(LinuxMemArea *psLinuxMemArea, IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32PageNumOffset, - IMG_UINT32 ui32PageNum); + IMG_UINT32 ui32PageNum, + unsigned long *pulStart); -static unsigned long VMallocAreaToPhys(LinuxMemArea *psLinuxMemArea, - IMG_VOID *pvRangeAddrStart, - IMG_UINT32 ui32PageNumOffset, - IMG_UINT32 ui32PageNum) +static IMG_BOOL VMallocAreaToPhys(LinuxMemArea *psLinuxMemArea, + IMG_VOID *pvRangeAddrStart, + IMG_UINT32 ui32PageNumOffset, + IMG_UINT32 ui32PageNum, + unsigned long *pulStart) { - return vmalloc_to_pfn(pvRangeAddrStart + ui32PageNum * PAGE_SIZE) << PAGE_SHIFT; + *pulStart = vmalloc_to_pfn(pvRangeAddrStart + ui32PageNum * PAGE_SIZE) << PAGE_SHIFT; + return IMG_TRUE; } -static unsigned long ExternalKVAreaToPhys(LinuxMemArea *psLinuxMemArea, - IMG_VOID *pvRangeAddrStart, - IMG_UINT32 ui32PageNumOffset, - IMG_UINT32 ui32PageNum) +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); - return CpuPAddr.uiAddr; + *pulStart = CpuPAddr.uiAddr; + return IMG_TRUE; } -static unsigned long AllocPagesAreaToPhys(LinuxMemArea *psLinuxMemArea, - IMG_VOID *pvRangeAddrStart, - IMG_UINT32 ui32PageNumOffset, - IMG_UINT32 ui32PageNum) +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.pvPageList[ui32PageNumOffset + ui32PageNum]; - return page_to_pfn(pPage) << PAGE_SHIFT; + + pPage = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageNumOffset + ui32PageNum]; + *pulStart = page_to_pfn(pPage) << PAGE_SHIFT; + return IMG_TRUE; } -#endif +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 */ -#ifndef __mips__ static IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) @@ -2740,7 +3946,10 @@ IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, 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) @@ -2748,7 +3957,7 @@ IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, pvMinVAddr = (IMG_VOID *)psOffsetStruct->ui32UserVAddr; - + /* Within permissible range */ if(pvRangeAddrStart >= pvMinVAddr && ui32Length <= psOffsetStruct->ui32RealByteSize) return pvMinVAddr; @@ -2757,8 +3966,42 @@ IMG_VOID *FindMMapBaseVAddr(struct list_head *psMMapOffsetStructList, 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, @@ -2776,8 +4019,10 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, PVR_ASSERT(psLinuxMemArea != IMG_NULL); - ui32AreaLength = psLinuxMemArea->ui32ByteSize; + LinuxLockMutex(&g_sMMapMutex); + psMMapOffsetStructList = &psLinuxMemArea->sMMapOffsetStructList; + ui32AreaLength = psLinuxMemArea->ui32ByteSize; PVR_ASSERT(ui32Length <= ui32AreaLength); @@ -2787,7 +4032,7 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, psLinuxMemArea = psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea; } - + /* Recursion surely isn't possible? */ PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); switch(psLinuxMemArea->eAreaType) @@ -2798,63 +4043,84 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, { pvMinVAddr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress + ui32AreaOffset; - + /* Outside permissible range */ if(pvRangeAddrStart < pvMinVAddr) goto err_blocked; - pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + 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; - pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + 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 +#else /* defined(CONFIG_OUTER_CACHE) */ } -#endif +#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; - pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); #if defined(CONFIG_OUTER_CACHE) ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; @@ -2863,6 +4129,26 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, 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, @@ -2870,11 +4156,22 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, if(!pvMinVAddr) goto err_blocked; - pfnInnerCacheOp(pvRangeAddrStart, pvRangeAddrStart + ui32Length); + DoInnerCacheOp(hOSMemHandle, + ui32ByteOffset, + pvRangeAddrStart, + ui32Length, + pfnInnerCacheOp); #if defined(CONFIG_OUTER_CACHE) ui32PageNumOffset = ((ui32AreaOffset & PAGE_MASK) + (pvRangeAddrStart - pvMinVAddr)) >> PAGE_SHIFT; - pfnMemAreaToPhys = AllocPagesAreaToPhys; + if (psLinuxMemArea->hBMHandle) + { + pfnMemAreaToPhys = AllocPagesSparseAreaToPhys; + } + else + { + pfnMemAreaToPhys = AllocPagesAreaToPhys; + } #endif break; } @@ -2883,35 +4180,41 @@ IMG_BOOL CheckExecuteCacheOp(IMG_HANDLE hOSMemHandle, 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++) { - ulStart = pfnMemAreaToPhys(psLinuxMemArea, pvRangeAddrStart, - ui32PageNumOffset, i); - ulEnd = ulStart + PAGE_SIZE; - - if(i == ui32NumPages - 1 && ulEndOffset != 0) - ulEnd = ulStart + ulEndOffset; - - if(i == 0) - ulStart += ulStartOffset; - - pfnOuterCacheOp(ulStart, ulEnd); + 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 @@ -2923,11 +4226,10 @@ err_blocked: "%p-%p (type %d)", __func__, pvRangeAddrStart, pvRangeAddrStart + ui32Length, psLinuxMemArea->eAreaType)); + LinuxUnLockMutex(&g_sMMapMutex); return IMG_FALSE; } -#endif - #if defined(__i386__) #define ROUND_UP(x,a) (((x) + (a) - 1) & ~((a) - 1)) @@ -2949,13 +4251,15 @@ static void x86_flush_cache_range(const void *pvStart, const void *pvEnd) 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); } @@ -2965,33 +4269,36 @@ IMG_VOID OSFlushCPUCacheKM(IMG_VOID) } IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) { - - return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, 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) { - - return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, 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) { - - return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + /* No invalidate-only support */ + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, pvRangeAddrStart, ui32Length, x86_flush_cache_range, IMG_NULL); } -#else +#else /* defined(__i386__) */ #if defined(__arm__) @@ -3003,25 +4310,35 @@ static void per_cpu_cache_flush(void *arg) 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) && !defined(PVR_NO_FULL_CACHE_OPS) - outer_clean_all(); +#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) && !defined(PVR_NO_FULL_CACHE_OPS) +#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) { @@ -3042,90 +4359,244 @@ static void pvr_dmac_clean_range(const void *pvStart, const void *pvEnd) } IMG_BOOL OSFlushCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) { - return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, 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) { - IMG_BOOL retval = IMG_TRUE; + IMG_BOOL retval = IMG_TRUE; #if defined(CONFIG_OUTER_CACHE) && !defined(PVR_NO_FULL_CACHE_OPS) if (ui32Length > PVR_FULL_CACHE_OP_THRESHOLD) OSCleanCPUCacheKM(); else #endif { - retval = CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + retval = CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, pvr_dmac_clean_range, outer_clean_range); } return retval; } IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, + IMG_UINT32 ui32ByteOffset, IMG_VOID *pvRangeAddrStart, IMG_UINT32 ui32Length) { - return CheckExecuteCacheOp(hOSMemHandle, pvRangeAddrStart, ui32Length, + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, pvr_dmac_inv_range, outer_inv_range); } -#else +#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) { - if (ui32Length) - dma_cache_wback_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); - return IMG_TRUE; + 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) { - if (ui32Length) - dma_cache_wback((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); - return IMG_TRUE; + 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) { - if (ui32Length) - dma_cache_inv((IMG_UINTPTR_T)pvRangeAddrStart, ui32Length); - return IMG_TRUE; + return CheckExecuteCacheOp(hOSMemHandle, ui32ByteOffset, + pvRangeAddrStart, ui32Length, + pvr_dma_cache_inv, IMG_NULL); } -#else +#else /* defined(__mips__) */ #error "Implement CPU cache flush/clean/invalidate primitives for this CPU!" -#endif +#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; +} + -#endif +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); +} -#endif +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) @@ -3152,11 +4623,30 @@ PVRSRV_ERROR PVROSFuncInit(IMG_VOID) } } #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) { diff --git a/sgx/services4/srvkm/env/linux/osperproc.c b/sgx/services4/srvkm/env/linux/osperproc.c index 6b57dfc..5116c36 100644 --- a/sgx/services4/srvkm/env/linux/osperproc.c +++ b/sgx/services4/srvkm/env/linux/osperproc.c @@ -1,35 +1,55 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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) @@ -57,14 +77,28 @@ PVRSRV_ERROR OSPerProcessPrivateDataInit(IMG_HANDLE *phOsPrivateData) 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; } @@ -80,17 +114,17 @@ PVRSRV_ERROR OSPerProcessPrivateDataDeInit(IMG_HANDLE hOsPrivateData) 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) { diff --git a/sgx/services4/srvkm/env/linux/pdump.c b/sgx/services4/srvkm/env/linux/pdump.c index 13d9b0d..192640f 100644 --- a/sgx/services4/srvkm/env/linux/pdump.c +++ b/sgx/services4/srvkm/env/linux/pdump.c @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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) @@ -30,7 +46,7 @@ #include <asm/atomic.h> #include <stdarg.h> #if defined (SUPPORT_SGX) -#include "sgxdefs.h" +#include "sgxdefs.h" /* Is this still needed? */ #endif #include "services_headers.h" @@ -39,14 +55,14 @@ #include "dbgdrvif.h" #if defined (SUPPORT_SGX) -#include "sgxmmu.h" +#include "sgxmmu.h"/* Is this still needed? */ #endif #include "mm.h" #include "pdump_km.h" #include "pdump_int.h" -#include <linux/kernel.h> -#include <linux/string.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); @@ -56,6 +72,9 @@ 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); @@ -87,13 +106,14 @@ static PDBG_PDUMP_STATE gsDBGPdumpState = {{IMG_NULL}, 0, IMG_NULL, IMG_NULL, IM -IMG_VOID DBGDrvGetServiceTable(IMG_VOID **fn_table); - 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) { @@ -106,6 +126,9 @@ PVRSRV_ERROR PDumpOSGetScriptString(IMG_HANDLE *phScript, return PVRSRV_OK; } +/*! + * \name PDumpOSGetMessageString + */ PVRSRV_ERROR PDumpOSGetMessageString(IMG_CHAR **ppszMsg, IMG_UINT32 *pui32MaxLen) { @@ -118,6 +141,9 @@ PVRSRV_ERROR PDumpOSGetMessageString(IMG_CHAR **ppszMsg, return PVRSRV_OK; } +/*! + * \name PDumpOSGetFilenameString + */ PVRSRV_ERROR PDumpOSGetFilenameString(IMG_CHAR **ppszFile, IMG_UINT32 *pui32MaxLen) { @@ -130,11 +156,17 @@ PVRSRV_ERROR PDumpOSGetFilenameString(IMG_CHAR **ppszFile, 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; @@ -147,7 +179,7 @@ PVRSRV_ERROR PDumpOSBufprintf(IMG_HANDLE hBuf, IMG_UINT32 ui32ScriptSizeMax, IMG va_end(vaArgs); - if (n>=(IMG_INT32)ui32ScriptSizeMax || n==-1) + 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.")); @@ -160,13 +192,16 @@ PVRSRV_ERROR PDumpOSBufprintf(IMG_HANDLE hBuf, IMG_UINT32 ui32ScriptSizeMax, IMG 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) + 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.")); @@ -176,13 +211,19 @@ PVRSRV_ERROR PDumpOSVSprintf(IMG_CHAR *pszComment, IMG_UINT32 ui32ScriptSizeMax, 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; @@ -194,7 +235,7 @@ PVRSRV_ERROR PDumpOSSprintf(IMG_CHAR *pszComment, IMG_UINT32 ui32ScriptSizeMax, va_end(vaArgs); - if (n>=(IMG_INT32)ui32ScriptSizeMax || n==-1) + 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.")); @@ -204,6 +245,9 @@ PVRSRV_ERROR PDumpOSSprintf(IMG_CHAR *pszComment, IMG_UINT32 ui32ScriptSizeMax, return PVRSRV_OK; } +/*! + * \name PDumpOSBuflen + */ IMG_UINT32 PDumpOSBuflen(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMax) { IMG_CHAR* pszBuf = hBuffer; @@ -216,15 +260,18 @@ IMG_UINT32 PDumpOSBuflen(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMax) 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'; @@ -240,22 +287,34 @@ IMG_VOID PDumpOSVerifyLineEnding(IMG_HANDLE hBuffer, IMG_UINT32 ui32BufferSizeMa } } +/*! + * \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, @@ -268,14 +327,21 @@ IMG_BOOL PDumpOSWriteString(IMG_HANDLE hStream, 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) @@ -285,11 +351,17 @@ IMG_BOOL PDumpOSJTInitialised(IMG_VOID) 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, @@ -300,19 +372,23 @@ IMG_VOID PDumpOSCPUVAddrToDevPAddr(PVRSRV_DEVICE_TYPE eDeviceType, IMG_CPU_PHYADDR sCpuPAddr; PVR_UNREFERENCED_PARAMETER(pui8LinAddr); - PVR_UNREFERENCED_PARAMETER(ui32PageSize); + 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, @@ -321,7 +397,9 @@ IMG_VOID PDumpOSCPUVAddrToPhysPages(IMG_HANDLE hOSMemHandle, { if(hOSMemHandle) { - + /* + * If a Services memory handle is provided then use it. + */ IMG_CPU_PHYADDR sCpuPAddr; PVR_UNREFERENCED_PARAMETER(pui8LinAddr); @@ -338,6 +416,9 @@ IMG_VOID PDumpOSCPUVAddrToPhysPages(IMG_HANDLE hOSMemHandle, } } +/*! + * \name PDumpOSDebugDriverWrite + */ IMG_UINT32 PDumpOSDebugDriverWrite( PDBG_STREAM psStream, PDUMP_DDWMODE eDbgDrvWriteMode, IMG_UINT8 *pui8Data, @@ -365,29 +446,43 @@ IMG_UINT32 PDumpOSDebugDriverWrite( PDBG_STREAM psStream, 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((IMG_VOID **)&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); @@ -457,7 +552,9 @@ init_failed: gsDBGPdumpState.pszMsg = IMG_NULL; } - + /* + * Remove the connection notify callback + */ sConnectNotifier.pfnConnectNotifier = 0; gpfnDbgDrv->pfnSetConnectNotifier(sConnectNotifier); @@ -493,13 +590,22 @@ IMG_VOID PDumpDeInit(IMG_VOID) 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; @@ -515,6 +621,13 @@ PVRSRV_ERROR PDumpStartInitPhaseKM(IMG_VOID) 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; @@ -531,12 +644,26 @@ PVRSRV_ERROR PDumpStopInitPhaseKM(IMG_VOID) 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()) @@ -546,6 +673,13 @@ IMG_BOOL PDumpOSIsCaptureFrameKM(IMG_VOID) 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; @@ -562,12 +696,30 @@ PVRSRV_ERROR PDumpOSSetFrameKM(IMG_UINT32 ui32Frame) } +/***************************************************************************** + 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; @@ -578,7 +730,9 @@ static IMG_BOOL PDumpWriteILock(PDBG_STREAM psStream, IMG_UINT8 *pui8Data, IMG_U } - + /* + Set the stream marker to split output files + */ if (psStream == gsDBGPdumpState.psStream[PDUMP_STREAM_PARAM2]) { @@ -604,11 +758,31 @@ static IMG_BOOL PDumpWriteILock(PDBG_STREAM psStream, IMG_UINT8 *pui8Data, IMG_U 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); @@ -624,5 +798,8 @@ IMG_VOID PDumpResumeKM(IMG_VOID) atomic_dec(&gsPDumpSuspended); } -#endif -#endif +#endif /* #if defined (PDUMP) */ +#endif /* #if defined (SUPPORT_SGX) */ +/***************************************************************************** + End of file (PDUMP.C) +*****************************************************************************/ diff --git a/sgx/services4/srvkm/env/linux/private_data.h b/sgx/services4/srvkm/env/linux/private_data.h index a460798..1b0f045 100644 --- a/sgx/services4/srvkm/env/linux/private_data.h +++ b/sgx/services4/srvkm/env/linux/private_data.h @@ -1,28 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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_ @@ -32,12 +48,23 @@ #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 @@ -45,22 +72,22 @@ typedef struct #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 +#endif /* defined(SUPPORT_MEMINFO_IDS) */ - + /* Accounting for OSAllocMem */ IMG_HANDLE hBlockAlloc; #if defined(SUPPORT_DRI_DRM_EXT) - IMG_PVOID pPriv; + IMG_PVOID pPriv; /*private data for extending this struct*/ #endif } PVRSRV_FILE_PRIVATE_DATA; @@ -97,5 +124,5 @@ static inline void set_private(struct file *file, PVRSRV_FILE_PRIVATE_DATA *priv #endif -#endif +#endif /* __INCLUDED_PRIVATE_DATA_H_ */ diff --git a/sgx/services4/srvkm/env/linux/proc.c b/sgx/services4/srvkm/env/linux/proc.c index 1df8aff..3b48c9e 100644 --- a/sgx/services4/srvkm/env/linux/proc.c +++ b/sgx/services4/srvkm/env/linux/proc.c @@ -1,28 +1,47 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -52,6 +71,7 @@ #include "lists.h" +// The proc entry for our /proc/pvr directory static struct proc_dir_entry * dir; static const IMG_CHAR PVRProcDirRoot[] = "pvr"; @@ -98,6 +118,28 @@ 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; @@ -109,10 +151,13 @@ off_t printAppend(IMG_CHAR * buffer, size_t size, off_t off, const IMG_CHAR * fo 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); } @@ -123,16 +168,50 @@ off_t printAppend(IMG_CHAR * buffer, size_t size, off_t off, const IMG_CHAR * fo } +/*! +****************************************************************************** + + @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); @@ -142,7 +221,7 @@ void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t 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; @@ -150,6 +229,22 @@ void* ProcSeq1ElementHeaderOff2Element(struct seq_file *sfile, loff_t off) } +/*! +****************************************************************************** + + @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); @@ -157,11 +252,22 @@ static IMG_INT pvr_proc_open(struct inode *inode,struct file *file) 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) { @@ -178,6 +284,23 @@ static ssize_t pvr_proc_write(struct file *file, const char __user *buffer, } +/*! +****************************************************************************** + + @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; @@ -186,6 +309,20 @@ static void *pvr_proc_seq_start (struct seq_file *proc_seq_file, loff_t *pos) 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; @@ -195,6 +332,25 @@ static void pvr_proc_seq_stop (struct seq_file *proc_seq_file, void *v) 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; @@ -204,6 +360,23 @@ static void *pvr_proc_seq_next (struct seq_file *proc_seq_file, void *v, loff_t 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; @@ -213,6 +386,38 @@ static int pvr_proc_seq_show (struct seq_file *proc_seq_file, void *v) +/*! +****************************************************************************** + + @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, @@ -259,7 +464,7 @@ static struct proc_dir_entry* CreateProcEntryInDirSeq( 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) { @@ -279,6 +484,35 @@ static struct proc_dir_entry* CreateProcEntryInDirSeq( } +/*! +****************************************************************************** + + @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, @@ -297,6 +531,40 @@ struct proc_dir_entry* CreateProcReadEntrySeq ( 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, @@ -321,6 +589,37 @@ struct proc_dir_entry* CreateProcEntrySeq ( +/*! +****************************************************************************** + + @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, @@ -379,6 +678,20 @@ struct proc_dir_entry* CreatePerProcessProcEntrySeq ( } +/*! +****************************************************************************** + + @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) @@ -393,6 +706,22 @@ IMG_VOID RemoveProcEntrySeq( struct proc_dir_entry* proc_entry ) } } +/*! +****************************************************************************** + + @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; @@ -420,10 +749,46 @@ IMG_VOID RemovePerProcessProcEntrySeq(struct proc_dir_entry* proc_entry) } } +/*! +****************************************************************************** + + @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); @@ -433,9 +798,9 @@ static IMG_INT pvr_read_proc(IMG_CHAR *page, IMG_CHAR **start, off_t off, len = 0; *eof = 1; } - else if (!len) + else if (!len) /* not enough space in the buffer */ { - *start = (IMG_CHAR *) 0; + *start = (IMG_CHAR *) 0; /* don't advance the offset */ } else { @@ -446,6 +811,27 @@ static IMG_INT pvr_read_proc(IMG_CHAR *page, IMG_CHAR **start, off_t off, } +/*! +****************************************************************************** + + @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; @@ -492,12 +878,54 @@ static IMG_INT CreateProcEntryInDir(struct proc_dir_entry *pdir, const IMG_CHAR } +/*! +****************************************************************************** + + @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; @@ -549,6 +977,25 @@ IMG_INT CreatePerProcessProcEntry(const IMG_CHAR * name, read_proc_t rhandler, w } +/*! +****************************************************************************** + + @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; @@ -560,7 +1007,7 @@ IMG_INT CreateProcReadEntry(const IMG_CHAR * name, pvr_read_proc_t handler) 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) @@ -577,6 +1024,23 @@ IMG_INT CreateProcReadEntry(const IMG_CHAR * name, pvr_read_proc_t handler) } +/*! +****************************************************************************** + + @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); @@ -631,6 +1095,20 @@ IMG_INT CreateProcEntries(IMG_VOID) } +/*! +****************************************************************************** + + @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) @@ -641,6 +1119,20 @@ IMG_VOID RemoveProcEntry(const IMG_CHAR * 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; @@ -666,6 +1158,20 @@ IMG_VOID RemovePerProcessProcEntry(const IMG_CHAR *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) @@ -680,13 +1186,28 @@ IMG_VOID RemovePerProcessProcDir(PVRSRV_ENV_PER_PROCESS_DATA *psPerProc) } } +/*! +****************************************************************************** + + @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 +#endif /* PVR_MANUAL_POWER_CONTROL */ #endif RemoveProcEntrySeq(g_pProcQueue); @@ -703,6 +1224,14 @@ IMG_VOID RemoveProcEntries(IMG_VOID) 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; @@ -726,6 +1255,24 @@ static void ProcSeqShowVersion(struct seq_file *sfile,void* el) 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) @@ -781,6 +1328,14 @@ static IMG_VOID* DecOffPsDev_AnyVaCb(PVRSRV_DEVICE_NODE *psNode, va_list va) } } +/***************************************************************************** + 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; @@ -807,6 +1362,16 @@ static void ProcSeqShowSysNodes(struct seq_file *sfile,void* el) 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; @@ -822,14 +1387,17 @@ static void* ProcSeqOff2ElementSysNodes(struct seq_file * sfile, loff_t off) 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/sgx/services4/srvkm/env/linux/proc.h b/sgx/services4/srvkm/env/linux/proc.h index 2066d71..67cded7 100644 --- a/sgx/services4/srvkm/env/linux/proc.h +++ b/sgx/services4/srvkm/env/linux/proc.h @@ -1,35 +1,53 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> -#include <linux/proc_fs.h> -#include <linux/seq_file.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 @@ -51,8 +69,10 @@ typedef struct _PVR_PROC_SEQ_HANDLERS_ { } 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, ...) diff --git a/sgx/services4/srvkm/env/linux/pvr_bridge_k.c b/sgx/services4/srvkm/env/linux/pvr_bridge_k.c index 373b586..037b4c9 100644 --- a/sgx/services4/srvkm/env/linux/pvr_bridge_k.c +++ b/sgx/services4/srvkm/env/linux/pvr_bridge_k.c @@ -1,28 +1,46 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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" @@ -36,6 +54,8 @@ #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> @@ -45,10 +65,12 @@ #endif #endif +/* VGX: */ #if defined(SUPPORT_VGX) #include "vgx_bridge.h" #endif +/* SGX: */ #if defined(SUPPORT_SGX) #include "sgx_bridge.h" #endif @@ -69,7 +91,7 @@ extern PVRSRV_LINUX_MUTEX gPVRSRVLock; #if defined(SUPPORT_MEMINFO_IDS) static IMG_UINT64 ui64Stamp; -#endif +#endif /* defined(SUPPORT_MEMINFO_IDS) */ PVRSRV_ERROR LinuxBridgeInit(IMG_VOID) @@ -103,6 +125,13 @@ LinuxBridgeDeInit(IMG_VOID) #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) @@ -116,6 +145,16 @@ static void ProcSeqStartstopBridgeStats(struct seq_file *sfile,IMG_BOOL start) } +/* + * 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) @@ -132,12 +171,28 @@ static void* ProcSeqOff2ElementBridgeStats(struct seq_file *sfile, loff_t off) 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; @@ -172,7 +227,7 @@ static void ProcSeqShowBridgeStats(struct seq_file *sfile,void* el) psEntry->ui32CopyToUserTotalBytes); } -#endif +#endif /* DEBUG_BRIDGE_KM */ #if defined(SUPPORT_DRI_DRM) @@ -211,7 +266,9 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig 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, @@ -249,7 +306,7 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig } else { - + /* lookup per-process data for this process */ psPerProc = PVRSRVPerProcessData(ui32PID); if(psPerProc == IMG_NULL) { @@ -328,7 +385,13 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig 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) { @@ -362,7 +425,7 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig default: break; } -#endif +#endif /* defined(SUPPORT_DRI_DRM) && defined(PVR_SECURE_DRM_AUTH_EXPORT) */ #if defined(SUPPORT_DRI_DRM_EXTERNAL) { @@ -384,15 +447,40 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *psExportDeviceMemOUT = (PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *)psBridgePackageKM->pvParamOut; PVRSRV_FILE_PRIVATE_DATA *psPrivateData = get_private(pFile); + IMG_HANDLE hMemInfo; + PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; - if (pvr_get_user(psPrivateData->hKernelMemInfo, &psExportDeviceMemOUT->hMemInfo) != 0) + 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; @@ -404,6 +492,7 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig #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; @@ -427,7 +516,7 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig } break; } -#endif +#endif /* defined(SUPPORT_MEMINFO_IDS) */ default: break; diff --git a/sgx/services4/srvkm/env/linux/pvr_debug.c b/sgx/services4/srvkm/env/linux/pvr_debug.c index 7690875..ad3e32b 100644 --- a/sgx/services4/srvkm/env/linux/pvr_debug.c +++ b/sgx/services4/srvkm/env/linux/pvr_debug.c @@ -1,28 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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> @@ -38,7 +55,7 @@ #include <linux/hardirq.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/string.h> +#include <linux/string.h> // strncpy, strlen #include <stdarg.h> #include "img_types.h" #include "servicesext.h" @@ -49,6 +66,10 @@ #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); @@ -62,67 +83,96 @@ 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 +#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 spinlock_t gsDebugLockIRQ = __SPIN_LOCK_UNLOCKED(gsDebugLockIRQ); +static DEFINE_SPINLOCK(gsDebugLockIRQ); #endif -#if !defined (USE_SPIN_LOCK) +#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; @@ -136,15 +186,27 @@ static IMG_BOOL VBAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, const IMG_CHAR 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; @@ -173,18 +235,16 @@ IMG_VOID PVRSRVReleasePrintf(const IMG_CHAR *pszFormat, ...) } -#if defined(PVRSRV_NEED_PVR_ASSERT) - -IMG_VOID PVRSRVDebugAssertFail(const IMG_CHAR* pszFile, IMG_UINT32 uLine) -{ - PVRSRVDebugPrintf(DBGPRIV_FATAL, pszFile, uLine, "Debug assertion failed!"); - BUG(); -} - -#endif - #if defined(PVRSRV_NEED_PVR_TRACE) +/*! +****************************************************************************** + @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; @@ -214,10 +274,15 @@ IMG_VOID PVRSRVTrace(const IMG_CHAR* pszFormat, ...) va_end(VArgs); } -#endif +#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; @@ -232,6 +297,17 @@ static IMG_BOOL BAppend(IMG_CHAR *pszBuf, IMG_UINT32 ui32BufSiz, const IMG_CHAR 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, @@ -260,7 +336,7 @@ IMG_VOID PVRSRVDebugPrintf ( GetBufferLock(&ulLockFlags); - + /* Add in the level of warning */ if (bTrace == IMG_FALSE) { switch(ui32DebugLevel) @@ -308,21 +384,22 @@ IMG_VOID PVRSRVDebugPrintf ( } 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) { @@ -334,7 +411,7 @@ IMG_VOID PVRSRVDebugPrintf ( while(*pszTruncIter++ != 0) { IMG_CHAR* pszNextStartPoint; - + /* Find '/../' pattern */ if( !( ( *pszTruncIter == '/' && (pszTruncIter-4 >= szFileNameRewrite) ) && ( *(pszTruncIter-1) == '.') && @@ -342,7 +419,7 @@ IMG_VOID PVRSRVDebugPrintf ( ( *(pszTruncIter-3) == '/') ) ) continue; - + /* Find previous '/' */ pszTruncBackInter = pszTruncIter - 3; while(*(--pszTruncBackInter) != '/') { @@ -350,19 +427,19 @@ IMG_VOID PVRSRVDebugPrintf ( } 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 @@ -373,7 +450,7 @@ IMG_VOID PVRSRVDebugPrintf ( { pszFileName = pszLeafName; } -#endif +#endif /* __sh__ */ if (BAppend(pszBuf, ui32BufSiz, " [%u, %s]", ui32Line, pszFileName)) { @@ -396,16 +473,16 @@ IMG_VOID PVRSRVDebugPrintf ( } } -#endif +#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 2 +#define _PROC_SET_BUFFER_SZ 6 IMG_CHAR data_buffer[_PROC_SET_BUFFER_SZ]; - if (count != _PROC_SET_BUFFER_SZ) + if (count > _PROC_SET_BUFFER_SZ) { return -EINVAL; } @@ -415,7 +492,9 @@ IMG_INT PVRDebugProcSetLevel(struct file *file, const IMG_CHAR *buffer, IMG_UINT return -EINVAL; if (data_buffer[count - 1] != '\n') return -EINVAL; - gPVRDebugLevel = data_buffer[0] - '0'; + if (sscanf(data_buffer, "%i", &gPVRDebugLevel) == 0) + return -EINVAL; + gPVRDebugLevel &= (1 << DBGPRIV_DBGLEVEL_COUNT) - 1; } return (count); } @@ -425,4 +504,4 @@ void ProcSeqShowDebugLevel(struct seq_file *sfile,void* el) seq_printf(sfile, "%u\n", gPVRDebugLevel); } -#endif +#endif /* defined(DEBUG) */ diff --git a/sgx/services4/srvkm/env/linux/pvr_drm.c b/sgx/services4/srvkm/env/linux/pvr_drm.c index 620c158..c955bcf 100644 --- a/sgx/services4/srvkm/env/linux/pvr_drm.c +++ b/sgx/services4/srvkm/env/linux/pvr_drm.c @@ -1,29 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ - +/*************************************************************************/ /*! +@Title PowerVR drm driver +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description linux module setup +@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_DRI_DRM) #include <linux/version.h> @@ -44,8 +60,9 @@ #include <drm/drmP.h> #include <drm/drm.h> -#ifdef SUPPORT_DRI_DRM_EXTERNAL +#if defined(SUPPORT_DRI_DRM_EXTERNAL) # include <linux/omap_drm.h> +# include <linux/omap_drv.h> #endif #include "img_defs.h" @@ -107,20 +124,20 @@ static struct platform_device_id asPlatIdList[] = { {} }; #endif -#else +#else /* defined(PVR_DRI_DRM_PLATFORM_DEV) */ static struct pci_device_id asPciIdList[] = { #if defined(PVR_DRI_DRM_NOT_PCI) {1, 1, 1, 1, 0, 0, 0}, -#else +#else /* defined(PVR_DRI_DRM_NOT_PCI) */ {SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, #if defined(SYS_SGX_DEV1_DEVICE_ID) {SYS_SGX_DEV_VENDOR_ID, SYS_SGX_DEV1_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, -#endif -#endif +#endif /* defined(SYS_SGX_DEV1_DEVICE_ID) */ +#endif /* defined(PVR_DRI_DRM_NOT_PCI) */ {0} }; -#endif -#endif +#endif /* defined(PVR_DRI_DRM_PLATFORM_DEV) */ +#endif /* !defined(SUPPORT_DRI_DRM_EXT) */ DRI_DRM_STATIC int PVRSRVDrmLoad(struct drm_device *dev, unsigned long flags) @@ -145,7 +162,7 @@ PVRSRVDrmLoad(struct drm_device *dev, unsigned long flags) goto exit; } #endif - + /* Module initialisation */ iRes = PVRCore_Init(); if (iRes != 0) { @@ -268,7 +285,10 @@ PVRSRVDrmRelease(struct inode *inode, struct file *filp) if (ret != 0) { - + /* + * An error means drm_release didn't call drm_lastclose, + * but it will have freed file_priv. + */ PVR_DPF((PVR_DBG_ERROR, "%s : drm_release failed: %d", __FUNCTION__, ret)); } @@ -352,13 +372,6 @@ PVRDRM_Display_ioctl(struct drm_device *dev, void *arg, struct drm_file *pFile) #endif #if !defined(SUPPORT_DRI_DRM_EXT) - -#if defined(DRM_IOCTL_DEF) -#define PVR_DRM_IOCTL_DEF(ioctl, _func, _flags) DRM_IOCTL_DEF(DRM_##ioctl, _func, _flags) -#else -#define PVR_DRM_IOCTL_DEF(ioctl, _func, _flags) DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) -#endif - struct drm_ioctl_desc sPVRDrmIoctls[] = { #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)) DRM_IOCTL_DEF(PVR_DRM_SRVKM_IOCTL, PVRSRV_BridgeDispatchKM, PVR_DRM_UNLOCKED), @@ -403,7 +416,7 @@ static struct omap_drm_plugin plugin = { .num_ioctls = ARRAY_SIZE(sPVRDrmIoctls), .ioctl_base = 0, /* initialized when plugin is registered */ }; -#else +#else /* defined(SUPPORT_DRI_DRM_EXTERNAL) */ static struct drm_driver sPVRDrmDriver = { #if defined(PVR_DRI_DRM_PLATFORM_DEV) @@ -419,10 +432,8 @@ static struct drm_driver sPVRDrmDriver = .suspend = PVRSRVDriverSuspend, .resume = PVRSRVDriverResume, #endif -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, -#endif .ioctls = sPVRDrmIoctls, .fops = { @@ -482,7 +493,7 @@ PVRSRVDrmRemove(struct platform_device *pDevice) return 0; } -#endif +#endif static int __init PVRSRVDrmInit(void) { @@ -491,7 +502,7 @@ static int __init PVRSRVDrmInit(void) #if !defined(SUPPORT_DRI_DRM_EXTERNAL) sPVRDrmDriver.num_ioctls = pvr_max_ioctl; #endif - + PVRDPFInit(); #if defined(PVR_DRI_DRM_NOT_PCI) @@ -533,9 +544,14 @@ static void __exit PVRSRVDrmExit(void) #endif } +/* + * 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. +*/ module_init(PVRSRVDrmInit); module_exit(PVRSRVDrmExit); -#endif -#endif - +#endif +#endif diff --git a/sgx/services4/srvkm/env/linux/pvr_drm.h b/sgx/services4/srvkm/env/linux/pvr_drm.h index 9d79602..5418c6d 100644 --- a/sgx/services4/srvkm/env/linux/pvr_drm.h +++ b/sgx/services4/srvkm/env/linux/pvr_drm.h @@ -1,29 +1,45 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@Title PowerVR drm driver +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@Description drm module +@License Dual MIT/GPLv2 +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ /**************************************************************************/ #if !defined(__PVR_DRM_H__) #define __PVR_DRM_H__ @@ -57,6 +73,7 @@ int PVRSRV_BridgeDispatchKM(struct drm_device *dev, void *arg, struct drm_file * #if defined(SUPPORT_DRI_DRM_EXT) #define DRI_DRM_STATIC +/*Exported functions to common drm layer*/ int PVRSRVDrmLoad(struct drm_device *dev, unsigned long flags); int PVRSRVDrmUnload(struct drm_device *dev); int PVRSRVDrmOpen(struct drm_device *dev, struct drm_file *file); @@ -70,7 +87,7 @@ int PVRDRMUnprivCmd(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFil int PVRDRM_Dummy_ioctl(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFile); #else #define DRI_DRM_STATIC static -#endif +#endif /* defined(SUPPORT_DRI_DRM_EXT) */ #if defined(DISPLAY_CONTROLLER) extern int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Init)(struct drm_device *); @@ -102,6 +119,7 @@ IMG_INT dbgdrv_ioctl(struct drm_device *dev, IMG_VOID *arg, struct drm_file *pFi #define DRM_IOCTL_PVR_UNPRIV DRM_IO(DRM_COMMAND_BASE + DRM_PVR_UNPRIV) #define DRM_IOCTL_PVR_DBGDRV DRM_IO(DRM_COMMAND_BASE + DRM_PVR_DBGDRV) #endif + #endif #endif diff --git a/sgx/services4/srvkm/env/linux/pvr_uaccess.h b/sgx/services4/srvkm/env/linux/pvr_uaccess.h index 6e7f1d3..31c218b 100644 --- a/sgx/services4/srvkm/env/linux/pvr_uaccess.h +++ b/sgx/services4/srvkm/env/linux/pvr_uaccess.h @@ -1,29 +1,44 @@ -/********************************************************************** - * - * Copyright (C) Imagination Technologies Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful but, except - * as otherwise stated in writing, without any warranty; without even the - * implied warranty of merchantability or fitness for a particular purpose. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * Imagination Technologies Ltd. <gpl-support@imgtec.com> - * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK - * - ******************************************************************************/ +/*************************************************************************/ /*! +@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__ @@ -53,7 +68,10 @@ static inline unsigned long pvr_copy_to_user(void __user *pvTo, const void *pvFr 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); @@ -67,5 +85,5 @@ static inline unsigned long pvr_copy_from_user(void *pvTo, const void __user *pv #define pvr_put_user put_user #define pvr_get_user get_user -#endif +#endif /* __PVR_UACCESS_H__ */ |