early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

View File

@@ -0,0 +1,301 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <type_traits>
#include "A32/testenv.h"
#include "a32_unicorn.h"
#include "common/assert.h"
#include "common/bit_util.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);
template <class TestEnvironment>
A32Unicorn<TestEnvironment>::A32Unicorn(TestEnvironment& testenv) : testenv{testenv} {
constexpr uc_mode open_mode = std::is_same_v<TestEnvironment, ArmTestEnv> ? 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));
}
template <class TestEnvironment>
A32Unicorn<TestEnvironment>::~A32Unicorn() {
ClearPageCache();
CHECKED(uc_hook_del(uc, intr_hook));
CHECKED(uc_hook_del(uc, mem_invalid_hook));
CHECKED(uc_close(uc));
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::Run() {
// Thumb execution mode requires the LSB to be set to 1.
constexpr u64 pc_mask = std::is_same_v<TestEnvironment, ArmTestEnv> ? 0 : 1;
while (testenv.ticks_left > 0) {
const u32 pc = GetPC() | pc_mask;
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
ASSERT_MSG(false, "uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
}
testenv.ticks_left--;
if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) {
return;
}
}
const bool T = Dynarmic::Common::Bit<5>(GetCpsr());
const u32 new_pc = GetPC() | (T ? 1 : 0);
SetPC(new_pc);
}
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetPC() const {
u32 pc;
CHECKED(uc_reg_read(uc, UC_ARM_REG_PC, &pc));
return pc;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetPC(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM_REG_PC, &value));
}
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetSP() const {
u32 sp;
CHECKED(uc_reg_read(uc, UC_ARM_REG_SP, &sp));
return sp;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetSP(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM_REG_SP, &value));
}
constexpr std::array<int, Unicorn::A32::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,
};
template <class TestEnvironment>
Unicorn::A32::RegisterArray A32Unicorn<TestEnvironment>::GetRegisters() const {
Unicorn::A32::RegisterArray regs{};
Unicorn::A32::RegisterPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i) {
ptrs[i] = &regs[i];
}
CHECKED(uc_reg_read_batch(uc, const_cast<int*>(gpr_ids.data()),
reinterpret_cast<void**>(ptrs.data()), static_cast<int>(Unicorn::A32::num_gprs)));
return regs;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::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<int*>(gpr_ids.data()),
reinterpret_cast<void**>(const_cast<u32**>(ptrs.data())), static_cast<int>(ptrs.size())));
}
using DoubleExtRegPtrArray = std::array<Unicorn::A32::ExtRegArray::pointer, Unicorn::A32::num_ext_regs/2>;
using DoubleExtRegConstPtrArray = std::array<Unicorn::A32::ExtRegArray::const_pointer, Unicorn::A32::num_ext_regs/2>;
constexpr std::array<int, Unicorn::A32::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,
};
template <class TestEnvironment>
Unicorn::A32::ExtRegArray A32Unicorn<TestEnvironment>::GetExtRegs() const {
Unicorn::A32::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()), static_cast<int>(ptrs.size())));
return ext_regs;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::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())), static_cast<int>(ptrs.size())));
}
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetFpscr() const {
u32 fpsr;
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &fpsr));
return fpsr;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetFpscr(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM_REG_FPSCR, &value));
}
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetFpexc() const {
u32 value = 0;
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPEXC, &value));
return value;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetFpexc(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM_REG_FPEXC, &value));
}
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetCpsr() const {
u32 pstate;
CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &pstate));
return pstate;
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetCpsr(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM_REG_CPSR, &value));
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::EnableFloatingPointAccess() {
const u32 new_fpexc = GetFpexc() | (1U << 30);
SetFpexc(new_fpexc);
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::ClearPageCache() {
for (const auto& page : pages) {
CHECKED(uc_mem_unmap(uc, page->address, 4096));
}
pages.clear();
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::DumpMemoryInformation() {
uc_mem_region* regions;
u32 count;
CHECKED(uc_mem_regions(uc, &regions, &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));
}
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::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;
}
}
template <class TestEnvironment>
bool A32Unicorn<TestEnvironment>::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() * sizeof(typename TestEnvironment::InstructionType)) {
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(static_cast<u32>(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;
}
template <class TestEnvironment>
bool A32Unicorn<TestEnvironment>::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;
}
template class A32Unicorn<ArmTestEnv>;
template class A32Unicorn<ThumbTestEnv>;

View File

@@ -0,0 +1,88 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <array>
#include <vector>
#ifdef _MSC_VER
#pragma warning(push, 0)
#include <unicorn/unicorn.h>
#pragma warning(pop)
#else
#include <unicorn/unicorn.h>
#endif
#include "common/common_types.h"
#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<u32, num_ext_regs>;
using RegisterArray = std::array<u32, num_gprs>;
using RegisterPtrArray = std::array<RegisterArray::pointer, num_gprs>;
using RegisterConstPtrArray = std::array<RegisterArray::const_pointer, num_gprs>;
} // namespace Unicorn::A32
template <class TestEnvironment>
class A32Unicorn final {
public:
using ExtRegArray = Unicorn::A32::ExtRegArray;
using RegisterArray = Unicorn::A32::RegisterArray;
explicit A32Unicorn(TestEnvironment& testenv);
~A32Unicorn();
void Run();
u32 GetSP() const;
void SetSP(u32 value);
u32 GetPC() const;
void SetPC(u32 value);
RegisterArray GetRegisters() const;
void SetRegisters(const RegisterArray& value);
ExtRegArray GetExtRegs() const;
void SetExtRegs(const ExtRegArray& value);
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();
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;
};
TestEnvironment& 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;
};

View File

@@ -0,0 +1,252 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "a64_unicorn.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 u64 BEGIN_ADDRESS = 0;
constexpr u64 END_ADDRESS = ~u64(0);
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));
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));
}
A64Unicorn::~A64Unicorn() {
ClearPageCache();
CHECKED(uc_hook_del(uc, intr_hook));
CHECKED(uc_hook_del(uc, mem_invalid_hook));
CHECKED(uc_close(uc));
}
void A64Unicorn::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;
}
}
}
u64 A64Unicorn::GetSP() const {
u64 sp;
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &sp));
return sp;
}
void A64Unicorn::SetSP(u64 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &value));
}
u64 A64Unicorn::GetPC() const {
u64 pc;
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &pc));
return pc;
}
void A64Unicorn::SetPC(u64 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &value));
}
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
};
A64Unicorn::RegisterArray A64Unicorn::GetRegisters() const {
RegisterArray regs{};
RegisterPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
ptrs[i] = &regs[i];
CHECKED(uc_reg_read_batch(uc, const_cast<int*>(gpr_ids.data()),
reinterpret_cast<void**>(ptrs.data()), static_cast<int>(num_gprs)));
return regs;
}
void A64Unicorn::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<u64**>(ptrs.data())), static_cast<int>(num_gprs)));
}
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
};
A64Unicorn::VectorArray A64Unicorn::GetVectors() const {
VectorArray vecs{};
VectorPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
ptrs[i] = &vecs[i];
CHECKED(uc_reg_read_batch(uc, const_cast<int*>(vec_ids.data()),
reinterpret_cast<void**>(ptrs.data()), static_cast<int>(num_vecs)));
return vecs;
}
void A64Unicorn::SetVectors(const VectorArray& value) {
VectorConstPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
ptrs[i] = &value[i];
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(vec_ids.data()),
reinterpret_cast<void* const *>(const_cast<Vector**>(ptrs.data())), static_cast<int>(num_vecs)));
}
u32 A64Unicorn::GetFpcr() const {
u32 fpcr;
CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPCR, &fpcr));
return fpcr;
}
void A64Unicorn::SetFpcr(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPCR, &value));
}
u32 A64Unicorn::GetFpsr() const {
u32 fpsr;
CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPSR, &fpsr));
return fpsr;
}
void A64Unicorn::SetFpsr(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPSR, &value));
}
u32 A64Unicorn::GetPstate() const {
u32 pstate;
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &pstate));
return pstate;
}
void A64Unicorn::SetPstate(u32 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &value));
}
void A64Unicorn::ClearPageCache() {
for (const auto& page : pages) {
CHECKED(uc_mem_unmap(uc, page->address, 4096));
}
pages.clear();
}
void A64Unicorn::DumpMemoryInformation() {
uc_mem_region* regions;
u32 count;
CHECKED(uc_mem_regions(uc, &regions, &count));
for (u32 i = 0; i < count; ++i) {
printf("region: start 0x%016" PRIx64 " end 0x%016" PRIx64 " perms 0x%08x\n", regions[i].begin, regions[i].end, regions[i].perms);
}
CHECKED(uc_free(regions));
}
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));
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 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);
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 = [](u64 addr, u64 start, u64 end) {
if (start <= end)
return addr >= start && addr <= end; // fffff[tttttt]fffff
return addr >= start || addr <= end; // ttttt]ffffff[ttttt
};
const u64 start_address_page = start_address & ~u64(0xFFF);
const u64 end_address = start_address + size - 1;
u64 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 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:
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;
}

View File

@@ -0,0 +1,81 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <array>
#include <vector>
#ifdef _MSC_VER
#pragma warning(push, 0)
#include <unicorn/unicorn.h>
#pragma warning(pop)
#else
#include <unicorn/unicorn.h>
#endif
#include "common/common_types.h"
#include "A64/testenv.h"
class A64Unicorn final {
public:
static constexpr size_t num_gprs = 31;
using RegisterArray = std::array<u64, num_gprs>;
using RegisterPtrArray = std::array<RegisterArray::pointer, num_gprs>;
using RegisterConstPtrArray = std::array<RegisterArray::const_pointer, num_gprs>;
static constexpr size_t num_vecs = 32;
using VectorArray = std::array<Vector, num_vecs>;
using VectorPtrArray = std::array<VectorArray::pointer, num_vecs>;
using VectorConstPtrArray = std::array<VectorArray::const_pointer, num_vecs>;
explicit A64Unicorn(A64TestEnv& testenv);
~A64Unicorn();
void Run();
u64 GetSP() const;
void SetSP(u64 value);
u64 GetPC() const;
void SetPC(u64 value);
RegisterArray GetRegisters() const;
void SetRegisters(const RegisterArray& value);
VectorArray GetVectors() const;
void SetVectors(const VectorArray& value);
u32 GetFpcr() const;
void SetFpcr(u32 value);
u32 GetFpsr() const;
void SetFpsr(u32 value);
u32 GetPstate() const;
void SetPstate(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, u64 addr, int size, u64 value, void* user_data);
static bool MemoryWriteHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, void* user_data);
struct Page {
u64 address;
std::array<u8, 4096> data;
};
A64TestEnv& 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;
};