libpldmresponder: bios: Add SetDataTime

Implement the time setting function based on decode requests,
set values and return responses.
It contains the error response.
Unit tests only include tests for numerical conversion.

Tested:
 ~# pldmtool bios SetDateTime -d 20200101080000
 Encode request successfully
 Request Message:
 08 01 80 03 0d 00 00 08 01 01 20 20
 On first recv(),response == request : RC = 0
 Total length: 6
 Shutdown Socket successful :  RC = 0
 Response Message:
 08 01 00 03 0d 00
 Set date time successfully.

 ~# busctl get-property xyz.openbmc_project.Time.Manager\
 /xyz/openbmc_project/time/host\
 xyz.openbmc_project.Time.EpochTime Elapsed

 t 1577865726549597

The value of epochtime matches the time entered.

Signed-off-by: Xiaochao Ma <maxiaochao@inspur.com>
Change-Id: I8d123b993d515bcadba1468974907eb56d7e98b9
diff --git a/libpldmresponder/bios.cpp b/libpldmresponder/bios.cpp
index 8b18333..13b36ab 100644
--- a/libpldmresponder/bios.cpp
+++ b/libpldmresponder/bios.cpp
@@ -3,6 +3,8 @@
 #include "utils.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
+#include <time.h>
+
 #include <array>
 #include <boost/crc.hpp>
 #include <chrono>
@@ -59,6 +61,24 @@
                                      1900); // The number of years since 1900
 }
 
+std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
+                        uint8_t day, uint8_t month, uint16_t year)
+{
+    struct std::tm stm;
+
+    stm.tm_year = year - 1900;
+    stm.tm_mon = month - 1;
+    stm.tm_mday = day;
+    stm.tm_hour = hours;
+    stm.tm_min = minutes;
+    stm.tm_sec = seconds;
+    stm.tm_isdst = -1;
+
+    // It will get the time in seconds since
+    // Epoch, 1970.1.1 00:00:00 +0000,UTC.
+    return timegm(&stm);
+}
+
 size_t getTableTotalsize(size_t sizeWithoutPad)
 {
     return sizeWithoutPad + pldm_bios_table_pad_checksum_size(sizeWithoutPad);
@@ -93,7 +113,10 @@
     catch (const std::exception& e)
     {
     }
-
+    handlers.emplace(PLDM_SET_DATE_TIME,
+                     [this](const pldm_msg* request, size_t payloadLength) {
+                         return this->setDateTime(request, payloadLength);
+                     });
     handlers.emplace(PLDM_GET_DATE_TIME,
                      [this](const pldm_msg* request, size_t payloadLength) {
                          return this->getDateTime(request, payloadLength);
@@ -155,6 +178,50 @@
     return response;
 }
 
+Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength)
+{
+    uint8_t seconds = 0;
+    uint8_t minutes = 0;
+    uint8_t hours = 0;
+    uint8_t day = 0;
+    uint8_t month = 0;
+    uint16_t year = 0;
+    std::time_t timeSec;
+
+    constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime";
+    constexpr auto setTimePath = "/xyz/openbmc_project/time/host";
+    constexpr auto timeSetPro = "Elapsed";
+
+    auto rc = decode_set_date_time_req(request, payloadLength, &seconds,
+                                       &minutes, &hours, &day, &month, &year);
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+    timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day,
+                                                  month, year);
+    uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>(
+                            std::chrono::seconds(timeSec))
+                            .count();
+    std::variant<uint64_t> value{timeUsec};
+    try
+    {
+        pldm::utils::DBusHandler().setDbusProperty(setTimePath, timeSetPro,
+                                                   setTimeInterface, value);
+    }
+    catch (std::exception& e)
+    {
+
+        std::cerr << "Error Setting time,PATH=" << setTimePath
+                  << "TIME INTERFACE=" << setTimeInterface
+                  << "ERROR=" << e.what() << "\n";
+
+        return ccOnlyResponse(request, PLDM_ERROR);
+    }
+
+    return ccOnlyResponse(request, PLDM_SUCCESS);
+}
+
 /** @brief Construct the BIOS string table
  *
  *  @param[in] BIOSStringTable - the string table
diff --git a/libpldmresponder/bios.hpp b/libpldmresponder/bios.hpp
index 41ac70c..4a42bf4 100644
--- a/libpldmresponder/bios.hpp
+++ b/libpldmresponder/bios.hpp
@@ -8,6 +8,7 @@
 
 #include <stdint.h>
 
+#include <ctime>
 #include <functional>
 #include <map>
 #include <vector>
@@ -56,7 +57,7 @@
     /** @brief Handler for GetDateTime
      *
      *  @param[in] request - Request message payload
-     *  @param[return] Response - PLDM Response message
+     *  @return Response - PLDM Response message
      */
     Response getDateTime(const pldm_msg* request, size_t payloadLength);
 
@@ -64,7 +65,7 @@
      *
      *  @param[in] request - Request message
      *  @param[in] payload_length - Request message payload length
-     *  @param[return] Response - PLDM Response message
+     *  @return Response - PLDM Response message
      */
     Response getBIOSTable(const pldm_msg* request, size_t payloadLength);
 
@@ -76,6 +77,14 @@
      */
     Response getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
                                                   size_t payloadLength);
+
+    /** @brief Handler for SetDateTime
+     *
+     *  @param[in] request - Request message payload
+     *  @param[in] payloadLength - Request message payload length
+     *  @return Response - PLDM Response message
+     */
+    Response setDateTime(const pldm_msg* request, size_t payloadLength);
 };
 
 } // namespace bios
@@ -96,6 +105,19 @@
 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
                     uint8_t& hours, uint8_t& day, uint8_t& month,
                     uint16_t& year);
+
+/** @brief Convert dec time to epoch time
+ *
+ *  @param[in] seconds - number of seconds in dec
+ *  @param[in] minutes - number of minutes in dec
+ *  @param[in] hours - number of hours in dec
+ *  @param[in] day - day of the month in dec
+ *  @param[in] month - month number in dec
+ *  @param[in] year - year number in dec
+ *  @return time - epoch time
+ */
+std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
+                        uint8_t day, uint8_t month, uint16_t year);
 } // namespace utils
 
 } // namespace responder