core: refactor emulated cpu core activation

This commit is contained in:
Liam
2023-11-28 14:30:39 -05:00
parent 90e87c40e8
commit 45c87c7e6e
47 changed files with 2984 additions and 3332 deletions

View File

@ -69,8 +69,16 @@ public:
};
template <typename AddressType>
void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) {
system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size) {
// TODO: lock the process list
for (auto& process : kernel.GetProcessList()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
auto* interface = process->GetArmInterface(i);
if (interface) {
interface->InvalidateCacheRange(GetInteger(addr), size);
}
}
}
}
template <typename AddressType>
@ -1261,7 +1269,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
bool reprotected_pages = false;
SCOPE_EXIT({
if (reprotected_pages && any_code_pages) {
InvalidateInstructionCache(m_system, dst_address, size);
InvalidateInstructionCache(m_kernel, dst_address, size);
}
});
@ -1997,7 +2005,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s
for (const auto& block : pg) {
StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
}
InvalidateInstructionCache(m_system, addr, size);
InvalidateInstructionCache(m_kernel, addr, size);
}
R_SUCCEED();
@ -3239,7 +3247,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
R_TRY(PerformCopy());
// Invalidate the instruction cache, as this svc allows modifying executable pages.
InvalidateInstructionCache(m_system, dst_address, size);
InvalidateInstructionCache(m_kernel, dst_address, size);
R_SUCCEED();
}

View File

@ -13,6 +13,12 @@
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_worker_task_manager.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
namespace Kernel {
namespace {
@ -957,10 +963,8 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread));
// Set the thread arguments.
main_thread->GetContext32().cpu_registers[0] = 0;
main_thread->GetContext64().cpu_registers[0] = 0;
main_thread->GetContext32().cpu_registers[1] = thread_handle;
main_thread->GetContext64().cpu_registers[1] = thread_handle;
main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle;
// Update our state.
this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached);
@ -1199,6 +1203,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
m_is_hbl = is_hbl;
m_ideal_core_id = metadata.GetMainThreadCore();
// Set up emulation context.
this->InitializeInterfaces();
// We succeeded.
R_SUCCEED();
}
@ -1227,6 +1234,31 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
#endif
}
void KProcess::InitializeInterfaces() {
this->GetMemory().SetCurrentPageTable(*this);
#ifdef HAS_NCE
if (this->Is64Bit() && Settings::IsNceEnabled()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
}
} else
#endif
if (this->Is64Bit()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
m_kernel.System(), m_kernel.IsMulticore(), this,
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
}
} else {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
m_kernel.System(), m_kernel.IsMulticore(), this,
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
}
}
}
bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) {
const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) {
return wp.type == DebugWatchpointType::None;

View File

@ -5,6 +5,7 @@
#include <map>
#include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h"
@ -106,6 +107,8 @@ private:
bool m_is_suspended{};
bool m_is_immortal{};
bool m_is_handle_table_initialized{};
std::array<std::unique_ptr<Core::ArmInterface>, Core::Hardware::NUM_CPU_CORES>
m_arm_interfaces{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_switch_counts{};
@ -476,6 +479,10 @@ public:
}
#endif
Core::ArmInterface* GetArmInterface(size_t core_index) const {
return m_arm_interfaces[core_index].get();
}
public:
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
@ -493,6 +500,8 @@ public:
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
void InitializeInterfaces();
Core::Memory::Memory& GetMemory() const;
public:

View File

@ -7,10 +7,6 @@
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/svc_types.h"
namespace Core {
class ARM_Interface;
}
namespace Kernel {
class KProcessPageTable {

View File

@ -494,12 +494,7 @@ void KScheduler::ScheduleImplFiber() {
}
void KScheduler::Unload(KThread* thread) {
auto& cpu_core = m_kernel.System().ArmInterface(m_core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTpidrEl0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
m_kernel.PhysicalCore(m_core_id).SaveContext(thread);
// Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
@ -509,14 +504,7 @@ void KScheduler::Unload(KThread* thread) {
}
void KScheduler::Reload(KThread* thread) {
auto& cpu_core = m_kernel.System().ArmInterface(m_core_id);
auto* process = thread->GetOwnerProcess();
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(GetInteger(thread->GetTlsAddress()));
cpu_core.SetTPIDR_EL0(thread->GetTpidrEl0());
cpu_core.LoadWatchpointArray(process ? &process->GetWatchpoints() : nullptr);
cpu_core.ClearExclusiveState();
m_kernel.PhysicalCore(m_core_id).LoadContext(thread);
}
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {

View File

@ -41,24 +41,25 @@ namespace {
constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1;
static void ResetThreadContext32(Kernel::KThread::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;
context.fpscr = 0;
static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
u64 arg) {
ctx = {};
ctx.r[0] = arg;
ctx.r[15] = entry_point;
ctx.r[13] = stack_top;
ctx.fpcr = 0;
ctx.fpsr = 0;
}
static void ResetThreadContext64(Kernel::KThread::ThreadContext64& context, u64 stack_top,
u64 entry_point, u64 arg) {
context = {};
context.cpu_registers[0] = arg;
context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
context.pc = entry_point;
context.sp = stack_top;
context.fpcr = 0;
context.fpsr = 0;
static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
u64 arg) {
ctx = {};
ctx.r[0] = arg;
ctx.r[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
ctx.pc = entry_point;
ctx.sp = stack_top;
ctx.fpcr = 0;
ctx.fpsr = 0;
}
} // namespace
@ -223,9 +224,11 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress
}
// Initialize thread context.
ResetThreadContext64(m_thread_context_64, GetInteger(user_stack_top), GetInteger(func), arg);
ResetThreadContext32(m_thread_context_32, static_cast<u32>(GetInteger(user_stack_top)),
static_cast<u32>(GetInteger(func)), static_cast<u32>(arg));
if (m_parent != nullptr && !m_parent->Is64Bit()) {
ResetThreadContext32(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg);
} else {
ResetThreadContext64(m_thread_context, GetInteger(user_stack_top), GetInteger(func), arg);
}
// Setup the stack parameters.
StackParameters& sp = this->GetStackParameters();
@ -823,20 +826,7 @@ void KThread::CloneFpuStatus() {
ASSERT(this->GetOwnerProcess() != nullptr);
ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel));
if (this->GetOwnerProcess()->Is64Bit()) {
// Clone FPSR and FPCR.
ThreadContext64 cur_ctx{};
m_kernel.System().CurrentArmInterface().SaveContext(cur_ctx);
this->GetContext64().fpcr = cur_ctx.fpcr;
this->GetContext64().fpsr = cur_ctx.fpsr;
} else {
// Clone FPSCR.
ThreadContext32 cur_ctx{};
m_kernel.System().CurrentArmInterface().SaveContext(cur_ctx);
this->GetContext32().fpscr = cur_ctx.fpscr;
}
m_kernel.CurrentPhysicalCore().CloneFpuStatus(this);
}
Result KThread::SetActivity(Svc::ThreadActivity activity) {
@ -912,7 +902,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) {
R_SUCCEED();
}
Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {
Result KThread::GetThreadContext3(Svc::ThreadContext* out) {
// Lock ourselves.
KScopedLightLock lk{m_activity_pause_lock};
@ -926,18 +916,16 @@ Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {
// If we're not terminating, get the thread's user context.
if (!this->IsTerminationRequested()) {
*out = m_thread_context;
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
constexpr u32 El0Aarch64PsrMask = 0xF0000000;
constexpr u32 El0Aarch32PsrMask = 0xFE0FFE20;
if (m_parent->Is64Bit()) {
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
auto context = GetContext64();
context.pstate &= 0xFF0FFE20;
out.resize_destructive(sizeof(context));
std::memcpy(out.data(), std::addressof(context), sizeof(context));
out->pstate &= El0Aarch64PsrMask;
} else {
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
auto context = GetContext32();
context.cpsr &= 0xFF0FFE20;
out.resize_destructive(sizeof(context));
std::memcpy(out.data(), std::addressof(context), sizeof(context));
out->pstate &= El0Aarch32PsrMask;
}
}
}

View File

@ -38,7 +38,6 @@ namespace Core {
namespace Memory {
class Memory;
}
class ARM_Interface;
class System;
} // namespace Core
@ -137,8 +136,6 @@ public:
~KThread() override;
public:
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
using WaiterList = Common::IntrusiveListBaseTraits<KThread>::ListType;
/**
@ -246,31 +243,22 @@ public:
* @returns The value of the TPIDR_EL0 register.
*/
u64 GetTpidrEl0() const {
return m_thread_context_64.tpidr;
return m_thread_context.tpidr;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTpidrEl0(u64 value) {
m_thread_context_64.tpidr = value;
m_thread_context_32.tpidr = static_cast<u32>(value);
m_thread_context.tpidr = value;
}
void CloneFpuStatus();
ThreadContext32& GetContext32() {
return m_thread_context_32;
Svc::ThreadContext& GetContext() {
return m_thread_context;
}
const ThreadContext32& GetContext32() const {
return m_thread_context_32;
}
ThreadContext64& GetContext64() {
return m_thread_context_64;
}
const ThreadContext64& GetContext64() const {
return m_thread_context_64;
const Svc::ThreadContext& GetContext() const {
return m_thread_context;
}
std::shared_ptr<Common::Fiber>& GetHostContext();
@ -577,7 +565,7 @@ public:
void RemoveWaiter(KThread* thread);
Result GetThreadContext3(Common::ScratchBuffer<u8>& out);
Result GetThreadContext3(Svc::ThreadContext* out);
KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) {
return this->RemoveWaiterByKey(out_has_waiters, key, false);
@ -734,8 +722,7 @@ private:
std::function<void()>&& init_func);
// For core KThread implementation
ThreadContext32 m_thread_context_32{};
ThreadContext64 m_thread_context_64{};
Svc::ThreadContext m_thread_context{};
Common::IntrusiveListNode m_process_list_node;
Common::IntrusiveRedBlackTreeNode m_condvar_arbiter_tree_node{};
s32 m_priority{};

View File

@ -99,13 +99,6 @@ struct KernelCore::Impl {
RegisterHostThread(nullptr);
}
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id]->Initialize((*application_process).Is64Bit());
system.ApplicationMemory().SetCurrentPageTable(*application_process, core_id);
}
}
void TerminateApplicationProcess() {
application_process.load()->Terminate();
}
@ -205,7 +198,7 @@ struct KernelCore::Impl {
const s32 core{static_cast<s32>(i)};
schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
cores[i] = std::make_unique<Kernel::PhysicalCore>(system.Kernel(), i);
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetCurrentCore(core);
@ -880,10 +873,6 @@ void KernelCore::Initialize() {
impl->Initialize(*this);
}
void KernelCore::InitializeCores() {
impl->InitializeCores();
}
void KernelCore::Shutdown() {
impl->Shutdown();
}
@ -993,21 +982,6 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
physical_core->ArmInterface().ClearInstructionCache();
}
}
void KernelCore::InvalidateCpuInstructionCacheRange(KProcessAddress addr, std::size_t size) {
for (auto& physical_core : impl->cores) {
if (!physical_core->IsInitialized()) {
continue;
}
physical_core->ArmInterface().InvalidateCacheRange(GetInteger(addr), size);
}
}
void KernelCore::PrepareReschedule(std::size_t id) {
// TODO: Reimplement, this
}

View File

@ -104,9 +104,6 @@ public:
/// Resets the kernel to a clean slate for use.
void Initialize();
/// Initializes the CPU cores.
void InitializeCores();
/// Clears all resources in use by the kernel instance.
void Shutdown();
@ -181,10 +178,6 @@ public:
const KAutoObjectWithListContainer& ObjectListContainer() const;
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(KProcessAddress addr, std::size_t size);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
void RegisterKernelObject(KAutoObject* object);

View File

@ -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<Core::ARM_Dynarmic_64>(
system, kernel.IsMulticore(),
reinterpret_cast<Core::DynarmicExclusiveMonitor&>(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<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(),
m_core_index);
return;
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;
}
}
#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<Core::ARM_Dynarmic_32>(
m_system, kernel.IsMulticore(),
reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()),
m_core_index);
}
#else
#error Platform not supported yet.
#endif
}
void PhysicalCore::Run() {
m_arm_interface->Run();
m_arm_interface->ClearExclusiveState();
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() {
@ -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

View File

@ -11,7 +11,7 @@
#include "core/arm/arm_interface.h"
namespace Kernel {
class KScheduler;
class KernelCore;
} // namespace Kernel
namespace Core {
@ -23,62 +23,55 @@ namespace Kernel {
class PhysicalCore {
public:
PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
PhysicalCore(KernelCore& kernel, std::size_t core_index);
~PhysicalCore();
YUZU_NON_COPYABLE(PhysicalCore);
YUZU_NON_MOVEABLE(PhysicalCore);
/// Initialize the core for the specified parameters.
void Initialize(bool is_64_bit);
// Execute guest code running on the given thread.
void RunThread(KThread* thread);
/// Execute current jit state
void Run();
// Copy context from thread to current core.
void LoadContext(const KThread* thread);
void LoadSvcArguments(const KProcess& process, std::span<const uint64_t, 8> args);
// Copy context from current core to thread.
void SaveContext(KThread* thread) const;
void SaveSvcArguments(KProcess& process, std::span<uint64_t, 8> args) const;
// Copy floating point status registers to the target thread.
void CloneFpuStatus(KThread* dst) const;
// Log backtrace of current processor state.
void LogBacktrace();
// Wait for an interrupt.
void Idle();
/// Interrupt this physical core.
// Interrupt this core.
void Interrupt();
/// Clear this core's interrupt
// Clear this core's interrupt.
void ClearInterrupt();
/// Check if this core is interrupted
// Check if this core is interrupted.
bool IsInterrupted() const;
bool IsInitialized() const {
return m_arm_interface != nullptr;
}
Core::ARM_Interface& ArmInterface() {
return *m_arm_interface;
}
const Core::ARM_Interface& ArmInterface() const {
return *m_arm_interface;
}
std::size_t CoreIndex() const {
return m_core_index;
}
Kernel::KScheduler& Scheduler() {
return m_scheduler;
}
const Kernel::KScheduler& Scheduler() const {
return m_scheduler;
}
private:
KernelCore& m_kernel;
const std::size_t m_core_index;
Core::System& m_system;
Kernel::KScheduler& m_scheduler;
std::mutex m_guard;
std::condition_variable m_on_interrupt;
std::unique_ptr<Core::ARM_Interface> m_arm_interface;
Core::ArmInterface* m_arm_interface{};
KThread* m_current_thread{};
bool m_is_interrupted{};
bool m_is_single_core{};
};
} // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@ namespace Core {
class System;
}
#include <span>
#include "common/common_types.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
@ -520,15 +522,15 @@ void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArgumen
void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
// Defined in svc_light_ipc.cpp.
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
void SvcWrap_SendSyncRequestLight64(Core::System& system);
void SvcWrap_SendSyncRequestLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_SendSyncRequestLight64(Core::System& system, std::span<uint64_t, 8> args);
// Defined in svc_secure_monitor_call.cpp.
void SvcWrap_CallSecureMonitor64From32(Core::System& system);
void SvcWrap_CallSecureMonitor64(Core::System& system);
void SvcWrap_CallSecureMonitor64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_CallSecureMonitor64(Core::System& system, std::span<uint64_t, 8> args);
// Perform a supervisor call by index.
void Call(Core::System& system, u32 imm);

View File

@ -103,9 +103,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
system.CurrentPhysicalCore().LogBacktrace();
}
const bool is_hbl = GetCurrentProcess(system.Kernel()).IsHbl();

View File

@ -37,37 +37,36 @@ Result ReplyAndReceiveLight64From32(Core::System& system, Handle session_handle,
// Custom ABI implementation for light IPC.
template <typename F>
static void SvcWrap_LightIpc(Core::System& system, F&& cb) {
auto& core = system.CurrentArmInterface();
std::array<u32, 7> arguments{};
static void SvcWrap_LightIpc(Core::System& system, std::span<uint64_t, 8> args, F&& cb) {
std::array<u32, 7> ipc_args{};
Handle session_handle = static_cast<Handle>(core.GetReg(0));
Handle session_handle = static_cast<Handle>(args[0]);
for (int i = 0; i < 7; i++) {
arguments[i] = static_cast<u32>(core.GetReg(i + 1));
ipc_args[i] = static_cast<u32>(args[i + 1]);
}
Result ret = cb(system, session_handle, arguments.data());
Result ret = cb(system, session_handle, ipc_args.data());
core.SetReg(0, ret.raw);
args[0] = ret.raw;
for (int i = 0; i < 7; i++) {
core.SetReg(i + 1, arguments[i]);
args[i + 1] = ipc_args[i];
}
}
void SvcWrap_SendSyncRequestLight64(Core::System& system) {
SvcWrap_LightIpc(system, SendSyncRequestLight64);
void SvcWrap_SendSyncRequestLight64(Core::System& system, std::span<uint64_t, 8> args) {
SvcWrap_LightIpc(system, args, SendSyncRequestLight64);
}
void SvcWrap_ReplyAndReceiveLight64(Core::System& system) {
SvcWrap_LightIpc(system, ReplyAndReceiveLight64);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system, std::span<uint64_t, 8> args) {
SvcWrap_LightIpc(system, args, ReplyAndReceiveLight64);
}
void SvcWrap_SendSyncRequestLight64From32(Core::System& system) {
SvcWrap_LightIpc(system, SendSyncRequestLight64From32);
void SvcWrap_SendSyncRequestLight64From32(Core::System& system, std::span<uint64_t, 8> args) {
SvcWrap_LightIpc(system, args, SendSyncRequestLight64From32);
}
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system) {
SvcWrap_LightIpc(system, ReplyAndReceiveLight64From32);
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system, std::span<uint64_t, 8> args) {
SvcWrap_LightIpc(system, args, ReplyAndReceiveLight64From32);
}
} // namespace Kernel::Svc

View File

@ -22,31 +22,29 @@ void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArgumen
// Custom ABI for CallSecureMonitor.
void SvcWrap_CallSecureMonitor64(Core::System& system) {
auto& core = system.CurrentPhysicalCore().ArmInterface();
lp64::SecureMonitorArguments args{};
void SvcWrap_CallSecureMonitor64(Core::System& system, std::span<uint64_t, 8> args) {
lp64::SecureMonitorArguments smc_args{};
for (int i = 0; i < 8; i++) {
args.r[i] = core.GetReg(i);
smc_args.r[i] = args[i];
}
CallSecureMonitor64(system, std::addressof(args));
CallSecureMonitor64(system, std::addressof(smc_args));
for (int i = 0; i < 8; i++) {
core.SetReg(i, args.r[i]);
args[i] = smc_args.r[i];
}
}
void SvcWrap_CallSecureMonitor64From32(Core::System& system) {
auto& core = system.CurrentPhysicalCore().ArmInterface();
ilp32::SecureMonitorArguments args{};
void SvcWrap_CallSecureMonitor64From32(Core::System& system, std::span<uint64_t, 8> args) {
ilp32::SecureMonitorArguments smc_args{};
for (int i = 0; i < 8; i++) {
args.r[i] = static_cast<u32>(core.GetReg(i));
smc_args.r[i] = static_cast<u32>(args[i]);
}
CallSecureMonitor64From32(system, std::addressof(args));
CallSecureMonitor64From32(system, std::addressof(smc_args));
for (int i = 0; i < 8; i++) {
core.SetReg(i, args.r[i]);
args[i] = smc_args.r[i];
}
}

View File

@ -90,8 +90,6 @@ Result StartThread(Core::System& system, Handle thread_handle) {
/// Called when a thread exits
void ExitThread(Core::System& system) {
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
system.GlobalSchedulerContext().RemoveThread(current_thread);
current_thread->Exit();
@ -147,47 +145,19 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Require the handle be to a non-current thread in the current process.
const auto* current_process = GetCurrentProcessPointer(kernel);
R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(kernel), ResultInvalidHandle);
R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultBusy);
// Verify that the thread isn't terminated.
R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
// Get the thread context.
Svc::ThreadContext context{};
R_TRY(thread->GetThreadContext3(std::addressof(context)));
/// Check that the thread is not the current one.
/// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
// Copy the thread context to user space.
R_UNLESS(
GetCurrentMemory(kernel).WriteBlock(out_context, std::addressof(context), sizeof(context)),
ResultInvalidPointer);
// Try to get the thread context until the thread isn't current on any core.
while (true) {
KScopedSchedulerLock sl{kernel};
// TODO(bunnei): Enforce that thread is suspended for debug here.
// If the thread's raw state isn't runnable, check if it's current on some core.
if (thread->GetRawState() != ThreadState::Runnable) {
bool current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
current = true;
break;
}
}
// If the thread is current, retry until it isn't.
if (current) {
continue;
}
}
// Get the thread context.
static thread_local Common::ScratchBuffer<u8> context;
R_TRY(thread->GetThreadContext3(context));
// Copy the thread context to user space.
GetCurrentMemory(kernel).WriteBlock(out_context, context.data(), context.size());
R_SUCCEED();
}
R_SUCCEED();
}
/// Gets the priority for the specified thread

View File

@ -374,11 +374,11 @@ def get_registers(parse_result, bitness):
# Collects possibly multiple source registers into the named C++ value.
def emit_gather(sources, name, type_name, reg_size):
get_fn = f"GetReg{reg_size*8}"
get_fn = f"GetArg{reg_size*8}"
if len(sources) == 1:
s, = sources
line = f"{name} = Convert<{type_name}>({get_fn}(system, {s}));"
line = f"{name} = Convert<{type_name}>({get_fn}(args, {s}));"
return [line]
var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
@ -387,7 +387,7 @@ def emit_gather(sources, name, type_name, reg_size):
]
for i in range(0, len(sources)):
lines.append(
f"{name}_gather[{i}] = {get_fn}(system, {sources[i]});")
f"{name}_gather[{i}] = {get_fn}(args, {sources[i]});")
lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
return lines
@ -396,12 +396,12 @@ def emit_gather(sources, name, type_name, reg_size):
# Produces one or more statements which assign the named C++ value
# into possibly multiple registers.
def emit_scatter(destinations, name, reg_size):
set_fn = f"SetReg{reg_size*8}"
set_fn = f"SetArg{reg_size*8}"
reg_type = f"uint{reg_size*8}_t"
if len(destinations) == 1:
d, = destinations
line = f"{set_fn}(system, {d}, Convert<{reg_type}>({name}));"
line = f"{set_fn}(args, {d}, Convert<{reg_type}>({name}));"
return [line]
var_type = f"std::array<{reg_type}, {len(destinations)}>"
@ -411,7 +411,7 @@ def emit_scatter(destinations, name, reg_size):
for i in range(0, len(destinations)):
lines.append(
f"{set_fn}(system, {destinations[i]}, {name}_scatter[{i}]);")
f"{set_fn}(args, {destinations[i]}, {name}_scatter[{i}]);")
return lines
@ -433,7 +433,7 @@ def emit_lines(lines, indent=' '):
def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
return_write, output_writes, input_reads = register_info
lines = [
f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system) {{"
f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system, std::span<uint64_t, 8> args) {{"
]
# Get everything ready.
@ -498,6 +498,8 @@ namespace Core {
class System;
}
#include <span>
#include "common/common_types.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
@ -524,15 +526,15 @@ void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArgumen
void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
// Defined in svc_light_ipc.cpp.
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
void SvcWrap_SendSyncRequestLight64(Core::System& system);
void SvcWrap_SendSyncRequestLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_SendSyncRequestLight64(Core::System& system, std::span<uint64_t, 8> args);
// Defined in svc_secure_monitor_call.cpp.
void SvcWrap_CallSecureMonitor64From32(Core::System& system);
void SvcWrap_CallSecureMonitor64(Core::System& system);
void SvcWrap_CallSecureMonitor64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_CallSecureMonitor64(Core::System& system, std::span<uint64_t, 8> args);
// Perform a supervisor call by index.
void Call(Core::System& system, u32 imm);
@ -550,20 +552,20 @@ PROLOGUE_CPP = """
namespace Kernel::Svc {
static uint32_t GetReg32(Core::System& system, int n) {
return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) {
return static_cast<uint32_t>(args[n]);
}
static void SetReg32(Core::System& system, int n, uint32_t result) {
system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
static void SetArg32(std::span<uint64_t, 8> args, int n, uint32_t result) {
args[n] = result;
}
static uint64_t GetReg64(Core::System& system, int n) {
return system.CurrentArmInterface().GetReg(n);
static uint64_t GetArg64(std::span<uint64_t, 8> args, int n) {
return args[n];
}
static void SetReg64(Core::System& system, int n, uint64_t result) {
system.CurrentArmInterface().SetReg(n, result);
static void SetArg64(std::span<uint64_t, 8> args, int n, uint64_t result) {
args[n] = result;
}
// Like bit_cast, but handles the case when the source and dest
@ -590,15 +592,20 @@ EPILOGUE_CPP = """
void Call(Core::System& system, u32 imm) {
auto& kernel = system.Kernel();
auto& process = GetCurrentProcess(kernel);
std::array<uint64_t, 8> args;
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
kernel.EnterSVCProfile();
if (GetCurrentProcess(system.Kernel()).Is64Bit()) {
Call64(system, imm);
if (process.Is64Bit()) {
Call64(system, imm, args);
} else {
Call32(system, imm);
Call32(system, imm, args);
}
kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
}
} // namespace Kernel::Svc
@ -609,13 +616,13 @@ def emit_call(bitness, names, suffix):
bit_size = REG_SIZES[bitness]*8
indent = " "
lines = [
f"static void Call{bit_size}(Core::System& system, u32 imm) {{",
f"static void Call{bit_size}(Core::System& system, u32 imm, std::span<uint64_t, 8> args) {{",
f"{indent}switch (static_cast<SvcId>(imm)) {{"
]
for _, name in names:
lines.append(f"{indent}case SvcId::{name}:")
lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system);")
lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system, args);")
lines.append(f"{indent}default:")
lines.append(