mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									e3dbdcbdff
								
							
						
					
					
						commit
						55ec7031cc
					
				
					 32 changed files with 760 additions and 535 deletions
				
			
		|  | @ -61,13 +61,14 @@ void RegistersWidget::OnDebugModeEntered() { | |||
|     if (!Core::System::GetInstance().IsPoweredOn()) | ||||
|         return; | ||||
| 
 | ||||
|     // Todo: Handle all cores
 | ||||
|     for (int i = 0; i < core_registers->childCount(); ++i) | ||||
|         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) | ||||
|         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(); | ||||
|     UpdateVFPSystemRegisterValues(); | ||||
|  | @ -127,7 +128,8 @@ void RegistersWidget::CreateCPSRChildren() { | |||
| } | ||||
| 
 | ||||
| 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->child(0)->setText( | ||||
|  | @ -191,10 +193,11 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() { | |||
| } | ||||
| 
 | ||||
| void RegistersWidget::UpdateVFPSystemRegisterValues() { | ||||
|     const u32 fpscr_val = Core::CPU().GetVFPSystemReg(VFP_FPSCR); | ||||
|     const u32 fpexc_val = Core::CPU().GetVFPSystemReg(VFP_FPEXC); | ||||
|     const u32 fpinst_val = Core::CPU().GetVFPSystemReg(VFP_FPINST); | ||||
|     const u32 fpinst2_val = Core::CPU().GetVFPSystemReg(VFP_FPINST2); | ||||
|     // Todo: handle all cores
 | ||||
|     const u32 fpscr_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPSCR); | ||||
|     const u32 fpexc_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPEXC); | ||||
|     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); | ||||
|     fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0'))); | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| WaitTreeItem::~WaitTreeItem() = default; | ||||
| 
 | ||||
|  | @ -51,12 +52,16 @@ std::size_t WaitTreeItem::Row() const { | |||
| } | ||||
| 
 | ||||
| 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; | ||||
|     item_list.reserve(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; | ||||
|     for (u32 i = 0; i < num_cores; ++i) { | ||||
|         const auto& threads = | ||||
|             Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList(); | ||||
|         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; | ||||
| } | ||||
|  |  | |||
|  | @ -9,10 +9,13 @@ | |||
| #include "common/common_types.h" | ||||
| #include "core/arm/skyeye_common/arm_regformat.h" | ||||
| #include "core/arm/skyeye_common/vfp/asm_vfp.h" | ||||
| #include "core/core_timing.h" | ||||
| 
 | ||||
| /// Generic ARM11 CPU interface
 | ||||
| class ARM_Interface : NonCopyable { | ||||
| public: | ||||
|     explicit ARM_Interface(u32 id, std::shared_ptr<Core::Timing::Timer> timer) | ||||
|         : timer(timer), id(id){}; | ||||
|     virtual ~ARM_Interface() {} | ||||
| 
 | ||||
|     class ThreadContext { | ||||
|  | @ -172,4 +175,18 @@ public: | |||
| 
 | ||||
|     /// Prepare core for thread reschedule (if needed to correctly handle state)
 | ||||
|     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; | ||||
| }; | ||||
|  |  | |||
|  | @ -72,8 +72,7 @@ private: | |||
| class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { | ||||
| public: | ||||
|     explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) | ||||
|         : parent(parent), timing(parent.system.CoreTiming()), svc_context(parent.system), | ||||
|           memory(parent.memory) {} | ||||
|         : parent(parent), svc_context(parent.system), memory(parent.memory) {} | ||||
|     ~DynarmicUserCallbacks() = default; | ||||
| 
 | ||||
|     std::uint8_t MemoryRead8(VAddr vaddr) override { | ||||
|  | @ -137,7 +136,7 @@ public: | |||
|                 parent.jit->HaltExecution(); | ||||
|                 parent.SetPC(pc); | ||||
|                 Kernel::Thread* thread = | ||||
|                     parent.system.Kernel().GetThreadManager().GetCurrentThread(); | ||||
|                     parent.system.Kernel().GetCurrentThreadManager().GetCurrentThread(); | ||||
|                 parent.SaveContext(thread->context); | ||||
|                 GDBStub::Break(); | ||||
|                 GDBStub::SendTrap(thread, 5); | ||||
|  | @ -150,22 +149,23 @@ public: | |||
|     } | ||||
| 
 | ||||
|     void AddTicks(std::uint64_t ticks) override { | ||||
|         timing.AddTicks(ticks); | ||||
|         parent.GetTimer()->AddTicks(ticks); | ||||
|     } | ||||
|     std::uint64_t GetTicksRemaining() override { | ||||
|         s64 ticks = timing.GetDowncount(); | ||||
|         s64 ticks = parent.GetTimer()->GetDowncount(); | ||||
|         return static_cast<u64>(ticks <= 0 ? 0 : ticks); | ||||
|     } | ||||
| 
 | ||||
|     ARM_Dynarmic& parent; | ||||
|     Core::Timing& timing; | ||||
|     Kernel::SVCContext svc_context; | ||||
|     Memory::MemorySystem& memory; | ||||
| }; | ||||
| 
 | ||||
| ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, | ||||
|                            PrivilegeMode initial_mode) | ||||
|     : system(*system), memory(memory), cb(std::make_unique<DynarmicUserCallbacks>(*this)) { | ||||
|                            PrivilegeMode initial_mode, u32 id, | ||||
|                            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); | ||||
|     PageTableChanged(); | ||||
| } | ||||
|  |  | |||
|  | @ -24,7 +24,8 @@ class DynarmicUserCallbacks; | |||
| 
 | ||||
| class ARM_Dynarmic final : public ARM_Interface { | ||||
| 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; | ||||
| 
 | ||||
|     void Run() override; | ||||
|  |  | |||
|  | @ -69,8 +69,9 @@ private: | |||
| }; | ||||
| 
 | ||||
| ARM_DynCom::ARM_DynCom(Core::System* system, Memory::MemorySystem& memory, | ||||
|                        PrivilegeMode initial_mode) | ||||
|     : system(system) { | ||||
|                        PrivilegeMode initial_mode, u32 id, | ||||
|                        std::shared_ptr<Core::Timing::Timer> timer) | ||||
|     : ARM_Interface(id, timer), system(system) { | ||||
|     state = std::make_unique<ARMul_State>(system, memory, initial_mode); | ||||
| } | ||||
| 
 | ||||
|  | @ -78,7 +79,7 @@ ARM_DynCom::~ARM_DynCom() {} | |||
| 
 | ||||
| void ARM_DynCom::Run() { | ||||
|     DEBUG_ASSERT(system != nullptr); | ||||
|     ExecuteInstructions(std::max<s64>(system->CoreTiming().GetDowncount(), 0)); | ||||
|     ExecuteInstructions(std::max<s64>(timer->GetDowncount(), 0)); | ||||
| } | ||||
| 
 | ||||
| void ARM_DynCom::Step() { | ||||
|  | @ -150,7 +151,7 @@ void ARM_DynCom::ExecuteInstructions(u64 num_instructions) { | |||
|     state->NumInstrsToExecute = num_instructions; | ||||
|     unsigned ticks_executed = InterpreterMainLoop(state.get()); | ||||
|     if (system != nullptr) { | ||||
|         system->CoreTiming().AddTicks(ticks_executed); | ||||
|         timer->AddTicks(ticks_executed); | ||||
|     } | ||||
|     state->ServeBreak(); | ||||
| } | ||||
|  |  | |||
|  | @ -21,7 +21,8 @@ class MemorySystem; | |||
| class ARM_DynCom final : public ARM_Interface { | ||||
| public: | ||||
|     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; | ||||
| 
 | ||||
|     void Run() override; | ||||
|  |  | |||
|  | @ -3865,7 +3865,7 @@ SWI_INST : { | |||
|     if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { | ||||
|         DEBUG_ASSERT(cpu->system != nullptr); | ||||
|         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 = | ||||
|             num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; | ||||
|         num_instrs = 0; | ||||
|  |  | |||
|  | @ -607,8 +607,8 @@ void ARMul_State::ServeBreak() { | |||
|     } | ||||
| 
 | ||||
|     DEBUG_ASSERT(system != nullptr); | ||||
|     Kernel::Thread* thread = system->Kernel().GetThreadManager().GetCurrentThread(); | ||||
|     system->CPU().SaveContext(thread->context); | ||||
|     Kernel::Thread* thread = system->Kernel().GetCurrentThreadManager().GetCurrentThread(); | ||||
|     system->GetRunningCore().SaveContext(thread->context); | ||||
| 
 | ||||
|     if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { | ||||
|         last_bkpt_hit = false; | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ static inline std::enable_if_t<std::is_integral_v<T>> WriteOp(const GatewayCheat | |||
|                                                               Core::System& system) { | ||||
|     u32 addr = line.address + state.offset; | ||||
|     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> | ||||
|  | @ -105,7 +105,7 @@ static inline std::enable_if_t<std::is_integral_v<T>> IncrementiveWriteOp( | |||
|     Core::System& system) { | ||||
|     u32 addr = line.value + state.offset; | ||||
|     write_func(addr, static_cast<T>(state.reg)); | ||||
|     system.CPU().InvalidateCacheRange(addr, sizeof(T)); | ||||
|     system.InvalidateCacheRange(addr, 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 addr = line.address + state.offset; | ||||
|     system.CPU().InvalidateCacheRange(addr, num_bytes); | ||||
|     system.InvalidateCacheRange(addr, num_bytes); | ||||
| 
 | ||||
|     bool first = true; | ||||
|     u32 bit_offset = 0; | ||||
|     if (num_bytes > 0) | ||||
|  |  | |||
|  | @ -44,7 +44,8 @@ namespace Core { | |||
| 
 | ||||
| System::ResultStatus System::RunLoop(bool tight_loop) { | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -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,
 | ||||
|     // instead advance to the next event and try to yield to the next thread
 | ||||
|     if (kernel->GetThreadManager().GetCurrentThread() == nullptr) { | ||||
|         LOG_TRACE(Core_ARM11, "Idling"); | ||||
|         timing->Idle(); | ||||
|         timing->Advance(); | ||||
|         PrepareReschedule(); | ||||
|     } else { | ||||
|         timing->Advance(); | ||||
|         if (tight_loop) { | ||||
|             cpu_core->Run(); | ||||
|         } else { | ||||
|             cpu_core->Step(); | ||||
|     // All cores should have executed the same amount of ticks. If this is not the case an event was
 | ||||
|     // scheduled with a cycles_into_future smaller then the current downcount.
 | ||||
|     // So we have to get those cores to the same global time first
 | ||||
|     u64 global_ticks = timing->GetGlobalTicks(); | ||||
|     s64 max_delay = 0; | ||||
|     std::shared_ptr<ARM_Interface> current_core_to_execute = nullptr; | ||||
|     for (auto& cpu_core : cpu_cores) { | ||||
|         if (cpu_core->GetTimer()->GetTicks() < global_ticks) { | ||||
|             s64 delay = global_ticks - cpu_core->GetTimer()->GetTicks(); | ||||
|             cpu_core->GetTimer()->Advance(delay); | ||||
|             if (max_delay < delay) { | ||||
|                 max_delay = delay; | ||||
|                 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()) { | ||||
|         GDBStub::SetCpuStepFlag(false); | ||||
|     } | ||||
|  | @ -174,7 +226,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st | |||
| } | ||||
| 
 | ||||
| void System::PrepareReschedule() { | ||||
|     cpu_core->PrepareReschedule(); | ||||
|     running_core->PrepareReschedule(); | ||||
|     reschedule_pending = true; | ||||
| } | ||||
| 
 | ||||
|  | @ -188,31 +240,50 @@ void System::Reschedule() { | |||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|     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>(); | ||||
| 
 | ||||
|     timing = std::make_unique<Timing>(); | ||||
|     timing = std::make_unique<Timing>(num_cores); | ||||
| 
 | ||||
|     kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, | ||||
|                                                     [this] { PrepareReschedule(); }, system_mode); | ||||
|     kernel = std::make_unique<Kernel::KernelSystem>( | ||||
|         *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores); | ||||
| 
 | ||||
|     if (Settings::values.use_cpu_jit) { | ||||
| #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 | ||||
|         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"); | ||||
| #endif | ||||
|     } 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) { | ||||
|         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"); | ||||
| 
 | ||||
|     initalized = true; | ||||
| 
 | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
|  | @ -362,7 +435,7 @@ void System::Shutdown() { | |||
|     cheat_engine.reset(); | ||||
|     service_manager.reset(); | ||||
|     dsp_core.reset(); | ||||
|     cpu_core.reset(); | ||||
|     cpu_cores.clear(); | ||||
|     kernel.reset(); | ||||
|     timing.reset(); | ||||
|     app_loader.reset(); | ||||
|  |  | |||
|  | @ -140,7 +140,10 @@ public: | |||
|      * @returns True if the emulated system is powered on, otherwise false. | ||||
|      */ | ||||
|     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. | ||||
|      * @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; | ||||
| 
 | ||||
|     /// 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
 | ||||
|     std::unique_ptr<AudioCore::DspInterface> dsp_core; | ||||
|  | @ -330,6 +355,8 @@ private: | |||
| private: | ||||
|     static System s_instance; | ||||
| 
 | ||||
|     bool initalized = false; | ||||
| 
 | ||||
|     ResultStatus status = ResultStatus::Success; | ||||
|     std::string status_details = ""; | ||||
|     /// Saved variables for reset
 | ||||
|  | @ -340,8 +367,16 @@ private: | |||
|     std::atomic<bool> shutdown_requested; | ||||
| }; | ||||
| 
 | ||||
| inline ARM_Interface& CPU() { | ||||
|     return System::GetInstance().CPU(); | ||||
| inline ARM_Interface& GetRunningCore() { | ||||
|     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() { | ||||
|  |  | |||
|  | @ -12,14 +12,22 @@ | |||
| namespace Core { | ||||
| 
 | ||||
| // 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); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     // check for existing type with same name.
 | ||||
|     // 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; | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| u64 Timing::GetTicks() const { | ||||
|     u64 ticks = static_cast<u64>(global_timer); | ||||
|     if (!is_global_timer_sane) { | ||||
| u64 Timing::Timer::GetTicks() const { | ||||
|     u64 ticks = static_cast<u64>(executed_ticks); | ||||
|     if (!is_timer_sane) { | ||||
|         ticks += slice_length - downcount; | ||||
|     } | ||||
|     return ticks; | ||||
| } | ||||
| 
 | ||||
| void Timing::AddTicks(u64 ticks) { | ||||
| void Timing::Timer::AddTicks(u64 ticks) { | ||||
|     downcount -= ticks; | ||||
| } | ||||
| 
 | ||||
| u64 Timing::GetIdleTicks() const { | ||||
| u64 Timing::Timer::GetIdleTicks() const { | ||||
|     return static_cast<u64>(idled_cycles); | ||||
| } | ||||
| 
 | ||||
| void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, | ||||
|                            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) { | ||||
| void Timing::Timer::ForceExceptionCheck(s64 cycles) { | ||||
|     cycles = std::max<s64>(0, cycles); | ||||
|     if (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);) { | ||||
|         ev.fifo_order = event_fifo_id++; | ||||
|         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(); | ||||
| 
 | ||||
|     s64 cycles_executed = slice_length - downcount; | ||||
|     global_timer += cycles_executed; | ||||
|     slice_length = MAX_SLICE_LENGTH; | ||||
|     idled_cycles = 0; | ||||
|     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()); | ||||
|         std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||||
|         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)
 | ||||
|     if (!event_queue.empty()) { | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| void Timing::Idle() { | ||||
| void Timing::Timer::Idle() { | ||||
|     idled_cycles += downcount; | ||||
|     downcount = 0; | ||||
| } | ||||
| 
 | ||||
| std::chrono::microseconds Timing::GetGlobalTimeUs() const { | ||||
|     return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11}; | ||||
| } | ||||
| 
 | ||||
| s64 Timing::GetDowncount() const { | ||||
| s64 Timing::Timer::GetDowncount() const { | ||||
|     return downcount; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -134,62 +134,6 @@ struct TimingEventType { | |||
| 
 | ||||
| class Timing { | ||||
| 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 { | ||||
|         s64 time; | ||||
|         u64 fifo_order; | ||||
|  | @ -202,33 +146,93 @@ private: | |||
| 
 | ||||
|     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 slice_length = MAX_SLICE_LENGTH; | ||||
|     s64 downcount = MAX_SLICE_LENGTH; | ||||
| 
 | ||||
|     // unordered_map stores each element separately as a linked list node so pointers to
 | ||||
|     // elements remain stable regardless of rehashes/resizing.
 | ||||
|     std::unordered_map<std::string, TimingEventType> event_types; | ||||
| 
 | ||||
|     // 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; | ||||
|     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; | ||||
|     std::vector<std::shared_ptr<Timer>> timers; | ||||
|     std::shared_ptr<Timer> current_timer; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Core
 | ||||
|  |  | |||
|  | @ -160,10 +160,14 @@ BreakpointMap breakpoints_write; | |||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| static Kernel::Thread* FindThreadById(int id) { | ||||
|     const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); | ||||
|     for (auto& thread : threads) { | ||||
|         if (thread->GetThreadId() == static_cast<u32>(id)) { | ||||
|             return thread.get(); | ||||
|     u32 num_cores = Core::GetNumCores(); | ||||
|     for (u32 i = 0; i < num_cores; ++i) { | ||||
|         const auto& threads = | ||||
|             Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList(); | ||||
|         for (auto& thread : threads) { | ||||
|             if (thread->GetThreadId() == static_cast<u32>(id)) { | ||||
|                 return thread.get(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return nullptr; | ||||
|  | @ -414,7 +418,10 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) { | |||
|         Core::System::GetInstance().Memory().WriteBlock( | ||||
|             *Core::System::GetInstance().Kernel().GetCurrentProcess(), bp->second.addr, | ||||
|             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); | ||||
| } | ||||
|  | @ -540,10 +547,13 @@ static void HandleQuery() { | |||
|         SendReply(target_xml); | ||||
|     } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | ||||
|         std::string val = "m"; | ||||
|         const auto& threads = | ||||
|             Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); | ||||
|         for (const auto& thread : threads) { | ||||
|             val += fmt::format("{:x},", thread->GetThreadId()); | ||||
|         u32 num_cores = Core::GetNumCores(); | ||||
|         for (u32 i = 0; i < num_cores; ++i) { | ||||
|             const auto& threads = | ||||
|                 Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList(); | ||||
|             for (const auto& thread : threads) { | ||||
|                 val += fmt::format("{:x},", thread->GetThreadId()); | ||||
|             } | ||||
|         } | ||||
|         val.pop_back(); | ||||
|         SendReply(val.c_str()); | ||||
|  | @ -553,11 +563,14 @@ static void HandleQuery() { | |||
|         std::string buffer; | ||||
|         buffer += "l<?xml version=\"1.0\"?>"; | ||||
|         buffer += "<threads>"; | ||||
|         const auto& threads = | ||||
|             Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList(); | ||||
|         for (const auto& thread : threads) { | ||||
|             buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*", | ||||
|                                   thread->GetThreadId(), thread->GetThreadId()); | ||||
|         u32 num_cores = Core::GetNumCores(); | ||||
|         for (u32 i = 0; i < num_cores; ++i) { | ||||
|             const auto& threads = | ||||
|                 Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList(); | ||||
|             for (const auto& thread : threads) { | ||||
|                 buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*", | ||||
|                                       thread->GetThreadId(), thread->GetThreadId()); | ||||
|             } | ||||
|         } | ||||
|         buffer += "</threads>"; | ||||
|         SendReply(buffer.c_str()); | ||||
|  | @ -619,9 +632,9 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { | |||
|     if (full) { | ||||
| 
 | ||||
|         buffer = fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};{:02x}:{:08x}", latest_signal, | ||||
|                              PC_REGISTER, htonl(Core::CPU().GetPC()), SP_REGISTER, | ||||
|                              htonl(Core::CPU().GetReg(SP_REGISTER)), LR_REGISTER, | ||||
|                              htonl(Core::CPU().GetReg(LR_REGISTER))); | ||||
|                              PC_REGISTER, htonl(Core::GetRunningCore().GetPC()), SP_REGISTER, | ||||
|                              htonl(Core::GetRunningCore().GetReg(SP_REGISTER)), LR_REGISTER, | ||||
|                              htonl(Core::GetRunningCore().GetReg(LR_REGISTER))); | ||||
|     } else { | ||||
|         buffer = fmt::format("T{:02x}", latest_signal); | ||||
|     } | ||||
|  | @ -782,7 +795,7 @@ static void WriteRegister() { | |||
|         return SendReply("E01"); | ||||
|     } | ||||
| 
 | ||||
|     Core::CPU().LoadContext(current_thread->context); | ||||
|     Core::GetRunningCore().LoadContext(current_thread->context); | ||||
| 
 | ||||
|     SendReply("OK"); | ||||
| } | ||||
|  | @ -812,7 +825,7 @@ static void WriteRegisters() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Core::CPU().LoadContext(current_thread->context); | ||||
|     Core::GetRunningCore().LoadContext(current_thread->context); | ||||
| 
 | ||||
|     SendReply("OK"); | ||||
| } | ||||
|  | @ -869,7 +882,7 @@ static void WriteMemory() { | |||
|     GdbHexToMem(data.data(), len_pos + 1, len); | ||||
|     Core::System::GetInstance().Memory().WriteBlock( | ||||
|         *Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len); | ||||
|     Core::CPU().ClearInstructionCache(); | ||||
|     Core::GetRunningCore().ClearInstructionCache(); | ||||
|     SendReply("OK"); | ||||
| } | ||||
| 
 | ||||
|  | @ -883,12 +896,12 @@ void Break(bool is_memory_break) { | |||
| static void Step() { | ||||
|     if (command_length > 1) { | ||||
|         RegWrite(PC_REGISTER, GdbHexToInt(command_buffer + 1), current_thread); | ||||
|         Core::CPU().LoadContext(current_thread->context); | ||||
|         Core::GetRunningCore().LoadContext(current_thread->context); | ||||
|     } | ||||
|     step_loop = true; | ||||
|     halt_loop = true; | ||||
|     send_trap = true; | ||||
|     Core::CPU().ClearInstructionCache(); | ||||
|     Core::GetRunningCore().ClearInstructionCache(); | ||||
| } | ||||
| 
 | ||||
| bool IsMemoryBreak() { | ||||
|  | @ -904,7 +917,7 @@ static void Continue() { | |||
|     memory_break = false; | ||||
|     step_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().Kernel().GetCurrentProcess(), addr, btrap.data(), | ||||
|             btrap.size()); | ||||
|         Core::CPU().ClearInstructionCache(); | ||||
|         Core::GetRunningCore().ClearInstructionCache(); | ||||
|     } | ||||
|     p.insert({addr, breakpoint}); | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,7 +83,7 @@ bool HandleTable::IsValid(Handle handle) const { | |||
| 
 | ||||
| std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const { | ||||
|     if (handle == CurrentThread) { | ||||
|         return SharedFrom(kernel.GetThreadManager().GetCurrentThread()); | ||||
|         return SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()); | ||||
|     } else if (handle == CurrentProcess) { | ||||
|         return kernel.GetCurrentProcess(); | ||||
|     } | ||||
|  |  | |||
|  | @ -18,19 +18,27 @@ namespace Kernel { | |||
| 
 | ||||
| /// Initialize the kernel
 | ||||
| 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), | ||||
|       prepare_reschedule_callback(std::move(prepare_reschedule_callback)) { | ||||
|     MemoryInit(system_mode); | ||||
| 
 | ||||
|     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); | ||||
|     ipc_recorder = std::make_unique<IPCDebugger::Recorder>(); | ||||
|     stored_processes.assign(num_cores, nullptr); | ||||
| 
 | ||||
|     next_thread_id = 1; | ||||
| } | ||||
| 
 | ||||
| /// Shutdown the kernel
 | ||||
| KernelSystem::~KernelSystem() = default; | ||||
| KernelSystem::~KernelSystem() { | ||||
|     ResetThreadIDs(); | ||||
| }; | ||||
| 
 | ||||
| ResourceLimitList& KernelSystem::ResourceLimit() { | ||||
|     return *resource_limits; | ||||
|  | @ -53,6 +61,15 @@ void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) { | |||
|     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) { | ||||
|     memory.SetCurrentPageTable(page_table); | ||||
|     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; | ||||
|     thread_manager->SetCPU(*cpu); | ||||
|     timing.SetCurrentTimer(cpu->GetID()); | ||||
|     if (stored_processes[current_cpu->GetID()]) { | ||||
|         SetCurrentProcess(stored_processes[current_cpu->GetID()]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ThreadManager& KernelSystem::GetThreadManager() { | ||||
|     return *thread_manager; | ||||
| ThreadManager& KernelSystem::GetThreadManager(u32 core_id) { | ||||
|     return *thread_managers[core_id]; | ||||
| } | ||||
| 
 | ||||
| const ThreadManager& KernelSystem::GetThreadManager() const { | ||||
|     return *thread_manager; | ||||
| const ThreadManager& KernelSystem::GetThreadManager(u32 core_id) const { | ||||
|     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() { | ||||
|  | @ -101,4 +140,12 @@ void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> po | |||
|     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
 | ||||
|  |  | |||
|  | @ -85,7 +85,8 @@ enum class MemoryRegion : u16 { | |||
| class KernelSystem { | ||||
| public: | ||||
|     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(); | ||||
| 
 | ||||
|     using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>; | ||||
|  | @ -210,13 +211,19 @@ public: | |||
| 
 | ||||
|     std::shared_ptr<Process> GetCurrentProcess() const; | ||||
|     void SetCurrentProcess(std::shared_ptr<Process> process); | ||||
|     void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id); | ||||
| 
 | ||||
|     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(); | ||||
|     const ThreadManager& GetThreadManager() const; | ||||
|     void SetRunningCPU(std::shared_ptr<ARM_Interface> cpu); | ||||
| 
 | ||||
|     ThreadManager& GetThreadManager(u32 core_id); | ||||
|     const ThreadManager& GetThreadManager(u32 core_id) const; | ||||
| 
 | ||||
|     ThreadManager& GetCurrentThreadManager(); | ||||
|     const ThreadManager& GetCurrentThreadManager() const; | ||||
| 
 | ||||
|     TimerManager& GetTimerManager(); | ||||
|     const TimerManager& GetTimerManager() const; | ||||
|  | @ -242,6 +249,10 @@ public: | |||
|         prepare_reschedule_callback(); | ||||
|     } | ||||
| 
 | ||||
|     u32 NewThreadId(); | ||||
| 
 | ||||
|     void ResetThreadIDs(); | ||||
| 
 | ||||
|     /// 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; | ||||
| 
 | ||||
|  | @ -276,13 +287,16 @@ private: | |||
|     std::vector<std::shared_ptr<Process>> process_list; | ||||
| 
 | ||||
|     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<SharedPage::Handler> shared_page_handler; | ||||
| 
 | ||||
|     std::unique_ptr<IPCDebugger::Recorder> ipc_recorder; | ||||
| 
 | ||||
|     u32 next_thread_id; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::strin | |||
| 
 | ||||
|     // Acquire mutex with current thread if initialized as locked
 | ||||
|     if (initial_locked) | ||||
|         mutex->Acquire(thread_manager->GetCurrentThread()); | ||||
|         mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread()); | ||||
| 
 | ||||
|     return mutex; | ||||
| } | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) { | |||
|     using namespace std::placeholders; | ||||
|     update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback", | ||||
|                                              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; | ||||
|     shared_page.sliderstate_3d = static_cast<float_le>(slidestate); | ||||
|  |  | |||
|  | @ -280,12 +280,12 @@ void SVC::ExitProcess() { | |||
|     current_process->status = ProcessStatus::Exited; | ||||
| 
 | ||||
|     // 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) { | ||||
|         if (thread->owner_process != current_process.get()) | ||||
|             continue; | ||||
| 
 | ||||
|         if (thread.get() == kernel.GetThreadManager().GetCurrentThread()) | ||||
|         if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread()) | ||||
|             continue; | ||||
| 
 | ||||
|         // TODO(Subv): When are the other running/ready threads terminated?
 | ||||
|  | @ -297,7 +297,7 @@ void SVC::ExitProcess() { | |||
|     } | ||||
| 
 | ||||
|     // Kill the current thread
 | ||||
|     kernel.GetThreadManager().GetCurrentThread()->Stop(); | ||||
|     kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); | ||||
| 
 | ||||
|     system.PrepareReschedule(); | ||||
| } | ||||
|  | @ -388,7 +388,7 @@ ResultCode SVC::SendSyncRequest(Handle handle) { | |||
| 
 | ||||
|     system.PrepareReschedule(); | ||||
| 
 | ||||
|     auto thread = SharedFrom(kernel.GetThreadManager().GetCurrentThread()); | ||||
|     auto thread = SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()); | ||||
| 
 | ||||
|     if (kernel.GetIPCRecorder().IsEnabled()) { | ||||
|         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
 | ||||
| ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||
|     auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle); | ||||
|     Thread* thread = kernel.GetThreadManager().GetCurrentThread(); | ||||
|     Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread(); | ||||
| 
 | ||||
|     if (object == nullptr) | ||||
|         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
 | ||||
| ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, | ||||
|                                      bool wait_all, s64 nano_seconds) { | ||||
|     Thread* thread = kernel.GetThreadManager().GetCurrentThread(); | ||||
|     Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread(); | ||||
| 
 | ||||
|     if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address)) | ||||
|         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.
 | ||||
|     // 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()); | ||||
|     IPC::Header header{cmd_buff_header}; | ||||
|     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; | ||||
| 
 | ||||
|     auto res = | ||||
|         arbiter->ArbitrateAddress(SharedFrom(kernel.GetThreadManager().GetCurrentThread()), | ||||
|         arbiter->ArbitrateAddress(SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()), | ||||
|                                   static_cast<ArbitrationType>(type), address, value, nanoseconds); | ||||
| 
 | ||||
|     // 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; | ||||
|     case ThreadProcessorIdAll: | ||||
|         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; | ||||
|     case ThreadProcessorId1: | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Newly created thread must run in the SysCore (Core1), unimplemented."); | ||||
|     case ThreadProcessorId2: | ||||
|     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; | ||||
|     default: | ||||
|         // TODO(bunnei): Implement support for other processor IDs
 | ||||
|         ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id); | ||||
|         break; | ||||
|     } | ||||
|  | @ -930,9 +935,9 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr | |||
| 
 | ||||
| /// Called when a thread exits
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
|  | @ -978,7 +983,7 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) { | |||
| /// Create a mutex
 | ||||
| ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) { | ||||
|     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))); | ||||
| 
 | ||||
|     LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}", | ||||
|  | @ -995,7 +1000,7 @@ ResultCode SVC::ReleaseMutex(Handle handle) { | |||
|     if (mutex == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
| 
 | ||||
|     return mutex->Release(kernel.GetThreadManager().GetCurrentThread()); | ||||
|     return mutex->Release(kernel.GetCurrentThreadManager().GetCurrentThread()); | ||||
| } | ||||
| 
 | ||||
| /// 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) { | ||||
|     CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore, | ||||
|                    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, | ||||
|                    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
 | ||||
| ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) { | ||||
|     std::shared_ptr<Event> evt = kernel.CreateEvent( | ||||
|         static_cast<ResetType>(reset_type), fmt::format("event-{:08x}", system.CPU().GetReg(14))); | ||||
|     std::shared_ptr<Event> evt = | ||||
|         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))); | ||||
| 
 | ||||
|     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
 | ||||
| ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) { | ||||
|     std::shared_ptr<Timer> timer = kernel.CreateTimer( | ||||
|         static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", system.CPU().GetReg(14))); | ||||
|     std::shared_ptr<Timer> timer = | ||||
|         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))); | ||||
| 
 | ||||
|     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) { | ||||
|     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,
 | ||||
|     // 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
 | ||||
| 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.
 | ||||
|     // 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; | ||||
| } | ||||
| 
 | ||||
|  | @ -1596,11 +1604,11 @@ void SVC::CallSVC(u32 immediate) { | |||
| SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {} | ||||
| 
 | ||||
| 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) { | ||||
|     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)) {} | ||||
|  |  | |||
|  | @ -33,13 +33,9 @@ void Thread::Acquire(Thread* thread) { | |||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||||
| } | ||||
| 
 | ||||
| u32 ThreadManager::NewThreadId() { | ||||
|     return next_thread_id++; | ||||
| } | ||||
| 
 | ||||
| Thread::Thread(KernelSystem& kernel) | ||||
|     : WaitObject(kernel), context(kernel.GetThreadManager().NewContext()), | ||||
|       thread_manager(kernel.GetThreadManager()) {} | ||||
| Thread::Thread(KernelSystem& kernel, u32 core_id) | ||||
|     : WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()), | ||||
|       thread_manager(kernel.GetThreadManager(core_id)) {} | ||||
| Thread::~Thread() {} | ||||
| 
 | ||||
| Thread* ThreadManager::GetCurrentThread() const { | ||||
|  | @ -84,7 +80,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) { | |||
| 
 | ||||
|     // Save context for previous thread
 | ||||
|     if (previous_thread) { | ||||
|         previous_thread->last_running_ticks = timing.GetTicks(); | ||||
|         previous_thread->last_running_ticks = timing.GetGlobalTicks(); | ||||
|         cpu->SaveContext(previous_thread->context); | ||||
| 
 | ||||
|         if (previous_thread->status == ThreadStatus::Running) { | ||||
|  | @ -111,7 +107,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) { | |||
|         new_thread->status = ThreadStatus::Running; | ||||
| 
 | ||||
|         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); | ||||
|  | @ -124,7 +120,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) { | |||
| } | ||||
| 
 | ||||
| Thread* ThreadManager::PopNextReadyThread() { | ||||
|     Thread* next; | ||||
|     Thread* next = nullptr; | ||||
|     Thread* thread = GetCurrentThread(); | ||||
| 
 | ||||
|     if (thread && thread->status == ThreadStatus::Running) { | ||||
|  | @ -309,22 +305,22 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name, | |||
|                           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_manager->ready_queue.prepare(priority); | ||||
|     thread_managers[processor_id]->thread_list.push_back(thread); | ||||
|     thread_managers[processor_id]->ready_queue.prepare(priority); | ||||
| 
 | ||||
|     thread->thread_id = thread_manager->NewThreadId(); | ||||
|     thread->thread_id = NewThreadId(); | ||||
|     thread->status = ThreadStatus::Dormant; | ||||
|     thread->entry_point = entry_point; | ||||
|     thread->stack_top = stack_top; | ||||
|     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->wait_objects.clear(); | ||||
|     thread->wait_address = 0; | ||||
|     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; | ||||
| 
 | ||||
|     // 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
 | ||||
|     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; | ||||
| 
 | ||||
|     return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | ||||
|  | @ -435,6 +431,9 @@ void ThreadManager::Reschedule() { | |||
|         LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); | ||||
|     } else if (next) { | ||||
|         LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); | ||||
|     } else { | ||||
|         LOG_TRACE(Kernel, "context switch idle -> idle, do nothing"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     SwitchContext(next); | ||||
|  | @ -461,11 +460,10 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
|     return GetTLSAddress() + command_header_offset; | ||||
| } | ||||
| 
 | ||||
| ThreadManager::ThreadManager(Kernel::KernelSystem& kernel) : kernel(kernel) { | ||||
|     ThreadWakeupEventType = | ||||
|         kernel.timing.RegisterEvent("ThreadWakeupCallback", [this](u64 thread_id, s64 cycle_late) { | ||||
|             ThreadWakeupCallback(thread_id, cycle_late); | ||||
|         }); | ||||
| ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id) : kernel(kernel) { | ||||
|     ThreadWakeupEventType = kernel.timing.RegisterEvent( | ||||
|         "ThreadWakeupCallback_" + std::to_string(core_id), | ||||
|         [this](u64 thread_id, s64 cycle_late) { ThreadWakeupCallback(thread_id, cycle_late); }); | ||||
| } | ||||
| 
 | ||||
| ThreadManager::~ThreadManager() { | ||||
|  |  | |||
|  | @ -34,7 +34,9 @@ enum ThreadProcessorId : s32 { | |||
|     ThreadProcessorIdAll = -1,     ///< Run thread on either core
 | ||||
|     ThreadProcessorId0 = 0,        ///< Run thread on core 0 (AppCore)
 | ||||
|     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 { | ||||
|  | @ -57,15 +59,9 @@ enum class ThreadWakeupReason { | |||
| 
 | ||||
| class ThreadManager { | ||||
| public: | ||||
|     explicit ThreadManager(Kernel::KernelSystem& kernel); | ||||
|     explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id); | ||||
|     ~ThreadManager(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a new thread ID | ||||
|      * @return The new thread ID | ||||
|      */ | ||||
|     u32 NewThreadId(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the current thread | ||||
|      */ | ||||
|  | @ -132,7 +128,6 @@ private: | |||
|     Kernel::KernelSystem& kernel; | ||||
|     ARM_Interface* cpu; | ||||
| 
 | ||||
|     u32 next_thread_id = 1; | ||||
|     std::shared_ptr<Thread> current_thread; | ||||
|     Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue; | ||||
|     std::unordered_map<u64, Thread*> wakeup_callback_table; | ||||
|  | @ -149,7 +144,7 @@ private: | |||
| 
 | ||||
| class Thread final : public WaitObject { | ||||
| public: | ||||
|     explicit Thread(KernelSystem&); | ||||
|     explicit Thread(KernelSystem&, u32 core_id); | ||||
|     ~Thread() override; | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const { | |||
|         return 0; | ||||
| 
 | ||||
|     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) | ||||
|         return 0; | ||||
|  | @ -71,12 +71,12 @@ ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType reloc | |||
|         break; | ||||
|     case RelocationType::AbsoluteAddress: | ||||
|     case RelocationType::AbsoluteAddress2: | ||||
|         memory.Write32(target_address, symbol_address + addend); | ||||
|         cpu.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         system.Memory().Write32(target_address, symbol_address + addend); | ||||
|         system.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         break; | ||||
|     case RelocationType::RelativeAddress: | ||||
|         memory.Write32(target_address, symbol_address + addend - target_future_address); | ||||
|         cpu.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         system.Memory().Write32(target_address, symbol_address + addend - target_future_address); | ||||
|         system.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         break; | ||||
|     case RelocationType::ThumbBranch: | ||||
|     case RelocationType::ArmBranch: | ||||
|  | @ -98,8 +98,8 @@ ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType reloc | |||
|     case RelocationType::AbsoluteAddress: | ||||
|     case RelocationType::AbsoluteAddress2: | ||||
|     case RelocationType::RelativeAddress: | ||||
|         memory.Write32(target_address, 0); | ||||
|         cpu.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         system.Memory().Write32(target_address, 0); | ||||
|         system.InvalidateCacheRange(target_address, sizeof(u32)); | ||||
|         break; | ||||
|     case RelocationType::ThumbBranch: | ||||
|     case RelocationType::ArmBranch: | ||||
|  | @ -121,7 +121,8 @@ ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool | |||
|     VAddr relocation_address = batch; | ||||
|     while (true) { | ||||
|         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); | ||||
|         if (relocation_target == 0) { | ||||
|  | @ -142,9 +143,9 @@ ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool | |||
|     } | ||||
| 
 | ||||
|     RelocationEntry relocation; | ||||
|     memory.ReadBlock(process, batch, &relocation, sizeof(RelocationEntry)); | ||||
|     system.Memory().ReadBlock(process, batch, &relocation, sizeof(RelocationEntry)); | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
|  | @ -154,13 +155,13 @@ VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const { | |||
| 
 | ||||
|     std::size_t len = name.size(); | ||||
|     ExportTreeEntry entry; | ||||
|     GetEntry(memory, 0, entry); | ||||
|     GetEntry(system.Memory(), 0, entry); | ||||
|     ExportTreeEntry::Child next; | ||||
|     next.raw = entry.left.raw; | ||||
|     u32 found_id; | ||||
| 
 | ||||
|     while (true) { | ||||
|         GetEntry(memory, next.next_index, entry); | ||||
|         GetEntry(system.Memory(), next.next_index, entry); | ||||
| 
 | ||||
|         if (next.is_end) { | ||||
|             found_id = entry.export_table_index; | ||||
|  | @ -186,9 +187,9 @@ VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const { | |||
| 
 | ||||
|     u32 export_strings_size = GetField(ExportStringsSize); | ||||
|     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 SegmentTagToAddress(symbol_entry.symbol_position); | ||||
|  | @ -279,7 +280,7 @@ ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_ | |||
|     u32 segment_num = GetField(SegmentNum); | ||||
|     for (u32 i = 0; i < segment_num; ++i) { | ||||
|         SegmentEntry segment; | ||||
|         GetEntry(memory, i, segment); | ||||
|         GetEntry(system.Memory(), i, segment); | ||||
|         if (segment.type == SegmentType::Data) { | ||||
|             if (segment.size != 0) { | ||||
|                 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) | ||||
|                 return CROFormatError(0x19); | ||||
|         } | ||||
|         SetEntry(memory, i, segment); | ||||
|         SetEntry(system.Memory(), i, segment); | ||||
|     } | ||||
|     return MakeResult<u32>(prev_data_segment + module_address); | ||||
| } | ||||
|  | @ -310,7 +311,7 @@ ResultCode CROHelper::RebaseExportNamedSymbolTable() { | |||
|     u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); | ||||
|     for (u32 i = 0; i < export_named_symbol_num; ++i) { | ||||
|         ExportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             entry.name_offset += module_address; | ||||
|  | @ -320,7 +321,7 @@ ResultCode CROHelper::RebaseExportNamedSymbolTable() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         SetEntry(memory, i, entry); | ||||
|         SetEntry(system.Memory(), i, entry); | ||||
|     } | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | @ -329,7 +330,7 @@ ResultCode CROHelper::VerifyExportTreeTable() const { | |||
|     u32 tree_num = GetField(ExportTreeNum); | ||||
|     for (u32 i = 0; i < tree_num; ++i) { | ||||
|         ExportTreeEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) { | ||||
|             return CROFormatError(0x11); | ||||
|  | @ -353,7 +354,7 @@ ResultCode CROHelper::RebaseImportModuleTable() { | |||
|     u32 module_num = GetField(ImportModuleNum); | ||||
|     for (u32 i = 0; i < module_num; ++i) { | ||||
|         ImportModuleEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             entry.name_offset += module_address; | ||||
|  | @ -379,7 +380,7 @@ ResultCode CROHelper::RebaseImportModuleTable() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         SetEntry(memory, i, entry); | ||||
|         SetEntry(system.Memory(), i, entry); | ||||
|     } | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | @ -395,7 +396,7 @@ ResultCode CROHelper::RebaseImportNamedSymbolTable() { | |||
|     u32 num = GetField(ImportNamedSymbolNum); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             entry.name_offset += module_address; | ||||
|  | @ -413,7 +414,7 @@ ResultCode CROHelper::RebaseImportNamedSymbolTable() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         SetEntry(memory, i, entry); | ||||
|         SetEntry(system.Memory(), i, entry); | ||||
|     } | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | @ -427,7 +428,7 @@ ResultCode CROHelper::RebaseImportIndexedSymbolTable() { | |||
|     u32 num = GetField(ImportIndexedSymbolNum); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportIndexedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.relocation_batch_offset != 0) { | ||||
|             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; | ||||
| } | ||||
|  | @ -451,7 +452,7 @@ ResultCode CROHelper::RebaseImportAnonymousSymbolTable() { | |||
|     u32 num = GetField(ImportAnonymousSymbolNum); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportAnonymousSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.relocation_batch_offset != 0) { | ||||
|             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; | ||||
| } | ||||
|  | @ -476,14 +477,14 @@ ResultCode CROHelper::ResetExternalRelocations() { | |||
|     ExternalRelocationEntry relocation; | ||||
| 
 | ||||
|     // 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) { | ||||
|         return CROFormatError(0x12); | ||||
|     } | ||||
| 
 | ||||
|     bool batch_begin = true; | ||||
|     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); | ||||
| 
 | ||||
|         if (relocation_target == 0) { | ||||
|  | @ -500,7 +501,7 @@ ResultCode CROHelper::ResetExternalRelocations() { | |||
|         if (batch_begin) { | ||||
|             // resets to unresolved state
 | ||||
|             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
 | ||||
|  | @ -516,7 +517,7 @@ ResultCode CROHelper::ClearExternalRelocations() { | |||
| 
 | ||||
|     bool batch_begin = true; | ||||
|     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); | ||||
| 
 | ||||
|         if (relocation_target == 0) { | ||||
|  | @ -532,7 +533,7 @@ ResultCode CROHelper::ClearExternalRelocations() { | |||
|         if (batch_begin) { | ||||
|             // resets to unresolved state
 | ||||
|             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
 | ||||
|  | @ -548,13 +549,13 @@ ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) { | |||
|         static_relocation_table_offset + | ||||
|         GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry); | ||||
| 
 | ||||
|     CROHelper crs(crs_address, process, memory, cpu); | ||||
|     CROHelper crs(crs_address, process, system); | ||||
|     u32 offset_export_num = GetField(StaticAnonymousSymbolNum); | ||||
|     LOG_INFO(Service_LDR, "CRO \"{}\" exports {} static anonymous symbols", ModuleName(), | ||||
|              offset_export_num); | ||||
|     for (u32 i = 0; i < offset_export_num; ++i) { | ||||
|         StaticAnonymousSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         u32 batch_address = entry.relocation_batch_offset + module_address; | ||||
| 
 | ||||
|         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); | ||||
|     for (u32 i = 0; i < internal_relocation_num; ++i) { | ||||
|         InternalRelocationEntry relocation; | ||||
|         GetEntry(memory, i, relocation); | ||||
|         GetEntry(system.Memory(), i, relocation); | ||||
|         VAddr target_addressB = SegmentTagToAddress(relocation.target_position); | ||||
|         if (target_addressB == 0) { | ||||
|             return CROFormatError(0x15); | ||||
|  | @ -587,7 +588,7 @@ ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) { | |||
| 
 | ||||
|         VAddr target_address; | ||||
|         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 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; | ||||
|         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, | ||||
|                   symbol_segment.offset); | ||||
|         ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend, | ||||
|  | @ -619,7 +620,7 @@ ResultCode CROHelper::ClearInternalRelocations() { | |||
|     u32 internal_relocation_num = GetField(InternalRelocationNum); | ||||
|     for (u32 i = 0; i < internal_relocation_num; ++i) { | ||||
|         InternalRelocationEntry relocation; | ||||
|         GetEntry(memory, i, relocation); | ||||
|         GetEntry(system.Memory(), i, relocation); | ||||
|         VAddr target_address = SegmentTagToAddress(relocation.target_position); | ||||
| 
 | ||||
|         if (target_address == 0) { | ||||
|  | @ -639,13 +640,13 @@ void CROHelper::UnrebaseImportAnonymousSymbolTable() { | |||
|     u32 num = GetField(ImportAnonymousSymbolNum); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportAnonymousSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.relocation_batch_offset != 0) { | ||||
|             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); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportIndexedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.relocation_batch_offset != 0) { | ||||
|             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); | ||||
|     for (u32 i = 0; i < num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             entry.name_offset -= module_address; | ||||
|  | @ -677,7 +678,7 @@ void CROHelper::UnrebaseImportNamedSymbolTable() { | |||
|             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); | ||||
|     for (u32 i = 0; i < module_num; ++i) { | ||||
|         ImportModuleEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             entry.name_offset -= module_address; | ||||
|  | @ -699,7 +700,7 @@ void CROHelper::UnrebaseImportModuleTable() { | |||
|             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); | ||||
|     for (u32 i = 0; i < export_named_symbol_num; ++i) { | ||||
|         ExportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
| 
 | ||||
|         if (entry.name_offset != 0) { | ||||
|             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); | ||||
|     for (u32 i = 0; i < segment_num; ++i) { | ||||
|         SegmentEntry segment; | ||||
|         GetEntry(memory, i, segment); | ||||
|         GetEntry(system.Memory(), i, segment); | ||||
| 
 | ||||
|         if (segment.type == SegmentType::BSS) { | ||||
|             segment.offset = 0; | ||||
|  | @ -729,7 +730,7 @@ void CROHelper::UnrebaseSegmentTable() { | |||
|             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); | ||||
|     for (u32 i = 0; i < symbol_import_num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         if (!relocation_entry.is_batch_resolved) { | ||||
|             ResultCode result = ForEachAutoLinkCRO( | ||||
|                 process, memory, cpu, crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||||
|                 process, system, crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||||
|                     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); | ||||
| 
 | ||||
|                     if (symbol_address != 0) { | ||||
|  | @ -794,11 +795,11 @@ ResultCode CROHelper::ResetImportNamedSymbol() { | |||
|     u32 symbol_import_num = GetField(ImportNamedSymbolNum); | ||||
|     for (u32 i = 0; i < symbol_import_num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||||
|         if (result.IsError()) { | ||||
|  | @ -815,11 +816,11 @@ ResultCode CROHelper::ResetImportIndexedSymbol() { | |||
|     u32 import_num = GetField(ImportIndexedSymbolNum); | ||||
|     for (u32 i = 0; i < import_num; ++i) { | ||||
|         ImportIndexedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||||
|         if (result.IsError()) { | ||||
|  | @ -836,11 +837,11 @@ ResultCode CROHelper::ResetImportAnonymousSymbol() { | |||
|     u32 import_num = GetField(ImportAnonymousSymbolNum); | ||||
|     for (u32 i = 0; i < import_num; ++i) { | ||||
|         ImportAnonymousSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||||
|         if (result.IsError()) { | ||||
|  | @ -857,19 +858,20 @@ ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) { | |||
|     u32 import_module_num = GetField(ImportModuleNum); | ||||
|     for (u32 i = 0; i < import_module_num; ++i) { | ||||
|         ImportModuleEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         std::string want_cro_name = memory.ReadCString(entry.name_offset, import_strings_size); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         std::string want_cro_name = | ||||
|             system.Memory().ReadCString(entry.name_offset, import_strings_size); | ||||
| 
 | ||||
|         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()) { | ||||
|                     LOG_INFO(Service_LDR, "CRO \"{}\" imports {} indexed symbols from \"{}\"", | ||||
|                              ModuleName(), entry.import_indexed_symbol_num, source.ModuleName()); | ||||
|                     for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||||
|                         ImportIndexedSymbolEntry im; | ||||
|                         entry.GetImportIndexedSymbolEntry(process, memory, j, im); | ||||
|                         entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im); | ||||
|                         ExportIndexedSymbolEntry ex; | ||||
|                         source.GetEntry(memory, im.index, ex); | ||||
|                         source.GetEntry(system.Memory(), im.index, ex); | ||||
|                         u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position); | ||||
|                         LOG_TRACE(Service_LDR, "    Imports 0x{:08X}", symbol_address); | ||||
|                         ResultCode result = | ||||
|  | @ -884,7 +886,7 @@ ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) { | |||
|                              ModuleName(), entry.import_anonymous_symbol_num, source.ModuleName()); | ||||
|                     for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||||
|                         ImportAnonymousSymbolEntry im; | ||||
|                         entry.GetImportAnonymousSymbolEntry(process, memory, j, im); | ||||
|                         entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im); | ||||
|                         u32 symbol_address = source.SegmentTagToAddress(im.symbol_position); | ||||
|                         LOG_TRACE(Service_LDR, "    Imports 0x{:08X}", symbol_address); | ||||
|                         ResultCode result = | ||||
|  | @ -913,15 +915,15 @@ ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) { | |||
|     u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); | ||||
|     for (u32 i = 0; i < target_symbol_import_num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         target.GetEntry(memory, i, entry); | ||||
|         target.GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         if (!relocation_entry.is_batch_resolved) { | ||||
|             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); | ||||
|             if (symbol_address != 0) { | ||||
|                 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); | ||||
|     for (u32 i = 0; i < target_symbol_import_num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         target.GetEntry(memory, i, entry); | ||||
|         target.GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   sizeof(ExternalRelocationEntry)); | ||||
| 
 | ||||
|         if (relocation_entry.is_batch_resolved) { | ||||
|             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); | ||||
|             if (symbol_address != 0) { | ||||
|                 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); | ||||
|     for (u32 i = 0; i < target_import_module_num; ++i) { | ||||
|         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; | ||||
| 
 | ||||
|         LOG_INFO(Service_LDR, "CRO \"{}\" exports {} indexed symbols to \"{}\"", module_name, | ||||
|                  entry.import_indexed_symbol_num, target.ModuleName()); | ||||
|         for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||||
|             ImportIndexedSymbolEntry im; | ||||
|             entry.GetImportIndexedSymbolEntry(process, memory, j, im); | ||||
|             entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im); | ||||
|             ExportIndexedSymbolEntry ex; | ||||
|             GetEntry(memory, im.index, ex); | ||||
|             GetEntry(system.Memory(), im.index, ex); | ||||
|             u32 symbol_address = SegmentTagToAddress(ex.symbol_position); | ||||
|             LOG_TRACE(Service_LDR, "    exports symbol 0x{:08X}", symbol_address); | ||||
|             ResultCode result = | ||||
|  | @ -1000,7 +1003,7 @@ ResultCode CROHelper::ApplyModuleExport(CROHelper target) { | |||
|                  entry.import_anonymous_symbol_num, target.ModuleName()); | ||||
|         for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||||
|             ImportAnonymousSymbolEntry im; | ||||
|             entry.GetImportAnonymousSymbolEntry(process, memory, j, im); | ||||
|             entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im); | ||||
|             u32 symbol_address = SegmentTagToAddress(im.symbol_position); | ||||
|             LOG_TRACE(Service_LDR, "    exports symbol 0x{:08X}", symbol_address); | ||||
|             ResultCode result = | ||||
|  | @ -1023,16 +1026,17 @@ ResultCode CROHelper::ResetModuleExport(CROHelper target) { | |||
|     u32 target_import_module_num = target.GetField(ImportModuleNum); | ||||
|     for (u32 i = 0; i < target_import_module_num; ++i) { | ||||
|         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; | ||||
| 
 | ||||
|         LOG_DEBUG(Service_LDR, "CRO \"{}\" unexports indexed symbols to \"{}\"", module_name, | ||||
|                   target.ModuleName()); | ||||
|         for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||||
|             ImportIndexedSymbolEntry im; | ||||
|             entry.GetImportIndexedSymbolEntry(process, memory, j, im); | ||||
|             entry.GetImportIndexedSymbolEntry(process, system.Memory(), j, im); | ||||
|             ResultCode result = | ||||
|                 target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); | ||||
|             if (result.IsError()) { | ||||
|  | @ -1045,7 +1049,7 @@ ResultCode CROHelper::ResetModuleExport(CROHelper target) { | |||
|                   target.ModuleName()); | ||||
|         for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||||
|             ImportAnonymousSymbolEntry im; | ||||
|             entry.GetImportAnonymousSymbolEntry(process, memory, j, im); | ||||
|             entry.GetImportAnonymousSymbolEntry(process, system.Memory(), j, im); | ||||
|             ResultCode result = | ||||
|                 target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); | ||||
|             if (result.IsError()) { | ||||
|  | @ -1063,15 +1067,16 @@ ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) { | |||
|     u32 symbol_import_num = GetField(ImportNamedSymbolNum); | ||||
|     for (u32 i = 0; i < symbol_import_num; ++i) { | ||||
|         ImportNamedSymbolEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         VAddr relocation_addr = entry.relocation_batch_offset; | ||||
|         ExternalRelocationEntry relocation_entry; | ||||
|         memory.ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                          sizeof(ExternalRelocationEntry)); | ||||
|         system.Memory().ReadBlock(process, relocation_addr, &relocation_entry, | ||||
|                                   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( | ||||
|                 process, memory, cpu, crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||||
|                 process, system, crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||||
|                     u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_"); | ||||
| 
 | ||||
|                     if (symbol_address != 0) { | ||||
|  | @ -1126,7 +1131,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     result = VerifyStringTableLength(memory, GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||||
|     result = VerifyStringTableLength(system.Memory(), GetField(ModuleNameOffset), | ||||
|                                      GetField(ModuleNameSize)); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error verifying module name {:08X}", result.raw); | ||||
|         return result; | ||||
|  | @ -1155,8 +1161,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     result = | ||||
|         VerifyStringTableLength(memory, GetField(ExportStringsOffset), GetField(ExportStringsSize)); | ||||
|     result = VerifyStringTableLength(system.Memory(), GetField(ExportStringsOffset), | ||||
|                                      GetField(ExportStringsSize)); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error verifying export strings {:08X}", result.raw); | ||||
|         return result; | ||||
|  | @ -1192,8 +1198,8 @@ ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     result = | ||||
|         VerifyStringTableLength(memory, GetField(ImportStringsOffset), GetField(ImportStringsSize)); | ||||
|     result = VerifyStringTableLength(system.Memory(), GetField(ImportStringsOffset), | ||||
|                                      GetField(ImportStringsSize)); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error verifying import strings {:08X}", result.raw); | ||||
|         return result; | ||||
|  | @ -1266,11 +1272,11 @@ ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) { | |||
|             // so we do the same
 | ||||
|             if (GetField(SegmentNum) >= 2) { // means we have .data segment
 | ||||
|                 SegmentEntry entry; | ||||
|                 GetEntry(memory, 2, entry); | ||||
|                 GetEntry(system.Memory(), 2, entry); | ||||
|                 ASSERT(entry.type == SegmentType::Data); | ||||
|                 data_segment_address = entry.offset; | ||||
|                 entry.offset = GetField(DataOffset); | ||||
|                 SetEntry(memory, 2, entry); | ||||
|                 SetEntry(system.Memory(), 2, entry); | ||||
|             } | ||||
|         } | ||||
|         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 (GetField(SegmentNum) >= 2) { | ||||
|                     SegmentEntry entry; | ||||
|                     GetEntry(memory, 2, entry); | ||||
|                     GetEntry(system.Memory(), 2, entry); | ||||
|                     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
 | ||||
|     result = ForEachAutoLinkCRO(process, memory, cpu, crs_address, | ||||
|     result = ForEachAutoLinkCRO(process, system, crs_address, | ||||
|                                 [this](CROHelper target) -> ResultVal<bool> { | ||||
|                                     ResultCode result = ApplyExportNamedSymbol(target); | ||||
|                                     if (result.IsError()) | ||||
|  | @ -1346,7 +1352,7 @@ ResultCode CROHelper::Unlink(VAddr crs_address) { | |||
| 
 | ||||
|     // Resets all symbols in other modules imported from this module
 | ||||
|     // 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> { | ||||
|                                     ResultCode result = ResetExportNamedSymbol(target); | ||||
|                                     if (result.IsError()) | ||||
|  | @ -1387,13 +1393,13 @@ void CROHelper::InitCRS() { | |||
| } | ||||
| 
 | ||||
| void CROHelper::Register(VAddr crs_address, bool auto_link) { | ||||
|     CROHelper crs(crs_address, process, memory, cpu); | ||||
|     CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule(), process, memory, cpu); | ||||
|     CROHelper crs(crs_address, process, system); | ||||
|     CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule(), process, system); | ||||
| 
 | ||||
|     if (head.module_address) { | ||||
|         // there are already CROs registered
 | ||||
|         // register as the new tail
 | ||||
|         CROHelper tail(head.PreviousModule(), process, memory, cpu); | ||||
|         CROHelper tail(head.PreviousModule(), process, system); | ||||
| 
 | ||||
|         // link with the old tail
 | ||||
|         ASSERT(tail.NextModule() == 0); | ||||
|  | @ -1419,11 +1425,11 @@ void CROHelper::Register(VAddr crs_address, bool auto_link) { | |||
| } | ||||
| 
 | ||||
| void CROHelper::Unregister(VAddr crs_address) { | ||||
|     CROHelper crs(crs_address, process, memory, cpu); | ||||
|     CROHelper next_head(crs.NextModule(), process, memory, cpu); | ||||
|     CROHelper previous_head(crs.PreviousModule(), process, memory, cpu); | ||||
|     CROHelper next(NextModule(), process, memory, cpu); | ||||
|     CROHelper previous(PreviousModule(), process, memory, cpu); | ||||
|     CROHelper crs(crs_address, process, system); | ||||
|     CROHelper next_head(crs.NextModule(), process, system); | ||||
|     CROHelper previous_head(crs.PreviousModule(), process, system); | ||||
|     CROHelper next(NextModule(), process, system); | ||||
|     CROHelper previous(PreviousModule(), process, system); | ||||
| 
 | ||||
|     if (module_address == next_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); | ||||
|     for (u32 i = 0; i < segment_num; ++i) { | ||||
|         SegmentEntry entry; | ||||
|         GetEntry(memory, i, entry); | ||||
|         GetEntry(system.Memory(), i, entry); | ||||
|         if (entry.type == SegmentType::Code && entry.size != 0) { | ||||
|             VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE); | ||||
|             VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE); | ||||
|  |  | |||
|  | @ -33,12 +33,11 @@ static constexpr u32 CRO_HASH_SIZE = 0x80; | |||
| class CROHelper final { | ||||
| public: | ||||
|     // TODO (wwylele): pass in the process handle for memory access
 | ||||
|     explicit CROHelper(VAddr cro_address, Kernel::Process& process, Memory::MemorySystem& memory, | ||||
|                        ARM_Interface& cpu) | ||||
|         : module_address(cro_address), process(process), memory(memory), cpu(cpu) {} | ||||
|     explicit CROHelper(VAddr cro_address, Kernel::Process& process, Core::System& system) | ||||
|         : module_address(cro_address), process(process), system(system) {} | ||||
| 
 | ||||
|     std::string ModuleName() const { | ||||
|         return memory.ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||||
|         return system.Memory().ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||||
|     } | ||||
| 
 | ||||
|     u32 GetFileSize() const { | ||||
|  | @ -144,8 +143,7 @@ public: | |||
| private: | ||||
|     const VAddr module_address; ///< the virtual address of this module
 | ||||
|     Kernel::Process& process;   ///< the owner process of this module
 | ||||
|     Memory::MemorySystem& memory; | ||||
|     ARM_Interface& cpu; | ||||
|     Core::System& system; | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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 { | ||||
|         return memory.Read32(Field(field)); | ||||
|         return system.Memory().Read32(Field(field)); | ||||
|     } | ||||
| 
 | ||||
|     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. | ||||
|      */ | ||||
|     template <typename FunctionObject> | ||||
|     static ResultCode ForEachAutoLinkCRO(Kernel::Process& process, Memory::MemorySystem& memory, | ||||
|                                          ARM_Interface& cpu, VAddr crs_address, | ||||
|                                          FunctionObject func) { | ||||
|     static ResultCode ForEachAutoLinkCRO(Kernel::Process& process, Core::System& system, | ||||
|                                          VAddr crs_address, FunctionObject func) { | ||||
|         VAddr current = crs_address; | ||||
|         while (current != 0) { | ||||
|             CROHelper cro(current, process, memory, cpu); | ||||
|             CROHelper cro(current, process, system); | ||||
|             CASCADE_RESULT(bool next, func(cro)); | ||||
|             if (!next) | ||||
|                 break; | ||||
|  |  | |||
|  | @ -115,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     CROHelper crs(crs_address, *process, system.Memory(), system.CPU()); | ||||
|     CROHelper crs(crs_address, *process, system); | ||||
|     crs.InitCRS(); | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     CROHelper cro(cro_address, *process, system.Memory(), system.CPU()); | ||||
|     CROHelper cro(cro_address, *process, system); | ||||
| 
 | ||||
|     result = cro.VerifyHash(cro_size, crr_address); | ||||
|     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(), | ||||
|              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}", | ||||
|               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); | ||||
| 
 | ||||
|  | @ -386,7 +386,7 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { | |||
|         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); | ||||
| } | ||||
|  | @ -398,7 +398,7 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | @ -438,7 +438,7 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | @ -487,7 +487,7 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     CROHelper crs(slot->loaded_crs, *process, system.Memory(), system.CPU()); | ||||
|     CROHelper crs(slot->loaded_crs, *process, system); | ||||
|     crs.Unrebase(true); | ||||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
|  |  | |||
|  | @ -46,7 +46,9 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, const u8* data, u | |||
|         Core::System::GetInstance().Memory().WriteBlock( | ||||
|             *Core::System::GetInstance().Kernel().GetCurrentProcess(), address, data, data_size); | ||||
|         // 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.SendReply(); | ||||
|  |  | |||
|  | @ -15,9 +15,9 @@ static Memory::PageTable* page_table = nullptr; | |||
| TestEnvironment::TestEnvironment(bool mutable_memory_) | ||||
|     : 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>(); | ||||
|     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))); | ||||
|     page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; | ||||
|  |  | |||
|  | @ -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(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{{ | ||||
| #include "vfp_vadd_f32.inc" | ||||
|  |  | |||
|  | @ -34,16 +34,16 @@ static void AdvanceAndCheck(Core::Timing& timing, u32 idx, int downcount, int ex | |||
|     expected_callback = CB_IDS[idx]; | ||||
|     lateness = expected_lateness; | ||||
| 
 | ||||
|     timing.AddTicks(timing.GetDowncount() - | ||||
|                     cpu_downcount); // Pretend we executed X cycles of instructions.
 | ||||
|     timing.Advance(); | ||||
|     timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount() - | ||||
|                                  cpu_downcount); // Pretend we executed X cycles of instructions.
 | ||||
|     timing.GetTimer(0)->Advance(); | ||||
| 
 | ||||
|     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]") { | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
| 
 | ||||
|     Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     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>); | ||||
| 
 | ||||
|     // Enter slice 0
 | ||||
|     timing.Advance(); | ||||
|     timing.GetTimer(0)->Advance(); | ||||
| 
 | ||||
|     // D -> B -> C -> A -> E
 | ||||
|     timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||||
|     REQUIRE(1000 == timing.GetDowncount()); | ||||
|     timing.ScheduleEvent(500, cb_b, CB_IDS[1]); | ||||
|     REQUIRE(500 == timing.GetDowncount()); | ||||
|     timing.ScheduleEvent(800, cb_c, CB_IDS[2]); | ||||
|     REQUIRE(500 == timing.GetDowncount()); | ||||
|     timing.ScheduleEvent(100, cb_d, CB_IDS[3]); | ||||
|     REQUIRE(100 == timing.GetDowncount()); | ||||
|     timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); | ||||
|     REQUIRE(100 == timing.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()); | ||||
|     timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0); | ||||
|     REQUIRE(1000 == timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.ScheduleEvent(500, cb_b, CB_IDS[1], 0); | ||||
|     REQUIRE(500 == timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.ScheduleEvent(800, cb_c, CB_IDS[2], 0); | ||||
|     REQUIRE(500 == timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.ScheduleEvent(100, cb_d, CB_IDS[3], 0); | ||||
|     REQUIRE(100 == timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.ScheduleEvent(1200, cb_e, CB_IDS[4], 0); | ||||
|     REQUIRE(100 == timing.GetTimer(0)->GetDowncount()); | ||||
| 
 | ||||
|     AdvanceAndCheck(timing, 3, 400); | ||||
|     AdvanceAndCheck(timing, 1, 300); | ||||
|  | @ -131,7 +90,7 @@ void FifoCallback(u64 userdata, s64 cycles_late) { | |||
| TEST_CASE("CoreTiming[SharedSlot]", "[core]") { | ||||
|     using namespace SharedSlotTest; | ||||
| 
 | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
| 
 | ||||
|     Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>); | ||||
|     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_e = timing.RegisterEvent("callbackE", FifoCallback<4>); | ||||
| 
 | ||||
|     timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||||
|     timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); | ||||
|     timing.ScheduleEvent(1000, cb_c, CB_IDS[2]); | ||||
|     timing.ScheduleEvent(1000, cb_d, CB_IDS[3]); | ||||
|     timing.ScheduleEvent(1000, cb_e, CB_IDS[4]); | ||||
|     timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0); | ||||
|     timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0); | ||||
|     timing.ScheduleEvent(1000, cb_c, CB_IDS[2], 0); | ||||
|     timing.ScheduleEvent(1000, cb_d, CB_IDS[3], 0); | ||||
|     timing.ScheduleEvent(1000, cb_e, CB_IDS[4], 0); | ||||
| 
 | ||||
|     // Enter slice 0
 | ||||
|     timing.Advance(); | ||||
|     REQUIRE(1000 == timing.GetDowncount()); | ||||
|     timing.GetTimer(0)->Advance(); | ||||
|     REQUIRE(1000 == timing.GetTimer(0)->GetDowncount()); | ||||
| 
 | ||||
|     callbacks_ran_flags = 0; | ||||
|     counter = 0; | ||||
|     lateness = 0; | ||||
|     timing.AddTicks(timing.GetDowncount()); | ||||
|     timing.Advance(); | ||||
|     REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); | ||||
|     timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.GetTimer(0)->Advance(); | ||||
|     REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount()); | ||||
|     REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
| 
 | ||||
|     Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); | ||||
| 
 | ||||
|     // Enter slice 0
 | ||||
|     timing.Advance(); | ||||
|     timing.GetTimer(0)->Advance(); | ||||
| 
 | ||||
|     timing.ScheduleEvent(100, cb_a, CB_IDS[0]); | ||||
|     timing.ScheduleEvent(200, cb_b, CB_IDS[1]); | ||||
|     timing.ScheduleEvent(100, cb_a, CB_IDS[0], 0); | ||||
|     timing.ScheduleEvent(200, cb_b, CB_IDS[1], 0); | ||||
| 
 | ||||
|     AdvanceAndCheck(timing, 0, 90, 10, -10); // (100 - 10)
 | ||||
|     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]") { | ||||
|     using namespace ChainSchedulingTest; | ||||
| 
 | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
| 
 | ||||
|     Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); | ||||
|  | @ -201,28 +160,30 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { | |||
|         }); | ||||
| 
 | ||||
|     // Enter slice 0
 | ||||
|     timing.Advance(); | ||||
|     timing.GetTimer(0)->Advance(); | ||||
| 
 | ||||
|     timing.ScheduleEvent(800, cb_a, CB_IDS[0]); | ||||
|     timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); | ||||
|     timing.ScheduleEvent(2200, cb_c, CB_IDS[2]); | ||||
|     timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs)); | ||||
|     REQUIRE(800 == timing.GetDowncount()); | ||||
|     timing.ScheduleEvent(800, cb_a, CB_IDS[0], 0); | ||||
|     timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0); | ||||
|     timing.ScheduleEvent(2200, cb_c, CB_IDS[2], 0); | ||||
|     timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs), 0); | ||||
|     REQUIRE(800 == timing.GetTimer(0)->GetDowncount()); | ||||
| 
 | ||||
|     reschedules = 3; | ||||
|     AdvanceAndCheck(timing, 0, 200);  // cb_a
 | ||||
|     AdvanceAndCheck(timing, 1, 1000); // cb_b, cb_rs
 | ||||
|     REQUIRE(2 == reschedules); | ||||
| 
 | ||||
|     timing.AddTicks(timing.GetDowncount()); | ||||
|     timing.Advance(); // cb_rs
 | ||||
|     timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.GetTimer(0)->Advance(); // cb_rs
 | ||||
|     REQUIRE(1 == reschedules); | ||||
|     REQUIRE(200 == timing.GetDowncount()); | ||||
|     REQUIRE(200 == timing.GetTimer(0)->GetDowncount()); | ||||
| 
 | ||||
|     AdvanceAndCheck(timing, 2, 800); // cb_c
 | ||||
| 
 | ||||
|     timing.AddTicks(timing.GetDowncount()); | ||||
|     timing.Advance(); // cb_rs
 | ||||
|     timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); | ||||
|     timing.GetTimer(0)->Advance(); // cb_rs
 | ||||
|     REQUIRE(0 == reschedules); | ||||
|     REQUIRE(MAX_SLICE_LENGTH == timing.GetDowncount()); | ||||
|     REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount()); | ||||
| } | ||||
| 
 | ||||
| // TODO: Add tests for multiple timers
 | ||||
|  |  | |||
|  | @ -21,9 +21,9 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) { | |||
| } | ||||
| 
 | ||||
| TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
|     Memory::MemorySystem memory; | ||||
|     Kernel::KernelSystem kernel(memory, timing, [] {}, 0); | ||||
|     Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1); | ||||
|     auto [server, client] = kernel.CreateSessionPair(); | ||||
|     HLERequestContext context(kernel, std::move(server), nullptr); | ||||
| 
 | ||||
|  | @ -233,9 +233,9 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel | |||
| } | ||||
| 
 | ||||
| TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
|     Memory::MemorySystem memory; | ||||
|     Kernel::KernelSystem kernel(memory, timing, [] {}, 0); | ||||
|     Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1); | ||||
|     auto [server, client] = kernel.CreateSessionPair(); | ||||
|     HLERequestContext context(kernel, std::move(server), nullptr); | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,9 +11,9 @@ | |||
| #include "core/memory.h" | ||||
| 
 | ||||
| TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { | ||||
|     Core::Timing timing; | ||||
|     Core::Timing timing(1); | ||||
|     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") { | ||||
|         auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); | ||||
|         CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue