--- /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 <AcpiTableGenerator.h>\r
+#include <ConfigurationManagerHelper.h>\r
+#include <ConfigurationManagerObject.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/TableHelperLib.h>\r
+#include <Protocol/AcpiTable.h>\r
+#include <Protocol/ConfigurationManagerProtocol.h>\r
+#include <X64NameSpaceObjects.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
+/** The ACPI HPET Table.\r
+*/\r
+STATIC\r
+EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER AcpiHpet = {\r
+ ACPI_HEADER (\r
+ EFI_ACPI_6_5_HIGH_PRECISION_EVENT_TIMER_TABLE_SIGNATURE,\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER,\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_REVISION\r
+ ),\r
+ // EventTimerBlockId,\r
+ 0,\r
+ // BaseAddressLower32Bit\r
+ { EFI_ACPI_6_5_SYSTEM_MEMORY, 64,0, EFI_ACPI_RESERVED_BYTE, 0 },\r
+ // HpetNumber\r
+ 0,\r
+ // MainCounterMinimumClockTickInPeriodicMode\r
+ 0,\r
+ // PageProtectionAndOemAttribute\r
+ EFI_ACPI_NO_PAGE_PROTECTION\r
+};\r
+\r
+/** Update HPET table information.\r
+\r
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager\r
+ Protocol Interface.\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
+HpetUpdateTableInfo (\r
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol\r
+ )\r
+{\r
+ CM_X64_HPET_INFO *HpetInfo;\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_BLOCK_ID HpetBlockId;\r
+ EFI_STATUS Status;\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
+ // Fill the Event Timer Block ID\r
+ AcpiHpet.EventTimerBlockId = HpetBlockId.Uint32;\r
+\r
+ // Fill the Base Address\r
+ AcpiHpet.BaseAddressLower32Bit.Address = HpetInfo->BaseAddressLower32Bit;\r
+\r
+ // Minimum clock tick in periodic mode\r
+ AcpiHpet.MainCounterMinimumClockTickInPeriodicMode = HpetInfo->MainCounterMinimumClockTickInPeriodicMode;\r
+\r
+ // Page protection and OEM attribute\r
+ AcpiHpet.PageProtectionAndOemAttribute = HpetInfo->PageProtectionAndOemAttribute;\r
+\r
+ return Status;\r
+}\r
+\r
+/** Construct the 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
+BuildHpetTable (\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
+\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 = AddAcpiHeader (\r
+ CfgMgrProtocol,\r
+ This,\r
+ (EFI_ACPI_DESCRIPTION_HEADER *)&AcpiHpet,\r
+ AcpiTableInfo,\r
+ sizeof (EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ERROR: HPET: Failed to add ACPI header. Status = %r\n",\r
+ Status\r
+ ));\r
+ goto error_handler;\r
+ }\r
+\r
+ // Update HPET table info\r
+ Status = HpetUpdateTableInfo (CfgMgrProtocol);\r
+ if (EFI_ERROR (Status)) {\r
+ goto error_handler;\r
+ }\r
+\r
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER *)&AcpiHpet;\r
+error_handler:\r
+ return Status;\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 HpetGenerator = {\r
+ // Generator ID\r
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdHpet),\r
+ // Generator Description\r
+ L"ACPI.STD.HPET.GENERATOR",\r
+ // ACPI Table Signature\r
+ EFI_ACPI_6_5_HIGH_PRECISION_EVENT_TIMER_TABLE_SIGNATURE,\r
+ // ACPI Table Revision supported by this Generator\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_REVISION,\r
+ // Minimum supported ACPI Table Revision\r
+ EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_REVISION,\r
+ // Creator ID\r
+ TABLE_GENERATOR_CREATOR_ID,\r
+ // Creator Revision\r
+ HPET_GENERATOR_REVISION,\r
+ // Build Table function\r
+ BuildHpetTable,\r
+ // No additional resources are allocated by the generator.\r
+ // Hence the Free Resource function is not required.\r
+ NULL,\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
+AcpiHpetLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = RegisterAcpiTableGenerator (&HpetGenerator);\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
+AcpiHpetLibDestructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = DeregisterAcpiTableGenerator (&HpetGenerator);\r
+ DEBUG ((DEBUG_INFO, "HPET: Deregister Generator. Status = %r\n", Status));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r