1 #include "bmc_epoch.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <sys/timerfd.h>
6 #include <unistd.h>
7 
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 // Need to do this since its not exported outside of the kernel.
14 // Refer : https://gist.github.com/lethean/446cea944b7441228298
15 #ifndef TFD_TIMER_CANCEL_ON_SET
16 #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
17 #endif
18 
19 // Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET
20 #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
21 
22 namespace phosphor
23 {
24 namespace time
25 {
26 namespace server = sdbusplus::xyz::openbmc_project::Time::server;
27 using namespace phosphor::logging;
28 
29 BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus, const char* objPath) :
30     EpochBase(bus, objPath)
31 {
32     initialize();
33 }
34 
35 void BmcEpoch::initialize()
36 {
37     using InternalFailure =
38         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
39 
40     // Subscribe time change event
41     // Choose the MAX time that is possible to avoid mis fires.
42     constexpr itimerspec maxTime = {
43         {0, 0},          // it_interval
44         {TIME_T_MAX, 0}, // it_value
45     };
46 
47     timeFd = timerfd_create(CLOCK_REALTIME, 0);
48     if (timeFd == -1)
49     {
50         lg2::error("Failed to create timerfd: {ERRNO}", "ERRNO", errno);
51         elog<InternalFailure>();
52     }
53 
54     auto r = timerfd_settime(
55         timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
56     if (r != 0)
57     {
58         lg2::error("Failed to set timerfd: {ERRNO}", "ERRNO", errno);
59         elog<InternalFailure>();
60     }
61 
62     sd_event_source* es;
63     r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange,
64                         this);
65     if (r < 0)
66     {
67         lg2::error("Failed to add event: {ERRNO}", "ERRNO", errno);
68         elog<InternalFailure>();
69     }
70     timeChangeEventSource.reset(es);
71 }
72 
73 BmcEpoch::~BmcEpoch()
74 {
75     close(timeFd);
76 }
77 
78 uint64_t BmcEpoch::elapsed() const
79 {
80     return getTime().count();
81 }
82 
83 uint64_t BmcEpoch::elapsed(uint64_t value)
84 {
85     /*
86         Mode  | Set BMC Time
87         ----- | -------------
88         NTP   | Fail to set
89         MANUAL| OK
90     */
91     auto time = microseconds(value);
92     setTime(time);
93 
94     server::EpochTime::elapsed(value);
95     return value;
96 }
97 
98 int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd,
99                            uint32_t /* revents */, void* /* userdata */)
100 {
101     std::array<char, 64> time{};
102 
103     // We are not interested in the data here.
104     // So read until there is no new data here in the FD
105     while (read(fd, time.data(), time.max_size()) > 0)
106         ;
107 
108     return 0;
109 }
110 
111 } // namespace time
112 } // namespace phosphor
113