libctru/libctru/source/system/syscalls.c
Teodor Spæren 813d28ddc4 Implement clock_gettime syscall
This implements the syscalls for `clock_gettime` and `clock_getres`. We
support two clocks: CLOCK_REALTIME and CLOCK_MONOTONIC. I've opted to
use the existing `osGetTime()` code for the realtime clock, because it's
known to work.

For CLOCK_MONOTONIC I've used `svcGetSystemTick()` directly, as it has a
higher resolution. We can ignore the drift and so on, because it's
supposed to be just the number of ticks since last boot.
2022-06-27 20:50:39 +02:00

191 lines
4.4 KiB
C

#include <sys/iosupport.h>
#include <sys/time.h>
#include <sys/lock.h>
#include <sys/reent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <3ds/types.h>
#include <3ds/svc.h>
#include <3ds/env.h>
#include <3ds/os.h>
#include <3ds/synchronization.h>
#include "../internal.h"
void __ctru_exit(int rc);
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
struct _reent* __SYSCALL(getreent)()
{
ThreadVars* tv = getThreadVars();
if (tv->magic != THREADVARS_MAGIC)
{
svcBreak(USERBREAK_PANIC);
for (;;);
}
return tv->reent;
}
int __SYSCALL(clock_gettime)(clockid_t clock_id, struct timespec *tp) {
if (clock_id == CLOCK_REALTIME)
{
if (tp != NULL)
{
// Retrieve current time, adjusting epoch from 1900 to 1970
s64 ms_since_epoch = osGetTime() - 2208988800000ULL;
tp->tv_sec = ms_since_epoch / 1000;
tp->tv_nsec = (ms_since_epoch % 1000) * 1000000;
}
}
else if (clock_id == CLOCK_MONOTONIC)
{
if (tp != NULL)
{
// Use the ticks directly, as it offer the highest precision
u64 ticks_since_boot = svcGetSystemTick();
tp->tv_sec = ticks_since_boot / SYSCLOCK_ARM11;
tp->tv_nsec = ((ticks_since_boot % SYSCLOCK_ARM11) * 1000000000ULL) / SYSCLOCK_ARM11;
}
}
else
{
errno = EINVAL;
return -1;
}
return 0;
}
int __SYSCALL(clock_getres)(clockid_t clock_id, struct timespec *res) {
if (clock_id == CLOCK_REALTIME)
{
if (res != NULL)
{
res->tv_sec = 0;
res->tv_nsec = 1000000;
}
}
else if (clock_id == CLOCK_MONOTONIC)
{
if (res != NULL)
{
res->tv_sec = 0;
res->tv_nsec = 1;
}
}
else
{
errno = EINVAL;
return -1;
}
return 0;
}
//---------------------------------------------------------------------------------
int __SYSCALL(gettod_r)(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
//---------------------------------------------------------------------------------
if (tp != NULL) {
// Retrieve current time, adjusting epoch from 1900 to 1970
s64 now = osGetTime() - 2208988800000ULL;
// Convert to struct timeval
tp->tv_sec = now / 1000;
tp->tv_usec = (now % 1000) * 1000;
}
if (tz != NULL) {
// Provide dummy information, as the 3DS does not have the concept of timezones
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
return 0;
}
int __SYSCALL(nanosleep)(const struct timespec *req, struct timespec *rem)
{
svcSleepThread(req->tv_sec * 1000000000ull + req->tv_nsec);
return 0;
}
void __SYSCALL(lock_init) (_LOCK_T *lock)
{
LightLock_Init(lock);
}
void __SYSCALL(lock_acquire) (_LOCK_T *lock)
{
LightLock_Lock(lock);
}
int __SYSCALL(lock_try_acquire) (_LOCK_T *lock)
{
return LightLock_TryLock(lock);
}
void __SYSCALL(lock_release) (_LOCK_T *lock)
{
LightLock_Unlock(lock);
}
void __SYSCALL(lock_init_recursive) (_LOCK_RECURSIVE_T *lock)
{
RecursiveLock_Init(lock);
}
void __SYSCALL(lock_acquire_recursive) (_LOCK_RECURSIVE_T *lock)
{
RecursiveLock_Lock(lock);
}
int __SYSCALL(lock_try_acquire_recursive) (_LOCK_RECURSIVE_T *lock)
{
return RecursiveLock_TryLock(lock);
}
void __SYSCALL(lock_release_recursive) (_LOCK_RECURSIVE_T *lock)
{
RecursiveLock_Unlock(lock);
}
void __SYSCALL(exit)(int rc) {
__ctru_exit(rc);
}
void initThreadVars(struct Thread_tag *thread)
{
ThreadVars* tv = getThreadVars();
tv->magic = THREADVARS_MAGIC;
tv->reent = thread != NULL ? &thread->reent : _impure_ptr;
tv->thread_ptr = thread;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
tv->tls_tp = (thread != NULL ? (u8*)thread->stacktop : __tls_start) - 8; // Arm ELF TLS ABI mandates an 8-byte header
#pragma GCC diagnostic pop
tv->srv_blocking_policy = false;
// Kernel does not initialize fpscr at all, so we must do it ourselves
// https://developer.arm.com/documentation/ddi0360/f/vfp-programmers-model/vfp11-system-registers/floating-point-status-and-control-register--fpscr
// All flags clear, all interrupts disabled, all instruction scalar.
// As for the 3 below fields: default NaN mode, flush-to-zero both enabled & round to nearest.
__builtin_arm_set_fpscr(BIT(25) | BIT(24) | (0u << 22));
}
void __system_initSyscalls(void)
{
// Initialize thread vars for the main thread
initThreadVars(NULL);
u32 tls_size = __tdata_lma_end - __tdata_lma;
if (tls_size)
memcpy(__tls_start, __tdata_lma, tls_size);
}