xref: /openbmc/phosphor-time-manager/bmc_epoch.cpp (revision dd42c7fa1d34a0e9fc89b30411e84698667280cd)
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>
10947b5346SGeorge Liu #include <phosphor-logging/lg2.hpp>
11ab4cc6a5SGunnar Mills #include <xyz/openbmc_project/Common/error.hpp>
12f1d54aecSGeorge Liu #include <xyz/openbmc_project/Time/error.hpp>
137b218796SLei YU 
1475710b6aSGunnar Mills // Need to do this since its not exported outside of the kernel.
157b218796SLei YU // Refer : https://gist.github.com/lethean/446cea944b7441228298
167b218796SLei YU #ifndef TFD_TIMER_CANCEL_ON_SET
177b218796SLei YU #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
187b218796SLei YU #endif
197b218796SLei YU 
207b218796SLei YU // Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
217b218796SLei YU #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
2296232827SLei YU 
2396232827SLei YU namespace phosphor
2496232827SLei YU {
2596232827SLei YU namespace time
2696232827SLei YU {
27f1d54aecSGeorge Liu namespace // anonymous
28f1d54aecSGeorge Liu {
29f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1";
30f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1";
31f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1";
32f1d54aecSGeorge Liu constexpr auto METHOD_SET_TIME = "SetTime";
33f1d54aecSGeorge Liu } // namespace
34f1d54aecSGeorge Liu 
35*dd42c7faSPavithra Barithaya PHOSPHOR_LOG2_USING;
36*dd42c7faSPavithra Barithaya 
3796232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server;
3896232827SLei YU using namespace phosphor::logging;
39f1d54aecSGeorge Liu using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed;
407b218796SLei YU 
417b218796SLei YU void BmcEpoch::initialize()
427b218796SLei YU {
43ab4cc6a5SGunnar Mills     using InternalFailure =
44ab4cc6a5SGunnar Mills         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
457b218796SLei YU 
467b218796SLei YU     // Subscribe time change event
477b218796SLei YU     // Choose the MAX time that is possible to avoid mis fires.
487b218796SLei YU     constexpr itimerspec maxTime = {
497b218796SLei YU         {0, 0},          // it_interval
507b218796SLei YU         {TIME_T_MAX, 0}, // it_value
517b218796SLei YU     };
527b218796SLei YU 
537b218796SLei YU     timeFd = timerfd_create(CLOCK_REALTIME, 0);
547b218796SLei YU     if (timeFd == -1)
557b218796SLei YU     {
56*dd42c7faSPavithra Barithaya         error("Failed to create timerfd: {ERRNO}", "ERRNO", errno);
577b218796SLei YU         elog<InternalFailure>();
587b218796SLei YU     }
597b218796SLei YU 
60ab4cc6a5SGunnar Mills     auto r = timerfd_settime(
61ab4cc6a5SGunnar Mills         timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
627b218796SLei YU     if (r != 0)
637b218796SLei YU     {
64*dd42c7faSPavithra Barithaya         error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
657b218796SLei YU         elog<InternalFailure>();
667b218796SLei YU     }
677b218796SLei YU 
687b218796SLei YU     sd_event_source* es;
69ab4cc6a5SGunnar Mills     r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
70ab4cc6a5SGunnar Mills                         this);
717b218796SLei YU     if (r < 0)
727b218796SLei YU     {
73*dd42c7faSPavithra Barithaya         error("Failed to add event: {ERRNO}", "ERRNO", errno);
747b218796SLei YU         elog<InternalFailure>();
757b218796SLei YU     }
767b218796SLei YU     timeChangeEventSource.reset(es);
777b218796SLei YU }
787b218796SLei YU 
797b218796SLei YU BmcEpoch::~BmcEpoch()
807b218796SLei YU {
817b218796SLei YU     close(timeFd);
8296232827SLei YU }
8396232827SLei YU 
8496232827SLei YU uint64_t BmcEpoch::elapsed() const
8596232827SLei YU {
8696232827SLei YU     return getTime().count();
8796232827SLei YU }
8896232827SLei YU 
8996232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value)
9096232827SLei YU {
917b218796SLei YU     /*
923c2f4496SGeorge Liu         Mode  | Set BMC Time
933c2f4496SGeorge Liu         ----- | -------------
943c2f4496SGeorge Liu         NTP   | Fail to set
953c2f4496SGeorge Liu         MANUAL| OK
967b218796SLei YU     */
977b218796SLei YU     auto time = microseconds(value);
983c2f4496SGeorge Liu     setTime(time);
997b218796SLei YU 
10096232827SLei YU     server::EpochTime::elapsed(value);
10196232827SLei YU     return value;
10296232827SLei YU }
10396232827SLei YU 
1041e1dc447SRatan Gupta int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
1051e1dc447SRatan Gupta                            uint32_t /* revents */, void* /* userdata */)
1067b218796SLei YU {
1077b218796SLei YU     std::array<char, 64> time{};
1087b218796SLei YU 
1097b218796SLei YU     // We are not interested in the data here.
1107b218796SLei YU     // So read until there is no new data here in the FD
111ab4cc6a5SGunnar Mills     while (read(fd, time.data(), time.max_size()) > 0)
112ab4cc6a5SGunnar Mills         ;
1137b218796SLei YU 
1147b218796SLei YU     return 0;
1157b218796SLei YU }
1167b218796SLei YU 
117f1d54aecSGeorge Liu void BmcEpoch::onModeChanged(Mode mode)
118f1d54aecSGeorge Liu {
119f1d54aecSGeorge Liu     manager.setTimeMode(mode);
120f1d54aecSGeorge Liu }
121f1d54aecSGeorge Liu 
122f1d54aecSGeorge Liu bool BmcEpoch::setTime(const microseconds& usec)
123f1d54aecSGeorge Liu {
124f1d54aecSGeorge Liu     auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH,
125f1d54aecSGeorge Liu                                       SYSTEMD_TIME_INTERFACE, METHOD_SET_TIME);
126f1d54aecSGeorge Liu     method.append(static_cast<int64_t>(usec.count()),
127f1d54aecSGeorge Liu                   false,  // relative
128f1d54aecSGeorge Liu                   false); // user_interaction
129f1d54aecSGeorge Liu 
130f1d54aecSGeorge Liu     try
131f1d54aecSGeorge Liu     {
132f1d54aecSGeorge Liu         bus.call_noreply(method);
133f1d54aecSGeorge Liu     }
134f1d54aecSGeorge Liu     catch (const sdbusplus::exception_t& ex)
135f1d54aecSGeorge Liu     {
136*dd42c7faSPavithra Barithaya         error("Error in setting system time: {ERROR}", "ERROR", ex);
137f1d54aecSGeorge Liu         using namespace xyz::openbmc_project::Time;
138f1d54aecSGeorge Liu         elog<FailedError>(Failed::REASON(ex.what()));
139f1d54aecSGeorge Liu     }
140f1d54aecSGeorge Liu     return true;
141f1d54aecSGeorge Liu }
142f1d54aecSGeorge Liu 
143f1d54aecSGeorge Liu microseconds BmcEpoch::getTime() const
144f1d54aecSGeorge Liu {
145f1d54aecSGeorge Liu     auto now = system_clock::now();
146f1d54aecSGeorge Liu     return duration_cast<microseconds>(now.time_since_epoch());
147f1d54aecSGeorge Liu }
148f1d54aecSGeorge Liu 
149af5abc57SLei YU } // namespace time
150af5abc57SLei YU } // namespace phosphor
151