blob: d000a71c6af498a38feebe5f1e80ae0900fbdf78 [file] [log] [blame]
Lei YU96232822017-01-20 14:05:46 +08001#include "bmc_epoch.hpp"
2
Gunnar Millsab4cc6a2018-09-14 14:42:39 -05003#include "utils.hpp"
Lei YU7b218792017-02-09 12:10:13 +08004
5#include <sys/timerfd.h>
6#include <unistd.h>
7
Gunnar Millsab4cc6a2018-09-14 14:42:39 -05008#include <phosphor-logging/elog-errors.hpp>
9#include <phosphor-logging/elog.hpp>
10#include <phosphor-logging/log.hpp>
11#include <xyz/openbmc_project/Common/error.hpp>
12#include <xyz/openbmc_project/Time/error.hpp>
Lei YU7b218792017-02-09 12:10:13 +080013
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
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050033BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus, const char* objPath) :
34 EpochBase(bus, objPath), bus(bus)
Lei YU96232822017-01-20 14:05:46 +080035{
Lei YU7b218792017-02-09 12:10:13 +080036 initialize();
37}
38
39void BmcEpoch::initialize()
40{
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050041 using InternalFailure =
42 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Lei YU7b218792017-02-09 12:10:13 +080043
44 // Subscribe time change event
45 // Choose the MAX time that is possible to avoid mis fires.
46 constexpr itimerspec maxTime = {
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050047 {0, 0}, // it_interval
48 {TIME_T_MAX, 0}, // it_value
Lei YU7b218792017-02-09 12:10:13 +080049 };
50
51 timeFd = timerfd_create(CLOCK_REALTIME, 0);
52 if (timeFd == -1)
53 {
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050054 log<level::ERR>("Failed to create timerfd", entry("ERRNO=%d", errno),
Lei YU7b218792017-02-09 12:10:13 +080055 entry("ERR=%s", strerror(errno)));
56 elog<InternalFailure>();
57 }
58
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050059 auto r = timerfd_settime(
60 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
Lei YU7b218792017-02-09 12:10:13 +080061 if (r != 0)
62 {
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050063 log<level::ERR>("Failed to set timerfd", entry("ERRNO=%d", errno),
Lei YU7b218792017-02-09 12:10:13 +080064 entry("ERR=%s", strerror(errno)));
65 elog<InternalFailure>();
66 }
67
68 sd_event_source* es;
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050069 r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
70 this);
Lei YU7b218792017-02-09 12:10:13 +080071 if (r < 0)
72 {
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050073 log<level::ERR>("Failed to add event", entry("ERRNO=%d", -r),
Lei YU7b218792017-02-09 12:10:13 +080074 entry("ERR=%s", strerror(-r)));
75 elog<InternalFailure>();
76 }
77 timeChangeEventSource.reset(es);
78}
79
80BmcEpoch::~BmcEpoch()
81{
82 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080083}
84
85uint64_t BmcEpoch::elapsed() const
86{
87 // It does not needs to check owner when getting time
88 return getTime().count();
89}
90
91uint64_t BmcEpoch::elapsed(uint64_t value)
92{
Lei YU7b218792017-02-09 12:10:13 +080093 /*
94 Mode | Owner | Set BMC Time
95 ----- | ----- | -------------
Lei YU1c2ce822017-08-01 17:37:41 +080096 NTP | BMC | Fail to set
Lei YU7b218792017-02-09 12:10:13 +080097 NTP | HOST | Not allowed
Lei YU1c2ce822017-08-01 17:37:41 +080098 NTP | SPLIT | Fail to set
99 NTP | BOTH | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800100 MANUAL| BMC | OK
101 MANUAL| HOST | Not allowed
102 MANUAL| SPLIT | OK
103 MANUAL| BOTH | OK
104 */
Lei YUad143542017-07-25 14:27:07 +0800105 if (timeOwner == Owner::Host)
Lei YUe7abcdc2017-01-16 15:05:24 +0800106 {
Lei YUf6fad822018-07-13 16:35:45 +0800107 using namespace xyz::openbmc_project::Time;
108 elog<NotAllowedError>(
109 NotAllowed::OWNER(utils::ownerToStr(timeOwner).c_str()),
110 NotAllowed::SYNC_METHOD(utils::modeToStr(timeMode).c_str()),
111 NotAllowed::REASON(
112 "Setting BmcTime with HOST owner is not allowed"));
Lei YUe7abcdc2017-01-16 15:05:24 +0800113 }
114
Lei YU7b218792017-02-09 12:10:13 +0800115 auto time = microseconds(value);
Lei YUfa024882017-11-09 10:49:26 +0800116 if (setTime(time))
117 {
118 notifyBmcTimeChange(time);
119 }
Lei YU7b218792017-02-09 12:10:13 +0800120
Lei YU96232822017-01-20 14:05:46 +0800121 server::EpochTime::elapsed(value);
122 return value;
123}
124
Lei YU7b218792017-02-09 12:10:13 +0800125void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
126{
127 timeChangeListener = listener;
128}
129
130void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
131{
132 // Notify listener if it exists
133 if (timeChangeListener)
134 {
135 timeChangeListener->onBmcTimeChanged(time);
136 }
137}
138
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500139int BmcEpoch::onTimeChange(sd_event_source* es, int fd, uint32_t /* revents */,
140 void* userdata)
Lei YU7b218792017-02-09 12:10:13 +0800141{
142 auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
143
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500144 std::array<char, 64> time{};
Lei YU7b218792017-02-09 12:10:13 +0800145
146 // We are not interested in the data here.
147 // So read until there is no new data here in the FD
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500148 while (read(fd, time.data(), time.max_size()) > 0)
149 ;
Lei YU7b218792017-02-09 12:10:13 +0800150
151 log<level::INFO>("BMC system time is changed");
152 bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
153
154 return 0;
155}
156
Lei YUaf5abc52017-03-07 17:49:17 +0800157} // namespace time
158} // namespace phosphor