[edk2-devel] [edk2-platforms] [PATCH v1 5/9] IpmiFeaturePkg: Add GenericIpmi PEIM

Nate DeSimone nathaniel.l.desimone at intel.com
Tue Mar 2 02:28:00 UTC 2021


From: Isaac Oram <isaac.w.oram at intel.com>

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3242

Adds the PEI version of the generic
IPMI transport driver.

Cc: Sai Chaganty <rangasai.v.chaganty at intel.com>
Cc: Liming Gao <gaoliming at byosoft.com.cn>
Cc: Michael Kubacki <michael.kubacki at microsoft.com>
Signed-off-by: Isaac Oram <isaac.w.oram at intel.com>
Co-authored-by: Nate DeSimone <nathaniel.l.desimone at intel.com>
---
 .../GenericIpmi/Pei/PeiGenericIpmi.c          | 362 ++++++++++++++++++
 .../GenericIpmi/Pei/PeiGenericIpmi.h          | 138 +++++++
 .../GenericIpmi/Pei/PeiGenericIpmi.inf        |  58 +++
 .../GenericIpmi/Pei/PeiIpmiBmc.c              | 277 ++++++++++++++
 .../GenericIpmi/Pei/PeiIpmiBmc.h              |  38 ++
 .../GenericIpmi/Pei/PeiIpmiBmcDef.h           | 156 ++++++++
 6 files changed, 1029 insertions(+)
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.c
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.h
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.inf
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.c
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.h
 create mode 100644 Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmcDef.h

diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.c b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.c
new file mode 100644
index 0000000000..31f613925d
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.c
@@ -0,0 +1,362 @@
+/** @file
+  Generic IPMI stack during PEI phase
+
+  @copyright
+  Copyright 2017 - 2021 Intel Corporation. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/Ipmi.h>
+#include "PeiGenericIpmi.h"
+#include <Library/ReportStatusCodeLib.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Function Implementations
+//
+
+/*****************************************************************************
+ @brief
+  Internal function
+
+ @param[in] PeiServices          General purpose services available to every PEIM.
+
+ @retval EFI_SUCCESS             Always return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+PeiInitializeIpmiKcsPhysicalLayer (
+  IN CONST EFI_PEI_SERVICES             **PeiServices
+  )
+{
+  EFI_STATUS                       Status;
+  PEI_IPMI_BMC_INSTANCE_DATA       *mIpmiInstance;
+
+  mIpmiInstance = NULL;
+
+  //
+  // Send Pre-Boot signal to BMC
+  //
+  if (PcdGetBool (PcdSignalPreBootToBmc)) {
+    Status = SendPreBootSignaltoBmc (PeiServices);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  //
+  // Enable OEM specific southbridge SIO KCS I/O address range 0xCA0 to 0xCAF at here
+  // if the the I/O address range has not been enabled.
+  //
+
+  mIpmiInstance = AllocateZeroPool (sizeof (PEI_IPMI_BMC_INSTANCE_DATA));
+  if (mIpmiInstance == NULL) {
+    DEBUG ((EFI_D_ERROR,"IPMI Peim:EFI_OUT_OF_RESOURCES of memory allocation\n"));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Calibrate TSC Counter.  Stall for 10ms, then multiply the resulting number of
+  // ticks in that period by 100 to get the number of ticks in a 1 second timeout
+  //
+  DEBUG ((DEBUG_INFO,"IPMI Peim:IPMI STACK Initialization\n"));
+  mIpmiInstance->KcsTimeoutPeriod = (BMC_KCS_TIMEOUT_PEI *1000*1000) / KCS_DELAY_UNIT_PEI;
+  DEBUG ((EFI_D_INFO,"IPMI Peim:KcsTimeoutPeriod = 0x%x\n", mIpmiInstance->KcsTimeoutPeriod));
+
+  //
+  // Initialize IPMI IO Base.
+  //
+  mIpmiInstance->IpmiIoBase                         = PcdGet16 (PcdIpmiIoBaseAddress);
+  DEBUG ((EFI_D_INFO,"IPMI Peim:IpmiIoBase=0x%x\n",mIpmiInstance->IpmiIoBase));
+  mIpmiInstance->Signature                          = SM_IPMI_BMC_SIGNATURE;
+  mIpmiInstance->SlaveAddress                       = BMC_SLAVE_ADDRESS;
+  mIpmiInstance->BmcStatus                          = BMC_NOTREADY;
+  mIpmiInstance->IpmiTransportPpi.IpmiSubmitCommand = PeiIpmiSendCommand;
+  mIpmiInstance->IpmiTransportPpi.GetBmcStatus      = PeiGetIpmiBmcStatus;
+
+  mIpmiInstance->PeiIpmiBmcDataDesc.Flags         = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;
+  mIpmiInstance->PeiIpmiBmcDataDesc.Guid          = &gPeiIpmiTransportPpiGuid;
+  mIpmiInstance->PeiIpmiBmcDataDesc.Ppi           = &mIpmiInstance->IpmiTransportPpi;
+
+  //
+  // Get the Device ID and check if the system is in Force Update mode.
+  //
+  Status = GetDeviceId (mIpmiInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR,"IPMI Peim:Get BMC Device Id Failed. Status=%r\n",Status));
+  }
+
+  //
+  // Do not continue initialization if the BMC is in Force Update Mode.
+  //
+  if (mIpmiInstance->BmcStatus == BMC_UPDATE_IN_PROGRESS || mIpmiInstance->BmcStatus == BMC_HARDFAIL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Just produce PPI
+  //
+  Status = PeiServicesInstallPpi (&mIpmiInstance->PeiIpmiBmcDataDesc);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*****************************************************************************
+ @bref
+  PRE-BOOT signal will be sent in very early PEI phase, to enable necessary KCS access for host boot.
+
+  @param[in] PeiServices          General purpose services available to every PEIM.
+
+  @retval EFI_SUCCESS   Indicates that the signal is sent successfully.
+**/
+EFI_STATUS
+SendPreBootSignaltoBmc (
+  IN CONST EFI_PEI_SERVICES             **PeiServices
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_PEI_CPU_IO_PPI                *CpuIoPpi;
+  UINT32                            ProvisionPort = 0;
+  UINT8                             PreBoot = 0;
+
+  //
+  // Locate CpuIo service
+  //
+  CpuIoPpi = (**PeiServices).CpuIo;
+  ProvisionPort = PcdGet32 (PcdSioMailboxBaseAddress) + MBXDAT_B;
+  PreBoot = 0x01;// PRE-BOOT
+
+  Status = CpuIoPpi->Io.Write (
+                          PeiServices,
+                          CpuIoPpi,
+                          EfiPeiCpuIoWidthUint8,
+                          ProvisionPort,
+                          1,
+                          &PreBoot
+                          );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "SendPreBootSignaltoBmc () Write PRE-BOOT Status=%r\n", Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*****************************************************************************
+ @bref
+  The entry point of the Ipmi PEIM. Instals Ipmi PPI interface.
+
+  @param  FileHandle  Handle of the file being invoked.
+  @param  PeiServices Describes the list of possible PEI Services.
+
+  @retval EFI_SUCCESS   Indicates that Ipmi initialization completed successfully.
+**/
+EFI_STATUS
+PeimIpmiInterfaceInit (
+  IN       EFI_PEI_FILE_HANDLE  FileHandle,
+  IN CONST EFI_PEI_SERVICES     **PeiServices
+  )
+{
+  EFI_STATUS  Status;
+
+
+  //
+  // Performing Ipmi KCS physical layer initialization
+  //
+  Status = PeiInitializeIpmiKcsPhysicalLayer (PeiServices);
+
+  return EFI_SUCCESS;
+} // PeimIpmiInterfaceInit()
+
+
+EFI_STATUS
+PeiIpmiSendCommand (
+  IN      PEI_IPMI_TRANSPORT_PPI       *This,
+  IN      UINT8                        NetFunction,
+  IN      UINT8                        Lun,
+  IN      UINT8                        Command,
+  IN      UINT8                        *CommandData,
+  IN      UINT32                       CommandDataSize,
+  IN OUT  UINT8                        *ResponseData,
+  IN OUT  UINT32                       *ResponseDataSize
+  )
+/*++
+
+Routine Description:
+
+  Send Ipmi Command in the right mode: HECI or KCS,  to the
+  appropiate device, ME or 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
+
+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
+
+--*/
+{
+  //
+  // This Will be unchanged ( BMC/KCS style )
+  //
+  return PeiIpmiSendCommandToBmc (
+           This,
+           NetFunction,
+           Lun,
+           Command,
+           CommandData,
+           (UINT8) CommandDataSize,
+           ResponseData,
+           (UINT8 *) ResponseDataSize,
+           NULL
+           );
+} // IpmiSendCommand()
+
+EFI_STATUS
+PeiGetIpmiBmcStatus (
+  IN      PEI_IPMI_TRANSPORT_PPI       *This,
+  OUT BMC_STATUS                       *BmcStatus,
+  OUT SM_COM_ADDRESS                   *ComAddress
+  )
+/*++
+
+Routine Description:
+
+  Updates the BMC status and returns the Com Address
+
+Arguments:
+
+  This        - Pointer to IPMI protocol instance
+  BmcStatus   - BMC status
+  ComAddress  - Com Address
+
+Returns:
+
+  EFI_SUCCESS - Success
+
+--*/
+{
+  return PeiIpmiBmcStatus (
+           This,
+           BmcStatus,
+           ComAddress,
+           NULL
+           );
+}
+
+
+EFI_STATUS
+GetDeviceId (
+  IN      PEI_IPMI_BMC_INSTANCE_DATA   *mIpmiInstance
+  )
+/*++
+
+Routine Description:
+  Execute the Get Device ID command to determine whether or not the BMC is in Force Update
+  Mode.  If it is, then report it to the error manager.
+
+Arguments:
+  mIpmiInstance   - Data structure describing BMC variables and used for sending commands
+  StatusCodeValue - An array used to accumulate error codes for later reporting.
+  ErrorCount      - Counter used to keep track of error codes in StatusCodeValue
+
+Returns:
+  Status
+
+--*/
+{
+  EFI_STATUS          Status;
+  UINT32              DataSize;
+  SM_CTRL_INFO        *pBmcInfo;
+  UINTN               Retries;
+
+  //
+  // Set up a loop to retry for up to PcdIpmiBmcReadyDelayTimer seconds. Calculate retries not timeout
+  // so that in case KCS is not enabled and IpmiSendCommand() returns
+  // immediately we will not wait all the PcdIpmiBmcReadyDelayTimer seconds.
+  //
+  Retries = PcdGet8 (PcdIpmiBmcReadyDelayTimer);
+  //
+  // Get the device ID information for the BMC.
+  //
+  DataSize = sizeof (mIpmiInstance->TempData);
+  while (EFI_ERROR (Status = PeiIpmiSendCommand (
+                               &mIpmiInstance->IpmiTransportPpi,
+                               IPMI_NETFN_APP,
+                               0,
+                               IPMI_APP_GET_DEVICE_ID,
+                               NULL,
+                               0,
+                               mIpmiInstance->TempData,
+                               &DataSize
+                               ))) {
+    DEBUG ((EFI_D_ERROR, "[IPMI] BMC does not respond (status: %r), %d retries left\n",
+            Status, Retries));
+
+    if (Retries-- == 0) {
+      ReportStatusCode (EFI_ERROR_CODE | EFI_ERROR_MAJOR, EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | EFI_CU_FP_EC_COMM_ERROR);
+      mIpmiInstance->BmcStatus = BMC_HARDFAIL;
+      return Status;
+    }
+    //
+    // Handle the case that BMC FW still not enable KCS channel after AC cycle. just stall 1 second
+    //
+    MicroSecondDelay (1*1000*1000);
+  }
+  pBmcInfo = (SM_CTRL_INFO*) &mIpmiInstance->TempData[0];
+  DEBUG ((DEBUG_INFO, "[IPMI PEI] BMC Device ID: 0x%02X, firmware version: %d.%02X UpdateMode:%x\n",
+          pBmcInfo->DeviceId, pBmcInfo->MajorFirmwareRev, pBmcInfo->MinorFirmwareRev, pBmcInfo->UpdateMode));
+  //
+  // In OpenBMC, UpdateMode: the bit 7 of byte 4 in get device id command is used for the BMC status:
+  // 0 means BMC is ready, 1 means BMC is not ready.
+  // At the very beginning of BMC power on, the status is 1 means BMC is in booting process and not ready. It is not the flag for force update mode.
+  //
+  if (pBmcInfo->UpdateMode == BMC_READY) {
+    mIpmiInstance->BmcStatus = BMC_OK;
+    return EFI_SUCCESS;
+  } else {
+    //
+    // Updatemode = 1 mean BMC is not ready, continue waiting.
+    //
+    while (Retries-- != 0) {
+      MicroSecondDelay(1*1000*1000); //delay 1 seconds
+      DEBUG ((DEBUG_INFO, "[IPMI PEI] UpdateMode Retries:%x \n",Retries));
+      Status = PeiIpmiSendCommand (
+                 &mIpmiInstance->IpmiTransportPpi,
+                 IPMI_NETFN_APP,
+                 0,
+                 IPMI_APP_GET_DEVICE_ID,
+                 NULL,
+                 0,
+                 mIpmiInstance->TempData,
+                 &DataSize
+                 );
+      if (!EFI_ERROR (Status)) {
+        pBmcInfo = (SM_CTRL_INFO*) &mIpmiInstance->TempData[0];
+        DEBUG ((DEBUG_INFO, "[IPMI PEI] UpdateMode Retries:%x   pBmcInfo->UpdateMode:%x\n", Retries, pBmcInfo->UpdateMode));
+        if (pBmcInfo->UpdateMode == BMC_READY) {
+          mIpmiInstance->BmcStatus = BMC_OK;
+          return EFI_SUCCESS;
+        }
+      }
+    }
+  }
+
+  mIpmiInstance->BmcStatus = BMC_HARDFAIL;
+  return Status;
+} // GetDeviceId()
diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.h b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.h
new file mode 100644
index 0000000000..d31af85325
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.h
@@ -0,0 +1,138 @@
+/** @file
+  Generic IPMI stack head file during PEI phase
+
+  @copyright
+  Copyright 2017 - 2021 Intel Corporation. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _PEI_IPMI_INIT_H_
+#define _PEI_IPMI_INIT_H_
+
+#include <PiPei.h>
+#include <Uefi.h>
+
+#include <Ppi/IpmiTransportPpi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PeiServicesTablePointerLib.h>
+#include <Library/TimerLib.h>
+#include <Library/PciLib.h>
+
+#include "PeiIpmiBmcDef.h"
+#include "PeiIpmiBmc.h"
+
+//
+// Prototypes
+//
+#define MBXDAT_B                          0x0B
+#define BMC_KCS_TIMEOUT_PEI               5     // [s] Single KSC request timeout
+#define KCS_DELAY_UNIT_PEI                1000  // [s] Each KSC IO delay
+#define IPMI_DEFAULT_IO_BASE              0xCA2
+
+//
+// Internal(hook) function list
+//
+EFI_STATUS
+SendPreBootSignaltoBmc (
+  IN CONST EFI_PEI_SERVICES             **PeiServices
+  )
+  /*++
+
+Routine Description:
+  Send Pre-Boot signal to BMC
+
+Arguments:
+  PeiServices           - General purpose services available to every PEIM.
+
+Returns:
+  EFI_SUCCESS           - Success
+--*/
+;
+
+EFI_STATUS
+PeiIpmiSendCommand (
+  IN      PEI_IPMI_TRANSPORT_PPI       *This,
+  IN      UINT8                        NetFunction,
+  IN      UINT8                        Lun,
+  IN      UINT8                        Command,
+  IN      UINT8                        *CommandData,
+  IN      UINT32                       CommandDataSize,
+  IN OUT  UINT8                        *ResponseData,
+  IN OUT  UINT32                       *ResponseDataSize
+  )
+/*++
+
+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
+
+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
+--*/
+;
+
+EFI_STATUS
+PeiGetIpmiBmcStatus (
+  IN  PEI_IPMI_TRANSPORT_PPI                           *This,
+  OUT BMC_STATUS                                       *BmcStatus,
+  OUT SM_COM_ADDRESS                                   *ComAddress
+  )
+/*++
+
+Routine Description:
+  Updates the BMC status and returns the Com Address
+
+Arguments:
+  This        - Pointer to IPMI protocol instance
+  BmcStatus   - BMC status
+  ComAddress  - Com Address
+
+Returns:
+  EFI_SUCCESS - Success
+--*/
+;
+
+//
+// internal function list
+//
+EFI_STATUS
+GetDeviceId (
+  IN      PEI_IPMI_BMC_INSTANCE_DATA   *mIpmiInstance
+  )
+/*++
+
+Routine Description:
+  Execute the Get Device ID command to determine whether or not the BMC is in Force Update
+  Mode.  If it is, then report it to the error manager.
+
+Arguments:
+  mIpmiInstance   - Data structure describing BMC variables and used for sending commands
+  StatusCodeValue - An array used to accumulate error codes for later reporting.
+  ErrorCount      - Counter used to keep track of error codes in StatusCodeValue
+
+Returns:
+  Status
+
+--*/
+;
+#endif //_PEI_IPMI_INIT_H_
+
diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.inf b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.inf
new file mode 100644
index 0000000000..0ef2c18116
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiGenericIpmi.inf
@@ -0,0 +1,58 @@
+## @file
+# Generic IPMI during PEI phase
+#
+# @copyright
+# Copyright 2017 - 2021 Intel Corporation. <BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PeiGenericIpmi
+  FILE_GUID                      = 0B161208-2958-460C-B97F-B912A8AD0F8D
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PeimIpmiInterfaceInit
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+#  HOB Guid C Name: gEfiHtBistHobGuid Hob Type: GUID_EXTENSION
+#
+[Sources]
+  ../Common/IpmiBmcCommon.h
+  ../Common/KcsBmc.c
+  ../Common/KcsBmc.h
+  PeiIpmiBmc.c
+  PeiIpmiBmc.h
+  PeiIpmiBmcDef.h
+  PeiGenericIpmi.c
+  PeiGenericIpmi.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OutOfBandManagement/IpmiFeaturePkg/IpmiFeaturePkg.dec
+
+[LibraryClasses]
+  PeimEntryPoint
+  PciLib
+  MemoryAllocationLib
+  DebugLib
+  IoLib
+  TimerLib
+
+[Guids]
+
+[Ppis]
+  gPeiIpmiTransportPpiGuid       #ALWAYS PRODUCE
+
+[Pcd]
+  gIpmiFeaturePkgTokenSpaceGuid.PcdIpmiIoBaseAddress
+  gIpmiFeaturePkgTokenSpaceGuid.PcdIpmiBmcReadyDelayTimer
+  gIpmiFeaturePkgTokenSpaceGuid.PcdSioMailboxBaseAddress
+  gIpmiFeaturePkgTokenSpaceGuid.PcdSignalPreBootToBmc
+
+[Depex]
+  TRUE
diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.c b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.c
new file mode 100644
index 0000000000..32665b3e22
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.c
@@ -0,0 +1,277 @@
+/** @file
+  Generic IPMI transport layer during PEI phase
+
+  @copyright
+  Copyright 2016 - 2021 Intel Corporation. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "PeiIpmiBmc.h"
+
+EFI_STATUS
+UpdateErrorStatus (
+  IN UINT8                      BmcError,
+  PEI_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
+PeiIpmiSendCommandToBmc (
+  IN      PEI_IPMI_TRANSPORT_PPI        *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
+
+--*/
+{
+  PEI_IPMI_BMC_INSTANCE_DATA  *IpmiInstance;
+  UINT8                       DataSize;
+  EFI_STATUS                  Status;
+  IPMI_COMMAND                *IpmiCommand;
+  IPMI_RESPONSE               *IpmiResponse;
+  UINT8                       Index;
+
+  IpmiInstance = INSTANCE_FROM_PEI_SM_IPMI_BMC_THIS (This);
+
+  //
+  // 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++;
+    DEBUG ((EFI_D_ERROR, "PEI Phase SendDataToBmcPort failed Status:%r\n", Status));
+    return Status;
+  }
+
+  //
+  // Get Response to IPMI Command from BMC.
+  //
+  DataSize = MAX_TEMP_DATA;
+  Status = ReceiveBmcDataFromPort (
+             IpmiInstance->KcsTimeoutPeriod,
+             IpmiInstance->IpmiIoBase,
+             Context,
+             (UINT8 *) IpmiResponse,
+             &DataSize
+             );
+
+  if (Status != EFI_SUCCESS) {
+    IpmiInstance->BmcStatus = BMC_SOFTFAIL;
+    IpmiInstance->SoftErrorCount++;
+    DEBUG ((EFI_D_ERROR, "PEI Phase ReceiveBmcDataFromPort failed Status:%r\n", Status));
+    return Status;
+  }
+
+  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
+      );
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // If we got this far without any error codes, but the DataSize is 0 then the
+  // command response failed, so do not continue.
+  //
+  //
+  // Some abnormal case, in order to avoid that BMC sent illegal data size.
+  // 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;
+  }
+
+  //
+  // Verify the response data buffer passed in is big enough.
+  //
+  if ((DataSize - IPMI_RESPONSE_HEADER_SIZE) > *ResponseDataSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // 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
+PeiIpmiBmcStatus (
+  IN  PEI_IPMI_TRANSPORT_PPI          *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
+
+--*/
+{
+  PEI_IPMI_BMC_INSTANCE_DATA  *IpmiInstance;
+
+  IpmiInstance = INSTANCE_FROM_PEI_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;
+}
diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.h b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.h
new file mode 100644
index 0000000000..40b9429e84
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmc.h
@@ -0,0 +1,38 @@
+/** @file
+  Generic IPMI transport layer head file during PEI phase
+
+  @copyright
+  Copyright 2016 - 2021 Intel Corporation. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _PEI_IPMI_BMC_H_
+#define _PEI_IPMI_BMC_H_
+
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/IpmiBaseLib.h>
+
+#include <Ppi/IpmiTransportPpi.h>
+
+#include "PeiIpmiBmcDef.h"
+#include "KcsBmc.h"
+
+//
+// IPMI Instance signature
+//
+#define SM_IPMI_BMC_SIGNATURE SIGNATURE_32 ('i', 'p', 'm', 'i')
+
+#define INSTANCE_FROM_PEI_SM_IPMI_BMC_THIS(a) \
+  CR ( \
+  a, \
+  PEI_IPMI_BMC_INSTANCE_DATA, \
+  IpmiTransportPpi, \
+  SM_IPMI_BMC_SIGNATURE \
+  )
+
+#endif // _PEI_IPMI_BMC_H_
diff --git a/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmcDef.h b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmcDef.h
new file mode 100644
index 0000000000..3fbe70ce62
--- /dev/null
+++ b/Features/Intel/OutOfBandManagement/IpmiFeaturePkg/GenericIpmi/Pei/PeiIpmiBmcDef.h
@@ -0,0 +1,156 @@
+/** @file
+  Generic IPMI transport layer common head file during PEI phase
+
+  @copyright
+  Copyright 2016 - 2021 Intel Corporation. <BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _PEI_IPMI_COMMON_BMC_H_
+#define _PEI_IPMI_COMMON_BMC_H_
+
+#include <Ppi/IpmiTransportPpi.h>
+
+#define MAX_TEMP_DATA     160
+#define BMC_SLAVE_ADDRESS 0x20
+#define MAX_SOFT_COUNT    10
+#define COMP_CODE_NORMAL  0x00
+
+//
+// IPMI command completion codes to check for in the UpdateErrorStatus routine.
+// These completion codes indicate a soft error and a running total of the occurrences
+// of these errors is maintained.
+//
+#define COMP_CODE_NODE_BUSY               0xC0
+#define COMP_CODE_TIMEOUT                 0xC3
+#define COMP_CODE_OUT_OF_SPACE            0xC4
+#define COMP_CODE_OUT_OF_RANGE            0xC9
+#define COMP_CODE_CMD_RESP_NOT_PROVIDED   0xCE
+#define COMP_CODE_FAIL_DUP_REQUEST        0xCF
+#define COMP_CODE_SDR_REP_IN_UPDATE_MODE  0xD0
+#define COMP_CODE_DEV_IN_FW_UPDATE_MODE   0xD1
+#define COMP_CODE_BMC_INIT_IN_PROGRESS    0xD2
+#define COMP_CODE_UNSPECIFIED             0xFF
+
+#define COMPLETION_CODES \
+  { \
+    COMP_CODE_NODE_BUSY, COMP_CODE_TIMEOUT, COMP_CODE_OUT_OF_SPACE, COMP_CODE_OUT_OF_RANGE, \
+    COMP_CODE_CMD_RESP_NOT_PROVIDED, COMP_CODE_FAIL_DUP_REQUEST, COMP_CODE_SDR_REP_IN_UPDATE_MODE, \
+    COMP_CODE_DEV_IN_FW_UPDATE_MODE, COMP_CODE_BMC_INIT_IN_PROGRESS, COMP_CODE_UNSPECIFIED \
+  }
+//
+// Ensure proper structure formats
+//
+#pragma pack(1)
+//
+// Pei Ipmi instance data
+//
+typedef struct {
+  UINTN                  Signature;
+  UINT64                 KcsTimeoutPeriod;
+  UINT8                  SlaveAddress;
+  UINT8                  TempData[MAX_TEMP_DATA];
+  BMC_STATUS             BmcStatus;
+  UINT64                 ErrorStatus;
+  UINT8                  SoftErrorCount;
+  UINT16                 IpmiIoBase;
+  PEI_IPMI_TRANSPORT_PPI IpmiTransportPpi;
+  EFI_PEI_PPI_DESCRIPTOR PeiIpmiBmcDataDesc;
+} PEI_IPMI_BMC_INSTANCE_DATA;
+
+//
+// Structure of IPMI Command buffer
+//
+#define IPMI_COMMAND_HEADER_SIZE  2
+
+typedef struct {
+  UINT8 Lun : 2;
+  UINT8 NetFunction : 6;
+  UINT8 Command;
+  UINT8 CommandData[MAX_TEMP_DATA - IPMI_COMMAND_HEADER_SIZE];
+} IPMI_COMMAND;
+
+//
+// Structure of IPMI Command response buffer
+//
+#define IPMI_RESPONSE_HEADER_SIZE 3
+
+typedef struct {
+  UINT8 Lun : 2;
+  UINT8 NetFunction : 6;
+  UINT8 Command;
+  UINT8 CompletionCode;
+  UINT8 ResponseData[MAX_TEMP_DATA - IPMI_RESPONSE_HEADER_SIZE];
+} IPMI_RESPONSE;
+#pragma pack()
+
+EFI_STATUS
+PeiIpmiSendCommandToBmc (
+  IN      PEI_IPMI_TRANSPORT_PPI        *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_SUCCESS           - Command completed successfully
+
+--*/
+;
+
+
+EFI_STATUS
+PeiIpmiBmcStatus (
+  IN      PEI_IPMI_TRANSPORT_PPI       *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
+
+--*/
+;
+
+
+#endif //_PEI_IPMI_COMMON_BMC_H_
-- 
2.27.0.windows.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#72313): https://edk2.groups.io/g/devel/message/72313
Mute This Topic: https://groups.io/mt/81016655/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list