Files
ps5upload/payload/include/sys_time.h
T
phantomptrandClaude Opus 5.5 c14782944d PS5Upload v5.35.0
Upload, install, convert and manage PS5 game packages over the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 04:31:13 -07:00

135 lines
6.3 KiB
C

#ifndef PS5UPLOAD2_SYS_TIME_H
#define PS5UPLOAD2_SYS_TIME_H
#include <stdint.h>
/*
* PS5 system clock get/set, via SCE's sceSystemServiceGetCurrentDateTime
* and sceSystemServiceSetCurrentDateTime.
*
* Both calls go through SceShellCore IPC. On a vanilla webkit / BD-JB
* payload context the IPC is rejected because the caller's authid +
* capabilities don't include the system-service privilege. On a
* kstuff-loaded payload the loader has already escalated this
* process's ucred to uid=0 with the right cap bits — at which point
* the IPC accepts and the set call actually changes the clock.
*
* On firmwares where the libSceSystemService SDK stub exists but
* the runtime SPRX doesn't implement the underlying syscall, the
* set call returns rc=0 but the clock doesn't move. The
* `out_prior_unix` / `out_new_unix` outputs let the desktop detect
* that case by comparing them — if they're equal AND the requested
* time was different, the call was a stub no-op.
*
* Defensive symbol resolution: we dlsym both functions at first call,
* not at link time. If either symbol is absent (SDK link order quirk,
* NID-only export on some FWs) we surface SYS_TIME_ERR_NO_SYMBOL
* instead of crashing the payload.
*
* Thread-safety: the dlsym cache is initialised at first call; the
* underlying SCE functions are documented thread-safe.
*/
/* Mirrors Sony's SceDateTime layout exactly — stable across every
* firmware ps5upload has been tested on (4.03 → 9.60+). All fields
* UTC. `microsecond` is part of the struct in Sony's header but we
* always pass 0 because the SCE set call rounds to whole seconds
* internally anyway. */
typedef struct sce_datetime {
uint16_t year; /* e.g. 2026 */
uint16_t month; /* 1-12 */
uint16_t day; /* 1-31 */
uint16_t hour; /* 0-23 */
uint16_t minute; /* 0-59 */
uint16_t second; /* 0-59 (no leap seconds — Sony rounds those out) */
uint32_t microsecond; /* 0-999999, ignored on set */
} sce_datetime_t;
/* Diagnostic err_code sentinels we surface in the OUT param. Picked
* in the 0xE0000000+ range so they don't collide with Sony's err_code
* space (Sony uses 0x80xxxxxx negative-as-unsigned), with our own
* BGFT_ERR_* (which use 0xE000_1xxx), or with the standard zero/rc
* semantics. */
#define SYS_TIME_ERR_NULL_ARG 0xE0002001U /* caller passed NULL */
#define SYS_TIME_ERR_NO_SYMBOL 0xE0002002U /* dlsym returned NULL */
/* Neither the SCE call nor settimeofday could move the clock. */
#define SYS_TIME_ERR_FALLBACK 0xE0002003U
/* How far the read-back clock may sit from the requested time and
* still count as "the set took". The read-back happens after the set
* and the clock API is whole-second granular, so a little slop is
* expected; 90 seconds of drift is not slop. Matches the engine-side
* stub-no-op heuristic in sys_time.rs. */
#define SYS_TIME_SET_TOLERANCE_SEC 5
/* Read the PS5's current system date/time (UTC). On success writes
* *out and returns 0; on failure writes *out_err_code with one of
* the SYS_TIME_ERR_* sentinels OR the raw int return from SCE
* (uint32 cast). */
/* Convert between Sony's date struct and a unix epoch (both UTC).
* Exposed because the settimeofday fallback needs both directions, and
* because they are the parts worth testing on the host.
* sys_time_sce_to_unix returns -1 for an unparseable date;
* sys_time_unix_to_sce returns 0 on success, -1 on a NULL out or an
* epoch it cannot represent. */
int64_t sys_time_sce_to_unix(const sce_datetime_t *dt);
int sys_time_unix_to_sce(int64_t unix_seconds, sce_datetime_t *out);
/* Read the PS5's current system date/time (UTC). On success writes
* *out and returns 0.
*
* Falls back to the kernel wall clock (gettimeofday) when the SCE
* getter is absent — on FW 9.60 it is not exported at all, so without
* this the console cannot report its own time and the desktop has no
* drift to show. */
int sys_time_get(sce_datetime_t *out, uint32_t *out_err_code);
/* Set the PS5's system date/time (UTC). Returns 0 on success, -1 on
* failure. *out_err_code follows the same convention as sys_time_get.
*
* out_prior_unix / out_new_unix are best-effort outputs the caller
* can compare to detect the "rc=0 but clock didn't actually move"
* stub-no-op case some firmwares produce. Either may be -1 if the
* companion sys_time_get failed (e.g. symbol absent — the set still
* runs in that case, we just can't verify).
*
* Either of out_prior_unix / out_new_unix may be NULL if the caller
* doesn't care about that side.
*
* Why prior + new (instead of just new): some firmwares' set call
* returns rc=0 even when the underlying syscall did nothing. Having
* the prior epoch in the response lets the desktop UI show the
* actual drift before/after so the user sees whether the set
* actually took. */
/* Whether the SCE set path failed to achieve `target_unix`, and the
* settimeofday fallback should therefore be attempted.
*
* Split out from sys_time_set so it can be tested without touching a
* clock (see payload/tests/sys_time_fallback_selftest.c). Pass
* `observed_unix` < 0 when the clock could not be read back.
*
* Three things send us to the fallback: the symbol is absent, the SCE
* call returned an error, or it returned success and the clock did not
* move (the stub-no-op some firmwares produce). An unverifiable
* success is left alone — with no evidence the set failed, writing the
* clock a second time buys nothing. */
int sys_time_needs_fallback(int have_set_symbol,
int set_rc,
int64_t target_unix,
int64_t observed_unix);
/* out_used_fallback (optional) reports whether the clock was
* ultimately set by settimeofday rather than the SCE call. The desktop
* surfaces this because the two paths have different consequences: the
* SCE call goes through ShellCore and lets Sony's UI update alongside
* it, whereas settimeofday moves the kernel wall clock underneath
* ShellCore, so the Settings UI may keep showing the old time until it
* is reopened. */
int sys_time_set(const sce_datetime_t *dt,
uint32_t *out_err_code,
int64_t *out_prior_unix,
int64_t *out_new_unix,
int *out_used_fallback);
#endif /* PS5UPLOAD2_SYS_TIME_H */