blob: db4051a22084dbaa5869b6fa783205bc0d9cf240 [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>
George Liu947b5342022-07-01 16:12:18 +080010#include <phosphor-logging/lg2.hpp>
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050011#include <xyz/openbmc_project/Common/error.hpp>
George Liuf1d54ae2022-09-01 16:54:57 +080012#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{
George Liuf1d54ae2022-09-01 16:54:57 +080027namespace // anonymous
28{
29constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1";
30constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1";
31constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1";
32constexpr auto METHOD_SET_TIME = "SetTime";
33} // namespace
34
Lei YU96232822017-01-20 14:05:46 +080035namespace server = sdbusplus::xyz::openbmc_project::Time::server;
36using namespace phosphor::logging;
George Liuf1d54ae2022-09-01 16:54:57 +080037using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed;
Lei YU7b218792017-02-09 12:10:13 +080038
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 {
George Liu947b5342022-07-01 16:12:18 +080054 lg2::error("Failed to create timerfd: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080055 elog<InternalFailure>();
56 }
57
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050058 auto r = timerfd_settime(
59 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
Lei YU7b218792017-02-09 12:10:13 +080060 if (r != 0)
61 {
George Liu947b5342022-07-01 16:12:18 +080062 lg2::error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080063 elog<InternalFailure>();
64 }
65
66 sd_event_source* es;
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050067 r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
68 this);
Lei YU7b218792017-02-09 12:10:13 +080069 if (r < 0)
70 {
George Liu947b5342022-07-01 16:12:18 +080071 lg2::error("Failed to add event: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080072 elog<InternalFailure>();
73 }
74 timeChangeEventSource.reset(es);
75}
76
77BmcEpoch::~BmcEpoch()
78{
79 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080080}
81
82uint64_t BmcEpoch::elapsed() const
83{
Lei YU96232822017-01-20 14:05:46 +080084 return getTime().count();
85}
86
87uint64_t BmcEpoch::elapsed(uint64_t value)
88{
Lei YU7b218792017-02-09 12:10:13 +080089 /*
George Liu3c2f4492020-04-12 11:35:57 +080090 Mode | Set BMC Time
91 ----- | -------------
92 NTP | Fail to set
93 MANUAL| OK
Lei YU7b218792017-02-09 12:10:13 +080094 */
Lei YU7b218792017-02-09 12:10:13 +080095 auto time = microseconds(value);
George Liu3c2f4492020-04-12 11:35:57 +080096 setTime(time);
Lei YU7b218792017-02-09 12:10:13 +080097
Lei YU96232822017-01-20 14:05:46 +080098 server::EpochTime::elapsed(value);
99 return value;
100}
101
Ratan Gupta1e1dc442021-02-02 05:51:08 -0600102int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
103 uint32_t /* revents */, void* /* userdata */)
Lei YU7b218792017-02-09 12:10:13 +0800104{
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500105 std::array<char, 64> time{};
Lei YU7b218792017-02-09 12:10:13 +0800106
107 // We are not interested in the data here.
108 // So read until there is no new data here in the FD
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500109 while (read(fd, time.data(), time.max_size()) > 0)
110 ;
Lei YU7b218792017-02-09 12:10:13 +0800111
Lei YU7b218792017-02-09 12:10:13 +0800112 return 0;
113}
114
George Liuf1d54ae2022-09-01 16:54:57 +0800115void BmcEpoch::onModeChanged(Mode mode)
116{
117 manager.setTimeMode(mode);
118}
119
120bool BmcEpoch::setTime(const microseconds& usec)
121{
122 auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH,
123 SYSTEMD_TIME_INTERFACE, METHOD_SET_TIME);
124 method.append(static_cast<int64_t>(usec.count()),
125 false, // relative
126 false); // user_interaction
127
128 try
129 {
130 bus.call_noreply(method);
131 }
132 catch (const sdbusplus::exception_t& ex)
133 {
134 lg2::error("Error in setting system time: {ERROR}", "ERROR", ex);
135 using namespace xyz::openbmc_project::Time;
136 elog<FailedError>(Failed::REASON(ex.what()));
137 }
138 return true;
139}
140
141microseconds BmcEpoch::getTime() const
142{
143 auto now = system_clock::now();
144 return duration_cast<microseconds>(now.time_since_epoch());
145}
146
Lei YUaf5abc52017-03-07 17:49:17 +0800147} // namespace time
148} // namespace phosphor