ARM: pass MemorySystem separately in the constructor and make System optional

So that unit test can test CPU without constructing the entire system. Also remove hacks in the System class
This commit is contained in:
Weiyi Wang 2019-02-01 14:50:25 -05:00
parent 9573ee46bd
commit 64f6e5e597
14 changed files with 80 additions and 65 deletions

View File

@ -73,7 +73,7 @@ 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.system.Memory()) {}
memory(parent.memory) {}
~DynarmicUserCallbacks() = default;
std::uint8_t MemoryRead8(VAddr vaddr) override {
@ -163,9 +163,10 @@ public:
Memory::MemorySystem& memory;
};
ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)
: system(system), cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
interpreter_state = std::make_shared<ARMul_State>(system, initial_mode);
ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode)
: system(*system), memory(memory), cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
interpreter_state = std::make_shared<ARMul_State>(system, memory, initial_mode);
PageTableChanged();
}
@ -174,7 +175,7 @@ ARM_Dynarmic::~ARM_Dynarmic() = default;
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Dynarmic::Run() {
ASSERT(system.Memory().GetCurrentPageTable() == current_page_table);
ASSERT(memory.GetCurrentPageTable() == current_page_table);
MICROPROFILE_SCOPE(ARM_Jit);
jit->Run();
@ -281,7 +282,7 @@ void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) {
}
void ARM_Dynarmic::PageTableChanged() {
current_page_table = system.Memory().GetCurrentPageTable();
current_page_table = memory.GetCurrentPageTable();
auto iter = jits.find(current_page_table);
if (iter != jits.end()) {

View File

@ -13,6 +13,7 @@
namespace Memory {
struct PageTable;
class MemorySystem;
} // namespace Memory
namespace Core {
@ -23,7 +24,7 @@ class DynarmicUserCallbacks;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode);
ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, PrivilegeMode initial_mode);
~ARM_Dynarmic();
void Run() override;
@ -55,6 +56,7 @@ public:
private:
friend class DynarmicUserCallbacks;
Core::System& system;
Memory::MemorySystem& memory;
std::unique_ptr<DynarmicUserCallbacks> cb;
std::unique_ptr<Dynarmic::A32::Jit> MakeJit();

View File

@ -68,14 +68,17 @@ private:
u32 fpexc;
};
ARM_DynCom::ARM_DynCom(Core::System& system, PrivilegeMode initial_mode) : system(system) {
state = std::make_unique<ARMul_State>(system, initial_mode);
ARM_DynCom::ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode)
: system(system) {
state = std::make_unique<ARMul_State>(system, memory, initial_mode);
}
ARM_DynCom::~ARM_DynCom() {}
void ARM_DynCom::Run() {
ExecuteInstructions(std::max<s64>(system.CoreTiming().GetDowncount(), 0));
DEBUG_ASSERT(system != nullptr);
ExecuteInstructions(std::max<s64>(system->CoreTiming().GetDowncount(), 0));
}
void ARM_DynCom::Step() {
@ -146,7 +149,9 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) {
void ARM_DynCom::ExecuteInstructions(u64 num_instructions) {
state->NumInstrsToExecute = num_instructions;
unsigned ticks_executed = InterpreterMainLoop(state.get());
system.CoreTiming().AddTicks(ticks_executed);
if (system != nullptr) {
system->CoreTiming().AddTicks(ticks_executed);
}
state->ServeBreak();
}

View File

@ -14,9 +14,14 @@ namespace Core {
struct System;
}
namespace Memory {
class MemorySystem;
}
class ARM_DynCom final : public ARM_Interface {
public:
explicit ARM_DynCom(Core::System& system, PrivilegeMode initial_mode);
explicit ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode);
~ARM_DynCom();
void Run() override;
@ -48,6 +53,6 @@ public:
private:
void ExecuteInstructions(u64 num_instructions);
Core::System& system;
Core::System* system;
std::unique_ptr<ARMul_State> state;
};

View File

@ -811,7 +811,7 @@ MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64));
static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr,
ARM_INST_PTR& inst_base) {
u32 inst_size = 4;
u32 inst = cpu->system.Memory().Read32(phys_addr & 0xFFFFFFFC);
u32 inst = cpu->memory.Read32(phys_addr & 0xFFFFFFFC);
// If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM
// instruction
@ -3859,12 +3859,13 @@ SUB_INST : {
}
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->CoreTiming().AddTicks(num_instrs);
cpu->NumInstrsToExecute =
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0;
Kernel::SVCContext{cpu->system}.CallSVC(inst_cream->num & 0xFFFF);
Kernel::SVCContext{*cpu->system}.CallSVC(inst_cream->num & 0xFFFF);
// The kernel would call ERET to get here, which clears exclusive memory state.
cpu->UnsetExclusiveMemoryAddress();
}

View File

@ -10,7 +10,9 @@
#include "core/core.h"
#include "core/memory.h"
ARMul_State::ARMul_State(Core::System& system, PrivilegeMode initial_mode) : system(system) {
ARMul_State::ARMul_State(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode)
: system(system), memory(memory) {
Reset();
ChangePrivilegeMode(initial_mode);
}
@ -191,13 +193,13 @@ static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type) {
u8 ARMul_State::ReadMemory8(u32 address) const {
CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
return system.Memory().Read8(address);
return memory.Read8(address);
}
u16 ARMul_State::ReadMemory16(u32 address) const {
CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u16 data = system.Memory().Read16(address);
u16 data = memory.Read16(address);
if (InBigEndianMode())
data = Common::swap16(data);
@ -208,7 +210,7 @@ u16 ARMul_State::ReadMemory16(u32 address) const {
u32 ARMul_State::ReadMemory32(u32 address) const {
CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u32 data = system.Memory().Read32(address);
u32 data = memory.Read32(address);
if (InBigEndianMode())
data = Common::swap32(data);
@ -219,7 +221,7 @@ u32 ARMul_State::ReadMemory32(u32 address) const {
u64 ARMul_State::ReadMemory64(u32 address) const {
CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u64 data = system.Memory().Read64(address);
u64 data = memory.Read64(address);
if (InBigEndianMode())
data = Common::swap64(data);
@ -230,7 +232,7 @@ u64 ARMul_State::ReadMemory64(u32 address) const {
void ARMul_State::WriteMemory8(u32 address, u8 data) {
CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
system.Memory().Write8(address, data);
memory.Write8(address, data);
}
void ARMul_State::WriteMemory16(u32 address, u16 data) {
@ -239,7 +241,7 @@ void ARMul_State::WriteMemory16(u32 address, u16 data) {
if (InBigEndianMode())
data = Common::swap16(data);
system.Memory().Write16(address, data);
memory.Write16(address, data);
}
void ARMul_State::WriteMemory32(u32 address, u32 data) {
@ -248,7 +250,7 @@ void ARMul_State::WriteMemory32(u32 address, u32 data) {
if (InBigEndianMode())
data = Common::swap32(data);
system.Memory().Write32(address, data);
memory.Write32(address, data);
}
void ARMul_State::WriteMemory64(u32 address, u64 data) {
@ -257,7 +259,7 @@ void ARMul_State::WriteMemory64(u32 address, u64 data) {
if (InBigEndianMode())
data = Common::swap64(data);
system.Memory().Write64(address, data);
memory.Write64(address, data);
}
// Reads from the CP15 registers. Used with implementation of the MRC instruction.
@ -603,8 +605,9 @@ void ARMul_State::ServeBreak() {
if (last_bkpt_hit) {
Reg[15] = last_bkpt.address;
}
Kernel::Thread* thread = system.Kernel().GetThreadManager().GetCurrentThread();
system.CPU().SaveContext(thread->context);
DEBUG_ASSERT(system != nullptr);
Kernel::Thread* thread = system->Kernel().GetThreadManager().GetCurrentThread();
system->CPU().SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();

View File

@ -27,6 +27,10 @@ namespace Core {
class System;
}
namespace Memory {
class MemorySystem;
}
// Signal levels
enum { LOW = 0, HIGH = 1, LOWHIGH = 1, HIGHLOW = 2 };
@ -143,7 +147,8 @@ enum {
struct ARMul_State final {
public:
explicit ARMul_State(Core::System& system, PrivilegeMode initial_mode);
explicit ARMul_State(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode);
void ChangePrivilegeMode(u32 new_mode);
void Reset();
@ -201,7 +206,8 @@ public:
void ServeBreak();
Core::System& system;
Core::System* system;
Memory::MemorySystem& memory;
std::array<u32, 16> Reg{}; // The current register file
std::array<u32, 2> Reg_usr{};

View File

@ -179,13 +179,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
cpu_core = std::make_unique<ARM_Dynarmic>(this, *memory, USER32MODE);
#else
cpu_core = std::make_unique<ARM_DynCom>(*this, USER32MODE);
cpu_core = std::make_unique<ARM_DynCom>(this, *memory, USER32MODE);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
cpu_core = std::make_unique<ARM_DynCom>(*this, USER32MODE);
cpu_core = std::make_unique<ARM_DynCom>(this, *memory, USER32MODE);
}
kernel->GetThreadManager().SetCPU(*cpu_core);

View File

@ -270,8 +270,6 @@ private:
std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
public: // HACK: this is temporary exposed for tests,
// due to WIP kernel refactor causing desync state in memory
std::unique_ptr<Memory::MemorySystem> memory;
std::unique_ptr<Kernel::KernelSystem> kernel;
std::unique_ptr<Timing> timing;

View File

@ -15,15 +15,9 @@ static Memory::PageTable* page_table = nullptr;
TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
// HACK: some memory functions are currently referring kernel from the global instance,
// so we need to create the kernel object there.
// Change this when all global states are eliminated.
Core::System::GetInstance().timing = std::make_unique<Core::Timing>();
Core::System::GetInstance().memory = std::make_unique<Memory::MemorySystem>();
Memory::MemorySystem& memory = *Core::System::GetInstance().memory;
Core::System::GetInstance().kernel = std::make_unique<Kernel::KernelSystem>(
memory, *Core::System::GetInstance().timing, [] {}, 0);
kernel = Core::System::GetInstance().kernel.get();
timing = std::make_unique<Core::Timing>();
memory = std::make_unique<Memory::MemorySystem>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0);
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;
@ -31,17 +25,15 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
page_table->pointers.fill(nullptr);
page_table->attributes.fill(Memory::PageType::Unmapped);
memory.MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
memory.MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
memory->MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
memory->MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
memory.SetCurrentPageTable(page_table);
memory->SetCurrentPageTable(page_table);
}
TestEnvironment::~TestEnvironment() {
Memory::MemorySystem& memory = *Core::System::GetInstance().memory;
memory.UnmapRegion(*page_table, 0x80000000, 0x80000000);
memory.UnmapRegion(*page_table, 0x00000000, 0x80000000);
Core::System::GetInstance().kernel.reset();
memory->UnmapRegion(*page_table, 0x80000000, 0x80000000);
memory->UnmapRegion(*page_table, 0x00000000, 0x80000000);
}
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {

View File

@ -49,6 +49,10 @@ public:
/// Empties the internal write-record store.
void ClearWriteRecords();
Memory::MemorySystem& GetMemory() {
return *memory;
}
private:
friend struct TestMemory;
struct TestMemory final : Memory::MMIORegion {
@ -80,7 +84,9 @@ private:
std::shared_ptr<TestMemory> test_memory;
std::vector<WriteRecord> write_records;
Kernel::KernelSystem* kernel;
std::unique_ptr<Core::Timing> timing;
std::unique_ptr<Memory::MemorySystem> memory;
std::unique_ptr<Kernel::KernelSystem> kernel;
};
} // namespace ArmTests

View File

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

View File

@ -21,10 +21,9 @@ static SharedPtr<Object> MakeObject(Kernel::KernelSystem& kernel) {
}
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
// HACK: see comments of member timing
Core::System::GetInstance().timing = std::make_unique<Core::Timing>();
auto memory = std::make_unique<Memory::MemorySystem>();
Kernel::KernelSystem kernel(*memory, *Core::System::GetInstance().timing, [] {}, 0);
Core::Timing timing;
Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
auto session = std::get<SharedPtr<ServerSession>>(kernel.CreateSessionPair());
HLERequestContext context(std::move(session));
@ -234,10 +233,9 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
}
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
// HACK: see comments of member timing
Core::System::GetInstance().timing = std::make_unique<Core::Timing>();
auto memory = std::make_unique<Memory::MemorySystem>();
Kernel::KernelSystem kernel(*memory, *Core::System::GetInstance().timing, [] {}, 0);
Core::Timing timing;
Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
auto session = std::get<SharedPtr<ServerSession>>(kernel.CreateSessionPair());
HLERequestContext context(std::move(session));

View File

@ -11,11 +11,9 @@
#include "core/memory.h"
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
// HACK: see comments of member timing
Core::System::GetInstance().timing = std::make_unique<Core::Timing>();
Core::System::GetInstance().memory = std::make_unique<Memory::MemorySystem>();
Kernel::KernelSystem kernel(*Core::System::GetInstance().memory,
*Core::System::GetInstance().timing, [] {}, 0);
Core::Timing timing;
Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
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);