1 /** @file
2   PPTT table parser
3 
4   Copyright (c) 2019 - 2020, ARM Limited. All rights reserved.
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7   @par Reference(s):
8     - ACPI 6.3 Specification - January 2019
9     - ARM Architecture Reference Manual ARMv8 (D.a)
10 **/
11 
12 #include <Library/PrintLib.h>
13 #include <Library/UefiLib.h>
14 #include "AcpiParser.h"
15 #include "AcpiView.h"
16 #include "AcpiViewConfig.h"
17 #include "PpttParser.h"
18 
19 // Local variables
20 STATIC CONST UINT8*  ProcessorTopologyStructureType;
21 STATIC CONST UINT8*  ProcessorTopologyStructureLength;
22 STATIC CONST UINT32* NumberOfPrivateResources;
23 STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo;
24 
25 /**
26   This function validates the Cache Type Structure (Type 1) 'Number of sets'
27   field.
28 
29   @param [in] Ptr       Pointer to the start of the field data.
30   @param [in] Context   Pointer to context specific information e.g. this
31                         could be a pointer to the ACPI table header.
32 **/
33 STATIC
34 VOID
35 EFIAPI
ValidateCacheNumberOfSets(IN UINT8 * Ptr,IN VOID * Context)36 ValidateCacheNumberOfSets (
37   IN UINT8* Ptr,
38   IN VOID*  Context
39   )
40 {
41   UINT32 NumberOfSets;
42   NumberOfSets = *(UINT32*)Ptr;
43 
44   if (NumberOfSets == 0) {
45     IncrementErrorCount ();
46     Print (L"\nERROR: Cache number of sets must be greater than 0");
47     return;
48   }
49 
50 #if defined(MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
51   if (NumberOfSets > PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX) {
52     IncrementErrorCount ();
53     Print (
54       L"\nERROR: When ARMv8.3-CCIDX is implemented the maximum cache number of "
55         L"sets must be less than or equal to %d",
56       PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX
57       );
58     return;
59   }
60 
61   if (NumberOfSets > PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX) {
62     IncrementWarningCount ();
63     Print (
64       L"\nWARNING: Without ARMv8.3-CCIDX, the maximum cache number of sets "
65         L"must be less than or equal to %d. Ignore this message if "
66         L"ARMv8.3-CCIDX is implemented",
67       PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX
68       );
69     return;
70   }
71 #endif
72 
73 }
74 
75 /**
76   This function validates the Cache Type Structure (Type 1) 'Associativity'
77   field.
78 
79   @param [in] Ptr       Pointer to the start of the field data.
80   @param [in] Context   Pointer to context specific information e.g. this
81                         could be a pointer to the ACPI table header.
82 **/
83 STATIC
84 VOID
85 EFIAPI
ValidateCacheAssociativity(IN UINT8 * Ptr,IN VOID * Context)86 ValidateCacheAssociativity (
87   IN UINT8* Ptr,
88   IN VOID*  Context
89   )
90 {
91   UINT8 Associativity;
92   Associativity = *(UINT8*)Ptr;
93 
94   if (Associativity == 0) {
95     IncrementErrorCount ();
96     Print (L"\nERROR: Cache associativity must be greater than 0");
97     return;
98   }
99 }
100 
101 /**
102   This function validates the Cache Type Structure (Type 1) Line size field.
103 
104   @param [in] Ptr     Pointer to the start of the field data.
105   @param [in] Context Pointer to context specific information e.g. this
106                       could be a pointer to the ACPI table header.
107 **/
108 STATIC
109 VOID
110 EFIAPI
ValidateCacheLineSize(IN UINT8 * Ptr,IN VOID * Context)111 ValidateCacheLineSize (
112   IN UINT8* Ptr,
113   IN VOID*  Context
114   )
115 {
116 #if defined(MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
117   // Reference: ARM Architecture Reference Manual ARMv8 (D.a)
118   // Section D12.2.25: CCSIDR_EL1, Current Cache Size ID Register
119   //   LineSize, bits [2:0]
120   //     (Log2(Number of bytes in cache line)) - 4.
121 
122   UINT16 LineSize;
123   LineSize = *(UINT16*)Ptr;
124 
125   if ((LineSize < PPTT_ARM_CACHE_LINE_SIZE_MIN) ||
126       (LineSize > PPTT_ARM_CACHE_LINE_SIZE_MAX)) {
127     IncrementErrorCount ();
128     Print (
129       L"\nERROR: The cache line size must be between %d and %d bytes"
130         L" on ARM Platforms.",
131       PPTT_ARM_CACHE_LINE_SIZE_MIN,
132       PPTT_ARM_CACHE_LINE_SIZE_MAX
133       );
134     return;
135   }
136 
137   if ((LineSize & (LineSize - 1)) != 0) {
138     IncrementErrorCount ();
139     Print (L"\nERROR: The cache line size is not a power of 2.");
140   }
141 #endif
142 }
143 
144 /**
145   This function validates the Cache Type Structure (Type 1) Attributes field.
146 
147   @param [in] Ptr     Pointer to the start of the field data.
148   @param [in] Context Pointer to context specific information e.g. this
149                       could be a pointer to the ACPI table header.
150 **/
151 STATIC
152 VOID
153 EFIAPI
ValidateCacheAttributes(IN UINT8 * Ptr,IN VOID * Context)154 ValidateCacheAttributes (
155   IN UINT8* Ptr,
156   IN VOID*  Context
157   )
158 {
159   // Reference: Advanced Configuration and Power Interface (ACPI) Specification
160   //            Version 6.2 Errata A, September 2017
161   // Table 5-153: Cache Type Structure
162   UINT8 Attributes;
163   Attributes = *(UINT8*)Ptr;
164 
165   if ((Attributes & 0xE0) != 0) {
166     IncrementErrorCount ();
167     Print (
168       L"\nERROR: Attributes bits [7:5] are reserved and must be zero.",
169       Attributes
170       );
171     return;
172   }
173 }
174 
175 /**
176   An ACPI_PARSER array describing the ACPI PPTT Table.
177 **/
178 STATIC CONST ACPI_PARSER PpttParser[] = {
179   PARSE_ACPI_HEADER (&AcpiHdrInfo)
180 };
181 
182 /**
183   An ACPI_PARSER array describing the processor topology structure header.
184 **/
185 STATIC CONST ACPI_PARSER ProcessorTopologyStructureHeaderParser[] = {
186   {L"Type", 1, 0, NULL, NULL, (VOID**)&ProcessorTopologyStructureType,
187    NULL, NULL},
188   {L"Length", 1, 1, NULL, NULL, (VOID**)&ProcessorTopologyStructureLength,
189    NULL, NULL},
190   {L"Reserved", 2, 2, NULL, NULL, NULL, NULL, NULL}
191 };
192 
193 /**
194   An ACPI_PARSER array describing the Processor Hierarchy Node Structure - Type 0.
195 **/
196 STATIC CONST ACPI_PARSER ProcessorHierarchyNodeStructureParser[] = {
197   {L"Type", 1, 0, L"0x%x", NULL, NULL, NULL, NULL},
198   {L"Length", 1, 1, L"%d", NULL, NULL, NULL, NULL},
199   {L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL},
200 
201   {L"Flags", 4, 4, L"0x%x", NULL, NULL, NULL, NULL},
202   {L"Parent", 4, 8, L"0x%x", NULL, NULL, NULL, NULL},
203   {L"ACPI Processor ID", 4, 12, L"0x%x", NULL, NULL, NULL, NULL},
204   {L"Number of private resources", 4, 16, L"%d", NULL,
205    (VOID**)&NumberOfPrivateResources, NULL, NULL}
206 };
207 
208 /**
209   An ACPI_PARSER array describing the Cache Type Structure - Type 1.
210 **/
211 STATIC CONST ACPI_PARSER CacheTypeStructureParser[] = {
212   {L"Type", 1, 0, L"0x%x", NULL, NULL, NULL, NULL},
213   {L"Length", 1, 1, L"%d", NULL, NULL, NULL, NULL},
214   {L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL},
215 
216   {L"Flags", 4, 4, L"0x%x", NULL, NULL, NULL, NULL},
217   {L"Next Level of Cache", 4, 8, L"0x%x", NULL, NULL, NULL, NULL},
218   {L"Size", 4, 12, L"0x%x", NULL, NULL, NULL, NULL},
219   {L"Number of sets", 4, 16, L"%d", NULL, NULL, ValidateCacheNumberOfSets, NULL},
220   {L"Associativity", 1, 20, L"%d", NULL, NULL, ValidateCacheAssociativity, NULL},
221   {L"Attributes", 1, 21, L"0x%x", NULL, NULL, ValidateCacheAttributes, NULL},
222   {L"Line size", 2, 22, L"%d", NULL, NULL, ValidateCacheLineSize, NULL}
223 };
224 
225 /**
226   An ACPI_PARSER array describing the ID Type Structure - Type 2.
227 **/
228 STATIC CONST ACPI_PARSER IdStructureParser[] = {
229   {L"Type", 1, 0, L"0x%x", NULL, NULL, NULL, NULL},
230   {L"Length", 1, 1, L"%d", NULL, NULL, NULL, NULL},
231   {L"Reserved", 2, 2, L"0x%x", NULL, NULL, NULL, NULL},
232 
233   {L"VENDOR_ID", 4, 4, NULL, Dump4Chars, NULL, NULL, NULL},
234   {L"LEVEL_1_ID", 8, 8, L"0x%x", NULL, NULL, NULL, NULL},
235   {L"LEVEL_2_ID", 8, 16, L"0x%x", NULL, NULL, NULL, NULL},
236   {L"MAJOR_REV", 2, 24, L"0x%x", NULL, NULL, NULL, NULL},
237   {L"MINOR_REV", 2, 26, L"0x%x", NULL, NULL, NULL, NULL},
238   {L"SPIN_REV", 2, 28, L"0x%x", NULL, NULL, NULL, NULL},
239 };
240 
241 /**
242   This function parses the Processor Hierarchy Node Structure (Type 0).
243 
244   @param [in] Ptr     Pointer to the start of the Processor Hierarchy Node
245                       Structure data.
246   @param [in] Length  Length of the Processor Hierarchy Node Structure.
247 **/
248 STATIC
249 VOID
DumpProcessorHierarchyNodeStructure(IN UINT8 * Ptr,IN UINT8 Length)250 DumpProcessorHierarchyNodeStructure (
251   IN UINT8* Ptr,
252   IN UINT8  Length
253   )
254 {
255   UINT32 Offset;
256   UINT32 Index;
257   CHAR16 Buffer[OUTPUT_FIELD_COLUMN_WIDTH];
258 
259   Offset = ParseAcpi (
260              TRUE,
261              2,
262              "Processor Hierarchy Node Structure",
263              Ptr,
264              Length,
265              PARSER_PARAMS (ProcessorHierarchyNodeStructureParser)
266              );
267 
268   // Check if the values used to control the parsing logic have been
269   // successfully read.
270   if (NumberOfPrivateResources == NULL) {
271     IncrementErrorCount ();
272     Print (
273       L"ERROR: Insufficient Processor Hierarchy Node length. Length = %d.\n",
274       Length
275       );
276     return;
277   }
278 
279   // Make sure the Private Resource array lies inside this structure
280   if (Offset + (*NumberOfPrivateResources * sizeof (UINT32)) > Length) {
281     IncrementErrorCount ();
282     Print (
283       L"ERROR: Invalid Number of Private Resources. " \
284         L"PrivateResourceCount = %d. RemainingBufferLength = %d. " \
285         L"Parsing of this structure aborted.\n",
286       *NumberOfPrivateResources,
287       Length - Offset
288       );
289     return;
290   }
291 
292   Index = 0;
293 
294   // Parse the specified number of private resource references or the Processor
295   // Hierarchy Node length. Whichever is minimum.
296   while (Index < *NumberOfPrivateResources) {
297     UnicodeSPrint (
298       Buffer,
299       sizeof (Buffer),
300       L"Private resources [%d]",
301       Index
302       );
303 
304     PrintFieldName (4, Buffer);
305     Print (
306       L"0x%x\n",
307       *((UINT32*)(Ptr + Offset))
308       );
309 
310     Offset += sizeof (UINT32);
311     Index++;
312   }
313 }
314 
315 /**
316   This function parses the Cache Type Structure (Type 1).
317 
318   @param [in] Ptr     Pointer to the start of the Cache Type Structure data.
319   @param [in] Length  Length of the Cache Type Structure.
320 **/
321 STATIC
322 VOID
DumpCacheTypeStructure(IN UINT8 * Ptr,IN UINT8 Length)323 DumpCacheTypeStructure (
324   IN UINT8* Ptr,
325   IN UINT8  Length
326   )
327 {
328   ParseAcpi (
329     TRUE,
330     2,
331     "Cache Type Structure",
332     Ptr,
333     Length,
334     PARSER_PARAMS (CacheTypeStructureParser)
335     );
336 }
337 
338 /**
339   This function parses the ID Structure (Type 2).
340 
341   @param [in] Ptr     Pointer to the start of the ID Structure data.
342   @param [in] Length  Length of the ID Structure.
343 **/
344 STATIC
345 VOID
DumpIDStructure(IN UINT8 * Ptr,IN UINT8 Length)346 DumpIDStructure (
347   IN UINT8* Ptr,
348   IN UINT8 Length
349   )
350 {
351   ParseAcpi (
352     TRUE,
353     2,
354     "ID Structure",
355     Ptr,
356     Length,
357     PARSER_PARAMS (IdStructureParser)
358     );
359 }
360 
361 /**
362   This function parses the ACPI PPTT table.
363   When trace is enabled this function parses the PPTT table and
364   traces the ACPI table fields.
365 
366   This function parses the following processor topology structures:
367     - Processor hierarchy node structure (Type 0)
368     - Cache Type Structure (Type 1)
369     - ID structure (Type 2)
370 
371   This function also performs validation of the ACPI table fields.
372 
373   @param [in] Trace              If TRUE, trace the ACPI fields.
374   @param [in] Ptr                Pointer to the start of the buffer.
375   @param [in] AcpiTableLength    Length of the ACPI table.
376   @param [in] AcpiTableRevision  Revision of the ACPI table.
377 **/
378 VOID
379 EFIAPI
ParseAcpiPptt(IN BOOLEAN Trace,IN UINT8 * Ptr,IN UINT32 AcpiTableLength,IN UINT8 AcpiTableRevision)380 ParseAcpiPptt (
381   IN BOOLEAN Trace,
382   IN UINT8*  Ptr,
383   IN UINT32  AcpiTableLength,
384   IN UINT8   AcpiTableRevision
385   )
386 {
387   UINT32 Offset;
388   UINT8* ProcessorTopologyStructurePtr;
389 
390   if (!Trace) {
391     return;
392   }
393 
394   Offset = ParseAcpi (
395              TRUE,
396              0,
397              "PPTT",
398              Ptr,
399              AcpiTableLength,
400              PARSER_PARAMS (PpttParser)
401              );
402 
403   ProcessorTopologyStructurePtr = Ptr + Offset;
404 
405   while (Offset < AcpiTableLength) {
406     // Parse Processor Hierarchy Node Structure to obtain Type and Length.
407     ParseAcpi (
408       FALSE,
409       0,
410       NULL,
411       ProcessorTopologyStructurePtr,
412       AcpiTableLength - Offset,
413       PARSER_PARAMS (ProcessorTopologyStructureHeaderParser)
414       );
415 
416     // Check if the values used to control the parsing logic have been
417     // successfully read.
418     if ((ProcessorTopologyStructureType == NULL) ||
419         (ProcessorTopologyStructureLength == NULL)) {
420       IncrementErrorCount ();
421       Print (
422         L"ERROR: Insufficient remaining table buffer length to read the " \
423           L"processor topology structure header. Length = %d.\n",
424         AcpiTableLength - Offset
425         );
426       return;
427     }
428 
429     // Validate Processor Topology Structure length
430     if ((*ProcessorTopologyStructureLength == 0) ||
431         ((Offset + (*ProcessorTopologyStructureLength)) > AcpiTableLength)) {
432       IncrementErrorCount ();
433       Print (
434         L"ERROR: Invalid Processor Topology Structure length. " \
435           L"Length = %d. Offset = %d. AcpiTableLength = %d.\n",
436         *ProcessorTopologyStructureLength,
437         Offset,
438         AcpiTableLength
439         );
440       return;
441     }
442 
443     PrintFieldName (2, L"* Structure Offset *");
444     Print (L"0x%x\n", Offset);
445 
446     switch (*ProcessorTopologyStructureType) {
447       case EFI_ACPI_6_2_PPTT_TYPE_PROCESSOR:
448         DumpProcessorHierarchyNodeStructure (
449           ProcessorTopologyStructurePtr,
450           *ProcessorTopologyStructureLength
451           );
452         break;
453       case EFI_ACPI_6_2_PPTT_TYPE_CACHE:
454         DumpCacheTypeStructure (
455           ProcessorTopologyStructurePtr,
456           *ProcessorTopologyStructureLength
457           );
458         break;
459       case EFI_ACPI_6_2_PPTT_TYPE_ID:
460         DumpIDStructure (
461           ProcessorTopologyStructurePtr,
462           *ProcessorTopologyStructureLength
463           );
464         break;
465       default:
466         IncrementErrorCount ();
467         Print (
468           L"ERROR: Unknown processor topology structure:"
469             L" Type = %d, Length = %d\n",
470           *ProcessorTopologyStructureType,
471           *ProcessorTopologyStructureLength
472           );
473     }
474 
475     ProcessorTopologyStructurePtr += *ProcessorTopologyStructureLength;
476     Offset += *ProcessorTopologyStructureLength;
477   } // while
478 }
479