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 
14*714a20b5SAndrew Geissler #include <chrono>
15*714a20b5SAndrew Geissler 
1675710b6aSGunnar Mills // Need to do this since its not exported outside of the kernel.
177b218796SLei YU // Refer : https://gist.github.com/lethean/446cea944b7441228298
187b218796SLei YU #ifndef TFD_TIMER_CANCEL_ON_SET
197b218796SLei YU #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
207b218796SLei YU #endif
217b218796SLei YU 
2296232827SLei YU namespace phosphor
2396232827SLei YU {
2496232827SLei YU namespace time
2596232827SLei YU {
26f1d54aecSGeorge Liu namespace // anonymous
27f1d54aecSGeorge Liu {
28864e173eSPavithra Barithaya constexpr auto systemdTimeService = "org.freedesktop.timedate1";
29864e173eSPavithra Barithaya constexpr auto systemdTimePath = "/org/freedesktop/timedate1";
30864e173eSPavithra Barithaya constexpr auto systemdTimeInterface = "org.freedesktop.timedate1";
31864e173eSPavithra Barithaya constexpr auto methodSetTime = "SetTime";
32f1d54aecSGeorge Liu } // namespace
33f1d54aecSGeorge Liu 
34dd42c7faSPavithra Barithaya PHOSPHOR_LOG2_USING;
35dd42c7faSPavithra Barithaya 
3696232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server;
3796232827SLei YU using namespace phosphor::logging;
38f1d54aecSGeorge Liu using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed;
39*714a20b5SAndrew Geissler using namespace std::chrono;
407b218796SLei YU 
initialize()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
50*714a20b5SAndrew Geissler         {system_clock::duration::max().count(), 0}, // it_value
517b218796SLei YU     };
527b218796SLei YU 
537b218796SLei YU     timeFd = timerfd_create(CLOCK_REALTIME, 0);
547b218796SLei YU     if (timeFd == -1)
557b218796SLei YU     {
56dd42c7faSPavithra 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     {
64dd42c7faSPavithra Barithaya         error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
657b218796SLei YU         elog<InternalFailure>();
667b218796SLei YU     }
677b218796SLei YU 
688af2a1e3SPavithra Barithaya     sd_event_source* es = nullptr;
69ab4cc6a5SGunnar Mills     r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
70ab4cc6a5SGunnar Mills                         this);
717b218796SLei YU     if (r < 0)
727b218796SLei YU     {
73dd42c7faSPavithra Barithaya         error("Failed to add event: {ERRNO}", "ERRNO", errno);
747b218796SLei YU         elog<InternalFailure>();
757b218796SLei YU     }
767b218796SLei YU     timeChangeEventSource.reset(es);
777b218796SLei YU }
787b218796SLei YU 
~BmcEpoch()797b218796SLei YU BmcEpoch::~BmcEpoch()
807b218796SLei YU {
817b218796SLei YU     close(timeFd);
8296232827SLei YU }
8396232827SLei YU 
elapsed() const8496232827SLei YU uint64_t BmcEpoch::elapsed() const
8596232827SLei YU {
8696232827SLei YU     return getTime().count();
8796232827SLei YU }
8896232827SLei YU 
elapsed(uint64_t value)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 
onTimeChange(sd_event_source *,int fd,uint32_t,void *)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)
112864e173eSPavithra Barithaya     {
113ab4cc6a5SGunnar Mills         ;
114864e173eSPavithra Barithaya     }
1157b218796SLei YU 
1167b218796SLei YU     return 0;
1177b218796SLei YU }
1187b218796SLei YU 
onModeChanged(Mode mode)119f1d54aecSGeorge Liu void BmcEpoch::onModeChanged(Mode mode)
120f1d54aecSGeorge Liu {
121f1d54aecSGeorge Liu     manager.setTimeMode(mode);
122f1d54aecSGeorge Liu }
123f1d54aecSGeorge Liu 
setTime(const microseconds & usec)124f1d54aecSGeorge Liu bool BmcEpoch::setTime(const microseconds& usec)
125f1d54aecSGeorge Liu {
126864e173eSPavithra Barithaya     auto method = bus.new_method_call(systemdTimeService, systemdTimePath,
127864e173eSPavithra Barithaya                                       systemdTimeInterface, methodSetTime);
128f1d54aecSGeorge Liu     method.append(static_cast<int64_t>(usec.count()),
129f1d54aecSGeorge Liu                   false,  // relative
130f1d54aecSGeorge Liu                   false); // user_interaction
131f1d54aecSGeorge Liu 
132f1d54aecSGeorge Liu     try
133f1d54aecSGeorge Liu     {
134f1d54aecSGeorge Liu         bus.call_noreply(method);
135f1d54aecSGeorge Liu     }
136f1d54aecSGeorge Liu     catch (const sdbusplus::exception_t& ex)
137f1d54aecSGeorge Liu     {
138dd42c7faSPavithra Barithaya         error("Error in setting system time: {ERROR}", "ERROR", ex);
139f1d54aecSGeorge Liu         using namespace xyz::openbmc_project::Time;
140f1d54aecSGeorge Liu         elog<FailedError>(Failed::REASON(ex.what()));
141f1d54aecSGeorge Liu     }
142f1d54aecSGeorge Liu     return true;
143f1d54aecSGeorge Liu }
144f1d54aecSGeorge Liu 
getTime()145864e173eSPavithra Barithaya microseconds BmcEpoch::getTime()
146f1d54aecSGeorge Liu {
147f1d54aecSGeorge Liu     auto now = system_clock::now();
148f1d54aecSGeorge Liu     return duration_cast<microseconds>(now.time_since_epoch());
149f1d54aecSGeorge Liu }
150f1d54aecSGeorge Liu 
151af5abc57SLei YU } // namespace time
152af5abc57SLei YU } // namespace phosphor
153