#include "inventory_manager.hpp" #include "common/utils.hpp" #include "xyz/openbmc_project/Software/Version/server.hpp" #include #include #include PHOSPHOR_LOG2_USING; namespace pldm { namespace fw_update { void InventoryManager::discoverFDs(const MctpInfos& mctpInfos) { for (const auto& mctpInfo : mctpInfos) { auto eid = std::get(mctpInfo); try { sendQueryDeviceIdentifiersRequest(eid); } catch (const std::exception& e) { error( "Failed to discover file descriptors for endpoint ID {EID} with {ERROR}", "EID", eid, "ERROR", e); } } } void InventoryManager::removeFDs(const MctpInfos& mctpInfos) { for (const auto& mctpInfo : mctpInfos) { auto eid = std::get(mctpInfo); firmwareDeviceNameMap.erase(eid); descriptorMap.erase(eid); downstreamDescriptorMap.erase(eid); componentInfoMap.erase(eid); firmwareInventoryManager.deleteFirmwareEntry(eid); } } void InventoryManager::sendQueryDeviceIdentifiersRequest(mctp_eid_t eid) { auto instanceId = instanceIdDb.next(eid); Request requestMsg( sizeof(pldm_msg_hdr) + PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES); auto request = new (requestMsg.data()) pldm_msg; auto rc = encode_query_device_identifiers_req( instanceId, PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES, request); if (rc) { instanceIdDb.free(eid, instanceId); error( "Failed to encode query device identifiers request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); throw std::runtime_error( "Failed to encode QueryDeviceIdentifiers request"); } rc = handler.registerRequest( eid, instanceId, PLDM_FWUP, PLDM_QUERY_DEVICE_IDENTIFIERS, std::move(requestMsg), [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { this->queryDeviceIdentifiers(eid, response, respMsgLen); }); if (rc) { error( "Failed to send query device identifiers request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); throw std::runtime_error( "Failed to send QueryDeviceIdentifiers request"); } } void InventoryManager::queryDeviceIdentifiers( mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { if (response == nullptr || !respMsgLen) { error( "No response received for query device identifiers for endpoint ID {EID}", "EID", eid); return; } uint8_t completionCode = PLDM_SUCCESS; uint32_t deviceIdentifiersLen = 0; uint8_t descriptorCount = 0; uint8_t* descriptorPtr = nullptr; auto rc = decode_query_device_identifiers_resp( response, respMsgLen, &completionCode, &deviceIdentifiersLen, &descriptorCount, &descriptorPtr); if (rc) { error( "Failed to decode query device identifiers response for endpoint ID {EID} and descriptor count {DESCRIPTOR_COUNT}, response code {RC}", "EID", eid, "DESCRIPTOR_COUNT", descriptorCount, "RC", rc); return; } if (completionCode) { error( "Failed to query device identifiers response for endpoint ID {EID}, completion code {CC}", "EID", eid, "CC", completionCode); return; } Descriptors descriptors{}; while (descriptorCount-- && (deviceIdentifiersLen > 0)) { uint16_t descriptorType = 0; variable_field descriptorData{}; rc = decode_descriptor_type_length_value( descriptorPtr, deviceIdentifiersLen, &descriptorType, &descriptorData); if (rc) { error( "Failed to decode descriptor type {TYPE}, length {LENGTH} and value for endpoint ID {EID}, response code {RC}", "TYPE", descriptorType, "LENGTH", deviceIdentifiersLen, "EID", eid, "RC", rc); return; } if (descriptorType != PLDM_FWUP_VENDOR_DEFINED) { std::vector descData( descriptorData.ptr, descriptorData.ptr + descriptorData.length); descriptors.emplace(descriptorType, std::move(descData)); } else { uint8_t descriptorTitleStrType = 0; variable_field descriptorTitleStr{}; variable_field vendorDefinedDescriptorData{}; rc = decode_vendor_defined_descriptor_value( descriptorData.ptr, descriptorData.length, &descriptorTitleStrType, &descriptorTitleStr, &vendorDefinedDescriptorData); if (rc) { error( "Failed to decode vendor-defined descriptor value for endpoint ID {EID}, response code {RC}", "EID", eid, "RC", rc); return; } auto vendorDefinedDescriptorTitleStr = utils::toString(descriptorTitleStr); std::vector vendorDescData( vendorDefinedDescriptorData.ptr, vendorDefinedDescriptorData.ptr + vendorDefinedDescriptorData.length); descriptors.emplace(descriptorType, std::make_tuple(vendorDefinedDescriptorTitleStr, vendorDescData)); } auto nextDescriptorOffset = sizeof(pldm_descriptor_tlv().descriptor_type) + sizeof(pldm_descriptor_tlv().descriptor_length) + descriptorData.length; descriptorPtr += nextDescriptorOffset; deviceIdentifiersLen -= nextDescriptorOffset; } obtainFirmwareDeviceName(eid, descriptors); descriptorMap.insert_or_assign(eid, std::move(descriptors)); // Send GetFirmwareParameters request sendGetFirmwareParametersRequest(eid); } void InventoryManager::sendQueryDownstreamDevicesRequest(mctp_eid_t eid) { Request requestMsg(sizeof(pldm_msg_hdr)); auto instanceId = instanceIdDb.next(eid); auto request = new (requestMsg.data()) pldm_msg; auto rc = encode_query_downstream_devices_req(instanceId, request); if (rc) { instanceIdDb.free(eid, instanceId); error( "Failed to encode query downstream devices request for endpoint ID EID {EID} with response code {RC}", "EID", eid, "RC", rc); throw std::runtime_error( "Failed to encode query downstream devices request"); } rc = handler.registerRequest( eid, instanceId, PLDM_FWUP, PLDM_QUERY_DOWNSTREAM_DEVICES, std::move(requestMsg), [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { this->queryDownstreamDevices(eid, response, respMsgLen); }); if (rc) { error( "Failed to send QueryDownstreamDevices request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); } } void InventoryManager::queryDownstreamDevices( mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { if (!response || !respMsgLen) { error( "No response received for QueryDownstreamDevices for endpoint ID {EID}", "EID", eid); return; } pldm_query_downstream_devices_resp downstreamDevicesResp{}; auto rc = decode_query_downstream_devices_resp(response, respMsgLen, &downstreamDevicesResp); if (rc) { error( "Decoding QueryDownstreamDevices response failed for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } switch (downstreamDevicesResp.completion_code) { case PLDM_SUCCESS: break; case PLDM_ERROR_UNSUPPORTED_PLDM_CMD: /* QueryDownstreamDevices is optional, consider the device does not * support Downstream Devices. */ info("Endpoint ID {EID} does not support QueryDownstreamDevices", "EID", eid); return; default: error( "QueryDownstreamDevices response failed with error completion code for endpoint ID {EID} with completion code {CC}", "EID", eid, "CC", downstreamDevicesResp.completion_code); return; } switch (downstreamDevicesResp.downstream_device_update_supported) { case PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED: /** DataTransferHandle will be skipped when TransferOperationFlag is * `GetFirstPart`. Use 0x0 as default by following example in * Figure 9 in DSP0267 1.1.0 */ try { sendQueryDownstreamIdentifiersRequest(eid, 0x0, PLDM_GET_FIRSTPART); } catch (const std::exception& e) { error( "Failed to send QueryDownstreamIdentifiers request for endpoint ID {EID} with {ERROR}", "EID", eid, "ERROR", e); } break; case PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_NOT_SUPPORTED: /* The FDP does not support firmware updates but may report * inventory information on downstream devices. * In this scenario, sends only GetDownstreamFirmwareParameters * to the FDP. * The definition can be found at Table 15 of DSP0267_1.1.0 */ break; default: error( "Unknown response of DownstreamDeviceUpdateSupported from endpoint ID {EID} with value {VALUE}", "EID", eid, "VALUE", downstreamDevicesResp.downstream_device_update_supported); return; } } void InventoryManager::sendQueryDownstreamIdentifiersRequest( mctp_eid_t eid, uint32_t dataTransferHandle, enum transfer_op_flag transferOperationFlag) { auto instanceId = instanceIdDb.next(eid); Request requestMsg( sizeof(pldm_msg_hdr) + PLDM_QUERY_DOWNSTREAM_IDENTIFIERS_REQ_BYTES); auto request = new (requestMsg.data()) pldm_msg; pldm_query_downstream_identifiers_req requestParameters{ dataTransferHandle, static_cast(transferOperationFlag)}; auto rc = encode_query_downstream_identifiers_req( instanceId, &requestParameters, request, PLDM_QUERY_DOWNSTREAM_IDENTIFIERS_REQ_BYTES); if (rc) { instanceIdDb.free(eid, instanceId); error( "Failed to encode query downstream identifiers request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); throw std::runtime_error( "Failed to encode query downstream identifiers request"); } rc = handler.registerRequest( eid, instanceId, PLDM_FWUP, PLDM_QUERY_DOWNSTREAM_IDENTIFIERS, std::move(requestMsg), [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { this->queryDownstreamIdentifiers(eid, response, respMsgLen); }); if (rc) { error( "Failed to send QueryDownstreamIdentifiers request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); } } void InventoryManager::queryDownstreamIdentifiers( mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { if (!response || !respMsgLen) { error( "No response received for QueryDownstreamIdentifiers for endpoint ID {EID}", "EID", eid); descriptorMap.erase(eid); return; } pldm_query_downstream_identifiers_resp downstreamIds{}; pldm_downstream_device_iter devs{}; auto rc = decode_query_downstream_identifiers_resp(response, respMsgLen, &downstreamIds, &devs); if (rc) { error( "Decoding QueryDownstreamIdentifiers response failed for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } if (downstreamIds.completion_code) { error( "QueryDownstreamIdentifiers response failed with error completion code for endpoint ID {EID} with completion code {CC}", "EID", eid, "CC", unsigned(downstreamIds.completion_code)); return; } DownstreamDeviceInfo initialDownstreamDevices{}; DownstreamDeviceInfo* downstreamDevices; if (!downstreamDescriptorMap.contains(eid) || downstreamIds.transfer_flag == PLDM_START || downstreamIds.transfer_flag == PLDM_START_AND_END) { downstreamDevices = &initialDownstreamDevices; } else { downstreamDevices = &downstreamDescriptorMap.at(eid); } pldm_downstream_device dev; foreach_pldm_downstream_device(devs, dev, rc) { pldm_descriptor desc; Descriptors descriptors{}; foreach_pldm_downstream_device_descriptor(devs, dev, desc, rc) { const auto descriptorData = new (const_cast(desc.descriptor_data)) uint8_t[desc.descriptor_length]; if (desc.descriptor_type != PLDM_FWUP_VENDOR_DEFINED) { std::vector descData( descriptorData, descriptorData + desc.descriptor_length); descriptors.emplace(desc.descriptor_type, std::move(descData)); } else { uint8_t descriptorTitleStrType = 0; variable_field descriptorTitleStr{}; variable_field vendorDefinedDescriptorData{}; rc = decode_vendor_defined_descriptor_value( descriptorData, desc.descriptor_length, &descriptorTitleStrType, &descriptorTitleStr, &vendorDefinedDescriptorData); if (rc) { error( "Decoding Vendor-defined descriptor value failed for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } auto vendorDefinedDescriptorTitleStr = utils::toString(descriptorTitleStr); std::vector vendorDescData( vendorDefinedDescriptorData.ptr, vendorDefinedDescriptorData.ptr + vendorDefinedDescriptorData.length); descriptors.emplace( desc.descriptor_type, std::make_tuple(vendorDefinedDescriptorTitleStr, vendorDescData)); } } if (rc) { error( "Failed to decode downstream device descriptor for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } downstreamDevices->emplace(dev.downstream_device_index, descriptors); } if (rc) { error( "Failed to decode downstream devices from iterator for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } switch (downstreamIds.transfer_flag) { case PLDM_START: downstreamDescriptorMap.insert_or_assign( eid, std::move(initialDownstreamDevices)); [[fallthrough]]; case PLDM_MIDDLE: sendQueryDownstreamIdentifiersRequest( eid, downstreamIds.next_data_transfer_handle, PLDM_GET_NEXTPART); break; case PLDM_START_AND_END: downstreamDescriptorMap.insert_or_assign( eid, std::move(initialDownstreamDevices)); /** DataTransferHandle will be skipped when TransferOperationFlag is * `GetFirstPart`. Use 0x0 as default by following example in * Figure 9 in DSP0267 1.1.0 */ [[fallthrough]]; case PLDM_END: sendGetDownstreamFirmwareParametersRequest(eid, 0x0, PLDM_GET_FIRSTPART); break; } } void InventoryManager::sendGetDownstreamFirmwareParametersRequest( mctp_eid_t eid, uint32_t dataTransferHandle, enum transfer_op_flag transferOperationFlag) { Request requestMsg(sizeof(pldm_msg_hdr) + PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMETERS_REQ_BYTES); auto instanceId = instanceIdDb.next(eid); auto request = new (requestMsg.data()) pldm_msg; pldm_get_downstream_firmware_parameters_req requestParameters{ dataTransferHandle, static_cast(transferOperationFlag)}; auto rc = encode_get_downstream_firmware_parameters_req( instanceId, &requestParameters, request, PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMETERS_REQ_BYTES); if (rc) { instanceIdDb.free(eid, instanceId); error( "Failed to encode query downstream firmware parameters request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); throw std::runtime_error( "Failed to encode query downstream firmware parameters request"); } rc = handler.registerRequest( eid, instanceId, PLDM_FWUP, PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS, std::move(requestMsg), [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { this->getDownstreamFirmwareParameters(eid, response, respMsgLen); }); if (rc) { error( "Failed to send QueryDownstreamFirmwareParameters request for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); } } void InventoryManager::getDownstreamFirmwareParameters( mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { if (!response || !respMsgLen) { error( "No response received for QueryDownstreamFirmwareParameters for endpoint ID {EID}", "EID", eid); descriptorMap.erase(eid); return; } pldm_get_downstream_firmware_parameters_resp resp{}; pldm_downstream_device_parameters_iter params{}; pldm_downstream_device_parameters_entry entry{}; auto rc = decode_get_downstream_firmware_parameters_resp( response, respMsgLen, &resp, ¶ms); if (rc) { error( "Decoding QueryDownstreamFirmwareParameters response failed for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } if (resp.completion_code) { error( "QueryDownstreamFirmwareParameters response failed with error completion code for endpoint ID {EID} with completion code {CC}", "EID", eid, "CC", resp.completion_code); return; } foreach_pldm_downstream_device_parameters_entry(params, entry, rc) { // Reserved for upcoming use [[maybe_unused]] variable_field activeCompVerStr{ reinterpret_cast(entry.active_comp_ver_str), entry.active_comp_ver_str_len}; } if (rc) { error( "Failed to decode downstream device parameters from iterator for endpoint ID {EID} with response code {RC}", "EID", eid, "RC", rc); return; } switch (resp.transfer_flag) { case PLDM_START: case PLDM_MIDDLE: sendGetDownstreamFirmwareParametersRequest( eid, resp.next_data_transfer_handle, PLDM_GET_NEXTPART); break; } } void InventoryManager::obtainFirmwareDeviceName(pldm::eid eid, const Descriptors& descriptors) { auto firmwareDeviceName = obtainDeviceNameFromConfigurations(configurations, eid); if (!firmwareDeviceName) { firmwareDeviceName = obtainDeviceNameFromDescriptors(descriptors); } if (!firmwareDeviceName) { firmwareDeviceName = std::format("Firmware_Device_{}", eid); } firmwareDeviceNameMap.insert_or_assign(eid, *firmwareDeviceName); } void InventoryManager::sendGetFirmwareParametersRequest(mctp_eid_t eid) { auto instanceId = instanceIdDb.next(eid); Request requestMsg( sizeof(pldm_msg_hdr) + PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES); auto request = new (requestMsg.data()) pldm_msg; auto rc = encode_get_firmware_parameters_req( instanceId, PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES, request); if (rc) { instanceIdDb.free(eid, instanceId); error( "Failed to encode get firmware parameters req for endpoint ID {EID}, response code {RC}", "EID", eid, "RC", rc); return; } rc = handler.registerRequest( eid, instanceId, PLDM_FWUP, PLDM_GET_FIRMWARE_PARAMETERS, std::move(requestMsg), [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { this->getFirmwareParameters(eid, response, respMsgLen); }); if (rc) { error( "Failed to send get firmware parameters request for endpoint ID {EID}, response code {RC}", "EID", eid, "RC", rc); } } void InventoryManager::getFirmwareParameters( mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) { if (response == nullptr || !respMsgLen) { error( "No response received for get firmware parameters for endpoint ID {EID}", "EID", eid); descriptorMap.erase(eid); return; } pldm_get_firmware_parameters_resp fwParams{}; variable_field activeCompImageSetVerStr{}; variable_field pendingCompImageSetVerStr{}; variable_field compParamTable{}; auto rc = decode_get_firmware_parameters_resp( response, respMsgLen, &fwParams, &activeCompImageSetVerStr, &pendingCompImageSetVerStr, &compParamTable); if (rc) { error( "Failed to decode get firmware parameters response for endpoint ID {EID}, response code {RC}", "EID", eid, "RC", rc); return; } if (fwParams.completion_code) { auto fw_param_cc = fwParams.completion_code; error( "Failed to get firmware parameters response for endpoint ID {EID}, completion code {CC}", "EID", eid, "CC", fw_param_cc); return; } auto compParamPtr = compParamTable.ptr; auto compParamTableLen = compParamTable.length; pldm_component_parameter_entry compEntry{}; variable_field activeCompVerStr{}; variable_field pendingCompVerStr{}; ComponentInfo componentInfo{}; while (fwParams.comp_count-- && (compParamTableLen > 0)) { auto rc = decode_get_firmware_parameters_resp_comp_entry( compParamPtr, compParamTableLen, &compEntry, &activeCompVerStr, &pendingCompVerStr); if (rc) { error( "Failed to decode component parameter table entry for endpoint ID {EID}, response code {RC}", "EID", eid, "RC", rc); return; } auto compClassification = compEntry.comp_classification; auto compIdentifier = compEntry.comp_identifier; componentInfo.emplace( std::make_pair(compClassification, compIdentifier), compEntry.comp_classification_index); if (firmwareDeviceNameMap.contains(eid) and descriptorMap.contains(eid)) { auto componentName = firmwareDeviceNameMap.at(eid) + "_Component_" + std::to_string(compIdentifier); firmwareInventoryManager.createFirmwareEntry( SoftwareIdentifier(eid, compIdentifier), componentName, utils::toString(activeCompVerStr), descriptorMap.at(eid), componentInfo); } else { error( "Firmware device name or descriptor map not found for endpoint ID {EID}", "EID", eid); } compParamPtr += sizeof(pldm_component_parameter_entry) + activeCompVerStr.length + pendingCompVerStr.length; compParamTableLen -= sizeof(pldm_component_parameter_entry) + activeCompVerStr.length + pendingCompVerStr.length; } componentInfoMap.insert_or_assign(eid, std::move(componentInfo)); } std::optional obtainDeviceNameFromConfigurations( const Configurations& configurations, pldm::eid eid) { for (const auto& [_, mctpInfo] : configurations) { if (std::get(mctpInfo) == eid) { auto nameOption = std::get>(mctpInfo); if (nameOption) { return *nameOption; } break; } } return std::nullopt; } std::optional obtainDeviceNameFromDescriptors( const Descriptors& descriptors) { for (const auto& [descriptorType, descriptorData] : descriptors) { if (descriptorType == PLDM_FWUP_VENDOR_DEFINED) { auto vendorInfo = std::get(descriptorData); auto title = std::get(vendorInfo); if (title == "OpenBMC.Name") { auto deviceNameData = std::get(vendorInfo); return SoftwareName{ reinterpret_cast(deviceNameData.data()), deviceNameData.size()}; } } } return std::nullopt; } } // namespace fw_update } // namespace pldm