xref: /openbmc/phosphor-time-manager/bmc_epoch.cpp (revision 7b21879622a974d4ca92da760be7c7008732b6c3)
196232827SLei YU #include "bmc_epoch.hpp"
296232827SLei YU 
3*7b218796SLei YU #include <phosphor-logging/elog.hpp>
4*7b218796SLei YU #include <phosphor-logging/elog-errors.hpp>
596232827SLei YU #include <phosphor-logging/log.hpp>
6*7b218796SLei YU #include <xyz/openbmc_project/Common/error.hpp>
7*7b218796SLei YU 
8*7b218796SLei YU #include <sys/timerfd.h>
9*7b218796SLei YU #include <unistd.h>
10*7b218796SLei YU 
11*7b218796SLei YU 
12*7b218796SLei YU // Neeed to do this since its not exported outside of the kernel.
13*7b218796SLei YU // Refer : https://gist.github.com/lethean/446cea944b7441228298
14*7b218796SLei YU #ifndef TFD_TIMER_CANCEL_ON_SET
15*7b218796SLei YU #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
16*7b218796SLei YU #endif
17*7b218796SLei YU 
18*7b218796SLei YU // Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
19*7b218796SLei YU #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
2096232827SLei YU 
2196232827SLei YU namespace phosphor
2296232827SLei YU {
2396232827SLei YU namespace time
2496232827SLei YU {
2596232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server;
2696232827SLei YU using namespace phosphor::logging;
2796232827SLei YU 
2896232827SLei YU BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus,
2996232827SLei YU                    const char* objPath)
30*7b218796SLei YU     : EpochBase(bus, objPath),
31*7b218796SLei YU       bus(bus)
3296232827SLei YU {
33*7b218796SLei YU     initialize();
34*7b218796SLei YU }
35*7b218796SLei YU 
36*7b218796SLei YU void BmcEpoch::initialize()
37*7b218796SLei YU {
38*7b218796SLei YU     using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
39*7b218796SLei YU                                 Error::InternalFailure;
40*7b218796SLei YU 
41*7b218796SLei YU     // Subscribe time change event
42*7b218796SLei YU     // Choose the MAX time that is possible to avoid mis fires.
43*7b218796SLei YU     constexpr itimerspec maxTime = {
44*7b218796SLei YU         {0, 0}, // it_interval
45*7b218796SLei YU         {TIME_T_MAX, 0}, //it_value
46*7b218796SLei YU     };
47*7b218796SLei YU 
48*7b218796SLei YU     timeFd = timerfd_create(CLOCK_REALTIME, 0);
49*7b218796SLei YU     if (timeFd == -1)
50*7b218796SLei YU     {
51*7b218796SLei YU         log<level::ERR>("Failed to create timerfd",
52*7b218796SLei YU                         entry("ERRNO=%d", errno),
53*7b218796SLei YU                         entry("ERR=%s", strerror(errno)));
54*7b218796SLei YU         elog<InternalFailure>();
55*7b218796SLei YU     }
56*7b218796SLei YU 
57*7b218796SLei YU     auto r = timerfd_settime(timeFd,
58*7b218796SLei YU                              TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
59*7b218796SLei YU                              &maxTime,
60*7b218796SLei YU                              nullptr);
61*7b218796SLei YU     if (r != 0)
62*7b218796SLei YU     {
63*7b218796SLei YU         log<level::ERR>("Failed to set timerfd",
64*7b218796SLei YU                         entry("ERRNO=%d", errno),
65*7b218796SLei YU                         entry("ERR=%s", strerror(errno)));
66*7b218796SLei YU         elog<InternalFailure>();
67*7b218796SLei YU     }
68*7b218796SLei YU 
69*7b218796SLei YU     sd_event_source* es;
70*7b218796SLei YU     r = sd_event_add_io(bus.get_event(), &es,
71*7b218796SLei YU                         timeFd, EPOLLIN, onTimeChange, this);
72*7b218796SLei YU     if (r < 0)
73*7b218796SLei YU     {
74*7b218796SLei YU         log<level::ERR>("Failed to add event",
75*7b218796SLei YU                         entry("ERRNO=%d", -r),
76*7b218796SLei YU                         entry("ERR=%s", strerror(-r)));
77*7b218796SLei YU         elog<InternalFailure>();
78*7b218796SLei YU     }
79*7b218796SLei YU     timeChangeEventSource.reset(es);
80*7b218796SLei YU }
81*7b218796SLei YU 
82*7b218796SLei YU BmcEpoch::~BmcEpoch()
83*7b218796SLei YU {
84*7b218796SLei YU     close(timeFd);
8596232827SLei YU }
8696232827SLei YU 
8796232827SLei YU uint64_t BmcEpoch::elapsed() const
8896232827SLei YU {
8996232827SLei YU     // It does not needs to check owner when getting time
9096232827SLei YU     return getTime().count();
9196232827SLei YU }
9296232827SLei YU 
9396232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value)
9496232827SLei YU {
95*7b218796SLei YU     /*
96*7b218796SLei YU         Mode  | Owner | Set BMC Time
97*7b218796SLei YU         ----- | ----- | -------------
98*7b218796SLei YU         NTP   | BMC   | Not allowed
99*7b218796SLei YU         NTP   | HOST  | Not allowed
100*7b218796SLei YU         NTP   | SPLIT | Not allowed
101*7b218796SLei YU         NTP   | BOTH  | Not allowed
102*7b218796SLei YU         MANUAL| BMC   | OK
103*7b218796SLei YU         MANUAL| HOST  | Not allowed
104*7b218796SLei YU         MANUAL| SPLIT | OK
105*7b218796SLei YU         MANUAL| BOTH  | OK
106*7b218796SLei YU     */
107e7abcdc7SLei YU     if (timeMode == Mode::NTP)
108e7abcdc7SLei YU     {
109e7abcdc7SLei YU         log<level::ERR>("Setting BmcTime with NTP mode is not allowed");
110e7abcdc7SLei YU         // TODO: throw NotAllowed exception
111e7abcdc7SLei YU         return 0;
112e7abcdc7SLei YU     }
113e7abcdc7SLei YU     if (timeOwner == Owner::HOST)
114e7abcdc7SLei YU     {
115e7abcdc7SLei YU         log<level::ERR>("Setting BmcTime with HOST owner is not allowed");
116e7abcdc7SLei YU         // TODO: throw NotAllowed exception
117e7abcdc7SLei YU         return 0;
118e7abcdc7SLei YU     }
119e7abcdc7SLei YU 
120*7b218796SLei YU     auto time = microseconds(value);
12196232827SLei YU     setTime(time);
122e7abcdc7SLei YU 
123*7b218796SLei YU     notifyBmcTimeChange(time);
124*7b218796SLei YU 
12596232827SLei YU     server::EpochTime::elapsed(value);
12696232827SLei YU     return value;
12796232827SLei YU }
12896232827SLei YU 
129*7b218796SLei YU void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
130*7b218796SLei YU {
131*7b218796SLei YU     timeChangeListener = listener;
132*7b218796SLei YU }
133*7b218796SLei YU 
134*7b218796SLei YU void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
135*7b218796SLei YU {
136*7b218796SLei YU     // Notify listener if it exists
137*7b218796SLei YU     if (timeChangeListener)
138*7b218796SLei YU     {
139*7b218796SLei YU         timeChangeListener->onBmcTimeChanged(time);
140*7b218796SLei YU     }
141*7b218796SLei YU }
142*7b218796SLei YU 
143*7b218796SLei YU int BmcEpoch::onTimeChange(sd_event_source* es, int fd,
144*7b218796SLei YU                            uint32_t /* revents */, void* userdata)
145*7b218796SLei YU {
146*7b218796SLei YU     auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
147*7b218796SLei YU 
148*7b218796SLei YU     std::array<char, 64> time {};
149*7b218796SLei YU 
150*7b218796SLei YU     // We are not interested in the data here.
151*7b218796SLei YU     // So read until there is no new data here in the FD
152*7b218796SLei YU     while (read(fd, time.data(), time.max_size()) > 0);
153*7b218796SLei YU 
154*7b218796SLei YU     log<level::INFO>("BMC system time is changed");
155*7b218796SLei YU     bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
156*7b218796SLei YU 
157*7b218796SLei YU     return 0;
158*7b218796SLei YU }
159*7b218796SLei YU 
160af5abc57SLei YU } // namespace time
161af5abc57SLei YU } // namespace phosphor
16296232827SLei YU 
163