blob: aa05060130cb70ef169826414a18d708ce4a692e [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
Pavithra Barithayadd42c7f2022-08-11 05:09:02 -050035PHOSPHOR_LOG2_USING;
36
Lei YU96232822017-01-20 14:05:46 +080037namespace server = sdbusplus::xyz::openbmc_project::Time::server;
38using namespace phosphor::logging;
George Liuf1d54ae2022-09-01 16:54:57 +080039using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed;
Lei YU7b218792017-02-09 12:10:13 +080040
41void BmcEpoch::initialize()
42{
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050043 using InternalFailure =
44 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Lei YU7b218792017-02-09 12:10:13 +080045
46 // Subscribe time change event
47 // Choose the MAX time that is possible to avoid mis fires.
48 constexpr itimerspec maxTime = {
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050049 {0, 0}, // it_interval
50 {TIME_T_MAX, 0}, // it_value
Lei YU7b218792017-02-09 12:10:13 +080051 };
52
53 timeFd = timerfd_create(CLOCK_REALTIME, 0);
54 if (timeFd == -1)
55 {
Pavithra Barithayadd42c7f2022-08-11 05:09:02 -050056 error("Failed to create timerfd: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080057 elog<InternalFailure>();
58 }
59
Gunnar Millsab4cc6a2018-09-14 14:42:39 -050060 auto r = timerfd_settime(
61 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
Lei YU7b218792017-02-09 12:10:13 +080062 if (r != 0)
63 {
Pavithra Barithayadd42c7f2022-08-11 05:09:02 -050064 error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080065 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 {
Pavithra Barithayadd42c7f2022-08-11 05:09:02 -050073 error("Failed to add event: {ERRNO}", "ERRNO", errno);
Lei YU7b218792017-02-09 12:10:13 +080074 elog<InternalFailure>();
75 }
76 timeChangeEventSource.reset(es);
77}
78
79BmcEpoch::~BmcEpoch()
80{
81 close(timeFd);
Lei YU96232822017-01-20 14:05:46 +080082}
83
84uint64_t BmcEpoch::elapsed() const
85{
Lei YU96232822017-01-20 14:05:46 +080086 return getTime().count();
87}
88
89uint64_t BmcEpoch::elapsed(uint64_t value)
90{
Lei YU7b218792017-02-09 12:10:13 +080091 /*
George Liu3c2f4492020-04-12 11:35:57 +080092 Mode | Set BMC Time
93 ----- | -------------
94 NTP | Fail to set
95 MANUAL| OK
Lei YU7b218792017-02-09 12:10:13 +080096 */
Lei YU7b218792017-02-09 12:10:13 +080097 auto time = microseconds(value);
George Liu3c2f4492020-04-12 11:35:57 +080098 setTime(time);
Lei YU7b218792017-02-09 12:10:13 +080099
Lei YU96232822017-01-20 14:05:46 +0800100 server::EpochTime::elapsed(value);
101 return value;
102}
103
Ratan Gupta1e1dc442021-02-02 05:51:08 -0600104int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
105 uint32_t /* revents */, void* /* userdata */)
Lei YU7b218792017-02-09 12:10:13 +0800106{
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500107 std::array<char, 64> time{};
Lei YU7b218792017-02-09 12:10:13 +0800108
109 // We are not interested in the data here.
110 // So read until there is no new data here in the FD
Gunnar Millsab4cc6a2018-09-14 14:42:39 -0500111 while (read(fd, time.data(), time.max_size()) > 0)
112 ;
Lei YU7b218792017-02-09 12:10:13 +0800113
Lei YU7b218792017-02-09 12:10:13 +0800114 return 0;
115}
116
George Liuf1d54ae2022-09-01 16:54:57 +0800117void BmcEpoch::onModeChanged(Mode mode)
118{
119 manager.setTimeMode(mode);
120}
121
122bool BmcEpoch::setTime(const microseconds& usec)
123{
124 auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH,
125 SYSTEMD_TIME_INTERFACE, METHOD_SET_TIME);
126 method.append(static_cast<int64_t>(usec.count()),
127 false, // relative
128 false); // user_interaction
129
130 try
131 {
132 bus.call_noreply(method);
133 }
134 catch (const sdbusplus::exception_t& ex)
135 {
Pavithra Barithayadd42c7f2022-08-11 05:09:02 -0500136 error("Error in setting system time: {ERROR}", "ERROR", ex);
George Liuf1d54ae2022-09-01 16:54:57 +0800137 using namespace xyz::openbmc_project::Time;
138 elog<FailedError>(Failed::REASON(ex.what()));
139 }
140 return true;
141}
142
143microseconds BmcEpoch::getTime() const
144{
145 auto now = system_clock::now();
146 return duration_cast<microseconds>(now.time_since_epoch());
147}
148
Lei YUaf5abc52017-03-07 17:49:17 +0800149} // namespace time
150} // namespace phosphor