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
SectionToGcdAttributes(IN UINT32 SectionAttributes,OUT UINT64 * GcdAttributes)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
PageToGcdAttributes(IN UINT32 PageAttributes,OUT UINT64 * GcdAttributes)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
SyncCacheConfigPage(IN UINT32 SectionIndex,IN UINT32 FirstLevelDescriptor,IN UINTN NumberOfDescriptors,IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR * MemorySpaceMap,IN OUT EFI_PHYSICAL_ADDRESS * NextRegionBase,IN OUT UINT64 * NextRegionLength,IN OUT UINT32 * NextSectionAttributes)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
SyncCacheConfig(IN EFI_CPU_ARCH_PROTOCOL * CpuProtocol)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
UpdatePageEntries(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,IN EFI_PHYSICAL_ADDRESS VirtualMask)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
UpdateSectionEntries(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,IN EFI_PHYSICAL_ADDRESS VirtualMask)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
ConvertSectionToPages(IN EFI_PHYSICAL_ADDRESS BaseAddress)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
SetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,IN EFI_PHYSICAL_ADDRESS VirtualMask)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
EfiAttributeToArmAttribute(IN UINT64 EfiAttributes)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
GetMemoryRegionPage(IN UINT32 * PageTable,IN OUT UINTN * BaseAddress,OUT UINTN * RegionLength,OUT UINTN * RegionAttributes)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
GetMemoryRegion(IN OUT UINTN * BaseAddress,OUT UINTN * RegionLength,OUT UINTN * RegionAttributes)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 }
886