xref: /openbmc/phosphor-time-manager/bmc_epoch.cpp (revision 947b5346bb6b0904cd5c00847d5af6c112bf1050)
196232827SLei YU #include "bmc_epoch.hpp"
296232827SLei YU 
3ab4cc6a5SGunnar Mills #include "utils.hpp"
47b218796SLei YU 
57b218796SLei YU #include <sys/timerfd.h>
67b218796SLei YU #include <unistd.h>
77b218796SLei YU 
8ab4cc6a5SGunnar Mills #include <phosphor-logging/elog-errors.hpp>
9ab4cc6a5SGunnar Mills #include <phosphor-logging/elog.hpp>
10*947b5346SGeorge Liu #include <phosphor-logging/lg2.hpp>
11ab4cc6a5SGunnar Mills #include <xyz/openbmc_project/Common/error.hpp>
127b218796SLei YU 
1375710b6aSGunnar Mills // Need to do this since its not exported outside of the kernel.
147b218796SLei YU // Refer : https://gist.github.com/lethean/446cea944b7441228298
157b218796SLei YU #ifndef TFD_TIMER_CANCEL_ON_SET
167b218796SLei YU #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
177b218796SLei YU #endif
187b218796SLei YU 
197b218796SLei YU // Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
207b218796SLei YU #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
2196232827SLei YU 
2296232827SLei YU namespace phosphor
2396232827SLei YU {
2496232827SLei YU namespace time
2596232827SLei YU {
2696232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server;
2796232827SLei YU using namespace phosphor::logging;
28f6fad820SLei YU 
29ab4cc6a5SGunnar Mills BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus, const char* objPath) :
30ab4cc6a5SGunnar Mills     EpochBase(bus, objPath), bus(bus)
3196232827SLei YU {
327b218796SLei YU     initialize();
337b218796SLei YU }
347b218796SLei YU 
357b218796SLei YU void BmcEpoch::initialize()
367b218796SLei YU {
37ab4cc6a5SGunnar Mills     using InternalFailure =
38ab4cc6a5SGunnar Mills         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
397b218796SLei YU 
407b218796SLei YU     // Subscribe time change event
417b218796SLei YU     // Choose the MAX time that is possible to avoid mis fires.
427b218796SLei YU     constexpr itimerspec maxTime = {
437b218796SLei YU         {0, 0},          // it_interval
447b218796SLei YU         {TIME_T_MAX, 0}, // it_value
457b218796SLei YU     };
467b218796SLei YU 
477b218796SLei YU     timeFd = timerfd_create(CLOCK_REALTIME, 0);
487b218796SLei YU     if (timeFd == -1)
497b218796SLei YU     {
50*947b5346SGeorge Liu         lg2::error("Failed to create timerfd: {ERRNO}", "ERRNO", errno);
517b218796SLei YU         elog<InternalFailure>();
527b218796SLei YU     }
537b218796SLei YU 
54ab4cc6a5SGunnar Mills     auto r = timerfd_settime(
55ab4cc6a5SGunnar Mills         timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
567b218796SLei YU     if (r != 0)
577b218796SLei YU     {
58*947b5346SGeorge Liu         lg2::error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
597b218796SLei YU         elog<InternalFailure>();
607b218796SLei YU     }
617b218796SLei YU 
627b218796SLei YU     sd_event_source* es;
63ab4cc6a5SGunnar Mills     r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
64ab4cc6a5SGunnar Mills                         this);
657b218796SLei YU     if (r < 0)
667b218796SLei YU     {
67*947b5346SGeorge Liu         lg2::error("Failed to add event: {ERRNO}", "ERRNO", errno);
687b218796SLei YU         elog<InternalFailure>();
697b218796SLei YU     }
707b218796SLei YU     timeChangeEventSource.reset(es);
717b218796SLei YU }
727b218796SLei YU 
737b218796SLei YU BmcEpoch::~BmcEpoch()
747b218796SLei YU {
757b218796SLei YU     close(timeFd);
7696232827SLei YU }
7796232827SLei YU 
7896232827SLei YU uint64_t BmcEpoch::elapsed() const
7996232827SLei YU {
8096232827SLei YU     return getTime().count();
8196232827SLei YU }
8296232827SLei YU 
8396232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value)
8496232827SLei YU {
857b218796SLei YU     /*
863c2f4496SGeorge Liu         Mode  | Set BMC Time
873c2f4496SGeorge Liu         ----- | -------------
883c2f4496SGeorge Liu         NTP   | Fail to set
893c2f4496SGeorge Liu         MANUAL| OK
907b218796SLei YU     */
917b218796SLei YU     auto time = microseconds(value);
923c2f4496SGeorge Liu     setTime(time);
937b218796SLei YU 
9496232827SLei YU     server::EpochTime::elapsed(value);
9596232827SLei YU     return value;
9696232827SLei YU }
9796232827SLei YU 
981e1dc447SRatan Gupta int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
991e1dc447SRatan Gupta                            uint32_t /* revents */, void* /* userdata */)
1007b218796SLei YU {
1017b218796SLei YU     std::array<char, 64> time{};
1027b218796SLei YU 
1037b218796SLei YU     // We are not interested in the data here.
1047b218796SLei YU     // So read until there is no new data here in the FD
105ab4cc6a5SGunnar Mills     while (read(fd, time.data(), time.max_size()) > 0)
106ab4cc6a5SGunnar Mills         ;
1077b218796SLei YU 
1087b218796SLei YU     return 0;
1097b218796SLei YU }
1107b218796SLei YU 
111af5abc57SLei YU } // namespace time
112af5abc57SLei YU } // namespace phosphor
113