]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
a2993cf16a35eb95cc36edd6ac5cf4d43117aec3
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / Arm / Mmu.c
1 /*++
2
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>
7
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
12
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.
15
16
17 --*/
18
19 #include <Library/MemoryAllocationLib.h>
20 #include "CpuDxe.h"
21
22 // First Level Descriptors
23 typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR;
24
25 // Second Level Descriptors
26 typedef UINT32 ARM_PAGE_TABLE_ENTRY;
27
28 EFI_STATUS
29 SectionToGcdAttributes (
30 IN UINT32 SectionAttributes,
31 OUT UINT64 *GcdAttributes
32 )
33 {
34 *GcdAttributes = 0;
35
36 // determine cacheability attributes
37 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) {
38 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED:
39 *GcdAttributes |= EFI_MEMORY_UC;
40 break;
41 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE:
42 *GcdAttributes |= EFI_MEMORY_UC;
43 break;
44 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:
45 *GcdAttributes |= EFI_MEMORY_WT;
46 break;
47 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC:
48 *GcdAttributes |= EFI_MEMORY_WB;
49 break;
50 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE:
51 *GcdAttributes |= EFI_MEMORY_WC;
52 break;
53 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC:
54 *GcdAttributes |= EFI_MEMORY_WB;
55 break;
56 case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE:
57 *GcdAttributes |= EFI_MEMORY_UC;
58 break;
59 default:
60 return EFI_UNSUPPORTED;
61 }
62
63 // determine protection attributes
64 switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) {
65 case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write
66 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
67 break;
68
69 case TT_DESCRIPTOR_SECTION_AP_RW_NO:
70 case TT_DESCRIPTOR_SECTION_AP_RW_RW:
71 // normal read/write access, do not add additional attributes
72 break;
73
74 // read only cases map to write-protect
75 case TT_DESCRIPTOR_SECTION_AP_RO_NO:
76 case TT_DESCRIPTOR_SECTION_AP_RO_RO:
77 *GcdAttributes |= EFI_MEMORY_RO;
78 break;
79
80 default:
81 return EFI_UNSUPPORTED;
82 }
83
84 // now process eXectue Never attribute
85 if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {
86 *GcdAttributes |= EFI_MEMORY_XP;
87 }
88
89 return EFI_SUCCESS;
90 }
91
92 EFI_STATUS
93 PageToGcdAttributes (
94 IN UINT32 PageAttributes,
95 OUT UINT64 *GcdAttributes
96 )
97 {
98 *GcdAttributes = 0;
99
100 // determine cacheability attributes
101 switch(PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) {
102 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED:
103 *GcdAttributes |= EFI_MEMORY_UC;
104 break;
105 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE:
106 *GcdAttributes |= EFI_MEMORY_UC;
107 break;
108 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:
109 *GcdAttributes |= EFI_MEMORY_WT;
110 break;
111 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC:
112 *GcdAttributes |= EFI_MEMORY_WB;
113 break;
114 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE:
115 *GcdAttributes |= EFI_MEMORY_WC;
116 break;
117 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC:
118 *GcdAttributes |= EFI_MEMORY_WB;
119 break;
120 case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE:
121 *GcdAttributes |= EFI_MEMORY_UC;
122 break;
123 default:
124 return EFI_UNSUPPORTED;
125 }
126
127 // determine protection attributes
128 switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) {
129 case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write
130 //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP;
131 break;
132
133 case TT_DESCRIPTOR_PAGE_AP_RW_NO:
134 case TT_DESCRIPTOR_PAGE_AP_RW_RW:
135 // normal read/write access, do not add additional attributes
136 break;
137
138 // read only cases map to write-protect
139 case TT_DESCRIPTOR_PAGE_AP_RO_NO:
140 case TT_DESCRIPTOR_PAGE_AP_RO_RO:
141 *GcdAttributes |= EFI_MEMORY_RO;
142 break;
143
144 default:
145 return EFI_UNSUPPORTED;
146 }
147
148 // now process eXectue Never attribute
149 if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {
150 *GcdAttributes |= EFI_MEMORY_XP;
151 }
152
153 return EFI_SUCCESS;
154 }
155
156 EFI_STATUS
157 SyncCacheConfigPage (
158 IN UINT32 SectionIndex,
159 IN UINT32 FirstLevelDescriptor,
160 IN UINTN NumberOfDescriptors,
161 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
162 IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase,
163 IN OUT UINT64 *NextRegionLength,
164 IN OUT UINT32 *NextSectionAttributes
165 )
166 {
167 EFI_STATUS Status;
168 UINT32 i;
169 volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable;
170 UINT32 NextPageAttributes = 0;
171 UINT32 PageAttributes = 0;
172 UINT32 BaseAddress;
173 UINT64 GcdAttributes;
174
175 // Get the Base Address from FirstLevelDescriptor;
176 BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
177
178 // Convert SectionAttributes into PageAttributes
179 NextPageAttributes =
180 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) |
181 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes);
182
183 // obtain page table base
184 SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
185
186 for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {
187 if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
188 // extract attributes (cacheability and permissions)
189 PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);
190
191 if (NextPageAttributes == 0) {
192 // start on a new region
193 *NextRegionLength = 0;
194 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
195 NextPageAttributes = PageAttributes;
196 } else if (PageAttributes != NextPageAttributes) {
197 // Convert Section Attributes into GCD Attributes
198 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
199 ASSERT_EFI_ERROR (Status);
200
201 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
202 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
203
204 // start on a new region
205 *NextRegionLength = 0;
206 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
207 NextPageAttributes = PageAttributes;
208 }
209 } else if (NextPageAttributes != 0) {
210 // Convert Page Attributes into GCD Attributes
211 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
212 ASSERT_EFI_ERROR (Status);
213
214 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
215 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
216
217 *NextRegionLength = 0;
218 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
219 NextPageAttributes = 0;
220 }
221 *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE;
222 }
223
224 // Convert back PageAttributes into SectionAttributes
225 *NextSectionAttributes =
226 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) |
227 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes);
228
229 return EFI_SUCCESS;
230 }
231
232 EFI_STATUS
233 SyncCacheConfig (
234 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol
235 )
236 {
237 EFI_STATUS Status;
238 UINT32 i;
239 EFI_PHYSICAL_ADDRESS NextRegionBase;
240 UINT64 NextRegionLength;
241 UINT32 NextSectionAttributes = 0;
242 UINT32 SectionAttributes = 0;
243 UINT64 GcdAttributes;
244 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
245 UINTN NumberOfDescriptors;
246 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
247
248
249 DEBUG ((EFI_D_PAGE, "SyncCacheConfig()\n"));
250
251 // This code assumes MMU is enabled and filed with section translations
252 ASSERT (ArmMmuEnabled ());
253
254 //
255 // Get the memory space map from GCD
256 //
257 MemorySpaceMap = NULL;
258 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
259 ASSERT_EFI_ERROR (Status);
260
261
262 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
263 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
264 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
265 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
266 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
267
268 // obtain page table base
269 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());
270
271 // Get the first region
272 NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
273
274 // iterate through each 1MB descriptor
275 NextRegionBase = NextRegionLength = 0;
276 for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {
277 if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
278 // extract attributes (cacheability and permissions)
279 SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
280
281 if (NextSectionAttributes == 0) {
282 // start on a new region
283 NextRegionLength = 0;
284 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
285 NextSectionAttributes = SectionAttributes;
286 } else if (SectionAttributes != NextSectionAttributes) {
287 // Convert Section Attributes into GCD Attributes
288 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
289 ASSERT_EFI_ERROR (Status);
290
291 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
292 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
293
294 // start on a new region
295 NextRegionLength = 0;
296 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
297 NextSectionAttributes = SectionAttributes;
298 }
299 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
300 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) {
301 // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from
302 // bits that are actually part of the pagetable address. We clear it out to zero so that
303 // the SyncCacheConfigPage will use the page attributes instead of trying to convert the
304 // section attributes into page attributes
305 NextSectionAttributes = 0;
306 Status = SyncCacheConfigPage (
307 i,FirstLevelTable[i],
308 NumberOfDescriptors, MemorySpaceMap,
309 &NextRegionBase,&NextRegionLength,&NextSectionAttributes);
310 ASSERT_EFI_ERROR (Status);
311 } else {
312 // We do not support yet 16MB sections
313 ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);
314
315 // start on a new region
316 if (NextSectionAttributes != 0) {
317 // Convert Section Attributes into GCD Attributes
318 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
319 ASSERT_EFI_ERROR (Status);
320
321 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
322 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
323
324 NextRegionLength = 0;
325 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
326 NextSectionAttributes = 0;
327 }
328 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
329 }
330 } // section entry loop
331
332 if (NextSectionAttributes != 0) {
333 // Convert Section Attributes into GCD Attributes
334 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
335 ASSERT_EFI_ERROR (Status);
336
337 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
338 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
339 }
340
341 FreePool (MemorySpaceMap);
342
343 return EFI_SUCCESS;
344 }
345
346
347
348 EFI_STATUS
349 UpdatePageEntries (
350 IN EFI_PHYSICAL_ADDRESS BaseAddress,
351 IN UINT64 Length,
352 IN UINT64 Attributes,
353 IN EFI_PHYSICAL_ADDRESS VirtualMask,
354 OUT BOOLEAN *FlushTlbs OPTIONAL
355 )
356 {
357 EFI_STATUS Status;
358 UINT32 EntryValue;
359 UINT32 EntryMask;
360 UINT32 FirstLevelIdx;
361 UINT32 Offset;
362 UINT32 NumPageEntries;
363 UINT32 Descriptor;
364 UINT32 p;
365 UINT32 PageTableIndex;
366 UINT32 PageTableEntry;
367 UINT32 CurrentPageTableEntry;
368 VOID *Mva;
369
370 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
371 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
372
373 Status = EFI_SUCCESS;
374
375 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
376 // EntryValue: values at bit positions specified by EntryMask
377 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;
378 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
379 // Although the PI spec is unclear on this the GCD guarantees that only
380 // one Attribute bit is set at a time, so we can safely use a switch statement
381 switch (Attributes) {
382 case EFI_MEMORY_UC:
383 // modify cacheability attributes
384 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
385 // map to strongly ordered
386 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
387 break;
388
389 case EFI_MEMORY_WC:
390 // modify cacheability attributes
391 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
392 // map to normal non-cachable
393 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
394 break;
395
396 case EFI_MEMORY_WT:
397 // modify cacheability attributes
398 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
399 // write through with no-allocate
400 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
401 break;
402
403 case EFI_MEMORY_WB:
404 // modify cacheability attributes
405 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
406 // write back (with allocate)
407 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
408 break;
409
410 case EFI_MEMORY_WP:
411 case EFI_MEMORY_XP:
412 case EFI_MEMORY_UCE:
413 // cannot be implemented UEFI definition unclear for ARM
414 // Cause a page fault if these ranges are accessed.
415 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;
416 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
417 break;
418
419 default:
420 return EFI_UNSUPPORTED;
421 }
422
423 // Obtain page table base
424 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
425
426 // Calculate number of 4KB page table entries to change
427 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
428
429 // Iterate for the number of 4KB pages to change
430 Offset = 0;
431 for(p = 0; p < NumPageEntries; p++) {
432 // Calculate index into first level translation table for page table value
433
434 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
435 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
436
437 // Read the descriptor from the first level page table
438 Descriptor = FirstLevelTable[FirstLevelIdx];
439
440 // Does this descriptor need to be converted from section entry to 4K pages?
441 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
442 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
443 if (EFI_ERROR(Status)) {
444 // Exit for loop
445 break;
446 }
447
448 // Re-read descriptor
449 Descriptor = FirstLevelTable[FirstLevelIdx];
450 if (FlushTlbs != NULL) {
451 *FlushTlbs = TRUE;
452 }
453 }
454
455 // Obtain page table base address
456 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
457
458 // Calculate index into the page table
459 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
460 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
461
462 // Get the entry
463 CurrentPageTableEntry = PageTable[PageTableIndex];
464
465 // Mask off appropriate fields
466 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
467
468 // Mask in new attributes and/or permissions
469 PageTableEntry |= EntryValue;
470
471 if (VirtualMask != 0) {
472 // Make this virtual address point at a physical page
473 PageTableEntry &= ~VirtualMask;
474 }
475
476 if (CurrentPageTableEntry != PageTableEntry) {
477 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
478
479 // Only need to update if we are changing the entry
480 PageTable[PageTableIndex] = PageTableEntry;
481 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
482
483 // Clean/invalidate the cache for this page, but only
484 // if we are modifying the memory type attributes
485 if (((CurrentPageTableEntry ^ PageTableEntry) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) != 0) {
486 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
487 }
488 }
489
490 Status = EFI_SUCCESS;
491 Offset += TT_DESCRIPTOR_PAGE_SIZE;
492
493 } // End first level translation table loop
494
495 return Status;
496 }
497
498
499
500 EFI_STATUS
501 UpdateSectionEntries (
502 IN EFI_PHYSICAL_ADDRESS BaseAddress,
503 IN UINT64 Length,
504 IN UINT64 Attributes,
505 IN EFI_PHYSICAL_ADDRESS VirtualMask
506 )
507 {
508 EFI_STATUS Status = EFI_SUCCESS;
509 UINT32 EntryMask;
510 UINT32 EntryValue;
511 UINT32 FirstLevelIdx;
512 UINT32 NumSections;
513 UINT32 i;
514 UINT32 CurrentDescriptor;
515 UINT32 Descriptor;
516 VOID *Mva;
517 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
518
519 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
520 // EntryValue: values at bit positions specified by EntryMask
521
522 // Make sure we handle a section range that is unmapped
523 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;
524 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
525
526 // Although the PI spec is unclear on this the GCD guarantees that only
527 // one Attribute bit is set at a time, so we can safely use a switch statement
528 switch(Attributes) {
529 case EFI_MEMORY_UC:
530 // modify cacheability attributes
531 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
532 // map to strongly ordered
533 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
534 break;
535
536 case EFI_MEMORY_WC:
537 // modify cacheability attributes
538 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
539 // map to normal non-cachable
540 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
541 break;
542
543 case EFI_MEMORY_WT:
544 // modify cacheability attributes
545 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
546 // write through with no-allocate
547 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
548 break;
549
550 case EFI_MEMORY_WB:
551 // modify cacheability attributes
552 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
553 // write back (with allocate)
554 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
555 break;
556
557 case EFI_MEMORY_WP:
558 case EFI_MEMORY_XP:
559 case EFI_MEMORY_RP:
560 case EFI_MEMORY_UCE:
561 // cannot be implemented UEFI definition unclear for ARM
562 // Cause a page fault if these ranges are accessed.
563 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
564 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
565 break;
566
567
568 default:
569 return EFI_UNSUPPORTED;
570 }
571
572 // obtain page table base
573 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
574
575 // calculate index into first level translation table for start of modification
576 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
577 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
578
579 // calculate number of 1MB first level entries this applies to
580 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
581
582 // iterate through each descriptor
583 for(i=0; i<NumSections; i++) {
584 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
585
586 // has this descriptor already been coverted to pages?
587 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
588 // forward this 1MB range to page table function instead
589 Status = UpdatePageEntries (
590 (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,
591 TT_DESCRIPTOR_SECTION_SIZE,
592 Attributes,
593 VirtualMask,
594 NULL);
595 } else {
596 // still a section entry
597
598 // mask off appropriate fields
599 Descriptor = CurrentDescriptor & ~EntryMask;
600
601 // mask in new attributes and/or permissions
602 Descriptor |= EntryValue;
603 if (VirtualMask != 0) {
604 Descriptor &= ~VirtualMask;
605 }
606
607 if (CurrentDescriptor != Descriptor) {
608 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
609
610 // Only need to update if we are changing the descriptor
611 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
612 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
613
614 // Clean/invalidate the cache for this section, but only
615 // if we are modifying the memory type attributes
616 if (((CurrentDescriptor ^ Descriptor) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) != 0) {
617 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
618 }
619 }
620
621 Status = EFI_SUCCESS;
622 }
623 }
624
625 return Status;
626 }
627
628 EFI_STATUS
629 ConvertSectionToPages (
630 IN EFI_PHYSICAL_ADDRESS BaseAddress
631 )
632 {
633 EFI_STATUS Status;
634 EFI_PHYSICAL_ADDRESS PageTableAddr;
635 UINT32 FirstLevelIdx;
636 UINT32 SectionDescriptor;
637 UINT32 PageTableDescriptor;
638 UINT32 PageDescriptor;
639 UINT32 Index;
640
641 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
642 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
643
644 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
645
646 // Obtain page table base
647 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
648
649 // Calculate index into first level translation table for start of modification
650 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
651 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
652
653 // Get section attributes and convert to page attributes
654 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
655 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
656
657 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
658 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
659 if (EFI_ERROR(Status)) {
660 return Status;
661 }
662
663 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
664
665 // Write the page table entries out
666 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
667 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
668 }
669
670 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
671 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
672
673 // Formulate page table entry, Domain=0, NS=0
674 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
675
676 // Write the page table entry out, replacing section entry
677 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
678
679 return EFI_SUCCESS;
680 }
681
682
683
684 EFI_STATUS
685 SetMemoryAttributes (
686 IN EFI_PHYSICAL_ADDRESS BaseAddress,
687 IN UINT64 Length,
688 IN UINT64 Attributes,
689 IN EFI_PHYSICAL_ADDRESS VirtualMask
690 )
691 {
692 EFI_STATUS Status;
693 UINT64 ChunkLength;
694 BOOLEAN FlushTlbs;
695
696 if (Length == 0) {
697 return EFI_SUCCESS;
698 }
699
700 //
701 // Ignore invocations that only modify permission bits
702 //
703 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {
704 return EFI_SUCCESS;
705 }
706
707 FlushTlbs = FALSE;
708 while (Length > 0) {
709 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
710 Length >= TT_DESCRIPTOR_SECTION_SIZE) {
711
712 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
713
714 DEBUG ((DEBUG_PAGE,
715 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
716 BaseAddress, ChunkLength, Attributes));
717
718 Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes,
719 VirtualMask);
720
721 FlushTlbs = TRUE;
722 } else {
723
724 //
725 // Process page by page until the next section boundary, but only if
726 // we have more than a section's worth of area to deal with after that.
727 //
728 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
729 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
730 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
731 ChunkLength = Length;
732 }
733
734 DEBUG ((DEBUG_PAGE,
735 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
736 BaseAddress, ChunkLength, Attributes));
737
738 Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
739 VirtualMask, &FlushTlbs);
740 }
741
742 if (EFI_ERROR (Status)) {
743 break;
744 }
745
746 BaseAddress += ChunkLength;
747 Length -= ChunkLength;
748 }
749
750 if (FlushTlbs) {
751 ArmInvalidateTlb ();
752 }
753 return Status;
754 }
755
756 UINT64
757 EfiAttributeToArmAttribute (
758 IN UINT64 EfiAttributes
759 )
760 {
761 UINT64 ArmAttributes;
762
763 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
764 case EFI_MEMORY_UC:
765 // Map to strongly ordered
766 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
767 break;
768
769 case EFI_MEMORY_WC:
770 // Map to normal non-cachable
771 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
772 break;
773
774 case EFI_MEMORY_WT:
775 // Write through with no-allocate
776 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
777 break;
778
779 case EFI_MEMORY_WB:
780 // Write back (with allocate)
781 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
782 break;
783
784 case EFI_MEMORY_UCE:
785 default:
786 ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
787 break;
788 }
789
790 // Determine protection attributes
791 if (EfiAttributes & EFI_MEMORY_RO) {
792 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
793 } else {
794 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
795 }
796
797 // Determine eXecute Never attribute
798 if (EfiAttributes & EFI_MEMORY_XP) {
799 ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
800 }
801
802 return ArmAttributes;
803 }
804
805 EFI_STATUS
806 GetMemoryRegionPage (
807 IN UINT32 *PageTable,
808 IN OUT UINTN *BaseAddress,
809 OUT UINTN *RegionLength,
810 OUT UINTN *RegionAttributes
811 )
812 {
813 UINT32 PageAttributes;
814 UINT32 TableIndex;
815 UINT32 PageDescriptor;
816
817 // Convert the section attributes into page attributes
818 PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);
819
820 // Calculate index into first level translation table for start of modification
821 TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
822 ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);
823
824 // Go through the page table to find the end of the section
825 for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {
826 // Get the section at the given index
827 PageDescriptor = PageTable[TableIndex];
828
829 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {
830 // Case: End of the boundary of the region
831 return EFI_SUCCESS;
832 } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
833 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {
834 *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;
835 } else {
836 // Case: End of the boundary of the region
837 return EFI_SUCCESS;
838 }
839 } else {
840 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
841 ASSERT(0);
842 return EFI_SUCCESS;
843 }
844 }
845
846 return EFI_NOT_FOUND;
847 }
848
849 EFI_STATUS
850 GetMemoryRegion (
851 IN OUT UINTN *BaseAddress,
852 OUT UINTN *RegionLength,
853 OUT UINTN *RegionAttributes
854 )
855 {
856 EFI_STATUS Status;
857 UINT32 TableIndex;
858 UINT32 PageAttributes;
859 UINT32 PageTableIndex;
860 UINT32 SectionDescriptor;
861 ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
862 UINT32 *PageTable;
863
864 // Initialize the arguments
865 *RegionLength = 0;
866
867 // Obtain page table base
868 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
869
870 // Calculate index into first level translation table for start of modification
871 TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
872 ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);
873
874 // Get the section at the given index
875 SectionDescriptor = FirstLevelTable[TableIndex];
876
877 // If 'BaseAddress' belongs to the section then round it to the section boundary
878 if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
879 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))
880 {
881 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;
882 *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;
883 } else {
884 // Otherwise, we round it to the page boundary
885 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;
886
887 // Get the attribute at the page table level (Level 2)
888 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
889
890 // Calculate index into first level translation table for start of modification
891 PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
892 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
893
894 PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
895 *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |
896 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
897 }
898
899 for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {
900 // Get the section at the given index
901 SectionDescriptor = FirstLevelTable[TableIndex];
902
903 // If the entry is a level-2 page table then we scan it to find the end of the region
904 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) {
905 // Extract the page table location from the descriptor
906 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
907
908 // Scan the page table to find the end of the region.
909 Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);
910
911 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
912 if (Status == EFI_SUCCESS) {
913 break;
914 }
915 } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
916 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {
917 if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {
918 // If the attributes of the section differ from the one targeted then we exit the loop
919 break;
920 } else {
921 *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;
922 }
923 } else {
924 // If we are on an invalid section then it means it is the end of our section.
925 break;
926 }
927 }
928
929 return EFI_SUCCESS;
930 }