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