]> git.proxmox.com Git - mirror_edk2.git/blob - DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
79e1fabc9223dab6c545d59694960f9d677f4d3a
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / SsdtSerialPortFixupLib / SsdtSerialPortFixupLib.c
1 /** @file
2 SSDT Serial Port Fixup Library.
3
4 Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 @par Reference(s):
9 - Arm Server Base Boot Requirements (SBBR), s4.2.1.8 "SPCR".
10 - Microsoft Debug Port Table 2 (DBG2) Specification - December 10, 2015.
11 **/
12
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>
20
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>
28
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.
32 */
33 extern CHAR8 ssdtserialporttemplate_aml_code[];
34
35 /** UART address range length.
36 */
37 #define MIN_UART_ADDRESS_LENGTH 0x1000U
38
39 /** Validate the Serial Port Information.
40
41 @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.
42 @param [in] SerialPortCount Count of SerialPort in the table.
43
44 @retval EFI_SUCCESS Success.
45 @retval EFI_INVALID_PARAMETER Invalid parameter.
46 **/
47 EFI_STATUS
48 EFIAPI
49 ValidateSerialPortInfo (
50 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfoTable,
51 IN UINT32 SerialPortCount
52 )
53 {
54 UINT32 Index;
55 CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo;
56
57 if ((SerialPortInfoTable == NULL) ||
58 (SerialPortCount == 0)) {
59 ASSERT (0);
60 return EFI_INVALID_PARAMETER;
61 }
62
63 for (Index = 0; Index < SerialPortCount; Index++) {
64 SerialPortInfo = &SerialPortInfoTable[Index];
65 ASSERT (SerialPortInfo != NULL);
66
67 if ((SerialPortInfo == NULL ) ||
68 (SerialPortInfo->BaseAddress == 0)) {
69 DEBUG ((
70 DEBUG_ERROR,
71 "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",
72 SerialPortInfo->BaseAddress
73 ));
74 return EFI_INVALID_PARAMETER;
75 }
76
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)) {
87 DEBUG ((
88 DEBUG_ERROR,
89 "ERROR: UART port subtype is invalid."
90 " UART Base = 0x%llx, PortSubtype = 0x%x\n",
91 SerialPortInfo->BaseAddress,
92 SerialPortInfo->PortSubtype
93 ));
94 return EFI_INVALID_PARAMETER;
95 }
96
97 DEBUG ((DEBUG_INFO, "UART Configuration:\n"));
98 DEBUG ((
99 DEBUG_INFO,
100 " UART Base = 0x%llx\n", SerialPortInfo->BaseAddress
101 ));
102 DEBUG ((
103 DEBUG_INFO,
104 " Length = 0x%llx\n",
105 SerialPortInfo->BaseAddressLength
106 ));
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));
110 } // for
111
112 return EFI_SUCCESS;
113 }
114
115 /** Fixup the Serial Port Ids (_UID, _HID, _CID).
116
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
120 structure.
121 Get the Serial Port Information from there.
122
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.
127 **/
128 STATIC
129 EFI_STATUS
130 EFIAPI
131 FixupIds (
132 IN AML_ROOT_NODE_HANDLE RootNodeHandle,
133 IN CONST UINT64 Uid,
134 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo
135 )
136 {
137 EFI_STATUS Status;
138 AML_OBJECT_NODE_HANDLE NameOpIdNode;
139 CONST CHAR8 * HidString;
140 CONST CHAR8 * CidString;
141 CONST CHAR8 * NonBsaHid;
142
143 // Get the _CID and _HID value to write.
144 switch (SerialPortInfo->PortSubtype) {
145 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550:
146 {
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;
152 }
153
154 HidString = NonBsaHid;
155 CidString = "";
156 } else {
157 HidString = "PNP0501";
158 CidString = "PNP0500";
159 }
160 break;
161 }
162 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART:
163 {
164 HidString = "ARMH0011";
165 CidString = "ARMHB000";
166 break;
167 }
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:
170 {
171 HidString = "ARMH0011";
172 CidString = "";
173 break;
174 }
175 default:
176 {
177 return EFI_INVALID_PARAMETER;
178 }
179 } // switch
180
181 // Get the _UID NameOp object defined by the "Name ()" statement,
182 // and update its value.
183 Status = AmlFindNode (
184 RootNodeHandle,
185 "\\_SB_.COM0._UID",
186 &NameOpIdNode
187 );
188 if (EFI_ERROR (Status)) {
189 return Status;
190 }
191
192 Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
193 if (EFI_ERROR (Status)) {
194 return Status;
195 }
196
197 // Get the _HID NameOp object defined by the "Name ()" statement,
198 // and update its value.
199 Status = AmlFindNode (
200 RootNodeHandle,
201 "\\_SB_.COM0._HID",
202 &NameOpIdNode
203 );
204 if (EFI_ERROR (Status)) {
205 return Status;
206 }
207
208 Status = AmlNameOpUpdateString (NameOpIdNode, HidString);
209 if (EFI_ERROR (Status)) {
210 return Status;
211 }
212
213 // Get the _CID NameOp object defined by the "Name ()" statement,
214 // and update its value.
215 Status = AmlFindNode (
216 RootNodeHandle,
217 "\\_SB_.COM0._CID",
218 &NameOpIdNode
219 );
220 if (EFI_ERROR (Status)) {
221 return Status;
222 }
223
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);
227 } else {
228 // First detach the node from the tree.
229 Status = AmlDetachNode (NameOpIdNode);
230 if (EFI_ERROR (Status)) {
231 return Status;
232 }
233
234 // Delete the detached node.
235 Status = AmlDeleteTree (NameOpIdNode);
236 }
237
238 return Status;
239 }
240
241 /** Fixup the Serial Port _CRS values (BaseAddress, ...).
242
243 @param [in] RootNodeHandle Pointer to the root of an AML tree.
244 @param [in] SerialPortInfo Pointer to a Serial Port Information
245 structure.
246 Get the Serial Port Information from there.
247
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.
252 **/
253 STATIC
254 EFI_STATUS
255 EFIAPI
256 FixupCrs (
257 IN AML_ROOT_NODE_HANDLE RootNodeHandle,
258 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo
259 )
260 {
261 EFI_STATUS Status;
262 AML_OBJECT_NODE_HANDLE NameOpCrsNode;
263 AML_DATA_NODE_HANDLE QWordRdNode;
264 AML_DATA_NODE_HANDLE InterruptRdNode;
265
266 // Get the "_CRS" object defined by the "Name ()" statement.
267 Status = AmlFindNode (
268 RootNodeHandle,
269 "\\_SB_.COM0._CRS",
270 &NameOpCrsNode
271 );
272 if (EFI_ERROR (Status)) {
273 return Status;
274 }
275
276 // Get the first Rd node in the "_CRS" object.
277 Status = AmlNameOpGetFirstRdNode (NameOpCrsNode, &QWordRdNode);
278 if (EFI_ERROR (Status)) {
279 return Status;
280 }
281
282 if (QWordRdNode == NULL) {
283 return EFI_INVALID_PARAMETER;
284 }
285
286 // Update the Serial Port base address and length.
287 Status = AmlUpdateRdQWord (
288 QWordRdNode,
289 SerialPortInfo->BaseAddress,
290 ((SerialPortInfo->BaseAddressLength < MIN_UART_ADDRESS_LENGTH) ?
291 MIN_UART_ADDRESS_LENGTH: SerialPortInfo->BaseAddressLength)
292 );
293 if (EFI_ERROR (Status)) {
294 return Status;
295 }
296
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)) {
302 return Status;
303 }
304
305 if (InterruptRdNode == NULL) {
306 return EFI_INVALID_PARAMETER;
307 }
308
309 // Update the interrupt number.
310 return AmlUpdateRdInterrupt (InterruptRdNode, SerialPortInfo->Interrupt);
311 }
312
313 /** Fixup the Serial Port device name.
314
315 @param [in] RootNodeHandle Pointer to the root of an AML tree.
316 @param [in] SerialPortInfo Pointer to a Serial Port Information
317 structure.
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.
322
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.
327 **/
328 STATIC
329 EFI_STATUS
330 EFIAPI
331 FixupName (
332 IN AML_ROOT_NODE_HANDLE RootNodeHandle,
333 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,
334 IN CONST CHAR8 * Name
335 )
336 {
337 EFI_STATUS Status;
338 AML_OBJECT_NODE_HANDLE DeviceNode;
339
340 // Get the COM0 variable defined by the "Device ()" statement.
341 Status = AmlFindNode (RootNodeHandle, "\\_SB_.COM0", &DeviceNode);
342 if (EFI_ERROR (Status)) {
343 return Status;
344 }
345
346 // Update the Device's name.
347 return AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);
348 }
349
350 /** Fixup the Serial Port Information in the AML tree.
351
352 For each template value:
353 - find the node to update;
354 - update the value.
355
356 @param [in] RootNodeHandle Pointer to the root of the AML tree.
357 @param [in] SerialPortInfo Pointer to a Serial Port Information
358 structure.
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
365 SSDT table.
366
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.
371 **/
372 STATIC
373 EFI_STATUS
374 EFIAPI
375 FixupSerialPortInfo (
376 IN AML_ROOT_NODE_HANDLE RootNodeHandle,
377 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,
378 IN CONST CHAR8 * Name,
379 IN CONST UINT64 Uid,
380 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table
381 )
382 {
383 EFI_STATUS Status;
384
385 ASSERT (RootNodeHandle != NULL);
386 ASSERT (SerialPortInfo != NULL);
387 ASSERT (Name != NULL);
388 ASSERT (Table != NULL);
389
390 // Fixup the _UID, _HID and _CID values.
391 Status = FixupIds (RootNodeHandle, Uid, SerialPortInfo);
392 if (EFI_ERROR (Status)) {
393 return Status;
394 }
395
396 // Fixup the _CRS values.
397 Status = FixupCrs (RootNodeHandle, SerialPortInfo);
398 if (EFI_ERROR (Status)) {
399 return Status;
400 }
401
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);
405 }
406
407 /** Free an SSDT table previously created by
408 the BuildSsdtSerialTable function.
409
410 @param [in] Table Pointer to a SSDT table allocated by
411 the BuildSsdtSerialTable function.
412
413 @retval EFI_SUCCESS Success.
414 **/
415 EFI_STATUS
416 EFIAPI
417 FreeSsdtSerialPortTable (
418 IN EFI_ACPI_DESCRIPTION_HEADER * Table
419 )
420 {
421 ASSERT (Table != NULL);
422 FreePool (Table);
423 return EFI_SUCCESS;
424 }
425
426 /** Build a SSDT table describing the input serial port.
427
428 The table created by this function must be freed by FreeSsdtSerialTable.
429
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.
437
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.
442 **/
443 EFI_STATUS
444 EFIAPI
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,
449 IN CONST UINT64 Uid,
450 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table
451 )
452 {
453 EFI_STATUS Status;
454 EFI_STATUS Status1;
455 AML_ROOT_NODE_HANDLE RootNodeHandle;
456
457 ASSERT (AcpiTableInfo != NULL);
458 ASSERT (SerialPortInfo != NULL);
459 ASSERT (Name != NULL);
460 ASSERT (Table != NULL);
461
462 // Validate the Serial Port Info.
463 Status = ValidateSerialPortInfo (SerialPortInfo, 1);
464 if (EFI_ERROR (Status)) {
465 return Status;
466 }
467
468 // Parse the SSDT Serial Port Template.
469 Status = AmlParseDefinitionBlock (
470 (EFI_ACPI_DESCRIPTION_HEADER*)ssdtserialporttemplate_aml_code,
471 &RootNodeHandle
472 );
473 if (EFI_ERROR (Status)) {
474 DEBUG ((
475 DEBUG_ERROR,
476 "ERROR: SSDT-SERIAL-PORT-FIXUP:"
477 " Failed to parse SSDT Serial Port Template. Status = %r\n",
478 Status
479 ));
480 return Status;
481 }
482
483 // Fixup the template values.
484 Status = FixupSerialPortInfo (
485 RootNodeHandle,
486 SerialPortInfo,
487 Name,
488 Uid,
489 Table
490 );
491 if (EFI_ERROR (Status)) {
492 DEBUG ((
493 DEBUG_ERROR,
494 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."
495 " Status = %r\n",
496 Status
497 ));
498 goto exit_handler;
499 }
500
501 // Serialize the tree.
502 Status = AmlSerializeDefinitionBlock (
503 RootNodeHandle,
504 Table
505 );
506 if (EFI_ERROR (Status)) {
507 DEBUG ((
508 DEBUG_ERROR,
509 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."
510 " Status = %r\n",
511 Status
512 ));
513 }
514
515 exit_handler:
516 // Cleanup
517 if (RootNodeHandle != NULL) {
518 Status1 = AmlDeleteTree (RootNodeHandle);
519 if (EFI_ERROR (Status1)) {
520 DEBUG ((
521 DEBUG_ERROR,
522 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."
523 " Status = %r\n",
524 Status1
525 ));
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)) {
529 return Status1;
530 }
531 }
532 }
533
534 return Status;
535 }