/** @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; | |
} |