]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
ArmPkg/CpuDxe: translate invalid memory types in EfiAttributeToArmAttribute
[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 )
355 {
356 EFI_STATUS Status;
357 UINT32 EntryValue;
358 UINT32 EntryMask;
359 UINT32 FirstLevelIdx;
360 UINT32 Offset;
361 UINT32 NumPageEntries;
362 UINT32 Descriptor;
363 UINT32 p;
364 UINT32 PageTableIndex;
365 UINT32 PageTableEntry;
366 UINT32 CurrentPageTableEntry;
367 VOID *Mva;
368
369 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
370 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
371
372 Status = EFI_SUCCESS;
373
374 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
375 // EntryValue: values at bit positions specified by EntryMask
376 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;
377 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
378 // Although the PI spec is unclear on this the GCD guarantees that only
379 // one Attribute bit is set at a time, so we can safely use a switch statement
380 switch (Attributes) {
381 case EFI_MEMORY_UC:
382 // modify cacheability attributes
383 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
384 // map to strongly ordered
385 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
386 break;
387
388 case EFI_MEMORY_WC:
389 // modify cacheability attributes
390 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
391 // map to normal non-cachable
392 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
393 break;
394
395 case EFI_MEMORY_WT:
396 // modify cacheability attributes
397 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
398 // write through with no-allocate
399 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
400 break;
401
402 case EFI_MEMORY_WB:
403 // modify cacheability attributes
404 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
405 // write back (with allocate)
406 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
407 break;
408
409 case EFI_MEMORY_WP:
410 case EFI_MEMORY_XP:
411 case EFI_MEMORY_UCE:
412 // cannot be implemented UEFI definition unclear for ARM
413 // Cause a page fault if these ranges are accessed.
414 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;
415 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
416 break;
417
418 default:
419 return EFI_UNSUPPORTED;
420 }
421
422 // Obtain page table base
423 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
424
425 // Calculate number of 4KB page table entries to change
426 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
427
428 // Iterate for the number of 4KB pages to change
429 Offset = 0;
430 for(p = 0; p < NumPageEntries; p++) {
431 // Calculate index into first level translation table for page table value
432
433 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
434 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
435
436 // Read the descriptor from the first level page table
437 Descriptor = FirstLevelTable[FirstLevelIdx];
438
439 // Does this descriptor need to be converted from section entry to 4K pages?
440 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
441 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
442 if (EFI_ERROR(Status)) {
443 // Exit for loop
444 break;
445 }
446
447 // Re-read descriptor
448 Descriptor = FirstLevelTable[FirstLevelIdx];
449 }
450
451 // Obtain page table base address
452 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
453
454 // Calculate index into the page table
455 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
456 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
457
458 // Get the entry
459 CurrentPageTableEntry = PageTable[PageTableIndex];
460
461 // Mask off appropriate fields
462 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
463
464 // Mask in new attributes and/or permissions
465 PageTableEntry |= EntryValue;
466
467 if (VirtualMask != 0) {
468 // Make this virtual address point at a physical page
469 PageTableEntry &= ~VirtualMask;
470 }
471
472 if (CurrentPageTableEntry != PageTableEntry) {
473 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
474 if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
475 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
476 // Note assumes switch(Attributes), not ARMv7 possibilities
477 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
478 }
479
480 // Only need to update if we are changing the entry
481 PageTable[PageTableIndex] = PageTableEntry;
482 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
483 }
484
485 Status = EFI_SUCCESS;
486 Offset += TT_DESCRIPTOR_PAGE_SIZE;
487
488 } // End first level translation table loop
489
490 return Status;
491 }
492
493
494
495 EFI_STATUS
496 UpdateSectionEntries (
497 IN EFI_PHYSICAL_ADDRESS BaseAddress,
498 IN UINT64 Length,
499 IN UINT64 Attributes,
500 IN EFI_PHYSICAL_ADDRESS VirtualMask
501 )
502 {
503 EFI_STATUS Status = EFI_SUCCESS;
504 UINT32 EntryMask;
505 UINT32 EntryValue;
506 UINT32 FirstLevelIdx;
507 UINT32 NumSections;
508 UINT32 i;
509 UINT32 CurrentDescriptor;
510 UINT32 Descriptor;
511 VOID *Mva;
512 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
513
514 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
515 // EntryValue: values at bit positions specified by EntryMask
516
517 // Make sure we handle a section range that is unmapped
518 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;
519 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
520
521 // Although the PI spec is unclear on this the GCD guarantees that only
522 // one Attribute bit is set at a time, so we can safely use a switch statement
523 switch(Attributes) {
524 case EFI_MEMORY_UC:
525 // modify cacheability attributes
526 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
527 // map to strongly ordered
528 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
529 break;
530
531 case EFI_MEMORY_WC:
532 // modify cacheability attributes
533 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
534 // map to normal non-cachable
535 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
536 break;
537
538 case EFI_MEMORY_WT:
539 // modify cacheability attributes
540 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
541 // write through with no-allocate
542 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
543 break;
544
545 case EFI_MEMORY_WB:
546 // modify cacheability attributes
547 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
548 // write back (with allocate)
549 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
550 break;
551
552 case EFI_MEMORY_WP:
553 case EFI_MEMORY_XP:
554 case EFI_MEMORY_RP:
555 case EFI_MEMORY_UCE:
556 // cannot be implemented UEFI definition unclear for ARM
557 // Cause a page fault if these ranges are accessed.
558 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
559 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
560 break;
561
562
563 default:
564 return EFI_UNSUPPORTED;
565 }
566
567 // obtain page table base
568 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
569
570 // calculate index into first level translation table for start of modification
571 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
572 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
573
574 // calculate number of 1MB first level entries this applies to
575 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
576
577 // iterate through each descriptor
578 for(i=0; i<NumSections; i++) {
579 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
580
581 // has this descriptor already been coverted to pages?
582 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
583 // forward this 1MB range to page table function instead
584 Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
585 } else {
586 // still a section entry
587
588 // mask off appropriate fields
589 Descriptor = CurrentDescriptor & ~EntryMask;
590
591 // mask in new attributes and/or permissions
592 Descriptor |= EntryValue;
593 if (VirtualMask != 0) {
594 Descriptor &= ~VirtualMask;
595 }
596
597 if (CurrentDescriptor != Descriptor) {
598 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
599 if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
600 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
601 // Note assumes switch(Attributes), not ARMv7 possabilities
602 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
603 }
604
605 // Only need to update if we are changing the descriptor
606 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
607 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
608 }
609
610 Status = EFI_SUCCESS;
611 }
612 }
613
614 return Status;
615 }
616
617 EFI_STATUS
618 ConvertSectionToPages (
619 IN EFI_PHYSICAL_ADDRESS BaseAddress
620 )
621 {
622 EFI_STATUS Status;
623 EFI_PHYSICAL_ADDRESS PageTableAddr;
624 UINT32 FirstLevelIdx;
625 UINT32 SectionDescriptor;
626 UINT32 PageTableDescriptor;
627 UINT32 PageDescriptor;
628 UINT32 Index;
629
630 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
631 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
632
633 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
634
635 // Obtain page table base
636 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
637
638 // Calculate index into first level translation table for start of modification
639 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
640 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
641
642 // Get section attributes and convert to page attributes
643 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
644 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
645
646 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
647 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
648 if (EFI_ERROR(Status)) {
649 return Status;
650 }
651
652 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
653
654 // Write the page table entries out
655 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
656 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
657 }
658
659 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
660 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
661
662 // Formulate page table entry, Domain=0, NS=0
663 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
664
665 // Write the page table entry out, replacing section entry
666 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
667
668 return EFI_SUCCESS;
669 }
670
671
672
673 EFI_STATUS
674 SetMemoryAttributes (
675 IN EFI_PHYSICAL_ADDRESS BaseAddress,
676 IN UINT64 Length,
677 IN UINT64 Attributes,
678 IN EFI_PHYSICAL_ADDRESS VirtualMask
679 )
680 {
681 EFI_STATUS Status;
682
683 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {
684 // Is the base and length a multiple of 1 MB?
685 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
686 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);
687 } else {
688 // Base and/or length is not a multiple of 1 MB
689 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
690 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);
691 }
692
693 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
694 // flush and invalidate pages
695 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
696 ArmCleanInvalidateDataCache ();
697
698 ArmInvalidateInstructionCache ();
699
700 // Invalidate all TLB entries so changes are synced
701 ArmInvalidateTlb ();
702
703 return Status;
704 }
705
706 UINT64
707 EfiAttributeToArmAttribute (
708 IN UINT64 EfiAttributes
709 )
710 {
711 UINT64 ArmAttributes;
712
713 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
714 case EFI_MEMORY_UC:
715 // Map to strongly ordered
716 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
717 break;
718
719 case EFI_MEMORY_WC:
720 // Map to normal non-cachable
721 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
722 break;
723
724 case EFI_MEMORY_WT:
725 // Write through with no-allocate
726 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
727 break;
728
729 case EFI_MEMORY_WB:
730 // Write back (with allocate)
731 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
732 break;
733
734 case EFI_MEMORY_UCE:
735 default:
736 ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
737 break;
738 }
739
740 // Determine protection attributes
741 if (EfiAttributes & EFI_MEMORY_RO) {
742 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
743 } else {
744 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
745 }
746
747 // Determine eXecute Never attribute
748 if (EfiAttributes & EFI_MEMORY_XP) {
749 ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
750 }
751
752 return ArmAttributes;
753 }
754
755 EFI_STATUS
756 GetMemoryRegionPage (
757 IN UINT32 *PageTable,
758 IN OUT UINTN *BaseAddress,
759 OUT UINTN *RegionLength,
760 OUT UINTN *RegionAttributes
761 )
762 {
763 UINT32 PageAttributes;
764 UINT32 TableIndex;
765 UINT32 PageDescriptor;
766
767 // Convert the section attributes into page attributes
768 PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);
769
770 // Calculate index into first level translation table for start of modification
771 TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
772 ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);
773
774 // Go through the page table to find the end of the section
775 for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {
776 // Get the section at the given index
777 PageDescriptor = PageTable[TableIndex];
778
779 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {
780 // Case: End of the boundary of the region
781 return EFI_SUCCESS;
782 } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
783 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {
784 *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;
785 } else {
786 // Case: End of the boundary of the region
787 return EFI_SUCCESS;
788 }
789 } else {
790 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
791 ASSERT(0);
792 return EFI_SUCCESS;
793 }
794 }
795
796 return EFI_NOT_FOUND;
797 }
798
799 EFI_STATUS
800 GetMemoryRegion (
801 IN OUT UINTN *BaseAddress,
802 OUT UINTN *RegionLength,
803 OUT UINTN *RegionAttributes
804 )
805 {
806 EFI_STATUS Status;
807 UINT32 TableIndex;
808 UINT32 PageAttributes;
809 UINT32 PageTableIndex;
810 UINT32 SectionDescriptor;
811 ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
812 UINT32 *PageTable;
813
814 // Initialize the arguments
815 *RegionLength = 0;
816
817 // Obtain page table base
818 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
819
820 // Calculate index into first level translation table for start of modification
821 TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
822 ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);
823
824 // Get the section at the given index
825 SectionDescriptor = FirstLevelTable[TableIndex];
826
827 // If 'BaseAddress' belongs to the section then round it to the section boundary
828 if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
829 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))
830 {
831 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;
832 *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;
833 } else {
834 // Otherwise, we round it to the page boundary
835 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;
836
837 // Get the attribute at the page table level (Level 2)
838 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
839
840 // Calculate index into first level translation table for start of modification
841 PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
842 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
843
844 PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
845 *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |
846 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
847 }
848
849 for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {
850 // Get the section at the given index
851 SectionDescriptor = FirstLevelTable[TableIndex];
852
853 // If the entry is a level-2 page table then we scan it to find the end of the region
854 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) {
855 // Extract the page table location from the descriptor
856 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
857
858 // Scan the page table to find the end of the region.
859 Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);
860
861 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
862 if (Status == EFI_SUCCESS) {
863 break;
864 }
865 } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
866 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {
867 if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {
868 // If the attributes of the section differ from the one targeted then we exit the loop
869 break;
870 } else {
871 *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;
872 }
873 } else {
874 // If we are on an invalid section then it means it is the end of our section.
875 break;
876 }
877 }
878
879 return EFI_SUCCESS;
880 }