blob: 4c8bf60fc978f2e2ac44563d90316c551ca6928d [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 ----- | ----- | -------------
98 NTP | BMC | Not allowed
99 NTP | HOST | Not allowed
100 NTP | SPLIT | Not allowed
101 NTP | BOTH | Not allowed
102 MANUAL| BMC | OK
103 MANUAL| HOST | Not allowed
104 MANUAL| SPLIT | OK
105 MANUAL| BOTH | OK
106 */
Lei YUe7abcdc2017-01-16 15:05:24 +0800107 if (timeMode == Mode::NTP)
Lei YU96232822017-01-20 14:05:46 +0800108 {
Lei YUe7abcdc2017-01-16 15:05:24 +0800109 log<level::ERR>("Setting BmcTime with NTP mode is not allowed");
110 // TODO: throw NotAllowed exception
111 return 0;
Lei YU96232822017-01-20 14:05:46 +0800112 }
Lei YUad143542017-07-25 14:27:07 +0800113 if (timeOwner == Owner::Host)
Lei YUe7abcdc2017-01-16 15:05:24 +0800114 {
115 log<level::ERR>("Setting BmcTime with HOST owner is not allowed");
116 // TODO: throw NotAllowed exception
117 return 0;
118 }
119
Lei YU7b218792017-02-09 12:10:13 +0800120 auto time = microseconds(value);
Lei YUe7abcdc2017-01-16 15:05:24 +0800121 setTime(time);
122
Lei YU7b218792017-02-09 12:10:13 +0800123 notifyBmcTimeChange(time);
124
Lei YU96232822017-01-20 14:05:46 +0800125 server::EpochTime::elapsed(value);
126 return value;
127}
128
Lei YU7b218792017-02-09 12:10:13 +0800129void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
130{
131 timeChangeListener = listener;
132}
133
134void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
135{
136 // Notify listener if it exists
137 if (timeChangeListener)
138 {
139 timeChangeListener->onBmcTimeChanged(time);
140 }
141}
142
143int BmcEpoch::onTimeChange(sd_event_source* es, int fd,
144 uint32_t /* revents */, void* userdata)
145{
146 auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
147
148 std::array<char, 64> time {};
149
150 // We are not interested in the data here.
151 // So read until there is no new data here in the FD
152 while (read(fd, time.data(), time.max_size()) > 0);
153
154 log<level::INFO>("BMC system time is changed");
155 bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
156
157 return 0;
158}
159
Lei YUaf5abc52017-03-07 17:49:17 +0800160} // namespace time
161} // namespace phosphor
Lei YU96232822017-01-20 14:05:46 +0800162