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