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