2 SSDT CMN-600 AML Table Generator.
4 Copyright (c) 2020 - 2021, Arm Limited. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 - Arm CoreLink CMN-600 Coherent Mesh Network Technical Reference Manual r3p0
10 - Generic ACPI for Arm Components 1.0 Platform Design Document
13 #include <Library/AcpiLib.h>
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Protocol/AcpiTable.h>
20 // Module specific include files.
21 #include <AcpiTableGenerator.h>
22 #include <ConfigurationManagerObject.h>
23 #include <ConfigurationManagerHelper.h>
24 #include <Library/AcpiHelperLib.h>
25 #include <Library/AmlLib/AmlLib.h>
26 #include <Protocol/ConfigurationManagerProtocol.h>
27 #include "SsdtCmn600Generator.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 ssdtcmn600template_aml_code
[];
35 /** SSDT CMN-600 Table Generator.
38 The following Configuration Manager Object(s) are required by
43 /** This macro expands to a function that retrieves the CMN-600
44 Information from the Configuration Manager.
52 /** Check the CMN-600 Information.
54 @param [in] Cmn600InfoList Array of CMN-600 information structure.
55 @param [in] Cmn600Count Count of CMN-600 information structure.
57 @retval EFI_SUCCESS The function completed successfully.
58 @retval EFI_INVALID_PARAMETER Invalid parameter.
64 IN CONST CM_ARM_CMN_600_INFO
*Cmn600InfoList
,
65 IN CONST UINT32 Cmn600Count
70 CONST CM_ARM_CMN_600_INFO
*Cmn600Info
;
71 CONST CM_ARM_GENERIC_INTERRUPT
*DtcInterrupt
;
73 if ((Cmn600InfoList
== NULL
) ||
76 return EFI_INVALID_PARAMETER
;
79 // Validate each Cmn600Info structure.
80 for (Index
= 0; Index
< Cmn600Count
; Index
++) {
81 Cmn600Info
= &Cmn600InfoList
[Index
];
83 // At least one DTC is required.
84 if ((Cmn600Info
->DtcCount
== 0) ||
85 (Cmn600Info
->DtcCount
> MAX_DTC_COUNT
))
89 "ERROR: SSDT-CMN-600: Invalid DTC configuration:\n"
94 // Check PERIPHBASE and ROOTNODEBASE address spaces are initialized.
95 if ((Cmn600Info
->PeriphBaseAddress
== 0) ||
96 (Cmn600Info
->RootNodeBaseAddress
== 0))
100 "ERROR: SSDT-CMN-600: Invalid PERIPHBASE or ROOTNODEBASE.\n"
105 // The PERIPHBASE address must be 64MB aligned for a (X < 4) && (Y < 4)
106 // dimension mesh, and 256MB aligned otherwise.
107 // Check it is a least 64MB aligned.
108 if ((Cmn600Info
->PeriphBaseAddress
&
109 (PERIPHBASE_MIN_ADDRESS_LENGTH
- 1)) != 0)
113 "ERROR: SSDT-CMN-600: PERIPHBASE address must be 64MB aligned.\n"
118 // The PERIPHBASE address is at most 64MB for a (X < 4) && (Y < 4)
119 // dimension mesh, and 256MB otherwise. Check it is not more than 256MB.
120 if (Cmn600Info
->PeriphBaseAddressLength
> PERIPHBASE_MAX_ADDRESS_LENGTH
) {
123 "ERROR: SSDT-CMN-600: PERIPHBASE address range must be < 256MB.\n"
128 // Check the 16 KB alignment of the ROOTNODEBASE address.
129 if ((Cmn600Info
->PeriphBaseAddress
&
130 (ROOTNODEBASE_ADDRESS_LENGTH
- 1)) != 0)
134 "ERROR: SSDT-CMN-600: Root base address must be 16KB aligned.\n"
139 // The ROOTNODEBASE address space should be included in the PERIPHBASE
141 if ((Cmn600Info
->PeriphBaseAddress
> Cmn600Info
->RootNodeBaseAddress
) ||
142 ((Cmn600Info
->PeriphBaseAddress
+ Cmn600Info
->PeriphBaseAddressLength
) <
143 (Cmn600Info
->RootNodeBaseAddress
+ ROOTNODEBASE_ADDRESS_LENGTH
)))
147 "ERROR: SSDT-CMN-600:"
148 " ROOTNODEBASE address space not in PERIPHBASE address space.\n"
153 for (DtcIndex
= 0; DtcIndex
< Cmn600Info
->DtcCount
; DtcIndex
++) {
154 DtcInterrupt
= &Cmn600Info
->DtcInterrupt
[DtcIndex
];
155 if (((DtcInterrupt
->Flags
&
156 EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK
) == 0))
160 "ERROR: SSDT-CMN-600: DTC Interrupt must be consumer.\n"
164 } // for DTC Interrupt
165 } // for Cmn600InfoList
173 "PeriphBaseAddress = 0x%llx\n"
174 "PeriphBaseAddressLength = 0x%llx\n"
175 "RootNodeBaseAddress = 0x%llx\n"
177 Cmn600Info
->PeriphBaseAddress
,
178 Cmn600Info
->PeriphBaseAddressLength
,
179 Cmn600Info
->RootNodeBaseAddress
,
184 for (DtcIndex
= 0; DtcIndex
< Cmn600Info
->DtcCount
; DtcIndex
++) {
185 DtcInterrupt
= &Cmn600Info
->DtcInterrupt
[DtcIndex
];
193 " Interrupt = 0x%lx\n",
194 DtcInterrupt
->Interrupt
205 return EFI_INVALID_PARAMETER
;
208 /** Build a SSDT table describing the CMN-600 device.
210 The table created by this function must be freed by FreeSsdtCmn600Table.
212 @param [in] Cmn600Info Pointer to a Cmn600 structure.
213 @param [in] Name The Name to give to the Device.
214 Must be a NULL-terminated ASL NameString
215 e.g.: "DEV0", "DV15.DEV0", etc.
216 @param [in] Uid UID for the CMN600 device.
217 @param [out] Table If success, pointer to the created SSDT table.
219 @retval EFI_SUCCESS Table generated successfully.
220 @retval EFI_INVALID_PARAMETER A parameter is invalid.
221 @retval EFI_NOT_FOUND Could not find information.
222 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
228 IN CONST CM_ARM_CMN_600_INFO
*Cmn600Info
,
229 IN CONST CHAR8
*Name
,
231 OUT EFI_ACPI_DESCRIPTION_HEADER
**Table
237 CONST CM_ARM_GENERIC_INTERRUPT
*DtcInt
;
239 EFI_ACPI_DESCRIPTION_HEADER
*SsdtCmn600Template
;
240 AML_ROOT_NODE_HANDLE RootNodeHandle
;
241 AML_OBJECT_NODE_HANDLE NameOpIdNode
;
242 AML_OBJECT_NODE_HANDLE NameOpCrsNode
;
243 AML_DATA_NODE_HANDLE CmnPeriphBaseRdNode
;
244 AML_DATA_NODE_HANDLE CmnRootNodeBaseRdNode
;
245 AML_OBJECT_NODE_HANDLE DeviceNode
;
247 // Parse the Ssdt CMN-600 Template.
248 SsdtCmn600Template
= (EFI_ACPI_DESCRIPTION_HEADER
*)
249 ssdtcmn600template_aml_code
;
251 RootNodeHandle
= NULL
;
252 Status
= AmlParseDefinitionBlock (
256 if (EFI_ERROR (Status
)) {
259 "ERROR: SSDT-CMN-600: Failed to parse SSDT CMN-600 Template."
266 // Get the _UID NameOp object defined by the "Name ()" statement,
267 // and update its value.
268 Status
= AmlFindNode (
273 if (EFI_ERROR (Status
)) {
277 Status
= AmlNameOpUpdateInteger (NameOpIdNode
, (UINT64
)Uid
);
278 if (EFI_ERROR (Status
)) {
282 // Get the _CRS object defined by the "Name ()" statement.
283 Status
= AmlFindNode (
288 if (EFI_ERROR (Status
)) {
292 // Get the first Rd node in the "_CRS" object.
293 // This is the PERIPHBASE node.
294 Status
= AmlNameOpGetFirstRdNode (NameOpCrsNode
, &CmnPeriphBaseRdNode
);
295 if (EFI_ERROR (Status
)) {
299 if (CmnPeriphBaseRdNode
== NULL
) {
300 Status
= EFI_INVALID_PARAMETER
;
304 // Update the PERIPHBASE base address and length.
305 Status
= AmlUpdateRdQWord (
307 Cmn600Info
->PeriphBaseAddress
,
308 Cmn600Info
->PeriphBaseAddressLength
310 if (EFI_ERROR (Status
)) {
314 // Get the QWord node corresponding to the ROOTNODEBASE.
315 // It is the second Resource Data element in the BufferNode's
316 // variable list of arguments.
317 Status
= AmlNameOpGetNextRdNode (
319 &CmnRootNodeBaseRdNode
321 if (EFI_ERROR (Status
)) {
325 if (CmnRootNodeBaseRdNode
== NULL
) {
326 Status
= EFI_INVALID_PARAMETER
;
330 // Update the ROOTNODEBASE base address and length.
331 Status
= AmlUpdateRdQWord (
332 CmnRootNodeBaseRdNode
,
333 Cmn600Info
->RootNodeBaseAddress
,
334 ROOTNODEBASE_ADDRESS_LENGTH
336 if (EFI_ERROR (Status
)) {
340 // Add the Interrupt node(s).
341 // Generate Resource Data node(s) corresponding to the "Interrupt ()"
342 // ASL function and add it at the last position in the list of
343 // Resource Data nodes.
344 for (Index
= 0; Index
< Cmn600Info
->DtcCount
; Index
++) {
345 DtcInt
= &Cmn600Info
->DtcInterrupt
[Index
];
347 Status
= AmlCodeGenRdInterrupt (
349 EFI_ACPI_EXTENDED_INTERRUPT_FLAG_PRODUCER_CONSUMER_MASK
) != 0),
351 EFI_ACPI_EXTENDED_INTERRUPT_FLAG_MODE_MASK
) != 0),
353 EFI_ACPI_EXTENDED_INTERRUPT_FLAG_POLARITY_MASK
) != 0),
355 EFI_ACPI_EXTENDED_INTERRUPT_FLAG_SHARABLE_MASK
) != 0),
356 (UINT32
*)&DtcInt
->Interrupt
,
361 if (EFI_ERROR (Status
)) {
366 // Fixup the CMN600 device name.
367 // This MUST be done at the end, otherwise AML paths won't be valid anymore.
368 // Get the CMN0 variable defined by the "Device ()" statement.
369 Status
= AmlFindNode (RootNodeHandle
, "\\_SB_.CMN0", &DeviceNode
);
370 if (EFI_ERROR (Status
)) {
374 // Update the CMN600 Device's name.
375 Status
= AmlDeviceOpUpdateName (DeviceNode
, (CHAR8
*)Name
);
376 if (EFI_ERROR (Status
)) {
380 // Serialise the definition block
381 Status
= AmlSerializeDefinitionBlock (
385 if (EFI_ERROR (Status
)) {
388 "ERROR: SSDT-CMN-600: Failed to Serialize SSDT Table Data."
396 if (RootNodeHandle
!= NULL
) {
397 Status1
= AmlDeleteTree (RootNodeHandle
);
398 if (EFI_ERROR (Status1
)) {
401 "ERROR: SSDT-CMN-600: Failed to cleanup AML tree."
405 // If Status was success but we failed to delete the AML Tree
406 // return Status1 else return the original error code, i.e. Status.
407 if (!EFI_ERROR (Status
)) {
416 /** Free any resources allocated for constructing the SSDT tables for CMN-600.
418 @param [in] This Pointer to the ACPI table generator.
419 @param [in] AcpiTableInfo Pointer to the ACPI Table Info.
420 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
422 @param [in, out] Table Pointer to an array of pointers
424 @param [in] TableCount Number of ACPI table(s).
426 @retval EFI_SUCCESS The resources were freed successfully.
427 @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
432 FreeSsdtCmn600TableResourcesEx (
433 IN CONST ACPI_TABLE_GENERATOR
*CONST This
,
434 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO
*CONST AcpiTableInfo
,
435 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL
*CONST CfgMgrProtocol
,
436 IN OUT EFI_ACPI_DESCRIPTION_HEADER
***CONST Table
,
437 IN CONST UINTN TableCount
440 EFI_ACPI_DESCRIPTION_HEADER
**TableList
;
443 ASSERT (This
!= NULL
);
444 ASSERT (AcpiTableInfo
!= NULL
);
445 ASSERT (CfgMgrProtocol
!= NULL
);
446 ASSERT (AcpiTableInfo
->TableGeneratorId
== This
->GeneratorID
);
447 ASSERT (AcpiTableInfo
->AcpiTableSignature
== This
->AcpiTableSignature
);
449 if ((Table
== NULL
) ||
453 DEBUG ((DEBUG_ERROR
, "ERROR: SSDT-CMN-600: Invalid Table Pointer\n"));
454 return EFI_INVALID_PARAMETER
;
459 for (Index
= 0; Index
< TableCount
; Index
++) {
460 if ((TableList
[Index
] != NULL
) &&
461 (TableList
[Index
]->Signature
==
462 EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
))
464 FreePool (TableList
[Index
]);
468 "ERROR: SSDT-CMN-600: Could not free SSDT table at index %d."
471 EFI_INVALID_PARAMETER
473 return EFI_INVALID_PARAMETER
;
477 // Free the table list.
483 /** Construct SSDT tables for describing CMN-600 meshes.
485 This function invokes the Configuration Manager protocol interface
486 to get the required hardware information for generating the ACPI
489 If this function allocates any resources then they must be freed
490 in the FreeXXXXTableResourcesEx function.
492 @param [in] This Pointer to the ACPI table generator.
493 @param [in] AcpiTableInfo Pointer to the ACPI table information.
494 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
496 @param [out] Table Pointer to a list of generated ACPI table(s).
497 @param [out] TableCount Number of generated ACPI table(s).
499 @retval EFI_SUCCESS Table generated successfully.
500 @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
501 Manager is less than the Object size for
502 the requested object.
503 @retval EFI_INVALID_PARAMETER A parameter is invalid.
504 @retval EFI_NOT_FOUND Could not find information.
505 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
506 @retval EFI_UNSUPPORTED Unsupported configuration.
511 BuildSsdtCmn600TableEx (
512 IN CONST ACPI_TABLE_GENERATOR
*This
,
513 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO
*CONST AcpiTableInfo
,
514 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL
*CONST CfgMgrProtocol
,
515 OUT EFI_ACPI_DESCRIPTION_HEADER
***Table
,
516 OUT UINTN
*CONST TableCount
521 CM_ARM_CMN_600_INFO
*Cmn600Info
;
523 CHAR8 NewName
[AML_NAME_SEG_SIZE
+ 1];
524 EFI_ACPI_DESCRIPTION_HEADER
**TableList
;
526 ASSERT (This
!= NULL
);
527 ASSERT (AcpiTableInfo
!= NULL
);
528 ASSERT (CfgMgrProtocol
!= NULL
);
529 ASSERT (Table
!= NULL
);
530 ASSERT (TableCount
!= NULL
);
531 ASSERT (AcpiTableInfo
->TableGeneratorId
== This
->GeneratorID
);
532 ASSERT (AcpiTableInfo
->AcpiTableSignature
== This
->AcpiTableSignature
);
536 // Get CMN-600 information.
537 Status
= GetEArmObjCmn600Info (
543 if (EFI_ERROR (Status
)) {
546 "ERROR: SSDT-CMN-600: Failed to get the CMN-600 information."
553 if ((Cmn600Count
== 0) || (Cmn600Count
> MAX_CMN600_DEVICES_SUPPORTED
)) {
556 "ERROR: SSDT-CMN-600: CMN600 peripheral count = %d."
557 " This must be between 1 to 16.\n",
560 return EFI_INVALID_PARAMETER
;
563 // Validate the CMN-600 Info.
564 Status
= ValidateCmn600Info (Cmn600Info
, Cmn600Count
);
565 if (EFI_ERROR (Status
)) {
568 "ERROR: SSDT-CMN-600: Invalid CMN600 information. Status = %r\n",
574 // Allocate a table to store pointers to the SSDT tables.
575 TableList
= (EFI_ACPI_DESCRIPTION_HEADER
**)
577 (sizeof (EFI_ACPI_DESCRIPTION_HEADER
*) * Cmn600Count
)
579 if (TableList
== NULL
) {
580 Status
= EFI_OUT_OF_RESOURCES
;
583 "ERROR: SSDT-CMN-600: Failed to allocate memory for Table List."
590 // Setup the table list early so that that appropriate cleanup
591 // can be done in case of failure.
598 for (Index
= 0; Index
< Cmn600Count
; Index
++) {
599 NewName
[3] = AsciiFromHex ((UINT8
)(Index
));
601 // Build a SSDT table describing the CMN600 device.
602 Status
= FixupCmn600Info (
608 if (EFI_ERROR (Status
)) {
611 "ERROR: SSDT-CMN-600: Failed to build associated SSDT table."
618 // Increment the table count here so that appropriate clean-up
619 // can be done in case of failure.
623 // Note: Table list and CMN600 device count has been setup. The
624 // framework will invoke FreeSsdtCmn600TableResourcesEx() even
625 // on failure, so appropriate clean-up will be done.
629 /** This macro defines the Raw Generator revision.
631 #define SSDT_CMN_600_GENERATOR_REVISION CREATE_REVISION (1, 0)
633 /** The interface for the Raw Table Generator.
637 ACPI_TABLE_GENERATOR SsdtCmn600Generator
= {
639 CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCmn600
),
640 // Generator Description
641 L
"ACPI.STD.SSDT.CMN600.GENERATOR",
642 // ACPI Table Signature
643 EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
,
644 // ACPI Table Revision - Unused
646 // Minimum ACPI Table Revision - Unused
649 TABLE_GENERATOR_CREATOR_ID_ARM
,
651 SSDT_CMN_600_GENERATOR_REVISION
,
652 // Build table function. Use the extended version instead.
654 // Free table function. Use the extended version instead.
656 // Build Table function
657 BuildSsdtCmn600TableEx
,
658 // Free Resource function
659 FreeSsdtCmn600TableResourcesEx
662 /** Register the Generator with the ACPI Table Factory.
664 @param [in] ImageHandle The handle to the image.
665 @param [in] SystemTable Pointer to the System Table.
667 @retval EFI_SUCCESS The Generator is registered.
668 @retval EFI_INVALID_PARAMETER A parameter is invalid.
669 @retval EFI_ALREADY_STARTED The Generator for the Table ID
670 is already registered.
674 AcpiSsdtCmn600LibConstructor (
675 IN EFI_HANDLE ImageHandle
,
676 IN EFI_SYSTEM_TABLE
*SystemTable
681 Status
= RegisterAcpiTableGenerator (&SsdtCmn600Generator
);
684 "SSDT-CMN-600: Register Generator. Status = %r\n",
687 ASSERT_EFI_ERROR (Status
);
691 /** Deregister the Generator from the ACPI Table Factory.
693 @param [in] ImageHandle The handle to the image.
694 @param [in] SystemTable Pointer to the System Table.
696 @retval EFI_SUCCESS The Generator is deregistered.
697 @retval EFI_INVALID_PARAMETER A parameter is invalid.
698 @retval EFI_NOT_FOUND The Generator is not registered.
702 AcpiSsdtCmn600LibDestructor (
703 IN EFI_HANDLE ImageHandle
,
704 IN EFI_SYSTEM_TABLE
*SystemTable
709 Status
= DeregisterAcpiTableGenerator (&SsdtCmn600Generator
);
712 "SSDT-CMN-600: Deregister Generator. Status = %r\n",
715 ASSERT_EFI_ERROR (Status
);