blob: 8c251eeede6ce3e26aba48b029d11f3699983845 [file] [log] [blame]
Lei YU96232822017-01-20 14:05:46 +08001#include "bmc_epoch.hpp"
Lei YUf6fad822018-07-13 16:35:45 +08002#include "utils.hpp"
Lei YU96232822017-01-20 14:05:46 +08003
Lei YU7b218792017-02-09 12:10:13 +08004#include <phosphor-logging/elog.hpp>
5#include <phosphor-logging/elog-errors.hpp>
Lei YU96232822017-01-20 14:05:46 +08006#include <phosphor-logging/log.hpp>
Lei YU7b218792017-02-09 12:10:13 +08007#include <xyz/openbmc_project/Common/error.hpp>
Lei YUf6fad822018-07-13 16:35:45 +08008#include <xyz/openbmc_project/Time/error.hpp>
Lei YU7b218792017-02-09 12:10:13 +08009
10#include <sys/timerfd.h>
11#include <unistd.h>
12
13
Gunnar Mills75710b62018-04-08 14:50:11 -050014// Need to do this since its not exported outside of the kernel.
Lei YU7b218792017-02-09 12:10:13 +080015// Refer : https://gist.github.com/lethean/446cea944b7441228298
16#ifndef TFD_TIMER_CANCEL_ON_SET
17#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
18#endif
19
20// Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
21#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
Lei YU96232822017-01-20 14:05:46 +080022
23namespace phosphor
24{
25namespace time
26{
27namespace server = sdbusplus::xyz::openbmc_project::Time::server;
28using namespace phosphor::logging;
Lei YUf6fad822018-07-13 16:35:45 +080029
30using NotAllowedError =
31 sdbusplus::xyz::openbmc_project::Time::Error::NotAllowed;
Lei YU96232822017-01-20 14:05:46 +080032
33BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus,
34 const char* objPath)
Lei YU7b218792017-02-09 12:10:13 +080035 : EpochBase(bus, objPath),
36 bus(bus)
Lei YU96232822017-01-20 14:05:46 +080037{
Lei YU7b218792017-02-09 12:10:13 +080038 initialize();
39}
40
41void BmcEpoch::initialize()
42{
43 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
44 Error::InternalFailure;
45
46 // Subscribe time change event
47 // Choose the MAX time that is possible to avoid mis fires.
48 constexpr itimerspec maxTime = {
49 {0, 0}, // it_interval
50 {TIME_T_MAX, 0}, //it_value
51 };
52
53 timeFd = timerfd_create(CLOCK_REALTIME, 0);
54 if (timeFd == -1)
55 {
56 log<level::ERR>("Failed to create timerfd",
57 entry("ERRNO=%d", errno),
58 entry("ERR=%s", strerror(errno)));
59 elog<InternalFailure>();
60 }
61
62 auto r = timerfd_settime(timeFd,
63 TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
64 &maxTime,
65 nullptr);
66 if (r != 0)
67 {
68 log<level::ERR>("Failed to set timerfd",
69 entry("ERRNO=%d", errno),
70 entry("ERR=%s", strerror(errno)));
71 elog<InternalFailure>();
72 }
73
74 sd_event_source* es;
75 r = sd_event_add_io(bus.get_event(), &es,
76 timeFd, EPOLLIN, onTimeChange, this);
77 if (r < 0)
78 {
79 log<level::ERR>("Failed to add event",
80 entry("ERRNO=%d", -r),
81 entry("ERR=%s", strerror(-r)));
82 elog<InternalFailure>();
83 }
84 timeChangeEventSource.reset(es);
85}
86
87BmcEpoch::~BmcEpoch()
88{
89 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080090}
91
92uint64_t BmcEpoch::elapsed() const
93{
94 // It does not needs to check owner when getting time
95 return getTime().count();
96}
97
98uint64_t BmcEpoch::elapsed(uint64_t value)
99{
Lei YU7b218792017-02-09 12:10:13 +0800100 /*
101 Mode | Owner | Set BMC Time
102 ----- | ----- | -------------
Lei YU1c2ce822017-08-01 17:37:41 +0800103 NTP | BMC | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800104 NTP | HOST | Not allowed
Lei YU1c2ce822017-08-01 17:37:41 +0800105 NTP | SPLIT | Fail to set
106 NTP | BOTH | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800107 MANUAL| BMC | OK
108 MANUAL| HOST | Not allowed
109 MANUAL| SPLIT | OK
110 MANUAL| BOTH | OK
111 */
Lei YUad143542017-07-25 14:27:07 +0800112 if (timeOwner == Owner::Host)
Lei YUe7abcdc2017-01-16 15:05:24 +0800113 {
Lei YUf6fad822018-07-13 16:35:45 +0800114 using namespace xyz::openbmc_project::Time;
115 elog<NotAllowedError>(
116 NotAllowed::OWNER(utils::ownerToStr(timeOwner).c_str()),
117 NotAllowed::SYNC_METHOD(utils::modeToStr(timeMode).c_str()),
118 NotAllowed::REASON(
119 "Setting BmcTime with HOST owner is not allowed"));
Lei YUe7abcdc2017-01-16 15:05:24 +0800120 }
121
Lei YU7b218792017-02-09 12:10:13 +0800122 auto time = microseconds(value);
Lei YUfa024882017-11-09 10:49:26 +0800123 if (setTime(time))
124 {
125 notifyBmcTimeChange(time);
126 }
Lei YU7b218792017-02-09 12:10:13 +0800127
Lei YU96232822017-01-20 14:05:46 +0800128 server::EpochTime::elapsed(value);
129 return value;
130}
131
Lei YU7b218792017-02-09 12:10:13 +0800132void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
133{
134 timeChangeListener = listener;
135}
136
137void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
138{
139 // Notify listener if it exists
140 if (timeChangeListener)
141 {
142 timeChangeListener->onBmcTimeChanged(time);
143 }
144}
145
146int BmcEpoch::onTimeChange(sd_event_source* es, int fd,
147 uint32_t /* revents */, void* userdata)
148{
149 auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
150
151 std::array<char, 64> time {};
152
153 // We are not interested in the data here.
154 // So read until there is no new data here in the FD
155 while (read(fd, time.data(), time.max_size()) > 0);
156
157 log<level::INFO>("BMC system time is changed");
158 bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
159
160 return 0;
161}
162
Lei YUaf5abc52017-03-07 17:49:17 +0800163} // namespace time
164} // namespace phosphor
Lei YU96232822017-01-20 14:05:46 +0800165