--- /dev/null
+/** @file\r
+\r
+ Generate ACPI HPET table for AMD platforms.\r
+\r
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.\r
+\r
+ SPDX-License-Identifier BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <IndustryStandard/HighPrecisionEventTimerTable.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Protocol/AcpiTable.h>\r
+\r
+// Module specific include files.\r
+#include <AcpiTableGenerator.h>\r
+#include <ConfigurationManagerObject.h>\r
+#include <ConfigurationManagerHelper.h>\r
+#include <Library/AcpiHelperLib.h>\r
+#include <Library/TableHelperLib.h>\r
+#include <Library/AmlLib/AmlLib.h>\r
+#include <Protocol/ConfigurationManagerProtocol.h>\r
+#include <Library/IoLib.h>\r
+\r
+/** This macro defines supported HPET page protection flags\r
+*/\r
+#define HPET_VALID_PAGE_PROTECTION \\r
+ (EFI_ACPI_NO_PAGE_PROTECTION | \\r
+ EFI_ACPI_4KB_PAGE_PROTECTION | \\r
+ EFI_ACPI_64KB_PAGE_PROTECTION)\r
+\r
+/** This macro expands to a function that retrieves the\r
+ HPET device information from the Configuration Manager.\r
+*/\r
+GET_OBJECT_LIST (\r
+ EObjNameSpaceX64,\r
+ EX64ObjHpetInfo,\r
+ CM_X64_HPET_INFO\r
+ );\r
+\r
+/** Update HPET table information.\r
+\r
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager\r
+ Protocol Interface.\r
+ @param [in, out] ScopeNode The Scope Node for the HPET table.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND The required object was not found or\r
+ the HPET is not enabled.\r
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration\r
+ Manager is less than the Object size for the\r
+ requested object.\r
+ @retval EFI_UNSUPPORTED If invalid protection and oem flags provided.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+SsdtHpetUpdateTableInfo (\r
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,\r
+ IN OUT AML_OBJECT_NODE_HANDLE ScopeNode\r
+ )\r
+{\r
+ CM_X64_HPET_INFO *HpetInfo;\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_BLOCK_ID HpetBlockId;\r
+ EFI_STATUS Status;\r
+ AML_OBJECT_NODE_HANDLE CrsNode;\r
+ AML_OBJECT_NODE_HANDLE HpetNode;\r
+ UINT32 EisaId;\r
+\r
+ ASSERT (CfgMgrProtocol != NULL);\r
+\r
+ // Get the HPET information from the Platform Configuration Manager\r
+ Status = GetEX64ObjHpetInfo (\r
+ CfgMgrProtocol,\r
+ CM_NULL_TOKEN,\r
+ &HpetInfo,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: HPET: Failed to get HPET information." \\r
+ " Status = %r\n",\r
+ Status\r
+ ));\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "HPET: Device base address = 0x%x\n"\r
+ " : Minimum clock tick in periodic mode = 0x%x\n"\r
+ " : Page protection and Oem flags = 0x%x\n",\r
+ HpetInfo->BaseAddressLower32Bit,\r
+ HpetInfo->MainCounterMinimumClockTickInPeriodicMode,\r
+ HpetInfo->PageProtectionAndOemAttribute\r
+ ));\r
+\r
+ // Validate the page protection flags bit0 to bit3\r
+ if (((HpetInfo->PageProtectionAndOemAttribute & 0xF) & ~HPET_VALID_PAGE_PROTECTION) != 0) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: HPET: unsupported page protection flags = 0x%x\n",\r
+ HpetInfo->PageProtectionAndOemAttribute\r
+ ));\r
+ ASSERT_EFI_ERROR (EFI_UNSUPPORTED);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ // Get HPET Capabilities ID register value and test if HPET is enabled\r
+ HpetBlockId.Uint32 = MmioRead32 (HpetInfo->BaseAddressLower32Bit);\r
+\r
+ // If mmio address is not mapped\r
+ if ((HpetBlockId.Uint32 == MAX_UINT32) || (HpetBlockId.Uint32 == 0)) {\r
+ DEBUG ((DEBUG_ERROR, "HPET Capabilities register read failed.\n"));\r
+ ASSERT_EFI_ERROR (EFI_NOT_FOUND);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ // Validate Reserved and Revision ID\r
+ if (HpetBlockId.Bits.Reserved != 0) {\r
+ DEBUG ((DEBUG_ERROR, "HPET Reserved bit is set.\n"));\r
+ ASSERT_EFI_ERROR (EFI_UNSUPPORTED);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (HpetBlockId.Bits.Revision == 0) {\r
+ DEBUG ((DEBUG_ERROR, "HPET Revision is not set.\n"));\r
+ ASSERT_EFI_ERROR (EFI_UNSUPPORTED);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = AmlCodeGenDevice ("HPET", ScopeNode, &HpetNode);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlGetEisaIdFromString ("PNP0103", &EisaId);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlCodeGenNameInteger ("_HID", EisaId, HpetNode, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlCodeGenNameInteger ("_UID", 0x00, HpetNode, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlCodeGenNameResourceTemplate ("_CRS", HpetNode, &CrsNode);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlCodeGenRdMemory32Fixed (\r
+ FALSE,\r
+ HpetInfo->BaseAddressLower32Bit,\r
+ SIZE_1KB,\r
+ CrsNode,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ if ((HpetInfo->PageProtectionAndOemAttribute & 0xF) != 0) {\r
+ Status = AmlCodeGenNameInteger (\r
+ "PAGE",\r
+ (HpetInfo->PageProtectionAndOemAttribute & 0xF),\r
+ HpetNode,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if ((HpetInfo->PageProtectionAndOemAttribute >> 4) != 0) {\r
+ Status = AmlCodeGenNameInteger (\r
+ "ATTR",\r
+ (HpetInfo->PageProtectionAndOemAttribute >> 4),\r
+ HpetNode,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/** Construct the SSDT HPET table.\r
+\r
+ This function invokes the Configuration Manager protocol interface\r
+ to get the required information for generating the ACPI table.\r
+\r
+ If this function allocates any resources then they must be freed\r
+ in the FreeXXXXTableResources function.\r
+\r
+ @param [in] This Pointer to the table generator.\r
+ @param [in] AcpiTableInfo Pointer to the ACPI Table Info.\r
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager\r
+ Protocol Interface.\r
+ @param [out] Table Pointer to the constructed ACPI Table.\r
+\r
+ @retval EFI_SUCCESS Table generated successfully.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND The required object was not found.\r
+ @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration\r
+ Manager is less than the Object size for the\r
+ requested object.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+BuildSsdtHpetTable (\r
+ IN CONST ACPI_TABLE_GENERATOR *CONST This,\r
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,\r
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,\r
+ OUT EFI_ACPI_DESCRIPTION_HEADER **CONST Table\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AML_ROOT_NODE_HANDLE RootNode;\r
+ AML_OBJECT_NODE_HANDLE ScopeNode;\r
+\r
+ ASSERT (This != NULL);\r
+ ASSERT (AcpiTableInfo != NULL);\r
+ ASSERT (CfgMgrProtocol != NULL);\r
+ ASSERT (Table != NULL);\r
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);\r
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);\r
+\r
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||\r
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision))\r
+ {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: HPET: Requested table revision = %d, is not supported."\r
+ "Supported table revision: Minimum = %d, Maximum = %d\n",\r
+ AcpiTableInfo->AcpiTableRevision,\r
+ This->MinAcpiTableRevision,\r
+ This->AcpiTableRevision\r
+ ));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *Table = NULL;\r
+\r
+ Status = AddSsdtAcpiHeader (\r
+ CfgMgrProtocol,\r
+ This,\r
+ AcpiTableInfo,\r
+ &RootNode\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlCodeGenScope ("\\_SB_", RootNode, &ScopeNode);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ goto exit_handler;\r
+ }\r
+\r
+ // Update HPET table info\r
+ Status = SsdtHpetUpdateTableInfo (CfgMgrProtocol, ScopeNode);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ goto exit_handler;\r
+ }\r
+\r
+ Status = AmlSerializeDefinitionBlock (\r
+ RootNode,\r
+ Table\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: SSDT-HPET: Failed to Serialize SSDT Table Data."\r
+ " Status = %r\n",\r
+ Status\r
+ ));\r
+ }\r
+\r
+exit_handler:\r
+ // Delete the RootNode and its attached children.\r
+ AmlDeleteTree (RootNode);\r
+ return Status;\r
+}\r
+\r
+/** Free any resources allocated for constructing the\r
+ SSDT HPET ACPI table.\r
+\r
+ @param [in] This Pointer to the table generator.\r
+ @param [in] AcpiTableInfo Pointer to the ACPI Table Info.\r
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager\r
+ Protocol Interface.\r
+ @param [in, out] Table Pointer to the ACPI Table.\r
+\r
+ @retval EFI_SUCCESS The resources were freed successfully.\r
+ @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+FreeSsdtHpetTableResources (\r
+ IN CONST ACPI_TABLE_GENERATOR *CONST This,\r
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,\r
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,\r
+ IN OUT EFI_ACPI_DESCRIPTION_HEADER **CONST Table\r
+ )\r
+{\r
+ ASSERT (This != NULL);\r
+ ASSERT (AcpiTableInfo != NULL);\r
+ ASSERT (CfgMgrProtocol != NULL);\r
+ ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);\r
+ ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);\r
+\r
+ if ((Table == NULL) || (*Table == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "ERROR: SSDT-HPET: Invalid Table Pointer\n"));\r
+ ASSERT ((Table != NULL) && (*Table != NULL));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ FreePool (*Table);\r
+ *Table = NULL;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** This macro defines the HPET Table Generator revision.\r
+*/\r
+#define HPET_GENERATOR_REVISION CREATE_REVISION (1, 0)\r
+\r
+/** The interface for the HPET Table Generator.\r
+*/\r
+STATIC\r
+CONST\r
+ACPI_TABLE_GENERATOR SsdtHpetGenerator = {\r
+ // Generator ID\r
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtHpet),\r
+ // Generator Description\r
+ L"ACPI.STD.SSDT.HPET.GENERATOR",\r
+ // ACPI Table Signature\r
+ EFI_ACPI_6_5_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE,\r
+ // ACPI Table Revision supported by this Generator\r
+ 0,\r
+ // Minimum supported ACPI Table Revision\r
+ 0,\r
+ // Creator ID\r
+ TABLE_GENERATOR_CREATOR_ID,\r
+ // Creator Revision\r
+ HPET_GENERATOR_REVISION,\r
+ // Build Table function\r
+ BuildSsdtHpetTable,\r
+ // Free Resource function\r
+ FreeSsdtHpetTableResources,\r
+ // Extended build function not needed\r
+ NULL,\r
+ // Extended build function not implemented by the generator.\r
+ // Hence extended free resource function is not required.\r
+ NULL\r
+};\r
+\r
+/** Register the Generator with the ACPI Table Factory.\r
+\r
+ @param [in] ImageHandle The handle to the image.\r
+ @param [in] SystemTable Pointer to the System Table.\r
+\r
+ @retval EFI_SUCCESS The Generator is registered.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID\r
+ is already registered.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AcpiSsdtHpetLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = RegisterAcpiTableGenerator (&SsdtHpetGenerator);\r
+ DEBUG ((DEBUG_INFO, "HPET: Register Generator. Status = %r\n", Status));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r
+\r
+/** Deregister the Generator from the ACPI Table Factory.\r
+\r
+ @param [in] ImageHandle The handle to the image.\r
+ @param [in] SystemTable Pointer to the System Table.\r
+\r
+ @retval EFI_SUCCESS The Generator is deregistered.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND The Generator is not registered.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AcpiSsdtHpetLibDestructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = DeregisterAcpiTableGenerator (&SsdtHpetGenerator);\r
+ DEBUG ((DEBUG_INFO, "HPET: Deregister Generator. Status = %r\n", Status));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r