blob: bb3872b02261dc2253ea7ce84cca61418940448e [file] [log] [blame]
Lei YU96232822017-01-20 14:05:46 +08001#include "bmc_epoch.hpp"
2
Lei YU7b218792017-02-09 12:10:13 +08003#include <phosphor-logging/elog.hpp>
4#include <phosphor-logging/elog-errors.hpp>
Lei YU96232822017-01-20 14:05:46 +08005#include <phosphor-logging/log.hpp>
Lei YU7b218792017-02-09 12:10:13 +08006#include <xyz/openbmc_project/Common/error.hpp>
7
8#include <sys/timerfd.h>
9#include <unistd.h>
10
11
12// Neeed to do this since its not exported outside of the kernel.
13// Refer : https://gist.github.com/lethean/446cea944b7441228298
14#ifndef TFD_TIMER_CANCEL_ON_SET
15#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
16#endif
17
18// Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
19#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
Lei YU96232822017-01-20 14:05:46 +080020
21namespace phosphor
22{
23namespace time
24{
25namespace server = sdbusplus::xyz::openbmc_project::Time::server;
26using namespace phosphor::logging;
27
28BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus,
29 const char* objPath)
Lei YU7b218792017-02-09 12:10:13 +080030 : EpochBase(bus, objPath),
31 bus(bus)
Lei YU96232822017-01-20 14:05:46 +080032{
Lei YU7b218792017-02-09 12:10:13 +080033 initialize();
34}
35
36void BmcEpoch::initialize()
37{
38 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
39 Error::InternalFailure;
40
41 // Subscribe time change event
42 // Choose the MAX time that is possible to avoid mis fires.
43 constexpr itimerspec maxTime = {
44 {0, 0}, // it_interval
45 {TIME_T_MAX, 0}, //it_value
46 };
47
48 timeFd = timerfd_create(CLOCK_REALTIME, 0);
49 if (timeFd == -1)
50 {
51 log<level::ERR>("Failed to create timerfd",
52 entry("ERRNO=%d", errno),
53 entry("ERR=%s", strerror(errno)));
54 elog<InternalFailure>();
55 }
56
57 auto r = timerfd_settime(timeFd,
58 TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
59 &maxTime,
60 nullptr);
61 if (r != 0)
62 {
63 log<level::ERR>("Failed to set timerfd",
64 entry("ERRNO=%d", errno),
65 entry("ERR=%s", strerror(errno)));
66 elog<InternalFailure>();
67 }
68
69 sd_event_source* es;
70 r = sd_event_add_io(bus.get_event(), &es,
71 timeFd, EPOLLIN, onTimeChange, this);
72 if (r < 0)
73 {
74 log<level::ERR>("Failed to add event",
75 entry("ERRNO=%d", -r),
76 entry("ERR=%s", strerror(-r)));
77 elog<InternalFailure>();
78 }
79 timeChangeEventSource.reset(es);
80}
81
82BmcEpoch::~BmcEpoch()
83{
84 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080085}
86
87uint64_t BmcEpoch::elapsed() const
88{
89 // It does not needs to check owner when getting time
90 return getTime().count();
91}
92
93uint64_t BmcEpoch::elapsed(uint64_t value)
94{
Lei YU7b218792017-02-09 12:10:13 +080095 /*
96 Mode | Owner | Set BMC Time
97 ----- | ----- | -------------
Lei YU1c2ce822017-08-01 17:37:41 +080098 NTP | BMC | Fail to set
Lei YU7b218792017-02-09 12:10:13 +080099 NTP | HOST | Not allowed
Lei YU1c2ce822017-08-01 17:37:41 +0800100 NTP | SPLIT | Fail to set
101 NTP | BOTH | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800102 MANUAL| BMC | OK
103 MANUAL| HOST | Not allowed
104 MANUAL| SPLIT | OK
105 MANUAL| BOTH | OK
106 */
Lei YUad143542017-07-25 14:27:07 +0800107 if (timeOwner == Owner::Host)
Lei YUe7abcdc2017-01-16 15:05:24 +0800108 {
109 log<level::ERR>("Setting BmcTime with HOST owner is not allowed");
110 // TODO: throw NotAllowed exception
111 return 0;
112 }
113
Lei YU7b218792017-02-09 12:10:13 +0800114 auto time = microseconds(value);
Lei YUe7abcdc2017-01-16 15:05:24 +0800115 setTime(time);
116
Lei YU7b218792017-02-09 12:10:13 +0800117 notifyBmcTimeChange(time);
118
Lei YU96232822017-01-20 14:05:46 +0800119 server::EpochTime::elapsed(value);
120 return value;
121}
122
Lei YU7b218792017-02-09 12:10:13 +0800123void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
124{
125 timeChangeListener = listener;
126}
127
128void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
129{
130 // Notify listener if it exists
131 if (timeChangeListener)
132 {
133 timeChangeListener->onBmcTimeChanged(time);
134 }
135}
136
137int BmcEpoch::onTimeChange(sd_event_source* es, int fd,
138 uint32_t /* revents */, void* userdata)
139{
140 auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
141
142 std::array<char, 64> time {};
143
144 // We are not interested in the data here.
145 // So read until there is no new data here in the FD
146 while (read(fd, time.data(), time.max_size()) > 0);
147
148 log<level::INFO>("BMC system time is changed");
149 bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
150
151 return 0;
152}
153
Lei YUaf5abc52017-03-07 17:49:17 +0800154} // namespace time
155} // namespace phosphor
Lei YU96232822017-01-20 14:05:46 +0800156