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, 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>
23 SectionToGcdAttributes (
24 IN UINT32 SectionAttributes
,
25 OUT UINT64
*GcdAttributes
30 // determine cacheability attributes
31 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
) {
32 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
:
33 *GcdAttributes
|= EFI_MEMORY_UC
;
35 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE
:
36 *GcdAttributes
|= EFI_MEMORY_UC
;
38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
39 *GcdAttributes
|= EFI_MEMORY_WT
;
41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
42 *GcdAttributes
|= EFI_MEMORY_WB
;
44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
:
45 *GcdAttributes
|= EFI_MEMORY_WC
;
47 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
:
48 *GcdAttributes
|= EFI_MEMORY_WB
;
50 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
51 *GcdAttributes
|= EFI_MEMORY_UC
;
54 return EFI_UNSUPPORTED
;
57 // determine protection attributes
58 switch(SectionAttributes
& TT_DESCRIPTOR_SECTION_AP_MASK
) {
59 case TT_DESCRIPTOR_SECTION_AP_NO_NO
: // no read, no write
60 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
63 case TT_DESCRIPTOR_SECTION_AP_RW_NO
:
64 case TT_DESCRIPTOR_SECTION_AP_RW_RW
:
65 // normal read/write access, do not add additional attributes
68 // read only cases map to write-protect
69 case TT_DESCRIPTOR_SECTION_AP_RO_NO
:
70 case TT_DESCRIPTOR_SECTION_AP_RO_RO
:
71 *GcdAttributes
|= EFI_MEMORY_RO
;
75 return EFI_UNSUPPORTED
;
78 // now process eXectue Never attribute
79 if ((SectionAttributes
& TT_DESCRIPTOR_SECTION_XN_MASK
) != 0 ) {
80 *GcdAttributes
|= EFI_MEMORY_XP
;
88 IN UINT32 PageAttributes
,
89 OUT UINT64
*GcdAttributes
94 // determine cacheability attributes
95 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
) {
96 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
:
97 *GcdAttributes
|= EFI_MEMORY_UC
;
99 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE
:
100 *GcdAttributes
|= EFI_MEMORY_UC
;
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
:
103 *GcdAttributes
|= EFI_MEMORY_WT
;
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC
:
106 *GcdAttributes
|= EFI_MEMORY_WB
;
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
:
109 *GcdAttributes
|= EFI_MEMORY_WC
;
111 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
:
112 *GcdAttributes
|= EFI_MEMORY_WB
;
114 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE
:
115 *GcdAttributes
|= EFI_MEMORY_UC
;
118 return EFI_UNSUPPORTED
;
121 // determine protection attributes
122 switch(PageAttributes
& TT_DESCRIPTOR_PAGE_AP_MASK
) {
123 case TT_DESCRIPTOR_PAGE_AP_NO_NO
: // no read, no write
124 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
127 case TT_DESCRIPTOR_PAGE_AP_RW_NO
:
128 case TT_DESCRIPTOR_PAGE_AP_RW_RW
:
129 // normal read/write access, do not add additional attributes
132 // read only cases map to write-protect
133 case TT_DESCRIPTOR_PAGE_AP_RO_NO
:
134 case TT_DESCRIPTOR_PAGE_AP_RO_RO
:
135 *GcdAttributes
|= EFI_MEMORY_RO
;
139 return EFI_UNSUPPORTED
;
142 // now process eXectue Never attribute
143 if ((PageAttributes
& TT_DESCRIPTOR_PAGE_XN_MASK
) != 0 ) {
144 *GcdAttributes
|= EFI_MEMORY_XP
;
151 SyncCacheConfigPage (
152 IN UINT32 SectionIndex
,
153 IN UINT32 FirstLevelDescriptor
,
154 IN UINTN NumberOfDescriptors
,
155 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
,
156 IN OUT EFI_PHYSICAL_ADDRESS
*NextRegionBase
,
157 IN OUT UINT64
*NextRegionLength
,
158 IN OUT UINT32
*NextSectionAttributes
163 volatile ARM_PAGE_TABLE_ENTRY
*SecondLevelTable
;
164 UINT32 NextPageAttributes
= 0;
165 UINT32 PageAttributes
= 0;
167 UINT64 GcdAttributes
;
169 // Get the Base Address from FirstLevelDescriptor;
170 BaseAddress
= TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
172 // Convert SectionAttributes into PageAttributes
174 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes
,0) |
175 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes
);
177 // obtain page table base
178 SecondLevelTable
= (ARM_PAGE_TABLE_ENTRY
*)(FirstLevelDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
180 for (i
=0; i
< TRANSLATION_TABLE_PAGE_COUNT
; i
++) {
181 if ((SecondLevelTable
[i
] & TT_DESCRIPTOR_PAGE_TYPE_MASK
) == TT_DESCRIPTOR_PAGE_TYPE_PAGE
) {
182 // extract attributes (cacheability and permissions)
183 PageAttributes
= SecondLevelTable
[i
] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
);
185 if (NextPageAttributes
== 0) {
186 // start on a new region
187 *NextRegionLength
= 0;
188 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
189 NextPageAttributes
= PageAttributes
;
190 } else if (PageAttributes
!= NextPageAttributes
) {
191 // Convert Section Attributes into GCD Attributes
192 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
193 ASSERT_EFI_ERROR (Status
);
195 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
196 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
198 // start on a new region
199 *NextRegionLength
= 0;
200 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
201 NextPageAttributes
= PageAttributes
;
203 } else if (NextPageAttributes
!= 0) {
204 // Convert Page Attributes into GCD Attributes
205 Status
= PageToGcdAttributes (NextPageAttributes
, &GcdAttributes
);
206 ASSERT_EFI_ERROR (Status
);
208 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
209 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, *NextRegionBase
, *NextRegionLength
, GcdAttributes
);
211 *NextRegionLength
= 0;
212 *NextRegionBase
= BaseAddress
| (i
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
);
213 NextPageAttributes
= 0;
215 *NextRegionLength
+= TT_DESCRIPTOR_PAGE_SIZE
;
218 // Convert back PageAttributes into SectionAttributes
219 *NextSectionAttributes
=
220 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes
,0) |
221 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes
);
228 IN EFI_CPU_ARCH_PROTOCOL
*CpuProtocol
233 EFI_PHYSICAL_ADDRESS NextRegionBase
;
234 UINT64 NextRegionLength
;
235 UINT32 NextSectionAttributes
= 0;
236 UINT32 SectionAttributes
= 0;
237 UINT64 GcdAttributes
;
238 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
239 UINTN NumberOfDescriptors
;
240 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
243 DEBUG ((EFI_D_PAGE
, "SyncCacheConfig()\n"));
245 // This code assumes MMU is enabled and filed with section translations
246 ASSERT (ArmMmuEnabled ());
249 // Get the memory space map from GCD
251 MemorySpaceMap
= NULL
;
252 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
253 ASSERT_EFI_ERROR (Status
);
256 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
257 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
258 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
259 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
260 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
262 // obtain page table base
263 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)(ArmGetTTBR0BaseAddress ());
265 // Get the first region
266 NextSectionAttributes
= FirstLevelTable
[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
268 // iterate through each 1MB descriptor
269 NextRegionBase
= NextRegionLength
= 0;
270 for (i
=0; i
< TRANSLATION_TABLE_SECTION_COUNT
; i
++) {
271 if ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) {
272 // extract attributes (cacheability and permissions)
273 SectionAttributes
= FirstLevelTable
[i
] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
| TT_DESCRIPTOR_SECTION_AP_MASK
);
275 if (NextSectionAttributes
== 0) {
276 // start on a new region
277 NextRegionLength
= 0;
278 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
279 NextSectionAttributes
= SectionAttributes
;
280 } else if (SectionAttributes
!= NextSectionAttributes
) {
281 // Convert Section Attributes into GCD Attributes
282 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
283 ASSERT_EFI_ERROR (Status
);
285 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
286 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
288 // start on a new region
289 NextRegionLength
= 0;
290 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
291 NextSectionAttributes
= SectionAttributes
;
293 NextRegionLength
+= TT_DESCRIPTOR_SECTION_SIZE
;
294 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable
[i
])) {
295 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from
296 // bits that are actually part of the pagetable address. We clear it out to zero so that
297 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the
298 // section attributes into page attributes
299 NextSectionAttributes
= 0;
300 Status
= SyncCacheConfigPage (
301 i
,FirstLevelTable
[i
],
302 NumberOfDescriptors
, MemorySpaceMap
,
303 &NextRegionBase
,&NextRegionLength
,&NextSectionAttributes
);
304 ASSERT_EFI_ERROR (Status
);
306 // We do not support yet 16MB sections
307 ASSERT ((FirstLevelTable
[i
] & TT_DESCRIPTOR_SECTION_TYPE_MASK
) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
);
309 // start on a new region
310 if (NextSectionAttributes
!= 0) {
311 // Convert Section Attributes into GCD Attributes
312 Status
= SectionToGcdAttributes (NextSectionAttributes
, &GcdAttributes
);
313 ASSERT_EFI_ERROR (Status
);
315 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
316 SetGcdMemorySpaceAttributes (MemorySpaceMap
, NumberOfDescriptors
, NextRegionBase
, NextRegionLength
, GcdAttributes
);
318 NextRegionLength
= 0;
319 NextRegionBase
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
320 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-cachable
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
) {
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
) {
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
];
461 // If 'BaseAddress' belongs to the section then round it to the section boundary
462 if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
463 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
))
465 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK
;
466 *RegionAttributes
= SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
;
468 // Otherwise, we round it to the page boundary
469 *BaseAddress
= (*BaseAddress
) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK
;
471 // Get the attribute at the page table level (Level 2)
472 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
474 // Calculate index into first level translation table for start of modification
475 PageTableIndex
= ((*BaseAddress
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
476 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
478 PageAttributes
= PageTable
[PageTableIndex
] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK
;
479 *RegionAttributes
= TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes
, 0) |
480 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes
);
483 for (;TableIndex
< TRANSLATION_TABLE_SECTION_COUNT
; TableIndex
++) {
484 // Get the section at the given index
485 SectionDescriptor
= FirstLevelTable
[TableIndex
];
487 // If the entry is a level-2 page table then we scan it to find the end of the region
488 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor
)) {
489 // Extract the page table location from the descriptor
490 PageTable
= (UINT32
*)(SectionDescriptor
& TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
);
492 // Scan the page table to find the end of the region.
493 Status
= GetMemoryRegionPage (PageTable
, BaseAddress
, RegionLength
, RegionAttributes
);
495 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
496 if (Status
== EFI_SUCCESS
) {
499 } else if (((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SECTION
) ||
500 ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_TYPE_MASK
) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION
)) {
501 if ((SectionDescriptor
& TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK
) != *RegionAttributes
) {
502 // If the attributes of the section differ from the one targeted then we exit the loop
505 *RegionLength
= *RegionLength
+ TT_DESCRIPTOR_SECTION_SIZE
;
508 // If we are on an invalid section then it means it is the end of our section.