196232827SLei YU #include "bmc_epoch.hpp" 296232827SLei YU 3ab4cc6a5SGunnar Mills #include "utils.hpp" 47b218796SLei YU 57b218796SLei YU #include <sys/timerfd.h> 67b218796SLei YU #include <unistd.h> 77b218796SLei YU 8ab4cc6a5SGunnar Mills #include <phosphor-logging/elog-errors.hpp> 9ab4cc6a5SGunnar Mills #include <phosphor-logging/elog.hpp> 10947b5346SGeorge Liu #include <phosphor-logging/lg2.hpp> 11ab4cc6a5SGunnar Mills #include <xyz/openbmc_project/Common/error.hpp> 12*f1d54aecSGeorge Liu #include <xyz/openbmc_project/Time/error.hpp> 137b218796SLei YU 1475710b6aSGunnar Mills // Need to do this since its not exported outside of the kernel. 157b218796SLei YU // Refer : https://gist.github.com/lethean/446cea944b7441228298 167b218796SLei YU #ifndef TFD_TIMER_CANCEL_ON_SET 177b218796SLei YU #define TFD_TIMER_CANCEL_ON_SET (1 << 1) 187b218796SLei YU #endif 197b218796SLei YU 207b218796SLei YU // Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET 217b218796SLei YU #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) 2296232827SLei YU 2396232827SLei YU namespace phosphor 2496232827SLei YU { 2596232827SLei YU namespace time 2696232827SLei YU { 27*f1d54aecSGeorge Liu namespace // anonymous 28*f1d54aecSGeorge Liu { 29*f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1"; 30*f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1"; 31*f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1"; 32*f1d54aecSGeorge Liu constexpr auto METHOD_SET_TIME = "SetTime"; 33*f1d54aecSGeorge Liu } // namespace 34*f1d54aecSGeorge Liu 3596232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server; 3696232827SLei YU using namespace phosphor::logging; 37*f1d54aecSGeorge Liu using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed; 387b218796SLei YU 397b218796SLei YU void BmcEpoch::initialize() 407b218796SLei YU { 41ab4cc6a5SGunnar Mills using InternalFailure = 42ab4cc6a5SGunnar Mills sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 437b218796SLei YU 447b218796SLei YU // Subscribe time change event 457b218796SLei YU // Choose the MAX time that is possible to avoid mis fires. 467b218796SLei YU constexpr itimerspec maxTime = { 477b218796SLei YU {0, 0}, // it_interval 487b218796SLei YU {TIME_T_MAX, 0}, // it_value 497b218796SLei YU }; 507b218796SLei YU 517b218796SLei YU timeFd = timerfd_create(CLOCK_REALTIME, 0); 527b218796SLei YU if (timeFd == -1) 537b218796SLei YU { 54947b5346SGeorge Liu lg2::error("Failed to create timerfd: {ERRNO}", "ERRNO", errno); 557b218796SLei YU elog<InternalFailure>(); 567b218796SLei YU } 577b218796SLei YU 58ab4cc6a5SGunnar Mills auto r = timerfd_settime( 59ab4cc6a5SGunnar Mills timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr); 607b218796SLei YU if (r != 0) 617b218796SLei YU { 62947b5346SGeorge Liu lg2::error("Failed to set timerfd: {ERRNO}", "ERRNO", errno); 637b218796SLei YU elog<InternalFailure>(); 647b218796SLei YU } 657b218796SLei YU 667b218796SLei YU sd_event_source* es; 67ab4cc6a5SGunnar Mills r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange, 68ab4cc6a5SGunnar Mills this); 697b218796SLei YU if (r < 0) 707b218796SLei YU { 71947b5346SGeorge Liu lg2::error("Failed to add event: {ERRNO}", "ERRNO", errno); 727b218796SLei YU elog<InternalFailure>(); 737b218796SLei YU } 747b218796SLei YU timeChangeEventSource.reset(es); 757b218796SLei YU } 767b218796SLei YU 777b218796SLei YU BmcEpoch::~BmcEpoch() 787b218796SLei YU { 797b218796SLei YU close(timeFd); 8096232827SLei YU } 8196232827SLei YU 8296232827SLei YU uint64_t BmcEpoch::elapsed() const 8396232827SLei YU { 8496232827SLei YU return getTime().count(); 8596232827SLei YU } 8696232827SLei YU 8796232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value) 8896232827SLei YU { 897b218796SLei YU /* 903c2f4496SGeorge Liu Mode | Set BMC Time 913c2f4496SGeorge Liu ----- | ------------- 923c2f4496SGeorge Liu NTP | Fail to set 933c2f4496SGeorge Liu MANUAL| OK 947b218796SLei YU */ 957b218796SLei YU auto time = microseconds(value); 963c2f4496SGeorge Liu setTime(time); 977b218796SLei YU 9896232827SLei YU server::EpochTime::elapsed(value); 9996232827SLei YU return value; 10096232827SLei YU } 10196232827SLei YU 1021e1dc447SRatan Gupta int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd, 1031e1dc447SRatan Gupta uint32_t /* revents */, void* /* userdata */) 1047b218796SLei YU { 1057b218796SLei YU std::array<char, 64> time{}; 1067b218796SLei YU 1077b218796SLei YU // We are not interested in the data here. 1087b218796SLei YU // So read until there is no new data here in the FD 109ab4cc6a5SGunnar Mills while (read(fd, time.data(), time.max_size()) > 0) 110ab4cc6a5SGunnar Mills ; 1117b218796SLei YU 1127b218796SLei YU return 0; 1137b218796SLei YU } 1147b218796SLei YU 115*f1d54aecSGeorge Liu void BmcEpoch::onModeChanged(Mode mode) 116*f1d54aecSGeorge Liu { 117*f1d54aecSGeorge Liu manager.setTimeMode(mode); 118*f1d54aecSGeorge Liu } 119*f1d54aecSGeorge Liu 120*f1d54aecSGeorge Liu bool BmcEpoch::setTime(const microseconds& usec) 121*f1d54aecSGeorge Liu { 122*f1d54aecSGeorge Liu auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH, 123*f1d54aecSGeorge Liu SYSTEMD_TIME_INTERFACE, METHOD_SET_TIME); 124*f1d54aecSGeorge Liu method.append(static_cast<int64_t>(usec.count()), 125*f1d54aecSGeorge Liu false, // relative 126*f1d54aecSGeorge Liu false); // user_interaction 127*f1d54aecSGeorge Liu 128*f1d54aecSGeorge Liu try 129*f1d54aecSGeorge Liu { 130*f1d54aecSGeorge Liu bus.call_noreply(method); 131*f1d54aecSGeorge Liu } 132*f1d54aecSGeorge Liu catch (const sdbusplus::exception_t& ex) 133*f1d54aecSGeorge Liu { 134*f1d54aecSGeorge Liu lg2::error("Error in setting system time: {ERROR}", "ERROR", ex); 135*f1d54aecSGeorge Liu using namespace xyz::openbmc_project::Time; 136*f1d54aecSGeorge Liu elog<FailedError>(Failed::REASON(ex.what())); 137*f1d54aecSGeorge Liu } 138*f1d54aecSGeorge Liu return true; 139*f1d54aecSGeorge Liu } 140*f1d54aecSGeorge Liu 141*f1d54aecSGeorge Liu microseconds BmcEpoch::getTime() const 142*f1d54aecSGeorge Liu { 143*f1d54aecSGeorge Liu auto now = system_clock::now(); 144*f1d54aecSGeorge Liu return duration_cast<microseconds>(now.time_since_epoch()); 145*f1d54aecSGeorge Liu } 146*f1d54aecSGeorge Liu 147af5abc57SLei YU } // namespace time 148af5abc57SLei YU } // namespace phosphor 149