/** @file Manageability Transport Helper Library Copyright (C) 2023-2026 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include // // BaseManageabilityTransportHelper is used by PEI, DXE and SMM. // Make sure the global variables added here should be unchangeable. // MANAGEABILITY_SPECIFICATION_NAME ManageabilitySpecNameTable[] = { { &gManageabilityTransportKcsGuid, L"KCS" }, { &gManageabilityTransportSmbusI2cGuid, L"SMBUS I2C" }, { &gManageabilityTransportPciVdmGuid, L"PCI VDM" }, { &gManageabilityTransportSerialGuid, L"SERIAL" }, { &gManageabilityTransportMctpGuid, L"MCTP" }, { &gManageabilityProtocolIpmiGuid, L"IPMI" }, { &gManageabilityProtocolMctpGuid, L"MCTP" }, { &gManageabilityProtocolPldmGuid, L"PLDM" } }; UINT16 mManageabilitySpecNum = sizeof (ManageabilitySpecNameTable)/ sizeof (MANAGEABILITY_SPECIFICATION_NAME); /** Helper function returns the human readable name of Manageability specification. @param[in] SpecificationGuid The Manageability specification GUID @retval !NULL Human readable name is returned; @retval NULL No string found, the given Manageability specification is not supported. **/ CHAR16 * EFIAPI HelperManageabilitySpecName ( IN EFI_GUID *SpecificationGuid ) { UINT16 Index; MANAGEABILITY_SPECIFICATION_NAME *ThisSpec; if (mManageabilitySpecNum == 0) { return NULL; } if ((SpecificationGuid == NULL) || IsZeroGuid (SpecificationGuid)) { DEBUG ((DEBUG_ERROR, "%a: Improper input GUIDs, could be NULL or zero GUID.\n", __func__)); return NULL; } ThisSpec = ManageabilitySpecNameTable; for (Index = 0; Index < mManageabilitySpecNum; Index++) { if (CompareGuid ( SpecificationGuid, ThisSpec->SpecificationGuid )) { return ThisSpec->SpecificationName; } ThisSpec++; } return NULL; } /** Helper function to check if the Manageability specification is supported by transport interface or not. @param[in] TransportGuid GUID of the transport interface. @param[in] SupportedManageabilityProtocolArray The Manageability protocols supported by the transport interface. @param[in] NumberOfSupportedProtocolInArray Number of protocols in the array. @param[in] ManageabilityProtocolToCheck The Manageability specification to check. @retval EFI_SUCCESS Token is created successfully. @retval EFI_INVALID_PARAMETER Either NumberOfSupportedProtocolInArray = 0 or SupportedManageabilityProtocolArray = NULL. @retval EFI_UNSUPPORTED Out of resource to create a new transport session. Otherwise Other errors. **/ EFI_STATUS EFIAPI HelperManageabilityCheckSupportedSpec ( IN EFI_GUID *TransportGuid, IN EFI_GUID **SupportedManageabilityProtocolArray, IN UINT8 NumberOfSupportedProtocolInArray, IN EFI_GUID *ManageabilityProtocolToCheck ) { UINT16 Index; EFI_GUID **ThisSpecGuid; if ((NumberOfSupportedProtocolInArray == 0) || (SupportedManageabilityProtocolArray == NULL)) { return EFI_INVALID_PARAMETER; } if ((TransportGuid == NULL) || IsZeroGuid (TransportGuid) || (ManageabilityProtocolToCheck == NULL) || IsZeroGuid (ManageabilityProtocolToCheck) ) { DEBUG ((DEBUG_ERROR, "%a: Improper input GUIDs, could be NULL or zero GUID.\n", __func__)); return EFI_INVALID_PARAMETER; } ThisSpecGuid = SupportedManageabilityProtocolArray; for (Index = 0; Index < NumberOfSupportedProtocolInArray; Index++) { if (CompareGuid ( *ThisSpecGuid, ManageabilityProtocolToCheck )) { DEBUG (( DEBUG_MANAGEABILITY_INFO, "%a: Transport interface %s supports %s manageability specification.\n", __func__, HelperManageabilitySpecName (TransportGuid), HelperManageabilitySpecName (ManageabilityProtocolToCheck) )); return EFI_SUCCESS; } ThisSpecGuid++; } DEBUG (( DEBUG_ERROR, "%a: Transport interface %s doesn't support %s manageability specification.\n", __func__, HelperManageabilitySpecName (TransportGuid), HelperManageabilitySpecName (ManageabilityProtocolToCheck) )); return EFI_UNSUPPORTED; } /** Helper function to acquire the Manageability transport token. @param[in] ManageabilityProtocolSpec The Manageability protocol specification. @param[out] TransportToken Pointer to receive Manageability transport token. @retval EFI_SUCCESS Token is created successfully. @retval EFI_OUT_OF_RESOURCES Out of resource to create a new transport session. @retval EFI_UNSUPPORTED Token is created successfully. @retval EFI_INVALID_PARAMETER Input parameter is not valid. Otherwise Other errors. **/ EFI_STATUS EFIAPI HelperAcquireManageabilityTransport ( IN EFI_GUID *ManageabilityProtocolSpec, OUT MANAGEABILITY_TRANSPORT_TOKEN **TransportToken ) { EFI_STATUS Status; CHAR16 *ManageabilityProtocolName; CHAR16 *ManageabilityTransportName; DEBUG ((DEBUG_MANAGEABILITY_INFO, "%a: Entry\n", __func__)); if ((TransportToken == NULL) || (ManageabilityProtocolSpec == NULL)) { DEBUG ((DEBUG_ERROR, "%a: One of the required input parameters is NULL.\n", __func__)); return EFI_INVALID_PARAMETER; } *TransportToken = NULL; ManageabilityProtocolName = HelperManageabilitySpecName (ManageabilityProtocolSpec); if (ManageabilityProtocolName == NULL) { DEBUG ((DEBUG_ERROR, "%a: Unsupported Manageability Protocol Specification.\n", __func__)); return EFI_UNSUPPORTED; } DEBUG ((DEBUG_MANAGEABILITY_INFO, " Manageability protocol %s is going to acquire transport interface token...\n", ManageabilityProtocolName)); Status = AcquireTransportSession (ManageabilityProtocolSpec, TransportToken); if (Status == EFI_UNSUPPORTED) { DEBUG ((DEBUG_ERROR, "%a: No supported transport interface for %s packet.\n", __func__, ManageabilityProtocolName)); return Status; } if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Fail to acquire Manageability transport token for %s (%r).\n", __func__, ManageabilityProtocolName, Status )); return Status; } ManageabilityTransportName = HelperManageabilitySpecName ((*TransportToken)->Transport->ManageabilityTransportSpecification); if (ManageabilityTransportName == NULL) { DEBUG ((DEBUG_ERROR, "%a: Unsupported Manageability Transport Interface Specification\n", __func__)); return EFI_UNSUPPORTED; } DEBUG ((DEBUG_MANAGEABILITY_INFO, "%a: This is the transfer session for %s over %s\n", __func__, ManageabilityProtocolName, ManageabilityTransportName)); return Status; } /** Helper function to initial the transport interface. @param[in] TransportToken Transport token. @param[in] HardwareInfo Optional hardware information of transport interface. @param[out] TransportAdditionalStatus Transport additional status. @retval EFI_SUCCESS Transport interface is initiated successfully. @retval EFI_DEVICE_ERROR The transport interface has problems @retval EFI_INVALID_PARAMETER INput parameter is not valid. Otherwise Other errors. **/ EFI_STATUS EFIAPI HelperInitManageabilityTransport ( IN MANAGEABILITY_TRANSPORT_TOKEN *TransportToken, IN MANAGEABILITY_TRANSPORT_HARDWARE_INFORMATION HardwareInfo OPTIONAL, OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS *TransportAdditionalStatus OPTIONAL ) { EFI_STATUS Status; if (TransportToken == NULL) { DEBUG ((DEBUG_ERROR, "%a: TransportToken is invalid.\n", __func__)); return EFI_INVALID_PARAMETER; } // Initial transport interface. Status = TransportToken->Transport->Function.Version1_0->TransportInit (TransportToken, HardwareInfo); if ((Status != EFI_SUCCESS) && (Status != EFI_ALREADY_STARTED)) { if (Status == EFI_DEVICE_ERROR) { // Try to reset the transport and initialize it again. Status = TransportToken->Transport->Function.Version1_0->TransportReset ( TransportToken, TransportAdditionalStatus ); if (EFI_ERROR (Status)) { if (Status == EFI_UNSUPPORTED) { DEBUG ((DEBUG_ERROR, "%a: Transport interface doesn't have reset capability.\n", __func__)); } else { DEBUG ((DEBUG_ERROR, "%a: Fail to reset transport interface (%r).\n", __func__, Status)); } Status = EFI_DEVICE_ERROR; } else { Status = TransportToken->Transport->Function.Version1_0->TransportInit (TransportToken, HardwareInfo); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Transport interface is not able to use after the reset (%r).\n", __func__, Status)); } } } else { DEBUG ((DEBUG_ERROR, "%a: Transport interface is not able to use (%r).\n", __func__, Status)); } } return Status; } /** This function generates CRC8 with given polynomial. @param[in] Polynomial Polynomial in 8-bit. @param[in] CrcInitialValue CRC initial value. @param[in] BufferStart Pointer to buffer starts the CRC calculation. @param[in] BufferSize Size of buffer. @retval UINT8 CRC value. **/ UINT8 EFIAPI HelperManageabilityGenerateCrc8 ( IN UINT8 Polynomial, IN UINT8 CrcInitialValue, IN UINT8 *BufferStart, IN UINT32 BufferSize ) { UINT8 BitIndex; UINT32 BufferIndex; BufferIndex = 0; while (BufferIndex < BufferSize) { CrcInitialValue = CrcInitialValue ^ *(BufferStart + BufferIndex); BufferIndex++; for (BitIndex = 0; BitIndex < 8; BitIndex++) { if ((CrcInitialValue & 0x80) != 0) { CrcInitialValue = (CrcInitialValue << 1) ^ Polynomial; } else { CrcInitialValue <<= 1; } } } return CrcInitialValue; } /** This function splits payload into multiple packages according to the given transport interface Maximum Transfer Unit (MTU). @param[in] PreambleSize The additional data size precedes each package. @param[in] PostambleSize The additional data size succeeds each package. @param[in] Payload Pointer to payload. @param[in] PayloadSize Payload size in byte. @param[in] MaximumTransferUnit MTU of transport interface. @param[out] MultiplePackages Pointer to receive MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES structure. Caller has to free the memory allocated for MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES. @retval EFI_SUCCESS MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES structure is returned successfully. @retval EFI_OUT_OF_RESOURCE Not enough resource to create MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES structure. **/ EFI_STATUS EFIAPI HelperManageabilitySplitPayload ( IN UINT16 PreambleSize, IN UINT16 PostambleSize, IN UINT8 *Payload, IN UINT32 PayloadSize, IN UINT32 MaximumTransferUnit, OUT MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES **MultiplePackages ) { UINT16 NumberOfPackages; UINT16 IndexOfPackage; UINT32 PackagePayloadSize; UINT32 TotalPayloadRemaining; MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES *ThisMultiplePackages; MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR *ThisPackage; if ((INT16)(MaximumTransferUnit - PreambleSize - PostambleSize) < 0) { DEBUG (( DEBUG_ERROR, "%a: (Preamble 0x%x + PostambleSize 0x%x) is greater than MaximumTransferUnit 0x%x.\n", __func__, PreambleSize, PostambleSize, MaximumTransferUnit )); return EFI_INVALID_PARAMETER; } PackagePayloadSize = MaximumTransferUnit -PreambleSize - PostambleSize; NumberOfPackages = (UINT16)((PayloadSize + (PackagePayloadSize - 1)) / PackagePayloadSize); ThisMultiplePackages = (MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES *)AllocateZeroPool ( sizeof (MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES) + sizeof (MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR) * NumberOfPackages ); if (ThisMultiplePackages == NULL) { DEBUG ((DEBUG_ERROR, "%a: Not enough memory for MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES\n", __func__)); return EFI_OUT_OF_RESOURCES; } ThisMultiplePackages->NumberOfPackages = NumberOfPackages; ThisPackage = (MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR *)(ThisMultiplePackages + 1); TotalPayloadRemaining = PayloadSize; for (IndexOfPackage = 0; IndexOfPackage < NumberOfPackages; IndexOfPackage++) { ThisPackage->PayloadPointer = Payload + (IndexOfPackage * PackagePayloadSize); ThisPackage->PayloadSize = MIN (TotalPayloadRemaining, PackagePayloadSize); TotalPayloadRemaining -= ThisPackage->PayloadSize; ThisPackage++; } if (TotalPayloadRemaining != 0) { DEBUG ((DEBUG_ERROR, "%a: Error processing multiple packages (TotalPayloadRemaining != 0)\n", __func__)); FreePool (ThisMultiplePackages); return EFI_INVALID_PARAMETER; } *MultiplePackages = ThisMultiplePackages; return EFI_SUCCESS; } /** Print out manageability transmit payload to the debug output device. @param[in] Payload Payload to print. @param[in] PayloadSize Payload size. **/ VOID EFIAPI HelperManageabilityPayLoadDebugPrint ( IN VOID *Payload, IN UINT32 PayloadSize ) { UINTN Block; UINTN BlockSize; UINTN RowSize; UINTN RemainingBytes; UINTN BytesPrinted; RemainingBytes = PayloadSize; BlockSize = 256; RowSize = 16; for (Block = 0, BytesPrinted = 0; RemainingBytes > 0;) { UINTN BlockBase; UINTN Row; UINTN RowsToPrint; UINTN Column; Block = BytesPrinted / BlockSize; BlockBase = Block * BlockSize; if (BytesPrinted % BlockSize == 0) { DEBUG ((DEBUG_MANAGEABILITY_INFO, "======== Manageability Payload %04xH - %04xH =========\n", BlockBase, BlockBase + MIN (RemainingBytes, BlockSize) - 1)); DEBUG ((DEBUG_MANAGEABILITY_INFO, " ")); for (Column = 0; Column < RowSize; Column++) { DEBUG ((DEBUG_MANAGEABILITY_INFO, "%02x ", Column)); } DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n -----------------------------------------------\n")); } if (RemainingBytes >= BlockSize) { RowsToPrint = BlockSize / RowSize; } else { RowsToPrint = RemainingBytes / RowSize + 1; } for (Row = 0; Row < RowsToPrint; Row++) { DEBUG ((DEBUG_MANAGEABILITY_INFO, "%04x | ", BlockBase + Row * RowSize)); for (Column = 0; Column < MIN (RemainingBytes, RowSize); Column++) { DEBUG ((DEBUG_MANAGEABILITY_INFO, "%02x ", *((UINT8 *)Payload + BlockBase + Row * RowSize + Column))); } RemainingBytes -= Column; BytesPrinted += Column; DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n")); } DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n")); } } /** Prints a debug message and manageability payload to the debug output device. @param[in] Payload Payload to print. @param[in] PayloadSize Payload size. @param[in] Format The format string for the debug message to print. @param[in] ... The variable argument list whose contents are accessed based on the format string specified by Format. **/ VOID EFIAPI HelperManageabilityDebugPrint ( IN VOID *Payload, IN UINT32 PayloadSize, IN CONST CHAR8 *Format, ... ) { VA_LIST Marker; VA_START (Marker, Format); DEBUG ((DEBUG_MANAGEABILITY_INFO, "Manageability Transmission: ")); DebugVPrint ((UINTN)DEBUG_MANAGEABILITY_INFO, Format, Marker); HelperManageabilityPayLoadDebugPrint (Payload, PayloadSize); VA_END (Marker); }