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;
210 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
213 // Convert back PageAttributes into SectionAttributes
214 *NextSectionAttributes
=
215 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes
, 0) |
216 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (NextPageAttributes
);
223 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
228 EFI_PHYSICAL_ADDRESS NextRegionBase
;
229 UINT64 NextRegionLength
;
230 UINT32 NextSectionAttributes
;
231 UINT32 SectionAttributes
;
232 UINT64 GcdAttributes
;
233 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
234 UINTN NumberOfDescriptors
;
235 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
);
249 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
250 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
251 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
252 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
253 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
255 // obtain page table base
256 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
258 // Get the first region
259 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
261 // iterate through each 1MB descriptor
262 NextRegionBase
= NextRegionLength
= 0;
263 for (i
= 0; i
< TRANSLATION_TABLE_SECTION_COUNT
; i
++) {
264 if ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
265 // extract attributes (cacheability and permissions)
266 SectionAttributes
= FirstLevelTable
[i
] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
268 if (NextSectionAttributes
== 0) {
269 // start on a new region
270 NextRegionLength
= 0;
271 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
272 NextSectionAttributes
= SectionAttributes
;
273 } else if (SectionAttributes
!= NextSectionAttributes
) {
274 // Convert Section Attributes into GCD Attributes
275 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
276 ASSERT_EFI_ERROR (Status
);
278 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
279 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
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
;
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 (
301 &NextSectionAttributes
303 ASSERT_EFI_ERROR (Status
);
305 // We do not support yet 16MB sections
306 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
308 // start on a new region
309 if (NextSectionAttributes
!= 0) {
310 // Convert Section Attributes into GCD Attributes
311 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
312 ASSERT_EFI_ERROR (Status
);
314 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
315 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
317 NextRegionLength
= 0;
318 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
319 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
);
341 EfiAttributeToArmAttribute (
342 IN UINT64 EfiAttributes
345 UINT64 ArmAttributes
;
347 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
349 // Map to strongly ordered
350 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
354 // Map to normal non-cacheable
355 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
359 // Write through with no-allocate
360 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
364 // Write back (with allocate)
365 ArmAttributes
= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
370 ArmAttributes
= TT_DESCRIPTOR_SECTION_TYPE_FAULT
;
374 // Determine protection attributes
375 if ((EfiAttributes
& EFI_MEMORY_RO
) != 0) {
376 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
378 ArmAttributes
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
381 // Determine eXecute Never attribute
382 if ((EfiAttributes
& EFI_MEMORY_XP
) != 0) {
383 ArmAttributes
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
386 return ArmAttributes
;
390 GetMemoryRegionPage (
391 IN UINT32
*PageTable
,
392 IN OUT UINTN
*BaseAddress
,
393 OUT UINTN
*RegionLength
,
394 OUT UINTN
*RegionAttributes
397 UINT32 PageAttributes
;
399 UINT32 PageDescriptor
;
401 // Convert the section attributes into page attributes
402 PageAttributes
= ConvertSectionAttributesToPageAttributes (*RegionAttributes
, 0);
404 // Calculate index into first level translation table for start of modification
405 TableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
406 ASSERT (TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
408 // Go through the page table to find the end of the section
409 for ( ; TableIndex
< TRANSLATION_TABLE_PAGE_COUNT
; TableIndex
++) {
410 // Get the section at the given index
411 PageDescriptor
= PageTable
[TableIndex
];
413 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_FAULT
) {
414 // Case: End of the boundary of the region
416 } else if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_TYPE_PAGE
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
417 if ((PageDescriptor
& TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
) == PageAttributes
) {
418 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_PAGE_SIZE
;
420 // Case: End of the boundary of the region
424 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
430 return EFI_NOT_FOUND
;
435 IN OUT UINTN
*BaseAddress
,
436 OUT UINTN
*RegionLength
,
437 OUT UINTN
*RegionAttributes
442 UINT32 PageAttributes
;
443 UINT32 PageTableIndex
;
444 UINT32 SectionDescriptor
;
445 ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
448 // Initialize the arguments
451 // Obtain page table base
452 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
454 // Calculate index into first level translation table for start of modification
455 TableIndex
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
456 ASSERT (TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
);
458 // Get the section at the given index
459 SectionDescriptor
= FirstLevelTable
[TableIndex
];
460 if (!SectionDescriptor
) {
461 return EFI_NOT_FOUND
;
464 // If 'BaseAddress' belongs to the section then round it to the section boundary
465 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
466 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
468 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
469 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
471 // Otherwise, we round it to the page boundary
472 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
474 // Get the attribute at the page table level (Level 2)
475 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
477 // Calculate index into first level translation table for start of modification
478 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
479 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
481 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
482 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
483 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
486 for ( ; TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
487 // Get the section at the given index
488 SectionDescriptor
= FirstLevelTable
[TableIndex
];
490 // If the entry is a level-2 page table then we scan it to find the end of the region
491 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
492 // Extract the page table location from the descriptor
493 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
495 // Scan the page table to find the end of the region.
496 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
498 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
499 if (Status
== EFI_SUCCESS
) {
502 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
503 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
505 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
506 // If the attributes of the section differ from the one targeted then we exit the loop
509 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
512 // If we are on an invalid section then it means it is the end of our section.