]> git.proxmox.com Git - mirror_edk2.git/blame - DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
DynamicTablesPkg: SsdtSerialPortFixupLib fix ECC errors
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / SsdtSerialPortFixupLib / SsdtSerialPortFixupLib.c
CommitLineData
bade7f42
PG
1/** @file\r
2 SSDT Serial Port Fixup Library.\r
3\r
4 Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8 @par Reference(s):\r
9 - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".\r
10 - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.\r
11**/\r
12\r
13#include <IndustryStandard/DebugPort2Table.h>\r
14#include <Library/AcpiLib.h>\r
15#include <Library/BaseLib.h>\r
16#include <Library/BaseMemoryLib.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/MemoryAllocationLib.h>\r
19#include <Library/UefiBootServicesTableLib.h>\r
20#include <Protocol/AcpiTable.h>\r
21\r
22// Module specific include files.\r
23#include <AcpiTableGenerator.h>\r
24#include <ConfigurationManagerObject.h>\r
25#include <ConfigurationManagerHelper.h>\r
26#include <Library/AmlLib/AmlLib.h>\r
27#include <Library/TableHelperLib.h>\r
28#include <Protocol/ConfigurationManagerProtocol.h>\r
29\r
30/** C array containing the compiled AML template.\r
31 This symbol is defined in the auto generated C file\r
32 containing the AML bytecode array.\r
33*/\r
34extern CHAR8 ssdtserialporttemplate_aml_code[];\r
35\r
36/** UART address range length.\r
37*/\r
38#define MIN_UART_ADDRESS_LENGTH 0x1000U\r
39\r
40/** Validate the Serial Port Information.\r
41\r
42 @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.\r
43 @param [in] SerialPortCount Count of SerialPort in the table.\r
44\r
45 @retval EFI_SUCCESS Success.\r
46 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
47**/\r
48EFI_STATUS\r
49EFIAPI\r
50ValidateSerialPortInfo (\r
51 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfoTable,\r
52 IN UINT32 SerialPortCount\r
53 )\r
54{\r
55 UINT32 Index;\r
56 CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo;\r
57\r
58 if ((SerialPortInfoTable == NULL) ||\r
59 (SerialPortCount == 0)) {\r
60 ASSERT (0);\r
61 return EFI_INVALID_PARAMETER;\r
62 }\r
63\r
64 for (Index = 0; Index < SerialPortCount; Index++) {\r
65 SerialPortInfo = &SerialPortInfoTable[Index];\r
66 ASSERT (SerialPortInfo != NULL);\r
67\r
68 if ((SerialPortInfo == NULL ) ||\r
69 (SerialPortInfo->BaseAddress == 0)) {\r
70 DEBUG ((\r
71 DEBUG_ERROR,\r
72 "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",\r
73 SerialPortInfo->BaseAddress\r
74 ));\r
75 return EFI_INVALID_PARAMETER;\r
76 }\r
77\r
78 if ((SerialPortInfo->PortSubtype !=\r
79 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART) &&\r
80 (SerialPortInfo->PortSubtype !=\r
81 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X) &&\r
82 (SerialPortInfo->PortSubtype !=\r
83 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART) &&\r
84 (SerialPortInfo->PortSubtype !=\r
85 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC) &&\r
86 (SerialPortInfo->PortSubtype !=\r
87 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550)) {\r
88 DEBUG ((\r
89 DEBUG_ERROR,\r
90 "ERROR: UART port subtype is invalid."\r
91 " UART Base = 0x%llx, PortSubtype = 0x%x\n",\r
92 SerialPortInfo->BaseAddress,\r
93 SerialPortInfo->PortSubtype\r
94 ));\r
95 return EFI_INVALID_PARAMETER;\r
96 }\r
97\r
98 DEBUG ((DEBUG_INFO, "UART Configuration:\n"));\r
99 DEBUG ((\r
100 DEBUG_INFO,\r
101 " UART Base = 0x%llx\n", SerialPortInfo->BaseAddress\r
102 ));\r
103 DEBUG ((\r
104 DEBUG_INFO,\r
105 " Length = 0x%llx\n",\r
106 SerialPortInfo->BaseAddressLength\r
107 ));\r
108 DEBUG ((DEBUG_INFO, " Clock = %lu\n", SerialPortInfo->Clock));\r
109 DEBUG ((DEBUG_INFO, " BaudRate = %llu\n", SerialPortInfo->BaudRate));\r
110 DEBUG ((DEBUG_INFO, " Interrupt = %lu\n", SerialPortInfo->Interrupt));\r
111 } // for\r
112\r
113 return EFI_SUCCESS;\r
114}\r
115\r
116/** Fixup the Serial Port Ids (_UID, _HID, _CID).\r
117\r
118 @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
119 @param [in] Uid UID for the Serial Port.\r
120 @param [in] SerialPortInfo Pointer to a Serial Port Information\r
121 structure.\r
122 Get the Serial Port Information from there.\r
123\r
124 @retval EFI_SUCCESS The function completed successfully.\r
125 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
126 @retval EFI_NOT_FOUND Could not find information.\r
127 @retval EFI_OUT_OF_RESOURCES Out of resources.\r
128**/\r
129STATIC\r
130EFI_STATUS\r
131EFIAPI\r
132FixupIds (\r
e3c7db50
SM
133 IN AML_ROOT_NODE_HANDLE RootNodeHandle,\r
134 IN CONST UINT64 Uid,\r
135 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo\r
bade7f42
PG
136 )\r
137{\r
138 EFI_STATUS Status;\r
139 AML_OBJECT_NODE_HANDLE NameOpIdNode;\r
140 CONST CHAR8 * HidString;\r
141 CONST CHAR8 * CidString;\r
142\r
143 // Get the _CID and _HID value to write.\r
144 switch (SerialPortInfo->PortSubtype) {\r
145 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550:\r
146 {\r
147 HidString = "PNP0501";\r
148 CidString = "PNP0500";\r
149 break;\r
150 }\r
151 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART:\r
152 {\r
153 HidString = "ARMH0011";\r
154 CidString = "ARMHB000";\r
155 break;\r
156 }\r
157 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART:\r
158 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X:\r
159 {\r
160 HidString = "ARMH0011";\r
161 CidString = "";\r
162 break;\r
163 }\r
164 default:\r
165 {\r
166 return EFI_INVALID_PARAMETER;\r
167 }\r
168 } // switch\r
169\r
170 // Get the _UID NameOp object defined by the "Name ()" statement,\r
171 // and update its value.\r
172 Status = AmlFindNode (\r
173 RootNodeHandle,\r
174 "\\_SB_.COM0._UID",\r
175 &NameOpIdNode\r
176 );\r
177 if (EFI_ERROR (Status)) {\r
178 return Status;\r
179 }\r
180\r
181 Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);\r
182 if (EFI_ERROR (Status)) {\r
183 return Status;\r
184 }\r
185\r
186 // Get the _HID NameOp object defined by the "Name ()" statement,\r
187 // and update its value.\r
188 Status = AmlFindNode (\r
189 RootNodeHandle,\r
190 "\\_SB_.COM0._HID",\r
191 &NameOpIdNode\r
192 );\r
193 if (EFI_ERROR (Status)) {\r
194 return Status;\r
195 }\r
196\r
197 Status = AmlNameOpUpdateString (NameOpIdNode, HidString);\r
198 if (EFI_ERROR (Status)) {\r
199 return Status;\r
200 }\r
201\r
202 // Get the _CID NameOp object defined by the "Name ()" statement,\r
203 // and update its value.\r
204 Status = AmlFindNode (\r
205 RootNodeHandle,\r
206 "\\_SB_.COM0._CID",\r
207 &NameOpIdNode\r
208 );\r
209 if (EFI_ERROR (Status)) {\r
210 return Status;\r
211 }\r
212\r
213 // If we have a CID then update a _CID node else delete the node.\r
214 if (AsciiStrLen (CidString) != 0) {\r
215 Status = AmlNameOpUpdateString (NameOpIdNode, CidString);\r
216 } else {\r
217 // First detach the node from the tree.\r
218 Status = AmlDetachNode (NameOpIdNode);\r
219 if (EFI_ERROR (Status)) {\r
220 return Status;\r
221 }\r
222\r
223 // Delete the detached node.\r
224 Status = AmlDeleteTree (NameOpIdNode);\r
225 }\r
226\r
227 return Status;\r
228}\r
229\r
230/** Fixup the Serial Port _CRS values (BaseAddress, ...).\r
231\r
232 @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
233 @param [in] SerialPortInfo Pointer to a Serial Port Information\r
234 structure.\r
235 Get the Serial Port Information from there.\r
236\r
237 @retval EFI_SUCCESS The function completed successfully.\r
238 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
239 @retval EFI_NOT_FOUND Could not find information.\r
240 @retval EFI_OUT_OF_RESOURCES Out of resources.\r
241**/\r
242STATIC\r
243EFI_STATUS\r
244EFIAPI\r
245FixupCrs (\r
e3c7db50
SM
246 IN AML_ROOT_NODE_HANDLE RootNodeHandle,\r
247 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo\r
bade7f42
PG
248 )\r
249{\r
250 EFI_STATUS Status;\r
251 AML_OBJECT_NODE_HANDLE NameOpCrsNode;\r
252 AML_DATA_NODE_HANDLE QWordRdNode;\r
253 AML_DATA_NODE_HANDLE InterruptRdNode;\r
254\r
255 // Get the "_CRS" object defined by the "Name ()" statement.\r
256 Status = AmlFindNode (\r
257 RootNodeHandle,\r
258 "\\_SB_.COM0._CRS",\r
259 &NameOpCrsNode\r
260 );\r
261 if (EFI_ERROR (Status)) {\r
262 return Status;\r
263 }\r
264\r
265 // Get the first Rd node in the "_CRS" object.\r
266 Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &QWordRdNode);\r
267 if (EFI_ERROR (Status)) {\r
268 return Status;\r
269 }\r
270\r
271 if (QWordRdNode == NULL) {\r
272 return EFI_INVALID_PARAMETER;\r
273 }\r
274\r
275 // Update the Serial Port base address and length.\r
276 Status = AmlUpdateRdQWord (\r
277 QWordRdNode,\r
278 SerialPortInfo->BaseAddress,\r
279 ((SerialPortInfo->BaseAddressLength < MIN_UART_ADDRESS_LENGTH) ?\r
280 MIN_UART_ADDRESS_LENGTH: SerialPortInfo->BaseAddressLength)\r
281 );\r
282 if (EFI_ERROR (Status)) {\r
283 return Status;\r
284 }\r
285\r
286 // Get the Interrupt node.\r
287 // It is the second Resource Data element in the NameOpCrsNode's\r
288 // variable list of arguments.\r
289 Status = AmlNameOpCrsGetNextRdNode (QWordRdNode, &InterruptRdNode);\r
290 if (EFI_ERROR (Status)) {\r
291 return Status;\r
292 }\r
293\r
294 if (InterruptRdNode == NULL) {\r
295 return EFI_INVALID_PARAMETER;\r
296 }\r
297\r
298 // Update the interrupt number.\r
299 return AmlUpdateRdInterrupt (InterruptRdNode, SerialPortInfo->Interrupt);\r
300}\r
301\r
302/** Fixup the Serial Port device name.\r
303\r
304 @param [in] RootNodeHandle Pointer to the root of an AML tree.\r
305 @param [in] SerialPortInfo Pointer to a Serial Port Information\r
306 structure.\r
307 Get the Serial Port Information from there.\r
308 @param [in] Name The Name to give to the Device.\r
309 Must be a NULL-terminated ASL NameString\r
310 e.g.: "DEV0", "DV15.DEV0", etc.\r
311\r
312 @retval EFI_SUCCESS The function completed successfully.\r
313 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
314 @retval EFI_NOT_FOUND Could not find information.\r
315 @retval EFI_OUT_OF_RESOURCES Out of resources.\r
316**/\r
317STATIC\r
318EFI_STATUS\r
319EFIAPI\r
320FixupName (\r
e3c7db50
SM
321 IN AML_ROOT_NODE_HANDLE RootNodeHandle,\r
322 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
323 IN CONST CHAR8 * Name\r
bade7f42
PG
324 )\r
325{\r
326 EFI_STATUS Status;\r
327 AML_OBJECT_NODE_HANDLE DeviceNode;\r
328\r
329 // Get the COM0 variable defined by the "Device ()" statement.\r
330 Status = AmlFindNode (RootNodeHandle, "\\_SB_.COM0", &DeviceNode);\r
331 if (EFI_ERROR (Status)) {\r
332 return Status;\r
333 }\r
334\r
335 // Update the Device's name.\r
336 return AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);\r
337}\r
338\r
339/** Fixup the Serial Port Information in the AML tree.\r
340\r
341 For each template value:\r
342 - find the node to update;\r
343 - update the value.\r
344\r
345 @param [in] RootNodeHandle Pointer to the root of the AML tree.\r
346 @param [in] SerialPortInfo Pointer to a Serial Port Information\r
347 structure.\r
348 Get the Serial Port Information from there.\r
349 @param [in] Name The Name to give to the Device.\r
350 Must be a NULL-terminated ASL NameString\r
351 e.g.: "DEV0", "DV15.DEV0", etc.\r
352 @param [in] Uid UID for the Serial Port.\r
353 @param [out] Table If success, contains the serialized\r
354 SSDT table.\r
355\r
356 @retval EFI_SUCCESS The function completed successfully.\r
357 @retval EFI_INVALID_PARAMETER Invalid parameter.\r
358 @retval EFI_NOT_FOUND Could not find information.\r
359 @retval EFI_OUT_OF_RESOURCES Out of resources.\r
360**/\r
361STATIC\r
362EFI_STATUS\r
363EFIAPI\r
364FixupSerialPortInfo (\r
e3c7db50 365 IN AML_ROOT_NODE_HANDLE RootNodeHandle,\r
bade7f42
PG
366 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
367 IN CONST CHAR8 * Name,\r
368 IN CONST UINT64 Uid,\r
369 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table\r
370 )\r
371{\r
372 EFI_STATUS Status;\r
373\r
374 ASSERT (RootNodeHandle != NULL);\r
375 ASSERT (SerialPortInfo != NULL);\r
376 ASSERT (Name != NULL);\r
377 ASSERT (Table != NULL);\r
378\r
379 // Fixup the _UID, _HID and _CID values.\r
380 Status = FixupIds (RootNodeHandle, Uid, SerialPortInfo);\r
381 if (EFI_ERROR (Status)) {\r
382 return Status;\r
383 }\r
384\r
385 // Fixup the _CRS values.\r
386 Status = FixupCrs (RootNodeHandle, SerialPortInfo);\r
387 if (EFI_ERROR (Status)) {\r
388 return Status;\r
389 }\r
390\r
391 // Fixup the serial-port name.\r
392 // This MUST be done at the end, otherwise AML paths won't be valid anymore.\r
393 return FixupName (RootNodeHandle, SerialPortInfo, Name);\r
394}\r
395\r
396/** Free an SSDT table previously created by\r
397 the BuildSsdtSerialTable function.\r
398\r
399 @param [in] Table Pointer to a SSDT table allocated by\r
400 the BuildSsdtSerialTable function.\r
401\r
402 @retval EFI_SUCCESS Success.\r
403**/\r
404EFI_STATUS\r
405EFIAPI\r
406FreeSsdtSerialPortTable (\r
407 IN EFI_ACPI_DESCRIPTION_HEADER * Table\r
408 )\r
409{\r
410 ASSERT (Table != NULL);\r
411 FreePool (Table);\r
412 return EFI_SUCCESS;\r
413}\r
414\r
415/** Build a SSDT table describing the input serial port.\r
416\r
417 The table created by this function must be freed by FreeSsdtSerialTable.\r
418\r
419 @param [in] AcpiTableInfo Pointer to the ACPI table information.\r
420 @param [in] SerialPortInfo Serial port to describe in the SSDT table.\r
421 @param [in] Name The Name to give to the Device.\r
422 Must be a NULL-terminated ASL NameString\r
423 e.g.: "DEV0", "DV15.DEV0", etc.\r
424 @param [in] Uid UID for the Serial Port.\r
425 @param [out] Table If success, pointer to the created SSDT table.\r
426\r
427 @retval EFI_SUCCESS Table generated successfully.\r
428 @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
429 @retval EFI_NOT_FOUND Could not find information.\r
430 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.\r
431**/\r
432EFI_STATUS\r
433EFIAPI\r
434BuildSsdtSerialPortTable (\r
435 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * AcpiTableInfo,\r
436 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,\r
437 IN CONST CHAR8 * Name,\r
438 IN CONST UINT64 Uid,\r
439 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table\r
440 )\r
441{\r
442 EFI_STATUS Status;\r
443 EFI_STATUS Status1;\r
444 AML_ROOT_NODE_HANDLE RootNodeHandle;\r
445\r
446 ASSERT (AcpiTableInfo != NULL);\r
447 ASSERT (SerialPortInfo != NULL);\r
448 ASSERT (Name != NULL);\r
449 ASSERT (Table != NULL);\r
450\r
451 // Validate the Serial Port Info.\r
452 Status = ValidateSerialPortInfo (SerialPortInfo, 1);\r
453 if (EFI_ERROR (Status)) {\r
454 return Status;\r
455 }\r
456\r
457 // Parse the SSDT Serial Port Template.\r
458 Status = AmlParseDefinitionBlock (\r
459 (EFI_ACPI_DESCRIPTION_HEADER*)ssdtserialporttemplate_aml_code,\r
460 &RootNodeHandle\r
461 );\r
462 if (EFI_ERROR (Status)) {\r
463 DEBUG ((\r
464 DEBUG_ERROR,\r
465 "ERROR: SSDT-SERIAL-PORT-FIXUP:"\r
466 " Failed to parse SSDT Serial Port Template. Status = %r\n",\r
467 Status\r
468 ));\r
469 return Status;\r
470 }\r
471\r
472 // Fixup the template values.\r
473 Status = FixupSerialPortInfo (\r
474 RootNodeHandle,\r
475 SerialPortInfo,\r
476 Name,\r
477 Uid,\r
478 Table\r
479 );\r
480 if (EFI_ERROR (Status)) {\r
481 DEBUG ((\r
482 DEBUG_ERROR,\r
483 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."\r
484 " Status = %r\n",\r
485 Status\r
486 ));\r
487 goto exit_handler;\r
488 }\r
489\r
490 // Serialize the tree.\r
491 Status = AmlSerializeDefinitionBlock (\r
492 RootNodeHandle,\r
493 Table\r
494 );\r
495 if (EFI_ERROR (Status)) {\r
496 DEBUG ((\r
497 DEBUG_ERROR,\r
498 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."\r
499 " Status = %r\n",\r
500 Status\r
501 ));\r
502 }\r
503\r
504exit_handler:\r
505 // Cleanup\r
506 if (RootNodeHandle != NULL) {\r
507 Status1 = AmlDeleteTree (RootNodeHandle);\r
508 if (EFI_ERROR (Status1)) {\r
509 DEBUG ((\r
510 DEBUG_ERROR,\r
511 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."\r
512 " Status = %r\n",\r
513 Status1\r
514 ));\r
515 // If Status was success but we failed to delete the AML Tree\r
516 // return Status1 else return the original error code, i.e. Status.\r
517 if (!EFI_ERROR (Status)) {\r
518 return Status1;\r
519 }\r
520 }\r
521 }\r
522\r
523 return Status;\r
524}\r