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