]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
ArmPkg/CpuDxe: ARM: ignore page table updates that only change permissions
[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 //
684 // Ignore invocations that only modify permission bits
685 //
686 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {
687 return EFI_SUCCESS;
688 }
689
690 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {
691 // Is the base and length a multiple of 1 MB?
692 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
693 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);
694 } else {
695 // Base and/or length is not a multiple of 1 MB
696 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
697 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);
698 }
699
700 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
701 // flush and invalidate pages
702 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
703 ArmCleanInvalidateDataCache ();
704
705 ArmInvalidateInstructionCache ();
706
707 // Invalidate all TLB entries so changes are synced
708 ArmInvalidateTlb ();
709
710 return Status;
711 }
712
713 UINT64
714 EfiAttributeToArmAttribute (
715 IN UINT64 EfiAttributes
716 )
717 {
718 UINT64 ArmAttributes;
719
720 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
721 case EFI_MEMORY_UC:
722 // Map to strongly ordered
723 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
724 break;
725
726 case EFI_MEMORY_WC:
727 // Map to normal non-cachable
728 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
729 break;
730
731 case EFI_MEMORY_WT:
732 // Write through with no-allocate
733 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
734 break;
735
736 case EFI_MEMORY_WB:
737 // Write back (with allocate)
738 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
739 break;
740
741 case EFI_MEMORY_UCE:
742 default:
743 ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
744 break;
745 }
746
747 // Determine protection attributes
748 if (EfiAttributes & EFI_MEMORY_RO) {
749 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
750 } else {
751 ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
752 }
753
754 // Determine eXecute Never attribute
755 if (EfiAttributes & EFI_MEMORY_XP) {
756 ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
757 }
758
759 return ArmAttributes;
760 }
761
762 EFI_STATUS
763 GetMemoryRegionPage (
764 IN UINT32 *PageTable,
765 IN OUT UINTN *BaseAddress,
766 OUT UINTN *RegionLength,
767 OUT UINTN *RegionAttributes
768 )
769 {
770 UINT32 PageAttributes;
771 UINT32 TableIndex;
772 UINT32 PageDescriptor;
773
774 // Convert the section attributes into page attributes
775 PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);
776
777 // Calculate index into first level translation table for start of modification
778 TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
779 ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT);
780
781 // Go through the page table to find the end of the section
782 for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) {
783 // Get the section at the given index
784 PageDescriptor = PageTable[TableIndex];
785
786 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) {
787 // Case: End of the boundary of the region
788 return EFI_SUCCESS;
789 } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
790 if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) {
791 *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE;
792 } else {
793 // Case: End of the boundary of the region
794 return EFI_SUCCESS;
795 }
796 } else {
797 // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region.
798 ASSERT(0);
799 return EFI_SUCCESS;
800 }
801 }
802
803 return EFI_NOT_FOUND;
804 }
805
806 EFI_STATUS
807 GetMemoryRegion (
808 IN OUT UINTN *BaseAddress,
809 OUT UINTN *RegionLength,
810 OUT UINTN *RegionAttributes
811 )
812 {
813 EFI_STATUS Status;
814 UINT32 TableIndex;
815 UINT32 PageAttributes;
816 UINT32 PageTableIndex;
817 UINT32 SectionDescriptor;
818 ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
819 UINT32 *PageTable;
820
821 // Initialize the arguments
822 *RegionLength = 0;
823
824 // Obtain page table base
825 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
826
827 // Calculate index into first level translation table for start of modification
828 TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
829 ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT);
830
831 // Get the section at the given index
832 SectionDescriptor = FirstLevelTable[TableIndex];
833
834 // If 'BaseAddress' belongs to the section then round it to the section boundary
835 if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
836 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION))
837 {
838 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;
839 *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK;
840 } else {
841 // Otherwise, we round it to the page boundary
842 *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK;
843
844 // Get the attribute at the page table level (Level 2)
845 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
846
847 // Calculate index into first level translation table for start of modification
848 PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
849 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
850
851 PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
852 *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |
853 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
854 }
855
856 for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) {
857 // Get the section at the given index
858 SectionDescriptor = FirstLevelTable[TableIndex];
859
860 // If the entry is a level-2 page table then we scan it to find the end of the region
861 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) {
862 // Extract the page table location from the descriptor
863 PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
864
865 // Scan the page table to find the end of the region.
866 Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);
867
868 // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
869 if (Status == EFI_SUCCESS) {
870 break;
871 }
872 } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) ||
873 ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) {
874 if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) {
875 // If the attributes of the section differ from the one targeted then we exit the loop
876 break;
877 } else {
878 *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE;
879 }
880 } else {
881 // If we are on an invalid section then it means it is the end of our section.
882 break;
883 }
884 }
885
886 return EFI_SUCCESS;
887 }