]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
ArmPkg/CpuDxe ARM: avoid unnecessary cache/TLB maintenance
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / Arm / Mmu.c
CommitLineData
aeb61534
A
1/*++\r
2\r
d6ebcab7
HT
3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>\r
4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>\r
5b53eaff 5Portions copyright (c) 2013, ARM Ltd. All rights reserved.<BR>\r
b7a09b71 6Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
aeb61534 7\r
5b53eaff
OM
8This program and the accompanying materials\r
9are licensed and made available under the terms and conditions of the BSD License\r
10which accompanies this distribution. The full text of the license may be found at\r
11http://opensource.org/licenses/bsd-license.php\r
12\r
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
aeb61534
A
15\r
16\r
17--*/\r
18\r
5b53eaff 19#include <Library/MemoryAllocationLib.h>\r
aeb61534 20#include "CpuDxe.h"\r
aeb61534
A
21\r
22// First Level Descriptors\r
23typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR;\r
24\r
aeb61534
A
25// Second Level Descriptors\r
26typedef UINT32 ARM_PAGE_TABLE_ENTRY;\r
27\r
3402aac7 28EFI_STATUS\r
aeb61534
A
29SectionToGcdAttributes (\r
30 IN UINT32 SectionAttributes,\r
31 OUT UINT64 *GcdAttributes\r
32 )\r
33{\r
34 *GcdAttributes = 0;\r
35\r
36 // determine cacheability attributes\r
1bfda055 37 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) {\r
38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED:\r
aeb61534
A
39 *GcdAttributes |= EFI_MEMORY_UC;\r
40 break;\r
1bfda055 41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE:\r
aeb61534
A
42 *GcdAttributes |= EFI_MEMORY_UC;\r
43 break;\r
1bfda055 44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:\r
aeb61534
A
45 *GcdAttributes |= EFI_MEMORY_WT;\r
46 break;\r
1bfda055 47 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC:\r
aeb61534
A
48 *GcdAttributes |= EFI_MEMORY_WB;\r
49 break;\r
1bfda055 50 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE:\r
aeb61534
A
51 *GcdAttributes |= EFI_MEMORY_WC;\r
52 break;\r
1bfda055 53 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC:\r
aeb61534
A
54 *GcdAttributes |= EFI_MEMORY_WB;\r
55 break;\r
1bfda055 56 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE:\r
aeb61534
A
57 *GcdAttributes |= EFI_MEMORY_UC;\r
58 break;\r
59 default:\r
60 return EFI_UNSUPPORTED;\r
aeb61534 61 }\r
1bfda055 62\r
aeb61534 63 // determine protection attributes\r
1bfda055 64 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) {\r
65 case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write\r
b7a09b71 66 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;\r
aeb61534
A
67 break;\r
68\r
1bfda055 69 case TT_DESCRIPTOR_SECTION_AP_RW_NO:\r
70 case TT_DESCRIPTOR_SECTION_AP_RW_RW:\r
aeb61534
A
71 // normal read/write access, do not add additional attributes\r
72 break;\r
73\r
74 // read only cases map to write-protect\r
1bfda055 75 case TT_DESCRIPTOR_SECTION_AP_RO_NO:\r
76 case TT_DESCRIPTOR_SECTION_AP_RO_RO:\r
b7a09b71 77 *GcdAttributes |= EFI_MEMORY_RO;\r
aeb61534
A
78 break;\r
79\r
80 default:\r
81 return EFI_UNSUPPORTED;\r
aeb61534
A
82 }\r
83\r
84 // now process eXectue Never attribute\r
1bfda055 85 if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {\r
aeb61534
A
86 *GcdAttributes |= EFI_MEMORY_XP;\r
87 }\r
88\r
89 return EFI_SUCCESS;\r
90}\r
91\r
2cf4b608 92EFI_STATUS\r
93PageToGcdAttributes (\r
94 IN UINT32 PageAttributes,\r
95 OUT UINT64 *GcdAttributes\r
96 )\r
97{\r
98 *GcdAttributes = 0;\r
99\r
100 // determine cacheability attributes\r
101 switch(PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) {\r
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED:\r
103 *GcdAttributes |= EFI_MEMORY_UC;\r
104 break;\r
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE:\r
106 *GcdAttributes |= EFI_MEMORY_UC;\r
107 break;\r
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:\r
109 *GcdAttributes |= EFI_MEMORY_WT;\r
110 break;\r
111 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC:\r
112 *GcdAttributes |= EFI_MEMORY_WB;\r
113 break;\r
114 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE:\r
115 *GcdAttributes |= EFI_MEMORY_WC;\r
116 break;\r
117 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC:\r
118 *GcdAttributes |= EFI_MEMORY_WB;\r
119 break;\r
120 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE:\r
121 *GcdAttributes |= EFI_MEMORY_UC;\r
122 break;\r
123 default:\r
124 return EFI_UNSUPPORTED;\r
125 }\r
126\r
127 // determine protection attributes\r
128 switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) {\r
129 case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write\r
b7a09b71 130 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;\r
2cf4b608 131 break;\r
132\r
133 case TT_DESCRIPTOR_PAGE_AP_RW_NO:\r
134 case TT_DESCRIPTOR_PAGE_AP_RW_RW:\r
135 // normal read/write access, do not add additional attributes\r
136 break;\r
137\r
138 // read only cases map to write-protect\r
139 case TT_DESCRIPTOR_PAGE_AP_RO_NO:\r
140 case TT_DESCRIPTOR_PAGE_AP_RO_RO:\r
b7a09b71 141 *GcdAttributes |= EFI_MEMORY_RO;\r
2cf4b608 142 break;\r
143\r
144 default:\r
145 return EFI_UNSUPPORTED;\r
146 }\r
147\r
148 // now process eXectue Never attribute\r
149 if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {\r
150 *GcdAttributes |= EFI_MEMORY_XP;\r
151 }\r
152\r
153 return EFI_SUCCESS;\r
154}\r
155\r
2cf4b608 156EFI_STATUS\r
157SyncCacheConfigPage (\r
158 IN UINT32 SectionIndex,\r
159 IN UINT32 FirstLevelDescriptor,\r
160 IN UINTN NumberOfDescriptors,\r
161 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
162 IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase,\r
163 IN OUT UINT64 *NextRegionLength,\r
164 IN OUT UINT32 *NextSectionAttributes\r
165 )\r
166{\r
167 EFI_STATUS Status;\r
168 UINT32 i;\r
169 volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable;\r
170 UINT32 NextPageAttributes = 0;\r
171 UINT32 PageAttributes = 0;\r
172 UINT32 BaseAddress;\r
173 UINT64 GcdAttributes;\r
174\r
175 // Get the Base Address from FirstLevelDescriptor;\r
176 BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
177\r
178 // Convert SectionAttributes into PageAttributes\r
179 NextPageAttributes =\r
180 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) |\r
181 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes);\r
182\r
183 // obtain page table base\r
184 SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
185\r
186 for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {\r
187 if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {\r
188 // extract attributes (cacheability and permissions)\r
189 PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);\r
190\r
191 if (NextPageAttributes == 0) {\r
192 // start on a new region\r
193 *NextRegionLength = 0;\r
194 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
195 NextPageAttributes = PageAttributes;\r
196 } else if (PageAttributes != NextPageAttributes) {\r
197 // Convert Section Attributes into GCD Attributes\r
198 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);\r
199 ASSERT_EFI_ERROR (Status);\r
200\r
201 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
202 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);\r
203\r
204 // start on a new region\r
205 *NextRegionLength = 0;\r
206 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
207 NextPageAttributes = PageAttributes;\r
208 }\r
209 } else if (NextPageAttributes != 0) {\r
210 // Convert Page Attributes into GCD Attributes\r
211 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);\r
212 ASSERT_EFI_ERROR (Status);\r
213\r
214 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
215 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);\r
216\r
217 *NextRegionLength = 0;\r
218 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
219 NextPageAttributes = 0;\r
220 }\r
221 *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE;\r
222 }\r
223\r
224 // Convert back PageAttributes into SectionAttributes\r
225 *NextSectionAttributes =\r
226 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) |\r
227 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes);\r
228\r
229 return EFI_SUCCESS;\r
230}\r
aeb61534
A
231\r
232EFI_STATUS\r
233SyncCacheConfig (\r
234 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
235 )\r
236{\r
f659880b
A
237 EFI_STATUS Status;\r
238 UINT32 i;\r
f659880b
A
239 EFI_PHYSICAL_ADDRESS NextRegionBase;\r
240 UINT64 NextRegionLength;\r
2cf4b608 241 UINT32 NextSectionAttributes = 0;\r
242 UINT32 SectionAttributes = 0;\r
f659880b 243 UINT64 GcdAttributes;\r
aeb61534 244 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
f659880b
A
245 UINTN NumberOfDescriptors;\r
246 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
aeb61534
A
247\r
248\r
225290eb 249 DEBUG ((EFI_D_PAGE, "SyncCacheConfig()\n"));\r
f659880b 250\r
aeb61534
A
251 // This code assumes MMU is enabled and filed with section translations\r
252 ASSERT (ArmMmuEnabled ());\r
253\r
f659880b
A
254 //\r
255 // Get the memory space map from GCD\r
256 //\r
257 MemorySpaceMap = NULL;\r
258 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
259 ASSERT_EFI_ERROR (Status);\r
260\r
aeb61534
A
261\r
262 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs\r
263 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a\r
264 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were\r
265 // a client) to update its copy of the attributes. This is bad architecture and should be replaced\r
266 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
267\r
268 // obtain page table base\r
1bfda055 269 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());\r
aeb61534 270\r
2cf4b608 271 // Get the first region\r
272 NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);\r
aeb61534
A
273\r
274 // iterate through each 1MB descriptor\r
275 NextRegionBase = NextRegionLength = 0;\r
2cf4b608 276 for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {\r
277 if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {\r
278 // extract attributes (cacheability and permissions)\r
279 SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);\r
280\r
281 if (NextSectionAttributes == 0) {\r
282 // start on a new region\r
283 NextRegionLength = 0;\r
284 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
285 NextSectionAttributes = SectionAttributes;\r
286 } else if (SectionAttributes != NextSectionAttributes) {\r
287 // Convert Section Attributes into GCD Attributes\r
288 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
f659880b 289 ASSERT_EFI_ERROR (Status);\r
aeb61534
A
290\r
291 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
f659880b
A
292 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
293\r
aeb61534
A
294 // start on a new region\r
295 NextRegionLength = 0;\r
2cf4b608 296 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
297 NextSectionAttributes = SectionAttributes;\r
aeb61534 298 }\r
2cf4b608 299 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;\r
300 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) {\r
f6be48e9
KK
301 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from\r
302 // bits that are actually part of the pagetable address. We clear it out to zero so that\r
303 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the\r
304 // section attributes into page attributes\r
305 NextSectionAttributes = 0;\r
2cf4b608 306 Status = SyncCacheConfigPage (\r
307 i,FirstLevelTable[i],\r
8ea50d2e 308 NumberOfDescriptors, MemorySpaceMap,\r
2cf4b608 309 &NextRegionBase,&NextRegionLength,&NextSectionAttributes);\r
310 ASSERT_EFI_ERROR (Status);\r
311 } else {\r
312 // We do not support yet 16MB sections\r
313 ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);\r
aeb61534 314\r
2cf4b608 315 // start on a new region\r
316 if (NextSectionAttributes != 0) {\r
317 // Convert Section Attributes into GCD Attributes\r
318 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
319 ASSERT_EFI_ERROR (Status);\r
aeb61534 320\r
2cf4b608 321 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
322 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
aeb61534 323\r
2cf4b608 324 NextRegionLength = 0;\r
325 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
326 NextSectionAttributes = 0;\r
327 }\r
328 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;\r
329 }\r
aeb61534
A
330 } // section entry loop\r
331\r
2cf4b608 332 if (NextSectionAttributes != 0) {\r
333 // Convert Section Attributes into GCD Attributes\r
334 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
335 ASSERT_EFI_ERROR (Status);\r
336\r
337 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
338 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
339 }\r
340\r
5b53eaff
OM
341 FreePool (MemorySpaceMap);\r
342\r
aeb61534
A
343 return EFI_SUCCESS;\r
344}\r
345\r
346\r
347\r
348EFI_STATUS\r
349UpdatePageEntries (\r
22c7a5b7
AB
350 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
351 IN UINT64 Length,\r
352 IN UINT64 Attributes,\r
353 IN EFI_PHYSICAL_ADDRESS VirtualMask,\r
354 OUT BOOLEAN *FlushTlbs OPTIONAL\r
aeb61534
A
355 )\r
356{\r
357 EFI_STATUS Status;\r
358 UINT32 EntryValue;\r
359 UINT32 EntryMask;\r
360 UINT32 FirstLevelIdx;\r
361 UINT32 Offset;\r
362 UINT32 NumPageEntries;\r
363 UINT32 Descriptor;\r
364 UINT32 p;\r
365 UINT32 PageTableIndex;\r
366 UINT32 PageTableEntry;\r
bb02cb80 367 UINT32 CurrentPageTableEntry;\r
368 VOID *Mva;\r
aeb61534
A
369\r
370 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
371 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
372\r
920cb926
A
373 Status = EFI_SUCCESS;\r
374\r
aeb61534
A
375 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
376 // EntryValue: values at bit positions specified by EntryMask\r
1bfda055 377 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;\r
378 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;\r
aeb61534
A
379 // Although the PI spec is unclear on this the GCD guarantees that only\r
380 // one Attribute bit is set at a time, so we can safely use a switch statement\r
381 switch (Attributes) {\r
382 case EFI_MEMORY_UC:\r
383 // modify cacheability attributes\r
1bfda055 384 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
9207c5d7 385 // map to strongly ordered\r
386 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
aeb61534
A
387 break;\r
388\r
389 case EFI_MEMORY_WC:\r
390 // modify cacheability attributes\r
1bfda055 391 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 392 // map to normal non-cachable\r
1bfda055 393 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
aeb61534
A
394 break;\r
395\r
396 case EFI_MEMORY_WT:\r
397 // modify cacheability attributes\r
1bfda055 398 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 399 // write through with no-allocate\r
1bfda055 400 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
aeb61534
A
401 break;\r
402\r
403 case EFI_MEMORY_WB:\r
404 // modify cacheability attributes\r
1bfda055 405 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 406 // write back (with allocate)\r
1bfda055 407 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
aeb61534
A
408 break;\r
409\r
410 case EFI_MEMORY_WP:\r
411 case EFI_MEMORY_XP:\r
412 case EFI_MEMORY_UCE:\r
413 // cannot be implemented UEFI definition unclear for ARM\r
414 // Cause a page fault if these ranges are accessed.\r
1bfda055 415 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;\r
225290eb 416 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
417 break;\r
418\r
419 default:\r
420 return EFI_UNSUPPORTED;\r
aeb61534
A
421 }\r
422\r
11c20f4e 423 // Obtain page table base\r
1bfda055 424 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534 425\r
11c20f4e 426 // Calculate number of 4KB page table entries to change\r
2297613a 427 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;\r
3402aac7 428\r
11c20f4e 429 // Iterate for the number of 4KB pages to change\r
aeb61534 430 Offset = 0;\r
11c20f4e 431 for(p = 0; p < NumPageEntries; p++) {\r
432 // Calculate index into first level translation table for page table value\r
3402aac7 433\r
1bfda055 434 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
435 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534 436\r
11c20f4e 437 // Read the descriptor from the first level page table\r
aeb61534
A
438 Descriptor = FirstLevelTable[FirstLevelIdx];\r
439\r
11c20f4e 440 // Does this descriptor need to be converted from section entry to 4K pages?\r
1bfda055 441 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {\r
442 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
aeb61534 443 if (EFI_ERROR(Status)) {\r
11c20f4e 444 // Exit for loop\r
3402aac7
RC
445 break;\r
446 }\r
447\r
11c20f4e 448 // Re-read descriptor\r
aeb61534 449 Descriptor = FirstLevelTable[FirstLevelIdx];\r
22c7a5b7
AB
450 if (FlushTlbs != NULL) {\r
451 *FlushTlbs = TRUE;\r
452 }\r
aeb61534
A
453 }\r
454\r
11c20f4e 455 // Obtain page table base address\r
1bfda055 456 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);\r
aeb61534 457\r
11c20f4e 458 // Calculate index into the page table\r
1bfda055 459 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
460 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
aeb61534 461\r
11c20f4e 462 // Get the entry\r
bb02cb80 463 CurrentPageTableEntry = PageTable[PageTableIndex];\r
aeb61534 464\r
11c20f4e 465 // Mask off appropriate fields\r
bb02cb80 466 PageTableEntry = CurrentPageTableEntry & ~EntryMask;\r
aeb61534 467\r
11c20f4e 468 // Mask in new attributes and/or permissions\r
aeb61534
A
469 PageTableEntry |= EntryValue;\r
470\r
471 if (VirtualMask != 0) {\r
472 // Make this virtual address point at a physical page\r
473 PageTableEntry &= ~VirtualMask;\r
474 }\r
3402aac7 475\r
bb02cb80 476 if (CurrentPageTableEntry != PageTableEntry) {\r
1bfda055 477 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));\r
bb02cb80 478\r
3402aac7
RC
479 // Only need to update if we are changing the entry\r
480 PageTable[PageTableIndex] = PageTableEntry;\r
bb02cb80 481 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);\r
22c7a5b7
AB
482\r
483 // Clean/invalidate the cache for this page, but only\r
484 // if we are modifying the memory type attributes\r
485 if (((CurrentPageTableEntry ^ PageTableEntry) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) != 0) {\r
486 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);\r
487 }\r
bb02cb80 488 }\r
aeb61534
A
489\r
490 Status = EFI_SUCCESS;\r
2297613a 491 Offset += TT_DESCRIPTOR_PAGE_SIZE;\r
3402aac7 492\r
11c20f4e 493 } // End first level translation table loop\r
aeb61534
A
494\r
495 return Status;\r
496}\r
497\r
498\r
499\r
500EFI_STATUS\r
501UpdateSectionEntries (\r
502 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
503 IN UINT64 Length,\r
504 IN UINT64 Attributes,\r
505 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
506 )\r
507{\r
508 EFI_STATUS Status = EFI_SUCCESS;\r
509 UINT32 EntryMask;\r
510 UINT32 EntryValue;\r
511 UINT32 FirstLevelIdx;\r
512 UINT32 NumSections;\r
513 UINT32 i;\r
bb02cb80 514 UINT32 CurrentDescriptor;\r
aeb61534 515 UINT32 Descriptor;\r
bb02cb80 516 VOID *Mva;\r
aeb61534
A
517 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
518\r
519 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
520 // EntryValue: values at bit positions specified by EntryMask\r
521\r
3402aac7 522 // Make sure we handle a section range that is unmapped\r
1bfda055 523 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;\r
524 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;\r
f659880b 525\r
aeb61534
A
526 // Although the PI spec is unclear on this the GCD guarantees that only\r
527 // one Attribute bit is set at a time, so we can safely use a switch statement\r
528 switch(Attributes) {\r
529 case EFI_MEMORY_UC:\r
530 // modify cacheability attributes\r
1bfda055 531 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
9207c5d7 532 // map to strongly ordered\r
533 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
aeb61534
A
534 break;\r
535\r
536 case EFI_MEMORY_WC:\r
537 // modify cacheability attributes\r
1bfda055 538 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 539 // map to normal non-cachable\r
1bfda055 540 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
aeb61534
A
541 break;\r
542\r
543 case EFI_MEMORY_WT:\r
544 // modify cacheability attributes\r
1bfda055 545 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 546 // write through with no-allocate\r
1bfda055 547 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
aeb61534
A
548 break;\r
549\r
550 case EFI_MEMORY_WB:\r
551 // modify cacheability attributes\r
1bfda055 552 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 553 // write back (with allocate)\r
1bfda055 554 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
aeb61534
A
555 break;\r
556\r
557 case EFI_MEMORY_WP:\r
558 case EFI_MEMORY_XP:\r
559 case EFI_MEMORY_RP:\r
560 case EFI_MEMORY_UCE:\r
561 // cannot be implemented UEFI definition unclear for ARM\r
562 // Cause a page fault if these ranges are accessed.\r
1bfda055 563 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;\r
225290eb 564 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
565 break;\r
566\r
567\r
568 default:\r
569 return EFI_UNSUPPORTED;\r
aeb61534
A
570 }\r
571\r
572 // obtain page table base\r
1bfda055 573 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534
A
574\r
575 // calculate index into first level translation table for start of modification\r
1bfda055 576 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
577 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534
A
578\r
579 // calculate number of 1MB first level entries this applies to\r
1bfda055 580 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;\r
3402aac7 581\r
aeb61534
A
582 // iterate through each descriptor\r
583 for(i=0; i<NumSections; i++) {\r
bb02cb80 584 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];\r
aeb61534
A
585\r
586 // has this descriptor already been coverted to pages?\r
1bfda055 587 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {\r
aeb61534 588 // forward this 1MB range to page table function instead\r
22c7a5b7
AB
589 Status = UpdatePageEntries (\r
590 (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,\r
591 TT_DESCRIPTOR_SECTION_SIZE,\r
592 Attributes,\r
593 VirtualMask,\r
594 NULL);\r
aeb61534
A
595 } else {\r
596 // still a section entry\r
3402aac7 597\r
aeb61534 598 // mask off appropriate fields\r
bb02cb80 599 Descriptor = CurrentDescriptor & ~EntryMask;\r
aeb61534
A
600\r
601 // mask in new attributes and/or permissions\r
602 Descriptor |= EntryValue;\r
603 if (VirtualMask != 0) {\r
604 Descriptor &= ~VirtualMask;\r
605 }\r
606\r
bb02cb80 607 if (CurrentDescriptor != Descriptor) {\r
1bfda055 608 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
bb02cb80 609\r
3402aac7 610 // Only need to update if we are changing the descriptor\r
bb02cb80 611 FirstLevelTable[FirstLevelIdx + i] = Descriptor;\r
612 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);\r
22c7a5b7
AB
613\r
614 // Clean/invalidate the cache for this section, but only\r
615 // if we are modifying the memory type attributes\r
616 if (((CurrentDescriptor ^ Descriptor) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) != 0) {\r
617 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);\r
618 }\r
bb02cb80 619 }\r
aeb61534
A
620\r
621 Status = EFI_SUCCESS;\r
622 }\r
623 }\r
624\r
625 return Status;\r
626}\r
627\r
3402aac7 628EFI_STATUS\r
aeb61534
A
629ConvertSectionToPages (\r
630 IN EFI_PHYSICAL_ADDRESS BaseAddress\r
631 )\r
632{\r
633 EFI_STATUS Status;\r
634 EFI_PHYSICAL_ADDRESS PageTableAddr;\r
635 UINT32 FirstLevelIdx;\r
636 UINT32 SectionDescriptor;\r
637 UINT32 PageTableDescriptor;\r
638 UINT32 PageDescriptor;\r
2cf4b608 639 UINT32 Index;\r
aeb61534
A
640\r
641 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
642 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
643\r
225290eb 644 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));\r
aeb61534 645\r
b34e4db3 646 // Obtain page table base\r
1bfda055 647 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534 648\r
b34e4db3 649 // Calculate index into first level translation table for start of modification\r
1bfda055 650 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
651 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534 652\r
b34e4db3 653 // Get section attributes and convert to page attributes\r
aeb61534 654 SectionDescriptor = FirstLevelTable[FirstLevelIdx];\r
6adbd5b4 655 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);\r
aeb61534 656\r
b34e4db3 657 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)\r
aeb61534
A
658 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);\r
659 if (EFI_ERROR(Status)) {\r
660 return Status;\r
661 }\r
662\r
663 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;\r
664\r
b34e4db3 665 // Write the page table entries out\r
2cf4b608 666 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {\r
667 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;\r
aeb61534
A
668 }\r
669\r
b34e4db3 670 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
2297613a 671 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);\r
aeb61534 672\r
b34e4db3 673 // Formulate page table entry, Domain=0, NS=0\r
1bfda055 674 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;\r
aeb61534 675\r
b34e4db3 676 // Write the page table entry out, replacing section entry\r
aeb61534
A
677 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;\r
678\r
679 return EFI_SUCCESS;\r
680}\r
681\r
682\r
683\r
684EFI_STATUS\r
685SetMemoryAttributes (\r
686 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
687 IN UINT64 Length,\r
688 IN UINT64 Attributes,\r
689 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
690 )\r
691{\r
692 EFI_STATUS Status;\r
df809efe 693 UINT64 ChunkLength;\r
22c7a5b7 694 BOOLEAN FlushTlbs;\r
df809efe
AB
695\r
696 if (Length == 0) {\r
697 return EFI_SUCCESS;\r
698 }\r
3402aac7 699\r
0a9d732c
AB
700 //\r
701 // Ignore invocations that only modify permission bits\r
702 //\r
703 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {\r
704 return EFI_SUCCESS;\r
705 }\r
706\r
22c7a5b7 707 FlushTlbs = FALSE;\r
df809efe
AB
708 while (Length > 0) {\r
709 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&\r
710 Length >= TT_DESCRIPTOR_SECTION_SIZE) {\r
711\r
712 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;\r
713\r
714 DEBUG ((DEBUG_PAGE,\r
715 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",\r
716 BaseAddress, ChunkLength, Attributes));\r
717\r
718 Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes,\r
719 VirtualMask);\r
22c7a5b7
AB
720\r
721 FlushTlbs = TRUE;\r
df809efe
AB
722 } else {\r
723\r
724 //\r
725 // Process page by page until the next section boundary, but only if\r
726 // we have more than a section's worth of area to deal with after that.\r
727 //\r
728 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -\r
729 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);\r
730 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {\r
731 ChunkLength = Length;\r
732 }\r
733\r
734 DEBUG ((DEBUG_PAGE,\r
735 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",\r
736 BaseAddress, ChunkLength, Attributes));\r
737\r
738 Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,\r
22c7a5b7 739 VirtualMask, &FlushTlbs);\r
df809efe
AB
740 }\r
741\r
742 if (EFI_ERROR (Status)) {\r
743 break;\r
744 }\r
745\r
746 BaseAddress += ChunkLength;\r
747 Length -= ChunkLength;\r
aeb61534
A
748 }\r
749\r
22c7a5b7
AB
750 if (FlushTlbs) {\r
751 ArmInvalidateTlb ();\r
752 }\r
aeb61534
A
753 return Status;\r
754}\r
2e969d2e
OM
755\r
756UINT64\r
757EfiAttributeToArmAttribute (\r
758 IN UINT64 EfiAttributes\r
759 )\r
760{\r
761 UINT64 ArmAttributes;\r
762\r
763 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {\r
764 case EFI_MEMORY_UC:\r
765 // Map to strongly ordered\r
766 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
767 break;\r
768\r
769 case EFI_MEMORY_WC:\r
770 // Map to normal non-cachable\r
771 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
772 break;\r
773\r
774 case EFI_MEMORY_WT:\r
775 // Write through with no-allocate\r
776 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
777 break;\r
778\r
779 case EFI_MEMORY_WB:\r
780 // Write back (with allocate)\r
781 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
782 break;\r
783\r
2e969d2e
OM
784 case EFI_MEMORY_UCE:\r
785 default:\r
2e969d2e 786 ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;\r
2e969d2e
OM
787 break;\r
788 }\r
789\r
790 // Determine protection attributes\r
b7a09b71 791 if (EfiAttributes & EFI_MEMORY_RO) {\r
2e969d2e
OM
792 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;\r
793 } else {\r
794 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;\r
795 }\r
796\r
797 // Determine eXecute Never attribute\r
798 if (EfiAttributes & EFI_MEMORY_XP) {\r
799 ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;\r
800 }\r
801\r
802 return ArmAttributes;\r
803}\r
804\r
805EFI_STATUS\r
806GetMemoryRegionPage (\r
807 IN UINT32 *PageTable,\r
808 IN OUT UINTN *BaseAddress,\r
809 OUT UINTN *RegionLength,\r
810 OUT UINTN *RegionAttributes\r
811 )\r
812{\r
813 UINT32 PageAttributes;\r
814 UINT32 TableIndex;\r
815 UINT32 PageDescriptor;\r
816\r
817 // Convert the section attributes into page attributes\r
818 PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);\r
819\r
820 // Calculate index into first level translation table for start of modification\r
b75d7605 821 TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
2e969d2e
OM
822 ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
823\r
824 // Go through the page table to find the end of the section\r
825 for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {\r
826 // Get the section at the given index\r
827 PageDescriptor = PageTable[TableIndex];\r
828\r
829 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {\r
830 // Case: End of the boundary of the region\r
831 return EFI_SUCCESS;\r
832 } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {\r
833 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {\r
834 *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;\r
835 } else {\r
836 // Case: End of the boundary of the region\r
837 return EFI_SUCCESS;\r
838 }\r
839 } else {\r
840 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.\r
841 ASSERT(0);\r
842 return EFI_SUCCESS;\r
843 }\r
844 }\r
845\r
846 return EFI_NOT_FOUND;\r
847}\r
848\r
849EFI_STATUS\r
850GetMemoryRegion (\r
851 IN OUT UINTN *BaseAddress,\r
852 OUT UINTN *RegionLength,\r
853 OUT UINTN *RegionAttributes\r
854 )\r
855{\r
856 EFI_STATUS Status;\r
857 UINT32 TableIndex;\r
858 UINT32 PageAttributes;\r
859 UINT32 PageTableIndex;\r
860 UINT32 SectionDescriptor;\r
861 ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
862 UINT32 *PageTable;\r
863\r
864 // Initialize the arguments\r
865 *RegionLength = 0;\r
866\r
867 // Obtain page table base\r
868 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
869\r
870 // Calculate index into first level translation table for start of modification\r
871 TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
872 ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);\r
873\r
874 // Get the section at the given index\r
875 SectionDescriptor = FirstLevelTable[TableIndex];\r
876\r
877 // If 'BaseAddress' belongs to the section then round it to the section boundary\r
878 if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
879 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))\r
880 {\r
881 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;\r
882 *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;\r
883 } else {\r
884 // Otherwise, we round it to the page boundary\r
885 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;\r
886\r
887 // Get the attribute at the page table level (Level 2)\r
888 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
889\r
890 // Calculate index into first level translation table for start of modification\r
b75d7605 891 PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
2e969d2e
OM
892 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
893\r
894 PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;\r
895 *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |\r
896 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);\r
897 }\r
898\r
899 for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {\r
900 // Get the section at the given index\r
901 SectionDescriptor = FirstLevelTable[TableIndex];\r
902\r
903 // If the entry is a level-2 page table then we scan it to find the end of the region\r
5ad9b48f 904 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) {\r
2e969d2e
OM
905 // Extract the page table location from the descriptor\r
906 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
907\r
908 // Scan the page table to find the end of the region.\r
909 Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);\r
910\r
911 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop\r
912 if (Status == EFI_SUCCESS) {\r
913 break;\r
914 }\r
915 } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||\r
916 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {\r
917 if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {\r
918 // If the attributes of the section differ from the one targeted then we exit the loop\r
919 break;\r
920 } else {\r
921 *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;\r
922 }\r
923 } else {\r
924 // If we are on an invalid section then it means it is the end of our section.\r
925 break;\r
926 }\r
927 }\r
928\r
929 return EFI_SUCCESS;\r
930}\r