diff --git a/tests/A32/fuzz_arm.cpp b/tests/A32/fuzz_arm.cpp index dedc63d1..b6037b6a 100644 --- a/tests/A32/fuzz_arm.cpp +++ b/tests/A32/fuzz_arm.cpp @@ -31,19 +31,18 @@ #include "ir_opt/passes.h" #include "rand_int.h" #include "testenv.h" -#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "unicorn_emu/a32_unicorn.h" using Dynarmic::Common::Bits; -static Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) { +namespace { +Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) { Dynarmic::A32::UserConfig user_config; user_config.enable_fast_dispatch = false; user_config.callbacks = testenv; return user_config; } -namespace { struct InstructionGenerator final { public: InstructionGenerator(const char* format, std::function is_valid = [](u32){ return true; }) : is_valid(is_valid) { @@ -92,16 +91,16 @@ private: u32 mask = 0; std::function is_valid; }; -} // namespace using WriteRecords = std::map; -static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) { - return interp.Reg == jit.Regs() - && interp.ExtReg == jit.ExtRegs() - && interp.Cpsr == jit.Cpsr() - //&& interp.VFP[VFP_FPSCR] == jit.Fpscr() - && interp_write_records == jit_write_records; +bool DoesBehaviorMatch(const A32Unicorn& uni, const Dynarmic::A32::Jit& jit, + const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) { + return uni.GetRegisters() == jit.Regs() && + uni.GetExtRegs() == jit.ExtRegs() && + uni.GetCpsr() == jit.Cpsr() && + // uni.GetFpscr() == jit.Fpscr() && + interp_write_records == jit_write_records; } void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function instruction_generator) { @@ -112,34 +111,32 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe test_env.code_mem.back() = 0xEAFFFFFE; // b +#0 // Prepare test subjects - ARMul_State interp{USER32MODE}; - interp.user_callbacks = &test_env; + A32Unicorn uni{test_env}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; for (size_t run_number = 0; run_number < run_count; run_number++) { - interp.instruction_cache.clear(); - InterpreterClearCache(); + uni.ClearPageCache(); jit.ClearCache(); // Setup initial state - u32 initial_cpsr = 0x000001D0; + const u32 initial_cpsr = 0x000001D0; ArmTestEnv::RegisterArray initial_regs; - std::generate_n(initial_regs.begin(), 15, []{ return RandInt(0, 0xFFFFFFFF); }); + std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt(0, 0xFFFFFFFF); }); initial_regs[15] = 0; ArmTestEnv::ExtRegsArray initial_extregs; std::generate(initial_extregs.begin(), initial_extregs.end(), []{ return RandInt(0, 0xFFFFFFFF); }); - u32 initial_fpscr = 0x01000000 | (RandInt(0, 3) << 22); + const u32 initial_fpscr = 0x01000000 | (RandInt(0, 3) << 22); - interp.UnsetExclusiveMemoryAddress(); - interp.Cpsr = initial_cpsr; - interp.Reg = initial_regs; - interp.ExtReg = initial_extregs; - interp.VFP[VFP_FPSCR] = initial_fpscr; + uni.SetCpsr(initial_cpsr); + uni.SetRegisters(initial_regs); + uni.SetExtRegs(initial_extregs); + uni.SetFpscr(initial_fpscr); + uni.EnableFloatingPointAccess(); jit.Reset(); jit.SetCpsr(initial_cpsr); jit.Regs() = initial_regs; @@ -157,35 +154,37 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe } printf("\nInitial Register Listing: \n"); - for (int i = 0; i <= 15; i++) { - auto reg = Dynarmic::A32::RegToString(static_cast(i)); + for (size_t i = 0; i < initial_regs.size(); i++) { + const auto reg = Dynarmic::A32::RegToString(static_cast(i)); printf("%4s: %08x\n", reg, initial_regs[i]); } printf("CPSR: %08x\n", initial_cpsr); printf("FPSCR:%08x\n", initial_fpscr); - for (int i = 0; i <= 63; i++) { - printf("S%3i: %08x\n", i, initial_extregs[i]); + for (size_t i = 0; i < initial_extregs.size(); i++) { + printf("S%3zu: %08x\n", i, initial_extregs[i]); } printf("\nFinal Register Listing: \n"); - printf(" interp jit\n"); - for (int i = 0; i <= 15; i++) { - auto reg = Dynarmic::A32::RegToString(static_cast(i)); - printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : ""); + printf(" unicorn jit\n"); + const auto uni_registers = uni.GetRegisters(); + for (size_t i = 0; i < uni_registers.size(); i++) { + const auto reg = Dynarmic::A32::RegToString(static_cast(i)); + printf("%4s: %08x %08x %s\n", reg, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : ""); } - printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : ""); - printf("FPSCR:%08x %08x %s\n", interp.VFP[VFP_FPSCR], jit.Fpscr(), interp.VFP[VFP_FPSCR] != jit.Fpscr() ? "*" : ""); - for (int i = 0; i <= 63; i++) { - printf("S%3i: %08x %08x %s\n", i, interp.ExtReg[i], jit.ExtRegs()[i], interp.ExtReg[i] != jit.ExtRegs()[i] ? "*" : ""); + printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : ""); + printf("FPSCR:%08x %08x %s\n", uni.GetFpscr(), jit.Fpscr(), uni.GetFpscr() != jit.Fpscr() ? "*" : ""); + const auto uni_ext_regs = uni.GetExtRegs(); + for (size_t i = 0; i < uni_ext_regs.size(); i++) { + printf("S%3zu: %08x %08x %s\n", i, uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : ""); } printf("\nInterp Write Records:\n"); - for (auto& record : interp_write_records) { + for (const auto& record : interp_write_records) { printf("[%08x] = %02x\n", record.first, record.second); } printf("\nJIT Write Records:\n"); - for (auto& record : jit_write_records) { + for (const auto& record : jit_write_records) { printf("[%08x] = %02x\n", record.first, record.second); } @@ -209,12 +208,14 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe // Run interpreter test_env.modified_memory.clear(); - interp.NumInstrsToExecute = static_cast(instructions_to_execute_count); - InterpreterMainLoop(&interp); + test_env.ticks_left = instructions_to_execute_count; + uni.Run(); interp_write_records = test_env.modified_memory; { - bool T = Dynarmic::Common::Bit<5>(interp.Cpsr); - interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC; + const bool T = Dynarmic::Common::Bit<5>(uni.GetCpsr()); + const u32 mask = T ? 0xFFFFFFFE : 0xFFFFFFFC; + const u32 new_pc = uni.GetPC() & mask; + uni.SetPC(new_pc); } // Run jit @@ -223,9 +224,10 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe jit.Run(); jit_write_records = test_env.modified_memory; - REQUIRE(DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)); + REQUIRE(DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)); } } +} // Anonymous namespace TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm][A32]" ) { // This was a randomized test-case that was failing. diff --git a/tests/A32/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp index af8069f2..1db7f11f 100644 --- a/tests/A32/fuzz_thumb.cpp +++ b/tests/A32/fuzz_thumb.cpp @@ -27,8 +27,7 @@ #include "ir_opt/passes.h" #include "rand_int.h" #include "testenv.h" -#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "unicorn_emu/a32_unicorn.h" static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) { Dynarmic::A32::UserConfig user_config; @@ -64,7 +63,7 @@ public: u16 inst; do { - u16 random = RandInt(0, 0xFFFF); + const u16 random = RandInt(0, 0xFFFF); inst = bits | (random & ~mask); } while (!is_valid(inst)); @@ -78,46 +77,52 @@ private: std::function is_valid; }; -static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, WriteRecords& interp_write_records, WriteRecords& jit_write_records) { - const auto interp_regs = interp.Reg; +static bool DoesBehaviorMatch(const A32Unicorn& uni, const Dynarmic::A32::Jit& jit, + const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) { + const auto interp_regs = uni.GetRegisters(); const auto jit_regs = jit.Regs(); - return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) - && interp.Cpsr == jit.Cpsr() - && interp_write_records == jit_write_records; + return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) && + uni.GetCpsr() == jit.Cpsr() && + interp_write_records == jit_write_records; } -static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& interp, Dynarmic::A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, +static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn& uni, Dynarmic::A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) { - interp.instruction_cache.clear(); - InterpreterClearCache(); + uni.ClearPageCache(); jit.ClearCache(); // Setup initial state - interp.Cpsr = 0x000001F0; - interp.Reg = initial_regs; + uni.SetCpsr(0x000001F0); + uni.SetRegisters(initial_regs); jit.SetCpsr(0x000001F0); jit.Regs() = initial_regs; // Run interpreter test_env.modified_memory.clear(); - interp.NumInstrsToExecute = static_cast(instructions_to_execute_count); - InterpreterMainLoop(&interp); - auto interp_write_records = test_env.modified_memory; - { - bool T = Dynarmic::Common::Bit<5>(interp.Cpsr); - interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC; - } + test_env.ticks_left = instructions_to_execute_count; + uni.SetPC(uni.GetPC() | 1); + uni.Run(); + const bool uni_code_memory_modified = test_env.code_mem_modified_by_guest; + const auto interp_write_records = test_env.modified_memory; // Run jit + test_env.code_mem_modified_by_guest = false; test_env.modified_memory.clear(); test_env.ticks_left = instructions_to_execute_count; jit.Run(); - auto jit_write_records = test_env.modified_memory; + const bool jit_code_memory_modified = test_env.code_mem_modified_by_guest; + const auto jit_write_records = test_env.modified_memory; + test_env.code_mem_modified_by_guest = false; + + REQUIRE(uni_code_memory_modified == jit_code_memory_modified); + if (uni_code_memory_modified) { + return; + } // Compare - if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) { + if (!DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)) { printf("Failed at execution number %zu\n", run_number); printf("\nInstruction Listing: \n"); @@ -126,24 +131,25 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& } printf("\nInitial Register Listing: \n"); - for (int i = 0; i <= 15; i++) { - printf("%4i: %08x\n", i, initial_regs[i]); + for (size_t i = 0; i < initial_regs.size(); i++) { + printf("%4zu: %08x\n", i, initial_regs[i]); } printf("\nFinal Register Listing: \n"); - printf(" interp jit\n"); - for (int i = 0; i <= 15; i++) { - printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : ""); + printf(" unicorn jit\n"); + const auto uni_registers = uni.GetRegisters(); + for (size_t i = 0; i < uni_registers.size(); i++) { + printf("%4zu: %08x %08x %s\n", i, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : ""); } - printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : ""); + printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : ""); - printf("\nInterp Write Records:\n"); - for (auto& record : interp_write_records) { + printf("\nUnicorn Write Records:\n"); + for (const auto& record : interp_write_records) { printf("[%08x] = %02x\n", record.first, record.second); } printf("\nJIT Write Records:\n"); - for (auto& record : jit_write_records) { + for (const auto& record : jit_write_records) { printf("[%08x] = %02x\n", record.first, record.second); } @@ -175,28 +181,27 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function instruction_generator) { ThumbTestEnv test_env; - // Prepare memory + // Prepare memory. test_env.code_mem.resize(instruction_count + 1); test_env.code_mem.back() = 0xE7FE; // b +#0 // Prepare test subjects - ARMul_State interp{USER32MODE}; - interp.user_callbacks = &test_env; + A32Unicorn uni{test_env}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; for (size_t run_number = 0; run_number < run_count; run_number++) { ThumbTestEnv::RegisterArray initial_regs; - std::generate_n(initial_regs.begin(), 15, []{ return RandInt(0, 0xFFFFFFFF); }); + std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt(0, 0xFFFFFFFF); }); initial_regs[15] = 0; std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator); - RunInstance(run_number, test_env, interp, jit, initial_regs, instruction_count, instructions_to_execute_count); + RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count); } } TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { - const std::array instructions = {{ + const std::array instructions = { ThumbInstGen("00000xxxxxxxxxxx"), // LSL , , # ThumbInstGen("00001xxxxxxxxxxx"), // LSR , , # ThumbInstGen("00010xxxxxxxxxxx"), // ASR , , # @@ -224,11 +229,23 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { [](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE ThumbInstGen("10111100xxxxxxxx", // POP (P = 0) [](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE - ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA + ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA + [](u16 inst) { + // Ensure that the architecturally undefined case of + // the base register being within the list isn't hit. + const u32 rn = Dynarmic::Common::Bits<8, 10>(inst); + return (inst & (1U << rn)) == 0; + }), + // TODO: We should properly test against swapped + // endianness cases, however Unicorn doesn't + // expose the intended endianness of a load/store + // operation to memory through its hooks. +#if 0 ThumbInstGen("101101100101x000"), // SETEND - }}; +#endif + }; - auto instruction_select = [&]() -> u16 { + const auto instruction_select = [&]() -> u16 { size_t inst_index = RandInt(0, instructions.size() - 1); return instructions[inst_index].Generate(); @@ -248,26 +265,39 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { } TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { - const std::array instructions = {{ + const std::array instructions = { + // TODO: We currently can't test BX/BLX as we have + // no way of preventing the unpredictable + // condition from occurring with the current interface. + // (bits zero and one within the specified register + // must not be address<1:0> == '10'. +#if 0 ThumbInstGen("01000111xmmmm000", // BLX/BX [](u16 inst){ - u32 Rm = Dynarmic::Common::Bits<3, 6>(inst); + const u32 Rm = Dynarmic::Common::Bits<3, 6>(inst); return Rm != 15; }), +#endif ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp ThumbInstGen("11100xxxxxxxxxxx"), // B ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers) ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers) ThumbInstGen("1101ccccxxxxxxxx", // B [](u16 inst){ - u32 c = Dynarmic::Common::Bits<9, 12>(inst); + const u32 c = Dynarmic::Common::Bits<9, 12>(inst); return c < 0b1110; // Don't want SWI or undefined instructions. }), ThumbInstGen("10110110011x0xxx"), // CPS - ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1) - }}; - auto instruction_select = [&]() -> u16 { + // TODO: We currently have no control over the generated + // values when creating new pages, so we can't + // reliably test this yet. +#if 0 + ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1) +#endif + }; + + const auto instruction_select = [&]() -> u16 { size_t inst_index = RandInt(0, instructions.size() - 1); return instructions[inst_index].Generate(); @@ -280,8 +310,7 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") { ThumbTestEnv test_env; // Prepare test subjects - ARMul_State interp{USER32MODE}; - interp.user_callbacks = &test_env; + A32Unicorn uni{test_env}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; constexpr ThumbTestEnv::RegisterArray initial_regs { @@ -312,5 +341,5 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") { 0xE7FE, // b +#0 }; - RunInstance(1, test_env, interp, jit, initial_regs, 5, 5); + RunInstance(1, test_env, uni, jit, initial_regs, 5, 5); } diff --git a/tests/A32/testenv.h b/tests/A32/testenv.h index 9feec677..a1968ff3 100644 --- a/tests/A32/testenv.h +++ b/tests/A32/testenv.h @@ -17,9 +17,10 @@ #include "common/assert.h" #include "common/common_types.h" -template +template class A32TestEnv final : public Dynarmic::A32::UserCallbacks { public: + using InstructionType = InstructionType_; using RegisterArray = std::array; using ExtRegsArray = std::array; diff --git a/tests/unicorn_emu/a32_unicorn.cpp b/tests/unicorn_emu/a32_unicorn.cpp index ce07a331..7dd487a5 100644 --- a/tests/unicorn_emu/a32_unicorn.cpp +++ b/tests/unicorn_emu/a32_unicorn.cpp @@ -4,6 +4,7 @@ * General Public License version 2 or any later version. */ +#include #include "A32/testenv.h" #include "a32_unicorn.h" #include "common/assert.h" @@ -19,23 +20,31 @@ constexpr u32 BEGIN_ADDRESS = 0; constexpr u32 END_ADDRESS = ~u32(0); -A32Unicorn::A32Unicorn(ArmTestEnv& testenv) : testenv(testenv) { - CHECKED(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc)); +template +A32Unicorn::A32Unicorn(TestEnvironment& testenv) : testenv{testenv} { + constexpr uc_mode open_mode = std::is_same_v ? UC_MODE_ARM : UC_MODE_THUMB; + + CHECKED(uc_open(UC_ARCH_ARM, open_mode, &uc)); CHECKED(uc_hook_add(uc, &intr_hook, UC_HOOK_INTR, (void*)InterruptHook, this, BEGIN_ADDRESS, END_ADDRESS)); CHECKED(uc_hook_add(uc, &mem_invalid_hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, BEGIN_ADDRESS, END_ADDRESS)); CHECKED(uc_hook_add(uc, &mem_write_prot_hook, UC_HOOK_MEM_WRITE, (void*)MemoryWriteHook, this, BEGIN_ADDRESS, END_ADDRESS)); } -A32Unicorn::~A32Unicorn() { +template +A32Unicorn::~A32Unicorn() { ClearPageCache(); CHECKED(uc_hook_del(uc, intr_hook)); CHECKED(uc_hook_del(uc, mem_invalid_hook)); CHECKED(uc_close(uc)); } -void A32Unicorn::Run() { +template +void A32Unicorn::Run() { + // Thumb execution mode requires the LSB to be set to 1. + constexpr u64 mask = std::is_same_v ? 0 : 1; + while (testenv.ticks_left > 0) { - CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1)); + CHECKED(uc_emu_start(uc, GetPC() | mask, END_ADDRESS, 0, 1)); testenv.ticks_left--; if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) { return; @@ -43,63 +52,72 @@ void A32Unicorn::Run() { } } -u32 A32Unicorn::GetPC() const { +template +u32 A32Unicorn::GetPC() const { u32 pc; CHECKED(uc_reg_read(uc, UC_ARM_REG_PC, &pc)); return pc; } -void A32Unicorn::SetPC(u32 value) { +template +void A32Unicorn::SetPC(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM_REG_PC, &value)); } -u32 A32Unicorn::GetSP() const { +template +u32 A32Unicorn::GetSP() const { u32 sp; CHECKED(uc_reg_read(uc, UC_ARM_REG_SP, &sp)); return sp; } -void A32Unicorn::SetSP(u32 value) { +template +void A32Unicorn::SetSP(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM_REG_SP, &value)); } -constexpr std::array gpr_ids{ +constexpr std::array gpr_ids{ UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7, UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12, UC_ARM_REG_R13, UC_ARM_REG_R14, UC_ARM_REG_R15, }; -A32Unicorn::RegisterArray A32Unicorn::GetRegisters() const { - RegisterArray regs; - RegisterPtrArray ptrs; - for (size_t i = 0; i < ptrs.size(); ++i) +template +Unicorn::A32::RegisterArray A32Unicorn::GetRegisters() const { + Unicorn::A32::RegisterArray regs; + Unicorn::A32::RegisterPtrArray ptrs; + for (size_t i = 0; i < ptrs.size(); ++i) { ptrs[i] = ®s[i]; + } CHECKED(uc_reg_read_batch(uc, const_cast(gpr_ids.data()), - reinterpret_cast(ptrs.data()), num_gprs)); + reinterpret_cast(ptrs.data()), Unicorn::A32::num_gprs)); return regs; } -void A32Unicorn::SetRegisters(const RegisterArray& value) { - RegisterConstPtrArray ptrs; - for (size_t i = 0; i < ptrs.size(); ++i) +template +void A32Unicorn::SetRegisters(const RegisterArray& value) { + Unicorn::A32::RegisterConstPtrArray ptrs; + for (size_t i = 0; i < ptrs.size(); ++i) { ptrs[i] = &value[i]; + } CHECKED(uc_reg_write_batch(uc, const_cast(gpr_ids.data()), reinterpret_cast(const_cast(ptrs.data())), ptrs.size())); } -using DoubleExtRegPtrArray = std::array; -using DoubleExtRegConstPtrArray = std::array; +using DoubleExtRegPtrArray = std::array; +using DoubleExtRegConstPtrArray = std::array; -constexpr std::array double_ext_reg_ids{ +constexpr std::array double_ext_reg_ids{ UC_ARM_REG_D0, UC_ARM_REG_D1, UC_ARM_REG_D2, UC_ARM_REG_D3, UC_ARM_REG_D4, UC_ARM_REG_D5, UC_ARM_REG_D6, UC_ARM_REG_D7, UC_ARM_REG_D8, UC_ARM_REG_D9, UC_ARM_REG_D10, UC_ARM_REG_D11, UC_ARM_REG_D12, UC_ARM_REG_D13, UC_ARM_REG_D14, UC_ARM_REG_D15, UC_ARM_REG_D16, UC_ARM_REG_D17, UC_ARM_REG_D18, UC_ARM_REG_D19, UC_ARM_REG_D20, UC_ARM_REG_D21, UC_ARM_REG_D22, UC_ARM_REG_D23, UC_ARM_REG_D24, UC_ARM_REG_D25, UC_ARM_REG_D26, UC_ARM_REG_D27, UC_ARM_REG_D28, UC_ARM_REG_D29, UC_ARM_REG_D30, UC_ARM_REG_D31, }; -A32Unicorn::ExtRegArray A32Unicorn::GetExtRegs() const { - ExtRegArray ext_regs; +template +Unicorn::A32::ExtRegArray A32Unicorn::GetExtRegs() const { + Unicorn::A32::ExtRegArray ext_regs; DoubleExtRegPtrArray ptrs; for (size_t i = 0; i < ptrs.size(); ++i) ptrs[i] = &ext_regs[i*2]; @@ -110,43 +128,69 @@ A32Unicorn::ExtRegArray A32Unicorn::GetExtRegs() const { return ext_regs; } -void A32Unicorn::SetExtRegs(const ExtRegArray& value) { +template +void A32Unicorn::SetExtRegs(const ExtRegArray& value) { DoubleExtRegConstPtrArray ptrs; - for (size_t i = 0; i < ptrs.size(); ++i) + for (size_t i = 0; i < ptrs.size(); ++i) { ptrs[i] = &value[i*2]; + } CHECKED(uc_reg_write_batch(uc, const_cast(double_ext_reg_ids.data()), reinterpret_cast(const_cast(ptrs.data())), ptrs.size())); } -u32 A32Unicorn::GetFpscr() const { +template +u32 A32Unicorn::GetFpscr() const { u32 fpsr; CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &fpsr)); return fpsr; } -void A32Unicorn::SetFpscr(u32 value) { +template +void A32Unicorn::SetFpscr(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM_REG_FPSCR, &value)); } -u32 A32Unicorn::GetCpsr() const { +template +u32 A32Unicorn::GetFpexc() const { + u32 value = 0; + CHECKED(uc_reg_read(uc, UC_ARM_REG_FPEXC, &value)); + return value; +} + +template +void A32Unicorn::SetFpexc(u32 value) { + CHECKED(uc_reg_write(uc, UC_ARM_REG_FPEXC, &value)); +} + +template +u32 A32Unicorn::GetCpsr() const { u32 pstate; CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &pstate)); return pstate; } -void A32Unicorn::SetCpsr(u32 value) { +template +void A32Unicorn::SetCpsr(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM_REG_CPSR, &value)); } -void A32Unicorn::ClearPageCache() { +template +void A32Unicorn::EnableFloatingPointAccess() { + const u32 new_fpexc = GetFpexc() | (1U << 30); + SetFpexc(new_fpexc); +} + +template +void A32Unicorn::ClearPageCache() { for (const auto& page : pages) { CHECKED(uc_mem_unmap(uc, page->address, 4096)); } pages.clear(); } -void A32Unicorn::DumpMemoryInformation() { +template +void A32Unicorn::DumpMemoryInformation() { uc_mem_region* regions; u32 count; CHECKED(uc_mem_regions(uc, ®ions, &count)); @@ -158,7 +202,8 @@ void A32Unicorn::DumpMemoryInformation() { CHECKED(uc_free(regions)); } -void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_data) { +template +void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_data) { auto* this_ = static_cast(user_data); u32 esr = 0; @@ -177,15 +222,17 @@ void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_dat } } -bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 start_address, int size, u64 /*value*/, void* user_data) { +template +bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 start_address, int size, u64 /*value*/, void* user_data) { auto* this_ = static_cast(user_data); const auto generate_page = [&](u32 base_address) { // printf("generate_page(%x)\n", base_address); const u32 permissions = [&]() -> u32 { - if (base_address < this_->testenv.code_mem.size() * 4) + if (base_address < this_->testenv.code_mem.size() * sizeof(typename TestEnvironment::InstructionType)) { return UC_PROT_READ | UC_PROT_EXEC; + } return UC_PROT_READ; }(); @@ -220,7 +267,8 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 sta return true; } -bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) { +template +bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) { auto* this_ = static_cast(user_data); switch (size) { @@ -242,3 +290,6 @@ bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 st return true; } + +template class A32Unicorn; +template class A32Unicorn; diff --git a/tests/unicorn_emu/a32_unicorn.h b/tests/unicorn_emu/a32_unicorn.h index 378ca792..bbac40f1 100644 --- a/tests/unicorn_emu/a32_unicorn.h +++ b/tests/unicorn_emu/a32_unicorn.h @@ -15,17 +15,23 @@ #include "A32/testenv.h" +namespace Unicorn::A32 { +static constexpr size_t num_gprs = 16; +static constexpr size_t num_ext_regs = 64; + +using ExtRegArray = std::array; +using RegisterArray = std::array; +using RegisterPtrArray = std::array; +using RegisterConstPtrArray = std::array; +} // namespace Unicorn::A32 + +template class A32Unicorn final { public: - static constexpr size_t num_gprs = 16; - using RegisterArray = std::array; - using RegisterPtrArray = std::array; - using RegisterConstPtrArray = std::array; + using ExtRegArray = Unicorn::A32::ExtRegArray; + using RegisterArray = Unicorn::A32::RegisterArray; - static constexpr size_t num_ext_regs = 64; - using ExtRegArray = std::array; - - explicit A32Unicorn(ArmTestEnv& testenv); + explicit A32Unicorn(TestEnvironment& testenv); ~A32Unicorn(); void Run(); @@ -45,9 +51,14 @@ public: u32 GetFpscr() const; void SetFpscr(u32 value); + u32 GetFpexc() const; + void SetFpexc(u32 value); + u32 GetCpsr() const; void SetCpsr(u32 value); + void EnableFloatingPointAccess(); + void ClearPageCache(); void DumpMemoryInformation(); @@ -62,7 +73,7 @@ private: std::array data; }; - ArmTestEnv& testenv; + TestEnvironment& testenv; uc_engine* uc{}; uc_hook intr_hook{}; uc_hook mem_invalid_hook{};