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