2 SSDT Serial Port Fixup Library.
4 Copyright (c) 2019 - 2020, 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 <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/AcpiTable.h>
22 // Module specific include files.
23 #include <AcpiTableGenerator.h>
24 #include <ConfigurationManagerObject.h>
25 #include <ConfigurationManagerHelper.h>
26 #include <Library/AmlLib/AmlLib.h>
27 #include <Library/TableHelperLib.h>
28 #include <Protocol/ConfigurationManagerProtocol.h>
30 /** C array containing the compiled AML template.
31 This symbol is defined in the auto generated C file
32 containing the AML bytecode array.
34 extern CHAR8 ssdtserialporttemplate_aml_code
[];
36 /** UART address range length.
38 #define MIN_UART_ADDRESS_LENGTH 0x1000U
40 /** Validate the Serial Port Information.
42 @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.
43 @param [in] SerialPortCount Count of SerialPort in the table.
45 @retval EFI_SUCCESS Success.
46 @retval EFI_INVALID_PARAMETER Invalid parameter.
50 ValidateSerialPortInfo (
51 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfoTable
,
52 IN UINT32 SerialPortCount
56 CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
;
58 if ((SerialPortInfoTable
== NULL
) ||
59 (SerialPortCount
== 0)) {
61 return EFI_INVALID_PARAMETER
;
64 for (Index
= 0; Index
< SerialPortCount
; Index
++) {
65 SerialPortInfo
= &SerialPortInfoTable
[Index
];
66 ASSERT (SerialPortInfo
!= NULL
);
68 if ((SerialPortInfo
== NULL
) ||
69 (SerialPortInfo
->BaseAddress
== 0)) {
72 "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",
73 SerialPortInfo
->BaseAddress
75 return EFI_INVALID_PARAMETER
;
78 if ((SerialPortInfo
->PortSubtype
!=
79 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART
) &&
80 (SerialPortInfo
->PortSubtype
!=
81 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X
) &&
82 (SerialPortInfo
->PortSubtype
!=
83 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART
) &&
84 (SerialPortInfo
->PortSubtype
!=
85 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_DCC
) &&
86 (SerialPortInfo
->PortSubtype
!=
87 EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550
)) {
90 "ERROR: UART port subtype is invalid."
91 " UART Base = 0x%llx, PortSubtype = 0x%x\n",
92 SerialPortInfo
->BaseAddress
,
93 SerialPortInfo
->PortSubtype
95 return EFI_INVALID_PARAMETER
;
98 DEBUG ((DEBUG_INFO
, "UART Configuration:\n"));
101 " UART Base = 0x%llx\n", SerialPortInfo
->BaseAddress
105 " Length = 0x%llx\n",
106 SerialPortInfo
->BaseAddressLength
108 DEBUG ((DEBUG_INFO
, " Clock = %lu\n", SerialPortInfo
->Clock
));
109 DEBUG ((DEBUG_INFO
, " BaudRate = %llu\n", SerialPortInfo
->BaudRate
));
110 DEBUG ((DEBUG_INFO
, " Interrupt = %lu\n", SerialPortInfo
->Interrupt
));
116 /** Fixup the Serial Port Ids (_UID, _HID, _CID).
118 @param [in] RootNodeHandle Pointer to the root of an AML tree.
119 @param [in] Uid UID for the Serial Port.
120 @param [in] SerialPortInfo Pointer to a Serial Port Information
122 Get the Serial Port Information from there.
124 @retval EFI_SUCCESS The function completed successfully.
125 @retval EFI_INVALID_PARAMETER Invalid parameter.
126 @retval EFI_NOT_FOUND Could not find information.
127 @retval EFI_OUT_OF_RESOURCES Out of resources.
133 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle
,
135 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
139 AML_OBJECT_NODE_HANDLE NameOpIdNode
;
140 CONST CHAR8
* HidString
;
141 CONST CHAR8
* CidString
;
143 // Get the _CID and _HID value to write.
144 switch (SerialPortInfo
->PortSubtype
) {
145 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550
:
147 HidString
= "PNP0501";
148 CidString
= "PNP0500";
151 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART
:
153 HidString
= "ARMH0011";
154 CidString
= "ARMHB000";
157 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART
:
158 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART_2X
:
160 HidString
= "ARMH0011";
166 return EFI_INVALID_PARAMETER
;
170 // Get the _UID NameOp object defined by the "Name ()" statement,
171 // and update its value.
172 Status
= AmlFindNode (
177 if (EFI_ERROR (Status
)) {
181 Status
= AmlNameOpUpdateInteger (NameOpIdNode
, (UINT64
)Uid
);
182 if (EFI_ERROR (Status
)) {
186 // Get the _HID NameOp object defined by the "Name ()" statement,
187 // and update its value.
188 Status
= AmlFindNode (
193 if (EFI_ERROR (Status
)) {
197 Status
= AmlNameOpUpdateString (NameOpIdNode
, HidString
);
198 if (EFI_ERROR (Status
)) {
202 // Get the _CID NameOp object defined by the "Name ()" statement,
203 // and update its value.
204 Status
= AmlFindNode (
209 if (EFI_ERROR (Status
)) {
213 // If we have a CID then update a _CID node else delete the node.
214 if (AsciiStrLen (CidString
) != 0) {
215 Status
= AmlNameOpUpdateString (NameOpIdNode
, CidString
);
217 // First detach the node from the tree.
218 Status
= AmlDetachNode (NameOpIdNode
);
219 if (EFI_ERROR (Status
)) {
223 // Delete the detached node.
224 Status
= AmlDeleteTree (NameOpIdNode
);
230 /** Fixup the Serial Port _CRS values (BaseAddress, ...).
232 @param [in] RootNodeHandle Pointer to the root of an AML tree.
233 @param [in] SerialPortInfo Pointer to a Serial Port Information
235 Get the Serial Port Information from there.
237 @retval EFI_SUCCESS The function completed successfully.
238 @retval EFI_INVALID_PARAMETER Invalid parameter.
239 @retval EFI_NOT_FOUND Could not find information.
240 @retval EFI_OUT_OF_RESOURCES Out of resources.
246 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle
,
247 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
251 AML_OBJECT_NODE_HANDLE NameOpCrsNode
;
252 AML_DATA_NODE_HANDLE QWordRdNode
;
253 AML_DATA_NODE_HANDLE InterruptRdNode
;
255 // Get the "_CRS" object defined by the "Name ()" statement.
256 Status
= AmlFindNode (
261 if (EFI_ERROR (Status
)) {
265 // Get the first Rd node in the "_CRS" object.
266 Status
= AmlNameOpCrsGetFirstRdNode (NameOpCrsNode
, &QWordRdNode
);
267 if (EFI_ERROR (Status
)) {
271 if (QWordRdNode
== NULL
) {
272 return EFI_INVALID_PARAMETER
;
275 // Update the Serial Port base address and length.
276 Status
= AmlUpdateRdQWord (
278 SerialPortInfo
->BaseAddress
,
279 ((SerialPortInfo
->BaseAddressLength
< MIN_UART_ADDRESS_LENGTH
) ?
280 MIN_UART_ADDRESS_LENGTH
: SerialPortInfo
->BaseAddressLength
)
282 if (EFI_ERROR (Status
)) {
286 // Get the Interrupt node.
287 // It is the second Resource Data element in the NameOpCrsNode's
288 // variable list of arguments.
289 Status
= AmlNameOpCrsGetNextRdNode (QWordRdNode
, &InterruptRdNode
);
290 if (EFI_ERROR (Status
)) {
294 if (InterruptRdNode
== NULL
) {
295 return EFI_INVALID_PARAMETER
;
298 // Update the interrupt number.
299 return AmlUpdateRdInterrupt (InterruptRdNode
, SerialPortInfo
->Interrupt
);
302 /** Fixup the Serial Port device name.
304 @param [in] RootNodeHandle Pointer to the root of an AML tree.
305 @param [in] SerialPortInfo Pointer to a Serial Port Information
307 Get the Serial Port Information from there.
308 @param [in] Name The Name to give to the Device.
309 Must be a NULL-terminated ASL NameString
310 e.g.: "DEV0", "DV15.DEV0", etc.
312 @retval EFI_SUCCESS The function completed successfully.
313 @retval EFI_INVALID_PARAMETER Invalid parameter.
314 @retval EFI_NOT_FOUND Could not find information.
315 @retval EFI_OUT_OF_RESOURCES Out of resources.
321 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle
,
322 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
323 IN CONST CHAR8
* Name
327 AML_OBJECT_NODE_HANDLE DeviceNode
;
329 // Get the COM0 variable defined by the "Device ()" statement.
330 Status
= AmlFindNode (RootNodeHandle
, "\\_SB_.COM0", &DeviceNode
);
331 if (EFI_ERROR (Status
)) {
335 // Update the Device's name.
336 return AmlDeviceOpUpdateName (DeviceNode
, (CHAR8
*)Name
);
339 /** Fixup the Serial Port Information in the AML tree.
341 For each template value:
342 - find the node to update;
345 @param [in] RootNodeHandle Pointer to the root of the AML tree.
346 @param [in] SerialPortInfo Pointer to a Serial Port Information
348 Get the Serial Port Information from there.
349 @param [in] Name The Name to give to the Device.
350 Must be a NULL-terminated ASL NameString
351 e.g.: "DEV0", "DV15.DEV0", etc.
352 @param [in] Uid UID for the Serial Port.
353 @param [out] Table If success, contains the serialized
356 @retval EFI_SUCCESS The function completed successfully.
357 @retval EFI_INVALID_PARAMETER Invalid parameter.
358 @retval EFI_NOT_FOUND Could not find information.
359 @retval EFI_OUT_OF_RESOURCES Out of resources.
364 FixupSerialPortInfo (
365 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle
,
366 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
367 IN CONST CHAR8
* Name
,
369 OUT EFI_ACPI_DESCRIPTION_HEADER
** Table
374 ASSERT (RootNodeHandle
!= NULL
);
375 ASSERT (SerialPortInfo
!= NULL
);
376 ASSERT (Name
!= NULL
);
377 ASSERT (Table
!= NULL
);
379 // Fixup the _UID, _HID and _CID values.
380 Status
= FixupIds (RootNodeHandle
, Uid
, SerialPortInfo
);
381 if (EFI_ERROR (Status
)) {
385 // Fixup the _CRS values.
386 Status
= FixupCrs (RootNodeHandle
, SerialPortInfo
);
387 if (EFI_ERROR (Status
)) {
391 // Fixup the serial-port name.
392 // This MUST be done at the end, otherwise AML paths won't be valid anymore.
393 return FixupName (RootNodeHandle
, SerialPortInfo
, Name
);
396 /** Free an SSDT table previously created by
397 the BuildSsdtSerialTable function.
399 @param [in] Table Pointer to a SSDT table allocated by
400 the BuildSsdtSerialTable function.
402 @retval EFI_SUCCESS Success.
406 FreeSsdtSerialPortTable (
407 IN EFI_ACPI_DESCRIPTION_HEADER
* Table
410 ASSERT (Table
!= NULL
);
415 /** Build a SSDT table describing the input serial port.
417 The table created by this function must be freed by FreeSsdtSerialTable.
419 @param [in] AcpiTableInfo Pointer to the ACPI table information.
420 @param [in] SerialPortInfo Serial port to describe in the SSDT table.
421 @param [in] Name The Name to give to the Device.
422 Must be a NULL-terminated ASL NameString
423 e.g.: "DEV0", "DV15.DEV0", etc.
424 @param [in] Uid UID for the Serial Port.
425 @param [out] Table If success, pointer to the created SSDT table.
427 @retval EFI_SUCCESS Table generated successfully.
428 @retval EFI_INVALID_PARAMETER A parameter is invalid.
429 @retval EFI_NOT_FOUND Could not find information.
430 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
434 BuildSsdtSerialPortTable (
435 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO
* AcpiTableInfo
,
436 IN CONST CM_ARM_SERIAL_PORT_INFO
* SerialPortInfo
,
437 IN CONST CHAR8
* Name
,
439 OUT EFI_ACPI_DESCRIPTION_HEADER
** Table
444 AML_ROOT_NODE_HANDLE RootNodeHandle
;
446 ASSERT (AcpiTableInfo
!= NULL
);
447 ASSERT (SerialPortInfo
!= NULL
);
448 ASSERT (Name
!= NULL
);
449 ASSERT (Table
!= NULL
);
451 // Validate the Serial Port Info.
452 Status
= ValidateSerialPortInfo (SerialPortInfo
, 1);
453 if (EFI_ERROR (Status
)) {
457 // Parse the SSDT Serial Port Template.
458 Status
= AmlParseDefinitionBlock (
459 (EFI_ACPI_DESCRIPTION_HEADER
*)ssdtserialporttemplate_aml_code
,
462 if (EFI_ERROR (Status
)) {
465 "ERROR: SSDT-SERIAL-PORT-FIXUP:"
466 " Failed to parse SSDT Serial Port Template. Status = %r\n",
472 // Fixup the template values.
473 Status
= FixupSerialPortInfo (
480 if (EFI_ERROR (Status
)) {
483 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."
490 // Serialize the tree.
491 Status
= AmlSerializeDefinitionBlock (
495 if (EFI_ERROR (Status
)) {
498 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."
506 if (RootNodeHandle
!= NULL
) {
507 Status1
= AmlDeleteTree (RootNodeHandle
);
508 if (EFI_ERROR (Status1
)) {
511 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."
515 // If Status was success but we failed to delete the AML Tree
516 // return Status1 else return the original error code, i.e. Status.
517 if (!EFI_ERROR (Status
)) {