a64_unicorn.cpp
1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2018 MerryMage 3 * SPDX-License-Identifier: 0BSD 4 */ 5 6 #include "./a64_unicorn.h" 7 8 #include <mcl/assert.hpp> 9 10 #define CHECKED(expr) \ 11 do { \ 12 if (auto cerr_ = (expr)) { \ 13 ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", static_cast<size_t>(cerr_), \ 14 uc_strerror(cerr_)); \ 15 } \ 16 } while (0) 17 18 constexpr u64 BEGIN_ADDRESS = 0; 19 constexpr u64 END_ADDRESS = ~u64(0); 20 21 A64Unicorn::A64Unicorn(A64TestEnv& testenv) 22 : testenv(testenv) { 23 CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); 24 u64 fpv = 3 << 20; 25 CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv)); 26 CHECKED(uc_hook_add(uc, &intr_hook, UC_HOOK_INTR, (void*)InterruptHook, this, BEGIN_ADDRESS, END_ADDRESS)); 27 CHECKED(uc_hook_add(uc, &mem_invalid_hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, BEGIN_ADDRESS, END_ADDRESS)); 28 CHECKED(uc_hook_add(uc, &mem_write_prot_hook, UC_HOOK_MEM_WRITE, (void*)MemoryWriteHook, this, BEGIN_ADDRESS, END_ADDRESS)); 29 } 30 31 A64Unicorn::~A64Unicorn() { 32 ClearPageCache(); 33 CHECKED(uc_hook_del(uc, intr_hook)); 34 CHECKED(uc_hook_del(uc, mem_invalid_hook)); 35 CHECKED(uc_close(uc)); 36 } 37 38 void A64Unicorn::Run() { 39 while (testenv.ticks_left > 0) { 40 CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1)); 41 testenv.ticks_left--; 42 if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) { 43 return; 44 } 45 } 46 } 47 48 u64 A64Unicorn::GetSP() const { 49 u64 sp; 50 CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &sp)); 51 return sp; 52 } 53 void A64Unicorn::SetSP(u64 value) { 54 CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &value)); 55 } 56 57 u64 A64Unicorn::GetPC() const { 58 u64 pc; 59 CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &pc)); 60 return pc; 61 } 62 63 void A64Unicorn::SetPC(u64 value) { 64 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &value)); 65 } 66 67 constexpr std::array<int, A64Unicorn::num_gprs> gpr_ids{ 68 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, 69 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, 70 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, 71 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}; 72 73 A64Unicorn::RegisterArray A64Unicorn::GetRegisters() const { 74 RegisterArray regs{}; 75 RegisterPtrArray ptrs; 76 for (size_t i = 0; i < ptrs.size(); ++i) 77 ptrs[i] = ®s[i]; 78 79 CHECKED(uc_reg_read_batch(uc, const_cast<int*>(gpr_ids.data()), 80 reinterpret_cast<void**>(ptrs.data()), static_cast<int>(num_gprs))); 81 return regs; 82 } 83 84 void A64Unicorn::SetRegisters(const RegisterArray& value) { 85 RegisterConstPtrArray ptrs; 86 for (size_t i = 0; i < ptrs.size(); ++i) 87 ptrs[i] = &value[i]; 88 89 CHECKED(uc_reg_write_batch(uc, const_cast<int*>(gpr_ids.data()), 90 reinterpret_cast<void**>(const_cast<u64**>(ptrs.data())), static_cast<int>(num_gprs))); 91 } 92 93 constexpr std::array<int, A64Unicorn::num_vecs> vec_ids{ 94 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, 95 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, 96 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, 97 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}; 98 99 A64Unicorn::VectorArray A64Unicorn::GetVectors() const { 100 VectorArray vecs{}; 101 VectorPtrArray ptrs; 102 for (size_t i = 0; i < ptrs.size(); ++i) 103 ptrs[i] = &vecs[i]; 104 105 CHECKED(uc_reg_read_batch(uc, const_cast<int*>(vec_ids.data()), 106 reinterpret_cast<void**>(ptrs.data()), static_cast<int>(num_vecs))); 107 108 return vecs; 109 } 110 111 void A64Unicorn::SetVectors(const VectorArray& value) { 112 VectorConstPtrArray ptrs; 113 for (size_t i = 0; i < ptrs.size(); ++i) 114 ptrs[i] = &value[i]; 115 116 CHECKED(uc_reg_write_batch(uc, const_cast<int*>(vec_ids.data()), 117 reinterpret_cast<void* const*>(const_cast<Vector**>(ptrs.data())), static_cast<int>(num_vecs))); 118 } 119 120 u32 A64Unicorn::GetFpcr() const { 121 u32 fpcr; 122 CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPCR, &fpcr)); 123 return fpcr; 124 } 125 126 void A64Unicorn::SetFpcr(u32 value) { 127 CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPCR, &value)); 128 } 129 130 u32 A64Unicorn::GetFpsr() const { 131 u32 fpsr; 132 CHECKED(uc_reg_read(uc, UC_ARM64_REG_FPSR, &fpsr)); 133 return fpsr; 134 } 135 136 void A64Unicorn::SetFpsr(u32 value) { 137 CHECKED(uc_reg_write(uc, UC_ARM64_REG_FPSR, &value)); 138 } 139 140 u32 A64Unicorn::GetPstate() const { 141 u32 pstate; 142 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &pstate)); 143 return pstate; 144 } 145 146 void A64Unicorn::SetPstate(u32 value) { 147 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &value)); 148 } 149 150 void A64Unicorn::ClearPageCache() { 151 for (const auto& page : pages) { 152 CHECKED(uc_mem_unmap(uc, page->address, 4096)); 153 } 154 pages.clear(); 155 } 156 157 void A64Unicorn::DumpMemoryInformation() { 158 uc_mem_region* regions; 159 u32 count; 160 CHECKED(uc_mem_regions(uc, ®ions, &count)); 161 162 for (u32 i = 0; i < count; ++i) { 163 printf("region: start 0x%016" PRIx64 " end 0x%016" PRIx64 " perms 0x%08x\n", regions[i].begin, regions[i].end, regions[i].perms); 164 } 165 166 CHECKED(uc_free(regions)); 167 } 168 169 void A64Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) { 170 auto* this_ = static_cast<A64Unicorn*>(user_data); 171 172 u32 esr; 173 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); 174 175 auto ec = esr >> 26; 176 auto iss = esr & 0xFFFFFF; 177 178 switch (ec) { 179 case 0x15: // SVC 180 this_->testenv.CallSVC(iss); 181 break; 182 default: 183 this_->testenv.interrupts.emplace_back(fmt::format("Unhandled interrupt: int_number: {:#x}, esr: {:#x} (ec: {:#x}, iss: {:#x})", int_number, esr, ec, iss)); 184 break; 185 } 186 } 187 188 bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 start_address, int size, u64 /*value*/, void* user_data) { 189 auto* this_ = static_cast<A64Unicorn*>(user_data); 190 191 const auto generate_page = [&](u64 base_address) { 192 // printf("generate_page(%" PRIx64 ")\n", base_address); 193 194 const u32 permissions = [&]() -> u32 { 195 if (base_address < this_->testenv.code_mem.size() * 4) 196 return UC_PROT_READ | UC_PROT_EXEC; 197 return UC_PROT_READ; 198 }(); 199 200 auto page = std::make_unique<Page>(); 201 page->address = base_address; 202 for (size_t i = 0; i < page->data.size(); ++i) 203 page->data[i] = this_->testenv.MemoryRead8(base_address + i); 204 205 uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); 206 if (err == UC_ERR_MAP) 207 return; // page already exists 208 CHECKED(err); 209 210 this_->pages.emplace_back(std::move(page)); 211 }; 212 213 const auto is_in_range = [](u64 addr, u64 start, u64 end) { 214 if (start <= end) 215 return addr >= start && addr <= end; // fffff[tttttt]fffff 216 return addr >= start || addr <= end; // ttttt]ffffff[ttttt 217 }; 218 219 const u64 start_address_page = start_address & ~u64(0xFFF); 220 const u64 end_address = start_address + size - 1; 221 222 u64 current_address = start_address_page; 223 do { 224 generate_page(current_address); 225 current_address += 0x1000; 226 } while (is_in_range(current_address, start_address_page, end_address) && current_address != start_address_page); 227 228 return true; 229 } 230 231 bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { 232 auto* this_ = static_cast<A64Unicorn*>(user_data); 233 234 switch (size) { 235 case 1: 236 this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value)); 237 break; 238 case 2: 239 this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value)); 240 break; 241 case 4: 242 this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value)); 243 break; 244 case 8: 245 this_->testenv.MemoryWrite64(start_address, value); 246 break; 247 default: 248 UNREACHABLE(); 249 } 250 251 return true; 252 }