From 35c758f2111b20c7c3b9e3bfc31a91827f90410a Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sat, 11 Aug 2018 11:39:53 +0100 Subject: [PATCH] tests: Refactor unicorn_emu to allow for A32 unicorn --- tests/A32/testenv.h | 1 + tests/A64/a64.cpp | 34 +-- tests/A64/fuzz_with_unicorn.cpp | 22 +- tests/A64/testenv.h | 2 +- tests/A64/verify_unicorn.cpp | 20 +- tests/CMakeLists.txt | 8 +- tests/unicorn_emu/a32_unicorn.cpp | 230 ++++++++++++++++++ tests/unicorn_emu/a32_unicorn.h | 68 ++++++ .../a64_unicorn.cpp} | 56 ++--- .../unicorn.h => unicorn_emu/a64_unicorn.h} | 10 +- tests/{A64 => }/unicorn_emu/unicorn_load.cpp | 0 11 files changed, 376 insertions(+), 75 deletions(-) create mode 100644 tests/unicorn_emu/a32_unicorn.cpp create mode 100644 tests/unicorn_emu/a32_unicorn.h rename tests/{A64/unicorn_emu/unicorn.cpp => unicorn_emu/a64_unicorn.cpp} (84%) rename tests/{A64/unicorn_emu/unicorn.h => unicorn_emu/a64_unicorn.h} (93%) rename tests/{A64 => }/unicorn_emu/unicorn_load.cpp (100%) diff --git a/tests/A32/testenv.h b/tests/A32/testenv.h index 5a42bb14..a2865152 100644 --- a/tests/A32/testenv.h +++ b/tests/A32/testenv.h @@ -25,6 +25,7 @@ public: bool code_mem_modified_by_guest = false; std::array code_mem{}; std::map modified_memory; + std::vector interrupts; std::uint32_t MemoryReadCode(u32 vaddr) override { const size_t index = vaddr / sizeof(InstructionType); diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index ce3df9bc..e49767c7 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -11,7 +11,7 @@ #include "testenv.h" TEST_CASE("A64: ADD", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x8b020020); // ADD X0, X1, X2 @@ -32,7 +32,7 @@ TEST_CASE("A64: ADD", "[a64]") { } TEST_CASE("A64: REV", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0xdac00c00); // REV X0, X0 @@ -52,7 +52,7 @@ TEST_CASE("A64: REV", "[a64]") { } TEST_CASE("A64: REV32", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0xdac00800); // REV32 X0, X0 @@ -68,7 +68,7 @@ TEST_CASE("A64: REV32", "[a64]") { } TEST_CASE("A64: REV16", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0xdac00400); // REV16 X0, X0 @@ -88,7 +88,7 @@ TEST_CASE("A64: REV16", "[a64]") { } TEST_CASE("A64: AND", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x8a020020); // AND X0, X1, X2 @@ -109,7 +109,7 @@ TEST_CASE("A64: AND", "[a64]") { } TEST_CASE("A64: Bitmasks", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x3200c3e0); // ORR W0, WZR, #0x01010101 @@ -129,7 +129,7 @@ TEST_CASE("A64: Bitmasks", "[a64]") { } TEST_CASE("A64: ANDS NZCV", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x6a020020); // ANDS W0, W1, W2 @@ -184,7 +184,7 @@ TEST_CASE("A64: ANDS NZCV", "[a64]") { } TEST_CASE("A64: CBZ", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x34000060); // 0x00 : CBZ X0, label @@ -217,7 +217,7 @@ TEST_CASE("A64: CBZ", "[a64]") { } TEST_CASE("A64: TBZ", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x36180060); // 0x00 : TBZ X0, 3, label @@ -261,7 +261,7 @@ TEST_CASE("A64: TBZ", "[a64]") { } TEST_CASE("A64: FABD", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x6eb5d556); // FABD.4S V22, V10, V21 @@ -278,7 +278,7 @@ TEST_CASE("A64: FABD", "[a64]") { } TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::ExclusiveMonitor monitor{1}; Dynarmic::A64::UserConfig conf; @@ -315,7 +315,7 @@ TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") { } TEST_CASE("A64: CNTPCT_EL0", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0xd53be021); // MRS X1, CNTPCT_EL0 @@ -336,7 +336,7 @@ TEST_CASE("A64: CNTPCT_EL0", "[a64]") { } TEST_CASE("A64: FNMSUB 1", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x1f618a9c); // FNMSUB D28, D20, D1, D2 @@ -354,7 +354,7 @@ TEST_CASE("A64: FNMSUB 1", "[a64]") { } TEST_CASE("A64: FNMSUB 2", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x1f2ab88e); // FNMSUB S14, S4, S10, S14 @@ -373,7 +373,7 @@ TEST_CASE("A64: FNMSUB 2", "[a64]") { } TEST_CASE("A64: FMADD", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x1f5e0e4a); // FMADD D10, D18, D30, D3 @@ -392,7 +392,7 @@ TEST_CASE("A64: FMADD", "[a64]") { } TEST_CASE("A64: FMLA.4S (denormal)", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x4e2fcccc); // FMLA.4S V12, V6, V15 @@ -411,7 +411,7 @@ TEST_CASE("A64: FMLA.4S (denormal)", "[a64]") { } TEST_CASE("A64: FMLA.4S (0x80800000)", "[a64]") { - TestEnv env; + A64TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; env.code_mem.emplace_back(0x4e38cc2b); // FMLA.4S V11, V1, V24 diff --git a/tests/A64/fuzz_with_unicorn.cpp b/tests/A64/fuzz_with_unicorn.cpp index 414e9429..a421d2a2 100644 --- a/tests/A64/fuzz_with_unicorn.cpp +++ b/tests/A64/fuzz_with_unicorn.cpp @@ -23,7 +23,7 @@ #include "inst_gen.h" #include "rand_int.h" #include "testenv.h" -#include "unicorn_emu/unicorn.h" +#include "unicorn_emu/a64_unicorn.h" // Needs to be declaerd before static std::ostream& operator<<(std::ostream& o, const Dynarmic::A64::Vector& vec) { @@ -156,10 +156,10 @@ static u32 GenFloatInst(u64 pc, bool is_last_inst) { } } -static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::VectorArray& vecs, const size_t instructions_start, +static void RunTestInstance(const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector& instructions, const u32 pstate, const u32 fpcr) { - static TestEnv jit_env{}; - static TestEnv uni_env{}; + static A64TestEnv jit_env{}; + static A64TestEnv uni_env{}; jit_env.code_mem = instructions; uni_env.code_mem = instructions; @@ -178,7 +178,7 @@ static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::V jit_user_config.ctr_el0 = 0x80038003; static Dynarmic::A64::Jit jit{jit_user_config}; - static Unicorn uni{uni_env}; + static A64Unicorn uni{uni_env}; const u64 initial_sp = RandInt(0x30'0000'0000, 0x40'0000'0000) * 4; @@ -283,8 +283,8 @@ static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::V } TEST_CASE("A64: Single random instruction", "[a64]") { - Unicorn::RegisterArray regs; - Unicorn::VectorArray vecs; + A64Unicorn::RegisterArray regs; + A64Unicorn::VectorArray vecs; std::vector instructions(1); for (size_t iteration = 0; iteration < 100000; ++iteration) { @@ -399,8 +399,8 @@ TEST_CASE("A64: Floating point instructions", "[a64]") { return Vector{lower, upper}; }; - Unicorn::RegisterArray regs; - Unicorn::VectorArray vecs; + A64Unicorn::RegisterArray regs; + A64Unicorn::VectorArray vecs; std::vector instructions(1); for (size_t iteration = 0; iteration < 100000; ++iteration) { @@ -420,8 +420,8 @@ TEST_CASE("A64: Floating point instructions", "[a64]") { } TEST_CASE("A64: Small random block", "[a64]") { - Unicorn::RegisterArray regs; - Unicorn::VectorArray vecs; + A64Unicorn::RegisterArray regs; + A64Unicorn::VectorArray vecs; std::vector instructions(5); for (size_t iteration = 0; iteration < 100000; ++iteration) { diff --git a/tests/A64/testenv.h b/tests/A64/testenv.h index a8b6e10c..cc2f9af7 100644 --- a/tests/A64/testenv.h +++ b/tests/A64/testenv.h @@ -16,7 +16,7 @@ using Vector = Dynarmic::A64::Vector; -class TestEnv final : public Dynarmic::A64::UserCallbacks { +class A64TestEnv final : public Dynarmic::A64::UserCallbacks { public: u64 ticks_left = 0; diff --git a/tests/A64/verify_unicorn.cpp b/tests/A64/verify_unicorn.cpp index da675f9b..e208c4b2 100644 --- a/tests/A64/verify_unicorn.cpp +++ b/tests/A64/verify_unicorn.cpp @@ -10,24 +10,24 @@ #include "rand_int.h" #include "testenv.h" -#include "unicorn_emu/unicorn.h" +#include "unicorn_emu/a64_unicorn.h" using namespace Dynarmic; TEST_CASE("Unicorn: Sanity test", "[a64]") { - TestEnv env; + A64TestEnv env; env.code_mem.emplace_back(0x8b020020); // ADD X0, X1, X2 env.code_mem.emplace_back(0x14000000); // B . - constexpr Unicorn::RegisterArray regs{ + constexpr A64Unicorn::RegisterArray regs{ 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - Unicorn unicorn{env}; + A64Unicorn unicorn{env}; unicorn.SetRegisters(regs); unicorn.SetPC(0); @@ -42,15 +42,15 @@ TEST_CASE("Unicorn: Sanity test", "[a64]") { } TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64]") { - TestEnv env; + A64TestEnv env; env.code_mem.emplace_back(0x385fed99); // LDRB W25, [X12, #0xfffffffffffffffe]! env.code_mem.emplace_back(0x14000000); // B . - Unicorn::RegisterArray regs{}; + A64Unicorn::RegisterArray regs{}; regs[12] = 1; - Unicorn unicorn{env}; + A64Unicorn unicorn{env}; unicorn.SetRegisters(regs); unicorn.SetPC(0); @@ -62,15 +62,15 @@ TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64]") { } TEST_CASE("Unicorn: Ensure is able to read across page boundaries", "[a64]") { - TestEnv env; + A64TestEnv env; env.code_mem.emplace_back(0xb85f93d9); // LDUR W25, [X30, #0xfffffffffffffff9] env.code_mem.emplace_back(0x14000000); // B . - Unicorn::RegisterArray regs{}; + A64Unicorn::RegisterArray regs{}; regs[30] = 4; - Unicorn unicorn{env}; + A64Unicorn unicorn{env}; unicorn.SetRegisters(regs); unicorn.SetPC(0); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 06928158..768611cb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,10 +41,12 @@ add_executable(dynarmic_tests if (DYNARMIC_TESTS_USE_UNICORN) target_sources(dynarmic_tests PRIVATE A64/fuzz_with_unicorn.cpp - A64/unicorn_emu/unicorn.cpp - A64/unicorn_emu/unicorn.h - A64/unicorn_emu/unicorn_load.cpp A64/verify_unicorn.cpp + unicorn_emu/a32_unicorn.cpp + unicorn_emu/a32_unicorn.h + unicorn_emu/a64_unicorn.cpp + unicorn_emu/a64_unicorn.h + unicorn_emu/unicorn_load.cpp ) target_link_libraries(dynarmic_tests PRIVATE Unicorn::Unicorn) endif() diff --git a/tests/unicorn_emu/a32_unicorn.cpp b/tests/unicorn_emu/a32_unicorn.cpp new file mode 100644 index 00000000..3fdf598b --- /dev/null +++ b/tests/unicorn_emu/a32_unicorn.cpp @@ -0,0 +1,230 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "A32/testenv.h" +#include "a32_unicorn.h" +#include "common/assert.h" + +#define CHECKED(expr) \ + do { \ + if (auto cerr_ = (expr)) { \ + ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", cerr_, \ + uc_strerror(cerr_)); \ + } \ + } while (0) + +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)); + 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() { + ClearPageCache(); + CHECKED(uc_hook_del(uc, intr_hook)); + CHECKED(uc_hook_del(uc, mem_invalid_hook)); + CHECKED(uc_close(uc)); +} + +void A32Unicorn::Run() { + while (testenv.ticks_left > 0) { + CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1)); + testenv.ticks_left--; + if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) { + return; + } + } +} + +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) + ptrs[i] = ®s[i]; + + CHECKED(uc_reg_read_batch(uc, const_cast(gpr_ids.data()), + reinterpret_cast(ptrs.data()), num_gprs)); + return regs; +} + +void A32Unicorn::SetRegisters(const RegisterArray& value) { + 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; + +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; + DoubleExtRegPtrArray ptrs; + for (size_t i = 0; i < ptrs.size(); ++i) + ptrs[i] = &ext_regs[i*2]; + + CHECKED(uc_reg_read_batch(uc, const_cast(double_ext_reg_ids.data()), + reinterpret_cast(ptrs.data()), ptrs.size())); + + return ext_regs; +} + +void A32Unicorn::SetExtRegs(const ExtRegArray& value) { + DoubleExtRegConstPtrArray ptrs; + 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::GetPC() const { + u32 pc; + CHECKED(uc_reg_read(uc, UC_ARM_REG_R15, &pc)); + return pc; +} + +u32 A32Unicorn::GetFpscr() const { + u32 fpsr; + CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &fpsr)); + return fpsr; +} + +void A32Unicorn::SetFpscr(u32 value) { + CHECKED(uc_reg_write(uc, UC_ARM_REG_FPSCR, &value)); +} + +u32 A32Unicorn::GetCpsr() const { + u32 pstate; + CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &pstate)); + return pstate; +} + +void A32Unicorn::SetCpsr(u32 value) { + CHECKED(uc_reg_write(uc, UC_ARM_REG_CPSR, &value)); +} + +void A32Unicorn::ClearPageCache() { + for (const auto& page : pages) { + CHECKED(uc_mem_unmap(uc, page->address, 4096)); + } + pages.clear(); +} + +void A32Unicorn::DumpMemoryInformation() { + uc_mem_region* regions; + u32 count; + CHECKED(uc_mem_regions(uc, ®ions, &count)); + + for (u32 i = 0; i < count; ++i) { + printf("region: start 0x%08x end 0x%08x perms 0x%08x\n", static_cast(regions[i].begin), static_cast(regions[i].end), regions[i].perms); + } + + CHECKED(uc_free(regions)); +} + +void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_data) { + auto* this_ = static_cast(user_data); + + u32 esr = 0; + //CHECKED(uc_reg_read(uc, UC_ARM_REG_ESR, &esr)); + + auto ec = esr >> 26; + auto iss = esr & 0xFFFFFF; + + switch (ec) { + case 0x15: // SVC + this_->testenv.CallSVC(iss); + break; + default: + this_->testenv.interrupts.emplace_back(fmt::format("Unhandled interrupt: int_number: {:#x}, esr: {:#x} (ec: {:#x}, iss: {:#x})", int_number, esr, ec, iss)); + break; + } +} + +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) + return UC_PROT_READ | UC_PROT_EXEC; + return UC_PROT_READ; + }(); + + auto page = std::make_unique(); + page->address = base_address; + for (size_t i = 0; i < page->data.size(); ++i) + page->data[i] = this_->testenv.MemoryRead8(base_address + i); + + uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); + if (err == UC_ERR_MAP) + return; // page already exists + CHECKED(err); + + this_->pages.emplace_back(std::move(page)); + }; + + const auto is_in_range = [](u32 addr, u32 start, u32 end) { + if (start <= end) + return addr >= start && addr <= end; // fffff[tttttt]fffff + return addr >= start || addr <= end; // ttttt]ffffff[ttttt + }; + + const u32 start_address_page = start_address & ~u32(0xFFF); + const u32 end_address = start_address + size - 1; + + u32 current_address = start_address_page; + do { + generate_page(current_address); + current_address += 0x1000; + } while (is_in_range(current_address, start_address_page, end_address) && current_address != start_address_page); + + return true; +} + +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) { + case 1: + this_->testenv.MemoryWrite8(start_address, static_cast(value)); + break; + case 2: + this_->testenv.MemoryWrite16(start_address, static_cast(value)); + break; + case 4: + this_->testenv.MemoryWrite32(start_address, static_cast(value)); + break; + case 8: + this_->testenv.MemoryWrite64(start_address, value); + break; + default: + UNREACHABLE(); + } + + return true; +} diff --git a/tests/unicorn_emu/a32_unicorn.h b/tests/unicorn_emu/a32_unicorn.h new file mode 100644 index 00000000..f8416fb2 --- /dev/null +++ b/tests/unicorn_emu/a32_unicorn.h @@ -0,0 +1,68 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#pragma once + +#include +#include + +#include + +#include "common/common_types.h" + +#include "A32/testenv.h" + +class A32Unicorn final { +public: + static constexpr size_t num_gprs = 16; + using RegisterArray = std::array; + using RegisterPtrArray = std::array; + using RegisterConstPtrArray = std::array; + + static constexpr size_t num_ext_regs = 64; + using ExtRegArray = std::array; + + explicit A32Unicorn(ArmTestEnv& testenv); + ~A32Unicorn(); + + void Run(); + + RegisterArray GetRegisters() const; + void SetRegisters(const RegisterArray& value); + + ExtRegArray GetExtRegs() const; + void SetExtRegs(const ExtRegArray& value); + + u32 GetPC() const; + + u32 GetFpscr() const; + void SetFpscr(u32 value); + + u32 GetCpsr() const; + void SetCpsr(u32 value); + + void ClearPageCache(); + + void DumpMemoryInformation(); + +private: + static void InterruptHook(uc_engine* uc, u32 interrupt, void* user_data); + static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u32 addr, int size, u64 value, void* user_data); + static bool MemoryWriteHook(uc_engine* uc, uc_mem_type type, u32 addr, int size, u64 value, void* user_data); + + struct Page { + u32 address; + std::array data; + }; + + ArmTestEnv& testenv; + uc_engine* uc{}; + uc_hook intr_hook{}; + uc_hook mem_invalid_hook{}; + uc_hook mem_write_prot_hook{}; + + std::vector> pages; +}; diff --git a/tests/A64/unicorn_emu/unicorn.cpp b/tests/unicorn_emu/a64_unicorn.cpp similarity index 84% rename from tests/A64/unicorn_emu/unicorn.cpp rename to tests/unicorn_emu/a64_unicorn.cpp index c0431f6a..1255bc07 100644 --- a/tests/A64/unicorn_emu/unicorn.cpp +++ b/tests/unicorn_emu/a64_unicorn.cpp @@ -5,7 +5,7 @@ */ #include "common/assert.h" -#include "unicorn.h" +#include "a64_unicorn.h" #define CHECKED(expr) \ do { \ @@ -18,7 +18,7 @@ constexpr u64 BEGIN_ADDRESS = 0; constexpr u64 END_ADDRESS = ~u64(0); -Unicorn::Unicorn(TestEnv& testenv) : testenv(testenv) { +A64Unicorn::A64Unicorn(A64TestEnv& testenv) : testenv(testenv) { CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); u64 fpv = 3 << 20; CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv)); @@ -27,14 +27,14 @@ Unicorn::Unicorn(TestEnv& testenv) : testenv(testenv) { CHECKED(uc_hook_add(uc, &mem_write_prot_hook, UC_HOOK_MEM_WRITE, (void*)MemoryWriteHook, this, BEGIN_ADDRESS, END_ADDRESS)); } -Unicorn::~Unicorn() { +A64Unicorn::~A64Unicorn() { ClearPageCache(); CHECKED(uc_hook_del(uc, intr_hook)); CHECKED(uc_hook_del(uc, mem_invalid_hook)); CHECKED(uc_close(uc)); } -void Unicorn::Run() { +void A64Unicorn::Run() { while (testenv.ticks_left > 0) { CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1)); testenv.ticks_left--; @@ -44,33 +44,33 @@ void Unicorn::Run() { } } -u64 Unicorn::GetSP() const { +u64 A64Unicorn::GetSP() const { u64 sp; CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &sp)); return sp; } -void Unicorn::SetSP(u64 value) { +void A64Unicorn::SetSP(u64 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &value)); } -u64 Unicorn::GetPC() const { +u64 A64Unicorn::GetPC() const { u64 pc; CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &pc)); return pc; } -void Unicorn::SetPC(u64 value) { +void A64Unicorn::SetPC(u64 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &value)); } -constexpr std::array gpr_ids{ +constexpr std::array gpr_ids{ UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7, UC_ARM64_REG_X8, UC_ARM64_REG_X9, UC_ARM64_REG_X10, UC_ARM64_REG_X11, UC_ARM64_REG_X12, UC_ARM64_REG_X13, UC_ARM64_REG_X14, UC_ARM64_REG_X15, UC_ARM64_REG_X16, UC_ARM64_REG_X17, UC_ARM64_REG_X18, UC_ARM64_REG_X19, UC_ARM64_REG_X20, UC_ARM64_REG_X21, UC_ARM64_REG_X22, UC_ARM64_REG_X23, UC_ARM64_REG_X24, UC_ARM64_REG_X25, UC_ARM64_REG_X26, UC_ARM64_REG_X27, UC_ARM64_REG_X28, UC_ARM64_REG_X29, UC_ARM64_REG_X30 }; -Unicorn::RegisterArray Unicorn::GetRegisters() const { +A64Unicorn::RegisterArray A64Unicorn::GetRegisters() const { RegisterArray regs; RegisterPtrArray ptrs; for (size_t i = 0; i < ptrs.size(); ++i) @@ -81,7 +81,7 @@ Unicorn::RegisterArray Unicorn::GetRegisters() const { return regs; } -void Unicorn::SetRegisters(const RegisterArray& value) { +void A64Unicorn::SetRegisters(const RegisterArray& value) { RegisterConstPtrArray ptrs; for (size_t i = 0; i < ptrs.size(); ++i) ptrs[i] = &value[i]; @@ -90,14 +90,14 @@ void Unicorn::SetRegisters(const RegisterArray& value) { reinterpret_cast(const_cast(ptrs.data())), num_gprs)); } -constexpr std::array vec_ids{ +constexpr std::array vec_ids{ UC_ARM64_REG_Q0, UC_ARM64_REG_Q1, UC_ARM64_REG_Q2, UC_ARM64_REG_Q3, UC_ARM64_REG_Q4, UC_ARM64_REG_Q5, UC_ARM64_REG_Q6, UC_ARM64_REG_Q7, UC_ARM64_REG_Q8, UC_ARM64_REG_Q9, UC_ARM64_REG_Q10, UC_ARM64_REG_Q11, UC_ARM64_REG_Q12, UC_ARM64_REG_Q13, UC_ARM64_REG_Q14, UC_ARM64_REG_Q15, UC_ARM64_REG_Q16, UC_ARM64_REG_Q17, UC_ARM64_REG_Q18, UC_ARM64_REG_Q19, UC_ARM64_REG_Q20, UC_ARM64_REG_Q21, UC_ARM64_REG_Q22, UC_ARM64_REG_Q23, UC_ARM64_REG_Q24, UC_ARM64_REG_Q25, UC_ARM64_REG_Q26, UC_ARM64_REG_Q27, UC_ARM64_REG_Q28, UC_ARM64_REG_Q29, UC_ARM64_REG_Q30, UC_ARM64_REG_Q31 }; -Unicorn::VectorArray Unicorn::GetVectors() const { +A64Unicorn::VectorArray A64Unicorn::GetVectors() const { VectorArray vecs; VectorPtrArray ptrs; for (size_t i = 0; i < ptrs.size(); ++i) @@ -109,7 +109,7 @@ Unicorn::VectorArray Unicorn::GetVectors() const { return vecs; } -void Unicorn::SetVectors(const VectorArray& value) { +void A64Unicorn::SetVectors(const VectorArray& value) { VectorConstPtrArray ptrs; for (size_t i = 0; i < ptrs.size(); ++i) ptrs[i] = &value[i]; @@ -118,44 +118,44 @@ void Unicorn::SetVectors(const VectorArray& value) { reinterpret_cast(const_cast(ptrs.data())), num_vecs)); } -u32 Unicorn::GetFpcr() const { +u32 A64Unicorn::GetFpcr() const { u32 fpcr; CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPCR, &fpcr)); return fpcr; } -void Unicorn::SetFpcr(u32 value) { +void A64Unicorn::SetFpcr(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPCR, &value)); } -u32 Unicorn::GetFpsr() const { +u32 A64Unicorn::GetFpsr() const { u32 fpsr; CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPSR, &fpsr)); return fpsr; } -void Unicorn::SetFpsr(u32 value) { +void A64Unicorn::SetFpsr(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPSR, &value)); } -u32 Unicorn::GetPstate() const { +u32 A64Unicorn::GetPstate() const { u32 pstate; CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &pstate)); return pstate; } -void Unicorn::SetPstate(u32 value) { +void A64Unicorn::SetPstate(u32 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &value)); } -void Unicorn::ClearPageCache() { +void A64Unicorn::ClearPageCache() { for (const auto& page : pages) { CHECKED(uc_mem_unmap(uc, page->address, 4096)); } pages.clear(); } -void Unicorn::DumpMemoryInformation() { +void A64Unicorn::DumpMemoryInformation() { uc_mem_region* regions; u32 count; CHECKED(uc_mem_regions(uc, ®ions, &count)); @@ -167,8 +167,8 @@ void Unicorn::DumpMemoryInformation() { CHECKED(uc_free(regions)); } -void Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) { - auto* this_ = static_cast(user_data); +void A64Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) { + auto* this_ = static_cast(user_data); u32 esr; CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); @@ -186,8 +186,8 @@ void Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) { } } -bool Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 start_address, int size, u64 /*value*/, void* user_data) { - auto* this_ = static_cast(user_data); +bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 start_address, int size, u64 /*value*/, void* user_data) { + auto* this_ = static_cast(user_data); const auto generate_page = [&](u64 base_address) { // printf("generate_page(%" PRIx64 ")\n", base_address); @@ -229,8 +229,8 @@ bool Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 start_ return true; } -bool Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { - auto* this_ = static_cast(user_data); +bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { + auto* this_ = static_cast(user_data); switch (size) { case 1: diff --git a/tests/A64/unicorn_emu/unicorn.h b/tests/unicorn_emu/a64_unicorn.h similarity index 93% rename from tests/A64/unicorn_emu/unicorn.h rename to tests/unicorn_emu/a64_unicorn.h index 50b37842..5bf7bee8 100644 --- a/tests/A64/unicorn_emu/unicorn.h +++ b/tests/unicorn_emu/a64_unicorn.h @@ -12,9 +12,9 @@ #include #include "common/common_types.h" -#include "../testenv.h" +#include "A64/testenv.h" -class Unicorn final { +class A64Unicorn final { public: static constexpr size_t num_gprs = 31; using RegisterArray = std::array; @@ -26,8 +26,8 @@ public: using VectorPtrArray = std::array; using VectorConstPtrArray = std::array; - explicit Unicorn(TestEnv& testenv); - ~Unicorn(); + explicit A64Unicorn(A64TestEnv& testenv); + ~A64Unicorn(); void Run(); @@ -66,7 +66,7 @@ private: std::array data; }; - TestEnv& testenv; + A64TestEnv& testenv; uc_engine* uc{}; uc_hook intr_hook{}; uc_hook mem_invalid_hook{}; diff --git a/tests/A64/unicorn_emu/unicorn_load.cpp b/tests/unicorn_emu/unicorn_load.cpp similarity index 100% rename from tests/A64/unicorn_emu/unicorn_load.cpp rename to tests/unicorn_emu/unicorn_load.cpp