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) 2011-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>
16 #define INVALID_ENTRY ((UINT32)~0)
19 #define BITS_PER_LEVEL 9
23 GetRootTranslationTableInfo (
25 OUT UINTN
*RootTableLevel
,
26 OUT UINTN
*RootTableEntryCount
29 *RootTableLevel
= (T0SZ
- MIN_T0SZ
) / BITS_PER_LEVEL
;
30 *RootTableEntryCount
= TT_ENTRY_COUNT
>> (T0SZ
- MIN_T0SZ
) % BITS_PER_LEVEL
;
35 PageAttributeToGcdAttribute (
36 IN UINT64 PageAttributes
41 switch (PageAttributes
& TT_ATTR_INDX_MASK
) {
42 case TT_ATTR_INDX_DEVICE_MEMORY
:
43 GcdAttributes
= EFI_MEMORY_UC
;
45 case TT_ATTR_INDX_MEMORY_NON_CACHEABLE
:
46 GcdAttributes
= EFI_MEMORY_WC
;
48 case TT_ATTR_INDX_MEMORY_WRITE_THROUGH
:
49 GcdAttributes
= EFI_MEMORY_WT
;
51 case TT_ATTR_INDX_MEMORY_WRITE_BACK
:
52 GcdAttributes
= EFI_MEMORY_WB
;
57 "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n",
61 // The Global Coherency Domain (GCD) value is defined as a bit set.
62 // Returning 0 means no attribute has been set.
66 // Determine protection attributes
67 if (((PageAttributes
& TT_AP_MASK
) == TT_AP_NO_RO
) ||
68 ((PageAttributes
& TT_AP_MASK
) == TT_AP_RO_RO
))
70 // Read only cases map to write-protect
71 GcdAttributes
|= EFI_MEMORY_RO
;
74 // Process eXecute Never attribute
75 if ((PageAttributes
& (TT_PXN_MASK
| TT_UXN_MASK
)) != 0) {
76 GcdAttributes
|= EFI_MEMORY_XP
;
84 GetFirstPageAttribute (
85 IN UINT64
*FirstLevelTableAddress
,
91 // Get the first entry of the table
92 FirstEntry
= *FirstLevelTableAddress
;
94 if ((TableLevel
!= 3) && ((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_TABLE_ENTRY
)) {
95 // Only valid for Levels 0, 1 and 2
97 // Get the attribute of the subsequent table
98 return GetFirstPageAttribute ((UINT64
*)(FirstEntry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
), TableLevel
+ 1);
99 } else if (((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_BLOCK_ENTRY
) ||
100 ((TableLevel
== 3) && ((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_BLOCK_ENTRY_LEVEL3
)))
102 return FirstEntry
& TT_ATTR_INDX_MASK
;
104 return INVALID_ENTRY
;
110 GetNextEntryAttribute (
111 IN UINT64
*TableAddress
,
114 IN UINT64 BaseAddress
,
115 IN OUT UINT32
*PrevEntryAttribute
,
116 IN OUT UINT64
*StartGcdRegion
121 UINT32 EntryAttribute
;
124 UINTN NumberOfDescriptors
;
125 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
127 // Get the memory space map from GCD
128 MemorySpaceMap
= NULL
;
129 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
130 ASSERT_EFI_ERROR (Status
);
132 // We cannot get more than 3-level page table
133 ASSERT (TableLevel
<= 3);
135 // While the top level table might not contain TT_ENTRY_COUNT entries;
136 // the subsequent ones should be filled up
137 for (Index
= 0; Index
< EntryCount
; Index
++) {
138 Entry
= TableAddress
[Index
];
139 EntryType
= Entry
& TT_TYPE_MASK
;
140 EntryAttribute
= Entry
& TT_ATTR_INDX_MASK
;
142 // If Entry is a Table Descriptor type entry then go through the sub-level table
143 if ((EntryType
== TT_TYPE_BLOCK_ENTRY
) ||
144 ((TableLevel
== 3) && (EntryType
== TT_TYPE_BLOCK_ENTRY_LEVEL3
)))
146 if ((*PrevEntryAttribute
== INVALID_ENTRY
) || (EntryAttribute
!= *PrevEntryAttribute
)) {
147 if (*PrevEntryAttribute
!= INVALID_ENTRY
) {
148 // Update GCD with the last region
149 SetGcdMemorySpaceAttributes (
153 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL (TableLevel
))) - *StartGcdRegion
,
154 PageAttributeToGcdAttribute (*PrevEntryAttribute
)
158 // Start of the new region
159 *StartGcdRegion
= BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL (TableLevel
));
160 *PrevEntryAttribute
= EntryAttribute
;
164 } else if (EntryType
== TT_TYPE_TABLE_ENTRY
) {
165 // Table Entry type is only valid for Level 0, 1, 2
166 ASSERT (TableLevel
< 3);
168 // Increase the level number and scan the sub-level table
169 GetNextEntryAttribute (
170 (UINT64
*)(Entry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
),
173 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL (TableLevel
))),
178 if (*PrevEntryAttribute
!= INVALID_ENTRY
) {
179 // Update GCD with the last region
180 SetGcdMemorySpaceAttributes (
184 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL (TableLevel
))) - *StartGcdRegion
,
185 PageAttributeToGcdAttribute (*PrevEntryAttribute
)
188 // Start of the new region
189 *StartGcdRegion
= BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL (TableLevel
));
190 *PrevEntryAttribute
= INVALID_ENTRY
;
195 FreePool (MemorySpaceMap
);
197 return BaseAddress
+ (EntryCount
* TT_ADDRESS_AT_LEVEL (TableLevel
));
202 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
206 UINT32 PageAttribute
;
207 UINT64
*FirstLevelTableAddress
;
210 UINTN NumberOfDescriptors
;
211 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
214 UINT64 BaseAddressGcdRegion
;
215 UINT64 EndAddressGcdRegion
;
217 // This code assumes MMU is enabled and filed with section translations
218 ASSERT (ArmMmuEnabled ());
221 // Get the memory space map from GCD
223 MemorySpaceMap
= NULL
;
224 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
225 ASSERT_EFI_ERROR (Status
);
227 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
228 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
229 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
230 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
231 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
233 // Obtain page table base
234 FirstLevelTableAddress
= (UINT64
*)(ArmGetTTBR0BaseAddress ());
236 // Get Translation Control Register value
238 // Get Address Region Size
239 T0SZ
= Tcr
& TCR_T0SZ_MASK
;
241 // Get the level of the first table for the indicated Address Region Size
242 GetRootTranslationTableInfo (T0SZ
, &TableLevel
, &TableCount
);
244 // First Attribute of the Page Tables
245 PageAttribute
= GetFirstPageAttribute (FirstLevelTableAddress
, TableLevel
);
247 // We scan from the start of the memory map (ie: at the address 0x0)
248 BaseAddressGcdRegion
= 0x0;
249 EndAddressGcdRegion
= GetNextEntryAttribute (
250 FirstLevelTableAddress
,
253 BaseAddressGcdRegion
,
255 &BaseAddressGcdRegion
258 // Update GCD with the last region if valid
259 if (PageAttribute
!= INVALID_ENTRY
) {
260 SetGcdMemorySpaceAttributes (
263 BaseAddressGcdRegion
,
264 EndAddressGcdRegion
- BaseAddressGcdRegion
,
265 PageAttributeToGcdAttribute (PageAttribute
)
269 FreePool (MemorySpaceMap
);
275 EfiAttributeToArmAttribute (
276 IN UINT64 EfiAttributes
279 UINT64 ArmAttributes
;
281 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
283 if (ArmReadCurrentEL () == AARCH64_EL2
) {
284 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
| TT_XN_MASK
;
286 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
| TT_UXN_MASK
| TT_PXN_MASK
;
291 ArmAttributes
= TT_ATTR_INDX_MEMORY_NON_CACHEABLE
;
294 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_THROUGH
| TT_SH_INNER_SHAREABLE
;
297 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_BACK
| TT_SH_INNER_SHAREABLE
;
300 ArmAttributes
= TT_ATTR_INDX_MASK
;
303 // Set the access flag to match the block attributes
304 ArmAttributes
|= TT_AF
;
306 // Determine protection attributes
307 if ((EfiAttributes
& EFI_MEMORY_RO
) != 0) {
308 ArmAttributes
|= TT_AP_RO_RO
;
311 // Process eXecute Never attribute
312 if ((EfiAttributes
& EFI_MEMORY_XP
) != 0) {
313 ArmAttributes
|= TT_PXN_MASK
;
316 return ArmAttributes
;
319 // This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.
320 // And then the function will identify the size of the region that has the same page table attribute.
323 IN UINT64
*TranslationTable
,
325 IN UINT64
*LastBlockEntry
,
326 IN OUT UINTN
*BaseAddress
,
327 OUT UINTN
*RegionLength
,
328 OUT UINTN
*RegionAttributes
332 UINT64
*NextTranslationTable
;
334 UINT64 BlockEntryType
;
337 if (TableLevel
!= 3) {
338 BlockEntryType
= TT_TYPE_BLOCK_ENTRY
;
340 BlockEntryType
= TT_TYPE_BLOCK_ENTRY_LEVEL3
;
343 // Find the block entry linked to the Base Address
344 BlockEntry
= (UINT64
*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable
, TableLevel
, *BaseAddress
);
345 EntryType
= *BlockEntry
& TT_TYPE_MASK
;
347 if ((TableLevel
< 3) && (EntryType
== TT_TYPE_TABLE_ENTRY
)) {
348 NextTranslationTable
= (UINT64
*)(*BlockEntry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
);
350 // The entry is a page table, so we go to the next level
351 Status
= GetMemoryRegionRec (
352 NextTranslationTable
, // Address of the next level page table
353 TableLevel
+ 1, // Next Page Table level
354 (UINTN
*)TT_LAST_BLOCK_ADDRESS (NextTranslationTable
, TT_ENTRY_COUNT
),
360 // In case of 'Success', it means the end of the block region has been found into the upper
361 // level translation table
362 if (!EFI_ERROR (Status
)) {
366 // Now we processed the table move to the next entry
368 } else if (EntryType
== BlockEntryType
) {
369 // We have found the BlockEntry attached to the address. We save its start address (the start
370 // address might be before the 'BaseAddress') and attributes
371 *BaseAddress
= *BaseAddress
& ~(TT_ADDRESS_AT_LEVEL (TableLevel
) - 1);
373 *RegionAttributes
= *BlockEntry
& TT_ATTRIBUTES_MASK
;
375 // We have an 'Invalid' entry
376 return EFI_UNSUPPORTED
;
379 while (BlockEntry
<= LastBlockEntry
) {
380 if ((*BlockEntry
& TT_ATTRIBUTES_MASK
) == *RegionAttributes
) {
381 *RegionLength
= *RegionLength
+ TT_BLOCK_ENTRY_SIZE_AT_LEVEL (TableLevel
);
383 // In case we have found the end of the region we return success
390 // If we have reached the end of the TranslationTable and we have not found the end of the region then
391 // we return EFI_NOT_FOUND.
392 // The caller will continue to look for the memory region at its level
393 return EFI_NOT_FOUND
;
398 IN OUT UINTN
*BaseAddress
,
399 OUT UINTN
*RegionLength
,
400 OUT UINTN
*RegionAttributes
404 UINT64
*TranslationTable
;
409 ASSERT ((BaseAddress
!= NULL
) && (RegionLength
!= NULL
) && (RegionAttributes
!= NULL
));
411 TranslationTable
= ArmGetTTBR0BaseAddress ();
413 T0SZ
= ArmGetTCR () & TCR_T0SZ_MASK
;
414 // Get the Table info from T0SZ
415 GetRootTranslationTableInfo (T0SZ
, &TableLevel
, &EntryCount
);
417 Status
= GetMemoryRegionRec (
420 (UINTN
*)TT_LAST_BLOCK_ADDRESS (TranslationTable
, EntryCount
),
426 // If the region continues up to the end of the root table then GetMemoryRegionRec()
427 // will return EFI_NOT_FOUND
428 if (Status
== EFI_NOT_FOUND
) {