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