Add setBIOSattributecurrentvalue command

Tested:
Type of Enum->
~# ./pldmtool bios GetBIOSAttributeCurrentValueByHandle -a Led
CurrentValue: On
~# ./pldmtool bios setbiosattributecurrentvalue -a Led -d Off
SetBIOSAttributeCurrentValue: SUCCESS
~# ./pldmtool bios GetBIOSAttributeCurrentValueByHandle -a Led
CurrentValue: Off
~# ./pldmtool bios setbiosattributecurrentvalue -a Led -d OUT
Set Attribute Error: It's not a possible value

Type of Int->
~# ./pldmtool bios GetBIOSAttributeCurrentValueByHandle -a OUTLET
CurrentValue: 4
~# ./pldmtool bios setbiosattributecurrentvalue -a OUTLET -d 0
SetBIOSAttributeCurrentValue: SUCCESS
~# ./pldmtool bios GetBIOSAttributeCurrentValueByHandle -a OUTLET
CurrentValue: 0
~# ./pldmtool bios setbiosattributecurrentvalue -a OUTLET -d -5
Response Message Error: rc=0,cc=2

Type of String->
~# ./pldmtool bios getbiosattributecurrentvaluebyhandle -a Model
CurrentValue: powersupply0
~# ./pldmtool bios setbiosattributecurrentvalue -a Model -d powersupply1
SetBIOSAttributeCurrentValue: SUCCESS
~# ./pldmtool bios getbiosattributecurrentvaluebyhandle -a Model
CurrentValue: powersupply1
~# ./pldmtool bios setbiosattributecurrentvalue -a Model -d EF...yueg
Response Message Error: rc=0,cc=3
(the EF...yueg is longer than 100 bytes)

Signed-off-by: Adair Li <lichao.lc01@inspur.com>
Signed-off-by: Xiaochao Ma <maxiaochao@inspur.com>
Change-Id: Ifc70b206eadc08f4de93f022f946854aac76452b
diff --git a/tool/pldm_bios_cmd.cpp b/tool/pldm_bios_cmd.cpp
index 126642f..afd34d8 100644
--- a/tool/pldm_bios_cmd.cpp
+++ b/tool/pldm_bios_cmd.cpp
@@ -242,15 +242,16 @@
         return std::make_optional<Table>(tableData, tableData + tableSize);
     }
 
-    std::optional<uint16_t> findAttrHandleByName(const std::string& name,
-                                                 const Table& attrTable,
-                                                 const Table& stringTable)
+    const pldm_bios_attr_table_entry*
+        findAttrEntryByName(const std::string& name, const Table& attrTable,
+                            const Table& stringTable)
     {
         auto stringEntry = pldm_bios_table_string_find_by_string(
             stringTable.data(), stringTable.size(), name.c_str());
         if (stringEntry == nullptr)
         {
-            return std::nullopt;
+            std::cout << "StringTable initialize failed" << std::endl;
+            return nullptr;
         }
 
         auto nameHandle =
@@ -263,10 +264,23 @@
                 pldm_bios_table_attr_entry_decode_string_handle(attr);
             if (attrNameHandle == nameHandle)
             {
-                return pldm_bios_table_attr_entry_decode_attribute_handle(attr);
+                return attr;
             }
         }
-        return std::nullopt;
+        return nullptr;
+    }
+
+    std::optional<uint16_t> findAttrHandleByName(const std::string& name,
+                                                 const Table& attrTable,
+                                                 const Table& stringTable)
+    {
+        auto attribute = findAttrEntryByName(name, attrTable, stringTable);
+        if (attribute == nullptr)
+        {
+            return std::nullopt;
+        }
+
+        return pldm_bios_table_attr_entry_decode_attribute_handle(attribute);
     }
 
     std::string decodeStringFromStringEntry(
@@ -677,6 +691,179 @@
     std::string attrName;
 };
 
+class SetBIOSAttributeCurrentValue : public GetBIOSTableHandler
+{
+  public:
+    ~SetBIOSAttributeCurrentValue() = default;
+    SetBIOSAttributeCurrentValue() = delete;
+    SetBIOSAttributeCurrentValue(const SetBIOSAttributeCurrentValue&) = delete;
+    SetBIOSAttributeCurrentValue(SetBIOSAttributeCurrentValue&&) = default;
+    SetBIOSAttributeCurrentValue&
+        operator=(const SetBIOSAttributeCurrentValue&) = delete;
+    SetBIOSAttributeCurrentValue&
+        operator=(SetBIOSAttributeCurrentValue&&) = default;
+
+    explicit SetBIOSAttributeCurrentValue(const char* type, const char* name,
+                                          CLI::App* app) :
+        GetBIOSTableHandler(type, name, app)
+    {
+        app->add_option("-a, --attribute", attrName, "pldm attribute name")
+            ->required();
+        app->add_option("-d, --data", attrValue, "pldm attribute value")
+            ->required();
+        // -v is conflict with --verbose in class CommandInterface, so used -d
+    }
+
+    void exec()
+    {
+        auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
+        auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+        auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
+
+        if (!stringTable || !attrTable)
+        {
+            std::cout << "StringTable/AttrTable Unavaliable" << std::endl;
+            return;
+        }
+
+        auto attrEntry =
+            findAttrEntryByName(attrName, *attrTable, *stringTable);
+        if (attrEntry == nullptr)
+        {
+            std::cout << "Could not find attribute :" << attrName << std::endl;
+            return;
+        }
+
+        std::vector<uint8_t> requestMsg;
+
+        int rc = 0;
+        auto attrType = attrEntry->attr_type;
+        size_t entryLength = 1;
+        std::vector<uint8_t> attrValueEntry(entryLength, 0);
+
+        switch (attrType)
+        {
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            case PLDM_BIOS_STRING_READ_ONLY:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            {
+                std::cerr << "Set  attribute error: " << attrName
+                          << "is read only." << std::endl;
+                return;
+            }
+            case PLDM_BIOS_ENUMERATION:
+            {
+                entryLength =
+                    pldm_bios_table_attr_value_entry_encode_enum_length(1);
+                auto pvNum =
+                    pldm_bios_table_attr_entry_enum_decode_pv_num(attrEntry);
+                std::vector<uint16_t> pvHdls(pvNum, 0);
+                pldm_bios_table_attr_entry_enum_decode_pv_hdls(
+                    attrEntry, pvHdls.data(), pvNum);
+                auto stringEntry = pldm_bios_table_string_find_by_string(
+                    stringTable->data(), stringTable->size(),
+                    attrValue.c_str());
+                if (stringEntry == nullptr)
+                {
+                    std::cout
+                        << "Set Attribute Error: It's not a possible value"
+                        << std::endl;
+                    return;
+                }
+                auto valueHandle =
+                    pldm_bios_table_string_entry_decode_handle(stringEntry);
+
+                uint8_t i;
+                for (i = 0; i < pvNum; i++)
+                {
+                    if (valueHandle == pvHdls[i])
+                        break;
+                }
+                if (i == pvNum)
+                {
+                    std::cout
+                        << "Set Attribute Error: It's not a possible value"
+                        << std::endl;
+                    return;
+                }
+
+                attrValueEntry.resize(entryLength);
+                std::vector<uint8_t> handles = {i};
+                pldm_bios_table_attr_value_entry_encode_enum(
+                    attrValueEntry.data(), attrValueEntry.size(),
+                    attrEntry->attr_handle, attrType, 1, handles.data());
+                break;
+            }
+            case PLDM_BIOS_STRING:
+            {
+                entryLength =
+                    pldm_bios_table_attr_value_entry_encode_string_length(
+                        attrValue.size());
+
+                attrValueEntry.resize(entryLength);
+
+                pldm_bios_table_attr_value_entry_encode_string(
+                    attrValueEntry.data(), entryLength, attrEntry->attr_handle,
+                    attrType, attrValue.size(), attrValue.c_str());
+                break;
+            }
+            case PLDM_BIOS_INTEGER:
+            {
+                uint64_t value = std::stoll(attrValue);
+                entryLength =
+                    pldm_bios_table_attr_value_entry_encode_integer_length();
+                attrValueEntry.resize(entryLength);
+                pldm_bios_table_attr_value_entry_encode_integer(
+                    attrValueEntry.data(), entryLength, attrEntry->attr_handle,
+                    attrType, value);
+                break;
+            }
+        }
+
+        requestMsg.resize(entryLength + sizeof(pldm_msg_hdr) +
+                          PLDM_SET_BIOS_ATTR_CURR_VAL_MIN_REQ_BYTES);
+
+        rc = encode_set_bios_attribute_current_value_req(
+            instanceId, 0, PLDM_START_AND_END, attrValueEntry.data(),
+            attrValueEntry.size(),
+            reinterpret_cast<pldm_msg*>(requestMsg.data()),
+            requestMsg.size() - sizeof(pldm_msg_hdr));
+
+        if (rc != PLDM_SUCCESS)
+        {
+            std::cerr << "PLDM: Request Message Error, rc =" << rc << std::endl;
+            return;
+        }
+        std::vector<uint8_t> responseMsg;
+        rc = pldmSendRecv(requestMsg, responseMsg);
+        if (rc != PLDM_SUCCESS)
+        {
+            std::cerr << "PLDM: Communication Error, rc =" << rc << std::endl;
+            return;
+        }
+        uint8_t cc = 0;
+        uint32_t nextTransferHandle = 0;
+        auto responsePtr =
+            reinterpret_cast<struct pldm_msg*>(responseMsg.data());
+        auto payloadLength = responseMsg.size() - sizeof(pldm_msg_hdr);
+
+        rc = decode_set_bios_attribute_current_value_resp(
+            responsePtr, payloadLength, &cc, &nextTransferHandle);
+        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
+        {
+            std::cerr << "Response Message Error: "
+                      << "rc=" << rc << ",cc=" << (int)cc << std::endl;
+            return;
+        }
+
+        std::cout << "SetBIOSAttributeCurrentValue: SUCCESS" << std::endl;
+    }
+
+  private:
+    std::string attrName;
+    std::string attrValue;
+};
+
 void registerCommand(CLI::App& app)
 {
     auto bios = app.add_subcommand("bios", "bios type command");
@@ -700,6 +887,11 @@
     commands.push_back(std::make_unique<GetBIOSAttributeCurrentValueByHandle>(
         "bios", "GetBIOSAttributeCurrentValueByHandle",
         getBIOSAttributeCurrentValueByHandle));
+
+    auto setBIOSAttributeCurrentValue = bios->add_subcommand(
+        "SetBIOSAttributeCurrentValue", "set bios attribute current value");
+    commands.push_back(std::make_unique<SetBIOSAttributeCurrentValue>(
+        "bios", "SetBIOSAttributeCurrentValue", setBIOSAttributeCurrentValue));
 }
 
 } // namespace bios