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>
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
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.
19 #include <Library/MemoryAllocationLib.h>
22 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
29 // First Level Descriptors
30 typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR
;
32 // Second Level Descriptors
33 typedef UINT32 ARM_PAGE_TABLE_ENTRY
;
36 SectionToGcdAttributes (
37 IN UINT32 SectionAttributes
,
38 OUT UINT64
*GcdAttributes
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
;
48 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE
:
49 *GcdAttributes
|= EFI_MEMORY_UC
;
51 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
52 *GcdAttributes
|= EFI_MEMORY_WT
;
54 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
55 *GcdAttributes
|= EFI_MEMORY_WB
;
57 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
:
58 *GcdAttributes
|= EFI_MEMORY_WC
;
60 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
:
61 *GcdAttributes
|= EFI_MEMORY_WB
;
63 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
64 *GcdAttributes
|= EFI_MEMORY_UC
;
67 return EFI_UNSUPPORTED
;
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;
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
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
;
88 return EFI_UNSUPPORTED
;
91 // now process eXectue Never attribute
92 if ((SectionAttributes
& TT_DESCRIPTOR_SECTION_XN_MASK
) != 0 ) {
93 *GcdAttributes
|= EFI_MEMORY_XP
;
100 PageToGcdAttributes (
101 IN UINT32 PageAttributes
,
102 OUT UINT64
*GcdAttributes
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
;
112 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE
:
113 *GcdAttributes
|= EFI_MEMORY_UC
;
115 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
116 *GcdAttributes
|= EFI_MEMORY_WT
;
118 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
119 *GcdAttributes
|= EFI_MEMORY_WB
;
121 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
:
122 *GcdAttributes
|= EFI_MEMORY_WC
;
124 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
:
125 *GcdAttributes
|= EFI_MEMORY_WB
;
127 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
128 *GcdAttributes
|= EFI_MEMORY_UC
;
131 return EFI_UNSUPPORTED
;
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;
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
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
;
152 return EFI_UNSUPPORTED
;
155 // now process eXectue Never attribute
156 if ((PageAttributes
& TT_DESCRIPTOR_PAGE_XN_MASK
) != 0 ) {
157 *GcdAttributes
|= EFI_MEMORY_XP
;
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
176 volatile ARM_PAGE_TABLE_ENTRY
*SecondLevelTable
;
177 UINT32 NextPageAttributes
= 0;
178 UINT32 PageAttributes
= 0;
180 UINT64 GcdAttributes
;
182 // Get the Base Address from FirstLevelDescriptor;
183 BaseAddress
= TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
185 // Convert SectionAttributes into PageAttributes
187 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes
,0) |
188 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes
);
190 // obtain page table base
191 SecondLevelTable
= (ARM_PAGE_TABLE_ENTRY
*)(FirstLevelDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
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
);
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
);
208 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
209 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
211 // start on a new region
212 *NextRegionLength
= 0;
213 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
214 NextPageAttributes
= PageAttributes
;
216 } else if (NextPageAttributes
!= 0) {
217 // Convert Page Attributes into GCD Attributes
218 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
219 ASSERT_EFI_ERROR (Status
);
221 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
222 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
224 *NextRegionLength
= 0;
225 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
226 NextPageAttributes
= 0;
228 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
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
);
241 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
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
;
256 DEBUG ((EFI_D_PAGE
, "SyncCacheConfig()\n"));
258 // This code assumes MMU is enabled and filed with section translations
259 ASSERT (ArmMmuEnabled ());
262 // Get the memory space map from GCD
264 MemorySpaceMap
= NULL
;
265 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
266 ASSERT_EFI_ERROR (Status
);
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.
275 // obtain page table base
276 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
278 // Get the first region
279 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
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
);
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
);
298 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
299 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
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
;
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
);
319 // We do not support yet 16MB sections
320 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
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
);
328 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
329 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
331 NextRegionLength
= 0;
332 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
333 NextSectionAttributes
= 0;
335 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
337 } // section entry loop
339 if (NextSectionAttributes
!= 0) {
340 // Convert Section Attributes into GCD Attributes
341 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
342 ASSERT_EFI_ERROR (Status
);
344 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
345 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
348 FreePool (MemorySpaceMap
);
357 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
359 IN UINT64 Attributes
,
360 IN EFI_PHYSICAL_ADDRESS VirtualMask
,
361 OUT BOOLEAN
*FlushTlbs OPTIONAL
367 UINT32 FirstLevelIdx
;
369 UINT32 NumPageEntries
;
372 UINT32 PageTableIndex
;
373 UINT32 PageTableEntry
;
374 UINT32 CurrentPageTableEntry
;
377 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
378 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
380 Status
= EFI_SUCCESS
;
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
;
388 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
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
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
419 return EFI_UNSUPPORTED
;
422 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
423 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RO_RO
;
425 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RW_RW
;
428 // Obtain page table base
429 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
431 // Calculate number of 4KB page table entries to change
432 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
434 // Iterate for the number of 4KB pages to change
436 for(p
= 0; p
< NumPageEntries
; p
++) {
437 // Calculate index into first level translation table for page table value
439 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
440 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
442 // Read the descriptor from the first level page table
443 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
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
)) {
453 // Re-read descriptor
454 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
455 if (FlushTlbs
!= NULL
) {
460 // Obtain page table base address
461 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
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
);
468 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
470 // Mask off appropriate fields
471 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
473 // Mask in new attributes and/or permissions
474 PageTableEntry
|= EntryValue
;
476 if (VirtualMask
!= 0) {
477 // Make this virtual address point at a physical page
478 PageTableEntry
&= ~VirtualMask
;
481 if (CurrentPageTableEntry
!= PageTableEntry
) {
482 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
484 // Only need to update if we are changing the entry
485 PageTable
[PageTableIndex
] = PageTableEntry
;
486 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
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
);
495 Status
= EFI_SUCCESS
;
496 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
498 } // End first level translation table loop
506 UpdateSectionEntries (
507 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
509 IN UINT64 Attributes
,
510 IN EFI_PHYSICAL_ADDRESS VirtualMask
513 EFI_STATUS Status
= EFI_SUCCESS
;
516 UINT32 FirstLevelIdx
;
519 UINT32 CurrentDescriptor
;
522 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
524 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
525 // EntryValue: values at bit positions specified by EntryMask
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
;
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
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
560 return EFI_UNSUPPORTED
;
563 if (Attributes
& EFI_MEMORY_RO
) {
564 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
566 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
569 if (Attributes
& EFI_MEMORY_XP
) {
570 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
573 // obtain page table base
574 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
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
);
580 // calculate number of 1MB first level entries this applies to
581 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
583 // iterate through each descriptor
584 for(i
=0; i
<NumSections
; i
++) {
585 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
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
,
597 // still a section entry
599 // mask off appropriate fields
600 Descriptor
= CurrentDescriptor
& ~EntryMask
;
602 // mask in new attributes and/or permissions
603 Descriptor
|= EntryValue
;
604 if (VirtualMask
!= 0) {
605 Descriptor
&= ~VirtualMask
;
608 if (CurrentDescriptor
!= Descriptor
) {
609 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
611 // Only need to update if we are changing the descriptor
612 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
613 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
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
);
622 Status
= EFI_SUCCESS
;
630 ConvertSectionToPages (
631 IN EFI_PHYSICAL_ADDRESS BaseAddress
635 EFI_PHYSICAL_ADDRESS PageTableAddr
;
636 UINT32 FirstLevelIdx
;
637 UINT32 SectionDescriptor
;
638 UINT32 PageTableDescriptor
;
639 UINT32 PageDescriptor
;
642 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
643 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
645 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
647 // Obtain page table base
648 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
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
);
654 // Get section attributes and convert to page attributes
655 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
656 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
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
)) {
664 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)(UINTN
)PageTableAddr
;
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
;
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
);
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
;
677 // Write the page table entry out, replacing section entry
678 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
686 SetMemoryAttributes (
687 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
689 IN UINT64 Attributes
,
690 IN EFI_PHYSICAL_ADDRESS VirtualMask
703 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
704 Length
>= TT_DESCRIPTOR_SECTION_SIZE
) {
706 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
709 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
710 BaseAddress
, ChunkLength
, Attributes
));
712 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
,
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.
722 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
723 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
724 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
725 ChunkLength
= Length
;
729 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
730 BaseAddress
, ChunkLength
, Attributes
));
732 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
733 VirtualMask
, &FlushTlbs
);
736 if (EFI_ERROR (Status
)) {
740 BaseAddress
+= ChunkLength
;
741 Length
-= ChunkLength
;
751 EfiAttributeToArmAttribute (
752 IN UINT64 EfiAttributes
755 UINT64 ArmAttributes
;
757 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
759 // Map to strongly ordered
760 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
764 // Map to normal non-cachable
765 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
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
774 // Write back (with allocate)
775 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
780 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
784 // Determine protection attributes
785 if (EfiAttributes
& EFI_MEMORY_RO
) {
786 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
788 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
791 // Determine eXecute Never attribute
792 if (EfiAttributes
& EFI_MEMORY_XP
) {
793 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
796 return ArmAttributes
;
800 GetMemoryRegionPage (
801 IN UINT32
*PageTable
,
802 IN OUT UINTN
*BaseAddress
,
803 OUT UINTN
*RegionLength
,
804 OUT UINTN
*RegionAttributes
807 UINT32 PageAttributes
;
809 UINT32 PageDescriptor
;
811 // Convert the section attributes into page attributes
812 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
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
);
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
];
823 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
824 // Case: End of the boundary of the region
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
;
830 // Case: End of the boundary of the region
834 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
840 return EFI_NOT_FOUND
;
845 IN OUT UINTN
*BaseAddress
,
846 OUT UINTN
*RegionLength
,
847 OUT UINTN
*RegionAttributes
852 UINT32 PageAttributes
;
853 UINT32 PageTableIndex
;
854 UINT32 SectionDescriptor
;
855 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
858 // Initialize the arguments
861 // Obtain page table base
862 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
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
);
868 // Get the section at the given index
869 SectionDescriptor
= FirstLevelTable
[TableIndex
];
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
))
875 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
876 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
878 // Otherwise, we round it to the page boundary
879 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
881 // Get the attribute at the page table level (Level 2)
882 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
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
);
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
);
893 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
894 // Get the section at the given index
895 SectionDescriptor
= FirstLevelTable
[TableIndex
];
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
);
902 // Scan the page table to find the end of the region.
903 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
905 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
906 if (Status
== EFI_SUCCESS
) {
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
915 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
918 // If we are on an invalid section then it means it is the end of our section.