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
66typedef enum { Level0, Level1,Level2 } MMU_LEVEL;
67
68typedef struct {
69  MMU_LEVEL   Level;
70  UINT32      Value;
71  UINT32      Index;
72  UINT32*     Table;
73} MMU_ENTRY;
74
75MMU_ENTRY
76MmuEntryCreate (
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
90UINT32
91MmuEntryIsValidAddress (
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
119UINT32
120MmuEntryGetAddress (
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
151UINT32
152MmuEntryGetSize (
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
188CONST CHAR8*
189MmuEntryGetAttributesName (
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
244UINT32
245MmuEntryGetAttributes (
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
289MMU_ENTRY
290DumpMmuLevel (
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
351EFI_STATUS
352EblDumpMmu (
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