Implement command GetDateTime

This commit implements the GetDateTime command which is
defined in PLDM Bios Control and Configuration Specification.

Change-Id: Iced21bbad7be07d357b6885b1b1e03b07a3da165
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/.gitignore b/.gitignore
index bdc08da..26a8d6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,4 +62,5 @@
 /test/*.gcno
 /test/libpldm_base_test
 /test/libpldmresponder_base_test
-
+/test/libpldm_bios_test
+/test/libpldmresponder_bios_formatTime_test
diff --git a/configure.ac b/configure.ac
index e7b9f44..23e07ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,8 @@
     [AM_CONDITIONAL(AUTOCONF_CODE_COVERAGE_2019_01_06, [false])])
 AX_ADD_AM_MACRO_STATIC([])
 
+AX_PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
+
 # Check/set gtest specific functions.
 AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
 AC_SUBST(GTEST_CPPFLAGS)
diff --git a/libpldm/Makefile.am b/libpldm/Makefile.am
index e778094..821f390 100644
--- a/libpldm/Makefile.am
+++ b/libpldm/Makefile.am
@@ -1,13 +1,15 @@
 nobase_include_HEADERS = \
 	base.h \
 	pldm_types.h \
-	platform.h
+	platform.h \
+	bios.h
 
 libpldm_LTLIBRARIES = libpldm.la
 libpldmdir = ${libdir}
 libpldm_la_SOURCES = \
 	base.c \
-	platform.c
+	platform.c \
+	bios.c
 libpldm_la_LDFLAGS = -version-info 1:0:0 -shared
 libpldm_la_CFLAGS = $(CODE_COVERAGE_CFLAGS)
 libpldm_la_LIBADD = $(CODE_COVERAGE_LIBS)
diff --git a/libpldm/base.h b/libpldm/base.h
index 3673e9f..77b1045 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -16,6 +16,7 @@
 enum pldm_supported_types {
 	PLDM_BASE = 0x00,
 	PLDM_PLATFORM = 0x02,
+	PLDM_BIOS = 0x03,
 };
 
 /** @brief PLDM Commands
diff --git a/libpldm/bios.c b/libpldm/bios.c
new file mode 100644
index 0000000..c757df3
--- /dev/null
+++ b/libpldm/bios.c
@@ -0,0 +1,88 @@
+#include <endian.h>
+#include <string.h>
+
+#include "bios.h"
+
+int encode_get_date_time_req(uint8_t instance_id, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_BIOS;
+	header.command = PLDM_GET_DATE_TIME;
+	return pack_pldm_header(&header, &(msg->hdr));
+}
+
+int encode_get_date_time_resp(uint8_t instance_id, uint8_t completion_code,
+			      uint8_t seconds, uint8_t minutes, uint8_t hours,
+			      uint8_t day, uint8_t month, uint16_t year,
+			      struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	msg->body.payload[0] = completion_code;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_BIOS;
+	header.command = PLDM_GET_DATE_TIME;
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	uint8_t *dst = msg->body.payload + sizeof(msg->body.payload[0]);
+
+	memcpy(dst, &seconds, sizeof(seconds));
+	dst += sizeof(seconds);
+	memcpy(dst, &minutes, sizeof(minutes));
+	dst += sizeof(minutes);
+	memcpy(dst, &hours, sizeof(hours));
+	dst += sizeof(hours);
+	memcpy(dst, &day, sizeof(day));
+	dst += sizeof(day);
+	memcpy(dst, &month, sizeof(month));
+	dst += sizeof(month);
+	uint16_t local_year = htole16(year);
+	memcpy(dst, &local_year, sizeof(local_year));
+
+	return PLDM_SUCCESS;
+}
+
+int decode_get_date_time_resp(const struct pldm_msg_payload *msg,
+			      uint8_t *completion_code, uint8_t *seconds,
+			      uint8_t *minutes, uint8_t *hours, uint8_t *day,
+			      uint8_t *month, uint16_t *year)
+{
+	if (msg == NULL || seconds == NULL || minutes == NULL ||
+	    hours == NULL || day == NULL || month == NULL || year == NULL ||
+	    completion_code == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	*completion_code = msg->payload[0];
+	if (PLDM_SUCCESS != *completion_code) {
+		return PLDM_SUCCESS;
+	}
+	const uint8_t *start = msg->payload + sizeof(uint8_t);
+	*seconds = *start;
+	*minutes = *(start + sizeof(*seconds));
+	*hours = *(start + sizeof(*seconds) + sizeof(*minutes));
+	*day = *(start + sizeof(*seconds) + sizeof(*minutes) + sizeof(*hours));
+	*month = *(start + sizeof(*seconds) + sizeof(*minutes) +
+		   sizeof(*hours) + sizeof(*day));
+	*year = le16toh(
+	    *((uint16_t *)(start + sizeof(*seconds) + sizeof(*minutes) +
+			   sizeof(*hours) + sizeof(*day) + sizeof(*month))));
+
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/bios.h b/libpldm/bios.h
new file mode 100644
index 0000000..d173aeb
--- /dev/null
+++ b/libpldm/bios.h
@@ -0,0 +1,80 @@
+#ifndef BIOS_H
+#define BIOS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <asm/byteorder.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base.h"
+
+/* Response lengths are inclusive of completion code */
+#define PLDM_GET_DATE_TIME_RESP_BYTES 8
+
+enum pldm_bios_commands { PLDM_GET_DATE_TIME = 0x0c };
+
+/* Requester */
+
+/* GetDateTime */
+
+/** @brief Create a PLDM request message for GetDateTime
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.body.payload'
+ */
+
+int encode_get_date_time_req(uint8_t instance_id, struct pldm_msg *msg);
+
+/** @brief Decode a GetDateTime response message
+ *
+ *  @param[in] msg - Response message payload
+ *  @param[out] completion_code - Pointer to response msg's PLDM completion code
+ *  @param[out] seconds - Seconds in BCD format
+ *  @param[out] minutes - minutes in BCD format
+ *  @param[out] hours - hours in BCD format
+ *  @param[out] day - day of month in BCD format
+ *  @param[out] month - number of month in BCD format
+ *  @param[out] year - year in BCD format
+ *  @return pldm_completion_codes
+ */
+int decode_get_date_time_resp(const struct pldm_msg_payload *msg,
+			      uint8_t *completion_code, uint8_t *seconds,
+			      uint8_t *minutes, uint8_t *hours, uint8_t *day,
+			      uint8_t *month, uint16_t *year);
+
+/* Responder */
+
+/* GetDateTime */
+
+/** @brief Create a PLDM response message for GetDateTime
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] seconds - seconds in BCD format
+ *  @param[in] minutes - minutes in BCD format
+ *  @param[in] hours - hours in BCD format
+ *  @param[in] day - day of the month in BCD format
+ *  @param[in] month - number of month in BCD format
+ *  @param[in] year - year in BCD format
+ *  @param[out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.body.payload'
+ */
+
+int encode_get_date_time_resp(uint8_t instance_id, uint8_t completion_code,
+			      uint8_t seconds, uint8_t minutes, uint8_t hours,
+			      uint8_t day, uint8_t month, uint16_t year,
+			      struct pldm_msg *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BIOS_H */
diff --git a/libpldmresponder/Makefile.am b/libpldmresponder/Makefile.am
index a9b1507..faa4934 100644
--- a/libpldmresponder/Makefile.am
+++ b/libpldmresponder/Makefile.am
@@ -1,12 +1,18 @@
 libpldmresponder_LTLIBRARIES = libpldmresponder.la
 libpldmresponderdir = ${libdir}
 libpldmresponder_la_SOURCES = \
-	base.cpp
+	base.cpp \
+	utils.cpp \
+	bios.cpp
 libpldmresponder_la_LIBADD = \
 	../libpldm/libpldm.la \
 	$(CODE_COVERAGE_LIBS)
 libpldmresponder_la_LDFLAGS = \
+	$(SDBUSPLUS_LIBS)
 	-version-info 1:0:0 -shared
-libpldmresponder_la_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
+libpldmresponder_la_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) \
+	$(SDBUSPLUS_CFLAGS)
+ 
 libpldmresponder_la_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS)
 
+
diff --git a/libpldmresponder/bios.cpp b/libpldmresponder/bios.cpp
new file mode 100644
index 0000000..dfd07b8
--- /dev/null
+++ b/libpldmresponder/bios.cpp
@@ -0,0 +1,99 @@
+#include "bios.hpp"
+
+#include "libpldmresponder/utils.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <array>
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <stdexcept>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace pldm
+{
+
+using namespace phosphor::logging;
+
+using EpochTimeUS = uint64_t;
+
+constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
+
+namespace responder
+{
+
+namespace utils
+{
+
+void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
+                    uint8_t& hours, uint8_t& day, uint8_t& month,
+                    uint16_t& year)
+{
+    auto t = time_t(timeSec);
+    auto time = localtime(&t);
+
+    seconds = decimalToBcd(time->tm_sec);
+    minutes = decimalToBcd(time->tm_min);
+    hours = decimalToBcd(time->tm_hour);
+    day = decimalToBcd(time->tm_mday);
+    month =
+        decimalToBcd(time->tm_mon + 1); // The number of months in the range
+                                        // 0 to 11.PLDM expects range 1 to 12
+    year = decimalToBcd(time->tm_year + 1900); // The number of years since 1900
+}
+
+} // namespace utils
+
+void getDateTime(const pldm_msg_payload* request, pldm_msg* response)
+{
+    uint8_t seconds = 0;
+    uint8_t minutes = 0;
+    uint8_t hours = 0;
+    uint8_t day = 0;
+    uint8_t month = 0;
+    uint16_t year = 0;
+
+    constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
+    constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc";
+    std::variant<EpochTimeUS> value;
+
+    auto bus = sdbusplus::bus::new_default();
+    try
+    {
+        auto service = getService(bus, bmcTimePath, timeInterface);
+
+        auto method = bus.new_method_call(service.c_str(), bmcTimePath,
+                                          dbusProperties, "Get");
+        method.append(timeInterface, "Elapsed");
+
+        auto reply = bus.call(method);
+        reply.read(value);
+    }
+
+    catch (std::exception& e)
+    {
+        log<level::ERR>("Error getting time", entry("PATH=%s", bmcTimePath),
+                        entry("TIME INTERACE=%s", timeInterface));
+
+        encode_get_date_time_resp(0, PLDM_ERROR, seconds, minutes, hours, day,
+                                  month, year, response);
+        return;
+    }
+
+    uint64_t timeUsec = std::get<EpochTimeUS>(value);
+
+    uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
+                           std::chrono::microseconds(timeUsec))
+                           .count();
+
+    utils::epochToBCDTime(timeSec, seconds, minutes, hours, day, month, year);
+
+    encode_get_date_time_resp(0, PLDM_SUCCESS, seconds, minutes, hours, day,
+                              month, year, response);
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios.hpp b/libpldmresponder/bios.hpp
new file mode 100644
index 0000000..80a732b
--- /dev/null
+++ b/libpldmresponder/bios.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "libpldm/bios.h"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+/** @brief Handler for GetDateTime
+ *
+ *  @param[in] request - Request message payload
+ *  @param[out] response - Response message written here
+ */
+void getDateTime(const pldm_msg_payload* request, pldm_msg* response);
+
+namespace utils
+{
+
+/** @brief Convert epoch time to BCD time
+ *
+ *  @param[in] timeSec - Time got from epoch time in seconds
+ *  @param[out] seconds - number of seconds in BCD
+ *  @param[out] minutes - number of minutes in BCD
+ *  @param[out] hours - number of hours in BCD
+ *  @param[out] day - day of the month in BCD
+ *  @param[out] month - month number in BCD
+ *  @param[out] year - year number in BCD
+ */
+void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
+                    uint8_t& hours, uint8_t& day, uint8_t& month,
+                    uint16_t& year);
+} // namespace utils
+
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/utils.cpp b/libpldmresponder/utils.cpp
new file mode 100644
index 0000000..fe23223
--- /dev/null
+++ b/libpldmresponder/utils.cpp
@@ -0,0 +1,51 @@
+#include "utils.hpp"
+
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <array>
+#include <ctime>
+#include <iostream>
+#include <map>
+#include <phosphor-logging/elog-errors.hpp>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace pldm
+{
+using namespace phosphor::logging;
+
+constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
+constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";
+
+namespace responder
+{
+
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                       const std::string& interface)
+{
+    using DbusInterfaceList = std::vector<std::string>;
+    std::map<std::string, std::vector<std::string>> mapperResponse;
+
+    try
+    {
+        auto mapper = bus.new_method_call(mapperBusName, mapperPath,
+                                          mapperInterface, "GetObject");
+        mapper.append(path, DbusInterfaceList({interface}));
+
+        auto mapperResponseMsg = bus.call(mapper);
+        mapperResponseMsg.read(mapperResponse);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("Error in mapper call", entry("ERROR=%s", e.what()),
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", interface.c_str()));
+        throw;
+    }
+    return mapperResponse.begin()->first;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/utils.hpp b/libpldmresponder/utils.hpp
new file mode 100644
index 0000000..d76b716
--- /dev/null
+++ b/libpldmresponder/utils.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <stdint.h>
+#include <systemd/sd-bus.h>
+
+#include <sdbusplus/server.hpp>
+#include <string>
+
+namespace pldm
+{
+namespace responder
+{
+
+/**
+ *  @brief Get the DBUS Service name for the input dbus path
+ *  @param[in] bus - DBUS Bus Object
+ *  @param[in] path - DBUS object path
+ *  @param[in] interface - DBUS Interface
+ *  @return std::string - the dbus service name
+ */
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                       const std::string& interface);
+
+/** @brief Convert any Decimal number to BCD
+ *
+ *  @tparam[in] decimal - Decimal number
+ *  @return Corresponding BCD number
+ */
+template <typename T>
+T decimalToBcd(T decimal)
+{
+    T bcd = 0;
+    T rem = 0;
+    auto cnt = 0;
+
+    while (decimal)
+    {
+        rem = decimal % 10;
+        bcd = bcd + (rem << cnt);
+        decimal = decimal / 10;
+        cnt += 4;
+    }
+
+    return bcd;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/test/Makefile.am b/test/Makefile.am
index 7211573..ebbf363 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -5,7 +5,9 @@
 check_PROGRAMS = \
 	libpldm_base_test \
 	libpldm_platform_test \
-	libpldmresponder_base_test
+	libpldmresponder_base_test \
+	libpldm_bios_test \
+	libpldmresponder_bios_test
 
 test_cppflags = \
 	-Igtest \
@@ -37,6 +39,27 @@
 	$(CODE_COVERAGE_LIBS)
 libpldm_platform_test_SOURCES = libpldm_platform_test.cpp
 
+libpldm_bios_test_CPPFLAGS = $(test_cppflags)
+libpldm_bios_test_CXXFLAGS = $(test_cxxflags)
+libpldm_bios_test_LDFLAGS = $(test_ldflags)
+libpldm_bios_test_LDADD = \
+	$(top_builddir)/libpldm/libpldm_la-base.o  \
+	$(top_builddir)/libpldm/libpldm_la-bios.o \
+	$(CODE_COVERAGE_LIBS)
+libpldm_bios_test_SOURCES = libpldm_bios_test.cpp
+
+libpldmresponder_bios_test_CPPFLAGS = $(test_cppflags)
+libpldmresponder_bios_test_CXXFLAGS = $(test_cxxflags)
+libpldmresponder_bios_test_LDFLAGS = $(test_ldflags) $(SDBUSPLUS_LIBS)
+libpldmresponder_bios_test_LDADD = \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-bios.o \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-utils.o \
+	$(top_builddir)/libpldm/libpldm_la-base.o  \
+	$(top_builddir)/libpldm/libpldm_la-bios.o \
+	$(CODE_COVERAGE_LIBS) $(SDBUSPLUS_LIBS)
+libpldmresponder_bios_test_SOURCES = \
+	libpldmresponder_bios_test.cpp
+
 
 libpldmresponder_base_test_CPPFLAGS = $(test_cppflags)
 libpldmresponder_base_test_CXXFLAGS = $(test_cxxflags)
@@ -46,3 +69,4 @@
 	$(top_builddir)/libpldmresponder/libpldmresponder_la-base.o $(CODE_COVERAGE_LIBS)
 
 libpldmresponder_base_test_SOURCES = libpldmresponder_base_test.cpp
+
diff --git a/test/libpldm_bios_test.cpp b/test/libpldm_bios_test.cpp
new file mode 100644
index 0000000..b1771c9
--- /dev/null
+++ b/test/libpldm_bios_test.cpp
@@ -0,0 +1,118 @@
+#include <string.h>
+
+#include <array>
+
+#include "libpldm/base.h"
+#include "libpldm/bios.h"
+
+#include <gtest/gtest.h>
+
+TEST(GetDateTime, testEncodeRequest)
+{
+    pldm_msg request{};
+    request.body.payload = nullptr;
+    request.body.payload_length = 0;
+
+    auto rc = encode_get_date_time_req(0, &request);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+}
+
+TEST(GetDateTime, testEncodeResponse)
+{
+    uint8_t completionCode = 0;
+    uint8_t seconds = 50;
+    uint8_t minutes = 20;
+    uint8_t hours = 5;
+    uint8_t day = 23;
+    uint8_t month = 11;
+    uint16_t year = 2019;
+
+    std::array<uint8_t, PLDM_GET_DATE_TIME_RESP_BYTES> responseMsg{};
+    pldm_msg response{};
+
+    response.body.payload = responseMsg.data();
+    response.body.payload_length = responseMsg.size();
+
+    auto rc = encode_get_date_time_resp(0, PLDM_SUCCESS, seconds, minutes,
+                                        hours, day, month, year, &response);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(completionCode, response.body.payload[0]);
+
+    ASSERT_EQ(0,
+              memcmp(response.body.payload + sizeof(response.body.payload[0]),
+                     &seconds, sizeof(seconds)));
+    ASSERT_EQ(0, memcmp(response.body.payload +
+                            sizeof(response.body.payload[0]) + sizeof(seconds),
+                        &minutes, sizeof(minutes)));
+    ASSERT_EQ(0,
+              memcmp(response.body.payload + sizeof(response.body.payload[0]) +
+                         sizeof(seconds) + sizeof(minutes),
+                     &hours, sizeof(hours)));
+    ASSERT_EQ(0,
+              memcmp(response.body.payload + sizeof(response.body.payload[0]) +
+                         sizeof(seconds) + sizeof(minutes) + sizeof(hours),
+                     &day, sizeof(day)));
+    ASSERT_EQ(0, memcmp(response.body.payload +
+                            sizeof(response.body.payload[0]) + sizeof(seconds) +
+                            sizeof(minutes) + sizeof(hours) + sizeof(day),
+                        &month, sizeof(month)));
+    ASSERT_EQ(0,
+              memcmp(response.body.payload + sizeof(response.body.payload[0]) +
+                         sizeof(seconds) + sizeof(minutes) + sizeof(hours) +
+                         sizeof(day) + sizeof(month),
+                     &year, sizeof(year)));
+}
+
+TEST(GetDateTime, testDecodeResponse)
+{
+    std::array<uint8_t, PLDM_GET_DATE_TIME_RESP_BYTES> responseMsg{};
+    pldm_msg_payload response{};
+    response.payload = responseMsg.data();
+    response.payload_length = responseMsg.size();
+
+    uint8_t completionCode = 0;
+
+    uint8_t seconds = 55;
+    uint8_t minutes = 2;
+    uint8_t hours = 8;
+    uint8_t day = 9;
+    uint8_t month = 7;
+    uint16_t year = 2020;
+
+    uint8_t retSeconds = 0;
+    uint8_t retMinutes = 0;
+    uint8_t retHours = 0;
+    uint8_t retDay = 0;
+    uint8_t retMonth = 0;
+    uint16_t retYear = 0;
+
+    memcpy(response.payload + sizeof(completionCode), &seconds,
+           sizeof(seconds));
+    memcpy(response.payload + sizeof(completionCode) + sizeof(seconds),
+           &minutes, sizeof(minutes));
+    memcpy(response.payload + sizeof(completionCode) + sizeof(seconds) +
+               sizeof(minutes),
+           &hours, sizeof(hours));
+    memcpy(response.payload + sizeof(completionCode) + sizeof(seconds) +
+               sizeof(minutes) + sizeof(hours),
+           &day, sizeof(day));
+    memcpy(response.payload + sizeof(completionCode) + sizeof(seconds) +
+               sizeof(minutes) + sizeof(hours) + sizeof(day),
+           &month, sizeof(month));
+    memcpy(response.payload + sizeof(completionCode) + sizeof(seconds) +
+               sizeof(minutes) + sizeof(hours) + sizeof(day) + sizeof(month),
+           &year, sizeof(year));
+
+    auto rc = decode_get_date_time_resp(&response, &completionCode, &retSeconds,
+                                        &retMinutes, &retHours, &retDay,
+                                        &retMonth, &retYear);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(seconds, retSeconds);
+    ASSERT_EQ(minutes, retMinutes);
+    ASSERT_EQ(hours, retHours);
+    ASSERT_EQ(day, retDay);
+    ASSERT_EQ(month, retMonth);
+    ASSERT_EQ(year, retYear);
+}
diff --git a/test/libpldmresponder_bios_test.cpp b/test/libpldmresponder_bios_test.cpp
new file mode 100644
index 0000000..bcd3e5c
--- /dev/null
+++ b/test/libpldmresponder_bios_test.cpp
@@ -0,0 +1,46 @@
+
+#include "libpldmresponder/bios.hpp"
+
+#include <string.h>
+
+#include <array>
+#include <ctime>
+
+#include "libpldm/base.h"
+#include "libpldm/bios.h"
+
+#include <gtest/gtest.h>
+
+using namespace pldm::responder;
+using namespace pldm::responder::utils;
+
+TEST(epochToBCDTime, testTime)
+{
+    struct tm time
+    {
+    };
+    time.tm_year = 119;
+    time.tm_mon = 3;
+    time.tm_mday = 13;
+    time.tm_hour = 5;
+    time.tm_min = 18;
+    time.tm_sec = 13;
+    time.tm_isdst = -1;
+
+    time_t epochTime = mktime(&time);
+    uint8_t seconds = 0;
+    uint8_t minutes = 0;
+    uint8_t hours = 0;
+    uint8_t day = 0;
+    uint8_t month = 0;
+    uint16_t year = 0;
+
+    epochToBCDTime(epochTime, seconds, minutes, hours, day, month, year);
+
+    ASSERT_EQ(0x13, seconds);
+    ASSERT_EQ(0x18, minutes);
+    ASSERT_EQ(0x5, hours);
+    ASSERT_EQ(0x13, day);
+    ASSERT_EQ(0x4, month);
+    ASSERT_EQ(0x2019, year);
+}