ENGR00156850 gpu-viv: add gpu-viv driver source
[wandboard.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_mmu.c
blob7ef88356a9b58ff5fe7e05daceef9acb15d5dabf
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
30 gcvMMU_USED = 0,
31 gcvMMU_SINGLE,
32 gcvMMU_FREE,
34 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;
69 gctSIZE_T size;
70 gctUINT32 physBase;
71 gctSIZE_T pageCount;
72 gctUINT32 mtlbIndex;
73 gctUINT32 mtlbEntryNum;
74 gcsMMU_STLB_PTR next;
75 } gcsMMU_STLB;
77 #define gcvMMU_STLB_SIZE gcmALIGN(sizeof(gcsMMU_STLB), 4)
79 static gceSTATUS
80 _Link(
81 IN gckMMU Mmu,
82 IN gctUINT32 Index,
83 IN gctUINT32 Next
86 if (Index >= Mmu->pageTableEntries)
88 /* Just move heap pointer. */
89 Mmu->heapList = Next;
91 else
93 /* Address page table. */
94 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
96 /* Dispatch on node type. */
97 switch (pageTable[Index] & 0xFF)
99 case gcvMMU_SINGLE:
100 /* Set single index. */
101 pageTable[Index] = (Next << 8) | gcvMMU_SINGLE;
102 break;
104 case gcvMMU_FREE:
105 /* Set index. */
106 pageTable[Index + 1] = Next;
107 break;
109 default:
110 gcmkFATAL("MMU table correcupted at index %u!", Index);
111 return gcvSTATUS_HEAP_CORRUPTED;
115 /* Success. */
116 return gcvSTATUS_OK;
119 static gceSTATUS
120 _AddFree(
121 IN gckMMU Mmu,
122 IN gctUINT32 Index,
123 IN gctUINT32 Node,
124 IN gctUINT32 Count
127 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
129 if (Count == 1)
131 /* Initialize a single page node. */
132 pageTable[Node] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
134 else
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);
145 static gceSTATUS
146 _Collect(
147 IN gckMMU Mmu
150 gctUINT32_PTR pageTable = Mmu->pageTableLogical;
151 gceSTATUS status;
152 gctUINT32 i, previous, start = 0, count = 0;
154 /* Flush the MMU cache. */
155 gcmkONERROR(
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)
167 case gcvMMU_USED:
168 /* Used page, so close any open node. */
169 if (count > 0)
171 /* Add the node. */
172 gcmkONERROR(_AddFree(Mmu, previous, start, count));
174 /* Reset the node. */
175 previous = start;
176 count = 0;
178 break;
180 case gcvMMU_SINGLE:
181 /* Single free node. */
182 if (count++ == 0)
184 /* Start a new node. */
185 start = i;
187 break;
189 case gcvMMU_FREE:
190 /* A free node. */
191 if (count == 0)
193 /* Start a new node. */
194 start = i;
197 /* Advance the count. */
198 count += pageTable[i] >> 8;
200 /* Advance the index into the page table. */
201 i += (pageTable[i] >> 8) - 1;
202 break;
204 default:
205 gcmkFATAL("MMU page table correcupted at index %u!", i);
206 return gcvSTATUS_HEAP_CORRUPTED;
210 /* See if we have an open node left. */
211 if (count > 0)
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.");
220 /* Success. */
221 return gcvSTATUS_OK;
223 OnError:
224 /* Return the staus. */
225 return status;
228 static gceSTATUS
229 _GetStlb(
230 IN gckMMU Mmu,
231 IN gctSIZE_T PageCount,
232 OUT gcsMMU_STLB_PTR *Stlb
235 gceSTATUS status;
236 gcsMMU_STLB_PTR stlb = gcvNULL;
237 gctPHYS_ADDR physical;
238 gctPOINTER logical = gcvNULL;
239 gctSIZE_T size = (PageCount << 2) + gcvMMU_STLB_SIZE;
240 gctUINT32 address;
242 gcmkONERROR(
243 gckOS_AllocateContiguous(Mmu->os,
244 gcvFALSE,
245 &size,
246 &physical,
247 &logical));
249 gcmkONERROR(gckOS_ZeroMemory(logical, size));
251 /* Convert logical address into a physical address. */
252 gcmkONERROR(
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;
260 stlb->size = size;
261 stlb->mtlbIndex = ~0U;
262 stlb->mtlbEntryNum = 0;
263 stlb->next = gcvNULL;
265 *Stlb = stlb;
267 return gcvSTATUS_OK;
269 OnError:
271 if (logical != gcvNULL)
273 gckOS_FreeContiguous(
274 Mmu->os,
275 physical,
276 logical,
277 size
281 return status;
284 static gceSTATUS
285 _PutStlb(
286 IN gckMMU Mmu,
287 IN gcsMMU_STLB_PTR Stlb
290 gcmkASSERT(Stlb->logical == (gctPOINTER)Stlb);
292 return gckOS_FreeContiguous(
293 Mmu->os,
294 Stlb->physical,
295 Stlb,
296 Stlb->size
300 static gctUINT32
301 _SetPage(gctUINT32 PageAddress)
303 return PageAddress
304 /* writable */
305 | (1 << 2)
306 /* Ignore exception */
307 | (0 << 1)
308 /* Present */
309 | (1 << 0);
312 static gceSTATUS
313 _FillFlatMapping(
314 IN gckMMU Mmu,
315 IN gctUINT32 PhysBase,
316 OUT gctSIZE_T Size
319 gceSTATUS status;
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));
331 mutex = gcvTRUE;
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));
343 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;
350 stlb->pageCount = 0;
352 if (pre == gcvNULL)
354 pre = head = stlb;
356 else
358 gcmkASSERT(pre->next == gcvNULL);
359 pre->next = stlb;
360 pre = stlb;
363 gcmkONERROR(
364 gckOS_AllocateContiguous(Mmu->os,
365 gcvFALSE,
366 &stlb->size,
367 &stlb->physical,
368 (gctPOINTER)&stlb->logical));
370 gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size));
372 gcmkONERROR(gckOS_GetPhysicalAddress(
373 Mmu->os,
374 stlb->logical,
375 &stlb->physBase));
377 if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1))
379 gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
382 *(Mmu->pageTableLogical + mStart)
383 = stlb->physBase
384 /* 64KB page size */
385 | (1 << 2)
386 /* Ignore exception */
387 | (0 << 1)
388 /* Present */
389 | (1 << 0);
390 #if gcdMMU_TABLE_DUMP
391 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
392 __FUNCTION__, __LINE__,
393 mStart,
394 *(Mmu->pageTableLogical + mStart));
395 #endif
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__,
402 stlb->logical,
403 stlb->physBase);
404 #endif
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__,
413 sStart,
414 *(stlb->logical + sStart));
415 #endif
416 /* next page. */
417 start += gcdMMU_PAGE_64K_SIZE;
418 sStart++;
419 stlb->pageCount++;
422 sStart = 0;
423 ++mStart;
425 else
427 gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
431 /* Insert the stlb into staticSTLB. */
432 if (Mmu->staticSTLB == gcvNULL)
434 Mmu->staticSTLB = head;
436 else
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));
447 return gcvSTATUS_OK;
449 OnError:
451 /* Roll back. */
452 while (head != gcvNULL)
454 pre = head;
455 head = head->next;
457 if (pre->physical != gcvNULL)
459 gcmkVERIFY_OK(
460 gckOS_FreeContiguous(Mmu->os,
461 pre->physical,
462 pre->logical,
463 pre->size));
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));
475 if (mutex)
477 /* Release the mutex. */
478 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
481 return status;
484 /*******************************************************************************
486 ** gckMMU_Construct
488 ** Construct a new gckMMU object.
490 ** INPUT:
492 ** gckKERNEL Kernel
493 ** Pointer to an gckKERNEL object.
495 ** gctSIZE_T MmuSize
496 ** Number of bytes for the page table.
498 ** OUTPUT:
500 ** gckMMU * Mmu
501 ** Pointer to a variable that receives the gckMMU object pointer.
503 gceSTATUS
504 gckMMU_Construct(
505 IN gckKERNEL Kernel,
506 IN gctSIZE_T MmuSize,
507 OUT gckMMU * Mmu
510 gckOS os;
511 gckHARDWARE hardware;
512 gceSTATUS status;
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. */
525 os = Kernel->os;
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));
535 mmu = pointer;
537 /* Initialize the gckMMU object. */
538 mmu->object.type = gcvOBJ_MMU;
539 mmu->os = os;
540 mmu->hardware = hardware;
541 mmu->pageTableMutex = gcvNULL;
542 mmu->pageTableLogical = gcvNULL;
543 mmu->staticSTLB = gcvNULL;
544 mmu->enabled = gcvFALSE;
545 #ifdef __QNXNTO__
546 mmu->nodeList = gcvNULL;
547 mmu->nodeMutex = gcvNULL;
548 #endif
550 /* Create the page table mutex. */
551 gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex));
553 #ifdef __QNXNTO__
554 /* Create the node list mutex. */
555 gcmkONERROR(gckOS_CreateMutex(os, &mmu->nodeMutex));
556 #endif
558 if (hardware->mmuVersion == 0)
560 /* Allocate the page table (not more than 256 kB). */
561 mmu->pageTableSize = gcmMIN(MmuSize, 256 << 10);
562 gcmkONERROR(
563 gckOS_AllocateContiguous(os,
564 gcvFALSE,
565 &mmu->pageTableSize,
566 &mmu->pageTablePhysical,
567 &pointer));
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;
577 pageTable[1] = ~0U;
578 mmu->heapList = 0;
579 mmu->freeNodes = gcvFALSE;
581 /* Set page table address. */
582 gcmkONERROR(
583 gckHARDWARE_SetMMU(hardware, (gctPOINTER) mmu->pageTableLogical));
585 else
587 /* Allocate the 4K mode MTLB table. */
588 mmu->pageTableSize = gcdMMU_MTLB_SIZE + 64;
590 gcmkONERROR(
591 gckOS_AllocateContiguous(os,
592 gcvFALSE,
593 &mmu->pageTableSize,
594 &mmu->pageTablePhysical,
595 &pointer));
597 mmu->pageTableLogical = pointer;
599 /* Invalid all the entries. */
600 gcmkONERROR(
601 gckOS_ZeroMemory(pointer, mmu->pageTableSize));
604 /* Return the gckMMU object pointer. */
605 *Mmu = mmu;
607 /* Success. */
608 gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu);
609 return gcvSTATUS_OK;
611 OnError:
612 /* Roll back. */
613 if (mmu != gcvNULL)
615 if (mmu->pageTableLogical != gcvNULL)
617 /* Free the page table. */
618 gcmkVERIFY_OK(
619 gckOS_FreeContiguous(os,
620 mmu->pageTablePhysical,
621 (gctPOINTER) mmu->pageTableLogical,
622 mmu->pageTableSize));
625 if (mmu->pageTableMutex != gcvNULL)
627 /* Delete the mutex. */
628 gcmkVERIFY_OK(
629 gckOS_DeleteMutex(os, mmu->pageTableMutex));
632 #ifdef __QNXNTO__
633 if (mmu->nodeMutex != gcvNULL)
635 /* Delete the mutex. */
636 gcmkVERIFY_OK(
637 gckOS_DeleteMutex(os, mmu->nodeMutex));
639 #endif
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. */
649 gcmkFOOTER();
650 return status;
653 /*******************************************************************************
655 ** gckMMU_Destroy
657 ** Destroy a gckMMU object.
659 ** INPUT:
661 ** gckMMU Mmu
662 ** Pointer to an gckMMU object.
664 ** OUTPUT:
666 ** Nothing.
668 gceSTATUS
669 gckMMU_Destroy(
670 IN gckMMU Mmu
673 #ifdef __QNXNTO__
674 gcuVIDMEM_NODE_PTR node, next;
675 #endif
677 gcmkHEADER_ARG("Mmu=0x%x", Mmu);
679 /* Verify the arguments. */
680 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
682 #ifdef __QNXNTO__
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));
689 #endif
691 while (Mmu->staticSTLB != gcvNULL)
693 gcsMMU_STLB_PTR pre = Mmu->staticSTLB;
694 Mmu->staticSTLB = pre->next;
696 if (pre->physical != gcvNULL)
698 gcmkVERIFY_OK(
699 gckOS_FreeContiguous(Mmu->os,
700 pre->physical,
701 pre->logical,
702 pre->size));
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__,
712 pre->mtlbIndex);
713 #endif
716 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
719 /* Free the page table. */
720 gcmkVERIFY_OK(
721 gckOS_FreeContiguous(Mmu->os,
722 Mmu->pageTablePhysical,
723 (gctPOINTER) Mmu->pageTableLogical,
724 Mmu->pageTableSize));
726 #ifdef __QNXNTO__
727 /* Delete the node list mutex. */
728 gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->nodeMutex));
729 #endif
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));
740 /* Success. */
741 gcmkFOOTER_NO();
742 return gcvSTATUS_OK;
745 /*******************************************************************************
747 ** gckMMU_AllocatePages
749 ** Allocate pages inside the page table.
751 ** INPUT:
753 ** gckMMU Mmu
754 ** Pointer to an gckMMU object.
756 ** gctSIZE_T PageCount
757 ** Number of pages to allocate.
759 ** OUTPUT:
761 ** gctPOINTER * PageTable
762 ** Pointer to a variable that receives the base address of the page
763 ** table.
765 ** gctUINT32 * Address
766 ** Pointer to a variable that receives the hardware specific address.
768 gceSTATUS
769 gckMMU_AllocatePages(
770 IN gckMMU Mmu,
771 IN gctSIZE_T PageCount,
772 OUT gctPOINTER * PageTable,
773 OUT gctUINT32 * Address
776 gceSTATUS status;
777 gctBOOL mutex = gcvFALSE;
778 gctUINT32 index = 0, previous = ~0U, left;
779 gctUINT32_PTR pageTable;
780 gctBOOL gotIt;
781 gctUINT32 address;
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));
800 mutex = gcvTRUE;
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)
811 case gcvMMU_SINGLE:
812 /* Single odes are valid if we only need 1 page. */
813 if (PageCount == 1)
815 gotIt = gcvTRUE;
817 else
819 /* Move to next node. */
820 previous = index;
821 index = pageTable[index] >> 8;
823 break;
825 case gcvMMU_FREE:
826 /* Test if the node has enough space. */
827 if (PageCount <= (pageTable[index] >> 8))
829 gotIt = gcvTRUE;
831 else
833 /* Move to next node. */
834 previous = index;
835 index = pageTable[index + 1];
837 break;
839 default:
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)
848 if (Mmu->freeNodes)
850 /* Time to move out the trash! */
851 gcmkONERROR(_Collect(Mmu));
853 else
855 /* Out of resources. */
856 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
861 switch (pageTable[index] & 0xFF)
863 case gcvMMU_SINGLE:
864 /* Unlink single node from free list. */
865 gcmkONERROR(
866 _Link(Mmu, previous, pageTable[index] >> 8));
867 break;
869 case gcvMMU_FREE:
870 /* Check how many pages will be left. */
871 left = (pageTable[index] >> 8) - PageCount;
872 switch (left)
874 case 0:
875 /* The entire node is consumed, just unlink it. */
876 gcmkONERROR(
877 _Link(Mmu, previous, pageTable[index + 1]));
878 break;
880 case 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;
884 index ++;
885 break;
887 default:
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;
891 index += left;
892 break;
894 break;
897 /* Mark node as used. */
898 pageTable[index] = gcvMMU_USED;
900 /* Return pointer to page table. */
901 *PageTable = &pageTable[index];
903 /* Build virtual address. */
904 gcmkONERROR(
905 gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address));
907 if (Address != gcvNULL)
909 *Address = address;
912 /* Release the mutex. */
913 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
915 /* Success. */
916 gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
917 *PageTable, gcmOPT_VALUE(Address));
918 return gcvSTATUS_OK;
920 else
922 gctUINT i, j;
923 gctUINT32 addr;
924 gctBOOL succeed = gcvFALSE;
925 gcsMMU_STLB_PTR stlb = gcvNULL;
926 gctUINT nMtlbEntry =
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));
939 mutex = gcvTRUE;
941 for (i = 0; i < gcdMMU_MTLB_ENTRY_NUM; i++)
943 if (*(Mmu->pageTableLogical + i) == 0)
945 succeed = gcvTRUE;
947 for (j = 1; j < nMtlbEntry; j++)
949 if (*(Mmu->pageTableLogical + i + j) != 0)
951 succeed = gcvFALSE;
952 break;
956 if (succeed == gcvTRUE)
958 break;
963 if (succeed == gcvFALSE)
965 gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
968 gcmkONERROR(_GetStlb(Mmu, PageCount, &stlb));
970 stlb->mtlbIndex = i;
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
978 /* 4KB page size */
979 | (0 << 2)
980 /* Ignore exception */
981 | (0 << 1)
982 /* Present */
983 | (1 << 0);
984 #if gcdMMU_TABLE_DUMP
985 gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
986 __FUNCTION__, __LINE__,
987 i + j,
988 *(Mmu->pageTableLogical + i + j));
989 #endif
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. */
1005 gcmkONERROR(
1006 gckHARDWARE_FlushMMU(Mmu->hardware));
1008 /* Success. */
1009 gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
1010 *PageTable, gcmOPT_VALUE(Address));
1011 return gcvSTATUS_OK;
1014 OnError:
1016 if (mutex)
1018 /* Release the mutex. */
1019 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1022 /* Return the status. */
1023 gcmkFOOTER();
1024 return status;
1027 /*******************************************************************************
1029 ** gckMMU_FreePages
1031 ** Free pages inside the page table.
1033 ** INPUT:
1035 ** gckMMU Mmu
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.
1044 ** OUTPUT:
1046 ** Nothing.
1048 gceSTATUS
1049 gckMMU_FreePages(
1050 IN gckMMU Mmu,
1051 IN gctPOINTER PageTable,
1052 IN gctSIZE_T PageCount
1055 gceSTATUS status;
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;
1073 if (PageCount == 1)
1075 /* Single page node. */
1076 pageTable[0] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
1078 else
1080 /* Mark the node as free. */
1081 pageTable[0] = (PageCount << 8) | gcvMMU_FREE;
1082 pageTable[1] = ~0U;
1085 /* We have free nodes. */
1086 Mmu->freeNodes = gcvTRUE;
1088 /* Success. */
1089 gcmkFOOTER_NO();
1090 return gcvSTATUS_OK;
1092 else
1094 gcsMMU_STLB_PTR stlb = (gcsMMU_STLB_PTR)((gctUINT8_PTR) PageTable - gcvMMU_STLB_SIZE);
1095 gctUINT32 i;
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. */
1114 gcmkONERROR(
1115 gckHARDWARE_FlushMMU(Mmu->hardware));
1117 /* Grab the mutex. */
1118 gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
1119 mutex = gcvTRUE;
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);
1130 #endif
1133 /* Release the mutex. */
1134 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1135 mutex = gcvFALSE;
1137 gcmkONERROR(_PutStlb(Mmu, stlb));
1139 /* Success. */
1140 gcmkFOOTER_NO();
1141 return gcvSTATUS_OK;
1144 OnError:
1145 if (mutex)
1147 /* Release the mutex. */
1148 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
1151 /* Return the status. */
1152 gcmkFOOTER();
1153 return status;
1156 gceSTATUS
1157 gckMMU_Enable(
1158 IN gckMMU Mmu,
1159 IN gctUINT32 PhysBaseAddr,
1160 IN gctUINT32 PhysSize
1163 gceSTATUS status;
1165 gcmkHEADER_ARG("Mmu=0x%x", Mmu);
1167 /* Verify the arguments. */
1168 gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
1170 if (Mmu->hardware->mmuVersion == 0)
1172 /* Success. */
1173 gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
1174 return gcvSTATUS_SKIP;
1176 else
1178 if (PhysSize != 0)
1180 gcmkONERROR(_FillFlatMapping(
1181 Mmu,
1182 PhysBaseAddr,
1183 PhysSize
1187 gcmkONERROR(
1188 gckHARDWARE_SetMMUv2(
1189 Mmu->hardware,
1190 gcvTRUE,
1191 Mmu->pageTableLogical,
1192 gcvMMU_MODE_4K,
1193 (gctUINT8_PTR)Mmu->pageTableLogical + gcdMMU_MTLB_SIZE
1196 Mmu->enabled = gcvTRUE;
1198 /* Success. */
1199 gcmkFOOTER_NO();
1200 return gcvSTATUS_OK;
1203 OnError:
1204 /* Return the status. */
1205 gcmkFOOTER();
1206 return status;
1209 gceSTATUS
1210 gckMMU_SetPage(
1211 IN gckMMU Mmu,
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;
1227 else
1229 *PageEntry = _SetPage(PageAddress);
1232 /* Success. */
1233 gcmkFOOTER_NO();
1234 return gcvSTATUS_OK;
1237 #ifdef __QNXNTO__
1238 gceSTATUS
1239 gckMMU_InsertNode(
1240 IN gckMMU Mmu,
1241 IN gcuVIDMEM_NODE_PTR Node)
1243 gceSTATUS status;
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));
1251 mutex = gcvTRUE;
1253 Node->Virtual.next = Mmu->nodeList;
1254 Mmu->nodeList = Node;
1256 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1258 gcmkFOOTER();
1259 return gcvSTATUS_OK;
1261 OnError:
1262 if (mutex)
1264 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1267 gcmkFOOTER();
1268 return status;
1271 gceSTATUS
1272 gckMMU_RemoveNode(
1273 IN gckMMU Mmu,
1274 IN gcuVIDMEM_NODE_PTR Node)
1276 gceSTATUS status;
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));
1285 mutex = gcvTRUE;
1287 for (iter = &Mmu->nodeList; *iter; iter = &(*iter)->Virtual.next)
1289 if (*iter == Node)
1291 *iter = Node->Virtual.next;
1292 break;
1296 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1298 gcmkFOOTER();
1299 return gcvSTATUS_OK;
1301 OnError:
1302 if (mutex)
1304 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1307 gcmkFOOTER();
1308 return status;
1311 gceSTATUS
1312 gckMMU_FreeHandleMemory(
1313 IN gckKERNEL Kernel,
1314 IN gckMMU Mmu,
1315 IN gctUINT32 Pid
1318 gceSTATUS status;
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));
1328 acquired = gcvTRUE;
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));
1347 gcmkFOOTER();
1348 return gcvSTATUS_OK;
1350 OnError:
1351 if (acquired)
1353 gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->nodeMutex));
1356 gcmkFOOTER();
1357 return status;
1359 #endif
1361 /******************************************************************************
1362 ****************************** T E S T C O D E ******************************
1363 ******************************************************************************/