2 SSDT Serial Port Fixup Library.
4 Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".
10 - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.
13 #include <IndustryStandard/DebugPort2Table.h>
14 #include <Library/AcpiLib.h>
15 #include <Library/BaseLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Protocol/AcpiTable.h>
21 // Module specific include files.
22 #include <AcpiTableGenerator.h>
23 #include <ConfigurationManagerObject.h>
24 #include <ConfigurationManagerHelper.h>
25 #include <Library/AcpiHelperLib.h>
26 #include <Library/AmlLib/AmlLib.h>
27 #include <Protocol/ConfigurationManagerProtocol.h>
29 /** C array containing the compiled AML template.
30 This symbol is defined in the auto generated C file
31 containing the AML bytecode array.
33 extern CHAR8 ssdtserialporttemplate_aml_code
[];
35 /** UART address range length.
37 #define MIN_UART_ADDRESS_LENGTH 0x1000U
39 /** Validate the Serial Port Information.
41 @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.
42 @param [in] SerialPortCount Count of SerialPort in the table.
44 @retval EFI_SUCCESS Success.
45 @retval EFI_INVALID_PARAMETER Invalid parameter.
49 ValidateSerialPortInfo (
50 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfoTable
,
51 IN UINT32 SerialPortCount
55 CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
;
57 if ((SerialPortInfoTable
== NULL
) ||
58 (SerialPortCount
== 0)) {
60 return EFI_INVALID_PARAMETER
;
63 for (Index
= 0; Index
< SerialPortCount
; Index
++) {
64 SerialPortInfo
= &SerialPortInfoTable
[Index
];
65 ASSERT (SerialPortInfo
!= NULL
);
67 if ((SerialPortInfo
== NULL
) ||
68 (SerialPortInfo
->BaseAddress
== 0)) {
71 "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",
72 SerialPortInfo
->BaseAddress
74 return EFI_INVALID_PARAMETER
;
77 if ((SerialPortInfo
->PortSubtype
!=
78 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART
) &&
79 (SerialPortInfo
->PortSubtype
!=
80 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X
) &&
81 (SerialPortInfo
->PortSubtype
!=
82 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART
) &&
83 (SerialPortInfo
->PortSubtype
!=
84 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC
) &&
85 (SerialPortInfo
->PortSubtype
!=
86 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550
)) {
89 "ERROR: UART port subtype is invalid."
90 " UART Base = 0x%llx, PortSubtype = 0x%x\n",
91 SerialPortInfo
->BaseAddress
,
92 SerialPortInfo
->PortSubtype
94 return EFI_INVALID_PARAMETER
;
97 DEBUG ((DEBUG_INFO
, "UART Configuration:\n"));
100 " UART Base = 0x%llx\n", SerialPortInfo
->BaseAddress
104 " Length = 0x%llx\n",
105 SerialPortInfo
->BaseAddressLength
107 DEBUG ((DEBUG_INFO
, " Clock = %lu\n", SerialPortInfo
->Clock
));
108 DEBUG ((DEBUG_INFO
, " BaudRate = %llu\n", SerialPortInfo
->BaudRate
));
109 DEBUG ((DEBUG_INFO
, " Interrupt = %lu\n", SerialPortInfo
->Interrupt
));
115 /** Fixup the Serial Port Ids (_UID, _HID, _CID).
117 @param [in] RootNodeHandle Pointer to the root of an AML tree.
118 @param [in] Uid UID for the Serial Port.
119 @param [in] SerialPortInfo Pointer to a Serial Port Information
121 Get the Serial Port Information from there.
123 @retval EFI_SUCCESS The function completed successfully.
124 @retval EFI_INVALID_PARAMETER Invalid parameter.
125 @retval EFI_NOT_FOUND Could not find information.
126 @retval EFI_OUT_OF_RESOURCES Out of resources.
132 IN AML_ROOT_NODE_HANDLE RootNodeHandle
,
134 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
138 AML_OBJECT_NODE_HANDLE NameOpIdNode
;
139 CONST CHAR8
* HidString
;
140 CONST CHAR8
* CidString
;
141 CONST CHAR8
* NonBsaHid
;
143 // Get the _CID and _HID value to write.
144 switch (SerialPortInfo
->PortSubtype
) {
145 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550
:
147 // If there is a non-BSA compliant HID, use that.
148 NonBsaHid
= (CONST CHAR8
*)PcdGetPtr (PcdNonBsaCompliant16550SerialHid
);
149 if ((NonBsaHid
!= NULL
) && (AsciiStrLen (NonBsaHid
) != 0)) {
150 if (!(IsValidPnpId (NonBsaHid
) || IsValidAcpiId (NonBsaHid
))) {
151 return EFI_INVALID_PARAMETER
;
154 HidString
= NonBsaHid
;
157 HidString
= "PNP0501";
158 CidString
= "PNP0500";
162 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART
:
164 HidString
= "ARMH0011";
165 CidString
= "ARMHB000";
168 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART
:
169 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X
:
171 HidString
= "ARMH0011";
177 return EFI_INVALID_PARAMETER
;
181 // Get the _UID NameOp object defined by the "Name ()" statement,
182 // and update its value.
183 Status
= AmlFindNode (
188 if (EFI_ERROR (Status
)) {
192 Status
= AmlNameOpUpdateInteger (NameOpIdNode
, (UINT64
)Uid
);
193 if (EFI_ERROR (Status
)) {
197 // Get the _HID NameOp object defined by the "Name ()" statement,
198 // and update its value.
199 Status
= AmlFindNode (
204 if (EFI_ERROR (Status
)) {
208 Status
= AmlNameOpUpdateString (NameOpIdNode
, HidString
);
209 if (EFI_ERROR (Status
)) {
213 // Get the _CID NameOp object defined by the "Name ()" statement,
214 // and update its value.
215 Status
= AmlFindNode (
220 if (EFI_ERROR (Status
)) {
224 // If we have a CID then update a _CID node else delete the node.
225 if (AsciiStrLen (CidString
) != 0) {
226 Status
= AmlNameOpUpdateString (NameOpIdNode
, CidString
);
228 // First detach the node from the tree.
229 Status
= AmlDetachNode (NameOpIdNode
);
230 if (EFI_ERROR (Status
)) {
234 // Delete the detached node.
235 Status
= AmlDeleteTree (NameOpIdNode
);
241 /** Fixup the Serial Port _CRS values (BaseAddress, ...).
243 @param [in] RootNodeHandle Pointer to the root of an AML tree.
244 @param [in] SerialPortInfo Pointer to a Serial Port Information
246 Get the Serial Port Information from there.
248 @retval EFI_SUCCESS The function completed successfully.
249 @retval EFI_INVALID_PARAMETER Invalid parameter.
250 @retval EFI_NOT_FOUND Could not find information.
251 @retval EFI_OUT_OF_RESOURCES Out of resources.
257 IN AML_ROOT_NODE_HANDLE RootNodeHandle
,
258 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
262 AML_OBJECT_NODE_HANDLE NameOpCrsNode
;
263 AML_DATA_NODE_HANDLE QWordRdNode
;
264 AML_DATA_NODE_HANDLE InterruptRdNode
;
266 // Get the "_CRS" object defined by the "Name ()" statement.
267 Status
= AmlFindNode (
272 if (EFI_ERROR (Status
)) {
276 // Get the first Rd node in the "_CRS" object.
277 Status
= AmlNameOpGetFirstRdNode (NameOpCrsNode
, &QWordRdNode
);
278 if (EFI_ERROR (Status
)) {
282 if (QWordRdNode
== NULL
) {
283 return EFI_INVALID_PARAMETER
;
286 // Update the Serial Port base address and length.
287 Status
= AmlUpdateRdQWord (
289 SerialPortInfo
->BaseAddress
,
290 ((SerialPortInfo
->BaseAddressLength
< MIN_UART_ADDRESS_LENGTH
) ?
291 MIN_UART_ADDRESS_LENGTH
: SerialPortInfo
->BaseAddressLength
)
293 if (EFI_ERROR (Status
)) {
297 // Get the Interrupt node.
298 // It is the second Resource Data element in the NameOpCrsNode's
299 // variable list of arguments.
300 Status
= AmlNameOpGetNextRdNode (QWordRdNode
, &InterruptRdNode
);
301 if (EFI_ERROR (Status
)) {
305 if (InterruptRdNode
== NULL
) {
306 return EFI_INVALID_PARAMETER
;
309 // Update the interrupt number.
310 return AmlUpdateRdInterrupt (InterruptRdNode
, SerialPortInfo
->Interrupt
);
313 /** Fixup the Serial Port device name.
315 @param [in] RootNodeHandle Pointer to the root of an AML tree.
316 @param [in] SerialPortInfo Pointer to a Serial Port Information
318 Get the Serial Port Information from there.
319 @param [in] Name The Name to give to the Device.
320 Must be a NULL-terminated ASL NameString
321 e.g.: "DEV0", "DV15.DEV0", etc.
323 @retval EFI_SUCCESS The function completed successfully.
324 @retval EFI_INVALID_PARAMETER Invalid parameter.
325 @retval EFI_NOT_FOUND Could not find information.
326 @retval EFI_OUT_OF_RESOURCES Out of resources.
332 IN AML_ROOT_NODE_HANDLE RootNodeHandle
,
333 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
334 IN CONST CHAR8
* Name
338 AML_OBJECT_NODE_HANDLE DeviceNode
;
340 // Get the COM0 variable defined by the "Device ()" statement.
341 Status
= AmlFindNode (RootNodeHandle
, "\\_SB_.COM0", &DeviceNode
);
342 if (EFI_ERROR (Status
)) {
346 // Update the Device's name.
347 return AmlDeviceOpUpdateName (DeviceNode
, (CHAR8
*)Name
);
350 /** Fixup the Serial Port Information in the AML tree.
352 For each template value:
353 - find the node to update;
356 @param [in] RootNodeHandle Pointer to the root of the AML tree.
357 @param [in] SerialPortInfo Pointer to a Serial Port Information
359 Get the Serial Port Information from there.
360 @param [in] Name The Name to give to the Device.
361 Must be a NULL-terminated ASL NameString
362 e.g.: "DEV0", "DV15.DEV0", etc.
363 @param [in] Uid UID for the Serial Port.
364 @param [out] Table If success, contains the serialized
367 @retval EFI_SUCCESS The function completed successfully.
368 @retval EFI_INVALID_PARAMETER Invalid parameter.
369 @retval EFI_NOT_FOUND Could not find information.
370 @retval EFI_OUT_OF_RESOURCES Out of resources.
375 FixupSerialPortInfo (
376 IN AML_ROOT_NODE_HANDLE RootNodeHandle
,
377 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
378 IN CONST CHAR8
* Name
,
380 OUT EFI_ACPI_DESCRIPTION_HEADER
** Table
385 ASSERT (RootNodeHandle
!= NULL
);
386 ASSERT (SerialPortInfo
!= NULL
);
387 ASSERT (Name
!= NULL
);
388 ASSERT (Table
!= NULL
);
390 // Fixup the _UID, _HID and _CID values.
391 Status
= FixupIds (RootNodeHandle
, Uid
, SerialPortInfo
);
392 if (EFI_ERROR (Status
)) {
396 // Fixup the _CRS values.
397 Status
= FixupCrs (RootNodeHandle
, SerialPortInfo
);
398 if (EFI_ERROR (Status
)) {
402 // Fixup the serial-port name.
403 // This MUST be done at the end, otherwise AML paths won't be valid anymore.
404 return FixupName (RootNodeHandle
, SerialPortInfo
, Name
);
407 /** Free an SSDT table previously created by
408 the BuildSsdtSerialTable function.
410 @param [in] Table Pointer to a SSDT table allocated by
411 the BuildSsdtSerialTable function.
413 @retval EFI_SUCCESS Success.
417 FreeSsdtSerialPortTable (
418 IN EFI_ACPI_DESCRIPTION_HEADER
* Table
421 ASSERT (Table
!= NULL
);
426 /** Build a SSDT table describing the input serial port.
428 The table created by this function must be freed by FreeSsdtSerialTable.
430 @param [in] AcpiTableInfo Pointer to the ACPI table information.
431 @param [in] SerialPortInfo Serial port to describe in the SSDT table.
432 @param [in] Name The Name to give to the Device.
433 Must be a NULL-terminated ASL NameString
434 e.g.: "DEV0", "DV15.DEV0", etc.
435 @param [in] Uid UID for the Serial Port.
436 @param [out] Table If success, pointer to the created SSDT table.
438 @retval EFI_SUCCESS Table generated successfully.
439 @retval EFI_INVALID_PARAMETER A parameter is invalid.
440 @retval EFI_NOT_FOUND Could not find information.
441 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
445 BuildSsdtSerialPortTable (
446 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO
* AcpiTableInfo
,
447 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
448 IN CONST CHAR8
* Name
,
450 OUT EFI_ACPI_DESCRIPTION_HEADER
** Table
455 AML_ROOT_NODE_HANDLE RootNodeHandle
;
457 ASSERT (AcpiTableInfo
!= NULL
);
458 ASSERT (SerialPortInfo
!= NULL
);
459 ASSERT (Name
!= NULL
);
460 ASSERT (Table
!= NULL
);
462 // Validate the Serial Port Info.
463 Status
= ValidateSerialPortInfo (SerialPortInfo
, 1);
464 if (EFI_ERROR (Status
)) {
468 // Parse the SSDT Serial Port Template.
469 Status
= AmlParseDefinitionBlock (
470 (EFI_ACPI_DESCRIPTION_HEADER
*)ssdtserialporttemplate_aml_code
,
473 if (EFI_ERROR (Status
)) {
476 "ERROR: SSDT-SERIAL-PORT-FIXUP:"
477 " Failed to parse SSDT Serial Port Template. Status = %r\n",
483 // Fixup the template values.
484 Status
= FixupSerialPortInfo (
491 if (EFI_ERROR (Status
)) {
494 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."
501 // Serialize the tree.
502 Status
= AmlSerializeDefinitionBlock (
506 if (EFI_ERROR (Status
)) {
509 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."
517 if (RootNodeHandle
!= NULL
) {
518 Status1
= AmlDeleteTree (RootNodeHandle
);
519 if (EFI_ERROR (Status1
)) {
522 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."
526 // If Status was success but we failed to delete the AML Tree
527 // return Status1 else return the original error code, i.e. Status.
528 if (!EFI_ERROR (Status
)) {