1
0
Fork 0

Core timing 2.0 (#4913)

* Core::Timing: Add multiple timer, one for each core

* revert clang-format; work on tests for CoreTiming

* Kernel:: Add support for multiple cores, asserts in HandleSyncRequest because Thread->status == WaitIPC

* Add some TRACE_LOGs

* fix tests

* make some adjustments to qt-debugger, cheats and gdbstub(probably still broken)

* Make ARM_Interface::id private, rework ARM_Interface ctor

* ReRename TimingManager to Timing for smaler diff

* addressed review comments
This commit is contained in:
Ben 2020-02-21 19:31:32 +01:00 committed by GitHub
parent e3dbdcbdff
commit 55ec7031cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 760 additions and 535 deletions

View File

@ -61,13 +61,14 @@ void RegistersWidget::OnDebugModeEntered() {
if (!Core::System::GetInstance().IsPoweredOn()) if (!Core::System::GetInstance().IsPoweredOn())
return; return;
// Todo: Handle all cores
for (int i = 0; i < core_registers->childCount(); ++i) for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText( core_registers->child(i)->setText(
1, QStringLiteral("0x%1").arg(Core::CPU().GetReg(i), 8, 16, QLatin1Char('0'))); 1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setText( vfp_registers->child(i)->setText(
1, QStringLiteral("0x%1").arg(Core::CPU().GetVFPReg(i), 8, 16, QLatin1Char('0'))); 1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues(); UpdateCPSRValues();
UpdateVFPSystemRegisterValues(); UpdateVFPSystemRegisterValues();
@ -127,7 +128,8 @@ void RegistersWidget::CreateCPSRChildren() {
} }
void RegistersWidget::UpdateCPSRValues() { void RegistersWidget::UpdateCPSRValues() {
const u32 cpsr_val = Core::CPU().GetCPSR(); // Todo: Handle all cores
const u32 cpsr_val = Core::GetCore(0).GetCPSR();
cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText( cpsr->child(0)->setText(
@ -191,10 +193,11 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
} }
void RegistersWidget::UpdateVFPSystemRegisterValues() { void RegistersWidget::UpdateVFPSystemRegisterValues() {
const u32 fpscr_val = Core::CPU().GetVFPSystemReg(VFP_FPSCR); // Todo: handle all cores
const u32 fpexc_val = Core::CPU().GetVFPSystemReg(VFP_FPEXC); const u32 fpscr_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPSCR);
const u32 fpinst_val = Core::CPU().GetVFPSystemReg(VFP_FPINST); const u32 fpexc_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPEXC);
const u32 fpinst2_val = Core::CPU().GetVFPSystemReg(VFP_FPINST2); const u32 fpinst_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPINST);
const u32 fpinst2_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPINST2);
QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0'))); fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0')));

View File

@ -12,6 +12,7 @@
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
#include "core/settings.h"
WaitTreeItem::~WaitTreeItem() = default; WaitTreeItem::~WaitTreeItem() = default;
@ -51,12 +52,16 @@ std::size_t WaitTreeItem::Row() const {
} }
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); u32 num_cores = Core::GetNumCores();
std::vector<std::unique_ptr<WaitTreeThread>> item_list; std::vector<std::unique_ptr<WaitTreeThread>> item_list;
item_list.reserve(threads.size()); for (u32 i = 0; i < num_cores; ++i) {
for (std::size_t i = 0; i < threads.size(); ++i) { const auto& threads =
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
item_list.back()->row = i; item_list.reserve(item_list.size() + threads.size());
for (std::size_t i = 0; i < threads.size(); ++i) {
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
item_list.back()->row = i;
}
} }
return item_list; return item_list;
} }

View File

@ -9,10 +9,13 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/arm/skyeye_common/arm_regformat.h" #include "core/arm/skyeye_common/arm_regformat.h"
#include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h"
#include "core/core_timing.h"
/// Generic ARM11 CPU interface /// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable { class ARM_Interface : NonCopyable {
public: public:
explicit ARM_Interface(u32 id, std::shared_ptr<Core::Timing::Timer> timer)
: timer(timer), id(id){};
virtual ~ARM_Interface() {} virtual ~ARM_Interface() {}
class ThreadContext { class ThreadContext {
@ -172,4 +175,18 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state) /// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0; virtual void PrepareReschedule() = 0;
std::shared_ptr<Core::Timing::Timer> GetTimer() {
return timer;
}
u32 GetID() const {
return id;
}
protected:
std::shared_ptr<Core::Timing::Timer> timer;
private:
u32 id;
}; };

View File

@ -72,8 +72,7 @@ private:
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
: parent(parent), timing(parent.system.CoreTiming()), svc_context(parent.system), : parent(parent), svc_context(parent.system), memory(parent.memory) {}
memory(parent.memory) {}
~DynarmicUserCallbacks() = default; ~DynarmicUserCallbacks() = default;
std::uint8_t MemoryRead8(VAddr vaddr) override { std::uint8_t MemoryRead8(VAddr vaddr) override {
@ -137,7 +136,7 @@ public:
parent.jit->HaltExecution(); parent.jit->HaltExecution();
parent.SetPC(pc); parent.SetPC(pc);
Kernel::Thread* thread = Kernel::Thread* thread =
parent.system.Kernel().GetThreadManager().GetCurrentThread(); parent.system.Kernel().GetCurrentThreadManager().GetCurrentThread();
parent.SaveContext(thread->context); parent.SaveContext(thread->context);
GDBStub::Break(); GDBStub::Break();
GDBStub::SendTrap(thread, 5); GDBStub::SendTrap(thread, 5);
@ -150,22 +149,23 @@ public:
} }
void AddTicks(std::uint64_t ticks) override { void AddTicks(std::uint64_t ticks) override {
timing.AddTicks(ticks); parent.GetTimer()->AddTicks(ticks);
} }
std::uint64_t GetTicksRemaining() override { std::uint64_t GetTicksRemaining() override {
s64 ticks = timing.GetDowncount(); s64 ticks = parent.GetTimer()->GetDowncount();
return static_cast<u64>(ticks <= 0 ? 0 : ticks); return static_cast<u64>(ticks <= 0 ? 0 : ticks);
} }
ARM_Dynarmic& parent; ARM_Dynarmic& parent;
Core::Timing& timing;
Kernel::SVCContext svc_context; Kernel::SVCContext svc_context;
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
}; };
ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode) PrivilegeMode initial_mode, u32 id,
: system(*system), memory(memory), cb(std::make_unique<DynarmicUserCallbacks>(*this)) { std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(*system), memory(memory),
cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
interpreter_state = std::make_shared<ARMul_State>(system, memory, initial_mode); interpreter_state = std::make_shared<ARMul_State>(system, memory, initial_mode);
PageTableChanged(); PageTableChanged();
} }

View File

@ -24,7 +24,8 @@ class DynarmicUserCallbacks;
class ARM_Dynarmic final : public ARM_Interface { class ARM_Dynarmic final : public ARM_Interface {
public: public:
ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, PrivilegeMode initial_mode); ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, PrivilegeMode initial_mode,
u32 id, std::shared_ptr<Core::Timing::Timer> timer);
~ARM_Dynarmic() override; ~ARM_Dynarmic() override;
void Run() override; void Run() override;

View File

@ -69,8 +69,9 @@ private:
}; };
ARM_DynCom::ARM_DynCom(Core::System* system, Memory::MemorySystem& memory, ARM_DynCom::ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode) PrivilegeMode initial_mode, u32 id,
: system(system) { std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(system) {
state = std::make_unique<ARMul_State>(system, memory, initial_mode); state = std::make_unique<ARMul_State>(system, memory, initial_mode);
} }
@ -78,7 +79,7 @@ ARM_DynCom::~ARM_DynCom() {}
void ARM_DynCom::Run() { void ARM_DynCom::Run() {
DEBUG_ASSERT(system != nullptr); DEBUG_ASSERT(system != nullptr);
ExecuteInstructions(std::max<s64>(system->CoreTiming().GetDowncount(), 0)); ExecuteInstructions(std::max<s64>(timer->GetDowncount(), 0));
} }
void ARM_DynCom::Step() { void ARM_DynCom::Step() {
@ -150,7 +151,7 @@ void ARM_DynCom::ExecuteInstructions(u64 num_instructions) {
state->NumInstrsToExecute = num_instructions; state->NumInstrsToExecute = num_instructions;
unsigned ticks_executed = InterpreterMainLoop(state.get()); unsigned ticks_executed = InterpreterMainLoop(state.get());
if (system != nullptr) { if (system != nullptr) {
system->CoreTiming().AddTicks(ticks_executed); timer->AddTicks(ticks_executed);
} }
state->ServeBreak(); state->ServeBreak();
} }

View File

@ -21,7 +21,8 @@ class MemorySystem;
class ARM_DynCom final : public ARM_Interface { class ARM_DynCom final : public ARM_Interface {
public: public:
explicit ARM_DynCom(Core::System* system, Memory::MemorySystem& memory, explicit ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode); PrivilegeMode initial_mode, u32 id,
std::shared_ptr<Core::Timing::Timer> timer);
~ARM_DynCom() override; ~ARM_DynCom() override;
void Run() override; void Run() override;

View File

@ -3865,7 +3865,7 @@ SWI_INST : {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
DEBUG_ASSERT(cpu->system != nullptr); DEBUG_ASSERT(cpu->system != nullptr);
swi_inst* const inst_cream = (swi_inst*)inst_base->component; swi_inst* const inst_cream = (swi_inst*)inst_base->component;
cpu->system->CoreTiming().AddTicks(num_instrs); cpu->system->GetRunningCore().GetTimer()->AddTicks(num_instrs);
cpu->NumInstrsToExecute = cpu->NumInstrsToExecute =
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0; num_instrs = 0;

View File

@ -607,8 +607,8 @@ void ARMul_State::ServeBreak() {
} }
DEBUG_ASSERT(system != nullptr); DEBUG_ASSERT(system != nullptr);
Kernel::Thread* thread = system->Kernel().GetThreadManager().GetCurrentThread(); Kernel::Thread* thread = system->Kernel().GetCurrentThreadManager().GetCurrentThread();
system->CPU().SaveContext(thread->context); system->GetRunningCore().SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false; last_bkpt_hit = false;

View File

@ -35,7 +35,7 @@ static inline std::enable_if_t<std::is_integral_v<T>> WriteOp(const GatewayCheat
Core::System& system) { Core::System& system) {
u32 addr = line.address + state.offset; u32 addr = line.address + state.offset;
write_func(addr, static_cast<T>(line.value)); write_func(addr, static_cast<T>(line.value));
system.CPU().InvalidateCacheRange(addr, sizeof(T)); system.InvalidateCacheRange(addr, sizeof(T));
} }
template <typename T, typename ReadFunction, typename CompareFunc> template <typename T, typename ReadFunction, typename CompareFunc>
@ -105,7 +105,7 @@ static inline std::enable_if_t<std::is_integral_v<T>> IncrementiveWriteOp(
Core::System& system) { Core::System& system) {
u32 addr = line.value + state.offset; u32 addr = line.value + state.offset;
write_func(addr, static_cast<T>(state.reg)); write_func(addr, static_cast<T>(state.reg));
system.CPU().InvalidateCacheRange(addr, sizeof(T)); system.InvalidateCacheRange(addr, sizeof(T));
state.offset += sizeof(T); state.offset += sizeof(T);
} }
@ -143,7 +143,8 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co
} }
u32 num_bytes = line.value; u32 num_bytes = line.value;
u32 addr = line.address + state.offset; u32 addr = line.address + state.offset;
system.CPU().InvalidateCacheRange(addr, num_bytes); system.InvalidateCacheRange(addr, num_bytes);
bool first = true; bool first = true;
u32 bit_offset = 0; u32 bit_offset = 0;
if (num_bytes > 0) if (num_bytes > 0)

View File

@ -44,7 +44,8 @@ namespace Core {
System::ResultStatus System::RunLoop(bool tight_loop) { System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success; status = ResultStatus::Success;
if (!cpu_core) { if (std::any_of(cpu_cores.begin(), cpu_cores.end(),
[](std::shared_ptr<ARM_Interface> ptr) { return ptr == nullptr; })) {
return ResultStatus::ErrorNotInitialized; return ResultStatus::ErrorNotInitialized;
} }
@ -62,22 +63,73 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
} }
} }
// If we don't have a currently active thread then don't execute instructions, // All cores should have executed the same amount of ticks. If this is not the case an event was
// instead advance to the next event and try to yield to the next thread // scheduled with a cycles_into_future smaller then the current downcount.
if (kernel->GetThreadManager().GetCurrentThread() == nullptr) { // So we have to get those cores to the same global time first
LOG_TRACE(Core_ARM11, "Idling"); u64 global_ticks = timing->GetGlobalTicks();
timing->Idle(); s64 max_delay = 0;
timing->Advance(); std::shared_ptr<ARM_Interface> current_core_to_execute = nullptr;
PrepareReschedule(); for (auto& cpu_core : cpu_cores) {
} else { if (cpu_core->GetTimer()->GetTicks() < global_ticks) {
timing->Advance(); s64 delay = global_ticks - cpu_core->GetTimer()->GetTicks();
if (tight_loop) { cpu_core->GetTimer()->Advance(delay);
cpu_core->Run(); if (max_delay < delay) {
} else { max_delay = delay;
cpu_core->Step(); current_core_to_execute = cpu_core;
}
} }
} }
if (max_delay > 0) {
LOG_TRACE(Core_ARM11, "Core {} running (delayed) for {} ticks",
current_core_to_execute->GetID(),
current_core_to_execute->GetTimer()->GetDowncount());
running_core = current_core_to_execute.get();
kernel->SetRunningCPU(current_core_to_execute);
if (kernel->GetCurrentThreadManager().GetCurrentThread() == nullptr) {
LOG_TRACE(Core_ARM11, "Core {} idling", current_core_to_execute->GetID());
current_core_to_execute->GetTimer()->Idle();
PrepareReschedule();
} else {
if (tight_loop) {
current_core_to_execute->Run();
} else {
current_core_to_execute->Step();
}
}
} else {
// Now all cores are at the same global time. So we will run them one after the other
// with a max slice that is the minimum of all max slices of all cores
// TODO: Make special check for idle since we can easily revert the time of idle cores
s64 max_slice = Timing::MAX_SLICE_LENGTH;
for (const auto& cpu_core : cpu_cores) {
max_slice = std::min(max_slice, cpu_core->GetTimer()->GetMaxSliceLength());
}
for (auto& cpu_core : cpu_cores) {
cpu_core->GetTimer()->Advance(max_slice);
}
for (auto& cpu_core : cpu_cores) {
LOG_TRACE(Core_ARM11, "Core {} running for {} ticks", cpu_core->GetID(),
cpu_core->GetTimer()->GetDowncount());
running_core = cpu_core.get();
kernel->SetRunningCPU(cpu_core);
// If we don't have a currently active thread then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
if (kernel->GetCurrentThreadManager().GetCurrentThread() == nullptr) {
LOG_TRACE(Core_ARM11, "Core {} idling", cpu_core->GetID());
cpu_core->GetTimer()->Idle();
PrepareReschedule();
} else {
if (tight_loop) {
cpu_core->Run();
} else {
cpu_core->Step();
}
}
}
timing->AddToGlobalTicks(max_slice);
}
if (GDBStub::IsServerEnabled()) { if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false); GDBStub::SetCpuStepFlag(false);
} }
@ -174,7 +226,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
} }
void System::PrepareReschedule() { void System::PrepareReschedule() {
cpu_core->PrepareReschedule(); running_core->PrepareReschedule();
reschedule_pending = true; reschedule_pending = true;
} }
@ -188,31 +240,50 @@ void System::Reschedule() {
} }
reschedule_pending = false; reschedule_pending = false;
kernel->GetThreadManager().Reschedule(); for (const auto& core : cpu_cores) {
LOG_TRACE(Core_ARM11, "Reschedule core {}", core->GetID());
kernel->GetThreadManager(core->GetID()).Reschedule();
}
} }
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode) { System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK"); LOG_DEBUG(HW_Memory, "initialized OK");
std::size_t num_cores = 2;
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>();
timing = std::make_unique<Timing>(); timing = std::make_unique<Timing>(num_cores);
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, kernel = std::make_unique<Kernel::KernelSystem>(
[this] { PrepareReschedule(); }, system_mode); *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores);
if (Settings::values.use_cpu_jit) { if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
cpu_core = std::make_shared<ARM_Dynarmic>(this, *memory, USER32MODE); for (std::size_t i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_Dynarmic>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
}
#else #else
cpu_core = std::make_shared<ARM_DynCom>(this, *memory, USER32MODE); for (std::size_t i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
}
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif #endif
} else { } else {
cpu_core = std::make_shared<ARM_DynCom>(this, *memory, USER32MODE); for (std::size_t i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
}
} }
running_core = cpu_cores[0].get();
kernel->SetCPU(cpu_core); kernel->SetCPUs(cpu_cores);
kernel->SetRunningCPU(cpu_cores[0]);
if (Settings::values.enable_dsp_lle) { if (Settings::values.enable_dsp_lle) {
dsp_core = std::make_unique<AudioCore::DspLle>(*memory, dsp_core = std::make_unique<AudioCore::DspLle>(*memory,
@ -257,6 +328,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
LOG_DEBUG(Core, "Initialized OK"); LOG_DEBUG(Core, "Initialized OK");
initalized = true;
return ResultStatus::Success; return ResultStatus::Success;
} }
@ -362,7 +435,7 @@ void System::Shutdown() {
cheat_engine.reset(); cheat_engine.reset();
service_manager.reset(); service_manager.reset();
dsp_core.reset(); dsp_core.reset();
cpu_core.reset(); cpu_cores.clear();
kernel.reset(); kernel.reset();
timing.reset(); timing.reset();
app_loader.reset(); app_loader.reset();

View File

@ -140,7 +140,10 @@ public:
* @returns True if the emulated system is powered on, otherwise false. * @returns True if the emulated system is powered on, otherwise false.
*/ */
bool IsPoweredOn() const { bool IsPoweredOn() const {
return cpu_core != nullptr; return cpu_cores.size() > 0 &&
std::all_of(cpu_cores.begin(), cpu_cores.end(),
[](std::shared_ptr<ARM_Interface> ptr) { return ptr != nullptr; });
;
} }
/** /**
@ -160,8 +163,29 @@ public:
* Gets a reference to the emulated CPU. * Gets a reference to the emulated CPU.
* @returns A reference to the emulated CPU. * @returns A reference to the emulated CPU.
*/ */
ARM_Interface& CPU() {
return *cpu_core; ARM_Interface& GetRunningCore() {
return *running_core;
};
/**
* Gets a reference to the emulated CPU.
* @param core_id The id of the core requested.
* @returns A reference to the emulated CPU.
*/
ARM_Interface& GetCore(u32 core_id) {
return *cpu_cores[core_id];
};
u32 GetNumCores() const {
return cpu_cores.size();
}
void InvalidateCacheRange(u32 start_address, std::size_t length) {
for (const auto& cpu : cpu_cores) {
cpu->InvalidateCacheRange(start_address, length);
}
} }
/** /**
@ -288,7 +312,8 @@ private:
std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<Loader::AppLoader> app_loader;
/// ARM11 CPU core /// ARM11 CPU core
std::shared_ptr<ARM_Interface> cpu_core; std::vector<std::shared_ptr<ARM_Interface>> cpu_cores;
ARM_Interface* running_core = nullptr;
/// DSP core /// DSP core
std::unique_ptr<AudioCore::DspInterface> dsp_core; std::unique_ptr<AudioCore::DspInterface> dsp_core;
@ -330,6 +355,8 @@ private:
private: private:
static System s_instance; static System s_instance;
bool initalized = false;
ResultStatus status = ResultStatus::Success; ResultStatus status = ResultStatus::Success;
std::string status_details = ""; std::string status_details = "";
/// Saved variables for reset /// Saved variables for reset
@ -340,8 +367,16 @@ private:
std::atomic<bool> shutdown_requested; std::atomic<bool> shutdown_requested;
}; };
inline ARM_Interface& CPU() { inline ARM_Interface& GetRunningCore() {
return System::GetInstance().CPU(); return System::GetInstance().GetRunningCore();
}
inline ARM_Interface& GetCore(u32 core_id) {
return System::GetInstance().GetCore(core_id);
}
inline u32 GetNumCores() {
return System::GetInstance().GetNumCores();
} }
inline AudioCore::DspInterface& DSP() { inline AudioCore::DspInterface& DSP() {

View File

@ -12,14 +12,22 @@
namespace Core { namespace Core {
// Sort by time, unless the times are the same, in which case sort by the order added to the queue // Sort by time, unless the times are the same, in which case sort by the order added to the queue
bool Timing::Event::operator>(const Event& right) const { bool Timing::Event::operator>(const Timing::Event& right) const {
return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order); return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order);
} }
bool Timing::Event::operator<(const Event& right) const { bool Timing::Event::operator<(const Timing::Event& right) const {
return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order); return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order);
} }
Timing::Timing(std::size_t num_cores) {
timers.resize(num_cores);
for (std::size_t i = 0; i < num_cores; ++i) {
timers[i] = std::make_shared<Timer>();
}
current_timer = timers[0];
}
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) { TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
// check for existing type with same name. // check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization. // we want event type names to remain unique so that we can use them for serialization.
@ -34,73 +42,102 @@ TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback ca
return event_type; return event_type;
} }
Timing::~Timing() { void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata,
std::size_t core_id) {
ASSERT(event_type != nullptr);
std::shared_ptr<Timing::Timer> timer;
if (core_id == std::numeric_limits<std::size_t>::max()) {
timer = current_timer;
} else {
ASSERT(core_id < timers.size());
timer = timers.at(core_id);
}
s64 timeout = timer->GetTicks() + cycles_into_future;
if (current_timer == timer) {
// If this event needs to be scheduled before the next advance(), force one early
if (!timer->is_timer_sane)
timer->ForceExceptionCheck(cycles_into_future);
timer->event_queue.emplace_back(
Event{timeout, timer->event_fifo_id++, userdata, event_type});
std::push_heap(timer->event_queue.begin(), timer->event_queue.end(), std::greater<>());
} else {
timer->ts_queue.Push(Event{static_cast<s64>(timer->GetTicks() + cycles_into_future), 0,
userdata, event_type});
}
}
void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
for (auto timer : timers) {
auto itr = std::remove_if(
timer->event_queue.begin(), timer->event_queue.end(),
[&](const Event& e) { return e.type == event_type && e.userdata == userdata; });
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != timer->event_queue.end()) {
timer->event_queue.erase(itr, timer->event_queue.end());
std::make_heap(timer->event_queue.begin(), timer->event_queue.end(), std::greater<>());
}
}
// TODO:remove events from ts_queue
}
void Timing::RemoveEvent(const TimingEventType* event_type) {
for (auto timer : timers) {
auto itr = std::remove_if(timer->event_queue.begin(), timer->event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != timer->event_queue.end()) {
timer->event_queue.erase(itr, timer->event_queue.end());
std::make_heap(timer->event_queue.begin(), timer->event_queue.end(), std::greater<>());
}
}
// TODO:remove events from ts_queue
}
void Timing::SetCurrentTimer(std::size_t core_id) {
current_timer = timers[core_id];
}
s64 Timing::GetTicks() const {
return current_timer->GetTicks();
}
s64 Timing::GetGlobalTicks() const {
return global_timer;
}
std::chrono::microseconds Timing::GetGlobalTimeUs() const {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11};
}
std::shared_ptr<Timing::Timer> Timing::GetTimer(std::size_t cpu_id) {
return timers[cpu_id];
}
Timing::Timer::~Timer() {
MoveEvents(); MoveEvents();
} }
u64 Timing::GetTicks() const { u64 Timing::Timer::GetTicks() const {
u64 ticks = static_cast<u64>(global_timer); u64 ticks = static_cast<u64>(executed_ticks);
if (!is_global_timer_sane) { if (!is_timer_sane) {
ticks += slice_length - downcount; ticks += slice_length - downcount;
} }
return ticks; return ticks;
} }
void Timing::AddTicks(u64 ticks) { void Timing::Timer::AddTicks(u64 ticks) {
downcount -= ticks; downcount -= ticks;
} }
u64 Timing::GetIdleTicks() const { u64 Timing::Timer::GetIdleTicks() const {
return static_cast<u64>(idled_cycles); return static_cast<u64>(idled_cycles);
} }
void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, void Timing::Timer::ForceExceptionCheck(s64 cycles) {
u64 userdata) {
ASSERT(event_type != nullptr);
s64 timeout = GetTicks() + cycles_into_future;
// If this event needs to be scheduled before the next advance(), force one early
if (!is_global_timer_sane)
ForceExceptionCheck(cycles_into_future);
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void Timing::ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
u64 userdata) {
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
}
void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata;
});
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
void Timing::RemoveEvent(const TimingEventType* event_type) {
auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
void Timing::RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type) {
MoveEvents();
RemoveEvent(event_type);
}
void Timing::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles); cycles = std::max<s64>(0, cycles);
if (downcount > cycles) { if (downcount > cycles) {
slice_length -= downcount - cycles; slice_length -= downcount - cycles;
@ -108,7 +145,7 @@ void Timing::ForceExceptionCheck(s64 cycles) {
} }
} }
void Timing::MoveEvents() { void Timing::Timer::MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) { for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++; ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev)); event_queue.emplace_back(std::move(ev));
@ -116,43 +153,49 @@ void Timing::MoveEvents() {
} }
} }
void Timing::Advance() { s64 Timing::Timer::GetMaxSliceLength() const {
auto next_event = std::find_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.time - executed_ticks > 0; });
if (next_event != event_queue.end()) {
return next_event->time - executed_ticks;
}
return MAX_SLICE_LENGTH;
}
void Timing::Timer::Advance(s64 max_slice_length) {
MoveEvents(); MoveEvents();
s64 cycles_executed = slice_length - downcount; s64 cycles_executed = slice_length - downcount;
global_timer += cycles_executed; idled_cycles = 0;
slice_length = MAX_SLICE_LENGTH; executed_ticks += cycles_executed;
slice_length = max_slice_length;
is_global_timer_sane = true; is_timer_sane = true;
while (!event_queue.empty() && event_queue.front().time <= global_timer) { while (!event_queue.empty() && event_queue.front().time <= executed_ticks) {
Event evt = std::move(event_queue.front()); Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back(); event_queue.pop_back();
evt.type->callback(evt.userdata, global_timer - evt.time); evt.type->callback(evt.userdata, executed_ticks - evt.time);
} }
is_global_timer_sane = false; is_timer_sane = false;
// Still events left (scheduled in the future) // Still events left (scheduled in the future)
if (!event_queue.empty()) { if (!event_queue.empty()) {
slice_length = static_cast<int>( slice_length = static_cast<int>(
std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH)); std::min<s64>(event_queue.front().time - executed_ticks, max_slice_length));
} }
downcount = slice_length; downcount = slice_length;
} }
void Timing::Idle() { void Timing::Timer::Idle() {
idled_cycles += downcount; idled_cycles += downcount;
downcount = 0; downcount = 0;
} }
std::chrono::microseconds Timing::GetGlobalTimeUs() const { s64 Timing::Timer::GetDowncount() const {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11};
}
s64 Timing::GetDowncount() const {
return downcount; return downcount;
} }

View File

@ -134,62 +134,6 @@ struct TimingEventType {
class Timing { class Timing {
public: public:
~Timing();
/**
* This should only be called from the emu thread, if you are calling it any other thread, you
* are doing something evil
*/
u64 GetTicks() const;
u64 GetIdleTicks() const;
void AddTicks(u64 ticks);
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback);
/**
* After the first Advance, the slice lengths and the downcount will be reduced whenever an
* event is scheduled earlier than the current values. Scheduling from a callback will not
* update the downcount until the Advance() completes.
*/
void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0);
/**
* This is to be called when outside of hle threads, such as the graphics thread, wants to
* schedule things to be executed on the main thread.
* Not that this doesn't change slice_length and thus events scheduled by this might be called
* with a delay of up to MAX_SLICE_LENGTH
*/
void ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
u64 userdata);
void UnscheduleEvent(const TimingEventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const TimingEventType* event_type);
void RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type);
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
* the previous timing slice and begins the next one, you must Advance from the previous
* slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
* Advance() is required to initialize the slice length before the first cycle of emulated
* instructions is executed.
*/
void Advance();
void MoveEvents();
/// Pretend that the main CPU has executed enough cycles to reach the next event.
void Idle();
void ForceExceptionCheck(s64 cycles);
std::chrono::microseconds GetGlobalTimeUs() const;
s64 GetDowncount() const;
private:
struct Event { struct Event {
s64 time; s64 time;
u64 fifo_order; u64 fifo_order;
@ -202,33 +146,93 @@ private:
static constexpr int MAX_SLICE_LENGTH = 20000; static constexpr int MAX_SLICE_LENGTH = 20000;
class Timer {
public:
~Timer();
s64 GetMaxSliceLength() const;
void Advance(s64 max_slice_length = MAX_SLICE_LENGTH);
void Idle();
u64 GetTicks() const;
u64 GetIdleTicks() const;
void AddTicks(u64 ticks);
s64 GetDowncount() const;
void ForceExceptionCheck(s64 cycles);
void MoveEvents();
private:
friend class Timing;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
// accomodated by the standard adaptor class.
std::vector<Event> event_queue;
u64 event_fifo_id = 0;
// the queue for storing the events from other threads threadsafe until they will be added
// to the event_queue by the emu thread
Common::MPSCQueue<Event> ts_queue;
// Are we in a function that has been called from Advance()
// If events are sheduled from a function that gets called from Advance(),
// don't change slice_length and downcount.
// The time between CoreTiming being intialized and the first call to Advance() is
// considered the slice boundary between slice -1 and slice 0. Dispatcher loops must call
// Advance() before executing the first cycle of each slice to prepare the slice length and
// downcount for that slice.
bool is_timer_sane = true;
s64 slice_length = MAX_SLICE_LENGTH;
s64 downcount = MAX_SLICE_LENGTH;
s64 executed_ticks = 0;
u64 idled_cycles;
};
explicit Timing(std::size_t num_cores);
~Timing(){};
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback);
void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0,
std::size_t core_id = std::numeric_limits<std::size_t>::max());
void UnscheduleEvent(const TimingEventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const TimingEventType* event_type);
void SetCurrentTimer(std::size_t core_id);
s64 GetTicks() const;
s64 GetGlobalTicks() const;
void AddToGlobalTicks(s64 ticks) {
global_timer += ticks;
}
std::chrono::microseconds GetGlobalTimeUs() const;
std::shared_ptr<Timer> GetTimer(std::size_t cpu_id);
private:
s64 global_timer = 0; s64 global_timer = 0;
s64 slice_length = MAX_SLICE_LENGTH;
s64 downcount = MAX_SLICE_LENGTH;
// unordered_map stores each element separately as a linked list node so pointers to // unordered_map stores each element separately as a linked list node so pointers to
// elements remain stable regardless of rehashes/resizing. // elements remain stable regardless of rehashes/resizing.
std::unordered_map<std::string, TimingEventType> event_types; std::unordered_map<std::string, TimingEventType> event_types;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap. std::vector<std::shared_ptr<Timer>> timers;
// We don't use std::priority_queue because we need to be able to serialize, unserialize and std::shared_ptr<Timer> current_timer;
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
// accomodated by the standard adaptor class.
std::vector<Event> event_queue;
u64 event_fifo_id = 0;
// the queue for storing the events from other threads threadsafe until they will be added
// to the event_queue by the emu thread
Common::MPSCQueue<Event> ts_queue;
s64 idled_cycles = 0;
// Are we in a function that has been called from Advance()
// If events are sheduled from a function that gets called from Advance(),
// don't change slice_length and downcount.
// The time between CoreTiming being intialized and the first call to Advance() is considered
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
// executing the first cycle of each slice to prepare the slice length and downcount for
// that slice.
bool is_global_timer_sane = true;
}; };
} // namespace Core } // namespace Core

View File

@ -160,10 +160,14 @@ BreakpointMap breakpoints_write;
} // Anonymous namespace } // Anonymous namespace
static Kernel::Thread* FindThreadById(int id) { static Kernel::Thread* FindThreadById(int id) {
const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); u32 num_cores = Core::GetNumCores();
for (auto& thread : threads) { for (u32 i = 0; i < num_cores; ++i) {
if (thread->GetThreadId() == static_cast<u32>(id)) { const auto& threads =
return thread.get(); Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadId() == static_cast<u32>(id)) {
return thread.get();
}
} }
} }
return nullptr; return nullptr;
@ -414,7 +418,10 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
Core::System::GetInstance().Memory().WriteBlock( Core::System::GetInstance().Memory().WriteBlock(
*Core::System::GetInstance().Kernel().GetCurrentProcess(), bp->second.addr, *Core::System::GetInstance().Kernel().GetCurrentProcess(), bp->second.addr,
bp->second.inst.data(), bp->second.inst.size()); bp->second.inst.data(), bp->second.inst.size());
Core::CPU().ClearInstructionCache(); u32 num_cores = Core::GetNumCores();
for (u32 i = 0; i < num_cores; ++i) {
Core::GetCore(i).ClearInstructionCache();
}
} }
p.erase(addr); p.erase(addr);
} }
@ -540,10 +547,13 @@ static void HandleQuery() {
SendReply(target_xml); SendReply(target_xml);
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m"; std::string val = "m";
const auto& threads = u32 num_cores = Core::GetNumCores();
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); for (u32 i = 0; i < num_cores; ++i) {
for (const auto& thread : threads) { const auto& threads =
val += fmt::format("{:x},", thread->GetThreadId()); Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x},", thread->GetThreadId());
}
} }
val.pop_back(); val.pop_back();
SendReply(val.c_str()); SendReply(val.c_str());
@ -553,11 +563,14 @@ static void HandleQuery() {
std::string buffer; std::string buffer;
buffer += "l<?xml version=\"1.0\"?>"; buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>"; buffer += "<threads>";
const auto& threads = u32 num_cores = Core::GetNumCores();
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); for (u32 i = 0; i < num_cores; ++i) {
for (const auto& thread : threads) { const auto& threads =
buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*", Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
thread->GetThreadId(), thread->GetThreadId()); for (const auto& thread : threads) {
buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*",
thread->GetThreadId(), thread->GetThreadId());
}
} }
buffer += "</threads>"; buffer += "</threads>";
SendReply(buffer.c_str()); SendReply(buffer.c_str());
@ -619,9 +632,9 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
if (full) { if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};{:02x}:{:08x}", latest_signal, buffer = fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};{:02x}:{:08x}", latest_signal,
PC_REGISTER, htonl(Core::CPU().GetPC()), SP_REGISTER, PC_REGISTER, htonl(Core::GetRunningCore().GetPC()), SP_REGISTER,
htonl(Core::CPU().GetReg(SP_REGISTER)), LR_REGISTER, htonl(Core::GetRunningCore().GetReg(SP_REGISTER)), LR_REGISTER,
htonl(Core::CPU().GetReg(LR_REGISTER))); htonl(Core::GetRunningCore().GetReg(LR_REGISTER)));
} else { } else {
buffer = fmt::format("T{:02x}", latest_signal); buffer = fmt::format("T{:02x}", latest_signal);
} }
@ -782,7 +795,7 @@ static void WriteRegister() {
return SendReply("E01"); return SendReply("E01");
} }
Core::CPU().LoadContext(current_thread->context); Core::GetRunningCore().LoadContext(current_thread->context);
SendReply("OK"); SendReply("OK");
} }
@ -812,7 +825,7 @@ static void WriteRegisters() {
} }
} }
Core::CPU().LoadContext(current_thread->context); Core::GetRunningCore().LoadContext(current_thread->context);
SendReply("OK"); SendReply("OK");
} }
@ -869,7 +882,7 @@ static void WriteMemory() {
GdbHexToMem(data.data(), len_pos + 1, len); GdbHexToMem(data.data(), len_pos + 1, len);
Core::System::GetInstance().Memory().WriteBlock( Core::System::GetInstance().Memory().WriteBlock(
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len); *Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len);
Core::CPU().ClearInstructionCache(); Core::GetRunningCore().ClearInstructionCache();
SendReply("OK"); SendReply("OK");
} }
@ -883,12 +896,12 @@ void Break(bool is_memory_break) {
static void Step() { static void Step() {
if (command_length > 1) { if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToInt(command_buffer + 1), current_thread); RegWrite(PC_REGISTER, GdbHexToInt(command_buffer + 1), current_thread);
Core::CPU().LoadContext(current_thread->context); Core::GetRunningCore().LoadContext(current_thread->context);
} }
step_loop = true; step_loop = true;
halt_loop = true; halt_loop = true;
send_trap = true; send_trap = true;
Core::CPU().ClearInstructionCache(); Core::GetRunningCore().ClearInstructionCache();
} }
bool IsMemoryBreak() { bool IsMemoryBreak() {
@ -904,7 +917,7 @@ static void Continue() {
memory_break = false; memory_break = false;
step_loop = false; step_loop = false;
halt_loop = false; halt_loop = false;
Core::CPU().ClearInstructionCache(); Core::GetRunningCore().ClearInstructionCache();
} }
/** /**
@ -930,7 +943,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u32 len) {
Core::System::GetInstance().Memory().WriteBlock( Core::System::GetInstance().Memory().WriteBlock(
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, btrap.data(), *Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, btrap.data(),
btrap.size()); btrap.size());
Core::CPU().ClearInstructionCache(); Core::GetRunningCore().ClearInstructionCache();
} }
p.insert({addr, breakpoint}); p.insert({addr, breakpoint});

View File

@ -83,7 +83,7 @@ bool HandleTable::IsValid(Handle handle) const {
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const { std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) { if (handle == CurrentThread) {
return SharedFrom(kernel.GetThreadManager().GetCurrentThread()); return SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
} else if (handle == CurrentProcess) { } else if (handle == CurrentProcess) {
return kernel.GetCurrentProcess(); return kernel.GetCurrentProcess();
} }

View File

@ -18,19 +18,27 @@ namespace Kernel {
/// Initialize the kernel /// Initialize the kernel
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode) std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores)
: memory(memory), timing(timing), : memory(memory), timing(timing),
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) { prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
MemoryInit(system_mode); MemoryInit(system_mode);
resource_limits = std::make_unique<ResourceLimitList>(*this); resource_limits = std::make_unique<ResourceLimitList>(*this);
thread_manager = std::make_unique<ThreadManager>(*this); for (u32 core_id = 0; core_id < num_cores; ++core_id) {
thread_managers.push_back(std::make_unique<ThreadManager>(*this, core_id));
}
timer_manager = std::make_unique<TimerManager>(timing); timer_manager = std::make_unique<TimerManager>(timing);
ipc_recorder = std::make_unique<IPCDebugger::Recorder>(); ipc_recorder = std::make_unique<IPCDebugger::Recorder>();
stored_processes.assign(num_cores, nullptr);
next_thread_id = 1;
} }
/// Shutdown the kernel /// Shutdown the kernel
KernelSystem::~KernelSystem() = default; KernelSystem::~KernelSystem() {
ResetThreadIDs();
};
ResourceLimitList& KernelSystem::ResourceLimit() { ResourceLimitList& KernelSystem::ResourceLimit() {
return *resource_limits; return *resource_limits;
@ -53,6 +61,15 @@ void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) {
SetCurrentMemoryPageTable(&process->vm_manager.page_table); SetCurrentMemoryPageTable(&process->vm_manager.page_table);
} }
void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id) {
if (current_cpu->GetID() == core_id) {
current_process = process;
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
} else {
stored_processes[core_id] = process;
}
}
void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) { void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
memory.SetCurrentPageTable(page_table); memory.SetCurrentPageTable(page_table);
if (current_cpu != nullptr) { if (current_cpu != nullptr) {
@ -60,17 +77,39 @@ void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
} }
} }
void KernelSystem::SetCPU(std::shared_ptr<ARM_Interface> cpu) { void KernelSystem::SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpus) {
ASSERT(cpus.size() == thread_managers.size());
u32 i = 0;
for (const auto& cpu : cpus) {
thread_managers[i++]->SetCPU(*cpu);
}
}
void KernelSystem::SetRunningCPU(std::shared_ptr<ARM_Interface> cpu) {
if (current_process) {
stored_processes[current_cpu->GetID()] = current_process;
}
current_cpu = cpu; current_cpu = cpu;
thread_manager->SetCPU(*cpu); timing.SetCurrentTimer(cpu->GetID());
if (stored_processes[current_cpu->GetID()]) {
SetCurrentProcess(stored_processes[current_cpu->GetID()]);
}
} }
ThreadManager& KernelSystem::GetThreadManager() { ThreadManager& KernelSystem::GetThreadManager(u32 core_id) {
return *thread_manager; return *thread_managers[core_id];
} }
const ThreadManager& KernelSystem::GetThreadManager() const { const ThreadManager& KernelSystem::GetThreadManager(u32 core_id) const {
return *thread_manager; return *thread_managers[core_id];
}
ThreadManager& KernelSystem::GetCurrentThreadManager() {
return *thread_managers[current_cpu->GetID()];
}
const ThreadManager& KernelSystem::GetCurrentThreadManager() const {
return *thread_managers[current_cpu->GetID()];
} }
TimerManager& KernelSystem::GetTimerManager() { TimerManager& KernelSystem::GetTimerManager() {
@ -101,4 +140,12 @@ void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> po
named_ports.emplace(std::move(name), std::move(port)); named_ports.emplace(std::move(name), std::move(port));
} }
u32 KernelSystem::NewThreadId() {
return next_thread_id++;
}
void KernelSystem::ResetThreadIDs() {
next_thread_id = 0;
}
} // namespace Kernel } // namespace Kernel

View File

@ -85,7 +85,8 @@ enum class MemoryRegion : u16 {
class KernelSystem { class KernelSystem {
public: public:
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode); std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores);
~KernelSystem(); ~KernelSystem();
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>; using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
@ -210,13 +211,19 @@ public:
std::shared_ptr<Process> GetCurrentProcess() const; std::shared_ptr<Process> GetCurrentProcess() const;
void SetCurrentProcess(std::shared_ptr<Process> process); void SetCurrentProcess(std::shared_ptr<Process> process);
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);
void SetCurrentMemoryPageTable(Memory::PageTable* page_table); void SetCurrentMemoryPageTable(Memory::PageTable* page_table);
void SetCPU(std::shared_ptr<ARM_Interface> cpu); void SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpu);
ThreadManager& GetThreadManager(); void SetRunningCPU(std::shared_ptr<ARM_Interface> cpu);
const ThreadManager& GetThreadManager() const;
ThreadManager& GetThreadManager(u32 core_id);
const ThreadManager& GetThreadManager(u32 core_id) const;
ThreadManager& GetCurrentThreadManager();
const ThreadManager& GetCurrentThreadManager() const;
TimerManager& GetTimerManager(); TimerManager& GetTimerManager();
const TimerManager& GetTimerManager() const; const TimerManager& GetTimerManager() const;
@ -242,6 +249,10 @@ public:
prepare_reschedule_callback(); prepare_reschedule_callback();
} }
u32 NewThreadId();
void ResetThreadIDs();
/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort
std::unordered_map<std::string, std::shared_ptr<ClientPort>> named_ports; std::unordered_map<std::string, std::shared_ptr<ClientPort>> named_ports;
@ -276,13 +287,16 @@ private:
std::vector<std::shared_ptr<Process>> process_list; std::vector<std::shared_ptr<Process>> process_list;
std::shared_ptr<Process> current_process; std::shared_ptr<Process> current_process;
std::vector<std::shared_ptr<Process>> stored_processes;
std::unique_ptr<ThreadManager> thread_manager; std::vector<std::unique_ptr<ThreadManager>> thread_managers;
std::unique_ptr<ConfigMem::Handler> config_mem_handler; std::unique_ptr<ConfigMem::Handler> config_mem_handler;
std::unique_ptr<SharedPage::Handler> shared_page_handler; std::unique_ptr<SharedPage::Handler> shared_page_handler;
std::unique_ptr<IPCDebugger::Recorder> ipc_recorder; std::unique_ptr<IPCDebugger::Recorder> ipc_recorder;
u32 next_thread_id;
}; };
} // namespace Kernel } // namespace Kernel

View File

@ -35,7 +35,7 @@ std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::strin
// Acquire mutex with current thread if initialized as locked // Acquire mutex with current thread if initialized as locked
if (initial_locked) if (initial_locked)
mutex->Acquire(thread_manager->GetCurrentThread()); mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread());
return mutex; return mutex;
} }

View File

@ -56,7 +56,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) {
using namespace std::placeholders; using namespace std::placeholders;
update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback", update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback",
std::bind(&Handler::UpdateTimeCallback, this, _1, _2)); std::bind(&Handler::UpdateTimeCallback, this, _1, _2));
timing.ScheduleEvent(0, update_time_event); timing.ScheduleEvent(0, update_time_event, 0, 0);
float slidestate = Settings::values.factor_3d / 100.0f; float slidestate = Settings::values.factor_3d / 100.0f;
shared_page.sliderstate_3d = static_cast<float_le>(slidestate); shared_page.sliderstate_3d = static_cast<float_le>(slidestate);

View File

@ -280,12 +280,12 @@ void SVC::ExitProcess() {
current_process->status = ProcessStatus::Exited; current_process->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects. // Stop all the process threads that are currently waiting for objects.
auto& thread_list = kernel.GetThreadManager().GetThreadList(); auto& thread_list = kernel.GetCurrentThreadManager().GetThreadList();
for (auto& thread : thread_list) { for (auto& thread : thread_list) {
if (thread->owner_process != current_process.get()) if (thread->owner_process != current_process.get())
continue; continue;
if (thread.get() == kernel.GetThreadManager().GetCurrentThread()) if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread())
continue; continue;
// TODO(Subv): When are the other running/ready threads terminated? // TODO(Subv): When are the other running/ready threads terminated?
@ -297,7 +297,7 @@ void SVC::ExitProcess() {
} }
// Kill the current thread // Kill the current thread
kernel.GetThreadManager().GetCurrentThread()->Stop(); kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
system.PrepareReschedule(); system.PrepareReschedule();
} }
@ -388,7 +388,7 @@ ResultCode SVC::SendSyncRequest(Handle handle) {
system.PrepareReschedule(); system.PrepareReschedule();
auto thread = SharedFrom(kernel.GetThreadManager().GetCurrentThread()); auto thread = SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
if (kernel.GetIPCRecorder().IsEnabled()) { if (kernel.GetIPCRecorder().IsEnabled()) {
kernel.GetIPCRecorder().RegisterRequest(session, thread); kernel.GetIPCRecorder().RegisterRequest(session, thread);
@ -406,7 +406,7 @@ ResultCode SVC::CloseHandle(Handle handle) {
/// Wait for a handle to synchronize, timeout after the specified nanoseconds /// Wait for a handle to synchronize, timeout after the specified nanoseconds
ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) { ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle); auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle);
Thread* thread = kernel.GetThreadManager().GetCurrentThread(); Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
if (object == nullptr) if (object == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -458,7 +458,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
bool wait_all, s64 nano_seconds) { bool wait_all, s64 nano_seconds) {
Thread* thread = kernel.GetThreadManager().GetCurrentThread(); Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
return ERR_INVALID_POINTER; return ERR_INVALID_POINTER;
@ -654,7 +654,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
// We are also sending a command reply. // We are also sending a command reply.
// Do not send a reply if the command id in the command buffer is 0xFFFF. // Do not send a reply if the command id in the command buffer is 0xFFFF.
Thread* thread = kernel.GetThreadManager().GetCurrentThread(); Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
u32 cmd_buff_header = memory.Read32(thread->GetCommandBufferAddress()); u32 cmd_buff_header = memory.Read32(thread->GetCommandBufferAddress());
IPC::Header header{cmd_buff_header}; IPC::Header header{cmd_buff_header};
if (reply_target != 0 && header.command_id != 0xFFFF) { if (reply_target != 0 && header.command_id != 0xFFFF) {
@ -776,7 +776,7 @@ ResultCode SVC::ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
auto res = auto res =
arbiter->ArbitrateAddress(SharedFrom(kernel.GetThreadManager().GetCurrentThread()), arbiter->ArbitrateAddress(SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()),
static_cast<ArbitrationType>(type), address, value, nanoseconds); static_cast<ArbitrationType>(type), address, value, nanoseconds);
// TODO(Subv): Identify in which specific cases this call should cause a reschedule. // TODO(Subv): Identify in which specific cases this call should cause a reschedule.
@ -897,14 +897,19 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
break; break;
case ThreadProcessorIdAll: case ThreadProcessorIdAll:
LOG_INFO(Kernel_SVC, LOG_INFO(Kernel_SVC,
"Newly created thread is allowed to be run in any Core, unimplemented."); "Newly created thread is allowed to be run in any Core, for now run in core 0.");
processor_id = ThreadProcessorId0;
break; break;
case ThreadProcessorId1: case ThreadProcessorId1:
LOG_ERROR(Kernel_SVC, case ThreadProcessorId2:
"Newly created thread must run in the SysCore (Core1), unimplemented."); case ThreadProcessorId3:
// TODO: Check and log for: When processorid==0x2 and the process is not a BASE mem-region
// process, exheader kernel-flags bitmask 0x2000 must be set (otherwise error 0xD9001BEA is
// returned). When processorid==0x3 and the process is not a BASE mem-region process, error
// 0xD9001BEA is returned. These are the only restriction checks done by the kernel for
// processorid.
break; break;
default: default:
// TODO(bunnei): Implement support for other processor IDs
ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id); ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id);
break; break;
} }
@ -930,9 +935,9 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
/// Called when a thread exits /// Called when a thread exits
void SVC::ExitThread() { void SVC::ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CPU().GetPC()); LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.GetRunningCore().GetPC());
kernel.GetThreadManager().ExitCurrentThread(); kernel.GetCurrentThreadManager().ExitCurrentThread();
system.PrepareReschedule(); system.PrepareReschedule();
} }
@ -978,7 +983,7 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
/// Create a mutex /// Create a mutex
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) { ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0); std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
mutex->name = fmt::format("mutex-{:08x}", system.CPU().GetReg(14)); mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}", LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
@ -995,7 +1000,7 @@ ResultCode SVC::ReleaseMutex(Handle handle) {
if (mutex == nullptr) if (mutex == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
return mutex->Release(kernel.GetThreadManager().GetCurrentThread()); return mutex->Release(kernel.GetCurrentThreadManager().GetCurrentThread());
} }
/// Get the ID of the specified process /// Get the ID of the specified process
@ -1045,7 +1050,7 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore, CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
kernel.CreateSemaphore(initial_count, max_count)); kernel.CreateSemaphore(initial_count, max_count));
semaphore->name = fmt::format("semaphore-{:08x}", system.CPU().GetReg(14)); semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, CASCADE_RESULT(*out_handle,
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore))); kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
@ -1115,8 +1120,9 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
/// Create an event /// Create an event
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Event> evt = kernel.CreateEvent( std::shared_ptr<Event> evt =
static_cast<ResetType>(reset_type), fmt::format("event-{:08x}", system.CPU().GetReg(14))); kernel.CreateEvent(static_cast<ResetType>(reset_type),
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -1158,8 +1164,9 @@ ResultCode SVC::ClearEvent(Handle handle) {
/// Creates a timer /// Creates a timer
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Timer> timer = kernel.CreateTimer( std::shared_ptr<Timer> timer =
static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", system.CPU().GetReg(14))); kernel.CreateTimer(static_cast<ResetType>(reset_type),
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer))); CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -1213,7 +1220,7 @@ ResultCode SVC::CancelTimer(Handle handle) {
void SVC::SleepThread(s64 nanoseconds) { void SVC::SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
ThreadManager& thread_manager = kernel.GetThreadManager(); ThreadManager& thread_manager = kernel.GetCurrentThreadManager();
// Don't attempt to yield execution if there are no available threads to run, // Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread. // this way we avoid a useless reschedule to the idle thread.
@ -1231,10 +1238,11 @@ void SVC::SleepThread(s64 nanoseconds) {
/// This returns the total CPU ticks elapsed since the CPU was powered-on /// This returns the total CPU ticks elapsed since the CPU was powered-on
s64 SVC::GetSystemTick() { s64 SVC::GetSystemTick() {
s64 result = system.CoreTiming().GetTicks(); // TODO: Use globalTicks here?
s64 result = system.GetRunningCore().GetTimer()->GetTicks();
// Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
// Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
system.CoreTiming().AddTicks(150); system.GetRunningCore().GetTimer()->AddTicks(150);
return result; return result;
} }
@ -1596,11 +1604,11 @@ void SVC::CallSVC(u32 immediate) {
SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {} SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {}
u32 SVC::GetReg(std::size_t n) { u32 SVC::GetReg(std::size_t n) {
return system.CPU().GetReg(static_cast<int>(n)); return system.GetRunningCore().GetReg(static_cast<int>(n));
} }
void SVC::SetReg(std::size_t n, u32 value) { void SVC::SetReg(std::size_t n, u32 value) {
system.CPU().SetReg(static_cast<int>(n), value); system.GetRunningCore().SetReg(static_cast<int>(n), value);
} }
SVCContext::SVCContext(Core::System& system) : impl(std::make_unique<SVC>(system)) {} SVCContext::SVCContext(Core::System& system) : impl(std::make_unique<SVC>(system)) {}

View File

@ -33,13 +33,9 @@ void Thread::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
} }
u32 ThreadManager::NewThreadId() { Thread::Thread(KernelSystem& kernel, u32 core_id)
return next_thread_id++; : WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()),
} thread_manager(kernel.GetThreadManager(core_id)) {}
Thread::Thread(KernelSystem& kernel)
: WaitObject(kernel), context(kernel.GetThreadManager().NewContext()),
thread_manager(kernel.GetThreadManager()) {}
Thread::~Thread() {} Thread::~Thread() {}
Thread* ThreadManager::GetCurrentThread() const { Thread* ThreadManager::GetCurrentThread() const {
@ -84,7 +80,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
// Save context for previous thread // Save context for previous thread
if (previous_thread) { if (previous_thread) {
previous_thread->last_running_ticks = timing.GetTicks(); previous_thread->last_running_ticks = timing.GetGlobalTicks();
cpu->SaveContext(previous_thread->context); cpu->SaveContext(previous_thread->context);
if (previous_thread->status == ThreadStatus::Running) { if (previous_thread->status == ThreadStatus::Running) {
@ -111,7 +107,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
new_thread->status = ThreadStatus::Running; new_thread->status = ThreadStatus::Running;
if (previous_process.get() != current_thread->owner_process) { if (previous_process.get() != current_thread->owner_process) {
kernel.SetCurrentProcess(SharedFrom(current_thread->owner_process)); kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID());
} }
cpu->LoadContext(new_thread->context); cpu->LoadContext(new_thread->context);
@ -124,7 +120,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
} }
Thread* ThreadManager::PopNextReadyThread() { Thread* ThreadManager::PopNextReadyThread() {
Thread* next; Thread* next = nullptr;
Thread* thread = GetCurrentThread(); Thread* thread = GetCurrentThread();
if (thread && thread->status == ThreadStatus::Running) { if (thread && thread->status == ThreadStatus::Running) {
@ -309,22 +305,22 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent); ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
} }
auto thread{std::make_shared<Thread>(*this)}; auto thread{std::make_shared<Thread>(*this, processor_id)};
thread_manager->thread_list.push_back(thread); thread_managers[processor_id]->thread_list.push_back(thread);
thread_manager->ready_queue.prepare(priority); thread_managers[processor_id]->ready_queue.prepare(priority);
thread->thread_id = thread_manager->NewThreadId(); thread->thread_id = NewThreadId();
thread->status = ThreadStatus::Dormant; thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point; thread->entry_point = entry_point;
thread->stack_top = stack_top; thread->stack_top = stack_top;
thread->nominal_priority = thread->current_priority = priority; thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = timing.GetTicks(); thread->last_running_ticks = timing.GetGlobalTicks();
thread->processor_id = processor_id; thread->processor_id = processor_id;
thread->wait_objects.clear(); thread->wait_objects.clear();
thread->wait_address = 0; thread->wait_address = 0;
thread->name = std::move(name); thread->name = std::move(name);
thread_manager->wakeup_callback_table[thread->thread_id] = thread.get(); thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get();
thread->owner_process = &owner_process; thread->owner_process = &owner_process;
// Find the next available TLS index, and mark it as used // Find the next available TLS index, and mark it as used
@ -369,7 +365,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
// to initialize the context // to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg); ResetThreadContext(thread->context, stack_top, entry_point, arg);
thread_manager->ready_queue.push_back(thread->current_priority, thread.get()); thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get());
thread->status = ThreadStatus::Ready; thread->status = ThreadStatus::Ready;
return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
@ -435,6 +431,9 @@ void ThreadManager::Reschedule() {
LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
} else if (next) { } else if (next) {
LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
} else {
LOG_TRACE(Kernel, "context switch idle -> idle, do nothing");
return;
} }
SwitchContext(next); SwitchContext(next);
@ -461,11 +460,10 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + command_header_offset; return GetTLSAddress() + command_header_offset;
} }
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel) : kernel(kernel) { ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id) : kernel(kernel) {
ThreadWakeupEventType = ThreadWakeupEventType = kernel.timing.RegisterEvent(
kernel.timing.RegisterEvent("ThreadWakeupCallback", [this](u64 thread_id, s64 cycle_late) { "ThreadWakeupCallback_" + std::to_string(core_id),
ThreadWakeupCallback(thread_id, cycle_late); [this](u64 thread_id, s64 cycle_late) { ThreadWakeupCallback(thread_id, cycle_late); });
});
} }
ThreadManager::~ThreadManager() { ThreadManager::~ThreadManager() {

View File

@ -34,7 +34,9 @@ enum ThreadProcessorId : s32 {
ThreadProcessorIdAll = -1, ///< Run thread on either core ThreadProcessorIdAll = -1, ///< Run thread on either core
ThreadProcessorId0 = 0, ///< Run thread on core 0 (AppCore) ThreadProcessorId0 = 0, ///< Run thread on core 0 (AppCore)
ThreadProcessorId1 = 1, ///< Run thread on core 1 (SysCore) ThreadProcessorId1 = 1, ///< Run thread on core 1 (SysCore)
ThreadProcessorIdMax = 2, ///< Processor ID must be less than this ThreadProcessorId2 = 2, ///< Run thread on core 2 (additional n3ds core)
ThreadProcessorId3 = 3, ///< Run thread on core 3 (additional n3ds core)
ThreadProcessorIdMax = 4, ///< Processor ID must be less than this
}; };
enum class ThreadStatus { enum class ThreadStatus {
@ -57,15 +59,9 @@ enum class ThreadWakeupReason {
class ThreadManager { class ThreadManager {
public: public:
explicit ThreadManager(Kernel::KernelSystem& kernel); explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id);
~ThreadManager(); ~ThreadManager();
/**
* Creates a new thread ID
* @return The new thread ID
*/
u32 NewThreadId();
/** /**
* Gets the current thread * Gets the current thread
*/ */
@ -132,7 +128,6 @@ private:
Kernel::KernelSystem& kernel; Kernel::KernelSystem& kernel;
ARM_Interface* cpu; ARM_Interface* cpu;
u32 next_thread_id = 1;
std::shared_ptr<Thread> current_thread; std::shared_ptr<Thread> current_thread;
Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue; Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue;
std::unordered_map<u64, Thread*> wakeup_callback_table; std::unordered_map<u64, Thread*> wakeup_callback_table;
@ -149,7 +144,7 @@ private:
class Thread final : public WaitObject { class Thread final : public WaitObject {
public: public:
explicit Thread(KernelSystem&); explicit Thread(KernelSystem&, u32 core_id);
~Thread() override; ~Thread() override;
std::string GetName() const override { std::string GetName() const override {

View File

@ -55,7 +55,7 @@ VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const {
return 0; return 0;
SegmentEntry entry; SegmentEntry entry;
GetEntry(memory, segment_tag.segment_index, entry); GetEntry(system.Memory(), segment_tag.segment_index, entry);
if (segment_tag.offset_into_segment >= entry.size) if (segment_tag.offset_into_segment >= entry.size)
return 0; return 0;
@ -71,12 +71,12 @@ ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType reloc
break; break;
case RelocationType::AbsoluteAddress: case RelocationType::AbsoluteAddress:
case RelocationType::AbsoluteAddress2: case RelocationType::AbsoluteAddress2:
memory.Write32(target_address, symbol_address + addend); system.Memory().Write32(target_address, symbol_address + addend);
cpu.InvalidateCacheRange(target_address, sizeof(u32)); system.InvalidateCacheRange(target_address, sizeof(u32));
break; break;
case RelocationType::RelativeAddress: case RelocationType::RelativeAddress:
memory.Write32(target_address, symbol_address + addend - target_future_address); system.Memory().Write32(target_address, symbol_address + addend - target_future_address);
cpu.InvalidateCacheRange(target_address, sizeof(u32)); system.InvalidateCacheRange(target_address, sizeof(u32));
break; break;
case RelocationType::ThumbBranch: case RelocationType::ThumbBranch:
case RelocationType::ArmBranch: case RelocationType::ArmBranch:
@ -98,8 +98,8 @@ ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType reloc
case RelocationType::AbsoluteAddress: case RelocationType::AbsoluteAddress:
case RelocationType::AbsoluteAddress2: case RelocationType::AbsoluteAddress2:
case RelocationType::RelativeAddress: case RelocationType::RelativeAddress:
memory.Write32(target_address, 0); system.Memory().Write32(target_address, 0);
cpu.InvalidateCacheRange(target_address, sizeof(u32)); system.InvalidateCacheRange(target_address, sizeof(u32));
break; break;
case RelocationType::ThumbBranch: case RelocationType::ThumbBranch:
case RelocationType::ArmBranch: case RelocationType::ArmBranch:
@ -121,7 +121,8 @@ ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool
VAddr relocation_address = batch; VAddr relocation_address = batch;
while (true) { while (true) {
RelocationEntry relocation; RelocationEntry relocation;
memory.ReadBlock(process, relocation_address, &relocation, sizeof(RelocationEntry)); system.Memory().ReadBlock(process, relocation_address, &relocation,
sizeof(RelocationEntry));
VAddr relocation_target = SegmentTagToAddress(relocation.target_position); VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
if (relocation_target == 0) { if (relocation_target == 0) {
@ -142,9 +143,9 @@ ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool
} }
RelocationEntry relocation; RelocationEntry relocation;
memory.ReadBlock(process, batch, &relocation, sizeof(RelocationEntry)); system.Memory().ReadBlock(process, batch, &relocation, sizeof(RelocationEntry));
relocation.is_batch_resolved = reset ? 0 : 1; relocation.is_batch_resolved = reset ? 0 : 1;
memory.WriteBlock(process, batch, &relocation, sizeof(RelocationEntry)); system.Memory().WriteBlock(process, batch, &relocation, sizeof(RelocationEntry));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -154,13 +155,13 @@ VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const {
std::size_t len = name.size(); std::size_t len = name.size();
ExportTreeEntry entry; ExportTreeEntry entry;
GetEntry(memory, 0, entry); GetEntry(system.Memory(), 0, entry);
ExportTreeEntry::Child next; ExportTreeEntry::Child next;
next.raw = entry.left.raw; next.raw = entry.left.raw;
u32 found_id; u32 found_id;
while (true) { while (true) {
GetEntry(memory, next.next_index, entry); GetEntry(system.Memory(), next.next_index, entry);
if (next.is_end) { if (next.is_end) {
found_id = entry.export_table_index; found_id = entry.export_table_index;
@ -186,9 +187,9 @@ VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const {
u32 export_strings_size = GetField(ExportStringsSize); u32 export_strings_size = GetField(ExportStringsSize);
ExportNamedSymbolEntry symbol_entry; ExportNamedSymbolEntry symbol_entry;
GetEntry(memory, found_id, symbol_entry); GetEntry(system.Memory(), found_id, symbol_entry);
if (memory.ReadCString(symbol_entry.name_offset, export_strings_size) != name) if (system.Memory().ReadCString(symbol_entry.name_offset, export_strings_size) != name)
return 0; return 0;
return SegmentTagToAddress(symbol_entry.symbol_position); return SegmentTagToAddress(symbol_entry.symbol_position);
@ -279,7 +280,7 @@ ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_
u32 segment_num = GetField(SegmentNum); u32 segment_num = GetField(SegmentNum);
for (u32 i = 0; i < segment_num; ++i) { for (u32 i = 0; i < segment_num; ++i) {
SegmentEntry segment; SegmentEntry segment;
GetEntry(memory, i, segment); GetEntry(system.Memory(), i, segment);
if (segment.type == SegmentType::Data) { if (segment.type == SegmentType::Data) {
if (segment.size != 0) { if (segment.size != 0) {
if (segment.size > data_segment_size) if (segment.size > data_segment_size)
@ -298,7 +299,7 @@ ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_
if (segment.offset > module_address + cro_size) if (segment.offset > module_address + cro_size)
return CROFormatError(0x19); return CROFormatError(0x19);
} }
SetEntry(memory, i, segment); SetEntry(system.Memory(), i, segment);
} }
return MakeResult<u32>(prev_data_segment + module_address); return MakeResult<u32>(prev_data_segment + module_address);
} }
@ -310,7 +311,7 @@ ResultCode CROHelper::RebaseExportNamedSymbolTable() {
u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
for (u32 i = 0; i < export_named_symbol_num; ++i) { for (u32 i = 0; i < export_named_symbol_num; ++i) {
ExportNamedSymbolEntry entry; ExportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset += module_address; entry.name_offset += module_address;
@ -320,7 +321,7 @@ ResultCode CROHelper::RebaseExportNamedSymbolTable() {
} }
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -329,7 +330,7 @@ ResultCode CROHelper::VerifyExportTreeTable() const {
u32 tree_num = GetField(ExportTreeNum); u32 tree_num = GetField(ExportTreeNum);
for (u32 i = 0; i < tree_num; ++i) { for (u32 i = 0; i < tree_num; ++i) {
ExportTreeEntry entry; ExportTreeEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) { if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) {
return CROFormatError(0x11); return CROFormatError(0x11);
@ -353,7 +354,7 @@ ResultCode CROHelper::RebaseImportModuleTable() {
u32 module_num = GetField(ImportModuleNum); u32 module_num = GetField(ImportModuleNum);
for (u32 i = 0; i < module_num; ++i) { for (u32 i = 0; i < module_num; ++i) {
ImportModuleEntry entry; ImportModuleEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset += module_address; entry.name_offset += module_address;
@ -379,7 +380,7 @@ ResultCode CROHelper::RebaseImportModuleTable() {
} }
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -395,7 +396,7 @@ ResultCode CROHelper::RebaseImportNamedSymbolTable() {
u32 num = GetField(ImportNamedSymbolNum); u32 num = GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset += module_address; entry.name_offset += module_address;
@ -413,7 +414,7 @@ ResultCode CROHelper::RebaseImportNamedSymbolTable() {
} }
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -427,7 +428,7 @@ ResultCode CROHelper::RebaseImportIndexedSymbolTable() {
u32 num = GetField(ImportIndexedSymbolNum); u32 num = GetField(ImportIndexedSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportIndexedSymbolEntry entry; ImportIndexedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.relocation_batch_offset != 0) { if (entry.relocation_batch_offset != 0) {
entry.relocation_batch_offset += module_address; entry.relocation_batch_offset += module_address;
@ -437,7 +438,7 @@ ResultCode CROHelper::RebaseImportIndexedSymbolTable() {
} }
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -451,7 +452,7 @@ ResultCode CROHelper::RebaseImportAnonymousSymbolTable() {
u32 num = GetField(ImportAnonymousSymbolNum); u32 num = GetField(ImportAnonymousSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportAnonymousSymbolEntry entry; ImportAnonymousSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.relocation_batch_offset != 0) { if (entry.relocation_batch_offset != 0) {
entry.relocation_batch_offset += module_address; entry.relocation_batch_offset += module_address;
@ -461,7 +462,7 @@ ResultCode CROHelper::RebaseImportAnonymousSymbolTable() {
} }
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -476,14 +477,14 @@ ResultCode CROHelper::ResetExternalRelocations() {
ExternalRelocationEntry relocation; ExternalRelocationEntry relocation;
// Verifies that the last relocation is the end of a batch // Verifies that the last relocation is the end of a batch
GetEntry(memory, external_relocation_num - 1, relocation); GetEntry(system.Memory(), external_relocation_num - 1, relocation);
if (!relocation.is_batch_end) { if (!relocation.is_batch_end) {
return CROFormatError(0x12); return CROFormatError(0x12);
} }
bool batch_begin = true; bool batch_begin = true;
for (u32 i = 0; i < external_relocation_num; ++i) { for (u32 i = 0; i < external_relocation_num; ++i) {
GetEntry(memory, i, relocation); GetEntry(system.Memory(), i, relocation);
VAddr relocation_target = SegmentTagToAddress(relocation.target_position); VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
if (relocation_target == 0) { if (relocation_target == 0) {
@ -500,7 +501,7 @@ ResultCode CROHelper::ResetExternalRelocations() {
if (batch_begin) { if (batch_begin) {
// resets to unresolved state // resets to unresolved state
relocation.is_batch_resolved = 0; relocation.is_batch_resolved = 0;
SetEntry(memory, i, relocation); SetEntry(system.Memory(), i, relocation);
} }
// if current is an end, then the next is a beginning // if current is an end, then the next is a beginning
@ -516,7 +517,7 @@ ResultCode CROHelper::ClearExternalRelocations() {
bool batch_begin = true; bool batch_begin = true;
for (u32 i = 0; i < external_relocation_num; ++i) { for (u32 i = 0; i < external_relocation_num; ++i) {
GetEntry(memory, i, relocation); GetEntry(system.Memory(), i, relocation);
VAddr relocation_target = SegmentTagToAddress(relocation.target_position); VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
if (relocation_target == 0) { if (relocation_target == 0) {
@ -532,7 +533,7 @@ ResultCode CROHelper::ClearExternalRelocations() {
if (batch_begin) { if (batch_begin) {
// resets to unresolved state // resets to unresolved state
relocation.is_batch_resolved = 0; relocation.is_batch_resolved = 0;
SetEntry(memory, i, relocation); SetEntry(system.Memory(), i, relocation);
} }
// if current is an end, then the next is a beginning // if current is an end, then the next is a beginning
@ -548,13 +549,13 @@ ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) {
static_relocation_table_offset + static_relocation_table_offset +
GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry); GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry);
CROHelper crs(crs_address, process, memory, cpu); CROHelper crs(crs_address, process, system);
u32 offset_export_num = GetField(StaticAnonymousSymbolNum); u32 offset_export_num = GetField(StaticAnonymousSymbolNum);
LOG_INFO(Service_LDR, "CRO \"{}\" exports {} static anonymous symbols", ModuleName(), LOG_INFO(Service_LDR, "CRO \"{}\" exports {} static anonymous symbols", ModuleName(),
offset_export_num); offset_export_num);
for (u32 i = 0; i < offset_export_num; ++i) { for (u32 i = 0; i < offset_export_num; ++i) {
StaticAnonymousSymbolEntry entry; StaticAnonymousSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
u32 batch_address = entry.relocation_batch_offset + module_address; u32 batch_address = entry.relocation_batch_offset + module_address;
if (batch_address < static_relocation_table_offset || if (batch_address < static_relocation_table_offset ||
@ -579,7 +580,7 @@ ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) {
u32 internal_relocation_num = GetField(InternalRelocationNum); u32 internal_relocation_num = GetField(InternalRelocationNum);
for (u32 i = 0; i < internal_relocation_num; ++i) { for (u32 i = 0; i < internal_relocation_num; ++i) {
InternalRelocationEntry relocation; InternalRelocationEntry relocation;
GetEntry(memory, i, relocation); GetEntry(system.Memory(), i, relocation);
VAddr target_addressB = SegmentTagToAddress(relocation.target_position); VAddr target_addressB = SegmentTagToAddress(relocation.target_position);
if (target_addressB == 0) { if (target_addressB == 0) {
return CROFormatError(0x15); return CROFormatError(0x15);
@ -587,7 +588,7 @@ ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) {
VAddr target_address; VAddr target_address;
SegmentEntry target_segment; SegmentEntry target_segment;
GetEntry(memory, relocation.target_position.segment_index, target_segment); GetEntry(system.Memory(), relocation.target_position.segment_index, target_segment);
if (target_segment.type == SegmentType::Data) { if (target_segment.type == SegmentType::Data) {
// If the relocation is to the .data segment, we need to relocate it in the old buffer // If the relocation is to the .data segment, we need to relocate it in the old buffer
@ -602,7 +603,7 @@ ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) {
} }
SegmentEntry symbol_segment; SegmentEntry symbol_segment;
GetEntry(memory, relocation.symbol_segment, symbol_segment); GetEntry(system.Memory(), relocation.symbol_segment, symbol_segment);
LOG_TRACE(Service_LDR, "Internally relocates 0x{:08X} with 0x{:08X}", target_address, LOG_TRACE(Service_LDR, "Internally relocates 0x{:08X} with 0x{:08X}", target_address,
symbol_segment.offset); symbol_segment.offset);
ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend, ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend,
@ -619,7 +620,7 @@ ResultCode CROHelper::ClearInternalRelocations() {
u32 internal_relocation_num = GetField(InternalRelocationNum); u32 internal_relocation_num = GetField(InternalRelocationNum);
for (u32 i = 0; i < internal_relocation_num; ++i) { for (u32 i = 0; i < internal_relocation_num; ++i) {
InternalRelocationEntry relocation; InternalRelocationEntry relocation;
GetEntry(memory, i, relocation); GetEntry(system.Memory(), i, relocation);
VAddr target_address = SegmentTagToAddress(relocation.target_position); VAddr target_address = SegmentTagToAddress(relocation.target_position);
if (target_address == 0) { if (target_address == 0) {
@ -639,13 +640,13 @@ void CROHelper::UnrebaseImportAnonymousSymbolTable() {
u32 num = GetField(ImportAnonymousSymbolNum); u32 num = GetField(ImportAnonymousSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportAnonymousSymbolEntry entry; ImportAnonymousSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.relocation_batch_offset != 0) { if (entry.relocation_batch_offset != 0) {
entry.relocation_batch_offset -= module_address; entry.relocation_batch_offset -= module_address;
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
} }
@ -653,13 +654,13 @@ void CROHelper::UnrebaseImportIndexedSymbolTable() {
u32 num = GetField(ImportIndexedSymbolNum); u32 num = GetField(ImportIndexedSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportIndexedSymbolEntry entry; ImportIndexedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.relocation_batch_offset != 0) { if (entry.relocation_batch_offset != 0) {
entry.relocation_batch_offset -= module_address; entry.relocation_batch_offset -= module_address;
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
} }
@ -667,7 +668,7 @@ void CROHelper::UnrebaseImportNamedSymbolTable() {
u32 num = GetField(ImportNamedSymbolNum); u32 num = GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < num; ++i) { for (u32 i = 0; i < num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset -= module_address; entry.name_offset -= module_address;
@ -677,7 +678,7 @@ void CROHelper::UnrebaseImportNamedSymbolTable() {
entry.relocation_batch_offset -= module_address; entry.relocation_batch_offset -= module_address;
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
} }
@ -685,7 +686,7 @@ void CROHelper::UnrebaseImportModuleTable() {
u32 module_num = GetField(ImportModuleNum); u32 module_num = GetField(ImportModuleNum);
for (u32 i = 0; i < module_num; ++i) { for (u32 i = 0; i < module_num; ++i) {
ImportModuleEntry entry; ImportModuleEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset -= module_address; entry.name_offset -= module_address;
@ -699,7 +700,7 @@ void CROHelper::UnrebaseImportModuleTable() {
entry.import_anonymous_symbol_table_offset -= module_address; entry.import_anonymous_symbol_table_offset -= module_address;
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
} }
@ -707,13 +708,13 @@ void CROHelper::UnrebaseExportNamedSymbolTable() {
u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
for (u32 i = 0; i < export_named_symbol_num; ++i) { for (u32 i = 0; i < export_named_symbol_num; ++i) {
ExportNamedSymbolEntry entry; ExportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.name_offset != 0) { if (entry.name_offset != 0) {
entry.name_offset -= module_address; entry.name_offset -= module_address;
} }
SetEntry(memory, i, entry); SetEntry(system.Memory(), i, entry);
} }
} }
@ -721,7 +722,7 @@ void CROHelper::UnrebaseSegmentTable() {
u32 segment_num = GetField(SegmentNum); u32 segment_num = GetField(SegmentNum);
for (u32 i = 0; i < segment_num; ++i) { for (u32 i = 0; i < segment_num; ++i) {
SegmentEntry segment; SegmentEntry segment;
GetEntry(memory, i, segment); GetEntry(system.Memory(), i, segment);
if (segment.type == SegmentType::BSS) { if (segment.type == SegmentType::BSS) {
segment.offset = 0; segment.offset = 0;
@ -729,7 +730,7 @@ void CROHelper::UnrebaseSegmentTable() {
segment.offset -= module_address; segment.offset -= module_address;
} }
SetEntry(memory, i, segment); SetEntry(system.Memory(), i, segment);
} }
} }
@ -751,17 +752,17 @@ ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) {
u32 symbol_import_num = GetField(ImportNamedSymbolNum); u32 symbol_import_num = GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < symbol_import_num; ++i) { for (u32 i = 0; i < symbol_import_num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
if (!relocation_entry.is_batch_resolved) { if (!relocation_entry.is_batch_resolved) {
ResultCode result = ForEachAutoLinkCRO( ResultCode result = ForEachAutoLinkCRO(
process, memory, cpu, crs_address, [&](CROHelper source) -> ResultVal<bool> { process, system, crs_address, [&](CROHelper source) -> ResultVal<bool> {
std::string symbol_name = std::string symbol_name =
memory.ReadCString(entry.name_offset, import_strings_size); system.Memory().ReadCString(entry.name_offset, import_strings_size);
u32 symbol_address = source.FindExportNamedSymbol(symbol_name); u32 symbol_address = source.FindExportNamedSymbol(symbol_name);
if (symbol_address != 0) { if (symbol_address != 0) {
@ -794,11 +795,11 @@ ResultCode CROHelper::ResetImportNamedSymbol() {
u32 symbol_import_num = GetField(ImportNamedSymbolNum); u32 symbol_import_num = GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < symbol_import_num; ++i) { for (u32 i = 0; i < symbol_import_num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
if (result.IsError()) { if (result.IsError()) {
@ -815,11 +816,11 @@ ResultCode CROHelper::ResetImportIndexedSymbol() {
u32 import_num = GetField(ImportIndexedSymbolNum); u32 import_num = GetField(ImportIndexedSymbolNum);
for (u32 i = 0; i < import_num; ++i) { for (u32 i = 0; i < import_num; ++i) {
ImportIndexedSymbolEntry entry; ImportIndexedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
if (result.IsError()) { if (result.IsError()) {
@ -836,11 +837,11 @@ ResultCode CROHelper::ResetImportAnonymousSymbol() {
u32 import_num = GetField(ImportAnonymousSymbolNum); u32 import_num = GetField(ImportAnonymousSymbolNum);
for (u32 i = 0; i < import_num; ++i) { for (u32 i = 0; i < import_num; ++i) {
ImportAnonymousSymbolEntry entry; ImportAnonymousSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
if (result.IsError()) { if (result.IsError()) {
@ -857,19 +858,20 @@ ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) {
u32 import_module_num = GetField(ImportModuleNum); u32 import_module_num = GetField(ImportModuleNum);
for (u32 i = 0; i < import_module_num; ++i) { for (u32 i = 0; i < import_module_num; ++i) {
ImportModuleEntry entry; ImportModuleEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
std::string want_cro_name = memory.ReadCString(entry.name_offset, import_strings_size); std::string want_cro_name =
system.Memory().ReadCString(entry.name_offset, import_strings_size);
ResultCode result = ForEachAutoLinkCRO( ResultCode result = ForEachAutoLinkCRO(
process, memory, cpu, crs_address, [&](CROHelper source) -> ResultVal<bool> { process, system, crs_address, [&](CROHelper source) -> ResultVal<bool> {
if (want_cro_name == source.ModuleName()) { if (want_cro_name == source.ModuleName()) {
LOG_INFO(Service_LDR, "CRO \"{}\" imports {} indexed symbols from \"{}\"", LOG_INFO(Service_LDR, "CRO \"{}\" imports {} indexed symbols from \"{}\"",
ModuleName(), entry.import_indexed_symbol_num, source.ModuleName()); ModuleName(), entry.import_indexed_symbol_num, source.ModuleName());
for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
ImportIndexedSymbolEntry im; ImportIndexedSymbolEntry im;
entry.GetImportIndexedSymbolEntry(process, memory, j, im); entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im);
ExportIndexedSymbolEntry ex; ExportIndexedSymbolEntry ex;
source.GetEntry(memory, im.index, ex); source.GetEntry(system.Memory(), im.index, ex);
u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position); u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position);
LOG_TRACE(Service_LDR, " Imports 0x{:08X}", symbol_address); LOG_TRACE(Service_LDR, " Imports 0x{:08X}", symbol_address);
ResultCode result = ResultCode result =
@ -884,7 +886,7 @@ ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) {
ModuleName(), entry.import_anonymous_symbol_num, source.ModuleName()); ModuleName(), entry.import_anonymous_symbol_num, source.ModuleName());
for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
ImportAnonymousSymbolEntry im; ImportAnonymousSymbolEntry im;
entry.GetImportAnonymousSymbolEntry(process, memory, j, im); entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im);
u32 symbol_address = source.SegmentTagToAddress(im.symbol_position); u32 symbol_address = source.SegmentTagToAddress(im.symbol_position);
LOG_TRACE(Service_LDR, " Imports 0x{:08X}", symbol_address); LOG_TRACE(Service_LDR, " Imports 0x{:08X}", symbol_address);
ResultCode result = ResultCode result =
@ -913,15 +915,15 @@ ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) {
u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < target_symbol_import_num; ++i) { for (u32 i = 0; i < target_symbol_import_num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
target.GetEntry(memory, i, entry); target.GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
if (!relocation_entry.is_batch_resolved) { if (!relocation_entry.is_batch_resolved) {
std::string symbol_name = std::string symbol_name =
memory.ReadCString(entry.name_offset, target_import_strings_size); system.Memory().ReadCString(entry.name_offset, target_import_strings_size);
u32 symbol_address = FindExportNamedSymbol(symbol_name); u32 symbol_address = FindExportNamedSymbol(symbol_name);
if (symbol_address != 0) { if (symbol_address != 0) {
LOG_TRACE(Service_LDR, " exports symbol \"{}\"", symbol_name); LOG_TRACE(Service_LDR, " exports symbol \"{}\"", symbol_name);
@ -944,15 +946,15 @@ ResultCode CROHelper::ResetExportNamedSymbol(CROHelper target) {
u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < target_symbol_import_num; ++i) { for (u32 i = 0; i < target_symbol_import_num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
target.GetEntry(memory, i, entry); target.GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
if (relocation_entry.is_batch_resolved) { if (relocation_entry.is_batch_resolved) {
std::string symbol_name = std::string symbol_name =
memory.ReadCString(entry.name_offset, target_import_strings_size); system.Memory().ReadCString(entry.name_offset, target_import_strings_size);
u32 symbol_address = FindExportNamedSymbol(symbol_name); u32 symbol_address = FindExportNamedSymbol(symbol_name);
if (symbol_address != 0) { if (symbol_address != 0) {
LOG_TRACE(Service_LDR, " unexports symbol \"{}\"", symbol_name); LOG_TRACE(Service_LDR, " unexports symbol \"{}\"", symbol_name);
@ -974,18 +976,19 @@ ResultCode CROHelper::ApplyModuleExport(CROHelper target) {
u32 target_import_module_num = target.GetField(ImportModuleNum); u32 target_import_module_num = target.GetField(ImportModuleNum);
for (u32 i = 0; i < target_import_module_num; ++i) { for (u32 i = 0; i < target_import_module_num; ++i) {
ImportModuleEntry entry; ImportModuleEntry entry;
target.GetEntry(memory, i, entry); target.GetEntry(system.Memory(), i, entry);
if (memory.ReadCString(entry.name_offset, target_import_string_size) != module_name) if (system.Memory().ReadCString(entry.name_offset, target_import_string_size) !=
module_name)
continue; continue;
LOG_INFO(Service_LDR, "CRO \"{}\" exports {} indexed symbols to \"{}\"", module_name, LOG_INFO(Service_LDR, "CRO \"{}\" exports {} indexed symbols to \"{}\"", module_name,
entry.import_indexed_symbol_num, target.ModuleName()); entry.import_indexed_symbol_num, target.ModuleName());
for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
ImportIndexedSymbolEntry im; ImportIndexedSymbolEntry im;
entry.GetImportIndexedSymbolEntry(process, memory, j, im); entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im);
ExportIndexedSymbolEntry ex; ExportIndexedSymbolEntry ex;
GetEntry(memory, im.index, ex); GetEntry(system.Memory(), im.index, ex);
u32 symbol_address = SegmentTagToAddress(ex.symbol_position); u32 symbol_address = SegmentTagToAddress(ex.symbol_position);
LOG_TRACE(Service_LDR, " exports symbol 0x{:08X}", symbol_address); LOG_TRACE(Service_LDR, " exports symbol 0x{:08X}", symbol_address);
ResultCode result = ResultCode result =
@ -1000,7 +1003,7 @@ ResultCode CROHelper::ApplyModuleExport(CROHelper target) {
entry.import_anonymous_symbol_num, target.ModuleName()); entry.import_anonymous_symbol_num, target.ModuleName());
for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
ImportAnonymousSymbolEntry im; ImportAnonymousSymbolEntry im;
entry.GetImportAnonymousSymbolEntry(process, memory, j, im); entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im);
u32 symbol_address = SegmentTagToAddress(im.symbol_position); u32 symbol_address = SegmentTagToAddress(im.symbol_position);
LOG_TRACE(Service_LDR, " exports symbol 0x{:08X}", symbol_address); LOG_TRACE(Service_LDR, " exports symbol 0x{:08X}", symbol_address);
ResultCode result = ResultCode result =
@ -1023,16 +1026,17 @@ ResultCode CROHelper::ResetModuleExport(CROHelper target) {
u32 target_import_module_num = target.GetField(ImportModuleNum); u32 target_import_module_num = target.GetField(ImportModuleNum);
for (u32 i = 0; i < target_import_module_num; ++i) { for (u32 i = 0; i < target_import_module_num; ++i) {
ImportModuleEntry entry; ImportModuleEntry entry;
target.GetEntry(memory, i, entry); target.GetEntry(system.Memory(), i, entry);
if (memory.ReadCString(entry.name_offset, target_import_string_size) != module_name) if (system.Memory().ReadCString(entry.name_offset, target_import_string_size) !=
module_name)
continue; continue;
LOG_DEBUG(Service_LDR, "CRO \"{}\" unexports indexed symbols to \"{}\"", module_name, LOG_DEBUG(Service_LDR, "CRO \"{}\" unexports indexed symbols to \"{}\"", module_name,
target.ModuleName()); target.ModuleName());
for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
ImportIndexedSymbolEntry im; ImportIndexedSymbolEntry im;
entry.GetImportIndexedSymbolEntry(process, memory, j, im); entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im);
ResultCode result = ResultCode result =
target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
if (result.IsError()) { if (result.IsError()) {
@ -1045,7 +1049,7 @@ ResultCode CROHelper::ResetModuleExport(CROHelper target) {
target.ModuleName()); target.ModuleName());
for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
ImportAnonymousSymbolEntry im; ImportAnonymousSymbolEntry im;
entry.GetImportAnonymousSymbolEntry(process, memory, j, im); entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im);
ResultCode result = ResultCode result =
target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
if (result.IsError()) { if (result.IsError()) {
@ -1063,15 +1067,16 @@ ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) {
u32 symbol_import_num = GetField(ImportNamedSymbolNum); u32 symbol_import_num = GetField(ImportNamedSymbolNum);
for (u32 i = 0; i < symbol_import_num; ++i) { for (u32 i = 0; i < symbol_import_num; ++i) {
ImportNamedSymbolEntry entry; ImportNamedSymbolEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
VAddr relocation_addr = entry.relocation_batch_offset; VAddr relocation_addr = entry.relocation_batch_offset;
ExternalRelocationEntry relocation_entry; ExternalRelocationEntry relocation_entry;
memory.ReadBlock(process, relocation_addr, &relocation_entry, system.Memory().ReadBlock(process, relocation_addr, &relocation_entry,
sizeof(ExternalRelocationEntry)); sizeof(ExternalRelocationEntry));
if (memory.ReadCString(entry.name_offset, import_strings_size) == "__aeabi_atexit") { if (system.Memory().ReadCString(entry.name_offset, import_strings_size) ==
"__aeabi_atexit") {
ResultCode result = ForEachAutoLinkCRO( ResultCode result = ForEachAutoLinkCRO(
process, memory, cpu, crs_address, [&](CROHelper source) -> ResultVal<bool> { process, system, crs_address, [&](CROHelper source) -> ResultVal<bool> {
u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_"); u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_");
if (symbol_address != 0) { if (symbol_address != 0) {
@ -1126,7 +1131,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment
return result; return result;
} }
result = VerifyStringTableLength(memory, GetField(ModuleNameOffset), GetField(ModuleNameSize)); result = VerifyStringTableLength(system.Memory(), GetField(ModuleNameOffset),
GetField(ModuleNameSize));
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_LDR, "Error verifying module name {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error verifying module name {:08X}", result.raw);
return result; return result;
@ -1155,8 +1161,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment
return result; return result;
} }
result = result = VerifyStringTableLength(system.Memory(), GetField(ExportStringsOffset),
VerifyStringTableLength(memory, GetField(ExportStringsOffset), GetField(ExportStringsSize)); GetField(ExportStringsSize));
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_LDR, "Error verifying export strings {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error verifying export strings {:08X}", result.raw);
return result; return result;
@ -1192,8 +1198,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment
return result; return result;
} }
result = result = VerifyStringTableLength(system.Memory(), GetField(ImportStringsOffset),
VerifyStringTableLength(memory, GetField(ImportStringsOffset), GetField(ImportStringsSize)); GetField(ImportStringsSize));
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_LDR, "Error verifying import strings {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error verifying import strings {:08X}", result.raw);
return result; return result;
@ -1266,11 +1272,11 @@ ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) {
// so we do the same // so we do the same
if (GetField(SegmentNum) >= 2) { // means we have .data segment if (GetField(SegmentNum) >= 2) { // means we have .data segment
SegmentEntry entry; SegmentEntry entry;
GetEntry(memory, 2, entry); GetEntry(system.Memory(), 2, entry);
ASSERT(entry.type == SegmentType::Data); ASSERT(entry.type == SegmentType::Data);
data_segment_address = entry.offset; data_segment_address = entry.offset;
entry.offset = GetField(DataOffset); entry.offset = GetField(DataOffset);
SetEntry(memory, 2, entry); SetEntry(system.Memory(), 2, entry);
} }
} }
SCOPE_EXIT({ SCOPE_EXIT({
@ -1278,9 +1284,9 @@ ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) {
if (link_on_load_bug_fix) { if (link_on_load_bug_fix) {
if (GetField(SegmentNum) >= 2) { if (GetField(SegmentNum) >= 2) {
SegmentEntry entry; SegmentEntry entry;
GetEntry(memory, 2, entry); GetEntry(system.Memory(), 2, entry);
entry.offset = data_segment_address; entry.offset = data_segment_address;
SetEntry(memory, 2, entry); SetEntry(system.Memory(), 2, entry);
} }
} }
}); });
@ -1301,7 +1307,7 @@ ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) {
} }
// Exports symbols to other modules // Exports symbols to other modules
result = ForEachAutoLinkCRO(process, memory, cpu, crs_address, result = ForEachAutoLinkCRO(process, system, crs_address,
[this](CROHelper target) -> ResultVal<bool> { [this](CROHelper target) -> ResultVal<bool> {
ResultCode result = ApplyExportNamedSymbol(target); ResultCode result = ApplyExportNamedSymbol(target);
if (result.IsError()) if (result.IsError())
@ -1346,7 +1352,7 @@ ResultCode CROHelper::Unlink(VAddr crs_address) {
// Resets all symbols in other modules imported from this module // Resets all symbols in other modules imported from this module
// Note: the RO service seems only searching in auto-link modules // Note: the RO service seems only searching in auto-link modules
result = ForEachAutoLinkCRO(process, memory, cpu, crs_address, result = ForEachAutoLinkCRO(process, system, crs_address,
[this](CROHelper target) -> ResultVal<bool> { [this](CROHelper target) -> ResultVal<bool> {
ResultCode result = ResetExportNamedSymbol(target); ResultCode result = ResetExportNamedSymbol(target);
if (result.IsError()) if (result.IsError())
@ -1387,13 +1393,13 @@ void CROHelper::InitCRS() {
} }
void CROHelper::Register(VAddr crs_address, bool auto_link) { void CROHelper::Register(VAddr crs_address, bool auto_link) {
CROHelper crs(crs_address, process, memory, cpu); CROHelper crs(crs_address, process, system);
CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule(), process, memory, cpu); CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule(), process, system);
if (head.module_address) { if (head.module_address) {
// there are already CROs registered // there are already CROs registered
// register as the new tail // register as the new tail
CROHelper tail(head.PreviousModule(), process, memory, cpu); CROHelper tail(head.PreviousModule(), process, system);
// link with the old tail // link with the old tail
ASSERT(tail.NextModule() == 0); ASSERT(tail.NextModule() == 0);
@ -1419,11 +1425,11 @@ void CROHelper::Register(VAddr crs_address, bool auto_link) {
} }
void CROHelper::Unregister(VAddr crs_address) { void CROHelper::Unregister(VAddr crs_address) {
CROHelper crs(crs_address, process, memory, cpu); CROHelper crs(crs_address, process, system);
CROHelper next_head(crs.NextModule(), process, memory, cpu); CROHelper next_head(crs.NextModule(), process, system);
CROHelper previous_head(crs.PreviousModule(), process, memory, cpu); CROHelper previous_head(crs.PreviousModule(), process, system);
CROHelper next(NextModule(), process, memory, cpu); CROHelper next(NextModule(), process, system);
CROHelper previous(PreviousModule(), process, memory, cpu); CROHelper previous(PreviousModule(), process, system);
if (module_address == next_head.module_address || if (module_address == next_head.module_address ||
module_address == previous_head.module_address) { module_address == previous_head.module_address) {
@ -1517,7 +1523,7 @@ std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const {
u32 segment_num = GetField(SegmentNum); u32 segment_num = GetField(SegmentNum);
for (u32 i = 0; i < segment_num; ++i) { for (u32 i = 0; i < segment_num; ++i) {
SegmentEntry entry; SegmentEntry entry;
GetEntry(memory, i, entry); GetEntry(system.Memory(), i, entry);
if (entry.type == SegmentType::Code && entry.size != 0) { if (entry.type == SegmentType::Code && entry.size != 0) {
VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE); VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE);
VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE); VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE);

View File

@ -33,12 +33,11 @@ static constexpr u32 CRO_HASH_SIZE = 0x80;
class CROHelper final { class CROHelper final {
public: public:
// TODO (wwylele): pass in the process handle for memory access // TODO (wwylele): pass in the process handle for memory access
explicit CROHelper(VAddr cro_address, Kernel::Process& process, Memory::MemorySystem& memory, explicit CROHelper(VAddr cro_address, Kernel::Process& process, Core::System& system)
ARM_Interface& cpu) : module_address(cro_address), process(process), system(system) {}
: module_address(cro_address), process(process), memory(memory), cpu(cpu) {}
std::string ModuleName() const { std::string ModuleName() const {
return memory.ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); return system.Memory().ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize));
} }
u32 GetFileSize() const { u32 GetFileSize() const {
@ -144,8 +143,7 @@ public:
private: private:
const VAddr module_address; ///< the virtual address of this module const VAddr module_address; ///< the virtual address of this module
Kernel::Process& process; ///< the owner process of this module Kernel::Process& process; ///< the owner process of this module
Memory::MemorySystem& memory; Core::System& system;
ARM_Interface& cpu;
/** /**
* Each item in this enum represents a u32 field in the header begin from address+0x80, * Each item in this enum represents a u32 field in the header begin from address+0x80,
@ -403,11 +401,11 @@ private:
} }
u32 GetField(HeaderField field) const { u32 GetField(HeaderField field) const {
return memory.Read32(Field(field)); return system.Memory().Read32(Field(field));
} }
void SetField(HeaderField field, u32 value) { void SetField(HeaderField field, u32 value) {
memory.Write32(Field(field), value); system.Memory().Write32(Field(field), value);
} }
/** /**
@ -474,12 +472,11 @@ private:
* otherwise error code of the last iteration. * otherwise error code of the last iteration.
*/ */
template <typename FunctionObject> template <typename FunctionObject>
static ResultCode ForEachAutoLinkCRO(Kernel::Process& process, Memory::MemorySystem& memory, static ResultCode ForEachAutoLinkCRO(Kernel::Process& process, Core::System& system,
ARM_Interface& cpu, VAddr crs_address, VAddr crs_address, FunctionObject func) {
FunctionObject func) {
VAddr current = crs_address; VAddr current = crs_address;
while (current != 0) { while (current != 0) {
CROHelper cro(current, process, memory, cpu); CROHelper cro(current, process, system);
CASCADE_RESULT(bool next, func(cro)); CASCADE_RESULT(bool next, func(cro));
if (!next) if (!next)
break; break;

View File

@ -115,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
return; return;
} }
CROHelper crs(crs_address, *process, system.Memory(), system.CPU()); CROHelper crs(crs_address, *process, system);
crs.InitCRS(); crs.InitCRS();
result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true); result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true);
@ -249,7 +249,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
return; return;
} }
CROHelper cro(cro_address, *process, system.Memory(), system.CPU()); CROHelper cro(cro_address, *process, system);
result = cro.VerifyHash(cro_size, crr_address); result = cro.VerifyHash(cro_size, crr_address);
if (result.IsError()) { if (result.IsError()) {
@ -313,7 +313,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
} }
} }
system.CPU().InvalidateCacheRange(cro_address, cro_size); system.InvalidateCacheRange(cro_address, cro_size);
LOG_INFO(Service_LDR, "CRO \"{}\" loaded at 0x{:08X}, fixed_end=0x{:08X}", cro.ModuleName(), LOG_INFO(Service_LDR, "CRO \"{}\" loaded at 0x{:08X}, fixed_end=0x{:08X}", cro.ModuleName(),
cro_address, cro_address + fix_size); cro_address, cro_address + fix_size);
@ -331,7 +331,7 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}, zero={}, cro_buffer_ptr=0x{:08X}", LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}, zero={}, cro_buffer_ptr=0x{:08X}",
cro_address, zero, cro_buffer_ptr); cro_address, zero, cro_buffer_ptr);
CROHelper cro(cro_address, *process, system.Memory(), system.CPU()); CROHelper cro(cro_address, *process, system);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -386,7 +386,7 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_LDR, "Error unmapping CRO {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error unmapping CRO {:08X}", result.raw);
} }
system.CPU().InvalidateCacheRange(cro_address, fixed_size); system.InvalidateCacheRange(cro_address, fixed_size);
rb.Push(result); rb.Push(result);
} }
@ -398,7 +398,7 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}", cro_address); LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}", cro_address);
CROHelper cro(cro_address, *process, system.Memory(), system.CPU()); CROHelper cro(cro_address, *process, system);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -438,7 +438,7 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}", cro_address); LOG_DEBUG(Service_LDR, "called, cro_address=0x{:08X}", cro_address);
CROHelper cro(cro_address, *process, system.Memory(), system.CPU()); CROHelper cro(cro_address, *process, system);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -487,7 +487,7 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) {
return; return;
} }
CROHelper crs(slot->loaded_crs, *process, system.Memory(), system.CPU()); CROHelper crs(slot->loaded_crs, *process, system);
crs.Unrebase(true); crs.Unrebase(true);
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;

View File

@ -46,7 +46,9 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, const u8* data, u
Core::System::GetInstance().Memory().WriteBlock( Core::System::GetInstance().Memory().WriteBlock(
*Core::System::GetInstance().Kernel().GetCurrentProcess(), address, data, data_size); *Core::System::GetInstance().Kernel().GetCurrentProcess(), address, data, data_size);
// If the memory happens to be executable code, make sure the changes become visible // If the memory happens to be executable code, make sure the changes become visible
Core::CPU().InvalidateCacheRange(address, data_size);
// Is current core correct here?
Core::System::GetInstance().InvalidateCacheRange(address, data_size);
} }
packet.SetPacketDataSize(0); packet.SetPacketDataSize(0);
packet.SendReply(); packet.SendReply();

View File

@ -15,9 +15,9 @@ static Memory::PageTable* page_table = nullptr;
TestEnvironment::TestEnvironment(bool mutable_memory_) TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
timing = std::make_unique<Core::Timing>(); timing = std::make_unique<Core::Timing>(1);
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0); kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1);
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;

View File

@ -23,7 +23,7 @@ TEST_CASE("ARM_DynCom (vfp): vadd", "[arm_dyncom]") {
test_env.SetMemory32(0, 0xEE321A03); // vadd.f32 s2, s4, s6 test_env.SetMemory32(0, 0xEE321A03); // vadd.f32 s2, s4, s6
test_env.SetMemory32(4, 0xEAFFFFFE); // b +#0 test_env.SetMemory32(4, 0xEAFFFFFE); // b +#0
ARM_DynCom dyncom(nullptr, test_env.GetMemory(), USER32MODE); ARM_DynCom dyncom(nullptr, test_env.GetMemory(), USER32MODE, 0, nullptr);
std::vector<VfpTestCase> test_cases{{ std::vector<VfpTestCase> test_cases{{
#include "vfp_vadd_f32.inc" #include "vfp_vadd_f32.inc"

View File

@ -34,16 +34,16 @@ static void AdvanceAndCheck(Core::Timing& timing, u32 idx, int downcount, int ex
expected_callback = CB_IDS[idx]; expected_callback = CB_IDS[idx];
lateness = expected_lateness; lateness = expected_lateness;
timing.AddTicks(timing.GetDowncount() - timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount() -
cpu_downcount); // Pretend we executed X cycles of instructions. cpu_downcount); // Pretend we executed X cycles of instructions.
timing.Advance(); timing.GetTimer(0)->Advance();
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
REQUIRE(downcount == timing.GetDowncount()); REQUIRE(downcount == timing.GetTimer(0)->GetDowncount());
} }
TEST_CASE("CoreTiming[BasicOrder]", "[core]") { TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
Core::Timing timing; Core::Timing timing(1);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
@ -52,60 +52,19 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>); Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>);
// Enter slice 0 // Enter slice 0
timing.Advance(); timing.GetTimer(0)->Advance();
// D -> B -> C -> A -> E // D -> B -> C -> A -> E
timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0);
REQUIRE(1000 == timing.GetDowncount()); REQUIRE(1000 == timing.GetTimer(0)->GetDowncount());
timing.ScheduleEvent(500, cb_b, CB_IDS[1]); timing.ScheduleEvent(500, cb_b, CB_IDS[1], 0);
REQUIRE(500 == timing.GetDowncount()); REQUIRE(500 == timing.GetTimer(0)->GetDowncount());
timing.ScheduleEvent(800, cb_c, CB_IDS[2]); timing.ScheduleEvent(800, cb_c, CB_IDS[2], 0);
REQUIRE(500 == timing.GetDowncount()); REQUIRE(500 == timing.GetTimer(0)->GetDowncount());
timing.ScheduleEvent(100, cb_d, CB_IDS[3]); timing.ScheduleEvent(100, cb_d, CB_IDS[3], 0);
REQUIRE(100 == timing.GetDowncount()); REQUIRE(100 == timing.GetTimer(0)->GetDowncount());
timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); timing.ScheduleEvent(1200, cb_e, CB_IDS[4], 0);
REQUIRE(100 == timing.GetDowncount()); REQUIRE(100 == timing.GetTimer(0)->GetDowncount());
AdvanceAndCheck(timing, 3, 400);
AdvanceAndCheck(timing, 1, 300);
AdvanceAndCheck(timing, 2, 200);
AdvanceAndCheck(timing, 0, 200);
AdvanceAndCheck(timing, 4, MAX_SLICE_LENGTH);
}
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
Core::Timing timing;
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>);
Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", CallbackTemplate<3>);
Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>);
// Enter slice 0
timing.Advance();
// D -> B -> C -> A -> E
timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
// Manually force since ScheduleEventThreadsafe doesn't call it
timing.ForceExceptionCheck(1000);
REQUIRE(1000 == timing.GetDowncount());
timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
// Manually force since ScheduleEventThreadsafe doesn't call it
timing.ForceExceptionCheck(500);
REQUIRE(500 == timing.GetDowncount());
timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
// Manually force since ScheduleEventThreadsafe doesn't call it
timing.ForceExceptionCheck(800);
REQUIRE(500 == timing.GetDowncount());
timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
// Manually force since ScheduleEventThreadsafe doesn't call it
timing.ForceExceptionCheck(100);
REQUIRE(100 == timing.GetDowncount());
timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
// Manually force since ScheduleEventThreadsafe doesn't call it
timing.ForceExceptionCheck(1200);
REQUIRE(100 == timing.GetDowncount());
AdvanceAndCheck(timing, 3, 400); AdvanceAndCheck(timing, 3, 400);
AdvanceAndCheck(timing, 1, 300); AdvanceAndCheck(timing, 1, 300);
@ -131,7 +90,7 @@ void FifoCallback(u64 userdata, s64 cycles_late) {
TEST_CASE("CoreTiming[SharedSlot]", "[core]") { TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
using namespace SharedSlotTest; using namespace SharedSlotTest;
Core::Timing timing; Core::Timing timing(1);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>);
@ -139,36 +98,36 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", FifoCallback<3>); Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", FifoCallback<3>);
Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", FifoCallback<4>); Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", FifoCallback<4>);
timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0);
timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0);
timing.ScheduleEvent(1000, cb_c, CB_IDS[2]); timing.ScheduleEvent(1000, cb_c, CB_IDS[2], 0);
timing.ScheduleEvent(1000, cb_d, CB_IDS[3]); timing.ScheduleEvent(1000, cb_d, CB_IDS[3], 0);
timing.ScheduleEvent(1000, cb_e, CB_IDS[4]); timing.ScheduleEvent(1000, cb_e, CB_IDS[4], 0);
// Enter slice 0 // Enter slice 0
timing.Advance(); timing.GetTimer(0)->Advance();
REQUIRE(1000 == timing.GetDowncount()); REQUIRE(1000 == timing.GetTimer(0)->GetDowncount());
callbacks_ran_flags = 0; callbacks_ran_flags = 0;
counter = 0; counter = 0;
lateness = 0; lateness = 0;
timing.AddTicks(timing.GetDowncount()); timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount());
timing.Advance(); timing.GetTimer(0)->Advance();
REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount());
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
} }
TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { TEST_CASE("CoreTiming[PredictableLateness]", "[core]") {
Core::Timing timing; Core::Timing timing(1);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
// Enter slice 0 // Enter slice 0
timing.Advance(); timing.GetTimer(0)->Advance();
timing.ScheduleEvent(100, cb_a, CB_IDS[0]); timing.ScheduleEvent(100, cb_a, CB_IDS[0], 0);
timing.ScheduleEvent(200, cb_b, CB_IDS[1]); timing.ScheduleEvent(200, cb_b, CB_IDS[1], 0);
AdvanceAndCheck(timing, 0, 90, 10, -10); // (100 - 10) AdvanceAndCheck(timing, 0, 90, 10, -10); // (100 - 10)
AdvanceAndCheck(timing, 1, MAX_SLICE_LENGTH, 50, -50); AdvanceAndCheck(timing, 1, MAX_SLICE_LENGTH, 50, -50);
@ -190,7 +149,7 @@ static void RescheduleCallback(Core::Timing& timing, u64 userdata, s64 cycles_la
TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
using namespace ChainSchedulingTest; using namespace ChainSchedulingTest;
Core::Timing timing; Core::Timing timing(1);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
@ -201,28 +160,30 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
}); });
// Enter slice 0 // Enter slice 0
timing.Advance(); timing.GetTimer(0)->Advance();
timing.ScheduleEvent(800, cb_a, CB_IDS[0]); timing.ScheduleEvent(800, cb_a, CB_IDS[0], 0);
timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0);
timing.ScheduleEvent(2200, cb_c, CB_IDS[2]); timing.ScheduleEvent(2200, cb_c, CB_IDS[2], 0);
timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs)); timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs), 0);
REQUIRE(800 == timing.GetDowncount()); REQUIRE(800 == timing.GetTimer(0)->GetDowncount());
reschedules = 3; reschedules = 3;
AdvanceAndCheck(timing, 0, 200); // cb_a AdvanceAndCheck(timing, 0, 200); // cb_a
AdvanceAndCheck(timing, 1, 1000); // cb_b, cb_rs AdvanceAndCheck(timing, 1, 1000); // cb_b, cb_rs
REQUIRE(2 == reschedules); REQUIRE(2 == reschedules);
timing.AddTicks(timing.GetDowncount()); timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount());
timing.Advance(); // cb_rs timing.GetTimer(0)->Advance(); // cb_rs
REQUIRE(1 == reschedules); REQUIRE(1 == reschedules);
REQUIRE(200 == timing.GetDowncount()); REQUIRE(200 == timing.GetTimer(0)->GetDowncount());
AdvanceAndCheck(timing, 2, 800); // cb_c AdvanceAndCheck(timing, 2, 800); // cb_c
timing.AddTicks(timing.GetDowncount()); timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount());
timing.Advance(); // cb_rs timing.GetTimer(0)->Advance(); // cb_rs
REQUIRE(0 == reschedules); REQUIRE(0 == reschedules);
REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount());
} }
// TODO: Add tests for multiple timers

View File

@ -21,9 +21,9 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
} }
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
Core::Timing timing; Core::Timing timing(1);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr); HLERequestContext context(kernel, std::move(server), nullptr);
@ -233,9 +233,9 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
} }
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
Core::Timing timing; Core::Timing timing(1);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr); HLERequestContext context(kernel, std::move(server), nullptr);

View File

@ -11,9 +11,9 @@
#include "core/memory.h" #include "core/memory.h"
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
Core::Timing timing; Core::Timing timing(1);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
SECTION("these regions should not be mapped on an empty process") { SECTION("these regions should not be mapped on an empty process") {
auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);