TianoCore EDK2 master
Loading...
Searching...
No Matches
Mmu.c
1/*++
2
3Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
4Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
5Portions copyright (c) 2011-2021, Arm Limited. All rights reserved.<BR>
6Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
7
8SPDX-License-Identifier: BSD-2-Clause-Patent
9
10
11--*/
12
14#include "CpuDxe.h"
15
16#define INVALID_ENTRY ((UINT64)~0)
17
18#define MIN_T0SZ 16
19#define BITS_PER_LEVEL 9
20
30VOID
31GetRootTranslationTableInfo (
32 IN UINTN T0SZ,
33 OUT UINTN *RootTableLevel,
34 OUT UINTN *RootTableEntryCount
35 )
36{
37 *RootTableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL;
38 *RootTableEntryCount = TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL;
39}
40
49UINT64
50PageAttributeToGcdAttribute (
51 IN UINT64 PageAttributes
52 )
53{
54 UINT64 GcdAttributes;
55
56 switch (PageAttributes & TT_ATTR_INDX_MASK) {
57 case TT_ATTR_INDX_DEVICE_MEMORY:
58 GcdAttributes = EFI_MEMORY_UC;
59 break;
60 case TT_ATTR_INDX_MEMORY_NON_CACHEABLE:
61 GcdAttributes = EFI_MEMORY_WC;
62 break;
63 case TT_ATTR_INDX_MEMORY_WRITE_THROUGH:
64 GcdAttributes = EFI_MEMORY_WT;
65 break;
66 case TT_ATTR_INDX_MEMORY_WRITE_BACK:
67 GcdAttributes = EFI_MEMORY_WB;
68 break;
69 default:
70 DEBUG ((
71 DEBUG_ERROR,
72 "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n",
73 PageAttributes
74 ));
75 ASSERT (0);
76 // The Global Coherency Domain (GCD) value is defined as a bit set.
77 // Returning 0 means no attribute has been set.
78 GcdAttributes = 0;
79 }
80
81 // Determine protection attributes
82 if ((PageAttributes & TT_AF) == 0) {
83 GcdAttributes |= EFI_MEMORY_RP;
84 }
85
86 if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) ||
87 ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO))
88 {
89 // Read only cases map to write-protect
90 GcdAttributes |= EFI_MEMORY_RO;
91 }
92
93 // Process eXecute Never attribute
94 if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0) {
95 GcdAttributes |= EFI_MEMORY_XP;
96 }
97
98 return GcdAttributes;
99}
100
110UINT64
111RegionAttributeToGcdAttribute (
112 IN UINTN PageAttributes
113 )
114{
115 return PageAttributeToGcdAttribute (PageAttributes);
116}
117
126STATIC
127UINT64
128GetFirstPageAttribute (
129 IN UINT64 *FirstLevelTableAddress,
130 IN UINTN TableLevel
131 )
132{
133 UINT64 FirstEntry;
134
135 // Get the first entry of the table
136 FirstEntry = *FirstLevelTableAddress;
137
138 if ((TableLevel != 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) {
139 // Only valid for Levels 0, 1 and 2
140
141 // Get the attribute of the subsequent table
142 return GetFirstPageAttribute ((UINT64 *)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);
143 } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) ||
144 ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3)))
145 {
146 return FirstEntry & TT_ATTRIBUTES_MASK;
147 } else {
148 return INVALID_ENTRY;
149 }
150}
151
165STATIC
166UINT64
167GetNextEntryAttribute (
168 IN UINT64 *TableAddress,
169 IN UINTN EntryCount,
170 IN UINTN TableLevel,
171 IN UINT64 BaseAddress,
172 IN OUT UINT64 *PrevEntryAttribute,
173 IN OUT UINT64 *StartGcdRegion
174 )
175{
176 UINTN Index;
177 UINT64 Entry;
178 UINT64 EntryAttribute;
179 UINT64 EntryType;
180 EFI_STATUS Status;
181 UINTN NumberOfDescriptors;
182 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
183
184 // Get the memory space map from GCD
185 MemorySpaceMap = NULL;
186 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
187
188 if (EFI_ERROR (Status) || (TableLevel > 3)) {
189 ASSERT_EFI_ERROR (Status);
190 ASSERT (TableLevel <= 3);
191 return 0;
192 }
193
194 // While the top level table might not contain TT_ENTRY_COUNT entries;
195 // the subsequent ones should be filled up
196 for (Index = 0; Index < EntryCount; Index++) {
197 Entry = TableAddress[Index];
198 EntryType = Entry & TT_TYPE_MASK;
199 EntryAttribute = Entry & TT_ATTRIBUTES_MASK;
200
201 // If Entry is a Table Descriptor type entry then go through the sub-level table
202 if ((EntryType == TT_TYPE_BLOCK_ENTRY) ||
203 ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3)))
204 {
205 if ((*PrevEntryAttribute == INVALID_ENTRY) || (EntryAttribute != *PrevEntryAttribute)) {
206 if (*PrevEntryAttribute != INVALID_ENTRY) {
207 // Update GCD with the last region
209 MemorySpaceMap,
210 NumberOfDescriptors,
211 *StartGcdRegion,
212 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion,
213 PageAttributeToGcdAttribute (*PrevEntryAttribute)
214 );
215 }
216
217 // Start of the new region
218 *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel));
219 *PrevEntryAttribute = EntryAttribute;
220 } else {
221 continue;
222 }
223 } else if (EntryType == TT_TYPE_TABLE_ENTRY) {
224 // Table Entry type is only valid for Level 0, 1, 2
225 ASSERT (TableLevel < 3);
226
227 // Increase the level number and scan the sub-level table
228 GetNextEntryAttribute (
229 (UINT64 *)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),
230 TT_ENTRY_COUNT,
231 TableLevel + 1,
232 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))),
233 PrevEntryAttribute,
234 StartGcdRegion
235 );
236 } else {
237 if (*PrevEntryAttribute != INVALID_ENTRY) {
238 // Update GCD with the last region
240 MemorySpaceMap,
241 NumberOfDescriptors,
242 *StartGcdRegion,
243 (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion,
244 PageAttributeToGcdAttribute (*PrevEntryAttribute)
245 );
246
247 // Start of the new region
248 *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel));
249 *PrevEntryAttribute = INVALID_ENTRY;
250 }
251 }
252 }
253
254 FreePool (MemorySpaceMap);
255
256 return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL (TableLevel));
257}
258
269SyncCacheConfig (
270 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol
271 )
272{
273 EFI_STATUS Status;
274 UINT64 PageAttribute;
275 UINT64 *FirstLevelTableAddress;
276 UINTN TableLevel;
277 UINTN TableCount;
278 UINTN NumberOfDescriptors;
279 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
280 UINTN Tcr;
281 UINTN T0SZ;
282 UINT64 BaseAddressGcdRegion;
283 UINT64 EndAddressGcdRegion;
284
285 // This code assumes MMU is enabled and filed with section translations
286 ASSERT (ArmMmuEnabled ());
287
288 //
289 // Get the memory space map from GCD
290 //
291 MemorySpaceMap = NULL;
292 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
293
294 if (EFI_ERROR (Status)) {
295 ASSERT_EFI_ERROR (Status);
296 return Status;
297 }
298
299 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
300 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
301 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
302 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
303 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
304
305 // Obtain page table base
306 FirstLevelTableAddress = (UINT64 *)(ArmGetTTBR0BaseAddress ());
307
308 // Get Translation Control Register value
309 Tcr = ArmGetTCR ();
310 // Get Address Region Size
311 T0SZ = Tcr & TCR_T0SZ_MASK;
312
313 // Get the level of the first table for the indicated Address Region Size
314 GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount);
315
316 // First Attribute of the Page Tables
317 PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel);
318
319 // We scan from the start of the memory map (ie: at the address 0x0)
320 BaseAddressGcdRegion = 0x0;
321 EndAddressGcdRegion = GetNextEntryAttribute (
322 FirstLevelTableAddress,
323 TableCount,
324 TableLevel,
325 BaseAddressGcdRegion,
326 &PageAttribute,
327 &BaseAddressGcdRegion
328 );
329
330 // Update GCD with the last region if valid
331 if ((PageAttribute != INVALID_ENTRY) && (EndAddressGcdRegion > BaseAddressGcdRegion)) {
333 MemorySpaceMap,
334 NumberOfDescriptors,
335 BaseAddressGcdRegion,
336 EndAddressGcdRegion - BaseAddressGcdRegion,
337 PageAttributeToGcdAttribute (PageAttribute)
338 );
339 }
340
341 FreePool (MemorySpaceMap);
342
343 return EFI_SUCCESS;
344}
345
353UINT64
354EfiAttributeToArmAttribute (
355 IN UINT64 EfiAttributes
356 )
357{
358 UINT64 ArmAttributes;
359
360 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
361 case EFI_MEMORY_UC:
362 if (ArmReadCurrentEL () == AARCH64_EL2) {
363 ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;
364 } else {
365 ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;
366 }
367
368 break;
369 case EFI_MEMORY_WC:
370 ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;
371 break;
372 case EFI_MEMORY_WT:
373 ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;
374 break;
375 case EFI_MEMORY_WB:
376 ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;
377 break;
378 default:
379 ArmAttributes = TT_ATTR_INDX_MASK;
380 }
381
382 // Set the access flag to match the block attributes
383 if ((EfiAttributes & EFI_MEMORY_RP) == 0) {
384 ArmAttributes |= TT_AF;
385 }
386
387 // Determine protection attributes
388 if ((EfiAttributes & EFI_MEMORY_RO) != 0) {
389 ArmAttributes |= TT_AP_NO_RO;
390 }
391
392 // Process eXecute Never attribute
393 if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
394 ArmAttributes |= TT_PXN_MASK;
395 }
396
397 return ArmAttributes;
398}
399
420GetMemoryRegionRec (
421 IN UINT64 *TranslationTable,
422 IN UINTN TableLevel,
423 IN UINT64 *LastBlockEntry,
424 IN OUT UINTN *BaseAddress,
425 OUT UINTN *RegionLength,
426 OUT UINTN *RegionAttributes
427 )
428{
429 EFI_STATUS Status;
430 UINT64 *NextTranslationTable;
431 UINT64 *BlockEntry;
432 UINT64 BlockEntryType;
433 UINT64 EntryType;
434
435 if (TableLevel != 3) {
436 BlockEntryType = TT_TYPE_BLOCK_ENTRY;
437 } else {
438 BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3;
439 }
440
441 // Find the block entry linked to the Base Address
442 BlockEntry = (UINT64 *)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress);
443 EntryType = *BlockEntry & TT_TYPE_MASK;
444
445 if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) {
446 NextTranslationTable = (UINT64 *)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);
447
448 // The entry is a page table, so we go to the next level
449 Status = GetMemoryRegionRec (
450 NextTranslationTable, // Address of the next level page table
451 TableLevel + 1, // Next Page Table level
452 (UINTN *)TT_LAST_BLOCK_ADDRESS (NextTranslationTable, TT_ENTRY_COUNT),
453 BaseAddress,
454 RegionLength,
455 RegionAttributes
456 );
457
458 // EFI_SUCCESS: The end of the end of the region was found.
459 // EFI_NO_MAPPING: The translation entry associated with BaseAddress is invalid.
460 if (Status != EFI_NOT_FOUND) {
461 return Status;
462 }
463
464 // Now we processed the table move to the next entry
465 BlockEntry++;
466 } else if (EntryType == BlockEntryType) {
467 // We have found the BlockEntry attached to the address. We save its start address (the start
468 // address might be before the 'BaseAddress') and attributes
469 *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL (TableLevel) - 1);
470 *RegionLength = 0;
471 *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK;
472 } else {
473 return EFI_NO_MAPPING;
474 }
475
476 while (BlockEntry <= LastBlockEntry) {
477 if (((*BlockEntry & TT_TYPE_MASK) == BlockEntryType) &&
478 ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes))
479 {
480 *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL (TableLevel);
481 } else {
482 // In case we have found the end of the region we return success
483 return EFI_SUCCESS;
484 }
485
486 BlockEntry++;
487 }
488
489 // If we have reached the end of the TranslationTable and we have not found the end of the region then
490 // we return EFI_NOT_FOUND.
491 // The caller will continue to look for the memory region at its level.
492 return EFI_NOT_FOUND;
493}
494
515GetMemoryRegion (
516 IN OUT UINTN *BaseAddress,
517 OUT UINTN *RegionLength,
518 OUT UINTN *RegionAttributes
519 )
520{
521 EFI_STATUS Status;
522 UINT64 *TranslationTable;
523 UINTN TableLevel;
524 UINTN EntryCount;
525 UINTN T0SZ;
526
527 if ((BaseAddress == NULL) || (RegionLength == NULL) || (RegionAttributes == NULL)) {
528 ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL));
529 return EFI_INVALID_PARAMETER;
530 }
531
532 TranslationTable = ArmGetTTBR0BaseAddress ();
533
534 // Initialize the output parameters. These paramaters are only valid if the
535 // result is EFI_SUCCESS.
536 *RegionLength = 0;
537 *RegionAttributes = 0;
538
539 T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;
540 // Get the Table info from T0SZ
541 GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount);
542
543 Status = GetMemoryRegionRec (
544 TranslationTable,
545 TableLevel,
546 (UINTN *)TT_LAST_BLOCK_ADDRESS (TranslationTable, EntryCount),
547 BaseAddress,
548 RegionLength,
549 RegionAttributes
550 );
551
552 // If the region continues up to the end of the root table then GetMemoryRegionRec()
553 // will return EFI_NOT_FOUND. Check if the region length was updated.
554 if ((Status == EFI_NOT_FOUND) && (*RegionLength > 0)) {
555 return EFI_SUCCESS;
556 }
557
558 return Status;
559}
UINT64 UINTN
EFI_STATUS SetGcdMemorySpaceAttributes(IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, IN UINTN NumberOfDescriptors, IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes)
Definition: CpuMmuCommon.c:79
EFI_DXE_SERVICES * gDS
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112