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>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Library/MemoryAllocationLib.h>
21 // First Level Descriptors
22 typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR
;
24 // Second Level Descriptors
25 typedef UINT32 ARM_PAGE_TABLE_ENTRY
;
28 SectionToGcdAttributes (
29 IN UINT32 SectionAttributes
,
30 OUT UINT64
*GcdAttributes
35 // determine cacheability attributes
36 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) {
37 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
:
38 *GcdAttributes
|= EFI_MEMORY_UC
;
40 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE
:
41 *GcdAttributes
|= EFI_MEMORY_UC
;
43 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
44 *GcdAttributes
|= EFI_MEMORY_WT
;
46 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
47 *GcdAttributes
|= EFI_MEMORY_WB
;
49 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
:
50 *GcdAttributes
|= EFI_MEMORY_WC
;
52 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
:
53 *GcdAttributes
|= EFI_MEMORY_WB
;
55 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
56 *GcdAttributes
|= EFI_MEMORY_UC
;
59 return EFI_UNSUPPORTED
;
62 // determine protection attributes
63 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_AP_MASK
) {
64 case TT_DESCRIPTOR_SECTION_AP_NO_NO
: // no read, no write
65 //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;
68 case TT_DESCRIPTOR_SECTION_AP_RW_NO
:
69 case TT_DESCRIPTOR_SECTION_AP_RW_RW
:
70 // normal read/write access, do not add additional attributes
73 // read only cases map to write-protect
74 case TT_DESCRIPTOR_SECTION_AP_RO_NO
:
75 case TT_DESCRIPTOR_SECTION_AP_RO_RO
:
76 *GcdAttributes
|= EFI_MEMORY_WP
;
80 return EFI_UNSUPPORTED
;
83 // now process eXectue Never attribute
84 if ((SectionAttributes
& TT_DESCRIPTOR_SECTION_XN_MASK
) != 0 ) {
85 *GcdAttributes
|= EFI_MEMORY_XP
;
93 IN UINT32 PageAttributes
,
94 OUT UINT64
*GcdAttributes
99 // determine cacheability attributes
100 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) {
101 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
:
102 *GcdAttributes
|= EFI_MEMORY_UC
;
104 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE
:
105 *GcdAttributes
|= EFI_MEMORY_UC
;
107 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
108 *GcdAttributes
|= EFI_MEMORY_WT
;
110 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
111 *GcdAttributes
|= EFI_MEMORY_WB
;
113 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
:
114 *GcdAttributes
|= EFI_MEMORY_WC
;
116 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
:
117 *GcdAttributes
|= EFI_MEMORY_WB
;
119 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
120 *GcdAttributes
|= EFI_MEMORY_UC
;
123 return EFI_UNSUPPORTED
;
126 // determine protection attributes
127 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_AP_MASK
) {
128 case TT_DESCRIPTOR_PAGE_AP_NO_NO
: // no read, no write
129 //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;
132 case TT_DESCRIPTOR_PAGE_AP_RW_NO
:
133 case TT_DESCRIPTOR_PAGE_AP_RW_RW
:
134 // normal read/write access, do not add additional attributes
137 // read only cases map to write-protect
138 case TT_DESCRIPTOR_PAGE_AP_RO_NO
:
139 case TT_DESCRIPTOR_PAGE_AP_RO_RO
:
140 *GcdAttributes
|= EFI_MEMORY_WP
;
144 return EFI_UNSUPPORTED
;
147 // now process eXectue Never attribute
148 if ((PageAttributes
& TT_DESCRIPTOR_PAGE_XN_MASK
) != 0 ) {
149 *GcdAttributes
|= EFI_MEMORY_XP
;
156 SyncCacheConfigPage (
157 IN UINT32 SectionIndex
,
158 IN UINT32 FirstLevelDescriptor
,
159 IN UINTN NumberOfDescriptors
,
160 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
,
161 IN OUT EFI_PHYSICAL_ADDRESS
*NextRegionBase
,
162 IN OUT UINT64
*NextRegionLength
,
163 IN OUT UINT32
*NextSectionAttributes
168 volatile ARM_PAGE_TABLE_ENTRY
*SecondLevelTable
;
169 UINT32 NextPageAttributes
= 0;
170 UINT32 PageAttributes
= 0;
172 UINT64 GcdAttributes
;
174 // Get the Base Address from FirstLevelDescriptor;
175 BaseAddress
= TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
177 // Convert SectionAttributes into PageAttributes
179 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes
,0) |
180 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes
);
182 // obtain page table base
183 SecondLevelTable
= (ARM_PAGE_TABLE_ENTRY
*)(FirstLevelDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
185 for (i
=0; i
< TRANSLATION_TABLE_PAGE_COUNT
; i
++) {
186 if ((SecondLevelTable
[i
] & TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
187 // extract attributes (cacheability and permissions)
188 PageAttributes
= SecondLevelTable
[i
] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
);
190 if (NextPageAttributes
== 0) {
191 // start on a new region
192 *NextRegionLength
= 0;
193 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
194 NextPageAttributes
= PageAttributes
;
195 } else if (PageAttributes
!= NextPageAttributes
) {
196 // Convert Section Attributes into GCD Attributes
197 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
198 ASSERT_EFI_ERROR (Status
);
200 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
201 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
203 // start on a new region
204 *NextRegionLength
= 0;
205 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
206 NextPageAttributes
= PageAttributes
;
208 } else if (NextPageAttributes
!= 0) {
209 // Convert Page Attributes into GCD Attributes
210 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
211 ASSERT_EFI_ERROR (Status
);
213 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
214 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
216 *NextRegionLength
= 0;
217 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
218 NextPageAttributes
= 0;
220 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
223 // Convert back PageAttributes into SectionAttributes
224 *NextSectionAttributes
=
225 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes
,0) |
226 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes
);
233 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
238 EFI_PHYSICAL_ADDRESS NextRegionBase
;
239 UINT64 NextRegionLength
;
240 UINT32 NextSectionAttributes
= 0;
241 UINT32 SectionAttributes
= 0;
242 UINT64 GcdAttributes
;
243 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
244 UINTN NumberOfDescriptors
;
245 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
248 DEBUG ((EFI_D_PAGE
, "SyncCacheConfig()\n"));
250 // This code assumes MMU is enabled and filed with section translations
251 ASSERT (ArmMmuEnabled ());
254 // Get the memory space map from GCD
256 MemorySpaceMap
= NULL
;
257 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
258 ASSERT_EFI_ERROR (Status
);
261 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
262 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
263 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
264 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
265 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
267 // obtain page table base
268 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
270 // Get the first region
271 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
273 // iterate through each 1MB descriptor
274 NextRegionBase
= NextRegionLength
= 0;
275 for (i
=0; i
< TRANSLATION_TABLE_SECTION_COUNT
; i
++) {
276 if ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
277 // extract attributes (cacheability and permissions)
278 SectionAttributes
= FirstLevelTable
[i
] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
280 if (NextSectionAttributes
== 0) {
281 // start on a new region
282 NextRegionLength
= 0;
283 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
284 NextSectionAttributes
= SectionAttributes
;
285 } else if (SectionAttributes
!= NextSectionAttributes
) {
286 // Convert Section Attributes into GCD Attributes
287 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
288 ASSERT_EFI_ERROR (Status
);
290 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
291 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
293 // start on a new region
294 NextRegionLength
= 0;
295 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
296 NextSectionAttributes
= SectionAttributes
;
298 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
299 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable
[i
])) {
300 Status
= SyncCacheConfigPage (
301 i
,FirstLevelTable
[i
],
302 NumberOfDescriptors
, MemorySpaceMap
,
303 &NextRegionBase
,&NextRegionLength
,&NextSectionAttributes
);
304 ASSERT_EFI_ERROR (Status
);
306 // We do not support yet 16MB sections
307 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
309 // start on a new region
310 if (NextSectionAttributes
!= 0) {
311 // Convert Section Attributes into GCD Attributes
312 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
313 ASSERT_EFI_ERROR (Status
);
315 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
316 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
318 NextRegionLength
= 0;
319 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
320 NextSectionAttributes
= 0;
322 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
324 } // section entry loop
326 if (NextSectionAttributes
!= 0) {
327 // Convert Section Attributes into GCD Attributes
328 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
329 ASSERT_EFI_ERROR (Status
);
331 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
332 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
335 FreePool (MemorySpaceMap
);
344 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
346 IN UINT64 Attributes
,
347 IN EFI_PHYSICAL_ADDRESS VirtualMask
353 UINT32 FirstLevelIdx
;
355 UINT32 NumPageEntries
;
358 UINT32 PageTableIndex
;
359 UINT32 PageTableEntry
;
360 UINT32 CurrentPageTableEntry
;
363 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
364 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
366 Status
= EFI_SUCCESS
;
368 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
369 // EntryValue: values at bit positions specified by EntryMask
370 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
;
371 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
372 // Although the PI spec is unclear on this the GCD guarantees that only
373 // one Attribute bit is set at a time, so we can safely use a switch statement
374 switch (Attributes
) {
376 // modify cacheability attributes
377 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
378 // map to strongly ordered
379 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
383 // modify cacheability attributes
384 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
385 // map to normal non-cachable
386 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
390 // modify cacheability attributes
391 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
392 // write through with no-allocate
393 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
397 // modify cacheability attributes
398 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
399 // write back (with allocate)
400 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
406 // cannot be implemented UEFI definition unclear for ARM
407 // Cause a page fault if these ranges are accessed.
408 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_FAULT
;
409 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
413 return EFI_UNSUPPORTED
;
416 // Obtain page table base
417 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
419 // Calculate number of 4KB page table entries to change
420 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
422 // Iterate for the number of 4KB pages to change
424 for(p
= 0; p
< NumPageEntries
; p
++) {
425 // Calculate index into first level translation table for page table value
427 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
428 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
430 // Read the descriptor from the first level page table
431 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
433 // Does this descriptor need to be converted from section entry to 4K pages?
434 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
435 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
436 if (EFI_ERROR(Status
)) {
441 // Re-read descriptor
442 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
445 // Obtain page table base address
446 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
448 // Calculate index into the page table
449 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
450 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
453 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
455 // Mask off appropriate fields
456 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
458 // Mask in new attributes and/or permissions
459 PageTableEntry
|= EntryValue
;
461 if (VirtualMask
!= 0) {
462 // Make this virtual address point at a physical page
463 PageTableEntry
&= ~VirtualMask
;
466 if (CurrentPageTableEntry
!= PageTableEntry
) {
467 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
468 if ((CurrentPageTableEntry
& TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) {
469 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
470 // Note assumes switch(Attributes), not ARMv7 possibilities
471 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
474 // Only need to update if we are changing the entry
475 PageTable
[PageTableIndex
] = PageTableEntry
;
476 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
479 Status
= EFI_SUCCESS
;
480 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
482 } // End first level translation table loop
490 UpdateSectionEntries (
491 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
493 IN UINT64 Attributes
,
494 IN EFI_PHYSICAL_ADDRESS VirtualMask
497 EFI_STATUS Status
= EFI_SUCCESS
;
500 UINT32 FirstLevelIdx
;
503 UINT32 CurrentDescriptor
;
506 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
508 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
509 // EntryValue: values at bit positions specified by EntryMask
511 // Make sure we handle a section range that is unmapped
512 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
;
513 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
515 // Although the PI spec is unclear on this the GCD guarantees that only
516 // one Attribute bit is set at a time, so we can safely use a switch statement
519 // modify cacheability attributes
520 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
521 // map to strongly ordered
522 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
526 // modify cacheability attributes
527 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
528 // map to normal non-cachable
529 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
533 // modify cacheability attributes
534 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
535 // write through with no-allocate
536 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
540 // modify cacheability attributes
541 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
542 // write back (with allocate)
543 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
550 // cannot be implemented UEFI definition unclear for ARM
551 // Cause a page fault if these ranges are accessed.
552 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
553 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
558 return EFI_UNSUPPORTED
;
561 // obtain page table base
562 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
564 // calculate index into first level translation table for start of modification
565 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
566 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
568 // calculate number of 1MB first level entries this applies to
569 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
571 // iterate through each descriptor
572 for(i
=0; i
<NumSections
; i
++) {
573 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
575 // has this descriptor already been coverted to pages?
576 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
577 // forward this 1MB range to page table function instead
578 Status
= UpdatePageEntries ((FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
, TT_DESCRIPTOR_SECTION_SIZE
, Attributes
, VirtualMask
);
580 // still a section entry
582 // mask off appropriate fields
583 Descriptor
= CurrentDescriptor
& ~EntryMask
;
585 // mask in new attributes and/or permissions
586 Descriptor
|= EntryValue
;
587 if (VirtualMask
!= 0) {
588 Descriptor
&= ~VirtualMask
;
591 if (CurrentDescriptor
!= Descriptor
) {
592 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
593 if ((CurrentDescriptor
& TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) {
594 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
595 // Note assumes switch(Attributes), not ARMv7 possabilities
596 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
599 // Only need to update if we are changing the descriptor
600 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
601 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
604 Status
= EFI_SUCCESS
;
612 ConvertSectionToPages (
613 IN EFI_PHYSICAL_ADDRESS BaseAddress
617 EFI_PHYSICAL_ADDRESS PageTableAddr
;
618 UINT32 FirstLevelIdx
;
619 UINT32 SectionDescriptor
;
620 UINT32 PageTableDescriptor
;
621 UINT32 PageDescriptor
;
624 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
625 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
627 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
629 // Obtain page table base
630 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
632 // Calculate index into first level translation table for start of modification
633 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
634 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
636 // Get section attributes and convert to page attributes
637 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
638 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
640 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
641 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
, 1, &PageTableAddr
);
642 if (EFI_ERROR(Status
)) {
646 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)(UINTN
)PageTableAddr
;
648 // Write the page table entries out
649 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
650 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
653 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
654 WriteBackInvalidateDataCacheRange ((VOID
*)(UINTN
)PageTableAddr
, TT_DESCRIPTOR_PAGE_SIZE
);
656 // Formulate page table entry, Domain=0, NS=0
657 PageTableDescriptor
= (((UINTN
)PageTableAddr
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
659 // Write the page table entry out, replacing section entry
660 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
668 SetMemoryAttributes (
669 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
671 IN UINT64 Attributes
,
672 IN EFI_PHYSICAL_ADDRESS VirtualMask
677 if(((BaseAddress
& 0xFFFFF) == 0) && ((Length
& 0xFFFFF) == 0)) {
678 // Is the base and length a multiple of 1 MB?
679 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
680 Status
= UpdateSectionEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
682 // Base and/or length is not a multiple of 1 MB
683 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
684 Status
= UpdatePageEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
687 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
688 // flush and invalidate pages
689 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
690 ArmCleanInvalidateDataCache ();
692 ArmInvalidateInstructionCache ();
694 // Invalidate all TLB entries so changes are synced
701 EfiAttributeToArmAttribute (
702 IN UINT64 EfiAttributes
705 UINT64 ArmAttributes
;
707 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
709 // Map to strongly ordered
710 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
714 // Map to normal non-cachable
715 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
719 // Write through with no-allocate
720 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
724 // Write back (with allocate)
725 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
733 // Cannot be implemented UEFI definition unclear for ARM
734 // Cause a page fault if these ranges are accessed.
735 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
736 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes
));
740 // Determine protection attributes
741 if (EfiAttributes
& EFI_MEMORY_WP
) {
742 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
744 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
747 // Determine eXecute Never attribute
748 if (EfiAttributes
& EFI_MEMORY_XP
) {
749 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
752 return ArmAttributes
;
756 GetMemoryRegionPage (
757 IN UINT32
*PageTable
,
758 IN OUT UINTN
*BaseAddress
,
759 OUT UINTN
*RegionLength
,
760 OUT UINTN
*RegionAttributes
763 UINT32 PageAttributes
;
765 UINT32 PageDescriptor
;
767 // Convert the section attributes into page attributes
768 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
770 // Calculate index into first level translation table for start of modification
771 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
772 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
774 // Go through the page table to find the end of the section
775 for (; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
776 // Get the section at the given index
777 PageDescriptor
= PageTable
[TableIndex
];
779 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
780 // Case: End of the boundary of the region
782 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
783 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
784 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
786 // Case: End of the boundary of the region
790 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
796 return EFI_NOT_FOUND
;
801 IN OUT UINTN
*BaseAddress
,
802 OUT UINTN
*RegionLength
,
803 OUT UINTN
*RegionAttributes
808 UINT32 PageAttributes
;
809 UINT32 PageTableIndex
;
810 UINT32 SectionDescriptor
;
811 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
814 // Initialize the arguments
817 // Obtain page table base
818 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
820 // Calculate index into first level translation table for start of modification
821 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
822 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
824 // Get the section at the given index
825 SectionDescriptor
= FirstLevelTable
[TableIndex
];
827 // If 'BaseAddress' belongs to the section then round it to the section boundary
828 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
829 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
831 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
832 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
834 // Otherwise, we round it to the page boundary
835 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
837 // Get the attribute at the page table level (Level 2)
838 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
840 // Calculate index into first level translation table for start of modification
841 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
842 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
844 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
845 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
846 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
849 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
850 // Get the section at the given index
851 SectionDescriptor
= FirstLevelTable
[TableIndex
];
853 // If the entry is a level-2 page table then we scan it to find the end of the region
854 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
855 // Extract the page table location from the descriptor
856 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
858 // Scan the page table to find the end of the region.
859 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
861 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
862 if (Status
== EFI_SUCCESS
) {
865 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
866 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
)) {
867 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
868 // If the attributes of the section differ from the one targeted then we exit the loop
871 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
874 // If we are on an invalid section then it means it is the end of our section.