Browse Source

ManageabilityPkg/ManageabilityTransportKcsLib

This is the KCS manageability transport library
instance follows the design guidance described
in Readme file under ManageabilityPkg.

Signed-off-by: Abner Chang <abner.chang@amd.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Isaac Oram <isaac.w.oram@intel.com>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Cc: Abdul Lateef Attar <abdattar@amd.com>
Cc: Nickle Wang <nicklew@nvidia.com>
Cc: Igor Kulchytskyy <igork@ami.com>
Reviewed-by: Nickle Wang <nicklew@nvidia.com>
Abner Chang 1 year ago
parent
commit
12aa3bd76a

+ 24 - 0
Features/ManageabilityPkg/Include/Library/ManageabilityTransportIpmiLib.h

@@ -0,0 +1,24 @@
+/** @file
+
+  This file defines the manageability IPMI protocol specific transport data.
+
+  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef MANAGEABILITY_TRANSPORT_IPMI_LIB_H_
+#define MANAGEABILITY_TRANSPORT_IPMI_LIB_H_
+
+#include <Library/ManageabilityTransportLib.h>
+
+///
+/// The IPMI command header which is apart from
+/// the payload.
+///
+typedef struct {
+  UINT8    Lun:2;
+  UINT8    NetFn:6;
+  UINT8    Command;
+} MANAGEABILITY_IPMI_TRANSPORT_HEADER;
+
+#endif

+ 502 - 0
Features/ManageabilityPkg/Library/ManageabilityTransportKcsLib/Common/KcsCommon.c

@@ -0,0 +1,502 @@
+/** @file
+
+  KCS instance of Manageability Transport Library
+
+  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Uefi.h>
+#include <IndustryStandard/IpmiKcs.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+
+#include "ManageabilityTransportKcs.h"
+
+extern MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO  mKcsHardwareInfo;
+
+/**
+  This function waits for parameter Flag to set.
+  Checks status flag in every 1ms internal till 5 seconds elapses.
+
+  @param[in]  Flag        KCS Flag to test.
+  @retval     EFI_SUCCESS The KCS flag under test is set.
+  @retval     EFI_TIMEOUT The KCS flag didn't set in 5 second windows.
+**/
+EFI_STATUS
+WaitStatusSet (
+  IN  UINT8  Flag
+  )
+{
+  UINT64  Timeout = 0;
+
+  while (!(KcsRegisterRead8 (KCS_REG_STATUS) & Flag)) {
+    MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS);
+    Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS;
+    if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) {
+      return EFI_TIMEOUT;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function waits for parameter Flag to get cleared.
+  Checks status flag in every 1ms internal till 5 seconds elapses.
+
+  @param[in]  Flag        KCS Flag to test.
+
+  @retval     EFI_SUCCESS The KCS flag under test is clear.
+  @retval     EFI_TIMEOUT The KCS flag didn't cleared in 5 second windows.
+**/
+EFI_STATUS
+WaitStatusClear (
+  IN  UINT8  Flag
+  )
+{
+  UINT64  Timeout = 0;
+
+  while (KcsRegisterRead8 (KCS_REG_STATUS) & Flag) {
+    MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS);
+    Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS;
+    if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) {
+      return EFI_TIMEOUT;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function validates KCS OBF bit.
+  Checks whether OBF bit is set or not.
+
+  @retval EFI_SUCCESS    OBF bit is set.
+  @retval EFI_NOT_READY  OBF bit is not set.
+**/
+EFI_STATUS
+ClearOBF (
+  VOID
+  )
+{
+  if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) {
+    KcsRegisterRead8 (KCS_REG_DATA_IN); // read the data to clear the OBF
+    if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) {
+      return EFI_NOT_READY;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function writes/sends data to the KCS port.
+  Algorithm is based on flow chart provided in IPMI spec 2.0
+  Figure 9-6, KCS Interface BMC to SMS Write Transfer Flow Chart
+
+  @param[in]  NetFunction           Net function of the command.
+  @param[in]  Command               IPMI Command.
+  @param[in]  RequestData           Command Request Data, could be NULL.
+                                    RequestDataSize must be zero, if RequestData
+                                    is NULL.
+  @param[in]  RequestDataSize       Size of Command Request 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
+KcsTransportWrite (
+  IN  UINT8   NetFunction,
+  IN  UINT8   Command,
+  IN  UINT8   *RequestData OPTIONAL,
+  IN  UINT32  RequestDataSize
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Length;
+  UINT8       *Buffer;
+  UINT8       *BufferPtr;
+
+  // Validation on RequestData and RequestDataSize.
+  if ((RequestData == NULL && RequestDataSize != 0) ||
+      (RequestData != NULL && RequestDataSize == 0)
+      ) {
+    DEBUG ((DEBUG_ERROR, "%a: Mismatched values of RequestData or RequestDataSize.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Length = sizeof (NetFunction) + sizeof (Command);
+  if (RequestData != NULL) {
+    Length = Length + RequestDataSize;
+  }
+
+  Buffer = AllocateZeroPool (Length);
+  if (Buffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Buffer[0] = NetFunction
+  // Buffer[1] = Command
+  // Buffer [2..RequestDataSize] = RequestData
+  //
+  BufferPtr = Buffer;
+  CopyMem (BufferPtr, &NetFunction, sizeof (NetFunction));
+  BufferPtr += sizeof (NetFunction);
+  CopyMem (BufferPtr, &Command, sizeof (Command));
+  BufferPtr += sizeof (Command);
+  if (Length > (sizeof (NetFunction) + sizeof (Command))) {
+    CopyMem (BufferPtr, RequestData, RequestDataSize);
+  }
+
+  BufferPtr = Buffer;
+
+  // Step 1. wait for IBF to get clear
+  Status = WaitStatusClear (IPMI_KCS_IBF);
+  if (EFI_ERROR (Status)) {
+    FreePool (Buffer);
+    return Status;
+  }
+
+  // Step 2. clear OBF
+  if (EFI_ERROR (ClearOBF ())) {
+    FreePool (Buffer);
+    return EFI_NOT_READY;
+  }
+
+  // Step 3. WR_START to CMD, phase=wr_start
+  KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_START);
+
+  // Step 4. wait for IBF to get clear
+  Status = WaitStatusClear (IPMI_KCS_IBF);
+  if (EFI_ERROR (Status)) {
+    FreePool (Buffer);
+    return Status;
+  }
+
+  // Step 5. check state it should be WRITE_STATE, else exit with error
+  if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
+    FreePool (Buffer);
+    return EFI_NOT_READY;
+  }
+
+  // Step 6, Clear OBF
+  if (EFI_ERROR (ClearOBF ())) {
+    FreePool (Buffer);
+    return EFI_NOT_READY;
+  }
+
+  while (Length > 1) {
+    // Step 7, phase wr_data, write one byte of Data
+    KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr);
+    Length--;
+    BufferPtr++;
+
+    // Step 8. wait for IBF clear
+    Status = WaitStatusClear (IPMI_KCS_IBF);
+    if (EFI_ERROR (Status)) {
+      FreePool (Buffer);
+      return Status;
+    }
+
+    // Step 9. check state it should be WRITE_STATE, else exit with error
+    if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
+      FreePool (Buffer);
+      return EFI_NOT_READY;
+    }
+
+    // Step 10
+    if (EFI_ERROR (ClearOBF ())) {
+      FreePool (Buffer);
+      return EFI_NOT_READY;
+    }
+
+    //
+    // Step 11, check for DATA completion if more than one byte;
+    // if still need to be transferred then go to step 7 and repeat
+    //
+  }
+
+  // Step 12, WR_END  to CMD
+  KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_END);
+
+  // Step 13. wait for IBF to get clear
+  Status = WaitStatusClear (IPMI_KCS_IBF);
+  if (EFI_ERROR (Status)) {
+    FreePool (Buffer);
+    return Status;
+  }
+
+  // Step 14. check state it should be WRITE_STATE, else exit with error
+  if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
+    FreePool (Buffer);
+    return EFI_NOT_READY;
+  }
+
+  // Step 15
+  if (EFI_ERROR (ClearOBF ())) {
+    FreePool (Buffer);
+    return EFI_NOT_READY;
+  }
+
+  // Step 16, write the last byte
+  KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr);
+  FreePool (Buffer);
+  return EFI_SUCCESS;
+}
+
+/**
+  This function sends/receives data from KCS port.
+  Algorithm is based on flow chart provided in IPMI spec 2.0
+  Figure 9-7, KCS Interface BMC to SMS Read Transfer Flow Chart
+
+  @param [in]       DataBytes             Buffer to hold the read Data.
+  @param [in, out]  Length                Number of Bytes read from KCS port.
+  @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 set
+                                          to the device.
+  @retval           EFI_OUT_OF_RESOURCES  The resource allocation is out of
+                                          resource or data size error.
+**/
+EFI_STATUS
+KcsTransportRead (
+  OUT    UINT8   *DataByte,
+  IN OUT UINT32  *Length
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      ReadLength;
+
+  if (DataByte == NULL || *Length == 0) {
+    DEBUG ((DEBUG_ERROR, "%a: Either DataByte is NULL or Length is 0.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+  ReadLength = 0;
+  while (ReadLength < *Length) {
+    // Step 1. wait for IBF to get clear
+    Status = WaitStatusClear (IPMI_KCS_IBF);
+    if (EFI_ERROR (Status)) {
+      *Length = ReadLength;
+      return Status;
+    }
+
+    // Step 2. check state it should be READ_STATE, else exit with error
+    if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsReadState) {
+      // Step 2.1.1 check of OBF to get clear
+      Status = WaitStatusSet (IPMI_KCS_OBF);
+      if (EFI_ERROR (Status)) {
+        *Length = ReadLength;
+        return Status;
+      }
+
+      // Step 2.1.2 read data from data out
+      DataByte[ReadLength++] = KcsRegisterRead8 (KCS_REG_DATA_IN);
+      Status                 = WaitStatusClear (IPMI_KCS_IBF);
+      if (EFI_ERROR (Status)) {
+        *Length = ReadLength;
+        return Status;
+      }
+
+      // Step 2.1.3 Write READ byte to data in register.
+      KcsRegisterWrite8 (KCS_REG_DATA_OUT, IPMI_KCS_CONTROL_CODE_READ);
+    } else if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsIdleState) {
+      // Step 2.2.1
+      Status = WaitStatusSet (IPMI_KCS_OBF);
+      if (EFI_ERROR (Status)) {
+        *Length = ReadLength;
+        return Status;
+      }
+
+      // Step 2.2.2 read dummy data
+      KcsRegisterRead8 (KCS_REG_DATA_IN); // Dummy read as per IPMI spec
+      *Length = ReadLength;
+      return EFI_SUCCESS;
+    } else {
+      *Length = ReadLength;
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  *Length = ReadLength;
+  return EFI_SUCCESS;
+}
+
+/**
+  This service communicates with BMC using KCS protocol.
+
+  @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.
+                                        When IN, it is the expected data size
+                                        of response data.
+                                        When OUT, it is the data size of response
+                                        exactly returned.
+  @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
+EFIAPI
+KcsTransportSendCommand (
+  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;
+  UINT32                    RspHeaderSize;
+  IPMI_KCS_RESPONSE_HEADER  RspHeader;
+
+  if ((RequestData != NULL) && (RequestDataSize == 0)) {
+    DEBUG((DEBUG_ERROR, "%a: Mismatched values of RequestData and RequestDataSize\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((ResponseData != NULL) && ((ResponseDataSize != NULL) && (*ResponseDataSize == 0))) {
+    DEBUG((DEBUG_ERROR, "%a: Mismatched values of ResponseData and ResponseDataSize\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = KcsTransportWrite (
+             (NetFunction << 2),
+             Command,
+             RequestData,
+             RequestDataSize
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "IPMI KCS Write Failed with Status(%r) for NetFunction(0x%x)," \
+      " Command(0x%x).\n",
+      Status,
+      NetFunction,
+      Command
+      ));
+    return Status;
+  }
+
+  //
+  // Read the response header
+  RspHeaderSize = sizeof (IPMI_KCS_RESPONSE_HEADER);
+  Status        = KcsTransportRead ((UINT8 *)&RspHeader, &RspHeaderSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "IPMI KCS read response header failed Status(%r), " \
+      "RspNetFunctionLun = 0x%x, " \
+      "Comamnd = 0x%x \n",
+      Status,
+      RspHeader.NetFunc,
+      RspHeader.Command
+      ));
+    return (Status);
+  }
+
+  Status = KcsTransportRead ((UINT8 *)ResponseData, ResponseDataSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "IPMI KCS response read Failed with Status(%r) for NetFunction(0x%x)," \
+      " Command(0x%x).\n",
+      Status,
+      NetFunction,
+      Command
+      ));
+  }
+
+  return Status;
+}
+
+/**
+  This function reads 8-bit value from register address.
+
+  @param[in]      Address               This represents either 16-bit IO address
+                                        or 32-bit memory mapped address.
+
+  @retval         UINT8                 8-bit value.
+**/
+UINT8
+KcsRegisterRead8 (
+  MANAGEABILITY_TRANSPORT_HARDWARE_IO  Address
+  )
+{
+  UINT8  Value;
+
+  if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) {
+    // Read 8-bit value from 32-bit Memory mapped address.
+    Value = MmioRead8 ((UINTN)Address.IoAddress32);
+  } else {
+    // Read 8-bit value from 16-bit I/O address
+    Value = IoRead8 ((UINTN)Address.IoAddress16);
+  }
+
+  return Value;
+}
+
+/**
+  This function writes 8-bit value to register address.
+
+  @param[in]      Address               This represents either 16-bit IO address
+                                        or 32-bit memory mapped address.
+  @param[in]      Value                 8-bit value write to register address
+
+**/
+VOID
+KcsRegisterWrite8 (
+  MANAGEABILITY_TRANSPORT_HARDWARE_IO  Address,
+  UINT8                                Value
+  )
+{
+  if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) {
+    // Write 8-bit value to 32-bit Memory mapped address.
+    MmioWrite8 ((UINTN)Address.IoAddress32, Value);
+  } else {
+    // Write 8-bit value to 16-bit I/O address
+    IoWrite8 ((UINTN)Address.IoAddress16, Value);
+  }
+}

+ 105 - 0
Features/ManageabilityPkg/Library/ManageabilityTransportKcsLib/Common/ManageabilityTransportKcs.h

@@ -0,0 +1,105 @@
+/** @file
+
+  Manageability transport KCS internal used definitions.
+
+  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef MANAGEABILITY_TRANSPORT_KCS_LIB_H_
+#define MANAGEABILITY_TRANSPORT_KCS_LIB_H_
+
+#include <Library/ManageabilityTransportLib.h>
+
+#define MANAGEABILITY_TRANSPORT_KCS_SIGNATURE  SIGNATURE_32 ('M', 'T', 'K', 'C')
+
+#define KCS_BASE_ADDRESS  mKcsHardwareInfo.IoBaseAddress
+#define KCS_REG_DATA_IN   mKcsHardwareInfo.IoDataInAddress
+#define KCS_REG_DATA_OUT  mKcsHardwareInfo.IoDataOutAddress
+#define KCS_REG_COMMAND   mKcsHardwareInfo.IoCommandAddress
+#define KCS_REG_STATUS    mKcsHardwareInfo.IoStatusAddress
+
+///
+/// Manageability transport KCS internal data structure.
+///
+typedef struct {
+  UINTN                            Signature;
+  MANAGEABILITY_TRANSPORT_TOKEN    Token;
+} MANAGEABILITY_TRANSPORT_KCS;
+
+#define MANAGEABILITY_TRANSPORT_KCS_FROM_LINK(a)  CR (a, MANAGEABILITY_TRANSPORT_KCS, Token, MANAGEABILITY_TRANSPORT_KCS_SIGNATURE)
+
+#define IPMI_KCS_GET_STATE(s)  (s >> 6)
+#define IPMI_KCS_SET_STATE(s)  (s << 6)
+
+/// 5 sec, according to IPMI spec
+#define IPMI_KCS_TIMEOUT_5_SEC  5000*1000
+#define IPMI_KCS_TIMEOUT_1MS    1000
+
+/**
+  This service communicates with BMC using KCS protocol.
+
+  @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
+EFIAPI
+KcsTransportSendCommand (
+  IN  UINT8      NetFunction,
+  IN  UINT8      Command,
+  IN  UINT8      *RequestData OPTIONAL,
+  IN  UINT32     RequestDataSize,
+  OUT UINT8      *ResponseData OPTIONAL,
+  IN OUT UINT32  *ResponseDataSize OPTIONAL
+  );
+
+/**
+  This function reads 8-bit value from register address.
+
+  @param[in]      Address               This represents either 16-bit IO address
+                                        or 32-bit memory mapped address.
+
+  @retval         UINT8                 8-bit value.
+**/
+UINT8
+KcsRegisterRead8 (
+  MANAGEABILITY_TRANSPORT_HARDWARE_IO  Address
+  );
+
+/**
+  This function writes 8-bit value to register address.
+
+  @param[in]      Address               This represents either 16-bit IO address
+                                        or 32-bit memory mapped address.
+  @param[in]      Value                 8-bit value write to register address
+
+**/
+VOID
+KcsRegisterWrite8 (
+  MANAGEABILITY_TRANSPORT_HARDWARE_IO  Address,
+  UINT8                                Value
+  );
+
+#endif

+ 44 - 0
Features/ManageabilityPkg/Library/ManageabilityTransportKcsLib/Dxe/DxeManageabilityTransportKcs.inf

@@ -0,0 +1,44 @@
+## @file
+# KCS instance of Manageability Transport Library
+#
+# Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = DxeManageabilityTransportKcs
+  MODULE_UNI_FILE                = ManageabilityTransportKcs.uni
+  FILE_GUID                      = FCCC8B34-145A-4927-9F08-553ADC579AF7
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ManageabilityTransportLib
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 ARM AARCH64
+#
+
+[Sources]
+  ManageabilityTransportKcs.c
+  ../Common/KcsCommon.c
+  ../Common/ManageabilityTransportKcs.h
+
+[Packages]
+  ManageabilityPkg/ManageabilityPkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
+  IoLib
+  TimerLib
+  MemoryAllocationLib
+
+[Guids]
+  gManageabilityTransportKcsGuid
+  gManageabilityProtocolMctpGuid
+  gManageabilityProtocolIpmiGuid
+
+[FixedPcd]
+  gEfiMdePkgTokenSpaceGuid.PcdIpmiKcsIoBaseAddress   # Used as default KCS I/O base adddress
+

+ 389 - 0
Features/ManageabilityPkg/Library/ManageabilityTransportKcsLib/Dxe/ManageabilityTransportKcs.c

@@ -0,0 +1,389 @@
+/** @file
+
+  KCS instance of Manageability Transport Library
+
+  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+*/
+
+#include <Uefi.h>
+#include <IndustryStandard/IpmiKcs.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ManageabilityTransportLib.h>
+#include <Library/ManageabilityTransportIpmiLib.h>
+#include <Library/ManageabilityTransportHelperLib.h>
+
+#include "ManageabilityTransportKcs.h"
+
+MANAGEABILITY_TRANSPORT_KCS  *mSingleSessionToken = NULL;
+
+EFI_GUID  *SupportedManageabilityProtocol[] = {
+  &gManageabilityProtocolIpmiGuid,
+  &gManageabilityProtocolMctpGuid
+};
+
+UINT8  NumberOfSupportedProtocol = (sizeof (SupportedManageabilityProtocol)/sizeof (EFI_GUID *));
+
+MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO  mKcsHardwareInfo;
+
+/**
+  This function initializes the transport interface.
+
+  @param [in]  TransportToken           The transport token acquired through
+                                        AcquireTransportSession function.
+  @param [in]  HardwareInfo             The hardware information
+                                        assigned to KCS transport interface.
+
+  @retval      EFI_SUCCESS              Transport interface is initialized
+                                        successfully.
+  @retval      EFI_INVALID_PARAMETER    The invalid transport token.
+  @retval      EFI_NOT_READY            The transport interface works fine but
+  @retval                               is not ready.
+  @retval      EFI_DEVICE_ERROR         The transport interface has problems.
+  @retval      EFI_ALREADY_STARTED      Teh protocol interface has already initialized.
+  @retval      Otherwise                Other errors.
+
+**/
+EFI_STATUS
+EFIAPI
+KcsTransportInit (
+  IN  MANAGEABILITY_TRANSPORT_TOKEN                 *TransportToken,
+  IN  MANAGEABILITY_TRANSPORT_HARDWARE_INFORMATION  HardwareInfo OPTIONAL
+  )
+{
+  CHAR16  *ManageabilityProtocolName;
+
+  if (TransportToken == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid transport token.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (HardwareInfo.Kcs == NULL) {
+    DEBUG ((DEBUG_INFO, "%a: Hardware information is not provided, use dfault settings.\n", __FUNCTION__));
+    mKcsHardwareInfo.MemoryMap                    = MANAGEABILITY_TRANSPORT_KCS_IO_MAP_IO;
+    mKcsHardwareInfo.IoBaseAddress.IoAddress16    = PcdGet16 (PcdIpmiKcsIoBaseAddress);
+    mKcsHardwareInfo.IoDataInAddress.IoAddress16  = mKcsHardwareInfo.IoBaseAddress.IoAddress16 + IPMI_KCS_DATA_IN_REGISTER_OFFSET;
+    mKcsHardwareInfo.IoDataOutAddress.IoAddress16 = mKcsHardwareInfo.IoBaseAddress.IoAddress16 + IPMI_KCS_DATA_OUT_REGISTER_OFFSET;
+    mKcsHardwareInfo.IoCommandAddress.IoAddress16 = mKcsHardwareInfo.IoBaseAddress.IoAddress16 + IPMI_KCS_COMMAND_REGISTER_OFFSET;
+    mKcsHardwareInfo.IoStatusAddress.IoAddress16  = mKcsHardwareInfo.IoBaseAddress.IoAddress16 + IPMI_KCS_STATUS_REGISTER_OFFSET;
+  } else {
+    mKcsHardwareInfo.MemoryMap        = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->MemoryMap;
+    mKcsHardwareInfo.IoBaseAddress    = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->IoBaseAddress;
+    mKcsHardwareInfo.IoDataInAddress  = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->IoDataInAddress;
+    mKcsHardwareInfo.IoDataOutAddress = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->IoDataOutAddress;
+    mKcsHardwareInfo.IoCommandAddress = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->IoCommandAddress;
+    mKcsHardwareInfo.IoStatusAddress  = ((MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO *)HardwareInfo.Kcs)->IoStatusAddress;
+  }
+
+  // Get protocol specification name.
+  ManageabilityProtocolName = HelperManageabilitySpecName (TransportToken->ManageabilityProtocolSpecification);
+
+  DEBUG ((DEBUG_INFO, "%a: KCS transport hardware for %s is:\n", __FUNCTION__, ManageabilityProtocolName));
+  if (mKcsHardwareInfo.MemoryMap) {
+    DEBUG ((DEBUG_INFO, "Memory Map I/O\n", __FUNCTION__));
+    DEBUG ((DEBUG_INFO, "Base Memory Address : 0x%08x\n", mKcsHardwareInfo.IoBaseAddress.IoAddress32));
+    DEBUG ((DEBUG_INFO, "Data in Address     : 0x%08x\n", mKcsHardwareInfo.IoDataInAddress.IoAddress32));
+    DEBUG ((DEBUG_INFO, "Data out Address    : 0x%08x\n", mKcsHardwareInfo.IoDataOutAddress.IoAddress32));
+    DEBUG ((DEBUG_INFO, "Command Address     : 0x%08x\n", mKcsHardwareInfo.IoCommandAddress.IoAddress32));
+    DEBUG ((DEBUG_INFO, "Status Address      : 0x%08x\n", mKcsHardwareInfo.IoStatusAddress.IoAddress32));
+  } else {
+    DEBUG ((DEBUG_INFO, "I/O Map I/O\n"));
+    DEBUG ((DEBUG_INFO, "Base I/O port    : 0x%04x\n", mKcsHardwareInfo.IoBaseAddress.IoAddress16));
+    DEBUG ((DEBUG_INFO, "Data in I/O port : 0x%04x\n", mKcsHardwareInfo.IoDataInAddress.IoAddress16));
+    DEBUG ((DEBUG_INFO, "Data out I/O port: 0x%04x\n", mKcsHardwareInfo.IoDataOutAddress.IoAddress16));
+    DEBUG ((DEBUG_INFO, "Command I/O port : 0x%04x\n", mKcsHardwareInfo.IoCommandAddress.IoAddress16));
+    DEBUG ((DEBUG_INFO, "Status I/O port  : 0x%04x\n", mKcsHardwareInfo.IoStatusAddress.IoAddress16));
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function returns the transport interface status.
+  The generic EFI_STATUS is returned to caller directly, The additional
+  information of transport interface could be optionally returned in
+  TransportAdditionalStatus to describes the status that can't be
+  described obviously through EFI_STATUS.
+  See the definition of MANAGEABILITY_TRANSPORT_STATUS.
+
+  @param [in]   TransportToken             The transport token acquired through
+                                           AcquireTransportSession function.
+  @param [out]  TransportAdditionalStatus  The additional status of transport
+                                           interface.
+                                           NULL means no additional status of this
+                                           transport interface.
+
+  @retval      EFI_SUCCESS              Transport interface status is returned.
+  @retval      EFI_INVALID_PARAMETER    The invalid transport token.
+  @retval      EFI_DEVICE_ERROR         The transport interface has problems to return
+  @retval      EFI_UNSUPPORTED          The transport interface doesn't have status report.
+               Otherwise                Other errors.
+
+**/
+EFI_STATUS
+EFIAPI
+KcsTransportStatus (
+  IN  MANAGEABILITY_TRANSPORT_TOKEN              *TransportToken,
+  OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS  *TransportAdditionalStatus OPTIONAL
+  )
+{
+  UINT8  TransportStatus;
+
+  if (TransportToken == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid transport token.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (TransportAdditionalStatus == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  TransportStatus            = IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS));
+  *TransportAdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_NO_ERRORS;
+  if (TransportStatus != IpmiKcsIdleState) {
+    if (TransportStatus == IpmiKcsReadState) {
+      //
+      // Transport is in read state.
+      *TransportAdditionalStatus |= MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_BUSY_IN_READ;
+      return EFI_NOT_READY;
+    } else if (TransportStatus == IpmiKcsWriteState) {
+      //
+      // Transport is in read state.
+      *TransportAdditionalStatus |= MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_BUSY_IN_WRITE;
+      return EFI_NOT_READY;
+    } else {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function resets the transport interface.
+  The generic EFI_STATUS is returned to caller directly after reseting transport
+  interface. The additional information of transport interface could be optionally
+  returned in TransportAdditionalStatus to describes the status that can't be
+  described obviously through EFI_STATUS.
+  See the definition of MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS.
+
+  @param [in]   TransportToken             The transport token acquired through
+                                           AcquireTransportSession function.
+  @param [out]  TransportAdditionalStatus  The additional status of specific transport
+                                           interface after the reset.
+                                           NULL means no additional status of this
+                                           transport interface.
+
+  @retval      EFI_SUCCESS              Transport interface status is returned.
+  @retval      EFI_INVALID_PARAMETER    The invalid transport token.
+  @retval      EFI_TIMEOUT              The reset process is time out.
+  @retval      EFI_DEVICE_ERROR         The transport interface has problems to return
+                                        status.
+               Otherwise                Other errors.
+
+**/
+EFI_STATUS
+EFIAPI
+KcsTransportReset (
+  IN  MANAGEABILITY_TRANSPORT_TOKEN              *TransportToken,
+  OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS  *TransportAdditionalStatus OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  This function transmit the request over target transport interface.
+  The generic EFI_STATUS is returned to caller directly after reseting transport
+  interface. The additional information of transport interface could be optionally
+  returned in TransportAdditionalStatus to describes the status that can't be
+  described obviously through EFI_STATUS.
+  See the definition of MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS.
+
+  @param [in]  TransportToken           The transport token acquired through
+                                        AcquireTransportSession function.
+  @param [in]  TransferToken            The transfer token, see the definition of
+                                        MANAGEABILITY_TRANSFER_TOKEN.
+
+  @retval      The EFI status is returned in MANAGEABILITY_TRANSFER_TOKEN.
+
+**/
+VOID
+EFIAPI
+KcsTransportTransmitReceive (
+  IN  MANAGEABILITY_TRANSPORT_TOKEN  *TransportToken,
+  IN  MANAGEABILITY_TRANSFER_TOKEN   *TransferToken
+  )
+{
+  EFI_STATUS                           Status;
+  MANAGEABILITY_IPMI_TRANSPORT_HEADER  *TransmitHeader;
+
+  if (TransportToken == NULL || TransferToken == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Invalid transport token or transfer token.\n", __FUNCTION__));
+    return;
+  }
+
+  TransmitHeader = (MANAGEABILITY_IPMI_TRANSPORT_HEADER *)TransferToken->TransmitHeader;
+  if (TransmitHeader == NULL) {
+    TransferToken->TransferStatus = EFI_INVALID_PARAMETER;
+    return;
+  }
+
+  Status = KcsTransportSendCommand (
+             TransmitHeader->NetFn,
+             TransmitHeader->Command,
+             TransferToken->TransmitPackage.TransmitPayload,
+             TransferToken->TransmitPackage.TransmitSizeInByte,
+             TransferToken->ReceivePackage.ReceiveBuffer,
+             &TransferToken->ReceivePackage.ReceiveSizeInByte
+             );
+
+  TransferToken->TransferStatus = Status;
+  KcsTransportStatus (TransportToken, &TransferToken->TransportAdditionalStatus);
+}
+
+/**
+  This function acquires to create a transport session to transmit manageability
+  packet. A transport token is returned to caller for the follow up operations.
+
+  @param [in]   ManageabilityProtocolSpec  The protocol spec the transport interface is acquired.
+  @param [out]  TransportToken             The pointer to receive the transport token created by
+                                           the target transport interface library.
+  @retval       EFI_SUCCESS                Token is created successfully.
+  @retval       EFI_OUT_OF_RESOURCES       Out of resource to create a new transport session.
+  @retval       EFI_UNSUPPORTED            Protocol is not supported on this transport interface.
+  @retval       Otherwise                  Other errors.
+
+**/
+EFI_STATUS
+AcquireTransportSession (
+  IN  EFI_GUID                       *ManageabilityProtocolSpec,
+  OUT MANAGEABILITY_TRANSPORT_TOKEN  **TransportToken
+  )
+{
+  EFI_STATUS                   Status;
+  MANAGEABILITY_TRANSPORT_KCS  *KcsTransportToken;
+
+  if (ManageabilityProtocolSpec == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: No Manageability protocol specification specified.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (TransportToken == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: TransportToken is NULL.\n", __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = HelperManageabilityCheckSupportedSpec (
+             &gManageabilityTransportKcsGuid,
+             SupportedManageabilityProtocol,
+             NumberOfSupportedProtocol,
+             ManageabilityProtocolSpec
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Protocol is not supported on this transport interface.\n", __FUNCTION__));
+    return EFI_UNSUPPORTED;
+  }
+
+  if (mSingleSessionToken != NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: This manageability transport library only supports one session transport token.\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  KcsTransportToken = AllocateZeroPool (sizeof (MANAGEABILITY_TRANSPORT_KCS));
+  if (KcsTransportToken == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to allocate memory for MANAGEABILITY_TRANSPORT_KCS\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+  KcsTransportToken->Token.Transport = AllocateZeroPool (sizeof (MANAGEABILITY_TRANSPORT));
+  if (KcsTransportToken->Token.Transport == NULL) {
+    FreePool (KcsTransportToken);
+    DEBUG ((DEBUG_ERROR, "%a: Fail to allocate memory for MANAGEABILITY_TRANSPORT\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  KcsTransportToken->Signature                                            = MANAGEABILITY_TRANSPORT_KCS_SIGNATURE;
+  KcsTransportToken->Token.ManageabilityProtocolSpecification             = ManageabilityProtocolSpec;
+  KcsTransportToken->Token.Transport->TransportVersion                    = MANAGEABILITY_TRANSPORT_TOKEN_VERSION;
+  KcsTransportToken->Token.Transport->ManageabilityTransportSpecification = &gManageabilityTransportKcsGuid;
+  KcsTransportToken->Token.Transport->TransportName                       = L"KCS";
+  KcsTransportToken->Token.Transport->Function.Version1_0                 = AllocateZeroPool (sizeof (MANAGEABILITY_TRANSPORT_FUNCTION_V1_0));
+  if (KcsTransportToken->Token.Transport->Function.Version1_0 == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to allocate memory for MANAGEABILITY_TRANSPORT_FUNCTION_V1_0\n", __FUNCTION__));
+    FreePool (KcsTransportToken);
+    FreePool (KcsTransportToken->Token.Transport);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  KcsTransportToken->Token.Transport->Function.Version1_0->TransportInit            = KcsTransportInit;
+  KcsTransportToken->Token.Transport->Function.Version1_0->TransportReset           = KcsTransportReset;
+  KcsTransportToken->Token.Transport->Function.Version1_0->TransportStatus          = KcsTransportStatus;
+  KcsTransportToken->Token.Transport->Function.Version1_0->TransportTransmitReceive = KcsTransportTransmitReceive;
+
+  mSingleSessionToken = KcsTransportToken;
+  *TransportToken     = &KcsTransportToken->Token;
+  return EFI_SUCCESS;
+}
+
+/**
+  This function returns the transport capabilities.
+
+  @param [out]  TransportFeature        Pointer to receive transport capabilities.
+                                        See the definitions of
+                                        MANAGEABILITY_TRANSPORT_CAPABILITY.
+
+**/
+VOID
+GetTransportCapability (
+  OUT MANAGEABILITY_TRANSPORT_CAPABILITY  *TransportCapability
+  )
+{
+  if (TransportCapability != NULL) {
+    *TransportCapability = 0;
+  }
+}
+
+/**
+  This function releases the manageability session.
+
+  @param [in]  TransportToken         The transport token acquired through
+                                      AcquireTransportSession.
+  @retval      EFI_SUCCESS            Token is released successfully.
+  @retval      EFI_INVALID_PARAMETER  Invalid TransportToken.
+  @retval      Otherwise              Other errors.
+
+**/
+EFI_STATUS
+ReleaseTransportSession (
+  IN MANAGEABILITY_TRANSPORT_TOKEN  *TransportToken
+  )
+{
+  EFI_STATUS                   Status;
+  MANAGEABILITY_TRANSPORT_KCS  *KcsTransportToken;
+
+  if (TransportToken == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  KcsTransportToken = MANAGEABILITY_TRANSPORT_KCS_FROM_LINK (TransportToken);
+  if (mSingleSessionToken != KcsTransportToken) {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  if (KcsTransportToken != NULL) {
+    FreePool (KcsTransportToken->Token.Transport->Function.Version1_0);
+    FreePool (KcsTransportToken->Token.Transport);
+    FreePool (KcsTransportToken);
+    mSingleSessionToken = NULL;
+    Status              = EFI_SUCCESS;
+  }
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to release KCS transport token (%r).\n", __FUNCTION__, Status));
+  }
+
+  return Status;
+}

+ 13 - 0
Features/ManageabilityPkg/Library/ManageabilityTransportKcsLib/Dxe/ManageabilityTransportKcs.uni

@@ -0,0 +1,13 @@
+// /** @file
+// KCS instance of Manageability Transport Library
+//
+// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT             #language en-US "KCS instance of Manageability Transport Library"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "KCS Manageability Transport library implementation."
+

+ 3 - 0
Features/ManageabilityPkg/ManageabilityPkg.dsc

@@ -35,6 +35,9 @@
 !include MinPlatformPkg/Include/Dsc/CorePeiLib.dsc
 !include MinPlatformPkg/Include/Dsc/CoreDxeLib.dsc
 
+[Components]
+  ManageabilityPkg/Library/ManageabilityTransportKcsLib/Dxe/DxeManageabilityTransportKcs.inf
+
 [LibraryClasses]
   ManageabilityTransportLib|ManageabilityPkg/Library/BaseManageabilityTransportNullLib/BaseManageabilityTransportNull.inf