aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/pvr/mmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/pvr/mmap.c')
-rw-r--r--drivers/gpu/pvr/mmap.c1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/drivers/gpu/pvr/mmap.c b/drivers/gpu/pvr/mmap.c
new file mode 100644
index 00000000000..66cef26e522
--- /dev/null
+++ b/drivers/gpu/pvr/mmap.c
@@ -0,0 +1,1132 @@
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/mm.h>
33#include <linux/module.h>
34#include <linux/vmalloc.h>
35#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
36#include <linux/wrapper.h>
37#endif
38#include <linux/slab.h>
39#include <asm/io.h>
40#include <asm/page.h>
41#include <asm/shmparam.h>
42#include <asm/pgtable.h>
43#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22))
44#include <linux/sched.h>
45#include <asm/current.h>
46#endif
47#if defined(SUPPORT_DRI_DRM)
48#include <drm/drmP.h>
49#endif
50
51#include "img_defs.h"
52#include "services.h"
53#include "servicesint.h"
54#include "pvrmmap.h"
55#include "mutils.h"
56#include "mmap.h"
57#include "mm.h"
58#include "pvr_debug.h"
59#include "osfunc.h"
60#include "proc.h"
61#include "mutex.h"
62#include "handle.h"
63#include "perproc.h"
64#include "env_perproc.h"
65#include "bridged_support.h"
66#if defined(SUPPORT_DRI_DRM)
67#include "pvr_drm.h"
68#endif
69
70#if !defined(PVR_SECURE_HANDLES)
71#error "The mmap code requires PVR_SECURE_HANDLES"
72#endif
73
74static PVRSRV_LINUX_MUTEX g_sMMapMutex;
75
76static LinuxKMemCache *g_psMemmapCache = NULL;
77static LIST_HEAD(g_sMMapAreaList);
78static LIST_HEAD(g_sMMapOffsetStructList);
79#if defined(DEBUG_LINUX_MMAP_AREAS)
80static IMG_UINT32 g_ui32RegisteredAreas = 0;
81static IMG_UINT32 g_ui32TotalByteSize = 0;
82#endif
83
84
85#if defined(DEBUG_LINUX_MMAP_AREAS)
86static struct proc_dir_entry *g_ProcMMap;
87#endif
88
89#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
90#define MMAP2_PGOFF_RESOLUTION (32-PAGE_SHIFT+12)
91#define RESERVED_PGOFF_BITS 1
92#define MAX_MMAP_HANDLE ((1UL<<(MMAP2_PGOFF_RESOLUTION-RESERVED_PGOFF_BITS))-1)
93
94#define FIRST_PHYSICAL_PFN 0
95#define LAST_PHYSICAL_PFN (FIRST_PHYSICAL_PFN + MAX_MMAP_HANDLE)
96#define FIRST_SPECIAL_PFN (LAST_PHYSICAL_PFN + 1)
97#define LAST_SPECIAL_PFN (FIRST_SPECIAL_PFN + MAX_MMAP_HANDLE)
98
99#else
100
101#if PAGE_SHIFT != 12
102#error This build variant has not yet been made non-4KB page-size aware
103#endif
104
105#if defined(PVR_MMAP_OFFSET_BASE)
106#define FIRST_SPECIAL_PFN PVR_MMAP_OFFSET_BASE
107#else
108#define FIRST_SPECIAL_PFN 0x80000000UL
109#endif
110
111#if defined(PVR_NUM_MMAP_HANDLES)
112#define MAX_MMAP_HANDLE PVR_NUM_MMAP_HANDLES
113#else
114#define MAX_MMAP_HANDLE 0x7fffffffUL
115#endif
116
117#endif
118
119#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
120static inline IMG_BOOL
121PFNIsPhysical(IMG_UINT32 pfn)
122{
123
124 return ( (pfn <= LAST_PHYSICAL_PFN)) ? IMG_TRUE : IMG_FALSE;
125}
126
127static inline IMG_BOOL
128PFNIsSpecial(IMG_UINT32 pfn)
129{
130
131 return ((pfn >= FIRST_SPECIAL_PFN) ) ? IMG_TRUE : IMG_FALSE;
132}
133#endif
134
135#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
136static inline IMG_HANDLE
137MMapOffsetToHandle(IMG_UINT32 pfn)
138{
139 if (PFNIsPhysical(pfn))
140 {
141 PVR_ASSERT(PFNIsPhysical(pfn));
142 return IMG_NULL;
143 }
144 return (IMG_HANDLE)(pfn - FIRST_SPECIAL_PFN);
145}
146#endif
147
148static inline IMG_UINT32
149HandleToMMapOffset(IMG_HANDLE hHandle)
150{
151 IMG_UINT32 ulHandle = (IMG_UINT32)hHandle;
152
153#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
154 if (PFNIsSpecial(ulHandle))
155 {
156 PVR_ASSERT(PFNIsSpecial(ulHandle));
157 return 0;
158 }
159#endif
160 return ulHandle + FIRST_SPECIAL_PFN;
161}
162
163#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
164static inline IMG_BOOL
165LinuxMemAreaUsesPhysicalMap(LinuxMemArea *psLinuxMemArea)
166{
167 return LinuxMemAreaPhysIsContig(psLinuxMemArea);
168}
169#endif
170
171#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
172static inline IMG_UINT32
173GetCurrentThreadID(IMG_VOID)
174{
175
176 return (IMG_UINT32)current->pid;
177}
178#endif
179
180static PKV_OFFSET_STRUCT
181CreateOffsetStruct(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize)
182{
183 PKV_OFFSET_STRUCT psOffsetStruct;
184#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS)
185 const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea));
186#endif
187
188#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS)
189 PVR_DPF((PVR_DBG_MESSAGE,
190 "%s(%s, psLinuxMemArea: 0x%p, ui32AllocFlags: 0x%8x)",
191 __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags));
192#endif
193
194 PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC);
195
196 PVR_ASSERT(psLinuxMemArea->bMMapRegistered);
197
198 psOffsetStruct = KMemCacheAllocWrapper(g_psMemmapCache, GFP_KERNEL);
199 if(psOffsetStruct == IMG_NULL)
200 {
201 PVR_DPF((PVR_DBG_ERROR,"PVRMMapRegisterArea: Couldn't alloc another mapping record from cache"));
202 return IMG_NULL;
203 }
204
205 psOffsetStruct->ui32MMapOffset = ui32Offset;
206
207 psOffsetStruct->psLinuxMemArea = psLinuxMemArea;
208
209 psOffsetStruct->ui32RealByteSize = ui32RealByteSize;
210
211
212#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
213 psOffsetStruct->ui32TID = GetCurrentThreadID();
214#endif
215 psOffsetStruct->ui32PID = OSGetCurrentProcessIDKM();
216
217#if defined(DEBUG_LINUX_MMAP_AREAS)
218
219 psOffsetStruct->pszName = pszName;
220#endif
221
222 list_add_tail(&psOffsetStruct->sAreaItem, &psLinuxMemArea->sMMapOffsetStructList);
223
224 return psOffsetStruct;
225}
226
227
228static IMG_VOID
229DestroyOffsetStruct(PKV_OFFSET_STRUCT psOffsetStruct)
230{
231#ifdef DEBUG
232 IMG_CPU_PHYADDR CpuPAddr;
233 CpuPAddr = LinuxMemAreaToCpuPAddr(psOffsetStruct->psLinuxMemArea, 0);
234#endif
235
236 list_del(&psOffsetStruct->sAreaItem);
237
238 if (psOffsetStruct->bOnMMapList)
239 {
240 list_del(&psOffsetStruct->sMMapItem);
241 }
242
243#ifdef DEBUG
244 PVR_DPF((PVR_DBG_MESSAGE, "%s: Table entry: "
245 "psLinuxMemArea=%p, CpuPAddr=0x%08X", __FUNCTION__,
246 psOffsetStruct->psLinuxMemArea,
247 CpuPAddr.uiAddr));
248#endif
249
250 KMemCacheFreeWrapper(g_psMemmapCache, psOffsetStruct);
251}
252
253
254static inline IMG_VOID
255DetermineUsersSizeAndByteOffset(LinuxMemArea *psLinuxMemArea,
256 IMG_UINT32 *pui32RealByteSize,
257 IMG_UINT32 *pui32ByteOffset)
258{
259 IMG_UINT32 ui32PageAlignmentOffset;
260 IMG_CPU_PHYADDR CpuPAddr;
261
262 CpuPAddr = LinuxMemAreaToCpuPAddr(psLinuxMemArea, 0);
263 ui32PageAlignmentOffset = ADDR_TO_PAGE_OFFSET(CpuPAddr.uiAddr);
264
265 *pui32ByteOffset = ui32PageAlignmentOffset;
266
267 *pui32RealByteSize = PAGE_ALIGN(psLinuxMemArea->ui32ByteSize + ui32PageAlignmentOffset);
268}
269
270
271PVRSRV_ERROR
272PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc,
273 IMG_HANDLE hMHandle,
274 IMG_UINT32 *pui32MMapOffset,
275 IMG_UINT32 *pui32ByteOffset,
276 IMG_UINT32 *pui32RealByteSize,
277 IMG_UINT32 *pui32UserVAddr)
278{
279 LinuxMemArea *psLinuxMemArea;
280 PKV_OFFSET_STRUCT psOffsetStruct;
281 IMG_HANDLE hOSMemHandle;
282 PVRSRV_ERROR eError;
283
284 LinuxLockMutex(&g_sMMapMutex);
285
286 PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE);
287
288 eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle);
289 if (eError != PVRSRV_OK)
290 {
291 PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle));
292
293 goto exit_unlock;
294 }
295
296 psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
297
298 DetermineUsersSizeAndByteOffset(psLinuxMemArea,
299 pui32RealByteSize,
300 pui32ByteOffset);
301
302
303 list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem)
304 {
305 if (psPerProc->ui32PID == psOffsetStruct->ui32PID)
306 {
307
308 PVR_ASSERT(*pui32RealByteSize == psOffsetStruct->ui32RealByteSize);
309
310 *pui32MMapOffset = psOffsetStruct->ui32MMapOffset;
311 *pui32UserVAddr = psOffsetStruct->ui32UserVAddr;
312 psOffsetStruct->ui32RefCount++;
313
314 eError = PVRSRV_OK;
315 goto exit_unlock;
316 }
317 }
318
319
320 *pui32UserVAddr = 0;
321
322#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
323 if (LinuxMemAreaUsesPhysicalMap(psLinuxMemArea))
324 {
325 *pui32MMapOffset = LinuxMemAreaToCpuPFN(psLinuxMemArea, 0);
326 PVR_ASSERT(PFNIsPhysical(*pui32MMapOffset));
327 }
328 else
329#endif
330 {
331 *pui32MMapOffset = HandleToMMapOffset(hMHandle);
332#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
333 PVR_ASSERT(PFNIsSpecial(*pui32MMapOffset));
334#endif
335 }
336
337 psOffsetStruct = CreateOffsetStruct(psLinuxMemArea, *pui32MMapOffset, *pui32RealByteSize);
338 if (psOffsetStruct == IMG_NULL)
339 {
340 eError = PVRSRV_ERROR_OUT_OF_MEMORY;
341 goto exit_unlock;
342 }
343
344
345 list_add_tail(&psOffsetStruct->sMMapItem, &g_sMMapOffsetStructList);
346
347 psOffsetStruct->bOnMMapList = IMG_TRUE;
348
349 psOffsetStruct->ui32RefCount++;
350
351 eError = PVRSRV_OK;
352
353
354
355
356 *pui32MMapOffset = *pui32MMapOffset << (PAGE_SHIFT - 12);
357
358exit_unlock:
359 LinuxUnLockMutex(&g_sMMapMutex);
360
361 return eError;
362}
363
364
365PVRSRV_ERROR
366PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc,
367 IMG_HANDLE hMHandle,
368 IMG_BOOL *pbMUnmap,
369 IMG_UINT32 *pui32RealByteSize,
370 IMG_UINT32 *pui32UserVAddr)
371{
372 LinuxMemArea *psLinuxMemArea;
373 PKV_OFFSET_STRUCT psOffsetStruct;
374 IMG_HANDLE hOSMemHandle;
375 PVRSRV_ERROR eError;
376 IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM();
377
378 LinuxLockMutex(&g_sMMapMutex);
379
380 PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE);
381
382 eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle);
383 if (eError != PVRSRV_OK)
384 {
385 PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle));
386
387 goto exit_unlock;
388 }
389
390 psLinuxMemArea = (LinuxMemArea *)hOSMemHandle;
391
392
393 list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem)
394 {
395 if (psOffsetStruct->ui32PID == ui32PID)
396 {
397 if (psOffsetStruct->ui32RefCount == 0)
398 {
399 PVR_DPF((PVR_DBG_ERROR, "%s: Attempt to release mmap data with zero reference count for offset struct 0x%p, memory area %p", __FUNCTION__, psOffsetStruct, psLinuxMemArea));
400 eError = PVRSRV_ERROR_STILL_MAPPED;
401 goto exit_unlock;
402 }
403
404 psOffsetStruct->ui32RefCount--;
405
406 *pbMUnmap = (IMG_BOOL)((psOffsetStruct->ui32RefCount == 0) && (psOffsetStruct->ui32UserVAddr != 0));
407
408 *pui32UserVAddr = (*pbMUnmap) ? psOffsetStruct->ui32UserVAddr : 0;
409 *pui32RealByteSize = (*pbMUnmap) ? psOffsetStruct->ui32RealByteSize : 0;
410
411 eError = PVRSRV_OK;
412 goto exit_unlock;
413 }
414 }
415
416
417 PVR_DPF((PVR_DBG_ERROR, "%s: Mapping data not found for handle %p (memory area %p)", __FUNCTION__, hMHandle, psLinuxMemArea));
418
419 eError = PVRSRV_ERROR_MAPPING_NOT_FOUND;
420
421exit_unlock:
422 LinuxUnLockMutex(&g_sMMapMutex);
423
424 return eError;
425}
426
427static inline PKV_OFFSET_STRUCT
428FindOffsetStructByOffset(IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize)
429{
430 PKV_OFFSET_STRUCT psOffsetStruct;
431#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
432 IMG_UINT32 ui32TID = GetCurrentThreadID();
433#endif
434 IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM();
435
436 list_for_each_entry(psOffsetStruct, &g_sMMapOffsetStructList, sMMapItem)
437 {
438 if (ui32Offset == psOffsetStruct->ui32MMapOffset && ui32RealByteSize == psOffsetStruct->ui32RealByteSize && psOffsetStruct->ui32PID == ui32PID)
439 {
440#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
441
442 if (!PFNIsPhysical(ui32Offset) || psOffsetStruct->ui32TID == ui32TID)
443#endif
444 {
445 return psOffsetStruct;
446 }
447 }
448 }
449
450 return IMG_NULL;
451}
452
453
454static IMG_BOOL
455DoMapToUser(LinuxMemArea *psLinuxMemArea,
456 struct vm_area_struct* ps_vma,
457 IMG_UINT32 ui32ByteOffset)
458{
459 IMG_UINT32 ui32ByteSize;
460
461 if (psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC)
462 {
463 return DoMapToUser(LinuxMemAreaRoot(psLinuxMemArea),
464 ps_vma,
465 psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + ui32ByteOffset);
466 }
467
468
469 ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start;
470 PVR_ASSERT(ADDR_TO_PAGE_OFFSET(ui32ByteSize) == 0);
471
472#if defined (__sparc__)
473
474#error "SPARC not supported"
475#endif
476
477#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
478 if (PFNIsPhysical(ps_vma->vm_pgoff))
479 {
480 IMG_INT result;
481
482 PVR_ASSERT(LinuxMemAreaPhysIsContig(psLinuxMemArea));
483 PVR_ASSERT(LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32ByteOffset) == ps_vma->vm_pgoff);
484
485 result = IO_REMAP_PFN_RANGE(ps_vma, ps_vma->vm_start, ps_vma->vm_pgoff, ui32ByteSize, ps_vma->vm_page_prot);
486
487 if(result == 0)
488 {
489 return IMG_TRUE;
490 }
491
492 PVR_DPF((PVR_DBG_MESSAGE, "%s: Failed to map contiguous physical address range (%d), trying non-contiguous path", __FUNCTION__, result));
493 }
494#endif
495
496 {
497
498 IMG_UINT32 ulVMAPos;
499 IMG_UINT32 ui32ByteEnd = ui32ByteOffset + ui32ByteSize;
500 IMG_UINT32 ui32PA;
501#if defined(PVR_MAKE_ALL_PFNS_SPECIAL)
502 IMG_BOOL bMixedMap = IMG_FALSE;
503#endif
504
505 for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE)
506 {
507 IMG_UINT32 pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA);
508
509 if (!pfn_valid(pfn))
510 {
511#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
512 PVR_DPF((PVR_DBG_ERROR,"%s: Error - PFN invalid: 0x%x", __FUNCTION__, pfn));
513 return IMG_FALSE;
514#else
515 bMixedMap = IMG_TRUE;
516#endif
517 }
518 }
519
520#if defined(PVR_MAKE_ALL_PFNS_SPECIAL)
521 if (bMixedMap)
522 {
523 ps_vma->vm_flags |= VM_MIXEDMAP;
524 }
525#endif
526
527 ulVMAPos = ps_vma->vm_start;
528 for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE)
529 {
530 IMG_UINT32 pfn;
531 IMG_INT result;
532
533 pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA);
534
535#if defined(PVR_MAKE_ALL_PFNS_SPECIAL)
536 if (bMixedMap)
537 {
538 result = vm_insert_mixed(ps_vma, ulVMAPos, pfn);
539 if(result != 0)
540 {
541 PVR_DPF((PVR_DBG_ERROR,"%s: Error - vm_insert_mixed failed (%d)", __FUNCTION__, result));
542 return IMG_FALSE;
543 }
544 }
545 else
546#endif
547 {
548 struct page *psPage;
549
550 PVR_ASSERT(pfn_valid(pfn));
551
552 psPage = pfn_to_page(pfn);
553
554 result = VM_INSERT_PAGE(ps_vma, ulVMAPos, psPage);
555 if(result != 0)
556 {
557 PVR_DPF((PVR_DBG_ERROR,"%s: Error - VM_INSERT_PAGE failed (%d)", __FUNCTION__, result));
558 return IMG_FALSE;
559 }
560 }
561 ulVMAPos += PAGE_SIZE;
562 }
563 }
564
565 return IMG_TRUE;
566}
567
568
569static IMG_VOID
570MMapVOpenNoLock(struct vm_area_struct* ps_vma)
571{
572 PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data;
573 PVR_ASSERT(psOffsetStruct != IMG_NULL)
574 psOffsetStruct->ui32Mapped++;
575 PVR_ASSERT(!psOffsetStruct->bOnMMapList);
576
577 if (psOffsetStruct->ui32Mapped > 1)
578 {
579 PVR_DPF((PVR_DBG_WARNING, "%s: Offset structure 0x%p is being shared across processes (psOffsetStruct->ui32Mapped: %u)", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32Mapped));
580 PVR_ASSERT((ps_vma->vm_flags & VM_DONTCOPY) == 0);
581 }
582
583#if defined(DEBUG_LINUX_MMAP_AREAS)
584
585 PVR_DPF((PVR_DBG_MESSAGE,
586 "%s: psLinuxMemArea 0x%p, KVAddress 0x%p MMapOffset %d, ui32Mapped %d",
587 __FUNCTION__,
588 psOffsetStruct->psLinuxMemArea,
589 LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea),
590 psOffsetStruct->ui32MMapOffset,
591 psOffsetStruct->ui32Mapped));
592#endif
593
594#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
595 MOD_INC_USE_COUNT;
596#endif
597}
598
599
600static void
601MMapVOpen(struct vm_area_struct* ps_vma)
602{
603 LinuxLockMutex(&g_sMMapMutex);
604
605 MMapVOpenNoLock(ps_vma);
606
607 LinuxUnLockMutex(&g_sMMapMutex);
608}
609
610
611static IMG_VOID
612MMapVCloseNoLock(struct vm_area_struct* ps_vma)
613{
614 PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data;
615 PVR_ASSERT(psOffsetStruct != IMG_NULL)
616
617#if defined(DEBUG_LINUX_MMAP_AREAS)
618 PVR_DPF((PVR_DBG_MESSAGE,
619 "%s: psLinuxMemArea %p, CpuVAddr %p ui32MMapOffset %d, ui32Mapped %d",
620 __FUNCTION__,
621 psOffsetStruct->psLinuxMemArea,
622 LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea),
623 psOffsetStruct->ui32MMapOffset,
624 psOffsetStruct->ui32Mapped));
625#endif
626
627 PVR_ASSERT(!psOffsetStruct->bOnMMapList);
628 psOffsetStruct->ui32Mapped--;
629 if (psOffsetStruct->ui32Mapped == 0)
630 {
631 if (psOffsetStruct->ui32RefCount != 0)
632 {
633 PVR_DPF((PVR_DBG_MESSAGE, "%s: psOffsetStruct %p has non-zero reference count (ui32RefCount = %u). User mode address of start of mapping: 0x%x", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32RefCount, psOffsetStruct->ui32UserVAddr));
634 }
635
636 DestroyOffsetStruct(psOffsetStruct);
637 }
638
639 ps_vma->vm_private_data = NULL;
640
641#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
642 MOD_DEC_USE_COUNT;
643#endif
644}
645
646static void
647MMapVClose(struct vm_area_struct* ps_vma)
648{
649 LinuxLockMutex(&g_sMMapMutex);
650
651 MMapVCloseNoLock(ps_vma);
652
653 LinuxUnLockMutex(&g_sMMapMutex);
654}
655
656
657static struct vm_operations_struct MMapIOOps =
658{
659 .open=MMapVOpen,
660 .close=MMapVClose
661};
662
663
664int
665PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma)
666{
667 IMG_UINT32 ui32ByteSize;
668 PKV_OFFSET_STRUCT psOffsetStruct;
669 int iRetVal = 0;
670
671 PVR_UNREFERENCED_PARAMETER(pFile);
672
673 LinuxLockMutex(&g_sMMapMutex);
674
675 ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start;
676
677 PVR_DPF((PVR_DBG_MESSAGE, "%s: Received mmap(2) request with ui32MMapOffset 0x%08lx,"
678 " and ui32ByteSize %d(0x%08x)",
679 __FUNCTION__,
680 ps_vma->vm_pgoff,
681 ui32ByteSize, ui32ByteSize));
682
683 psOffsetStruct = FindOffsetStructByOffset(ps_vma->vm_pgoff, ui32ByteSize);
684 if (psOffsetStruct == IMG_NULL)
685 {
686#if defined(SUPPORT_DRI_DRM)
687 LinuxUnLockMutex(&g_sMMapMutex);
688
689#if !defined(SUPPORT_DRI_DRM_EXT)
690
691 return drm_mmap(pFile, ps_vma);
692#else
693
694 return -ENOENT;
695#endif
696#else
697 PVR_UNREFERENCED_PARAMETER(pFile);
698
699 PVR_DPF((PVR_DBG_ERROR,
700 "%s: Attempted to mmap unregistered area at vm_pgoff 0x%lx",
701 __FUNCTION__, ps_vma->vm_pgoff));
702 iRetVal = -EINVAL;
703#endif
704 goto unlock_and_return;
705 }
706 list_del(&psOffsetStruct->sMMapItem);
707 psOffsetStruct->bOnMMapList = IMG_FALSE;
708
709
710 if (((ps_vma->vm_flags & VM_WRITE) != 0) &&
711 ((ps_vma->vm_flags & VM_SHARED) == 0))
712 {
713 PVR_DPF((PVR_DBG_ERROR, "%s: Cannot mmap non-shareable writable areas", __FUNCTION__));
714 iRetVal = -EINVAL;
715 goto unlock_and_return;
716 }
717
718 PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped psLinuxMemArea 0x%p\n",
719 __FUNCTION__, psOffsetStruct->psLinuxMemArea));
720
721 ps_vma->vm_flags |= VM_RESERVED;
722 ps_vma->vm_flags |= VM_IO;
723
724
725 ps_vma->vm_flags |= VM_DONTEXPAND;
726
727
728 ps_vma->vm_flags |= VM_DONTCOPY;
729
730 ps_vma->vm_private_data = (void *)psOffsetStruct;
731
732 switch(psOffsetStruct->psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_CACHETYPE_MASK)
733 {
734 case PVRSRV_HAP_CACHED:
735
736 break;
737 case PVRSRV_HAP_WRITECOMBINE:
738 ps_vma->vm_page_prot = PGPROT_WC(ps_vma->vm_page_prot);
739 break;
740 case PVRSRV_HAP_UNCACHED:
741 ps_vma->vm_page_prot = PGPROT_UC(ps_vma->vm_page_prot);
742 break;
743 default:
744 PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type", __FUNCTION__));
745 iRetVal = -EINVAL;
746 goto unlock_and_return;
747 }
748
749
750 ps_vma->vm_ops = &MMapIOOps;
751
752 if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0))
753 {
754 iRetVal = -EAGAIN;
755 goto unlock_and_return;
756 }
757
758 PVR_ASSERT(psOffsetStruct->ui32UserVAddr == 0)
759
760 psOffsetStruct->ui32UserVAddr = ps_vma->vm_start;
761
762
763 if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate)
764 {
765 IMG_UINT32 ui32RealByteSize, ui32ByteOffset;
766 IMG_VOID *pvBase;
767
768 DetermineUsersSizeAndByteOffset(psOffsetStruct->psLinuxMemArea,
769 &ui32RealByteSize,
770 &ui32ByteOffset);
771
772 ui32RealByteSize = psOffsetStruct->psLinuxMemArea->ui32ByteSize;
773 pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset;
774
775 OSInvalidateCPUCacheRangeKM(psOffsetStruct->psLinuxMemArea,
776 pvBase, ui32RealByteSize);
777 psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE;
778 }
779
780
781 MMapVOpenNoLock(ps_vma);
782
783 PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped area at offset 0x%08lx\n",
784 __FUNCTION__, ps_vma->vm_pgoff));
785
786unlock_and_return:
787 if (iRetVal != 0 && psOffsetStruct != IMG_NULL)
788 {
789 DestroyOffsetStruct(psOffsetStruct);
790 }
791
792 LinuxUnLockMutex(&g_sMMapMutex);
793
794 return iRetVal;
795}
796
797
798#if defined(DEBUG_LINUX_MMAP_AREAS)
799
800static void ProcSeqStartstopMMapRegistations(struct seq_file *sfile,IMG_BOOL start)
801{
802 if(start)
803 {
804 LinuxLockMutex(&g_sMMapMutex);
805 }
806 else
807 {
808 LinuxUnLockMutex(&g_sMMapMutex);
809 }
810}
811
812
813static void* ProcSeqOff2ElementMMapRegistrations(struct seq_file *sfile, loff_t off)
814{
815 LinuxMemArea *psLinuxMemArea;
816 if(!off)
817 {
818 return PVR_PROC_SEQ_START_TOKEN;
819 }
820
821 list_for_each_entry(psLinuxMemArea, &g_sMMapAreaList, sMMapItem)
822 {
823 PKV_OFFSET_STRUCT psOffsetStruct;
824
825 list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem)
826 {
827 off--;
828 if (off == 0)
829 {
830 PVR_ASSERT(psOffsetStruct->psLinuxMemArea == psLinuxMemArea);
831 return (void*)psOffsetStruct;
832 }
833 }
834 }
835 return (void*)0;
836}
837
838static void* ProcSeqNextMMapRegistrations(struct seq_file *sfile,void* el,loff_t off)
839{
840 return ProcSeqOff2ElementMMapRegistrations(sfile,off);
841}
842
843
844static void ProcSeqShowMMapRegistrations(struct seq_file *sfile, void *el)
845{
846 KV_OFFSET_STRUCT *psOffsetStruct = (KV_OFFSET_STRUCT*)el;
847 LinuxMemArea *psLinuxMemArea;
848 IMG_UINT32 ui32RealByteSize;
849 IMG_UINT32 ui32ByteOffset;
850
851 if(el == PVR_PROC_SEQ_START_TOKEN)
852 {
853 seq_printf( sfile,
854#if !defined(DEBUG_LINUX_XML_PROC_FILES)
855 "Allocations registered for mmap: %u\n"
856 "In total these areas correspond to %u bytes\n"
857 "psLinuxMemArea "
858 "UserVAddr "
859 "KernelVAddr "
860 "CpuPAddr "
861 "MMapOffset "
862 "ByteLength "
863 "LinuxMemType "
864 "Pid Name Flags\n",
865#else
866 "<mmap_header>\n"
867 "\t<count>%u</count>\n"
868 "\t<bytes>%u</bytes>\n"
869 "</mmap_header>\n",
870#endif
871 g_ui32RegisteredAreas,
872 g_ui32TotalByteSize
873 );
874 return;
875 }
876
877 psLinuxMemArea = psOffsetStruct->psLinuxMemArea;
878
879 DetermineUsersSizeAndByteOffset(psLinuxMemArea,
880 &ui32RealByteSize,
881 &ui32ByteOffset);
882
883 seq_printf( sfile,
884#if !defined(DEBUG_LINUX_XML_PROC_FILES)
885 "%-8p %08x %-8p %08x %08x %-8d %-24s %-5u %-8s %08x(%s)\n",
886#else
887 "<mmap_record>\n"
888 "\t<pointer>%-8p</pointer>\n"
889 "\t<user_virtual>%-8x</user_virtual>\n"
890 "\t<kernel_virtual>%-8p</kernel_virtual>\n"
891 "\t<cpu_physical>%08x</cpu_physical>\n"
892 "\t<mmap_offset>%08x</mmap_offset>\n"
893 "\t<bytes>%-8d</bytes>\n"
894 "\t<linux_mem_area_type>%-24s</linux_mem_area_type>\n"
895 "\t<pid>%-5u</pid>\n"
896 "\t<name>%-8s</name>\n"
897 "\t<flags>%08x</flags>\n"
898 "\t<flags_string>%s</flags_string>\n"
899 "</mmap_record>\n",
900#endif
901 psLinuxMemArea,
902 psOffsetStruct->ui32UserVAddr + ui32ByteOffset,
903 LinuxMemAreaToCpuVAddr(psLinuxMemArea),
904 LinuxMemAreaToCpuPAddr(psLinuxMemArea,0).uiAddr,
905 psOffsetStruct->ui32MMapOffset,
906 psLinuxMemArea->ui32ByteSize,
907 LinuxMemAreaTypeToString(psLinuxMemArea->eAreaType),
908 psOffsetStruct->ui32PID,
909 psOffsetStruct->pszName,
910 psLinuxMemArea->ui32AreaFlags,
911 HAPFlagsToString(psLinuxMemArea->ui32AreaFlags));
912}
913
914#endif
915
916
917PVRSRV_ERROR
918PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea)
919{
920 PVRSRV_ERROR eError;
921#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS)
922 const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea));
923#endif
924
925 LinuxLockMutex(&g_sMMapMutex);
926
927#if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS)
928 PVR_DPF((PVR_DBG_MESSAGE,
929 "%s(%s, psLinuxMemArea 0x%p, ui32AllocFlags 0x%8x)",
930 __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags));
931#endif
932
933 PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC);
934
935
936 if(psLinuxMemArea->bMMapRegistered)
937 {
938 PVR_DPF((PVR_DBG_ERROR, "%s: psLinuxMemArea 0x%p is already registered",
939 __FUNCTION__, psLinuxMemArea));
940 eError = PVRSRV_ERROR_INVALID_PARAMS;
941 goto exit_unlock;
942 }
943
944 list_add_tail(&psLinuxMemArea->sMMapItem, &g_sMMapAreaList);
945
946 psLinuxMemArea->bMMapRegistered = IMG_TRUE;
947
948#if defined(DEBUG_LINUX_MMAP_AREAS)
949 g_ui32RegisteredAreas++;
950
951 if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC)
952 {
953 g_ui32TotalByteSize += psLinuxMemArea->ui32ByteSize;
954 }
955#endif
956
957 eError = PVRSRV_OK;
958
959exit_unlock:
960 LinuxUnLockMutex(&g_sMMapMutex);
961
962 return eError;
963}
964
965
966PVRSRV_ERROR
967PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea)
968{
969 PVRSRV_ERROR eError;
970 PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct;
971
972 LinuxLockMutex(&g_sMMapMutex);
973
974 PVR_ASSERT(psLinuxMemArea->bMMapRegistered);
975
976 list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem)
977 {
978 if (psOffsetStruct->ui32Mapped != 0)
979 {
980 PVR_DPF((PVR_DBG_ERROR, "%s: psOffsetStruct 0x%p for memory area 0x0x%p is still mapped; psOffsetStruct->ui32Mapped %u", __FUNCTION__, psOffsetStruct, psLinuxMemArea, psOffsetStruct->ui32Mapped));
981 eError = PVRSRV_ERROR_STILL_MAPPED;
982 goto exit_unlock;
983 }
984 else
985 {
986
987 PVR_DPF((PVR_DBG_WARNING, "%s: psOffsetStruct 0x%p was never mapped", __FUNCTION__, psOffsetStruct));
988 }
989
990 PVR_ASSERT((psOffsetStruct->ui32Mapped == 0) && psOffsetStruct->bOnMMapList);
991
992 DestroyOffsetStruct(psOffsetStruct);
993 }
994
995 list_del(&psLinuxMemArea->sMMapItem);
996
997 psLinuxMemArea->bMMapRegistered = IMG_FALSE;
998
999#if defined(DEBUG_LINUX_MMAP_AREAS)
1000 g_ui32RegisteredAreas--;
1001 if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC)
1002 {
1003 g_ui32TotalByteSize -= psLinuxMemArea->ui32ByteSize;
1004 }
1005#endif
1006
1007 eError = PVRSRV_OK;
1008
1009exit_unlock:
1010 LinuxUnLockMutex(&g_sMMapMutex);
1011 return eError;
1012}
1013
1014
1015PVRSRV_ERROR
1016LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc)
1017{
1018 PVR_UNREFERENCED_PARAMETER(psEnvPerProc);
1019
1020 return PVRSRV_OK;
1021}
1022
1023IMG_VOID
1024LinuxMMapPerProcessDisconnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc)
1025{
1026 PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct;
1027 IMG_BOOL bWarn = IMG_FALSE;
1028 IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM();
1029
1030 PVR_UNREFERENCED_PARAMETER(psEnvPerProc);
1031
1032 LinuxLockMutex(&g_sMMapMutex);
1033
1034 list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &g_sMMapOffsetStructList, sMMapItem)
1035 {
1036 if (psOffsetStruct->ui32PID == ui32PID)
1037 {
1038 if (!bWarn)
1039 {
1040 PVR_DPF((PVR_DBG_WARNING, "%s: process has unmapped offset structures. Removing them", __FUNCTION__));
1041 bWarn = IMG_TRUE;
1042 }
1043 PVR_ASSERT(psOffsetStruct->ui32Mapped == 0);
1044 PVR_ASSERT(psOffsetStruct->bOnMMapList);
1045
1046 DestroyOffsetStruct(psOffsetStruct);
1047 }
1048 }
1049
1050 LinuxUnLockMutex(&g_sMMapMutex);
1051}
1052
1053
1054PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase)
1055{
1056 PVRSRV_ERROR eError;
1057
1058 eError = PVRSRVSetMaxHandle(psHandleBase, MAX_MMAP_HANDLE);
1059 if (eError != PVRSRV_OK)
1060 {
1061 PVR_DPF((PVR_DBG_ERROR,"%s: failed to set handle limit (%d)", __FUNCTION__, eError));
1062 return eError;
1063 }
1064
1065 return eError;
1066}
1067
1068
1069IMG_VOID
1070PVRMMapInit(IMG_VOID)
1071{
1072 LinuxInitMutex(&g_sMMapMutex);
1073
1074 g_psMemmapCache = KMemCacheCreateWrapper("img-mmap", sizeof(KV_OFFSET_STRUCT), 0, 0);
1075 if (!g_psMemmapCache)
1076 {
1077 PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__));
1078 goto error;
1079 }
1080
1081#if defined(DEBUG_LINUX_MMAP_AREAS)
1082 g_ProcMMap = CreateProcReadEntrySeq("mmap", NULL,
1083 ProcSeqNextMMapRegistrations,
1084 ProcSeqShowMMapRegistrations,
1085 ProcSeqOff2ElementMMapRegistrations,
1086 ProcSeqStartstopMMapRegistations
1087 );
1088#endif
1089 return;
1090
1091error:
1092 PVRMMapCleanup();
1093 return;
1094}
1095
1096
1097IMG_VOID
1098PVRMMapCleanup(IMG_VOID)
1099{
1100 PVRSRV_ERROR eError;
1101
1102 if (!list_empty(&g_sMMapAreaList))
1103 {
1104 LinuxMemArea *psLinuxMemArea, *psTmpMemArea;
1105
1106 PVR_DPF((PVR_DBG_ERROR, "%s: Memory areas are still registered with MMap", __FUNCTION__));
1107
1108 PVR_TRACE(("%s: Unregistering memory areas", __FUNCTION__));
1109 list_for_each_entry_safe(psLinuxMemArea, psTmpMemArea, &g_sMMapAreaList, sMMapItem)
1110 {
1111 eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
1112 if (eError != PVRSRV_OK)
1113 {
1114 PVR_DPF((PVR_DBG_ERROR, "%s: PVRMMapRemoveRegisteredArea failed (%d)", __FUNCTION__, eError));
1115 }
1116 PVR_ASSERT(eError == PVRSRV_OK);
1117
1118 LinuxMemAreaDeepFree(psLinuxMemArea);
1119 }
1120 }
1121 PVR_ASSERT(list_empty((&g_sMMapAreaList)));
1122
1123#if defined(DEBUG_LINUX_MMAP_AREAS)
1124 RemoveProcEntrySeq(g_ProcMMap);
1125#endif
1126
1127 if(g_psMemmapCache)
1128 {
1129 KMemCacheDestroyWrapper(g_psMemmapCache);
1130 g_psMemmapCache = NULL;
1131 }
1132}