1 /** @file
2 *
3 *  Copyright (c) 2011-2012, ARM Limited. All rights reserved.
4 *
5 *  This program and the accompanying materials
6 *  are licensed and made available under the terms and conditions of the BSD License
7 *  which accompanies this distribution.  The full text of the license may be found at
8 *  http://opensource.org/licenses/bsd-license.php
9 *
10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14 
15 #include <PiDxe.h>
16 #include <Library/UefiLib.h>
17 #include <Library/ArmLib.h>
18 #include <Chipset/ArmV7.h>
19 #include <Library/CacheMaintenanceLib.h>
20 #include <Library/EblCmdLib.h>
21 #include <Library/BaseLib.h>
22 #include <Library/DebugLib.h>
23 
24 #define GET_TT_ATTRIBUTES(TTEntry)  ((TTEntry) & ~(TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK))
25 #define GET_TT_PAGE_ATTRIBUTES(TTEntry)  ((TTEntry) & 0xFFF)
26 #define GET_TT_LARGEPAGE_ATTRIBUTES(TTEntry)  ((TTEntry) & 0xFFFF)
27 
28 // Section
29 #define TT_DESCRIPTOR_SECTION_STRONGLY_ORDER   (TT_DESCRIPTOR_SECTION_TYPE_SECTION    | \
30                                                 TT_DESCRIPTOR_SECTION_NG_GLOBAL       | \
31                                                 TT_DESCRIPTOR_SECTION_S_NOT_SHARED    | \
32                                                 TT_DESCRIPTOR_SECTION_DOMAIN(0)       | \
33                                                 TT_DESCRIPTOR_SECTION_AP_RW_RW        | \
34                                                 TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED)
35 
36 // Small Page
37 #define TT_DESCRIPTOR_PAGE_STRONGLY_ORDER      (TT_DESCRIPTOR_PAGE_TYPE_PAGE          | \
38                                                 TT_DESCRIPTOR_PAGE_NG_GLOBAL          | \
39                                                 TT_DESCRIPTOR_PAGE_S_NOT_SHARED       | \
40                                                 TT_DESCRIPTOR_PAGE_AP_RW_RW           | \
41                                                 TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED)
42 
43 // Large Page
44 #define TT_DESCRIPTOR_LARGEPAGE_WRITE_BACK     (TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE     | \
45                                                 TT_DESCRIPTOR_PAGE_NG_GLOBAL          | \
46                                                 TT_DESCRIPTOR_PAGE_S_NOT_SHARED       | \
47                                                 TT_DESCRIPTOR_PAGE_AP_RW_RW           | \
48                                                 TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC)
49 #define TT_DESCRIPTOR_LARGEPAGE_WRITE_THROUGH  (TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE     | \
50                                                 TT_DESCRIPTOR_PAGE_NG_GLOBAL          | \
51                                                 TT_DESCRIPTOR_PAGE_S_NOT_SHARED       | \
52                                                 TT_DESCRIPTOR_PAGE_AP_RW_RW           | \
53                                                 TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
54 #define TT_DESCRIPTOR_LARGEPAGE_DEVICE         (TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE     | \
55                                                 TT_DESCRIPTOR_PAGE_NG_GLOBAL          | \
56                                                 TT_DESCRIPTOR_PAGE_S_NOT_SHARED       | \
57                                                 TT_DESCRIPTOR_PAGE_AP_RW_RW           | \
58                                                 TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE)
59 #define TT_DESCRIPTOR_LARGEPAGE_UNCACHED       (TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE     | \
60                                                 TT_DESCRIPTOR_PAGE_NG_GLOBAL          | \
61                                                 TT_DESCRIPTOR_PAGE_S_NOT_SHARED       | \
62                                                 TT_DESCRIPTOR_PAGE_AP_RW_RW           | \
63                                                 TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE)
64 
65 
66 typedef enum { Level0, Level1,Level2 } MMU_LEVEL;
67 
68 typedef struct {
69   MMU_LEVEL   Level;
70   UINT32      Value;
71   UINT32      Index;
72   UINT32*     Table;
73 } MMU_ENTRY;
74 
75 MMU_ENTRY
MmuEntryCreate(IN MMU_LEVEL Level,IN UINT32 * Table,IN UINT32 Index)76 MmuEntryCreate (
77   IN MMU_LEVEL Level,
78   IN UINT32* Table,
79   IN UINT32 Index
80   )
81 {
82   MMU_ENTRY Entry;
83   Entry.Level = Level;
84   Entry.Value = Table[Index];
85   Entry.Table = Table;
86   Entry.Index = Index;
87   return Entry;
88 }
89 
90 UINT32
MmuEntryIsValidAddress(IN MMU_LEVEL Level,IN UINT32 Entry)91 MmuEntryIsValidAddress (
92   IN MMU_LEVEL Level,
93   IN UINT32 Entry
94   )
95 {
96   if (Level == Level0) {
97     return 0;
98   } else if (Level == Level1) {
99     if ((Entry & 0x3) == 0) {           // Ignored
100       return 0;
101     } else if ((Entry & 0x3) == 2) {    // Section Type
102       return 1;
103     } else {                            // Page Type
104       return 0;
105     }
106   } else if (Level == Level2){
107     if ((Entry & 0x3) == 0) {           // Ignored
108       return 0;
109     } else {                            // Page Type
110       return 1;
111     }
112   } else {
113     DEBUG((EFI_D_ERROR,"MmuEntryIsValidAddress: Level:%d Entry:0x%X\n",(UINT32)Level,(UINT32)Entry));
114     ASSERT(0);
115     return 0;
116   }
117 }
118 
119 UINT32
MmuEntryGetAddress(IN MMU_ENTRY Entry)120 MmuEntryGetAddress (
121   IN MMU_ENTRY Entry
122   )
123 {
124   if (Entry.Level == Level1) {
125     if ((Entry.Value & 0x3) == 0) {
126       return 0;
127     } else if ((Entry.Value & 0x3) == 2) {    // Section Type
128       return Entry.Value & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK;
129     } else if ((Entry.Value & 0x3) == 1) {    // Level2 Table
130       MMU_ENTRY Level2Entry = MmuEntryCreate (Level2,(UINT32*)(Entry.Value & 0xFFFFC000),0);
131       return MmuEntryGetAddress (Level2Entry);
132     } else {                                  // Page Type
133       return 0;
134     }
135   } else if (Entry.Level == Level2) {
136     if ((Entry.Value & 0x3) == 0) {           // Ignored
137       return 0;
138     } else if ((Entry.Value & 0x3) == 1) {    // Large Page
139       return Entry.Value & 0xFFFF0000;
140     } else if ((Entry.Value & 0x2) == 2) {    // Small Page
141       return Entry.Value & 0xFFFFF000;
142     } else {
143       return 0;
144     }
145   } else {
146     ASSERT(0);
147     return 0;
148   }
149 }
150 
151 UINT32
MmuEntryGetSize(IN MMU_ENTRY Entry)152 MmuEntryGetSize (
153   IN MMU_ENTRY Entry
154   )
155 {
156   if (Entry.Level == Level1) {
157     if ((Entry.Value & 0x3) == 0) {
158       return 0;
159     } else if ((Entry.Value & 0x3) == 2) {
160       if (Entry.Value & (1 << 18))
161         return 16*SIZE_1MB;
162       else
163         return SIZE_1MB;
164     } else if ((Entry.Value & 0x3) == 1) {      // Level2 Table split 1MB section
165       return SIZE_1MB;
166     } else {
167       DEBUG((EFI_D_ERROR, "MmuEntryGetSize: Value:0x%X",Entry.Value));
168       ASSERT(0);
169       return 0;
170     }
171   } else if (Entry.Level == Level2) {
172     if ((Entry.Value & 0x3) == 0) {           // Ignored
173       return 0;
174     } else if ((Entry.Value & 0x3) == 1) {    // Large Page
175       return SIZE_64KB;
176     } else if ((Entry.Value & 0x2) == 2) {    // Small Page
177       return SIZE_4KB;
178     } else {
179       ASSERT(0);
180       return 0;
181     }
182   } else {
183     ASSERT(0);
184     return 0;
185   }
186 }
187 
188 CONST CHAR8*
MmuEntryGetAttributesName(IN MMU_ENTRY Entry)189 MmuEntryGetAttributesName (
190   IN MMU_ENTRY Entry
191   )
192 {
193   UINT32 Value;
194 
195   if (Entry.Level == Level1) {
196     Value = GET_TT_ATTRIBUTES(Entry.Value) | TT_DESCRIPTOR_SECTION_NS_MASK;
197     if (Value == TT_DESCRIPTOR_SECTION_WRITE_BACK(0))
198       return "TT_DESCRIPTOR_SECTION_WRITE_BACK";
199     else if (Value == TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0))
200       return "TT_DESCRIPTOR_SECTION_WRITE_THROUGH";
201     else if (Value == TT_DESCRIPTOR_SECTION_DEVICE(0))
202       return "TT_DESCRIPTOR_SECTION_DEVICE";
203     else if (Value == TT_DESCRIPTOR_SECTION_UNCACHED(0))
204       return "TT_DESCRIPTOR_SECTION_UNCACHED";
205     else if (Value == TT_DESCRIPTOR_SECTION_STRONGLY_ORDER)
206       return "TT_DESCRIPTOR_SECTION_STRONGLY_ORDERED";
207     else {
208       return "SectionUnknown";
209     }
210   } else if ((Entry.Level == Level2) && ((Entry.Value & 0x2) == 2)) { //Small Page
211     Value = GET_TT_PAGE_ATTRIBUTES(Entry.Value);
212     if (Value == TT_DESCRIPTOR_PAGE_WRITE_BACK)
213       return "TT_DESCRIPTOR_PAGE_WRITE_BACK";
214     else if (Value == TT_DESCRIPTOR_PAGE_WRITE_THROUGH)
215       return "TT_DESCRIPTOR_PAGE_WRITE_THROUGH";
216     else if (Value == TT_DESCRIPTOR_PAGE_DEVICE)
217       return "TT_DESCRIPTOR_PAGE_DEVICE";
218     else if (Value == TT_DESCRIPTOR_PAGE_UNCACHED)
219       return "TT_DESCRIPTOR_PAGE_UNCACHED";
220     else if (Value == TT_DESCRIPTOR_PAGE_STRONGLY_ORDER)
221       return "TT_DESCRIPTOR_PAGE_STRONGLY_ORDERED";
222     else {
223       return "PageUnknown";
224     }
225   } else if ((Entry.Level == Level2) && ((Entry.Value & 0x3) == 1)) { //Large Page
226     Value = GET_TT_LARGEPAGE_ATTRIBUTES(Entry.Value);
227     if (Value == TT_DESCRIPTOR_LARGEPAGE_WRITE_BACK)
228       return "TT_DESCRIPTOR_LARGEPAGE_WRITE_BACK";
229     else if (Value == TT_DESCRIPTOR_LARGEPAGE_WRITE_THROUGH)
230       return "TT_DESCRIPTOR_LARGEPAGE_WRITE_THROUGH";
231     else if (Value == TT_DESCRIPTOR_LARGEPAGE_DEVICE)
232       return "TT_DESCRIPTOR_LARGEPAGE_DEVICE";
233     else if (Value == TT_DESCRIPTOR_LARGEPAGE_UNCACHED)
234       return "TT_DESCRIPTOR_LARGEPAGE_UNCACHED";
235     else {
236       return "LargePageUnknown";
237     }
238   } else {
239     ASSERT(0);
240     return "";
241   }
242 }
243 
244 UINT32
MmuEntryGetAttributes(IN MMU_ENTRY Entry)245 MmuEntryGetAttributes (
246   IN MMU_ENTRY Entry
247   )
248 {
249   if (Entry.Level == Level1) {
250     if ((Entry.Value & 0x3) == 0) {
251       return 0;
252     } else if ((Entry.Value & 0x3) == 2) {
253       return GET_TT_ATTRIBUTES(Entry.Value);
254     } else {
255       return 0;
256     }
257   } else if ((Entry.Level == Level2) && ((Entry.Value & 0x2) == 2)) { //Small Page
258     if (GET_TT_PAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_PAGE_WRITE_BACK)
259       return TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
260     else if (GET_TT_PAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_PAGE_WRITE_THROUGH)
261       return TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
262     else if (GET_TT_PAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_PAGE_DEVICE)
263       return TT_DESCRIPTOR_SECTION_DEVICE(0);
264     else if (GET_TT_PAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_PAGE_UNCACHED)
265       return TT_DESCRIPTOR_SECTION_UNCACHED(0);
266     else if (GET_TT_PAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_PAGE_STRONGLY_ORDER)
267       return TT_DESCRIPTOR_SECTION_STRONGLY_ORDER;
268     else {
269       return 0;
270     }
271   } else if ((Entry.Level == Level2) && ((Entry.Value & 0x3) == 1)) { //Large Page
272     if (GET_TT_LARGEPAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_LARGEPAGE_WRITE_BACK)
273       return TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
274     else if (GET_TT_LARGEPAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_LARGEPAGE_WRITE_THROUGH)
275       return TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
276     else if (GET_TT_LARGEPAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_LARGEPAGE_DEVICE)
277       return TT_DESCRIPTOR_SECTION_DEVICE(0);
278     else if (GET_TT_LARGEPAGE_ATTRIBUTES(Entry.Value) == TT_DESCRIPTOR_LARGEPAGE_UNCACHED)
279       return TT_DESCRIPTOR_SECTION_UNCACHED(0);
280     else {
281       return 0;
282     }
283   } else {
284     return 0;
285   }
286 }
287 
288 
289 MMU_ENTRY
DumpMmuLevel(IN MMU_LEVEL Level,IN UINT32 * Table,IN MMU_ENTRY PreviousEntry)290 DumpMmuLevel (
291   IN MMU_LEVEL Level,
292   IN UINT32* Table,
293   IN MMU_ENTRY PreviousEntry
294   )
295 {
296   UINT32      Index = 0, Count;
297   MMU_ENTRY   LastEntry, Entry;
298 
299   ASSERT((Level == Level1) || (Level == Level2));
300 
301   if (Level == Level1)    Count = 4096;
302   else                    Count = 256;
303 
304   // At Level1, we will get into this function because PreviousEntry is not valid
305   if (!MmuEntryIsValidAddress((MMU_LEVEL)(Level-1),PreviousEntry.Value)) {
306       // Find the first valid address
307       for (; (Index < Count) && (!MmuEntryIsValidAddress(Level,Table[Index])); Index++);
308 
309       LastEntry = MmuEntryCreate(Level,Table,Index);
310       Index++;
311   } else {
312     LastEntry = PreviousEntry;
313   }
314 
315   for (; Index < Count; Index++) {
316     Entry = MmuEntryCreate(Level,Table,Index);
317     if ((Level == Level1) && ((Entry.Value & 0x3) == 1)) {       // We have got a Level2 table redirection
318       LastEntry = DumpMmuLevel(Level2,(UINT32*)(Entry.Value & 0xFFFFFC00),LastEntry);
319     } else if (!MmuEntryIsValidAddress(Level,Table[Index])) {
320       if (MmuEntryIsValidAddress(LastEntry.Level,LastEntry.Value)) {
321           AsciiPrint("0x%08X-0x%08X\t%a\n",
322               MmuEntryGetAddress(LastEntry),MmuEntryGetAddress(PreviousEntry)+MmuEntryGetSize(PreviousEntry)-1,
323               MmuEntryGetAttributesName(LastEntry));
324       }
325       LastEntry = Entry;
326     } else {
327       if (MmuEntryGetAttributes(LastEntry) != MmuEntryGetAttributes(Entry)) {
328           if (MmuEntryIsValidAddress(Level,LastEntry.Value)) {
329             AsciiPrint("0x%08X-0x%08X\t%a\n",
330                       MmuEntryGetAddress(LastEntry),MmuEntryGetAddress(PreviousEntry)+MmuEntryGetSize(PreviousEntry)-1,
331                       MmuEntryGetAttributesName(LastEntry));
332           }
333           LastEntry = Entry;
334       } else {
335         ASSERT(LastEntry.Value != 0);
336       }
337     }
338     PreviousEntry = Entry;
339   }
340 
341   if ((Level == Level1) && (LastEntry.Index != Index) && MmuEntryIsValidAddress(Level,LastEntry.Value)) {
342     AsciiPrint("0x%08X-0x%08X\t%a\n",
343                   MmuEntryGetAddress(LastEntry),MmuEntryGetAddress(PreviousEntry)+MmuEntryGetSize(PreviousEntry)-1,
344                   MmuEntryGetAttributesName(LastEntry));
345   }
346 
347   return LastEntry;
348 }
349 
350 
351 EFI_STATUS
EblDumpMmu(IN UINTN Argc,IN CHAR8 ** Argv)352 EblDumpMmu (
353   IN UINTN  Argc,
354   IN CHAR8  **Argv
355   )
356 {
357   UINT32  *TTEntry;
358   MMU_ENTRY NoEntry;
359 
360   TTEntry = ArmGetTTBR0BaseAddress();
361 
362   AsciiPrint ("\nTranslation Table:0x%X\n",TTEntry);
363   AsciiPrint ("Address Range\t\tAttributes\n");
364   AsciiPrint ("____________________________________________________\n");
365 
366   NoEntry.Level = (MMU_LEVEL)200;
367   DumpMmuLevel(Level1,TTEntry,NoEntry);
368 
369   return EFI_SUCCESS;
370 }
371