]> git.proxmox.com Git - mirror_edk2.git/blob - DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.c
DynamicTablesPkg: SSDT Serial Port Fixup library
[mirror_edk2.git] / DynamicTablesPkg / Library / Common / SsdtSerialPortFixupLib / SsdtSerialPortFixupLib.c
1 /** @file
2 SSDT Serial Port Fixup Library.
3
4 Copyright (c) 2019 - 2020, 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 <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/AcpiTable.h>
21
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>
29
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.
33 */
34 extern CHAR8 ssdtserialporttemplate_aml_code[];
35
36 /** UART address range length.
37 */
38 #define MIN_UART_ADDRESS_LENGTH 0x1000U
39
40 /** Validate the Serial Port Information.
41
42 @param [in] SerialPortInfoTable Table of CM_ARM_SERIAL_PORT_INFO.
43 @param [in] SerialPortCount Count of SerialPort in the table.
44
45 @retval EFI_SUCCESS Success.
46 @retval EFI_INVALID_PARAMETER Invalid parameter.
47 **/
48 EFI_STATUS
49 EFIAPI
50 ValidateSerialPortInfo (
51 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfoTable,
52 IN UINT32 SerialPortCount
53 )
54 {
55 UINT32 Index;
56 CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo;
57
58 if ((SerialPortInfoTable == NULL) ||
59 (SerialPortCount == 0)) {
60 ASSERT (0);
61 return EFI_INVALID_PARAMETER;
62 }
63
64 for (Index = 0; Index < SerialPortCount; Index++) {
65 SerialPortInfo = &SerialPortInfoTable[Index];
66 ASSERT (SerialPortInfo != NULL);
67
68 if ((SerialPortInfo == NULL ) ||
69 (SerialPortInfo->BaseAddress == 0)) {
70 DEBUG ((
71 DEBUG_ERROR,
72 "ERROR: UART port base address is invalid. BaseAddress = 0x%llx\n",
73 SerialPortInfo->BaseAddress
74 ));
75 return EFI_INVALID_PARAMETER;
76 }
77
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)) {
88 DEBUG ((
89 DEBUG_ERROR,
90 "ERROR: UART port subtype is invalid."
91 " UART Base = 0x%llx, PortSubtype = 0x%x\n",
92 SerialPortInfo->BaseAddress,
93 SerialPortInfo->PortSubtype
94 ));
95 return EFI_INVALID_PARAMETER;
96 }
97
98 DEBUG ((DEBUG_INFO, "UART Configuration:\n"));
99 DEBUG ((
100 DEBUG_INFO,
101 " UART Base = 0x%llx\n", SerialPortInfo->BaseAddress
102 ));
103 DEBUG ((
104 DEBUG_INFO,
105 " Length = 0x%llx\n",
106 SerialPortInfo->BaseAddressLength
107 ));
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));
111 } // for
112
113 return EFI_SUCCESS;
114 }
115
116 /** Fixup the Serial Port Ids (_UID, _HID, _CID).
117
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
121 structure.
122 Get the Serial Port Information from there.
123
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.
128 **/
129 STATIC
130 EFI_STATUS
131 EFIAPI
132 FixupIds (
133 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,
134 IN CONST UINT64 Uid,
135 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo
136 )
137 {
138 EFI_STATUS Status;
139 AML_OBJECT_NODE_HANDLE NameOpIdNode;
140 CONST CHAR8 * HidString;
141 CONST CHAR8 * CidString;
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 HidString = "PNP0501";
148 CidString = "PNP0500";
149 break;
150 }
151 case EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_PL011_UART:
152 {
153 HidString = "ARMH0011";
154 CidString = "ARMHB000";
155 break;
156 }
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:
159 {
160 HidString = "ARMH0011";
161 CidString = "";
162 break;
163 }
164 default:
165 {
166 return EFI_INVALID_PARAMETER;
167 }
168 } // switch
169
170 // Get the _UID NameOp object defined by the "Name ()" statement,
171 // and update its value.
172 Status = AmlFindNode (
173 RootNodeHandle,
174 "\\_SB_.COM0._UID",
175 &NameOpIdNode
176 );
177 if (EFI_ERROR (Status)) {
178 return Status;
179 }
180
181 Status = AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
182 if (EFI_ERROR (Status)) {
183 return Status;
184 }
185
186 // Get the _HID NameOp object defined by the "Name ()" statement,
187 // and update its value.
188 Status = AmlFindNode (
189 RootNodeHandle,
190 "\\_SB_.COM0._HID",
191 &NameOpIdNode
192 );
193 if (EFI_ERROR (Status)) {
194 return Status;
195 }
196
197 Status = AmlNameOpUpdateString (NameOpIdNode, HidString);
198 if (EFI_ERROR (Status)) {
199 return Status;
200 }
201
202 // Get the _CID NameOp object defined by the "Name ()" statement,
203 // and update its value.
204 Status = AmlFindNode (
205 RootNodeHandle,
206 "\\_SB_.COM0._CID",
207 &NameOpIdNode
208 );
209 if (EFI_ERROR (Status)) {
210 return Status;
211 }
212
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);
216 } else {
217 // First detach the node from the tree.
218 Status = AmlDetachNode (NameOpIdNode);
219 if (EFI_ERROR (Status)) {
220 return Status;
221 }
222
223 // Delete the detached node.
224 Status = AmlDeleteTree (NameOpIdNode);
225 }
226
227 return Status;
228 }
229
230 /** Fixup the Serial Port _CRS values (BaseAddress, ...).
231
232 @param [in] RootNodeHandle Pointer to the root of an AML tree.
233 @param [in] SerialPortInfo Pointer to a Serial Port Information
234 structure.
235 Get the Serial Port Information from there.
236
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.
241 **/
242 STATIC
243 EFI_STATUS
244 EFIAPI
245 FixupCrs (
246 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,
247 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo
248 )
249 {
250 EFI_STATUS Status;
251 AML_OBJECT_NODE_HANDLE NameOpCrsNode;
252 AML_DATA_NODE_HANDLE QWordRdNode;
253 AML_DATA_NODE_HANDLE InterruptRdNode;
254
255 // Get the "_CRS" object defined by the "Name ()" statement.
256 Status = AmlFindNode (
257 RootNodeHandle,
258 "\\_SB_.COM0._CRS",
259 &NameOpCrsNode
260 );
261 if (EFI_ERROR (Status)) {
262 return Status;
263 }
264
265 // Get the first Rd node in the "_CRS" object.
266 Status = AmlNameOpCrsGetFirstRdNode (NameOpCrsNode, &QWordRdNode);
267 if (EFI_ERROR (Status)) {
268 return Status;
269 }
270
271 if (QWordRdNode == NULL) {
272 return EFI_INVALID_PARAMETER;
273 }
274
275 // Update the Serial Port base address and length.
276 Status = AmlUpdateRdQWord (
277 QWordRdNode,
278 SerialPortInfo->BaseAddress,
279 ((SerialPortInfo->BaseAddressLength < MIN_UART_ADDRESS_LENGTH) ?
280 MIN_UART_ADDRESS_LENGTH: SerialPortInfo->BaseAddressLength)
281 );
282 if (EFI_ERROR (Status)) {
283 return Status;
284 }
285
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)) {
291 return Status;
292 }
293
294 if (InterruptRdNode == NULL) {
295 return EFI_INVALID_PARAMETER;
296 }
297
298 // Update the interrupt number.
299 return AmlUpdateRdInterrupt (InterruptRdNode, SerialPortInfo->Interrupt);
300 }
301
302 /** Fixup the Serial Port device name.
303
304 @param [in] RootNodeHandle Pointer to the root of an AML tree.
305 @param [in] SerialPortInfo Pointer to a Serial Port Information
306 structure.
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.
311
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.
316 **/
317 STATIC
318 EFI_STATUS
319 EFIAPI
320 FixupName (
321 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,
322 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,
323 IN CONST CHAR8 * Name
324 )
325 {
326 EFI_STATUS Status;
327 AML_OBJECT_NODE_HANDLE DeviceNode;
328
329 // Get the COM0 variable defined by the "Device ()" statement.
330 Status = AmlFindNode (RootNodeHandle, "\\_SB_.COM0", &DeviceNode);
331 if (EFI_ERROR (Status)) {
332 return Status;
333 }
334
335 // Update the Device's name.
336 return AmlDeviceOpUpdateName (DeviceNode, (CHAR8*)Name);
337 }
338
339 /** Fixup the Serial Port Information in the AML tree.
340
341 For each template value:
342 - find the node to update;
343 - update the value.
344
345 @param [in] RootNodeHandle Pointer to the root of the AML tree.
346 @param [in] SerialPortInfo Pointer to a Serial Port Information
347 structure.
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
354 SSDT table.
355
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.
360 **/
361 STATIC
362 EFI_STATUS
363 EFIAPI
364 FixupSerialPortInfo (
365 IN OUT AML_ROOT_NODE_HANDLE RootNodeHandle,
366 IN CONST CM_ARM_SERIAL_PORT_INFO * SerialPortInfo,
367 IN CONST CHAR8 * Name,
368 IN CONST UINT64 Uid,
369 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table
370 )
371 {
372 EFI_STATUS Status;
373
374 ASSERT (RootNodeHandle != NULL);
375 ASSERT (SerialPortInfo != NULL);
376 ASSERT (Name != NULL);
377 ASSERT (Table != NULL);
378
379 // Fixup the _UID, _HID and _CID values.
380 Status = FixupIds (RootNodeHandle, Uid, SerialPortInfo);
381 if (EFI_ERROR (Status)) {
382 return Status;
383 }
384
385 // Fixup the _CRS values.
386 Status = FixupCrs (RootNodeHandle, SerialPortInfo);
387 if (EFI_ERROR (Status)) {
388 return Status;
389 }
390
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);
394 }
395
396 /** Free an SSDT table previously created by
397 the BuildSsdtSerialTable function.
398
399 @param [in] Table Pointer to a SSDT table allocated by
400 the BuildSsdtSerialTable function.
401
402 @retval EFI_SUCCESS Success.
403 **/
404 EFI_STATUS
405 EFIAPI
406 FreeSsdtSerialPortTable (
407 IN EFI_ACPI_DESCRIPTION_HEADER * Table
408 )
409 {
410 ASSERT (Table != NULL);
411 FreePool (Table);
412 return EFI_SUCCESS;
413 }
414
415 /** Build a SSDT table describing the input serial port.
416
417 The table created by this function must be freed by FreeSsdtSerialTable.
418
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.
426
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.
431 **/
432 EFI_STATUS
433 EFIAPI
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,
438 IN CONST UINT64 Uid,
439 OUT EFI_ACPI_DESCRIPTION_HEADER ** Table
440 )
441 {
442 EFI_STATUS Status;
443 EFI_STATUS Status1;
444 AML_ROOT_NODE_HANDLE RootNodeHandle;
445
446 ASSERT (AcpiTableInfo != NULL);
447 ASSERT (SerialPortInfo != NULL);
448 ASSERT (Name != NULL);
449 ASSERT (Table != NULL);
450
451 // Validate the Serial Port Info.
452 Status = ValidateSerialPortInfo (SerialPortInfo, 1);
453 if (EFI_ERROR (Status)) {
454 return Status;
455 }
456
457 // Parse the SSDT Serial Port Template.
458 Status = AmlParseDefinitionBlock (
459 (EFI_ACPI_DESCRIPTION_HEADER*)ssdtserialporttemplate_aml_code,
460 &RootNodeHandle
461 );
462 if (EFI_ERROR (Status)) {
463 DEBUG ((
464 DEBUG_ERROR,
465 "ERROR: SSDT-SERIAL-PORT-FIXUP:"
466 " Failed to parse SSDT Serial Port Template. Status = %r\n",
467 Status
468 ));
469 return Status;
470 }
471
472 // Fixup the template values.
473 Status = FixupSerialPortInfo (
474 RootNodeHandle,
475 SerialPortInfo,
476 Name,
477 Uid,
478 Table
479 );
480 if (EFI_ERROR (Status)) {
481 DEBUG ((
482 DEBUG_ERROR,
483 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to fixup SSDT Serial Port Table."
484 " Status = %r\n",
485 Status
486 ));
487 goto exit_handler;
488 }
489
490 // Serialize the tree.
491 Status = AmlSerializeDefinitionBlock (
492 RootNodeHandle,
493 Table
494 );
495 if (EFI_ERROR (Status)) {
496 DEBUG ((
497 DEBUG_ERROR,
498 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to Serialize SSDT Table Data."
499 " Status = %r\n",
500 Status
501 ));
502 }
503
504 exit_handler:
505 // Cleanup
506 if (RootNodeHandle != NULL) {
507 Status1 = AmlDeleteTree (RootNodeHandle);
508 if (EFI_ERROR (Status1)) {
509 DEBUG ((
510 DEBUG_ERROR,
511 "ERROR: SSDT-SERIAL-PORT-FIXUP: Failed to cleanup AML tree."
512 " Status = %r\n",
513 Status1
514 ));
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)) {
518 return Status1;
519 }
520 }
521 }
522
523 return Status;
524 }