/** @file
IPMI Manageability Protocol common file.
Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include "IpmiProtocolCommon.h"
/**
This functions setup the IPMI transport hardware information according
to the specification of transport token acquired from transport library.
@param[in] TransportToken The transport interface.
@param[out] HardwareInformation Pointer to receive the hardware information.
@retval EFI_SUCCESS Hardware information is returned in HardwareInformation.
Caller must free the memory allocated for HardwareInformation
once it doesn't need it.
@retval EFI_UNSUPPORTED No hardware information for the specification specified
in the transport token.
#retval EFI_OUT_OF_RESOURCES Not enough memory for MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO.
**/
EFI_STATUS
SetupIpmiTransportHardwareInformation (
IN MANAGEABILITY_TRANSPORT_TOKEN *TransportToken,
OUT MANAGEABILITY_TRANSPORT_HARDWARE_INFORMATION *HardwareInformation
)
{
MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *KcsHardwareInfo;
KcsHardwareInfo = AllocatePool (sizeof (MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO));
if (KcsHardwareInfo == NULL) {
DEBUG ((DEBUG_ERROR, "%a: Not enough memory for MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO.\n", __FUNCTION__));
return EFI_OUT_OF_RESOURCES;
}
if (CompareGuid (&gManageabilityTransportKcsGuid, TransportToken->Transport->ManageabilityTransportSpecification)) {
// This is KCS transport interface.
KcsHardwareInfo->MemoryMap = MANAGEABILITY_TRANSPORT_KCS_IO_MAP_IO;
KcsHardwareInfo->IoBaseAddress.IoAddress16 = IPMI_KCS_BASE_ADDRESS;
KcsHardwareInfo->IoDataInAddress.IoAddress16 = IPMI_KCS_REG_DATA_IN;
KcsHardwareInfo->IoDataOutAddress.IoAddress16 = IPMI_KCS_REG_DATA_OUT;
KcsHardwareInfo->IoCommandAddress.IoAddress16 = IPMI_KCS_REG_COMMAND;
KcsHardwareInfo->IoStatusAddress.IoAddress16 = IPMI_KCS_REG_STATUS;
HardwareInformation->Kcs = KcsHardwareInfo;
return EFI_SUCCESS;
} else {
DEBUG ((DEBUG_ERROR, "%a: No implementation of setting hardware information.", __FUNCTION__));
ASSERT (FALSE);
}
return EFI_UNSUPPORTED;
}
/**
This functions setup the final header/body/trailer packets for
the acquired transport interface.
@param[in] TransportToken The transport interface.
@param[in] NetFunction IPMI function.
@param[in] Command IPMI command.
@param[out] PacketHeader The pointer to receive header of request.
@param[in, out] PacketBody The request body.
When IN, it is the caller's request body.
When OUT and NULL, the request body is not
changed.
When OUT and non-NULL, the request body is
changed to conform the transport interface.
@param[in, out] PacketBodySize The request body size.
When OUT and non-zero, it is the new data
length of request body.
When OUT and zero, the request body is unchanged.
@param[out] PacketTrailer The pointer to receive trailer of request.
@retval EFI_SUCCESS Request packet is returned.
@retval EFI_UNSUPPORTED Request packet is not returned because
the unsupported transport interface.
**/
EFI_STATUS
SetupIpmiRequestTransportPacket (
IN MANAGEABILITY_TRANSPORT_TOKEN *TransportToken,
IN UINT8 NetFunction,
IN UINT8 Command,
OUT MANAGEABILITY_TRANSPORT_HEADER *PacketHeader OPTIONAL,
IN OUT UINT8 **PacketBody OPTIONAL,
IN OUT UINT32 *PacketBodySize OPTIONAL,
OUT MANAGEABILITY_TRANSPORT_TRAILER *PacketTrailer OPTIONAL
)
{
MANAGEABILITY_IPMI_TRANSPORT_HEADER *IpmiHeader;
if (CompareGuid (&gManageabilityTransportKcsGuid, TransportToken->Transport->ManageabilityTransportSpecification)) {
// This is KCS transport interface.
IpmiHeader = AllocateZeroPool (sizeof (MANAGEABILITY_IPMI_TRANSPORT_HEADER));
if (IpmiHeader == NULL) {
return EFI_OUT_OF_RESOURCES;
}
IpmiHeader->Command = Command;
IpmiHeader->Lun = 0;
IpmiHeader->NetFn = NetFunction;
if (PacketHeader != NULL) {
*PacketHeader = (MANAGEABILITY_TRANSPORT_HEADER *)IpmiHeader;
}
if (PacketTrailer != NULL) {
*PacketTrailer = NULL;
}
if (PacketBody != NULL) {
*PacketBody = NULL;
}
if (PacketBodySize != NULL) {
*PacketBodySize = 0;
}
} else {
DEBUG ((DEBUG_ERROR, "%a: No implementation of building up packet.", __FUNCTION__));
ASSERT (FALSE);
}
return EFI_SUCCESS;
}
/**
Common code to submit IPMI commands
@param[in] TransportToken TRansport token.
@param[in] NetFunction Net function of the command.
@param[in] Command IPMI Command.
@param[in] RequestData Command Request Data.
@param[in] RequestDataSize Size of Command Request Data.
@param[out] ResponseData Command Response Data. The completion code is the first byte of response data.
@param[in, out] ResponseDataSize Size of Command Response Data.
@retval EFI_SUCCESS The command byte stream was successfully submit to the device and a response was successfully received.
@retval EFI_NOT_FOUND The command was not successfully sent to the device or a response was not successfully received from the device.
@retval EFI_NOT_READY Ipmi Device is not ready for Ipmi command access.
@retval EFI_DEVICE_ERROR Ipmi Device hardware error.
@retval EFI_TIMEOUT The command time out.
@retval EFI_UNSUPPORTED The command was not successfully sent to the device.
@retval EFI_OUT_OF_RESOURCES The resource allocation is out of resource or data size error.
**/
EFI_STATUS
CommonIpmiSubmitCommand (
IN MANAGEABILITY_TRANSPORT_TOKEN *TransportToken,
IN UINT8 NetFunction,
IN UINT8 Command,
IN UINT8 *RequestData OPTIONAL,
IN UINT32 RequestDataSize,
OUT UINT8 *ResponseData OPTIONAL,
IN OUT UINT32 *ResponseDataSize OPTIONAL
)
{
EFI_STATUS Status;
UINT8 *ThisRequestData;
UINT32 ThisRequestDataSize;
MANAGEABILITY_TRANSFER_TOKEN TransferToken;
MANAGEABILITY_TRANSPORT_HEADER IpmiTransportHeader;
MANAGEABILITY_TRANSPORT_TRAILER IpmiTransportTrailer;
MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS TransportAdditionalStatus;
if (TransportToken == NULL) {
DEBUG ((DEBUG_ERROR, "%a: No transport toke for IPMI\n", __FUNCTION__));
return EFI_UNSUPPORTED;
}
Status = TransportToken->Transport->Function.Version1_0->TransportStatus (
TransportToken,
&TransportAdditionalStatus
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Transport for IPMI has problem - (%r)\n", __FUNCTION__, Status));
return Status;
}
ThisRequestData = RequestData;
ThisRequestDataSize = RequestDataSize;
IpmiTransportHeader = NULL;
IpmiTransportTrailer = NULL;
Status = SetupIpmiRequestTransportPacket (
TransportToken,
NetFunction,
Command,
&IpmiTransportHeader,
&ThisRequestData,
&ThisRequestDataSize,
&IpmiTransportTrailer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Fail to build packets - (%r)\n", __FUNCTION__, Status));
return Status;
}
ZeroMem (&TransferToken, sizeof (MANAGEABILITY_TRANSFER_TOKEN));
TransferToken.TransmitHeader = IpmiTransportHeader;
TransferToken.TransmitTrailer = IpmiTransportTrailer;
// Transmit packet.
if ((ThisRequestData == NULL) || (ThisRequestDataSize == 0)) {
// Transmit parameter were not changed by SetupIpmiRequestTransportPacket().
TransferToken.TransmitPackage.TransmitPayload = RequestData;
TransferToken.TransmitPackage.TransmitSizeInByte = RequestDataSize;
} else {
TransferToken.TransmitPackage.TransmitPayload = ThisRequestData;
TransferToken.TransmitPackage.TransmitSizeInByte = ThisRequestDataSize;
}
TransferToken.TransmitPackage.TransmitTimeoutInMillisecond = MANAGEABILITY_TRANSPORT_NO_TIMEOUT;
// Receive packet.
TransferToken.ReceivePackage.ReceiveBuffer = ResponseData;
TransferToken.ReceivePackage.ReceiveSizeInByte = *ResponseDataSize;
TransferToken.ReceivePackage.TransmitTimeoutInMillisecond = MANAGEABILITY_TRANSPORT_NO_TIMEOUT;
TransportToken->Transport->Function.Version1_0->TransportTransmitReceive (
TransportToken,
&TransferToken
);
if (IpmiTransportHeader != NULL) {
FreePool ((VOID *)IpmiTransportHeader);
}
if (IpmiTransportTrailer != NULL) {
FreePool ((VOID *)IpmiTransportTrailer);
}
if (ThisRequestData != NULL) {
FreePool ((VOID *)ThisRequestData);
}
// Return transfer status.
//
Status = TransferToken.TransferStatus;
TransportAdditionalStatus = TransferToken.TransportAdditionalStatus;
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to send IPMI command.\n", __FUNCTION__));
return Status;
}
if (ResponseDataSize != NULL) {
*ResponseDataSize = TransferToken.ReceivePackage.ReceiveSizeInByte;
}
return Status;
}