]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
NetworkPkg/Ip6Dxe: Fix ASSERT logic in Ip6ProcessRouterAdvertise()
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / AArch64 / Mmu.c
CommitLineData
25402f5d
HL
1/*++\r
2\r
3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>\r
4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>\r
5Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.<BR>\r
b7a09b71 6Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
25402f5d 7\r
4059386c 8SPDX-License-Identifier: BSD-2-Clause-Patent\r
25402f5d
HL
9\r
10\r
11--*/\r
12\r
5b53eaff 13#include <Library/MemoryAllocationLib.h>\r
25402f5d
HL
14#include "CpuDxe.h"\r
15\r
16#define TT_ATTR_INDX_INVALID ((UINT32)~0)\r
17\r
18STATIC\r
19UINT64\r
20GetFirstPageAttribute (\r
21 IN UINT64 *FirstLevelTableAddress,\r
22 IN UINTN TableLevel\r
23 )\r
24{\r
25 UINT64 FirstEntry;\r
26\r
27 // Get the first entry of the table\r
28 FirstEntry = *FirstLevelTableAddress;\r
29\r
48ef4e42 30 if ((TableLevel != 3) && (FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {\r
25402f5d 31 // Only valid for Levels 0, 1 and 2\r
25402f5d
HL
32\r
33 // Get the attribute of the subsequent table\r
34 return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);\r
35 } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) ||\r
36 ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3)))\r
37 {\r
38 return FirstEntry & TT_ATTR_INDX_MASK;\r
39 } else {\r
40 return TT_ATTR_INDX_INVALID;\r
41 }\r
42}\r
43\r
44STATIC\r
45UINT64\r
46GetNextEntryAttribute (\r
47 IN UINT64 *TableAddress,\r
48 IN UINTN EntryCount,\r
49 IN UINTN TableLevel,\r
50 IN UINT64 BaseAddress,\r
51 IN OUT UINT32 *PrevEntryAttribute,\r
52 IN OUT UINT64 *StartGcdRegion\r
53 )\r
54{\r
55 UINTN Index;\r
56 UINT64 Entry;\r
57 UINT32 EntryAttribute;\r
58 UINT32 EntryType;\r
59 EFI_STATUS Status;\r
60 UINTN NumberOfDescriptors;\r
61 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
62\r
63 // Get the memory space map from GCD\r
64 MemorySpaceMap = NULL;\r
65 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
66 ASSERT_EFI_ERROR (Status);\r
67\r
68 // We cannot get more than 3-level page table\r
69 ASSERT (TableLevel <= 3);\r
70\r
71 // While the top level table might not contain TT_ENTRY_COUNT entries;\r
72 // the subsequent ones should be filled up\r
73 for (Index = 0; Index < EntryCount; Index++) {\r
74 Entry = TableAddress[Index];\r
75 EntryType = Entry & TT_TYPE_MASK;\r
76 EntryAttribute = Entry & TT_ATTR_INDX_MASK;\r
77\r
78 // If Entry is a Table Descriptor type entry then go through the sub-level table\r
79 if ((EntryType == TT_TYPE_BLOCK_ENTRY) ||\r
80 ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) {\r
81 if ((*PrevEntryAttribute == TT_ATTR_INDX_INVALID) || (EntryAttribute != *PrevEntryAttribute)) {\r
82 if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
83 // Update GCD with the last region\r
84 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
85 *StartGcdRegion,\r
047c0cbb 86 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion,\r
429358b5 87 PageAttributeToGcdAttribute (*PrevEntryAttribute));\r
25402f5d
HL
88 }\r
89\r
90 // Start of the new region\r
91 *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
92 *PrevEntryAttribute = EntryAttribute;\r
93 } else {\r
94 continue;\r
95 }\r
96 } else if (EntryType == TT_TYPE_TABLE_ENTRY) {\r
97 // Table Entry type is only valid for Level 0, 1, 2\r
98 ASSERT (TableLevel < 3);\r
99\r
100 // Increase the level number and scan the sub-level table\r
101 GetNextEntryAttribute ((UINT64*)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),\r
102 TT_ENTRY_COUNT, TableLevel + 1,\r
103 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))),\r
104 PrevEntryAttribute, StartGcdRegion);\r
105 } else {\r
106 if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {\r
107 // Update GCD with the last region\r
108 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
109 *StartGcdRegion,\r
047c0cbb
OM
110 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion,\r
111 PageAttributeToGcdAttribute (*PrevEntryAttribute));\r
25402f5d
HL
112\r
113 // Start of the new region\r
114 *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));\r
115 *PrevEntryAttribute = TT_ATTR_INDX_INVALID;\r
116 }\r
117 }\r
118 }\r
119\r
5b53eaff
OM
120 FreePool (MemorySpaceMap);\r
121\r
25402f5d
HL
122 return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL(TableLevel));\r
123}\r
124\r
125EFI_STATUS\r
126SyncCacheConfig (\r
127 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
128 )\r
129{\r
130 EFI_STATUS Status;\r
131 UINT32 PageAttribute = 0;\r
132 UINT64 *FirstLevelTableAddress;\r
133 UINTN TableLevel;\r
134 UINTN TableCount;\r
135 UINTN NumberOfDescriptors;\r
136 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
137 UINTN Tcr;\r
138 UINTN T0SZ;\r
139 UINT64 BaseAddressGcdRegion;\r
140 UINT64 EndAddressGcdRegion;\r
141\r
142 // This code assumes MMU is enabled and filed with section translations\r
143 ASSERT (ArmMmuEnabled ());\r
144\r
145 //\r
146 // Get the memory space map from GCD\r
147 //\r
148 MemorySpaceMap = NULL;\r
149 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
150 ASSERT_EFI_ERROR (Status);\r
151\r
152 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs\r
153 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a\r
154 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were\r
155 // a client) to update its copy of the attributes. This is bad architecture and should be replaced\r
156 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
157\r
158 // Obtain page table base\r
159 FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ());\r
160\r
161 // Get Translation Control Register value\r
162 Tcr = ArmGetTCR ();\r
163 // Get Address Region Size\r
164 T0SZ = Tcr & TCR_T0SZ_MASK;\r
165\r
166 // Get the level of the first table for the indicated Address Region Size\r
167 GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount);\r
168\r
169 // First Attribute of the Page Tables\r
170 PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel);\r
171\r
172 // We scan from the start of the memory map (ie: at the address 0x0)\r
173 BaseAddressGcdRegion = 0x0;\r
174 EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress,\r
175 TableCount, TableLevel,\r
176 BaseAddressGcdRegion,\r
177 &PageAttribute, &BaseAddressGcdRegion);\r
178\r
047c0cbb
OM
179 // Update GCD with the last region if valid\r
180 if (PageAttribute != TT_ATTR_INDX_INVALID) {\r
181 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,\r
182 BaseAddressGcdRegion,\r
183 EndAddressGcdRegion - BaseAddressGcdRegion,\r
184 PageAttributeToGcdAttribute (PageAttribute));\r
185 }\r
25402f5d 186\r
5b53eaff
OM
187 FreePool (MemorySpaceMap);\r
188\r
25402f5d
HL
189 return EFI_SUCCESS;\r
190}\r
2e969d2e
OM
191\r
192UINT64\r
193EfiAttributeToArmAttribute (\r
194 IN UINT64 EfiAttributes\r
195 )\r
196{\r
197 UINT64 ArmAttributes;\r
198\r
199 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
200 case EFI_MEMORY_UC:\r
bdecff6c
AB
201 if (ArmReadCurrentEL () == AARCH64_EL2) {\r
202 ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;\r
203 } else {\r
204 ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;\r
205 }\r
2e969d2e
OM
206 break;\r
207 case EFI_MEMORY_WC:\r
208 ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;\r
209 break;\r
210 case EFI_MEMORY_WT:\r
bdecff6c 211 ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;\r
2e969d2e
OM
212 break;\r
213 case EFI_MEMORY_WB:\r
bdecff6c 214 ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;\r
2e969d2e
OM
215 break;\r
216 default:\r
ce82984f 217 ArmAttributes = TT_ATTR_INDX_MASK;\r
2e969d2e
OM
218 }\r
219\r
220 // Set the access flag to match the block attributes\r
221 ArmAttributes |= TT_AF;\r
222\r
223 // Determine protection attributes\r
b7a09b71 224 if (EfiAttributes & EFI_MEMORY_RO) {\r
2e969d2e
OM
225 ArmAttributes |= TT_AP_RO_RO;\r
226 }\r
227\r
228 // Process eXecute Never attribute\r
229 if (EfiAttributes & EFI_MEMORY_XP) {\r
230 ArmAttributes |= TT_PXN_MASK;\r
231 }\r
232\r
233 return ArmAttributes;\r
234}\r
235\r
236// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.\r
237// And then the function will identify the size of the region that has the same page table attribute.\r
238EFI_STATUS\r
239GetMemoryRegionRec (\r
240 IN UINT64 *TranslationTable,\r
241 IN UINTN TableLevel,\r
242 IN UINT64 *LastBlockEntry,\r
243 IN OUT UINTN *BaseAddress,\r
244 OUT UINTN *RegionLength,\r
245 OUT UINTN *RegionAttributes\r
246 )\r
247{\r
248 EFI_STATUS Status;\r
249 UINT64 *NextTranslationTable;\r
250 UINT64 *BlockEntry;\r
251 UINT64 BlockEntryType;\r
252 UINT64 EntryType;\r
253\r
254 if (TableLevel != 3) {\r
255 BlockEntryType = TT_TYPE_BLOCK_ENTRY;\r
256 } else {\r
257 BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3;\r
258 }\r
259\r
260 // Find the block entry linked to the Base Address\r
261 BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress);\r
262 EntryType = *BlockEntry & TT_TYPE_MASK;\r
263\r
01273724 264 if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) {\r
2e969d2e
OM
265 NextTranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);\r
266\r
267 // The entry is a page table, so we go to the next level\r
268 Status = GetMemoryRegionRec (\r
269 NextTranslationTable, // Address of the next level page table\r
270 TableLevel + 1, // Next Page Table level\r
271 (UINTN*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable, TT_ENTRY_COUNT),\r
272 BaseAddress, RegionLength, RegionAttributes);\r
273\r
274 // In case of 'Success', it means the end of the block region has been found into the upper\r
275 // level translation table\r
276 if (!EFI_ERROR(Status)) {\r
277 return EFI_SUCCESS;\r
278 }\r
c4149528
OM
279\r
280 // Now we processed the table move to the next entry\r
281 BlockEntry++;\r
2e969d2e
OM
282 } else if (EntryType == BlockEntryType) {\r
283 // We have found the BlockEntry attached to the address. We save its start address (the start\r
ff5fef14 284 // address might be before the 'BaseAddress') and attributes\r
2e969d2e
OM
285 *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL(TableLevel) - 1);\r
286 *RegionLength = 0;\r
287 *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK;\r
288 } else {\r
289 // We have an 'Invalid' entry\r
290 return EFI_UNSUPPORTED;\r
291 }\r
292\r
293 while (BlockEntry <= LastBlockEntry) {\r
294 if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) {\r
295 *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel);\r
296 } else {\r
297 // In case we have found the end of the region we return success\r
298 return EFI_SUCCESS;\r
299 }\r
300 BlockEntry++;\r
301 }\r
302\r
303 // If we have reached the end of the TranslationTable and we have not found the end of the region then\r
304 // we return EFI_NOT_FOUND.\r
305 // The caller will continue to look for the memory region at its level\r
306 return EFI_NOT_FOUND;\r
307}\r
308\r
309EFI_STATUS\r
310GetMemoryRegion (\r
311 IN OUT UINTN *BaseAddress,\r
312 OUT UINTN *RegionLength,\r
313 OUT UINTN *RegionAttributes\r
314 )\r
315{\r
316 EFI_STATUS Status;\r
317 UINT64 *TranslationTable;\r
318 UINTN TableLevel;\r
319 UINTN EntryCount;\r
320 UINTN T0SZ;\r
321\r
322 ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL));\r
323\r
324 TranslationTable = ArmGetTTBR0BaseAddress ();\r
325\r
326 T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;\r
327 // Get the Table info from T0SZ\r
328 GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount);\r
329\r
330 Status = GetMemoryRegionRec (TranslationTable, TableLevel,\r
331 (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount),\r
332 BaseAddress, RegionLength, RegionAttributes);\r
333\r
334 // If the region continues up to the end of the root table then GetMemoryRegionRec()\r
335 // will return EFI_NOT_FOUND\r
336 if (Status == EFI_NOT_FOUND) {\r
337 return EFI_SUCCESS;\r
338 } else {\r
339 return Status;\r
340 }\r
341}\r