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 { 29864e173eSPavithra Barithaya constexpr auto systemdTimeService = "org.freedesktop.timedate1"; 30864e173eSPavithra Barithaya constexpr auto systemdTimePath = "/org/freedesktop/timedate1"; 31864e173eSPavithra Barithaya constexpr auto systemdTimeInterface = "org.freedesktop.timedate1"; 32864e173eSPavithra Barithaya constexpr auto methodSetTime = "SetTime"; 33f1d54aecSGeorge Liu } // namespace 34f1d54aecSGeorge Liu 35dd42c7faSPavithra Barithaya PHOSPHOR_LOG2_USING; 36dd42c7faSPavithra 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 { 56dd42c7faSPavithra 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 { 64dd42c7faSPavithra Barithaya error("Failed to set timerfd: {ERRNO}", "ERRNO", errno); 657b218796SLei YU elog<InternalFailure>(); 667b218796SLei YU } 677b218796SLei YU 68*8af2a1e3SPavithra Barithaya sd_event_source* es = nullptr; 69ab4cc6a5SGunnar Mills r = sd_event_add_io(bus.get_event(), &es, timeFd, EPOLLIN, onTimeChange, 70ab4cc6a5SGunnar Mills this); 717b218796SLei YU if (r < 0) 727b218796SLei YU { 73dd42c7faSPavithra 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) 112864e173eSPavithra Barithaya { 113ab4cc6a5SGunnar Mills ; 114864e173eSPavithra Barithaya } 1157b218796SLei YU 1167b218796SLei YU return 0; 1177b218796SLei YU } 1187b218796SLei YU 119f1d54aecSGeorge Liu void BmcEpoch::onModeChanged(Mode mode) 120f1d54aecSGeorge Liu { 121f1d54aecSGeorge Liu manager.setTimeMode(mode); 122f1d54aecSGeorge Liu } 123f1d54aecSGeorge Liu 124f1d54aecSGeorge Liu bool BmcEpoch::setTime(const microseconds& usec) 125f1d54aecSGeorge Liu { 126864e173eSPavithra Barithaya auto method = bus.new_method_call(systemdTimeService, systemdTimePath, 127864e173eSPavithra Barithaya systemdTimeInterface, methodSetTime); 128f1d54aecSGeorge Liu method.append(static_cast<int64_t>(usec.count()), 129f1d54aecSGeorge Liu false, // relative 130f1d54aecSGeorge Liu false); // user_interaction 131f1d54aecSGeorge Liu 132f1d54aecSGeorge Liu try 133f1d54aecSGeorge Liu { 134f1d54aecSGeorge Liu bus.call_noreply(method); 135f1d54aecSGeorge Liu } 136f1d54aecSGeorge Liu catch (const sdbusplus::exception_t& ex) 137f1d54aecSGeorge Liu { 138dd42c7faSPavithra Barithaya error("Error in setting system time: {ERROR}", "ERROR", ex); 139f1d54aecSGeorge Liu using namespace xyz::openbmc_project::Time; 140f1d54aecSGeorge Liu elog<FailedError>(Failed::REASON(ex.what())); 141f1d54aecSGeorge Liu } 142f1d54aecSGeorge Liu return true; 143f1d54aecSGeorge Liu } 144f1d54aecSGeorge Liu 145864e173eSPavithra Barithaya microseconds BmcEpoch::getTime() 146f1d54aecSGeorge Liu { 147f1d54aecSGeorge Liu auto now = system_clock::now(); 148f1d54aecSGeorge Liu return duration_cast<microseconds>(now.time_since_epoch()); 149f1d54aecSGeorge Liu } 150f1d54aecSGeorge Liu 151af5abc57SLei YU } // namespace time 152af5abc57SLei YU } // namespace phosphor 153