]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c
456ec246241e789eb7532225bd05de175e5ee2f6
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_mmu.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2013 by Vivante Corp.
4 *
5 *    This program is free software; you can redistribute it and/or modify
6 *    it under the terms of the GNU General Public License as published by
7 *    the Free Software Foundation; either version 2 of the license, or
8 *    (at your option) any later version.
9 *
10 *    This program is distributed in the hope that it will be useful,
11 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 *    GNU General Public License for more details.
14 *
15 *    You should have received a copy of the GNU General Public License
16 *    along with this program; if not write to the Free Software
17 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 *****************************************************************************/
20
21
22 #include "gc_hal_kernel_precomp.h"
23
24 #define _GC_OBJ_ZONE    gcvZONE_MMU
25
26 typedef enum _gceMMU_TYPE
27 {
28     gcvMMU_USED     = (0 << 4),
29     gcvMMU_SINGLE   = (1 << 4),
30     gcvMMU_FREE     = (2 << 4),
31 }
32 gceMMU_TYPE;
33
34 #define gcmENTRY_TYPE(x) (x & 0xF0)
35
36 #define gcdMMU_TABLE_DUMP       0
37
38 #define gcdUSE_MMU_EXCEPTION    0
39
40 /*
41     gcdMMU_CLEAR_VALUE
42
43         The clear value for the entry of the old MMU.
44 */
45 #ifndef gcdMMU_CLEAR_VALUE
46 #   define gcdMMU_CLEAR_VALUE                   0x00000ABC
47 #endif
48
49 /* VIV: Start GPU address for gcvSURF_VERTEX.  */
50 #define gcdVERTEX_START      (128 << 10)
51
52 typedef struct _gcsMMU_STLB *gcsMMU_STLB_PTR;
53
54 typedef struct _gcsMMU_STLB
55 {
56     gctPHYS_ADDR    physical;
57     gctUINT32_PTR   logical;
58     gctSIZE_T       size;
59     gctUINT32       physBase;
60     gctSIZE_T       pageCount;
61     gctUINT32       mtlbIndex;
62     gctUINT32       mtlbEntryNum;
63     gcsMMU_STLB_PTR next;
64 } gcsMMU_STLB;
65
66 #if gcdSHARED_PAGETABLE
67 typedef struct _gcsSharedPageTable * gcsSharedPageTable_PTR;
68 typedef struct _gcsSharedPageTable
69 {
70     /* Shared gckMMU object. */
71     gckMMU          mmu;
72
73     /* Hardwares which use this shared pagetable. */
74     gckHARDWARE     hardwares[gcdMAX_GPU_COUNT];
75
76     /* Number of cores use this shared pagetable. */
77     gctUINT32       reference;
78 }
79 gcsSharedPageTable;
80
81 static gcsSharedPageTable_PTR sharedPageTable = gcvNULL;
82 #endif
83
84 #if gcdMIRROR_PAGETABLE
85 typedef struct _gcsMirrorPageTable * gcsMirrorPageTable_PTR;
86 typedef struct _gcsMirrorPageTable
87 {
88     /* gckMMU objects. */
89     gckMMU          mmus[gcdMAX_GPU_COUNT];
90
91     /* Hardwares which use this shared pagetable. */
92     gckHARDWARE     hardwares[gcdMAX_GPU_COUNT];
93
94     /* Number of cores use this shared pagetable. */
95     gctUINT32       reference;
96 }
97 gcsMirrorPageTable;
98
99 static gcsMirrorPageTable_PTR mirrorPageTable = gcvNULL;
100 static gctPOINTER mirrorPageTableMutex = gcvNULL;
101 #endif
102
103 typedef struct _gcsDynamicSpaceNode * gcsDynamicSpaceNode_PTR;
104 typedef struct _gcsDynamicSpaceNode
105 {
106     gctUINT32       start;
107     gctINT32        entries;
108 }
109 gcsDynamicSpaceNode;
110
111 static void
112 _WritePageEntry(
113     IN gctUINT32_PTR PageEntry,
114     IN gctUINT32     EntryValue
115     )
116 {
117     static gctUINT16 data = 0xff00;
118
119     if (*(gctUINT8 *)&data == 0xff)
120     {
121         *PageEntry = gcmSWAB32(EntryValue);
122     }
123     else
124     {
125         *PageEntry = EntryValue;
126     }
127 }
128
129 static gctUINT32
130 _ReadPageEntry(
131     IN gctUINT32_PTR PageEntry
132     )
133 {
134     static gctUINT16 data = 0xff00;
135     gctUINT32 entryValue;
136
137     if (*(gctUINT8 *)&data == 0xff)
138     {
139         entryValue = *PageEntry;
140         return gcmSWAB32(entryValue);
141     }
142     else
143     {
144         return *PageEntry;
145     }
146 }
147
148 static gceSTATUS
149 _FillPageTable(
150     IN gctUINT32_PTR PageTable,
151     IN gctUINT32     PageCount,
152     IN gctUINT32     EntryValue
153 )
154 {
155     gctUINT i;
156
157     for (i = 0; i < PageCount; i++)
158     {
159         _WritePageEntry(PageTable + i, EntryValue);
160     }
161
162     return gcvSTATUS_OK;
163 }
164
165 static gceSTATUS
166 _Link(
167     IN gckMMU Mmu,
168     IN gctUINT32 Index,
169     IN gctUINT32 Next
170     )
171 {
172     if (Index >= Mmu->pageTableEntries)
173     {
174         /* Just move heap pointer. */
175         Mmu->heapList = Next;
176     }
177     else
178     {
179         /* Address page table. */
180         gctUINT32_PTR pageTable = Mmu->pageTableLogical;
181
182         /* Dispatch on node type. */
183         switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[Index])))
184         {
185         case gcvMMU_SINGLE:
186             /* Set single index. */
187             _WritePageEntry(&pageTable[Index], (Next << 8) | gcvMMU_SINGLE);
188             break;
189
190         case gcvMMU_FREE:
191             /* Set index. */
192             _WritePageEntry(&pageTable[Index + 1], Next);
193             break;
194
195         default:
196             gcmkFATAL("MMU table correcupted at index %u!", Index);
197             return gcvSTATUS_HEAP_CORRUPTED;
198         }
199     }
200
201     /* Success. */
202     return gcvSTATUS_OK;
203 }
204
205 static gceSTATUS
206 _AddFree(
207     IN gckMMU Mmu,
208     IN gctUINT32 Index,
209     IN gctUINT32 Node,
210     IN gctUINT32 Count
211     )
212 {
213     gctUINT32_PTR pageTable = Mmu->pageTableLogical;
214
215     if (Count == 1)
216     {
217         /* Initialize a single page node. */
218         _WritePageEntry(pageTable + Node, (~((1U<<8)-1)) | gcvMMU_SINGLE);
219     }
220     else
221     {
222         /* Initialize the node. */
223         _WritePageEntry(pageTable + Node + 0, (Count << 8) | gcvMMU_FREE);
224         _WritePageEntry(pageTable + Node + 1, ~0U);
225     }
226
227     /* Append the node. */
228     return _Link(Mmu, Index, Node);
229 }
230
231 static gceSTATUS
232 _Collect(
233     IN gckMMU Mmu
234     )
235 {
236     gctUINT32_PTR pageTable = Mmu->pageTableLogical;
237     gceSTATUS status;
238     gctUINT32 i, previous, start = 0, count = 0;
239
240     previous = Mmu->heapList = ~0U;
241     Mmu->freeNodes = gcvFALSE;
242
243     /* Walk the entire page table. */
244     for (i = 0; i < Mmu->pageTableEntries; ++i)
245     {
246         /* Dispatch based on type of page. */
247         switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[i])))
248         {
249         case gcvMMU_USED:
250             /* Used page, so close any open node. */
251             if (count > 0)
252             {
253                 /* Add the node. */
254                 gcmkONERROR(_AddFree(Mmu, previous, start, count));
255
256                 /* Reset the node. */
257                 previous = start;
258                 count    = 0;
259             }
260             break;
261
262         case gcvMMU_SINGLE:
263             /* Single free node. */
264             if (count++ == 0)
265             {
266                 /* Start a new node. */
267                 start = i;
268             }
269             break;
270
271         case gcvMMU_FREE:
272             /* A free node. */
273             if (count == 0)
274             {
275                 /* Start a new node. */
276                 start = i;
277             }
278
279             /* Advance the count. */
280             count += _ReadPageEntry(&pageTable[i]) >> 8;
281
282             /* Advance the index into the page table. */
283             i     += (_ReadPageEntry(&pageTable[i]) >> 8) - 1;
284             break;
285
286         default:
287             gcmkFATAL("MMU page table correcupted at index %u!", i);
288             return gcvSTATUS_HEAP_CORRUPTED;
289         }
290     }
291
292     /* See if we have an open node left. */
293     if (count > 0)
294     {
295         /* Add the node to the list. */
296         gcmkONERROR(_AddFree(Mmu, previous, start, count));
297     }
298
299     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_MMU,
300                    "Performed a garbage collection of the MMU heap.");
301
302     /* Success. */
303     return gcvSTATUS_OK;
304
305 OnError:
306     /* Return the staus. */
307     return status;
308 }
309
310 static gctUINT32
311 _SetPage(gctUINT32 PageAddress)
312 {
313     return PageAddress
314            /* writable */
315            | (1 << 2)
316            /* Ignore exception */
317            | (0 << 1)
318            /* Present */
319            | (1 << 0);
320 }
321
322 static gceSTATUS
323 _FillFlatMapping(
324     IN gckMMU Mmu,
325     IN gctUINT32 PhysBase,
326     OUT gctSIZE_T Size
327     )
328 {
329     gceSTATUS status;
330     gctBOOL mutex = gcvFALSE;
331     gcsMMU_STLB_PTR head = gcvNULL, pre = gcvNULL;
332     gctUINT32 start = PhysBase & (~gcdMMU_PAGE_64K_MASK);
333     gctUINT32 end = (PhysBase + Size - 1) & (~gcdMMU_PAGE_64K_MASK);
334     gctUINT32 mStart = start >> gcdMMU_MTLB_SHIFT;
335     gctUINT32 mEnd = end >> gcdMMU_MTLB_SHIFT;
336     gctUINT32 sStart = (start & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
337     gctUINT32 sEnd = (end & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
338
339     /* Grab the mutex. */
340     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
341     mutex = gcvTRUE;
342
343     while (mStart <= mEnd)
344     {
345         gcmkASSERT(mStart < gcdMMU_MTLB_ENTRY_NUM);
346         if (*(Mmu->mtlbLogical + mStart) == 0)
347         {
348             gcsMMU_STLB_PTR stlb;
349             gctPOINTER pointer = gcvNULL;
350             gctUINT32 last = (mStart == mEnd) ? sEnd : (gcdMMU_STLB_64K_ENTRY_NUM - 1);
351
352             gcmkONERROR(gckOS_Allocate(Mmu->os, sizeof(struct _gcsMMU_STLB), &pointer));
353             stlb = pointer;
354
355             stlb->mtlbEntryNum = 0;
356             stlb->next = gcvNULL;
357             stlb->physical = gcvNULL;
358             stlb->logical = gcvNULL;
359             stlb->size = gcdMMU_STLB_64K_SIZE;
360             stlb->pageCount = 0;
361
362             if (pre == gcvNULL)
363             {
364                 pre = head = stlb;
365             }
366             else
367             {
368                 gcmkASSERT(pre->next == gcvNULL);
369                 pre->next = stlb;
370                 pre = stlb;
371             }
372
373             gcmkONERROR(
374                     gckOS_AllocateContiguous(Mmu->os,
375                                              gcvFALSE,
376                                              &stlb->size,
377                                              &stlb->physical,
378                                              (gctPOINTER)&stlb->logical));
379
380             gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size));
381
382             gcmkONERROR(gckOS_GetPhysicalAddress(
383                 Mmu->os,
384                 stlb->logical,
385                 &stlb->physBase));
386
387             if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1))
388             {
389                 gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
390             }
391
392             _WritePageEntry(Mmu->mtlbLogical + mStart,
393                             stlb->physBase
394                             /* 64KB page size */
395                             | (1 << 2)
396                             /* Ignore exception */
397                             | (0 << 1)
398                             /* Present */
399                             | (1 << 0)
400                             );
401 #if gcdMMU_TABLE_DUMP
402             gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
403                 __FUNCTION__, __LINE__,
404                 mStart,
405                 _ReadPageEntry(Mmu->mtlbLogical + mStart));
406 #endif
407
408             stlb->mtlbIndex = mStart;
409             stlb->mtlbEntryNum = 1;
410 #if gcdMMU_TABLE_DUMP
411             gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n",
412                     __FUNCTION__, __LINE__,
413                     stlb->logical,
414                     stlb->physBase);
415 #endif
416
417             while (sStart <= last)
418             {
419                 gcmkASSERT(!(start & gcdMMU_PAGE_64K_MASK));
420                 _WritePageEntry(stlb->logical + sStart, _SetPage(start));
421 #if gcdMMU_TABLE_DUMP
422                 gckOS_Print("%s(%d): insert STLB[%d]: %08x\n",
423                     __FUNCTION__, __LINE__,
424                     sStart,
425                     _ReadPageEntry(stlb->logical + sStart));
426 #endif
427                 /* next page. */
428                 start += gcdMMU_PAGE_64K_SIZE;
429                 sStart++;
430                 stlb->pageCount++;
431             }
432
433             sStart = 0;
434             ++mStart;
435         }
436         else
437         {
438             gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
439         }
440     }
441
442     /* Insert the stlb into staticSTLB. */
443     if (Mmu->staticSTLB == gcvNULL)
444     {
445         Mmu->staticSTLB = head;
446     }
447     else
448     {
449         gcmkASSERT(pre == gcvNULL);
450         gcmkASSERT(pre->next == gcvNULL);
451         pre->next = Mmu->staticSTLB;
452         Mmu->staticSTLB = head;
453     }
454
455     /* Release the mutex. */
456     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
457
458     return gcvSTATUS_OK;
459
460 OnError:
461
462     /* Roll back. */
463     while (head != gcvNULL)
464     {
465         pre = head;
466         head = head->next;
467
468         if (pre->physical != gcvNULL)
469         {
470             gcmkVERIFY_OK(
471                 gckOS_FreeContiguous(Mmu->os,
472                     pre->physical,
473                     pre->logical,
474                     pre->size));
475         }
476
477         if (pre->mtlbEntryNum != 0)
478         {
479             gcmkASSERT(pre->mtlbEntryNum == 1);
480             _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0);
481         }
482
483         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
484     }
485
486     if (mutex)
487     {
488         /* Release the mutex. */
489         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
490     }
491
492     return status;
493 }
494
495 static gceSTATUS
496 _FindDynamicSpace(
497     IN gckMMU Mmu,
498     OUT gcsDynamicSpaceNode_PTR *Array,
499     OUT gctINT * Size
500     )
501 {
502     gceSTATUS status = gcvSTATUS_OK;
503     gctPOINTER pointer = gcvNULL;
504     gcsDynamicSpaceNode_PTR array = gcvNULL;
505     gctINT size = 0;
506     gctINT i = 0, nodeStart = -1, nodeEntries = 0;
507
508     /* Allocate memory for the array. */
509     gcmkONERROR(gckOS_Allocate(Mmu->os,
510                                gcmSIZEOF(*array) * (gcdMMU_MTLB_ENTRY_NUM / 2),
511                                &pointer));
512
513     array = (gcsDynamicSpaceNode_PTR)pointer;
514
515     /* Loop all the entries. */
516     while (i < gcdMMU_MTLB_ENTRY_NUM)
517     {
518         if (!Mmu->mtlbLogical[i])
519         {
520             if (nodeStart < 0)
521             {
522                 /* This is the first entry of the dynamic space. */
523                 nodeStart   = i;
524                 nodeEntries = 1;
525             }
526             else
527             {
528                 /* Other entries of the dynamic space. */
529                 nodeEntries++;
530             }
531         }
532         else if (nodeStart >= 0)
533         {
534             /* Save the previous node. */
535             array[size].start   = nodeStart;
536             array[size].entries = nodeEntries;
537             size++;
538
539             /* Reset the start. */
540             nodeStart   = -1;
541             nodeEntries = 0;
542         }
543
544         i++;
545     }
546
547     /* Save the previous node. */
548     if (nodeStart >= 0)
549     {
550         array[size].start   = nodeStart;
551         array[size].entries = nodeEntries;
552         size++;
553     }
554
555 #if gcdMMU_TABLE_DUMP
556     for (i = 0; i < size; i++)
557     {
558         gckOS_Print("%s(%d): [%d]: start=%d, entries=%d.\n",
559                 __FUNCTION__, __LINE__,
560                 i,
561                 array[i].start,
562                 array[i].entries);
563     }
564 #endif
565
566     *Array = array;
567     *Size  = size;
568
569     return gcvSTATUS_OK;
570
571 OnError:
572     if (pointer != gcvNULL)
573     {
574         gckOS_Free(Mmu->os, pointer);
575     }
576
577     return status;
578 }
579
580 static gceSTATUS
581 _SetupDynamicSpace(
582     IN gckMMU Mmu
583     )
584 {
585     gceSTATUS status;
586     gcsDynamicSpaceNode_PTR nodeArray = gcvNULL;
587     gctINT i, nodeArraySize = 0;
588     gctUINT32 physical;
589     gctINT numEntries = 0;
590     gctUINT32_PTR pageTable;
591     gctBOOL acquired = gcvFALSE;
592
593     /* Find all the dynamic address space. */
594     gcmkONERROR(_FindDynamicSpace(Mmu, &nodeArray, &nodeArraySize));
595
596     /* TODO: We only use the largest one for now. */
597     for (i = 0; i < nodeArraySize; i++)
598     {
599         if (nodeArray[i].entries > numEntries)
600         {
601             Mmu->dynamicMappingStart = nodeArray[i].start;
602             numEntries               = nodeArray[i].entries;
603         }
604     }
605
606     gckOS_Free(Mmu->os, (gctPOINTER)nodeArray);
607
608     Mmu->pageTableSize = numEntries * 4096;
609
610     Mmu->pageTableEntries = Mmu->pageTableSize / gcmSIZEOF(gctUINT32);
611
612     /* Construct Slave TLB. */
613     gcmkONERROR(gckOS_AllocateContiguous(Mmu->os,
614                 gcvFALSE,
615                 &Mmu->pageTableSize,
616                 &Mmu->pageTablePhysical,
617                 (gctPOINTER)&Mmu->pageTableLogical));
618
619 #if gcdUSE_MMU_EXCEPTION
620     gcmkONERROR(_FillPageTable(Mmu->pageTableLogical,
621                                Mmu->pageTableEntries,
622                                /* Enable exception */
623                                1 << 1));
624 #else
625     /* Invalidate all entries. */
626     gcmkONERROR(gckOS_ZeroMemory(Mmu->pageTableLogical,
627                 Mmu->pageTableSize));
628 #endif
629
630     /* Initilization. */
631     pageTable      = Mmu->pageTableLogical;
632     _WritePageEntry(pageTable,     (Mmu->pageTableEntries << 8) | gcvMMU_FREE);
633     _WritePageEntry(pageTable + 1, ~0U);
634     Mmu->heapList  = 0;
635     Mmu->freeNodes = gcvFALSE;
636
637     gcmkONERROR(gckOS_GetPhysicalAddress(Mmu->os,
638                 Mmu->pageTableLogical,
639                 &physical));
640
641     /* Grab the mutex. */
642     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
643     acquired = gcvTRUE;
644
645     /* Map to Master TLB. */
646     for (i = (gctINT)Mmu->dynamicMappingStart;
647          i < (gctINT)Mmu->dynamicMappingStart + numEntries;
648          i++)
649     {
650         _WritePageEntry(Mmu->mtlbLogical + i,
651                         physical
652                         /* 4KB page size */
653                         | (0 << 2)
654                         /* Ignore exception */
655                         | (0 << 1)
656                         /* Present */
657                         | (1 << 0)
658                         );
659 #if gcdMMU_TABLE_DUMP
660         gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
661                 __FUNCTION__, __LINE__,
662                 i,
663                 _ReadPageEntry(Mmu->mtlbLogical + i));
664 #endif
665         physical += gcdMMU_STLB_4K_SIZE;
666     }
667
668     /* Release the mutex. */
669     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
670
671     return gcvSTATUS_OK;
672
673 OnError:
674     if (Mmu->pageTableLogical)
675     {
676         /* Free the page table. */
677         gcmkVERIFY_OK(
678                 gckOS_FreeContiguous(Mmu->os,
679                     Mmu->pageTablePhysical,
680                     (gctPOINTER) Mmu->pageTableLogical,
681                     Mmu->pageTableSize));
682     }
683
684     if (acquired)
685     {
686         /* Release the mutex. */
687         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
688     }
689
690     return status;
691 }
692
693 /*******************************************************************************
694 **
695 **  _Construct
696 **
697 **  Construct a new gckMMU object.
698 **
699 **  INPUT:
700 **
701 **      gckKERNEL Kernel
702 **          Pointer to an gckKERNEL object.
703 **
704 **      gctSIZE_T MmuSize
705 **          Number of bytes for the page table.
706 **
707 **  OUTPUT:
708 **
709 **      gckMMU * Mmu
710 **          Pointer to a variable that receives the gckMMU object pointer.
711 */
712 gceSTATUS
713 _Construct(
714     IN gckKERNEL Kernel,
715     IN gctSIZE_T MmuSize,
716     OUT gckMMU * Mmu
717     )
718 {
719     gckOS os;
720     gckHARDWARE hardware;
721     gceSTATUS status;
722     gckMMU mmu = gcvNULL;
723     gctUINT32_PTR pageTable;
724     gctPOINTER pointer = gcvNULL;
725
726     gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel, MmuSize);
727
728     /* Verify the arguments. */
729     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
730     gcmkVERIFY_ARGUMENT(MmuSize > 0);
731     gcmkVERIFY_ARGUMENT(Mmu != gcvNULL);
732
733     /* Extract the gckOS object pointer. */
734     os = Kernel->os;
735     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
736
737     /* Extract the gckHARDWARE object pointer. */
738     hardware = Kernel->hardware;
739     gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
740
741     /* Allocate memory for the gckMMU object. */
742     gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckMMU), &pointer));
743
744     mmu = pointer;
745
746     /* Initialize the gckMMU object. */
747     mmu->object.type      = gcvOBJ_MMU;
748     mmu->os               = os;
749     mmu->hardware         = hardware;
750     mmu->pageTableMutex   = gcvNULL;
751     mmu->pageTableLogical = gcvNULL;
752     mmu->mtlbLogical      = gcvNULL;
753     mmu->staticSTLB       = gcvNULL;
754     mmu->enabled          = gcvFALSE;
755 #ifdef __QNXNTO__
756     mmu->nodeList         = gcvNULL;
757     mmu->nodeMutex        = gcvNULL;
758 #endif
759
760     /* Create the page table mutex. */
761     gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex));
762
763 #ifdef __QNXNTO__
764     /* Create the node list mutex. */
765     gcmkONERROR(gckOS_CreateMutex(os, &mmu->nodeMutex));
766 #endif
767
768     if (hardware->mmuVersion == 0)
769     {
770         mmu->pageTableSize = MmuSize;
771
772         gcmkONERROR(
773             gckOS_AllocateContiguous(os,
774                                      gcvFALSE,
775                                      &mmu->pageTableSize,
776                                      &mmu->pageTablePhysical,
777                                      &pointer));
778
779         mmu->pageTableLogical = pointer;
780
781         /* Compute number of entries in page table. */
782         mmu->pageTableEntries = mmu->pageTableSize / sizeof(gctUINT32);
783
784         /* Mark all pages as free. */
785         pageTable      = mmu->pageTableLogical;
786
787 #if gcdMMU_CLEAR_VALUE
788         _FillPageTable(pageTable, mmu->pageTableEntries, gcdMMU_CLEAR_VALUE);
789 #endif
790
791         _WritePageEntry(pageTable,     (mmu->pageTableEntries << 8) | gcvMMU_FREE);
792         _WritePageEntry(pageTable + 1, ~0U);
793         mmu->heapList  = 0;
794         mmu->freeNodes = gcvFALSE;
795
796         /* Set page table address. */
797         gcmkONERROR(
798             gckHARDWARE_SetMMU(hardware, (gctPOINTER) mmu->pageTableLogical));
799     }
800     else
801     {
802         /* Allocate the 4K mode MTLB table. */
803         mmu->mtlbSize = gcdMMU_MTLB_SIZE + 64;
804
805         gcmkONERROR(
806             gckOS_AllocateContiguous(os,
807                                      gcvFALSE,
808                                      &mmu->mtlbSize,
809                                      &mmu->mtlbPhysical,
810                                      &pointer));
811
812         mmu->mtlbLogical = pointer;
813
814         /* Invalid all the entries. */
815         gcmkONERROR(
816             gckOS_ZeroMemory(pointer, mmu->mtlbSize));
817     }
818
819     /* Return the gckMMU object pointer. */
820     *Mmu = mmu;
821
822     /* Success. */
823     gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu);
824     return gcvSTATUS_OK;
825
826 OnError:
827     /* Roll back. */
828     if (mmu != gcvNULL)
829     {
830         if (mmu->pageTableLogical != gcvNULL)
831         {
832             /* Free the page table. */
833             gcmkVERIFY_OK(
834                 gckOS_FreeContiguous(os,
835                                      mmu->pageTablePhysical,
836                                      (gctPOINTER) mmu->pageTableLogical,
837                                      mmu->pageTableSize));
838
839         }
840
841         if (mmu->mtlbLogical != gcvNULL)
842         {
843             gcmkVERIFY_OK(
844                 gckOS_FreeContiguous(os,
845                                      mmu->mtlbPhysical,
846                                      (gctPOINTER) mmu->mtlbLogical,
847                                      mmu->mtlbSize));
848         }
849
850         if (mmu->pageTableMutex != gcvNULL)
851         {
852             /* Delete the mutex. */
853             gcmkVERIFY_OK(
854                 gckOS_DeleteMutex(os, mmu->pageTableMutex));
855         }
856
857 #ifdef __QNXNTO__
858         if (mmu->nodeMutex != gcvNULL)
859         {
860             /* Delete the mutex. */
861             gcmkVERIFY_OK(
862                 gckOS_DeleteMutex(os, mmu->nodeMutex));
863         }
864 #endif
865
866         /* Mark the gckMMU object as unknown. */
867         mmu->object.type = gcvOBJ_UNKNOWN;
868
869         /* Free the allocates memory. */
870         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, mmu));
871     }
872
873     /* Return the status. */
874     gcmkFOOTER();
875     return status;
876 }
877
878 /*******************************************************************************
879 **
880 **  _Destroy
881 **
882 **  Destroy a gckMMU object.
883 **
884 **  INPUT:
885 **
886 **      gckMMU Mmu
887 **          Pointer to an gckMMU object.
888 **
889 **  OUTPUT:
890 **
891 **      Nothing.
892 */
893 gceSTATUS
894 _Destroy(
895     IN gckMMU Mmu
896     )
897 {
898 #ifdef __QNXNTO__
899     gcuVIDMEM_NODE_PTR node, next;
900 #endif
901
902     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
903
904     /* Verify the arguments. */
905     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
906
907 #ifdef __QNXNTO__
908     /* Free all associated virtual memory. */
909     for (node = Mmu->nodeList; node != gcvNULL; node = next)
910     {
911         next = node->Virtual.next;
912         gcmkVERIFY_OK(gckVIDMEM_Free(node));
913     }
914 #endif
915
916     while (Mmu->staticSTLB != gcvNULL)
917     {
918         gcsMMU_STLB_PTR pre = Mmu->staticSTLB;
919         Mmu->staticSTLB = pre->next;
920
921         if (pre->physical != gcvNULL)
922         {
923             gcmkVERIFY_OK(
924                 gckOS_FreeContiguous(Mmu->os,
925                     pre->physical,
926                     pre->logical,
927                     pre->size));
928         }
929
930         if (pre->mtlbEntryNum != 0)
931         {
932             gcmkASSERT(pre->mtlbEntryNum == 1);
933             _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0);
934 #if gcdMMU_TABLE_DUMP
935             gckOS_Print("%s(%d): clean MTLB[%d]\n",
936                 __FUNCTION__, __LINE__,
937                 pre->mtlbIndex);
938 #endif
939         }
940
941         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
942     }
943
944     if (Mmu->hardware->mmuVersion != 0)
945     {
946         gcmkVERIFY_OK(
947                 gckOS_FreeContiguous(Mmu->os,
948                     Mmu->mtlbPhysical,
949                     (gctPOINTER) Mmu->mtlbLogical,
950                     Mmu->mtlbSize));
951     }
952
953     /* Free the page table. */
954     gcmkVERIFY_OK(
955             gckOS_FreeContiguous(Mmu->os,
956                 Mmu->pageTablePhysical,
957                 (gctPOINTER) Mmu->pageTableLogical,
958                 Mmu->pageTableSize));
959
960 #ifdef __QNXNTO__
961     /* Delete the node list mutex. */
962     gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->nodeMutex));
963 #endif
964
965     /* Delete the page table mutex. */
966     gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->pageTableMutex));
967
968     /* Mark the gckMMU object as unknown. */
969     Mmu->object.type = gcvOBJ_UNKNOWN;
970
971     /* Free the gckMMU object. */
972     gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, Mmu));
973
974     /* Success. */
975     gcmkFOOTER_NO();
976     return gcvSTATUS_OK;
977 }
978
979 /*******************************************************************************
980 ** _AdjstIndex
981 **
982 **  Adjust the index from which we search for a usable node to make sure
983 **  index allocated is greater than Start.
984 */
985 gceSTATUS
986 _AdjustIndex(
987     IN gckMMU Mmu,
988     IN gctUINT32 Index,
989     IN gctUINT32 PageCount,
990     IN gctUINT32 Start,
991     OUT gctUINT32 * IndexAdjusted
992     )
993 {
994     gceSTATUS status;
995     gctUINT32 index = Index;
996     gctUINT32_PTR map = Mmu->pageTableLogical;
997
998     gcmkHEADER();
999
1000     for (; index < Mmu->pageTableEntries;)
1001     {
1002         gctUINT32 result = 0;
1003         gctUINT32 nodeSize = 0;
1004
1005         if (index >= Start)
1006         {
1007             break;
1008         }
1009
1010         switch (gcmENTRY_TYPE(map[index]))
1011         {
1012         case gcvMMU_SINGLE:
1013             nodeSize = 1;
1014             break;
1015
1016         case gcvMMU_FREE:
1017             nodeSize = map[index] >> 8;
1018             break;
1019
1020         default:
1021             gcmkFATAL("MMU table correcupted at index %u!", index);
1022             gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1023         }
1024
1025         if (nodeSize > PageCount)
1026         {
1027             result = index + (nodeSize - PageCount);
1028
1029             if (result >= Start)
1030             {
1031                 break;
1032             }
1033         }
1034
1035         switch (gcmENTRY_TYPE(map[index]))
1036         {
1037         case gcvMMU_SINGLE:
1038             index = map[index] >> 8;
1039             break;
1040
1041         case gcvMMU_FREE:
1042             index = map[index + 1];
1043             break;
1044
1045         default:
1046             gcmkFATAL("MMU table correcupted at index %u!", index);
1047             gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1048         }
1049     }
1050
1051     *IndexAdjusted = index;
1052
1053     gcmkFOOTER_NO();
1054     return gcvSTATUS_OK;
1055
1056 OnError:
1057     gcmkFOOTER();
1058     return status;
1059 }
1060
1061 gceSTATUS
1062 gckMMU_Construct(
1063     IN gckKERNEL Kernel,
1064     IN gctSIZE_T MmuSize,
1065     OUT gckMMU * Mmu
1066     )
1067 {
1068 #if gcdSHARED_PAGETABLE
1069     gceSTATUS status;
1070     gctPOINTER pointer;
1071
1072     gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
1073
1074     if (sharedPageTable == gcvNULL)
1075     {
1076         gcmkONERROR(
1077                 gckOS_Allocate(Kernel->os,
1078                                sizeof(struct _gcsSharedPageTable),
1079                                &pointer));
1080         sharedPageTable = pointer;
1081
1082         gcmkONERROR(
1083                 gckOS_ZeroMemory(sharedPageTable,
1084                     sizeof(struct _gcsSharedPageTable)));
1085
1086         gcmkONERROR(_Construct(Kernel, MmuSize, &sharedPageTable->mmu));
1087     }
1088     else if (Kernel->hardware->mmuVersion == 0)
1089     {
1090         /* Set page table address. */
1091         gcmkONERROR(
1092             gckHARDWARE_SetMMU(Kernel->hardware, (gctPOINTER) sharedPageTable->mmu->pageTableLogical));
1093     }
1094
1095     *Mmu = sharedPageTable->mmu;
1096
1097     sharedPageTable->hardwares[sharedPageTable->reference] = Kernel->hardware;
1098
1099     sharedPageTable->reference++;
1100
1101     gcmkFOOTER_ARG("sharedPageTable->reference=%lu", sharedPageTable->reference);
1102     return gcvSTATUS_OK;
1103
1104 OnError:
1105     if (sharedPageTable)
1106     {
1107         if (sharedPageTable->mmu)
1108         {
1109             gcmkVERIFY_OK(gckMMU_Destroy(sharedPageTable->mmu));
1110         }
1111
1112         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, sharedPageTable));
1113     }
1114
1115     gcmkFOOTER();
1116     return status;
1117 #elif gcdMIRROR_PAGETABLE
1118     gceSTATUS status;
1119     gctPOINTER pointer;
1120
1121     gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
1122
1123     if (mirrorPageTable == gcvNULL)
1124     {
1125         gcmkONERROR(
1126             gckOS_Allocate(Kernel->os,
1127                            sizeof(struct _gcsMirrorPageTable),
1128                            &pointer));
1129         mirrorPageTable = pointer;
1130
1131         gcmkONERROR(
1132             gckOS_ZeroMemory(mirrorPageTable,
1133                     sizeof(struct _gcsMirrorPageTable)));
1134
1135         gcmkONERROR(
1136             gckOS_CreateMutex(Kernel->os, &mirrorPageTableMutex));
1137     }
1138
1139     gcmkONERROR(_Construct(Kernel, MmuSize, Mmu));
1140
1141     mirrorPageTable->mmus[mirrorPageTable->reference] = *Mmu;
1142
1143     mirrorPageTable->hardwares[mirrorPageTable->reference] = Kernel->hardware;
1144
1145     mirrorPageTable->reference++;
1146
1147     gcmkFOOTER_ARG("mirrorPageTable->reference=%lu", mirrorPageTable->reference);
1148     return gcvSTATUS_OK;
1149
1150 OnError:
1151     if (mirrorPageTable && mirrorPageTable->reference == 0)
1152     {
1153         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, mirrorPageTable));
1154     }
1155
1156     gcmkFOOTER();
1157     return status;
1158 #else
1159     return _Construct(Kernel, MmuSize, Mmu);
1160 #endif
1161 }
1162
1163 gceSTATUS
1164 gckMMU_Destroy(
1165     IN gckMMU Mmu
1166     )
1167 {
1168 #if gcdSHARED_PAGETABLE
1169     sharedPageTable->reference--;
1170
1171     if (sharedPageTable->reference == 0)
1172     {
1173         if (sharedPageTable->mmu)
1174         {
1175             gcmkVERIFY_OK(_Destroy(Mmu));
1176         }
1177
1178         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, sharedPageTable));
1179     }
1180
1181     return gcvSTATUS_OK;
1182 #elif gcdMIRROR_PAGETABLE
1183     mirrorPageTable->reference--;
1184
1185     if (mirrorPageTable->reference == 0)
1186     {
1187         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTable));
1188         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTableMutex));
1189     }
1190
1191     return _Destroy(Mmu);
1192 #else
1193     return _Destroy(Mmu);
1194 #endif
1195 }
1196
1197 /*******************************************************************************
1198 **
1199 **  gckMMU_AllocatePages
1200 **
1201 **  Allocate pages inside the page table.
1202 **
1203 **  INPUT:
1204 **
1205 **      gckMMU Mmu
1206 **          Pointer to an gckMMU object.
1207 **
1208 **      gctSIZE_T PageCount
1209 **          Number of pages to allocate.
1210 **
1211 **  OUTPUT:
1212 **
1213 **      gctPOINTER * PageTable
1214 **          Pointer to a variable that receives the base address of the page
1215 **          table.
1216 **
1217 **      gctUINT32 * Address
1218 **          Pointer to a variable that receives the hardware specific address.
1219 */
1220 gceSTATUS
1221 _AllocatePages(
1222     IN gckMMU Mmu,
1223     IN gctSIZE_T PageCount,
1224     IN gceSURF_TYPE Type,
1225     OUT gctPOINTER * PageTable,
1226     OUT gctUINT32 * Address
1227     )
1228 {
1229     gceSTATUS status;
1230     gctBOOL mutex = gcvFALSE;
1231     gctUINT32 index = 0, previous = ~0U, left;
1232     gctUINT32_PTR pageTable;
1233     gctBOOL gotIt;
1234     gctUINT32 address;
1235
1236     gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu, PageCount);
1237
1238     /* Verify the arguments. */
1239     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1240     gcmkVERIFY_ARGUMENT(PageCount > 0);
1241     gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
1242
1243     if (PageCount > Mmu->pageTableEntries)
1244     {
1245         gcmkPRINT("[galcore]: %s(%d): Run out of free page entry.",
1246                   __FUNCTION__, __LINE__);
1247
1248         /* Not enough pages avaiable. */
1249         gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1250     }
1251
1252     /* Grab the mutex. */
1253     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
1254     mutex = gcvTRUE;
1255
1256     /* Cast pointer to page table. */
1257     for (pageTable = Mmu->pageTableLogical, gotIt = gcvFALSE; !gotIt;)
1258     {
1259         index = Mmu->heapList;
1260
1261         if ((Mmu->hardware->mmuVersion == 0) && (Type == gcvSURF_VERTEX))
1262         {
1263             gcmkONERROR(_AdjustIndex(
1264                 Mmu,
1265                 index,
1266                 PageCount,
1267                 gcdVERTEX_START / gcmSIZEOF(gctUINT32),
1268                 &index
1269                 ));
1270         }
1271
1272         /* Walk the heap list. */
1273         for (; !gotIt && (index < Mmu->pageTableEntries);)
1274         {
1275             /* Check the node type. */
1276             switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[index])))
1277             {
1278             case gcvMMU_SINGLE:
1279                 /* Single odes are valid if we only need 1 page. */
1280                 if (PageCount == 1)
1281                 {
1282                     gotIt = gcvTRUE;
1283                 }
1284                 else
1285                 {
1286                     /* Move to next node. */
1287                     previous = index;
1288                     index    = _ReadPageEntry(&pageTable[index]) >> 8;
1289                 }
1290                 break;
1291
1292             case gcvMMU_FREE:
1293                 /* Test if the node has enough space. */
1294                 if (PageCount <= (_ReadPageEntry(&pageTable[index]) >> 8))
1295                 {
1296                     gotIt = gcvTRUE;
1297                 }
1298                 else
1299                 {
1300                     /* Move to next node. */
1301                     previous = index;
1302                     index    = _ReadPageEntry(&pageTable[index + 1]);
1303                 }
1304                 break;
1305
1306             default:
1307                 gcmkFATAL("MMU table correcupted at index %u!", index);
1308                 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1309             }
1310         }
1311
1312         /* Test if we are out of memory. */
1313         if (index >= Mmu->pageTableEntries)
1314         {
1315             if (Mmu->freeNodes)
1316             {
1317                 /* Time to move out the trash! */
1318                 gcmkONERROR(_Collect(Mmu));
1319             }
1320             else
1321             {
1322                 gcmkPRINT("[galcore]: %s(%d): Run out of free page entry.",
1323                           __FUNCTION__, __LINE__);
1324
1325                 /* Out of resources. */
1326                 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1327             }
1328         }
1329     }
1330
1331     switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[index])))
1332     {
1333     case gcvMMU_SINGLE:
1334         /* Unlink single node from free list. */
1335         gcmkONERROR(
1336             _Link(Mmu, previous, _ReadPageEntry(&pageTable[index]) >> 8));
1337         break;
1338
1339     case gcvMMU_FREE:
1340         /* Check how many pages will be left. */
1341         left = (_ReadPageEntry(&pageTable[index]) >> 8) - PageCount;
1342         switch (left)
1343         {
1344         case 0:
1345             /* The entire node is consumed, just unlink it. */
1346             gcmkONERROR(
1347                 _Link(Mmu, previous, _ReadPageEntry(&pageTable[index + 1])));
1348             break;
1349
1350         case 1:
1351             /* One page will remain.  Convert the node to a single node and
1352             ** advance the index. */
1353             _WritePageEntry(&pageTable[index], (_ReadPageEntry(&pageTable[index + 1]) << 8) | gcvMMU_SINGLE);
1354             index ++;
1355             break;
1356
1357         default:
1358             /* Enough pages remain for a new node.  However, we will just adjust
1359             ** the size of the current node and advance the index. */
1360             _WritePageEntry(&pageTable[index], (left << 8) | gcvMMU_FREE);
1361             index += left;
1362             break;
1363         }
1364         break;
1365     }
1366
1367     /* Mark node as used. */
1368     gcmkONERROR(_FillPageTable(&pageTable[index], PageCount, gcvMMU_USED));
1369
1370     /* Return pointer to page table. */
1371     *PageTable = &pageTable[index];
1372
1373     /* Build virtual address. */
1374     if (Mmu->hardware->mmuVersion == 0)
1375     {
1376         gcmkONERROR(
1377                 gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address));
1378     }
1379     else
1380     {
1381         gctUINT32 masterOffset = index / gcdMMU_STLB_4K_ENTRY_NUM
1382                                + Mmu->dynamicMappingStart;
1383         gctUINT32 slaveOffset = index % gcdMMU_STLB_4K_ENTRY_NUM;
1384
1385         address = (masterOffset << gcdMMU_MTLB_SHIFT)
1386                 | (slaveOffset << gcdMMU_STLB_4K_SHIFT);
1387     }
1388
1389     if (Address != gcvNULL)
1390     {
1391         *Address = address;
1392     }
1393
1394     /* Release the mutex. */
1395     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1396
1397     /* Success. */
1398     gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
1399                    *PageTable, gcmOPT_VALUE(Address));
1400     return gcvSTATUS_OK;
1401
1402 OnError:
1403
1404     if (mutex)
1405     {
1406         /* Release the mutex. */
1407         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1408     }
1409
1410     /* Return the status. */
1411     gcmkFOOTER();
1412     return status;
1413 }
1414
1415 /*******************************************************************************
1416 **
1417 **  gckMMU_FreePages
1418 **
1419 **  Free pages inside the page table.
1420 **
1421 **  INPUT:
1422 **
1423 **      gckMMU Mmu
1424 **          Pointer to an gckMMU object.
1425 **
1426 **      gctPOINTER PageTable
1427 **          Base address of the page table to free.
1428 **
1429 **      gctSIZE_T PageCount
1430 **          Number of pages to free.
1431 **
1432 **  OUTPUT:
1433 **
1434 **      Nothing.
1435 */
1436 gceSTATUS
1437 _FreePages(
1438     IN gckMMU Mmu,
1439     IN gctPOINTER PageTable,
1440     IN gctSIZE_T PageCount
1441     )
1442 {
1443     gctUINT32_PTR pageTable;
1444     gceSTATUS status;
1445     gctBOOL acquired = gcvFALSE;
1446
1447     gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu",
1448                    Mmu, PageTable, PageCount);
1449
1450     /* Verify the arguments. */
1451     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1452     gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
1453     gcmkVERIFY_ARGUMENT(PageCount > 0);
1454
1455     /* Convert the pointer. */
1456     pageTable = (gctUINT32_PTR) PageTable;
1457
1458     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
1459     acquired = gcvTRUE;
1460
1461 #if gcdMMU_CLEAR_VALUE
1462     if (Mmu->hardware->mmuVersion == 0)
1463     {
1464         _FillPageTable(pageTable, PageCount, gcdMMU_CLEAR_VALUE);
1465     }
1466 #endif
1467
1468     if (PageCount == 1)
1469     {
1470         /* Single page node. */
1471         _WritePageEntry(pageTable,
1472                         (~((1U<<8)-1)) | gcvMMU_SINGLE
1473 #if gcdUSE_MMU_EXCEPTION
1474                         /* Enable exception */
1475                         | 1 << 1
1476 #endif
1477                         );
1478     }
1479     else
1480     {
1481         /* Mark the node as free. */
1482         _WritePageEntry(pageTable,
1483                         (PageCount << 8) | gcvMMU_FREE
1484 #if gcdUSE_MMU_EXCEPTION
1485                         /* Enable exception */
1486                         | 1 << 1
1487 #endif
1488                        );
1489         _WritePageEntry(pageTable + 1, ~0U);
1490
1491 #if gcdUSE_MMU_EXCEPTION
1492         /* Enable exception */
1493         gcmkVERIFY_OK(_FillPageTable(pageTable + 2, PageCount - 2, 1 << 1));
1494 #endif
1495     }
1496
1497     /* We have free nodes. */
1498     Mmu->freeNodes = gcvTRUE;
1499
1500     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1501     acquired = gcvFALSE;
1502
1503     /* Success. */
1504     gcmkFOOTER_NO();
1505     return gcvSTATUS_OK;
1506
1507 OnError:
1508     if (acquired)
1509     {
1510         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1511     }
1512
1513     gcmkFOOTER();
1514     return status;
1515 }
1516
1517 gceSTATUS
1518 gckMMU_AllocatePages(
1519     IN gckMMU Mmu,
1520     IN gctSIZE_T PageCount,
1521     OUT gctPOINTER * PageTable,
1522     OUT gctUINT32 * Address
1523     )
1524 {
1525     return gckMMU_AllocatePagesEx(
1526                 Mmu, PageCount, gcvSURF_UNKNOWN, PageTable, Address);
1527 }
1528
1529 gceSTATUS
1530 gckMMU_AllocatePagesEx(
1531     IN gckMMU Mmu,
1532     IN gctSIZE_T PageCount,
1533     IN gceSURF_TYPE Type,
1534     OUT gctPOINTER * PageTable,
1535     OUT gctUINT32 * Address
1536     )
1537 {
1538 #if gcdMIRROR_PAGETABLE
1539     gceSTATUS status;
1540     gctPOINTER pageTable;
1541     gctUINT32 address;
1542     gctINT i;
1543     gckMMU mmu;
1544     gctBOOL acquired = gcvFALSE;
1545     gctBOOL allocated = gcvFALSE;
1546
1547     gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE);
1548     acquired = gcvTRUE;
1549
1550     /* Allocate page table for current MMU. */
1551     for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1552     {
1553         if (Mmu == mirrorPageTable->mmus[i])
1554         {
1555             gcmkONERROR(_AllocatePages(Mmu, PageCount, Type, PageTable, Address));
1556             allocated = gcvTRUE;
1557         }
1558     }
1559
1560     /* Allocate page table for other MMUs. */
1561     for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1562     {
1563         mmu = mirrorPageTable->mmus[i];
1564
1565         if (Mmu != mmu)
1566         {
1567             gcmkONERROR(_AllocatePages(mmu, PageCount, Type, &pageTable, &address));
1568             gcmkASSERT(address == *Address);
1569         }
1570     }
1571
1572     gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1573     acquired = gcvFALSE;
1574
1575     return gcvSTATUS_OK;
1576 OnError:
1577
1578     if (allocated)
1579     {
1580         /* Page tables for multiple GPU always keep the same. So it is impossible
1581          * the fist one allocates successfully but others fail.
1582          */
1583         gcmkASSERT(0);
1584     }
1585
1586     if (acquired)
1587     {
1588         gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1589     }
1590
1591     return status;
1592 #else
1593     return _AllocatePages(Mmu, PageCount, Type, PageTable, Address);
1594 #endif
1595 }
1596
1597 gceSTATUS
1598 gckMMU_FreePages(
1599     IN gckMMU Mmu,
1600     IN gctPOINTER PageTable,
1601     IN gctSIZE_T PageCount
1602     )
1603 {
1604 #if gcdMIRROR_PAGETABLE
1605     gctINT i;
1606     gctUINT32 offset;
1607     gckMMU mmu;
1608
1609     gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE);
1610
1611     gcmkVERIFY_OK(_FreePages(Mmu, PageTable, PageCount));
1612
1613     offset = (gctUINT32)PageTable - (gctUINT32)Mmu->pageTableLogical;
1614
1615     for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1616     {
1617         mmu = mirrorPageTable->mmus[i];
1618
1619         if (mmu != Mmu)
1620         {
1621             gcmkVERIFY_OK(_FreePages(mmu, mmu->pageTableLogical + offset/4, PageCount));
1622         }
1623     }
1624
1625     gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1626
1627     return gcvSTATUS_OK;
1628 #else
1629     return _FreePages(Mmu, PageTable, PageCount);
1630 #endif
1631 }
1632
1633 gceSTATUS
1634 gckMMU_Enable(
1635     IN gckMMU Mmu,
1636     IN gctUINT32 PhysBaseAddr,
1637     IN gctUINT32 PhysSize
1638     )
1639 {
1640     gceSTATUS status;
1641 #if gcdSHARED_PAGETABLE
1642     gckHARDWARE hardware;
1643     gctINT i;
1644 #endif
1645
1646     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
1647
1648     /* Verify the arguments. */
1649     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1650
1651 #if gcdSHARED_PAGETABLE
1652     if (Mmu->enabled)
1653     {
1654         gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
1655         return gcvSTATUS_SKIP;
1656     }
1657 #endif
1658
1659     if (Mmu->hardware->mmuVersion == 0)
1660     {
1661         /* Success. */
1662         gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
1663         return gcvSTATUS_SKIP;
1664     }
1665     else
1666     {
1667         if (PhysSize != 0)
1668         {
1669             gcmkONERROR(_FillFlatMapping(
1670                 Mmu,
1671                 PhysBaseAddr,
1672                 PhysSize
1673                 ));
1674         }
1675
1676         gcmkONERROR(_SetupDynamicSpace(Mmu));
1677
1678 #if gcdSHARED_PAGETABLE
1679         for(i = 0; i < gcdMAX_GPU_COUNT; i++)
1680         {
1681             hardware = sharedPageTable->hardwares[i];
1682             if (hardware != gcvNULL)
1683             {
1684                 gcmkONERROR(
1685                     gckHARDWARE_SetMMUv2(
1686                         hardware,
1687                         gcvTRUE,
1688                         Mmu->mtlbLogical,
1689                         gcvMMU_MODE_4K,
1690                         (gctUINT8_PTR)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
1691                         gcvFALSE
1692                         ));
1693             }
1694         }
1695 #else
1696         gcmkONERROR(
1697             gckHARDWARE_SetMMUv2(
1698                 Mmu->hardware,
1699                 gcvTRUE,
1700                 Mmu->mtlbLogical,
1701                 gcvMMU_MODE_4K,
1702                 (gctUINT8_PTR)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
1703                 gcvFALSE
1704                 ));
1705 #endif
1706
1707         Mmu->enabled = gcvTRUE;
1708
1709         /* Success. */
1710         gcmkFOOTER_NO();
1711         return gcvSTATUS_OK;
1712     }
1713
1714 OnError:
1715     /* Return the status. */
1716     gcmkFOOTER();
1717     return status;
1718 }
1719
1720 gceSTATUS
1721 gckMMU_SetPage(
1722     IN gckMMU Mmu,
1723     IN gctUINT32 PageAddress,
1724     IN gctUINT32 *PageEntry
1725     )
1726 {
1727 #if gcdMIRROR_PAGETABLE
1728     gctUINT32_PTR pageEntry;
1729     gctINT i;
1730     gckMMU mmu;
1731     gctUINT32 offset = (gctUINT32)PageEntry - (gctUINT32)Mmu->pageTableLogical;
1732 #endif
1733
1734     gctUINT32 data;
1735     gcmkHEADER_ARG("Mmu=0x%x", Mmu);
1736
1737     /* Verify the arguments. */
1738     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1739     gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL);
1740     gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
1741
1742     if (Mmu->hardware->mmuVersion == 0)
1743     {
1744         data = PageAddress;
1745     }
1746     else
1747     {
1748         data = _SetPage(PageAddress);
1749     }
1750
1751     _WritePageEntry(PageEntry, data);
1752
1753 #if gcdMIRROR_PAGETABLE
1754     for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1755     {
1756         mmu = mirrorPageTable->mmus[i];
1757
1758         if (mmu != Mmu)
1759         {
1760             pageEntry = mmu->pageTableLogical + offset / 4;
1761
1762             if (mmu->hardware->mmuVersion == 0)
1763             {
1764                 _WritePageEntry(pageEntry, PageAddress);
1765             }
1766             else
1767             {
1768                 _WritePageEntry(pageEntry, _SetPage(PageAddress));
1769             }
1770         }
1771
1772     }
1773 #endif
1774     /* Success. */
1775     gcmkFOOTER_NO();
1776     return gcvSTATUS_OK;
1777 }
1778
1779 #ifdef __QNXNTO__
1780 gceSTATUS
1781 gckMMU_InsertNode(
1782     IN gckMMU Mmu,
1783     IN gcuVIDMEM_NODE_PTR Node)
1784 {
1785     gceSTATUS status;
1786     gctBOOL mutex = gcvFALSE;
1787
1788     gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu, Node);
1789
1790     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1791
1792     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1793     mutex = gcvTRUE;
1794
1795     Node->Virtual.next = Mmu->nodeList;
1796     Mmu->nodeList = Node;
1797
1798     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1799
1800     gcmkFOOTER();
1801     return gcvSTATUS_OK;
1802
1803 OnError:
1804     if (mutex)
1805     {
1806         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1807     }
1808
1809     gcmkFOOTER();
1810     return status;
1811 }
1812
1813 gceSTATUS
1814 gckMMU_RemoveNode(
1815     IN gckMMU Mmu,
1816     IN gcuVIDMEM_NODE_PTR Node)
1817 {
1818     gceSTATUS status;
1819     gctBOOL mutex = gcvFALSE;
1820     gcuVIDMEM_NODE_PTR *iter;
1821
1822     gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu, Node);
1823
1824     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1825
1826     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1827     mutex = gcvTRUE;
1828
1829     for (iter = &Mmu->nodeList; *iter; iter = &(*iter)->Virtual.next)
1830     {
1831         if (*iter == Node)
1832         {
1833             *iter = Node->Virtual.next;
1834             break;
1835         }
1836     }
1837
1838     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1839
1840     gcmkFOOTER();
1841     return gcvSTATUS_OK;
1842
1843 OnError:
1844     if (mutex)
1845     {
1846         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1847     }
1848
1849     gcmkFOOTER();
1850     return status;
1851 }
1852
1853 gceSTATUS
1854 gckMMU_FreeHandleMemory(
1855     IN gckKERNEL Kernel,
1856     IN gckMMU Mmu,
1857     IN gctUINT32 Pid
1858     )
1859 {
1860     gceSTATUS status;
1861     gctBOOL acquired = gcvFALSE;
1862     gcuVIDMEM_NODE_PTR curr, next;
1863
1864     gcmkHEADER_ARG("Kernel=0x%x, Mmu=0x%x Pid=%u", Kernel, Mmu, Pid);
1865
1866     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1867     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1868
1869     gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1870     acquired = gcvTRUE;
1871
1872     for (curr = Mmu->nodeList; curr != gcvNULL; curr = next)
1873     {
1874         next = curr->Virtual.next;
1875
1876         if (curr->Virtual.processID == Pid)
1877         {
1878             while (curr->Virtual.unlockPendings[Kernel->core] == 0 && curr->Virtual.lockeds[Kernel->core] > 0)
1879             {
1880                 gcmkONERROR(gckVIDMEM_Unlock(Kernel, curr, gcvSURF_TYPE_UNKNOWN, gcvNULL));
1881             }
1882
1883             gcmkVERIFY_OK(gckVIDMEM_Free(curr));
1884         }
1885     }
1886
1887     gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1888
1889     gcmkFOOTER();
1890     return gcvSTATUS_OK;
1891
1892 OnError:
1893     if (acquired)
1894     {
1895         gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1896     }
1897
1898     gcmkFOOTER();
1899     return status;
1900 }
1901 #endif
1902
1903 gceSTATUS
1904 gckMMU_Flush(
1905     IN gckMMU Mmu
1906     )
1907 {
1908     gckHARDWARE hardware;
1909 #if gcdSHARED_PAGETABLE
1910     gctINT i;
1911     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
1912     {
1913 #if gcdENABLE_VG
1914         if (i == gcvCORE_VG)
1915         {
1916             continue;
1917         }
1918 #endif
1919         hardware = sharedPageTable->hardwares[i];
1920         if (hardware)
1921         {
1922             /* Notify cores who use this page table. */
1923             gcmkVERIFY_OK(
1924                 gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1925         }
1926     }
1927 #elif gcdMIRROR_PAGETABLE
1928     gctINT i;
1929     for (i = 0; i < mirrorPageTable->reference; i++)
1930     {
1931         hardware = mirrorPageTable->hardwares[i];
1932
1933         /* Notify cores who use this page table. */
1934         gcmkVERIFY_OK(
1935             gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1936     }
1937 #else
1938     hardware = Mmu->hardware;
1939     gcmkVERIFY_OK(
1940         gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1941 #endif
1942
1943     return gcvSTATUS_OK;
1944 }
1945
1946 gceSTATUS
1947 gckMMU_DumpPageTableEntry(
1948     IN gckMMU Mmu,
1949     IN gctUINT32 Address
1950     )
1951 {
1952     gctUINT32_PTR pageTable;
1953     gctUINT32 index;
1954     gctUINT32 mtlb, stlb;
1955
1956     gcmkHEADER_ARG("Mmu=0x%08X Address=0x%08X", Mmu, Address);
1957     gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1958
1959     gcmkASSERT(Mmu->hardware->mmuVersion > 0);
1960
1961     mtlb   = (Address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT;
1962     stlb   = (Address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT;
1963
1964     if (Address >= 0x80000000)
1965     {
1966         pageTable = Mmu->pageTableLogical;
1967
1968         index = (mtlb - Mmu->dynamicMappingStart)
1969               * gcdMMU_STLB_4K_ENTRY_NUM
1970               + stlb;
1971
1972         gcmkPRINT("    Page table entry = 0x%08X", _ReadPageEntry(pageTable + index));
1973     }
1974
1975     gcmkFOOTER_NO();
1976     return gcvSTATUS_OK;
1977 }
1978
1979 /******************************************************************************
1980 ****************************** T E S T   C O D E ******************************
1981 ******************************************************************************/
1982