blob: c4598f0d223b145b5f410553e1894032ff9d605f [file]
/** @file
BERT Table Generator
Copyright (c) 2026, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Glossary:
- Cm or CM - Configuration Manager
- Obj or OBJ - Object
@par Reference(s):
- ACPI 6.6 Specification, January 2025
**/
#include <AcpiTableGenerator.h>
#include <ConfigurationManagerHelper.h>
#include <ConfigurationManagerObject.h>
#include <IndustryStandard/Acpi66.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/TableHelperLib.h>
#include <Protocol/ConfigurationManagerProtocol.h>
/** Standard BERT Generator
Requirements:
The following Configuration Manager Object is required by this Generator:
- EArchCommonObjBootErrorRegionInfo
*/
/** Retrieve the Boot Error Region information. */
GET_OBJECT_LIST (
EObjNameSpaceArchCommon,
EArchCommonObjBootErrorRegionInfo,
CM_ARCH_COMMON_BOOT_ERROR_REGION_INFO
);
/** Construct the BERT ACPI table.
This function invokes the Configuration Manager protocol interface to get
the Boot Error Region information required for generating the ACPI table.
@param [in] This Pointer to the table generator.
@param [in] AcpiTableInfo Pointer to the ACPI Table Info.
@param [in] CfgMgrProtocol Pointer to the Configuration Manager Protocol.
@param [out] Table Pointer to the constructed ACPI Table.
@retval EFI_SUCCESS Table generated successfully.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_NOT_FOUND The required object was not found.
@retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration Manager
is smaller than the requested object.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
**/
STATIC
EFI_STATUS
EFIAPI
BuildBertTable (
IN CONST ACPI_TABLE_GENERATOR *CONST This,
IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
OUT EFI_ACPI_DESCRIPTION_HEADER **CONST Table
)
{
EFI_STATUS Status;
CM_ARCH_COMMON_BOOT_ERROR_REGION_INFO *BootErrorRegionInfo;
EFI_ACPI_6_6_BOOT_ERROR_RECORD_TABLE_HEADER *Bert;
ASSERT (This != NULL);
ASSERT (AcpiTableInfo != NULL);
ASSERT (CfgMgrProtocol != NULL);
ASSERT (Table != NULL);
ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
(AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision))
{
DEBUG ((
DEBUG_ERROR,
"ERROR: BERT: Requested table revision = %d is not supported. "
"Supported table revision: Minimum = %d, Maximum = %d\n",
AcpiTableInfo->AcpiTableRevision,
This->MinAcpiTableRevision,
This->AcpiTableRevision
));
return EFI_INVALID_PARAMETER;
}
*Table = NULL;
Status = GetEArchCommonObjBootErrorRegionInfo (
CfgMgrProtocol,
CM_NULL_TOKEN,
&BootErrorRegionInfo,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: BERT: Failed to get Boot Error Region information. "
"Status = %r\n",
Status
));
return Status;
}
if (BootErrorRegionInfo->BootErrorRegion == 0) {
DEBUG ((
DEBUG_ERROR,
"ERROR: BERT: Boot Error Region address must not be zero.\n"
));
return EFI_INVALID_PARAMETER;
}
if (BootErrorRegionInfo->BootErrorRegionLength <
sizeof (EFI_ACPI_6_6_GENERIC_ERROR_STATUS_STRUCTURE))
{
DEBUG ((
DEBUG_ERROR,
"ERROR: BERT: Boot Error Region is too small. "
"Length = 0x%x, Minimum = 0x%x\n",
BootErrorRegionInfo->BootErrorRegionLength,
(UINT32)sizeof (EFI_ACPI_6_6_GENERIC_ERROR_STATUS_STRUCTURE)
));
return EFI_INVALID_PARAMETER;
}
Bert = AllocateZeroPool (sizeof (*Bert));
if (Bert == NULL) {
DEBUG ((DEBUG_ERROR, "ERROR: BERT: Failed to allocate the BERT table.\n"));
return EFI_OUT_OF_RESOURCES;
}
Status = AddAcpiHeader (
CfgMgrProtocol,
This,
&Bert->Header,
AcpiTableInfo,
sizeof (*Bert)
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: BERT: Failed to add ACPI header. Status = %r\n",
Status
));
FreePool (Bert);
return Status;
}
Bert->BootErrorRegionLength = BootErrorRegionInfo->BootErrorRegionLength;
Bert->BootErrorRegion = BootErrorRegionInfo->BootErrorRegion;
*Table = &Bert->Header;
return EFI_SUCCESS;
}
/** Free resources allocated for constructing the BERT table.
@param [in] This Pointer to the table generator.
@param [in] AcpiTableInfo Pointer to the ACPI Table Info.
@param [in] CfgMgrProtocol Pointer to the Configuration Manager
Protocol interface.
@param [in, out] Table Pointer to the constructed ACPI Table.
@retval EFI_SUCCESS The resources were freed successfully.
@retval EFI_INVALID_PARAMETER The table pointer is NULL.
**/
STATIC
EFI_STATUS
EFIAPI
FreeBertTableResources (
IN CONST ACPI_TABLE_GENERATOR *CONST This,
IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
IN OUT EFI_ACPI_DESCRIPTION_HEADER **CONST Table
)
{
ASSERT (This != NULL);
ASSERT (AcpiTableInfo != NULL);
ASSERT (CfgMgrProtocol != NULL);
ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
if (Table == NULL) {
DEBUG ((DEBUG_ERROR, "ERROR: BERT: Invalid Table Pointer\n"));
ASSERT (Table != NULL);
return EFI_INVALID_PARAMETER;
}
if (*Table != NULL) {
FreePool (*Table);
*Table = NULL;
}
return EFI_SUCCESS;
}
/** This macro defines the BERT Table Generator revision. */
#define BERT_GENERATOR_REVISION CREATE_REVISION (1, 0)
/** The interface for the BERT Table Generator. */
STATIC
CONST
ACPI_TABLE_GENERATOR BertGenerator = {
// Generator ID
CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdBert),
// Generator Description
L"ACPI.STD.BERT.GENERATOR",
// ACPI Table Signature
EFI_ACPI_6_6_BOOT_ERROR_RECORD_TABLE_SIGNATURE,
// ACPI Table Revision supported by this Generator
EFI_ACPI_6_6_BOOT_ERROR_RECORD_TABLE_REVISION,
// Minimum supported ACPI Table Revision
EFI_ACPI_6_6_BOOT_ERROR_RECORD_TABLE_REVISION,
// Creator ID
TABLE_GENERATOR_CREATOR_ID,
// Creator Revision
BERT_GENERATOR_REVISION,
// Build Table function
BuildBertTable,
// Free Resource function
FreeBertTableResources,
// Extended build function not needed.
NULL,
// Extended free resource function not needed.
NULL
};
/** Register the Generator with the ACPI Table Factory.
@param [in] ImageHandle The handle to the image.
@param [in] SystemTable Pointer to the System Table.
@retval EFI_SUCCESS The Generator was registered.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_ALREADY_STARTED The Generator is already registered.
**/
EFI_STATUS
EFIAPI
AcpiBertLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = RegisterAcpiTableGenerator (&BertGenerator);
DEBUG ((DEBUG_INFO, "BERT: Register Generator. Status = %r\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Deregister the Generator from the ACPI Table Factory.
@param [in] ImageHandle The handle to the image.
@param [in] SystemTable Pointer to the System Table.
@retval EFI_SUCCESS The Generator was deregistered.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_NOT_FOUND The Generator is not registered.
**/
EFI_STATUS
EFIAPI
AcpiBertLibDestructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = DeregisterAcpiTableGenerator (&BertGenerator);
DEBUG ((DEBUG_INFO, "BERT: Deregister Generator. Status = %r\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}