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>
---
RedfishPkg/RedfishPkg.dec | 10 +
.../PlatformHostInterfaceBmcUsbNicLib.inf | 48 +
RedfishPkg/Include/Library/RedfishDebugLib.h | 3 +-
.../PlatformHostInterfaceBmcUsbNicLib.h | 84 ++
.../PlatformHostInterfaceBmcUsbNicLib.c | 1291 +++++++++++++++++
5 files changed, 1435 insertions(+), 1 deletion(-)
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/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
index 53e52c2b008..42d28d6dac3 100644
--- a/RedfishPkg/RedfishPkg.dec
+++ b/RedfishPkg/RedfishPkg.dec
@@ -113,3 +113,13 @@
# Default is set to not add.
#
gEfiRedfishPkgTokenSpaceGuid.PcdRedfishRestExAddingExpect|FALSE|BOOLEAN|0x00001004
+ #
+ # Use PCD to declare the Redfish host nmae becasue there is no
+ # specification for that.
+ #
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishHostName|"Redfish service"|VOID*|0x00001005
+ #
+ # Use PCD to declare the Redfish service UUID becasue there is no
+ # specification for that.
+ #
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceUuid|L"00000000-0000-0000-0000-000000000000"|VOID*|0x00001006
diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf
new file mode 100644
index 00000000000..f2c7d7fec89
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf
@@ -0,0 +1,48 @@
+## @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
+
+[Pcd]
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishHostName ## CONSUMED
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceUuid ## CONSUMED
+
+[Depex]
+ gIpmiProtocolGuid
diff --git a/RedfishPkg/Include/Library/RedfishDebugLib.h b/RedfishPkg/Include/Library/RedfishDebugLib.h
index 21f01353ede..913f2b2f358 100644
--- a/RedfishPkg/Include/Library/RedfishDebugLib.h
+++ b/RedfishPkg/Include/Library/RedfishDebugLib.h
@@ -14,7 +14,8 @@
#include <Library/JsonLib.h>
#include <Library/RedfishLib.h>
-#define DEBUG_REDFISH_NETWORK DEBUG_INFO ///< Debug error level for Redfish networking function
+#define DEBUG_REDFISH_NETWORK DEBUG_INFO ///< Debug error level for Redfish networking function
+#define DEBUG_REDFISH_HOST_INTERFACE DEBUG_INFO ///< Debug error level for Redfish networking function
/**
diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h
new file mode 100644
index 00000000000..669c304fc3d
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h
@@ -0,0 +1,84 @@
+/** @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 <Library/RedfishDebugLib.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..718f05c98fc
--- /dev/null
+++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c
@@ -0,0 +1,1291 @@
+/** @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_REDFISH_HOST_INTERFACE, " Redfish Credentail Bootstrapping is supported\n", __FUNCTION__));
+ ReturnBool = TRUE;
+ } else {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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;
+ CHAR8 *HostNameString;
+
+ 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) {
+ // Get the host name before allocating memory.
+ HostNameString = (CHAR8 *)PcdGetPtr (PcdRedfishHostName);
+ HostNameLength = (UINT8)AsciiStrSize (HostNameString);
+ 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, " Allocate 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
+ //
+
+ // Service UUID
+ ZeroMem ((VOID *)&RedfishOverIpData->ServiceUuid, sizeof (EFI_GUID));
+ if (StrLen ((CONST CHAR16 *)PcdGetPtr (PcdRedfishServiceUuid)) != 0) {
+ StrToGuid ((CONST CHAR16 *)PcdGetPtr (PcdRedfishServiceUuid), &RedfishOverIpData->ServiceUuid);
+ DEBUG((DEBUG_REDFISH_HOST_INTERFACE, " Service UUID: %g", &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 = HostNameLength;
+
+ // Redfish host name.
+ CopyMem (
+ (VOID *)&RedfishOverIpData->RedfishServiceHostname,
+ (VOID *)HostNameString,
+ HostNameLength
+ );
+
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE,
+ " 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_REDFISH_HOST_INTERFACE,
+ " 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_REDFISH_HOST_INTERFACE,
+ " 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_REDFISH_HOST_INTERFACE,
+ " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " Got cached IPMI LAN info.\n"));
+ IpmiLanMacAddressSize = sizeof (IPMI_LAN_MAC_ADDRESS);
+ CopyMem ((VOID *)&IpmiLanChannelMacAddress.Addr, (VOID *)&CachedIpmiLanChannel->MacAddress.Addr, IpmiLanMacAddressSize);
+ } else {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " No cached IPMI LAN info\n"));
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " - 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_REDFISH_HOST_INTERFACE,
+ " - 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_REDFISH_HOST_INTERFACE, " - 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_REDFISH_HOST_INTERFACE, " The MAC address of channel %d.\n", ChannelNum));
+ DEBUG ((
+ DEBUG_REDFISH_HOST_INTERFACE,
+ " %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_REDFISH_HOST_INTERFACE, " Cache this IPMI LAN channel.\n"));
+ CacheIpmiLanMac ((UINT8)ChannelNum, &IpmiLanChannelMacAddress, IpmiLanMacAddressSize);
+ }
+
+ //
+ // According to design spec in Readme file under RedfishPkg.
+ // 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_REDFISH_HOST_INTERFACE, " MAC address is not matched.\n"));
+ continue;
+ }
+
+ // This is the NIC exposed by BMC.
+ UsbNicInfo->IpmiLanChannelNumber = (UINT8)ChannelNum;
+ UsbNicInfo->IsExposedByBmc = TRUE;
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, "Device path on the EFI handle which has UsbIo and SNP instaleld on it.\n"));
+ DevicePathStr = ConvertDevicePathToText (UsbDevicePath, FALSE, FALSE);
+ if (DevicePathStr != NULL) {
+ DEBUG((DEBUG_REDFISH_HOST_INTERFACE, "%s\n", DevicePathStr));
+ FreePool (DevicePathStr);
+ } else {
+ DEBUG((DEBUG_ERROR, "Failed to convert device path.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BufferSize = 0;
+ HandleBuffer = NULL;
+ *UsbIo = NULL;
+ Status = gBS->LocateHandle (
+ ByProtocol,
+ &gEfiUsbIoProtocolGuid,
+ NULL,
+ &BufferSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " %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_REDFISH_HOST_INTERFACE, "Device path on #%d instance of UsbIo.\n", Index));
+ DevicePathStr = ConvertDevicePathToText (ThisDevicePath, FALSE, FALSE);
+ if (DevicePathStr != NULL) {
+ DEBUG((DEBUG_REDFISH_HOST_INTERFACE, "%s\n", DevicePathStr));
+ FreePool (DevicePathStr);
+ } else {
+ DEBUG((DEBUG_ERROR, "Failed to convert device path on #%d instance of UsbIo.\n", Index));
+ continue;
+ }
+
+ 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 = (UINT16)((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_REDFISH_HOST_INTERFACE, "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_REDFISH_HOST_INTERFACE, " MAC address (in size %d) for this SNP instance:\n ", BmcUsbNic->MacAddressSize));
+ for (Index = 0; Index < BmcUsbNic->MacAddressSize; Index++) {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "%02x ", *(BmcUsbNic->MacAddress + Index)));
+ }
+
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "\n"));
+ BmcUsbNic->ThisSnp = Snp;
+ BmcUsbNic->ThisUsbIo = UsbIo;
+
+ Status = HostInterfaceIpmiCheckMacAddress (BmcUsbNic);
+ if (Status == EFI_SUCCESS) {
+ BmcUsbNic->IsExposedByBmc = TRUE;
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " BMC exposed USB NIC is found.\n"));
+ } else {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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;
+ BOOLEAN GotOneUsbNIc;
+
+ if ((HandleNumer == 0) || (HandleBuffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: Entry, #%d SNP handle\n", __FUNCTION__, HandleNumer));
+
+ GotOneUsbNIc = FALSE;
+ 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)) {
+ GotOneUsbNIc = TRUE;
+ break;
+ }
+ }
+
+ DevicePath = NextDevicePathNode (DevicePath);
+ if (IsDevicePathEnd (DevicePath)) {
+ break;
+ }
+ }
+ }
+
+ if (GotOneUsbNIc) {
+ return EFI_SUCCESS;
+ }
+
+ 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_REDFISH_HOST_INTERFACE, " %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 (!EFI_ERROR (Status)) {
+ // Retrieve the rest of BMC USB NIC information for Redfish over IP information
+ // and USB Network Interface V2.
+ Status = RetrievedBmcUsbNicInfo ();
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, "%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
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#101408): https://edk2.groups.io/g/devel/message/101408
Mute This Topic: https://groups.io/mt/97726812/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
Thanks for addressing my comments. Reviewed-by: Nickle Wang <nicklew@nvidia.com> Regards, Nickle -----Original Message----- From: abner.chang@amd.com <abner.chang@amd.com> Sent: Monday, March 20, 2023 4:14 PM To: devel@edk2.groups.io Cc: Nickle Wang <nicklew@nvidia.com>; Igor Kulchytskyy <igork@ami.com> Subject: [PATCH v2 1/3] RedfishPkg/Library: Redfish BMC USBNIC Host Interface External email: Use caution opening links or attachments 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> --- RedfishPkg/RedfishPkg.dec | 10 + .../PlatformHostInterfaceBmcUsbNicLib.inf | 48 + RedfishPkg/Include/Library/RedfishDebugLib.h | 3 +- .../PlatformHostInterfaceBmcUsbNicLib.h | 84 ++ .../PlatformHostInterfaceBmcUsbNicLib.c | 1291 +++++++++++++++++ 5 files changed, 1435 insertions(+), 1 deletion(-) 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/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec index 53e52c2b008..42d28d6dac3 100644 --- a/RedfishPkg/RedfishPkg.dec +++ b/RedfishPkg/RedfishPkg.dec @@ -113,3 +113,13 @@ # Default is set to not add. # gEfiRedfishPkgTokenSpaceGuid.PcdRedfishRestExAddingExpect|FALSE|BOOLEAN|0x00001004 + # + # Use PCD to declare the Redfish host nmae becasue there is no + # specification for that. + # + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishHostName|"Redfish service"|VOID*|0x00001005 + # + # Use PCD to declare the Redfish service UUID becasue there is no + # specification for that. + # + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceUuid|L"00000000-0000-0000-0000-000000000000"|VOID*|0x00001006 diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf new file mode 100644 index 00000000000..f2c7d7fec89 --- /dev/null +++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.inf @@ -0,0 +1,48 @@ +## @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 + +[Pcd] + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishHostName ## CONSUMED + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceUuid ## CONSUMED + +[Depex] + gIpmiProtocolGuid diff --git a/RedfishPkg/Include/Library/RedfishDebugLib.h b/RedfishPkg/Include/Library/RedfishDebugLib.h index 21f01353ede..913f2b2f358 100644 --- a/RedfishPkg/Include/Library/RedfishDebugLib.h +++ b/RedfishPkg/Include/Library/RedfishDebugLib.h @@ -14,7 +14,8 @@ #include <Library/JsonLib.h> #include <Library/RedfishLib.h> -#define DEBUG_REDFISH_NETWORK DEBUG_INFO ///< Debug error level for Redfish networking function +#define DEBUG_REDFISH_NETWORK DEBUG_INFO ///< Debug error level for Redfish networking function +#define DEBUG_REDFISH_HOST_INTERFACE DEBUG_INFO ///< Debug error level for Redfish networking function /** diff --git a/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h new file mode 100644 index 00000000000..669c304fc3d --- /dev/null +++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.h @@ -0,0 +1,84 @@ +/** @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 <Library/RedfishDebugLib.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..718f05c98fc --- /dev/null +++ b/RedfishPkg/Library/PlatformHostInterfaceBmcUsbNicLib/PlatformHostInterfaceBmcUsbNicLib.c @@ -0,0 +1,1291 @@ +/** @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_REDFISH_HOST_INTERFACE, " Redfish Credentail Bootstrapping is supported\n", __FUNCTION__)); + ReturnBool = TRUE; + } else { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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; + CHAR8 *HostNameString; + + 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) { + // Get the host name before allocating memory. + HostNameString = (CHAR8 *)PcdGetPtr (PcdRedfishHostName); + HostNameLength = (UINT8)AsciiStrSize (HostNameString); + 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, " Allocate 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 + // + + // Service UUID + ZeroMem ((VOID *)&RedfishOverIpData->ServiceUuid, sizeof (EFI_GUID)); + if (StrLen ((CONST CHAR16 *)PcdGetPtr (PcdRedfishServiceUuid)) != 0) { + StrToGuid ((CONST CHAR16 *)PcdGetPtr (PcdRedfishServiceUuid), &RedfishOverIpData->ServiceUuid); + DEBUG((DEBUG_REDFISH_HOST_INTERFACE, " Service UUID: %g", &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 = HostNameLength; + + // Redfish host name. + CopyMem ( + (VOID *)&RedfishOverIpData->RedfishServiceHostname, + (VOID *)HostNameString, + HostNameLength + ); + + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, + " 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_REDFISH_HOST_INTERFACE, + " 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_REDFISH_HOST_INTERFACE, + " 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_REDFISH_HOST_INTERFACE, + " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " Got cached IPMI LAN info.\n")); + IpmiLanMacAddressSize = sizeof (IPMI_LAN_MAC_ADDRESS); + CopyMem ((VOID *)&IpmiLanChannelMacAddress.Addr, (VOID *)&CachedIpmiLanChannel->MacAddress.Addr, IpmiLanMacAddressSize); + } else { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " No cached IPMI LAN info\n")); + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, " - 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_REDFISH_HOST_INTERFACE, + " - 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_REDFISH_HOST_INTERFACE, " - 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_REDFISH_HOST_INTERFACE, " The MAC address of channel %d.\n", ChannelNum)); + DEBUG (( + DEBUG_REDFISH_HOST_INTERFACE, + " %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_REDFISH_HOST_INTERFACE, " Cache this IPMI LAN channel.\n")); + CacheIpmiLanMac ((UINT8)ChannelNum, &IpmiLanChannelMacAddress, IpmiLanMacAddressSize); + } + + // + // According to design spec in Readme file under RedfishPkg. + // 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_REDFISH_HOST_INTERFACE, " MAC address is not matched.\n")); + continue; + } + + // This is the NIC exposed by BMC. + UsbNicInfo->IpmiLanChannelNumber = (UINT8)ChannelNum; + UsbNicInfo->IsExposedByBmc = TRUE; + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, "Device path on the EFI handle which has UsbIo and SNP instaleld on it.\n")); + DevicePathStr = ConvertDevicePathToText (UsbDevicePath, FALSE, FALSE); + if (DevicePathStr != NULL) { + DEBUG((DEBUG_REDFISH_HOST_INTERFACE, "%s\n", DevicePathStr)); + FreePool (DevicePathStr); + } else { + DEBUG((DEBUG_ERROR, "Failed to convert device path.\n")); + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + HandleBuffer = NULL; + *UsbIo = NULL; + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiUsbIoProtocolGuid, + NULL, + &BufferSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " %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_REDFISH_HOST_INTERFACE, "Device path on #%d instance of UsbIo.\n", Index)); + DevicePathStr = ConvertDevicePathToText (ThisDevicePath, FALSE, FALSE); + if (DevicePathStr != NULL) { + DEBUG((DEBUG_REDFISH_HOST_INTERFACE, "%s\n", DevicePathStr)); + FreePool (DevicePathStr); + } else { + DEBUG((DEBUG_ERROR, "Failed to convert device path on #%d instance of UsbIo.\n", Index)); + continue; + } + + 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 = (UINT16)((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_REDFISH_HOST_INTERFACE, "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_REDFISH_HOST_INTERFACE, " MAC address (in size %d) for this SNP instance:\n ", BmcUsbNic->MacAddressSize)); + for (Index = 0; Index < BmcUsbNic->MacAddressSize; Index++) { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "%02x ", *(BmcUsbNic->MacAddress + Index))); + } + + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "\n")); + BmcUsbNic->ThisSnp = Snp; + BmcUsbNic->ThisUsbIo = UsbIo; + + Status = HostInterfaceIpmiCheckMacAddress (BmcUsbNic); + if (Status == EFI_SUCCESS) { + BmcUsbNic->IsExposedByBmc = TRUE; + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " BMC exposed USB NIC is found.\n")); + } else { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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; + BOOLEAN GotOneUsbNIc; + + if ((HandleNumer == 0) || (HandleBuffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((DEBUG_INFO, "%a: Entry, #%d SNP handle\n", __FUNCTION__, HandleNumer)); + + GotOneUsbNIc = FALSE; + 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)) { + GotOneUsbNIc = TRUE; + break; + } + } + + DevicePath = NextDevicePathNode (DevicePath); + if (IsDevicePathEnd (DevicePath)) { + break; + } + } + } + + if (GotOneUsbNIc) { + return EFI_SUCCESS; + } + + 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_REDFISH_HOST_INTERFACE, " %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 (!EFI_ERROR (Status)) { + // Retrieve the rest of BMC USB NIC information for Redfish over IP information + // and USB Network Interface V2. + Status = RetrievedBmcUsbNicInfo (); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " 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_REDFISH_HOST_INTERFACE, "%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 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#101413): https://edk2.groups.io/g/devel/message/101413 Mute This Topic: https://groups.io/mt/97726812/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2024 Red Hat, Inc.