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