| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- // 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/core.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(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::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;
- }
- auto* interface = process->GetArmInterface(m_core_index);
- if (interface) {
- interface->SetContext(thread->GetContext());
- interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
- interface->SetWatchpointArray(&process->GetWatchpoints());
- }
- }
- void PhysicalCore::LoadSvcArguments(const KProcess& process, std::span<const uint64_t, 8> 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<uint64_t, 8> 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() {
- std::unique_lock lk{m_guard};
- m_on_interrupt.wait(lk, [this] { return m_is_interrupted; });
- }
- bool PhysicalCore::IsInterrupted() const {
- return m_is_interrupted;
- }
- void PhysicalCore::Interrupt() {
- // 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;
- // 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::scoped_lock lk{m_guard};
- m_is_interrupted = false;
- }
- } // namespace Kernel
|