blob: c06c464e63b19da27681d4f5175ec9fa9c61ff88 [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
Gunnar Mills75710b62018-04-08 14:50:11 -050012// Need to do this since its not exported outside of the kernel.
Lei YU7b218792017-02-09 12:10:13 +080013// 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;
Lei YU33752c72018-06-07 17:06:58 +080027using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Lei YU96232822017-01-20 14:05:46 +080028
29BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus,
30 const char* objPath)
Lei YU7b218792017-02-09 12:10:13 +080031 : EpochBase(bus, objPath),
32 bus(bus)
Lei YU96232822017-01-20 14:05:46 +080033{
Lei YU7b218792017-02-09 12:10:13 +080034 initialize();
35}
36
37void BmcEpoch::initialize()
38{
39 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
40 Error::InternalFailure;
41
42 // Subscribe time change event
43 // Choose the MAX time that is possible to avoid mis fires.
44 constexpr itimerspec maxTime = {
45 {0, 0}, // it_interval
46 {TIME_T_MAX, 0}, //it_value
47 };
48
49 timeFd = timerfd_create(CLOCK_REALTIME, 0);
50 if (timeFd == -1)
51 {
52 log<level::ERR>("Failed to create timerfd",
53 entry("ERRNO=%d", errno),
54 entry("ERR=%s", strerror(errno)));
55 elog<InternalFailure>();
56 }
57
58 auto r = timerfd_settime(timeFd,
59 TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
60 &maxTime,
61 nullptr);
62 if (r != 0)
63 {
64 log<level::ERR>("Failed to set timerfd",
65 entry("ERRNO=%d", errno),
66 entry("ERR=%s", strerror(errno)));
67 elog<InternalFailure>();
68 }
69
70 sd_event_source* es;
71 r = sd_event_add_io(bus.get_event(), &es,
72 timeFd, EPOLLIN, onTimeChange, this);
73 if (r < 0)
74 {
75 log<level::ERR>("Failed to add event",
76 entry("ERRNO=%d", -r),
77 entry("ERR=%s", strerror(-r)));
78 elog<InternalFailure>();
79 }
80 timeChangeEventSource.reset(es);
81}
82
83BmcEpoch::~BmcEpoch()
84{
85 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080086}
87
88uint64_t BmcEpoch::elapsed() const
89{
90 // It does not needs to check owner when getting time
91 return getTime().count();
92}
93
94uint64_t BmcEpoch::elapsed(uint64_t value)
95{
Lei YU7b218792017-02-09 12:10:13 +080096 /*
97 Mode | Owner | Set BMC Time
98 ----- | ----- | -------------
Lei YU1c2ce822017-08-01 17:37:41 +080099 NTP | BMC | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800100 NTP | HOST | Not allowed
Lei YU1c2ce822017-08-01 17:37:41 +0800101 NTP | SPLIT | Fail to set
102 NTP | BOTH | Fail to set
Lei YU7b218792017-02-09 12:10:13 +0800103 MANUAL| BMC | OK
104 MANUAL| HOST | Not allowed
105 MANUAL| SPLIT | OK
106 MANUAL| BOTH | OK
107 */
Lei YUad143542017-07-25 14:27:07 +0800108 if (timeOwner == Owner::Host)
Lei YUe7abcdc2017-01-16 15:05:24 +0800109 {
110 log<level::ERR>("Setting BmcTime with HOST owner is not allowed");
Lei YU33752c72018-06-07 17:06:58 +0800111 elog<InsufficientPermission>();
Lei YUe7abcdc2017-01-16 15:05:24 +0800112 }
113
Lei YU7b218792017-02-09 12:10:13 +0800114 auto time = microseconds(value);
Lei YUfa024882017-11-09 10:49:26 +0800115 if (setTime(time))
116 {
117 notifyBmcTimeChange(time);
118 }
Lei YU7b218792017-02-09 12:10:13 +0800119
Lei YU96232822017-01-20 14:05:46 +0800120 server::EpochTime::elapsed(value);
121 return value;
122}
123
Lei YU7b218792017-02-09 12:10:13 +0800124void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener)
125{
126 timeChangeListener = listener;
127}
128
129void BmcEpoch::notifyBmcTimeChange(const microseconds& time)
130{
131 // Notify listener if it exists
132 if (timeChangeListener)
133 {
134 timeChangeListener->onBmcTimeChanged(time);
135 }
136}
137
138int BmcEpoch::onTimeChange(sd_event_source* es, int fd,
139 uint32_t /* revents */, void* userdata)
140{
141 auto bmcEpoch = static_cast<BmcEpoch*>(userdata);
142
143 std::array<char, 64> time {};
144
145 // We are not interested in the data here.
146 // So read until there is no new data here in the FD
147 while (read(fd, time.data(), time.max_size()) > 0);
148
149 log<level::INFO>("BMC system time is changed");
150 bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime());
151
152 return 0;
153}
154
Lei YUaf5abc52017-03-07 17:49:17 +0800155} // namespace time
156} // namespace phosphor
Lei YU96232822017-01-20 14:05:46 +0800157