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> 12f1d54aecSGeorge 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 { 27f1d54aecSGeorge Liu namespace // anonymous 28f1d54aecSGeorge Liu { 29f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1"; 30f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1"; 31f1d54aecSGeorge Liu constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1"; 32f1d54aecSGeorge Liu constexpr auto METHOD_SET_TIME = "SetTime"; 33f1d54aecSGeorge Liu } // namespace 34f1d54aecSGeorge Liu 35*dd42c7faSPavithra Barithaya PHOSPHOR_LOG2_USING; 36*dd42c7faSPavithra Barithaya 3796232827SLei YU namespace server = sdbusplus::xyz::openbmc_project::Time::server; 3896232827SLei YU using namespace phosphor::logging; 39f1d54aecSGeorge Liu using FailedError = sdbusplus::xyz::openbmc_project::Time::Error::Failed; 407b218796SLei YU 417b218796SLei YU void BmcEpoch::initialize() 427b218796SLei YU { 43ab4cc6a5SGunnar Mills using InternalFailure = 44ab4cc6a5SGunnar Mills sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 457b218796SLei YU 467b218796SLei YU // Subscribe time change event 477b218796SLei YU // Choose the MAX time that is possible to avoid mis fires. 487b218796SLei YU constexpr itimerspec maxTime = { 497b218796SLei YU {0, 0}, // it_interval 507b218796SLei YU {TIME_T_MAX, 0}, // it_value 517b218796SLei YU }; 527b218796SLei YU 537b218796SLei YU timeFd = timerfd_create(CLOCK_REALTIME, 0); 547b218796SLei YU if (timeFd == -1) 557b218796SLei YU { 56*dd42c7faSPavithra Barithaya error("Failed to create timerfd: {ERRNO}", "ERRNO", errno); 577b218796SLei YU elog<InternalFailure>(); 587b218796SLei YU } 597b218796SLei YU 60ab4cc6a5SGunnar Mills auto r = timerfd_settime( 61ab4cc6a5SGunnar Mills timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr); 627b218796SLei YU if (r != 0) 637b218796SLei YU { 64*dd42c7faSPavithra Barithaya error("Failed to set timerfd: {ERRNO}", "ERRNO", errno); 657b218796SLei YU elog<InternalFailure>(); 667b218796SLei YU } 677b218796SLei YU 687b218796SLei YU sd_event_source* es; 69ab4cc6a5SGunnar Mills r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange, 70ab4cc6a5SGunnar Mills this); 717b218796SLei YU if (r < 0) 727b218796SLei YU { 73*dd42c7faSPavithra Barithaya error("Failed to add event: {ERRNO}", "ERRNO", errno); 747b218796SLei YU elog<InternalFailure>(); 757b218796SLei YU } 767b218796SLei YU timeChangeEventSource.reset(es); 777b218796SLei YU } 787b218796SLei YU 797b218796SLei YU BmcEpoch::~BmcEpoch() 807b218796SLei YU { 817b218796SLei YU close(timeFd); 8296232827SLei YU } 8396232827SLei YU 8496232827SLei YU uint64_t BmcEpoch::elapsed() const 8596232827SLei YU { 8696232827SLei YU return getTime().count(); 8796232827SLei YU } 8896232827SLei YU 8996232827SLei YU uint64_t BmcEpoch::elapsed(uint64_t value) 9096232827SLei YU { 917b218796SLei YU /* 923c2f4496SGeorge Liu Mode | Set BMC Time 933c2f4496SGeorge Liu ----- | ------------- 943c2f4496SGeorge Liu NTP | Fail to set 953c2f4496SGeorge Liu MANUAL| OK 967b218796SLei YU */ 977b218796SLei YU auto time = microseconds(value); 983c2f4496SGeorge Liu setTime(time); 997b218796SLei YU 10096232827SLei YU server::EpochTime::elapsed(value); 10196232827SLei YU return value; 10296232827SLei YU } 10396232827SLei YU 1041e1dc447SRatan Gupta int BmcEpoch::onTimeChange(sd_event_source* /* es */, int fd, 1051e1dc447SRatan Gupta uint32_t /* revents */, void* /* userdata */) 1067b218796SLei YU { 1077b218796SLei YU std::array<char, 64> time{}; 1087b218796SLei YU 1097b218796SLei YU // We are not interested in the data here. 1107b218796SLei YU // So read until there is no new data here in the FD 111ab4cc6a5SGunnar Mills while (read(fd, time.data(), time.max_size()) > 0) 112ab4cc6a5SGunnar Mills ; 1137b218796SLei YU 1147b218796SLei YU return 0; 1157b218796SLei YU } 1167b218796SLei YU 117f1d54aecSGeorge Liu void BmcEpoch::onModeChanged(Mode mode) 118f1d54aecSGeorge Liu { 119f1d54aecSGeorge Liu manager.setTimeMode(mode); 120f1d54aecSGeorge Liu } 121f1d54aecSGeorge Liu 122f1d54aecSGeorge Liu bool BmcEpoch::setTime(const microseconds& usec) 123f1d54aecSGeorge Liu { 124f1d54aecSGeorge Liu auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH, 125f1d54aecSGeorge Liu SYSTEMD_TIME_INTERFACE, METHOD_SET_TIME); 126f1d54aecSGeorge Liu method.append(static_cast<int64_t>(usec.count()), 127f1d54aecSGeorge Liu false, // relative 128f1d54aecSGeorge Liu false); // user_interaction 129f1d54aecSGeorge Liu 130f1d54aecSGeorge Liu try 131f1d54aecSGeorge Liu { 132f1d54aecSGeorge Liu bus.call_noreply(method); 133f1d54aecSGeorge Liu } 134f1d54aecSGeorge Liu catch (const sdbusplus::exception_t& ex) 135f1d54aecSGeorge Liu { 136*dd42c7faSPavithra Barithaya error("Error in setting system time: {ERROR}", "ERROR", ex); 137f1d54aecSGeorge Liu using namespace xyz::openbmc_project::Time; 138f1d54aecSGeorge Liu elog<FailedError>(Failed::REASON(ex.what())); 139f1d54aecSGeorge Liu } 140f1d54aecSGeorge Liu return true; 141f1d54aecSGeorge Liu } 142f1d54aecSGeorge Liu 143f1d54aecSGeorge Liu microseconds BmcEpoch::getTime() const 144f1d54aecSGeorge Liu { 145f1d54aecSGeorge Liu auto now = system_clock::now(); 146f1d54aecSGeorge Liu return duration_cast<microseconds>(now.time_since_epoch()); 147f1d54aecSGeorge Liu } 148f1d54aecSGeorge Liu 149af5abc57SLei YU } // namespace time 150af5abc57SLei YU } // namespace phosphor 151