// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include #include #include #include #include #include #include "time_shared_memory.h" #include "time_manager_server.h" #include "timezone_manager.h" #include "results.h" namespace skyline::service::timesrv::core { /** * @brief A steady clock provides a monotonically increasing timepoint calibrated from a specific base */ class SteadyClockCore { private: bool rtcResetDetected{}; //!< True if the RTC this clock is based off has reset before this boot. bool initialized{}; //!< If this clock is calibrated with offsets etc and ready for use by applications protected: void SetRtcReset() { rtcResetDetected = true; } void MarkInitialized() { initialized = true; } public: virtual ~SteadyClockCore() = default; bool IsRtcResetDetected() { return rtcResetDetected; } bool IsClockInitialized() { return initialized; } /** * @brief Returns the current timepoint of the clock including offsets in a SteadyClockTimePoint struct with a source UUID */ ResultValue GetCurrentTimePoint(); /** * @brief Returns the current raw timepoint of the clock including offsets but without any UUID, this may have higher accuracy */ TimeSpanType GetCurrentRawTimePoint(); /** * @brief Returns the base timepoint of the clock without any offsets applied in a SteadyClockTimePoint struct with a source UUID */ virtual ResultValue GetTimePoint() = 0; /** * @brief Returns the current raw timepoint of the clock without any offsets applied without any UUID, this may have higher accuracy */ virtual TimeSpanType GetRawTimePoint(); /** * @brief A test offset is used to alter the base timepoint of the steady clock without it being visible to applications */ virtual TimeSpanType GetTestOffset() { return {}; } virtual void SetTestOffset(TimeSpanType offset) {} /** * @brief The internal offset is the offset between the raw steady clock time and the target time of this steady clock */ virtual TimeSpanType GetInternalOffset() { return {}; } virtual void SetInternalOffset(TimeSpanType offset) { return; } /** * @brief Returns the current value of the RTC that backs this clock */ virtual ResultValue GetRtcValue() { return result::Unimplemented; } virtual Result GetSetupResult() { return {}; } }; /** * @brief The standard steady clock is calibrated against system RTC time and is used as a base for all clocks aside from alarms and ephemeral */ class StandardSteadyClockCore : public SteadyClockCore { private: std::mutex mutex; //!< Protects accesses to cachedValue TimeSpanType testOffset{}; TimeSpanType internalOffset{}; TimeSpanType rtcOffset{}; //!< The offset between the RTC timepoint and the raw timepoints of this clock TimeSpanType cachedValue{}; //!< Stores the cached time value, used to prevent time ever decreasing UUID rtcId{}; //!< UUID of the RTC this is calibrated against public: void Setup(UUID rtcId, TimeSpanType pRtcOffset, TimeSpanType pInternalOffset, TimeSpanType pTestOffset, bool rtcResetDetected); void SetRtcOffset(TimeSpanType offset) { rtcOffset = offset; } ResultValue GetTimePoint() override; TimeSpanType GetRawTimePoint() override; TimeSpanType GetTestOffset() override { return testOffset; } void SetTestOffset(TimeSpanType offset) override { testOffset = offset; } TimeSpanType GetInternalOffset() override { return internalOffset; } void SetInternalOffset(TimeSpanType offset) override { internalOffset = offset; } }; /** * @brief The tick-based steady clock provides a monotonically increasing steady clock that is based off system boot */ class TickBasedSteadyClockCore : public SteadyClockCore { private: UUID id{UUID::GenerateUuidV4()}; public: ResultValue GetTimePoint() override; }; class SystemClockContextUpdateCallback; /** * @brief System clocks make use of the steady clock in order to provide an adjusted POSIX timepoint that is synchronised with the network or adapted to user time optionss */ class SystemClockCore { private: bool initialized{}; //!< True if the clock is safe to be used by applications and in a defined state SystemClockContextUpdateCallback *updateCallback; //!< Called when the context of the clock is updated protected: SteadyClockCore &steadyClock; //!< Clock that backs this system clock SystemClockContext context{}; //!< Holds the currently in-use context of the clock void MarkInitialized() { initialized = true; } public: SystemClockCore(SteadyClockCore &steadyClock) : steadyClock(steadyClock) {} virtual ~SystemClockCore() = default; void AddOperationEvent(const std::shared_ptr &event) { updateCallback->AddOperationEvent(event); } void SetUpdateCallback(SystemClockContextUpdateCallback *callback) { updateCallback = callback; } bool IsClockInitialized() { return initialized; } /** * @brief Checks if this system clock can produce a valid timepoint */ bool IsClockSetup(); /** * @brief Updates the clock to use the given context and calls the update callback */ Result UpdateClockContext(const SystemClockContext &newContext); /** * @brief Sets the current clock offsets as if posixTimePoint is the current time, this updates the clock comtext so will call the callback */ Result SetCurrentTime(PosixTime posixTimePoint); /** * @brief Returns the current POSIX time for this system clock */ ResultValue GetCurrentTime(); virtual ResultValue GetClockContext() { return context; } virtual Result SetClockContext(const SystemClockContext &newContext) { context = newContext; return {}; } }; /** * @brief The local system clock is a user configurable system clock based off of the system steady clock */ class StandardLocalSystemClockCore : public SystemClockCore { public: StandardLocalSystemClockCore(SteadyClockCore &steadyClock) : SystemClockCore(steadyClock) {} void Setup(const SystemClockContext &context, PosixTime posixTime); }; /** * @brief The network system clock is a network based system clock that is inconfigurable by the user in HOS */ class StandardNetworkSystemClockCore : public SystemClockCore { private: TimeSpanType sufficientAccuracy{TimeSpanType::FromDays(10)}; //!< Maxiumum drift between the current steady time and the timestamp of the context currently in use public: StandardNetworkSystemClockCore(SteadyClockCore &steadyClock) : SystemClockCore(steadyClock) {} void Setup(const SystemClockContext &context, TimeSpanType newSufficientAccuracy); /** * @return If the clock accuracy is less than sufficientAccuracy */ bool IsAccuracySufficient(); }; /** * @brief The standard user system clock provides an automatically corrected clock based on both local and network time, it is what should be used in most cases for time measurement */ class StandardUserSystemClockCore : public SystemClockCore { private: StandardLocalSystemClockCore &localSystemClock; //!< The StandardLocalSystemClockCore this clock uses for correction StandardNetworkSystemClockCore &networkSystemClock; //!< The StandardNetworkSystemClockCore this clock uses for correction bool automaticCorrectionEnabled{}; //!< If automatic correction with the network clock should be enabled SteadyClockTimePoint automaticCorrectionUpdatedTime; //!< When automatic correction was last enabled TimeSharedMemory &timeSharedMemory; //!< Shmem reference for automatic correction state updating /** * @brief Sets automatic correction state and resyncs with network clock on changes */ Result SetAutomaticCorrectionEnabled(bool enable); void SetAutomaticCorrectionUpdatedTime(const SteadyClockTimePoint &timePoint); public: std::shared_ptr automaticCorrectionUpdatedEvent; StandardUserSystemClockCore(const DeviceState &state, StandardSteadyClockCore &standardSteadyClock, StandardLocalSystemClockCore &localSystemClock, StandardNetworkSystemClockCore &networkSystemClock, TimeSharedMemory &timeSharedMemory) : SystemClockCore(standardSteadyClock), localSystemClock(localSystemClock), networkSystemClock(networkSystemClock), automaticCorrectionUpdatedEvent(std::make_shared(state, false)), timeSharedMemory(timeSharedMemory) {} void Setup(bool enableAutomaticCorrection, const SteadyClockTimePoint &automaticCorrectionUpdateTime); bool IsAutomaticCorrectionEnabled() { return automaticCorrectionEnabled; } SteadyClockTimePoint GetAutomaticCorrectionUpdatedTime() { return automaticCorrectionUpdatedTime; } /** * @brief Updates the automatic correction state in shared memory and this clock */ Result UpdateAutomaticCorrectionState(bool enable); ResultValue GetClockContext() override; /** * @brief Context is not directly settable here as it is derived from network and local clocks */ Result SetClockContext(const SystemClockContext &pContext) override { return result::Unimplemented; } }; /** * @brief The ephemeral network system clock provides a per-boot timepoint */ class EphemeralNetworkSystemClockCore : public SystemClockCore { public: EphemeralNetworkSystemClockCore(SteadyClockCore &steadyClock) : SystemClockCore(steadyClock) {} void Setup() { MarkInitialized(); } }; /** * @brief Stores the global state of timesrv and exposes a manager interface for use by IPC */ struct TimeServiceObject { TimeSharedMemory timeSharedMemory; LocalSystemClockUpdateCallback localSystemClockContextWriter; NetworkSystemClockUpdateCallback networkSystemClockContextWriter; EphemeralNetworkSystemClockUpdateCallback ephemeralNetworkSystemClockContextWriter; StandardSteadyClockCore standardSteadyClock; TickBasedSteadyClockCore tickBasedSteadyClock; StandardLocalSystemClockCore localSystemClock; StandardNetworkSystemClockCore networkSystemClock; StandardUserSystemClockCore userSystemClock; EphemeralNetworkSystemClockCore empheralSystemClock; TimeZoneManager timeZoneManager; std::vector locationNameList; //!< N stores in glue TimeManagerServer managerServer; /** * @brief Sets up all clocks with offsets based off of the current time */ TimeServiceObject(const DeviceState &state); }; }