xref: /openbmc/phosphor-time-manager/bmc_epoch.cpp (revision 1e1dc447321adf858d8f4c685764a2b01e8754bb)
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>
10ab4cc6a5SGunnar Mills #include <phosphor-logging/log.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     {
50ab4cc6a5SGunnar Mills         log<level::ERR>("Failed to create timerfd", entry("ERRNO=%d", errno),
517b218796SLei YU                         entry("ERR=%s", strerror(errno)));
527b218796SLei YU         elog<InternalFailure>();
537b218796SLei YU     }
547b218796SLei YU 
55ab4cc6a5SGunnar Mills     auto r = timerfd_settime(
56ab4cc6a5SGunnar Mills         timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
577b218796SLei YU     if (r != 0)
587b218796SLei YU     {
59ab4cc6a5SGunnar Mills         log<level::ERR>("Failed to set timerfd", entry("ERRNO=%d", errno),
607b218796SLei YU                         entry("ERR=%s", strerror(errno)));
617b218796SLei YU         elog<InternalFailure>();
627b218796SLei YU     }
637b218796SLei YU 
647b218796SLei YU     sd_event_source* es;
65ab4cc6a5SGunnar Mills     r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
66ab4cc6a5SGunnar Mills                         this);
677b218796SLei YU     if (r < 0)
687b218796SLei YU     {
69ab4cc6a5SGunnar Mills         log<level::ERR>("Failed to add event", entry("ERRNO=%d", -r),
707b218796SLei YU                         entry("ERR=%s", strerror(-r)));
717b218796SLei YU         elog<InternalFailure>();
727b218796SLei YU     }
737b218796SLei YU     timeChangeEventSource.reset(es);
747b218796SLei YU }
757b218796SLei YU 
767b218796SLei YU BmcEpoch::~BmcEpoch()
777b218796SLei YU {
787b218796SLei YU     close(timeFd);
7996232827SLei YU }
8096232827SLei YU 
8196232827SLei YU uint64_t BmcEpoch::elapsed() const
8296232827SLei YU {
8396232827SLei YU     return getTime().count();
8496232827SLei YU }
8596232827SLei YU 
8696232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value)
8796232827SLei YU {
887b218796SLei YU     /*
893c2f4496SGeorge Liu         Mode  | Set BMC Time
903c2f4496SGeorge Liu         ----- | -------------
913c2f4496SGeorge Liu         NTP   | Fail to set
923c2f4496SGeorge Liu         MANUAL| OK
937b218796SLei YU     */
947b218796SLei YU     auto time = microseconds(value);
953c2f4496SGeorge Liu     setTime(time);
967b218796SLei YU 
9796232827SLei YU     server::EpochTime::elapsed(value);
9896232827SLei YU     return value;
9996232827SLei YU }
10096232827SLei YU 
101*1e1dc447SRatan Gupta int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
102*1e1dc447SRatan Gupta                            uint32_t /* revents */, void* /* userdata */)
1037b218796SLei YU {
1047b218796SLei YU     std::array<char, 64> time{};
1057b218796SLei YU 
1067b218796SLei YU     // We are not interested in the data here.
1077b218796SLei YU     // So read until there is no new data here in the FD
108ab4cc6a5SGunnar Mills     while (read(fd, time.data(), time.max_size()) > 0)
109ab4cc6a5SGunnar Mills         ;
1107b218796SLei YU 
1117b218796SLei YU     return 0;
1127b218796SLei YU }
1137b218796SLei YU 
114af5abc57SLei YU } // namespace time
115af5abc57SLei YU } // namespace phosphor
116