| /** @file | |
| SPMI Table Generator | |
| Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| @par Reference(s): | |
| - IPMI - Revision 2.0, April 21, 2015. | |
| **/ | |
| #include <IndustryStandard/ServiceProcessorManagementInterfaceTable.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Protocol/AcpiTable.h> | |
| // Module specific include files. | |
| #include <AcpiTableGenerator.h> | |
| #include <ConfigurationManagerObject.h> | |
| #include <ConfigurationManagerHelper.h> | |
| #include <Library/TableHelperLib.h> | |
| #include <IndustryStandard/IpmiNetFnApp.h> | |
| #include <Library/IpmiCommandLib.h> | |
| #include <Protocol/ConfigurationManagerProtocol.h> | |
| /** Standard SPMI Generator | |
| Requirements: | |
| The following Configuration Manager Object(s) are required by | |
| this Generator: | |
| - EArchCommonObjSpmiInterfaceInfo | |
| - EArchCommonObjSpmiInterruptDeviceInfo (OPTIONAL) | |
| */ | |
| /** Retrieve the SPMI interface information. */ | |
| GET_OBJECT_LIST ( | |
| EObjNameSpaceArchCommon, | |
| EArchCommonObjSpmiInterfaceInfo, | |
| CM_ARCH_COMMON_SPMI_INTERFACE_INFO | |
| ); | |
| /** Retrieve the SPMI interrupt and device information. */ | |
| GET_OBJECT_LIST ( | |
| EObjNameSpaceArchCommon, | |
| EArchCommonObjSpmiInterruptDeviceInfo, | |
| CM_ARCH_COMMON_SPMI_INTERRUPT_DEVICE_INFO | |
| ); | |
| STATIC | |
| EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_TABLE AcpiSpmi = { | |
| ACPI_HEADER ( | |
| EFI_ACPI_6_5_SERVER_PLATFORM_MANAGEMENT_INTERFACE_TABLE_SIGNATURE, | |
| EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_TABLE, | |
| EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_5_TABLE_REVISION | |
| ), | |
| /// Interface Type | |
| /// 0 - Reserved | |
| /// 1 - KCS | |
| /// 2 - SMIC | |
| /// 3 - BT | |
| /// 4 - SSIF | |
| /// 5-255 - Reserved | |
| 0x00, | |
| /// Reserved1, must be 0x01 as per the IPMI specification. | |
| 0x01, | |
| /// Specification Revision | |
| 0x0200, | |
| /// Interrupt Type | |
| 0x00, | |
| /// GPE Number | |
| 0x00, | |
| /// Reserved2 | |
| 0x00, | |
| /// PCI device flag | |
| 0x00, | |
| /// Global System Interrupt | |
| 0x00, | |
| /// Base Address | |
| { 0, 0,0, 0, 0 }, | |
| /// Device ID | |
| { | |
| { 0x00 } | |
| }, | |
| /// Reserved3 | |
| 0x00 | |
| }; | |
| /** Construct the SPMI ACPI table. | |
| This function invokes the Configuration Manager protocol interface | |
| to get the required hardware information for generating the ACPI | |
| table. | |
| If this function allocates any resources then they must be freed | |
| in the FreeXXXXTableResources function. | |
| @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 [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 less than the Object size for the | |
| requested object. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| BuildSpmiTable ( | |
| 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_SPMI_INTERFACE_INFO *SpmiInfo; | |
| CM_ARCH_COMMON_SPMI_INTERRUPT_DEVICE_INFO *SpmiIntrDeviceInfo; | |
| IPMI_GET_DEVICE_ID_RESPONSE DeviceId; | |
| 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: SPMI: 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 = GetEArchCommonObjSpmiInterfaceInfo ( | |
| CfgMgrProtocol, | |
| CM_NULL_TOKEN, | |
| &SpmiInfo, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: Failed to retrieve interface type and base address.\n" | |
| )); | |
| return Status; | |
| } | |
| /// Validate interface type. | |
| if ((SpmiInfo->InterfaceType < EFI_ACPI_SPMI_INTERFACE_TYPE_KCS) || | |
| (SpmiInfo->InterfaceType > EFI_ACPI_SPMI_INTERFACE_TYPE_SSIF)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: The Interface Type is invalid. Type = %d\n", | |
| SpmiInfo->InterfaceType | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /// If the interface type is SSIF, the Address Space ID should be SMBUS. | |
| if ((SpmiInfo->InterfaceType == EFI_ACPI_SPMI_INTERFACE_TYPE_SSIF) && | |
| (SpmiInfo->BaseAddress.AddressSpaceId != EFI_ACPI_6_5_SMBUS)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: Invalid Address Space ID for SSIF. ID = %d\n", | |
| SpmiInfo->BaseAddress.AddressSpaceId | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /// For non-ssif interface types, the Address Space ID should be System Memory or System I/O. | |
| if ((SpmiInfo->InterfaceType != EFI_ACPI_SPMI_INTERFACE_TYPE_SSIF) && | |
| ((SpmiInfo->BaseAddress.AddressSpaceId != EFI_ACPI_6_5_SYSTEM_MEMORY) && | |
| (SpmiInfo->BaseAddress.AddressSpaceId != EFI_ACPI_6_5_SYSTEM_IO))) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: Invalid Address Space ID. ID = %d\n", | |
| SpmiInfo->BaseAddress.AddressSpaceId | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Status = GetEArchCommonObjSpmiInterruptDeviceInfo ( | |
| CfgMgrProtocol, | |
| CM_NULL_TOKEN, | |
| &SpmiIntrDeviceInfo, | |
| NULL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| /// Validate Interrupt Type, bit[7:2] should be zero. | |
| if ((SpmiIntrDeviceInfo->InterruptType >> 2) != 0 ) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: The Interrupt Type has non-zero reserved bits. InterruptType = 0x%x\n", | |
| SpmiIntrDeviceInfo->InterruptType | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (SpmiInfo->InterfaceType == EFI_ACPI_SPMI_INTERFACE_TYPE_SSIF) { | |
| /// Interrupt Type bit[0] should be zero for SSIF interface type. | |
| if ((SpmiIntrDeviceInfo->InterruptType & BIT0) != 0) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: The Interrupt Type bit0 should be zero for SSIF interface type.\n" | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /// PCI device flag bit0 should be zero for SSIF interface type. | |
| if ((SpmiIntrDeviceInfo->PciDeviceFlag & BIT0) != 0) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: PCI Device Flag is invalid for SSIF interface type.\n" | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| /// Validate SCI GPE bit if GPE number is provided. | |
| if ((SpmiIntrDeviceInfo->Gpe != 0) && | |
| ((SpmiIntrDeviceInfo->InterruptType & BIT0) == 0)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: The Interrupt Type bit0 should be set if a GPE number is provided.\n" | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /// If GlobalSystemInterrupt is provided, the interrupt type should be GSI. | |
| if ((SpmiIntrDeviceInfo->GlobalSystemInterrupt != 0) && | |
| ((SpmiIntrDeviceInfo->InterruptType & BIT1) == 0)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: Invalid interrupt type = 0x%x for GSI 0x%x\n", | |
| SpmiIntrDeviceInfo->InterruptType, | |
| SpmiIntrDeviceInfo->GlobalSystemInterrupt | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| AcpiSpmi.InterruptType = SpmiIntrDeviceInfo->InterruptType; | |
| AcpiSpmi.Gpe = SpmiIntrDeviceInfo->Gpe; | |
| AcpiSpmi.PciDeviceFlag = SpmiIntrDeviceInfo->PciDeviceFlag; | |
| AcpiSpmi.GlobalSystemInterrupt = SpmiIntrDeviceInfo->GlobalSystemInterrupt; | |
| AcpiSpmi.DeviceId.Uid = SpmiIntrDeviceInfo->DeviceId; | |
| } else { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "INFO: SPMI: The platform does not provide interrupt and PCI device information.\n" | |
| )); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "Using default values (0) for the interrupt and PCI device information.\n" | |
| )); | |
| } | |
| /// Update IPMI specification version | |
| Status = IpmiGetDeviceId (&DeviceId); | |
| if (!EFI_ERROR (Status) && (DeviceId.CompletionCode == IPMI_COMP_CODE_NORMAL)) { | |
| AcpiSpmi.SpecificationRevision = DeviceId.SpecificationVersion & 0xF0; | |
| AcpiSpmi.SpecificationRevision |= (DeviceId.SpecificationVersion & 0xF) << 8; | |
| } | |
| AcpiSpmi.InterfaceType = SpmiInfo->InterfaceType; | |
| CopyMem ( | |
| &AcpiSpmi.BaseAddress, | |
| &SpmiInfo->BaseAddress, | |
| sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE) | |
| ); | |
| Status = AddAcpiHeader ( | |
| CfgMgrProtocol, | |
| This, | |
| (EFI_ACPI_DESCRIPTION_HEADER *)&AcpiSpmi, | |
| AcpiTableInfo, | |
| sizeof (EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_TABLE) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: SPMI: Failed to add ACPI header. Status = %r\n", | |
| Status | |
| )); | |
| } | |
| *Table = (EFI_ACPI_DESCRIPTION_HEADER *)&AcpiSpmi; | |
| return Status; | |
| } | |
| /** This macro defines the SPMI Table Generator revision. | |
| */ | |
| #define SPMI_GENERATOR_REVISION CREATE_REVISION (1, 0) | |
| /** The interface for the SPMI Table Generator. | |
| */ | |
| STATIC | |
| CONST | |
| ACPI_TABLE_GENERATOR SpmiGenerator = { | |
| // Generator ID | |
| CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSpmi), | |
| // Generator Description | |
| L"ACPI.STD.SPMI.GENERATOR", | |
| // ACPI Table Signature | |
| EFI_ACPI_6_5_SERVER_PLATFORM_MANAGEMENT_INTERFACE_TABLE_SIGNATURE, | |
| // ACPI Table Revision supported by this Generator | |
| EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_5_TABLE_REVISION, | |
| // Minimum supported ACPI Table Revision | |
| EFI_ACPI_SERVICE_PROCESSOR_MANAGEMENT_INTERFACE_5_TABLE_REVISION, | |
| // Creator ID | |
| TABLE_GENERATOR_CREATOR_ID, | |
| // Creator Revision | |
| SPMI_GENERATOR_REVISION, | |
| // Build Table function | |
| BuildSpmiTable, | |
| // Free Resource function | |
| NULL, | |
| // Extended build function not needed | |
| NULL, | |
| // Extended build function not implemented by the generator. | |
| // Hence extended free resource function is not required. | |
| 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 is registered. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_ALREADY_STARTED The Generator for the Table ID | |
| is already registered. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiSpmiLibConstructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = RegisterAcpiTableGenerator (&SpmiGenerator); | |
| DEBUG ((DEBUG_INFO, "SPMI: 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 is deregistered. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_NOT_FOUND The Generator is not registered. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiSpmiLibDestructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = DeregisterAcpiTableGenerator (&SpmiGenerator); | |
| DEBUG ((DEBUG_INFO, "SPMI: Deregister Generator. Status = %r\n", Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |