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