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-2021, Arm Limited. All rights reserved.<BR>
6 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <Library/MemoryAllocationLib.h>
17 SectionToGcdAttributes (
18 IN UINT32 SectionAttributes
,
19 OUT UINT64
*GcdAttributes
24 // determine cacheability attributes
25 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) {
26 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
:
27 *GcdAttributes
|= EFI_MEMORY_UC
;
29 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE
:
30 *GcdAttributes
|= EFI_MEMORY_UC
;
32 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
33 *GcdAttributes
|= EFI_MEMORY_WT
;
35 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
36 *GcdAttributes
|= EFI_MEMORY_WB
;
38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
:
39 *GcdAttributes
|= EFI_MEMORY_WC
;
41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
:
42 *GcdAttributes
|= EFI_MEMORY_WB
;
44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
45 *GcdAttributes
|= EFI_MEMORY_UC
;
48 return EFI_UNSUPPORTED
;
51 // determine protection attributes
52 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_AP_MASK
) {
53 case TT_DESCRIPTOR_SECTION_AP_NO_NO
: // no read, no write
54 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
57 case TT_DESCRIPTOR_SECTION_AP_RW_NO
:
58 case TT_DESCRIPTOR_SECTION_AP_RW_RW
:
59 // normal read/write access, do not add additional attributes
62 // read only cases map to write-protect
63 case TT_DESCRIPTOR_SECTION_AP_RO_NO
:
64 case TT_DESCRIPTOR_SECTION_AP_RO_RO
:
65 *GcdAttributes
|= EFI_MEMORY_RO
;
69 return EFI_UNSUPPORTED
;
72 // now process eXectue Never attribute
73 if ((SectionAttributes
& TT_DESCRIPTOR_SECTION_XN_MASK
) != 0 ) {
74 *GcdAttributes
|= EFI_MEMORY_XP
;
82 IN UINT32 PageAttributes
,
83 OUT UINT64
*GcdAttributes
88 // determine cacheability attributes
89 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) {
90 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
:
91 *GcdAttributes
|= EFI_MEMORY_UC
;
93 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE
:
94 *GcdAttributes
|= EFI_MEMORY_UC
;
96 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
97 *GcdAttributes
|= EFI_MEMORY_WT
;
99 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
100 *GcdAttributes
|= EFI_MEMORY_WB
;
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
:
103 *GcdAttributes
|= EFI_MEMORY_WC
;
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
:
106 *GcdAttributes
|= EFI_MEMORY_WB
;
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
109 *GcdAttributes
|= EFI_MEMORY_UC
;
112 return EFI_UNSUPPORTED
;
115 // determine protection attributes
116 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_AP_MASK
) {
117 case TT_DESCRIPTOR_PAGE_AP_NO_NO
: // no read, no write
118 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
121 case TT_DESCRIPTOR_PAGE_AP_RW_NO
:
122 case TT_DESCRIPTOR_PAGE_AP_RW_RW
:
123 // normal read/write access, do not add additional attributes
126 // read only cases map to write-protect
127 case TT_DESCRIPTOR_PAGE_AP_RO_NO
:
128 case TT_DESCRIPTOR_PAGE_AP_RO_RO
:
129 *GcdAttributes
|= EFI_MEMORY_RO
;
133 return EFI_UNSUPPORTED
;
136 // now process eXectue Never attribute
137 if ((PageAttributes
& TT_DESCRIPTOR_PAGE_XN_MASK
) != 0 ) {
138 *GcdAttributes
|= EFI_MEMORY_XP
;
145 SyncCacheConfigPage (
146 IN UINT32 SectionIndex
,
147 IN UINT32 FirstLevelDescriptor
,
148 IN UINTN NumberOfDescriptors
,
149 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
,
150 IN OUT EFI_PHYSICAL_ADDRESS
*NextRegionBase
,
151 IN OUT UINT64
*NextRegionLength
,
152 IN OUT UINT32
*NextSectionAttributes
157 volatile ARM_PAGE_TABLE_ENTRY
*SecondLevelTable
;
158 UINT32 NextPageAttributes
;
159 UINT32 PageAttributes
;
161 UINT64 GcdAttributes
;
163 // Get the Base Address from FirstLevelDescriptor;
164 BaseAddress
= TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
166 // Convert SectionAttributes into PageAttributes
168 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes
,0) |
169 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes
);
171 // obtain page table base
172 SecondLevelTable
= (ARM_PAGE_TABLE_ENTRY
*)(FirstLevelDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
174 for (i
=0; i
< TRANSLATION_TABLE_PAGE_COUNT
; i
++) {
175 if ((SecondLevelTable
[i
] & TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
176 // extract attributes (cacheability and permissions)
177 PageAttributes
= SecondLevelTable
[i
] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
);
179 if (NextPageAttributes
== 0) {
180 // start on a new region
181 *NextRegionLength
= 0;
182 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
183 NextPageAttributes
= PageAttributes
;
184 } else if (PageAttributes
!= NextPageAttributes
) {
185 // Convert Section Attributes into GCD Attributes
186 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
187 ASSERT_EFI_ERROR (Status
);
189 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
190 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
192 // start on a new region
193 *NextRegionLength
= 0;
194 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
195 NextPageAttributes
= PageAttributes
;
197 } else if (NextPageAttributes
!= 0) {
198 // Convert Page Attributes into GCD Attributes
199 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
200 ASSERT_EFI_ERROR (Status
);
202 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
203 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
205 *NextRegionLength
= 0;
206 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
207 NextPageAttributes
= 0;
209 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
212 // Convert back PageAttributes into SectionAttributes
213 *NextSectionAttributes
=
214 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes
,0) |
215 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes
);
222 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
227 EFI_PHYSICAL_ADDRESS NextRegionBase
;
228 UINT64 NextRegionLength
;
229 UINT32 NextSectionAttributes
;
230 UINT32 SectionAttributes
;
231 UINT64 GcdAttributes
;
232 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
233 UINTN NumberOfDescriptors
;
234 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
237 DEBUG ((DEBUG_PAGE
, "SyncCacheConfig()\n"));
239 // This code assumes MMU is enabled and filed with section translations
240 ASSERT (ArmMmuEnabled ());
243 // Get the memory space map from GCD
245 MemorySpaceMap
= NULL
;
246 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
247 ASSERT_EFI_ERROR (Status
);
250 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
251 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
252 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
253 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
254 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
256 // obtain page table base
257 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
259 // Get the first region
260 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
262 // iterate through each 1MB descriptor
263 NextRegionBase
= NextRegionLength
= 0;
264 for (i
=0; i
< TRANSLATION_TABLE_SECTION_COUNT
; i
++) {
265 if ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
266 // extract attributes (cacheability and permissions)
267 SectionAttributes
= FirstLevelTable
[i
] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
269 if (NextSectionAttributes
== 0) {
270 // start on a new region
271 NextRegionLength
= 0;
272 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
273 NextSectionAttributes
= SectionAttributes
;
274 } else if (SectionAttributes
!= NextSectionAttributes
) {
275 // Convert Section Attributes into GCD Attributes
276 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
277 ASSERT_EFI_ERROR (Status
);
279 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
280 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
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
;
287 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
288 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable
[i
])) {
289 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from
290 // bits that are actually part of the pagetable address. We clear it out to zero so that
291 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the
292 // section attributes into page attributes
293 NextSectionAttributes
= 0;
294 Status
= SyncCacheConfigPage (
295 i
,FirstLevelTable
[i
],
296 NumberOfDescriptors
, MemorySpaceMap
,
297 &NextRegionBase
,&NextRegionLength
,&NextSectionAttributes
);
298 ASSERT_EFI_ERROR (Status
);
300 // We do not support yet 16MB sections
301 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
303 // start on a new region
304 if (NextSectionAttributes
!= 0) {
305 // Convert Section Attributes into GCD Attributes
306 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
307 ASSERT_EFI_ERROR (Status
);
309 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
310 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
312 NextRegionLength
= 0;
313 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
314 NextSectionAttributes
= 0;
316 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
318 } // section entry loop
320 if (NextSectionAttributes
!= 0) {
321 // Convert Section Attributes into GCD Attributes
322 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
323 ASSERT_EFI_ERROR (Status
);
325 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
326 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
329 FreePool (MemorySpaceMap
);
335 EfiAttributeToArmAttribute (
336 IN UINT64 EfiAttributes
339 UINT64 ArmAttributes
;
341 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
343 // Map to strongly ordered
344 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
348 // Map to normal non-cacheable
349 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
353 // Write through with no-allocate
354 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
358 // Write back (with allocate)
359 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
364 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
368 // Determine protection attributes
369 if ((EfiAttributes
& EFI_MEMORY_RO
) != 0) {
370 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
372 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
375 // Determine eXecute Never attribute
376 if ((EfiAttributes
& EFI_MEMORY_XP
) != 0) {
377 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
380 return ArmAttributes
;
384 GetMemoryRegionPage (
385 IN UINT32
*PageTable
,
386 IN OUT UINTN
*BaseAddress
,
387 OUT UINTN
*RegionLength
,
388 OUT UINTN
*RegionAttributes
391 UINT32 PageAttributes
;
393 UINT32 PageDescriptor
;
395 // Convert the section attributes into page attributes
396 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
398 // Calculate index into first level translation table for start of modification
399 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
400 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
402 // Go through the page table to find the end of the section
403 for (; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
404 // Get the section at the given index
405 PageDescriptor
= PageTable
[TableIndex
];
407 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
408 // Case: End of the boundary of the region
410 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
411 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
412 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
414 // Case: End of the boundary of the region
418 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
424 return EFI_NOT_FOUND
;
429 IN OUT UINTN
*BaseAddress
,
430 OUT UINTN
*RegionLength
,
431 OUT UINTN
*RegionAttributes
436 UINT32 PageAttributes
;
437 UINT32 PageTableIndex
;
438 UINT32 SectionDescriptor
;
439 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
442 // Initialize the arguments
445 // Obtain page table base
446 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
448 // Calculate index into first level translation table for start of modification
449 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
450 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
452 // Get the section at the given index
453 SectionDescriptor
= FirstLevelTable
[TableIndex
];
454 if (!SectionDescriptor
) {
455 return EFI_NOT_FOUND
;
458 // If 'BaseAddress' belongs to the section then round it to the section boundary
459 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
460 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
462 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
463 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
465 // Otherwise, we round it to the page boundary
466 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
468 // Get the attribute at the page table level (Level 2)
469 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
471 // Calculate index into first level translation table for start of modification
472 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
473 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
475 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
476 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
477 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
480 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
481 // Get the section at the given index
482 SectionDescriptor
= FirstLevelTable
[TableIndex
];
484 // If the entry is a level-2 page table then we scan it to find the end of the region
485 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
486 // Extract the page table location from the descriptor
487 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
489 // Scan the page table to find the end of the region.
490 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
492 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
493 if (Status
== EFI_SUCCESS
) {
496 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
497 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
)) {
498 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
499 // If the attributes of the section differ from the one targeted then we exit the loop
502 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
505 // If we are on an invalid section then it means it is the end of our section.