summaryrefslogtreecommitdiff
path: root/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c')
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c1236
1 files changed, 1236 insertions, 0 deletions
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c
new file mode 100644
index 0000000..e9e1d5d
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c
@@ -0,0 +1,1236 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+#include "gc_hal.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#define _GC_OBJ_ZONE gcvZONE_MMU
+
+typedef enum _gceMMU_TYPE
+{
+ gcvMMU_USED = (0 << 4),
+ gcvMMU_SINGLE = (1 << 4),
+ gcvMMU_FREE = (2 << 4),
+}
+gceMMU_TYPE;
+
+#define gcmENTRY_TYPE(x) (x & 0xF0)
+
+#define gcdMMU_TABLE_DUMP 0
+
+/*
+ gcdMMU_CLEAR_VALUE
+
+ The clear value for the entry of the old MMU.
+*/
+#ifndef gcdMMU_CLEAR_VALUE
+# define gcdMMU_CLEAR_VALUE 0x00000ABC
+#endif
+
+typedef struct _gcsMMU_STLB *gcsMMU_STLB_PTR;
+
+typedef struct _gcsMMU_STLB
+{
+ gctPHYS_ADDR physical;
+ u32 * logical;
+ size_t size;
+ u32 physBase;
+ size_t pageCount;
+ u32 mtlbIndex;
+ u32 mtlbEntryNum;
+ gcsMMU_STLB_PTR next;
+} gcsMMU_STLB;
+
+#if gcdSHARED_PAGETABLE
+typedef struct _gcsSharedPageTable * gcsSharedPageTable_PTR;
+typedef struct _gcsSharedPageTable
+{
+ /* Shared gckMMU object. */
+ gckMMU mmu;
+
+ /* Hardwares which use this shared pagetable. */
+ gckHARDWARE hardwares[gcdCORE_COUNT];
+
+ /* Number of cores use this shared pagetable. */
+ u32 reference;
+}
+gcsSharedPageTable;
+
+static gcsSharedPageTable_PTR sharedPageTable = NULL;
+#endif
+
+static gceSTATUS
+_Link(
+ IN gckMMU Mmu,
+ IN u32 Index,
+ IN u32 Next
+ )
+{
+ if (Index >= Mmu->pageTableEntries)
+ {
+ /* Just move heap pointer. */
+ Mmu->heapList = Next;
+ }
+ else
+ {
+ /* Address page table. */
+ u32 *pageTable = Mmu->pageTableLogical;
+
+ /* Dispatch on node type. */
+ switch (gcmENTRY_TYPE(pageTable[Index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Set single index. */
+ pageTable[Index] = (Next << 8) | gcvMMU_SINGLE;
+ break;
+
+ case gcvMMU_FREE:
+ /* Set index. */
+ pageTable[Index + 1] = Next;
+ break;
+
+ default:
+ gcmkFATAL("MMU table correcupted at index %u!", Index);
+ return gcvSTATUS_HEAP_CORRUPTED;
+ }
+ }
+
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+_AddFree(
+ IN gckMMU Mmu,
+ IN u32 Index,
+ IN u32 Node,
+ IN u32 Count
+ )
+{
+ u32 *pageTable = Mmu->pageTableLogical;
+
+ if (Count == 1)
+ {
+ /* Initialize a single page node. */
+ pageTable[Node] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
+ }
+ else
+ {
+ /* Initialize the node. */
+ pageTable[Node + 0] = (Count << 8) | gcvMMU_FREE;
+ pageTable[Node + 1] = ~0U;
+ }
+
+ /* Append the node. */
+ return _Link(Mmu, Index, Node);
+}
+
+static gceSTATUS
+_Collect(
+ IN gckMMU Mmu
+ )
+{
+ u32 *pageTable = Mmu->pageTableLogical;
+ gceSTATUS status;
+ u32 i, previous, start = 0, count = 0;
+
+ previous = Mmu->heapList = ~0U;
+ Mmu->freeNodes = gcvFALSE;
+
+ /* Walk the entire page table. */
+ for (i = 0; i < Mmu->pageTableEntries; ++i)
+ {
+ /* Dispatch based on type of page. */
+ switch (gcmENTRY_TYPE(pageTable[i]))
+ {
+ case gcvMMU_USED:
+ /* Used page, so close any open node. */
+ if (count > 0)
+ {
+ /* Add the node. */
+ gcmkONERROR(_AddFree(Mmu, previous, start, count));
+
+ /* Reset the node. */
+ previous = start;
+ count = 0;
+ }
+ break;
+
+ case gcvMMU_SINGLE:
+ /* Single free node. */
+ if (count++ == 0)
+ {
+ /* Start a new node. */
+ start = i;
+ }
+ break;
+
+ case gcvMMU_FREE:
+ /* A free node. */
+ if (count == 0)
+ {
+ /* Start a new node. */
+ start = i;
+ }
+
+ /* Advance the count. */
+ count += pageTable[i] >> 8;
+
+ /* Advance the index into the page table. */
+ i += (pageTable[i] >> 8) - 1;
+ break;
+
+ default:
+ gcmkFATAL("MMU page table correcupted at index %u!", i);
+ return gcvSTATUS_HEAP_CORRUPTED;
+ }
+ }
+
+ /* See if we have an open node left. */
+ if (count > 0)
+ {
+ /* Add the node to the list. */
+ gcmkONERROR(_AddFree(Mmu, previous, start, count));
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_MMU,
+ "Performed a garbage collection of the MMU heap.");
+
+ /* Success. */
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the staus. */
+ return status;
+}
+
+static u32
+_SetPage(u32 PageAddress)
+{
+ return PageAddress
+ /* writable */
+ | (1 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+}
+
+static gceSTATUS
+_FillFlatMapping(
+ IN gckMMU Mmu,
+ IN u32 PhysBase,
+ OUT size_t Size
+ )
+{
+ gceSTATUS status;
+ int mutex = gcvFALSE;
+ gcsMMU_STLB_PTR head = NULL, pre = NULL;
+ u32 start = PhysBase & (~gcdMMU_PAGE_64K_MASK);
+ u32 end = (PhysBase + Size - 1) & (~gcdMMU_PAGE_64K_MASK);
+ u32 mStart = start >> gcdMMU_MTLB_SHIFT;
+ u32 mEnd = end >> gcdMMU_MTLB_SHIFT;
+ u32 sStart = (start & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
+ u32 sEnd = (end & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ mutex = gcvTRUE;
+
+ while (mStart <= mEnd)
+ {
+ gcmkASSERT(mStart < gcdMMU_MTLB_ENTRY_NUM);
+ if (*(Mmu->mtlbLogical + mStart) == 0)
+ {
+ gcsMMU_STLB_PTR stlb;
+ void *pointer = NULL;
+ u32 last = (mStart == mEnd) ? sEnd : (gcdMMU_STLB_64K_ENTRY_NUM - 1);
+
+ gcmkONERROR(gckOS_Allocate(Mmu->os, sizeof(struct _gcsMMU_STLB), &pointer));
+ stlb = pointer;
+
+ stlb->mtlbEntryNum = 0;
+ stlb->next = NULL;
+ stlb->physical = NULL;
+ stlb->logical = NULL;
+ stlb->size = gcdMMU_STLB_64K_SIZE;
+ stlb->pageCount = 0;
+
+ if (pre == NULL)
+ {
+ pre = head = stlb;
+ }
+ else
+ {
+ gcmkASSERT(pre->next == NULL);
+ pre->next = stlb;
+ pre = stlb;
+ }
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(Mmu->os,
+ gcvFALSE,
+ &stlb->size,
+ &stlb->physical,
+ (void *)&stlb->logical));
+
+ gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size));
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(
+ Mmu->os,
+ stlb->logical,
+ &stlb->physBase));
+
+ if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1))
+ {
+ gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
+ }
+
+ *(Mmu->mtlbLogical + mStart)
+ = stlb->physBase
+ /* 64KB page size */
+ | (1 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ mStart,
+ *(Mmu->mtlbLogical + mStart));
+#endif
+
+ stlb->mtlbIndex = mStart;
+ stlb->mtlbEntryNum = 1;
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n",
+ __FUNCTION__, __LINE__,
+ stlb->logical,
+ stlb->physBase);
+#endif
+
+ while (sStart <= last)
+ {
+ gcmkASSERT(!(start & gcdMMU_PAGE_64K_MASK));
+ *(stlb->logical + sStart) = _SetPage(start);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert STLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ sStart,
+ *(stlb->logical + sStart));
+#endif
+ /* next page. */
+ start += gcdMMU_PAGE_64K_SIZE;
+ sStart++;
+ stlb->pageCount++;
+ }
+
+ sStart = 0;
+ ++mStart;
+ }
+ else
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+ }
+
+ /* Insert the stlb into staticSTLB. */
+ if (Mmu->staticSTLB == NULL)
+ {
+ Mmu->staticSTLB = head;
+ }
+ else
+ {
+ gcmkASSERT(pre == NULL);
+ gcmkASSERT(pre->next == NULL);
+ pre->next = Mmu->staticSTLB;
+ Mmu->staticSTLB = head;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ return gcvSTATUS_OK;
+
+OnError:
+
+ /* Roll back. */
+ while (head != NULL)
+ {
+ pre = head;
+ head = head->next;
+
+ if (pre->physical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ pre->physical,
+ pre->logical,
+ pre->size));
+ }
+
+ if (pre->mtlbEntryNum != 0)
+ {
+ gcmkASSERT(pre->mtlbEntryNum == 1);
+ *(Mmu->mtlbLogical + pre->mtlbIndex) = 0;
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
+ }
+
+ if (mutex)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ return status;
+}
+
+static gceSTATUS
+_SetupDynamicSpace(
+ IN gckMMU Mmu
+ )
+{
+ gceSTATUS status;
+ int i;
+ u32 physical;
+ int numEntries;
+ u32 *pageTable;
+ int acquired = gcvFALSE;
+
+ /* find the start of dynamic address space. */
+ for (i = 0; i < gcdMMU_MTLB_ENTRY_NUM; i++)
+ {
+ if (!Mmu->mtlbLogical[i])
+ {
+ break;
+ }
+ }
+
+ Mmu->dynamicMappingStart = i;
+
+ /* Number of entries in Master TLB for dynamic mapping. */
+ numEntries = gcdMMU_MTLB_ENTRY_NUM - i;
+
+ Mmu->pageTableSize = numEntries * 4096;
+
+ Mmu->pageTableEntries = Mmu->pageTableSize / sizeof(u32);
+
+ /* Construct Slave TLB. */
+ gcmkONERROR(gckOS_AllocateContiguous(Mmu->os,
+ gcvFALSE,
+ &Mmu->pageTableSize,
+ &Mmu->pageTablePhysical,
+ (void *)&Mmu->pageTableLogical));
+
+ /* Invalidate all entries. */
+ gcmkONERROR(gckOS_ZeroMemory(Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+
+ /* Initilization. */
+ pageTable = Mmu->pageTableLogical;
+ pageTable[0] = (Mmu->pageTableEntries << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ Mmu->heapList = 0;
+ Mmu->freeNodes = gcvFALSE;
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(Mmu->os,
+ Mmu->pageTableLogical,
+ &physical));
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Map to Master TLB. */
+ for (; i < gcdMMU_MTLB_ENTRY_NUM; i++)
+ {
+ Mmu->mtlbLogical[i] = physical
+ /* 4KB page size */
+ | (0 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ i,
+ *(Mmu->mtlbLogical + i));
+#endif
+ physical += gcdMMU_STLB_4K_SIZE;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ return gcvSTATUS_OK;
+
+OnError:
+ if (Mmu->pageTableLogical)
+ {
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->pageTablePhysical,
+ (void *) Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+ }
+
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** _Construct
+**
+** Construct a new gckMMU object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** size_t MmuSize
+** Number of bytes for the page table.
+**
+** OUTPUT:
+**
+** gckMMU * Mmu
+** Pointer to a variable that receives the gckMMU object pointer.
+*/
+static gceSTATUS
+_Construct(
+ IN gckKERNEL Kernel,
+ IN size_t MmuSize,
+ OUT gckMMU * Mmu
+ )
+{
+ gckOS os;
+ gckHARDWARE hardware;
+ gceSTATUS status;
+ gckMMU mmu = NULL;
+ u32 *pageTable;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel, MmuSize);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(MmuSize > 0);
+ gcmkVERIFY_ARGUMENT(Mmu != NULL);
+
+ /* Extract the gckOS object pointer. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Extract the gckHARDWARE object pointer. */
+ hardware = Kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Allocate memory for the gckMMU object. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckMMU), &pointer));
+
+ mmu = pointer;
+
+ /* Initialize the gckMMU object. */
+ mmu->object.type = gcvOBJ_MMU;
+ mmu->os = os;
+ mmu->hardware = hardware;
+ mmu->pageTableMutex = NULL;
+ mmu->pageTableLogical = NULL;
+ mmu->mtlbLogical = NULL;
+ mmu->staticSTLB = NULL;
+ mmu->enabled = gcvFALSE;
+
+ /* Create the page table mutex. */
+ gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex));
+
+ if (hardware->mmuVersion == 0)
+ {
+ mmu->pageTableSize = MmuSize;
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(os,
+ gcvFALSE,
+ &mmu->pageTableSize,
+ &mmu->pageTablePhysical,
+ &pointer));
+
+ mmu->pageTableLogical = pointer;
+
+ /* Compute number of entries in page table. */
+ mmu->pageTableEntries = mmu->pageTableSize / sizeof(u32);
+
+ /* Mark all pages as free. */
+ pageTable = mmu->pageTableLogical;
+
+#if gcdMMU_CLEAR_VALUE
+ {
+ u32 i;
+
+ for (i = 0; i < mmu->pageTableEntries; ++i)
+ {
+ pageTable[i] = gcdMMU_CLEAR_VALUE;
+ }
+ }
+#endif
+
+ pageTable[0] = (mmu->pageTableEntries << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ mmu->heapList = 0;
+ mmu->freeNodes = gcvFALSE;
+
+ /* Set page table address. */
+ gcmkONERROR(
+ gckHARDWARE_SetMMU(hardware, (void *) mmu->pageTableLogical));
+ }
+ else
+ {
+ /* Allocate the 4K mode MTLB table. */
+ mmu->mtlbSize = gcdMMU_MTLB_SIZE + 64;
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(os,
+ gcvFALSE,
+ &mmu->mtlbSize,
+ &mmu->mtlbPhysical,
+ &pointer));
+
+ mmu->mtlbLogical = pointer;
+
+ /* Invalid all the entries. */
+ gcmkONERROR(
+ gckOS_ZeroMemory(pointer, mmu->mtlbSize));
+ }
+
+ /* Return the gckMMU object pointer. */
+ *Mmu = mmu;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (mmu != NULL)
+ {
+ if (mmu->pageTableLogical != NULL)
+ {
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(os,
+ mmu->pageTablePhysical,
+ (void *) mmu->pageTableLogical,
+ mmu->pageTableSize));
+
+ }
+
+ if (mmu->mtlbLogical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(os,
+ mmu->mtlbPhysical,
+ (void *) mmu->mtlbLogical,
+ mmu->mtlbSize));
+ }
+
+ if (mmu->pageTableMutex != NULL)
+ {
+ /* Delete the mutex. */
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, mmu->pageTableMutex));
+ }
+
+ /* Mark the gckMMU object as unknown. */
+ mmu->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the allocates memory. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, mmu));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** _Destroy
+**
+** Destroy a gckMMU object.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+static gceSTATUS
+_Destroy(
+ IN gckMMU Mmu
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+
+ while (Mmu->staticSTLB != NULL)
+ {
+ gcsMMU_STLB_PTR pre = Mmu->staticSTLB;
+ Mmu->staticSTLB = pre->next;
+
+ if (pre->physical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ pre->physical,
+ pre->logical,
+ pre->size));
+ }
+
+ if (pre->mtlbEntryNum != 0)
+ {
+ gcmkASSERT(pre->mtlbEntryNum == 1);
+ *(Mmu->mtlbLogical + pre->mtlbIndex) = 0;
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): clean MTLB[%d]\n",
+ __FUNCTION__, __LINE__,
+ pre->mtlbIndex);
+#endif
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
+ }
+
+ if (Mmu->hardware->mmuVersion != 0)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->mtlbPhysical,
+ (void *) Mmu->mtlbLogical,
+ Mmu->mtlbSize));
+ }
+
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->pageTablePhysical,
+ (void *) Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+
+ /* Delete the page table mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->pageTableMutex));
+
+ /* Mark the gckMMU object as unknown. */
+ Mmu->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckMMU object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, Mmu));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Construct(
+ IN gckKERNEL Kernel,
+ IN size_t MmuSize,
+ OUT gckMMU * Mmu
+ )
+{
+#if gcdSHARED_PAGETABLE
+ gceSTATUS status;
+ void *pointer;
+
+ gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
+
+ if (sharedPageTable == NULL)
+ {
+ gcmkONERROR(
+ gckOS_Allocate(Kernel->os,
+ sizeof(struct _gcsSharedPageTable),
+ &pointer));
+ sharedPageTable = pointer;
+
+ gcmkONERROR(
+ gckOS_ZeroMemory(sharedPageTable,
+ sizeof(struct _gcsSharedPageTable)));
+
+ gcmkONERROR(_Construct(Kernel, MmuSize, &sharedPageTable->mmu));
+ }
+
+ *Mmu = sharedPageTable->mmu;
+
+ sharedPageTable->hardwares[sharedPageTable->reference] = Kernel->hardware;
+
+ sharedPageTable->reference++;
+
+ gcmkFOOTER_ARG("sharedPageTable->reference=%lu", sharedPageTable->reference);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (sharedPageTable)
+ {
+ if (sharedPageTable->mmu)
+ {
+ gcmkVERIFY_OK(gckMMU_Destroy(sharedPageTable->mmu));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, sharedPageTable));
+ }
+
+ gcmkFOOTER();
+ return status;
+#else
+ return _Construct(Kernel, MmuSize, Mmu);
+#endif
+}
+
+gceSTATUS
+gckMMU_Destroy(
+ IN gckMMU Mmu
+ )
+{
+#if gcdSHARED_PAGETABLE
+ sharedPageTable->reference--;
+
+ if (sharedPageTable->reference == 0)
+ {
+ if (sharedPageTable->mmu)
+ {
+ gcmkVERIFY_OK(_Destroy(Mmu));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, sharedPageTable));
+ }
+
+ return gcvSTATUS_OK;
+#else
+ return _Destroy(Mmu);
+#endif
+}
+
+/*******************************************************************************
+**
+** gckMMU_AllocatePages
+**
+** Allocate pages inside the page table.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** size_t PageCount
+** Number of pages to allocate.
+**
+** OUTPUT:
+**
+** void ** PageTable
+** Pointer to a variable that receives the base address of the page
+** table.
+**
+** u32 * Address
+** Pointer to a variable that receives the hardware specific address.
+*/
+gceSTATUS
+gckMMU_AllocatePages(
+ IN gckMMU Mmu,
+ IN size_t PageCount,
+ OUT void **PageTable,
+ OUT u32 * Address
+ )
+{
+ gceSTATUS status;
+ int mutex = gcvFALSE;
+ u32 index = 0, previous = ~0U, left;
+ u32 *pageTable;
+ int gotIt;
+ u32 address;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu, PageCount);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+ gcmkVERIFY_ARGUMENT(PageTable != NULL);
+
+ if (PageCount > Mmu->pageTableEntries)
+ {
+ /* Not enough pages avaiable. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ mutex = gcvTRUE;
+
+ /* Cast pointer to page table. */
+ for (pageTable = Mmu->pageTableLogical, gotIt = gcvFALSE; !gotIt;)
+ {
+ /* Walk the heap list. */
+ for (index = Mmu->heapList; !gotIt && (index < Mmu->pageTableEntries);)
+ {
+ /* Check the node type. */
+ switch (gcmENTRY_TYPE(pageTable[index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Single odes are valid if we only need 1 page. */
+ if (PageCount == 1)
+ {
+ gotIt = gcvTRUE;
+ }
+ else
+ {
+ /* Move to next node. */
+ previous = index;
+ index = pageTable[index] >> 8;
+ }
+ break;
+
+ case gcvMMU_FREE:
+ /* Test if the node has enough space. */
+ if (PageCount <= (pageTable[index] >> 8))
+ {
+ gotIt = gcvTRUE;
+ }
+ else
+ {
+ /* Move to next node. */
+ previous = index;
+ index = pageTable[index + 1];
+ }
+ break;
+
+ default:
+ gcmkFATAL("MMU table correcupted at index %u!", index);
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+
+ /* Test if we are out of memory. */
+ if (index >= Mmu->pageTableEntries)
+ {
+ if (Mmu->freeNodes)
+ {
+ /* Time to move out the trash! */
+ gcmkONERROR(_Collect(Mmu));
+ }
+ else
+ {
+ /* Out of resources. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ }
+
+ switch (gcmENTRY_TYPE(pageTable[index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Unlink single node from free list. */
+ gcmkONERROR(
+ _Link(Mmu, previous, pageTable[index] >> 8));
+ break;
+
+ case gcvMMU_FREE:
+ /* Check how many pages will be left. */
+ left = (pageTable[index] >> 8) - PageCount;
+ switch (left)
+ {
+ case 0:
+ /* The entire node is consumed, just unlink it. */
+ gcmkONERROR(
+ _Link(Mmu, previous, pageTable[index + 1]));
+ break;
+
+ case 1:
+ /* One page will remain. Convert the node to a single node and
+ ** advance the index. */
+ pageTable[index] = (pageTable[index + 1] << 8) | gcvMMU_SINGLE;
+ index ++;
+ break;
+
+ default:
+ /* Enough pages remain for a new node. However, we will just adjust
+ ** the size of the current node and advance the index. */
+ pageTable[index] = (left << 8) | gcvMMU_FREE;
+ index += left;
+ break;
+ }
+ break;
+ }
+
+ /* Mark node as used. */
+ pageTable[index] = gcvMMU_USED;
+
+ /* Return pointer to page table. */
+ *PageTable = &pageTable[index];
+
+ /* Build virtual address. */
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ gcmkONERROR(
+ gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address));
+ }
+ else
+ {
+ u32 masterOffset = index / gcdMMU_STLB_4K_ENTRY_NUM
+ + Mmu->dynamicMappingStart;
+ u32 slaveOffset = index % gcdMMU_STLB_4K_ENTRY_NUM;
+
+ address = (masterOffset << gcdMMU_MTLB_SHIFT)
+ | (slaveOffset << gcdMMU_STLB_4K_SHIFT);
+ }
+
+ if (Address != NULL)
+ {
+ *Address = address;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
+ *PageTable, gcmOPT_VALUE(Address));
+ return gcvSTATUS_OK;
+
+OnError:
+
+ if (mutex)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckMMU_FreePages
+**
+** Free pages inside the page table.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** void *PageTable
+** Base address of the page table to free.
+**
+** size_t PageCount
+** Number of pages to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckMMU_FreePages(
+ IN gckMMU Mmu,
+ IN void *PageTable,
+ IN size_t PageCount
+ )
+{
+ u32 *pageTable;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu",
+ Mmu, PageTable, PageCount);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageTable != NULL);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+
+ /* Convert the pointer. */
+ pageTable = (u32 *) PageTable;
+
+#if gcdMMU_CLEAR_VALUE
+ {
+ u32 i;
+
+ for (i = 0; i < PageCount; ++i)
+ {
+ pageTable[i] = gcdMMU_CLEAR_VALUE;
+ }
+ }
+#endif
+
+ if (PageCount == 1)
+ {
+ /* Single page node. */
+ pageTable[0] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
+ }
+ else
+ {
+ /* Mark the node as free. */
+ pageTable[0] = (PageCount << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ }
+
+ /* We have free nodes. */
+ Mmu->freeNodes = gcvTRUE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Enable(
+ IN gckMMU Mmu,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+
+#if gcdSHARED_PAGETABLE
+ if (Mmu->enabled)
+ {
+ gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
+ return gcvSTATUS_SKIP;
+ }
+#endif
+
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ /* Success. */
+ gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
+ return gcvSTATUS_SKIP;
+ }
+ else
+ {
+ if (PhysSize != 0)
+ {
+ gcmkONERROR(_FillFlatMapping(
+ Mmu,
+ PhysBaseAddr,
+ PhysSize
+ ));
+ }
+
+ gcmkONERROR(_SetupDynamicSpace(Mmu));
+
+ gcmkONERROR(
+ gckHARDWARE_SetMMUv2(
+ Mmu->hardware,
+ gcvTRUE,
+ Mmu->mtlbLogical,
+ gcvMMU_MODE_4K,
+ (u8 *)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
+ gcvFALSE
+ ));
+
+ Mmu->enabled = gcvTRUE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckMMU_SetPage(
+ IN gckMMU Mmu,
+ IN u32 PageAddress,
+ IN u32 *PageEntry
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageEntry != NULL);
+ gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
+
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ *PageEntry = PageAddress;
+ }
+ else
+ {
+ *PageEntry = _SetPage(PageAddress);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Flush(
+ IN gckMMU Mmu
+ )
+{
+ gckHARDWARE hardware;
+#if gcdSHARED_PAGETABLE
+ int i;
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ hardware = sharedPageTable->hardwares[i];
+ if (hardware)
+ {
+ /* Notify cores who use this page table. */
+ gcmkVERIFY_OK(
+ gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
+ }
+ }
+#else
+ hardware = Mmu->hardware;
+ gcmkVERIFY_OK(
+ gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
+#endif
+
+ return gcvSTATUS_OK;
+}
+
+/******************************************************************************
+****************************** T E S T C O D E ******************************
+******************************************************************************/