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>
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 #define TT_ATTR_INDX_INVALID ((UINT32)~0)
25 GetFirstPageAttribute (
26 IN UINT64
*FirstLevelTableAddress
,
32 // Get the first entry of the table
33 FirstEntry
= *FirstLevelTableAddress
;
35 if ((FirstEntry
& TT_TYPE_MASK
) == TT_TYPE_TABLE_ENTRY
) {
36 // Only valid for Levels 0, 1 and 2
37 ASSERT (TableLevel
< 3);
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 (EntryAttribute
));
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 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
;
210 ArmAttributes
= TT_ATTR_INDX_MEMORY_NON_CACHEABLE
;
213 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_THROUGH
;
216 ArmAttributes
= TT_ATTR_INDX_MEMORY_WRITE_BACK
;
219 DEBUG ((EFI_D_ERROR
, "EfiAttributeToArmAttribute: 0x%lX attributes is not supported.\n", EfiAttributes
));
221 ArmAttributes
= TT_ATTR_INDX_DEVICE_MEMORY
;
224 // Set the access flag to match the block attributes
225 ArmAttributes
|= TT_AF
;
227 // Determine protection attributes
228 if (EfiAttributes
& EFI_MEMORY_WP
) {
229 ArmAttributes
|= TT_AP_RO_RO
;
232 // Process eXecute Never attribute
233 if (EfiAttributes
& EFI_MEMORY_XP
) {
234 ArmAttributes
|= TT_PXN_MASK
;
237 return ArmAttributes
;
240 // This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.
241 // And then the function will identify the size of the region that has the same page table attribute.
244 IN UINT64
*TranslationTable
,
246 IN UINT64
*LastBlockEntry
,
247 IN OUT UINTN
*BaseAddress
,
248 OUT UINTN
*RegionLength
,
249 OUT UINTN
*RegionAttributes
253 UINT64
*NextTranslationTable
;
255 UINT64 BlockEntryType
;
258 if (TableLevel
!= 3) {
259 BlockEntryType
= TT_TYPE_BLOCK_ENTRY
;
261 BlockEntryType
= TT_TYPE_BLOCK_ENTRY_LEVEL3
;
264 // Find the block entry linked to the Base Address
265 BlockEntry
= (UINT64
*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable
, TableLevel
, *BaseAddress
);
266 EntryType
= *BlockEntry
& TT_TYPE_MASK
;
268 if (EntryType
== TT_TYPE_TABLE_ENTRY
) {
269 NextTranslationTable
= (UINT64
*)(*BlockEntry
& TT_ADDRESS_MASK_DESCRIPTION_TABLE
);
271 // The entry is a page table, so we go to the next level
272 Status
= GetMemoryRegionRec (
273 NextTranslationTable
, // Address of the next level page table
274 TableLevel
+ 1, // Next Page Table level
275 (UINTN
*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable
, TT_ENTRY_COUNT
),
276 BaseAddress
, RegionLength
, RegionAttributes
);
278 // In case of 'Success', it means the end of the block region has been found into the upper
279 // level translation table
280 if (!EFI_ERROR(Status
)) {
283 } else if (EntryType
== BlockEntryType
) {
284 // We have found the BlockEntry attached to the address. We save its start address (the start
285 // address might be before the 'BaseAdress') and attributes
286 *BaseAddress
= *BaseAddress
& ~(TT_ADDRESS_AT_LEVEL(TableLevel
) - 1);
288 *RegionAttributes
= *BlockEntry
& TT_ATTRIBUTES_MASK
;
290 // We have an 'Invalid' entry
291 return EFI_UNSUPPORTED
;
294 while (BlockEntry
<= LastBlockEntry
) {
295 if ((*BlockEntry
& TT_ATTRIBUTES_MASK
) == *RegionAttributes
) {
296 *RegionLength
= *RegionLength
+ TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel
);
298 // In case we have found the end of the region we return success
304 // If we have reached the end of the TranslationTable and we have not found the end of the region then
305 // we return EFI_NOT_FOUND.
306 // The caller will continue to look for the memory region at its level
307 return EFI_NOT_FOUND
;
312 IN OUT UINTN
*BaseAddress
,
313 OUT UINTN
*RegionLength
,
314 OUT UINTN
*RegionAttributes
318 UINT64
*TranslationTable
;
323 ASSERT ((BaseAddress
!= NULL
) && (RegionLength
!= NULL
) && (RegionAttributes
!= NULL
));
325 TranslationTable
= ArmGetTTBR0BaseAddress ();
327 T0SZ
= ArmGetTCR () & TCR_T0SZ_MASK
;
328 // Get the Table info from T0SZ
329 GetRootTranslationTableInfo (T0SZ
, &TableLevel
, &EntryCount
);
331 Status
= GetMemoryRegionRec (TranslationTable
, TableLevel
,
332 (UINTN
*)TT_LAST_BLOCK_ADDRESS(TranslationTable
, EntryCount
),
333 BaseAddress
, RegionLength
, RegionAttributes
);
335 // If the region continues up to the end of the root table then GetMemoryRegionRec()
336 // will return EFI_NOT_FOUND
337 if (Status
== EFI_NOT_FOUND
) {