gpu: pvr: pdumpfs: add Kconfig and debugfs pdump mode handling
[sgx.git] / pvr / osfunc.c
1 /**********************************************************************
2  *
3  * Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful but, except
10  * as otherwise stated in writing, without any warranty; without even the
11  * implied warranty of merchantability or fitness for a particular purpose.
12  * See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * The full GNU General Public License is included in this distribution in
19  * the file called "COPYING".
20  *
21  * Contact Information:
22  * Imagination Technologies Ltd. <gpl-support@imgtec.com>
23  * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
24  *
25  ******************************************************************************/
26
27 #include <linux/version.h>
28 #include <linux/io.h>
29 #include <asm/page.h>
30 #include <asm/system.h>
31 #include <linux/mm.h>
32 #include <linux/pagemap.h>
33 #include <linux/hugetlb.h>
34 #include <linux/slab.h>
35 #include <linux/vmalloc.h>
36 #include <linux/delay.h>
37 #include <linux/pci.h>
38
39 #include <linux/string.h>
40 #include <linux/sched.h>
41 #include <linux/interrupt.h>
42 #include <linux/hardirq.h>
43 #include <linux/timer.h>
44 #include <linux/capability.h>
45 #include <linux/uaccess.h>
46 #include <linux/spinlock.h>
47
48 #include "img_types.h"
49 #include "services_headers.h"
50 #include "mm.h"
51 #include "pvrmmap.h"
52 #include "mmap.h"
53 #include "env_data.h"
54 #include "proc.h"
55 #include "event.h"
56
57 #define EVENT_OBJECT_TIMEOUT_MS         (100)
58
59 #define HOST_ALLOC_MEM_USING_KMALLOC ((void *)0)
60 #define HOST_ALLOC_MEM_USING_VMALLOC ((void *)1)
61
62 #define LINUX_KMALLOC_LIMIT     PAGE_SIZE       /* 4k */
63
64 #if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
65 enum PVRSRV_ERROR OSAllocMem(u32 ui32Flags, u32 ui32Size,
66                         void **ppvCpuVAddr, void **phBlockAlloc)
67 #else
68 enum PVRSRV_ERROR _OSAllocMem(u32 ui32Flags, u32 ui32Size,
69                          void **ppvCpuVAddr, void **phBlockAlloc,
70                          char *pszFilename, u32 ui32Line)
71 #endif
72 {
73         u32 ui32Threshold;
74
75         PVR_UNREFERENCED_PARAMETER(ui32Flags);
76
77         /* determine whether to go straight to vmalloc */
78         ui32Threshold = LINUX_KMALLOC_LIMIT;
79
80         if (ui32Size > ui32Threshold) {
81 #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
82                 *ppvCpuVAddr = _VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED,
83                                                pszFilename, ui32Line);
84 #else
85                 *ppvCpuVAddr = VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED);
86 #endif
87                 if (!*ppvCpuVAddr)
88                         return PVRSRV_ERROR_OUT_OF_MEMORY;
89
90                 if (phBlockAlloc)
91                         *phBlockAlloc = HOST_ALLOC_MEM_USING_VMALLOC;
92         } else {
93                 /* default - try kmalloc first */
94
95 #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
96                 *ppvCpuVAddr = _KMallocWrapper(ui32Size, pszFilename, ui32Line);
97 #else
98                 *ppvCpuVAddr = KMallocWrapper(ui32Size);
99 #endif
100
101                 if (!*ppvCpuVAddr)
102                         return PVRSRV_ERROR_OUT_OF_MEMORY;
103
104                 if (phBlockAlloc)
105                         *phBlockAlloc = HOST_ALLOC_MEM_USING_KMALLOC;
106
107         }
108
109         return PVRSRV_OK;
110 }
111
112 #if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
113 void OSFreeMem(u32 ui32Flags, u32 ui32Size, void *pvCpuVAddr, void *hBlockAlloc)
114 #else
115 void _OSFreeMem(u32 ui32Flags, u32 ui32Size, void *pvCpuVAddr,
116                 void *hBlockAlloc, char *pszFilename, u32 ui32Line)
117 #endif
118 {
119         PVR_UNREFERENCED_PARAMETER(ui32Flags);
120
121         if (ui32Size > LINUX_KMALLOC_LIMIT) {
122 #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
123                 _VFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
124 #else
125                 VFreeWrapper(pvCpuVAddr);
126 #endif
127         } else {
128 #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
129                 _KFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
130 #else
131                 KFreeWrapper(pvCpuVAddr);
132 #endif
133         }
134 }
135
136 enum PVRSRV_ERROR OSAllocPages(u32 ui32AllocFlags, u32 ui32Size,
137                                u32 ui32PageSize, void **ppvCpuVAddr,
138                                void **phOSMemHandle)
139 {
140         struct LinuxMemArea *psLinuxMemArea;
141
142         PVR_UNREFERENCED_PARAMETER(ui32PageSize);
143
144         switch (ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) {
145         case PVRSRV_HAP_KERNEL_ONLY:
146                 {
147                         psLinuxMemArea =
148                             NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
149                         if (!psLinuxMemArea)
150                                 return PVRSRV_ERROR_OUT_OF_MEMORY;
151                         break;
152                 }
153         case PVRSRV_HAP_SINGLE_PROCESS:
154                 {
155                         psLinuxMemArea =
156                             NewAllocPagesLinuxMemArea(ui32Size, ui32AllocFlags);
157                         if (!psLinuxMemArea)
158                                 return PVRSRV_ERROR_OUT_OF_MEMORY;
159                         PVRMMapRegisterArea(psLinuxMemArea);
160                         break;
161                 }
162
163         case PVRSRV_HAP_MULTI_PROCESS:
164                 {
165                         psLinuxMemArea =
166                             NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
167                         if (!psLinuxMemArea)
168                                 return PVRSRV_ERROR_OUT_OF_MEMORY;
169                         PVRMMapRegisterArea(psLinuxMemArea);
170                         break;
171                 }
172         default:
173                 PVR_DPF(PVR_DBG_ERROR, "OSAllocPages: invalid flags 0x%x\n",
174                          ui32AllocFlags);
175                 *ppvCpuVAddr = NULL;
176                 *phOSMemHandle = (void *) 0;
177                 return PVRSRV_ERROR_INVALID_PARAMS;
178         }
179
180         if (ui32AllocFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED))
181                 inv_cache_mem_area(psLinuxMemArea);
182
183         *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
184         *phOSMemHandle = psLinuxMemArea;
185
186         return PVRSRV_OK;
187 }
188
189 enum PVRSRV_ERROR OSFreePages(u32 ui32AllocFlags, u32 ui32Bytes,
190                               void *pvCpuVAddr, void *hOSMemHandle)
191 {
192         struct LinuxMemArea *psLinuxMemArea;
193         PVR_UNREFERENCED_PARAMETER(ui32Bytes);
194         PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
195
196         psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
197
198         switch (ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) {
199         case PVRSRV_HAP_KERNEL_ONLY:
200                 break;
201         case PVRSRV_HAP_SINGLE_PROCESS:
202         case PVRSRV_HAP_MULTI_PROCESS:
203                 if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) != PVRSRV_OK) {
204                         PVR_DPF(PVR_DBG_ERROR,
205                         "OSFreePages(ui32AllocFlags=0x%08X, ui32Bytes=%ld, "
206                                  "pvCpuVAddr=%p, hOSMemHandle=%p) FAILED!",
207                                  ui32AllocFlags, ui32Bytes, pvCpuVAddr,
208                                  hOSMemHandle);
209                         return PVRSRV_ERROR_GENERIC;
210                 }
211                 break;
212         default:
213                 PVR_DPF(PVR_DBG_ERROR, "%s: invalid flags 0x%x\n",
214                          __func__, ui32AllocFlags);
215                 return PVRSRV_ERROR_INVALID_PARAMS;
216         }
217
218         LinuxMemAreaDeepFree(psLinuxMemArea);
219
220         return PVRSRV_OK;
221 }
222
223 enum PVRSRV_ERROR OSGetSubMemHandle(void *hOSMemHandle, u32 ui32ByteOffset,
224                                     u32 ui32Bytes, u32 ui32Flags,
225                                     void **phOSMemHandleRet)
226 {
227         struct LinuxMemArea *psParentLinuxMemArea, *psLinuxMemArea;
228         enum PVRSRV_ERROR eError;
229
230         psParentLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
231
232         psLinuxMemArea =
233             NewSubLinuxMemArea(psParentLinuxMemArea, ui32ByteOffset, ui32Bytes);
234         if (!psLinuxMemArea) {
235                 *phOSMemHandleRet = NULL;
236                 return PVRSRV_ERROR_OUT_OF_MEMORY;
237         }
238         *phOSMemHandleRet = psLinuxMemArea;
239
240         if (ui32Flags & PVRSRV_HAP_KERNEL_ONLY)
241                 return PVRSRV_OK;
242
243         eError = PVRMMapRegisterArea(psLinuxMemArea);
244                 if (eError != PVRSRV_OK)
245                         goto failed_register_area;
246
247         return PVRSRV_OK;
248
249 failed_register_area:
250         *phOSMemHandleRet = NULL;
251         LinuxMemAreaDeepFree(psLinuxMemArea);
252         return eError;
253 }
254
255 enum PVRSRV_ERROR OSReleaseSubMemHandle(void *hOSMemHandle, u32 ui32Flags)
256 {
257         struct LinuxMemArea *psLinuxMemArea;
258         enum PVRSRV_ERROR eError;
259
260         psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
261         PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC);
262
263         if ((ui32Flags & PVRSRV_HAP_KERNEL_ONLY) == 0) {
264                 eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
265                 if (eError != PVRSRV_OK)
266                         return eError;
267         }
268         LinuxMemAreaDeepFree(psLinuxMemArea);
269
270         return PVRSRV_OK;
271 }
272
273 struct IMG_CPU_PHYADDR OSMemHandleToCpuPAddr(void *hOSMemHandle,
274                                              u32 ui32ByteOffset)
275 {
276         PVR_ASSERT(hOSMemHandle);
277
278         return LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset);
279 }
280
281 void OSMemCopy(void *pvDst, void *pvSrc, u32 ui32Size)
282 {
283         memcpy(pvDst, pvSrc, ui32Size);
284 }
285
286 void OSMemSet(void *pvDest, u8 ui8Value, u32 ui32Size)
287 {
288         memset(pvDest, (int)ui8Value, (size_t) ui32Size);
289 }
290
291 char *OSStringCopy(char *pszDest, const char *pszSrc)
292 {
293         return strcpy(pszDest, pszSrc);
294 }
295
296 s32 OSSNPrintf(char *pStr, u32 ui32Size, const char *pszFormat, ...)
297 {
298         va_list argList;
299         s32 iCount;
300
301         va_start(argList, pszFormat);
302         iCount = vsnprintf(pStr, (size_t) ui32Size, pszFormat, argList);
303         va_end(argList);
304
305         return iCount;
306 }
307
308 enum PVRSRV_ERROR OSInitEnvData(void **ppvEnvSpecificData)
309 {
310         struct ENV_DATA *psEnvData;
311
312         if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
313              (void *)&psEnvData, NULL) != PVRSRV_OK)
314                 return PVRSRV_ERROR_GENERIC;
315
316         memset(psEnvData, 0, sizeof(*psEnvData));
317
318         if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
319              PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE,
320              &psEnvData->pvBridgeData, NULL) != PVRSRV_OK) {
321                 OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
322                           psEnvData, NULL);
323                 return PVRSRV_ERROR_GENERIC;
324         }
325
326         psEnvData->bMISRInstalled = IMG_FALSE;
327         psEnvData->bLISRInstalled = IMG_FALSE;
328
329         *ppvEnvSpecificData = psEnvData;
330
331         return PVRSRV_OK;
332 }
333
334 enum PVRSRV_ERROR OSDeInitEnvData(void *pvEnvSpecificData)
335 {
336         struct ENV_DATA *psEnvData = (struct ENV_DATA *)pvEnvSpecificData;
337
338         PVR_ASSERT(!psEnvData->bMISRInstalled);
339         PVR_ASSERT(!psEnvData->bLISRInstalled);
340
341         OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
342                   PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE,
343                   psEnvData->pvBridgeData, NULL);
344
345         OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
346                   pvEnvSpecificData, NULL);
347
348         return PVRSRV_OK;
349 }
350
351 void OSReleaseThreadQuanta(void)
352 {
353         schedule();
354 }
355
356 u32 OSClockus(void)
357 {
358         u32 time, j = jiffies;
359
360         time = j * (1000000 / HZ);
361
362         return time;
363 }
364
365 void OSWaitus(u32 ui32Timeus)
366 {
367         udelay(ui32Timeus);
368 }
369
370 u32 OSGetCurrentProcessIDKM(void)
371 {
372         if (in_interrupt())
373                 return KERNEL_ID;
374
375         return (u32) task_tgid_nr(current);
376 }
377
378 u32 OSGetPageSize(void)
379 {
380         return PAGE_SIZE;
381 }
382
383 static irqreturn_t DeviceISRWrapper(int irq, void *dev_id)
384 {
385         struct PVRSRV_DEVICE_NODE *psDeviceNode;
386         IMG_BOOL bStatus = IMG_FALSE;
387         PVR_UNREFERENCED_PARAMETER(irq);
388
389         psDeviceNode = (struct PVRSRV_DEVICE_NODE *)dev_id;
390         if (!psDeviceNode) {
391                 PVR_DPF(PVR_DBG_ERROR, "DeviceISRWrapper: invalid params\n");
392                 goto out;
393         }
394
395         bStatus = PVRSRVDeviceLISR(psDeviceNode);
396
397         if (bStatus) {
398                 struct SYS_DATA *psSysData = psDeviceNode->psSysData;
399                 struct ENV_DATA *psEnvData =
400                                 (struct ENV_DATA *)psSysData->pvEnvSpecificData;
401
402                 queue_work(psEnvData->psMISRWorkqueue, &psEnvData->sMISRWork);
403         }
404
405 out:
406         return bStatus ? IRQ_HANDLED : IRQ_NONE;
407 }
408
409 enum PVRSRV_ERROR OSInstallDeviceLISR(void *pvSysData,
410                                  u32 ui32Irq,
411                                  char *pszISRName, void *pvDeviceNode)
412 {
413         struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
414         struct ENV_DATA *psEnvData =
415                         (struct ENV_DATA *)psSysData->pvEnvSpecificData;
416
417         if (psEnvData->bLISRInstalled) {
418                 PVR_DPF(PVR_DBG_ERROR, "OSInstallDeviceLISR: "
419                         "An ISR has already been installed: IRQ %d cookie %x",
420                         psEnvData->ui32IRQ, psEnvData->pvISRCookie);
421                 return PVRSRV_ERROR_GENERIC;
422         }
423
424         PVR_TRACE("Installing device LISR %s on IRQ %d with cookie %x",
425                    pszISRName, ui32Irq, pvDeviceNode);
426
427         if (request_irq(ui32Irq, DeviceISRWrapper,
428                         IRQF_SHARED, pszISRName, pvDeviceNode)) {
429                 PVR_DPF(PVR_DBG_ERROR, "OSInstallDeviceLISR: "
430                                       "Couldn't install device LISR on IRQ %d",
431                          ui32Irq);
432
433                 return PVRSRV_ERROR_GENERIC;
434         }
435
436         psEnvData->ui32IRQ = ui32Irq;
437         psEnvData->pvISRCookie = pvDeviceNode;
438         psEnvData->bLISRInstalled = IMG_TRUE;
439
440         return PVRSRV_OK;
441 }
442
443 enum PVRSRV_ERROR OSUninstallDeviceLISR(void *pvSysData)
444 {
445         struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
446         struct ENV_DATA *psEnvData =
447                                 (struct ENV_DATA *)psSysData->pvEnvSpecificData;
448
449         if (!psEnvData->bLISRInstalled) {
450                 PVR_DPF(PVR_DBG_ERROR,
451                          "OSUninstallDeviceLISR: No LISR has been installed");
452                 return PVRSRV_ERROR_GENERIC;
453         }
454
455         PVR_TRACE("Uninstalling device LISR on IRQ %d with cookie %x",
456                    psEnvData->ui32IRQ, psEnvData->pvISRCookie);
457
458         free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie);
459
460         psEnvData->bLISRInstalled = IMG_FALSE;
461
462         return PVRSRV_OK;
463 }
464
465 static void MISRWrapper(struct work_struct *work)
466 {
467         struct ENV_DATA *psEnvData = container_of(work, struct ENV_DATA,
468                                                   sMISRWork);
469         struct SYS_DATA *psSysData = (struct SYS_DATA *)psEnvData->pvSysData;
470         PVRSRVMISR(psSysData);
471 }
472
473 enum PVRSRV_ERROR OSInstallMISR(void *pvSysData)
474 {
475         struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
476         struct ENV_DATA *psEnvData =
477                                 (struct ENV_DATA *)psSysData->pvEnvSpecificData;
478
479         if (psEnvData->bMISRInstalled) {
480                 PVR_DPF(PVR_DBG_ERROR,
481                          "OSInstallMISR: An MISR has already been installed");
482                 return PVRSRV_ERROR_GENERIC;
483         }
484
485         PVR_TRACE("Installing MISR with cookie %x", pvSysData);
486
487         psEnvData->pvSysData = pvSysData;
488         psEnvData->psMISRWorkqueue = create_singlethread_workqueue("sgx_misr");
489         INIT_WORK(&psEnvData->sMISRWork, MISRWrapper);
490
491         psEnvData->bMISRInstalled = IMG_TRUE;
492
493         return PVRSRV_OK;
494 }
495
496 enum PVRSRV_ERROR OSUninstallMISR(void *pvSysData)
497 {
498         struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
499         struct ENV_DATA *psEnvData =
500                                 (struct ENV_DATA *)psSysData->pvEnvSpecificData;
501
502         if (!psEnvData->bMISRInstalled) {
503                 PVR_DPF(PVR_DBG_ERROR,
504                          "OSUninstallMISR: No MISR has been installed");
505                 return PVRSRV_ERROR_GENERIC;
506         }
507
508         PVR_TRACE("Uninstalling MISR");
509
510         flush_workqueue(psEnvData->psMISRWorkqueue);
511         destroy_workqueue(psEnvData->psMISRWorkqueue);
512
513         psEnvData->bMISRInstalled = IMG_FALSE;
514
515         return PVRSRV_OK;
516 }
517
518 enum PVRSRV_ERROR OSScheduleMISR(void *pvSysData)
519 {
520         struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
521         struct ENV_DATA *psEnvData =
522                                 (struct ENV_DATA *)psSysData->pvEnvSpecificData;
523
524         if (psEnvData->bMISRInstalled)
525                 queue_work(psEnvData->psMISRWorkqueue, &psEnvData->sMISRWork);
526
527         return PVRSRV_OK;
528 }
529
530
531 struct IMG_CPU_PHYADDR OSMapLinToCPUPhys(void *pvLinAddr)
532 {
533         struct IMG_CPU_PHYADDR CpuPAddr;
534
535         CpuPAddr.uiAddr = (u32) VMallocToPhys(pvLinAddr);
536
537         return CpuPAddr;
538 }
539
540 void __iomem *OSMapPhysToLin(struct IMG_CPU_PHYADDR BasePAddr, u32 ui32Bytes,
541                          u32 ui32MappingFlags, void **phOSMemHandle)
542 {
543         if (phOSMemHandle)
544                 *phOSMemHandle = (void *) 0;
545
546         if (ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) {
547                 void __iomem *pvIORemapCookie;
548                 pvIORemapCookie =
549                     IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags);
550                 if (pvIORemapCookie == NULL)
551                         return NULL;
552                 return pvIORemapCookie;
553         } else {
554                 PVR_DPF(PVR_DBG_ERROR, "OSMapPhysToLin "
555                          "should only be used with PVRSRV_HAP_KERNEL_ONLY "
556                          "(Use OSReservePhys otherwise)");
557                 return NULL;
558         }
559
560 }
561
562 IMG_BOOL
563 OSUnMapPhysToLin(void __iomem *pvLinAddr, u32 ui32Bytes,
564                  u32 ui32MappingFlags, void *hPageAlloc)
565 {
566         PVR_TRACE("%s: unmapping %d bytes from 0x%08x", __func__,
567                    ui32Bytes, pvLinAddr);
568
569         PVR_UNREFERENCED_PARAMETER(hPageAlloc);
570         PVR_UNREFERENCED_PARAMETER(ui32Bytes);
571
572         if (ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) {
573                 IOUnmapWrapper(pvLinAddr);
574                 return IMG_TRUE;
575         } else {
576                 PVR_DPF(PVR_DBG_ERROR, "OSUnMapPhysToLin "
577                          "should only be used with PVRSRV_HAP_KERNEL_ONLY "
578                          " (Use OSUnReservePhys otherwise)");
579                 return IMG_FALSE;
580         }
581
582 }
583
584 static enum PVRSRV_ERROR RegisterExternalMem(struct IMG_SYS_PHYADDR *pBasePAddr,
585                     void *pvCPUVAddr, u32 ui32Bytes, IMG_BOOL bPhysContig,
586                     u32 ui32MappingFlags, void **phOSMemHandle)
587 {
588         struct LinuxMemArea *psLinuxMemArea;
589
590         switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
591         case PVRSRV_HAP_KERNEL_ONLY:
592                 {
593                         psLinuxMemArea =
594                             NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
595                                                       ui32Bytes, bPhysContig,
596                                                       ui32MappingFlags);
597
598                         if (!psLinuxMemArea)
599                                 return PVRSRV_ERROR_GENERIC;
600                         break;
601                 }
602         case PVRSRV_HAP_SINGLE_PROCESS:
603                 {
604                         psLinuxMemArea =
605                             NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
606                                                       ui32Bytes, bPhysContig,
607                                                       ui32MappingFlags);
608
609                         if (!psLinuxMemArea)
610                                 return PVRSRV_ERROR_GENERIC;
611                         PVRMMapRegisterArea(psLinuxMemArea);
612                         break;
613                 }
614         case PVRSRV_HAP_MULTI_PROCESS:
615                 {
616                         psLinuxMemArea =
617                             NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
618                                                       ui32Bytes, bPhysContig,
619                                                       ui32MappingFlags);
620
621                         if (!psLinuxMemArea)
622                                 return PVRSRV_ERROR_GENERIC;
623                         PVRMMapRegisterArea(psLinuxMemArea);
624                         break;
625                 }
626         default:
627                 PVR_DPF(PVR_DBG_ERROR, "OSRegisterMem : invalid flags 0x%x\n",
628                          ui32MappingFlags);
629                 *phOSMemHandle = (void *) 0;
630                 return PVRSRV_ERROR_GENERIC;
631         }
632
633         *phOSMemHandle = (void *) psLinuxMemArea;
634
635         return PVRSRV_OK;
636 }
637
638 enum PVRSRV_ERROR OSRegisterMem(struct IMG_CPU_PHYADDR BasePAddr,
639                                 void *pvCPUVAddr, u32 ui32Bytes,
640                                 u32 ui32MappingFlags, void **phOSMemHandle)
641 {
642         struct IMG_SYS_PHYADDR SysPAddr = SysCpuPAddrToSysPAddr(BasePAddr);
643
644         return RegisterExternalMem(&SysPAddr, pvCPUVAddr, ui32Bytes, IMG_TRUE,
645                                    ui32MappingFlags, phOSMemHandle);
646 }
647
648 enum PVRSRV_ERROR OSRegisterDiscontigMem(struct IMG_SYS_PHYADDR *pBasePAddr,
649                                          void *pvCPUVAddr, u32 ui32Bytes,
650                                          u32 ui32MappingFlags,
651                                          void **phOSMemHandle)
652 {
653         return RegisterExternalMem(pBasePAddr, pvCPUVAddr, ui32Bytes,
654                                    IMG_FALSE, ui32MappingFlags, phOSMemHandle);
655 }
656
657 enum PVRSRV_ERROR OSUnRegisterMem(void *pvCpuVAddr, u32 ui32Bytes,
658                                   u32 ui32MappingFlags, void *hOSMemHandle)
659 {
660         struct LinuxMemArea *psLinuxMemArea = (struct LinuxMemArea *)
661                                                                 hOSMemHandle;
662
663         PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
664         PVR_UNREFERENCED_PARAMETER(ui32Bytes);
665
666         switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
667         case PVRSRV_HAP_KERNEL_ONLY:
668                 break;
669         case PVRSRV_HAP_SINGLE_PROCESS:
670         case PVRSRV_HAP_MULTI_PROCESS:
671                 {
672                         if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) !=
673                             PVRSRV_OK) {
674                                 PVR_DPF(PVR_DBG_ERROR,
675                                          "%s(%p, %d, 0x%08X, %p) FAILED!",
676                                          __func__, pvCpuVAddr, ui32Bytes,
677                                          ui32MappingFlags, hOSMemHandle);
678                                 BUG();
679                                 return PVRSRV_ERROR_GENERIC;
680                         }
681                         break;
682                 }
683         default:
684                 {
685                         PVR_DPF(PVR_DBG_ERROR,
686                                  "OSUnRegisterMem : invalid flags 0x%x",
687                                  ui32MappingFlags);
688                         return PVRSRV_ERROR_INVALID_PARAMS;
689                 }
690         }
691
692         LinuxMemAreaDeepFree(psLinuxMemArea);
693
694         return PVRSRV_OK;
695 }
696
697 enum PVRSRV_ERROR OSUnRegisterDiscontigMem(void *pvCpuVAddr, u32 ui32Bytes,
698                                       u32 ui32Flags, void *hOSMemHandle)
699 {
700         return OSUnRegisterMem(pvCpuVAddr, ui32Bytes, ui32Flags, hOSMemHandle);
701 }
702
703 enum PVRSRV_ERROR OSReservePhys(struct IMG_CPU_PHYADDR BasePAddr,
704               u32 ui32Bytes, u32 ui32MappingFlags, void **ppvCpuVAddr,
705               void **phOSMemHandle)
706 {
707         struct LinuxMemArea *psLinuxMemArea;
708
709         switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
710         case PVRSRV_HAP_KERNEL_ONLY:
711                 {
712                         psLinuxMemArea =
713                             NewIORemapLinuxMemArea(BasePAddr, ui32Bytes,
714                                                    ui32MappingFlags);
715                         if (!psLinuxMemArea)
716                                 return PVRSRV_ERROR_GENERIC;
717                         break;
718                 }
719         case PVRSRV_HAP_SINGLE_PROCESS:
720                 {
721                         psLinuxMemArea =
722                             NewIOLinuxMemArea(BasePAddr, ui32Bytes,
723                                               ui32MappingFlags);
724                         if (!psLinuxMemArea)
725                                 return PVRSRV_ERROR_GENERIC;
726                         PVRMMapRegisterArea(psLinuxMemArea);
727                         break;
728                 }
729         case PVRSRV_HAP_MULTI_PROCESS:
730                 {
731                         psLinuxMemArea =
732                             NewIORemapLinuxMemArea(BasePAddr, ui32Bytes,
733                                                    ui32MappingFlags);
734                         if (!psLinuxMemArea)
735                                 return PVRSRV_ERROR_GENERIC;
736                         PVRMMapRegisterArea(psLinuxMemArea);
737                         break;
738                 }
739         default:
740                 PVR_DPF(PVR_DBG_ERROR, "OSMapPhysToLin : invalid flags 0x%x\n",
741                          ui32MappingFlags);
742                 *ppvCpuVAddr = NULL;
743                 *phOSMemHandle = (void *) 0;
744                 return PVRSRV_ERROR_GENERIC;
745         }
746
747         *phOSMemHandle = (void *) psLinuxMemArea;
748         *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
749
750         return PVRSRV_OK;
751 }
752
753 enum PVRSRV_ERROR OSUnReservePhys(void *pvCpuVAddr,
754                 u32 ui32Bytes, u32 ui32MappingFlags, void *hOSMemHandle)
755 {
756         struct LinuxMemArea *psLinuxMemArea;
757         PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
758         PVR_UNREFERENCED_PARAMETER(ui32Bytes);
759
760         psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
761
762         switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
763         case PVRSRV_HAP_KERNEL_ONLY:
764                 break;
765         case PVRSRV_HAP_SINGLE_PROCESS:
766         case PVRSRV_HAP_MULTI_PROCESS:
767                 {
768                         if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) !=
769                             PVRSRV_OK) {
770                                 PVR_DPF(PVR_DBG_ERROR,
771                                          "%s(%p, %d, 0x%08X, %p) FAILED!",
772                                          __func__, pvCpuVAddr, ui32Bytes,
773                                          ui32MappingFlags, hOSMemHandle);
774                                 return PVRSRV_ERROR_GENERIC;
775                         }
776                         break;
777                 }
778         default:
779                 {
780                         PVR_DPF(PVR_DBG_ERROR,
781                                  "OSUnMapPhysToLin : invalid flags 0x%x",
782                                  ui32MappingFlags);
783                         return PVRSRV_ERROR_INVALID_PARAMS;
784                 }
785         }
786
787         LinuxMemAreaDeepFree(psLinuxMemArea);
788
789         return PVRSRV_OK;
790 }
791
792 enum PVRSRV_ERROR OSBaseAllocContigMemory(u32 ui32Size, void **pvLinAddr,
793                                      struct IMG_CPU_PHYADDR *psPhysAddr)
794 {
795 #if !defined(NO_HARDWARE)
796         PVR_UNREFERENCED_PARAMETER(ui32Size);
797         PVR_UNREFERENCED_PARAMETER(pvLinAddr);
798         PVR_UNREFERENCED_PARAMETER(psPhysAddr);
799         PVR_DPF(PVR_DBG_ERROR, "%s: Not available", __func__);
800
801         return PVRSRV_ERROR_OUT_OF_MEMORY;
802 #else
803         void *pvKernLinAddr;
804
805 #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
806         pvKernLinAddr = _KMallocWrapper(ui32Size, __FILE__, __LINE__);
807 #else
808         pvKernLinAddr = KMallocWrapper(ui32Size);
809 #endif
810         if (!pvKernLinAddr)
811                 return PVRSRV_ERROR_OUT_OF_MEMORY;
812
813         *pvLinAddr = pvKernLinAddr;
814
815         psPhysAddr->uiAddr = virt_to_phys(pvKernLinAddr);
816
817         return PVRSRV_OK;
818 #endif
819 }
820
821 enum PVRSRV_ERROR OSBaseFreeContigMemory(u32 ui32Size, void *pvLinAddr,
822                                     struct IMG_CPU_PHYADDR psPhysAddr)
823 {
824 #if !defined(NO_HARDWARE)
825         PVR_UNREFERENCED_PARAMETER(ui32Size);
826         PVR_UNREFERENCED_PARAMETER(pvLinAddr);
827         PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr);
828
829         PVR_DPF(PVR_DBG_WARNING, "%s: Not available", __func__);
830 #else
831         PVR_UNREFERENCED_PARAMETER(ui32Size);
832         PVR_UNREFERENCED_PARAMETER(psPhysAddr.uiAddr);
833
834         KFreeWrapper(pvLinAddr);
835 #endif
836         return PVRSRV_OK;
837 }
838
839 u32 OSReadHWReg(void __iomem *pvLinRegBaseAddr, u32 ui32Offset)
840 {
841 #if !defined(NO_HARDWARE)
842         return (u32)readl(pvLinRegBaseAddr + ui32Offset);
843 #else
844         return *(u32 *)((u8 *) pvLinRegBaseAddr + ui32Offset);
845 #endif
846 }
847
848 void OSWriteHWReg(void __iomem *pvLinRegBaseAddr, u32 ui32Offset, u32 ui32Value)
849 {
850 #if !defined(NO_HARDWARE)
851         writel(ui32Value, pvLinRegBaseAddr + ui32Offset);
852 #else
853         *(u32 *)((u8 *)pvLinRegBaseAddr + ui32Offset) = ui32Value;
854 #endif
855 }
856
857 #define OS_MAX_TIMERS   8
858
859 struct TIMER_CALLBACK_DATA {
860         IMG_BOOL bInUse;
861         void (*pfnTimerFunc)(void *);
862         void *pvData;
863         struct timer_list sTimer;
864         u32 ui32Delay;
865         IMG_BOOL bActive;
866 };
867
868 static struct TIMER_CALLBACK_DATA sTimers[OS_MAX_TIMERS];
869 static DEFINE_SPINLOCK(sTimerStructLock);
870 static void OSTimerCallbackWrapper(unsigned long ui32Data)
871 {
872         struct TIMER_CALLBACK_DATA *psTimerCBData =
873                                         (struct TIMER_CALLBACK_DATA *)ui32Data;
874
875         if (!psTimerCBData->bActive)
876                 return;
877
878         psTimerCBData->pfnTimerFunc(psTimerCBData->pvData);
879
880         mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies);
881 }
882
883 void *OSAddTimer(void (*pfnTimerFunc)(void *), void *pvData, u32 ui32MsTimeout)
884 {
885         struct TIMER_CALLBACK_DATA *psTimerCBData;
886         u32 ui32i;
887         unsigned long ulLockFlags;
888
889         if (!pfnTimerFunc) {
890                 PVR_DPF(PVR_DBG_ERROR, "OSAddTimer: passed invalid callback");
891                 return NULL;
892         }
893
894         spin_lock_irqsave(&sTimerStructLock, ulLockFlags);
895         for (ui32i = 0; ui32i < OS_MAX_TIMERS; ui32i++) {
896                 psTimerCBData = &sTimers[ui32i];
897                 if (!psTimerCBData->bInUse) {
898                         psTimerCBData->bInUse = IMG_TRUE;
899                         break;
900                 }
901         }
902         spin_unlock_irqrestore(&sTimerStructLock, ulLockFlags);
903
904         if (ui32i >= OS_MAX_TIMERS) {
905                 PVR_DPF(PVR_DBG_ERROR, "OSAddTimer: all timers are in use");
906                 return NULL;
907         }
908
909         psTimerCBData->pfnTimerFunc = pfnTimerFunc;
910         psTimerCBData->pvData = pvData;
911         psTimerCBData->bActive = IMG_FALSE;
912
913         psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000)
914             ? 1 : ((HZ * ui32MsTimeout) / 1000);
915
916         init_timer(&psTimerCBData->sTimer);
917
918         psTimerCBData->sTimer.function = OSTimerCallbackWrapper;
919         psTimerCBData->sTimer.data = (u32) psTimerCBData;
920         psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies;
921
922         return (void *)(ui32i + 1);
923 }
924
925 static inline struct TIMER_CALLBACK_DATA *GetTimerStructure(void *hTimer)
926 {
927         u32 ui32i = ((u32) hTimer) - 1;
928         PVR_ASSERT(ui32i < OS_MAX_TIMERS);
929         return &sTimers[ui32i];
930 }
931
932 enum PVRSRV_ERROR OSRemoveTimer(void *hTimer)
933 {
934         struct TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
935
936         PVR_ASSERT(psTimerCBData->bInUse);
937         PVR_ASSERT(!psTimerCBData->bActive);
938
939         /* free timer callback data struct */
940         psTimerCBData->bInUse = IMG_FALSE;
941
942         return PVRSRV_OK;
943 }
944
945 enum PVRSRV_ERROR OSEnableTimer(void *hTimer)
946 {
947         struct TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
948
949         PVR_ASSERT(psTimerCBData->bInUse);
950         PVR_ASSERT(!psTimerCBData->bActive);
951
952         psTimerCBData->bActive = IMG_TRUE;
953
954         add_timer(&psTimerCBData->sTimer);
955
956         return PVRSRV_OK;
957 }
958
959 enum PVRSRV_ERROR OSDisableTimer(void *hTimer)
960 {
961         struct TIMER_CALLBACK_DATA *psTimerCBData = GetTimerStructure(hTimer);
962
963         PVR_ASSERT(psTimerCBData->bInUse);
964         PVR_ASSERT(psTimerCBData->bActive);
965
966         psTimerCBData->bActive = IMG_FALSE;
967
968         del_timer_sync(&psTimerCBData->sTimer);
969
970         return PVRSRV_OK;
971 }
972
973 enum PVRSRV_ERROR OSEventObjectCreate(const char *pszName,
974                                  struct PVRSRV_EVENTOBJECT *psEventObject)
975 {
976         enum PVRSRV_ERROR eError = PVRSRV_OK;
977
978         if (psEventObject) {
979                 if (pszName) {
980                         strncpy(psEventObject->szName, pszName,
981                                 EVENTOBJNAME_MAXLENGTH);
982                 } else {
983                         static u16 ui16NameIndex;
984                         snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH,
985                                  "PVRSRV_EVENTOBJECT_%d", ui16NameIndex++);
986                 }
987
988                 if (LinuxEventObjectListCreate(&psEventObject->hOSEventKM) !=
989                     PVRSRV_OK)
990                         eError = PVRSRV_ERROR_OUT_OF_MEMORY;
991
992         } else {
993                 PVR_DPF(PVR_DBG_ERROR, "OSEventObjectCreate: "
994                                         "psEventObject is not a valid pointer");
995                 eError = PVRSRV_ERROR_GENERIC;
996         }
997
998         return eError;
999
1000 }
1001
1002 enum PVRSRV_ERROR OSEventObjectDestroy(struct PVRSRV_EVENTOBJECT *psEventObject)
1003 {
1004         enum PVRSRV_ERROR eError = PVRSRV_OK;
1005
1006         if (psEventObject) {
1007                 if (psEventObject->hOSEventKM) {
1008                         LinuxEventObjectListDestroy(psEventObject->hOSEventKM);
1009                 } else {
1010                         PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
1011                                         "hOSEventKM is not a valid pointer");
1012                         eError = PVRSRV_ERROR_INVALID_PARAMS;
1013                 }
1014         } else {
1015                 PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
1016                                         "psEventObject is not a valid pointer");
1017                 eError = PVRSRV_ERROR_INVALID_PARAMS;
1018         }
1019
1020         return eError;
1021 }
1022
1023 enum PVRSRV_ERROR OSEventObjectWait(void *hOSEventKM)
1024 {
1025         enum PVRSRV_ERROR eError;
1026
1027         if (hOSEventKM) {
1028                 eError =
1029                     LinuxEventObjectWait(hOSEventKM, EVENT_OBJECT_TIMEOUT_MS);
1030         } else {
1031                 PVR_DPF(PVR_DBG_ERROR,
1032                          "OSEventObjectWait: hOSEventKM is not a valid handle");
1033                 eError = PVRSRV_ERROR_INVALID_PARAMS;
1034         }
1035
1036         return eError;
1037 }
1038
1039 enum PVRSRV_ERROR OSEventObjectOpen(struct PVRSRV_EVENTOBJECT *psEventObject,
1040                                void **phOSEvent)
1041 {
1042         enum PVRSRV_ERROR eError = PVRSRV_OK;
1043
1044         if (psEventObject) {
1045                 if (LinuxEventObjectAdd(psEventObject->hOSEventKM, phOSEvent) !=
1046                     PVRSRV_OK) {
1047                         PVR_DPF(PVR_DBG_ERROR, "LinuxEventObjectAdd: failed");
1048                         eError = PVRSRV_ERROR_INVALID_PARAMS;
1049                 }
1050
1051         } else {
1052                 PVR_DPF(PVR_DBG_ERROR, "OSEventObjectCreate: "
1053                                         "psEventObject is not a valid pointer");
1054                 eError = PVRSRV_ERROR_INVALID_PARAMS;
1055         }
1056
1057         return eError;
1058 }
1059
1060 enum PVRSRV_ERROR OSEventObjectClose(struct PVRSRV_EVENTOBJECT *psEventObject,
1061                                 void *hOSEventKM)
1062 {
1063         enum PVRSRV_ERROR eError = PVRSRV_OK;
1064
1065         if (psEventObject) {
1066                 if (LinuxEventObjectDelete
1067                     (psEventObject->hOSEventKM, hOSEventKM) != PVRSRV_OK) {
1068                         PVR_DPF(PVR_DBG_ERROR,
1069                                  "LinuxEventObjectDelete: failed");
1070                         eError = PVRSRV_ERROR_INVALID_PARAMS;
1071                 }
1072
1073         } else {
1074                 PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
1075                                         "psEventObject is not a valid pointer");
1076                 eError = PVRSRV_ERROR_INVALID_PARAMS;
1077         }
1078
1079         return eError;
1080
1081 }
1082
1083 enum PVRSRV_ERROR OSEventObjectSignal(void *hOSEventKM)
1084 {
1085         enum PVRSRV_ERROR eError;
1086
1087         if (hOSEventKM) {
1088                 eError = LinuxEventObjectSignal(hOSEventKM);
1089         } else {
1090                 PVR_DPF(PVR_DBG_ERROR, "OSEventObjectSignal: "
1091                                         "hOSEventKM is not a valid handle");
1092                 eError = PVRSRV_ERROR_INVALID_PARAMS;
1093         }
1094
1095         return eError;
1096 }
1097
1098 IMG_BOOL OSProcHasPrivSrvInit(void)
1099 {
1100         return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE;
1101 }
1102
1103 enum PVRSRV_ERROR OSCopyToUser(void *pvProcess, void __user *pvDest,
1104                                const void *pvSrc, u32 ui32Bytes)
1105 {
1106         PVR_UNREFERENCED_PARAMETER(pvProcess);
1107
1108         if (copy_to_user(pvDest, pvSrc, ui32Bytes) == 0)
1109                 return PVRSRV_OK;
1110         else
1111                 return PVRSRV_ERROR_GENERIC;
1112 }
1113
1114 enum PVRSRV_ERROR OSCopyFromUser(void *pvProcess, void *pvDest,
1115                                  const void __user *pvSrc, u32 ui32Bytes)
1116 {
1117         PVR_UNREFERENCED_PARAMETER(pvProcess);
1118
1119         if (copy_from_user(pvDest, pvSrc, ui32Bytes) == 0)
1120                 return PVRSRV_OK;
1121         else
1122                 return PVRSRV_ERROR_GENERIC;
1123 }
1124
1125 IMG_BOOL OSAccessOK(enum IMG_VERIFY_TEST eVerification,
1126                     const void __user *pvUserPtr, u32 ui32Bytes)
1127 {
1128         int linuxType;
1129
1130         if (eVerification == PVR_VERIFY_READ) {
1131                 linuxType = VERIFY_READ;
1132         } else {
1133                 PVR_ASSERT(eVerification == PVR_VERIFY_WRITE);
1134                 linuxType = VERIFY_WRITE;
1135         }
1136
1137         return access_ok(linuxType, pvUserPtr, ui32Bytes);
1138 }
1139
1140 enum eWrapMemType {
1141         WRAP_TYPE_CLEANUP,
1142         WRAP_TYPE_GET_USER_PAGES,
1143         WRAP_TYPE_FIND_VMA_PAGES,
1144         WRAP_TYPE_FIND_VMA_PFN
1145 };
1146
1147 struct sWrapMemInfo {
1148         enum eWrapMemType eType;
1149         int iNumPages;
1150         struct page **ppsPages;
1151         struct IMG_SYS_PHYADDR *psPhysAddr;
1152         int iPageOffset;
1153         int iContiguous;
1154 #if defined(CONFIG_PVR_DEBUG_EXTRA)
1155         u32 ulStartAddr;
1156         u32 ulBeyondEndAddr;
1157         struct vm_area_struct *psVMArea;
1158 #endif
1159 };
1160
1161 static void CheckPagesContiguous(struct sWrapMemInfo *psInfo)
1162 {
1163         int i;
1164         u32 ui32AddrChk;
1165
1166         BUG_ON(psInfo == NULL);
1167
1168         psInfo->iContiguous = 1;
1169
1170         for (i = 0, ui32AddrChk = psInfo->psPhysAddr[0].uiAddr;
1171              i < psInfo->iNumPages; i++, ui32AddrChk += PAGE_SIZE)
1172                 if (psInfo->psPhysAddr[i].uiAddr != ui32AddrChk) {
1173                         psInfo->iContiguous = 0;
1174                         break;
1175                 }
1176 }
1177
1178 static struct page *CPUVAddrToPage(struct vm_area_struct *psVMArea,
1179                                    u32 ulCPUVAddr)
1180 {
1181         pgd_t *psPGD;
1182         pud_t *psPUD;
1183         pmd_t *psPMD;
1184         pte_t *psPTE;
1185         struct mm_struct *psMM = psVMArea->vm_mm;
1186         u32 ulPFN;
1187         spinlock_t *psPTLock;
1188         struct page *psPage;
1189
1190         psPGD = pgd_offset(psMM, ulCPUVAddr);
1191         if (pgd_none(*psPGD) || pgd_bad(*psPGD))
1192                 return NULL;
1193
1194         psPUD = pud_offset(psPGD, ulCPUVAddr);
1195         if (pud_none(*psPUD) || pud_bad(*psPUD))
1196                 return NULL;
1197
1198         psPMD = pmd_offset(psPUD, ulCPUVAddr);
1199         if (pmd_none(*psPMD) || pmd_bad(*psPMD))
1200                 return NULL;
1201
1202         psPage = NULL;
1203
1204         psPTE = (pte_t *)pte_offset_map_lock(psMM, psPMD, ulCPUVAddr,
1205                                              &psPTLock);
1206         if ((pte_none(*psPTE) != 0) || (pte_present(*psPTE) == 0) ||
1207             (pte_write(*psPTE) == 0))
1208                 goto exit_unlock;
1209
1210         ulPFN = pte_pfn(*psPTE);
1211         if (!pfn_valid(ulPFN))
1212                 goto exit_unlock;
1213
1214         psPage = pfn_to_page(ulPFN);
1215
1216         get_page(psPage);
1217
1218 exit_unlock:
1219         pte_unmap_unlock(psPTE, psPTLock);
1220
1221         return psPage;
1222 }
1223
1224 enum PVRSRV_ERROR OSReleasePhysPageAddr(void *hOSWrapMem)
1225 {
1226         struct sWrapMemInfo *psInfo = (struct sWrapMemInfo *)hOSWrapMem;
1227         int i;
1228
1229         BUG_ON(psInfo == NULL);
1230
1231         switch (psInfo->eType) {
1232         case WRAP_TYPE_CLEANUP:
1233                 break;
1234         case WRAP_TYPE_FIND_VMA_PFN:
1235                 break;
1236         case WRAP_TYPE_GET_USER_PAGES:
1237                 {
1238                         for (i = 0; i < psInfo->iNumPages; i++) {
1239                                 struct page *psPage = psInfo->ppsPages[i];
1240
1241                                 if (!PageReserved(psPage))
1242                                         SetPageDirty(psPage);
1243                                 page_cache_release(psPage);
1244                         }
1245                         break;
1246                 }
1247         case WRAP_TYPE_FIND_VMA_PAGES:
1248                 {
1249                         for (i = 0; i < psInfo->iNumPages; i++)
1250                                 put_page_testzero(psInfo->ppsPages[i]);
1251                         break;
1252                 }
1253         default:
1254                 {
1255                         PVR_DPF(PVR_DBG_ERROR,
1256                                 "OSReleasePhysPageAddr: Unknown wrap type (%d)",
1257                                  psInfo->eType);
1258                         return PVRSRV_ERROR_GENERIC;
1259                 }
1260         }
1261
1262         if (psInfo->ppsPages != NULL)
1263                 kfree(psInfo->ppsPages);
1264
1265         if (psInfo->psPhysAddr != NULL)
1266                 kfree(psInfo->psPhysAddr);
1267
1268         kfree(psInfo);
1269
1270         return PVRSRV_OK;
1271 }
1272
1273 enum PVRSRV_ERROR OSAcquirePhysPageAddr(void *pvCPUVAddr, u32 ui32Bytes,
1274                                         struct IMG_SYS_PHYADDR *psSysPAddr,
1275                                         void **phOSWrapMem)
1276 {
1277         u32 ulStartAddrOrig = (u32) pvCPUVAddr;
1278         u32 ulAddrRangeOrig = (u32) ui32Bytes;
1279         u32 ulBeyondEndAddrOrig = ulStartAddrOrig + ulAddrRangeOrig;
1280         u32 ulStartAddr;
1281         u32 ulAddrRange;
1282         u32 ulBeyondEndAddr;
1283         u32 ulAddr;
1284         int iNumPagesMapped;
1285         int i;
1286         struct vm_area_struct *psVMArea;
1287         struct sWrapMemInfo *psInfo;
1288
1289         ulStartAddr = ulStartAddrOrig & PAGE_MASK;
1290         ulBeyondEndAddr = PAGE_ALIGN(ulBeyondEndAddrOrig);
1291         ulAddrRange = ulBeyondEndAddr - ulStartAddr;
1292
1293         psInfo = kmalloc(sizeof(*psInfo), GFP_KERNEL);
1294         if (psInfo == NULL) {
1295                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1296                                 "Couldn't allocate information structure");
1297                 return PVRSRV_ERROR_OUT_OF_MEMORY;
1298         }
1299         memset(psInfo, 0, sizeof(*psInfo));
1300
1301 #if defined(CONFIG_PVR_DEBUG_EXTRA)
1302         psInfo->ulStartAddr = ulStartAddrOrig;
1303         psInfo->ulBeyondEndAddr = ulBeyondEndAddrOrig;
1304 #endif
1305
1306         psInfo->iNumPages = (int)(ulAddrRange >> PAGE_SHIFT);
1307         psInfo->iPageOffset = (int)(ulStartAddrOrig & ~PAGE_MASK);
1308
1309         psInfo->psPhysAddr =
1310             kmalloc((size_t) psInfo->iNumPages * sizeof(*psInfo->psPhysAddr),
1311                     GFP_KERNEL);
1312         if (psInfo->psPhysAddr == NULL) {
1313                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1314                                         "Couldn't allocate page array");
1315                 goto error_free;
1316         }
1317
1318         psInfo->ppsPages =
1319             kmalloc((size_t) psInfo->iNumPages * sizeof(*psInfo->ppsPages),
1320                     GFP_KERNEL);
1321         if (psInfo->ppsPages == NULL) {
1322                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1323                                         "Couldn't allocate page array");
1324                 goto error_free;
1325         }
1326
1327         down_read(&current->mm->mmap_sem);
1328
1329         iNumPagesMapped = get_user_pages(current, current->mm, ulStartAddr,
1330                                          psInfo->iNumPages, 1, 0,
1331                                          psInfo->ppsPages, NULL);
1332         up_read(&current->mm->mmap_sem);
1333
1334
1335         if (iNumPagesMapped >= 0) {
1336                 if (iNumPagesMapped != psInfo->iNumPages) {
1337                         PVR_TRACE("OSAcquirePhysPageAddr: "
1338                                   "Couldn't map all the pages needed "
1339                                   "(wanted: %d, got %d)",
1340                                psInfo->iNumPages, iNumPagesMapped);
1341
1342                         for (i = 0; i < iNumPagesMapped; i++)
1343                                 page_cache_release(psInfo->ppsPages[i]);
1344
1345                         goto error_free;
1346                 }
1347
1348                 for (i = 0; i < psInfo->iNumPages; i++) {
1349                         struct IMG_CPU_PHYADDR CPUPhysAddr;
1350
1351                         CPUPhysAddr.uiAddr =
1352                             page_to_pfn(psInfo->ppsPages[i]) << PAGE_SHIFT;
1353                         psInfo->psPhysAddr[i] =
1354                             SysCpuPAddrToSysPAddr(CPUPhysAddr);
1355                         psSysPAddr[i] = psInfo->psPhysAddr[i];
1356
1357                 }
1358
1359                 psInfo->eType = WRAP_TYPE_GET_USER_PAGES;
1360
1361                 goto exit_check;
1362         }
1363
1364         PVR_TRACE("OSAcquirePhysPageAddr: "
1365                   "get_user_pages failed (%d), trying something else",
1366                             iNumPagesMapped);
1367
1368         down_read(&current->mm->mmap_sem);
1369
1370         psVMArea = find_vma(current->mm, ulStartAddrOrig);
1371         if (psVMArea == NULL) {
1372                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1373                                         "Couldn't find memory region "
1374                                         "containing start address %lx",
1375                        ulStartAddrOrig);
1376
1377                 goto error_release_mmap_sem;
1378         }
1379 #if defined(CONFIG_PVR_DEBUG_EXTRA)
1380         psInfo->psVMArea = psVMArea;
1381 #endif
1382
1383         if (ulStartAddrOrig < psVMArea->vm_start) {
1384                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1385                                 "Start address %lx is outside of the "
1386                                 "region returned by find_vma",
1387                        ulStartAddrOrig);
1388                 goto error_release_mmap_sem;
1389         }
1390
1391         if (ulBeyondEndAddrOrig > psVMArea->vm_end) {
1392                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1393                                 "End address %lx is outside of the region "
1394                                 "returned by find_vma",
1395                        ulBeyondEndAddrOrig);
1396                 goto error_release_mmap_sem;
1397         }
1398
1399         if ((psVMArea->vm_flags & (VM_IO | VM_RESERVED)) !=
1400             (VM_IO | VM_RESERVED)) {
1401                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1402                                 "Memory region does not represent memory "
1403                                 "mapped I/O (VMA flags: 0x%lx)",
1404                        psVMArea->vm_flags);
1405                 goto error_release_mmap_sem;
1406         }
1407
1408         if ((psVMArea->vm_flags & (VM_READ | VM_WRITE)) !=
1409             (VM_READ | VM_WRITE)) {
1410                 PVR_DPF(PVR_DBG_ERROR, "OSAcquirePhysPageAddr: "
1411                                         "No read/write access to memory region "
1412                                         "(VMA flags: 0x%lx)",
1413                        psVMArea->vm_flags);
1414                 goto error_release_mmap_sem;
1415         }
1416
1417         for (ulAddr = ulStartAddrOrig, i = 0; ulAddr < ulBeyondEndAddrOrig;
1418              ulAddr += PAGE_SIZE, i++) {
1419                 struct page *psPage;
1420
1421                 BUG_ON(i >= psInfo->iNumPages);
1422
1423                 psPage = CPUVAddrToPage(psVMArea, ulAddr);
1424                 if (psPage == NULL) {
1425                         int j;
1426
1427                         PVR_TRACE("OSAcquirePhysPageAddr: "
1428                                 "Couldn't lookup page structure "
1429                                 "for address 0x%lx, trying something else",
1430                                ulAddr);
1431
1432                         for (j = 0; j < i; j++)
1433                                 put_page_testzero(psInfo->ppsPages[j]);
1434                         break;
1435                 }
1436
1437                 psInfo->ppsPages[i] = psPage;
1438         }
1439
1440         BUG_ON(i > psInfo->iNumPages);
1441         if (i == psInfo->iNumPages) {
1442                 for (i = 0; i < psInfo->iNumPages; i++) {
1443                         struct page *psPage = psInfo->ppsPages[i];
1444                         struct IMG_CPU_PHYADDR CPUPhysAddr;
1445
1446                         CPUPhysAddr.uiAddr = page_to_pfn(psPage) << PAGE_SHIFT;
1447
1448                         psInfo->psPhysAddr[i] =
1449                             SysCpuPAddrToSysPAddr(CPUPhysAddr);
1450                         psSysPAddr[i] = psInfo->psPhysAddr[i];
1451                 }
1452
1453                 psInfo->eType = WRAP_TYPE_FIND_VMA_PAGES;
1454         } else {
1455
1456                 if ((psVMArea->vm_flags & VM_PFNMAP) == 0) {
1457                         PVR_DPF(PVR_DBG_WARNING, "OSAcquirePhysPageAddr: "
1458                                         "Region isn't a raw PFN mapping.  "
1459                                         "Giving up.");
1460                         goto error_release_mmap_sem;
1461                 }
1462
1463                 for (ulAddr = ulStartAddrOrig, i = 0;
1464                      ulAddr < ulBeyondEndAddrOrig; ulAddr += PAGE_SIZE, i++) {
1465                         struct IMG_CPU_PHYADDR CPUPhysAddr;
1466
1467                         CPUPhysAddr.uiAddr = ((ulAddr - psVMArea->vm_start) +
1468                              (psVMArea->vm_pgoff << PAGE_SHIFT)) & PAGE_MASK;
1469
1470                         psInfo->psPhysAddr[i] =
1471                             SysCpuPAddrToSysPAddr(CPUPhysAddr);
1472                         psSysPAddr[i] = psInfo->psPhysAddr[i];
1473                 }
1474                 BUG_ON(i != psInfo->iNumPages);
1475
1476                 psInfo->eType = WRAP_TYPE_FIND_VMA_PFN;
1477
1478                 PVR_DPF(PVR_DBG_WARNING, "OSAcquirePhysPageAddr: "
1479                                                 "Region can't be locked down");
1480         }
1481
1482         up_read(&current->mm->mmap_sem);
1483
1484 exit_check:
1485         CheckPagesContiguous(psInfo);
1486
1487         *phOSWrapMem = (void *) psInfo;
1488
1489         return PVRSRV_OK;
1490
1491 error_release_mmap_sem:
1492         up_read(&current->mm->mmap_sem);
1493
1494 error_free:
1495         psInfo->eType = WRAP_TYPE_CLEANUP;
1496         OSReleasePhysPageAddr((void *)psInfo);
1497         return PVRSRV_ERROR_GENERIC;
1498 }