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 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from
301 // bits that are actually part of the pagetable address. We clear it out to zero so that
302 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the
303 // section attributes into page attributes
304 NextSectionAttributes
= 0;
305 Status
= SyncCacheConfigPage (
306 i
,FirstLevelTable
[i
],
307 NumberOfDescriptors
, MemorySpaceMap
,
308 &NextRegionBase
,&NextRegionLength
,&NextSectionAttributes
);
309 ASSERT_EFI_ERROR (Status
);
311 // We do not support yet 16MB sections
312 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
314 // start on a new region
315 if (NextSectionAttributes
!= 0) {
316 // Convert Section Attributes into GCD Attributes
317 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
318 ASSERT_EFI_ERROR (Status
);
320 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
321 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
323 NextRegionLength
= 0;
324 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
325 NextSectionAttributes
= 0;
327 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
329 } // section entry loop
331 if (NextSectionAttributes
!= 0) {
332 // Convert Section Attributes into GCD Attributes
333 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
334 ASSERT_EFI_ERROR (Status
);
336 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
337 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
340 FreePool (MemorySpaceMap
);
349 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
351 IN UINT64 Attributes
,
352 IN EFI_PHYSICAL_ADDRESS VirtualMask
358 UINT32 FirstLevelIdx
;
360 UINT32 NumPageEntries
;
363 UINT32 PageTableIndex
;
364 UINT32 PageTableEntry
;
365 UINT32 CurrentPageTableEntry
;
368 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
369 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
371 Status
= EFI_SUCCESS
;
373 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
374 // EntryValue: values at bit positions specified by EntryMask
375 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
;
376 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
377 // Although the PI spec is unclear on this the GCD guarantees that only
378 // one Attribute bit is set at a time, so we can safely use a switch statement
379 switch (Attributes
) {
381 // modify cacheability attributes
382 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
383 // map to strongly ordered
384 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
388 // modify cacheability attributes
389 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
390 // map to normal non-cachable
391 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
395 // modify cacheability attributes
396 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
397 // write through with no-allocate
398 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
402 // modify cacheability attributes
403 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
404 // write back (with allocate)
405 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
411 // cannot be implemented UEFI definition unclear for ARM
412 // Cause a page fault if these ranges are accessed.
413 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_FAULT
;
414 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
418 return EFI_UNSUPPORTED
;
421 // Obtain page table base
422 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
424 // Calculate number of 4KB page table entries to change
425 NumPageEntries
= Length
/ TT_DESCRIPTOR_PAGE_SIZE
;
427 // Iterate for the number of 4KB pages to change
429 for(p
= 0; p
< NumPageEntries
; p
++) {
430 // Calculate index into first level translation table for page table value
432 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
433 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
435 // Read the descriptor from the first level page table
436 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
438 // Does this descriptor need to be converted from section entry to 4K pages?
439 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor
)) {
440 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
441 if (EFI_ERROR(Status
)) {
446 // Re-read descriptor
447 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
450 // Obtain page table base address
451 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor
);
453 // Calculate index into the page table
454 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
455 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
458 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
460 // Mask off appropriate fields
461 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
463 // Mask in new attributes and/or permissions
464 PageTableEntry
|= EntryValue
;
466 if (VirtualMask
!= 0) {
467 // Make this virtual address point at a physical page
468 PageTableEntry
&= ~VirtualMask
;
471 if (CurrentPageTableEntry
!= PageTableEntry
) {
472 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
473 if ((CurrentPageTableEntry
& TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK
) {
474 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
475 // Note assumes switch(Attributes), not ARMv7 possibilities
476 WriteBackInvalidateDataCacheRange (Mva
, TT_DESCRIPTOR_PAGE_SIZE
);
479 // Only need to update if we are changing the entry
480 PageTable
[PageTableIndex
] = PageTableEntry
;
481 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
484 Status
= EFI_SUCCESS
;
485 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
487 } // End first level translation table loop
495 UpdateSectionEntries (
496 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
498 IN UINT64 Attributes
,
499 IN EFI_PHYSICAL_ADDRESS VirtualMask
502 EFI_STATUS Status
= EFI_SUCCESS
;
505 UINT32 FirstLevelIdx
;
508 UINT32 CurrentDescriptor
;
511 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
513 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
514 // EntryValue: values at bit positions specified by EntryMask
516 // Make sure we handle a section range that is unmapped
517 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
;
518 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
520 // Although the PI spec is unclear on this the GCD guarantees that only
521 // one Attribute bit is set at a time, so we can safely use a switch statement
524 // modify cacheability attributes
525 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
526 // map to strongly ordered
527 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
531 // modify cacheability attributes
532 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
533 // map to normal non-cachable
534 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
538 // modify cacheability attributes
539 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
540 // write through with no-allocate
541 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
545 // modify cacheability attributes
546 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
547 // write back (with allocate)
548 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
555 // cannot be implemented UEFI definition unclear for ARM
556 // Cause a page fault if these ranges are accessed.
557 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
558 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress
, Attributes
));
563 return EFI_UNSUPPORTED
;
566 // obtain page table base
567 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
569 // calculate index into first level translation table for start of modification
570 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
571 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
573 // calculate number of 1MB first level entries this applies to
574 NumSections
= Length
/ TT_DESCRIPTOR_SECTION_SIZE
;
576 // iterate through each descriptor
577 for(i
=0; i
<NumSections
; i
++) {
578 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
580 // has this descriptor already been coverted to pages?
581 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
582 // forward this 1MB range to page table function instead
583 Status
= UpdatePageEntries ((FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
, TT_DESCRIPTOR_SECTION_SIZE
, Attributes
, VirtualMask
);
585 // still a section entry
587 // mask off appropriate fields
588 Descriptor
= CurrentDescriptor
& ~EntryMask
;
590 // mask in new attributes and/or permissions
591 Descriptor
|= EntryValue
;
592 if (VirtualMask
!= 0) {
593 Descriptor
&= ~VirtualMask
;
596 if (CurrentDescriptor
!= Descriptor
) {
597 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelTable
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
598 if ((CurrentDescriptor
& TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK
) {
599 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
600 // Note assumes switch(Attributes), not ARMv7 possabilities
601 WriteBackInvalidateDataCacheRange (Mva
, SIZE_1MB
);
604 // Only need to update if we are changing the descriptor
605 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
606 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
609 Status
= EFI_SUCCESS
;
617 ConvertSectionToPages (
618 IN EFI_PHYSICAL_ADDRESS BaseAddress
622 EFI_PHYSICAL_ADDRESS PageTableAddr
;
623 UINT32 FirstLevelIdx
;
624 UINT32 SectionDescriptor
;
625 UINT32 PageTableDescriptor
;
626 UINT32 PageDescriptor
;
629 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
630 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
632 DEBUG ((EFI_D_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
634 // Obtain page table base
635 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
637 // Calculate index into first level translation table for start of modification
638 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
639 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
641 // Get section attributes and convert to page attributes
642 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
643 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
645 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
646 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
, 1, &PageTableAddr
);
647 if (EFI_ERROR(Status
)) {
651 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)(UINTN
)PageTableAddr
;
653 // Write the page table entries out
654 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
655 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
658 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
659 WriteBackInvalidateDataCacheRange ((VOID
*)(UINTN
)PageTableAddr
, TT_DESCRIPTOR_PAGE_SIZE
);
661 // Formulate page table entry, Domain=0, NS=0
662 PageTableDescriptor
= (((UINTN
)PageTableAddr
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
664 // Write the page table entry out, replacing section entry
665 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
673 SetMemoryAttributes (
674 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
676 IN UINT64 Attributes
,
677 IN EFI_PHYSICAL_ADDRESS VirtualMask
682 if(((BaseAddress
& 0xFFFFF) == 0) && ((Length
& 0xFFFFF) == 0)) {
683 // Is the base and length a multiple of 1 MB?
684 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
685 Status
= UpdateSectionEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
687 // Base and/or length is not a multiple of 1 MB
688 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN
)BaseAddress
, (UINTN
)Length
, Attributes
));
689 Status
= UpdatePageEntries (BaseAddress
, Length
, Attributes
, VirtualMask
);
692 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
693 // flush and invalidate pages
694 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
695 ArmCleanInvalidateDataCache ();
697 ArmInvalidateInstructionCache ();
699 // Invalidate all TLB entries so changes are synced
706 EfiAttributeToArmAttribute (
707 IN UINT64 EfiAttributes
710 UINT64 ArmAttributes
;
712 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
714 // Map to strongly ordered
715 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
719 // Map to normal non-cachable
720 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
724 // Write through with no-allocate
725 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
729 // Write back (with allocate)
730 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
738 // Cannot be implemented UEFI definition unclear for ARM
739 // Cause a page fault if these ranges are accessed.
740 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
741 DEBUG ((EFI_D_PAGE
, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes
));
745 // Determine protection attributes
746 if (EfiAttributes
& EFI_MEMORY_WP
) {
747 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
749 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
752 // Determine eXecute Never attribute
753 if (EfiAttributes
& EFI_MEMORY_XP
) {
754 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
757 return ArmAttributes
;
761 GetMemoryRegionPage (
762 IN UINT32
*PageTable
,
763 IN OUT UINTN
*BaseAddress
,
764 OUT UINTN
*RegionLength
,
765 OUT UINTN
*RegionAttributes
768 UINT32 PageAttributes
;
770 UINT32 PageDescriptor
;
772 // Convert the section attributes into page attributes
773 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
775 // Calculate index into first level translation table for start of modification
776 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
777 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
779 // Go through the page table to find the end of the section
780 for (; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
781 // Get the section at the given index
782 PageDescriptor
= PageTable
[TableIndex
];
784 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
785 // Case: End of the boundary of the region
787 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
788 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
789 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
791 // Case: End of the boundary of the region
795 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
801 return EFI_NOT_FOUND
;
806 IN OUT UINTN
*BaseAddress
,
807 OUT UINTN
*RegionLength
,
808 OUT UINTN
*RegionAttributes
813 UINT32 PageAttributes
;
814 UINT32 PageTableIndex
;
815 UINT32 SectionDescriptor
;
816 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
819 // Initialize the arguments
822 // Obtain page table base
823 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
825 // Calculate index into first level translation table for start of modification
826 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
827 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
829 // Get the section at the given index
830 SectionDescriptor
= FirstLevelTable
[TableIndex
];
832 // If 'BaseAddress' belongs to the section then round it to the section boundary
833 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
834 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
836 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
837 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
839 // Otherwise, we round it to the page boundary
840 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
842 // Get the attribute at the page table level (Level 2)
843 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
845 // Calculate index into first level translation table for start of modification
846 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
847 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
849 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
850 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
851 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
854 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
855 // Get the section at the given index
856 SectionDescriptor
= FirstLevelTable
[TableIndex
];
858 // If the entry is a level-2 page table then we scan it to find the end of the region
859 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
860 // Extract the page table location from the descriptor
861 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
863 // Scan the page table to find the end of the region.
864 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
866 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
867 if (Status
== EFI_SUCCESS
) {
870 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
871 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
)) {
872 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
873 // If the attributes of the section differ from the one targeted then we exit the loop
876 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
879 // If we are on an invalid section then it means it is the end of our section.