diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h index d2988d923..410fda5d0 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h @@ -263,7 +263,7 @@ /* NOTE: Due to constraints on ldarb relative offsets, KSCHEDULER_NEEDS_SCHEDULING cannot trivially be changed, and will require assembly edits. */ #define KSCHEDULER_NEEDS_SCHEDULING 0x00 #define KSCHEDULER_INTERRUPT_TASK_RUNNABLE 0x01 -#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x10 -#define KSCHEDULER_IDLE_THREAD_STACK 0x18 -#define KSCHEDULER_PREVIOUS_THREAD 0x20 -#define KSCHEDULER_INTERRUPT_TASK_MANAGER 0x28 +#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x18 +#define KSCHEDULER_IDLE_THREAD_STACK 0x20 +#define KSCHEDULER_PREVIOUS_THREAD 0x28 +#define KSCHEDULER_INTERRUPT_TASK_MANAGER 0x30 diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 46c579f18..9ff663f5f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -108,6 +108,7 @@ namespace ams::kern { KWaitObject m_wait_object; KThread *m_running_threads[cpu::NumCores]; u64 m_running_thread_idle_counts[cpu::NumCores]; + u64 m_running_thread_switch_counts[cpu::NumCores]; KThread *m_pinned_threads[cpu::NumCores]; util::Atomic m_cpu_time; util::Atomic m_num_process_switches; @@ -285,9 +286,10 @@ namespace ams::kern { return m_system_resource->IsSecureResource() ? static_cast(m_system_resource)->GetUsedSize() : 0; } - void SetRunningThread(s32 core, KThread *thread, u64 idle_count) { - m_running_threads[core] = thread; - m_running_thread_idle_counts[core] = idle_count; + void SetRunningThread(s32 core, KThread *thread, u64 idle_count, u64 switch_count) { + m_running_threads[core] = thread; + m_running_thread_idle_counts[core] = idle_count; + m_running_thread_switch_counts[core] = switch_count; } void ClearRunningThread(KThread *thread) { @@ -306,6 +308,7 @@ namespace ams::kern { constexpr KThread *GetRunningThread(s32 core) const { return m_running_threads[core]; } constexpr u64 GetRunningThreadIdleCount(s32 core) const { return m_running_thread_idle_counts[core]; } + constexpr u64 GetRunningThreadSwitchCount(s32 core) const { return m_running_thread_switch_counts[core]; } void RegisterThread(KThread *thread); void UnregisterThread(KThread *thread); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index 3a1634461..cc19d8f3f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -43,6 +43,7 @@ namespace ams::kern { bool interrupt_task_runnable{false}; bool should_count_idle{false}; u64 idle_count{0}; + u64 switch_count{0}; KThread *highest_priority_thread{nullptr}; void *idle_thread_stack{nullptr}; KThread *prev_thread{nullptr}; @@ -67,6 +68,7 @@ namespace ams::kern { m_state.interrupt_task_runnable = false; m_state.should_count_idle = false; m_state.idle_count = 0; + m_state.switch_count = 0; m_state.idle_thread_stack = nullptr; m_state.highest_priority_thread = nullptr; m_state.prev_thread = nullptr; @@ -93,6 +95,10 @@ namespace ams::kern { return m_state.idle_count; } + ALWAYS_INLINE u64 GetSwitchCount() const { + return m_state.switch_count; + } + ALWAYS_INLINE KThread *GetIdleThread() const { return m_idle_thread; } diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index c971a94b0..d10d1df86 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -219,12 +219,21 @@ namespace ams::kern { const s32 core_id = GetCurrentCoreId(); KThread *thread = process->GetRunningThread(core_id); - /* Check that the thread's idle count is correct. */ - R_UNLESS(process->GetRunningThreadIdleCount(core_id) == Kernel::GetScheduler(core_id).GetIdleCount(), svc::ResultNoThread()); - - /* Check that the thread is running on the current core. */ - R_UNLESS(thread != nullptr, svc::ResultUnknownThread()); - R_UNLESS(thread->GetActiveCore() == core_id, svc::ResultUnknownThread()); + /* We want to check that the thread is actually running. */ + /* If it is, then the scheduler will have just switched from the thread to the current thread. */ + /* This implies exactly one switch will have taken place, and the current thread will be on the current core. */ + const auto &scheduler = Kernel::GetScheduler(core_id); + if (!(thread != nullptr && thread->GetActiveCore() == core_id && process->GetRunningThreadSwitchCount(core_id) + 1 == scheduler.GetSwitchCount())) { + /* The most recent thread switch was from a thread other than the expected one to the current one. */ + /* We want to use the appropriate result to inform userland about what thread we switched from. */ + if (scheduler.GetIdleCount() + 1 == scheduler.GetSwitchCount()) { + /* We switched from the idle thread. */ + R_THROW(svc::ResultNoThread()); + } else { + /* We switched from some other unknown thread. */ + R_THROW(svc::ResultUnknownThread()); + } + } /* Get the thread's exception context. */ GetExceptionContext(thread)->GetSvcThreadContext(out_context); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 75cb0f7a6..4389e3081 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -211,9 +211,10 @@ namespace ams::kern { /* Set thread fields. */ for (size_t i = 0; i < cpu::NumCores; i++) { - m_running_threads[i] = nullptr; - m_pinned_threads[i] = nullptr; - m_running_thread_idle_counts[i] = 0; + m_running_threads[i] = nullptr; + m_pinned_threads[i] = nullptr; + m_running_thread_idle_counts[i] = 0; + m_running_thread_switch_counts[i] = 0; } /* Set max memory based on address space type. */ diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index 810ed1c37..167474234 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -88,10 +88,12 @@ namespace ams::kern { if (m_state.should_count_idle) { if (AMS_LIKELY(highest_thread != nullptr)) { if (KProcess *process = highest_thread->GetOwnerProcess(); process != nullptr) { - process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count); + /* Set running thread (and increment switch count). */ + process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, ++m_state.switch_count); } } else { - m_state.idle_count++; + /* Set idle count and switch count to switch count + 1. */ + m_state.idle_count = ++m_state.switch_count; } }