tests: Refactor unicorn_emu to allow for A32 unicorn
This commit is contained in:
parent
3f4d118d73
commit
35c758f211
11 changed files with 376 additions and 75 deletions
|
@ -25,6 +25,7 @@ public:
|
|||
bool code_mem_modified_by_guest = false;
|
||||
std::array<InstructionType, 2048> code_mem{};
|
||||
std::map<u32, u8> modified_memory;
|
||||
std::vector<std::string> interrupts;
|
||||
|
||||
std::uint32_t MemoryReadCode(u32 vaddr) override {
|
||||
const size_t index = vaddr / sizeof(InstructionType);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <fmt/ostream.h>
|
||||
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<u32>& 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<u64>(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<u32> 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<u32> 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<u32> instructions(5);
|
||||
|
||||
for (size_t iteration = 0; iteration < 100000; ++iteration) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
230
tests/unicorn_emu/a32_unicorn.cpp
Normal file
230
tests/unicorn_emu/a32_unicorn.cpp
Normal file
|
@ -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<int, A32Unicorn::num_gprs> 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<int*>(gpr_ids.data()),
|
||||
reinterpret_cast<void**>(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<int*>(gpr_ids.data()),
|
||||
reinterpret_cast<void**>(const_cast<u32**>(ptrs.data())), ptrs.size()));
|
||||
}
|
||||
|
||||
using DoubleExtRegPtrArray = std::array<A32Unicorn::ExtRegArray::pointer, A32Unicorn::num_ext_regs/2>;
|
||||
using DoubleExtRegConstPtrArray = std::array<A32Unicorn::ExtRegArray::const_pointer, A32Unicorn::num_ext_regs/2>;
|
||||
|
||||
constexpr std::array<int, A32Unicorn::num_ext_regs/2> 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<int*>(double_ext_reg_ids.data()),
|
||||
reinterpret_cast<void**>(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<int*>(double_ext_reg_ids.data()),
|
||||
reinterpret_cast<void* const *>(const_cast<u32**>(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<u32>(regions[i].begin), static_cast<u32>(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<A32Unicorn*>(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<A32Unicorn*>(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>();
|
||||
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<A32Unicorn*>(user_data);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
|
||||
break;
|
||||
case 2:
|
||||
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
|
||||
break;
|
||||
case 4:
|
||||
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
|
||||
break;
|
||||
case 8:
|
||||
this_->testenv.MemoryWrite64(start_address, value);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
68
tests/unicorn_emu/a32_unicorn.h
Normal file
68
tests/unicorn_emu/a32_unicorn.h
Normal file
|
@ -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 <array>
|
||||
#include <vector>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "A32/testenv.h"
|
||||
|
||||
class A32Unicorn final {
|
||||
public:
|
||||
static constexpr size_t num_gprs = 16;
|
||||
using RegisterArray = std::array<u32, num_gprs>;
|
||||
using RegisterPtrArray = std::array<RegisterArray::pointer, num_gprs>;
|
||||
using RegisterConstPtrArray = std::array<RegisterArray::const_pointer, num_gprs>;
|
||||
|
||||
static constexpr size_t num_ext_regs = 64;
|
||||
using ExtRegArray = std::array<u32, num_ext_regs>;
|
||||
|
||||
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<u8, 4096> data;
|
||||
};
|
||||
|
||||
ArmTestEnv& testenv;
|
||||
uc_engine* uc{};
|
||||
uc_hook intr_hook{};
|
||||
uc_hook mem_invalid_hook{};
|
||||
uc_hook mem_write_prot_hook{};
|
||||
|
||||
std::vector<std::unique_ptr<Page>> pages;
|
||||
};
|
|
@ -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<int, Unicorn::num_gprs> gpr_ids{
|
||||
constexpr std::array<int, A64Unicorn::num_gprs> 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<void**>(const_cast<u64**>(ptrs.data())), num_gprs));
|
||||
}
|
||||
|
||||
constexpr std::array<int, Unicorn::num_vecs> vec_ids{
|
||||
constexpr std::array<int, A64Unicorn::num_vecs> 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<void* const *>(const_cast<Vector**>(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<Unicorn*>(user_data);
|
||||
void A64Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) {
|
||||
auto* this_ = static_cast<A64Unicorn*>(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<Unicorn*>(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<A64Unicorn*>(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<Unicorn*>(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<A64Unicorn*>(user_data);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
|
@ -12,9 +12,9 @@
|
|||
#include <unicorn/unicorn.h>
|
||||
|
||||
#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<u64, num_gprs>;
|
||||
|
@ -26,8 +26,8 @@ public:
|
|||
using VectorPtrArray = std::array<VectorArray::pointer, num_vecs>;
|
||||
using VectorConstPtrArray = std::array<VectorArray::const_pointer, num_vecs>;
|
||||
|
||||
explicit Unicorn(TestEnv& testenv);
|
||||
~Unicorn();
|
||||
explicit A64Unicorn(A64TestEnv& testenv);
|
||||
~A64Unicorn();
|
||||
|
||||
void Run();
|
||||
|
||||
|
@ -66,7 +66,7 @@ private:
|
|||
std::array<u8, 4096> data;
|
||||
};
|
||||
|
||||
TestEnv& testenv;
|
||||
A64TestEnv& testenv;
|
||||
uc_engine* uc{};
|
||||
uc_hook intr_hook{};
|
||||
uc_hook mem_invalid_hook{};
|
Loading…
Reference in a new issue