From 45c87c7e6e841c11def43e5ab25160006dab6d77 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 28 Nov 2023 14:30:39 -0500 Subject: core: refactor emulated cpu core activation --- src/core/hle/kernel/physical_core.cpp | 251 +++++++++++++++++++++++++++------- 1 file changed, 205 insertions(+), 46 deletions(-) (limited to 'src/core/hle/kernel/physical_core.cpp') diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 073039825..7fa8e2a85 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -1,62 +1,206 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/scope_exit.h" #include "common/settings.h" -#include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" -#ifdef HAS_NCE -#include "core/arm/nce/arm_nce.h" -#endif #include "core/core.h" -#include "core/hle/kernel/k_scheduler.h" +#include "core/debugger/debugger.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/svc.h" namespace Kernel { -PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KScheduler& scheduler) - : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} { -#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) - // TODO(bunnei): Initialization relies on a core being available. We may later replace this with - // an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU - // manager. - auto& kernel = system.Kernel(); - m_arm_interface = std::make_unique( - system, kernel.IsMulticore(), - reinterpret_cast(kernel.GetExclusiveMonitor()), - m_core_index); -#else -#error Platform not supported yet. -#endif +PhysicalCore::PhysicalCore(KernelCore& kernel, std::size_t core_index) + : m_kernel{kernel}, m_core_index{core_index} { + m_is_single_core = !kernel.IsMulticore(); } - PhysicalCore::~PhysicalCore() = default; -void PhysicalCore::Initialize(bool is_64_bit) { -#if defined(HAS_NCE) - if (Settings::IsNceEnabled()) { - m_arm_interface = std::make_unique(m_system, m_system.Kernel().IsMulticore(), - m_core_index); +void PhysicalCore::RunThread(Kernel::KThread* thread) { + auto* process = thread->GetOwnerProcess(); + auto& system = m_kernel.System(); + auto* interface = process->GetArmInterface(m_core_index); + + interface->Initialize(); + + const auto EnterContext = [&]() { + system.EnterCPUProfile(); + + // Lock the core context. + std::scoped_lock lk{m_guard}; + + // Check if we are already interrupted. If we are, we can just stop immediately. + if (m_is_interrupted) { + return false; + } + + // Mark that we are running. + m_arm_interface = interface; + m_current_thread = thread; + + // Acquire the lock on the thread parameters. + // This allows us to force synchronization with Interrupt. + interface->LockThread(thread); + + return true; + }; + + const auto ExitContext = [&]() { + // Unlock the thread. + interface->UnlockThread(thread); + + // Lock the core context. + std::scoped_lock lk{m_guard}; + + // On exit, we no longer are running. + m_arm_interface = nullptr; + m_current_thread = nullptr; + + system.ExitCPUProfile(); + }; + + while (true) { + // If the thread is scheduled for termination, exit. + if (thread->HasDpc() && thread->IsTerminationRequested()) { + thread->Exit(); + } + + // Notify the debugger and go to sleep if a step was performed + // and this thread has been scheduled again. + if (thread->GetStepState() == StepState::StepPerformed) { + system.GetDebugger().NotifyThreadStopped(thread); + thread->RequestSuspend(SuspendType::Debug); + return; + } + + // Otherwise, run the thread. + Core::HaltReason hr{}; + { + // If we were interrupted, exit immediately. + if (!EnterContext()) { + return; + } + + if (thread->GetStepState() == StepState::StepPending) { + hr = interface->StepThread(thread); + + if (True(hr & Core::HaltReason::StepThread)) { + thread->SetStepState(StepState::StepPerformed); + } + } else { + hr = interface->RunThread(thread); + } + + ExitContext(); + } + + // Determine why we stopped. + const bool supervisor_call = True(hr & Core::HaltReason::SupervisorCall); + const bool prefetch_abort = True(hr & Core::HaltReason::PrefetchAbort); + const bool breakpoint = True(hr & Core::HaltReason::InstructionBreakpoint); + const bool data_abort = True(hr & Core::HaltReason::DataAbort); + const bool interrupt = True(hr & Core::HaltReason::BreakLoop); + + // Since scheduling may occur here, we cannot use any cached + // state after returning from calls we make. + + // Notify the debugger and go to sleep if a breakpoint was hit, + // or if the thread is unable to continue for any reason. + if (breakpoint || prefetch_abort) { + if (breakpoint) { + interface->RewindBreakpointInstruction(); + } + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadStopped(thread); + } else { + interface->LogBacktrace(process); + } + thread->RequestSuspend(SuspendType::Debug); + return; + } + + // Notify the debugger and go to sleep on data abort. + if (data_abort) { + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint()); + } + thread->RequestSuspend(SuspendType::Debug); + return; + } + + // Handle system calls. + if (supervisor_call) { + // Perform call. + Svc::Call(system, interface->GetSvcNumber()); + return; + } + + // Handle external interrupt sources. + if (interrupt || !m_is_single_core) { + return; + } + } +} + +void PhysicalCore::LoadContext(const KThread* thread) { + auto* const process = thread->GetOwnerProcess(); + if (!process) { + // Kernel threads do not run on emulated CPU cores. return; } -#endif -#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) - auto& kernel = m_system.Kernel(); - if (!is_64_bit) { - // We already initialized a 64-bit core, replace with a 32-bit one. - m_arm_interface = std::make_unique( - m_system, kernel.IsMulticore(), - reinterpret_cast(kernel.GetExclusiveMonitor()), - m_core_index); + + auto* interface = process->GetArmInterface(m_core_index); + if (interface) { + interface->SetContext(thread->GetContext()); + interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress())); + interface->SetWatchpointArray(&process->GetWatchpoints()); } -#else -#error Platform not supported yet. -#endif } -void PhysicalCore::Run() { - m_arm_interface->Run(); - m_arm_interface->ClearExclusiveState(); +void PhysicalCore::LoadSvcArguments(const KProcess& process, std::span args) { + process.GetArmInterface(m_core_index)->SetSvcArguments(args); +} + +void PhysicalCore::SaveContext(KThread* thread) const { + auto* const process = thread->GetOwnerProcess(); + if (!process) { + // Kernel threads do not run on emulated CPU cores. + return; + } + + auto* interface = process->GetArmInterface(m_core_index); + if (interface) { + interface->GetContext(thread->GetContext()); + } +} + +void PhysicalCore::SaveSvcArguments(KProcess& process, std::span args) const { + process.GetArmInterface(m_core_index)->GetSvcArguments(args); +} + +void PhysicalCore::CloneFpuStatus(KThread* dst) const { + auto* process = dst->GetOwnerProcess(); + + Svc::ThreadContext ctx{}; + process->GetArmInterface(m_core_index)->GetContext(ctx); + + dst->GetContext().fpcr = ctx.fpcr; + dst->GetContext().fpsr = ctx.fpsr; +} + +void PhysicalCore::LogBacktrace() { + auto* process = GetCurrentProcessPointer(m_kernel); + if (!process) { + return; + } + + auto* interface = process->GetArmInterface(m_core_index); + if (interface) { + interface->LogBacktrace(process); + } } void PhysicalCore::Idle() { @@ -69,16 +213,31 @@ bool PhysicalCore::IsInterrupted() const { } void PhysicalCore::Interrupt() { - std::unique_lock lk{m_guard}; + // Lock core context. + std::scoped_lock lk{m_guard}; + + // Load members. + auto* arm_interface = m_arm_interface; + auto* thread = m_current_thread; + + // Add interrupt flag. m_is_interrupted = true; - m_arm_interface->SignalInterrupt(); - m_on_interrupt.notify_all(); + + // Interrupt ourselves. + m_on_interrupt.notify_one(); + + // If there is no thread running, we are done. + if (arm_interface == nullptr) { + return; + } + + // Interrupt the CPU. + arm_interface->SignalInterrupt(thread); } void PhysicalCore::ClearInterrupt() { - std::unique_lock lk{m_guard}; + std::scoped_lock lk{m_guard}; m_is_interrupted = false; - m_arm_interface->ClearInterrupt(); } } // namespace Kernel -- cgit v1.2.3