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