ShellPkg: Add acpiview tool to dump ACPI tables
[mirror_edk2.git] / ShellPkg / Library / UefiShellAcpiViewCommandLib / AcpiParser.c
1 /**\r
2   ACPI parser\r
3 \r
4   Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.\r
5   This program and the accompanying materials\r
6   are licensed and made available under the terms and conditions of the BSD License\r
7   which accompanies this distribution.  The full text of the license may be found at\r
8   http://opensource.org/licenses/bsd-license.php\r
9 \r
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 **/\r
13 \r
14 #include <Uefi.h>\r
15 #include <Library/UefiLib.h>\r
16 #include <Library/UefiBootServicesTableLib.h>\r
17 #include "AcpiParser.h"\r
18 #include "AcpiView.h"\r
19 \r
20 STATIC UINT32   gIndent;\r
21 STATIC UINT32   mTableErrorCount;\r
22 STATIC UINT32   mTableWarningCount;\r
23 \r
24 /** This function resets the ACPI table error counter to Zero.\r
25 */\r
26 VOID\r
27 ResetErrorCount (\r
28   VOID\r
29   )\r
30 {\r
31   mTableErrorCount = 0;\r
32 }\r
33 \r
34 /** This function returns the ACPI table error count.\r
35 \r
36   @retval Returns the count of errors detected in the ACPI tables.\r
37 */\r
38 UINT32\r
39 GetErrorCount (\r
40   VOID\r
41   )\r
42 {\r
43   return mTableErrorCount;\r
44 }\r
45 \r
46 /** This function resets the ACPI table warning counter to Zero.\r
47 */\r
48 VOID\r
49 ResetWarningCount (\r
50   VOID\r
51   )\r
52 {\r
53   mTableWarningCount = 0;\r
54 }\r
55 \r
56 /** This function returns the ACPI table warning count.\r
57 \r
58   @retval Returns the count of warning detected in the ACPI tables.\r
59 */\r
60 UINT32\r
61 GetWarningCount (\r
62   VOID\r
63   )\r
64 {\r
65   return mTableWarningCount;\r
66 }\r
67 \r
68 /** This function increments the ACPI table error counter.\r
69 */\r
70 VOID\r
71 EFIAPI\r
72 IncrementErrorCount (\r
73   VOID\r
74   )\r
75 {\r
76   mTableErrorCount++;\r
77 }\r
78 \r
79 /** This function increments the ACPI table warning counter.\r
80 */\r
81 VOID\r
82 EFIAPI\r
83 IncrementWarningCount (\r
84   VOID\r
85   )\r
86 {\r
87   mTableWarningCount++;\r
88 }\r
89 \r
90 /** This function verifies the ACPI table checksum.\r
91 \r
92   This function verifies the checksum for the ACPI table and optionally\r
93   prints the status.\r
94 \r
95   @param [in] Log     If TRUE log the status of the checksum.\r
96   @param [in] Ptr     Pointer to the start of the table buffer.\r
97   @param [in] Length  The length of the buffer.\r
98 \r
99   @retval TRUE        The checksum is OK.\r
100   @retval FALSE       The checksum failed.\r
101 */\r
102 BOOLEAN\r
103 EFIAPI\r
104 VerifyChecksum (\r
105   IN BOOLEAN Log,\r
106   IN UINT8*  Ptr,\r
107   IN UINT32  Length\r
108   )\r
109 {\r
110   UINTN ByteCount = 0;\r
111   UINT8 Checksum = 0;\r
112   UINTN OriginalAttribute;\r
113 \r
114   while (ByteCount < Length) {\r
115     Checksum += *(Ptr++);\r
116     ByteCount++;\r
117   }\r
118 \r
119   if (Log) {\r
120     OriginalAttribute = gST->ConOut->Mode->Attribute;\r
121     if (Checksum == 0) {\r
122       if (GetColourHighlighting ()) {\r
123         gST->ConOut->SetAttribute (\r
124                        gST->ConOut,\r
125                        EFI_TEXT_ATTR (EFI_GREEN,\r
126                          ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))\r
127                        );\r
128       }\r
129       Print (L"\n\nTable Checksum : OK\n\n");\r
130     } else {\r
131       IncrementErrorCount ();\r
132       if (GetColourHighlighting ()) {\r
133         gST->ConOut->SetAttribute (\r
134                        gST->ConOut,\r
135                        EFI_TEXT_ATTR (EFI_RED,\r
136                          ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))\r
137                        );\r
138       }\r
139       Print (L"\n\nTable Checksum : FAILED (0x%X)\n\n", Checksum);\r
140     }\r
141     if (GetColourHighlighting ()) {\r
142       gST->ConOut->SetAttribute (gST->ConOut, OriginalAttribute);\r
143     }\r
144   }\r
145 \r
146   return (Checksum == 0);\r
147 }\r
148 \r
149 /** This function performs a raw data dump of the ACPI table.\r
150 \r
151   @param [in] Ptr     Pointer to the start of the table buffer.\r
152   @param [in] Length  The length of the buffer.\r
153 */\r
154 VOID\r
155 EFIAPI\r
156 DumpRaw (\r
157   IN UINT8* Ptr,\r
158   IN UINT32 Length\r
159   )\r
160 {\r
161   UINTN ByteCount = 0;\r
162   UINTN PartLineChars;\r
163   UINTN AsciiBufferIndex = 0;\r
164   CHAR8 AsciiBuffer[17];\r
165 \r
166   Print (L"Address  : 0x%p\n", Ptr);\r
167   Print (L"Length   : %d\n", Length);\r
168 \r
169   while (ByteCount < Length) {\r
170     if ((ByteCount & 0x0F) == 0) {\r
171       AsciiBuffer[AsciiBufferIndex] = '\0';\r
172       Print (L"  %a\n%08X : ", AsciiBuffer, ByteCount);\r
173       AsciiBufferIndex = 0;\r
174     } else if ((ByteCount & 0x07) == 0) {\r
175       Print (L"- ");\r
176     }\r
177 \r
178     if ((*Ptr >= ' ') && (*Ptr < 0x7F)) {\r
179       AsciiBuffer[AsciiBufferIndex++] = *Ptr;\r
180     } else {\r
181       AsciiBuffer[AsciiBufferIndex++] = '.';\r
182     }\r
183 \r
184     Print (L"%02X ", *Ptr++);\r
185 \r
186     ByteCount++;\r
187   }\r
188 \r
189   // Justify the final line using spaces before printing\r
190   // the ASCII data.\r
191   PartLineChars = (Length & 0x0F);\r
192   if (PartLineChars != 0) {\r
193     PartLineChars = 48 - (PartLineChars * 3);\r
194     if ((Length & 0x0F) <= 8) {\r
195       PartLineChars += 2;\r
196     }\r
197     while (PartLineChars > 0) {\r
198       Print (L" ");\r
199       PartLineChars--;\r
200     }\r
201   }\r
202 \r
203   // Print ASCII data for the final line.\r
204   AsciiBuffer[AsciiBufferIndex] = '\0';\r
205   Print (L"  %a", AsciiBuffer);\r
206 }\r
207 \r
208 /** This function traces 1 byte of data as specified in the\r
209     format string.\r
210 \r
211   @param [in] Format  The format string for tracing the data.\r
212   @param [in] Ptr     Pointer to the start of the buffer.\r
213 */\r
214 VOID\r
215 EFIAPI\r
216 DumpUint8 (\r
217   IN CONST CHAR16* Format,\r
218   IN UINT8*        Ptr\r
219   )\r
220 {\r
221   Print (Format, *Ptr);\r
222 }\r
223 \r
224 /** This function traces 2 bytes of data as specified in the\r
225     format string.\r
226 \r
227   @param [in] Format  The format string for tracing the data.\r
228   @param [in] Ptr     Pointer to the start of the buffer.\r
229 */\r
230 VOID\r
231 EFIAPI\r
232 DumpUint16 (\r
233   IN CONST CHAR16* Format,\r
234   IN UINT8*        Ptr\r
235   )\r
236 {\r
237   Print (Format, *(UINT16*)Ptr);\r
238 }\r
239 \r
240 /** This function traces 4 bytes of data as specified in the\r
241     format string.\r
242 \r
243   @param [in] Format  The format string for tracing the data.\r
244   @param [in] Ptr     Pointer to the start of the buffer.\r
245 */\r
246 VOID\r
247 EFIAPI\r
248 DumpUint32 (\r
249   IN CONST CHAR16* Format,\r
250   IN UINT8*        Ptr\r
251   )\r
252 {\r
253   Print (Format, *(UINT32*)Ptr);\r
254 }\r
255 \r
256 /** This function traces 8 bytes of data as specified by the\r
257     format string.\r
258 \r
259   @param [in] Format  The format string for tracing the data.\r
260   @param [in] Ptr     Pointer to the start of the buffer.\r
261 */\r
262 VOID\r
263 EFIAPI\r
264 DumpUint64 (\r
265   IN CONST CHAR16* Format,\r
266   IN UINT8*        Ptr\r
267   )\r
268 {\r
269   // Some fields are not aligned and this causes alignment faults\r
270   // on ARM platforms if the compiler generates LDRD instructions.\r
271   // Perform word access so that LDRD instructions are not generated.\r
272   UINT64 Val = *(UINT32*)(Ptr + sizeof (UINT32));\r
273   Val <<= 32;\r
274   Val |= *(UINT32*)Ptr;\r
275 \r
276   Print (Format, Val);\r
277 }\r
278 \r
279 /** This function traces 3 characters which can be optionally\r
280    formated using the format string if specified.\r
281 \r
282   If no format string is specified the Format must be NULL.\r
283 \r
284   @param [in] Format  Optional format string for tracing the data.\r
285   @param [in] Ptr     Pointer to the start of the buffer.\r
286 */\r
287 VOID\r
288 EFIAPI\r
289 Dump3Chars (\r
290   IN CONST CHAR16* Format OPTIONAL,\r
291   IN UINT8*        Ptr\r
292   )\r
293 {\r
294   Print (\r
295     (Format != NULL) ? Format : L"%c%c%c",\r
296     Ptr[0],\r
297     Ptr[1],\r
298     Ptr[2]\r
299     );\r
300 }\r
301 \r
302 /** This function traces 4 characters which can be optionally\r
303    formated using the format string if specified.\r
304 \r
305   If no format string is specified the Format must be NULL.\r
306 \r
307   @param [in] Format  Optional format string for tracing the data.\r
308   @param [in] Ptr     Pointer to the start of the buffer.\r
309 */\r
310 VOID\r
311 EFIAPI\r
312 Dump4Chars (\r
313   IN CONST CHAR16* Format OPTIONAL,\r
314   IN UINT8*        Ptr\r
315   )\r
316 {\r
317   Print (\r
318     (Format != NULL) ? Format : L"%c%c%c%c",\r
319     Ptr[0],\r
320     Ptr[1],\r
321     Ptr[2],\r
322     Ptr[3]\r
323     );\r
324 }\r
325 \r
326 /** This function traces 6 characters which can be optionally\r
327    formated using the format string if specified.\r
328 \r
329   If no format string is specified the Format must be NULL.\r
330 \r
331   @param [in] Format  Optional format string for tracing the data.\r
332   @param [in] Ptr     Pointer to the start of the buffer.\r
333 */\r
334 VOID\r
335 EFIAPI\r
336 Dump6Chars (\r
337   IN CONST CHAR16* Format OPTIONAL,\r
338   IN UINT8*        Ptr\r
339   )\r
340 {\r
341   Print (\r
342     (Format != NULL) ? Format : L"%c%c%c%c%c%c",\r
343     Ptr[0],\r
344     Ptr[1],\r
345     Ptr[2],\r
346     Ptr[3],\r
347     Ptr[4],\r
348     Ptr[5]\r
349     );\r
350 }\r
351 \r
352 /** This function traces 8 characters which can be optionally\r
353    formated using the format string if specified.\r
354 \r
355   If no format string is specified the Format must be NULL.\r
356 \r
357   @param [in] Format  Optional format string for tracing the data.\r
358   @param [in] Ptr     Pointer to the start of the buffer.\r
359 */\r
360 VOID\r
361 EFIAPI\r
362 Dump8Chars (\r
363   IN CONST CHAR16* Format OPTIONAL,\r
364   IN UINT8*        Ptr\r
365   )\r
366 {\r
367   Print (\r
368     (Format != NULL) ? Format : L"%c%c%c%c%c%c%c%c",\r
369     Ptr[0],\r
370     Ptr[1],\r
371     Ptr[2],\r
372     Ptr[3],\r
373     Ptr[4],\r
374     Ptr[5],\r
375     Ptr[6],\r
376     Ptr[7]\r
377     );\r
378 }\r
379 \r
380 /** This function indents and prints the ACPI table Field Name.\r
381 \r
382   @param [in] Indent      Number of spaces to add to the global table indent.\r
383                           The global table indent is 0 by default; however\r
384                           this value is updated on entry to the ParseAcpi()\r
385                           by adding the indent value provided to ParseAcpi()\r
386                           and restored back on exit.\r
387                           Therefore the total indent in the output is\r
388                           dependent on from where this function is called.\r
389   @param [in] FieldName   Pointer to the Field Name.\r
390 */\r
391 VOID\r
392 EFIAPI\r
393 PrintFieldName (\r
394   IN UINT32         Indent,\r
395   IN CONST CHAR16*  FieldName\r
396 )\r
397 {\r
398   Print (\r
399     L"%*a%-*s : ",\r
400     gIndent + Indent,\r
401     "",\r
402     (OUTPUT_FIELD_COLUMN_WIDTH - gIndent - Indent),\r
403     FieldName\r
404     );\r
405 }\r
406 \r
407 /** This function is used to parse an ACPI table buffer.\r
408 \r
409   The ACPI table buffer is parsed using the ACPI table parser information\r
410   specified by a pointer to an array of ACPI_PARSER elements. This parser\r
411   function iterates through each item on the ACPI_PARSER array and logs the\r
412   ACPI table fields.\r
413 \r
414   This function can optionally be used to parse ACPI tables and fetch specific\r
415   field values. The ItemPtr member of the ACPI_PARSER structure (where used)\r
416   is updated by this parser function to point to the selected field data\r
417   (e.g. useful for variable length nested fields).\r
418 \r
419   @param [in] Trace        Trace the ACPI fields TRUE else only parse the\r
420                            table.\r
421   @param [in] Indent       Number of spaces to indent the output.\r
422   @param [in] AsciiName    Optional pointer to an ASCII string that describes\r
423                            the table being parsed.\r
424   @param [in] Ptr          Pointer to the start of the buffer.\r
425   @param [in] Length       Length of the buffer pointed by Ptr.\r
426   @param [in] Parser       Pointer to an array of ACPI_PARSER structure that\r
427                            describes the table being parsed.\r
428   @param [in] ParserItems  Number of items in the ACPI_PARSER array.\r
429 \r
430   @retval Number of bytes parsed.\r
431 */\r
432 UINT32\r
433 EFIAPI\r
434 ParseAcpi (\r
435   IN BOOLEAN            Trace,\r
436   IN UINT32             Indent,\r
437   IN CONST CHAR8*       AsciiName OPTIONAL,\r
438   IN UINT8*             Ptr,\r
439   IN UINT32             Length,\r
440   IN CONST ACPI_PARSER* Parser,\r
441   IN UINT32             ParserItems\r
442 )\r
443 {\r
444   UINT32  Index;\r
445   UINT32  Offset = 0;\r
446 \r
447   // Increment the Indent\r
448   gIndent += Indent;\r
449 \r
450   if (Trace && (AsciiName != NULL)){\r
451     BOOLEAN HighLight = GetColourHighlighting ();\r
452     UINTN   OriginalAttribute;\r
453 \r
454     if (HighLight) {\r
455       OriginalAttribute = gST->ConOut->Mode->Attribute;\r
456       gST->ConOut->SetAttribute (\r
457                      gST->ConOut,\r
458                      EFI_TEXT_ATTR(EFI_YELLOW,\r
459                        ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))\r
460                      );\r
461     }\r
462     Print (\r
463       L"%*a%-*a :\n",\r
464       gIndent,\r
465       "",\r
466       (OUTPUT_FIELD_COLUMN_WIDTH - gIndent),\r
467       AsciiName\r
468       );\r
469     if (HighLight) {\r
470       gST->ConOut->SetAttribute (gST->ConOut, OriginalAttribute);\r
471     }\r
472   }\r
473 \r
474   for (Index = 0; Index < ParserItems; Index++) {\r
475     if ((Offset + Parser[Index].Length) > Length) {\r
476       // We don't parse past the end of the max length specified\r
477       break;\r
478     }\r
479 \r
480     if (Offset != Parser[Index].Offset) {\r
481       IncrementErrorCount ();\r
482       Print (\r
483         L"\nERROR: %a: Offset Mismatch for %s\n"\r
484           "CurrentOffset = %d FieldOffset = %d\n",\r
485         AsciiName,\r
486         Parser[Index].NameStr,\r
487         Offset,\r
488         Parser[Index].Offset\r
489         );\r
490     }\r
491 \r
492     if (Trace) {\r
493       // if there is a Formatter function let the function handle\r
494       // the printing else if a Format is specified in the table use\r
495       // the Format for printing\r
496       PrintFieldName (2, Parser[Index].NameStr);\r
497       if (Parser[Index].PrintFormatter != NULL) {\r
498         Parser[Index].PrintFormatter (Parser[Index].Format, Ptr);\r
499       } else if (Parser[Index].Format != NULL) {\r
500         switch (Parser[Index].Length) {\r
501           case 1:\r
502             DumpUint8 (Parser[Index].Format, Ptr);\r
503             break;\r
504           case 2:\r
505             DumpUint16 (Parser[Index].Format, Ptr);\r
506             break;\r
507           case 4:\r
508             DumpUint32 (Parser[Index].Format, Ptr);\r
509             break;\r
510           case 8:\r
511             DumpUint64 (Parser[Index].Format, Ptr);\r
512             break;\r
513           default:\r
514             Print (\r
515               L"\nERROR: %a: CANNOT PARSE THIS FIELD, Field Length = %d\n",\r
516               AsciiName,\r
517               Parser[Index].Length\r
518               );\r
519         } // switch\r
520 \r
521         // Validating only makes sense if we are tracing\r
522         // the parsed table entries, to report by table name.\r
523         if (Parser[Index].FieldValidator != NULL) {\r
524           Parser[Index].FieldValidator (Ptr, Parser[Index].Context);\r
525         }\r
526       }\r
527       Print (L"\n");\r
528     } // if (Trace)\r
529 \r
530     if (Parser[Index].ItemPtr != NULL) {\r
531       *Parser[Index].ItemPtr = (VOID*)Ptr;\r
532     }\r
533 \r
534     Ptr += Parser[Index].Length;\r
535     Offset += Parser[Index].Length;\r
536   } // for\r
537 \r
538   // Decrement the Indent\r
539   gIndent -= Indent;\r
540   return Offset;\r
541 }\r
542 \r
543 /** An array describing the ACPI Generic Address Structure.\r
544   The GasParser array is used by the ParseAcpi function to parse and/or trace\r
545   the GAS structure.\r
546 */\r
547 STATIC CONST ACPI_PARSER GasParser[] = {\r
548   {L"Address Space ID", 1, 0, L"0x%x", NULL, NULL, NULL, NULL},\r
549   {L"Register Bit Width", 1, 1, L"0x%x", NULL, NULL, NULL, NULL},\r
550   {L"Register Bit Offset", 1, 2, L"0x%x", NULL, NULL, NULL, NULL},\r
551   {L"Address Size", 1, 3, L"0x%x", NULL, NULL, NULL, NULL},\r
552   {L"Address", 8, 4, L"0x%lx", NULL, NULL, NULL, NULL}\r
553 };\r
554 \r
555 /** This function indents and traces the GAS structure as described\r
556     by the GasParser.\r
557 \r
558   @param [in] Ptr     Pointer to the start of the buffer.\r
559   @param [in] Indent  Number of spaces to indent the output.\r
560 */\r
561 VOID\r
562 EFIAPI\r
563 DumpGasStruct (\r
564   IN UINT8*        Ptr,\r
565   IN UINT32        Indent\r
566   )\r
567 {\r
568   Print (L"\n");\r
569   ParseAcpi (\r
570     TRUE,\r
571     Indent,\r
572     NULL,\r
573     Ptr,\r
574     GAS_LENGTH,\r
575     PARSER_PARAMS (GasParser)\r
576     );\r
577 }\r
578 \r
579 /** This function traces the GAS structure as described by the GasParser.\r
580 \r
581   @param [in] Format  Optional format string for tracing the data.\r
582   @param [in] Ptr     Pointer to the start of the buffer.\r
583 */\r
584 VOID\r
585 EFIAPI\r
586 DumpGas (\r
587   IN CONST CHAR16* Format OPTIONAL,\r
588   IN UINT8*        Ptr\r
589   )\r
590 {\r
591   DumpGasStruct (Ptr, 2);\r
592 }\r
593 \r
594 /** This function traces the ACPI header as described by the AcpiHeaderParser.\r
595 \r
596   @param [in] Ptr          Pointer to the start of the buffer.\r
597 \r
598   @retval Number of bytes parsed.\r
599 */\r
600 UINT32\r
601 EFIAPI\r
602 DumpAcpiHeader (\r
603   IN UINT8* Ptr\r
604   )\r
605 {\r
606   ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo;\r
607   ACPI_PARSER AcpiHeaderParser[] = {\r
608     PARSE_ACPI_HEADER (&AcpiHdrInfo)\r
609   };\r
610 \r
611   return ParseAcpi (\r
612            TRUE,\r
613            0,\r
614            "ACPI Table Header",\r
615            Ptr,\r
616            ACPI_DESCRIPTION_HEADER_LENGTH,\r
617            PARSER_PARAMS (AcpiHeaderParser)\r
618            );\r
619 }\r
620 \r
621 /** This function parses the ACPI header as described by the AcpiHeaderParser.\r
622 \r
623   This function optionally returns the signature, length and revision of the\r
624   ACPI table.\r
625 \r
626   @param [in]  Ptr        Pointer to the start of the buffer.\r
627   @param [out] Signature  Gets location of the ACPI table signature.\r
628   @param [out] Length     Gets location of the length of the ACPI table.\r
629   @param [out] Revision   Gets location of the revision of the ACPI table.\r
630 \r
631   @retval Number of bytes parsed.\r
632 */\r
633 UINT32\r
634 EFIAPI\r
635 ParseAcpiHeader (\r
636   IN  UINT8*         Ptr,\r
637   OUT CONST UINT32** Signature,\r
638   OUT CONST UINT32** Length,\r
639   OUT CONST UINT8**  Revision\r
640   )\r
641 {\r
642   UINT32                        BytesParsed;\r
643   ACPI_DESCRIPTION_HEADER_INFO  AcpiHdrInfo;\r
644   ACPI_PARSER AcpiHeaderParser[] = {\r
645     PARSE_ACPI_HEADER (&AcpiHdrInfo)\r
646   };\r
647 \r
648   BytesParsed = ParseAcpi (\r
649                   FALSE,\r
650                   0,\r
651                   NULL,\r
652                   Ptr,\r
653                   ACPI_DESCRIPTION_HEADER_LENGTH,\r
654                   PARSER_PARAMS (AcpiHeaderParser)\r
655                   );\r
656 \r
657   *Signature = AcpiHdrInfo.Signature;\r
658   *Length = AcpiHdrInfo.Length;\r
659   *Revision = AcpiHdrInfo.Revision;\r
660 \r
661   return BytesParsed;\r
662 }\r