mirror of
https://github.com/skyline-emu/skyline.git
synced 2025-01-09 00:30:41 +01:00
Implement the entirety of time services
This reimplements our time backend to be significantly more accurate to the real PSC and provides complete implementations for every time IPC allowing many newer games to work properly. Time is unique in its use of glue services, the core sysmodule is fully isolated and doesn't interface with any other services. Glue is instead used where that is needed (e.g. for fetching settings), this distinction is also present in our implementation. Another unique feature of time is its global state, as time is calibrated from the start of the service its state cannot be lost as that would result in the application offsetting time incorrectly whenever it closed a session. A large proportion of this is based off of Thog's 9.0.0 PSC reversing.
This commit is contained in:
parent
cb9f5f144e
commit
34cb0b49e8
@ -125,10 +125,15 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/services/hid/IHidServer.cpp
|
||||
${source_DIR}/skyline/services/hid/IAppletResource.cpp
|
||||
${source_DIR}/skyline/services/hid/IActiveVibrationDeviceList.cpp
|
||||
${source_DIR}/skyline/services/timesrv/common.cpp
|
||||
${source_DIR}/skyline/services/timesrv/core.cpp
|
||||
${source_DIR}/skyline/services/timesrv/time_shared_memory.cpp
|
||||
${source_DIR}/skyline/services/timesrv/time_manager_server.cpp
|
||||
${source_DIR}/skyline/services/timesrv/IStaticService.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ISystemClock.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ISteadyClock.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp
|
||||
${source_DIR}/skyline/services/glue/IStaticService.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFileSystem.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFile.cpp
|
||||
|
@ -126,6 +126,7 @@ namespace skyline {
|
||||
constexpr u16 DockedResolutionH{1080}; //!< The height component of the docked resolution
|
||||
// Time
|
||||
constexpr u64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second
|
||||
constexpr u64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day
|
||||
}
|
||||
|
||||
namespace util {
|
||||
|
95
app/src/main/cpp/skyline/services/glue/IStaticService.cpp
Normal file
95
app/src/main/cpp/skyline/services/glue/IStaticService.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/timesrv/IStaticService.h>
|
||||
#include <services/timesrv/results.h>
|
||||
#include "IStaticService.h"
|
||||
|
||||
namespace skyline::service::glue {
|
||||
IStaticService::IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::StaticServicePermissions permissions) : BaseService(state, manager), core(std::move(core)), permissions(permissions) {}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardUserSystemClock(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardNetworkSystemClock(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardSteadyClock(session, request, response);
|
||||
}
|
||||
Result IStaticService::GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
// STUFFF
|
||||
return core->GetTimeZoneService(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardLocalSystemClock(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetEphemeralNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetEphemeralNetworkSystemClock(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetSharedMemoryNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetSharedMemoryNativeHandle(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::SetStandardSteadyClockInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!permissions.writeSteadyClock)
|
||||
return timesrv::result::PermissionDenied;
|
||||
|
||||
// HOS would write the offset between the RTC and the epoch here, however as we emulate an RTC with no offset we can ignore this
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardSteadyClockRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
// std::time is effectively our RTC
|
||||
response.Push<timesrv::PosixTime>(std::time(nullptr));
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->IsStandardUserSystemClockAutomaticCorrectionEnabled(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->SetStandardUserSystemClockAutomaticCorrectionEnabled(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClockInitialYear(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
constexpr i32 standardUserSystemClockInitialYear{2019}; //!< https://switchbrew.org/wiki/System_Settings#time
|
||||
response.Push(standardUserSystemClockInitialYear);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::IsStandardNetworkSystemClockAccuracySufficient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->IsStandardNetworkSystemClockAccuracySufficient(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateMonotonicSystemClockBaseTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->CalculateMonotonicSystemClockBaseTimePoint(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetClockSnapshot(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetClockSnapshot(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetClockSnapshotFromSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetClockSnapshotFromSystemClockContext(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateStandardUserSystemClockDifferenceByUser(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->CalculateStandardUserSystemClockDifferenceByUser(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateSpanBetween(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->CalculateSpanBetween(session, request, response);
|
||||
}
|
||||
}
|
82
app/src/main/cpp/skyline/services/glue/IStaticService.h
Normal file
82
app/src/main/cpp/skyline/services/glue/IStaticService.h
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
#include <services/timesrv/IStaticService.h>
|
||||
|
||||
namespace skyline::service::glue {
|
||||
/**
|
||||
* @brief IStaticService (covers time:a, time:r, time:u) is glue's version of pcv::IStaticService, it adds some more functions and provides the user variant
|
||||
* @url https://switchbrew.org/wiki/PSC_services#time:su.2C_time:s
|
||||
*/
|
||||
class IStaticService : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<timesrv::IStaticService> core;
|
||||
timesrv::StaticServicePermissions permissions;
|
||||
|
||||
public:
|
||||
IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::StaticServicePermissions permissions);
|
||||
|
||||
Result GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetEphemeralNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetSharedMemoryNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetStandardSteadyClockInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardSteadyClockRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardUserSystemClockInitialYear(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsStandardNetworkSystemClockAccuracySufficient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result CalculateMonotonicSystemClockBaseTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetClockSnapshot(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetClockSnapshotFromSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result CalculateStandardUserSystemClockDifferenceByUser(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result CalculateSpanBetween(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IStaticService, GetStandardUserSystemClock),
|
||||
SFUNC(0x1, IStaticService, GetStandardNetworkSystemClock),
|
||||
SFUNC(0x2, IStaticService, GetStandardSteadyClock),
|
||||
SFUNC(0x3, IStaticService, GetTimeZoneService),
|
||||
SFUNC(0x4, IStaticService, GetStandardLocalSystemClock),
|
||||
SFUNC(0x5, IStaticService, GetEphemeralNetworkSystemClock),
|
||||
SFUNC(0x14, IStaticService, GetSharedMemoryNativeHandle),
|
||||
SFUNC(0x32, IStaticService, SetStandardSteadyClockInternalOffset),
|
||||
SFUNC(0x33, IStaticService, GetStandardSteadyClockRtcValue),
|
||||
SFUNC(0x64, IStaticService, IsStandardUserSystemClockAutomaticCorrectionEnabled),
|
||||
SFUNC(0x65, IStaticService, SetStandardUserSystemClockAutomaticCorrectionEnabled),
|
||||
SFUNC(0x66, IStaticService, GetStandardUserSystemClockInitialYear),
|
||||
SFUNC(0xC8, IStaticService, IsStandardNetworkSystemClockAccuracySufficient),
|
||||
SFUNC(0xC9, IStaticService, GetStandardUserSystemClockAutomaticCorrectionUpdatedTime),
|
||||
SFUNC(0x12C, IStaticService, CalculateMonotonicSystemClockBaseTimePoint),
|
||||
SFUNC(0x190, IStaticService, GetClockSnapshot),
|
||||
SFUNC(0x191, IStaticService, GetClockSnapshotFromSystemClockContext),
|
||||
SFUNC(0x1F4, IStaticService, CalculateStandardUserSystemClockDifferenceByUser),
|
||||
SFUNC(0x1F5, IStaticService, CalculateSpanBetween),
|
||||
)
|
||||
};
|
||||
}
|
@ -13,6 +13,8 @@
|
||||
#include "fatalsrv/IService.h"
|
||||
#include "hid/IHidServer.h"
|
||||
#include "timesrv/IStaticService.h"
|
||||
#include "glue/IStaticService.h"
|
||||
#include "services/timesrv/core.h"
|
||||
#include "fssrv/IFileSystemProxy.h"
|
||||
#include "services/nvdrv/INvDrvServices.h"
|
||||
#include "visrv/IManagerRootService.h"
|
||||
@ -29,15 +31,21 @@
|
||||
#include "prepo/IPrepoService.h"
|
||||
#include "serviceman.h"
|
||||
|
||||
#define SERVICE_CASE(class, name) \
|
||||
#define SERVICE_CASE(class, name, ...) \
|
||||
case util::MakeMagic<ServiceName>(name): { \
|
||||
std::shared_ptr<BaseService> serviceObject = std::make_shared<class>(state, *this); \
|
||||
std::shared_ptr<BaseService> serviceObject = std::make_shared<class>(state, *this __VA_OPT__(,) __VA_ARGS__); \
|
||||
serviceMap[util::MakeMagic<ServiceName>(name)] = serviceObject; \
|
||||
return serviceObject; \
|
||||
}
|
||||
|
||||
namespace skyline::service {
|
||||
ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared<sm::IUserInterface>(state, *this)) {}
|
||||
struct GlobalServiceState {
|
||||
timesrv::core::TimeServiceObject timesrv;
|
||||
|
||||
explicit GlobalServiceState(const DeviceState &state) : timesrv(state) {}
|
||||
};
|
||||
|
||||
ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared<sm::IUserInterface>(state, *this)), globalServiceState(std::make_shared<GlobalServiceState>(state)) {}
|
||||
|
||||
std::shared_ptr<BaseService> ServiceManager::CreateService(ServiceName name) {
|
||||
auto serviceIter{serviceMap.find(name)};
|
||||
@ -54,9 +62,11 @@ namespace skyline::service {
|
||||
SERVICE_CASE(audio::IAudioOutManager, "audout:u")
|
||||
SERVICE_CASE(audio::IAudioRendererManager, "audren:u")
|
||||
SERVICE_CASE(hid::IHidServer, "hid")
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:s")
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:a")
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:u")
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:s", globalServiceState->timesrv, timesrv::constant::StaticServiceSystemPermissions) // Both of these would be registered after TimeServiceManager::Setup normally but we call that in the GlobalServiceState constructor so can just list them here directly
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:su", globalServiceState->timesrv, timesrv::constant::StaticServiceSystemUpdatePermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:a", globalServiceState->timesrv.managerServer.GetAdminStaticService(state, *this), timesrv::constant::StaticServiceAdminPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:r", globalServiceState->timesrv.managerServer.GetRepairStaticService(state, *this), timesrv::constant::StaticServiceRepairPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:u", globalServiceState->timesrv.managerServer.GetUserStaticService(state, *this), timesrv::constant::StaticServiceUserPermissions)
|
||||
SERVICE_CASE(fssrv::IFileSystemProxy, "fsp-srv")
|
||||
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv")
|
||||
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a")
|
||||
|
@ -7,6 +7,11 @@
|
||||
#include "base_service.h"
|
||||
|
||||
namespace skyline::service {
|
||||
/**
|
||||
* @brief Holds global service state for service data that persists across sessions
|
||||
*/
|
||||
struct GlobalServiceState;
|
||||
|
||||
/**
|
||||
* @brief The ServiceManager class manages passing IPC requests to the right Service and running event loops of Services
|
||||
*/
|
||||
@ -25,6 +30,7 @@ namespace skyline::service {
|
||||
|
||||
public:
|
||||
std::shared_ptr<BaseService> smUserInterface; //!< Used by applications to open connections to services
|
||||
std::shared_ptr<GlobalServiceState> globalServiceState;
|
||||
|
||||
ServiceManager(const DeviceState &state);
|
||||
|
||||
|
@ -1,26 +1,29 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include "results.h"
|
||||
#include "core.h"
|
||||
#include "ISteadyClock.h"
|
||||
#include "ISystemClock.h"
|
||||
#include "ITimeZoneService.h"
|
||||
#include "ISystemClock.h"
|
||||
#include "IStaticService.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
IStaticService::IStaticService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
IStaticService::IStaticService(const DeviceState &state, ServiceManager &manager, core::TimeServiceObject &core, StaticServicePermissions permissions) : BaseService(state, manager), core(core), permissions(permissions) {}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(SystemClockType::User, state, manager), session, response);
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(state, manager, core.userSystemClock, permissions.writeUserSystemClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(SystemClockType::Network, state, manager), session, response);
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(state, manager, core.networkSystemClock, permissions.writeNetworkSystemClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISteadyClock>(state, manager), session, response);
|
||||
manager.RegisterService(std::make_shared<ISteadyClock>(state, manager, core.standardSteadyClock, permissions.writeSteadyClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -30,7 +33,187 @@ namespace skyline::service::timesrv {
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(SystemClockType::Local, state, manager), session, response);
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(state, manager, core.localSystemClock, permissions.writeLocalSystemClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetEphemeralNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(state, manager, core.networkSystemClock, permissions.writeNetworkSystemClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetSharedMemoryNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto sharedMemory{core.timeSharedMemory.GetSharedMemory()};
|
||||
auto handle{state.process->InsertItem<type::KSharedMemory>(sharedMemory)};
|
||||
response.copyHandles.push_back(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::SetStandardSteadyClockInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (permissions.writeSteadyClock)
|
||||
return result::Unimplemented;
|
||||
else
|
||||
return result::PermissionDenied;
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardSteadyClockRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result IStaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!core.userSystemClock.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push<u8>(core.userSystemClock.IsAutomaticCorrectionEnabled());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!core.userSystemClock.IsClockInitialized() || !core.standardSteadyClock.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
if (!permissions.writeUserSystemClock)
|
||||
return result::PermissionDenied;
|
||||
|
||||
return core.userSystemClock.UpdateAutomaticCorrectionState(request.Pop<u8>());
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClockInitialYear(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result IStaticService::IsStandardNetworkSystemClockAccuracySufficient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u8>(core.networkSystemClock.IsAccuracySufficient());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!core.userSystemClock.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push(core.userSystemClock.GetAutomaticCorrectionUpdatedTime());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateMonotonicSystemClockBaseTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!core.standardSteadyClock.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto timePoint{core.standardSteadyClock.GetCurrentTimePoint()};
|
||||
if (!timePoint)
|
||||
return timePoint;
|
||||
|
||||
auto clockContext{request.Pop<SystemClockContext>()};
|
||||
if (clockContext.timestamp.clockSourceId != timePoint->clockSourceId)
|
||||
return result::ClockSourceIdMismatch;
|
||||
|
||||
i64 baseTimePoint{timePoint->timePoint + clockContext.offset - TimeSpanType::FromNanoseconds(util::GetTimeNs()).Seconds()};
|
||||
response.Push(baseTimePoint);
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultValue<ClockSnapshot> IStaticService::GetClockSnapshotFromSystemClockContextImpl(const SystemClockContext &userContext, const SystemClockContext &networkContext, u8 unk) {
|
||||
ClockSnapshot out{};
|
||||
|
||||
out.userContext = userContext;
|
||||
out.networkContext = networkContext;
|
||||
auto timePoint{core.standardSteadyClock.GetCurrentTimePoint()};
|
||||
if (!timePoint)
|
||||
return timePoint;
|
||||
|
||||
out.steadyClockTimePoint = *timePoint;
|
||||
out.automaticCorrectionEnabled = core.userSystemClock.IsAutomaticCorrectionEnabled();
|
||||
// TODO GetDeviceLocationName
|
||||
auto userPosixTime{ClockSnapshot::GetCurrentTime(out.steadyClockTimePoint, out.userContext)};
|
||||
if (!userPosixTime)
|
||||
return userPosixTime;
|
||||
|
||||
out.userPosixTime = *userPosixTime;
|
||||
|
||||
// TODO CalendarTimeWithMyRule
|
||||
|
||||
// Not necessarily a fatal error if this fails
|
||||
auto networkPosixTime{ClockSnapshot::GetCurrentTime(out.steadyClockTimePoint, out.networkContext)};
|
||||
if (networkPosixTime)
|
||||
out.networkPosixTime = *networkPosixTime;
|
||||
else
|
||||
out.networkPosixTime = 0;
|
||||
|
||||
// TODO CalendarTimeWithMyRule
|
||||
|
||||
out._unk_ = unk;
|
||||
out.version = 0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Result IStaticService::GetClockSnapshot(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto unk{request.Pop<u8>()};
|
||||
|
||||
auto userContext{core.userSystemClock.GetClockContext()};
|
||||
if (!userContext)
|
||||
return userContext;
|
||||
|
||||
auto networkContext{core.networkSystemClock.GetClockContext()};
|
||||
if (!networkContext)
|
||||
return networkContext;
|
||||
|
||||
auto snapshot{GetClockSnapshotFromSystemClockContextImpl(*userContext, *networkContext, unk)};
|
||||
if (!snapshot)
|
||||
return snapshot;
|
||||
|
||||
request.outputBuf.at(0).as<ClockSnapshot>() = *snapshot;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetClockSnapshotFromSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto unk{request.Pop<u8>()};
|
||||
request.Skip<std::array<u8, 7>>();
|
||||
auto userContext{request.Pop<SystemClockContext>()};
|
||||
auto networkContext{request.Pop<SystemClockContext>()};
|
||||
|
||||
auto snapshot{GetClockSnapshotFromSystemClockContextImpl(userContext, networkContext, unk)};
|
||||
if (!snapshot)
|
||||
return snapshot;
|
||||
|
||||
request.outputBuf.at(0).as<ClockSnapshot>() = *snapshot;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateStandardUserSystemClockDifferenceByUser(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto snapshotA{request.inputBuf.at(0).as<ClockSnapshot>()};
|
||||
auto snapshotB{request.inputBuf.at(1).as<ClockSnapshot>()};
|
||||
|
||||
TimeSpanType difference{TimeSpanType::FromSeconds(snapshotB.userContext.offset - snapshotA.userContext.offset)};
|
||||
|
||||
if (snapshotA.userContext.timestamp.clockSourceId != snapshotB.userContext.timestamp.clockSourceId) {
|
||||
difference = 0;
|
||||
} else if (snapshotA.automaticCorrectionEnabled && snapshotB.automaticCorrectionEnabled) {
|
||||
if (snapshotA.networkContext.timestamp.clockSourceId != snapshotA.steadyClockTimePoint.clockSourceId || snapshotB.networkContext.timestamp.clockSourceId != snapshotB.steadyClockTimePoint.clockSourceId)
|
||||
difference = 0;
|
||||
}
|
||||
|
||||
response.Push<i64>(difference.Nanoseconds());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::CalculateSpanBetween(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto snapshotA{request.inputBuf.at(0).as<ClockSnapshot>()};
|
||||
auto snapshotB{request.inputBuf.at(1).as<ClockSnapshot>()};
|
||||
|
||||
auto difference{GetSpanBetween(snapshotA.steadyClockTimePoint, snapshotB.steadyClockTimePoint)};
|
||||
if (difference) {
|
||||
response.Push(TimeSpanType::FromSeconds(*difference).Nanoseconds());
|
||||
return difference;
|
||||
}
|
||||
|
||||
// If GetSpanBetween fails then fall back to comparing POSIX timepoints
|
||||
if (snapshotA.networkPosixTime && snapshotB.networkPosixTime) {
|
||||
response.Push(TimeSpanType::FromSeconds(snapshotB.networkPosixTime - snapshotA.networkPosixTime).Nanoseconds());
|
||||
return {};
|
||||
} else {
|
||||
return result::InvalidComparison;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,47 +4,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
#include <services/timesrv/common.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
/**
|
||||
* @brief IStaticService (covers time:u, time:a and time:s) is responsible for providing handles to various clock services
|
||||
* @brief Holds permissions for an instance of IStaticService
|
||||
*/
|
||||
struct StaticServicePermissions {
|
||||
bool writeLocalSystemClock;
|
||||
bool writeUserSystemClock;
|
||||
bool writeNetworkSystemClock;
|
||||
bool writeTimezone;
|
||||
bool writeSteadyClock;
|
||||
bool ignoreUninitializedChecks;
|
||||
};
|
||||
|
||||
namespace constant {
|
||||
constexpr StaticServicePermissions StaticServiceUserPermissions{};
|
||||
constexpr StaticServicePermissions StaticServiceAdminPermissions{
|
||||
.writeLocalSystemClock = true,
|
||||
.writeUserSystemClock = true,
|
||||
.writeTimezone = true,
|
||||
};
|
||||
constexpr StaticServicePermissions StaticServiceRepairPermissions{
|
||||
.writeSteadyClock = true,
|
||||
};
|
||||
constexpr StaticServicePermissions StaticServiceManagerPermissions{
|
||||
.writeLocalSystemClock = true,
|
||||
.writeUserSystemClock = true,
|
||||
.writeNetworkSystemClock = true,
|
||||
.writeTimezone = true,
|
||||
.writeSteadyClock = true,
|
||||
.ignoreUninitializedChecks = false,
|
||||
};
|
||||
constexpr StaticServicePermissions StaticServiceSystemPermissions{
|
||||
.writeNetworkSystemClock = true,
|
||||
};
|
||||
constexpr StaticServicePermissions StaticServiceSystemUpdatePermissions{
|
||||
.ignoreUninitializedChecks = true,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace core {
|
||||
struct TimeServiceObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief IStaticService (covers time:su, time:s) is responsible for providing the system access to various clocks
|
||||
* @url https://switchbrew.org/wiki/PSC_services#time:su.2C_time:s
|
||||
*/
|
||||
class IStaticService : public BaseService {
|
||||
public:
|
||||
IStaticService(const DeviceState &state, ServiceManager &manager);
|
||||
private:
|
||||
core::TimeServiceObject &core;
|
||||
StaticServicePermissions permissions; //!< What this instance is allowed to do
|
||||
|
||||
ResultValue<ClockSnapshot> GetClockSnapshotFromSystemClockContextImpl(const SystemClockContext &userContext, const SystemClockContext &networkContext, u8 unk);
|
||||
|
||||
public:
|
||||
IStaticService(const DeviceState &state, ServiceManager &manager, core::TimeServiceObject &core, StaticServicePermissions permissions);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a ISystemClock for user time
|
||||
*/
|
||||
Result GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a ISystemClock for network time
|
||||
*/
|
||||
Result GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a ISteadyClock for a steady timepoint
|
||||
*/
|
||||
Result GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a ITimeZoneService for reading time zone information
|
||||
*/
|
||||
Result GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a ISystemClock for local time
|
||||
*/
|
||||
Result GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetEphemeralNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetSharedMemoryNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetStandardSteadyClockInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardSteadyClockRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetStandardUserSystemClockAutomaticCorrectionEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardUserSystemClockInitialYear(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsStandardNetworkSystemClockAccuracySufficient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Generates an appropriate base timepoint from the supplied context
|
||||
*/
|
||||
Result CalculateMonotonicSystemClockBaseTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Generates a snapshot of all clocks in the system using the current contexts
|
||||
*/
|
||||
Result GetClockSnapshot(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Generates a shapshot of all clocks using the supplied contexts
|
||||
*/
|
||||
Result GetClockSnapshotFromSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Takes two snapshots and compares the user time between the them
|
||||
*/
|
||||
Result CalculateStandardUserSystemClockDifferenceByUser(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Calculates the timespan between the two given clock snapshots
|
||||
*/
|
||||
Result CalculateSpanBetween(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IStaticService, GetStandardUserSystemClock),
|
||||
SFUNC(0x1, IStaticService, GetStandardNetworkSystemClock),
|
||||
SFUNC(0x2, IStaticService, GetStandardSteadyClock),
|
||||
SFUNC(0x3, IStaticService, GetTimeZoneService),
|
||||
SFUNC(0x4, IStaticService, GetStandardLocalSystemClock)
|
||||
SFUNC(0x4, IStaticService, GetStandardLocalSystemClock),
|
||||
SFUNC(0x5, IStaticService, GetEphemeralNetworkSystemClock),
|
||||
SFUNC(0x14, IStaticService, GetSharedMemoryNativeHandle),
|
||||
SFUNC(0x32, IStaticService, SetStandardSteadyClockInternalOffset),
|
||||
SFUNC(0x33, IStaticService, GetStandardSteadyClockRtcValue),
|
||||
SFUNC(0x64, IStaticService, IsStandardUserSystemClockAutomaticCorrectionEnabled),
|
||||
SFUNC(0x65, IStaticService, SetStandardUserSystemClockAutomaticCorrectionEnabled),
|
||||
SFUNC(0x66, IStaticService, GetStandardUserSystemClockInitialYear),
|
||||
SFUNC(0xC8, IStaticService, IsStandardNetworkSystemClockAccuracySufficient),
|
||||
SFUNC(0xC9, IStaticService, GetStandardUserSystemClockAutomaticCorrectionUpdatedTime),
|
||||
SFUNC(0x12C, IStaticService, CalculateMonotonicSystemClockBaseTimePoint),
|
||||
SFUNC(0x190, IStaticService, GetClockSnapshot),
|
||||
SFUNC(0x191, IStaticService, GetClockSnapshotFromSystemClockContext),
|
||||
SFUNC(0x1F4, IStaticService, CalculateStandardUserSystemClockDifferenceByUser),
|
||||
SFUNC(0x1F5, IStaticService, CalculateSpanBetween),
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
@ -1,13 +1,73 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "core.h"
|
||||
#include "ISteadyClock.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
ISteadyClock::ISteadyClock(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
ISteadyClock::ISteadyClock(const DeviceState &state, ServiceManager &manager, core::SteadyClockCore &core, bool writeable, bool ignoreUninitializedChecks) : BaseService(state, manager), core(core), writeable(writeable), ignoreUninitializedChecks(ignoreUninitializedChecks) {}
|
||||
|
||||
Result ISteadyClock::GetCurrentTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push(SteadyClockTimePoint{static_cast<u64>(std::time(nullptr))});
|
||||
// When a clock is uninitialized it still ticks however the offsets aren't configured
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto timePoint{core.GetCurrentTimePoint()};
|
||||
if (timePoint)
|
||||
response.Push(*timePoint);
|
||||
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
Result ISteadyClock::GetTestOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push(core.GetTestOffset());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ISteadyClock::SetTestOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto testOffset{request.Pop<TimeSpanType>()};
|
||||
core.SetTestOffset(testOffset);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ISteadyClock::GetRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto rtcValue{core.GetRtcValue()};
|
||||
if (rtcValue)
|
||||
response.Push(*rtcValue);
|
||||
|
||||
return rtcValue;
|
||||
}
|
||||
|
||||
Result ISteadyClock::IsRtcResetDetected(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push<u8>(core.IsRtcResetDetected());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ISteadyClock::GetSetupResultValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push(core.GetSetupResult());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ISteadyClock::GetInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
response.Push(core.GetInternalOffset());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@ -6,29 +6,45 @@
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/PSC_services#SteadyClockTimePoint
|
||||
*/
|
||||
struct SteadyClockTimePoint {
|
||||
u64 timepoint; //!< The point in time of this timepoint
|
||||
u8 id[0x10]; //!< The ID of the source clock
|
||||
};
|
||||
namespace core {
|
||||
class SteadyClockCore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ISteadyClock is used to retrieve a steady time that increments uniformly for the lifetime on an application
|
||||
* @brief ISteadyClock is used to interface with timesrv steady clocks
|
||||
* @url https://switchbrew.org/wiki/PSC_services#ISteadyClock
|
||||
*/
|
||||
class ISteadyClock : public BaseService {
|
||||
public:
|
||||
ISteadyClock(const DeviceState &state, ServiceManager &manager);
|
||||
private:
|
||||
core::SteadyClockCore &core;
|
||||
bool writeable;
|
||||
bool ignoreUninitializedChecks;
|
||||
|
||||
public:
|
||||
ISteadyClock(const DeviceState &state, ServiceManager &manager, core::SteadyClockCore &core, bool writeable, bool ignoreUninitializedChecks);
|
||||
|
||||
/**
|
||||
* @brief Returns the current value of the steady clock
|
||||
*/
|
||||
Result GetCurrentTimePoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTestOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetTestOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetRtcValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsRtcResetDetected(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetSetupResultValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetInternalOffset(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, ISteadyClock, GetCurrentTimePoint)
|
||||
SFUNC(0x0, ISteadyClock, GetCurrentTimePoint),
|
||||
SFUNC(0x2, ISteadyClock, GetTestOffset),
|
||||
SFUNC(0x3, ISteadyClock, SetTestOffset),
|
||||
SFUNC(0x64, ISteadyClock, GetRtcValue),
|
||||
SFUNC(0x65, ISteadyClock, IsRtcResetDetected),
|
||||
SFUNC(0x66, ISteadyClock, GetSetupResultValue),
|
||||
SFUNC(0xC8, ISteadyClock, GetInternalOffset),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -1,20 +1,66 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "ISteadyClock.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include "results.h"
|
||||
#include "core.h"
|
||||
#include "ISystemClock.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
ISystemClock::ISystemClock(const SystemClockType clockType, const DeviceState &state, ServiceManager &manager) : type(clockType), BaseService(state, manager) {}
|
||||
ISystemClock::ISystemClock(const DeviceState &state, ServiceManager &manager, core::SystemClockCore &core, bool writeClock, bool ignoreUninitializedChecks) : BaseService(state, manager), core(core), writeClock(writeClock), ignoreUninitializedChecks(ignoreUninitializedChecks) {}
|
||||
|
||||
Result ISystemClock::GetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u64>(static_cast<u64>(std::time(nullptr)));
|
||||
return {};
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto posixTime{core.GetCurrentTime()};
|
||||
if (posixTime)
|
||||
response.Push(*posixTime);
|
||||
|
||||
return posixTime;
|
||||
}
|
||||
|
||||
Result ISystemClock::SetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!writeClock)
|
||||
return result::PermissionDenied;
|
||||
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return core.SetCurrentTime(request.Pop<PosixTime>());
|
||||
}
|
||||
|
||||
Result ISystemClock::GetSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u64>(static_cast<u64>(std::time(nullptr)));
|
||||
response.Push(SteadyClockTimePoint{static_cast<u64>(std::time(nullptr))});
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
auto context{core.GetClockContext()};
|
||||
if (context)
|
||||
response.Push(*context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
Result ISystemClock::SetSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!writeClock)
|
||||
return result::PermissionDenied;
|
||||
|
||||
if (!ignoreUninitializedChecks && !core.IsClockInitialized())
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return core.SetClockContext(request.Pop<SystemClockContext>());
|
||||
}
|
||||
|
||||
Result ISystemClock::GetOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!operationEvent) {
|
||||
operationEvent = std::make_shared<kernel::type::KEvent>(state, false);
|
||||
core.AddOperationEvent(operationEvent);
|
||||
}
|
||||
|
||||
auto handle{state.process->InsertItem(operationEvent)};
|
||||
state.logger->Debug("ISystemClock Operation Event Handle: 0x{:X}", handle);
|
||||
response.copyHandles.push_back(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,41 +3,45 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
/**
|
||||
* @brief The type of a #SystemClockType
|
||||
*/
|
||||
enum class SystemClockType {
|
||||
User, //!< Use time provided by user
|
||||
Network, //!< Use network time
|
||||
Local, //!< Use local time
|
||||
};
|
||||
namespace core {
|
||||
class SystemClockCore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ISystemClock is used to retrieve and set time
|
||||
* @brief ISystemClock is used to interface with timesrv system clocks
|
||||
* @url https://switchbrew.org/wiki/PSC_services#ISystemClock
|
||||
*/
|
||||
class ISystemClock : public BaseService {
|
||||
private:
|
||||
core::SystemClockCore &core;
|
||||
bool writeClock;
|
||||
bool ignoreUninitializedChecks;
|
||||
|
||||
std::shared_ptr<kernel::type::KEvent> operationEvent{};
|
||||
|
||||
public:
|
||||
const SystemClockType type;
|
||||
ISystemClock(const DeviceState &state, ServiceManager &manager, core::SystemClockCore &core, bool writeClock, bool ignoreUninitializedChecks);
|
||||
|
||||
ISystemClock(const SystemClockType clockType, const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
/**
|
||||
* @brief Returns the amount of seconds since epoch
|
||||
*/
|
||||
Result GetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns the system clock context
|
||||
*/
|
||||
Result SetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetSystemClockContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, ISystemClock, GetCurrentTime),
|
||||
SFUNC(0x2, ISystemClock, GetSystemClockContext)
|
||||
SFUNC(0x1, ISystemClock, SetCurrentTime),
|
||||
SFUNC(0x2, ISystemClock, GetSystemClockContext),
|
||||
SFUNC(0x3, ISystemClock, SetSystemClockContext),
|
||||
SFUNC(0x4, ISystemClock, GetOperationEventReadableHandle),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "common.h"
|
||||
#include "ITimeZoneService.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto &time{request.Pop<u64>()};
|
||||
auto calender{*std::gmtime(reinterpret_cast<const time_t *>(&time))};
|
||||
auto time{static_cast<time_t>(request.Pop<PosixTime>())};
|
||||
auto calender{*std::gmtime(&time)};
|
||||
|
||||
CalendarTime calendarTime{
|
||||
.year = static_cast<u16>(calender.tm_year),
|
||||
@ -20,11 +21,11 @@ namespace skyline::service::timesrv {
|
||||
response.Push(calendarTime);
|
||||
|
||||
CalendarAdditionalInfo calendarInfo{
|
||||
.dayWeek = static_cast<u32>(calender.tm_wday),
|
||||
.dayMonth = static_cast<u32>(calender.tm_mday),
|
||||
.tzName = *reinterpret_cast<const u64 *>(calender.tm_zone),
|
||||
.dst = static_cast<i32>(calender.tm_isdst),
|
||||
.utcRel = static_cast<u32>(calender.tm_gmtoff),
|
||||
.dayOfWeek = static_cast<u32>(calender.tm_wday),
|
||||
.dayOfYear = static_cast<u32>(calender.tm_yday),
|
||||
.timezoneName = "GMT",
|
||||
.dst = static_cast<u32>(calender.tm_isdst),
|
||||
.gmtOffset = static_cast<i32>(calender.tm_gmtoff),
|
||||
};
|
||||
response.Push(calendarInfo);
|
||||
return {};
|
||||
|
@ -11,33 +11,6 @@ namespace skyline::service::timesrv {
|
||||
* @url https://switchbrew.org/wiki/PSC_services#ITimeZoneService
|
||||
*/
|
||||
class ITimeZoneService : public BaseService {
|
||||
private:
|
||||
/**
|
||||
* @brief A particular time point in Nintendo's calendar format
|
||||
*/
|
||||
struct CalendarTime {
|
||||
u16 year; //!< Amount of years that have passed since 1900
|
||||
u8 month; //!< Month of the year (1-12) [POSIX time uses 0-11]
|
||||
u8 day; //!< Day of the month (1-31)
|
||||
u8 hour; //!< Hour of the day (0-23)
|
||||
u8 minute; //!< Minute of the hour (0-59)
|
||||
u8 second; //!< Second of the minute (0-60)
|
||||
u8 _pad_;
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8);
|
||||
|
||||
/**
|
||||
* @brief Additional metadata about the time alongside CalendarTime
|
||||
*/
|
||||
struct CalendarAdditionalInfo {
|
||||
u32 dayWeek; //!< Amount of days since Sunday
|
||||
u32 dayMonth; //!< Amount of days since the start of the month
|
||||
u64 tzName; //!< The name of the time zone
|
||||
i32 dst; //!< If DST is in effect or not
|
||||
u32 utcRel; //!< Offset of the time from GMT in seconds
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18);
|
||||
|
||||
public:
|
||||
ITimeZoneService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
|
24
app/src/main/cpp/skyline/services/timesrv/common.cpp
Normal file
24
app/src/main/cpp/skyline/services/timesrv/common.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <common.h>
|
||||
#include "results.h"
|
||||
#include "common.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
ResultValue<i64> ClockSnapshot::GetCurrentTime(const SteadyClockTimePoint &timePoint, const SystemClockContext &context) {
|
||||
if (context.timestamp.clockSourceId != timePoint.clockSourceId)
|
||||
return result::ClockSourceIdMismatch;
|
||||
|
||||
return context.offset + timePoint.timePoint;
|
||||
}
|
||||
|
||||
ResultValue<i64> GetSpanBetween(const SteadyClockTimePoint &start, const SteadyClockTimePoint &end) {
|
||||
// We can't compare between different clocks as they don't necessarily operate from the same origin
|
||||
if (start.clockSourceId != end.clockSourceId)
|
||||
return result::InvalidComparison;
|
||||
|
||||
if (((start.timePoint > 0) && (end.timePoint < std::numeric_limits<i64>::min() + start.timePoint)) ||
|
||||
((start.timePoint < 0) && (end.timePoint > std::numeric_limits<i64>::max() + start.timePoint)))
|
||||
return result::CompareOverflow;
|
||||
|
||||
return end.timePoint - start.timePoint;
|
||||
}
|
||||
}
|
135
app/src/main/cpp/skyline/services/timesrv/common.h
Normal file
135
app/src/main/cpp/skyline/services/timesrv/common.h
Normal file
@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <common/uuid.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
using PosixTime = i64; //!< Unit for time in seconds since the epoch
|
||||
|
||||
/**
|
||||
* @brief Stores a quantity of time with nanosecond accuracy and provides helper functions to convert it to other units
|
||||
*/
|
||||
class TimeSpanType {
|
||||
private:
|
||||
i64 ns{}; //!< Timepoint of the timespan in nanoseconds
|
||||
|
||||
public:
|
||||
constexpr TimeSpanType() {}
|
||||
|
||||
constexpr TimeSpanType(i64 ns) : ns(ns) {}
|
||||
|
||||
static constexpr TimeSpanType FromNanoseconds(i64 ns) {
|
||||
return {ns};
|
||||
}
|
||||
|
||||
static constexpr TimeSpanType FromSeconds(i64 s) {
|
||||
return {s * static_cast<i64>(skyline::constant::NsInSecond)};
|
||||
}
|
||||
|
||||
static constexpr TimeSpanType FromDays(i64 d) {
|
||||
return {d * static_cast<i64>(skyline::constant::NsInDay)};
|
||||
}
|
||||
|
||||
constexpr i64 Nanoseconds() const {
|
||||
return ns;
|
||||
}
|
||||
|
||||
constexpr i64 Seconds() const {
|
||||
return ns / static_cast<i64>(skyline::constant::NsInSecond);
|
||||
}
|
||||
|
||||
constexpr friend bool operator>(const TimeSpanType &lhs, const TimeSpanType &rhs) {
|
||||
return lhs.ns > rhs.ns;
|
||||
}
|
||||
|
||||
constexpr friend bool operator<(const TimeSpanType &lhs, const TimeSpanType &rhs) {
|
||||
return lhs.ns < rhs.ns;
|
||||
}
|
||||
|
||||
constexpr friend TimeSpanType operator+(const TimeSpanType &lhs, const TimeSpanType &rhs) {
|
||||
return FromNanoseconds(lhs.ns + rhs.ns);
|
||||
}
|
||||
|
||||
constexpr friend TimeSpanType operator-(const TimeSpanType &lhs, const TimeSpanType &rhs) {
|
||||
return FromNanoseconds(lhs.ns - rhs.ns);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Holds details about a point in time sourced from a steady clock (e.g. RTC)
|
||||
*/
|
||||
struct __attribute__((packed)) SteadyClockTimePoint {
|
||||
i64 timePoint; //!< Time in seconds
|
||||
UUID clockSourceId; //!< The UUID of the steady clock this timepoint comes from
|
||||
|
||||
auto operator<=>(const SteadyClockTimePoint &) const = default;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18);
|
||||
|
||||
/**
|
||||
* @brief Describes a system clocks offset from its associated steady clock
|
||||
*/
|
||||
struct __attribute__((packed)) SystemClockContext {
|
||||
i64 offset; // Offset between the steady timepoint and the epoch
|
||||
SteadyClockTimePoint timestamp; //!< The steady timepoint this context was calibrated from
|
||||
|
||||
auto operator<=>(const SystemClockContext &) const = default;
|
||||
};
|
||||
static_assert(sizeof(SystemClockContext) == 0x20);
|
||||
|
||||
/**
|
||||
* @brief A particular time point in Nintendo's calendar format
|
||||
*/
|
||||
struct CalendarTime {
|
||||
u16 year; //!< The current year minus 1900
|
||||
u8 month; //!< 1-12 (POSIX time uses 0-11)
|
||||
u8 day; //!< 1-31
|
||||
u8 hour; //!< 0-23
|
||||
u8 minute; //!< 0-59
|
||||
u8 second; //!< 0-60
|
||||
u8 _pad_;
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8);
|
||||
|
||||
/**
|
||||
* @brief Additional metadata about the time alongside CalendarTime
|
||||
*/
|
||||
struct CalendarAdditionalInfo {
|
||||
u32 dayOfWeek; //!< 0-6
|
||||
u32 dayOfYear; //!< 0-365
|
||||
std::array<u8, 8> timezoneName;
|
||||
u32 dst; //!< If DST is in effect or not
|
||||
i32 gmtOffset; //!< Offset of the time from GMT in seconds
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18);
|
||||
|
||||
/**
|
||||
* @brief A snapshot of all clocks in the system
|
||||
*/
|
||||
struct ClockSnapshot {
|
||||
SystemClockContext userContext;
|
||||
SystemClockContext networkContext;
|
||||
PosixTime userPosixTime;
|
||||
PosixTime networkPosixTime;
|
||||
CalendarTime userCalendarTime;
|
||||
CalendarTime networkCalendarTime;
|
||||
CalendarAdditionalInfo userCalendarAdditionalInfo;
|
||||
CalendarAdditionalInfo networkCalendarAdditionalInfo;
|
||||
SteadyClockTimePoint steadyClockTimePoint;
|
||||
std::array<u8, 36> locationName;
|
||||
u8 automaticCorrectionEnabled;
|
||||
u8 _unk_;
|
||||
u16 version;
|
||||
|
||||
/**
|
||||
* @brief Gets the current time based off of the supplied timepoint and context
|
||||
*/
|
||||
static ResultValue<PosixTime> GetCurrentTime(const SteadyClockTimePoint &timePoint, const SystemClockContext &context);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xD0);
|
||||
|
||||
/**
|
||||
* @brief Gets the time between a pair of steady clock timepoints
|
||||
*/
|
||||
ResultValue<i64> GetSpanBetween(const SteadyClockTimePoint &start, const SteadyClockTimePoint &end);
|
||||
}
|
260
app/src/main/cpp/skyline/services/timesrv/core.cpp
Normal file
260
app/src/main/cpp/skyline/services/timesrv/core.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "core.h"
|
||||
#include "time_manager_server.h"
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
TimeSpanType SteadyClockCore::GetRawTimePoint() {
|
||||
auto timePoint{GetTimePoint()};
|
||||
|
||||
if (timePoint)
|
||||
return TimeSpanType::FromSeconds(timePoint->timePoint);
|
||||
else
|
||||
throw exception("Error reading timepoint");
|
||||
}
|
||||
|
||||
ResultValue<SteadyClockTimePoint> SteadyClockCore::GetCurrentTimePoint() {
|
||||
auto timePoint{GetTimePoint()};
|
||||
if (timePoint)
|
||||
timePoint->timePoint += (GetTestOffset() + GetInternalOffset()).Seconds();
|
||||
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
TimeSpanType SteadyClockCore::GetCurrentRawTimePoint() {
|
||||
return GetRawTimePoint() + GetTestOffset() + GetInternalOffset();
|
||||
}
|
||||
|
||||
void StandardSteadyClockCore::Setup(UUID pRtcId, TimeSpanType pRtcOffset, TimeSpanType pInternalOffset, TimeSpanType pTestOffset, bool rtcResetDetected) {
|
||||
rtcId = pRtcId;
|
||||
rtcOffset = pRtcOffset;
|
||||
internalOffset = pInternalOffset;
|
||||
testOffset = pTestOffset;
|
||||
|
||||
if (rtcResetDetected)
|
||||
SetRtcReset();
|
||||
|
||||
MarkInitialized();
|
||||
}
|
||||
|
||||
ResultValue<SteadyClockTimePoint> StandardSteadyClockCore::GetTimePoint() {
|
||||
SteadyClockTimePoint timePoint{
|
||||
.timePoint = GetRawTimePoint().Seconds(),
|
||||
.clockSourceId = rtcId,
|
||||
};
|
||||
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
TimeSpanType StandardSteadyClockCore::GetRawTimePoint() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
auto timePoint{TimeSpanType::FromNanoseconds(util::GetTimeNs()) + rtcOffset};
|
||||
|
||||
if (timePoint > cachedValue)
|
||||
cachedValue = timePoint;
|
||||
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
ResultValue<SteadyClockTimePoint> TickBasedSteadyClockCore::GetTimePoint() {
|
||||
SteadyClockTimePoint timePoint{
|
||||
.timePoint = TimeSpanType::FromNanoseconds(util::GetTimeNs()).Seconds(),
|
||||
.clockSourceId = id,
|
||||
};
|
||||
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
bool SystemClockCore::IsClockSetup() {
|
||||
if (GetClockContext()) {
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
if (timePoint)
|
||||
return timePoint->clockSourceId.Valid();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Result SystemClockCore::UpdateClockContext(const SystemClockContext &newContext) {
|
||||
auto ret{SetClockContext(newContext)};
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
// Writes new state to shared memory etc
|
||||
if (updateCallback) {
|
||||
return updateCallback->UpdateContext(newContext);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Result SystemClockCore::SetCurrentTime(PosixTime posixTimePoint) {
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
if (!timePoint)
|
||||
return timePoint;
|
||||
|
||||
// Set new context with an offset relative to the given POSIX time
|
||||
SystemClockContext newContext{
|
||||
.timestamp = *timePoint,
|
||||
.offset = posixTimePoint - timePoint->timePoint,
|
||||
};
|
||||
|
||||
UpdateClockContext(newContext);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultValue<PosixTime> SystemClockCore::GetCurrentTime() {
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
if (!timePoint)
|
||||
return timePoint;
|
||||
|
||||
auto clockContext{GetClockContext()};
|
||||
if (!clockContext)
|
||||
return clockContext;
|
||||
|
||||
if (clockContext->timestamp.clockSourceId != timePoint->clockSourceId)
|
||||
return result::ClockSourceIdMismatch;
|
||||
|
||||
return clockContext->offset + timePoint->timePoint;
|
||||
}
|
||||
|
||||
void StandardLocalSystemClockCore::Setup(const SystemClockContext &context, PosixTime posixTime) {
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
|
||||
Result ret{};
|
||||
|
||||
// If the new context comes from the same clock as what we currently have we don't need to set any offset as they share the same base
|
||||
if (timePoint && timePoint->clockSourceId == context.timestamp.clockSourceId)
|
||||
ret = UpdateClockContext(context);
|
||||
else
|
||||
ret = SetCurrentTime(posixTime);
|
||||
|
||||
if (ret)
|
||||
throw exception("Failed to setup StandardLocalSystemClockCore");
|
||||
|
||||
MarkInitialized();
|
||||
}
|
||||
|
||||
void StandardNetworkSystemClockCore::Setup(const SystemClockContext &context, TimeSpanType newSufficientAccuracy) {
|
||||
if (UpdateClockContext(context))
|
||||
throw exception("Failed to set up StandardNetworkSystemClockCore");
|
||||
|
||||
sufficientAccuracy = newSufficientAccuracy;
|
||||
MarkInitialized();
|
||||
}
|
||||
|
||||
bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
|
||||
if (!IsClockInitialized())
|
||||
return false;
|
||||
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
if (!timePoint)
|
||||
return false;
|
||||
|
||||
auto spanBetween{GetSpanBetween(context.timestamp, *timePoint)};
|
||||
return spanBetween && *spanBetween < sufficientAccuracy.Seconds();
|
||||
}
|
||||
|
||||
Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(bool enable) {
|
||||
// Resync with network clock before any state transitions
|
||||
if (enable != automaticCorrectionEnabled && networkSystemClock.IsClockSetup()) {
|
||||
auto ctx{networkSystemClock.GetClockContext()};
|
||||
if (!ctx)
|
||||
return ctx;
|
||||
|
||||
auto ret{localSystemClock.SetClockContext(*ctx)};
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
automaticCorrectionEnabled = enable;
|
||||
return {};
|
||||
}
|
||||
|
||||
void StandardUserSystemClockCore::SetAutomaticCorrectionUpdatedTime(const SteadyClockTimePoint &timePoint) {
|
||||
automaticCorrectionUpdatedTime = timePoint;
|
||||
automaticCorrectionUpdatedEvent->Signal();
|
||||
}
|
||||
|
||||
Result StandardUserSystemClockCore::UpdateAutomaticCorrectionState(bool enable) {
|
||||
auto ret{SetAutomaticCorrectionEnabled(enable)};
|
||||
if (!ret) {
|
||||
timeSharedMemory.SetStandardUserSystemClockAutomaticCorrectionEnabled(enable);
|
||||
|
||||
auto timePoint{steadyClock.GetCurrentTimePoint()};
|
||||
if (timePoint)
|
||||
SetAutomaticCorrectionUpdatedTime(*timePoint);
|
||||
else
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void StandardUserSystemClockCore::Setup(bool enableAutomaticCorrection, const SteadyClockTimePoint &automaticCorrectionUpdateTime) {
|
||||
if (SetAutomaticCorrectionEnabled(enableAutomaticCorrection))
|
||||
throw exception("Failed to set up SetupStandardUserSystemClock: failed to set automatic correction state!");
|
||||
|
||||
SetAutomaticCorrectionUpdatedTime(automaticCorrectionUpdateTime);
|
||||
|
||||
MarkInitialized();
|
||||
|
||||
timeSharedMemory.SetStandardUserSystemClockAutomaticCorrectionEnabled(enableAutomaticCorrection);
|
||||
}
|
||||
|
||||
ResultValue<SystemClockContext> StandardUserSystemClockCore::GetClockContext() {
|
||||
if (automaticCorrectionEnabled && networkSystemClock.IsClockSetup()) {
|
||||
auto ctx{networkSystemClock.GetClockContext()};
|
||||
if (!ctx)
|
||||
return ctx;
|
||||
|
||||
auto ret{localSystemClock.SetClockContext(*ctx)};
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return localSystemClock.GetClockContext();
|
||||
}
|
||||
|
||||
TimeServiceObject::TimeServiceObject(const DeviceState &state) : timeSharedMemory(state), localSystemClockContextWriter(timeSharedMemory), networkSystemClockContextWriter(timeSharedMemory), localSystemClock(standardSteadyClock), networkSystemClock(standardSteadyClock), userSystemClock(state, standardSteadyClock, localSystemClock, networkSystemClock, timeSharedMemory), empheralSystemClock(tickBasedSteadyClock), managerServer(*this) {
|
||||
|
||||
// Setup time service:
|
||||
// A new rtc UUID is generated every time glue inits time
|
||||
auto rtcId{UUID::GenerateUuidV4()};
|
||||
auto rtcOffset{TimeSpanType::FromSeconds(std::time(nullptr)) - TimeSpanType::FromNanoseconds(util::GetTimeNs())};
|
||||
|
||||
// On the switch the RTC may not always start from the epoch so it is compensated with the internal offset.
|
||||
// We however emulate RTC to start from the epoch so we can set it to zero, if we wanted to add an option for a system time offset we would change this.
|
||||
TimeSpanType internalOffset{};
|
||||
|
||||
// Setup the standard steady clock from which everything in the system counts
|
||||
managerServer.SetupStandardSteadyClock(rtcId, rtcOffset, internalOffset, {}, false);
|
||||
|
||||
SystemClockContext localSystemClockContext{
|
||||
.timestamp {
|
||||
.timePoint = 0,
|
||||
.clockSourceId = rtcId,
|
||||
},
|
||||
.offset = 0 //!< Zero offset as the RTC is calibrated already
|
||||
};
|
||||
// Don't supply a POSIX time as the offset will be taken from the above context instead.
|
||||
// Normally the POSIX time would be the initial year for the clock to reset to if the context got wiped.
|
||||
managerServer.SetupStandardLocalSystemClock(localSystemClockContext, 0);
|
||||
|
||||
// Use the context just created in local clock for the network clock, HOS gets this from settings
|
||||
auto context{localSystemClock.GetClockContext()};
|
||||
if (!context)
|
||||
throw exception("Failed to get local system clock context!");
|
||||
|
||||
constexpr TimeSpanType sufficientAccuracy{TimeSpanType::FromDays(30)}; //!< https://switchbrew.org/wiki/System_Settings#time
|
||||
|
||||
managerServer.SetupStandardNetworkSystemClock(*context, sufficientAccuracy);
|
||||
|
||||
// Initialise the user system clock with automatic correction disabled as we don't emulate the automatic correction thread
|
||||
managerServer.SetupStandardUserSystemClock(false, SteadyClockTimePoint{.clockSourceId = UUID::GenerateUuidV4()});
|
||||
managerServer.SetupEphemeralSystemClock();
|
||||
}
|
||||
}
|
318
app/src/main/cpp/skyline/services/timesrv/core.h
Normal file
318
app/src/main/cpp/skyline/services/timesrv/core.h
Normal file
@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <common/uuid.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <kernel/types/KSharedMemory.h>
|
||||
#include <services/timesrv/common.h>
|
||||
#include "time_shared_memory.h"
|
||||
#include "time_manager_server.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 {
|
||||
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:
|
||||
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<SteadyClockTimePoint> 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<SteadyClockTimePoint> 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<PosixTime> 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 {
|
||||
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<SteadyClockTimePoint> 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<SteadyClockTimePoint> 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) {}
|
||||
|
||||
void AddOperationEvent(const std::shared_ptr<kernel::type::KEvent> &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<PosixTime> GetCurrentTime();
|
||||
|
||||
virtual ResultValue<SystemClockContext> 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);
|
||||
|
||||
/**
|
||||
* @brief Checks 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<kernel::type::KEvent> automaticCorrectionUpdatedEvent;
|
||||
|
||||
StandardUserSystemClockCore(const DeviceState &state, StandardSteadyClockCore &standardSteadyClock, StandardLocalSystemClockCore &localSystemClock, StandardNetworkSystemClockCore &networkSystemClock, TimeSharedMemory &timeSharedMemory) : SystemClockCore(standardSteadyClock), localSystemClock(localSystemClock), networkSystemClock(networkSystemClock), automaticCorrectionUpdatedEvent(std::make_shared<kernel::type::KEvent>(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<SystemClockContext> 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 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;
|
||||
|
||||
TimeManagerServer managerServer;
|
||||
|
||||
/**
|
||||
* @brief Sets up all clocks with offsets based off of the current time
|
||||
*/
|
||||
TimeServiceObject(const DeviceState &state);
|
||||
};
|
||||
}
|
15
app/src/main/cpp/skyline/services/timesrv/results.h
Normal file
15
app/src/main/cpp/skyline/services/timesrv/results.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline::service::timesrv::result {
|
||||
constexpr Result PermissionDenied(116, 1);
|
||||
constexpr Result ClockSourceIdMismatch(116, 102);
|
||||
constexpr Result ClockUninitialized(116, 103);
|
||||
constexpr Result InvalidComparison(116, 200);
|
||||
constexpr Result CompareOverflow(116, 201);
|
||||
constexpr Result Unimplemented(116, 990);
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <common.h>
|
||||
#include <services/serviceman.h>
|
||||
#include "core.h"
|
||||
#include "time_manager_server.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
TimeManagerServer::TimeManagerServer(core::TimeServiceObject &core) : core(core) {}
|
||||
|
||||
std::shared_ptr<IStaticService> TimeManagerServer::GetUserStaticService(const DeviceState &state, ServiceManager &manager) {
|
||||
return std::make_shared<IStaticService>(state, manager, core, constant::StaticServiceUserPermissions);
|
||||
}
|
||||
|
||||
std::shared_ptr<IStaticService> TimeManagerServer::GetAdminStaticService(const DeviceState &state, ServiceManager &manager) {
|
||||
return std::make_shared<IStaticService>(state, manager, core, constant::StaticServiceAdminPermissions);
|
||||
}
|
||||
|
||||
std::shared_ptr<IStaticService> TimeManagerServer::GetRepairStaticService(const DeviceState &state, ServiceManager &manager) {
|
||||
return std::make_shared<IStaticService>(state, manager, core, constant::StaticServiceRepairPermissions);
|
||||
}
|
||||
|
||||
std::shared_ptr<IStaticService> TimeManagerServer::GetManagerStaticService(const DeviceState &state, ServiceManager &manager) {
|
||||
return std::make_shared<IStaticService>(state, manager, core, constant::StaticServiceManagerPermissions);
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupStandardSteadyClock(UUID rtcId, TimeSpanType rtcOffset, TimeSpanType internalOffset, TimeSpanType testOffset, bool rtcResetDetected) {
|
||||
core.standardSteadyClock.Setup(rtcId, rtcOffset, internalOffset, testOffset, rtcResetDetected);
|
||||
|
||||
auto timePoint{core.standardSteadyClock.GetCurrentRawTimePoint()};
|
||||
core.timeSharedMemory.SetupStandardSteadyClock(rtcId, timePoint);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupStandardLocalSystemClock(const SystemClockContext &context, PosixTime posixTime) {
|
||||
core.localSystemClock.SetUpdateCallback(&core.localSystemClockContextWriter);
|
||||
core.localSystemClock.Setup(context, posixTime);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupStandardNetworkSystemClock(const SystemClockContext &context, TimeSpanType sufficientAccuracy) {
|
||||
core.networkSystemClock.SetUpdateCallback(&core.networkSystemClockContextWriter);
|
||||
core.networkSystemClock.Setup(context, sufficientAccuracy);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupStandardUserSystemClock(bool enableAutomaticCorrection, const SteadyClockTimePoint &automaticCorrectionUpdateTime) {
|
||||
core.userSystemClock.Setup(enableAutomaticCorrection, automaticCorrectionUpdateTime);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupEphemeralSystemClock() {
|
||||
core.empheralSystemClock.SetUpdateCallback(&core.ephemeralNetworkSystemClockContextWriter);
|
||||
core.empheralSystemClock.Setup();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<kernel::type::KEvent> TimeManagerServer::GetStandardUserSystemClockAutomaticCorrectionEvent() {
|
||||
return core.userSystemClock.automaticCorrectionUpdatedEvent;
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetStandardSteadyClockRtcOffset(TimeSpanType rtcOffset) {
|
||||
core.standardSteadyClock.SetRtcOffset(rtcOffset);
|
||||
core.timeSharedMemory.SetSteadyClockRawTimePoint(core.standardSteadyClock.GetCurrentRawTimePoint());
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <common/uuid.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include "common.h"
|
||||
#include "IStaticService.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
namespace core {
|
||||
struct TimeServiceObject;
|
||||
}
|
||||
|
||||
class TimeManagerServer {
|
||||
private:
|
||||
core::TimeServiceObject &core;
|
||||
|
||||
public:
|
||||
TimeManagerServer(core::TimeServiceObject &core);
|
||||
|
||||
std::shared_ptr<IStaticService> GetUserStaticService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
std::shared_ptr<IStaticService> GetAdminStaticService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
std::shared_ptr<IStaticService> GetRepairStaticService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
std::shared_ptr<IStaticService> GetManagerStaticService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result SetupStandardSteadyClock(UUID rtcId, TimeSpanType rtcOffset, TimeSpanType internalOffset, TimeSpanType testOffset, bool rtcResetDetected);
|
||||
|
||||
Result SetupStandardLocalSystemClock(const SystemClockContext &context, PosixTime posixTime);
|
||||
|
||||
Result SetupStandardNetworkSystemClock(const SystemClockContext &context, TimeSpanType sufficientAccuracy);
|
||||
|
||||
Result SetupStandardUserSystemClock(bool enableAutomaticCorrection, const SteadyClockTimePoint &automaticCorrectionUpdateTime);
|
||||
|
||||
Result SetupEphemeralSystemClock();
|
||||
|
||||
std::shared_ptr<kernel::type::KEvent> GetStandardUserSystemClockAutomaticCorrectionEvent();
|
||||
|
||||
Result SetStandardSteadyClockRtcOffset(TimeSpanType rtcOffset);
|
||||
|
||||
/*
|
||||
Result SetupTimeZoneManager
|
||||
*/
|
||||
|
||||
|
||||
};
|
||||
}
|
146
app/src/main/cpp/skyline/services/timesrv/time_shared_memory.cpp
Normal file
146
app/src/main/cpp/skyline/services/timesrv/time_shared_memory.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "core.h"
|
||||
#include "time_manager_server.h"
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
struct __attribute__((packed)) TimeSharedMemoryLayout {
|
||||
template<typename T>
|
||||
struct ClockContextEntry {
|
||||
u32 updateCount;
|
||||
u32 _pad_;
|
||||
std::array<T, 2> context;
|
||||
};
|
||||
|
||||
ClockContextEntry<SteadyClockTimePoint> standardSteadyClockContextEntry;
|
||||
ClockContextEntry<SystemClockContext> localSystemClockContextEntry;
|
||||
ClockContextEntry<SystemClockContext> networkSystemClockContextEntry;
|
||||
struct __attribute__((packed)) {
|
||||
u32 updateCount;
|
||||
std::array<u8, 2> enabled;
|
||||
} standardUserSystemClockAutomaticCorrectionEnabledEntry;
|
||||
};
|
||||
static_assert(offsetof(TimeSharedMemoryLayout, localSystemClockContextEntry) == 0x38);
|
||||
static_assert(offsetof(TimeSharedMemoryLayout, networkSystemClockContextEntry) == 0x80);
|
||||
static_assert(offsetof(TimeSharedMemoryLayout, standardUserSystemClockAutomaticCorrectionEnabledEntry) == 0xC8);
|
||||
|
||||
/**
|
||||
* @brief Time Shared Memory uses a double buffered format that alternates writes context data, this is a helper to simplify that
|
||||
*/
|
||||
template<typename T>
|
||||
static void UpdateTimeSharedMemoryItem(u32 &updateCount, std::array<T, 2> &item, const T &newValue) {
|
||||
u32 newCount{updateCount + 1};
|
||||
item[newCount & 1] = newValue;
|
||||
asm volatile("DMB ISHST"); // 0xA
|
||||
updateCount = newCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for Time Shared Memory to settle then returns the latest version of the requested value
|
||||
*/
|
||||
template<typename T>
|
||||
static T ReadTimeSharedMemoryItem(u32 &updateCount, std::array<T, 2> &item) {
|
||||
u32 checkUpdateCount{};
|
||||
T out{};
|
||||
|
||||
do {
|
||||
checkUpdateCount = updateCount;
|
||||
out = item[updateCount & 1];
|
||||
asm volatile("DMB ISHLD"); // 0x9
|
||||
} while (checkUpdateCount != updateCount);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace constant {
|
||||
constexpr size_t TimeSharedMemorySize{0x1000}; //!< The size of the time shared memory region
|
||||
}
|
||||
|
||||
TimeSharedMemory::TimeSharedMemory(const DeviceState &state) : kTimeSharedMemory(std::make_shared<kernel::type::KSharedMemory>(state, constant::TimeSharedMemorySize)), timeSharedMemory(reinterpret_cast<TimeSharedMemoryLayout *>(kTimeSharedMemory->kernel.ptr)) {}
|
||||
|
||||
void TimeSharedMemory::SetupStandardSteadyClock(UUID rtcId, TimeSpanType baseTimePoint) {
|
||||
SteadyClockTimePoint context{
|
||||
.timePoint = baseTimePoint.Nanoseconds() - static_cast<i64>(util::GetTimeNs()),
|
||||
.clockSourceId = rtcId
|
||||
};
|
||||
|
||||
UpdateTimeSharedMemoryItem(timeSharedMemory->standardSteadyClockContextEntry.updateCount, timeSharedMemory->standardSteadyClockContextEntry.context, context);
|
||||
}
|
||||
|
||||
void TimeSharedMemory::SetSteadyClockRawTimePoint(TimeSpanType timePoint) {
|
||||
auto context{ReadTimeSharedMemoryItem(timeSharedMemory->standardSteadyClockContextEntry.updateCount, timeSharedMemory->standardSteadyClockContextEntry.context)};
|
||||
context.timePoint = timePoint.Nanoseconds() - static_cast<i64>(util::GetTimeNs());
|
||||
|
||||
UpdateTimeSharedMemoryItem(timeSharedMemory->standardSteadyClockContextEntry.updateCount, timeSharedMemory->standardSteadyClockContextEntry.context, context);
|
||||
}
|
||||
|
||||
void TimeSharedMemory::UpdateLocalSystemClockContext(const SystemClockContext &context) {
|
||||
UpdateTimeSharedMemoryItem(timeSharedMemory->localSystemClockContextEntry.updateCount, timeSharedMemory->localSystemClockContextEntry.context, context);
|
||||
}
|
||||
|
||||
void TimeSharedMemory::UpdateNetworkSystemClockContext(const SystemClockContext &context) {
|
||||
UpdateTimeSharedMemoryItem(timeSharedMemory->networkSystemClockContextEntry.updateCount, timeSharedMemory->networkSystemClockContextEntry.context, context);
|
||||
}
|
||||
|
||||
void TimeSharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
|
||||
UpdateTimeSharedMemoryItem(timeSharedMemory->standardUserSystemClockAutomaticCorrectionEnabledEntry.updateCount, timeSharedMemory->standardUserSystemClockAutomaticCorrectionEnabledEntry.enabled, static_cast<u8>(enabled));
|
||||
}
|
||||
|
||||
bool SystemClockContextUpdateCallback::UpdateBaseContext(const SystemClockContext &newContext) {
|
||||
if (context && context == newContext)
|
||||
return false;
|
||||
|
||||
context = newContext;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::SignalOperationEvent() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
for (const auto &event : operationEventList)
|
||||
event->Signal();
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::AddOperationEvent(const std::shared_ptr<kernel::type::KEvent> &event) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
operationEventList.push_back(event);
|
||||
}
|
||||
|
||||
LocalSystemClockUpdateCallback::LocalSystemClockUpdateCallback(TimeSharedMemory &timeSharedMemory) : timeSharedMemory(timeSharedMemory) {}
|
||||
|
||||
Result LocalSystemClockUpdateCallback::UpdateContext(const SystemClockContext &newContext) {
|
||||
// No need to update shmem state redundantly
|
||||
if (!UpdateBaseContext(newContext))
|
||||
return {};
|
||||
|
||||
timeSharedMemory.UpdateLocalSystemClockContext(newContext);
|
||||
|
||||
SignalOperationEvent();
|
||||
return {};
|
||||
}
|
||||
|
||||
NetworkSystemClockUpdateCallback::NetworkSystemClockUpdateCallback(TimeSharedMemory &timeSharedMemory) : timeSharedMemory(timeSharedMemory) {}
|
||||
|
||||
Result NetworkSystemClockUpdateCallback::UpdateContext(const SystemClockContext &newContext) {
|
||||
// No need to update shmem state redundantly
|
||||
if (!UpdateBaseContext(newContext))
|
||||
return {};
|
||||
|
||||
timeSharedMemory.UpdateNetworkSystemClockContext(newContext);
|
||||
|
||||
SignalOperationEvent();
|
||||
return {};
|
||||
}
|
||||
|
||||
Result EphemeralNetworkSystemClockUpdateCallback::UpdateContext(const SystemClockContext &newContext) {
|
||||
// Avoid signalling the event when there is no change in context
|
||||
if (!UpdateBaseContext(newContext))
|
||||
return {};
|
||||
|
||||
SignalOperationEvent();
|
||||
return {};
|
||||
}
|
||||
}
|
110
app/src/main/cpp/skyline/services/timesrv/time_shared_memory.h
Normal file
110
app/src/main/cpp/skyline/services/timesrv/time_shared_memory.h
Normal file
@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <common/uuid.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <kernel/types/KSharedMemory.h>
|
||||
#include <services/timesrv/common.h>
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
struct TimeSharedMemoryLayout;
|
||||
|
||||
/**
|
||||
* @brief TimeSharedMemory holds context data about clocks in a double buffered format
|
||||
*/
|
||||
class TimeSharedMemory {
|
||||
private:
|
||||
std::shared_ptr<kernel::type::KSharedMemory> kTimeSharedMemory;
|
||||
TimeSharedMemoryLayout *timeSharedMemory;
|
||||
|
||||
public:
|
||||
TimeSharedMemory(const DeviceState &state);
|
||||
|
||||
std::shared_ptr<kernel::type::KSharedMemory> GetSharedMemory() {
|
||||
return kTimeSharedMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fills in the steady clock section of shmem, the current time is subtracted from baseTimePoint to workout the offset
|
||||
*/
|
||||
void SetupStandardSteadyClock(UUID rtcId, TimeSpanType baseTimePoint);
|
||||
|
||||
void SetSteadyClockRawTimePoint(TimeSpanType timePoint);
|
||||
|
||||
void UpdateLocalSystemClockContext(const SystemClockContext &context);
|
||||
|
||||
void UpdateNetworkSystemClockContext(const SystemClockContext &context);
|
||||
|
||||
void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base class for callbacks that run after a system clock context is updated
|
||||
*/
|
||||
class SystemClockContextUpdateCallback {
|
||||
private:
|
||||
std::list<std::shared_ptr<kernel::type::KEvent>> operationEventList; //!< List of KEvents to be signalled when this callback is called
|
||||
std::mutex mutex; //!< Protects access to operationEventList
|
||||
std::optional<SystemClockContext> context; //!< The context that used when this callback was last called
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Updates the base callback context with the one supplied as an argument
|
||||
* @return true if the context was updated
|
||||
*/
|
||||
bool UpdateBaseContext(const SystemClockContext &newContext);
|
||||
|
||||
/**
|
||||
* @brief Signals all events in the operation event list
|
||||
*/
|
||||
void SignalOperationEvent();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Adds an operation event to be siignalled on context updates
|
||||
*/
|
||||
void AddOperationEvent(const std::shared_ptr<kernel::type::KEvent> &event);
|
||||
|
||||
/**
|
||||
* @brief Repllaces the current context with the supplied one and signals events if the context differs from the last used one
|
||||
*/
|
||||
virtual Result UpdateContext(const SystemClockContext &newContext) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update callback for the local system clock, handles writing data to shmem
|
||||
*/
|
||||
class LocalSystemClockUpdateCallback : public SystemClockContextUpdateCallback {
|
||||
private:
|
||||
TimeSharedMemory &timeSharedMemory;
|
||||
|
||||
public:
|
||||
LocalSystemClockUpdateCallback(TimeSharedMemory &timeSharedMemory);
|
||||
|
||||
Result UpdateContext(const SystemClockContext &newContext) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update callback for the network system clock, handles writing data to shmem
|
||||
*/
|
||||
class NetworkSystemClockUpdateCallback : public SystemClockContextUpdateCallback {
|
||||
private:
|
||||
TimeSharedMemory &timeSharedMemory;
|
||||
|
||||
public:
|
||||
NetworkSystemClockUpdateCallback(TimeSharedMemory &timeSharedMemory);
|
||||
|
||||
Result UpdateContext(const SystemClockContext &newContext) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update callback for the ephemeral network system clock, only handles signalling the event as there is no shmem entry for ephemeral
|
||||
*/
|
||||
class EphemeralNetworkSystemClockUpdateCallback : public SystemClockContextUpdateCallback {
|
||||
public:
|
||||
Result UpdateContext(const SystemClockContext &newContext) override;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user