]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
ArmPkg: Added Aarch64 support
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / AArch64 / Mmu.c
diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
new file mode 100644 (file)
index 0000000..9043afa
--- /dev/null
@@ -0,0 +1,189 @@
+/*++\r
+\r
+Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>\r
+Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>\r
+Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+--*/\r
+\r
+#include "CpuDxe.h"\r
+\r
+#define TT_ATTR_INDX_INVALID    ((UINT32)~0)\r
+\r
+STATIC\r
+UINT64\r
+GetFirstPageAttribute (\r
+  IN UINT64  *FirstLevelTableAddress,\r
+  IN UINTN    TableLevel\r
+  )\r
+{\r
+  UINT64 FirstEntry;\r
+\r
+  // Get the first entry of the table\r
+  FirstEntry = *FirstLevelTableAddress;\r
+\r
+  if ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
+    // Only valid for Levels 0, 1 and 2\r
+    ASSERT (TableLevel < 3);\r
+\r
+    // Get the attribute of the subsequent table\r
+    return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);\r
+  } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) ||\r
+             ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3)))\r
+  {\r
+    return FirstEntry & TT_ATTR_INDX_MASK;\r
+  } else {\r
+    return TT_ATTR_INDX_INVALID;\r
+  }\r
+}\r
+\r
+STATIC\r
+UINT64\r
+GetNextEntryAttribute (\r
+  IN     UINT64 *TableAddress,\r
+  IN     UINTN   EntryCount,\r
+  IN     UINTN   TableLevel,\r
+  IN     UINT64  BaseAddress,\r
+  IN OUT UINT32 *PrevEntryAttribute,\r
+  IN OUT UINT64 *StartGcdRegion\r
+  )\r
+{\r
+  UINTN                             Index;\r
+  UINT64                            Entry;\r
+  UINT32                            EntryAttribute;\r
+  UINT32                            EntryType;\r
+  EFI_STATUS                        Status;\r
+  UINTN                             NumberOfDescriptors;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemorySpaceMap;\r
+\r
+  // Get the memory space map from GCD\r
+  MemorySpaceMap = NULL;\r
+  Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  // We cannot get more than 3-level page table\r
+  ASSERT (TableLevel <= 3);\r
+\r
+  // While the top level table might not contain TT_ENTRY_COUNT entries;\r
+  // the subsequent ones should be filled up\r
+  for (Index = 0; Index < EntryCount; Index++) {\r
+    Entry = TableAddress[Index];\r
+    EntryType = Entry & TT_TYPE_MASK;\r
+    EntryAttribute = Entry  & TT_ATTR_INDX_MASK;\r
+\r
+    // If Entry is a Table Descriptor type entry then go through the sub-level table\r
+    if ((EntryType == TT_TYPE_BLOCK_ENTRY) ||\r
+        ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) {\r
+      if ((*PrevEntryAttribute == TT_ATTR_INDX_INVALID) || (EntryAttribute != *PrevEntryAttribute)) {\r
+        if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
+          // Update GCD with the last region\r
+          SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
+              *StartGcdRegion,\r
+              (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion,\r
+              PageAttributeToGcdAttribute (EntryAttribute));\r
+        }\r
+\r
+        // Start of the new region\r
+        *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
+        *PrevEntryAttribute = EntryAttribute;\r
+      } else {\r
+        continue;\r
+      }\r
+    } else if (EntryType == TT_TYPE_TABLE_ENTRY) {\r
+      // Table Entry type is only valid for Level 0, 1, 2\r
+      ASSERT (TableLevel < 3);\r
+\r
+      // Increase the level number and scan the sub-level table\r
+      GetNextEntryAttribute ((UINT64*)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),\r
+                             TT_ENTRY_COUNT, TableLevel + 1,\r
+                             (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))),\r
+                             PrevEntryAttribute, StartGcdRegion);\r
+    } else {\r
+      if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
+        // Update GCD with the last region\r
+        SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
+            *StartGcdRegion,\r
+            (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)) - 1) - *StartGcdRegion,\r
+            PageAttributeToGcdAttribute (EntryAttribute));\r
+\r
+        // Start of the new region\r
+        *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
+        *PrevEntryAttribute = TT_ATTR_INDX_INVALID;\r
+      }\r
+    }\r
+  }\r
+\r
+  return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL(TableLevel));\r
+}\r
+\r
+EFI_STATUS\r
+SyncCacheConfig (\r
+  IN  EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  UINT32                              PageAttribute = 0;\r
+  UINT64                             *FirstLevelTableAddress;\r
+  UINTN                               TableLevel;\r
+  UINTN                               TableCount;\r
+  UINTN                               NumberOfDescriptors;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap;\r
+  UINTN                               Tcr;\r
+  UINTN                               T0SZ;\r
+  UINT64                              BaseAddressGcdRegion;\r
+  UINT64                              EndAddressGcdRegion;\r
+\r
+  // This code assumes MMU is enabled and filed with section translations\r
+  ASSERT (ArmMmuEnabled ());\r
+\r
+  //\r
+  // Get the memory space map from GCD\r
+  //\r
+  MemorySpaceMap = NULL;\r
+  Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  // The GCD implementation maintains its own copy of the state of memory space attributes.  GCD needs\r
+  // to know what the initial memory space attributes are.  The CPU Arch. Protocol does not provide a\r
+  // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were\r
+  // a client) to update its copy of the attributes.  This is bad architecture and should be replaced\r
+  // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
+\r
+  // Obtain page table base\r
+  FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ());\r
+\r
+  // Get Translation Control Register value\r
+  Tcr = ArmGetTCR ();\r
+  // Get Address Region Size\r
+  T0SZ = Tcr & TCR_T0SZ_MASK;\r
+\r
+  // Get the level of the first table for the indicated Address Region Size\r
+  GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount);\r
+\r
+  // First Attribute of the Page Tables\r
+  PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel);\r
+\r
+  // We scan from the start of the memory map (ie: at the address 0x0)\r
+  BaseAddressGcdRegion = 0x0;\r
+  EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress,\r
+                                               TableCount, TableLevel,\r
+                                               BaseAddressGcdRegion,\r
+                                               &PageAttribute, &BaseAddressGcdRegion);\r
+\r
+  // Update GCD with the last region\r
+  SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
+      BaseAddressGcdRegion,\r
+      EndAddressGcdRegion - BaseAddressGcdRegion,\r
+      PageAttributeToGcdAttribute (PageAttribute));\r
+\r
+  return EFI_SUCCESS;\r
+}\r