/** @file
|
IPMI Transport common layer driver
|
|
@copyright
|
Copyright 1999 - 2021 Intel Corporation. <BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
**/
|
|
#include "IpmiBmc.h"
|
|
EFI_STATUS
|
UpdateErrorStatus (
|
IN UINT8 BmcError,
|
IPMI_BMC_INSTANCE_DATA *IpmiInstance
|
)
|
/*++
|
|
Routine Description:
|
|
Check if the completion code is a Soft Error and increment the count. The count
|
is not updated if the BMC is in Force Update Mode.
|
|
Arguments:
|
|
BmcError - Completion code to check
|
IpmiInstance - BMC instance data
|
|
Returns:
|
|
EFI_SUCCESS - Status
|
|
--*/
|
{
|
UINT8 Errors[] = COMPLETION_CODES;
|
UINT16 CodeCount;
|
UINT8 i;
|
|
CodeCount = sizeof (Errors) / sizeof (Errors[0]);
|
for (i = 0; i < CodeCount; i++) {
|
if (BmcError == Errors[i]) {
|
//
|
// Don't change Bmc Status flag if the BMC is in Force Update Mode.
|
//
|
if (IpmiInstance->BmcStatus != BMC_UPDATE_IN_PROGRESS) {
|
IpmiInstance->BmcStatus = BMC_SOFTFAIL;
|
}
|
|
IpmiInstance->SoftErrorCount++;
|
break;
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
EFIAPI
|
IpmiSendCommandToBmc (
|
IN IPMI_TRANSPORT *This,
|
IN UINT8 NetFunction,
|
IN UINT8 Lun,
|
IN UINT8 Command,
|
IN UINT8 *CommandData,
|
IN UINT8 CommandDataSize,
|
IN OUT UINT8 *ResponseData,
|
IN OUT UINT8 *ResponseDataSize,
|
IN VOID *Context
|
)
|
/*++
|
|
Routine Description:
|
|
Send IPMI command to BMC
|
|
Arguments:
|
|
This - Pointer to IPMI protocol instance
|
NetFunction - Net Function of command to send
|
Lun - LUN of command to send
|
Command - IPMI command to send
|
CommandData - Pointer to command data buffer, if needed
|
CommandDataSize - Size of command data buffer
|
ResponseData - Pointer to response data buffer
|
ResponseDataSize - Pointer to response data buffer size
|
Context - Context
|
|
Returns:
|
|
EFI_INVALID_PARAMETER - One of the input values is bad
|
EFI_DEVICE_ERROR - IPMI command failed
|
EFI_BUFFER_TOO_SMALL - Response buffer is too small
|
EFI_UNSUPPORTED - Command is not supported by BMC
|
EFI_SUCCESS - Command completed successfully
|
|
--*/
|
{
|
IPMI_BMC_INSTANCE_DATA *IpmiInstance;
|
UINT8 DataSize;
|
EFI_STATUS Status;
|
IPMI_COMMAND *IpmiCommand;
|
IPMI_RESPONSE *IpmiResponse;
|
UINT8 RetryCnt = IPMI_SEND_COMMAND_MAX_RETRY;
|
UINT8 Index;
|
|
IpmiInstance = INSTANCE_FROM_SM_IPMI_BMC_THIS (This);
|
|
while (RetryCnt--) {
|
//
|
// The TempData buffer is used for both sending command data and receiving
|
// response data. Since the command format is different from the response
|
// format, the buffer is cast to both structure definitions.
|
//
|
IpmiCommand = (IPMI_COMMAND*) IpmiInstance->TempData;
|
IpmiResponse = (IPMI_RESPONSE*) IpmiInstance->TempData;
|
|
//
|
// Send IPMI command to BMC
|
//
|
IpmiCommand->Lun = Lun;
|
IpmiCommand->NetFunction = NetFunction;
|
IpmiCommand->Command = Command;
|
|
//
|
// Ensure that the buffer is valid before attempting to copy the command data
|
// buffer into the IpmiCommand structure.
|
//
|
if (CommandDataSize > 0) {
|
if (CommandData == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
CopyMem (
|
IpmiCommand->CommandData,
|
CommandData,
|
CommandDataSize
|
);
|
}
|
|
Status = SendDataToBmcPort (
|
IpmiInstance->KcsTimeoutPeriod,
|
IpmiInstance->IpmiIoBase,
|
Context,
|
(UINT8 *) IpmiCommand,
|
(CommandDataSize + IPMI_COMMAND_HEADER_SIZE)
|
);
|
|
if (Status != EFI_SUCCESS) {
|
IpmiInstance->BmcStatus = BMC_SOFTFAIL;
|
IpmiInstance->SoftErrorCount++;
|
return Status;
|
}
|
|
//
|
// Get Response to IPMI Command from BMC.
|
// Subtract 1 from DataSize so memory past the end of the buffer can't be written
|
//
|
DataSize = MAX_TEMP_DATA - 1;
|
Status = ReceiveBmcDataFromPort (
|
IpmiInstance->KcsTimeoutPeriod,
|
IpmiInstance->IpmiIoBase,
|
Context,
|
(UINT8 *) IpmiResponse,
|
&DataSize
|
);
|
|
if (Status != EFI_SUCCESS) {
|
IpmiInstance->BmcStatus = BMC_SOFTFAIL;
|
IpmiInstance->SoftErrorCount++;
|
return Status;
|
}
|
|
//
|
// If we got this far without any error codes, but the DataSize less than IPMI_RESPONSE_HEADER_SIZE, then the
|
// command response failed, so do not continue.
|
//
|
if (DataSize < IPMI_RESPONSE_HEADER_SIZE) {
|
return EFI_DEVICE_ERROR;
|
}
|
|
if ((IpmiResponse->CompletionCode != COMP_CODE_NORMAL) &&
|
(IpmiInstance->BmcStatus == BMC_UPDATE_IN_PROGRESS)) {
|
//
|
// If the completion code is not normal and the BMC is in Force Update
|
// mode, then update the error status and return EFI_UNSUPPORTED.
|
//
|
UpdateErrorStatus (
|
IpmiResponse->CompletionCode,
|
IpmiInstance
|
);
|
return EFI_UNSUPPORTED;
|
} else if (IpmiResponse->CompletionCode != COMP_CODE_NORMAL) {
|
//
|
// Otherwise if the BMC is in normal mode, but the completion code
|
// is not normal, then update the error status and return device error.
|
//
|
UpdateErrorStatus (
|
IpmiResponse->CompletionCode,
|
IpmiInstance
|
);
|
//
|
// Intel Server System Integrated Baseboard Management Controller (BMC) Firmware v0.62
|
// D4h C Insufficient privilege, in KCS channel this indicates KCS Policy Control Mode is Deny All.
|
// In authenticated channels this indicates invalid authentication/privilege.
|
//
|
if (IpmiResponse->CompletionCode == COMP_INSUFFICIENT_PRIVILEGE) {
|
return EFI_SECURITY_VIOLATION;
|
} else {
|
return EFI_DEVICE_ERROR;
|
}
|
}
|
|
//
|
// Verify the response data buffer passed in is big enough.
|
//
|
if ((DataSize - IPMI_RESPONSE_HEADER_SIZE) > *ResponseDataSize) {
|
//
|
//Verify the response data matched with the cmd sent.
|
//
|
if ((IpmiResponse->NetFunction != (NetFunction | 0x1)) || (IpmiResponse->Command != Command)) {
|
if (0 == RetryCnt) {
|
return EFI_DEVICE_ERROR;
|
} else {
|
continue;
|
}
|
}
|
return EFI_BUFFER_TOO_SMALL;
|
}
|
|
break;
|
}
|
//
|
// Copy data over to the response data buffer.
|
//
|
*ResponseDataSize = DataSize - IPMI_RESPONSE_HEADER_SIZE;
|
CopyMem (
|
ResponseData,
|
IpmiResponse->ResponseData,
|
*ResponseDataSize
|
);
|
|
//
|
// Add completion code in response data to meet the requirement of IPMI spec 2.0
|
//
|
*ResponseDataSize += 1; // Add one byte for Completion Code
|
for (Index = 1; Index < *ResponseDataSize; Index++) {
|
ResponseData [*ResponseDataSize - Index] = ResponseData [*ResponseDataSize - (Index + 1)];
|
}
|
ResponseData [0] = IpmiResponse->CompletionCode;
|
|
IpmiInstance->BmcStatus = BMC_OK;
|
return EFI_SUCCESS;
|
}
|
|
|
EFI_STATUS
|
EFIAPI
|
IpmiBmcStatus (
|
IN IPMI_TRANSPORT *This,
|
OUT BMC_STATUS *BmcStatus,
|
OUT SM_COM_ADDRESS *ComAddress,
|
IN VOID *Context
|
)
|
/*++
|
|
Routine Description:
|
|
Updates the BMC status and returns the Com Address
|
|
Arguments:
|
|
This - Pointer to IPMI protocol instance
|
BmcStatus - BMC status
|
ComAddress - Com Address
|
Context - Context
|
|
Returns:
|
|
EFI_SUCCESS - Success
|
|
--*/
|
{
|
IPMI_BMC_INSTANCE_DATA *IpmiInstance;
|
|
IpmiInstance = INSTANCE_FROM_SM_IPMI_BMC_THIS (This);
|
|
if ((IpmiInstance->BmcStatus == BMC_SOFTFAIL) && (IpmiInstance->SoftErrorCount >= MAX_SOFT_COUNT)) {
|
IpmiInstance->BmcStatus = BMC_HARDFAIL;
|
}
|
|
*BmcStatus = IpmiInstance->BmcStatus;
|
ComAddress->ChannelType = SmBmc;
|
ComAddress->Address.BmcAddress.LunAddress = 0x0;
|
ComAddress->Address.BmcAddress.SlaveAddress = IpmiInstance->SlaveAddress;
|
ComAddress->Address.BmcAddress.ChannelAddress = 0x0;
|
|
return EFI_SUCCESS;
|
}
|