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