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-2013, ARM Ltd. All rights reserved.<BR>
6 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 #include <Library/MemoryAllocationLib.h>
22 #define TT_ATTR_INDX_INVALID ((UINT32)~0)
26 GetFirstPageAttribute (
27 IN UINT64
*FirstLevelTableAddress
,
33 // Get the first entry of the table
34 FirstEntry
= *FirstLevelTableAddress
;
36 if ((TableLevel
!= 3) && (FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_TABLE_ENTRY
) {
37 // Only valid for Levels 0, 1 and 2
39 // Get the attribute of the subsequent table
40 return GetFirstPageAttribute ((UINT64
*)(FirstEntry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
), TableLevel
+ 1);
41 } else if (((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_BLOCK_ENTRY
) ||
42 ((TableLevel
== 3) && ((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_BLOCK_ENTRY_LEVEL3
)))
44 return FirstEntry
& TT_ATTR_INDX_MASK
;
46 return TT_ATTR_INDX_INVALID
;
52 GetNextEntryAttribute (
53 IN UINT64
*TableAddress
,
56 IN UINT64 BaseAddress
,
57 IN OUT UINT32
*PrevEntryAttribute
,
58 IN OUT UINT64
*StartGcdRegion
63 UINT32 EntryAttribute
;
66 UINTN NumberOfDescriptors
;
67 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
69 // Get the memory space map from GCD
70 MemorySpaceMap
= NULL
;
71 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
72 ASSERT_EFI_ERROR (Status
);
74 // We cannot get more than 3-level page table
75 ASSERT (TableLevel
<= 3);
77 // While the top level table might not contain TT_ENTRY_COUNT entries;
78 // the subsequent ones should be filled up
79 for (Index
= 0; Index
< EntryCount
; Index
++) {
80 Entry
= TableAddress
[Index
];
81 EntryType
= Entry
& TT_TYPE_MASK
;
82 EntryAttribute
= Entry
& TT_ATTR_INDX_MASK
;
84 // If Entry is a Table Descriptor type entry then go through the sub-level table
85 if ((EntryType
== TT_TYPE_BLOCK_ENTRY
) ||
86 ((TableLevel
== 3) && (EntryType
== TT_TYPE_BLOCK_ENTRY_LEVEL3
))) {
87 if ((*PrevEntryAttribute
== TT_ATTR_INDX_INVALID
) || (EntryAttribute
!= *PrevEntryAttribute
)) {
88 if (*PrevEntryAttribute
!= TT_ATTR_INDX_INVALID
) {
89 // Update GCD with the last region
90 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
,
92 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL(TableLevel
))) - *StartGcdRegion
,
93 PageAttributeToGcdAttribute (*PrevEntryAttribute
));
96 // Start of the new region
97 *StartGcdRegion
= BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL(TableLevel
));
98 *PrevEntryAttribute
= EntryAttribute
;
102 } else if (EntryType
== TT_TYPE_TABLE_ENTRY
) {
103 // Table Entry type is only valid for Level 0, 1, 2
104 ASSERT (TableLevel
< 3);
106 // Increase the level number and scan the sub-level table
107 GetNextEntryAttribute ((UINT64
*)(Entry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
),
108 TT_ENTRY_COUNT
, TableLevel
+ 1,
109 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL(TableLevel
))),
110 PrevEntryAttribute
, StartGcdRegion
);
112 if (*PrevEntryAttribute
!= TT_ATTR_INDX_INVALID
) {
113 // Update GCD with the last region
114 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
,
116 (BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL(TableLevel
))) - *StartGcdRegion
,
117 PageAttributeToGcdAttribute (*PrevEntryAttribute
));
119 // Start of the new region
120 *StartGcdRegion
= BaseAddress
+ (Index
* TT_ADDRESS_AT_LEVEL(TableLevel
));
121 *PrevEntryAttribute
= TT_ATTR_INDX_INVALID
;
126 FreePool (MemorySpaceMap
);
128 return BaseAddress
+ (EntryCount
* TT_ADDRESS_AT_LEVEL(TableLevel
));
133 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
137 UINT32 PageAttribute
= 0;
138 UINT64
*FirstLevelTableAddress
;
141 UINTN NumberOfDescriptors
;
142 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
145 UINT64 BaseAddressGcdRegion
;
146 UINT64 EndAddressGcdRegion
;
148 // This code assumes MMU is enabled and filed with section translations
149 ASSERT (ArmMmuEnabled ());
152 // Get the memory space map from GCD
154 MemorySpaceMap
= NULL
;
155 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
156 ASSERT_EFI_ERROR (Status
);
158 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
159 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
160 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
161 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
162 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
164 // Obtain page table base
165 FirstLevelTableAddress
= (UINT64
*)(ArmGetTTBR0BaseAddress ());
167 // Get Translation Control Register value
169 // Get Address Region Size
170 T0SZ
= Tcr
& TCR_T0SZ_MASK
;
172 // Get the level of the first table for the indicated Address Region Size
173 GetRootTranslationTableInfo (T0SZ
, &TableLevel
, &TableCount
);
175 // First Attribute of the Page Tables
176 PageAttribute
= GetFirstPageAttribute (FirstLevelTableAddress
, TableLevel
);
178 // We scan from the start of the memory map (ie: at the address 0x0)
179 BaseAddressGcdRegion
= 0x0;
180 EndAddressGcdRegion
= GetNextEntryAttribute (FirstLevelTableAddress
,
181 TableCount
, TableLevel
,
182 BaseAddressGcdRegion
,
183 &PageAttribute
, &BaseAddressGcdRegion
);
185 // Update GCD with the last region if valid
186 if (PageAttribute
!= TT_ATTR_INDX_INVALID
) {
187 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
,
188 BaseAddressGcdRegion
,
189 EndAddressGcdRegion
- BaseAddressGcdRegion
,
190 PageAttributeToGcdAttribute (PageAttribute
));
193 FreePool (MemorySpaceMap
);
199 EfiAttributeToArmAttribute (
200 IN UINT64 EfiAttributes
203 UINT64 ArmAttributes
;
205 switch (EfiAttributes
& EFI_MEMORY_CACHETYPE_MASK
) {
207 if (ArmReadCurrentEL () == AARCH64_EL2
) {
208 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
| TT_XN_MASK
;
210 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
| TT_UXN_MASK
| TT_PXN_MASK
;
214 ArmAttributes
= TT_ATTR_INDX_MEMORY_NON_CACHEABLE
;
217 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_THROUGH
| TT_SH_INNER_SHAREABLE
;
220 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_BACK
| TT_SH_INNER_SHAREABLE
;
223 ArmAttributes
= TT_ATTR_INDX_MASK
;
226 // Set the access flag to match the block attributes
227 ArmAttributes
|= TT_AF
;
229 // Determine protection attributes
230 if (EfiAttributes
& EFI_MEMORY_RO
) {
231 ArmAttributes
|= TT_AP_RO_RO
;
234 // Process eXecute Never attribute
235 if (EfiAttributes
& EFI_MEMORY_XP
) {
236 ArmAttributes
|= TT_PXN_MASK
;
239 return ArmAttributes
;
242 // This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.
243 // And then the function will identify the size of the region that has the same page table attribute.
246 IN UINT64
*TranslationTable
,
248 IN UINT64
*LastBlockEntry
,
249 IN OUT UINTN
*BaseAddress
,
250 OUT UINTN
*RegionLength
,
251 OUT UINTN
*RegionAttributes
255 UINT64
*NextTranslationTable
;
257 UINT64 BlockEntryType
;
260 if (TableLevel
!= 3) {
261 BlockEntryType
= TT_TYPE_BLOCK_ENTRY
;
263 BlockEntryType
= TT_TYPE_BLOCK_ENTRY_LEVEL3
;
266 // Find the block entry linked to the Base Address
267 BlockEntry
= (UINT64
*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable
, TableLevel
, *BaseAddress
);
268 EntryType
= *BlockEntry
& TT_TYPE_MASK
;
270 if ((TableLevel
< 3) && (EntryType
== TT_TYPE_TABLE_ENTRY
)) {
271 NextTranslationTable
= (UINT64
*)(*BlockEntry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
);
273 // The entry is a page table, so we go to the next level
274 Status
= GetMemoryRegionRec (
275 NextTranslationTable
, // Address of the next level page table
276 TableLevel
+ 1, // Next Page Table level
277 (UINTN
*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable
, TT_ENTRY_COUNT
),
278 BaseAddress
, RegionLength
, RegionAttributes
);
280 // In case of 'Success', it means the end of the block region has been found into the upper
281 // level translation table
282 if (!EFI_ERROR(Status
)) {
286 // Now we processed the table move to the next entry
288 } else if (EntryType
== BlockEntryType
) {
289 // We have found the BlockEntry attached to the address. We save its start address (the start
290 // address might be before the 'BaseAdress') and attributes
291 *BaseAddress
= *BaseAddress
& ~(TT_ADDRESS_AT_LEVEL(TableLevel
) - 1);
293 *RegionAttributes
= *BlockEntry
& TT_ATTRIBUTES_MASK
;
295 // We have an 'Invalid' entry
296 return EFI_UNSUPPORTED
;
299 while (BlockEntry
<= LastBlockEntry
) {
300 if ((*BlockEntry
& TT_ATTRIBUTES_MASK
) == *RegionAttributes
) {
301 *RegionLength
= *RegionLength
+ TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel
);
303 // In case we have found the end of the region we return success
309 // If we have reached the end of the TranslationTable and we have not found the end of the region then
310 // we return EFI_NOT_FOUND.
311 // The caller will continue to look for the memory region at its level
312 return EFI_NOT_FOUND
;
317 IN OUT UINTN
*BaseAddress
,
318 OUT UINTN
*RegionLength
,
319 OUT UINTN
*RegionAttributes
323 UINT64
*TranslationTable
;
328 ASSERT ((BaseAddress
!= NULL
) && (RegionLength
!= NULL
) && (RegionAttributes
!= NULL
));
330 TranslationTable
= ArmGetTTBR0BaseAddress ();
332 T0SZ
= ArmGetTCR () & TCR_T0SZ_MASK
;
333 // Get the Table info from T0SZ
334 GetRootTranslationTableInfo (T0SZ
, &TableLevel
, &EntryCount
);
336 Status
= GetMemoryRegionRec (TranslationTable
, TableLevel
,
337 (UINTN
*)TT_LAST_BLOCK_ADDRESS(TranslationTable
, EntryCount
),
338 BaseAddress
, RegionLength
, RegionAttributes
);
340 // If the region continues up to the end of the root table then GetMemoryRegionRec()
341 // will return EFI_NOT_FOUND
342 if (Status
== EFI_NOT_FOUND
) {