blob: c73e76df57913d86d717922a1a8a61647307acfc [file] [log] [blame]
/** @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_MANAGEABILITY, "%a: Entry\n", __func__));
//
// 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 Credential Bootstrapping is supported\n"));
ReturnBool = TRUE;
} else {
DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " Redfish Credential Bootstrapping is not supported\n"));
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_MANAGEABILITY, "%a: Entry\n", __func__));
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_MANAGEABILITY, "%a: Entry\n", __func__));
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) - 1 +
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) -1 + 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 = PcdGet16 (PcdRedfishServicePort);
// 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_MANAGEABILITY, "%a: Entry\n", __func__));
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 the design spec:
// https://github.com/tianocore/edk2/tree/master/RedfishPkg#platform-with-bmc-and-the-bmc-exposed-usb-network-device
// 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_MANAGEABILITY, "%a: Entry.\n", __func__));
GetLanConfigReps = NULL;
AlreadyCached = FALSE;
if (!IsListEmpty (&mBmcIpmiLan)) {
AlreadyCached = TRUE;
}
// Initial the get MAC address request.
GetLanConfigReq.ChannelNumber.Uint8 = 0;
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.Uint8 = 0;
GetChanelInfoRequest.ChannelNumber.Bits.ChannelNo = (UINT8)ChannelNum;
Status = IpmiGetChannelInfo (
&GetChanelInfoRequest,
&GetChanelInfoResponse,
&ResponseDataSize
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, " - Channel %d 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.
// https://github.com/tianocore/edk2/tree/master/RedfishPkg#platform-with-bmc-and-the-bmc-exposed-usb-network-device
// Compare the first five elements of MAC address and the 6th element of MAC address.
// The 6th element of MAC address must be the 6th element of
// IPMI channel MAC address minus 1.
//
if ((IpmiLanMacAddressSize != UsbNicInfo->MacAddressSize) ||
(CompareMem (
(VOID *)UsbNicInfo->MacAddress,
(VOID *)&IpmiLanChannelMacAddress.Addr,
IpmiLanMacAddressSize - 1
) != 0) ||
((IpmiLanChannelMacAddress.Addr[IpmiLanMacAddressSize - 1] - 1) !=
*(UsbNicInfo->MacAddress + IpmiLanMacAddressSize - 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_MANAGEABILITY, "%a: Entry.\n", __func__));
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 has MAC address and internet
protocol device path installed. (Only support IPv4)
@param[in] UsbDevicePath USB device path.
@retval EFI_SUCCESS Yes, this is IPv4 SNP handle
@retval EFI_NOT_FOUND No, this is not IPv4 SNP handle
**/
EFI_STATUS
IdentifyNetworkMessageDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *UsbDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
DevicePath = UsbDevicePath;
while (TRUE) {
DevicePath = NextDevicePathNode (DevicePath);
if (IsDevicePathEnd (DevicePath)) {
DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "MAC address device path is not found on this handle.\n"));
break;
}
if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_MAC_ADDR_DP)) {
DevicePath = NextDevicePathNode (DevicePath); // Advance to next device path protocol.
if (IsDevicePathEnd (DevicePath)) {
DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, "IPv4 device path is not found on this handle.\n"));
break;
}
if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_IPv4_DP)) {
return EFI_SUCCESS;
}
break;
}
}
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_MANAGEABILITY, "%a: Entry.\n", __func__));
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 = AllocatePool (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 GotBmcUsbNic;
CHAR16 *DevicePathStr;
if ((HandleNumer == 0) || (HandleBuffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
DEBUG ((DEBUG_MANAGEABILITY, "%a: Entry, #%d SNP handle\n", __func__, HandleNumer));
GotBmcUsbNic = FALSE;
for (Index = 0; Index < HandleNumer; Index++) {
DEBUG ((DEBUG_MANAGEABILITY, " Locate device path on handle 0x%08x\n", *(HandleBuffer + Index)));
Status = gBS->HandleProtocol (
*(HandleBuffer + Index),
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, " Failed to locate device path on %d handle.\n", Index));
continue;
}
DevicePathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
if (DevicePathStr != NULL) {
DEBUG ((DEBUG_MANAGEABILITY, " Device path: %s\n", DevicePathStr));
FreePool (DevicePathStr);
}
// Check if this is an BMC exposed USB NIC device.
while (TRUE) {
if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_USB_DP)) {
Status = IdentifyNetworkMessageDevicePath (DevicePath);
if (!EFI_ERROR (Status)) {
Status = IdentifyUsbNicBmcChannel (*(HandleBuffer + Index), DevicePath);
if (!EFI_ERROR (Status)) {
GotBmcUsbNic = TRUE;
}
}
break; // Advance to next SNP handle.
}
DevicePath = NextDevicePathNode (DevicePath);
if (IsDevicePathEnd (DevicePath)) {
break;
}
}
}
if (GotBmcUsbNic) {
return EFI_SUCCESS;
}
DEBUG ((DEBUG_MANAGEABILITY, "No BMC USB NIC found on SNP handles\n"));
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_MANAGEABILITY, "%a: Entry, the registration key - 0x%08x.\n", __func__, Registration));
Handle = NULL;
Status = EFI_SUCCESS;
do {
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 instance(s).\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)) {
if (Registration != NULL) {
DEBUG ((DEBUG_REDFISH_HOST_INTERFACE, " No more newly installed SNP protocol for this registration - %r.\n", Status));
return EFI_SUCCESS;
}
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);
} while (Registration != NULL);
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_MANAGEABILITY, "%a: Entry.\n", __func__));
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_MANAGEABILITY, "%a: Entry\n", __func__));
*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", __func__));
// 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.", __func__));
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.", __func__));
return Status;
}
*InformationReadinessGuid = &mPlatformHostInterfaceBmcUsbNicReadinessGuid;
return EFI_SUCCESS;
}
DEBUG ((DEBUG_ERROR, "%a: Something wrong when look for BMC USB NIC.\n", __func__));
return Status;
}