/** @file
Tpm2 device table generating Library
Copyright (c) 2025, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- TCG ACPI specification.
(https://trustedcomputinggroup.org/resource/tcg-acpi-specification/)
**/
#include
#include
#include
#include
#include
#include
#include
#include
// Module specific include files.
#include
#include
#include
#include
#include
#include
/** C array containing the compiled AML template.
This symbol is defined in the auto generated C file
containing the AML bytecode array.
*/
extern CHAR8 tpm2devicetabletemplate_aml_code[];
/** Fixup the TPM2 device UID (_UID).
@param [in] RootNodeHandle Pointer to the root of an AML tree.
@param [in] Uid UID for the TPM2 device.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Could not find information.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
FixupTpm2DeviceUid (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST UINT64 Uid
)
{
EFI_STATUS Status;
AML_OBJECT_NODE_HANDLE NameOpIdNode;
// Get the _UID NameOp object defined by the "Name ()" statement,
// and update its value.
Status = AmlFindNode (
RootNodeHandle,
"\\_SB_.TPM0._UID",
&NameOpIdNode
);
if (EFI_ERROR (Status)) {
return Status;
}
return AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
}
/** Fixup the Tpm2 device name.
@param [in] RootNodeHandle Pointer to the root of an AML tree.
@param [in] Name The Name to give to the Device.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Could not find information.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
FixupTpm2DeviceName (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST CHAR8 *Name
)
{
EFI_STATUS Status;
AML_OBJECT_NODE_HANDLE DeviceNode;
// Get the COM0 variable defined by the "Device ()" statement.
Status = AmlFindNode (RootNodeHandle, "\\_SB_.TPM0", &DeviceNode);
if (EFI_ERROR (Status)) {
return Status;
}
// Update the Device's name.
return AmlDeviceOpUpdateName (DeviceNode, Name);
}
/** Fixup the Tpm2 device _CRS values (BaseAddress, ...).
@param [in] RootNodeHandle Pointer to the root of an AML tree.
@param [in] TpmDevInfo Pointer to a TPM2 device Information
structure.
Get the device size Information from there.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Could not find information.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
FixupTpm2DeviceCrs (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST CM_ARCH_COMMON_TPM2_DEVICE_INFO *TpmDevInfo
)
{
EFI_STATUS Status;
AML_OBJECT_NODE_HANDLE NameOpCrsNode;
AML_DATA_NODE_HANDLE QWordRdNode;
// Get the "_CRS" object defined by the "Name ()" statement.
Status = AmlFindNode (
RootNodeHandle,
"\\_SB_.TPM0._CRS",
&NameOpCrsNode
);
if (EFI_ERROR (Status)) {
return Status;
}
// Get the first Rd node in the "_CRS" object.
Status = AmlNameOpGetFirstRdNode (NameOpCrsNode, &QWordRdNode);
if (EFI_ERROR (Status)) {
return Status;
}
if (QWordRdNode == NULL) {
return EFI_INVALID_PARAMETER;
}
// Update the TPM2 device's base address and length.
Status = AmlUpdateRdQWord (
QWordRdNode,
TpmDevInfo->Tpm2DeviceBaseAddress,
TpmDevInfo->Tpm2DeviceSize
);
if (EFI_ERROR (Status)) {
return Status;
}
return Status;
}
/** Fixup the Tpm2 Device in the AML tree.
For each template value:
- find the node to update;
- update the value.
@param [in] RootNodeHandle Pointer to the root of the AML tree.
@param [in] TpmDevInfo Pointer to a TPM2 device Information
structure.
@param [in] Name The Name to give to the Device.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
@param [in] Uid UID for the TPM2 device.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Could not find information.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
STATIC
EFI_STATUS
EFIAPI
FixupTpm2DeviceInfo (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST CM_ARCH_COMMON_TPM2_DEVICE_INFO *TpmDevInfo,
IN CONST CHAR8 *Name,
IN CONST UINT64 Uid
)
{
EFI_STATUS Status;
ASSERT (RootNodeHandle != NULL);
ASSERT (TpmDevInfo != NULL);
ASSERT (Name != NULL);
// Fixup the _UID value.
Status = FixupTpm2DeviceUid (RootNodeHandle, Uid);
if (EFI_ERROR (Status)) {
return Status;
}
// Fixup the _CRS values.
Status = FixupTpm2DeviceCrs (RootNodeHandle, TpmDevInfo);
if (EFI_ERROR (Status)) {
return Status;
}
// Fixup the Tpm2 device name.
// This MUST be done at the end, otherwise AML paths won't be valid anymore.
return FixupTpm2DeviceName (RootNodeHandle, Name);
}
/** Build a SSDT table describing the TPM2 device.
The table created by this function must be freed by FreeSImpleTpm2DeviceTable.
@param [in] TpmDevInfo TPM2 device info to describe in the SSDT table.
@param [in] Name The Name to give to the Device.
Must be a NULL-terminated ASL NameString
e.g.: "DEV0", "DV15.DEV0", etc.
@param [in] Uid UID for the TPM2 device
@param [out] Table If success, pointer to the created SSDT table.
@retval EFI_SUCCESS Table generated successfully.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_NOT_FOUND Could not find information.
@retval EFI_OUT_OF_RESOURCES Could not allocate memory.
**/
EFI_STATUS
EFIAPI
BuildTpm2DeviceTable (
IN CONST CM_ARCH_COMMON_TPM2_DEVICE_INFO *TpmDevInfo,
IN CONST CHAR8 *Name,
IN CONST UINT64 Uid,
OUT EFI_ACPI_DESCRIPTION_HEADER **Table
)
{
EFI_STATUS Status;
EFI_STATUS Status1;
AML_ROOT_NODE_HANDLE RootNodeHandle;
ASSERT (TpmDevInfo != NULL);
ASSERT (Name != NULL);
ASSERT (Table != NULL);
// Parse the Tpm2 Device Table Template.
Status = AmlParseDefinitionBlock (
(EFI_ACPI_DESCRIPTION_HEADER *)tpm2devicetabletemplate_aml_code,
&RootNodeHandle
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: TPM2-DEVICE-FIXUP:"
" Failed to parse SSDT TPM2 device Template. Status = %r\n",
Status
));
return Status;
}
// Fixup the template values.
Status = FixupTpm2DeviceInfo (
RootNodeHandle,
TpmDevInfo,
Name,
Uid
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: TPM2-DEVICE-FIXUP: Failed to fixup SSDT TPM2 Device Table."
" Status = %r\n",
Status
));
goto ExitHandler;
}
// Serialize the tree.
Status = AmlSerializeDefinitionBlock (
RootNodeHandle,
Table
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: TPM2-DEVICE-FIXUP: Failed to Serialize SSDT Table Data."
" Status = %r\n",
Status
));
}
ExitHandler:
// Cleanup
if (RootNodeHandle != NULL) {
Status1 = AmlDeleteTree (RootNodeHandle);
if (EFI_ERROR (Status1)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: TPM2-DEVICE-FIXUP: Failed to cleanup AML tree."
" Status = %r\n",
Status1
));
// If Status was success but we failed to delete the AML Tree
// return Status1 else return the original error code, i.e. Status.
if (!EFI_ERROR (Status)) {
return Status1;
}
}
}
return Status;
}
/** Free an Tpm2 device table previously created by
the BuildTpm2DeviceTable function.
@param [in] Table Pointer to a Tpm2 Device table allocated by
the BuildTpm2DeviceTable function.
**/
VOID
EFIAPI
FreeTpm2DeviceTable (
IN EFI_ACPI_DESCRIPTION_HEADER *Table
)
{
ASSERT (Table != NULL);
FreePool (Table);
}