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 SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1"; 30 constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1"; 31 constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1"; 32 constexpr auto METHOD_SET_TIME = "SetTime"; 33 } // namespace 34 35 namespace server = sdbusplus::xyz::openbmc_project::Time::server; 36 using namespace phosphor::logging; 37 using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed; 38 39 void BmcEpoch::initialize() 40 { 41 using InternalFailure = 42 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 43 44 // Subscribe time change event 45 // Choose the MAX time that is possible to avoid mis fires. 46 constexpr itimerspec maxTime = { 47 {0, 0}, // it_interval 48 {TIME_T_MAX, 0}, // it_value 49 }; 50 51 timeFd = timerfd_create(CLOCK_REALTIME, 0); 52 if (timeFd == -1) 53 { 54 lg2::error("Failed to create timerfd: {ERRNO}", "ERRNO", errno); 55 elog<InternalFailure>(); 56 } 57 58 auto r = timerfd_settime( 59 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr); 60 if (r != 0) 61 { 62 lg2::error("Failed to set timerfd: {ERRNO}", "ERRNO", errno); 63 elog<InternalFailure>(); 64 } 65 66 sd_event_source* es; 67 r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange, 68 this); 69 if (r < 0) 70 { 71 lg2::error("Failed to add event: {ERRNO}", "ERRNO", errno); 72 elog<InternalFailure>(); 73 } 74 timeChangeEventSource.reset(es); 75 } 76 77 BmcEpoch::~BmcEpoch() 78 { 79 close(timeFd); 80 } 81 82 uint64_t BmcEpoch::elapsed() const 83 { 84 return getTime().count(); 85 } 86 87 uint64_t BmcEpoch::elapsed(uint64_t value) 88 { 89 /* 90 Mode | Set BMC Time 91 ----- | ------------- 92 NTP | Fail to set 93 MANUAL| OK 94 */ 95 auto time = microseconds(value); 96 setTime(time); 97 98 server::EpochTime::elapsed(value); 99 return value; 100 } 101 102 int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd, 103 uint32_t /* revents */, void* /* userdata */) 104 { 105 std::array<char, 64> time{}; 106 107 // We are not interested in the data here. 108 // So read until there is no new data here in the FD 109 while (read(fd, time.data(), time.max_size()) > 0) 110 ; 111 112 return 0; 113 } 114 115 void BmcEpoch::onModeChanged(Mode mode) 116 { 117 manager.setTimeMode(mode); 118 } 119 120 bool 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 141 microseconds BmcEpoch::getTime() const 142 { 143 auto now = system_clock::now(); 144 return duration_cast<microseconds>(now.time_since_epoch()); 145 } 146 147 } // namespace time 148 } // namespace phosphor 149