libNX Time Initalization

What was added:
* Time service
* CNTPCT_EL0 redirected to CNTVCT_EL0
This commit is contained in:
◱ PixelyIon 2019-10-17 01:53:35 +05:30
parent 2476c5d48a
commit e44809355c
11 changed files with 272 additions and 65 deletions

View File

@ -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)

View File

@ -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

View File

@ -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);
};

View File

@ -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;

View File

@ -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<IAppletResource>(manager.NewService(Service::hid_IAppletResource, session, response));
resource = std::make_shared<IAppletResource>(state, manager);
manager.RegisterService(resource, session, response);
}
void hid::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {

View File

@ -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) {}
@ -64,13 +65,18 @@ namespace skyline::kernel::service {
case Service::hid_IAppletResource:
serviceObj = std::make_shared<hid::IAppletResource>(state, *this);
break;
case Service::time:
serviceObj = std::make_shared<time::time>(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<type::KSession>(GetService(serviceType), serviceType).handle;
return state.thisProcess->NewHandle<type::KSession>(GetService(serviceType)).handle;
}
std::shared_ptr<BaseService> 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<type::KSession>(serviceObject, serviceType).handle);
state.logger->Write(Logger::Debug, "Service has been registered: \"{}\"", serviceObject->getName());
response.moveHandles.push_back(state.thisProcess->NewHandle<type::KSession>(serviceObject).handle);
state.logger->Write(Logger::Debug, "Service has been created: \"{}\"", serviceObject->getName());
return serviceObject;
}
void ServiceManager::RegisterService(std::shared_ptr<BaseService> 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<type::KSession>(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<type::KSession>(handle);
if (session->serviceStatus == type::KSession::ServiceStatus::Open) {

View File

@ -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<BaseService> 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<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response);
/**
* @brief Closes an existing session to a service
* @param service The handle of the KService object

View File

@ -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<ISystemClock>(SystemClockType::User, state, manager), session, response);
}
void time::GetStandardNetworkSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(std::make_shared<ISystemClock>(SystemClockType::Network, state, manager), session, response);
}
void time::GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(std::make_shared<ITimeZoneService>(state, manager), session, response);
}
void time::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(std::make_shared<ISystemClock>(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<u64>(static_cast<u64>(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<u16>(calender.tm_year),
.month = static_cast<u8>(calender.tm_mon),
.day = static_cast<u8>(calender.tm_hour),
.minute = static_cast<u8>(calender.tm_min),
.second = static_cast<u8>(calender.tm_sec)
};
response.WriteValue(calendarTime);
CalendarAdditionalInfo calendarInfo {
.day_week = static_cast<u32>(calender.tm_wday),
.day_month = static_cast<u32>(calender.tm_mday),
.name = *reinterpret_cast<const u64*>(calender.tm_zone),
.dst = static_cast<i32>(calender.tm_isdst),
.utc_rel = static_cast<u32>(calender.tm_gmtoff)
};
response.WriteValue(calendarInfo);
}
}

View File

@ -0,0 +1,97 @@
#pragma once
#include <kernel/services/base_service.h>
#include <kernel/services/serviceman.h>
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);
};
}

View File

@ -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<service::BaseService> &serviceObject, const service::Service &serviceType) : serviceObject(serviceObject), serviceType(serviceType), KSyncObject(state, KType::KSession) {}
KSession(const DeviceState &state, std::shared_ptr<service::BaseService> &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)

View File

@ -37,9 +37,14 @@ namespace skyline::loader {
if (instrSvc->Verify()) {
instr::Brk brk(static_cast<u16>(instrSvc->value));
*address = *reinterpret_cast<u32 *>(&brk);
} else if (instrMrs->Verify() && instrMrs->srcReg == constant::TpidrroEl0) {
} else if (instrMrs->Verify()) {
if(instrMrs->srcReg == constant::TpidrroEl0) {
instr::Brk brk(static_cast<u16>(constant::SvcLast + 1 + instrMrs->dstReg));
*address = *reinterpret_cast<u32 *>(&brk);
} else if(instrMrs->srcReg == constant::CntpctEl0) {
instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg);
*address = *reinterpret_cast<u32 *>(&mrs);
}
}
}