diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/cpu_manager.cpp | 3 | ||||
-rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 3 | ||||
-rw-r--r-- | src/core/hle/kernel/k_scheduler.cpp | 144 | ||||
-rw-r--r-- | src/core/hle/kernel/k_scheduler.h | 24 | ||||
-rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 993 | ||||
-rw-r--r-- | src/core/hle/kernel/k_thread.h | 590 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 10 | ||||
-rw-r--r-- | src/core/hle/kernel/process.cpp | 74 | ||||
-rw-r--r-- | src/core/hle/kernel/process.h | 63 | ||||
-rw-r--r-- | src/core/hle/kernel/server_session.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 355 | ||||
-rw-r--r-- | src/core/hle/kernel/svc_types.h | 6 | ||||
-rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 56 |
13 files changed, 1554 insertions, 769 deletions
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 719258250..9bbb82b97 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -279,8 +279,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { auto& scheduler = system.Kernel().Scheduler(current_core); scheduler.Reload(scheduler.GetCurrentThread()); - auto* currrent_thread2 = scheduler.GetCurrentThread(); - if (!currrent_thread2->IsKernelThread()) { + if (!scheduler.IsIdle()) { idle_count = 0; } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b8a746882..9f764c79a 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -126,9 +126,6 @@ public: return server_session; } - using WakeupCallback = std::function<void( - std::shared_ptr<KThread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; - /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf); diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 233022023..5bdbd9a9b 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -31,11 +31,15 @@ static void IncrementScheduledCount(Kernel::KThread* thread) { } } -void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, - Core::EmuThreadHandle global_thread) { - const u32 current_core = global_thread.host_handle; - bool must_context_switch = global_thread.guest_handle != InvalidHandle && - (current_core < Core::Hardware::NUM_CPU_CORES); +void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) { + auto scheduler = kernel.CurrentScheduler(); + + u32 current_core{0xF}; + bool must_context_switch{}; + if (scheduler) { + current_core = scheduler->core_id; + must_context_switch = true; + } while (cores_pending_reschedule != 0) { const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); @@ -58,26 +62,25 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { std::scoped_lock lock{guard}; - if (KThread* prev_highest_thread = this->state.highest_priority_thread; + if (KThread* prev_highest_thread = state.highest_priority_thread; prev_highest_thread != highest_thread) { if (prev_highest_thread != nullptr) { IncrementScheduledCount(prev_highest_thread); prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks()); } - if (this->state.should_count_idle) { + if (state.should_count_idle) { if (highest_thread != nullptr) { - // if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) { - // process->SetRunningThread(this->core_id, highest_thread, - // this->state.idle_count); - //} + if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) { + process->SetRunningThread(core_id, highest_thread, state.idle_count); + } } else { - this->state.idle_count++; + state.idle_count++; } } - this->state.highest_priority_thread = highest_thread; - this->state.needs_scheduling = true; - return (1ULL << this->core_id); + state.highest_priority_thread = highest_thread; + state.needs_scheduling = true; + return (1ULL << core_id); } else { return 0; } @@ -99,7 +102,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id)); if (top_thread != nullptr) { // If the thread has no waiters, we need to check if the process has a thread pinned. - // TODO(bunnei): Implement thread pinning + if (top_thread->GetNumKernelWaiters() == 0) { + if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) { + if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); + pinned != nullptr && pinned != top_thread) { + // We prefer our parent's pinned thread if possible. However, we also don't + // want to schedule un-runnable threads. + if (pinned->GetRawState() == ThreadState::Runnable) { + top_thread = pinned; + } else { + top_thread = nullptr; + } + } + } + } } else { idle_cores |= (1ULL << core_id); } @@ -182,6 +198,19 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } +void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) { + // Get an atomic reference to the core scheduler's previous thread. + std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread); + static_assert(std::atomic_ref<KThread*>::is_always_lock_free); + + // Atomically clear the previous thread if it's our target. + KThread* compare = thread; + prev_thread.compare_exchange_strong(compare, nullptr); + } +} + void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -352,12 +381,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) { } } -void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, - Core::EmuThreadHandle global_thread) { +void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { - scheduler->GetCurrentThread()->EnableDispatch(); + ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); + if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { + scheduler->GetCurrentThread()->EnableDispatch(); + } } - RescheduleCores(kernel, cores_needing_scheduling, global_thread); + RescheduleCores(kernel, cores_needing_scheduling); } u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { @@ -372,15 +403,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) { return kernel.GlobalSchedulerContext().priority_queue; } -void KScheduler::YieldWithoutCoreMigration() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -413,15 +442,13 @@ void KScheduler::YieldWithoutCoreMigration() { } } -void KScheduler::YieldWithCoreMigration() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -503,15 +530,13 @@ void KScheduler::YieldWithCoreMigration() { } } -void KScheduler::YieldToAnyThread() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldToAnyThread(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -581,15 +606,14 @@ void KScheduler::YieldToAnyThread() { } } -KScheduler::KScheduler(Core::System& system, std::size_t core_id) - : system(system), core_id(core_id) { +KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) { switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); - this->state.needs_scheduling = true; - this->state.interrupt_task_thread_runnable = false; - this->state.should_count_idle = false; - this->state.idle_count = 0; - this->state.idle_thread_stack = nullptr; - this->state.highest_priority_thread = nullptr; + state.needs_scheduling = true; + state.interrupt_task_thread_runnable = false; + state.should_count_idle = false; + state.idle_count = 0; + state.idle_thread_stack = nullptr; + state.highest_priority_thread = nullptr; } KScheduler::~KScheduler() = default; @@ -613,7 +637,7 @@ void KScheduler::RescheduleCurrentCore() { phys_core.ClearInterrupt(); } guard.lock(); - if (this->state.needs_scheduling) { + if (state.needs_scheduling) { Schedule(); } else { guard.unlock(); @@ -625,32 +649,34 @@ void KScheduler::OnThreadStart() { } void KScheduler::Unload(KThread* thread) { + LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + if (thread) { - thread->SetIsRunning(false); - if (thread->IsContinuousOnSVC()) { + if (thread->IsCallingSvc()) { system.ArmInterface(core_id).ExceptionalExit(); - thread->SetContinuousOnSVC(false); + thread->ClearIsCallingSvc(); } - if (!thread->HasExited()) { + if (!thread->IsTerminationRequested()) { + prev_thread = thread; + Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); cpu_core.SaveContext(thread->GetContext32()); cpu_core.SaveContext(thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); + } else { + prev_thread = nullptr; } - thread->context_guard.unlock(); } } void KScheduler::Reload(KThread* thread) { + LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + if (thread) { ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); - // Cancel any outstanding wakeup events for this thread - thread->SetIsRunning(true); - thread->SetWasRunning(false); - auto* const thread_owner_process = thread->GetOwnerProcess(); if (thread_owner_process != nullptr) { system.Kernel().MakeCurrentProcess(thread_owner_process); @@ -676,7 +702,7 @@ void KScheduler::ScheduleImpl() { KThread* previous_thread = current_thread; current_thread = state.highest_priority_thread; - this->state.needs_scheduling = false; + state.needs_scheduling = false; if (current_thread == previous_thread) { guard.unlock(); @@ -714,7 +740,7 @@ void KScheduler::SwitchToCurrent() { { std::scoped_lock lock{guard}; current_thread = state.highest_priority_thread; - this->state.needs_scheduling = false; + state.needs_scheduling = false; } const auto is_switch_pending = [this] { std::scoped_lock lock{guard}; @@ -722,13 +748,10 @@ void KScheduler::SwitchToCurrent() { }; do { if (current_thread != nullptr) { - current_thread->context_guard.lock(); if (current_thread->GetRawState() != ThreadState::Runnable) { - current_thread->context_guard.unlock(); break; } - if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) { - current_thread->context_guard.unlock(); + if (static_cast<u32>(current_thread->GetActiveCore()) != core_id) { break; } } @@ -749,7 +772,7 @@ void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; if (thread != nullptr) { - thread->UpdateCPUTimeTicks(update_ticks); + thread->AddCpuTime(core_id, update_ticks); } if (process != nullptr) { @@ -763,15 +786,10 @@ void KScheduler::Initialize() { std::string name = "Idle Thread Id:" + std::to_string(core_id); std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - auto thread_res = KThread::Create(system, ThreadType::Kernel, name, 0, + auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter); idle_thread = thread_res.Unwrap().get(); - - { - KScopedSchedulerLock lock{system.Kernel()}; - idle_thread->SetState(ThreadState::Runnable); - } } KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 157373934..2308a55be 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -33,15 +33,14 @@ class KThread; class KScheduler final { public: - explicit KScheduler(Core::System& system, std::size_t core_id); + explicit KScheduler(Core::System& system, s32 core_id); ~KScheduler(); /// Reschedules to the next available thread (call after current thread is suspended) void RescheduleCurrentCore(); /// Reschedules cores pending reschedule, to be called on EnableScheduling. - static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, - Core::EmuThreadHandle global_thread); + static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule); /// The next two are for SingleCore Only. /// Unload current thread before preempting core. @@ -53,6 +52,11 @@ public: /// Gets the current running thread [[nodiscard]] KThread* GetCurrentThread() const; + /// Returns true if the scheduler is idle + [[nodiscard]] bool IsIdle() const { + return GetCurrentThread() == idle_thread; + } + /// Gets the timestamp for the last context switch in ticks. [[nodiscard]] u64 GetLastContextSwitchTicks() const; @@ -79,7 +83,7 @@ public: * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldWithoutCoreMigration(); + static void YieldWithoutCoreMigration(KernelCore& kernel); /** * Takes a thread and moves it to the back of the it's priority list. @@ -88,7 +92,7 @@ public: * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldWithCoreMigration(); + static void YieldWithCoreMigration(KernelCore& kernel); /** * Takes a thread and moves it out of the scheduling queue. @@ -97,7 +101,9 @@ public: * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldToAnyThread(); + static void YieldToAnyThread(KernelCore& kernel); + + static void ClearPreviousThread(KernelCore& kernel, KThread* thread); /// Notify the scheduler a thread's status has changed. static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state); @@ -114,8 +120,7 @@ public: static void SetSchedulerUpdateNeeded(KernelCore& kernel); static void ClearSchedulerUpdateNeeded(KernelCore& kernel); static void DisableScheduling(KernelCore& kernel); - static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, - Core::EmuThreadHandle global_thread); + static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel); private: @@ -168,6 +173,7 @@ private: static void OnSwitch(void* this_scheduler); void SwitchToCurrent(); + KThread* prev_thread{}; KThread* current_thread{}; KThread* idle_thread{}; @@ -186,7 +192,7 @@ private: Core::System& system; u64 last_context_switch_time{}; - const std::size_t core_id; + const s32 core_id; Common::SpinLock guard{}; }; diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index e5be849bb..f021b0550 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -8,10 +8,12 @@ #include <vector> #include "common/assert.h" +#include "common/bit_util.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/fiber.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/thread_queue_list.h" #include "core/core.h" #include "core/cpu_manager.h" @@ -22,10 +24,12 @@ #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_layout.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/result.h" @@ -36,185 +40,734 @@ #include "core/arm/dynarmic/arm_dynarmic_64.h" #endif -namespace Kernel { +namespace { +static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, + u32 entry_point, u32 arg) { + context = {}; + context.cpu_registers[0] = arg; + context.cpu_registers[15] = entry_point; + context.cpu_registers[13] = stack_top; +} -bool KThread::IsSignaled() const { - return signaled; +static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top, + VAddr entry_point, u64 arg) { + context = {}; + context.cpu_registers[0] = arg; + context.pc = entry_point; + context.sp = stack_top; + // TODO(merry): Perform a hardware test to determine the below value. + context.fpcr = 0; } +} // namespace + +namespace Kernel { -KThread::KThread(KernelCore& kernel) : KSynchronizationObject{kernel} {} +KThread::KThread(KernelCore& kernel) + : KSynchronizationObject{kernel}, activity_pause_lock{kernel} {} KThread::~KThread() = default; -void KThread::Stop() { - { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Terminated); - signaled = true; - NotifyAvailable(); - kernel.GlobalHandleTable().Close(global_handle); +ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 virt_core, Process* owner, ThreadType type) { + // Assert parameters are valid. + ASSERT((type == ThreadType::Main) || + (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); + ASSERT((owner != nullptr) || (type != ThreadType::User)); + ASSERT(0 <= virt_core && virt_core < static_cast<s32>(Common::BitSize<u64>())); + + // Convert the virtual core to a physical core. + const s32 phys_core = Core::Hardware::VirtualToPhysicalCoreMap[virt_core]; + ASSERT(0 <= phys_core && phys_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + + // First, clear the TLS address. + tls_address = {}; + + // Next, assert things based on the type. + switch (type) { + case ThreadType::Main: + ASSERT(arg == 0); + [[fallthrough]]; + case ThreadType::HighPriority: + [[fallthrough]]; + case ThreadType::User: + ASSERT(((owner == nullptr) || + (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); + ASSERT(((owner == nullptr) || + (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); + break; + case ThreadType::Kernel: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); + break; + } + + // Set the ideal core ID and affinity mask. + virtual_ideal_core_id = virt_core; + physical_ideal_core_id = phys_core; + virtual_affinity_mask = (static_cast<u64>(1) << virt_core); + physical_affinity_mask.SetAffinity(phys_core, true); + + // Set the thread state. + thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; + + // Set TLS address. + tls_address = 0; + + // Set parent and condvar tree. + parent = nullptr; + condvar_tree = nullptr; + + // Set sync booleans. + signaled = false; + termination_requested = false; + wait_cancelled = false; + cancellable = false; + + // Set core ID and wait result. + core_id = phys_core; + wait_result = Svc::ResultNoSynchronizationObject; + + // Set priorities. + priority = prio; + base_priority = prio; + + // Set sync object and waiting lock to null. + synced_object = nullptr; + + // Initialize sleeping queue. + sleeping_queue = nullptr; + + // Set suspend flags. + suspend_request_flags = 0; + suspend_allowed_flags = static_cast<u32>(ThreadState::SuspendFlagMask); + + // We're neither debug attached, nor are we nesting our priority inheritance. + debug_attached = false; + priority_inheritance_count = 0; + + // We haven't been scheduled, and we have done no light IPC. + schedule_count = -1; + last_scheduled_tick = 0; + light_ipc_data = nullptr; + + // We're not waiting for a lock, and we haven't disabled migration. + lock_owner = nullptr; + num_core_migration_disables = 0; + + // We have no waiters, but we do have an entrypoint. + num_kernel_waiters = 0; + + // Set our current core id. + current_core_id = phys_core; + + // We haven't released our resource limit hint, and we've spent no time on the cpu. + resource_limit_release_hint = false; + cpu_time = 0; + + // Clear our stack parameters. + std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, + sizeof(StackParameters)); + + // Setup the TLS, if needed. + if (type == ThreadType::User) { + tls_address = owner->CreateTLSRegion(); + } + + // Set parent, if relevant. + if (owner != nullptr) { + parent = owner; + parent->IncrementThreadCount(); + } - if (owner_process) { - owner_process->UnregisterThread(this); + // Initialize thread context. + ResetThreadContext64(thread_context_64, user_stack_top, func, arg); + ResetThreadContext32(thread_context_32, static_cast<u32>(user_stack_top), + static_cast<u32>(func), static_cast<u32>(arg)); - // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSRegion(tls_address); + // Setup the stack parameters. + StackParameters& sp = GetStackParameters(); + sp.cur_thread = this; + sp.disable_count = 1; + SetInExceptionHandler(); + + // Set thread ID. + thread_id = kernel.CreateNewThreadID(); + + // We initialized! + initialized = true; + + // Register ourselves with our parent process. + if (parent != nullptr) { + parent->RegisterThread(this); + if (parent->IsSuspended()) { + RequestSuspend(SuspendType::Process); } - has_exited = true; } - global_handle = 0; -} -void KThread::Wakeup() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); + return RESULT_SUCCESS; } -ResultCode KThread::Start() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); +ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 core, Process* owner, + ThreadType type) { + // Initialize the thread. + R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); + return RESULT_SUCCESS; } -void KThread::CancelWait() { - KScopedSchedulerLock lock(kernel); - if (GetState() != ThreadState::Waiting || !is_cancellable) { - is_sync_cancelled = true; - return; +void KThread::Finalize() { + // If the thread has an owner process, unregister it. + if (parent != nullptr) { + parent->UnregisterThread(this); + } + + // If the thread has a local region, delete it. + if (tls_address != 0) { + parent->FreeTLSRegion(tls_address); + } + + // Release any waiters. + { + ASSERT(lock_owner == nullptr); + KScopedSchedulerLock sl{kernel}; + + auto it = waiter_list.begin(); + while (it != waiter_list.end()) { + // The thread shouldn't be a kernel waiter. + it->SetLockOwner(nullptr); + it->SetSyncedObject(nullptr, Svc::ResultInvalidState); + it->Wakeup(); + it = waiter_list.erase(it); + } + } + + // Decrement the parent process's thread count. + if (parent != nullptr) { + parent->DecrementThreadCount(); } - // TODO(Blinkhawk): Implement cancel of server session - is_sync_cancelled = false; - SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - SetState(ThreadState::Runnable); } -static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, - u32 entry_point, u32 arg) { - context = {}; - context.cpu_registers[0] = arg; - context.cpu_registers[15] = entry_point; - context.cpu_registers[13] = stack_top; +bool KThread::IsSignaled() const { + return signaled; } -static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top, - VAddr entry_point, u64 arg) { - context = {}; - context.cpu_registers[0] = arg; - context.pc = entry_point; - context.sp = stack_top; - // TODO(merry): Perform a hardware test to determine the below value. - context.fpcr = 0; +void KThread::Wakeup() { + KScopedSchedulerLock sl{kernel}; + + if (GetState() == ThreadState::Waiting) { + if (sleeping_queue != nullptr) { + sleeping_queue->WakeupThread(this); + } else { + SetState(ThreadState::Runnable); + } + } } -std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { - return host_context; +void KThread::StartTermination() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Release user exception and unpin, if relevant. + if (parent != nullptr) { + parent->ReleaseUserException(this); + if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { + parent->UnpinCurrentThread(); + } + } + + // Set state to terminated. + SetState(ThreadState::Terminated); + + // Clear the thread's status as running in parent. + if (parent != nullptr) { + parent->ClearRunningThread(this); + } + + // Signal. + signaled = true; + NotifyAvailable(); + + // Clear previous thread in KScheduler. + KScheduler::ClearPreviousThread(kernel, this); + + // Register terminated dpc flag. + RegisterDpc(DpcFlag::Terminated); } -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process) { - std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); - void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, - owner_process, std::move(init_func), init_func_parameter); +void KThread::Pin() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Set ourselves as pinned. + GetStackParameters().is_pinned = true; + + // Disable core migration. + ASSERT(num_core_migration_disables == 0); + { + ++num_core_migration_disables; + + // Save our ideal state to restore when we're unpinned. + original_physical_ideal_core_id = physical_ideal_core_id; + original_physical_affinity_mask = physical_affinity_mask; + + // Bind ourselves to this core. + const s32 active_core = GetActiveCore(); + const s32 current_core = GetCurrentCoreId(kernel); + + SetActiveCore(current_core); + physical_ideal_core_id = current_core; + physical_affinity_mask.SetAffinityMask(1ULL << current_core); + + if (active_core != current_core || physical_affinity_mask.GetAffinityMask() != + original_physical_affinity_mask.GetAffinityMask()) { + KScheduler::OnThreadAffinityMaskChanged(kernel, this, original_physical_affinity_mask, + active_core); + } + } + + // Disallow performing thread suspension. + { + // Update our allow flags. + suspend_allowed_flags &= ~(1 << (static_cast<u32>(SuspendType::Thread) + + static_cast<u32>(ThreadState::SuspendShift))); + + // Update our state. + const ThreadState old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } + } + + // TODO(bunnei): Update our SVC access permissions. + ASSERT(parent != nullptr); } -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process, - std::function<void(void*)>&& thread_start_func, - void* thread_start_parameter) { - auto& kernel = system.Kernel(); +void KThread::Unpin() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Set ourselves as unpinned. + GetStackParameters().is_pinned = false; + + // Enable core migration. + ASSERT(num_core_migration_disables == 1); + { + --num_core_migration_disables; + + // Restore our original state. + const KAffinityMask old_mask = physical_affinity_mask; - if (owner_process) { - if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { - LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); - // TODO (bunnei): Find the correct error code to use here - return RESULT_UNKNOWN; + physical_ideal_core_id = original_physical_ideal_core_id; + physical_affinity_mask = original_physical_affinity_mask; + + if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = GetActiveCore(); + + if (!physical_affinity_mask.GetAffinity(active_core)) { + if (physical_ideal_core_id >= 0) { + SetActiveCore(physical_ideal_core_id); + } else { + SetActiveCore(static_cast<s32>( + Common::BitSize<u64>() - 1 - + std::countl_zero(physical_affinity_mask.GetAffinityMask()))); + } + } + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); } } - std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); + // Allow performing thread suspension (if termination hasn't been requested). + { + // Update our allow flags. + if (!IsTerminationRequested()) { + suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) + + static_cast<u32>(ThreadState::SuspendShift))); + } - thread->thread_id = kernel.CreateNewThreadID(); - thread->thread_state = ThreadState::Initialized; - thread->entry_point = entry_point; - thread->stack_top = stack_top; - thread->disable_count = 1; - thread->tpidr_el0 = 0; - thread->current_priority = priority; - thread->base_priority = priority; - thread->lock_owner = nullptr; - thread->schedule_count = -1; - thread->last_scheduled_tick = 0; - thread->processor_id = processor_id; - thread->ideal_core = processor_id; - thread->affinity_mask.SetAffinity(processor_id, true); - thread->name = std::move(name); - thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); - thread->owner_process = owner_process; - thread->type = type_flags; - thread->signaled = false; + // Update our state. + const ThreadState old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } + } - auto& scheduler = kernel.GlobalSchedulerContext(); - scheduler.AddThread(thread); + // TODO(bunnei): Update our SVC access permissions. + ASSERT(parent != nullptr); + + // Resume any threads that began waiting on us while we were pinned. + for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { + if (it->GetState() == ThreadState::Waiting) { + it->SetState(ThreadState::Runnable); + } + } +} + +ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; + + // Get the virtual mask. + *out_ideal_core = virtual_ideal_core_id; + *out_affinity_mask = virtual_affinity_mask; + + return RESULT_SUCCESS; +} - if (owner_process) { - thread->tls_address = thread->owner_process->CreateTLSRegion(); - thread->owner_process->RegisterThread(thread.get()); +ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; + ASSERT(num_core_migration_disables >= 0); + + // Select between core mask and original core mask. + if (num_core_migration_disables == 0) { + *out_ideal_core = physical_ideal_core_id; + *out_affinity_mask = physical_affinity_mask.GetAffinityMask(); } else { - thread->tls_address = 0; + *out_ideal_core = original_physical_ideal_core_id; + *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask(); } - ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), - static_cast<u32>(entry_point), static_cast<u32>(arg)); - ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); + return RESULT_SUCCESS; +} - thread->host_context = - std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); +ResultCode KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) { + ASSERT(parent != nullptr); + ASSERT(v_affinity_mask != 0); + KScopedLightLock lk{activity_pause_lock}; - return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); + // Set the core mask. + u64 p_affinity_mask = 0; + { + KScopedSchedulerLock sl{kernel}; + ASSERT(num_core_migration_disables >= 0); + + // If the core id is no-update magic, preserve the ideal core id. + if (core_id == Svc::IdealCoreNoUpdate) { + core_id = virtual_ideal_core_id; + R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, Svc::ResultInvalidCombination); + } + + // Set the virtual core/affinity mask. + virtual_ideal_core_id = core_id; + virtual_affinity_mask = v_affinity_mask; + + // Translate the virtual core to a physical core. + if (core_id >= 0) { + core_id = Core::Hardware::VirtualToPhysicalCoreMap[core_id]; + } + + // Translate the virtual affinity mask to a physical one. + while (v_affinity_mask != 0) { + const u64 next = std::countr_zero(v_affinity_mask); + v_affinity_mask &= ~(1ULL << next); + p_affinity_mask |= (1ULL << Core::Hardware::VirtualToPhysicalCoreMap[next]); + } + + // If we haven't disabled migration, perform an affinity change. + if (num_core_migration_disables == 0) { + const KAffinityMask old_mask = physical_affinity_mask; + + // Set our new ideals. + physical_ideal_core_id = core_id; + physical_affinity_mask.SetAffinityMask(p_affinity_mask); + + if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = GetActiveCore(); + + if (active_core >= 0 && !physical_affinity_mask.GetAffinity(active_core)) { + const s32 new_core = static_cast<s32>( + physical_ideal_core_id >= 0 + ? physical_ideal_core_id + : Common::BitSize<u64>() - 1 - + std::countl_zero(physical_affinity_mask.GetAffinityMask())); + SetActiveCore(new_core); + } + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); + } + } else { + // Otherwise, we edit the original affinity for restoration later. + original_physical_ideal_core_id = core_id; + original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); + } + } + + // Update the pinned waiter list. + { + bool retry_update = false; + bool thread_is_pinned = false; + do { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(IsTerminationRequested()); + + // By default, we won't need to retry. + retry_update = false; + + // Check if the thread is currently running. + bool thread_is_current = false; + s32 thread_core; + for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); + ++thread_core) { + if (kernel.Scheduler(thread_core).GetCurrentThread() == this) { + thread_is_current = true; + break; + } + } + + // If the thread is currently running, check whether it's no longer allowed under the + // new mask. + if (thread_is_current && ((1ULL << thread_core) & p_affinity_mask) == 0) { + // If the thread is pinned, we want to wait until it's not pinned. + if (GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Note that the thread was pinned. + thread_is_pinned = true; + + // Wait until the thread isn't pinned any more. + pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).SetState(ThreadState::Waiting); + } else { + // If the thread isn't pinned, release the scheduler lock and retry until it's + // not current. + retry_update = true; + } + } + } while (retry_update); + + // If the thread was pinned, it no longer is, and we should remove the current thread from + // our waiter list. + if (thread_is_pinned) { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Remove from the list. + pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); + } + } + + return RESULT_SUCCESS; } -void KThread::SetBasePriority(u32 priority) { - ASSERT(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority); +void KThread::SetBasePriority(s32 value) { + ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority); - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; // Change our base priority. - base_priority = priority; + base_priority = value; // Perform a priority restoration. RestorePriority(kernel, this); } -void KThread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { - signaling_object = object; - signaling_result = result; -} +void KThread::RequestSuspend(SuspendType type) { + KScopedSchedulerLock sl{kernel}; -VAddr KThread::GetCommandBufferAddress() const { - // Offset from the start of TLS at which the IPC command buffer begins. - constexpr u64 command_header_offset = 0x80; - return GetTLSAddress() + command_header_offset; + // Note the request in our flags. + suspend_request_flags |= + (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); + + // Try to perform the suspend. + TrySuspend(); } -void KThread::SetState(ThreadState state) { - KScopedSchedulerLock sl(kernel); +void KThread::Resume(SuspendType type) { + KScopedSchedulerLock sl{kernel}; - // Clear debugging state - SetMutexWaitAddressForDebugging({}); - SetWaitReasonForDebugging({}); + // Clear the request in our flags. + suspend_request_flags &= + ~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); + // Update our state. const ThreadState old_state = thread_state; - thread_state = - static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); if (thread_state != old_state) { KScheduler::OnThreadStateChanged(kernel, this, old_state); } } +void KThread::WaitCancel() { + KScopedSchedulerLock sl{kernel}; + + // Check if we're waiting and cancellable. + if (GetState() == ThreadState::Waiting && cancellable) { + if (sleeping_queue != nullptr) { + sleeping_queue->WakeupThread(this); + wait_cancelled = true; + } else { + SetSyncedObject(nullptr, Svc::ResultCancelled); + SetState(ThreadState::Runnable); + wait_cancelled = false; + } + } else { + // Otherwise, note that we cancelled a wait. + wait_cancelled = true; + } +} + +void KThread::TrySuspend() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSuspendRequested()); + + // Ensure that we have no waiters. + if (GetNumKernelWaiters() > 0) { + return; + } + ASSERT(GetNumKernelWaiters() == 0); + + // Perform the suspend. + Suspend(); +} + +void KThread::Suspend() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSuspendRequested()); + + // Set our suspend flags in state. + const auto old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask); + + // Note the state change in scheduler. + KScheduler::OnThreadStateChanged(kernel, this, old_state); +} + +void KThread::Continue() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Clear our suspend flags in state. + const auto old_state = thread_state; + thread_state = old_state & ThreadState::Mask; + + // Note the state change in scheduler. + KScheduler::OnThreadStateChanged(kernel, this, old_state); +} + +ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { + // Lock ourselves. + KScopedLightLock lk(activity_pause_lock); + + // Set the activity. + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Verify our state. + const auto cur_state = GetState(); + R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), + Svc::ResultInvalidState); + + // Either pause or resume. + if (activity == Svc::ThreadActivity::Paused) { + // Verify that we're not suspended. + R_UNLESS(!IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // Suspend. + RequestSuspend(SuspendType::Thread); + } else { + ASSERT(activity == Svc::ThreadActivity::Runnable); + + // Verify that we're suspended. + R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // Resume. + Resume(SuspendType::Thread); + } + } + + // If the thread is now paused, update the pinned waiter list. + if (activity == Svc::ThreadActivity::Paused) { + bool thread_is_pinned = false; + bool thread_is_current; + do { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(IsTerminationRequested()); + + // Check whether the thread is pinned. + if (GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Note that the thread was pinned and not current. + thread_is_pinned = true; + thread_is_current = false; + + // Wait until the thread isn't pinned any more. + pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).SetState(ThreadState::Waiting); + } else { + // Check if the thread is currently running. + // If it is, we'll need to retry. + thread_is_current = false; + + for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { + if (kernel.Scheduler(i).GetCurrentThread() == this) { + thread_is_current = true; + break; + } + } + } + } while (thread_is_current); + + // If the thread was pinned, it no longer is, and we should remove the current thread from + // our waiter list. + if (thread_is_pinned) { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Remove from the list. + pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); + } + } + + return RESULT_SUCCESS; +} + +ResultCode KThread::GetThreadContext3(std::vector<u8>& out) { + // Lock ourselves. + KScopedLightLock lk{activity_pause_lock}; + + // Get the context. + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Verify that we're suspended. + R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // If we're not terminating, get the thread's user context. + if (!IsTerminationRequested()) { + if (parent->Is64BitProcess()) { + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + auto context = GetContext64(); + context.pstate &= 0xFF0FFE20; + + out.resize(sizeof(context)); + std::memcpy(out.data(), &context, sizeof(context)); + } else { + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + auto context = GetContext32(); + context.cpsr &= 0xFF0FFE20; + + out.resize(sizeof(context)); + std::memcpy(out.data(), &context, sizeof(context)); + } + } + } + + return RESULT_SUCCESS; +} + void KThread::AddWaiterImpl(KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -345,104 +898,150 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { return next_lock_owner; } -ResultCode KThread::SetActivity(ThreadActivity value) { - KScopedSchedulerLock lock(kernel); +ResultCode KThread::Run() { + while (true) { + KScopedSchedulerLock lk{kernel}; - auto sched_status = GetState(); + // If either this thread or the current thread are requesting termination, note it. + R_UNLESS(!IsTerminationRequested(), Svc::ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); - if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { - return ERR_INVALID_STATE; - } + // Ensure our thread state is correct. + R_UNLESS(GetState() == ThreadState::Initialized, Svc::ResultInvalidState); - if (IsTerminationRequested()) { + // If the current thread has been asked to suspend, suspend it and retry. + if (GetCurrentThread(kernel).IsSuspended()) { + GetCurrentThread(kernel).Suspend(); + continue; + } + + // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. + if (IsUserThread() && IsSuspended()) { + Suspend(); + } + + // Set our state and finish. + SetState(ThreadState::Runnable); return RESULT_SUCCESS; } +} - if (value == ThreadActivity::Paused) { - if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) { - return ERR_INVALID_STATE; - } - AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); - } else { - if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) { - return ERR_INVALID_STATE; - } - RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); +void KThread::Exit() { + ASSERT(this == GetCurrentThreadPointer(kernel)); + + // Release the thread resource hint from parent. + if (parent != nullptr) { + // TODO(bunnei): Hint that the resource is about to be released. + resource_limit_release_hint = true; + } + + // Perform termination. + { + KScopedSchedulerLock sl{kernel}; + + // Disallow all suspension. + suspend_allowed_flags = 0; + + // Start termination. + StartTermination(); } - return RESULT_SUCCESS; } -ResultCode KThread::Sleep(s64 nanoseconds) { - Handle event_handle{}; +ResultCode KThread::Sleep(s64 timeout) { + ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(this == GetCurrentThreadPointer(kernel)); + ASSERT(timeout > 0); + { - KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); + // Setup the scheduling lock and sleep. + KScopedSchedulerLockAndSleep slp{kernel, this, timeout}; + + // Check if the thread should terminate. + if (IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Mark the thread as waiting. SetState(ThreadState::Waiting); SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } + // The lock/sleep is done. + + // Cancel the timer. + kernel.TimeManager().UnscheduleTimeEvent(this); + return RESULT_SUCCESS; } -void KThread::AddSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state |= static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); +void KThread::SetState(ThreadState state) { + KScopedSchedulerLock sl{kernel}; + + // Clear debugging state + SetMutexWaitAddressForDebugging({}); + SetWaitReasonForDebugging({}); + + const ThreadState old_state = thread_state; + thread_state = + static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } } -void KThread::RemoveSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state &= ~static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); +std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { + return host_context; } -ResultCode KThread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { - KScopedSchedulerLock lock(kernel); - const auto HighestSetCore = [](u64 mask, u32 max_cores) { - for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { - if (((mask >> core) & 1) != 0) { - return core; - } - } - return -1; - }; - - const bool use_override = affinity_override_count != 0; - if (new_core == Svc::IdealCoreNoUpdate) { - new_core = use_override ? ideal_core_override : ideal_core; - if ((new_affinity_mask & (1ULL << new_core)) == 0) { - LOG_ERROR(Kernel, "New affinity mask is incorrect! new_core={}, new_affinity_mask={}", - new_core, new_affinity_mask); - return ERR_INVALID_COMBINATION; - } - } - if (use_override) { - ideal_core_override = new_core; - } else { - const auto old_affinity_mask = affinity_mask; - affinity_mask.SetAffinityMask(new_affinity_mask); - ideal_core = new_core; - if (old_affinity_mask.GetAffinityMask() != new_affinity_mask) { - const s32 old_core = processor_id; - if (processor_id >= 0 && !affinity_mask.GetAffinity(processor_id)) { - if (static_cast<s32>(ideal_core) < 0) { - processor_id = HighestSetCore(affinity_mask.GetAffinityMask(), - Core::Hardware::NUM_CPU_CORES); - } else { - processor_id = ideal_core; - } - } - KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_affinity_mask, old_core); - } +ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process) { + std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, + owner_process, std::move(init_func), init_func_parameter); +} + +ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process, + std::function<void(void*)>&& thread_start_func, + void* thread_start_parameter) { + auto& kernel = system.Kernel(); + + std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); + + thread->InitializeThread(thread.get(), entry_point, arg, stack_top, priority, processor_id, + owner_process, type_flags); + thread->name = name; + + auto& scheduler = kernel.GlobalSchedulerContext(); + scheduler.AddThread(thread); + + thread->host_context = + std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); + + return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); +} + +KThread* GetCurrentThreadPointer(KernelCore& kernel) { + if (!kernel.CurrentScheduler()) { + // We are not called from a core thread + return {}; } - return RESULT_SUCCESS; + return kernel.CurrentScheduler()->GetCurrentThread(); +} + +KThread& GetCurrentThread(KernelCore& kernel) { + return *GetCurrentThreadPointer(kernel); +} + +s32 GetCurrentCoreId(KernelCore& kernel) { + return GetCurrentThread(kernel).GetCurrentCore(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index ef2313f87..7845821ba 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -1,11 +1,10 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include <array> -#include <functional> #include <span> #include <string> #include <utility> @@ -18,9 +17,11 @@ #include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" +#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" namespace Common { @@ -38,6 +39,9 @@ class GlobalSchedulerContext; class KernelCore; class Process; class KScheduler; +class KThreadQueue; + +using KThreadFunction = VAddr; enum class ThreadType : u32 { Main = 0, @@ -47,6 +51,16 @@ enum class ThreadType : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(ThreadType); +enum class SuspendType : u32 { + Process = 0, + Thread = 1, + Debug = 2, + Backtrace = 3, + Init = 4, + + Count, +}; + enum class ThreadState : u16 { Initialized = 0, Waiting = 1, @@ -66,21 +80,9 @@ enum class ThreadState : u16 { }; DECLARE_ENUM_FLAG_OPERATORS(ThreadState); -enum class ThreadWakeupReason { - Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. - Timeout // The thread was woken up due to a wait timeout. -}; - -enum class ThreadActivity : u32 { - Normal = 0, - Paused = 1, -}; - -enum class ThreadSchedFlags : u32 { - ProcessPauseFlag = 1 << 4, - ThreadPauseFlag = 1 << 5, - ProcessDebugPauseFlag = 1 << 6, - KernelInitPauseFlag = 1 << 8, +enum class DpcFlag : u32 { + Terminating = (1 << 0), + Terminated = (1 << 1), }; enum class ThreadWaitReasonForDebugging : u32 { @@ -93,21 +95,25 @@ enum class ThreadWaitReasonForDebugging : u32 { Suspended, ///< Thread is waiting due to process suspension }; +[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); +[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); +[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); + class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { friend class KScheduler; friend class Process; public: static constexpr s32 DefaultThreadPriority = 44; - static constexpr s32 IdleThreadPriority = 64; + static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1; explicit KThread(KernelCore& kernel); ~KThread() override; - using MutexWaitingThreads = std::vector<std::shared_ptr<KThread>>; - +public: using ThreadContext32 = Core::ARM_Interface::ThreadContext32; using ThreadContext64 = Core::ARM_Interface::ThreadContext64; + using WaiterList = boost::intrusive::list<KThread>; /** * Creates and returns a new thread. The new thread is immediately scheduled @@ -121,10 +127,9 @@ public: * @param owner_process The parent process for the thread, if null, it's a kernel thread * @return A shared pointer to the newly created thread */ - static ResultVal<std::shared_ptr<KThread>> Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process); + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); /** * Creates and returns a new thread. The new thread is immediately scheduled @@ -140,12 +145,12 @@ public: * @param thread_start_parameter The parameter which will passed to host context on init * @return A shared pointer to the newly created thread */ - static ResultVal<std::shared_ptr<KThread>> Create( + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); - std::string GetName() const override { + [[nodiscard]] std::string GetName() const override { return name; } @@ -153,12 +158,12 @@ public: name = std::move(new_name); } - std::string GetTypeName() const override { + [[nodiscard]] std::string GetTypeName() const override { return "Thread"; } static constexpr HandleType HANDLE_TYPE = HandleType::Thread; - HandleType GetHandleType() const override { + [[nodiscard]] HandleType GetHandleType() const override { return HANDLE_TYPE; } @@ -167,15 +172,15 @@ public: * @return The current thread's priority */ [[nodiscard]] s32 GetPriority() const { - return current_priority; + return priority; } /** * Sets the thread's current priority. * @param priority The new priority. */ - void SetPriority(s32 priority) { - current_priority = priority; + void SetPriority(s32 value) { + priority = value; } /** @@ -187,15 +192,6 @@ public: } /** - * Sets the thread's nominal priority. - * @param priority The new priority. - */ - void SetBasePriority(u32 priority); - - /// Changes the core that the thread is running or scheduled to run on. - [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); - - /** * Gets the thread's thread ID * @return The thread's ID */ @@ -203,46 +199,67 @@ public: return thread_id; } - /// Resumes a thread from waiting + void ContinueIfHasKernelWaiters() { + if (GetNumKernelWaiters() > 0) { + Continue(); + } + } + void Wakeup(); - ResultCode Start(); + void SetBasePriority(s32 value); - virtual bool IsSignaled() const override; + [[nodiscard]] ResultCode Run(); - /// Cancels a waiting operation that this thread may or may not be within. - /// - /// When the thread is within a waiting state, this will set the thread's - /// waiting result to signal a canceled wait. The function will then resume - /// this thread. - /// - void CancelWait(); + void Exit(); - void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); + [[nodiscard]] u32 GetSuspendFlags() const { + return suspend_allowed_flags & suspend_request_flags; + } - void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { - SetSynchronizationResults(object, result); + [[nodiscard]] bool IsSuspended() const { + return GetSuspendFlags() != 0; } - ResultCode GetWaitResult(KSynchronizationObject** out) const { - *out = signaling_object; - return signaling_result; + [[nodiscard]] bool IsSuspendRequested(SuspendType type) const { + return (suspend_request_flags & + (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) != + 0; } - ResultCode GetSignalingResult() const { - return signaling_result; + [[nodiscard]] bool IsSuspendRequested() const { + return suspend_request_flags != 0; } - /** - * Stops a thread, invalidating it from further use - */ - void Stop(); + void RequestSuspend(SuspendType type); + + void Resume(SuspendType type); + + void TrySuspend(); + + void Continue(); + + void Suspend(); + + void Finalize() override; + + bool IsSignaled() const override; + + void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) { + synced_object = obj; + wait_result = wait_res; + } + + [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { + *out = synced_object; + return wait_result; + } /* * Returns the Thread Local Storage address of the current thread * @returns VAddr of the thread's TLS */ - VAddr GetTLSAddress() const { + [[nodiscard]] VAddr GetTLSAddress() const { return tls_address; } @@ -250,62 +267,45 @@ public: * Returns the value of the TPIDR_EL0 Read/Write system register for this thread. * @returns The value of the TPIDR_EL0 register. */ - u64 GetTPIDR_EL0() const { - return tpidr_el0; + [[nodiscard]] u64 GetTPIDR_EL0() const { + return thread_context_64.tpidr; } /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. void SetTPIDR_EL0(u64 value) { - tpidr_el0 = value; + thread_context_64.tpidr = value; + thread_context_32.tpidr = static_cast<u32>(value); } - /* - * Returns the address of the current thread's command buffer, located in the TLS. - * @returns VAddr of the thread's command buffer. - */ - VAddr GetCommandBufferAddress() const; - - ThreadContext32& GetContext32() { - return context_32; - } - - const ThreadContext32& GetContext32() const { - return context_32; + [[nodiscard]] ThreadContext32& GetContext32() { + return thread_context_32; } - ThreadContext64& GetContext64() { - return context_64; + [[nodiscard]] const ThreadContext32& GetContext32() const { + return thread_context_32; } - const ThreadContext64& GetContext64() const { - return context_64; + [[nodiscard]] ThreadContext64& GetContext64() { + return thread_context_64; } - bool IsKernelThread() const { - return type == ThreadType::Kernel; + [[nodiscard]] const ThreadContext64& GetContext64() const { + return thread_context_64; } - bool WasRunning() const { - return was_running; - } - - void SetWasRunning(bool value) { - was_running = value; - } + [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); - std::shared_ptr<Common::Fiber>& GetHostContext(); - - ThreadState GetState() const { + [[nodiscard]] ThreadState GetState() const { return thread_state & ThreadState::Mask; } - ThreadState GetRawState() const { + [[nodiscard]] ThreadState GetRawState() const { return thread_state; } void SetState(ThreadState state); - s64 GetLastScheduledTick() const { + [[nodiscard]] s64 GetLastScheduledTick() const { return last_scheduled_tick; } @@ -313,43 +313,44 @@ public: last_scheduled_tick = tick; } - u64 GetTotalCPUTimeTicks() const { - return total_cpu_time_ticks; + void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) { + cpu_time += amount; + // TODO(bunnei): Debug kernels track per-core tick counts. Should we? } - void UpdateCPUTimeTicks(u64 ticks) { - total_cpu_time_ticks += ticks; + [[nodiscard]] s64 GetCpuTime() const { + return cpu_time; } - s32 GetProcessorID() const { - return processor_id; + [[nodiscard]] s32 GetActiveCore() const { + return core_id; } - s32 GetActiveCore() const { - return GetProcessorID(); + void SetActiveCore(s32 core) { + core_id = core; } - void SetProcessorID(s32 new_core) { - processor_id = new_core; + [[nodiscard]] s32 GetCurrentCore() const { + return current_core_id; } - void SetActiveCore(s32 new_core) { - processor_id = new_core; + void SetCurrentCore(s32 core) { + current_core_id = core; } - Process* GetOwnerProcess() { - return owner_process; + [[nodiscard]] Process* GetOwnerProcess() { + return parent; } - const Process* GetOwnerProcess() const { - return owner_process; + [[nodiscard]] const Process* GetOwnerProcess() const { + return parent; } - const MutexWaitingThreads& GetMutexWaitingThreads() const { - return wait_mutex_threads; + [[nodiscard]] bool IsUserThread() const { + return parent != nullptr; } - KThread* GetLockOwner() const { + [[nodiscard]] KThread* GetLockOwner() const { return lock_owner; } @@ -357,20 +358,21 @@ public: lock_owner = owner; } - u32 GetIdealCore() const { - return ideal_core; + [[nodiscard]] const KAffinityMask& GetAffinityMask() const { + return physical_affinity_mask; } - const KAffinityMask& GetAffinityMask() const { - return affinity_mask; - } + [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + + [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); - ResultCode SetActivity(ThreadActivity value); + [[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask); - /// Sleeps this thread for the given amount of nanoseconds. - ResultCode Sleep(s64 nanoseconds); + [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity); - s64 GetYieldScheduleCount() const { + [[nodiscard]] ResultCode Sleep(s64 timeout); + + [[nodiscard]] s64 GetYieldScheduleCount() const { return schedule_count; } @@ -378,56 +380,49 @@ public: schedule_count = count; } - bool IsRunning() const { - return is_running; - } - - void SetIsRunning(bool value) { - is_running = value; - } - - bool IsWaitCancelled() const { - return is_sync_cancelled; - } + void WaitCancel(); - void ClearWaitCancelled() { - is_sync_cancelled = false; + [[nodiscard]] bool IsWaitCancelled() const { + return wait_cancelled; } - Handle GetGlobalHandle() const { - return global_handle; + [[nodiscard]] void ClearWaitCancelled() { + wait_cancelled = false; } - bool IsCancellable() const { - return is_cancellable; + [[nodiscard]] bool IsCancellable() const { + return cancellable; } void SetCancellable() { - is_cancellable = true; + cancellable = true; } void ClearCancellable() { - is_cancellable = false; - } - - bool IsTerminationRequested() const { - return will_be_terminated || GetRawState() == ThreadState::Terminated; + cancellable = false; } - bool IsPaused() const { - return pausing_state != 0; + [[nodiscard]] bool IsTerminationRequested() const { + return termination_requested || GetRawState() == ThreadState::Terminated; } - bool IsContinuousOnSVC() const { - return is_continuous_on_svc; - } + struct StackParameters { + u8 svc_permission[0x10]; + std::atomic<u8> dpc_flags; + u8 current_svc_id; + bool is_calling_svc; + bool is_in_exception_handler; + bool is_pinned; + s32 disable_count; + KThread* cur_thread; + }; - void SetContinuousOnSVC(bool is_continuous) { - is_continuous_on_svc = is_continuous; + [[nodiscard]] StackParameters& GetStackParameters() { + return stack_parameters; } - bool HasExited() const { - return has_exited; + [[nodiscard]] const StackParameters& GetStackParameters() const { + return stack_parameters; } class QueueEntry { @@ -457,26 +452,78 @@ public: KThread* next{}; }; - QueueEntry& GetPriorityQueueEntry(s32 core) { + [[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) { return per_core_priority_queue_entry[core]; } - const QueueEntry& GetPriorityQueueEntry(s32 core) const { + [[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const { return per_core_priority_queue_entry[core]; } - s32 GetDisableDispatchCount() const { - return disable_count; + void SetSleepingQueue(KThreadQueue* q) { + sleeping_queue = q; + } + + [[nodiscard]] s32 GetDisableDispatchCount() const { + return this->GetStackParameters().disable_count; } void DisableDispatch() { - ASSERT(GetDisableDispatchCount() >= 0); - disable_count++; + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); + this->GetStackParameters().disable_count++; } void EnableDispatch() { - ASSERT(GetDisableDispatchCount() > 0); - disable_count--; + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); + this->GetStackParameters().disable_count--; + } + + void Pin(); + + void Unpin(); + + void SetInExceptionHandler() { + this->GetStackParameters().is_in_exception_handler = true; + } + + void ClearInExceptionHandler() { + this->GetStackParameters().is_in_exception_handler = false; + } + + [[nodiscard]] bool IsInExceptionHandler() const { + return this->GetStackParameters().is_in_exception_handler; + } + + void SetIsCallingSvc() { + this->GetStackParameters().is_calling_svc = true; + } + + void ClearIsCallingSvc() { + this->GetStackParameters().is_calling_svc = false; + } + + [[nodiscard]] bool IsCallingSvc() const { + return this->GetStackParameters().is_calling_svc; + } + + [[nodiscard]] u8 GetSvcId() const { + return this->GetStackParameters().current_svc_id; + } + + void RegisterDpc(DpcFlag flag) { + this->GetStackParameters().dpc_flags |= static_cast<u8>(flag); + } + + void ClearDpc(DpcFlag flag) { + this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag); + } + + [[nodiscard]] u8 GetDpc() const { + return this->GetStackParameters().dpc_flags; + } + + [[nodiscard]] bool HasDpc() const { + return this->GetDpc() != 0; } void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { @@ -507,10 +554,16 @@ public: return mutex_wait_address_for_debugging; } + [[nodiscard]] s32 GetIdealCoreForDebugging() const { + return virtual_ideal_core_id; + } + void AddWaiter(KThread* thread); void RemoveWaiter(KThread* thread); + [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out); + [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); [[nodiscard]] VAddr GetAddressKey() const { @@ -530,6 +583,22 @@ public: address_key_value = val; } + [[nodiscard]] bool HasWaiters() const { + return !waiter_list.empty(); + } + + [[nodiscard]] s32 GetNumKernelWaiters() const { + return num_kernel_waiters; + } + + [[nodiscard]] u64 GetConditionVariableKey() const { + return condvar_key; + } + + [[nodiscard]] u64 GetAddressArbiterKey() const { + return condvar_key; + } + private: static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { @@ -560,8 +629,8 @@ private: std::same_as<T, KThread> || std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, const KThread& rhs) { - const uintptr_t l_key = lhs.GetConditionVariableKey(); - const uintptr_t r_key = rhs.GetConditionVariableKey(); + const u64 l_key = lhs.GetConditionVariableKey(); + const u64 r_key = rhs.GetConditionVariableKey(); if (l_key < r_key) { // Sort first by key @@ -575,26 +644,88 @@ private: } }; - Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; + void AddWaiterImpl(KThread* thread); + + void RemoveWaiterImpl(KThread* thread); + void StartTermination(); + + [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, + s32 prio, s32 virt_core, Process* owner, ThreadType type); + + [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 core, Process* owner, ThreadType type); + + static void RestorePriority(KernelCore& kernel, KThread* thread); + + // For core KThread implementation + ThreadContext32 thread_context_32{}; + ThreadContext64 thread_context_64{}; + Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; + s32 priority{}; using ConditionVariableThreadTreeTraits = Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert< &KThread::condvar_arbiter_tree_node>; using ConditionVariableThreadTree = ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; + ConditionVariableThreadTree* condvar_tree{}; + u64 condvar_key{}; + u64 virtual_affinity_mask{}; + KAffinityMask physical_affinity_mask{}; + u64 thread_id{}; + std::atomic<s64> cpu_time{}; + KSynchronizationObject* synced_object{}; + VAddr address_key{}; + Process* parent{}; + VAddr kernel_stack_top{}; + u32* light_ipc_data{}; + VAddr tls_address{}; + KLightLock activity_pause_lock; + s64 schedule_count{}; + s64 last_scheduled_tick{}; + std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; + KThreadQueue* sleeping_queue{}; + WaiterList waiter_list{}; + WaiterList pinned_waiter_list{}; + KThread* lock_owner{}; + u32 address_key_value{}; + u32 suspend_request_flags{}; + u32 suspend_allowed_flags{}; + ResultCode wait_result{RESULT_SUCCESS}; + s32 base_priority{}; + s32 physical_ideal_core_id{}; + s32 virtual_ideal_core_id{}; + s32 num_kernel_waiters{}; + s32 current_core_id{}; + s32 core_id{}; + KAffinityMask original_physical_affinity_mask{}; + s32 original_physical_ideal_core_id{}; + s32 num_core_migration_disables{}; + ThreadState thread_state{}; + std::atomic<bool> termination_requested{}; + bool wait_cancelled{}; + bool cancellable{}; + bool signaled{}; + bool initialized{}; + bool debug_attached{}; + s8 priority_inheritance_count{}; + bool resource_limit_release_hint{}; + StackParameters stack_parameters{}; -public: - using ConditionVariableThreadTreeType = ConditionVariableThreadTree; + // For emulation + std::shared_ptr<Common::Fiber> host_context{}; - [[nodiscard]] uintptr_t GetConditionVariableKey() const { - return condvar_key; - } + // For debugging + std::vector<KSynchronizationObject*> wait_objects_for_debugging; + VAddr mutex_wait_address_for_debugging{}; + ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + std::string name; - [[nodiscard]] uintptr_t GetAddressArbiterKey() const { - return condvar_key; - } +public: + using ConditionVariableThreadTreeType = ConditionVariableThreadTree; - void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key, + void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key, u32 value) { condvar_tree = tree; condvar_key = cv_key; @@ -610,7 +741,7 @@ public: return condvar_tree != nullptr; } - void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) { + void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) { condvar_tree = tree; condvar_key = address; } @@ -626,111 +757,6 @@ public: [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const { return condvar_tree; } - - [[nodiscard]] bool HasWaiters() const { - return !waiter_list.empty(); - } - -private: - void AddSchedulingFlag(ThreadSchedFlags flag); - void RemoveSchedulingFlag(ThreadSchedFlags flag); - void AddWaiterImpl(KThread* thread); - void RemoveWaiterImpl(KThread* thread); - static void RestorePriority(KernelCore& kernel, KThread* thread); - - Common::SpinLock context_guard{}; - ThreadContext32 context_32{}; - ThreadContext64 context_64{}; - std::shared_ptr<Common::Fiber> host_context{}; - - ThreadState thread_state = ThreadState::Initialized; - - u64 thread_id = 0; - - VAddr entry_point = 0; - VAddr stack_top = 0; - std::atomic_int disable_count = 0; - - ThreadType type; - - /// Nominal thread priority, as set by the emulated application. - /// The nominal priority is the thread priority without priority - /// inheritance taken into account. - s32 base_priority{}; - - /// Current thread priority. This may change over the course of the - /// thread's lifetime in order to facilitate priority inheritance. - s32 current_priority{}; - - u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. - s64 schedule_count{}; - s64 last_scheduled_tick{}; - - s32 processor_id = 0; - - VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread - u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. - - /// Process that owns this thread - Process* owner_process; - - /// Objects that the thread is waiting on, in the same order as they were - /// passed to WaitSynchronization. This is used for debugging only. - std::vector<KSynchronizationObject*> wait_objects_for_debugging; - - /// The current mutex wait address. This is used for debugging only. - VAddr mutex_wait_address_for_debugging{}; - - /// The reason the thread is waiting. This is used for debugging only. - ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - - KSynchronizationObject* signaling_object; - ResultCode signaling_result{RESULT_SUCCESS}; - - /// List of threads that are waiting for a mutex that is held by this thread. - MutexWaitingThreads wait_mutex_threads; - - /// Thread that owns the lock that this thread is waiting for. - KThread* lock_owner{}; - - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle global_handle = 0; - - KScheduler* scheduler = nullptr; - - std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; - - u32 ideal_core{0xFFFFFFFF}; - KAffinityMask affinity_mask{}; - - s32 ideal_core_override = -1; - u32 affinity_override_count = 0; - - u32 pausing_state = 0; - bool is_running = false; - bool is_cancellable = false; - bool is_sync_cancelled = false; - - bool is_continuous_on_svc = false; - - bool will_be_terminated = false; - bool has_exited = false; - - bool was_running = false; - - bool signaled{}; - - ConditionVariableThreadTree* condvar_tree{}; - uintptr_t condvar_key{}; - VAddr address_key{}; - u32 address_key_value{}; - s32 num_kernel_waiters{}; - - using WaiterList = boost::intrusive::list<KThread>; - WaiterList waiter_list{}; - WaiterList pinned_waiter_list{}; - - std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 39d5122f5..d61659453 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -117,14 +117,14 @@ struct KernelCore::Impl { void InitializePhysicalCores() { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); - for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + for (s32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i); cores.emplace_back(i, system, *schedulers[i], interrupts); } } void InitializeSchedulers() { - for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + for (s32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { cores[i].Scheduler().Initialize(); } } @@ -169,9 +169,9 @@ struct KernelCore::Impl { std::string name = "Suspend Thread Id:" + std::to_string(i); std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - auto thread_res = KThread::Create(system, ThreadType::Kernel, std::move(name), 0, 0, 0, - static_cast<u32>(i), 0, nullptr, std::move(init_func), - init_func_parameter); + auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, + 0, 0, static_cast<u32>(i), 0, nullptr, + std::move(init_func), init_func_parameter); suspend_threads[i] = std::move(thread_res).Unwrap(); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 819e275ff..9f4583b49 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -136,6 +136,23 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const { return resource_limit; } +void Process::IncrementThreadCount() { + ASSERT(num_threads >= 0); + ++num_created_threads; + + if (const auto count = ++num_threads; count > peak_num_threads) { + peak_num_threads = count; + } +} + +void Process::DecrementThreadCount() { + ASSERT(num_threads > 0); + + if (const auto count = --num_threads; count == 0) { + UNIMPLEMENTED_MSG("Process termination is not implemented!"); + } +} + u64 Process::GetTotalPhysicalMemoryAvailable() const { const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + @@ -161,6 +178,61 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } +bool Process::ReleaseUserException(KThread* thread) { + KScopedSchedulerLock sl{kernel}; + + if (exception_thread == thread) { + exception_thread = nullptr; + + // Remove waiter thread. + s32 num_waiters{}; + KThread* next = thread->RemoveWaiterByKey( + std::addressof(num_waiters), + reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); + if (next != nullptr) { + if (next->GetState() == ThreadState::Waiting) { + next->SetState(ThreadState::Runnable); + } else { + KScheduler::SetSchedulerUpdateNeeded(kernel); + } + } + + return true; + } else { + return false; + } +} + +void Process::PinCurrentThread() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); + + // Pin it. + PinThread(core_id, cur_thread); + cur_thread->Pin(); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(kernel); +} + +void Process::UnpinCurrentThread() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); + + // Unpin it. + cur_thread->Unpin(); + UnpinThread(core_id, cur_thread); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(kernel); +} + void Process::RegisterThread(const KThread* thread) { thread_list.push_back(thread); } @@ -278,7 +350,7 @@ void Process::PrepareForTermination() { ASSERT_MSG(thread->GetState() == ThreadState::Waiting, "Exiting processes with non-waiting threads is currently unimplemented"); - thread->Stop(); + thread->Exit(); } }; diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 917babfb4..11d78f3a8 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -217,6 +217,14 @@ public: return is_64bit_process; } + [[nodiscard]] bool IsSuspended() const { + return is_suspended; + } + + void SetSuspended(bool suspended) { + is_suspended = suspended; + } + /// Gets the total running time of the process instance in ticks. u64 GetCPUTimeTicks() const { return total_process_running_time_ticks; @@ -237,6 +245,33 @@ public: ++schedule_count; } + void IncrementThreadCount(); + void DecrementThreadCount(); + + void SetRunningThread(s32 core, KThread* thread, u64 idle_count) { + running_threads[core] = thread; + running_thread_idle_counts[core] = idle_count; + } + + void ClearRunningThread(KThread* thread) { + for (size_t i = 0; i < running_threads.size(); ++i) { + if (running_threads[i] == thread) { + running_threads[i] = nullptr; + } + } + } + + [[nodiscard]] KThread* GetRunningThread(s32 core) const { + return running_threads[core]; + } + + bool ReleaseUserException(KThread* thread); + + [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + return pinned_threads[core_id]; + } + /// Gets 8 bytes of random data for svcGetInfo RandomEntropy u64 GetRandomEntropy(std::size_t index) const { return random_entropy.at(index); @@ -310,6 +345,9 @@ public: void Finalize() override {} + void PinCurrentThread(); + void UnpinCurrentThread(); + /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management @@ -320,6 +358,20 @@ public: void FreeTLSRegion(VAddr tls_address); private: + void PinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(pinned_threads[core_id] == nullptr); + pinned_threads[core_id] = thread; + } + + void UnpinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(pinned_threads[core_id] == thread); + pinned_threads[core_id] = nullptr; + } + /// Changes the process status. If the status is different /// from the current process status, then this will trigger /// a process signal. @@ -408,6 +460,17 @@ private: s64 schedule_count{}; bool is_signaled{}; + bool is_suspended{}; + + std::atomic<s32> num_created_threads{}; + std::atomic<u16> num_threads{}; + u16 peak_num_threads{}; + + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; + std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{}; + + KThread* exception_thread{}; /// System context Core::System& system; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 280c9b5f6..790dbb998 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -154,7 +154,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { KScopedSchedulerLock lock(kernel); if (!context.IsThreadWaiting()) { context.GetThread().Wakeup(); - context.GetThread().SetSynchronizationResults(nullptr, result); + context.GetThread().SetSyncedObject(nullptr, result); } } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2512bfd98..dbef854f8 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -351,7 +351,8 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } - return thread->GetSignalingResult(); + KSynchronizationObject* dummy{}; + return thread->GetWaitResult(std::addressof(dummy)); } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { @@ -359,27 +360,26 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { } /// Get the ID for the specified thread. -static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { +static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - *thread_id = thread->GetThreadID(); + // Get the thread's id. + *out_thread_id = thread->GetThreadID(); return RESULT_SUCCESS; } -static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high, - Handle thread_handle) { - u64 thread_id{}; - const ResultCode result{GetThreadId(system, &thread_id, thread_handle)}; +static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, + u32* out_thread_id_high, Handle thread_handle) { + u64 out_thread_id{}; + const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)}; - *thread_id_low = static_cast<u32>(thread_id >> 32); - *thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max()); + *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); + *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); return result; } @@ -473,15 +473,13 @@ static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - thread->CancelWait(); + // Cancel the thread's wait. + thread->WaitCancel(); return RESULT_SUCCESS; } @@ -630,7 +628,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { handle_debug_buffer(info1, info2); auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); - const auto thread_processor_id = current_thread->GetProcessorID(); + const auto thread_processor_id = current_thread->GetActiveCore(); system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); } } @@ -888,7 +886,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); u64 out_ticks = 0; if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { - const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); + const u64 thread_ticks = current_thread->GetCpuTime(); out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { @@ -1025,127 +1023,109 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size return UnmapPhysicalMemory(system, addr, size); } -/// Sets the thread activity -static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { - LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); - if (activity > static_cast<u32>(ThreadActivity::Paused)) { - return ERR_INVALID_ENUM_VALUE; +constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) { + switch (thread_activity) { + case Svc::ThreadActivity::Runnable: + case Svc::ThreadActivity::Paused: + return true; + default: + return false; } +} - const auto* current_process = system.Kernel().CurrentProcess(); - const std::shared_ptr<KThread> thread = current_process->GetHandleTable().Get<KThread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } +/// Sets the thread activity +static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, + thread_activity); - if (thread->GetOwnerProcess() != current_process) { - LOG_ERROR(Kernel_SVC, - "The current process does not own the current thread, thread_handle={:08X} " - "thread_pid={}, " - "current_process_pid={}", - handle, thread->GetOwnerProcess()->GetProcessID(), - current_process->GetProcessID()); - return ERR_INVALID_HANDLE; - } + // Validate the activity. + R_UNLESS(IsValidThreadActivity(thread_activity), Svc::ResultInvalidEnumValue); - if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) { - LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); - return ERR_BUSY; - } + // Get the thread from its handle. + auto& kernel = system.Kernel(); + const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); + + // Check that the activity is being set on a non-current thread for the current process. + R_UNLESS(thread->GetOwnerProcess() == kernel.CurrentProcess(), Svc::ResultInvalidHandle); + R_UNLESS(thread.get() != GetCurrentThreadPointer(kernel), Svc::ResultBusy); + + // Set the activity. + R_TRY(thread->SetActivity(thread_activity)); - return thread->SetActivity(static_cast<ThreadActivity>(activity)); + return RESULT_SUCCESS; } -static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { - return SetThreadActivity(system, handle, activity); +static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { + return SetThreadActivity(system, thread_handle, thread_activity); } /// Gets the thread context -static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { - LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); +static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { + LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, + thread_handle); + // Get the thread from its handle. const auto* current_process = system.Kernel().CurrentProcess(); - const std::shared_ptr<KThread> thread = current_process->GetHandleTable().Get<KThread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = + current_process->GetHandleTable().Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - if (thread->GetOwnerProcess() != current_process) { - LOG_ERROR(Kernel_SVC, - "The current process does not own the current thread, thread_handle={:08X} " - "thread_pid={}, " - "current_process_pid={}", - handle, thread->GetOwnerProcess()->GetProcessID(), - current_process->GetProcessID()); - return ERR_INVALID_HANDLE; - } + // Require the handle be to a non-current thread in the current process. + R_UNLESS(thread->GetOwnerProcess() == current_process, Svc::ResultInvalidHandle); + R_UNLESS(thread.get() != system.Kernel().CurrentScheduler()->GetCurrentThread(), + Svc::ResultBusy); - if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) { - LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); - return ERR_BUSY; - } + // Get the thread context. + std::vector<u8> context; + R_TRY(thread->GetThreadContext3(context)); - Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64(); - // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. - ctx.pstate &= 0xFF0FFE20; + // Copy the thread context to user space. + system.Memory().WriteBlock(out_context, context.data(), context.size()); - // If 64-bit, we can just write the context registers directly and we're good. - // However, if 32-bit, we have to ensure some registers are zeroed out. - if (!current_process->Is64BitProcess()) { - std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0); - std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); - } - - system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx)); return RESULT_SUCCESS; } -static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { - return GetThreadContext(system, thread_context, handle); +static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { + return GetThreadContext(system, out_context, thread_handle); } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { +static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle); - if (!thread) { - *priority = 0; - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - *priority = thread->GetPriority(); + // Get the thread's priority. + *out_priority = thread->GetPriority(); return RESULT_SUCCESS; } -static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) { - return GetThreadPriority(system, priority, handle); +static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { + return GetThreadPriority(system, out_priority, handle); } /// Sets the priority for the specified thread static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { LOG_TRACE(Kernel_SVC, "called"); - if (priority > Svc::LowestThreadPriority) { - LOG_ERROR(Kernel_SVC, "An invalid priority was specified {} for thread_handle={:08X}", - priority, handle); - return ERR_INVALID_THREAD_PRIORITY; - } - - const auto* const current_process = system.Kernel().CurrentProcess(); + // Validate the priority. + R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority, + Svc::ResultInvalidPriority); - std::shared_ptr<KThread> thread = current_process->GetHandleTable().Get<KThread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + // Get the thread from its handle. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); + // Set the thread priority. thread->SetBasePriority(priority); - return RESULT_SUCCESS; } @@ -1436,7 +1416,7 @@ static void ExitProcess(Core::System& system) { current_process->PrepareForTermination(); // Kill the current thread - system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop(); + system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit(); } static void ExitProcess32(Core::System& system) { @@ -1500,17 +1480,15 @@ static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 p static ResultCode StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - ASSERT(thread->GetState() == ThreadState::Initialized); + // Try to start the thread. + R_TRY(thread->Run()); - return thread->Start(); + return RESULT_SUCCESS; } static ResultCode StartThread32(Core::System& system, Handle thread_handle) { @@ -1523,7 +1501,7 @@ static void ExitThread(Core::System& system) { auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread)); - current_thread->Stop(); + current_thread->Exit(); } static void ExitThread32(Core::System& system) { @@ -1532,34 +1510,28 @@ static void ExitThread32(Core::System& system) { /// Sleep the current thread static void SleepThread(Core::System& system, s64 nanoseconds) { - LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + auto& kernel = system.Kernel(); + const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); - enum class SleepType : s64 { - YieldWithoutCoreMigration = 0, - YieldWithCoreMigration = -1, - YieldAndWaitForLoadBalancing = -2, - }; + LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); - auto& scheduler = *system.Kernel().CurrentScheduler(); - if (nanoseconds <= 0) { - switch (static_cast<SleepType>(nanoseconds)) { - case SleepType::YieldWithoutCoreMigration: { - scheduler.YieldWithoutCoreMigration(); - break; - } - case SleepType::YieldWithCoreMigration: { - scheduler.YieldWithCoreMigration(); - break; - } - case SleepType::YieldAndWaitForLoadBalancing: { - scheduler.YieldToAnyThread(); - break; - } - default: - UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); - } + // When the input tick is positive, sleep. + if (nanoseconds > 0) { + // Convert the timeout from nanoseconds to ticks. + // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... + + // Sleep. + // NOTE: Nintendo does not check the result of this sleep. + static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); + } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { + KScheduler::YieldWithoutCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::WithCoreMigration) { + KScheduler::YieldWithCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::ToAnyThread) { + KScheduler::YieldToAnyThread(kernel); } else { - scheduler.GetCurrentThread()->Sleep(nanoseconds); + // Nintendo does nothing at all if an otherwise invalid value is passed. + UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } @@ -1822,95 +1794,72 @@ static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u return CreateTransferMemory(system, handle, addr, size, permissions); } -static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, - u64* mask) { +static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, + u64* out_affinity_mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - *core = 0; - *mask = 0; - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - *core = thread->GetIdealCore(); - *mask = thread->GetAffinityMask().GetAffinityMask(); + // Get the core mask. + R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); return RESULT_SUCCESS; } -static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, - u32* mask_low, u32* mask_high) { - u64 mask{}; - const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); - *mask_high = static_cast<u32>(mask >> 32); - *mask_low = static_cast<u32>(mask); +static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, + u32* out_affinity_mask_low, u32* out_affinity_mask_high) { + u64 out_affinity_mask{}; + const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); + *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); + *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); return result; } -static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, +static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, u64 affinity_mask) { - LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", - thread_handle, core, affinity_mask); - - const auto* const current_process = system.Kernel().CurrentProcess(); + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}", + thread_handle, core_id, affinity_mask); - if (core == static_cast<u32>(Svc::IdealCoreUseProcessValue)) { - const u8 ideal_cpu_core = current_process->GetIdealCoreId(); + const auto& current_process = *system.Kernel().CurrentProcess(); - ASSERT(ideal_cpu_core != static_cast<u8>(Svc::IdealCoreUseProcessValue)); - - // Set the target CPU to the ideal core specified by the process. - core = ideal_cpu_core; - affinity_mask = 1ULL << core; + // Determine the core id/affinity mask. + if (core_id == Svc::IdealCoreUseProcessValue) { + core_id = current_process.GetIdealCoreId(); + affinity_mask = (1ULL << core_id); } else { - const u64 core_mask = current_process->GetCoreMask(); - - if ((core_mask | affinity_mask) != core_mask) { - LOG_ERROR( - Kernel_SVC, - "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})", - core_mask, affinity_mask); - return ERR_INVALID_PROCESSOR_ID; - } - - if (affinity_mask == 0) { - LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero."); - return ERR_INVALID_COMBINATION; - } - - if (core < Core::Hardware::NUM_CPU_CORES) { - if ((affinity_mask & (1ULL << core)) == 0) { - LOG_ERROR(Kernel_SVC, - "Core is not enabled for the current mask, core={}, mask={:016X}", core, - affinity_mask); - return ERR_INVALID_COMBINATION; - } - } else if (core != static_cast<u32>(Svc::IdealCoreDontCare) && - core != static_cast<u32>(Svc::IdealCoreNoUpdate)) { - LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core); - return ERR_INVALID_PROCESSOR_ID; + // Validate the affinity mask. + const u64 process_core_mask = current_process.GetCoreMask(); + R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, + Svc::ResultInvalidCoreId); + R_UNLESS(affinity_mask != 0, Svc::ResultInvalidCombination); + + // Validate the core id. + if (IsValidCoreId(core_id)) { + R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, Svc::ResultInvalidCombination); + } else { + R_UNLESS(core_id == Svc::IdealCoreNoUpdate || core_id == Svc::IdealCoreDontCare, + Svc::ResultInvalidCoreId); } } - const auto& handle_table = current_process->GetHandleTable(); + // Get the thread from its handle. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + R_UNLESS(thread, Svc::ResultInvalidHandle); - return thread->SetCoreAndAffinityMask(core, affinity_mask); + // Set the core mask. + R_TRY(thread->SetCoreMask(core_id, affinity_mask)); + + return RESULT_SUCCESS; } -static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, +static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, u32 affinity_mask_low, u32 affinity_mask_high) { const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); - return SetThreadCoreMask(system, thread_handle, core, affinity_mask); + return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); } static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { @@ -2474,7 +2423,7 @@ void Call(Core::System& system, u32 immediate) { kernel.EnterSVCProfile(); auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - thread->SetContinuousOnSVC(true); + thread->SetIsCallingSvc(); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); @@ -2490,7 +2439,7 @@ void Call(Core::System& system, u32 immediate) { kernel.ExitSVCProfile(); - if (!thread->IsContinuousOnSVC()) { + if (!thread->IsCallingSvc()) { auto* host_context = thread->GetHostContext().get(); host_context->Rewind(); } diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index ded55af9a..ec463b97c 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -77,6 +77,12 @@ enum class ArbitrationType : u32 { WaitIfEqual = 2, }; +enum class YieldType : s64 { + WithoutCoreMigration = 0, + WithCoreMigration = -1, + ToAnyThread = -2, +}; + enum class ThreadActivity : u32 { Runnable = 0, Paused = 1, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a32750ed7..96afd544b 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -58,6 +58,14 @@ void SvcWrap64(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); } +// Used by SetThreadActivity +template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +void SvcWrap64(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), + static_cast<Svc::ThreadActivity>(Param(system, 1))) + .raw); +} + template <ResultCode func(Core::System&, u32, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), @@ -158,9 +166,18 @@ void SvcWrap64(Core::System& system) { .raw); } -template <ResultCode func(Core::System&, u32, u32*, u64*)> +// Used by SetThreadCoreMask +template <ResultCode func(Core::System&, Handle, s32, u64)> void SvcWrap64(Core::System& system) { - u32 param_1 = 0; + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), + static_cast<s32>(Param(system, 1)), Param(system, 2)) + .raw); +} + +// Used by GetThreadCoreMask +template <ResultCode func(Core::System&, Handle, s32*, u64*)> +void SvcWrap64(Core::System& system) { + s32 param_1 = 0; u64 param_2 = 0; const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); @@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by GetThreadCoreMask32 +template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)> +void SvcWrap32(Core::System& system) { + s32 param_1 = 0; + u32 param_2 = 0; + u32 param_3 = 0; + + const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw; + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + system.CurrentArmInterface().SetReg(3, param_3); + FuncReturn(system, retval); +} + // Used by SignalProcessWideKey32 template <void func(Core::System&, u32, s32)> void SvcWrap32(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); } +// Used by SetThreadActivity32 +template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +void SvcWrap32(Core::System& system) { + const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), + static_cast<Svc::ThreadActivity>(Param(system, 1))) + .raw; + FuncReturn(system, retval); +} + // Used by SetThreadPriority32 template <ResultCode func(Core::System&, Handle, u32)> void SvcWrap32(Core::System& system) { @@ -487,7 +527,7 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } -// Used by SetThreadCoreMask32 +// Used by SetMemoryAttribute32 template <ResultCode func(Core::System&, Handle, u32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = @@ -497,6 +537,16 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by SetThreadCoreMask32 +template <ResultCode func(Core::System&, Handle, s32, u32, u32)> +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)), + static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) + .raw; + FuncReturn(system, retval); +} + // Used by WaitProcessWideKeyAtomic32 template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> void SvcWrap32(Core::System& system) { |