]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/CpuDxe/ArmV6/Mmu.c
ArmPkg/ArmLib/AArch64: Use the appropriate macros and update comments
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / ArmV6 / 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
aeb61534 5\r
d6ebcab7 6This program and the accompanying materials \r
aeb61534
A
7are licensed and made available under the terms and conditions of the BSD License \r
8which accompanies this distribution. The full text of the license may be found at \r
9http://opensource.org/licenses/bsd-license.php \r
10 \r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
13\r
14\r
15--*/\r
16\r
17#include "CpuDxe.h"\r
aeb61534
A
18\r
19// First Level Descriptors\r
20typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR;\r
21\r
aeb61534
A
22// Second Level Descriptors\r
23typedef UINT32 ARM_PAGE_TABLE_ENTRY;\r
24\r
aeb61534
A
25EFI_STATUS \r
26SectionToGcdAttributes (\r
27 IN UINT32 SectionAttributes,\r
28 OUT UINT64 *GcdAttributes\r
29 )\r
30{\r
31 *GcdAttributes = 0;\r
32\r
33 // determine cacheability attributes\r
1bfda055 34 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) {\r
35 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED:\r
aeb61534
A
36 *GcdAttributes |= EFI_MEMORY_UC;\r
37 break;\r
1bfda055 38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE:\r
aeb61534
A
39 *GcdAttributes |= EFI_MEMORY_UC;\r
40 break;\r
1bfda055 41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:\r
aeb61534
A
42 *GcdAttributes |= EFI_MEMORY_WT;\r
43 break;\r
1bfda055 44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC:\r
aeb61534
A
45 *GcdAttributes |= EFI_MEMORY_WB;\r
46 break;\r
1bfda055 47 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE:\r
aeb61534
A
48 *GcdAttributes |= EFI_MEMORY_WC;\r
49 break;\r
1bfda055 50 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC:\r
aeb61534
A
51 *GcdAttributes |= EFI_MEMORY_WB;\r
52 break;\r
1bfda055 53 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE:\r
aeb61534
A
54 *GcdAttributes |= EFI_MEMORY_UC;\r
55 break;\r
56 default:\r
57 return EFI_UNSUPPORTED;\r
aeb61534 58 }\r
1bfda055 59\r
aeb61534 60 // determine protection attributes\r
1bfda055 61 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) {\r
62 case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write\r
f659880b 63 //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;\r
aeb61534
A
64 break;\r
65\r
1bfda055 66 case TT_DESCRIPTOR_SECTION_AP_RW_NO:\r
67 case TT_DESCRIPTOR_SECTION_AP_RW_RW:\r
aeb61534
A
68 // normal read/write access, do not add additional attributes\r
69 break;\r
70\r
71 // read only cases map to write-protect\r
1bfda055 72 case TT_DESCRIPTOR_SECTION_AP_RO_NO:\r
73 case TT_DESCRIPTOR_SECTION_AP_RO_RO:\r
aeb61534
A
74 *GcdAttributes |= EFI_MEMORY_WP;\r
75 break;\r
76\r
77 default:\r
78 return EFI_UNSUPPORTED;\r
aeb61534
A
79 }\r
80\r
81 // now process eXectue Never attribute\r
1bfda055 82 if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {\r
aeb61534
A
83 *GcdAttributes |= EFI_MEMORY_XP;\r
84 }\r
85\r
86 return EFI_SUCCESS;\r
87}\r
88\r
2cf4b608 89EFI_STATUS\r
90PageToGcdAttributes (\r
91 IN UINT32 PageAttributes,\r
92 OUT UINT64 *GcdAttributes\r
93 )\r
94{\r
95 *GcdAttributes = 0;\r
96\r
97 // determine cacheability attributes\r
98 switch(PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) {\r
99 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED:\r
100 *GcdAttributes |= EFI_MEMORY_UC;\r
101 break;\r
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE:\r
103 *GcdAttributes |= EFI_MEMORY_UC;\r
104 break;\r
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:\r
106 *GcdAttributes |= EFI_MEMORY_WT;\r
107 break;\r
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC:\r
109 *GcdAttributes |= EFI_MEMORY_WB;\r
110 break;\r
111 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE:\r
112 *GcdAttributes |= EFI_MEMORY_WC;\r
113 break;\r
114 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC:\r
115 *GcdAttributes |= EFI_MEMORY_WB;\r
116 break;\r
117 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE:\r
118 *GcdAttributes |= EFI_MEMORY_UC;\r
119 break;\r
120 default:\r
121 return EFI_UNSUPPORTED;\r
122 }\r
123\r
124 // determine protection attributes\r
125 switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) {\r
126 case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write\r
127 //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;\r
128 break;\r
129\r
130 case TT_DESCRIPTOR_PAGE_AP_RW_NO:\r
131 case TT_DESCRIPTOR_PAGE_AP_RW_RW:\r
132 // normal read/write access, do not add additional attributes\r
133 break;\r
134\r
135 // read only cases map to write-protect\r
136 case TT_DESCRIPTOR_PAGE_AP_RO_NO:\r
137 case TT_DESCRIPTOR_PAGE_AP_RO_RO:\r
138 *GcdAttributes |= EFI_MEMORY_WP;\r
139 break;\r
140\r
141 default:\r
142 return EFI_UNSUPPORTED;\r
143 }\r
144\r
145 // now process eXectue Never attribute\r
146 if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {\r
147 *GcdAttributes |= EFI_MEMORY_XP;\r
148 }\r
149\r
150 return EFI_SUCCESS;\r
151}\r
152\r
2cf4b608 153EFI_STATUS\r
154SyncCacheConfigPage (\r
155 IN UINT32 SectionIndex,\r
156 IN UINT32 FirstLevelDescriptor,\r
157 IN UINTN NumberOfDescriptors,\r
158 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
159 IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase,\r
160 IN OUT UINT64 *NextRegionLength,\r
161 IN OUT UINT32 *NextSectionAttributes\r
162 )\r
163{\r
164 EFI_STATUS Status;\r
165 UINT32 i;\r
166 volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable;\r
167 UINT32 NextPageAttributes = 0;\r
168 UINT32 PageAttributes = 0;\r
169 UINT32 BaseAddress;\r
170 UINT64 GcdAttributes;\r
171\r
172 // Get the Base Address from FirstLevelDescriptor;\r
173 BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
174\r
175 // Convert SectionAttributes into PageAttributes\r
176 NextPageAttributes =\r
177 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) |\r
178 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes);\r
179\r
180 // obtain page table base\r
181 SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);\r
182\r
183 for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {\r
184 if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {\r
185 // extract attributes (cacheability and permissions)\r
186 PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);\r
187\r
188 if (NextPageAttributes == 0) {\r
189 // start on a new region\r
190 *NextRegionLength = 0;\r
191 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
192 NextPageAttributes = PageAttributes;\r
193 } else if (PageAttributes != NextPageAttributes) {\r
194 // Convert Section Attributes into GCD Attributes\r
195 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);\r
196 ASSERT_EFI_ERROR (Status);\r
197\r
198 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
199 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);\r
200\r
201 // start on a new region\r
202 *NextRegionLength = 0;\r
203 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
204 NextPageAttributes = PageAttributes;\r
205 }\r
206 } else if (NextPageAttributes != 0) {\r
207 // Convert Page Attributes into GCD Attributes\r
208 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);\r
209 ASSERT_EFI_ERROR (Status);\r
210\r
211 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
212 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);\r
213\r
214 *NextRegionLength = 0;\r
215 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);\r
216 NextPageAttributes = 0;\r
217 }\r
218 *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE;\r
219 }\r
220\r
221 // Convert back PageAttributes into SectionAttributes\r
222 *NextSectionAttributes =\r
223 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) |\r
224 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes);\r
225\r
226 return EFI_SUCCESS;\r
227}\r
aeb61534
A
228\r
229EFI_STATUS\r
230SyncCacheConfig (\r
231 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
232 )\r
233{\r
f659880b
A
234 EFI_STATUS Status;\r
235 UINT32 i;\r
f659880b
A
236 EFI_PHYSICAL_ADDRESS NextRegionBase;\r
237 UINT64 NextRegionLength;\r
2cf4b608 238 UINT32 NextSectionAttributes = 0;\r
239 UINT32 SectionAttributes = 0;\r
f659880b 240 UINT64 GcdAttributes;\r
aeb61534 241 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
f659880b
A
242 UINTN NumberOfDescriptors;\r
243 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
aeb61534
A
244\r
245\r
225290eb 246 DEBUG ((EFI_D_PAGE, "SyncCacheConfig()\n"));\r
f659880b 247\r
aeb61534
A
248 // This code assumes MMU is enabled and filed with section translations\r
249 ASSERT (ArmMmuEnabled ());\r
250\r
f659880b
A
251 //\r
252 // Get the memory space map from GCD\r
253 //\r
254 MemorySpaceMap = NULL;\r
255 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
256 ASSERT_EFI_ERROR (Status);\r
257\r
aeb61534
A
258\r
259 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs\r
260 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a\r
261 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were\r
262 // a client) to update its copy of the attributes. This is bad architecture and should be replaced\r
263 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
264\r
265 // obtain page table base\r
1bfda055 266 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());\r
aeb61534 267\r
2cf4b608 268 // Get the first region\r
269 NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);\r
aeb61534
A
270\r
271 // iterate through each 1MB descriptor\r
272 NextRegionBase = NextRegionLength = 0;\r
2cf4b608 273 for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {\r
274 if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {\r
275 // extract attributes (cacheability and permissions)\r
276 SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);\r
277\r
278 if (NextSectionAttributes == 0) {\r
279 // start on a new region\r
280 NextRegionLength = 0;\r
281 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
282 NextSectionAttributes = SectionAttributes;\r
283 } else if (SectionAttributes != NextSectionAttributes) {\r
284 // Convert Section Attributes into GCD Attributes\r
285 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
f659880b 286 ASSERT_EFI_ERROR (Status);\r
aeb61534
A
287\r
288 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
f659880b
A
289 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
290\r
aeb61534
A
291 // start on a new region\r
292 NextRegionLength = 0;\r
2cf4b608 293 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
294 NextSectionAttributes = SectionAttributes;\r
aeb61534 295 }\r
2cf4b608 296 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;\r
297 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) {\r
298 Status = SyncCacheConfigPage (\r
299 i,FirstLevelTable[i],\r
8ea50d2e 300 NumberOfDescriptors, MemorySpaceMap,\r
2cf4b608 301 &NextRegionBase,&NextRegionLength,&NextSectionAttributes);\r
302 ASSERT_EFI_ERROR (Status);\r
303 } else {\r
304 // We do not support yet 16MB sections\r
305 ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);\r
aeb61534 306\r
2cf4b608 307 // start on a new region\r
308 if (NextSectionAttributes != 0) {\r
309 // Convert Section Attributes into GCD Attributes\r
310 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
311 ASSERT_EFI_ERROR (Status);\r
aeb61534 312\r
2cf4b608 313 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
314 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
aeb61534 315\r
2cf4b608 316 NextRegionLength = 0;\r
317 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
318 NextSectionAttributes = 0;\r
319 }\r
320 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;\r
321 }\r
aeb61534
A
322 } // section entry loop\r
323\r
2cf4b608 324 if (NextSectionAttributes != 0) {\r
325 // Convert Section Attributes into GCD Attributes\r
326 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);\r
327 ASSERT_EFI_ERROR (Status);\r
328\r
329 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
330 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
331 }\r
332\r
aeb61534
A
333 return EFI_SUCCESS;\r
334}\r
335\r
336\r
337\r
338EFI_STATUS\r
339UpdatePageEntries (\r
340 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
341 IN UINT64 Length,\r
342 IN UINT64 Attributes,\r
343 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
344 )\r
345{\r
346 EFI_STATUS Status;\r
347 UINT32 EntryValue;\r
348 UINT32 EntryMask;\r
349 UINT32 FirstLevelIdx;\r
350 UINT32 Offset;\r
351 UINT32 NumPageEntries;\r
352 UINT32 Descriptor;\r
353 UINT32 p;\r
354 UINT32 PageTableIndex;\r
355 UINT32 PageTableEntry;\r
bb02cb80 356 UINT32 CurrentPageTableEntry;\r
357 VOID *Mva;\r
aeb61534
A
358\r
359 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
360 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
361\r
920cb926
A
362 Status = EFI_SUCCESS;\r
363\r
aeb61534
A
364 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
365 // EntryValue: values at bit positions specified by EntryMask\r
1bfda055 366 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;\r
367 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;\r
aeb61534
A
368 // Although the PI spec is unclear on this the GCD guarantees that only\r
369 // one Attribute bit is set at a time, so we can safely use a switch statement\r
370 switch (Attributes) {\r
371 case EFI_MEMORY_UC:\r
372 // modify cacheability attributes\r
1bfda055 373 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
9207c5d7 374 // map to strongly ordered\r
375 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
aeb61534
A
376 break;\r
377\r
378 case EFI_MEMORY_WC:\r
379 // modify cacheability attributes\r
1bfda055 380 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 381 // map to normal non-cachable\r
1bfda055 382 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
aeb61534
A
383 break;\r
384\r
385 case EFI_MEMORY_WT:\r
386 // modify cacheability attributes\r
1bfda055 387 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 388 // write through with no-allocate\r
1bfda055 389 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
aeb61534
A
390 break;\r
391\r
392 case EFI_MEMORY_WB:\r
393 // modify cacheability attributes\r
1bfda055 394 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
aeb61534 395 // write back (with allocate)\r
1bfda055 396 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
aeb61534
A
397 break;\r
398\r
399 case EFI_MEMORY_WP:\r
400 case EFI_MEMORY_XP:\r
401 case EFI_MEMORY_UCE:\r
402 // cannot be implemented UEFI definition unclear for ARM\r
403 // Cause a page fault if these ranges are accessed.\r
1bfda055 404 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;\r
225290eb 405 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
406 break;\r
407\r
408 default:\r
409 return EFI_UNSUPPORTED;\r
aeb61534
A
410 }\r
411\r
11c20f4e 412 // Obtain page table base\r
1bfda055 413 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534 414\r
11c20f4e 415 // Calculate number of 4KB page table entries to change\r
2297613a 416 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;\r
aeb61534 417 \r
11c20f4e 418 // Iterate for the number of 4KB pages to change\r
aeb61534 419 Offset = 0;\r
11c20f4e 420 for(p = 0; p < NumPageEntries; p++) {\r
421 // Calculate index into first level translation table for page table value\r
aeb61534 422 \r
1bfda055 423 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
424 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534 425\r
11c20f4e 426 // Read the descriptor from the first level page table\r
aeb61534
A
427 Descriptor = FirstLevelTable[FirstLevelIdx];\r
428\r
11c20f4e 429 // Does this descriptor need to be converted from section entry to 4K pages?\r
1bfda055 430 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {\r
431 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
aeb61534 432 if (EFI_ERROR(Status)) {\r
11c20f4e 433 // Exit for loop\r
aeb61534
A
434 break; \r
435 } \r
436 \r
11c20f4e 437 // Re-read descriptor\r
aeb61534
A
438 Descriptor = FirstLevelTable[FirstLevelIdx];\r
439 }\r
440\r
11c20f4e 441 // Obtain page table base address\r
1bfda055 442 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);\r
aeb61534 443\r
11c20f4e 444 // Calculate index into the page table\r
1bfda055 445 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
446 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
aeb61534 447\r
11c20f4e 448 // Get the entry\r
bb02cb80 449 CurrentPageTableEntry = PageTable[PageTableIndex];\r
aeb61534 450\r
11c20f4e 451 // Mask off appropriate fields\r
bb02cb80 452 PageTableEntry = CurrentPageTableEntry & ~EntryMask;\r
aeb61534 453\r
11c20f4e 454 // Mask in new attributes and/or permissions\r
aeb61534
A
455 PageTableEntry |= EntryValue;\r
456\r
457 if (VirtualMask != 0) {\r
458 // Make this virtual address point at a physical page\r
459 PageTableEntry &= ~VirtualMask;\r
460 }\r
aeb61534 461 \r
bb02cb80 462 if (CurrentPageTableEntry != PageTableEntry) {\r
1bfda055 463 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));\r
464 if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {\r
bb02cb80 465 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page\r
466 // Note assumes switch(Attributes), not ARMv7 possibilities\r
2297613a 467 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);\r
bb02cb80 468 }\r
469\r
470 // Only need to update if we are changing the entry \r
471 PageTable[PageTableIndex] = PageTableEntry; \r
472 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);\r
473 }\r
aeb61534
A
474\r
475 Status = EFI_SUCCESS;\r
2297613a 476 Offset += TT_DESCRIPTOR_PAGE_SIZE;\r
aeb61534 477 \r
11c20f4e 478 } // End first level translation table loop\r
aeb61534
A
479\r
480 return Status;\r
481}\r
482\r
483\r
484\r
485EFI_STATUS\r
486UpdateSectionEntries (\r
487 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
488 IN UINT64 Length,\r
489 IN UINT64 Attributes,\r
490 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
491 )\r
492{\r
493 EFI_STATUS Status = EFI_SUCCESS;\r
494 UINT32 EntryMask;\r
495 UINT32 EntryValue;\r
496 UINT32 FirstLevelIdx;\r
497 UINT32 NumSections;\r
498 UINT32 i;\r
bb02cb80 499 UINT32 CurrentDescriptor;\r
aeb61534 500 UINT32 Descriptor;\r
bb02cb80 501 VOID *Mva;\r
aeb61534
A
502 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
503\r
504 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
505 // EntryValue: values at bit positions specified by EntryMask\r
506\r
f659880b 507 // Make sure we handle a section range that is unmapped \r
1bfda055 508 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;\r
509 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;\r
f659880b 510\r
aeb61534
A
511 // Although the PI spec is unclear on this the GCD guarantees that only\r
512 // one Attribute bit is set at a time, so we can safely use a switch statement\r
513 switch(Attributes) {\r
514 case EFI_MEMORY_UC:\r
515 // modify cacheability attributes\r
1bfda055 516 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
9207c5d7 517 // map to strongly ordered\r
518 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
aeb61534
A
519 break;\r
520\r
521 case EFI_MEMORY_WC:\r
522 // modify cacheability attributes\r
1bfda055 523 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 524 // map to normal non-cachable\r
1bfda055 525 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
aeb61534
A
526 break;\r
527\r
528 case EFI_MEMORY_WT:\r
529 // modify cacheability attributes\r
1bfda055 530 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 531 // write through with no-allocate\r
1bfda055 532 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
aeb61534
A
533 break;\r
534\r
535 case EFI_MEMORY_WB:\r
536 // modify cacheability attributes\r
1bfda055 537 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
aeb61534 538 // write back (with allocate)\r
1bfda055 539 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
aeb61534
A
540 break;\r
541\r
542 case EFI_MEMORY_WP:\r
543 case EFI_MEMORY_XP:\r
544 case EFI_MEMORY_RP:\r
545 case EFI_MEMORY_UCE:\r
546 // cannot be implemented UEFI definition unclear for ARM\r
547 // Cause a page fault if these ranges are accessed.\r
1bfda055 548 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;\r
225290eb 549 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
550 break;\r
551\r
552\r
553 default:\r
554 return EFI_UNSUPPORTED;\r
aeb61534
A
555 }\r
556\r
557 // obtain page table base\r
1bfda055 558 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534
A
559\r
560 // calculate index into first level translation table for start of modification\r
1bfda055 561 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
562 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534
A
563\r
564 // calculate number of 1MB first level entries this applies to\r
1bfda055 565 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;\r
aeb61534
A
566 \r
567 // iterate through each descriptor\r
568 for(i=0; i<NumSections; i++) {\r
bb02cb80 569 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];\r
aeb61534
A
570\r
571 // has this descriptor already been coverted to pages?\r
1bfda055 572 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {\r
aeb61534 573 // forward this 1MB range to page table function instead\r
1bfda055 574 Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);\r
aeb61534
A
575 } else {\r
576 // still a section entry\r
577 \r
578 // mask off appropriate fields\r
bb02cb80 579 Descriptor = CurrentDescriptor & ~EntryMask;\r
aeb61534
A
580\r
581 // mask in new attributes and/or permissions\r
582 Descriptor |= EntryValue;\r
583 if (VirtualMask != 0) {\r
584 Descriptor &= ~VirtualMask;\r
585 }\r
586\r
bb02cb80 587 if (CurrentDescriptor != Descriptor) {\r
1bfda055 588 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
589 if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {\r
bb02cb80 590 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section\r
591 // Note assumes switch(Attributes), not ARMv7 possabilities\r
592 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);\r
593 }\r
594\r
595 // Only need to update if we are changing the descriptor \r
596 FirstLevelTable[FirstLevelIdx + i] = Descriptor;\r
597 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);\r
598 }\r
aeb61534
A
599\r
600 Status = EFI_SUCCESS;\r
601 }\r
602 }\r
603\r
604 return Status;\r
605}\r
606\r
607EFI_STATUS \r
608ConvertSectionToPages (\r
609 IN EFI_PHYSICAL_ADDRESS BaseAddress\r
610 )\r
611{\r
612 EFI_STATUS Status;\r
613 EFI_PHYSICAL_ADDRESS PageTableAddr;\r
614 UINT32 FirstLevelIdx;\r
615 UINT32 SectionDescriptor;\r
616 UINT32 PageTableDescriptor;\r
617 UINT32 PageDescriptor;\r
2cf4b608 618 UINT32 Index;\r
aeb61534
A
619\r
620 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
621 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
622\r
225290eb 623 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));\r
aeb61534 624\r
b34e4db3 625 // Obtain page table base\r
1bfda055 626 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
aeb61534 627\r
b34e4db3 628 // Calculate index into first level translation table for start of modification\r
1bfda055 629 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
630 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
aeb61534 631\r
b34e4db3 632 // Get section attributes and convert to page attributes\r
aeb61534 633 SectionDescriptor = FirstLevelTable[FirstLevelIdx];\r
1bfda055 634 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE;\r
635 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(SectionDescriptor,0);\r
636 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(SectionDescriptor);\r
637 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(SectionDescriptor,0);\r
638 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(SectionDescriptor);\r
639 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S(SectionDescriptor);\r
aeb61534 640\r
b34e4db3 641 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)\r
aeb61534
A
642 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);\r
643 if (EFI_ERROR(Status)) {\r
644 return Status;\r
645 }\r
646\r
647 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;\r
648\r
b34e4db3 649 // Write the page table entries out\r
2cf4b608 650 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {\r
651 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;\r
aeb61534
A
652 }\r
653\r
b34e4db3 654 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
2297613a 655 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);\r
aeb61534 656\r
b34e4db3 657 // Formulate page table entry, Domain=0, NS=0\r
1bfda055 658 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;\r
aeb61534 659\r
b34e4db3 660 // Write the page table entry out, replacing section entry\r
aeb61534
A
661 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;\r
662\r
663 return EFI_SUCCESS;\r
664}\r
665\r
666\r
667\r
668EFI_STATUS\r
669SetMemoryAttributes (\r
670 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
671 IN UINT64 Length,\r
672 IN UINT64 Attributes,\r
673 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
674 )\r
675{\r
676 EFI_STATUS Status;\r
677 \r
678 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {\r
11c20f4e 679 // Is the base and length a multiple of 1 MB?\r
225290eb 680 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));\r
aeb61534
A
681 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);\r
682 } else {\r
11c20f4e 683 // Base and/or length is not a multiple of 1 MB\r
225290eb 684 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));\r
aeb61534
A
685 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);\r
686 }\r
687\r
11c20f4e 688 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
aeb61534 689 // flush and invalidate pages\r
11c20f4e 690 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?\r
aeb61534 691 ArmCleanInvalidateDataCache ();\r
11c20f4e 692\r
aeb61534
A
693 ArmInvalidateInstructionCache ();\r
694\r
11c20f4e 695 // Invalidate all TLB entries so changes are synced\r
696 ArmInvalidateTlb ();\r
aeb61534
A
697\r
698 return Status;\r
699}\r