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