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
359 UINT32 FirstLevelIdx
;
361 UINT32 NumPageEntries
;
364 UINT32 PageTableIndex
;
365 UINT32 PageTableEntry
;
366 UINT32 CurrentPageTableEntry
;
369 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
370 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
372 Status
= EFI_SUCCESS
;
374 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
375 // EntryValue: values at bit positions specified by EntryMask
376 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
;
377 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
378 // Although the PI spec is unclear on this the GCD guarantees that only
379 // one Attribute bit is set at a time, so we can safely use a switch statement
380 switch (Attributes
) {
382 // modify cacheability attributes
383 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
384 // map to strongly ordered
385 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
389 // modify cacheability attributes
390 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
391 // map to normal non-cachable
392 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
396 // modify cacheability attributes
397 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
398 // write through with no-allocate
399 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
403 // modify cacheability attributes
404 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
405 // write back (with allocate)
406 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
412 // cannot be implemented UEFI definition unclear for ARM
413 // Cause a page fault if these ranges are accessed.
414 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_FAULT
;
415 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
419 return EFI_UNSUPPORTED
;
422 // Obtain page table base
423 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
425 // Calculate number of 4KB page table entries to change
426 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
428 // Iterate for the number of 4KB pages to change
430 for(p
= 0; p
< NumPageEntries
; p
++) {
431 // Calculate index into first level translation table for page table value
433 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
434 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
436 // Read the descriptor from the first level page table
437 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
439 // Does this descriptor need to be converted from section entry to 4K pages?
440 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
441 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
442 if (EFI_ERROR(Status
)) {
447 // Re-read descriptor
448 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
451 // Obtain page table base address
452 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
454 // Calculate index into the page table
455 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
456 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
459 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
461 // Mask off appropriate fields
462 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
464 // Mask in new attributes and/or permissions
465 PageTableEntry
|= EntryValue
;
467 if (VirtualMask
!= 0) {
468 // Make this virtual address point at a physical page
469 PageTableEntry
&= ~VirtualMask
;
472 if (CurrentPageTableEntry
!= PageTableEntry
) {
473 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
474 if ((CurrentPageTableEntry
& TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) {
475 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
476 // Note assumes switch(Attributes), not ARMv7 possibilities
477 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
480 // Only need to update if we are changing the entry
481 PageTable
[PageTableIndex
] = PageTableEntry
;
482 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
485 Status
= EFI_SUCCESS
;
486 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
488 } // End first level translation table loop
496 UpdateSectionEntries (
497 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
499 IN UINT64 Attributes
,
500 IN EFI_PHYSICAL_ADDRESS VirtualMask
503 EFI_STATUS Status
= EFI_SUCCESS
;
506 UINT32 FirstLevelIdx
;
509 UINT32 CurrentDescriptor
;
512 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
514 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
515 // EntryValue: values at bit positions specified by EntryMask
517 // Make sure we handle a section range that is unmapped
518 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
;
519 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
521 // Although the PI spec is unclear on this the GCD guarantees that only
522 // one Attribute bit is set at a time, so we can safely use a switch statement
525 // modify cacheability attributes
526 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
527 // map to strongly ordered
528 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
532 // modify cacheability attributes
533 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
534 // map to normal non-cachable
535 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
539 // modify cacheability attributes
540 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
541 // write through with no-allocate
542 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
546 // modify cacheability attributes
547 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
548 // write back (with allocate)
549 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
556 // cannot be implemented UEFI definition unclear for ARM
557 // Cause a page fault if these ranges are accessed.
558 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
559 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
564 return EFI_UNSUPPORTED
;
567 // obtain page table base
568 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
570 // calculate index into first level translation table for start of modification
571 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
572 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
574 // calculate number of 1MB first level entries this applies to
575 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
577 // iterate through each descriptor
578 for(i
=0; i
<NumSections
; i
++) {
579 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
581 // has this descriptor already been coverted to pages?
582 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
583 // forward this 1MB range to page table function instead
584 Status
= UpdatePageEntries ((FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
, TT_DESCRIPTOR_SECTION_SIZE
, Attributes
, VirtualMask
);
586 // still a section entry
588 // mask off appropriate fields
589 Descriptor
= CurrentDescriptor
& ~EntryMask
;
591 // mask in new attributes and/or permissions
592 Descriptor
|= EntryValue
;
593 if (VirtualMask
!= 0) {
594 Descriptor
&= ~VirtualMask
;
597 if (CurrentDescriptor
!= Descriptor
) {
598 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
599 if ((CurrentDescriptor
& TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) {
600 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
601 // Note assumes switch(Attributes), not ARMv7 possabilities
602 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
605 // Only need to update if we are changing the descriptor
606 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
607 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
610 Status
= EFI_SUCCESS
;
618 ConvertSectionToPages (
619 IN EFI_PHYSICAL_ADDRESS BaseAddress
623 EFI_PHYSICAL_ADDRESS PageTableAddr
;
624 UINT32 FirstLevelIdx
;
625 UINT32 SectionDescriptor
;
626 UINT32 PageTableDescriptor
;
627 UINT32 PageDescriptor
;
630 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
631 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
633 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
635 // Obtain page table base
636 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
638 // Calculate index into first level translation table for start of modification
639 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
640 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
642 // Get section attributes and convert to page attributes
643 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
644 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
646 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
647 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
, 1, &PageTableAddr
);
648 if (EFI_ERROR(Status
)) {
652 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)(UINTN
)PageTableAddr
;
654 // Write the page table entries out
655 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
656 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
659 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
660 WriteBackInvalidateDataCacheRange ((VOID
*)(UINTN
)PageTableAddr
, TT_DESCRIPTOR_PAGE_SIZE
);
662 // Formulate page table entry, Domain=0, NS=0
663 PageTableDescriptor
= (((UINTN
)PageTableAddr
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
665 // Write the page table entry out, replacing section entry
666 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
674 SetMemoryAttributes (
675 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
677 IN UINT64 Attributes
,
678 IN EFI_PHYSICAL_ADDRESS VirtualMask
684 // Ignore invocations that only modify permission bits
686 if ((Attributes
& EFI_MEMORY_CACHETYPE_MASK
) == 0) {
690 if(((BaseAddress
& 0xFFFFF) == 0) && ((Length
& 0xFFFFF) == 0)) {
691 // Is the base and length a multiple of 1 MB?
692 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
693 Status
= UpdateSectionEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
695 // Base and/or length is not a multiple of 1 MB
696 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
697 Status
= UpdatePageEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
700 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
701 // flush and invalidate pages
702 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
703 ArmCleanInvalidateDataCache ();
705 ArmInvalidateInstructionCache ();
707 // Invalidate all TLB entries so changes are synced
714 EfiAttributeToArmAttribute (
715 IN UINT64 EfiAttributes
718 UINT64 ArmAttributes
;
720 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
722 // Map to strongly ordered
723 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
727 // Map to normal non-cachable
728 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
732 // Write through with no-allocate
733 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
737 // Write back (with allocate)
738 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
743 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
747 // Determine protection attributes
748 if (EfiAttributes
& EFI_MEMORY_RO
) {
749 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
751 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
754 // Determine eXecute Never attribute
755 if (EfiAttributes
& EFI_MEMORY_XP
) {
756 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
759 return ArmAttributes
;
763 GetMemoryRegionPage (
764 IN UINT32
*PageTable
,
765 IN OUT UINTN
*BaseAddress
,
766 OUT UINTN
*RegionLength
,
767 OUT UINTN
*RegionAttributes
770 UINT32 PageAttributes
;
772 UINT32 PageDescriptor
;
774 // Convert the section attributes into page attributes
775 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
777 // Calculate index into first level translation table for start of modification
778 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
779 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
781 // Go through the page table to find the end of the section
782 for (; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
783 // Get the section at the given index
784 PageDescriptor
= PageTable
[TableIndex
];
786 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
787 // Case: End of the boundary of the region
789 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
790 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
791 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
793 // Case: End of the boundary of the region
797 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
803 return EFI_NOT_FOUND
;
808 IN OUT UINTN
*BaseAddress
,
809 OUT UINTN
*RegionLength
,
810 OUT UINTN
*RegionAttributes
815 UINT32 PageAttributes
;
816 UINT32 PageTableIndex
;
817 UINT32 SectionDescriptor
;
818 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
821 // Initialize the arguments
824 // Obtain page table base
825 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
827 // Calculate index into first level translation table for start of modification
828 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
829 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
831 // Get the section at the given index
832 SectionDescriptor
= FirstLevelTable
[TableIndex
];
834 // If 'BaseAddress' belongs to the section then round it to the section boundary
835 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
836 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
838 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
839 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
841 // Otherwise, we round it to the page boundary
842 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
844 // Get the attribute at the page table level (Level 2)
845 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
847 // Calculate index into first level translation table for start of modification
848 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
849 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
851 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
852 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
853 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
856 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
857 // Get the section at the given index
858 SectionDescriptor
= FirstLevelTable
[TableIndex
];
860 // If the entry is a level-2 page table then we scan it to find the end of the region
861 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
862 // Extract the page table location from the descriptor
863 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
865 // Scan the page table to find the end of the region.
866 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
868 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
869 if (Status
== EFI_SUCCESS
) {
872 } else 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 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
875 // If the attributes of the section differ from the one targeted then we exit the loop
878 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
881 // If we are on an invalid section then it means it is the end of our section.