pldmtool: Add GetFirmwareParameters firmware update command

Tested:

pldmtool fw_update GetFwParams -m <eid>
{
    "CapabilitiesDuringUpdate": {
        "Component Update Failure Recovery Capability": "Device will revert to previous component image upon failure, timeout or cancellation of the transfer.",
        "Component Update Failure Retry Capability": " Device can have component updated again without exiting update mode and restarting transfer via RequestUpdate command.",
        "Firmware Device Partial Updates": "Firmware Device cannot accept a partial update and all components present on the FD shall be updated.",
        "Firmware Device Host Functionality during Firmware Update": "Device will revert to previous component image upon failure, timeout or cancellation of the transfer",
        "Firmware Device Update Mode Restrictions": "No host OS environment restriction for update mode"
    },
    "ComponentCount": 2,
    "ActiveComponentImageSetVersionString": "XXXXXXXX",
    "PendingComponentImageSetVersionString": "",
    "ComponentParameterEntries": [
        {
            "ComponentClassification": "Firmware",
            "ComponentIdentifier": 1,
            "ComponentClassificationIndex": 0,
            "ActiveComponentComparisonStamp": 0,
            "ActiveComponentReleaseDate": "",
            "PendingComponentComparisonStamp": 0,
            "PendingComponentReleaseDate": "",
            "ComponentActivationMethods": [
                "System reboot"
            ],
            "CapabilitiesDuringUpdate": {
                "Firmware Device apply state functionality": " 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."
            },
            "ActiveComponentVersionString": "XXXXXXXX",
            "PendingComponentVersionString": ""
        },
        {
            "ComponentClassification": "Firmware",
            "ComponentIdentifier": 2,
            "ComponentClassificationIndex": 0,
            "ActiveComponentComparisonStamp": 0,
            "ActiveComponentReleaseDate": "",
            "PendingComponentComparisonStamp": 0,
            "PendingComponentReleaseDate": "",
            "ComponentActivationMethods": [
                "System reboot"
            ],
            "CapabilitiesDuringUpdate": {
                "Firmware Device apply state functionality": " 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."
            },
            "ActiveComponentVersionString": "XXXXXXXX",
            "PendingComponentVersionString": ""
        }
    ]
}

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I1370beca144853f3b638a7541a8589282ccf1273
diff --git a/pldmtool/pldm_fw_update_cmd.cpp b/pldmtool/pldm_fw_update_cmd.cpp
index 857c2e6..b48a7f6 100644
--- a/pldmtool/pldm_fw_update_cmd.cpp
+++ b/pldmtool/pldm_fw_update_cmd.cpp
@@ -126,6 +126,269 @@
     }
 };
 
+const std::map<uint16_t, std::string> componentClassification{
+    {PLDM_COMP_UNKNOWN, "Unknown"},
+    {PLDM_COMP_OTHER, "Other"},
+    {PLDM_COMP_DRIVER, "Driver"},
+    {PLDM_COMP_CONFIGURATION_SOFTWARE, "Configuration Software"},
+    {PLDM_COMP_APPLICATION_SOFTWARE, "Application Software"},
+    {PLDM_COMP_INSTRUMENTATION, "Instrumentation"},
+    {PLDM_COMP_FIRMWARE_OR_BIOS, "Firmware/BIOS"},
+    {PLDM_COMP_DIAGNOSTIC_SOFTWARE, "Diagnostic Software"},
+    {PLDM_COMP_OPERATING_SYSTEM, "Operating System"},
+    {PLDM_COMP_MIDDLEWARE, "Middleware"},
+    {PLDM_COMP_FIRMWARE, "Firmware"},
+    {PLDM_COMP_BIOS_OR_FCODE, "BIOS/FCode"},
+    {PLDM_COMP_SUPPORT_OR_SERVICEPACK, "Support/Service Pack"},
+    {PLDM_COMP_SOFTWARE_BUNDLE, "Software Bundle"},
+    {PLDM_COMP_DOWNSTREAM_DEVICE, "Downstream Device"}};
+
+class GetFwParams : public CommandInterface
+{
+  public:
+    ~GetFwParams() = default;
+    GetFwParams() = delete;
+    GetFwParams(const GetFwParams&) = delete;
+    GetFwParams(GetFwParams&&) = default;
+    GetFwParams& operator=(const GetFwParams&) = delete;
+    GetFwParams& operator=(GetFwParams&&) = default;
+
+    using CommandInterface::CommandInterface;
+
+    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
+    {
+        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                        PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES);
+        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+        auto rc = encode_get_firmware_parameters_req(
+            instanceId, PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES, request);
+        return {rc, requestMsg};
+    }
+
+    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
+    {
+        pldm_get_firmware_parameters_resp fwParams{};
+        variable_field activeCompImageSetVersion{};
+        variable_field pendingCompImageSetVersion{};
+        variable_field compParameterTable{};
+
+        auto rc = decode_get_firmware_parameters_resp(
+            responsePtr, payloadLength, &fwParams, &activeCompImageSetVersion,
+            &pendingCompImageSetVersion, &compParameterTable);
+        if (rc != PLDM_SUCCESS || fwParams.completion_code != PLDM_SUCCESS)
+        {
+            std::cerr << "Response Message Error: "
+                      << "rc=" << rc << ",cc=" << (int)fwParams.completion_code
+                      << "\n";
+            return;
+        }
+
+        ordered_json capabilitiesDuringUpdate;
+        if (fwParams.capabilities_during_update.bits.bit0)
+        {
+            capabilitiesDuringUpdate
+                ["Component Update Failure Recovery Capability"] =
+                    "Device will not revert to previous component image upon failure, timeout or cancellation of the transfer.";
+        }
+        else
+        {
+            capabilitiesDuringUpdate
+                ["Component Update Failure Recovery Capability"] =
+                    "Device will revert to previous component image upon failure, timeout or cancellation of the transfer.";
+        }
+
+        if (fwParams.capabilities_during_update.bits.bit1)
+        {
+            capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
+                "Device will not be able to update component again unless it exits update mode and the UA sends a new Request Update command.";
+        }
+        else
+        {
+            capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
+                " Device can have component updated again without exiting update mode and restarting transfer via RequestUpdate command.";
+        }
+
+        if (fwParams.capabilities_during_update.bits.bit2)
+        {
+            capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
+                "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.";
+        }
+        else
+        {
+            capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
+                "Firmware Device cannot accept a partial update and all components present on the FD shall be updated.";
+        }
+
+        if (fwParams.capabilities_during_update.bits.bit3)
+        {
+            capabilitiesDuringUpdate
+                ["Firmware Device Host Functionality during Firmware Update"] =
+                    "Device will not revert to previous component image upon failure, timeout or cancellation of the transfer";
+        }
+        else
+        {
+            capabilitiesDuringUpdate
+                ["Firmware Device Host Functionality during Firmware Update"] =
+                    "Device will revert to previous component image upon failure, timeout or cancellation of the transfer";
+        }
+
+        if (fwParams.capabilities_during_update.bits.bit4)
+        {
+            capabilitiesDuringUpdate["Firmware Device Update Mode Restrictions"] =
+                "Firmware device unable to enter update mode if host OS environment is active.";
+        }
+        else
+        {
+            capabilitiesDuringUpdate
+                ["Firmware Device Update Mode Restrictions"] =
+                    "No host OS environment restriction for update mode";
+        }
+
+        ordered_json data;
+        data["CapabilitiesDuringUpdate"] = capabilitiesDuringUpdate;
+        data["ComponentCount"] = static_cast<uint16_t>(fwParams.comp_count);
+        data["ActiveComponentImageSetVersionString"] =
+            pldm::utils::toString(activeCompImageSetVersion);
+        data["PendingComponentImageSetVersionString"] =
+            pldm::utils::toString(pendingCompImageSetVersion);
+
+        auto compParamPtr = compParameterTable.ptr;
+        auto compParamTableLen = compParameterTable.length;
+        pldm_component_parameter_entry compEntry{};
+        variable_field activeCompVerStr{};
+        variable_field pendingCompVerStr{};
+        ordered_json compDataEntries;
+
+        while (fwParams.comp_count-- && (compParamTableLen > 0))
+        {
+            ordered_json compData;
+            auto rc = decode_get_firmware_parameters_resp_comp_entry(
+                compParamPtr, compParamTableLen, &compEntry, &activeCompVerStr,
+                &pendingCompVerStr);
+            if (rc)
+            {
+                std::cerr
+                    << "Decoding component parameter table entry failed, RC="
+                    << rc << "\n";
+                return;
+            }
+
+            if (componentClassification.contains(compEntry.comp_classification))
+            {
+                compData["ComponentClassification"] =
+                    componentClassification.at(compEntry.comp_classification);
+            }
+            else
+            {
+                compData["ComponentClassification"] =
+                    static_cast<uint16_t>(compEntry.comp_classification);
+            }
+            compData["ComponentIdentifier"] =
+                static_cast<uint16_t>(compEntry.comp_identifier);
+            compData["ComponentClassificationIndex"] =
+                static_cast<uint8_t>(compEntry.comp_classification_index);
+            compData["ActiveComponentComparisonStamp"] =
+                static_cast<uint32_t>(compEntry.active_comp_comparison_stamp);
+
+            // ActiveComponentReleaseData
+            std::array<uint8_t, 8> noReleaseData{0x00, 0x00, 0x00, 0x00,
+                                                 0x00, 0x00, 0x00, 0x00};
+            if (std::equal(noReleaseData.begin(), noReleaseData.end(),
+                           compEntry.active_comp_release_date))
+            {
+                compData["ActiveComponentReleaseDate"] = "";
+            }
+            else
+            {
+                std::string activeComponentReleaseDate(
+                    reinterpret_cast<const char*>(
+                        compEntry.active_comp_release_date),
+                    sizeof(compEntry.active_comp_release_date));
+                compData["ActiveComponentReleaseDate"] =
+                    activeComponentReleaseDate;
+            }
+
+            compData["PendingComponentComparisonStamp"] =
+                static_cast<uint32_t>(compEntry.pending_comp_comparison_stamp);
+
+            // PendingComponentReleaseData
+            if (std::equal(noReleaseData.begin(), noReleaseData.end(),
+                           compEntry.pending_comp_release_date))
+            {
+                compData["PendingComponentReleaseDate"] = "";
+            }
+            else
+            {
+                std::string pendingComponentReleaseDate(
+                    reinterpret_cast<const char*>(
+                        compEntry.pending_comp_release_date),
+                    sizeof(compEntry.pending_comp_release_date));
+                compData["PendingComponentReleaseDate"] =
+                    pendingComponentReleaseDate;
+            }
+
+            // ComponentActivationMethods
+            ordered_json componentActivationMethods;
+            if (compEntry.comp_activation_methods.bits.bit0)
+            {
+                componentActivationMethods.push_back("Automatic");
+            }
+            else if (compEntry.comp_activation_methods.bits.bit1)
+            {
+                componentActivationMethods.push_back("Self-Contained");
+            }
+            else if (compEntry.comp_activation_methods.bits.bit2)
+            {
+                componentActivationMethods.push_back("Medium-specific reset");
+            }
+            else if (compEntry.comp_activation_methods.bits.bit3)
+            {
+                componentActivationMethods.push_back("System reboot");
+            }
+            else if (compEntry.comp_activation_methods.bits.bit4)
+            {
+                componentActivationMethods.push_back("DC power cycel");
+            }
+            else if (compEntry.comp_activation_methods.bits.bit5)
+            {
+                componentActivationMethods.push_back("AC power cycle");
+            }
+            compData["ComponentActivationMethods"] = componentActivationMethods;
+
+            // CapabilitiesDuringUpdate
+            ordered_json compCapabilitiesDuringUpdate;
+            if (compEntry.capabilities_during_update.bits.bit0)
+            {
+                compCapabilitiesDuringUpdate
+                    ["Firmware Device apply state functionality"] =
+                        "Firmware Device performs an auto-apply during transfer phase and apply step will be completed immediately.";
+            }
+            else
+            {
+                compCapabilitiesDuringUpdate
+                    ["Firmware Device apply state functionality"] =
+                        " 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.";
+            }
+            compData["CapabilitiesDuringUpdate"] = compCapabilitiesDuringUpdate;
+
+            compData["ActiveComponentVersionString"] =
+                pldm::utils::toString(activeCompVerStr);
+            compData["PendingComponentVersionString"] =
+                pldm::utils::toString(pendingCompVerStr);
+
+            compParamPtr += sizeof(pldm_component_parameter_entry) +
+                            activeCompVerStr.length + pendingCompVerStr.length;
+            compParamTableLen -= sizeof(pldm_component_parameter_entry) +
+                                 activeCompVerStr.length +
+                                 pendingCompVerStr.length;
+            compDataEntries.push_back(compData);
+        }
+        data["ComponentParameterEntries"] = compDataEntries;
+
+        pldmtool::helper::DisplayInJson(data);
+    }
+};
+
 void registerCommand(CLI::App& app)
 {
     auto fwUpdate =
@@ -135,6 +398,11 @@
     auto getStatus = fwUpdate->add_subcommand("GetStatus", "Status of the FD");
     commands.push_back(
         std::make_unique<GetStatus>("fw_update", "GetStatus", getStatus));
+
+    auto getFwParams = fwUpdate->add_subcommand(
+        "GetFwParams", "To get the component details of the FD");
+    commands.push_back(
+        std::make_unique<GetFwParams>("fw_update", "GetFwParams", getFwParams));
 }
 
 } // namespace fw_update