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 // First Level Descriptors
23 typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR
;
25 // Second Level Descriptors
26 typedef UINT32 ARM_PAGE_TABLE_ENTRY
;
29 SectionToGcdAttributes (
30 IN UINT32 SectionAttributes
,
31 OUT UINT64
*GcdAttributes
36 // determine cacheability attributes
37 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) {
38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
:
39 *GcdAttributes
|= EFI_MEMORY_UC
;
41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE
:
42 *GcdAttributes
|= EFI_MEMORY_UC
;
44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
45 *GcdAttributes
|= EFI_MEMORY_WT
;
47 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
48 *GcdAttributes
|= EFI_MEMORY_WB
;
50 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
:
51 *GcdAttributes
|= EFI_MEMORY_WC
;
53 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
:
54 *GcdAttributes
|= EFI_MEMORY_WB
;
56 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
57 *GcdAttributes
|= EFI_MEMORY_UC
;
60 return EFI_UNSUPPORTED
;
63 // determine protection attributes
64 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_AP_MASK
) {
65 case TT_DESCRIPTOR_SECTION_AP_NO_NO
: // no read, no write
66 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
69 case TT_DESCRIPTOR_SECTION_AP_RW_NO
:
70 case TT_DESCRIPTOR_SECTION_AP_RW_RW
:
71 // normal read/write access, do not add additional attributes
74 // read only cases map to write-protect
75 case TT_DESCRIPTOR_SECTION_AP_RO_NO
:
76 case TT_DESCRIPTOR_SECTION_AP_RO_RO
:
77 *GcdAttributes
|= EFI_MEMORY_RO
;
81 return EFI_UNSUPPORTED
;
84 // now process eXectue Never attribute
85 if ((SectionAttributes
& TT_DESCRIPTOR_SECTION_XN_MASK
) != 0 ) {
86 *GcdAttributes
|= EFI_MEMORY_XP
;
94 IN UINT32 PageAttributes
,
95 OUT UINT64
*GcdAttributes
100 // determine cacheability attributes
101 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) {
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
:
103 *GcdAttributes
|= EFI_MEMORY_UC
;
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE
:
106 *GcdAttributes
|= EFI_MEMORY_UC
;
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
109 *GcdAttributes
|= EFI_MEMORY_WT
;
111 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
112 *GcdAttributes
|= EFI_MEMORY_WB
;
114 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
:
115 *GcdAttributes
|= EFI_MEMORY_WC
;
117 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
:
118 *GcdAttributes
|= EFI_MEMORY_WB
;
120 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
121 *GcdAttributes
|= EFI_MEMORY_UC
;
124 return EFI_UNSUPPORTED
;
127 // determine protection attributes
128 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_AP_MASK
) {
129 case TT_DESCRIPTOR_PAGE_AP_NO_NO
: // no read, no write
130 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
133 case TT_DESCRIPTOR_PAGE_AP_RW_NO
:
134 case TT_DESCRIPTOR_PAGE_AP_RW_RW
:
135 // normal read/write access, do not add additional attributes
138 // read only cases map to write-protect
139 case TT_DESCRIPTOR_PAGE_AP_RO_NO
:
140 case TT_DESCRIPTOR_PAGE_AP_RO_RO
:
141 *GcdAttributes
|= EFI_MEMORY_RO
;
145 return EFI_UNSUPPORTED
;
148 // now process eXectue Never attribute
149 if ((PageAttributes
& TT_DESCRIPTOR_PAGE_XN_MASK
) != 0 ) {
150 *GcdAttributes
|= EFI_MEMORY_XP
;
157 SyncCacheConfigPage (
158 IN UINT32 SectionIndex
,
159 IN UINT32 FirstLevelDescriptor
,
160 IN UINTN NumberOfDescriptors
,
161 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
,
162 IN OUT EFI_PHYSICAL_ADDRESS
*NextRegionBase
,
163 IN OUT UINT64
*NextRegionLength
,
164 IN OUT UINT32
*NextSectionAttributes
169 volatile ARM_PAGE_TABLE_ENTRY
*SecondLevelTable
;
170 UINT32 NextPageAttributes
= 0;
171 UINT32 PageAttributes
= 0;
173 UINT64 GcdAttributes
;
175 // Get the Base Address from FirstLevelDescriptor;
176 BaseAddress
= TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
178 // Convert SectionAttributes into PageAttributes
180 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes
,0) |
181 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes
);
183 // obtain page table base
184 SecondLevelTable
= (ARM_PAGE_TABLE_ENTRY
*)(FirstLevelDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
186 for (i
=0; i
< TRANSLATION_TABLE_PAGE_COUNT
; i
++) {
187 if ((SecondLevelTable
[i
] & TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
188 // extract attributes (cacheability and permissions)
189 PageAttributes
= SecondLevelTable
[i
] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
);
191 if (NextPageAttributes
== 0) {
192 // start on a new region
193 *NextRegionLength
= 0;
194 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
195 NextPageAttributes
= PageAttributes
;
196 } else if (PageAttributes
!= NextPageAttributes
) {
197 // Convert Section Attributes into GCD Attributes
198 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
199 ASSERT_EFI_ERROR (Status
);
201 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
202 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
204 // start on a new region
205 *NextRegionLength
= 0;
206 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
207 NextPageAttributes
= PageAttributes
;
209 } else if (NextPageAttributes
!= 0) {
210 // Convert Page Attributes into GCD Attributes
211 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
212 ASSERT_EFI_ERROR (Status
);
214 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
215 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
217 *NextRegionLength
= 0;
218 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
219 NextPageAttributes
= 0;
221 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
224 // Convert back PageAttributes into SectionAttributes
225 *NextSectionAttributes
=
226 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes
,0) |
227 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes
);
234 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
239 EFI_PHYSICAL_ADDRESS NextRegionBase
;
240 UINT64 NextRegionLength
;
241 UINT32 NextSectionAttributes
= 0;
242 UINT32 SectionAttributes
= 0;
243 UINT64 GcdAttributes
;
244 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
245 UINTN NumberOfDescriptors
;
246 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
249 DEBUG ((EFI_D_PAGE
, "SyncCacheConfig()\n"));
251 // This code assumes MMU is enabled and filed with section translations
252 ASSERT (ArmMmuEnabled ());
255 // Get the memory space map from GCD
257 MemorySpaceMap
= NULL
;
258 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
259 ASSERT_EFI_ERROR (Status
);
262 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
263 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
264 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
265 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
266 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
268 // obtain page table base
269 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
271 // Get the first region
272 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
274 // iterate through each 1MB descriptor
275 NextRegionBase
= NextRegionLength
= 0;
276 for (i
=0; i
< TRANSLATION_TABLE_SECTION_COUNT
; i
++) {
277 if ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
278 // extract attributes (cacheability and permissions)
279 SectionAttributes
= FirstLevelTable
[i
] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
281 if (NextSectionAttributes
== 0) {
282 // start on a new region
283 NextRegionLength
= 0;
284 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
285 NextSectionAttributes
= SectionAttributes
;
286 } else if (SectionAttributes
!= NextSectionAttributes
) {
287 // Convert Section Attributes into GCD Attributes
288 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
289 ASSERT_EFI_ERROR (Status
);
291 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
292 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
294 // start on a new region
295 NextRegionLength
= 0;
296 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
297 NextSectionAttributes
= SectionAttributes
;
299 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
300 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable
[i
])) {
301 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from
302 // bits that are actually part of the pagetable address. We clear it out to zero so that
303 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the
304 // section attributes into page attributes
305 NextSectionAttributes
= 0;
306 Status
= SyncCacheConfigPage (
307 i
,FirstLevelTable
[i
],
308 NumberOfDescriptors
, MemorySpaceMap
,
309 &NextRegionBase
,&NextRegionLength
,&NextSectionAttributes
);
310 ASSERT_EFI_ERROR (Status
);
312 // We do not support yet 16MB sections
313 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
315 // start on a new region
316 if (NextSectionAttributes
!= 0) {
317 // Convert Section Attributes into GCD Attributes
318 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
319 ASSERT_EFI_ERROR (Status
);
321 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
322 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
324 NextRegionLength
= 0;
325 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
326 NextSectionAttributes
= 0;
328 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
330 } // section entry loop
332 if (NextSectionAttributes
!= 0) {
333 // Convert Section Attributes into GCD Attributes
334 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
335 ASSERT_EFI_ERROR (Status
);
337 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
338 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
341 FreePool (MemorySpaceMap
);
350 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
352 IN UINT64 Attributes
,
353 IN EFI_PHYSICAL_ADDRESS VirtualMask
,
354 OUT BOOLEAN
*FlushTlbs OPTIONAL
360 UINT32 FirstLevelIdx
;
362 UINT32 NumPageEntries
;
365 UINT32 PageTableIndex
;
366 UINT32 PageTableEntry
;
367 UINT32 CurrentPageTableEntry
;
370 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
371 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
373 Status
= EFI_SUCCESS
;
375 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
376 // EntryValue: values at bit positions specified by EntryMask
377 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
;
378 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
379 // Although the PI spec is unclear on this the GCD guarantees that only
380 // one Attribute bit is set at a time, so we can safely use a switch statement
381 switch (Attributes
) {
383 // modify cacheability attributes
384 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
385 // map to strongly ordered
386 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
390 // modify cacheability attributes
391 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
392 // map to normal non-cachable
393 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
397 // modify cacheability attributes
398 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
399 // write through with no-allocate
400 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
404 // modify cacheability attributes
405 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
406 // write back (with allocate)
407 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
413 // cannot be implemented UEFI definition unclear for ARM
414 // Cause a page fault if these ranges are accessed.
415 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_FAULT
;
416 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
420 return EFI_UNSUPPORTED
;
423 // Obtain page table base
424 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
426 // Calculate number of 4KB page table entries to change
427 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
429 // Iterate for the number of 4KB pages to change
431 for(p
= 0; p
< NumPageEntries
; p
++) {
432 // Calculate index into first level translation table for page table value
434 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
435 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
437 // Read the descriptor from the first level page table
438 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
440 // Does this descriptor need to be converted from section entry to 4K pages?
441 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
442 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
443 if (EFI_ERROR(Status
)) {
448 // Re-read descriptor
449 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
450 if (FlushTlbs
!= NULL
) {
455 // Obtain page table base address
456 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
458 // Calculate index into the page table
459 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
460 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
463 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
465 // Mask off appropriate fields
466 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
468 // Mask in new attributes and/or permissions
469 PageTableEntry
|= EntryValue
;
471 if (VirtualMask
!= 0) {
472 // Make this virtual address point at a physical page
473 PageTableEntry
&= ~VirtualMask
;
476 if (CurrentPageTableEntry
!= PageTableEntry
) {
477 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
479 // Only need to update if we are changing the entry
480 PageTable
[PageTableIndex
] = PageTableEntry
;
481 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
483 // Clean/invalidate the cache for this page, but only
484 // if we are modifying the memory type attributes
485 if (((CurrentPageTableEntry
^ PageTableEntry
) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) != 0) {
486 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
490 Status
= EFI_SUCCESS
;
491 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
493 } // End first level translation table loop
501 UpdateSectionEntries (
502 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
504 IN UINT64 Attributes
,
505 IN EFI_PHYSICAL_ADDRESS VirtualMask
508 EFI_STATUS Status
= EFI_SUCCESS
;
511 UINT32 FirstLevelIdx
;
514 UINT32 CurrentDescriptor
;
517 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
519 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
520 // EntryValue: values at bit positions specified by EntryMask
522 // Make sure we handle a section range that is unmapped
523 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
;
524 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
526 // Although the PI spec is unclear on this the GCD guarantees that only
527 // one Attribute bit is set at a time, so we can safely use a switch statement
530 // modify cacheability attributes
531 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
532 // map to strongly ordered
533 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
537 // modify cacheability attributes
538 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
539 // map to normal non-cachable
540 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
544 // modify cacheability attributes
545 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
546 // write through with no-allocate
547 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
551 // modify cacheability attributes
552 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
553 // write back (with allocate)
554 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
561 // cannot be implemented UEFI definition unclear for ARM
562 // Cause a page fault if these ranges are accessed.
563 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
564 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
569 return EFI_UNSUPPORTED
;
572 // obtain page table base
573 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
575 // calculate index into first level translation table for start of modification
576 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
577 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
579 // calculate number of 1MB first level entries this applies to
580 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
582 // iterate through each descriptor
583 for(i
=0; i
<NumSections
; i
++) {
584 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
586 // has this descriptor already been coverted to pages?
587 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
588 // forward this 1MB range to page table function instead
589 Status
= UpdatePageEntries (
590 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
591 TT_DESCRIPTOR_SECTION_SIZE
,
596 // still a section entry
598 // mask off appropriate fields
599 Descriptor
= CurrentDescriptor
& ~EntryMask
;
601 // mask in new attributes and/or permissions
602 Descriptor
|= EntryValue
;
603 if (VirtualMask
!= 0) {
604 Descriptor
&= ~VirtualMask
;
607 if (CurrentDescriptor
!= Descriptor
) {
608 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
610 // Only need to update if we are changing the descriptor
611 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
612 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
614 // Clean/invalidate the cache for this section, but only
615 // if we are modifying the memory type attributes
616 if (((CurrentDescriptor
^ Descriptor
) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) != 0) {
617 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
621 Status
= EFI_SUCCESS
;
629 ConvertSectionToPages (
630 IN EFI_PHYSICAL_ADDRESS BaseAddress
634 EFI_PHYSICAL_ADDRESS PageTableAddr
;
635 UINT32 FirstLevelIdx
;
636 UINT32 SectionDescriptor
;
637 UINT32 PageTableDescriptor
;
638 UINT32 PageDescriptor
;
641 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
642 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
644 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
646 // Obtain page table base
647 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
649 // Calculate index into first level translation table for start of modification
650 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
651 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
653 // Get section attributes and convert to page attributes
654 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
655 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
657 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
658 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
, 1, &PageTableAddr
);
659 if (EFI_ERROR(Status
)) {
663 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)(UINTN
)PageTableAddr
;
665 // Write the page table entries out
666 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
667 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
670 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
671 WriteBackInvalidateDataCacheRange ((VOID
*)(UINTN
)PageTableAddr
, TT_DESCRIPTOR_PAGE_SIZE
);
673 // Formulate page table entry, Domain=0, NS=0
674 PageTableDescriptor
= (((UINTN
)PageTableAddr
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
676 // Write the page table entry out, replacing section entry
677 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
685 SetMemoryAttributes (
686 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
688 IN UINT64 Attributes
,
689 IN EFI_PHYSICAL_ADDRESS VirtualMask
701 // Ignore invocations that only modify permission bits
703 if ((Attributes
& EFI_MEMORY_CACHETYPE_MASK
) == 0) {
709 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
710 Length
>= TT_DESCRIPTOR_SECTION_SIZE
) {
712 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
715 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
716 BaseAddress
, ChunkLength
, Attributes
));
718 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
,
725 // Process page by page until the next section boundary, but only if
726 // we have more than a section's worth of area to deal with after that.
728 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
729 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
730 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
731 ChunkLength
= Length
;
735 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
736 BaseAddress
, ChunkLength
, Attributes
));
738 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
739 VirtualMask
, &FlushTlbs
);
742 if (EFI_ERROR (Status
)) {
746 BaseAddress
+= ChunkLength
;
747 Length
-= ChunkLength
;
757 EfiAttributeToArmAttribute (
758 IN UINT64 EfiAttributes
761 UINT64 ArmAttributes
;
763 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
765 // Map to strongly ordered
766 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
770 // Map to normal non-cachable
771 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
775 // Write through with no-allocate
776 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
780 // Write back (with allocate)
781 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
786 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
790 // Determine protection attributes
791 if (EfiAttributes
& EFI_MEMORY_RO
) {
792 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
794 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
797 // Determine eXecute Never attribute
798 if (EfiAttributes
& EFI_MEMORY_XP
) {
799 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
802 return ArmAttributes
;
806 GetMemoryRegionPage (
807 IN UINT32
*PageTable
,
808 IN OUT UINTN
*BaseAddress
,
809 OUT UINTN
*RegionLength
,
810 OUT UINTN
*RegionAttributes
813 UINT32 PageAttributes
;
815 UINT32 PageDescriptor
;
817 // Convert the section attributes into page attributes
818 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
820 // Calculate index into first level translation table for start of modification
821 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
822 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
824 // Go through the page table to find the end of the section
825 for (; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
826 // Get the section at the given index
827 PageDescriptor
= PageTable
[TableIndex
];
829 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
830 // Case: End of the boundary of the region
832 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
833 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
834 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
836 // Case: End of the boundary of the region
840 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
846 return EFI_NOT_FOUND
;
851 IN OUT UINTN
*BaseAddress
,
852 OUT UINTN
*RegionLength
,
853 OUT UINTN
*RegionAttributes
858 UINT32 PageAttributes
;
859 UINT32 PageTableIndex
;
860 UINT32 SectionDescriptor
;
861 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
864 // Initialize the arguments
867 // Obtain page table base
868 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
870 // Calculate index into first level translation table for start of modification
871 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
872 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
874 // Get the section at the given index
875 SectionDescriptor
= FirstLevelTable
[TableIndex
];
877 // If 'BaseAddress' belongs to the section then round it to the section boundary
878 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
879 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
881 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
882 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
884 // Otherwise, we round it to the page boundary
885 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
887 // Get the attribute at the page table level (Level 2)
888 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
890 // Calculate index into first level translation table for start of modification
891 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
892 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
894 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
895 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
896 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
899 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
900 // Get the section at the given index
901 SectionDescriptor
= FirstLevelTable
[TableIndex
];
903 // If the entry is a level-2 page table then we scan it to find the end of the region
904 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
905 // Extract the page table location from the descriptor
906 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
908 // Scan the page table to find the end of the region.
909 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
911 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
912 if (Status
== EFI_SUCCESS
) {
915 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
916 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
)) {
917 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
918 // If the attributes of the section differ from the one targeted then we exit the loop
921 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
924 // If we are on an invalid section then it means it is the end of our section.