blob: e12bf40a9685d8efc6705f50a8cceebb79b0fc49 [file]
/** @file
Generate IWB SSDT table.
Copyright (c) 2026, Arm Limited. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- IO Remapping Table, Platform Design Document, Revision E.g, Mar 2026
(https://developer.arm.com/documentation/den0049/)
**/
#include <Library/AcpiLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Protocol/AcpiTable.h>
// Module specific include files.
#include <ConfigurationManagerObject.h>
#include <Library/AcpiHelperLib.h>
#include <Library/AmlLib/AmlLib.h>
#include "IortGenerator.h"
/** According to GICv5 specification chapther 10.4.1 IWB_CONFIG_FRAME,
IWB_CONFIG_FRAME's block size is 0x10000 bytes.
*/
#define IWB_FRAME_BLOCK_SIZE 0x10000
/** C array containing the compiled AML template.
This symbol is defined in the auto generated C file
containing the AML bytecode array.
*/
extern CHAR8 iwbdevicetabletemplate_aml_code[];
/** Fixup the IWB device UID (_UID).
@param [in] RootNodeHandle Pointer to the root of an AML tree.
@param [in] Uid UID for the IWB 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
FixupIwbDeviceUid (
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_.IWB0._UID",
&NameOpIdNode
);
if (EFI_ERROR (Status)) {
return Status;
}
return AmlNameOpUpdateInteger (NameOpIdNode, (UINT64)Uid);
}
/** Fixup the Iwb 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
FixupIwbDeviceName (
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_.IWB0", &DeviceNode);
if (EFI_ERROR (Status)) {
return Status;
}
// Update the Device's name.
return AmlDeviceOpUpdateName (DeviceNode, Name);
}
/** Fixup the Iwb device _CRS values (BaseAddress, ...).
@param [in] RootNodeHandle Pointer to the root of an AML tree.
@param [in] IwbInfo Pointer to a IWB 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
FixupIwbDeviceCrs (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST CM_ARM_GIC_IWB_INFO *IwbInfo
)
{
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_.IWB0._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 IWB device's base address and length.
Status = AmlUpdateRdQWord (
QWordRdNode,
IwbInfo->ConfigFrameBase,
IWB_FRAME_BLOCK_SIZE
);
if (EFI_ERROR (Status)) {
return Status;
}
return Status;
}
/** Fixup the IWB 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] IwbInfo Pointer to a IWB 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 IWB 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
FixupIwbDeviceInfo (
IN AML_ROOT_NODE_HANDLE RootNodeHandle,
IN CONST CM_ARM_GIC_IWB_INFO *IwbInfo,
IN CONST CHAR8 *Name,
IN CONST UINT64 Uid
)
{
EFI_STATUS Status;
ASSERT (RootNodeHandle != NULL);
ASSERT (IwbInfo != NULL);
ASSERT (Name != NULL);
// Fixup the _UID value.
Status = FixupIwbDeviceUid (RootNodeHandle, Uid);
if (EFI_ERROR (Status)) {
return Status;
}
// Fixup the _CRS values.
Status = FixupIwbDeviceCrs (RootNodeHandle, IwbInfo);
if (EFI_ERROR (Status)) {
return Status;
}
// Fixup the Iwb device name.
// This MUST be done at the end, otherwise AML paths won't be valid anymore.
return FixupIwbDeviceName (RootNodeHandle, Name);
}
/** Build a SSDT table describing the IWB device.
The table created by this function must be freed by FreeSimpleIwbDeviceTable.
@param [in] Idx Index to generate device name.
@param [in] IwbInfo IWB device info to describe in the SSDT table.
@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
BuildIwbDeviceTable (
IN UINT32 Idx,
IN CONST CM_ARM_GIC_IWB_INFO *IwbInfo,
OUT EFI_ACPI_DESCRIPTION_HEADER **Table
)
{
EFI_STATUS Status;
EFI_STATUS Status1;
AML_ROOT_NODE_HANDLE RootNodeHandle;
CHAR8 IwbDevName[IWB_DEV_NAME_SIZE];
ASSERT (IwbInfo != NULL);
ASSERT (Table != NULL);
if (Idx >= MAX_IWB_COUNT) {
DEBUG ((
DEBUG_ERROR,
"ERROR: IWB-DEVICE-FIXUP:"
"Device index to generate device name should be < %d\n",
MAX_IWB_COUNT
));
return EFI_INVALID_PARAMETER;
}
IwbDevName[0] = 'I';
IwbDevName[1] = 'W';
IwbDevName[2] = 'B';
IwbDevName[3] = AsciiFromHex (Idx);
IwbDevName[4] = '\0';
// Parse the IWB Device Table Template.
Status = AmlParseDefinitionBlock (
(EFI_ACPI_DESCRIPTION_HEADER *)iwbdevicetabletemplate_aml_code,
&RootNodeHandle
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: IWB-DEVICE-FIXUP:"
" Failed to parse SSDT IWB device Template. Status = %r\n",
Status
));
return Status;
}
// Fixup the template values.
Status = FixupIwbDeviceInfo (
RootNodeHandle,
IwbInfo,
IwbDevName,
IwbInfo->GicIwbId
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: IWB-DEVICE-FIXUP: Failed to fixup SSDT IWB Device Table."
" Status = %r\n",
Status
));
goto ExitHandler;
}
// Serialize the tree.
Status = AmlSerializeDefinitionBlock (
RootNodeHandle,
Table
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: IWB-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: IWB-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 Iwb device table previously created by
the BuildIwbDeviceTable function.
@param [in] Table Pointer to a Iwb Device table allocated by
the BuildIwbDeviceTable function.
**/
VOID
EFIAPI
FreeIwbDeviceTable (
IN EFI_ACPI_DESCRIPTION_HEADER *Table
)
{
ASSERT (Table != NULL);
FreePool (Table);
}