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 #include <xyz/openbmc_project/Time/error.hpp> 13 14 // Need to do this since its not exported outside of the kernel. 15 // 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) 22 23 namespace phosphor 24 { 25 namespace time 26 { 27 namespace // anonymous 28 { 29 constexpr auto systemdTimeService = "org.freedesktop.timedate1"; 30 constexpr auto systemdTimePath = "/org/freedesktop/timedate1"; 31 constexpr auto systemdTimeInterface = "org.freedesktop.timedate1"; 32 constexpr auto methodSetTime = "SetTime"; 33 } // namespace 34 35 PHOSPHOR_LOG2_USING; 36 37 namespace server = sdbusplus::xyz::openbmc_project::Time::server; 38 using namespace phosphor::logging; 39 using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed; 40 41 void BmcEpoch::initialize() 42 { 43 using InternalFailure = 44 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 45 46 // Subscribe time change event 47 // Choose the MAX time that is possible to avoid mis fires. 48 constexpr itimerspec maxTime = { 49 {0, 0}, // it_interval 50 {TIME_T_MAX, 0}, // it_value 51 }; 52 53 timeFd = timerfd_create(CLOCK_REALTIME, 0); 54 if (timeFd == -1) 55 { 56 error("Failed to create timerfd: {ERRNO}", "ERRNO", errno); 57 elog<InternalFailure>(); 58 } 59 60 auto r = timerfd_settime( 61 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr); 62 if (r != 0) 63 { 64 error("Failed to set timerfd: {ERRNO}", "ERRNO", errno); 65 elog<InternalFailure>(); 66 } 67 68 sd_event_source* es = nullptr; 69 r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange, 70 this); 71 if (r < 0) 72 { 73 error("Failed to add event: {ERRNO}", "ERRNO", errno); 74 elog<InternalFailure>(); 75 } 76 timeChangeEventSource.reset(es); 77 } 78 79 BmcEpoch::~BmcEpoch() 80 { 81 close(timeFd); 82 } 83 84 uint64_t BmcEpoch::elapsed() const 85 { 86 return getTime().count(); 87 } 88 89 uint64_t BmcEpoch::elapsed(uint64_t value) 90 { 91 /* 92 Mode | Set BMC Time 93 ----- | ------------- 94 NTP | Fail to set 95 MANUAL| OK 96 */ 97 auto time = microseconds(value); 98 setTime(time); 99 100 server::EpochTime::elapsed(value); 101 return value; 102 } 103 104 int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd, 105 uint32_t /* revents */, void* /* userdata */) 106 { 107 std::array<char, 64> time{}; 108 109 // We are not interested in the data here. 110 // So read until there is no new data here in the FD 111 while (read(fd, time.data(), time.max_size()) > 0) 112 { 113 ; 114 } 115 116 return 0; 117 } 118 119 void BmcEpoch::onModeChanged(Mode mode) 120 { 121 manager.setTimeMode(mode); 122 } 123 124 bool BmcEpoch::setTime(const microseconds& usec) 125 { 126 auto method = bus.new_method_call(systemdTimeService, systemdTimePath, 127 systemdTimeInterface, methodSetTime); 128 method.append(static_cast<int64_t>(usec.count()), 129 false, // relative 130 false); // user_interaction 131 132 try 133 { 134 bus.call_noreply(method); 135 } 136 catch (const sdbusplus::exception_t& ex) 137 { 138 error("Error in setting system time: {ERROR}", "ERROR", ex); 139 using namespace xyz::openbmc_project::Time; 140 elog<FailedError>(Failed::REASON(ex.what())); 141 } 142 return true; 143 } 144 145 microseconds BmcEpoch::getTime() 146 { 147 auto now = system_clock::now(); 148 return duration_cast<microseconds>(now.time_since_epoch()); 149 } 150 151 } // namespace time 152 } // namespace phosphor 153