1 /****************************************************************************
3 * Copyright (C) 2005 - 2013 by Vivante Corp.
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.
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.
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.
19 *****************************************************************************/
22 #include "gc_hal_kernel_precomp.h"
24 #define _GC_OBJ_ZONE gcvZONE_MMU
26 typedef enum _gceMMU_TYPE
28 gcvMMU_USED = (0 << 4),
29 gcvMMU_SINGLE = (1 << 4),
30 gcvMMU_FREE = (2 << 4),
34 #define gcmENTRY_TYPE(x) (x & 0xF0)
36 #define gcdMMU_TABLE_DUMP 0
38 #define gcdUSE_MMU_EXCEPTION 0
43 The clear value for the entry of the old MMU.
45 #ifndef gcdMMU_CLEAR_VALUE
46 # define gcdMMU_CLEAR_VALUE 0x00000ABC
49 /* VIV: Start GPU address for gcvSURF_VERTEX. */
50 #define gcdVERTEX_START (128 << 10)
52 typedef struct _gcsMMU_STLB *gcsMMU_STLB_PTR;
54 typedef struct _gcsMMU_STLB
56 gctPHYS_ADDR physical;
57 gctUINT32_PTR logical;
62 gctUINT32 mtlbEntryNum;
66 #if gcdSHARED_PAGETABLE
67 typedef struct _gcsSharedPageTable * gcsSharedPageTable_PTR;
68 typedef struct _gcsSharedPageTable
70 /* Shared gckMMU object. */
73 /* Hardwares which use this shared pagetable. */
74 gckHARDWARE hardwares[gcdMAX_GPU_COUNT];
76 /* Number of cores use this shared pagetable. */
81 static gcsSharedPageTable_PTR sharedPageTable = gcvNULL;
84 #if gcdMIRROR_PAGETABLE
85 typedef struct _gcsMirrorPageTable * gcsMirrorPageTable_PTR;
86 typedef struct _gcsMirrorPageTable
89 gckMMU mmus[gcdMAX_GPU_COUNT];
91 /* Hardwares which use this shared pagetable. */
92 gckHARDWARE hardwares[gcdMAX_GPU_COUNT];
94 /* Number of cores use this shared pagetable. */
99 static gcsMirrorPageTable_PTR mirrorPageTable = gcvNULL;
100 static gctPOINTER mirrorPageTableMutex = gcvNULL;
103 typedef struct _gcsDynamicSpaceNode * gcsDynamicSpaceNode_PTR;
104 typedef struct _gcsDynamicSpaceNode
113 IN gctUINT32_PTR PageEntry,
114 IN gctUINT32 EntryValue
117 static gctUINT16 data = 0xff00;
119 if (*(gctUINT8 *)&data == 0xff)
121 *PageEntry = gcmSWAB32(EntryValue);
125 *PageEntry = EntryValue;
131 IN gctUINT32_PTR PageEntry
134 static gctUINT16 data = 0xff00;
135 gctUINT32 entryValue;
137 if (*(gctUINT8 *)&data == 0xff)
139 entryValue = *PageEntry;
140 return gcmSWAB32(entryValue);
150 IN gctUINT32_PTR PageTable,
151 IN gctUINT32 PageCount,
152 IN gctUINT32 EntryValue
157 for (i = 0; i < PageCount; i++)
159 _WritePageEntry(PageTable + i, EntryValue);
172 if (Index >= Mmu->pageTableEntries)
174 /* Just move heap pointer. */
175 Mmu->heapList = Next;
179 /* Address page table. */
180 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
182 /* Dispatch on node type. */
183 switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[Index])))
186 /* Set single index. */
187 _WritePageEntry(&pageTable[Index], (Next << 8) | gcvMMU_SINGLE);
192 _WritePageEntry(&pageTable[Index + 1], Next);
196 gcmkFATAL("MMU table correcupted at index %u!", Index);
197 return gcvSTATUS_HEAP_CORRUPTED;
213 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
217 /* Initialize a single page node. */
218 _WritePageEntry(pageTable + Node, (~((1U<<8)-1)) | gcvMMU_SINGLE);
222 /* Initialize the node. */
223 _WritePageEntry(pageTable + Node + 0, (Count << 8) | gcvMMU_FREE);
224 _WritePageEntry(pageTable + Node + 1, ~0U);
227 /* Append the node. */
228 return _Link(Mmu, Index, Node);
236 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
238 gctUINT32 i, previous, start = 0, count = 0;
240 previous = Mmu->heapList = ~0U;
241 Mmu->freeNodes = gcvFALSE;
243 /* Walk the entire page table. */
244 for (i = 0; i < Mmu->pageTableEntries; ++i)
246 /* Dispatch based on type of page. */
247 switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[i])))
250 /* Used page, so close any open node. */
254 gcmkONERROR(_AddFree(Mmu, previous, start, count));
256 /* Reset the node. */
263 /* Single free node. */
266 /* Start a new node. */
275 /* Start a new node. */
279 /* Advance the count. */
280 count += _ReadPageEntry(&pageTable[i]) >> 8;
282 /* Advance the index into the page table. */
283 i += (_ReadPageEntry(&pageTable[i]) >> 8) - 1;
287 gcmkFATAL("MMU page table correcupted at index %u!", i);
288 return gcvSTATUS_HEAP_CORRUPTED;
292 /* See if we have an open node left. */
295 /* Add the node to the list. */
296 gcmkONERROR(_AddFree(Mmu, previous, start, count));
299 gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_MMU,
300 "Performed a garbage collection of the MMU heap.");
306 /* Return the staus. */
311 _SetPage(gctUINT32 PageAddress)
316 /* Ignore exception */
325 IN gctUINT32 PhysBase,
330 gctBOOL mutex = gcvFALSE;
331 gcsMMU_STLB_PTR head = gcvNULL, pre = gcvNULL;
332 gctUINT32 start = PhysBase & (~gcdMMU_PAGE_64K_MASK);
333 gctUINT32 end = (PhysBase + Size - 1) & (~gcdMMU_PAGE_64K_MASK);
334 gctUINT32 mStart = start >> gcdMMU_MTLB_SHIFT;
335 gctUINT32 mEnd = end >> gcdMMU_MTLB_SHIFT;
336 gctUINT32 sStart = (start & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
337 gctUINT32 sEnd = (end & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
339 /* Grab the mutex. */
340 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
343 while (mStart <= mEnd)
345 gcmkASSERT(mStart < gcdMMU_MTLB_ENTRY_NUM);
346 if (*(Mmu->mtlbLogical + mStart) == 0)
348 gcsMMU_STLB_PTR stlb;
349 gctPOINTER pointer = gcvNULL;
350 gctUINT32 last = (mStart == mEnd) ? sEnd : (gcdMMU_STLB_64K_ENTRY_NUM - 1);
352 gcmkONERROR(gckOS_Allocate(Mmu->os, sizeof(struct _gcsMMU_STLB), &pointer));
355 stlb->mtlbEntryNum = 0;
356 stlb->next = gcvNULL;
357 stlb->physical = gcvNULL;
358 stlb->logical = gcvNULL;
359 stlb->size = gcdMMU_STLB_64K_SIZE;
368 gcmkASSERT(pre->next == gcvNULL);
374 gckOS_AllocateContiguous(Mmu->os,
378 (gctPOINTER)&stlb->logical));
380 gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size));
382 gcmkONERROR(gckOS_GetPhysicalAddress(
387 if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1))
389 gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
392 _WritePageEntry(Mmu->mtlbLogical + mStart,
396 /* Ignore exception */
401 #if gcdMMU_TABLE_DUMP
402 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
403 __FUNCTION__, __LINE__,
405 _ReadPageEntry(Mmu->mtlbLogical + mStart));
408 stlb->mtlbIndex = mStart;
409 stlb->mtlbEntryNum = 1;
410 #if gcdMMU_TABLE_DUMP
411 gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n",
412 __FUNCTION__, __LINE__,
417 while (sStart <= last)
419 gcmkASSERT(!(start & gcdMMU_PAGE_64K_MASK));
420 _WritePageEntry(stlb->logical + sStart, _SetPage(start));
421 #if gcdMMU_TABLE_DUMP
422 gckOS_Print("%s(%d): insert STLB[%d]: %08x\n",
423 __FUNCTION__, __LINE__,
425 _ReadPageEntry(stlb->logical + sStart));
428 start += gcdMMU_PAGE_64K_SIZE;
438 gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
442 /* Insert the stlb into staticSTLB. */
443 if (Mmu->staticSTLB == gcvNULL)
445 Mmu->staticSTLB = head;
449 gcmkASSERT(pre == gcvNULL);
450 gcmkASSERT(pre->next == gcvNULL);
451 pre->next = Mmu->staticSTLB;
452 Mmu->staticSTLB = head;
455 /* Release the mutex. */
456 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
463 while (head != gcvNULL)
468 if (pre->physical != gcvNULL)
471 gckOS_FreeContiguous(Mmu->os,
477 if (pre->mtlbEntryNum != 0)
479 gcmkASSERT(pre->mtlbEntryNum == 1);
480 _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0);
483 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
488 /* Release the mutex. */
489 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
498 OUT gcsDynamicSpaceNode_PTR *Array,
502 gceSTATUS status = gcvSTATUS_OK;
503 gctPOINTER pointer = gcvNULL;
504 gcsDynamicSpaceNode_PTR array = gcvNULL;
506 gctINT i = 0, nodeStart = -1, nodeEntries = 0;
508 /* Allocate memory for the array. */
509 gcmkONERROR(gckOS_Allocate(Mmu->os,
510 gcmSIZEOF(*array) * (gcdMMU_MTLB_ENTRY_NUM / 2),
513 array = (gcsDynamicSpaceNode_PTR)pointer;
515 /* Loop all the entries. */
516 while (i < gcdMMU_MTLB_ENTRY_NUM)
518 if (!Mmu->mtlbLogical[i])
522 /* This is the first entry of the dynamic space. */
528 /* Other entries of the dynamic space. */
532 else if (nodeStart >= 0)
534 /* Save the previous node. */
535 array[size].start = nodeStart;
536 array[size].entries = nodeEntries;
539 /* Reset the start. */
547 /* Save the previous node. */
550 array[size].start = nodeStart;
551 array[size].entries = nodeEntries;
555 #if gcdMMU_TABLE_DUMP
556 for (i = 0; i < size; i++)
558 gckOS_Print("%s(%d): [%d]: start=%d, entries=%d.\n",
559 __FUNCTION__, __LINE__,
572 if (pointer != gcvNULL)
574 gckOS_Free(Mmu->os, pointer);
586 gcsDynamicSpaceNode_PTR nodeArray = gcvNULL;
587 gctINT i, nodeArraySize = 0;
589 gctINT numEntries = 0;
590 gctUINT32_PTR pageTable;
591 gctBOOL acquired = gcvFALSE;
593 /* Find all the dynamic address space. */
594 gcmkONERROR(_FindDynamicSpace(Mmu, &nodeArray, &nodeArraySize));
596 /* TODO: We only use the largest one for now. */
597 for (i = 0; i < nodeArraySize; i++)
599 if (nodeArray[i].entries > numEntries)
601 Mmu->dynamicMappingStart = nodeArray[i].start;
602 numEntries = nodeArray[i].entries;
606 gckOS_Free(Mmu->os, (gctPOINTER)nodeArray);
608 Mmu->pageTableSize = numEntries * 4096;
610 Mmu->pageTableEntries = Mmu->pageTableSize / gcmSIZEOF(gctUINT32);
612 /* Construct Slave TLB. */
613 gcmkONERROR(gckOS_AllocateContiguous(Mmu->os,
616 &Mmu->pageTablePhysical,
617 (gctPOINTER)&Mmu->pageTableLogical));
619 #if gcdUSE_MMU_EXCEPTION
620 gcmkONERROR(_FillPageTable(Mmu->pageTableLogical,
621 Mmu->pageTableEntries,
622 /* Enable exception */
625 /* Invalidate all entries. */
626 gcmkONERROR(gckOS_ZeroMemory(Mmu->pageTableLogical,
627 Mmu->pageTableSize));
631 pageTable = Mmu->pageTableLogical;
632 _WritePageEntry(pageTable, (Mmu->pageTableEntries << 8) | gcvMMU_FREE);
633 _WritePageEntry(pageTable + 1, ~0U);
635 Mmu->freeNodes = gcvFALSE;
637 gcmkONERROR(gckOS_GetPhysicalAddress(Mmu->os,
638 Mmu->pageTableLogical,
641 /* Grab the mutex. */
642 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
645 /* Map to Master TLB. */
646 for (i = (gctINT)Mmu->dynamicMappingStart;
647 i < (gctINT)Mmu->dynamicMappingStart + numEntries;
650 _WritePageEntry(Mmu->mtlbLogical + i,
654 /* Ignore exception */
659 #if gcdMMU_TABLE_DUMP
660 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
661 __FUNCTION__, __LINE__,
663 _ReadPageEntry(Mmu->mtlbLogical + i));
665 physical += gcdMMU_STLB_4K_SIZE;
668 /* Release the mutex. */
669 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
674 if (Mmu->pageTableLogical)
676 /* Free the page table. */
678 gckOS_FreeContiguous(Mmu->os,
679 Mmu->pageTablePhysical,
680 (gctPOINTER) Mmu->pageTableLogical,
681 Mmu->pageTableSize));
686 /* Release the mutex. */
687 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
693 /*******************************************************************************
697 ** Construct a new gckMMU object.
702 ** Pointer to an gckKERNEL object.
705 ** Number of bytes for the page table.
710 ** Pointer to a variable that receives the gckMMU object pointer.
715 IN gctSIZE_T MmuSize,
720 gckHARDWARE hardware;
722 gckMMU mmu = gcvNULL;
723 gctUINT32_PTR pageTable;
724 gctPOINTER pointer = gcvNULL;
726 gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel, MmuSize);
728 /* Verify the arguments. */
729 gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
730 gcmkVERIFY_ARGUMENT(MmuSize > 0);
731 gcmkVERIFY_ARGUMENT(Mmu != gcvNULL);
733 /* Extract the gckOS object pointer. */
735 gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
737 /* Extract the gckHARDWARE object pointer. */
738 hardware = Kernel->hardware;
739 gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
741 /* Allocate memory for the gckMMU object. */
742 gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckMMU), &pointer));
746 /* Initialize the gckMMU object. */
747 mmu->object.type = gcvOBJ_MMU;
749 mmu->hardware = hardware;
750 mmu->pageTableMutex = gcvNULL;
751 mmu->pageTableLogical = gcvNULL;
752 mmu->mtlbLogical = gcvNULL;
753 mmu->staticSTLB = gcvNULL;
754 mmu->enabled = gcvFALSE;
756 mmu->nodeList = gcvNULL;
757 mmu->nodeMutex = gcvNULL;
760 /* Create the page table mutex. */
761 gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex));
764 /* Create the node list mutex. */
765 gcmkONERROR(gckOS_CreateMutex(os, &mmu->nodeMutex));
768 if (hardware->mmuVersion == 0)
770 mmu->pageTableSize = MmuSize;
773 gckOS_AllocateContiguous(os,
776 &mmu->pageTablePhysical,
779 mmu->pageTableLogical = pointer;
781 /* Compute number of entries in page table. */
782 mmu->pageTableEntries = mmu->pageTableSize / sizeof(gctUINT32);
784 /* Mark all pages as free. */
785 pageTable = mmu->pageTableLogical;
787 #if gcdMMU_CLEAR_VALUE
788 _FillPageTable(pageTable, mmu->pageTableEntries, gcdMMU_CLEAR_VALUE);
791 _WritePageEntry(pageTable, (mmu->pageTableEntries << 8) | gcvMMU_FREE);
792 _WritePageEntry(pageTable + 1, ~0U);
794 mmu->freeNodes = gcvFALSE;
796 /* Set page table address. */
798 gckHARDWARE_SetMMU(hardware, (gctPOINTER) mmu->pageTableLogical));
802 /* Allocate the 4K mode MTLB table. */
803 mmu->mtlbSize = gcdMMU_MTLB_SIZE + 64;
806 gckOS_AllocateContiguous(os,
812 mmu->mtlbLogical = pointer;
814 /* Invalid all the entries. */
816 gckOS_ZeroMemory(pointer, mmu->mtlbSize));
819 /* Return the gckMMU object pointer. */
823 gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu);
830 if (mmu->pageTableLogical != gcvNULL)
832 /* Free the page table. */
834 gckOS_FreeContiguous(os,
835 mmu->pageTablePhysical,
836 (gctPOINTER) mmu->pageTableLogical,
837 mmu->pageTableSize));
841 if (mmu->mtlbLogical != gcvNULL)
844 gckOS_FreeContiguous(os,
846 (gctPOINTER) mmu->mtlbLogical,
850 if (mmu->pageTableMutex != gcvNULL)
852 /* Delete the mutex. */
854 gckOS_DeleteMutex(os, mmu->pageTableMutex));
858 if (mmu->nodeMutex != gcvNULL)
860 /* Delete the mutex. */
862 gckOS_DeleteMutex(os, mmu->nodeMutex));
866 /* Mark the gckMMU object as unknown. */
867 mmu->object.type = gcvOBJ_UNKNOWN;
869 /* Free the allocates memory. */
870 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, mmu));
873 /* Return the status. */
878 /*******************************************************************************
882 ** Destroy a gckMMU object.
887 ** Pointer to an gckMMU object.
899 gcuVIDMEM_NODE_PTR node, next;
902 gcmkHEADER_ARG("Mmu=0x%x", Mmu);
904 /* Verify the arguments. */
905 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
908 /* Free all associated virtual memory. */
909 for (node = Mmu->nodeList; node != gcvNULL; node = next)
911 next = node->Virtual.next;
912 gcmkVERIFY_OK(gckVIDMEM_Free(node));
916 while (Mmu->staticSTLB != gcvNULL)
918 gcsMMU_STLB_PTR pre = Mmu->staticSTLB;
919 Mmu->staticSTLB = pre->next;
921 if (pre->physical != gcvNULL)
924 gckOS_FreeContiguous(Mmu->os,
930 if (pre->mtlbEntryNum != 0)
932 gcmkASSERT(pre->mtlbEntryNum == 1);
933 _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0);
934 #if gcdMMU_TABLE_DUMP
935 gckOS_Print("%s(%d): clean MTLB[%d]\n",
936 __FUNCTION__, __LINE__,
941 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
944 if (Mmu->hardware->mmuVersion != 0)
947 gckOS_FreeContiguous(Mmu->os,
949 (gctPOINTER) Mmu->mtlbLogical,
953 /* Free the page table. */
955 gckOS_FreeContiguous(Mmu->os,
956 Mmu->pageTablePhysical,
957 (gctPOINTER) Mmu->pageTableLogical,
958 Mmu->pageTableSize));
961 /* Delete the node list mutex. */
962 gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->nodeMutex));
965 /* Delete the page table mutex. */
966 gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->pageTableMutex));
968 /* Mark the gckMMU object as unknown. */
969 Mmu->object.type = gcvOBJ_UNKNOWN;
971 /* Free the gckMMU object. */
972 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, Mmu));
979 /*******************************************************************************
982 ** Adjust the index from which we search for a usable node to make sure
983 ** index allocated is greater than Start.
989 IN gctUINT32 PageCount,
991 OUT gctUINT32 * IndexAdjusted
995 gctUINT32 index = Index;
996 gctUINT32_PTR map = Mmu->pageTableLogical;
1000 for (; index < Mmu->pageTableEntries;)
1002 gctUINT32 result = 0;
1003 gctUINT32 nodeSize = 0;
1010 switch (gcmENTRY_TYPE(map[index]))
1017 nodeSize = map[index] >> 8;
1021 gcmkFATAL("MMU table correcupted at index %u!", index);
1022 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1025 if (nodeSize > PageCount)
1027 result = index + (nodeSize - PageCount);
1029 if (result >= Start)
1035 switch (gcmENTRY_TYPE(map[index]))
1038 index = map[index] >> 8;
1042 index = map[index + 1];
1046 gcmkFATAL("MMU table correcupted at index %u!", index);
1047 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1051 *IndexAdjusted = index;
1054 return gcvSTATUS_OK;
1063 IN gckKERNEL Kernel,
1064 IN gctSIZE_T MmuSize,
1068 #if gcdSHARED_PAGETABLE
1072 gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
1074 if (sharedPageTable == gcvNULL)
1077 gckOS_Allocate(Kernel->os,
1078 sizeof(struct _gcsSharedPageTable),
1080 sharedPageTable = pointer;
1083 gckOS_ZeroMemory(sharedPageTable,
1084 sizeof(struct _gcsSharedPageTable)));
1086 gcmkONERROR(_Construct(Kernel, MmuSize, &sharedPageTable->mmu));
1088 else if (Kernel->hardware->mmuVersion == 0)
1090 /* Set page table address. */
1092 gckHARDWARE_SetMMU(Kernel->hardware, (gctPOINTER) sharedPageTable->mmu->pageTableLogical));
1095 *Mmu = sharedPageTable->mmu;
1097 sharedPageTable->hardwares[sharedPageTable->reference] = Kernel->hardware;
1099 sharedPageTable->reference++;
1101 gcmkFOOTER_ARG("sharedPageTable->reference=%lu", sharedPageTable->reference);
1102 return gcvSTATUS_OK;
1105 if (sharedPageTable)
1107 if (sharedPageTable->mmu)
1109 gcmkVERIFY_OK(gckMMU_Destroy(sharedPageTable->mmu));
1112 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, sharedPageTable));
1117 #elif gcdMIRROR_PAGETABLE
1121 gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
1123 if (mirrorPageTable == gcvNULL)
1126 gckOS_Allocate(Kernel->os,
1127 sizeof(struct _gcsMirrorPageTable),
1129 mirrorPageTable = pointer;
1132 gckOS_ZeroMemory(mirrorPageTable,
1133 sizeof(struct _gcsMirrorPageTable)));
1136 gckOS_CreateMutex(Kernel->os, &mirrorPageTableMutex));
1139 gcmkONERROR(_Construct(Kernel, MmuSize, Mmu));
1141 mirrorPageTable->mmus[mirrorPageTable->reference] = *Mmu;
1143 mirrorPageTable->hardwares[mirrorPageTable->reference] = Kernel->hardware;
1145 mirrorPageTable->reference++;
1147 gcmkFOOTER_ARG("mirrorPageTable->reference=%lu", mirrorPageTable->reference);
1148 return gcvSTATUS_OK;
1151 if (mirrorPageTable && mirrorPageTable->reference == 0)
1153 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, mirrorPageTable));
1159 return _Construct(Kernel, MmuSize, Mmu);
1168 #if gcdSHARED_PAGETABLE
1169 sharedPageTable->reference--;
1171 if (sharedPageTable->reference == 0)
1173 if (sharedPageTable->mmu)
1175 gcmkVERIFY_OK(_Destroy(Mmu));
1178 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, sharedPageTable));
1181 return gcvSTATUS_OK;
1182 #elif gcdMIRROR_PAGETABLE
1183 mirrorPageTable->reference--;
1185 if (mirrorPageTable->reference == 0)
1187 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTable));
1188 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTableMutex));
1191 return _Destroy(Mmu);
1193 return _Destroy(Mmu);
1197 /*******************************************************************************
1199 ** gckMMU_AllocatePages
1201 ** Allocate pages inside the page table.
1206 ** Pointer to an gckMMU object.
1208 ** gctSIZE_T PageCount
1209 ** Number of pages to allocate.
1213 ** gctPOINTER * PageTable
1214 ** Pointer to a variable that receives the base address of the page
1217 ** gctUINT32 * Address
1218 ** Pointer to a variable that receives the hardware specific address.
1223 IN gctSIZE_T PageCount,
1224 IN gceSURF_TYPE Type,
1225 OUT gctPOINTER * PageTable,
1226 OUT gctUINT32 * Address
1230 gctBOOL mutex = gcvFALSE;
1231 gctUINT32 index = 0, previous = ~0U, left;
1232 gctUINT32_PTR pageTable;
1236 gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu, PageCount);
1238 /* Verify the arguments. */
1239 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1240 gcmkVERIFY_ARGUMENT(PageCount > 0);
1241 gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
1243 if (PageCount > Mmu->pageTableEntries)
1245 gcmkPRINT("[galcore]: %s(%d): Run out of free page entry.",
1246 __FUNCTION__, __LINE__);
1248 /* Not enough pages avaiable. */
1249 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1252 /* Grab the mutex. */
1253 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
1256 /* Cast pointer to page table. */
1257 for (pageTable = Mmu->pageTableLogical, gotIt = gcvFALSE; !gotIt;)
1259 index = Mmu->heapList;
1261 if ((Mmu->hardware->mmuVersion == 0) && (Type == gcvSURF_VERTEX))
1263 gcmkONERROR(_AdjustIndex(
1267 gcdVERTEX_START / gcmSIZEOF(gctUINT32),
1272 /* Walk the heap list. */
1273 for (; !gotIt && (index < Mmu->pageTableEntries);)
1275 /* Check the node type. */
1276 switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[index])))
1279 /* Single odes are valid if we only need 1 page. */
1286 /* Move to next node. */
1288 index = _ReadPageEntry(&pageTable[index]) >> 8;
1293 /* Test if the node has enough space. */
1294 if (PageCount <= (_ReadPageEntry(&pageTable[index]) >> 8))
1300 /* Move to next node. */
1302 index = _ReadPageEntry(&pageTable[index + 1]);
1307 gcmkFATAL("MMU table correcupted at index %u!", index);
1308 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1312 /* Test if we are out of memory. */
1313 if (index >= Mmu->pageTableEntries)
1317 /* Time to move out the trash! */
1318 gcmkONERROR(_Collect(Mmu));
1322 gcmkPRINT("[galcore]: %s(%d): Run out of free page entry.",
1323 __FUNCTION__, __LINE__);
1325 /* Out of resources. */
1326 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
1331 switch (gcmENTRY_TYPE(_ReadPageEntry(&pageTable[index])))
1334 /* Unlink single node from free list. */
1336 _Link(Mmu, previous, _ReadPageEntry(&pageTable[index]) >> 8));
1340 /* Check how many pages will be left. */
1341 left = (_ReadPageEntry(&pageTable[index]) >> 8) - PageCount;
1345 /* The entire node is consumed, just unlink it. */
1347 _Link(Mmu, previous, _ReadPageEntry(&pageTable[index + 1])));
1351 /* One page will remain. Convert the node to a single node and
1352 ** advance the index. */
1353 _WritePageEntry(&pageTable[index], (_ReadPageEntry(&pageTable[index + 1]) << 8) | gcvMMU_SINGLE);
1358 /* Enough pages remain for a new node. However, we will just adjust
1359 ** the size of the current node and advance the index. */
1360 _WritePageEntry(&pageTable[index], (left << 8) | gcvMMU_FREE);
1367 /* Mark node as used. */
1368 gcmkONERROR(_FillPageTable(&pageTable[index], PageCount, gcvMMU_USED));
1370 /* Return pointer to page table. */
1371 *PageTable = &pageTable[index];
1373 /* Build virtual address. */
1374 if (Mmu->hardware->mmuVersion == 0)
1377 gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address));
1381 gctUINT32 masterOffset = index / gcdMMU_STLB_4K_ENTRY_NUM
1382 + Mmu->dynamicMappingStart;
1383 gctUINT32 slaveOffset = index % gcdMMU_STLB_4K_ENTRY_NUM;
1385 address = (masterOffset << gcdMMU_MTLB_SHIFT)
1386 | (slaveOffset << gcdMMU_STLB_4K_SHIFT);
1389 if (Address != gcvNULL)
1394 /* Release the mutex. */
1395 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1398 gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
1399 *PageTable, gcmOPT_VALUE(Address));
1400 return gcvSTATUS_OK;
1406 /* Release the mutex. */
1407 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1410 /* Return the status. */
1415 /*******************************************************************************
1419 ** Free pages inside the page table.
1424 ** Pointer to an gckMMU object.
1426 ** gctPOINTER PageTable
1427 ** Base address of the page table to free.
1429 ** gctSIZE_T PageCount
1430 ** Number of pages to free.
1439 IN gctPOINTER PageTable,
1440 IN gctSIZE_T PageCount
1443 gctUINT32_PTR pageTable;
1445 gctBOOL acquired = gcvFALSE;
1447 gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu",
1448 Mmu, PageTable, PageCount);
1450 /* Verify the arguments. */
1451 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1452 gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
1453 gcmkVERIFY_ARGUMENT(PageCount > 0);
1455 /* Convert the pointer. */
1456 pageTable = (gctUINT32_PTR) PageTable;
1458 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
1461 #if gcdMMU_CLEAR_VALUE
1462 if (Mmu->hardware->mmuVersion == 0)
1464 _FillPageTable(pageTable, PageCount, gcdMMU_CLEAR_VALUE);
1470 /* Single page node. */
1471 _WritePageEntry(pageTable,
1472 (~((1U<<8)-1)) | gcvMMU_SINGLE
1473 #if gcdUSE_MMU_EXCEPTION
1474 /* Enable exception */
1481 /* Mark the node as free. */
1482 _WritePageEntry(pageTable,
1483 (PageCount << 8) | gcvMMU_FREE
1484 #if gcdUSE_MMU_EXCEPTION
1485 /* Enable exception */
1489 _WritePageEntry(pageTable + 1, ~0U);
1491 #if gcdUSE_MMU_EXCEPTION
1492 /* Enable exception */
1493 gcmkVERIFY_OK(_FillPageTable(pageTable + 2, PageCount - 2, 1 << 1));
1497 /* We have free nodes. */
1498 Mmu->freeNodes = gcvTRUE;
1500 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1501 acquired = gcvFALSE;
1505 return gcvSTATUS_OK;
1510 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1518 gckMMU_AllocatePages(
1520 IN gctSIZE_T PageCount,
1521 OUT gctPOINTER * PageTable,
1522 OUT gctUINT32 * Address
1525 return gckMMU_AllocatePagesEx(
1526 Mmu, PageCount, gcvSURF_UNKNOWN, PageTable, Address);
1530 gckMMU_AllocatePagesEx(
1532 IN gctSIZE_T PageCount,
1533 IN gceSURF_TYPE Type,
1534 OUT gctPOINTER * PageTable,
1535 OUT gctUINT32 * Address
1538 #if gcdMIRROR_PAGETABLE
1540 gctPOINTER pageTable;
1544 gctBOOL acquired = gcvFALSE;
1545 gctBOOL allocated = gcvFALSE;
1547 gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE);
1550 /* Allocate page table for current MMU. */
1551 for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1553 if (Mmu == mirrorPageTable->mmus[i])
1555 gcmkONERROR(_AllocatePages(Mmu, PageCount, Type, PageTable, Address));
1556 allocated = gcvTRUE;
1560 /* Allocate page table for other MMUs. */
1561 for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1563 mmu = mirrorPageTable->mmus[i];
1567 gcmkONERROR(_AllocatePages(mmu, PageCount, Type, &pageTable, &address));
1568 gcmkASSERT(address == *Address);
1572 gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1573 acquired = gcvFALSE;
1575 return gcvSTATUS_OK;
1580 /* Page tables for multiple GPU always keep the same. So it is impossible
1581 * the fist one allocates successfully but others fail.
1588 gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1593 return _AllocatePages(Mmu, PageCount, Type, PageTable, Address);
1600 IN gctPOINTER PageTable,
1601 IN gctSIZE_T PageCount
1604 #if gcdMIRROR_PAGETABLE
1609 gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE);
1611 gcmkVERIFY_OK(_FreePages(Mmu, PageTable, PageCount));
1613 offset = (gctUINT32)PageTable - (gctUINT32)Mmu->pageTableLogical;
1615 for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1617 mmu = mirrorPageTable->mmus[i];
1621 gcmkVERIFY_OK(_FreePages(mmu, mmu->pageTableLogical + offset/4, PageCount));
1625 gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex);
1627 return gcvSTATUS_OK;
1629 return _FreePages(Mmu, PageTable, PageCount);
1636 IN gctUINT32 PhysBaseAddr,
1637 IN gctUINT32 PhysSize
1641 #if gcdSHARED_PAGETABLE
1642 gckHARDWARE hardware;
1646 gcmkHEADER_ARG("Mmu=0x%x", Mmu);
1648 /* Verify the arguments. */
1649 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1651 #if gcdSHARED_PAGETABLE
1654 gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
1655 return gcvSTATUS_SKIP;
1659 if (Mmu->hardware->mmuVersion == 0)
1662 gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
1663 return gcvSTATUS_SKIP;
1669 gcmkONERROR(_FillFlatMapping(
1676 gcmkONERROR(_SetupDynamicSpace(Mmu));
1678 #if gcdSHARED_PAGETABLE
1679 for(i = 0; i < gcdMAX_GPU_COUNT; i++)
1681 hardware = sharedPageTable->hardwares[i];
1682 if (hardware != gcvNULL)
1685 gckHARDWARE_SetMMUv2(
1690 (gctUINT8_PTR)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
1697 gckHARDWARE_SetMMUv2(
1702 (gctUINT8_PTR)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
1707 Mmu->enabled = gcvTRUE;
1711 return gcvSTATUS_OK;
1715 /* Return the status. */
1723 IN gctUINT32 PageAddress,
1724 IN gctUINT32 *PageEntry
1727 #if gcdMIRROR_PAGETABLE
1728 gctUINT32_PTR pageEntry;
1731 gctUINT32 offset = (gctUINT32)PageEntry - (gctUINT32)Mmu->pageTableLogical;
1735 gcmkHEADER_ARG("Mmu=0x%x", Mmu);
1737 /* Verify the arguments. */
1738 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1739 gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL);
1740 gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
1742 if (Mmu->hardware->mmuVersion == 0)
1748 data = _SetPage(PageAddress);
1751 _WritePageEntry(PageEntry, data);
1753 #if gcdMIRROR_PAGETABLE
1754 for (i = 0; i < (gctINT)mirrorPageTable->reference; i++)
1756 mmu = mirrorPageTable->mmus[i];
1760 pageEntry = mmu->pageTableLogical + offset / 4;
1762 if (mmu->hardware->mmuVersion == 0)
1764 _WritePageEntry(pageEntry, PageAddress);
1768 _WritePageEntry(pageEntry, _SetPage(PageAddress));
1776 return gcvSTATUS_OK;
1783 IN gcuVIDMEM_NODE_PTR Node)
1786 gctBOOL mutex = gcvFALSE;
1788 gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu, Node);
1790 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1792 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1795 Node->Virtual.next = Mmu->nodeList;
1796 Mmu->nodeList = Node;
1798 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1801 return gcvSTATUS_OK;
1806 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1816 IN gcuVIDMEM_NODE_PTR Node)
1819 gctBOOL mutex = gcvFALSE;
1820 gcuVIDMEM_NODE_PTR *iter;
1822 gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu, Node);
1824 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1826 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1829 for (iter = &Mmu->nodeList; *iter; iter = &(*iter)->Virtual.next)
1833 *iter = Node->Virtual.next;
1838 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1841 return gcvSTATUS_OK;
1846 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1854 gckMMU_FreeHandleMemory(
1855 IN gckKERNEL Kernel,
1861 gctBOOL acquired = gcvFALSE;
1862 gcuVIDMEM_NODE_PTR curr, next;
1864 gcmkHEADER_ARG("Kernel=0x%x, Mmu=0x%x Pid=%u", Kernel, Mmu, Pid);
1866 gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1867 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1869 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->nodeMutex, gcvINFINITE));
1872 for (curr = Mmu->nodeList; curr != gcvNULL; curr = next)
1874 next = curr->Virtual.next;
1876 if (curr->Virtual.processID == Pid)
1878 while (curr->Virtual.unlockPendings[Kernel->core] == 0 && curr->Virtual.lockeds[Kernel->core] > 0)
1880 gcmkONERROR(gckVIDMEM_Unlock(Kernel, curr, gcvSURF_TYPE_UNKNOWN, gcvNULL));
1883 gcmkVERIFY_OK(gckVIDMEM_Free(curr));
1887 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1890 return gcvSTATUS_OK;
1895 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1908 gckHARDWARE hardware;
1909 #if gcdSHARED_PAGETABLE
1911 for (i = 0; i < gcdMAX_GPU_COUNT; i++)
1914 if (i == gcvCORE_VG)
1919 hardware = sharedPageTable->hardwares[i];
1922 /* Notify cores who use this page table. */
1924 gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1927 #elif gcdMIRROR_PAGETABLE
1929 for (i = 0; i < mirrorPageTable->reference; i++)
1931 hardware = mirrorPageTable->hardwares[i];
1933 /* Notify cores who use this page table. */
1935 gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1938 hardware = Mmu->hardware;
1940 gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
1943 return gcvSTATUS_OK;
1947 gckMMU_DumpPageTableEntry(
1949 IN gctUINT32 Address
1952 gctUINT32_PTR pageTable;
1954 gctUINT32 mtlb, stlb;
1956 gcmkHEADER_ARG("Mmu=0x%08X Address=0x%08X", Mmu, Address);
1957 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1959 gcmkASSERT(Mmu->hardware->mmuVersion > 0);
1961 mtlb = (Address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT;
1962 stlb = (Address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT;
1964 if (Address >= 0x80000000)
1966 pageTable = Mmu->pageTableLogical;
1968 index = (mtlb - Mmu->dynamicMappingStart)
1969 * gcdMMU_STLB_4K_ENTRY_NUM
1972 gcmkPRINT(" Page table entry = 0x%08X", _ReadPageEntry(pageTable + index));
1976 return gcvSTATUS_OK;
1979 /******************************************************************************
1980 ****************************** T E S T C O D E ******************************
1981 ******************************************************************************/