]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/Mmu.c
Patch from open source community for CryptoPkg to allow it to build for ARM using...
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / 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
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14
15 --*/
16
17 #include "CpuDxe.h"
18 //FIXME: Remove this ARMv7 specific header
19 #include <Chipset/ArmV7.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 /**
156 Searches memory descriptors covered by given memory range.
157
158 This function searches into the Gcd Memory Space for descriptors
159 (from StartIndex to EndIndex) that contains the memory range
160 specified by BaseAddress and Length.
161
162 @param MemorySpaceMap Gcd Memory Space Map as array.
163 @param NumberOfDescriptors Number of descriptors in map.
164 @param BaseAddress BaseAddress for the requested range.
165 @param Length Length for the requested range.
166 @param StartIndex Start index into the Gcd Memory Space Map.
167 @param EndIndex End index into the Gcd Memory Space Map.
168
169 @retval EFI_SUCCESS Search successfully.
170 @retval EFI_NOT_FOUND The requested descriptors does not exist.
171
172 **/
173 EFI_STATUS
174 SearchGcdMemorySpaces (
175 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
176 IN UINTN NumberOfDescriptors,
177 IN EFI_PHYSICAL_ADDRESS BaseAddress,
178 IN UINT64 Length,
179 OUT UINTN *StartIndex,
180 OUT UINTN *EndIndex
181 )
182 {
183 UINTN Index;
184
185 *StartIndex = 0;
186 *EndIndex = 0;
187 for (Index = 0; Index < NumberOfDescriptors; Index++) {
188 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress &&
189 BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
190 *StartIndex = Index;
191 }
192 if (BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress &&
193 BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
194 *EndIndex = Index;
195 return EFI_SUCCESS;
196 }
197 }
198 return EFI_NOT_FOUND;
199 }
200
201
202 /**
203 Sets the attributes for a specified range in Gcd Memory Space Map.
204
205 This function sets the attributes for a specified range in
206 Gcd Memory Space Map.
207
208 @param MemorySpaceMap Gcd Memory Space Map as array
209 @param NumberOfDescriptors Number of descriptors in map
210 @param BaseAddress BaseAddress for the range
211 @param Length Length for the range
212 @param Attributes Attributes to set
213
214 @retval EFI_SUCCESS Memory attributes set successfully
215 @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space
216
217 **/
218 EFI_STATUS
219 SetGcdMemorySpaceAttributes (
220 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
221 IN UINTN NumberOfDescriptors,
222 IN EFI_PHYSICAL_ADDRESS BaseAddress,
223 IN UINT64 Length,
224 IN UINT64 Attributes
225 )
226 {
227 EFI_STATUS Status;
228 UINTN Index;
229 UINTN StartIndex;
230 UINTN EndIndex;
231 EFI_PHYSICAL_ADDRESS RegionStart;
232 UINT64 RegionLength;
233
234 //
235 // Get all memory descriptors covered by the memory range
236 //
237 Status = SearchGcdMemorySpaces (
238 MemorySpaceMap,
239 NumberOfDescriptors,
240 BaseAddress,
241 Length,
242 &StartIndex,
243 &EndIndex
244 );
245 if (EFI_ERROR (Status)) {
246 return Status;
247 }
248
249 //
250 // Go through all related descriptors and set attributes accordingly
251 //
252 for (Index = StartIndex; Index <= EndIndex; Index++) {
253 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
254 continue;
255 }
256 //
257 // Calculate the start and end address of the overlapping range
258 //
259 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
260 RegionStart = BaseAddress;
261 } else {
262 RegionStart = MemorySpaceMap[Index].BaseAddress;
263 }
264 if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
265 RegionLength = BaseAddress + Length - RegionStart;
266 } else {
267 RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
268 }
269 //
270 // Set memory attributes according to MTRR attribute and the original attribute of descriptor
271 //
272 gDS->SetMemorySpaceAttributes (
273 RegionStart,
274 RegionLength,
275 (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
276 );
277 }
278
279 return EFI_SUCCESS;
280 }
281
282 EFI_STATUS
283 SyncCacheConfigPage (
284 IN UINT32 SectionIndex,
285 IN UINT32 FirstLevelDescriptor,
286 IN UINTN NumberOfDescriptors,
287 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
288 IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase,
289 IN OUT UINT64 *NextRegionLength,
290 IN OUT UINT32 *NextSectionAttributes
291 )
292 {
293 EFI_STATUS Status;
294 UINT32 i;
295 volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable;
296 UINT32 NextPageAttributes = 0;
297 UINT32 PageAttributes = 0;
298 UINT32 BaseAddress;
299 UINT64 GcdAttributes;
300
301 // Get the Base Address from FirstLevelDescriptor;
302 BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
303
304 // Convert SectionAttributes into PageAttributes
305 NextPageAttributes =
306 TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) |
307 TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes);
308
309 // obtain page table base
310 SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
311
312 for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {
313 if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
314 // extract attributes (cacheability and permissions)
315 PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);
316
317 if (NextPageAttributes == 0) {
318 // start on a new region
319 *NextRegionLength = 0;
320 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
321 NextPageAttributes = PageAttributes;
322 } else if (PageAttributes != NextPageAttributes) {
323 // Convert Section Attributes into GCD Attributes
324 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
325 ASSERT_EFI_ERROR (Status);
326
327 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
328 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
329
330 // start on a new region
331 *NextRegionLength = 0;
332 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
333 NextPageAttributes = PageAttributes;
334 }
335 } else if (NextPageAttributes != 0) {
336 // Convert Page Attributes into GCD Attributes
337 Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
338 ASSERT_EFI_ERROR (Status);
339
340 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
341 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
342
343 *NextRegionLength = 0;
344 *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
345 NextPageAttributes = 0;
346 }
347 *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE;
348 }
349
350 // Convert back PageAttributes into SectionAttributes
351 *NextSectionAttributes =
352 TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) |
353 TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes);
354
355 return EFI_SUCCESS;
356 }
357
358 EFI_STATUS
359 SyncCacheConfig (
360 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol
361 )
362 {
363 EFI_STATUS Status;
364 UINT32 i;
365 EFI_PHYSICAL_ADDRESS NextRegionBase;
366 UINT64 NextRegionLength;
367 UINT32 NextSectionAttributes = 0;
368 UINT32 SectionAttributes = 0;
369 UINT64 GcdAttributes;
370 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
371 UINTN NumberOfDescriptors;
372 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
373
374
375 DEBUG ((EFI_D_PAGE, "SyncCacheConfig()\n"));
376
377 // This code assumes MMU is enabled and filed with section translations
378 ASSERT (ArmMmuEnabled ());
379
380 //
381 // Get the memory space map from GCD
382 //
383 MemorySpaceMap = NULL;
384 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
385 ASSERT_EFI_ERROR (Status);
386
387
388 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs
389 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a
390 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
391 // a client) to update its copy of the attributes. This is bad architecture and should be replaced
392 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
393
394 // obtain page table base
395 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());
396
397 // Get the first region
398 NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
399
400 // iterate through each 1MB descriptor
401 NextRegionBase = NextRegionLength = 0;
402 for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {
403 if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
404 // extract attributes (cacheability and permissions)
405 SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
406
407 if (NextSectionAttributes == 0) {
408 // start on a new region
409 NextRegionLength = 0;
410 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
411 NextSectionAttributes = SectionAttributes;
412 } else if (SectionAttributes != NextSectionAttributes) {
413 // Convert Section Attributes into GCD Attributes
414 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
415 ASSERT_EFI_ERROR (Status);
416
417 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
418 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
419
420 // start on a new region
421 NextRegionLength = 0;
422 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
423 NextSectionAttributes = SectionAttributes;
424 }
425 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
426 } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) {
427 Status = SyncCacheConfigPage (
428 i,FirstLevelTable[i],
429 NumberOfDescriptors, MemorySpaceMap,
430 &NextRegionBase,&NextRegionLength,&NextSectionAttributes);
431 ASSERT_EFI_ERROR (Status);
432 } else {
433 // We do not support yet 16MB sections
434 ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);
435
436 // start on a new region
437 if (NextSectionAttributes != 0) {
438 // Convert Section Attributes into GCD Attributes
439 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
440 ASSERT_EFI_ERROR (Status);
441
442 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
443 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
444
445 NextRegionLength = 0;
446 NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
447 NextSectionAttributes = 0;
448 }
449 NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
450 }
451 } // section entry loop
452
453 if (NextSectionAttributes != 0) {
454 // Convert Section Attributes into GCD Attributes
455 Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
456 ASSERT_EFI_ERROR (Status);
457
458 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
459 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
460 }
461
462 return EFI_SUCCESS;
463 }
464
465
466
467 EFI_STATUS
468 UpdatePageEntries (
469 IN EFI_PHYSICAL_ADDRESS BaseAddress,
470 IN UINT64 Length,
471 IN UINT64 Attributes,
472 IN EFI_PHYSICAL_ADDRESS VirtualMask
473 )
474 {
475 EFI_STATUS Status;
476 UINT32 EntryValue;
477 UINT32 EntryMask;
478 UINT32 FirstLevelIdx;
479 UINT32 Offset;
480 UINT32 NumPageEntries;
481 UINT32 Descriptor;
482 UINT32 p;
483 UINT32 PageTableIndex;
484 UINT32 PageTableEntry;
485 UINT32 CurrentPageTableEntry;
486 VOID *Mva;
487
488 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
489 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
490
491 Status = EFI_SUCCESS;
492
493 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
494 // EntryValue: values at bit positions specified by EntryMask
495 EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;
496 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
497 // Although the PI spec is unclear on this the GCD guarantees that only
498 // one Attribute bit is set at a time, so we can safely use a switch statement
499 switch (Attributes) {
500 case EFI_MEMORY_UC:
501 // modify cacheability attributes
502 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
503 if (FeaturePcdGet(PcdEfiUncachedMemoryToStronglyOrdered)) {
504 // map to strongly ordered
505 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
506 } else {
507 // map to normal non-cachable
508 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
509 }
510 break;
511
512 case EFI_MEMORY_WC:
513 // modify cacheability attributes
514 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
515 // map to normal non-cachable
516 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
517 break;
518
519 case EFI_MEMORY_WT:
520 // modify cacheability attributes
521 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
522 // write through with no-allocate
523 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
524 break;
525
526 case EFI_MEMORY_WB:
527 // modify cacheability attributes
528 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
529 // write back (with allocate)
530 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
531 break;
532
533 case EFI_MEMORY_WP:
534 case EFI_MEMORY_XP:
535 case EFI_MEMORY_UCE:
536 // cannot be implemented UEFI definition unclear for ARM
537 // Cause a page fault if these ranges are accessed.
538 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;
539 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
540 break;
541
542 default:
543 return EFI_UNSUPPORTED;
544 }
545
546 // obtain page table base
547 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
548
549 // calculate number of 4KB page table entries to change
550 NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
551
552 // iterate for the number of 4KB pages to change
553 Offset = 0;
554 for(p=0; p<NumPageEntries; p++) {
555 // calculate index into first level translation table for page table value
556
557 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
558 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
559
560 // read the descriptor from the first level page table
561 Descriptor = FirstLevelTable[FirstLevelIdx];
562
563 // does this descriptor need to be converted from section entry to 4K pages?
564 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
565 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
566 if (EFI_ERROR(Status)) {
567 // exit for loop
568 break;
569 }
570
571 // re-read descriptor
572 Descriptor = FirstLevelTable[FirstLevelIdx];
573 }
574
575 // obtain page table base address
576 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
577
578 // calculate index into the page table
579 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
580 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
581
582 // get the entry
583 CurrentPageTableEntry = PageTable[PageTableIndex];
584
585 // mask off appropriate fields
586 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
587
588 // mask in new attributes and/or permissions
589 PageTableEntry |= EntryValue;
590
591 if (VirtualMask != 0) {
592 // Make this virtual address point at a physical page
593 PageTableEntry &= ~VirtualMask;
594 }
595
596 if (CurrentPageTableEntry != PageTableEntry) {
597 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
598 if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
599 // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
600 // Note assumes switch(Attributes), not ARMv7 possibilities
601 WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
602 }
603
604 // Only need to update if we are changing the entry
605 PageTable[PageTableIndex] = PageTableEntry;
606 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
607 }
608
609 Status = EFI_SUCCESS;
610 Offset += TT_DESCRIPTOR_PAGE_SIZE;
611
612 } // end first level translation table loop
613
614 return Status;
615 }
616
617
618
619 EFI_STATUS
620 UpdateSectionEntries (
621 IN EFI_PHYSICAL_ADDRESS BaseAddress,
622 IN UINT64 Length,
623 IN UINT64 Attributes,
624 IN EFI_PHYSICAL_ADDRESS VirtualMask
625 )
626 {
627 EFI_STATUS Status = EFI_SUCCESS;
628 UINT32 EntryMask;
629 UINT32 EntryValue;
630 UINT32 FirstLevelIdx;
631 UINT32 NumSections;
632 UINT32 i;
633 UINT32 CurrentDescriptor;
634 UINT32 Descriptor;
635 VOID *Mva;
636 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
637
638 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
639 // EntryValue: values at bit positions specified by EntryMask
640
641 // Make sure we handle a section range that is unmapped
642 EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;
643 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
644
645 // Although the PI spec is unclear on this the GCD guarantees that only
646 // one Attribute bit is set at a time, so we can safely use a switch statement
647 switch(Attributes) {
648 case EFI_MEMORY_UC:
649 // modify cacheability attributes
650 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
651 if (FeaturePcdGet(PcdEfiUncachedMemoryToStronglyOrdered)) {
652 // map to strongly ordered
653 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
654 } else {
655 // map to normal non-cachable
656 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
657 }
658 break;
659
660 case EFI_MEMORY_WC:
661 // modify cacheability attributes
662 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
663 // map to normal non-cachable
664 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
665 break;
666
667 case EFI_MEMORY_WT:
668 // modify cacheability attributes
669 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
670 // write through with no-allocate
671 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
672 break;
673
674 case EFI_MEMORY_WB:
675 // modify cacheability attributes
676 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
677 // write back (with allocate)
678 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
679 break;
680
681 case EFI_MEMORY_WP:
682 case EFI_MEMORY_XP:
683 case EFI_MEMORY_RP:
684 case EFI_MEMORY_UCE:
685 // cannot be implemented UEFI definition unclear for ARM
686 // Cause a page fault if these ranges are accessed.
687 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
688 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
689 break;
690
691
692 default:
693 return EFI_UNSUPPORTED;
694 }
695
696 // obtain page table base
697 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
698
699 // calculate index into first level translation table for start of modification
700 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
701 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
702
703 // calculate number of 1MB first level entries this applies to
704 NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
705
706 // iterate through each descriptor
707 for(i=0; i<NumSections; i++) {
708 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
709
710 // has this descriptor already been coverted to pages?
711 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
712 // forward this 1MB range to page table function instead
713 Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
714 } else {
715 // still a section entry
716
717 // mask off appropriate fields
718 Descriptor = CurrentDescriptor & ~EntryMask;
719
720 // mask in new attributes and/or permissions
721 Descriptor |= EntryValue;
722 if (VirtualMask != 0) {
723 Descriptor &= ~VirtualMask;
724 }
725
726 if (CurrentDescriptor != Descriptor) {
727 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
728 if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
729 // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
730 // Note assumes switch(Attributes), not ARMv7 possabilities
731 WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
732 }
733
734 // Only need to update if we are changing the descriptor
735 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
736 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
737 }
738
739 Status = EFI_SUCCESS;
740 }
741 }
742
743 return Status;
744 }
745
746 EFI_STATUS
747 ConvertSectionToPages (
748 IN EFI_PHYSICAL_ADDRESS BaseAddress
749 )
750 {
751 EFI_STATUS Status;
752 EFI_PHYSICAL_ADDRESS PageTableAddr;
753 UINT32 FirstLevelIdx;
754 UINT32 SectionDescriptor;
755 UINT32 PageTableDescriptor;
756 UINT32 PageDescriptor;
757 UINT32 Index;
758
759 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
760 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
761
762 DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
763
764 // obtain page table base
765 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
766
767 // calculate index into first level translation table for start of modification
768 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
769 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
770
771 // get section attributes and convert to page attributes
772 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
773 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
774 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(SectionDescriptor,0);
775 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(SectionDescriptor);
776 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(SectionDescriptor,0);
777 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(SectionDescriptor);
778 PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S(SectionDescriptor);
779
780 // allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
781 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
782 if (EFI_ERROR(Status)) {
783 return Status;
784 }
785
786 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
787
788 // write the page table entries out
789 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
790 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
791 }
792
793 // flush d-cache so descriptors make it back to uncached memory for subsequent table walks
794 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
795
796 // formulate page table entry, Domain=0, NS=0
797 PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
798
799 // write the page table entry out, repalcing section entry
800 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
801
802 return EFI_SUCCESS;
803 }
804
805
806
807 EFI_STATUS
808 SetMemoryAttributes (
809 IN EFI_PHYSICAL_ADDRESS BaseAddress,
810 IN UINT64 Length,
811 IN UINT64 Attributes,
812 IN EFI_PHYSICAL_ADDRESS VirtualMask
813 )
814 {
815 EFI_STATUS Status;
816
817 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {
818 // is the base and length a multiple of 1 MB?
819 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
820 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);
821 } else {
822 // base and/or length is not a multiple of 1 MB
823 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
824 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);
825 }
826
827 // flush d-cache so descriptors make it back to uncached memory for subsequent table walks
828 // flush and invalidate pages
829 ArmCleanInvalidateDataCache ();
830
831 ArmInvalidateInstructionCache ();
832
833 // invalidate all TLB entries so changes are synced
834 ArmInvalidateTlb ();
835
836 return Status;
837 }
838
839
840 /**
841 This function modifies the attributes for the memory region specified by BaseAddress and
842 Length from their current attributes to the attributes specified by Attributes.
843
844 @param This The EFI_CPU_ARCH_PROTOCOL instance.
845 @param BaseAddress The physical address that is the start address of a memory region.
846 @param Length The size in bytes of the memory region.
847 @param Attributes The bit mask of attributes to set for the memory region.
848
849 @retval EFI_SUCCESS The attributes were set for the memory region.
850 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
851 BaseAddress and Length cannot be modified.
852 @retval EFI_INVALID_PARAMETER Length is zero.
853 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
854 the memory resource range.
855 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
856 resource range specified by BaseAddress and Length.
857 The bit mask of attributes is not support for the memory resource
858 range specified by BaseAddress and Length.
859
860 **/
861 EFI_STATUS
862 EFIAPI
863 CpuSetMemoryAttributes (
864 IN EFI_CPU_ARCH_PROTOCOL *This,
865 IN EFI_PHYSICAL_ADDRESS BaseAddress,
866 IN UINT64 Length,
867 IN UINT64 Attributes
868 )
869 {
870 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));
871 if ( ((BaseAddress & ~TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK) != 0) || ((Length & ~TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK) != 0)){
872 // minimum granularity is SIZE_4KB (4KB on ARM)
873 DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx): minimum ganularity is SIZE_4KB\n", BaseAddress, Length, Attributes));
874 return EFI_UNSUPPORTED;
875 }
876
877 return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);
878 }
879
880
881
882 //
883 // Add a new protocol to support
884 //
885
886 EFI_STATUS
887 EFIAPI
888 CpuConvertPagesToUncachedVirtualAddress (
889 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,
890 IN EFI_PHYSICAL_ADDRESS Address,
891 IN UINTN Length,
892 IN EFI_PHYSICAL_ADDRESS VirtualMask,
893 OUT UINT64 *Attributes OPTIONAL
894 )
895 {
896 EFI_STATUS Status;
897 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
898
899
900 if (Attributes != NULL) {
901 Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);
902 if (!EFI_ERROR (Status)) {
903 *Attributes = GcdDescriptor.Attributes;
904 }
905 }
906
907 //
908 // Make this address range page fault if accessed. If it is a DMA buffer than this would
909 // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask
910 // to that address.
911 //
912 Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_WP, 0);
913 if (!EFI_ERROR (Status)) {
914 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);
915 }
916
917 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "ConvertPagesToUncachedVirtualAddress()\n Unmapped 0x%08lx Mapped 0x%08lx 0x%x bytes\n", Address, Address | VirtualMask, Length));
918
919 return Status;
920 }
921
922
923 EFI_STATUS
924 EFIAPI
925 CpuReconvertPages (
926 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,
927 IN EFI_PHYSICAL_ADDRESS Address,
928 IN UINTN Length,
929 IN EFI_PHYSICAL_ADDRESS VirtualMask,
930 IN UINT64 Attributes
931 )
932 {
933 EFI_STATUS Status;
934
935 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuReconvertPages(%lx, %x, %lx, %lx)\n", Address, Length, VirtualMask, Attributes));
936
937 //
938 // Unmap the alaised Address
939 //
940 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_WP, 0);
941 if (!EFI_ERROR (Status)) {
942 //
943 // Restore atttributes
944 //
945 Status = SetMemoryAttributes (Address, Length, Attributes, 0);
946 }
947
948 return Status;
949 }
950
951
952 VIRTUAL_UNCACHED_PAGES_PROTOCOL gVirtualUncachedPages = {
953 CpuConvertPagesToUncachedVirtualAddress,
954 CpuReconvertPages
955 };