]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/ArmV6/Mmu.c
18778f3ac6636f41f7bc4ceb4408f0a9fc0e2ee9
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / ArmV6 / 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 Status = SyncCacheConfigPage (
301 i,FirstLevelTable[i],
302 NumberOfDescriptors, MemorySpaceMap,
303 &NextRegionBase,&NextRegionLength,&NextSectionAttributes);
304 ASSERT_EFI_ERROR (Status);
305 } else {
306 // We do not support yet 16MB sections
307 ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);
308
309 // start on a new region
310 if (NextSectionAttributes != 0) {
311 // Convert Section Attributes into GCD Attributes
312 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
313 ASSERT_EFI_ERROR (Status);
314
315 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
316 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
317
318 NextRegionLength = 0;
319 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
320 NextSectionAttributes = 0;
321 }
322 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
323 }
324 } // section entry loop
325
326 if (NextSectionAttributes != 0) {
327 // Convert Section Attributes into GCD Attributes
328 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
329 ASSERT_EFI_ERROR (Status);
330
331 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
332 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
333 }
334
335 FreePool (MemorySpaceMap);
336
337 return EFI_SUCCESS;
338 }
339
340
341
342 EFI_STATUS
343 UpdatePageEntries (
344 IN EFI_PHYSICAL_ADDRESS BaseAddress,
345 IN UINT64 Length,
346 IN UINT64 Attributes,
347 IN EFI_PHYSICAL_ADDRESS VirtualMask
348 )
349 {
350 EFI_STATUS Status;
351 UINT32 EntryValue;
352 UINT32 EntryMask;
353 UINT32 FirstLevelIdx;
354 UINT32 Offset;
355 UINT32 NumPageEntries;
356 UINT32 Descriptor;
357 UINT32 p;
358 UINT32 PageTableIndex;
359 UINT32 PageTableEntry;
360 UINT32 CurrentPageTableEntry;
361 VOID *Mva;
362
363 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
364 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
365
366 Status = EFI_SUCCESS;
367
368 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
369 // EntryValue: values at bit positions specified by EntryMask
370 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;
371 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
372 // Although the PI spec is unclear on this the GCD guarantees that only
373 // one Attribute bit is set at a time, so we can safely use a switch statement
374 switch (Attributes) {
375 case EFI_MEMORY_UC:
376 // modify cacheability attributes
377 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
378 // map to strongly ordered
379 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
380 break;
381
382 case EFI_MEMORY_WC:
383 // modify cacheability attributes
384 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
385 // map to normal non-cachable
386 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
387 break;
388
389 case EFI_MEMORY_WT:
390 // modify cacheability attributes
391 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
392 // write through with no-allocate
393 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
394 break;
395
396 case EFI_MEMORY_WB:
397 // modify cacheability attributes
398 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
399 // write back (with allocate)
400 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
401 break;
402
403 case EFI_MEMORY_WP:
404 case EFI_MEMORY_XP:
405 case EFI_MEMORY_UCE:
406 // cannot be implemented UEFI definition unclear for ARM
407 // Cause a page fault if these ranges are accessed.
408 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;
409 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
410 break;
411
412 default:
413 return EFI_UNSUPPORTED;
414 }
415
416 // Obtain page table base
417 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
418
419 // Calculate number of 4KB page table entries to change
420 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
421
422 // Iterate for the number of 4KB pages to change
423 Offset = 0;
424 for(p = 0; p < NumPageEntries; p++) {
425 // Calculate index into first level translation table for page table value
426
427 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
428 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
429
430 // Read the descriptor from the first level page table
431 Descriptor = FirstLevelTable[FirstLevelIdx];
432
433 // Does this descriptor need to be converted from section entry to 4K pages?
434 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
435 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
436 if (EFI_ERROR(Status)) {
437 // Exit for loop
438 break;
439 }
440
441 // Re-read descriptor
442 Descriptor = FirstLevelTable[FirstLevelIdx];
443 }
444
445 // Obtain page table base address
446 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
447
448 // Calculate index into the page table
449 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
450 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
451
452 // Get the entry
453 CurrentPageTableEntry = PageTable[PageTableIndex];
454
455 // Mask off appropriate fields
456 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
457
458 // Mask in new attributes and/or permissions
459 PageTableEntry |= EntryValue;
460
461 if (VirtualMask != 0) {
462 // Make this virtual address point at a physical page
463 PageTableEntry &= ~VirtualMask;
464 }
465
466 if (CurrentPageTableEntry != PageTableEntry) {
467 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
468 if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
469 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
470 // Note assumes switch(Attributes), not ARMv7 possibilities
471 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
472 }
473
474 // Only need to update if we are changing the entry
475 PageTable[PageTableIndex] = PageTableEntry;
476 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
477 }
478
479 Status = EFI_SUCCESS;
480 Offset += TT_DESCRIPTOR_PAGE_SIZE;
481
482 } // End first level translation table loop
483
484 return Status;
485 }
486
487
488
489 EFI_STATUS
490 UpdateSectionEntries (
491 IN EFI_PHYSICAL_ADDRESS BaseAddress,
492 IN UINT64 Length,
493 IN UINT64 Attributes,
494 IN EFI_PHYSICAL_ADDRESS VirtualMask
495 )
496 {
497 EFI_STATUS Status = EFI_SUCCESS;
498 UINT32 EntryMask;
499 UINT32 EntryValue;
500 UINT32 FirstLevelIdx;
501 UINT32 NumSections;
502 UINT32 i;
503 UINT32 CurrentDescriptor;
504 UINT32 Descriptor;
505 VOID *Mva;
506 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
507
508 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
509 // EntryValue: values at bit positions specified by EntryMask
510
511 // Make sure we handle a section range that is unmapped
512 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;
513 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
514
515 // Although the PI spec is unclear on this the GCD guarantees that only
516 // one Attribute bit is set at a time, so we can safely use a switch statement
517 switch(Attributes) {
518 case EFI_MEMORY_UC:
519 // modify cacheability attributes
520 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
521 // map to strongly ordered
522 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
523 break;
524
525 case EFI_MEMORY_WC:
526 // modify cacheability attributes
527 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
528 // map to normal non-cachable
529 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
530 break;
531
532 case EFI_MEMORY_WT:
533 // modify cacheability attributes
534 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
535 // write through with no-allocate
536 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
537 break;
538
539 case EFI_MEMORY_WB:
540 // modify cacheability attributes
541 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
542 // write back (with allocate)
543 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
544 break;
545
546 case EFI_MEMORY_WP:
547 case EFI_MEMORY_XP:
548 case EFI_MEMORY_RP:
549 case EFI_MEMORY_UCE:
550 // cannot be implemented UEFI definition unclear for ARM
551 // Cause a page fault if these ranges are accessed.
552 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
553 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
554 break;
555
556
557 default:
558 return EFI_UNSUPPORTED;
559 }
560
561 // obtain page table base
562 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
563
564 // calculate index into first level translation table for start of modification
565 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
566 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
567
568 // calculate number of 1MB first level entries this applies to
569 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
570
571 // iterate through each descriptor
572 for(i=0; i<NumSections; i++) {
573 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
574
575 // has this descriptor already been coverted to pages?
576 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
577 // forward this 1MB range to page table function instead
578 Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
579 } else {
580 // still a section entry
581
582 // mask off appropriate fields
583 Descriptor = CurrentDescriptor & ~EntryMask;
584
585 // mask in new attributes and/or permissions
586 Descriptor |= EntryValue;
587 if (VirtualMask != 0) {
588 Descriptor &= ~VirtualMask;
589 }
590
591 if (CurrentDescriptor != Descriptor) {
592 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
593 if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
594 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
595 // Note assumes switch(Attributes), not ARMv7 possabilities
596 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
597 }
598
599 // Only need to update if we are changing the descriptor
600 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
601 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
602 }
603
604 Status = EFI_SUCCESS;
605 }
606 }
607
608 return Status;
609 }
610
611 EFI_STATUS
612 ConvertSectionToPages (
613 IN EFI_PHYSICAL_ADDRESS BaseAddress
614 )
615 {
616 EFI_STATUS Status;
617 EFI_PHYSICAL_ADDRESS PageTableAddr;
618 UINT32 FirstLevelIdx;
619 UINT32 SectionDescriptor;
620 UINT32 PageTableDescriptor;
621 UINT32 PageDescriptor;
622 UINT32 Index;
623
624 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
625 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
626
627 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
628
629 // Obtain page table base
630 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
631
632 // Calculate index into first level translation table for start of modification
633 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
634 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
635
636 // Get section attributes and convert to page attributes
637 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
638 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
639
640 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
641 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
642 if (EFI_ERROR(Status)) {
643 return Status;
644 }
645
646 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
647
648 // Write the page table entries out
649 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
650 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
651 }
652
653 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
654 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
655
656 // Formulate page table entry, Domain=0, NS=0
657 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
658
659 // Write the page table entry out, replacing section entry
660 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
661
662 return EFI_SUCCESS;
663 }
664
665
666
667 EFI_STATUS
668 SetMemoryAttributes (
669 IN EFI_PHYSICAL_ADDRESS BaseAddress,
670 IN UINT64 Length,
671 IN UINT64 Attributes,
672 IN EFI_PHYSICAL_ADDRESS VirtualMask
673 )
674 {
675 EFI_STATUS Status;
676
677 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {
678 // Is the base and length a multiple of 1 MB?
679 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
680 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);
681 } else {
682 // Base and/or length is not a multiple of 1 MB
683 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
684 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);
685 }
686
687 // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
688 // flush and invalidate pages
689 //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
690 ArmCleanInvalidateDataCache ();
691
692 ArmInvalidateInstructionCache ();
693
694 // Invalidate all TLB entries so changes are synced
695 ArmInvalidateTlb ();
696
697 return Status;
698 }
699
700 UINT64
701 EfiAttributeToArmAttribute (
702 IN UINT64 EfiAttributes
703 )
704 {
705 UINT64 ArmAttributes;
706
707 switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
708 case EFI_MEMORY_UC:
709 // Map to strongly ordered
710 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
711 break;
712
713 case EFI_MEMORY_WC:
714 // Map to normal non-cachable
715 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
716 break;
717
718 case EFI_MEMORY_WT:
719 // Write through with no-allocate
720 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
721 break;
722
723 case EFI_MEMORY_WB:
724 // Write back (with allocate)
725 ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
726 break;
727
728 case EFI_MEMORY_WP:
729 case EFI_MEMORY_XP:
730 case EFI_MEMORY_RP:
731 case EFI_MEMORY_UCE:
732 default:
733 // Cannot be implemented UEFI definition unclear for ARM
734 // Cause a page fault if these ranges are accessed.
735 ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
736 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes));
737 break;
738 }
739
740 // Determine protection attributes
741 if (EfiAttributes & EFI_MEMORY_WP) {
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 }