From e44809355c578c5335c750ffbcb956a526c2887a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Thu, 17 Oct 2019 01:53:35 +0530 Subject: [PATCH] libNX Time Initalization What was added: * Time service * CNTPCT_EL0 redirected to CNTVCT_EL0 --- app/CMakeLists.txt | 1 + app/src/main/cpp/skyline/common.h | 19 ++- .../cpp/skyline/kernel/services/am/appletOE.h | 2 +- .../skyline/kernel/services/base_service.h | 7 + .../cpp/skyline/kernel/services/hid/hid.cpp | 3 +- .../skyline/kernel/services/serviceman.cpp | 124 ++++++++++-------- .../cpp/skyline/kernel/services/serviceman.h | 11 +- .../cpp/skyline/kernel/services/time/time.cpp | 59 +++++++++ .../skyline/kernel/services/time/timesrv.h | 97 ++++++++++++++ .../main/cpp/skyline/kernel/types/KSession.h | 3 +- app/src/main/cpp/skyline/loader/nro.cpp | 11 +- 11 files changed, 272 insertions(+), 65 deletions(-) create mode 100644 app/src/main/cpp/skyline/kernel/services/time/time.cpp create mode 100644 app/src/main/cpp/skyline/kernel/services/time/timesrv.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 6090aa24..82e4e43f 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/kernel/services/apm/apm.cpp ${source_DIR}/skyline/kernel/services/am/appletOE.cpp ${source_DIR}/skyline/kernel/services/hid/hid.cpp + ${source_DIR}/skyline/kernel/services/time/time.cpp ) target_link_libraries(skyline fmt tinyxml2) target_compile_options(skyline PRIVATE -Wno-c++17-extensions) diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 4480a10b..5a044242 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -49,6 +49,8 @@ namespace skyline { constexpr u16 SvcLast = 0x7F; //!< The index of the last SVC constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS + constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS + constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS // Kernel constexpr u64 MaxSyncHandles = 0x40; //!< The total amount of handles that can be passed to WaitSynchronization constexpr handle_t BaseHandleIndex = 0xD000; // The index of the base handle @@ -82,13 +84,13 @@ namespace skyline { */ struct Brk { /** - * Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes + * @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes * @param val The immediate value of the instruction */ Brk(u16 val) { - start = 0x0; // First 5 bits of an BRK instruction are 0 + start = 0x0; // First 5 bits of a BRK instruction are 0 value = val; - end = 0x6A1; // Last 11 bits of an BRK instruction stored as u16 + end = 0x6A1; // Last 11 bits of a BRK instruction stored as u16 } /** @@ -129,6 +131,17 @@ namespace skyline { * @brief A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register. */ struct Mrs { + /** + * @brief Creates a MRS instruction, used for generating BRK opcodes + * @param srcReg The source system register + * @param dstReg The destination Xn register + */ + Mrs(u32 srcReg, u8 dstReg) { + this->srcReg = srcReg; + this->dstReg = dstReg; + end = 0xD53; // Last 12 bits of a MRS instruction stored as u16 + } + /** * @brief Returns if the opcode is valid or not * @return If the opcode represents a valid MRS instruction diff --git a/app/src/main/cpp/skyline/kernel/services/am/appletOE.h b/app/src/main/cpp/skyline/kernel/services/am/appletOE.h index 3d229cd5..4a03b8f5 100644 --- a/app/src/main/cpp/skyline/kernel/services/am/appletOE.h +++ b/app/src/main/cpp/skyline/kernel/services/am/appletOE.h @@ -180,7 +180,7 @@ namespace skyline::kernel::service::am { IApplicationFunctions(const DeviceState &state, ServiceManager &manager); /** - * @brief This just returns 1 regardless of input (https://switchbrew.org/wiki/Applet_Manager_services#NotifyRunning) + * @brief This just returns a boolean true (https://switchbrew.org/wiki/Applet_Manager_services#NotifyRunning) */ void NotifyRunning(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); }; diff --git a/app/src/main/cpp/skyline/kernel/services/base_service.h b/app/src/main/cpp/skyline/kernel/services/base_service.h index 7d072acf..f3b7b007 100644 --- a/app/src/main/cpp/skyline/kernel/services/base_service.h +++ b/app/src/main/cpp/skyline/kernel/services/base_service.h @@ -31,6 +31,9 @@ namespace skyline::kernel::service { am_IDebugFunctions, hid, hid_IAppletResource, + time, + time_ISystemClock, + time_ITimeZoneService, }; /** @@ -54,6 +57,10 @@ namespace skyline::kernel::service { {"am:IDebugFunctions", Service::am_IDebugFunctions}, {"hid", Service::hid}, {"hid:IAppletResource", Service::hid_IAppletResource}, + {"time:s", Service::time}, + {"time:a", Service::time}, + {"time:ISystemClock", Service::time_ISystemClock}, + {"time:ITimeZoneService", Service::time_ITimeZoneService}, }; class ServiceManager; diff --git a/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp b/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp index 65745642..c975af24 100644 --- a/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp +++ b/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp @@ -23,7 +23,8 @@ namespace skyline::kernel::service::hid { }) {} void hid::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - resource = std::static_pointer_cast(manager.NewService(Service::hid_IAppletResource, session, response)); + resource = std::make_shared(state, manager); + manager.RegisterService(resource, session, response); } void hid::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { diff --git a/app/src/main/cpp/skyline/kernel/services/serviceman.cpp b/app/src/main/cpp/skyline/kernel/services/serviceman.cpp index 2c8b3783..e31f5835 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.cpp @@ -6,6 +6,7 @@ #include "am/appletOE.h" #include "fatal/fatal.h" #include "hid/hid.h" +#include "time/timesrv.h" namespace skyline::kernel::service { ServiceManager::ServiceManager(const DeviceState &state) : state(state) {} @@ -13,64 +14,69 @@ namespace skyline::kernel::service { std::shared_ptr ServiceManager::GetService(const Service serviceType) { std::shared_ptr serviceObj; switch (serviceType) { - case Service::sm: - serviceObj = std::make_shared(state, *this); - break; - case Service::fatal_u: - serviceObj = std::make_shared(state, *this); - break; - case Service::set_sys: - serviceObj = std::make_shared(state, *this); - break; - case Service::apm: - serviceObj = std::make_shared(state, *this); - break; - case Service::apm_ISession: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_appletOE: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IApplicationProxy: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_ICommonStateGetter: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IWindowController: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IAudioController: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IDisplayController: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_ISelfController: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_ILibraryAppletCreator: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IApplicationFunctions: - serviceObj = std::make_shared(state, *this); - break; - case Service::am_IDebugFunctions: - serviceObj = std::make_shared(state, *this); - break; - case Service::hid: - serviceObj = std::make_shared(state, *this); - break; - case Service::hid_IAppletResource: - serviceObj = std::make_shared(state, *this); - break; + case Service::sm: + serviceObj = std::make_shared(state, *this); + break; + case Service::fatal_u: + serviceObj = std::make_shared(state, *this); + break; + case Service::set_sys: + serviceObj = std::make_shared(state, *this); + break; + case Service::apm: + serviceObj = std::make_shared(state, *this); + break; + case Service::apm_ISession: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_appletOE: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IApplicationProxy: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_ICommonStateGetter: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IWindowController: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IAudioController: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IDisplayController: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_ISelfController: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_ILibraryAppletCreator: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IApplicationFunctions: + serviceObj = std::make_shared(state, *this); + break; + case Service::am_IDebugFunctions: + serviceObj = std::make_shared(state, *this); + break; + case Service::hid: + serviceObj = std::make_shared(state, *this); + break; + case Service::hid_IAppletResource: + serviceObj = std::make_shared(state, *this); + break; + case Service::time: + serviceObj = std::make_shared(state, *this); + break; + default: + throw exception("GetService called on missing object"); } serviceVec.push_back(serviceObj); return serviceObj; } handle_t ServiceManager::NewSession(const Service serviceType) { - return state.thisProcess->NewHandle(GetService(serviceType), serviceType).handle; + return state.thisProcess->NewHandle(GetService(serviceType)).handle; } std::shared_ptr ServiceManager::NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response) { @@ -79,11 +85,21 @@ namespace skyline::kernel::service { session.domainTable[++session.handleIndex] = serviceObject; response.domainObjects.push_back(session.handleIndex); } else - response.moveHandles.push_back(state.thisProcess->NewHandle(serviceObject, serviceType).handle); - state.logger->Write(Logger::Debug, "Service has been registered: \"{}\"", serviceObject->getName()); + response.moveHandles.push_back(state.thisProcess->NewHandle(serviceObject).handle); + state.logger->Write(Logger::Debug, "Service has been created: \"{}\"", serviceObject->getName()); return serviceObject; } + void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response) { + serviceVec.push_back(serviceObject); + if (response.isDomain) { + session.domainTable[++session.handleIndex] = serviceObject; + response.domainObjects.push_back(session.handleIndex); + } else + response.moveHandles.push_back(state.thisProcess->NewHandle(serviceObject).handle); + state.logger->Write(Logger::Debug, "Service has been registered: \"{}\"", serviceObject->getName()); + } + void ServiceManager::CloseSession(const handle_t handle) { auto session = state.thisProcess->GetHandle(handle); if (session->serviceStatus == type::KSession::ServiceStatus::Open) { diff --git a/app/src/main/cpp/skyline/kernel/services/serviceman.h b/app/src/main/cpp/skyline/kernel/services/serviceman.h index 5c33e17f..f12f0c6a 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.h +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.h @@ -33,13 +33,22 @@ namespace skyline::kernel::service { handle_t NewSession(const Service serviceType); /** - * @brief Creates a new service and writes it's handle or virtual handle (If it's a domain request) to IpcResponse + * @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse * @param serviceType The type of the service * @param session The session object of the command * @param response The response object to write the handle or virtual handle to */ std::shared_ptr NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response); + /** + * @brief Registers a service object in the manager and writes it's handle or virtual handle (If it's a domain request) to IpcResponse + * @param serviceObject An instance of the service + * @param session The session object of the command + * @param response The response object to write the handle or virtual handle to + */ + void RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response); + + /** * @brief Closes an existing session to a service * @param service The handle of the KService object diff --git a/app/src/main/cpp/skyline/kernel/services/time/time.cpp b/app/src/main/cpp/skyline/kernel/services/time/time.cpp new file mode 100644 index 00000000..83b35746 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/services/time/time.cpp @@ -0,0 +1,59 @@ +#include "timesrv.h" + +namespace skyline::kernel::service::time { + time::time(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::time, { + {0x0, SFunc(time::GetStandardUserSystemClock)}, + {0x1, SFunc(time::GetStandardNetworkSystemClock)}, + {0x3, SFunc(time::GetTimeZoneService)}, + {0x4, SFunc(time::GetStandardLocalSystemClock)} + }) {} + + void time::GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(std::make_shared(SystemClockType::User, state, manager), session, response); + } + + void time::GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(std::make_shared(SystemClockType::Network, state, manager), session, response); + } + + void time::GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(std::make_shared(state, manager), session, response); + } + + void time::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(std::make_shared(SystemClockType::Local, state, manager), session, response); + } + + ISystemClock::ISystemClock(SystemClockType clockType, const DeviceState &state, ServiceManager &manager) : type(clockType), BaseService(state, manager, false, Service::time_ISystemClock, { + {0x0, SFunc(ISystemClock::GetCurrentTime)} + }) {} + + void ISystemClock::GetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + response.WriteValue(static_cast(std::time(nullptr))); + } + + ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::time_ITimeZoneService, { + {0x65, SFunc(ITimeZoneService::ToCalendarTimeWithMyRule)} + }) {} + + void ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + time_t curTime = std::time(nullptr); + tm calender = *std::gmtime(&curTime); + CalendarTime calendarTime { + .year = static_cast(calender.tm_year), + .month = static_cast(calender.tm_mon), + .day = static_cast(calender.tm_hour), + .minute = static_cast(calender.tm_min), + .second = static_cast(calender.tm_sec) + }; + response.WriteValue(calendarTime); + CalendarAdditionalInfo calendarInfo { + .day_week = static_cast(calender.tm_wday), + .day_month = static_cast(calender.tm_mday), + .name = *reinterpret_cast(calender.tm_zone), + .dst = static_cast(calender.tm_isdst), + .utc_rel = static_cast(calender.tm_gmtoff) + }; + response.WriteValue(calendarInfo); + } +} diff --git a/app/src/main/cpp/skyline/kernel/services/time/timesrv.h b/app/src/main/cpp/skyline/kernel/services/time/timesrv.h new file mode 100644 index 00000000..3e96e616 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/services/time/timesrv.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +namespace skyline::kernel::service::time { + /** + * @brief The type of a SystemClockType + */ + enum SystemClockType { + User, //!< Use time provided by user + Network, //!< Use network time + Local, //!< Use local time + }; + + /** + * @brief time (This covers both time:a and time:s) is responsible for providing handles to various clock services (https://switchbrew.org/wiki/PSC_services#time:su.2C_time:s) + */ + class time : public BaseService { + public: + time(const DeviceState &state, ServiceManager& manager); + + /** + * @brief This returns a handle to a ISystemClock for user time (https://switchbrew.org/wiki/Services_API#GetStandardUserSystemClock) + */ + void GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns a handle to a ISystemClock for user time (https://switchbrew.org/wiki/Services_API#GetStandardNetworkSystemClock) + */ + void GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns a handle to a ISystemClock for user time (https://switchbrew.org/wiki/Services_API#GetStandardNetworkSystemClock) + */ + void GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns a handle to a ISystemClock for user time (https://switchbrew.org/wiki/Services_API#GetStandardNetworkSystemClock) + */ + void GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; + + /** + * @brief ISystemClock is used to retrieve and set time (https://switchbrew.org/wiki/PSC_services#ISystemClock) + */ + class ISystemClock : public BaseService { + public: + SystemClockType type; //!< The type of the system clock + + ISystemClock(SystemClockType clockType, const DeviceState &state, ServiceManager& manager); + + /** + * @brief This returns the amount of seconds since epoch + */ + void GetCurrentTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; + + /** + * @brief ITimeZoneService is used to retrieve and set time (https://switchbrew.org/wiki/PSC_services#ITimeZoneService) + */ + class ITimeZoneService : public BaseService { + public: + /** + * @brief This holds a particular time point in calendar format + */ + struct CalendarTime { + u16 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + u8 : 8; + }; + static_assert(sizeof(CalendarTime)==8); + + /** + * @brief This is passed in addition to CalendarTime + */ + struct CalendarAdditionalInfo { + u32 day_week; + u32 day_month; + u64 name; + i32 dst; + u32 utc_rel; + }; + static_assert(sizeof(CalendarAdditionalInfo)==24); + + ITimeZoneService(const DeviceState &state, ServiceManager& manager); + + /** + * @brief This receives a u64 #PosixTime (https://switchbrew.org/wiki/PSC_services#PosixTime), and returns a #CalendarTime (https://switchbrew.org/wiki/PSC_services#CalendarTime), #CalendarAdditionalInfo (https://switchbrew.org/wiki/PSC_services#CalendarAdditionalInfo) + */ + void ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/kernel/types/KSession.h b/app/src/main/cpp/skyline/kernel/types/KSession.h index 8319fa7c..c7a4c44c 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSession.h +++ b/app/src/main/cpp/skyline/kernel/types/KSession.h @@ -20,9 +20,8 @@ namespace skyline::kernel::type { /** * @param state The state of the device * @param serviceObject A shared pointer to the service class - * @param serviceType The type of the service */ - KSession(const DeviceState &state, std::shared_ptr &serviceObject, const service::Service &serviceType) : serviceObject(serviceObject), serviceType(serviceType), KSyncObject(state, KType::KSession) {} + KSession(const DeviceState &state, std::shared_ptr &serviceObject) : serviceObject(serviceObject), serviceType(serviceObject->serviceType), KSyncObject(state, KType::KSession) {} /** * This converts this session into a domain session (https://switchbrew.org/wiki/IPC_Marshalling#Domains) diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp index d16ecf53..7889f864 100644 --- a/app/src/main/cpp/skyline/loader/nro.cpp +++ b/app/src/main/cpp/skyline/loader/nro.cpp @@ -37,9 +37,14 @@ namespace skyline::loader { if (instrSvc->Verify()) { instr::Brk brk(static_cast(instrSvc->value)); *address = *reinterpret_cast(&brk); - } else if (instrMrs->Verify() && instrMrs->srcReg == constant::TpidrroEl0) { - instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg)); - *address = *reinterpret_cast(&brk); + } else if (instrMrs->Verify()) { + if(instrMrs->srcReg == constant::TpidrroEl0) { + instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg)); + *address = *reinterpret_cast(&brk); + } else if(instrMrs->srcReg == constant::CntpctEl0) { + instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg); + *address = *reinterpret_cast(&mrs); + } } }