--- /dev/null
+/*++\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