1 /** @file
2   FADT table parser
3 
4   Copyright (c) 2016 - 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 **/
10 
11 #include <IndustryStandard/Acpi.h>
12 #include <Library/UefiLib.h>
13 #include "AcpiParser.h"
14 #include "AcpiTableParser.h"
15 #include "AcpiView.h"
16 
17 // Local variables
18 STATIC CONST UINT32* DsdtAddress;
19 STATIC CONST UINT64* X_DsdtAddress;
20 STATIC CONST UINT32* Flags;
21 STATIC CONST UINT32* FirmwareCtrl;
22 STATIC CONST UINT64* X_FirmwareCtrl;
23 STATIC CONST UINT8*  FadtMinorRevision;
24 STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo;
25 
26 /**
27   A macro defining the Hardware reduced ACPI flag
28 **/
29 #define HW_REDUCED_ACPI   BIT20
30 
31 /**
32   Offset to the FACS signature from the start of the FACS.
33 **/
34 #define FACS_SIGNATURE_OFFSET   0
35 
36 /**
37   Offset to the FACS revision from the start of the FACS.
38 **/
39 #define FACS_VERSION_OFFSET     32
40 
41 /**
42   Offset to the FACS length from the start of the FACS.
43 **/
44 #define FACS_LENGTH_OFFSET      4
45 
46 /**
47   Get the ACPI XSDT header info.
48 **/
49 CONST ACPI_DESCRIPTION_HEADER_INFO *
50 EFIAPI
51 GetAcpiXsdtHeaderInfo (
52   VOID
53   );
54 
55 /**
56   This function validates the Firmware Control Field.
57 
58   @param [in] Ptr     Pointer to the start of the field data.
59   @param [in] Context Pointer to context specific information e.g. this
60                       could be a pointer to the ACPI table header.
61 **/
62 STATIC
63 VOID
64 EFIAPI
ValidateFirmwareCtrl(IN UINT8 * Ptr,IN VOID * Context)65 ValidateFirmwareCtrl (
66   IN UINT8* Ptr,
67   IN VOID*  Context
68 )
69 {
70 #if defined (MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
71   if (*(UINT32*)Ptr != 0) {
72     IncrementErrorCount ();
73     Print (
74       L"\nERROR: Firmware Control must be zero for ARM platforms."
75     );
76   }
77 #endif
78 }
79 
80 /**
81   This function validates the X_Firmware Control Field.
82 
83   @param [in] Ptr     Pointer to the start of the field data.
84   @param [in] Context Pointer to context specific information e.g. this
85                       could be a pointer to the ACPI table header.
86 **/
87 STATIC
88 VOID
89 EFIAPI
ValidateXFirmwareCtrl(IN UINT8 * Ptr,IN VOID * Context)90 ValidateXFirmwareCtrl (
91   IN UINT8* Ptr,
92   IN VOID*  Context
93 )
94 {
95 #if defined (MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
96   if (*(UINT64*)Ptr != 0) {
97     IncrementErrorCount ();
98     Print (
99       L"\nERROR: X Firmware Control must be zero for ARM platforms."
100     );
101   }
102 #endif
103 }
104 
105 /**
106   This function validates the flags.
107 
108   @param [in] Ptr     Pointer to the start of the field data.
109   @param [in] Context Pointer to context specific information e.g. this
110                       could be a pointer to the ACPI table header.
111 **/
112 STATIC
113 VOID
114 EFIAPI
ValidateFlags(IN UINT8 * Ptr,IN VOID * Context)115 ValidateFlags (
116   IN UINT8* Ptr,
117   IN VOID*  Context
118 )
119 {
120 #if defined (MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
121   if (((*(UINT32*)Ptr) & HW_REDUCED_ACPI) == 0) {
122     IncrementErrorCount ();
123     Print (
124       L"\nERROR: HW_REDUCED_ACPI flag must be set for ARM platforms."
125     );
126   }
127 #endif
128 }
129 
130 /**
131   An ACPI_PARSER array describing the ACPI FADT Table.
132 **/
133 STATIC CONST ACPI_PARSER FadtParser[] = {
134   PARSE_ACPI_HEADER (&AcpiHdrInfo),
135   {L"FIRMWARE_CTRL", 4, 36, L"0x%x", NULL, (VOID**)&FirmwareCtrl,
136     ValidateFirmwareCtrl, NULL},
137   {L"DSDT", 4, 40, L"0x%x", NULL, (VOID**)&DsdtAddress, NULL, NULL},
138   {L"Reserved", 1, 44, L"%x", NULL, NULL, NULL, NULL},
139   {L"Preferred_PM_Profile", 1, 45, L"0x%x", NULL, NULL, NULL, NULL},
140   {L"SCI_INT", 2, 46, L"0x%x", NULL, NULL, NULL, NULL},
141   {L"SMI_CMD", 4, 48, L"0x%x", NULL, NULL, NULL, NULL},
142   {L"ACPI_ENABLE", 1, 52, L"0x%x", NULL, NULL, NULL, NULL},
143   {L"ACPI_DISABLE", 1, 53, L"0x%x", NULL, NULL, NULL, NULL},
144   {L"S4BIOS_REQ", 1, 54, L"0x%x", NULL, NULL, NULL, NULL},
145   {L"PSTATE_CNT", 1, 55, L"0x%x", NULL, NULL, NULL, NULL},
146   {L"PM1a_EVT_BLK", 4, 56, L"0x%x", NULL, NULL, NULL, NULL},
147   {L"PM1b_EVT_BLK", 4, 60, L"0x%x", NULL, NULL, NULL, NULL},
148   {L"PM1a_CNT_BLK", 4, 64, L"0x%x", NULL, NULL, NULL, NULL},
149   {L"PM1b_CNT_BLK", 4, 68, L"0x%x", NULL, NULL, NULL, NULL},
150   {L"PM2_CNT_BLK", 4, 72, L"0x%x", NULL, NULL, NULL, NULL},
151   {L"PM_TMR_BLK", 4, 76, L"0x%x", NULL, NULL, NULL, NULL},
152   {L"GPE0_BLK", 4, 80, L"0x%x", NULL, NULL, NULL, NULL},
153   {L"GPE1_BLK", 4, 84, L"0x%x", NULL, NULL, NULL, NULL},
154   {L"PM1_EVT_LEN", 1, 88, L"0x%x", NULL, NULL, NULL, NULL},
155   {L"PM1_CNT_LEN", 1, 89, L"0x%x", NULL, NULL, NULL, NULL},
156   {L"PM2_CNT_LEN", 1, 90, L"0x%x", NULL, NULL, NULL, NULL},
157   {L"PM_TMR_LEN", 1, 91, L"0x%x", NULL, NULL, NULL, NULL},
158   {L"GPE0_BLK_LEN", 1, 92, L"0x%x", NULL, NULL, NULL, NULL},
159   {L"GPE1_BLK_LEN", 1, 93, L"0x%x", NULL, NULL, NULL, NULL},
160   {L"GPE1_BASE", 1, 94, L"0x%x", NULL, NULL, NULL, NULL},
161   {L"CST_CNT", 1, 95, L"0x%x", NULL, NULL, NULL, NULL},
162   {L"P_LVL2_LAT", 2, 96, L"0x%x", NULL, NULL, NULL, NULL},
163   {L"P_LVL3_LAT", 2, 98, L"0x%x", NULL, NULL, NULL, NULL},
164   {L"FLUSH_SIZE", 2, 100, L"0x%x", NULL, NULL, NULL, NULL},
165   {L"FLUSH_STRIDE", 2, 102, L"0x%x", NULL, NULL, NULL, NULL},
166   {L"DUTY_OFFSET", 1, 104, L"0x%x", NULL, NULL, NULL, NULL},
167   {L"DUTY_WIDTH", 1, 105, L"0x%x", NULL, NULL, NULL, NULL},
168   {L"DAY_ALRM", 1, 106, L"0x%x", NULL, NULL, NULL, NULL},
169   {L"MON_ALRM", 1, 107, L"0x%x", NULL, NULL, NULL, NULL},
170   {L"CENTURY", 1, 108, L"0x%x", NULL, NULL, NULL, NULL},
171   {L"IAPC_BOOT_ARCH", 2, 109, L"0x%x", NULL, NULL, NULL, NULL},
172   {L"Reserved", 1, 111, L"0x%x", NULL, NULL, NULL, NULL},
173   {L"Flags", 4, 112, L"0x%x", NULL, (VOID**)&Flags, ValidateFlags, NULL},
174   {L"RESET_REG", 12, 116, NULL, DumpGas, NULL, NULL, NULL},
175   {L"RESET_VALUE", 1, 128, L"0x%x", NULL, NULL, NULL, NULL},
176   {L"ARM_BOOT_ARCH", 2, 129, L"0x%x", NULL, NULL, NULL, NULL},
177   {L"FADT Minor Version", 1, 131, L"0x%x", NULL, (VOID**)&FadtMinorRevision,
178     NULL, NULL},
179   {L"X_FIRMWARE_CTRL", 8, 132, L"0x%lx", NULL, (VOID**)&X_FirmwareCtrl,
180     ValidateXFirmwareCtrl, NULL},
181   {L"X_DSDT", 8, 140, L"0x%lx", NULL, (VOID**)&X_DsdtAddress, NULL, NULL},
182   {L"X_PM1a_EVT_BLK", 12, 148, NULL, DumpGas, NULL, NULL, NULL},
183   {L"X_PM1b_EVT_BLK", 12, 160, NULL, DumpGas, NULL, NULL, NULL},
184   {L"X_PM1a_CNT_BLK", 12, 172, NULL, DumpGas, NULL, NULL, NULL},
185   {L"X_PM1b_CNT_BLK", 12, 184, NULL, DumpGas, NULL, NULL, NULL},
186   {L"X_PM2_CNT_BLK", 12, 196, NULL, DumpGas, NULL, NULL, NULL},
187   {L"X_PM_TMR_BLK", 12, 208, NULL, DumpGas, NULL, NULL, NULL},
188   {L"X_GPE0_BLK", 12, 220, NULL, DumpGas, NULL, NULL, NULL},
189   {L"X_GPE1_BLK", 12, 232, NULL, DumpGas, NULL, NULL, NULL},
190   {L"SLEEP_CONTROL_REG", 12, 244, NULL, DumpGas, NULL, NULL, NULL},
191   {L"SLEEP_STATUS_REG", 12, 256, NULL, DumpGas, NULL, NULL, NULL},
192   {L"Hypervisor VendorIdentity", 8, 268, L"%lx", NULL, NULL, NULL, NULL}
193 };
194 
195 /**
196   This function parses the ACPI FADT table.
197   This function parses the FADT table and optionally traces the ACPI table fields.
198 
199   This function also performs validation of the ACPI table fields.
200 
201   @param [in] Trace              If TRUE, trace the ACPI fields.
202   @param [in] Ptr                Pointer to the start of the buffer.
203   @param [in] AcpiTableLength    Length of the ACPI table.
204   @param [in] AcpiTableRevision  Revision of the ACPI table.
205 **/
206 VOID
207 EFIAPI
ParseAcpiFadt(IN BOOLEAN Trace,IN UINT8 * Ptr,IN UINT32 AcpiTableLength,IN UINT8 AcpiTableRevision)208 ParseAcpiFadt (
209   IN BOOLEAN Trace,
210   IN UINT8*  Ptr,
211   IN UINT32  AcpiTableLength,
212   IN UINT8   AcpiTableRevision
213   )
214 {
215   EFI_STATUS              Status;
216   UINT8*                  DsdtPtr;
217   UINT8*                  FirmwareCtrlPtr;
218   UINT32                  FacsSignature;
219   UINT32                  FacsLength;
220   UINT8                   FacsRevision;
221   PARSE_ACPI_TABLE_PROC   FacsParserProc;
222 
223   ParseAcpi (
224     Trace,
225     0,
226     "FADT",
227     Ptr,
228     AcpiTableLength,
229     PARSER_PARAMS (FadtParser)
230     );
231 
232   if (Trace) {
233     if (FadtMinorRevision != NULL) {
234       Print (L"\nSummary:\n");
235       PrintFieldName (2, L"FADT Version");
236       Print (L"%d.%d\n",  *AcpiHdrInfo.Revision, *FadtMinorRevision);
237     }
238 
239     if (*GetAcpiXsdtHeaderInfo ()->OemTableId != *AcpiHdrInfo.OemTableId) {
240       IncrementErrorCount ();
241       Print (L"ERROR: OEM Table Id does not match with RSDT/XSDT.\n");
242     }
243   }
244 
245   // If X_FIRMWARE_CTRL is not zero then use X_FIRMWARE_CTRL and ignore
246   // FIRMWARE_CTRL, else use FIRMWARE_CTRL.
247   if ((X_FirmwareCtrl != NULL) && (*X_FirmwareCtrl != 0)) {
248     FirmwareCtrlPtr = (UINT8*)(UINTN)(*X_FirmwareCtrl);
249   } else if ((FirmwareCtrl != NULL) && (*FirmwareCtrl != 0)) {
250     FirmwareCtrlPtr = (UINT8*)(UINTN)(*FirmwareCtrl);
251   } else {
252     FirmwareCtrlPtr = NULL;
253     // if HW_REDUCED_ACPI flag is not set, both FIRMWARE_CTRL and
254     // X_FIRMWARE_CTRL cannot be zero, and the FACS Table must be
255     // present.
256     if ((Trace) &&
257         (Flags != NULL) &&
258         ((*Flags & EFI_ACPI_6_3_HW_REDUCED_ACPI) != EFI_ACPI_6_3_HW_REDUCED_ACPI)) {
259       IncrementErrorCount ();
260       Print (L"ERROR: No FACS table found, "
261                L"both X_FIRMWARE_CTRL and FIRMWARE_CTRL are zero.\n");
262     }
263   }
264 
265   if (FirmwareCtrlPtr != NULL) {
266     // The FACS table does not have a standard ACPI table header. Therefore,
267     // the signature, length and version needs to be initially parsed.
268     // The FACS signature is 4 bytes starting at offset 0.
269     FacsSignature = *(UINT32*)(FirmwareCtrlPtr + FACS_SIGNATURE_OFFSET);
270 
271     // The FACS length is 4 bytes starting at offset 4.
272     FacsLength = *(UINT32*)(FirmwareCtrlPtr + FACS_LENGTH_OFFSET);
273 
274     // The FACS version is 1 byte starting at offset 32.
275     FacsRevision = *(UINT8*)(FirmwareCtrlPtr + FACS_VERSION_OFFSET);
276 
277     Trace = ProcessTableReportOptions (
278               FacsSignature,
279               FirmwareCtrlPtr,
280               FacsLength
281               );
282 
283     Status = GetParser (FacsSignature, &FacsParserProc);
284     if (EFI_ERROR (Status)) {
285       Print (
286         L"ERROR: No registered parser found for FACS.\n"
287         );
288       return;
289     }
290 
291     FacsParserProc (
292       Trace,
293       FirmwareCtrlPtr,
294       FacsLength,
295       FacsRevision
296       );
297   }
298 
299   // If X_DSDT is valid then use X_DSDT and ignore DSDT, else use DSDT.
300   if ((X_DsdtAddress != NULL) && (*X_DsdtAddress != 0)) {
301     DsdtPtr = (UINT8*)(UINTN)(*X_DsdtAddress);
302   } else if ((DsdtAddress != NULL) && (*DsdtAddress != 0)) {
303     DsdtPtr = (UINT8*)(UINTN)(*DsdtAddress);
304   } else {
305     // Both DSDT and X_DSDT cannot be invalid.
306 #if defined (MDE_CPU_ARM) || defined (MDE_CPU_AARCH64)
307     if (Trace) {
308       // The DSDT Table is mandatory for ARM systems
309       // as the CPU information MUST be presented in
310       // the DSDT.
311       IncrementErrorCount ();
312       Print (L"ERROR: Both X_DSDT and DSDT are invalid.\n");
313     }
314 #endif
315     return;
316   }
317 
318   ProcessAcpiTable (DsdtPtr);
319 }
320