From 082cd441f627104e8b3fcedb7baba7cf31da8c66 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 11 Nov 2025 10:50:17 -0700 Subject: [PATCH] kern: write cpu tick differential to tls +0x108 on thread switch --- libmesosphere/source/kern_k_scheduler.cpp | 8 +++++++- .../vapours/svc/arch/arm/svc_thread_local_region.hpp | 4 +++- .../vapours/svc/arch/arm64/svc_thread_local_region.hpp | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libmesosphere/source/kern_k_scheduler.cpp b/libmesosphere/source/kern_k_scheduler.cpp index 34fc4aea..bf0e6c19 100644 --- a/libmesosphere/source/kern_k_scheduler.cpp +++ b/libmesosphere/source/kern_k_scheduler.cpp @@ -270,7 +270,13 @@ namespace ams::kern { m_current_thread = next_thread; /* Set the new Thread Local region. */ - cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress())); + const auto tls_address = GetInteger(next_thread->GetThreadLocalRegionAddress()); + cpu::SwitchThreadLocalRegion(tls_address); + + /* Update the thread's cpu time differential in TLS, if relevant. */ + if (tls_address != 0) { + static_cast(next_thread->GetThreadLocalRegionHeapAddress())->thread_cpu_time = next_thread->GetCpuTime() - cur_tick; + } } void KScheduler::ClearPreviousThread(KThread *thread) { diff --git a/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp b/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp index 1822eaaf..9fdd954a 100644 --- a/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp +++ b/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp @@ -25,8 +25,10 @@ namespace ams::svc::arch::arm { u32 message_buffer[MessageBufferSize / sizeof(u32)]; volatile u16 disable_count; volatile u16 interrupt_flag; + volatile u8 cache_maintenance_flag; + volatile s64 thread_cpu_time; /* TODO: Should we bother adding the Nintendo aarch32 thread local context here? */ - uintptr_t TODO[(0x200 - 0x104) / sizeof(uintptr_t)]; + uintptr_t TODO[(0x200 - 0x110) / sizeof(uintptr_t)]; }; ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() { diff --git a/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp b/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp index e32c9a57..f3146dcb 100644 --- a/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp +++ b/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp @@ -26,12 +26,14 @@ namespace ams::svc::arch::arm64 { volatile u16 disable_count; volatile u16 interrupt_flag; volatile u8 cache_maintenance_flag; + volatile s64 thread_cpu_time; /* TODO: How should we handle libnx vs Nintendo user thread local space? */ - uintptr_t TODO[(0x200 - 0x108) / sizeof(uintptr_t)]; + uintptr_t TODO[(0x200 - 0x110) / sizeof(uintptr_t)]; }; static_assert(__builtin_offsetof(ThreadLocalRegion, disable_count) == 0x100); static_assert(__builtin_offsetof(ThreadLocalRegion, interrupt_flag) == 0x102); static_assert(__builtin_offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104); + static_assert(__builtin_offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108); ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() { ThreadLocalRegion *tlr;