From 6b3746f3a688cd4c7c2acfbbe8238ebd97235d37 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 7 Mar 2025 21:42:25 +0300 Subject: [PATCH] kernel: re-implement clock_gettime (#2615) --- src/core/libraries/kernel/time.cpp | 192 +++++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 24 deletions(-) diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 508e54089..42d959885 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/native_clock.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -19,6 +20,7 @@ #if __APPLE__ #include #endif +#include #include #include #include @@ -93,46 +95,188 @@ u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) { return 0; } -int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { - if (tp == nullptr) { +#ifdef _WIN64 +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif +#ifndef CLOCK_PROCESS_CPUTIME_ID +#define CLOCK_PROCESS_CPUTIME_ID 2 +#endif +#ifndef CLOCK_THREAD_CPUTIME_ID +#define CLOCK_THREAD_CPUTIME_ID 3 +#endif +#ifndef CLOCK_REALTIME_COARSE +#define CLOCK_REALTIME_COARSE 5 +#endif +#ifndef CLOCK_MONOTONIC_COARSE +#define CLOCK_MONOTONIC_COARSE 6 +#endif + +#define DELTA_EPOCH_IN_100NS 116444736000000000ULL + +static u64 FileTimeTo100Ns(FILETIME& ft) { + return *reinterpret_cast(&ft); +} + +static s32 clock_gettime(u32 clock_id, struct timespec* ts) { + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS; + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_COARSE: { + static LARGE_INTEGER pf = [] { + LARGE_INTEGER res{}; + QueryPerformanceFrequency(&pf); + return res; + }(); + + LARGE_INTEGER pc{}; + QueryPerformanceCounter(&pc); + ts->tv_sec = pc.QuadPart / pf.QuadPart; + ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart; + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: { + FILETIME ct, et, kt, ut; + if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + case CLOCK_THREAD_CPUTIME_ID: { + FILETIME ct, et, kt, ut; + if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + default: + return EINVAL; + } +} +#endif + +int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct timespec* ts) { + if (ts == nullptr) { return ORBIS_KERNEL_ERROR_EFAULT; } - clockid_t pclock_id = CLOCK_REALTIME; + + clockid_t pclock_id = CLOCK_MONOTONIC; switch (clock_id) { case ORBIS_CLOCK_REALTIME: case ORBIS_CLOCK_REALTIME_PRECISE: - case ORBIS_CLOCK_REALTIME_FAST: pclock_id = CLOCK_REALTIME; break; case ORBIS_CLOCK_SECOND: + case ORBIS_CLOCK_REALTIME_FAST: +#ifndef __APPLE__ + pclock_id = CLOCK_REALTIME_COARSE; +#else + pclock_id = CLOCK_REALTIME; +#endif + break; + case ORBIS_CLOCK_UPTIME: + case ORBIS_CLOCK_UPTIME_PRECISE: case ORBIS_CLOCK_MONOTONIC: case ORBIS_CLOCK_MONOTONIC_PRECISE: - case ORBIS_CLOCK_MONOTONIC_FAST: pclock_id = CLOCK_MONOTONIC; break; - default: - LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_REALTIME", clock_id); + case ORBIS_CLOCK_UPTIME_FAST: + case ORBIS_CLOCK_MONOTONIC_FAST: +#ifndef __APPLE__ + pclock_id = CLOCK_MONOTONIC_COARSE; +#else + pclock_id = CLOCK_MONOTONIC; +#endif break; + case ORBIS_CLOCK_THREAD_CPUTIME_ID: + pclock_id = CLOCK_THREAD_CPUTIME_ID; + break; + case ORBIS_CLOCK_PROCTIME: { + const auto us = sceKernelGetProcessTime(); + ts->tv_sec = us / 1'000'000; + ts->tv_nsec = (us % 1'000'000) * 1000; + return 0; + } + case ORBIS_CLOCK_VIRTUAL: { +#ifdef _WIN64 + FILETIME ct, et, kt, ut; + if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(ut); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; +#else + struct rusage ru; + const auto res = getrusage(RUSAGE_SELF, &ru); + if (res < 0) { + return res; + } + ts->tv_sec = ru.ru_utime.tv_sec; + ts->tv_nsec = ru.ru_utime.tv_usec * 1000; +#endif + return 0; + } + case ORBIS_CLOCK_PROF: { +#ifdef _WIN64 + FILETIME ct, et, kt, ut; + if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(kt); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; +#else + struct rusage ru; + const auto res = getrusage(RUSAGE_SELF, &ru); + if (res < 0) { + return res; + } + ts->tv_sec = ru.ru_stime.tv_sec; + ts->tv_nsec = ru.ru_stime.tv_usec * 1000; +#endif + return 0; + } + case ORBIS_CLOCK_EXT_NETWORK: + case ORBIS_CLOCK_EXT_DEBUG_NETWORK: + case ORBIS_CLOCK_EXT_AD_NETWORK: + case ORBIS_CLOCK_EXT_RAW_NETWORK: + pclock_id = CLOCK_MONOTONIC; + LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_MONOTONIC", clock_id); + break; + default: + return EINVAL; } - time_t raw_time = time(nullptr); - - if (raw_time == (time_t)(-1)) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - tp->tv_sec = static_cast(raw_time); - tp->tv_nsec = 0; - - return ORBIS_OK; + return clock_gettime(pclock_id, ts); } -int PS4_SYSV_ABI posix_clock_gettime(s32 clock_id, OrbisKernelTimespec* time) { - int result = sceKernelClockGettime(clock_id, time); - if (result < 0) { - UNREACHABLE(); // TODO return posix error code +int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { + struct timespec ts; + const auto res = orbis_clock_gettime(clock_id, &ts); + if (res < 0) { + return ErrnoToSceKernelError(res); } - return result; + tp->tv_sec = ts.tv_sec; + tp->tv_nsec = ts.tv_nsec; + return ORBIS_OK; } int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { @@ -318,8 +462,8 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep); LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime); LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone); - LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime); - LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime); + LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, orbis_clock_gettime); + LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, orbis_clock_gettime); LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres); LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc); LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);