1 /****************************************************************************
3 * Copyright (C) 2005 - 2011 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 *****************************************************************************/
24 #include "gc_hal_kernel_precomp.h"
26 #define _GC_OBJ_ZONE gcvZONE_MMU
28 typedef enum _gceMMU_TYPE
36 #define gcdMMU_TABLE_DUMP 0
38 #define gcdMMU_MTLB_SHIFT 22
39 #define gcdMMU_STLB_4K_SHIFT 12
40 #define gcdMMU_STLB_64K_SHIFT 16
42 #define gcdMMU_MTLB_BITS (32 - gcdMMU_MTLB_SHIFT)
43 #define gcdMMU_PAGE_4K_BITS gcdMMU_STLB_4K_SHIFT
44 #define gcdMMU_STLB_4K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_4K_BITS)
45 #define gcdMMU_PAGE_64K_BITS gcdMMU_STLB_64K_SHIFT
46 #define gcdMMU_STLB_64K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_64K_BITS)
48 #define gcdMMU_MTLB_ENTRY_NUM (1 << gcdMMU_MTLB_BITS)
49 #define gcdMMU_MTLB_SIZE (gcdMMU_MTLB_ENTRY_NUM << 2)
50 #define gcdMMU_STLB_4K_ENTRY_NUM (1 << gcdMMU_STLB_4K_BITS)
51 #define gcdMMU_STLB_4K_SIZE (gcdMMU_STLB_4K_ENTRY_NUM << 2)
52 #define gcdMMU_PAGE_4K_SIZE (1 << gcdMMU_STLB_4K_SHIFT)
53 #define gcdMMU_STLB_64K_ENTRY_NUM (1 << gcdMMU_STLB_64K_BITS)
54 #define gcdMMU_STLB_64K_SIZE (gcdMMU_STLB_64K_ENTRY_NUM << 2)
55 #define gcdMMU_PAGE_64K_SIZE (1 << gcdMMU_STLB_64K_SHIFT)
57 #define gcdMMU_MTLB_MASK (~((1U << gcdMMU_MTLB_SHIFT)-1))
58 #define gcdMMU_STLB_4K_MASK ((~0U << gcdMMU_STLB_4K_SHIFT) ^ gcdMMU_MTLB_MASK)
59 #define gcdMMU_PAGE_4K_MASK (gcdMMU_PAGE_4K_SIZE - 1)
60 #define gcdMMU_STLB_64K_MASK ((~((1U << gcdMMU_STLB_64K_SHIFT)-1)) ^ gcdMMU_MTLB_MASK)
61 #define gcdMMU_PAGE_64K_MASK (gcdMMU_PAGE_64K_SIZE - 1)
63 typedef struct _gcsMMU_STLB
*gcsMMU_STLB_PTR
;
65 typedef struct _gcsMMU_STLB
67 gctPHYS_ADDR physical
;
68 gctUINT32_PTR logical
;
73 gctUINT32 mtlbEntryNum
;
77 #define gcvMMU_STLB_SIZE gcmALIGN(sizeof(gcsMMU_STLB), 4)
86 if (Index
>= Mmu
->pageTableEntries
)
88 /* Just move heap pointer. */
93 /* Address page table. */
94 gctUINT32_PTR pageTable
= Mmu
->pageTableLogical
;
96 /* Dispatch on node type. */
97 switch (pageTable
[Index
] & 0xFF)
100 /* Set single index. */
101 pageTable
[Index
] = (Next
<< 8) | gcvMMU_SINGLE
;
106 pageTable
[Index
+ 1] = Next
;
110 gcmkFATAL("MMU table correcupted at index %u!", Index
);
111 return gcvSTATUS_HEAP_CORRUPTED
;
127 gctUINT32_PTR pageTable
= Mmu
->pageTableLogical
;
131 /* Initialize a single page node. */
132 pageTable
[Node
] = (~((1U<<8)-1)) | gcvMMU_SINGLE
;
136 /* Initialize the node. */
137 pageTable
[Node
+ 0] = (Count
<< 8) | gcvMMU_FREE
;
138 pageTable
[Node
+ 1] = ~0U;
141 /* Append the node. */
142 return _Link(Mmu
, Index
, Node
);
150 gctUINT32_PTR pageTable
= Mmu
->pageTableLogical
;
152 gctUINT32 i
, previous
, start
= 0, count
= 0;
154 /* Flush the MMU cache. */
156 gckHARDWARE_FlushMMU(Mmu
->hardware
));
158 previous
= Mmu
->heapList
= ~0U;
159 Mmu
->freeNodes
= gcvFALSE
;
161 /* Walk the entire page table. */
162 for (i
= 0; i
< Mmu
->pageTableEntries
; ++i
)
164 /* Dispatch based on type of page. */
165 switch (pageTable
[i
] & 0xFF)
168 /* Used page, so close any open node. */
172 gcmkONERROR(_AddFree(Mmu
, previous
, start
, count
));
174 /* Reset the node. */
181 /* Single free node. */
184 /* Start a new node. */
193 /* Start a new node. */
197 /* Advance the count. */
198 count
+= pageTable
[i
] >> 8;
200 /* Advance the index into the page table. */
201 i
+= (pageTable
[i
] >> 8) - 1;
205 gcmkFATAL("MMU page table correcupted at index %u!", i
);
206 return gcvSTATUS_HEAP_CORRUPTED
;
210 /* See if we have an open node left. */
213 /* Add the node to the list. */
214 gcmkONERROR(_AddFree(Mmu
, previous
, start
, count
));
217 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_MMU
,
218 "Performed a garbage collection of the MMU heap.");
224 /* Return the staus. */
231 IN gctSIZE_T PageCount
,
232 OUT gcsMMU_STLB_PTR
*Stlb
236 gcsMMU_STLB_PTR stlb
= gcvNULL
;
237 gctPHYS_ADDR physical
;
238 gctPOINTER logical
= gcvNULL
;
239 gctSIZE_T size
= (PageCount
<< 2) + gcvMMU_STLB_SIZE
;
243 gckOS_AllocateContiguous(Mmu
->os
,
249 gcmkONERROR(gckOS_ZeroMemory(logical
, size
));
251 /* Convert logical address into a physical address. */
253 gckOS_GetPhysicalAddress(Mmu
->os
, logical
, &address
));
255 stlb
= (gcsMMU_STLB_PTR
)logical
;
256 stlb
->pageCount
= PageCount
;
257 stlb
->logical
= logical
;
258 stlb
->physical
= physical
;
259 stlb
->physBase
= address
;
261 stlb
->mtlbIndex
= ~0U;
262 stlb
->mtlbEntryNum
= 0;
263 stlb
->next
= gcvNULL
;
271 if (logical
!= gcvNULL
)
273 gckOS_FreeContiguous(
287 IN gcsMMU_STLB_PTR Stlb
290 gcmkASSERT(Stlb
->logical
== (gctPOINTER
)Stlb
);
292 return gckOS_FreeContiguous(
301 _SetPage(gctUINT32 PageAddress
)
306 /* Ignore exception */
315 IN gctUINT32 PhysBase
,
320 gctBOOL mutex
= gcvFALSE
;
321 gcsMMU_STLB_PTR head
= gcvNULL
, pre
= gcvNULL
;
322 gctUINT32 start
= PhysBase
& (~gcdMMU_PAGE_64K_MASK
);
323 gctUINT32 end
= (PhysBase
+ Size
- 1) & (~gcdMMU_PAGE_64K_MASK
);
324 gctUINT32 mStart
= start
>> gcdMMU_MTLB_SHIFT
;
325 gctUINT32 mEnd
= end
>> gcdMMU_MTLB_SHIFT
;
326 gctUINT32 sStart
= (start
& gcdMMU_STLB_64K_MASK
) >> gcdMMU_STLB_64K_SHIFT
;
327 gctUINT32 sEnd
= (end
& gcdMMU_STLB_64K_MASK
) >> gcdMMU_STLB_64K_SHIFT
;
329 /* Grab the mutex. */
330 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->pageTableMutex
, gcvINFINITE
));
333 while (mStart
<= mEnd
)
335 gcmkASSERT(mStart
< gcdMMU_MTLB_ENTRY_NUM
);
336 if (*(Mmu
->pageTableLogical
+ mStart
) == 0)
338 gcsMMU_STLB_PTR stlb
;
339 gctPOINTER pointer
= gcvNULL
;
340 gctUINT32 last
= (mStart
== mEnd
) ? sEnd
: (gcdMMU_STLB_64K_ENTRY_NUM
- 1);
342 gcmkONERROR(gckOS_Allocate(Mmu
->os
, sizeof(struct _gcsMMU_STLB
), &pointer
));
345 stlb
->mtlbEntryNum
= 0;
346 stlb
->next
= gcvNULL
;
347 stlb
->physical
= gcvNULL
;
348 stlb
->logical
= gcvNULL
;
349 stlb
->size
= gcdMMU_STLB_64K_SIZE
;
358 gcmkASSERT(pre
->next
== gcvNULL
);
364 gckOS_AllocateContiguous(Mmu
->os
,
368 (gctPOINTER
)&stlb
->logical
));
370 gcmkONERROR(gckOS_ZeroMemory(stlb
->logical
, stlb
->size
));
372 gcmkONERROR(gckOS_GetPhysicalAddress(
377 if (stlb
->physBase
& (gcdMMU_STLB_64K_SIZE
- 1))
379 gcmkONERROR(gcvSTATUS_NOT_ALIGNED
);
382 *(Mmu
->pageTableLogical
+ mStart
)
386 /* Ignore exception */
390 #if gcdMMU_TABLE_DUMP
391 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
392 __FUNCTION__
, __LINE__
,
394 *(Mmu
->pageTableLogical
+ mStart
));
397 stlb
->mtlbIndex
= mStart
;
398 stlb
->mtlbEntryNum
= 1;
399 #if gcdMMU_TABLE_DUMP
400 gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n",
401 __FUNCTION__
, __LINE__
,
406 while (sStart
<= last
)
408 gcmkASSERT(!(start
& gcdMMU_PAGE_64K_MASK
));
409 *(stlb
->logical
+ sStart
) = _SetPage(start
);
410 #if gcdMMU_TABLE_DUMP
411 gckOS_Print("%s(%d): insert STLB[%d]: %08x\n",
412 __FUNCTION__
, __LINE__
,
414 *(stlb
->logical
+ sStart
));
417 start
+= gcdMMU_PAGE_64K_SIZE
;
427 gcmkONERROR(gcvSTATUS_INVALID_REQUEST
);
431 /* Insert the stlb into staticSTLB. */
432 if (Mmu
->staticSTLB
== gcvNULL
)
434 Mmu
->staticSTLB
= head
;
438 gcmkASSERT(pre
== gcvNULL
);
439 gcmkASSERT(pre
->next
== gcvNULL
);
440 pre
->next
= Mmu
->staticSTLB
;
441 Mmu
->staticSTLB
= head
;
444 /* Release the mutex. */
445 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
452 while (head
!= gcvNULL
)
457 if (pre
->physical
!= gcvNULL
)
460 gckOS_FreeContiguous(Mmu
->os
,
466 if (pre
->mtlbEntryNum
!= 0)
468 gcmkASSERT(pre
->mtlbEntryNum
== 1);
469 *(Mmu
->pageTableLogical
+ pre
->mtlbIndex
) = 0;
472 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu
->os
, pre
));
477 /* Release the mutex. */
478 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
484 /*******************************************************************************
488 ** Construct a new gckMMU object.
493 ** Pointer to an gckKERNEL object.
496 ** Number of bytes for the page table.
501 ** Pointer to a variable that receives the gckMMU object pointer.
506 IN gctSIZE_T MmuSize
,
511 gckHARDWARE hardware
;
513 gckMMU mmu
= gcvNULL
;
514 gctUINT32_PTR pageTable
;
515 gctPOINTER pointer
= gcvNULL
;
517 gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel
, MmuSize
);
519 /* Verify the arguments. */
520 gcmkVERIFY_OBJECT(Kernel
, gcvOBJ_KERNEL
);
521 gcmkVERIFY_ARGUMENT(MmuSize
> 0);
522 gcmkVERIFY_ARGUMENT(Mmu
!= gcvNULL
);
524 /* Extract the gckOS object pointer. */
526 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
528 /* Extract the gckHARDWARE object pointer. */
529 hardware
= Kernel
->hardware
;
530 gcmkVERIFY_OBJECT(hardware
, gcvOBJ_HARDWARE
);
532 /* Allocate memory for the gckMMU object. */
533 gcmkONERROR(gckOS_Allocate(os
, sizeof(struct _gckMMU
), &pointer
));
537 /* Initialize the gckMMU object. */
538 mmu
->object
.type
= gcvOBJ_MMU
;
540 mmu
->hardware
= hardware
;
541 mmu
->pageTableMutex
= gcvNULL
;
542 mmu
->pageTableLogical
= gcvNULL
;
543 mmu
->staticSTLB
= gcvNULL
;
544 mmu
->enabled
= gcvFALSE
;
546 mmu
->nodeList
= gcvNULL
;
547 mmu
->nodeMutex
= gcvNULL
;
550 /* Create the page table mutex. */
551 gcmkONERROR(gckOS_CreateMutex(os
, &mmu
->pageTableMutex
));
554 /* Create the node list mutex. */
555 gcmkONERROR(gckOS_CreateMutex(os
, &mmu
->nodeMutex
));
558 if (hardware
->mmuVersion
== 0)
560 /* Allocate the page table (not more than 256 kB). */
561 mmu
->pageTableSize
= gcmMIN(MmuSize
, 256 << 10);
563 gckOS_AllocateContiguous(os
,
566 &mmu
->pageTablePhysical
,
569 mmu
->pageTableLogical
= pointer
;
571 /* Compute number of entries in page table. */
572 mmu
->pageTableEntries
= mmu
->pageTableSize
/ sizeof(gctUINT32
);
574 /* Mark all pages as free. */
575 pageTable
= mmu
->pageTableLogical
;
576 pageTable
[0] = (mmu
->pageTableEntries
<< 8) | gcvMMU_FREE
;
579 mmu
->freeNodes
= gcvFALSE
;
581 /* Set page table address. */
583 gckHARDWARE_SetMMU(hardware
, (gctPOINTER
) mmu
->pageTableLogical
));
587 /* Allocate the 4K mode MTLB table. */
588 mmu
->pageTableSize
= gcdMMU_MTLB_SIZE
+ 64;
591 gckOS_AllocateContiguous(os
,
594 &mmu
->pageTablePhysical
,
597 mmu
->pageTableLogical
= pointer
;
599 /* Invalid all the entries. */
601 gckOS_ZeroMemory(pointer
, mmu
->pageTableSize
));
604 /* Return the gckMMU object pointer. */
608 gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu
);
615 if (mmu
->pageTableLogical
!= gcvNULL
)
617 /* Free the page table. */
619 gckOS_FreeContiguous(os
,
620 mmu
->pageTablePhysical
,
621 (gctPOINTER
) mmu
->pageTableLogical
,
622 mmu
->pageTableSize
));
625 if (mmu
->pageTableMutex
!= gcvNULL
)
627 /* Delete the mutex. */
629 gckOS_DeleteMutex(os
, mmu
->pageTableMutex
));
633 if (mmu
->nodeMutex
!= gcvNULL
)
635 /* Delete the mutex. */
637 gckOS_DeleteMutex(os
, mmu
->nodeMutex
));
641 /* Mark the gckMMU object as unknown. */
642 mmu
->object
.type
= gcvOBJ_UNKNOWN
;
644 /* Free the allocates memory. */
645 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os
, mmu
));
648 /* Return the status. */
653 /*******************************************************************************
657 ** Destroy a gckMMU object.
662 ** Pointer to an gckMMU object.
674 gcuVIDMEM_NODE_PTR node
, next
;
677 gcmkHEADER_ARG("Mmu=0x%x", Mmu
);
679 /* Verify the arguments. */
680 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
683 /* Free all associated virtual memory. */
684 for (node
= Mmu
->nodeList
; node
!= gcvNULL
; node
= next
)
686 next
= node
->Virtual
.next
;
687 gcmkVERIFY_OK(gckVIDMEM_Free(node
));
691 while (Mmu
->staticSTLB
!= gcvNULL
)
693 gcsMMU_STLB_PTR pre
= Mmu
->staticSTLB
;
694 Mmu
->staticSTLB
= pre
->next
;
696 if (pre
->physical
!= gcvNULL
)
699 gckOS_FreeContiguous(Mmu
->os
,
705 if (pre
->mtlbEntryNum
!= 0)
707 gcmkASSERT(pre
->mtlbEntryNum
== 1);
708 *(Mmu
->pageTableLogical
+ pre
->mtlbIndex
) = 0;
709 #if gcdMMU_TABLE_DUMP
710 gckOS_Print("%s(%d): clean MTLB[%d]\n",
711 __FUNCTION__
, __LINE__
,
716 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu
->os
, pre
));
719 /* Free the page table. */
721 gckOS_FreeContiguous(Mmu
->os
,
722 Mmu
->pageTablePhysical
,
723 (gctPOINTER
) Mmu
->pageTableLogical
,
724 Mmu
->pageTableSize
));
727 /* Delete the node list mutex. */
728 gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu
->os
, Mmu
->nodeMutex
));
731 /* Delete the page table mutex. */
732 gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu
->os
, Mmu
->pageTableMutex
));
734 /* Mark the gckMMU object as unknown. */
735 Mmu
->object
.type
= gcvOBJ_UNKNOWN
;
737 /* Free the gckMMU object. */
738 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu
->os
, Mmu
));
745 /*******************************************************************************
747 ** gckMMU_AllocatePages
749 ** Allocate pages inside the page table.
754 ** Pointer to an gckMMU object.
756 ** gctSIZE_T PageCount
757 ** Number of pages to allocate.
761 ** gctPOINTER * PageTable
762 ** Pointer to a variable that receives the base address of the page
765 ** gctUINT32 * Address
766 ** Pointer to a variable that receives the hardware specific address.
769 gckMMU_AllocatePages(
771 IN gctSIZE_T PageCount
,
772 OUT gctPOINTER
* PageTable
,
773 OUT gctUINT32
* Address
777 gctBOOL mutex
= gcvFALSE
;
778 gctUINT32 index
= 0, previous
= ~0U, left
;
779 gctUINT32_PTR pageTable
;
783 gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu
, PageCount
);
785 /* Verify the arguments. */
786 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
787 gcmkVERIFY_ARGUMENT(PageCount
> 0);
788 gcmkVERIFY_ARGUMENT(PageTable
!= gcvNULL
);
790 if (Mmu
->hardware
->mmuVersion
== 0)
792 if (PageCount
> Mmu
->pageTableEntries
)
794 /* Not enough pages avaiable. */
795 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES
);
798 /* Grab the mutex. */
799 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->pageTableMutex
, gcvINFINITE
));
802 /* Cast pointer to page table. */
803 for (pageTable
= Mmu
->pageTableLogical
, gotIt
= gcvFALSE
; !gotIt
;)
805 /* Walk the heap list. */
806 for (index
= Mmu
->heapList
; !gotIt
&& (index
< Mmu
->pageTableEntries
);)
808 /* Check the node type. */
809 switch (pageTable
[index
] & 0xFF)
812 /* Single odes are valid if we only need 1 page. */
819 /* Move to next node. */
821 index
= pageTable
[index
] >> 8;
826 /* Test if the node has enough space. */
827 if (PageCount
<= (pageTable
[index
] >> 8))
833 /* Move to next node. */
835 index
= pageTable
[index
+ 1];
840 gcmkFATAL("MMU table correcupted at index %u!", index
);
841 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES
);
845 /* Test if we are out of memory. */
846 if (index
>= Mmu
->pageTableEntries
)
850 /* Time to move out the trash! */
851 gcmkONERROR(_Collect(Mmu
));
855 /* Out of resources. */
856 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES
);
861 switch (pageTable
[index
] & 0xFF)
864 /* Unlink single node from free list. */
866 _Link(Mmu
, previous
, pageTable
[index
] >> 8));
870 /* Check how many pages will be left. */
871 left
= (pageTable
[index
] >> 8) - PageCount
;
875 /* The entire node is consumed, just unlink it. */
877 _Link(Mmu
, previous
, pageTable
[index
+ 1]));
881 /* One page will remain. Convert the node to a single node and
882 ** advance the index. */
883 pageTable
[index
] = (pageTable
[index
+ 1] << 8) | gcvMMU_SINGLE
;
888 /* Enough pages remain for a new node. However, we will just adjust
889 ** the size of the current node and advance the index. */
890 pageTable
[index
] = (left
<< 8) | gcvMMU_FREE
;
897 /* Mark node as used. */
898 pageTable
[index
] = gcvMMU_USED
;
900 /* Return pointer to page table. */
901 *PageTable
= &pageTable
[index
];
903 /* Build virtual address. */
905 gckHARDWARE_BuildVirtualAddress(Mmu
->hardware
, index
, 0, &address
));
907 if (Address
!= gcvNULL
)
912 /* Release the mutex. */
913 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
916 gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
917 *PageTable
, gcmOPT_VALUE(Address
));
924 gctBOOL succeed
= gcvFALSE
;
925 gcsMMU_STLB_PTR stlb
= gcvNULL
;
927 gcmALIGN(PageCount
, gcdMMU_STLB_4K_ENTRY_NUM
) / gcdMMU_STLB_4K_ENTRY_NUM
;
929 if (Mmu
->enabled
== gcvFALSE
)
931 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_MMU
,
932 "gckMMU_AllocatePages(New MMU): failed by the MMU not enabled");
934 gcmkONERROR(gcvSTATUS_INVALID_REQUEST
);
937 /* Grab the mutex. */
938 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->pageTableMutex
, gcvINFINITE
));
941 for (i
= 0; i
< gcdMMU_MTLB_ENTRY_NUM
; i
++)
943 if (*(Mmu
->pageTableLogical
+ i
) == 0)
947 for (j
= 1; j
< nMtlbEntry
; j
++)
949 if (*(Mmu
->pageTableLogical
+ i
+ j
) != 0)
956 if (succeed
== gcvTRUE
)
963 if (succeed
== gcvFALSE
)
965 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES
);
968 gcmkONERROR(_GetStlb(Mmu
, PageCount
, &stlb
));
971 stlb
->mtlbEntryNum
= nMtlbEntry
;
973 addr
= stlb
->physBase
;
974 for (j
= 0; j
< nMtlbEntry
; j
++)
976 gcmkASSERT(!(addr
& (gcdMMU_STLB_4K_SIZE
- 1)));
977 *(Mmu
->pageTableLogical
+ i
+ j
) = addr
980 /* Ignore exception */
984 #if gcdMMU_TABLE_DUMP
985 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
986 __FUNCTION__
, __LINE__
,
988 *(Mmu
->pageTableLogical
+ i
+ j
));
990 addr
+= gcdMMU_STLB_4K_SIZE
;
993 /* Release the mutex. */
994 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
996 *PageTable
= (gctUINT8_PTR
)stlb
+ gcvMMU_STLB_SIZE
;
998 if (Address
!= gcvNULL
)
1000 *Address
= (i
<< gcdMMU_MTLB_SHIFT
)
1001 | (gcvMMU_STLB_SIZE
<< 10);
1004 /* Flush the MMU cache. */
1006 gckHARDWARE_FlushMMU(Mmu
->hardware
));
1009 gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
1010 *PageTable
, gcmOPT_VALUE(Address
));
1011 return gcvSTATUS_OK
;
1018 /* Release the mutex. */
1019 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
1022 /* Return the status. */
1027 /*******************************************************************************
1031 ** Free pages inside the page table.
1036 ** Pointer to an gckMMU object.
1038 ** gctPOINTER PageTable
1039 ** Base address of the page table to free.
1041 ** gctSIZE_T PageCount
1042 ** Number of pages to free.
1051 IN gctPOINTER PageTable
,
1052 IN gctSIZE_T PageCount
1056 gctBOOL mutex
= gcvFALSE
;
1058 gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu",
1059 Mmu
, PageTable
, PageCount
);
1061 /* Verify the arguments. */
1062 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1063 gcmkVERIFY_ARGUMENT(PageTable
!= gcvNULL
);
1064 gcmkVERIFY_ARGUMENT(PageCount
> 0);
1066 if (Mmu
->hardware
->mmuVersion
== 0)
1068 gctUINT32_PTR pageTable
;
1070 /* Convert the pointer. */
1071 pageTable
= (gctUINT32_PTR
) PageTable
;
1075 /* Single page node. */
1076 pageTable
[0] = (~((1U<<8)-1)) | gcvMMU_SINGLE
;
1080 /* Mark the node as free. */
1081 pageTable
[0] = (PageCount
<< 8) | gcvMMU_FREE
;
1085 /* We have free nodes. */
1086 Mmu
->freeNodes
= gcvTRUE
;
1090 return gcvSTATUS_OK
;
1094 gcsMMU_STLB_PTR stlb
= (gcsMMU_STLB_PTR
)((gctUINT8_PTR
) PageTable
- gcvMMU_STLB_SIZE
);
1097 if (Mmu
->enabled
== gcvFALSE
)
1099 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_MMU
,
1100 "gckMMU_FreePages(New MMU): failed by the MMU not enabled");
1102 gcmkONERROR(gcvSTATUS_INVALID_REQUEST
);
1105 if ((stlb
->logical
!= (gctPOINTER
)stlb
)
1106 || (stlb
->pageCount
!= PageCount
)
1107 || (stlb
->mtlbIndex
>= gcdMMU_MTLB_ENTRY_NUM
)
1108 || (stlb
->mtlbEntryNum
== 0))
1110 gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT
);
1113 /* Flush the MMU cache. */
1115 gckHARDWARE_FlushMMU(Mmu
->hardware
));
1117 /* Grab the mutex. */
1118 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->pageTableMutex
, gcvINFINITE
));
1121 for (i
= 0; i
< stlb
->mtlbEntryNum
; i
++)
1123 /* clean the MTLB entries. */
1124 gcmkASSERT((*(Mmu
->pageTableLogical
+ stlb
->mtlbIndex
+ i
) & 7) == 1);
1125 *(Mmu
->pageTableLogical
+ stlb
->mtlbIndex
+ i
) = 0;
1126 #if gcdMMU_TABLE_DUMP
1127 gckOS_Print("%s(%d): clean MTLB[%d]\n",
1128 __FUNCTION__
, __LINE__
,
1129 stlb
->mtlbIndex
+ i
);
1133 /* Release the mutex. */
1134 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
1137 gcmkONERROR(_PutStlb(Mmu
, stlb
));
1141 return gcvSTATUS_OK
;
1147 /* Release the mutex. */
1148 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->pageTableMutex
));
1151 /* Return the status. */
1159 IN gctUINT32 PhysBaseAddr
,
1160 IN gctUINT32 PhysSize
1165 gcmkHEADER_ARG("Mmu=0x%x", Mmu
);
1167 /* Verify the arguments. */
1168 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1170 if (Mmu
->hardware
->mmuVersion
== 0)
1173 gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP
);
1174 return gcvSTATUS_SKIP
;
1180 gcmkONERROR(_FillFlatMapping(
1188 gckHARDWARE_SetMMUv2(
1191 Mmu
->pageTableLogical
,
1193 (gctUINT8_PTR
)Mmu
->pageTableLogical
+ gcdMMU_MTLB_SIZE
1196 Mmu
->enabled
= gcvTRUE
;
1200 return gcvSTATUS_OK
;
1204 /* Return the status. */
1212 IN gctUINT32 PageAddress
,
1213 IN gctUINT32
*PageEntry
1216 gcmkHEADER_ARG("Mmu=0x%x", Mmu
);
1218 /* Verify the arguments. */
1219 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1220 gcmkVERIFY_ARGUMENT(PageEntry
!= gcvNULL
);
1221 gcmkVERIFY_ARGUMENT(!(PageAddress
& 0xFFF));
1223 if (Mmu
->hardware
->mmuVersion
== 0)
1225 *PageEntry
= PageAddress
;
1229 *PageEntry
= _SetPage(PageAddress
);
1234 return gcvSTATUS_OK
;
1241 IN gcuVIDMEM_NODE_PTR Node
)
1244 gctBOOL mutex
= gcvFALSE
;
1246 gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu
, Node
);
1248 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1250 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->nodeMutex
, gcvINFINITE
));
1253 Node
->Virtual
.next
= Mmu
->nodeList
;
1254 Mmu
->nodeList
= Node
;
1256 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1259 return gcvSTATUS_OK
;
1264 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1274 IN gcuVIDMEM_NODE_PTR Node
)
1277 gctBOOL mutex
= gcvFALSE
;
1278 gcuVIDMEM_NODE_PTR
*iter
;
1280 gcmkHEADER_ARG("Mmu=0x%x Node=0x%x", Mmu
, Node
);
1282 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1284 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->nodeMutex
, gcvINFINITE
));
1287 for (iter
= &Mmu
->nodeList
; *iter
; iter
= &(*iter
)->Virtual
.next
)
1291 *iter
= Node
->Virtual
.next
;
1296 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1299 return gcvSTATUS_OK
;
1304 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1312 gckMMU_FreeHandleMemory(
1313 IN gckKERNEL Kernel
,
1319 gctBOOL acquired
= gcvFALSE
;
1320 gcuVIDMEM_NODE_PTR curr
, next
;
1322 gcmkHEADER_ARG("Kernel=0x%x, Mmu=0x%x Pid=%u", Kernel
, Mmu
, Pid
);
1324 gcmkVERIFY_OBJECT(Kernel
, gcvOBJ_KERNEL
);
1325 gcmkVERIFY_OBJECT(Mmu
, gcvOBJ_MMU
);
1327 gcmkONERROR(gckOS_AcquireMutex(Mmu
->os
, Mmu
->nodeMutex
, gcvINFINITE
));
1330 for (curr
= Mmu
->nodeList
; curr
!= gcvNULL
; curr
= next
)
1332 next
= curr
->Virtual
.next
;
1334 if (curr
->Virtual
.processID
== Pid
)
1336 while (curr
->Virtual
.unlockPendings
[Kernel
->core
] == 0 && curr
->Virtual
.lockeds
[Kernel
->core
] > 0)
1338 gcmkONERROR(gckVIDMEM_Unlock(Kernel
, curr
, gcvSURF_TYPE_UNKNOWN
, gcvNULL
));
1341 gcmkVERIFY_OK(gckVIDMEM_Free(curr
));
1345 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1348 return gcvSTATUS_OK
;
1353 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu
->os
, Mmu
->nodeMutex
));
1361 /******************************************************************************
1362 ****************************** T E S T C O D E ******************************
1363 ******************************************************************************/