public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Chang, Abner" <abner.chang@amd.com>
To: <devel@edk2.groups.io>
Cc: Nickle Wang <nicklew@nvidia.com>, Igor Kulchytskyy <igork@ami.com>
Subject: [edk2-staging][PATCH 1/3] RedfishPkg/Library: Redfish BMC USBNIC Host Interface
Date: Mon, 9 Jan 2023 22:11:22 +0800	[thread overview]
Message-ID: <20230109141124.622-2-abner.chang@amd.com> (raw)
In-Reply-To: <20230109141124.622-1-abner.chang@amd.com>

From: Abner Chang <abner.chang@amd.com>

BMC exposed USB NIC platform Redfish Host Interface
library implementation.

Signed-off-by: Abner Chang <abner.chang@amd.com>
Cc: Nickle Wang <nicklew@nvidia.com>
Cc: Igor Kulchytskyy <igork@ami.com>
---
 .../PlatformHostInterfaceBmcUsbNicLib.inf     |   44 +
 .../PlatformHostInterfaceBmcUsbNicLib.h       |   83 ++
 .../PlatformHostInterfaceBmcUsbNicLib.c       | 1262 +++++++++++++++++
 3 files changed, 1389 insertions(+)
 create mode 100644 RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf
 create mode 100644 RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h
 create mode 100644 RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c

diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf
new file mode 100644
index 00000000000..6150e3d8480
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf
@@ -0,0 +1,44 @@
+## @file
+#  Module to provide the platform Redfish Host Interface information
+#  of USB NIC Device exposed by BMC.
+#
+# Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001000b
+  BASE_NAME                      = PlatformHostInterfaceBmcUsbNicLib
+  FILE_GUID                      = C4837B58-225E-4352-8FDC-4C52A5D65891
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PlatformHostInterfaceBmcUsbNicLib
+
+[Sources]
+  PlatformHostInterfaceBmcUsbNicLib.c
+  PlatformHostInterfaceBmcUsbNicLib.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  NetworkPkg/NetworkPkg.dec
+  RedfishPkg/RedfishPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  IpmiLib
+  IpmiCommandLib
+  MemoryAllocationLib
+  UefiLib
+  UefiBootServicesTableLib
+
+[Protocols]
+  gEfiSimpleNetworkProtocolGuid                 ## CONSUMED
+  gEfiUsbIoProtocolGuid                         ## CONSUMED
+  gEfiDevicePathProtocolGuid                    ## CONSUMED
+
+[Depex]
+  TRUE
diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h
new file mode 100644
index 00000000000..6ec59a79892
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h
@@ -0,0 +1,83 @@
+/** @file
+*  Header file to provide the platform Redfish Host Interface information
+*  of USB NIC Device exposed by BMC.
+*
+*  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#ifndef PLATFORM_HOST_INTERFACE_BMC_USB_NIC_LIB_H_
+#define PLATFORM_HOST_INTERFACE_BMC_USB_NIC_LIB_H_
+
+#include <Uefi.h>
+#include <IndustryStandard/Ipmi.h>
+#include <IndustryStandard/IpmiNetFnApp.h>
+#include <IndustryStandard/IpmiNetFnTransport.h>
+#include <IndustryStandard/RedfishHostInterfaceIpmi.h>
+#include <IndustryStandard/SmBios.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IpmiLib.h>
+#include <Library/IpmiCommandLib.h>
+#include <Library/RedfishHostInterfaceLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/UsbIo.h>
+
+#define BMC_USB_NIC_HOST_INTERFASCE_READINESS_GUID \
+    {  \
+      0xDD96F5D7, 0x4AE1, 0x4E6C, {0xA1, 0x30, 0xA5, 0xAC, 0x77, 0xDD, 0xE4, 0xA5} \
+    }
+
+//
+// This is the structure for BMC exposed
+// USB NIC information.
+//
+typedef struct {
+  LIST_ENTRY                     NextInstance;              ///< Link to the next instance.
+  BOOLEAN                        IsExposedByBmc;            ///< Flag indicates this USB NIC is
+                                                            ///< exposed by BMC.
+  BOOLEAN                        IsSuppportedHostInterface; ///< This BMC USB NIC is supported
+                                                            ///< as Redfish host interface
+  EFI_SIMPLE_NETWORK_PROTOCOL    *ThisSnp;                  ///< The SNP instance associated with
+                                                            ///< this USB NIC.
+  EFI_USB_IO_PROTOCOL            *ThisUsbIo;                ///< The USBIO instance associated with
+                                                            ///< this USB NIC.
+  UINT16                         UsbVendorId;               ///< USB Vendor ID of this BMC exposed USB NIC.
+  UINT16                         UsbProductId;              ///< USB Product ID of this BMC exposed USB NIC.
+  UINTN                          MacAddressSize;            ///< HW address size.
+  UINT8                          *MacAddress;               ///< HW address.
+  UINT8                          IpmiLanChannelNumber;      ///< BMC IPMI Lan Channel number.
+
+  //
+  // Below is the infortmation for building SMBIOS type 42.
+  //
+  UINT8                          IpAssignedType;          ///< Redfish service IP assign type.
+  UINT8                          IpAddressFormat;         ///< Redfish service IP version.
+  UINT8                          HostIpAddressIpv4[4];    ///< Host IP address.
+  UINT8                          RedfishIpAddressIpv4[4]; ///< Redfish service IP address.
+  UINT8                          SubnetMaskIpv4[4];       ///< Subnet mask.
+  UINT8                          GatewayIpv4[4];          ///< Gateway IP address.
+  UINT16                         VLanId;                  ///< VLAN ID.
+  BOOLEAN                        CredentialBootstrapping; ///< If Credential bootstrapping is
+                                                          ///< supported.
+} HOST_INTERFACE_BMC_USB_NIC_INFO;
+
+//
+// This is the structure for caching
+// BMC IPMI LAN Channel
+//
+typedef struct {
+  LIST_ENTRY         NextInstance;            ///< Link to the next IPMI LAN Channel.
+  UINT8              Channel;                 ///< IPMI Channel number.
+  EFI_MAC_ADDRESS    MacAddress;              ///< IPMI LAN Channel MAC address.
+  UINT8              MacAddressSize;          ///< MAC address size;
+} BMC_IPMI_LAN_CHANNEL_INFO;
+#endif
diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c
new file mode 100644
index 00000000000..465ae7ecd40
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c
@@ -0,0 +1,1262 @@
+/** @file
+*  Source file to provide the platform Redfish Host Interface information
+*  of USB NIC Device exposed by BMC.
+*
+*  Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+*
+*  SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include "PlatformHostInterfaceBmcUsbNicLib.h"
+
+static EFI_GUID  mPlatformHostInterfaceBmcUsbNicReadinessGuid =
+  BMC_USB_NIC_HOST_INTERFASCE_READINESS_GUID;
+static EFI_EVENT  mPlatformHostInterfaceSnpEvent         = NULL;
+static VOID       *mPlatformHostInterfaceSnpRegistration = NULL;
+
+static LIST_ENTRY  mBmcUsbNic;
+static LIST_ENTRY  mBmcIpmiLan;
+
+/**
+  Probe if the system supports Redfish Host Interface Credentail
+  Bootstrapping.
+
+  @retval TRUE   Yes, it is supported.
+          TRUE   No, it is not supported.
+
+**/
+BOOLEAN
+ProbeRedfishCredentialBootstrap (
+  VOID
+  )
+{
+  EFI_STATUS                                  Status;
+  IPMI_BOOTSTRAP_CREDENTIALS_COMMAND_DATA     CommandData;
+  IPMI_BOOTSTRAP_CREDENTIALS_RESULT_RESPONSE  ResponseData;
+  UINT32                                      ResponseSize;
+  BOOLEAN                                     ReturnBool;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry\n", __FUNCTION__));
+
+  //
+  // IPMI callout to NetFn 2C, command 02
+  //    Request data:
+  //      Byte 1: REDFISH_IPMI_GROUP_EXTENSION
+  //      Byte 2: DisableBootstrapControl
+  //
+  CommandData.GroupExtensionId        = REDFISH_IPMI_GROUP_EXTENSION;
+  CommandData.DisableBootstrapControl = REDFISH_IPMI_BOOTSTRAP_CREDENTIAL_ENABLE;
+  ResponseData.CompletionCode         = IPMI_COMP_CODE_UNSPECIFIED;
+  ResponseSize                        = sizeof (ResponseData);
+  //
+  //  Response data: Ignored.
+  //
+  Status = IpmiSubmitCommand (
+             IPMI_NETFN_GROUP_EXT,
+             REDFISH_IPMI_GET_BOOTSTRAP_CREDENTIALS_CMD,
+             (UINT8 *)&CommandData,
+             sizeof (CommandData),
+             (UINT8 *)&ResponseData,
+             &ResponseSize
+             );
+  if (!EFI_ERROR (Status) &&
+      ((ResponseData.CompletionCode == IPMI_COMP_CODE_NORMAL) ||
+       (ResponseData.CompletionCode == REDFISH_IPMI_COMP_CODE_BOOTSTRAP_CREDENTIAL_DISABLED)
+      ))
+  {
+    DEBUG ((DEBUG_INFO, "    Redfish Credentail Bootstrapping is supported\n", __FUNCTION__));
+    ReturnBool = TRUE;
+  } else {
+    DEBUG ((DEBUG_INFO, "    Redfish Credentail Bootstrapping is not supported\n", __FUNCTION__));
+    ReturnBool = FALSE;
+  }
+
+  return ReturnBool;
+}
+
+/**
+  Get platform Redfish host interface device descriptor.
+
+  @param[in] DeviceType         Pointer to retrieve device type.
+  @param[out] DeviceDescriptor  Pointer to retrieve REDFISH_INTERFACE_DATA, caller has to free
+                                this memory using FreePool().
+
+  @retval EFI_NOT_FOUND   No Redfish host interface descriptor provided on this platform.
+
+**/
+EFI_STATUS
+RedfishPlatformHostInterfaceDeviceDescriptor (
+  IN UINT8                    *DeviceType,
+  OUT REDFISH_INTERFACE_DATA  **DeviceDescriptor
+  )
+{
+  HOST_INTERFACE_BMC_USB_NIC_INFO  *ThisInstance;
+  REDFISH_INTERFACE_DATA           *InterfaceData;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry\n", __FUNCTION__));
+
+  if (IsListEmpty (&mBmcUsbNic)) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Check if BMC exposed USB NIC is found and ready for using.
+  ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)GetFirstNode (&mBmcUsbNic);
+  while (TRUE) {
+    if (ThisInstance->IsExposedByBmc && ThisInstance->IsSuppportedHostInterface) {
+      *DeviceType = REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2;
+
+      // Fill up REDFISH_INTERFACE_DATA defined in Redfish host interface spec v1.3
+      InterfaceData = (REDFISH_INTERFACE_DATA *)AllocateZeroPool (USB_INTERFACE_DEVICE_DESCRIPTOR_V2_SIZE_1_3);
+      if (InterfaceData == NULL) {
+        DEBUG ((DEBUG_ERROR, "Failed to allocate memory for REDFISH_INTERFACE_DATA\n"));
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      InterfaceData->DeviceType                                   = REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2;
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.Length          = USB_INTERFACE_DEVICE_DESCRIPTOR_V2_SIZE_1_3;
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.IdVendor        = ThisInstance->UsbVendorId;
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.IdProduct       = ThisInstance->UsbProductId;
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.SerialNumberStr = 0;
+      CopyMem (
+        (VOID *)&InterfaceData->DeviceDescriptor.UsbDeviceV2.MacAddress,
+        (VOID *)&ThisInstance->MacAddress,
+        sizeof (InterfaceData->DeviceDescriptor.UsbDeviceV2.MacAddress)
+        );
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.Characteristics              |= (UINT16)ThisInstance->CredentialBootstrapping;
+      InterfaceData->DeviceDescriptor.UsbDeviceV2.CredentialBootstrappingHandle = 0;
+      *DeviceDescriptor                                                         = InterfaceData;
+      DEBUG ((DEBUG_INFO, "    REDFISH_INTERFACE_DATA is returned successfully.\n"));
+      return EFI_SUCCESS;
+    }
+
+    if (IsNodeAtEnd (&mBmcUsbNic, &ThisInstance->NextInstance)) {
+      break;
+    }
+
+    ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)
+                   GetNextNode (&mBmcUsbNic, &ThisInstance->NextInstance);
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Get platform Redfish host interface protocol data.
+  Caller should pass NULL in ProtocolRecord to retrive the first protocol record.
+  Then continuously pass previous ProtocolRecord for retrieving the next ProtocolRecord.
+
+  @param[in, out] ProtocolRecord  Pointer to retrieve the first or the next protocol record.
+                                  caller has to free the new protocol record returned from
+                                  this function using FreePool().
+  @param[in] IndexOfProtocolData  The index of protocol data.
+
+  @retval EFI_NOT_FOUND   No more protocol records.
+
+**/
+EFI_STATUS
+RedfishPlatformHostInterfaceProtocolData (
+  IN OUT MC_HOST_INTERFACE_PROTOCOL_RECORD  **ProtocolRecord,
+  IN UINT8                                  IndexOfProtocolData
+  )
+{
+  HOST_INTERFACE_BMC_USB_NIC_INFO    *ThisInstance;
+  MC_HOST_INTERFACE_PROTOCOL_RECORD  *ThisProtocolRecord;
+  REDFISH_OVER_IP_PROTOCOL_DATA      *RedfishOverIpData;
+  UINT8                              HostNameLength;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry\n", __FUNCTION__));
+
+  if (IsListEmpty (&mBmcUsbNic) || (IndexOfProtocolData > 0)) {
+    return EFI_NOT_FOUND;
+  }
+
+  ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)GetFirstNode (&mBmcUsbNic);
+  while (TRUE) {
+    if (ThisInstance->IsExposedByBmc  && ThisInstance->IsSuppportedHostInterface) {
+      HostNameLength     = 0;
+      ThisProtocolRecord = (MC_HOST_INTERFACE_PROTOCOL_RECORD *)AllocateZeroPool (
+                                                                  sizeof (MC_HOST_INTERFACE_PROTOCOL_RECORD) - 1 +
+                                                                  sizeof (REDFISH_OVER_IP_PROTOCOL_DATA) +
+                                                                  HostNameLength
+                                                                  );
+      if (ThisProtocolRecord == NULL) {
+        DEBUG ((DEBUG_ERROR, "    Aloocate memory fail for MC_HOST_INTERFACE_PROTOCOL_RECORD.\n"));
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      ThisProtocolRecord->ProtocolType        = MCHostInterfaceProtocolTypeRedfishOverIP;
+      ThisProtocolRecord->ProtocolTypeDataLen = sizeof (REDFISH_OVER_IP_PROTOCOL_DATA) + HostNameLength;
+      RedfishOverIpData                       = (REDFISH_OVER_IP_PROTOCOL_DATA *)&ThisProtocolRecord->ProtocolTypeData[0];
+      //
+      // Fill up REDFISH_OVER_IP_PROTOCOL_DATA
+      //
+
+      // ServiceUuid TODO: Redfish Host Interface spec update.
+      // RedfishOverIpData->ServiceUuid = ;
+
+      // HostIpAddressFormat and RedfishServiceIpDiscoveryType
+      RedfishOverIpData->HostIpAssignmentType          = RedfishHostIpAssignmentUnknown;
+      RedfishOverIpData->RedfishServiceIpDiscoveryType = RedfishHostIpAssignmentUnknown;
+      if (ThisInstance->IpAssignedType == IpmiStaticAddrsss) {
+        RedfishOverIpData->HostIpAssignmentType          = RedfishHostIpAssignmentStatic;
+        RedfishOverIpData->RedfishServiceIpDiscoveryType = RedfishHostIpAssignmentStatic;
+      } else if (ThisInstance->IpAssignedType == IpmiDynamicAddressBmcDhcp) {
+        RedfishOverIpData->HostIpAssignmentType          = RedfishHostIpAssignmentDhcp;
+        RedfishOverIpData->RedfishServiceIpDiscoveryType = RedfishHostIpAssignmentDhcp;
+      }
+
+      // HostIpAddressFormat and RedfishServiceIpAddressFormat, only support IPv4 for now.
+      RedfishOverIpData->HostIpAddressFormat           = REDFISH_HOST_INTERFACE_HOST_IP_ADDRESS_FORMAT_IP4;
+      RedfishOverIpData->RedfishServiceIpAddressFormat = REDFISH_HOST_INTERFACE_HOST_IP_ADDRESS_FORMAT_IP4;
+
+      // HostIpAddress
+      CopyMem (
+        (VOID *)RedfishOverIpData->HostIpAddress,
+        (VOID *)ThisInstance->HostIpAddressIpv4,
+        sizeof (ThisInstance->HostIpAddressIpv4)
+        );
+
+      // HostIpMask and RedfishServiceIpMask
+      CopyMem (
+        (VOID *)RedfishOverIpData->HostIpMask,
+        (VOID *)ThisInstance->SubnetMaskIpv4,
+        sizeof (ThisInstance->SubnetMaskIpv4)
+        );
+      CopyMem (
+        (VOID *)RedfishOverIpData->RedfishServiceIpMask,
+        (VOID *)ThisInstance->SubnetMaskIpv4,
+        sizeof (ThisInstance->SubnetMaskIpv4)
+        );
+
+      // RedfishServiceIpAddress
+      CopyMem (
+        (VOID *)RedfishOverIpData->RedfishServiceIpAddress,
+        (VOID *)ThisInstance->RedfishIpAddressIpv4,
+        sizeof (ThisInstance->RedfishIpAddressIpv4)
+        );
+
+      // RedfishServiceIpPort
+      RedfishOverIpData->RedfishServiceIpPort = 0;
+
+      // RedfishServiceVlanId
+      RedfishOverIpData->RedfishServiceVlanId = ThisInstance->VLanId;
+
+      // RedfishServiceHostnameLength
+      RedfishOverIpData->RedfishServiceHostnameLength = 0;
+
+      // RedfishServiceHostname TODO: Redfish Host Interface spec update.
+      // RedfishOverIpData->RedfishServiceHostname = ;
+
+      DEBUG ((DEBUG_INFO, "    MC_HOST_INTERFACE_PROTOCOL_RECORD is returned successfully.\n"));
+      *ProtocolRecord = ThisProtocolRecord;
+      return EFI_SUCCESS;
+    }
+
+    if (IsNodeAtEnd (&mBmcUsbNic, &ThisInstance->NextInstance)) {
+      break;
+    }
+
+    ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)
+                   GetNextNode (&mBmcUsbNic, &ThisInstance->NextInstance);
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function retrieve the information of BMC USB NIC.
+
+  @retval EFI_SUCCESS      All necessary information is retrieved.
+  @retval EFI_NOT_FOUND    There is no BMC exposed USB NIC.
+  @retval Others           Other errors.
+
+**/
+EFI_STATUS
+RetrievedBmcUsbNicInfo (
+  VOID
+  )
+{
+  EFI_STATUS                                      Status;
+  UINT32                                          ResponseDataSize;
+  HOST_INTERFACE_BMC_USB_NIC_INFO                 *ThisInstance;
+  IPMI_GET_LAN_CONFIGURATION_PARAMETERS_REQUEST   GetLanConfigReq;
+  IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE  *GetLanConfigReps;
+  IPMI_LAN_IP_ADDRESS_SRC                         *IpAddressSrc;
+  IPMI_LAN_IP_ADDRESS                             *DestIpAddress;
+  IPMI_LAN_SUBNET_MASK                            *SubnetMask;
+  IPMI_LAN_DEFAULT_GATEWAY                        *DefaultGateway;
+  IPMI_LAN_VLAN_ID                                *LanVlanId;
+  EFI_USB_DEVICE_DESCRIPTOR                       UsbDeviceDescriptor;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry\n", __FUNCTION__));
+
+  if (IsListEmpty (&mBmcUsbNic)) {
+    return EFI_NOT_FOUND;
+  }
+
+  ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)GetFirstNode (&mBmcUsbNic);
+  while (TRUE) {
+    if (ThisInstance->IsExposedByBmc) {
+      ThisInstance->IsSuppportedHostInterface = FALSE;
+
+      // Probe if Redfish Host Interface Credential Bootstrapping is supported.
+      ThisInstance->CredentialBootstrapping = ProbeRedfishCredentialBootstrap ();
+
+      // Get IP address source
+      GetLanConfigReq.SetSelector                     = 0;
+      GetLanConfigReq.BlockSelector                   = 0;
+      GetLanConfigReq.ChannelNumber.Bits.ChannelNo    = ThisInstance->IpmiLanChannelNumber;
+      GetLanConfigReq.ChannelNumber.Bits.GetParameter = 0;
+      GetLanConfigReq.ChannelNumber.Bits.Reserved     = 0;
+      GetLanConfigReq.ParameterSelector               = IpmiLanIpAddressSource;
+      ResponseDataSize                                = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) + sizeof (IPMI_LAN_IP_ADDRESS_SRC);
+      GetLanConfigReps                                = (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+      GetLanConfigReps->CompletionCode                = IPMI_COMP_CODE_UNSPECIFIED;
+      Status                                          = IpmiGetLanConfigurationParameters (
+                                                          &GetLanConfigReq,
+                                                          GetLanConfigReps,
+                                                          &ResponseDataSize
+                                                          );
+      if (EFI_ERROR (Status) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+        DEBUG ((DEBUG_ERROR, "    Failed to get IP address source at channel %d: %r, 0x%02x.\n", ThisInstance->IpmiLanChannelNumber, Status, GetLanConfigReps->CompletionCode));
+        FreePool (GetLanConfigReps);
+        return Status;
+      }
+
+      IpAddressSrc = (IPMI_LAN_IP_ADDRESS_SRC *)(GetLanConfigReps + 1);
+      DEBUG ((DEBUG_INFO, "    IP address source at channel %d: %x\n", ThisInstance->IpmiLanChannelNumber, IpAddressSrc->Bits.AddressSrc));
+      ThisInstance->IpAssignedType = IpAddressSrc->Bits.AddressSrc;
+      FreePool (GetLanConfigReps);
+
+      // Get LAN IPv4 IP address
+      GetLanConfigReq.ParameterSelector = IpmiLanIpAddress;
+      ResponseDataSize                  = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) + sizeof (IPMI_LAN_IP_ADDRESS);
+      GetLanConfigReps                  = (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+      GetLanConfigReps->CompletionCode  = IPMI_COMP_CODE_UNSPECIFIED;
+      Status                            = IpmiGetLanConfigurationParameters (
+                                            &GetLanConfigReq,
+                                            GetLanConfigReps,
+                                            &ResponseDataSize
+                                            );
+      if (EFI_ERROR (Status) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+        DEBUG ((DEBUG_ERROR, "    Failed to get Dest IP address at channel %d: %r, 0x%02x.\n", ThisInstance->IpmiLanChannelNumber, Status, GetLanConfigReps->CompletionCode));
+        FreePool (GetLanConfigReps);
+        return Status;
+      }
+
+      DestIpAddress = (IPMI_LAN_IP_ADDRESS *)(GetLanConfigReps + 1);
+      DEBUG ((
+        DEBUG_INFO,
+        "    Dest IP address at channel %d: %d.%d.%d.%d\n",
+        ThisInstance->IpmiLanChannelNumber,
+        DestIpAddress->IpAddress[0],
+        DestIpAddress->IpAddress[1],
+        DestIpAddress->IpAddress[2],
+        DestIpAddress->IpAddress[3]
+        ));
+      CopyMem ((VOID *)&ThisInstance->RedfishIpAddressIpv4, (VOID *)&DestIpAddress->IpAddress, sizeof (DestIpAddress->IpAddress));
+      //
+      // According to UEFI spec, the IP address at BMC USB NIC host end is the IP address at BMC end minus 1.
+      //
+      CopyMem ((VOID *)&ThisInstance->HostIpAddressIpv4, (VOID *)&DestIpAddress->IpAddress, sizeof (DestIpAddress->IpAddress));
+      ThisInstance->HostIpAddressIpv4[sizeof (ThisInstance->HostIpAddressIpv4) - 1] -= 1;
+      FreePool (GetLanConfigReps);
+      DEBUG ((
+        DEBUG_INFO,
+        "    Host IP address at channel %d: %d.%d.%d.%d\n",
+        ThisInstance->IpmiLanChannelNumber,
+        ThisInstance->HostIpAddressIpv4[0],
+        ThisInstance->HostIpAddressIpv4[1],
+        ThisInstance->HostIpAddressIpv4[2],
+        ThisInstance->HostIpAddressIpv4[3]
+        ));
+
+      // Get IPv4 subnet mask
+      GetLanConfigReq.ParameterSelector = IpmiLanSubnetMask;
+      ResponseDataSize                  = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) + sizeof (IPMI_LAN_SUBNET_MASK);
+      GetLanConfigReps                  = (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+      GetLanConfigReps->CompletionCode  = IPMI_COMP_CODE_UNSPECIFIED;
+      Status                            = IpmiGetLanConfigurationParameters (
+                                            &GetLanConfigReq,
+                                            GetLanConfigReps,
+                                            &ResponseDataSize
+                                            );
+      if ((EFI_ERROR (Status)) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+        DEBUG ((DEBUG_ERROR, "    Failed to get subnet mask at channel %d: %r, 0x%02x.\n", ThisInstance->IpmiLanChannelNumber, Status, GetLanConfigReps->CompletionCode));
+        FreePool (GetLanConfigReps);
+        return Status;
+      }
+
+      SubnetMask = (IPMI_LAN_SUBNET_MASK *)(GetLanConfigReps + 1);
+      DEBUG ((
+        DEBUG_INFO,
+        "    Subnet mask at channel %d: %d.%d.%d.%d\n",
+        ThisInstance->IpmiLanChannelNumber,
+        SubnetMask->IpAddress[0],
+        SubnetMask->IpAddress[1],
+        SubnetMask->IpAddress[2],
+        SubnetMask->IpAddress[3]
+        ));
+      CopyMem ((VOID *)&ThisInstance->SubnetMaskIpv4, (VOID *)&SubnetMask->IpAddress, sizeof (SubnetMask->IpAddress));
+      FreePool (GetLanConfigReps);
+
+      // Get Gateway IP address.
+      GetLanConfigReq.ParameterSelector = IpmiLanDefaultGateway;
+      ResponseDataSize                  = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) + sizeof (IPMI_LAN_DEFAULT_GATEWAY);
+      GetLanConfigReps                  = (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+      GetLanConfigReps->CompletionCode  = IPMI_COMP_CODE_UNSPECIFIED;
+      Status                            = IpmiGetLanConfigurationParameters (
+                                            &GetLanConfigReq,
+                                            GetLanConfigReps,
+                                            &ResponseDataSize
+                                            );
+      if ((EFI_ERROR (Status)) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+        DEBUG ((DEBUG_ERROR, "    Failed to get default gateway at channel %d: %r, 0x%02x.\n", ThisInstance->IpmiLanChannelNumber, Status, GetLanConfigReps->CompletionCode));
+        FreePool (GetLanConfigReps);
+        return Status;
+      }
+
+      DefaultGateway = (IPMI_LAN_DEFAULT_GATEWAY *)(GetLanConfigReps + 1);
+      DEBUG ((
+        DEBUG_INFO,
+        "    Gateway at channel %d: %d.%d.%d.%d\n",
+        ThisInstance->IpmiLanChannelNumber,
+        DefaultGateway->IpAddress[0],
+        DefaultGateway->IpAddress[1],
+        DefaultGateway->IpAddress[2],
+        DefaultGateway->IpAddress[3]
+        ));
+      CopyMem ((VOID *)&ThisInstance->GatewayIpv4, (VOID *)&DefaultGateway->IpAddress, sizeof (DefaultGateway->IpAddress));
+      FreePool (GetLanConfigReps);
+
+      // Get VLAN ID
+      GetLanConfigReq.ParameterSelector = IpmiLanVlanId;
+      ResponseDataSize                  = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) + sizeof (IPMI_LAN_VLAN_ID);
+      GetLanConfigReps                  = (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+      GetLanConfigReps->CompletionCode  = IPMI_COMP_CODE_UNSPECIFIED;
+      Status                            = IpmiGetLanConfigurationParameters (
+                                            &GetLanConfigReq,
+                                            GetLanConfigReps,
+                                            &ResponseDataSize
+                                            );
+      if ((EFI_ERROR (Status)) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+        DEBUG ((DEBUG_ERROR, "    Failed to get VLAN ID at channel %d: %r, 0x%02x.\n", ThisInstance->IpmiLanChannelNumber, Status, GetLanConfigReps->CompletionCode));
+        FreePool (GetLanConfigReps);
+        return Status;
+      }
+
+      LanVlanId            = (IPMI_LAN_VLAN_ID *)(GetLanConfigReps + 1);
+      ThisInstance->VLanId = 0;
+      if (LanVlanId->Data2.Bits.Enabled == 1) {
+        ThisInstance->VLanId = LanVlanId->Data1.VanIdLowByte | (LanVlanId->Data2.Bits.VanIdHighByte << 8);
+      }
+
+      DEBUG ((DEBUG_INFO, "    VLAN ID %x\n", ThisInstance->VLanId));
+
+      FreePool (GetLanConfigReps);
+
+      //
+      // Read USB device information.
+      //
+      if (ThisInstance->ThisUsbIo != NULL) {
+        Status = ThisInstance->ThisUsbIo->UsbGetDeviceDescriptor (ThisInstance->ThisUsbIo, &UsbDeviceDescriptor);
+        if (!EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_INFO, "    USB NIC Vendor ID: 0x%04x, Device ID: 0x%04x\n", UsbDeviceDescriptor.IdVendor, UsbDeviceDescriptor.IdProduct));
+          ThisInstance->UsbVendorId  = UsbDeviceDescriptor.IdVendor;
+          ThisInstance->UsbProductId = UsbDeviceDescriptor.IdProduct;
+        } else {
+          DEBUG ((DEBUG_INFO, "    Fail to get USB device descriptor.\n"));
+        }
+      }
+
+      // All information is retrieved.
+      ThisInstance->IsSuppportedHostInterface = TRUE;
+      return EFI_SUCCESS;
+    }
+
+    if (IsNodeAtEnd (&mBmcUsbNic, &ThisInstance->NextInstance)) {
+      break;
+    }
+
+    ThisInstance = (HOST_INTERFACE_BMC_USB_NIC_INFO *)
+                   GetNextNode (&mBmcUsbNic, &ThisInstance->NextInstance);
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function caches the found IPMI LAN channel. So we
+  don't have to sedn IPMI commands again if the USB NIC is
+  connected later.
+
+  @param[in] ChannelNum                The IPMI channel number.
+  @param[in] IpmiLanChannelMacAddress  Pointer to EFI_MAC_ADDRESS.
+  @param[in] IpmiLanMacAddressSize     The MAC address size.
+
+  @retval EFI_SUCCESS          IPMI LAN channel is cached.
+  @retval EFI_OUT_OF_RESOURCE  Memory allocated failed.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+CacheIpmiLanMac (
+  IN UINT8            ChannelNum,
+  IN EFI_MAC_ADDRESS  *IpmiLanChannelMacAddress,
+  IN UINT8            IpmiLanMacAddressSize
+  )
+{
+  BMC_IPMI_LAN_CHANNEL_INFO  *ChannelInfo;
+
+  ChannelInfo = (BMC_IPMI_LAN_CHANNEL_INFO *)AllocateZeroPool (sizeof (BMC_IPMI_LAN_CHANNEL_INFO));
+  if (ChannelInfo == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ChannelInfo->Channel = ChannelNum;
+  CopyMem ((VOID *)&ChannelInfo->MacAddress.Addr, (VOID *)IpmiLanChannelMacAddress->Addr, IpmiLanMacAddressSize);
+  ChannelInfo->MacAddressSize = IpmiLanMacAddressSize;
+  InitializeListHead (&ChannelInfo->NextInstance);
+  InsertTailList (&mBmcIpmiLan, &ChannelInfo->NextInstance);
+  return EFI_SUCCESS;
+}
+
+/**
+  This function checks if the IPMI channel already identified
+  previously.
+
+  @param[in]  ChannelNum            The IPMI channel number.
+  @param[out] CachedIpmiLanChannel  Pointer to retrieve the cached
+                                    BMC_IPMI_LAN_CHANNEL_INFO.
+
+  @retval EFI_SUCCESS          IPMI LAN channel is found.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+CheckCachedIpmiLanMac (
+  IN UINT8                       ChannelNum,
+  OUT BMC_IPMI_LAN_CHANNEL_INFO  **CachedIpmiLanChannel
+  )
+{
+  BMC_IPMI_LAN_CHANNEL_INFO  *ThisInstance;
+
+  if (IsListEmpty (&mBmcIpmiLan)) {
+    return EFI_NOT_FOUND;
+  }
+
+  ThisInstance = (BMC_IPMI_LAN_CHANNEL_INFO *)GetFirstNode (&mBmcIpmiLan);
+  while (TRUE) {
+    if (ThisInstance->Channel == ChannelNum) {
+      *CachedIpmiLanChannel = ThisInstance;
+      return EFI_SUCCESS;
+    }
+
+    if (IsNodeAtEnd (&mBmcIpmiLan, &ThisInstance->NextInstance)) {
+      break;
+    }
+
+    ThisInstance = (BMC_IPMI_LAN_CHANNEL_INFO *)
+                   GetNextNode (&mBmcIpmiLan, &ThisInstance->NextInstance);
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function goes through IPMI channels to find the
+  mactched MAC addrss of BMC USB NIC endpoint.
+
+  @param[in] UsbNicInfo  The instance of HOST_INTERFACE_BMC_USB_NIC_INFO.
+
+  @retval EFI_SUCCESS          Yes, USB NIC exposed by BMC is found.
+  @retval EFI_NOT_FOUND        No, USB NIC exposed by BMC is not found
+                               on the existing SNP handle.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+HostInterfaceIpmiCheckMacAddress (
+  IN HOST_INTERFACE_BMC_USB_NIC_INFO  *UsbNicInfo
+  )
+{
+  EFI_STATUS                                      Status;
+  EFI_STATUS                                      ExitStatus;
+  UINTN                                           ChannelNum;
+  UINT32                                          ResponseDataSize;
+  IPMI_GET_CHANNEL_INFO_REQUEST                   GetChanelInfoRequest;
+  IPMI_GET_CHANNEL_INFO_RESPONSE                  GetChanelInfoResponse;
+  IPMI_GET_LAN_CONFIGURATION_PARAMETERS_REQUEST   GetLanConfigReq;
+  IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE  *GetLanConfigReps;
+  BMC_IPMI_LAN_CHANNEL_INFO                       *CachedIpmiLanChannel;
+  UINT8                                           IpmiLanMacAddressSize;
+  EFI_MAC_ADDRESS                                 IpmiLanChannelMacAddress;
+  BOOLEAN                                         AlreadyCached;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry.\n", __FUNCTION__));
+
+  GetLanConfigReps = NULL;
+  AlreadyCached    = FALSE;
+  if (!IsListEmpty (&mBmcIpmiLan)) {
+    AlreadyCached = TRUE;
+  }
+
+  // Initial the get MAC address request.
+  GetLanConfigReq.SetSelector       = 0;
+  GetLanConfigReq.BlockSelector     = 0;
+  GetLanConfigReq.ParameterSelector = IpmiLanMacAddress;
+
+  ExitStatus = EFI_NOT_FOUND;
+  for (ChannelNum = IPMI_CHANNEL_NUMBER_IMPLEMENTATION_SPECIFIC_1;
+       ChannelNum <= IPMI_CHANNEL_NUMBER_IMPLEMENTATION_SPECIFIC_11;
+       ChannelNum++)
+  {
+    IpmiLanMacAddressSize = 0;
+
+    // Check if the IPMI channel information is already cached.
+    Status = EFI_NOT_FOUND;
+    if (AlreadyCached) {
+      Status = CheckCachedIpmiLanMac ((UINT8)ChannelNum, &CachedIpmiLanChannel);
+    }
+
+    if (Status == EFI_SUCCESS) {
+      DEBUG ((DEBUG_INFO, "  Got cached IPMI LAN info.\n"));
+      IpmiLanMacAddressSize = sizeof (IPMI_LAN_MAC_ADDRESS);
+      CopyMem ((VOID *)&IpmiLanChannelMacAddress.Addr, (VOID *)&CachedIpmiLanChannel->MacAddress.Addr, IpmiLanMacAddressSize);
+    } else {
+      DEBUG ((DEBUG_INFO, "  No cached IPMI LAN info\n"));
+      DEBUG ((DEBUG_INFO, "  Send NetFn = App, Command = 0x42 to channel %d\n", ChannelNum));
+      GetChanelInfoRequest.ChannelNumber.Bits.ChannelNo = (UINT8)ChannelNum;
+      Status                                            = IpmiGetChannelInfo (
+                                                            &GetChanelInfoRequest,
+                                                            &GetChanelInfoResponse,
+                                                            &ResponseDataSize
+                                                            );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "    - Fails to send command.\n", ChannelNum));
+        continue;
+      }
+
+      DEBUG ((DEBUG_INFO, "    - Response data size = 0x%x\n", ResponseDataSize));
+      if ((GetChanelInfoResponse.CompletionCode != IPMI_COMP_CODE_NORMAL) || (ResponseDataSize == 0)) {
+        DEBUG ((DEBUG_ERROR, "    - Command returned fail: 0x%x.\n", GetChanelInfoResponse.CompletionCode));
+        continue;
+      }
+
+      DEBUG ((
+        DEBUG_INFO,
+        "    - Channel protocol = 0x%x, Media = 0x%x\n",
+        GetChanelInfoResponse.ProtocolType.Bits.ChannelProtocolType,
+        GetChanelInfoResponse.MediumType.Bits.ChannelMediumType
+        ));
+
+      if (GetChanelInfoResponse.ChannelNumber.Bits.ChannelNo != ChannelNum) {
+        DEBUG ((
+          DEBUG_ERROR,
+          "    - ChannelNumber = %d in the response which is not macthed to the request.\n",
+          GetChanelInfoResponse.ChannelNumber.Bits.ChannelNo
+          ));
+        continue;
+      }
+
+      if ((GetChanelInfoResponse.MediumType.Bits.ChannelMediumType == IPMI_CHANNEL_MEDIA_TYPE_802_3_LAN) &&
+          (GetChanelInfoResponse.ProtocolType.Bits.ChannelProtocolType == IPMI_CHANNEL_PROTOCOL_TYPE_IPMB_1_0))
+      {
+        DEBUG ((DEBUG_INFO, "    - Channel %d is a LAN device!\n", ChannelNum));
+
+        ResponseDataSize = sizeof (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE) +
+                           sizeof (IPMI_LAN_MAC_ADDRESS);
+        if (GetLanConfigReps == NULL) {
+          GetLanConfigReps =
+            (IPMI_GET_LAN_CONFIGURATION_PARAMETERS_RESPONSE *)AllocateZeroPool (ResponseDataSize);
+          if (GetLanConfigReps == NULL) {
+            DEBUG ((DEBUG_ERROR, "    Allocate memory failed for getting MAC address.\n"));
+            continue;
+          }
+        }
+
+        GetLanConfigReq.ChannelNumber.Bits.ChannelNo = (UINT8)ChannelNum;
+        GetLanConfigReps->CompletionCode             = IPMI_COMP_CODE_UNSPECIFIED;
+        Status                                       = IpmiGetLanConfigurationParameters (
+                                                         &GetLanConfigReq,
+                                                         GetLanConfigReps,
+                                                         &ResponseDataSize
+                                                         );
+        if (EFI_ERROR (Status) || (GetLanConfigReps->CompletionCode != IPMI_COMP_CODE_NORMAL)) {
+          DEBUG ((
+            DEBUG_ERROR,
+            "    Fails to get MAC address of channel %d, CompletionCode = %02x.\n",
+            ChannelNum,
+            GetLanConfigReps->CompletionCode
+            ));
+          continue;
+        } else {
+          DEBUG ((DEBUG_INFO, "    The MAC address of channel %d.\n", ChannelNum));
+          DEBUG ((
+            DEBUG_INFO,
+            "      %02x:%02x:%02x:%02x:%02x:%02x\n",
+            *((UINT8 *)(GetLanConfigReps + 1) + 0),
+            *((UINT8 *)(GetLanConfigReps + 1) + 1),
+            *((UINT8 *)(GetLanConfigReps + 1) + 2),
+            *((UINT8 *)(GetLanConfigReps + 1) + 3),
+            *((UINT8 *)(GetLanConfigReps + 1) + 4),
+            *((UINT8 *)(GetLanConfigReps + 1) + 5)
+            ));
+          IpmiLanMacAddressSize = sizeof (IPMI_LAN_MAC_ADDRESS);
+          CopyMem ((VOID *)&IpmiLanChannelMacAddress.Addr, (VOID *)(GetLanConfigReps + 1), IpmiLanMacAddressSize);
+        }
+      }
+    }
+
+    if (IpmiLanMacAddressSize != 0) {
+      if (!AlreadyCached) {
+        // Cache this IPMI LAN channel.
+        DEBUG ((DEBUG_INFO, "    Cache this IPMI LAN channel.\n"));
+        CacheIpmiLanMac ((UINT8)ChannelNum, &IpmiLanChannelMacAddress, IpmiLanMacAddressSize);
+      }
+
+      //
+      // According to UEFI spec section: TBD.
+      // Compare the first five MAC address and
+      // the 6th MAC address.
+      //
+      if ((IpmiLanMacAddressSize != UsbNicInfo->MacAddressSize) ||
+          (CompareMem (
+             (VOID *)UsbNicInfo->MacAddress,
+             (VOID *)&IpmiLanChannelMacAddress.Addr,
+             IpmiLanMacAddressSize - 1
+             ) != 0) ||
+          (IpmiLanChannelMacAddress.Addr[IpmiLanMacAddressSize - 1] !=
+           *(UsbNicInfo->MacAddress + IpmiLanMacAddressSize - 1) - 1)
+          )
+      {
+        DEBUG ((DEBUG_INFO, "    MAC address is not matched.\n"));
+        continue;
+      }
+
+      // This is the NIC exposed by BMC.
+      UsbNicInfo->IpmiLanChannelNumber = (UINT8)ChannelNum;
+      UsbNicInfo->IsExposedByBmc       = TRUE;
+      DEBUG ((DEBUG_INFO, "    MAC address is matched.\n"));
+      ExitStatus = EFI_SUCCESS;
+      break;
+    }
+  }
+
+  if (GetLanConfigReps != NULL) {
+    FreePool (GetLanConfigReps);
+  }
+
+  return ExitStatus;
+}
+
+/**
+  This function searches the next MSG_USB_DP device path node.
+
+  @param[in]  ThisDevicePath    Device path to search.
+
+  @retval NULL          MSG_USB_DP is not found.
+          Otherwise     MSG_USB_DP is found.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+UsbNicGetNextMsgUsbDp (
+  IN EFI_DEVICE_PATH_PROTOCOL  *ThisDevicePath
+  )
+{
+  if (ThisDevicePath == NULL) {
+    return NULL;
+  }
+
+  while (TRUE) {
+    ThisDevicePath = NextDevicePathNode (ThisDevicePath);
+    if (IsDevicePathEnd (ThisDevicePath)) {
+      return NULL;
+    }
+
+    if ((ThisDevicePath->Type == MESSAGING_DEVICE_PATH) && (ThisDevicePath->SubType == MSG_USB_DP)) {
+      return ThisDevicePath;
+    }
+  }
+
+  return NULL;
+}
+
+/**
+  This function search the UsbIo handle that matches the UsbDevicePath.
+
+  @param[in]  UsbDevicePath    Device path of this SNP handle.
+  @param[out] UsbIo            Return the UsbIo protocol.
+
+  @retval EFI_SUCCESS          Yes, UsbIo protocl is found.
+  @retval EFI_NOT_FOUND        No, UsbIo protocl is not found
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+UsbNicSearchUsbIo (
+  IN   EFI_DEVICE_PATH_PROTOCOL  *UsbDevicePath,
+  OUT  EFI_USB_IO_PROTOCOL       **UsbIo
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     BufferSize;
+  EFI_HANDLE                *HandleBuffer;
+  UINT16                    Length;
+  UINTN                     Index;
+  CHAR16                    *DevicePathStr;
+  EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *ThisDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *ThisDevicePathEnd;
+  EFI_DEVICE_PATH_PROTOCOL  *ThisUsbDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *ThisUsbDevicePathEnd;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry.\n", __FUNCTION__));
+  DEBUG ((DEBUG_INFO, "Device path on the EFI handle which has UsbIo and SNP instaleld on it.\n"));
+  DevicePathStr = ConvertDevicePathToText (UsbDevicePath, FALSE, FALSE);
+  DEBUG ((DEBUG_INFO, "%s\n", DevicePathStr));
+  FreePool (DevicePathStr);
+
+  BufferSize   = 0;
+  HandleBuffer = NULL;
+  *UsbIo       = NULL;
+  Status       = gBS->LocateHandle (
+                        ByProtocol,
+                        &gEfiUsbIoProtocolGuid,
+                        NULL,
+                        &BufferSize,
+                        NULL
+                        );
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    DEBUG ((DEBUG_INFO, "  %d UsbIo protocol instances.\n", BufferSize/sizeof (EFI_HANDLE)));
+    HandleBuffer = AllocateZeroPool (BufferSize);
+    if (HandleBuffer == NULL) {
+      DEBUG ((DEBUG_ERROR, "    Falied to allocate buffer for the handles.\n"));
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = gBS->LocateHandle (
+                    ByProtocol,
+                    &gEfiUsbIoProtocolGuid,
+                    NULL,
+                    &BufferSize,
+                    HandleBuffer
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "  Falied to locate UsbIo protocol handles.\n"));
+      FreePool (HandleBuffer);
+      return Status;
+    }
+  } else {
+    return Status;
+  }
+
+  for (Index = 0; Index < (BufferSize/sizeof (EFI_HANDLE)); Index++) {
+    Status = gBS->HandleProtocol (
+                    *(HandleBuffer + Index),
+                    &gEfiDevicePathProtocolGuid,
+                    (VOID **)&ThisDevicePath
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "Device path on #%d instance of UsbIo.\n", Index));
+    DevicePathStr = ConvertDevicePathToText (ThisDevicePath, FALSE, FALSE);
+    DEBUG ((DEBUG_INFO, "%s\n", DevicePathStr));
+    FreePool (DevicePathStr);
+
+    Status = EFI_NOT_FOUND;
+
+    // Search for the starting MSG_USB_DP node.
+    ThisUsbDevicePath = UsbDevicePath;
+    if ((DevicePathType (ThisUsbDevicePath) != MESSAGING_DEVICE_PATH) ||
+        (DevicePathSubType (ThisUsbDevicePath) != MSG_USB_DP))
+    {
+      ThisUsbDevicePath = UsbNicGetNextMsgUsbDp (ThisUsbDevicePath);
+      if (ThisUsbDevicePath == NULL) {
+        continue;
+      }
+    }
+
+    if ((DevicePathType (ThisDevicePath) != MESSAGING_DEVICE_PATH) ||
+        (DevicePathSubType (ThisDevicePath) != MSG_USB_DP))
+    {
+      ThisDevicePath = UsbNicGetNextMsgUsbDp (ThisDevicePath);
+      if (ThisDevicePath == NULL) {
+        continue;
+      }
+    }
+
+    // Search for the ending MSG_USB_DP node.
+    ThisDevicePathEnd    = ThisDevicePath;
+    ThisUsbDevicePathEnd = ThisUsbDevicePath;
+    while (TRUE) {
+      TempDevicePath = UsbNicGetNextMsgUsbDp (ThisDevicePathEnd);
+      if (TempDevicePath == NULL) {
+        break;
+      }
+
+      ThisDevicePathEnd = TempDevicePath;
+    }
+
+    while (TRUE) {
+      TempDevicePath = UsbNicGetNextMsgUsbDp (ThisUsbDevicePathEnd);
+      if (TempDevicePath == NULL) {
+        break;
+      }
+
+      ThisUsbDevicePathEnd = TempDevicePath;
+    }
+
+    // Compare these two device paths
+    Length = (UINTN)(UINT8 *)ThisDevicePathEnd + DevicePathNodeLength (ThisDevicePathEnd) - (UINTN)(UINT8 *)ThisDevicePath;
+    if (Length != ((UINTN)(UINT8 *)ThisUsbDevicePathEnd + DevicePathNodeLength (ThisUsbDevicePathEnd) - (UINTN)(UINT8 *)ThisUsbDevicePath)) {
+      continue;
+    }
+
+    if (CompareMem (
+          (VOID *)ThisDevicePath,
+          (VOID *)ThisUsbDevicePath,
+          Length
+          ) == 0)
+    {
+      Status = EFI_SUCCESS;
+      DEBUG ((DEBUG_INFO, "EFI handle with the correct UsbIo is found at #%d instance of UsbIo.\n", Index));
+      break;
+    }
+  }
+
+  if (Status == EFI_SUCCESS) {
+    // Locate UsbIo from this handle.
+    Status = gBS->HandleProtocol (
+                    *(HandleBuffer + Index),
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **)UsbIo
+                    );
+    return Status;
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function identifies if the USB NIC is exposed by BMC as
+  the host-BMC channel.
+
+  @param[in] Handle          This is the EFI handle with SNP installed.
+  @param[in] UsbDevicePath   USB device path.
+
+  @retval EFI_SUCCESS          Yes, USB NIC exposed by BMC is found.
+  @retval EFI_NOT_FOUND        No, USB NIC exposed by BMC is not found
+                               on the existing SNP handle.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+IdentifyUsbNicBmcChannel (
+  IN EFI_HANDLE                Handle,
+  IN EFI_DEVICE_PATH_PROTOCOL  *UsbDevicePath
+  )
+{
+  UINTN                            Index;
+  EFI_STATUS                       Status;
+  EFI_SIMPLE_NETWORK_PROTOCOL      *Snp;
+  EFI_USB_IO_PROTOCOL              *UsbIo;
+  HOST_INTERFACE_BMC_USB_NIC_INFO  *BmcUsbNic;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry.\n", __FUNCTION__));
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiSimpleNetworkProtocolGuid,
+                  (VOID **)&Snp
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "    Failed to locate SNP.\n"));
+    return Status;
+  }
+
+  Status = UsbNicSearchUsbIo (UsbDevicePath, &UsbIo);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "    Failed to find USBIO.\n"));
+    return Status;
+  }
+
+  // Get the MAC address of this SNP instance.
+  BmcUsbNic = AllocateZeroPool (sizeof (HOST_INTERFACE_BMC_USB_NIC_INFO));
+  if (BmcUsbNic == NULL) {
+    DEBUG ((DEBUG_ERROR, "    Failed to allocate memory for HOST_INTERFACE_BMC_USB_NIC_INFO.\n"));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InitializeListHead (&BmcUsbNic->NextInstance);
+  BmcUsbNic->MacAddressSize = Snp->Mode->HwAddressSize;
+  BmcUsbNic->MacAddress     = AllocateZeroPool (sizeof (BmcUsbNic->MacAddressSize));
+  if (BmcUsbNic->MacAddress == NULL) {
+    DEBUG ((DEBUG_ERROR, "    Failed to allocate memory for HW MAC addresss.\n"));
+    FreePool (BmcUsbNic);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CopyMem (
+    (VOID *)BmcUsbNic->MacAddress,
+    (VOID *)&Snp->Mode->CurrentAddress,
+    BmcUsbNic->MacAddressSize
+    );
+  DEBUG ((DEBUG_INFO, "    MAC address (in size %d) for this SNP instance:\n      ", BmcUsbNic->MacAddressSize));
+  for (Index = 0; Index < BmcUsbNic->MacAddressSize; Index++) {
+    DEBUG ((DEBUG_INFO, "%02x ", *(BmcUsbNic->MacAddress + Index)));
+  }
+
+  DEBUG ((DEBUG_INFO, "\n"));
+  BmcUsbNic->ThisSnp   = Snp;
+  BmcUsbNic->ThisUsbIo = UsbIo;
+
+  Status = HostInterfaceIpmiCheckMacAddress (BmcUsbNic);
+  if (Status == EFI_SUCCESS) {
+    BmcUsbNic->IsExposedByBmc = TRUE;
+    DEBUG ((DEBUG_INFO, "    BMC exposed USB NIC is found.\n"));
+  } else {
+    DEBUG ((DEBUG_INFO, "    BMC exposed USB NIC is not found.\n"));
+  }
+
+  InsertTailList (&mBmcUsbNic, &BmcUsbNic->NextInstance);
+  return Status;
+}
+
+/**
+  This function checks if the USB NIC exposed by BMC
+  on each handle has SNP protocol installed on it.
+
+  @param[in] HandleNumer    Number of handles to check.
+  @param[in] HandleBuffer   Handles buffer.
+
+  @retval EFI_SUCCESS          Yes, USB NIC exposed by BMC is found.
+  @retval EFI_NOT_FOUND        No, USB NIC exposed by BMC is not found
+                               on the existing SNP handle.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+CheckBmcUsbNicOnHandles (
+  IN  UINTN       HandleNumer,
+  IN  EFI_HANDLE  *HandleBuffer
+  )
+{
+  UINTN                     Index;
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+
+  if ((HandleNumer == 0) || (HandleBuffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: Entry, #%d SNP handle\n", __FUNCTION__, HandleNumer));
+
+  for (Index = 0; Index < HandleNumer; Index++) {
+    Status = gBS->HandleProtocol (
+                    *(HandleBuffer + Index),
+                    &gEfiDevicePathProtocolGuid,
+                    (VOID **)&DevicePath
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "    Failed to locate SNP on %d handle.\n", __FUNCTION__, Index));
+      continue;
+    }
+
+    // Check if this is an BMC exposed USB NIC device.
+    while (TRUE) {
+      if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_USB_DP)) {
+        Status = IdentifyUsbNicBmcChannel (*(HandleBuffer + Index), DevicePath);
+        if (!EFI_ERROR (Status)) {
+          break;
+        }
+      }
+
+      DevicePath = NextDevicePathNode (DevicePath);
+      if (IsDevicePathEnd (DevicePath)) {
+        break;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function checks if the USB NIC exposed by BMC
+  is already connected.
+
+  @param[in] Registration      Locate SNP protocol from the notification
+                               registeration key.
+                               NULL means locate SNP protocol from the existing
+                               handles.
+
+  @retval EFI_SUCCESS          Yes, USB NIC exposed by BMC is found.
+  @retval EFI_NOT_FOUND        No, USB NIC exposed by BMC is not found
+                               on the existing SNP handle.
+  @retval Others               Other errors.
+
+**/
+EFI_STATUS
+CheckBmcUsbNic (
+  VOID  *Registration
+  )
+{
+  EFI_STATUS  Status;
+  EFI_HANDLE  Handle;
+  UINTN       BufferSize;
+  EFI_HANDLE  *HandleBuffer;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry, the registration key - 0x%08x.\n", __FUNCTION__, Registration));
+
+  Handle     = NULL;
+  Status     = EFI_SUCCESS;
+  BufferSize = 0;
+
+  Status = gBS->LocateHandle (
+                  Registration == NULL ? ByProtocol : ByRegisterNotify,
+                  &gEfiSimpleNetworkProtocolGuid,
+                  Registration,
+                  &BufferSize,
+                  NULL
+                  );
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    DEBUG ((DEBUG_INFO, "    %d SNP protocol instances.\n", BufferSize/sizeof (EFI_HANDLE)));
+    HandleBuffer = AllocateZeroPool (BufferSize);
+    if (HandleBuffer == NULL) {
+      DEBUG ((DEBUG_ERROR, "    Falied to allocate buffer for the handles.\n"));
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = gBS->LocateHandle (
+                    Registration == NULL ? ByProtocol : ByRegisterNotify,
+                    &gEfiSimpleNetworkProtocolGuid,
+                    Registration,
+                    &BufferSize,
+                    HandleBuffer
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "    Falied to locate SNP protocol handles.\n"));
+      FreePool (HandleBuffer);
+      return Status;
+    }
+  } else if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Check USB NIC on handles.
+  Status = CheckBmcUsbNicOnHandles (BufferSize/sizeof (EFI_HANDLE), HandleBuffer);
+  if ((Registration != NULL) && !EFI_ERROR (Status)) {
+    // Retrieve the rest of BMC USB NIC information for Redfish over IP information
+    // and USB Network Interface V2.
+    Status = RetrievedBmcUsbNicInfo ();
+
+    DEBUG ((DEBUG_INFO, "    Install protocol to notify the platform Redfish Host Interface information is ready.\n"));
+    Status = gBS->InstallProtocolInterface (
+                    &Handle,
+                    &mPlatformHostInterfaceBmcUsbNicReadinessGuid,
+                    EFI_NATIVE_INTERFACE,
+                    NULL
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "    Install protocol fail %r.\n", Status));
+    }
+  }
+
+  FreePool (HandleBuffer);
+  return Status;
+}
+
+/**
+  Notification event of SNP readiness.
+
+  @param[in]  Event                 Event whose notification function is being invoked.
+  @param[in]  Context               The pointer to the notification function's context,
+                                    which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+PlatformHostInterfaceSnpCallback (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  DEBUG ((DEBUG_INFO, "%a: Entry.\n", __FUNCTION__));
+
+  CheckBmcUsbNic (mPlatformHostInterfaceSnpRegistration);
+  return;
+}
+
+/**
+  Get the EFI protocol GUID installed by platform library which
+  indicates the necessary information is ready for building
+  SMBIOS 42h record.
+
+  @param[out] InformationReadinessGuid  Pointer to retrive the protocol
+                                        GUID.
+
+  @retval EFI_SUCCESS          Notification is required for building up
+                               SMBIOS type 42h record.
+  @retval EFI_UNSUPPORTED      Notification is not required for building up
+                               SMBIOS type 42h record.
+  @retval EFI_ALREADY_STARTED  Platform host information is already ready.
+  @retval Others               Other errors.
+**/
+EFI_STATUS
+RedfishPlatformHostInterfaceNotification (
+  OUT EFI_GUID  **InformationReadinessGuid
+  )
+{
+  EFI_STATUS  Status;
+
+  DEBUG ((DEBUG_INFO, "%a: Entry\n", __FUNCTION__));
+
+  *InformationReadinessGuid = NULL;
+  InitializeListHead (&mBmcUsbNic);
+  InitializeListHead (&mBmcIpmiLan);
+
+  //
+  // Check if USB NIC exposed by BMC is already
+  // connected.
+  //
+  Status = CheckBmcUsbNic (NULL);
+  if (!EFI_ERROR (Status)) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  if (Status == EFI_NOT_FOUND) {
+    DEBUG ((DEBUG_INFO, "%a: BMC USB NIC is not found. Register the notification.\n", __FUNCTION__));
+
+    // Register the notification of SNP installation.
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    PlatformHostInterfaceSnpCallback,
+                    NULL,
+                    &mPlatformHostInterfaceSnpEvent
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: Fail to create event for the installation of SNP protocol.", __FUNCTION__));
+      return Status;
+    }
+
+    Status = gBS->RegisterProtocolNotify (
+                    &gEfiSimpleNetworkProtocolGuid,
+                    mPlatformHostInterfaceSnpEvent,
+                    &mPlatformHostInterfaceSnpRegistration
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: Fail to register event for the installation of SNP protocol.", __FUNCTION__));
+      return Status;
+    }
+
+    *InformationReadinessGuid = &mPlatformHostInterfaceBmcUsbNicReadinessGuid;
+    return EFI_SUCCESS;
+  }
+
+  DEBUG ((DEBUG_ERROR, "%a: Something wrong when look for BMC USB NIC.\n", __FUNCTION__));
+  return Status;
+}
-- 
2.37.1.windows.1


  reply	other threads:[~2023-01-09 14:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-09 14:11 [edk2-staging][PATCH 0/3] BMC USBNIC Platform HI Lib Chang, Abner
2023-01-09 14:11 ` Chang, Abner [this message]
2023-01-09 14:11 ` [edk2-staging][PATCH 2/3] RedfishPkg: Update Redfish DSC Chang, Abner
2023-01-09 14:11 ` [edk2-staging][PATCH 3/3] RedfishPkg: Update Readme.md Chang, Abner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230109141124.622-2-abner.chang@amd.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox