xref: /openbmc/pldm/pldmtool/pldm_fw_update_cmd.cpp (revision 2bc3dd0d23c525aaea29cc845a3391be6e029187)
1 #include "pldm_fw_update_cmd.hpp"
2 
3 #include "common/utils.hpp"
4 #include "pldm_cmd_helper.hpp"
5 
6 #include <libpldm/firmware_update.h>
7 
8 #include <format>
9 
10 namespace pldmtool
11 {
12 
13 namespace fw_update
14 {
15 
16 namespace
17 {
18 
19 using namespace pldmtool::helper;
20 using namespace pldm::fw_update;
21 
22 std::vector<std::unique_ptr<CommandInterface>> commands;
23 
24 } // namespace
25 
26 const std::map<uint8_t, std::string> fdStateMachine{
27     {PLDM_FD_STATE_IDLE, "IDLE"},
28     {PLDM_FD_STATE_LEARN_COMPONENTS, "LEARN COMPONENTS"},
29     {PLDM_FD_STATE_READY_XFER, "READY XFER"},
30     {PLDM_FD_STATE_DOWNLOAD, "DOWNLOAD"},
31     {PLDM_FD_STATE_VERIFY, "VERIFY"},
32     {PLDM_FD_STATE_APPLY, "APPLY"},
33     {PLDM_FD_STATE_ACTIVATE, "ACTIVATE"}};
34 
35 const std::map<uint8_t, const char*> fdAuxState{
36     {PLDM_FD_OPERATION_IN_PROGRESS, "Operation in progress"},
37     {PLDM_FD_OPERATION_SUCCESSFUL, "Operation successful"},
38     {PLDM_FD_OPERATION_FAILED, "Operation Failed"},
39     {PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER,
40      "Not applicable in current state"}};
41 
42 const std::map<uint8_t, const char*> fdAuxStateStatus{
43     {PLDM_FD_AUX_STATE_IN_PROGRESS_OR_SUCCESS,
44      "AuxState is In Progress or Success"},
45     {PLDM_FD_TIMEOUT, "Timeout occurred while performing action"},
46     {PLDM_FD_GENERIC_ERROR, "Generic Error has occurred"}};
47 
48 const std::map<uint8_t, const char*> fdReasonCode{
49     {PLDM_FD_INITIALIZATION, "Initialization of firmware device has occurred"},
50     {PLDM_FD_ACTIVATE_FW, "ActivateFirmware command was received"},
51     {PLDM_FD_CANCEL_UPDATE, "CancelUpdate command was received"},
52     {PLDM_FD_TIMEOUT_LEARN_COMPONENT,
53      "Timeout occurred when in LEARN COMPONENT state"},
54     {PLDM_FD_TIMEOUT_READY_XFER, "Timeout occurred when in READY XFER state"},
55     {PLDM_FD_TIMEOUT_DOWNLOAD, "Timeout occurred when in DOWNLOAD state"},
56     {PLDM_FD_TIMEOUT_VERIFY, "Timeout occurred when in VERIFY state"},
57     {PLDM_FD_TIMEOUT_APPLY, "Timeout occurred when in APPLY state"}};
58 
59 /**
60  * @brief descriptor type to name mapping
61  *
62  */
63 const std::map<DescriptorType, const char*> descriptorName{
64     {PLDM_FWUP_PCI_VENDOR_ID, "PCI Vendor ID"},
65     {PLDM_FWUP_IANA_ENTERPRISE_ID, "IANA Enterprise ID"},
66     {PLDM_FWUP_UUID, "UUID"},
67     {PLDM_FWUP_PNP_VENDOR_ID, "PnP Vendor ID"},
68     {PLDM_FWUP_ACPI_VENDOR_ID, "ACPI Vendor ID"},
69     {PLDM_FWUP_PCI_DEVICE_ID, "PCI Device ID"},
70     {PLDM_FWUP_PCI_SUBSYSTEM_VENDOR_ID, "PCI Subsystem Vendor ID"},
71     {PLDM_FWUP_PCI_SUBSYSTEM_ID, "PCI Subsystem ID"},
72     {PLDM_FWUP_PCI_REVISION_ID, "PCI Revision ID"},
73     {PLDM_FWUP_PNP_PRODUCT_IDENTIFIER, "PnP Product Identifier"},
74     {PLDM_FWUP_ACPI_PRODUCT_IDENTIFIER, "ACPI Product Identifier"},
75     {PLDM_FWUP_VENDOR_DEFINED, "Vendor Defined"}};
76 
77 /*
78  * Convert PLDM Firmware String Type to uint8_t
79  *
80  *  @param[in]  compImgVerStrType - the component version string
81  *
82  *  @return - the component version string converted to a numeric value.
83  */
convertStringTypeToUInt8(std::string compImgVerStrType)84 uint8_t convertStringTypeToUInt8(std::string compImgVerStrType)
85 {
86     static const std::map<std::string, pldm_firmware_update_string_type>
87         pldmFirmwareUpdateStringType{
88             {"UNKNOWN", PLDM_STR_TYPE_UNKNOWN},
89             {"ASCII", PLDM_STR_TYPE_ASCII},
90             {"UTF_8", PLDM_STR_TYPE_UTF_8},
91             {"UTF_16", PLDM_STR_TYPE_UTF_16},
92             {"UTF_16LE", PLDM_STR_TYPE_UTF_16LE},
93             {"UTF_16BE", PLDM_STR_TYPE_UTF_16BE},
94         };
95 
96     if (pldmFirmwareUpdateStringType.contains(compImgVerStrType))
97     {
98         return pldmFirmwareUpdateStringType.at(compImgVerStrType);
99     }
100     else
101     {
102         return static_cast<uint8_t>(std::stoi(compImgVerStrType));
103     }
104 }
105 
106 class GetStatus : public CommandInterface
107 {
108   public:
109     ~GetStatus() = default;
110     GetStatus() = delete;
111     GetStatus(const GetStatus&) = delete;
112     GetStatus(GetStatus&&) = default;
113     GetStatus& operator=(const GetStatus&) = delete;
114     GetStatus& operator=(GetStatus&&) = delete;
115 
116     using CommandInterface::CommandInterface;
117 
createRequestMsg()118     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
119     {
120         std::vector<uint8_t> requestMsg(
121             sizeof(pldm_msg_hdr) + PLDM_GET_STATUS_REQ_BYTES);
122         auto request = new (requestMsg.data()) pldm_msg;
123         auto rc = encode_get_status_req(instanceId, request,
124                                         PLDM_GET_STATUS_REQ_BYTES);
125         return {rc, requestMsg};
126     }
127 
parseResponseMsg(pldm_msg * responsePtr,size_t payloadLength)128     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
129     {
130         uint8_t completionCode = 0;
131         uint8_t currentState = 0;
132         uint8_t previousState = 0;
133         uint8_t auxState = 0;
134         uint8_t auxStateStatus = 0;
135         uint8_t progressPercent = 0;
136         uint8_t reasonCode = 0;
137         bitfield32_t updateOptionFlagsEnabled{0};
138 
139         auto rc = decode_get_status_resp(
140             responsePtr, payloadLength, &completionCode, &currentState,
141             &previousState, &auxState, &auxStateStatus, &progressPercent,
142             &reasonCode, &updateOptionFlagsEnabled);
143         if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
144         {
145             std::cerr << "Response Message Error: "
146                       << "rc=" << rc << ",cc=" << (int)completionCode << "\n";
147             return;
148         }
149 
150         ordered_json data;
151         data["CurrentState"] = fdStateMachine.at(currentState);
152         data["PreviousState"] = fdStateMachine.at(previousState);
153         data["AuxState"] = fdAuxState.at(auxState);
154         if (auxStateStatus >= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_START &&
155             auxStateStatus <= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_END)
156         {
157             data["AuxStateStatus"] = auxStateStatus;
158         }
159         else
160         {
161             data["AuxStateStatus"] = fdAuxStateStatus.at(auxStateStatus);
162         }
163         data["ProgressPercent"] = progressPercent;
164         if (reasonCode >= PLDM_FD_STATUS_VENDOR_DEFINED_MIN &&
165             reasonCode <= PLDM_FD_STATUS_VENDOR_DEFINED_MAX)
166         {
167             data["ReasonCode"] = reasonCode;
168         }
169         else
170         {
171             data["ReasonCode"] = fdReasonCode.at(reasonCode);
172         }
173         data["UpdateOptionFlagsEnabled"] = updateOptionFlagsEnabled.value;
174 
175         pldmtool::helper::DisplayInJson(data);
176     }
177 };
178 
179 const std::map<uint16_t, std::string> componentClassification{
180     {PLDM_COMP_UNKNOWN, "Unknown"},
181     {PLDM_COMP_OTHER, "Other"},
182     {PLDM_COMP_DRIVER, "Driver"},
183     {PLDM_COMP_CONFIGURATION_SOFTWARE, "Configuration Software"},
184     {PLDM_COMP_APPLICATION_SOFTWARE, "Application Software"},
185     {PLDM_COMP_INSTRUMENTATION, "Instrumentation"},
186     {PLDM_COMP_FIRMWARE_OR_BIOS, "Firmware/BIOS"},
187     {PLDM_COMP_DIAGNOSTIC_SOFTWARE, "Diagnostic Software"},
188     {PLDM_COMP_OPERATING_SYSTEM, "Operating System"},
189     {PLDM_COMP_MIDDLEWARE, "Middleware"},
190     {PLDM_COMP_FIRMWARE, "Firmware"},
191     {PLDM_COMP_BIOS_OR_FCODE, "BIOS/FCode"},
192     {PLDM_COMP_SUPPORT_OR_SERVICEPACK, "Support/Service Pack"},
193     {PLDM_COMP_SOFTWARE_BUNDLE, "Software Bundle"},
194     {PLDM_COMP_DOWNSTREAM_DEVICE, "Downstream Device"}};
195 
196 class GetFwParams : public CommandInterface
197 {
198   public:
199     ~GetFwParams() = default;
200     GetFwParams() = delete;
201     GetFwParams(const GetFwParams&) = delete;
202     GetFwParams(GetFwParams&&) = default;
203     GetFwParams& operator=(const GetFwParams&) = delete;
204     GetFwParams& operator=(GetFwParams&&) = delete;
205 
206     using CommandInterface::CommandInterface;
207 
createRequestMsg()208     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
209     {
210         std::vector<uint8_t> requestMsg(
211             sizeof(pldm_msg_hdr) + PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES);
212         auto request = new (requestMsg.data()) pldm_msg;
213         auto rc = encode_get_firmware_parameters_req(
214             instanceId, PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES, request);
215         return {rc, requestMsg};
216     }
217 
parseResponseMsg(pldm_msg * responsePtr,size_t payloadLength)218     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
219     {
220         pldm_get_firmware_parameters_resp fwParams{};
221         variable_field activeCompImageSetVersion{};
222         variable_field pendingCompImageSetVersion{};
223         variable_field compParameterTable{};
224 
225         auto rc = decode_get_firmware_parameters_resp(
226             responsePtr, payloadLength, &fwParams, &activeCompImageSetVersion,
227             &pendingCompImageSetVersion, &compParameterTable);
228         if (rc != PLDM_SUCCESS || fwParams.completion_code != PLDM_SUCCESS)
229         {
230             std::cerr << "Response Message Error: "
231                       << "rc=" << rc << ",cc=" << (int)fwParams.completion_code
232                       << "\n";
233             return;
234         }
235 
236         ordered_json capabilitiesDuringUpdate;
237         if (fwParams.capabilities_during_update.bits.bit0)
238         {
239             capabilitiesDuringUpdate
240                 ["Component Update Failure Recovery Capability"] =
241                     "Device will not revert to previous component image upon failure, timeout or cancellation of the transfer.";
242         }
243         else
244         {
245             capabilitiesDuringUpdate
246                 ["Component Update Failure Recovery Capability"] =
247                     "Device will revert to previous component image upon failure, timeout or cancellation of the transfer.";
248         }
249 
250         if (fwParams.capabilities_during_update.bits.bit1)
251         {
252             capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
253                 "Device will not be able to update component again unless it exits update mode and the UA sends a new Request Update command.";
254         }
255         else
256         {
257             capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
258                 " Device can have component updated again without exiting update mode and restarting transfer via RequestUpdate command.";
259         }
260 
261         if (fwParams.capabilities_during_update.bits.bit2)
262         {
263             capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
264                 "Firmware Device can support a partial update, whereby a package which contains a component image set that is a subset of all components currently residing on the FD, can be transferred.";
265         }
266         else
267         {
268             capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
269                 "Firmware Device cannot accept a partial update and all components present on the FD shall be updated.";
270         }
271 
272         if (fwParams.capabilities_during_update.bits.bit3)
273         {
274             capabilitiesDuringUpdate
275                 ["Firmware Device Host Functionality during Firmware Update"] =
276                     "Device will not revert to previous component image upon failure, timeout or cancellation of the transfer";
277         }
278         else
279         {
280             capabilitiesDuringUpdate
281                 ["Firmware Device Host Functionality during Firmware Update"] =
282                     "Device will revert to previous component image upon failure, timeout or cancellation of the transfer";
283         }
284 
285         if (fwParams.capabilities_during_update.bits.bit4)
286         {
287             capabilitiesDuringUpdate["Firmware Device Update Mode Restrictions"] =
288                 "Firmware device unable to enter update mode if host OS environment is active.";
289         }
290         else
291         {
292             capabilitiesDuringUpdate
293                 ["Firmware Device Update Mode Restrictions"] =
294                     "No host OS environment restriction for update mode";
295         }
296 
297         ordered_json data;
298         data["CapabilitiesDuringUpdate"] = capabilitiesDuringUpdate;
299         data["ComponentCount"] = static_cast<uint16_t>(fwParams.comp_count);
300         data["ActiveComponentImageSetVersionString"] =
301             pldm::utils::toString(activeCompImageSetVersion);
302         data["PendingComponentImageSetVersionString"] =
303             pldm::utils::toString(pendingCompImageSetVersion);
304 
305         auto compParamPtr = compParameterTable.ptr;
306         auto compParamTableLen = compParameterTable.length;
307         pldm_component_parameter_entry compEntry{};
308         variable_field activeCompVerStr{};
309         variable_field pendingCompVerStr{};
310         ordered_json compDataEntries;
311 
312         while (fwParams.comp_count-- && (compParamTableLen > 0))
313         {
314             ordered_json compData;
315             auto rc = decode_get_firmware_parameters_resp_comp_entry(
316                 compParamPtr, compParamTableLen, &compEntry, &activeCompVerStr,
317                 &pendingCompVerStr);
318             if (rc)
319             {
320                 std::cerr
321                     << "Decoding component parameter table entry failed, RC="
322                     << rc << "\n";
323                 return;
324             }
325 
326             if (componentClassification.contains(compEntry.comp_classification))
327             {
328                 compData["ComponentClassification"] =
329                     componentClassification.at(compEntry.comp_classification);
330             }
331             else
332             {
333                 compData["ComponentClassification"] =
334                     static_cast<uint16_t>(compEntry.comp_classification);
335             }
336             compData["ComponentIdentifier"] =
337                 static_cast<uint16_t>(compEntry.comp_identifier);
338             compData["ComponentClassificationIndex"] =
339                 static_cast<uint8_t>(compEntry.comp_classification_index);
340             compData["ActiveComponentComparisonStamp"] =
341                 static_cast<uint32_t>(compEntry.active_comp_comparison_stamp);
342 
343             // ActiveComponentReleaseData
344             std::array<uint8_t, 8> noReleaseData{0x00, 0x00, 0x00, 0x00,
345                                                  0x00, 0x00, 0x00, 0x00};
346             if (std::equal(noReleaseData.begin(), noReleaseData.end(),
347                            compEntry.active_comp_release_date))
348             {
349                 compData["ActiveComponentReleaseDate"] = "";
350             }
351             else
352             {
353                 std::string activeComponentReleaseDate(
354                     reinterpret_cast<const char*>(
355                         compEntry.active_comp_release_date),
356                     sizeof(compEntry.active_comp_release_date));
357                 compData["ActiveComponentReleaseDate"] =
358                     activeComponentReleaseDate;
359             }
360 
361             compData["PendingComponentComparisonStamp"] =
362                 static_cast<uint32_t>(compEntry.pending_comp_comparison_stamp);
363 
364             // PendingComponentReleaseData
365             if (std::equal(noReleaseData.begin(), noReleaseData.end(),
366                            compEntry.pending_comp_release_date))
367             {
368                 compData["PendingComponentReleaseDate"] = "";
369             }
370             else
371             {
372                 std::string pendingComponentReleaseDate(
373                     reinterpret_cast<const char*>(
374                         compEntry.pending_comp_release_date),
375                     sizeof(compEntry.pending_comp_release_date));
376                 compData["PendingComponentReleaseDate"] =
377                     pendingComponentReleaseDate;
378             }
379 
380             // ComponentActivationMethods
381             ordered_json componentActivationMethods;
382             if (compEntry.comp_activation_methods.bits.bit0)
383             {
384                 componentActivationMethods.push_back("Automatic");
385             }
386             else if (compEntry.comp_activation_methods.bits.bit1)
387             {
388                 componentActivationMethods.push_back("Self-Contained");
389             }
390             else if (compEntry.comp_activation_methods.bits.bit2)
391             {
392                 componentActivationMethods.push_back("Medium-specific reset");
393             }
394             else if (compEntry.comp_activation_methods.bits.bit3)
395             {
396                 componentActivationMethods.push_back("System reboot");
397             }
398             else if (compEntry.comp_activation_methods.bits.bit4)
399             {
400                 componentActivationMethods.push_back("DC power cycel");
401             }
402             else if (compEntry.comp_activation_methods.bits.bit5)
403             {
404                 componentActivationMethods.push_back("AC power cycle");
405             }
406             compData["ComponentActivationMethods"] = componentActivationMethods;
407 
408             // CapabilitiesDuringUpdate
409             ordered_json compCapabilitiesDuringUpdate;
410             if (compEntry.capabilities_during_update.bits.bit0)
411             {
412                 compCapabilitiesDuringUpdate
413                     ["Firmware Device apply state functionality"] =
414                         "Firmware Device performs an auto-apply during transfer phase and apply step will be completed immediately.";
415             }
416             else
417             {
418                 compCapabilitiesDuringUpdate
419                     ["Firmware Device apply state functionality"] =
420                         " Firmware Device will execute an operation during the APPLY state which will include migrating the new component image to its final non-volatile storage destination.";
421             }
422             compData["CapabilitiesDuringUpdate"] = compCapabilitiesDuringUpdate;
423 
424             compData["ActiveComponentVersionString"] =
425                 pldm::utils::toString(activeCompVerStr);
426             compData["PendingComponentVersionString"] =
427                 pldm::utils::toString(pendingCompVerStr);
428 
429             compParamPtr += sizeof(pldm_component_parameter_entry) +
430                             activeCompVerStr.length + pendingCompVerStr.length;
431             compParamTableLen -=
432                 sizeof(pldm_component_parameter_entry) +
433                 activeCompVerStr.length + pendingCompVerStr.length;
434             compDataEntries.push_back(compData);
435         }
436         data["ComponentParameterEntries"] = compDataEntries;
437 
438         pldmtool::helper::DisplayInJson(data);
439     }
440 };
441 
442 class QueryDeviceIdentifiers : public CommandInterface
443 {
444   public:
445     ~QueryDeviceIdentifiers() = default;
446     QueryDeviceIdentifiers() = delete;
447     QueryDeviceIdentifiers(const QueryDeviceIdentifiers&) = delete;
448     QueryDeviceIdentifiers(QueryDeviceIdentifiers&&) = default;
449     QueryDeviceIdentifiers& operator=(const QueryDeviceIdentifiers&) = delete;
450     QueryDeviceIdentifiers& operator=(QueryDeviceIdentifiers&&) = delete;
451 
452     /**
453      * @brief Implementation of createRequestMsg for QueryDeviceIdentifiers
454      *
455      * @return std::pair<int, std::vector<uint8_t>>
456      */
457     std::pair<int, std::vector<uint8_t>> createRequestMsg() override;
458 
459     /**
460      * @brief Implementation of parseResponseMsg for QueryDeviceIdentifiers
461      *
462      * @param[in] responsePtr
463      * @param[in] payloadLength
464      */
465     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override;
466     using CommandInterface::CommandInterface;
467 
468   private:
469     /**
470      * @brief Method to update QueryDeviceIdentifiers json response in a user
471      * friendly format
472      *
473      * @param[in] descriptors - descriptor json response
474      * @param[in] descriptorType - descriptor type
475      * @param[in] descriptorVal - descriptor value
476      */
477     void updateDescriptor(
478         ordered_json& descriptors, const DescriptorType& descriptorType,
479         const std::variant<DescriptorData, VendorDefinedDescriptorInfo>&
480             descriptorVal);
481 };
482 
updateDescriptor(ordered_json & descriptors,const DescriptorType & descriptorType,const std::variant<DescriptorData,VendorDefinedDescriptorInfo> & descriptorVal)483 void QueryDeviceIdentifiers::updateDescriptor(
484     ordered_json& descriptors, const DescriptorType& descriptorType,
485     const std::variant<DescriptorData, VendorDefinedDescriptorInfo>&
486         descriptorVal)
487 {
488     std::ostringstream descDataStream;
489     DescriptorData descData;
490     if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
491     {
492         descData = std::get<DescriptorData>(descriptorVal);
493     }
494     else
495     {
496         descData = std::get<VendorDefinedDescriptorData>(
497             std::get<VendorDefinedDescriptorInfo>(descriptorVal));
498     }
499     for (int byte : descData)
500     {
501         descDataStream << std::setfill('0') << std::setw(2) << std::hex << byte;
502     }
503 
504     if (descriptorName.contains(descriptorType))
505     {
506         // Update the existing json response if entry is already present
507         for (auto& descriptor : descriptors)
508         {
509             if (descriptor["Type"] == descriptorName.at(descriptorType))
510             {
511                 if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
512                 {
513                     descriptor["Value"].emplace_back(descDataStream.str());
514                 }
515                 else
516                 {
517                     ordered_json vendorDefinedVal;
518                     vendorDefinedVal[std::get<VendorDefinedDescriptorTitle>(
519                         std::get<VendorDefinedDescriptorInfo>(descriptorVal))] =
520                         descDataStream.str();
521                     descriptor["Value"].emplace_back(vendorDefinedVal);
522                 }
523                 return;
524             }
525         }
526         // Entry is not present, add type and value to json response
527         ordered_json descriptor = ordered_json::object(
528             {{"Type", descriptorName.at(descriptorType)},
529              {"Value", ordered_json::array()}});
530         if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
531         {
532             descriptor["Value"].emplace_back(descDataStream.str());
533         }
534         else
535         {
536             ordered_json vendorDefinedVal;
537             vendorDefinedVal[std::get<VendorDefinedDescriptorTitle>(
538                 std::get<VendorDefinedDescriptorInfo>(descriptorVal))] =
539                 descDataStream.str();
540             descriptor["Value"].emplace_back(vendorDefinedVal);
541         }
542         descriptors.emplace_back(descriptor);
543     }
544     else
545     {
546         std::cerr << "Unknown descriptor type, type=" << descriptorType << "\n";
547     }
548 }
createRequestMsg()549 std::pair<int, std::vector<uint8_t>> QueryDeviceIdentifiers::createRequestMsg()
550 {
551     std::vector<uint8_t> requestMsg(
552         sizeof(pldm_msg_hdr) + PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES);
553     auto request = new (requestMsg.data()) pldm_msg;
554     auto rc = encode_query_device_identifiers_req(
555         instanceId, PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES, request);
556     return {rc, requestMsg};
557 }
558 
parseResponseMsg(pldm_msg * responsePtr,size_t payloadLength)559 void QueryDeviceIdentifiers::parseResponseMsg(pldm_msg* responsePtr,
560                                               size_t payloadLength)
561 {
562     uint8_t completionCode = PLDM_SUCCESS;
563     uint32_t deviceIdentifiersLen = 0;
564     uint8_t descriptorCount = 0;
565     uint8_t* descriptorPtr = nullptr;
566     uint8_t eid = getMCTPEID();
567     auto rc = decode_query_device_identifiers_resp(
568         responsePtr, payloadLength, &completionCode, &deviceIdentifiersLen,
569         &descriptorCount, &descriptorPtr);
570     if (rc)
571     {
572         std::cerr << "Decoding QueryDeviceIdentifiers response failed,EID="
573                   << unsigned(eid) << ", RC=" << rc << "\n";
574         return;
575     }
576     if (completionCode)
577     {
578         std::cerr << "QueryDeviceIdentifiers response failed with error "
579                      "completion code, EID="
580                   << unsigned(eid) << ", CC=" << unsigned(completionCode)
581                   << "\n";
582         return;
583     }
584     ordered_json data;
585     data["EID"] = eid;
586     ordered_json descriptors;
587     while (descriptorCount-- && (deviceIdentifiersLen > 0))
588     {
589         DescriptorType descriptorType = 0;
590         variable_field descriptorData{};
591 
592         rc = decode_descriptor_type_length_value(
593             descriptorPtr, deviceIdentifiersLen, &descriptorType,
594             &descriptorData);
595         if (rc)
596         {
597             std::cerr << "Decoding descriptor type, length and value failed,"
598                       << "EID=" << unsigned(eid) << ",RC=" << rc << "\n ";
599             return;
600         }
601 
602         if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
603         {
604             std::vector<uint8_t> descData(
605                 descriptorData.ptr, descriptorData.ptr + descriptorData.length);
606             updateDescriptor(descriptors, descriptorType, descData);
607         }
608         else
609         {
610             uint8_t descriptorTitleStrType = 0;
611             variable_field descriptorTitleStr{};
612             variable_field vendorDefinedDescriptorData{};
613 
614             rc = decode_vendor_defined_descriptor_value(
615                 descriptorData.ptr, descriptorData.length,
616                 &descriptorTitleStrType, &descriptorTitleStr,
617                 &vendorDefinedDescriptorData);
618             if (rc)
619             {
620                 std::cerr << "Decoding Vendor-defined descriptor value"
621                           << "failed EID=" << unsigned(eid) << ", RC=" << rc
622                           << "\n ";
623                 return;
624             }
625 
626             auto vendorDescTitle = pldm::utils::toString(descriptorTitleStr);
627             std::vector<uint8_t> vendorDescData(
628                 vendorDefinedDescriptorData.ptr,
629                 vendorDefinedDescriptorData.ptr +
630                     vendorDefinedDescriptorData.length);
631             updateDescriptor(descriptors, descriptorType,
632                              std::make_tuple(vendorDescTitle, vendorDescData));
633         }
634         auto nextDescriptorOffset =
635             sizeof(pldm_descriptor_tlv().descriptor_type) +
636             sizeof(pldm_descriptor_tlv().descriptor_length) +
637             descriptorData.length;
638         descriptorPtr += nextDescriptorOffset;
639         deviceIdentifiersLen -= nextDescriptorOffset;
640     }
641     data["Descriptors"] = descriptors;
642     pldmtool::helper::DisplayInJson(data);
643 }
644 
645 class RequestUpdate : public CommandInterface
646 {
647   public:
648     ~RequestUpdate() = default;
649     RequestUpdate() = delete;
650     RequestUpdate(const RequestUpdate&) = delete;
651     RequestUpdate(RequestUpdate&&) = delete;
652     RequestUpdate& operator=(const RequestUpdate&) = delete;
653     RequestUpdate& operator=(RequestUpdate&&) = delete;
654 
RequestUpdate(const char * type,const char * name,CLI::App * app)655     explicit RequestUpdate(const char* type, const char* name, CLI::App* app) :
656         CommandInterface(type, name, app)
657     {
658         app->add_option(
659                "--max_transfer_size", maxTransferSize,
660                "Specifies the maximum size, in bytes, of the variable payload allowed to\n"
661                "be requested by the FD via the RequestFirmwareData command that is contained\n"
662                "within a PLDM message. This value shall be equal to or greater than firmware update\n"
663                "baseline transfer size.")
664             ->required();
665 
666         app->add_option(
667                "--num_comps", numComps,
668                "Specifies the number of components that will be passed to the FD during the update.\n"
669                "The FD can use this value to compare against the number of PassComponentTable\n"
670                "commands received.")
671             ->required();
672 
673         app->add_option(
674                "--max_transfer_reqs", maxTransferReqs,
675                "Specifies the number of outstanding RequestFirmwareData commands that can be\n"
676                "sent by the FD. The minimum required value is '1' which the UA shall support.\n"
677                "It is optional for the UA to support a value higher than '1' for this field.")
678             ->required();
679 
680         app->add_option(
681                "--package_data_length", packageDataLength,
682                "This field shall be set to the value contained within\n"
683                "the FirmwareDevicePackageDataLength field that was provided in\n"
684                "the firmware package header. If no firmware package data was\n"
685                "provided in the firmware update package then this length field\n"
686                "shall be set to 0x0000.")
687             ->required();
688 
689         app->add_option(
690                "--comp_img_ver_str_type", compImgVerStrType,
691                "The type of string used in the ComponentImageSetVersionString\n"
692                "field. Possible values\n"
693                "{UNKNOWN->0, ASCII->1, UTF_8->2, UTF_16->3, UTF_16LE->4, UTF_16BE->5}\n"
694                "OR {0,1,2,3,4,5}")
695             ->required()
696             ->check([](const std::string& value) -> std::string {
697                 static const std::set<std::string> validStrings{
698                     "UNKNOWN", "ASCII",    "UTF_8",
699                     "UTF_16",  "UTF_16LE", "UTF_16BE"};
700 
701                 if (validStrings.contains(value))
702                 {
703                     return "";
704                 }
705 
706                 try
707                 {
708                     int intValue = std::stoi(value);
709                     if (intValue >= 0 && intValue <= 255)
710                     {
711                         return "";
712                     }
713                     return "Invalid value. Must be one of UNKNOWN, ASCII, UTF_8, UTF_16, UTF_16LE, UTF_16BE, or a number between 0 and 255";
714                 }
715                 catch (const std::exception&)
716                 {
717                     return "Invalid value. Must be one of UNKNOWN, ASCII, UTF_8, UTF_16, UTF_16LE, UTF_16BE, or a number between 0 and 255";
718                 }
719             });
720 
721         app->add_option(
722                "--comp_img_ver_str_len", compImgVerStrLen,
723                "The length, in bytes, of the ComponentImageSetVersionString.")
724             ->required();
725 
726         app->add_option(
727                "--comp_img_set_ver_str", compImgSetVerStr,
728                "Component Image Set version information, up to 255 bytes.")
729             ->required();
730     }
731 
createRequestMsg()732     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
733     {
734         variable_field compImgSetVerStrInfo{};
735         compImgSetVerStrInfo.ptr =
736             reinterpret_cast<const uint8_t*>(compImgSetVerStr.data());
737         compImgSetVerStrInfo.length =
738             static_cast<uint8_t>(compImgSetVerStr.size());
739 
740         std::vector<uint8_t> requestMsg(
741             sizeof(pldm_msg_hdr) + sizeof(struct pldm_request_update_req) +
742             compImgSetVerStrInfo.length);
743 
744         auto request = new (requestMsg.data()) pldm_msg;
745 
746         auto rc = encode_request_update_req(
747             instanceId, maxTransferSize, numComps, maxTransferReqs,
748             packageDataLength, convertStringTypeToUInt8(compImgVerStrType),
749             compImgVerStrLen, &compImgSetVerStrInfo, request,
750             sizeof(struct pldm_request_update_req) +
751                 compImgSetVerStrInfo.length);
752 
753         return {rc, requestMsg};
754     }
755 
parseResponseMsg(pldm_msg * responsePtr,size_t payloadLength)756     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
757     {
758         uint8_t cc = 0;
759         uint16_t fdMetaDataLen = 0;
760         uint8_t fdWillSendPkgData = 0;
761 
762         auto rc =
763             decode_request_update_resp(responsePtr, payloadLength, &cc,
764                                        &fdMetaDataLen, &fdWillSendPkgData);
765         if (rc)
766         {
767             std::cerr << "Response Message Error: "
768                       << "rc=" << rc << ",cc=" << (int)cc << "\n";
769             return;
770         }
771 
772         ordered_json data;
773         fillCompletionCode(cc, data, PLDM_FWUP);
774 
775         if (cc == PLDM_SUCCESS)
776         {
777             data["FirmwareDeviceMetaDataLength"] = fdMetaDataLen;
778             data["FDWillSendGetPackageDataCommand"] =
779                 std::format("0x{:02X}", fdWillSendPkgData);
780         }
781         pldmtool::helper::DisplayInJson(data);
782     }
783 
784   private:
785     uint32_t maxTransferSize;
786     uint16_t numComps;
787     uint8_t maxTransferReqs;
788     uint16_t packageDataLength;
789     std::string compImgVerStrType;
790     uint8_t compImgVerStrLen;
791     std::string compImgSetVerStr;
792 };
793 
registerCommand(CLI::App & app)794 void registerCommand(CLI::App& app)
795 {
796     auto fwUpdate =
797         app.add_subcommand("fw_update", "firmware update type commands");
798     fwUpdate->require_subcommand(1);
799 
800     auto getStatus = fwUpdate->add_subcommand("GetStatus", "Status of the FD");
801     commands.push_back(
802         std::make_unique<GetStatus>("fw_update", "GetStatus", getStatus));
803 
804     auto getFwParams = fwUpdate->add_subcommand(
805         "GetFwParams", "To get the component details of the FD");
806     commands.push_back(
807         std::make_unique<GetFwParams>("fw_update", "GetFwParams", getFwParams));
808 
809     auto queryDeviceIdentifiers = fwUpdate->add_subcommand(
810         "QueryDeviceIdentifiers", "To query device identifiers of the FD");
811     commands.push_back(std::make_unique<QueryDeviceIdentifiers>(
812         "fw_update", "QueryDeviceIdentifiers", queryDeviceIdentifiers));
813 
814     auto requestUpdate = fwUpdate->add_subcommand(
815         "RequestUpdate", "To initiate a firmware update");
816     commands.push_back(std::make_unique<RequestUpdate>(
817         "fw_update", "RequestUpdate", requestUpdate));
818 }
819 
820 } // namespace fw_update
821 
822 } // namespace pldmtool
823