1 /* This file is part of the dynarmic project.
2 * Copyright (c) 2016 MerryMage
3 * SPDX-License-Identifier: 0BSD
4 */
5
6 #include <algorithm>
7 #include <array>
8 #include <cinttypes>
9 #include <cstdio>
10 #include <cstring>
11 #include <functional>
12 #include <tuple>
13
14 #include <catch.hpp>
15
16 #include <dynarmic/A32/a32.h>
17
18 #include "common/bit_util.h"
19 #include "common/common_types.h"
20 #include "frontend/A32/disassembler/disassembler.h"
21 #include "frontend/A32/FPSCR.h"
22 #include "frontend/A32/location_descriptor.h"
23 #include "frontend/A32/PSR.h"
24 #include "frontend/A32/translate/translate.h"
25 #include "frontend/ir/basic_block.h"
26 #include "ir_opt/passes.h"
27 #include "rand_int.h"
28 #include "testenv.h"
29 #include "unicorn_emu/a32_unicorn.h"
30
31 using namespace Dynarmic;
32
GetUserConfig(ThumbTestEnv * testenv)33 static A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
34 A32::UserConfig user_config;
35 user_config.optimizations &= ~OptimizationFlag::FastDispatch;
36 user_config.callbacks = testenv;
37 return user_config;
38 }
39
40 using WriteRecords = std::map<u32, u8>;
41
42 struct ThumbInstGen final {
43 public:
ThumbInstGenThumbInstGen44 ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
45 REQUIRE(strlen(format) == 16);
46
47 for (int i = 0; i < 16; i++) {
48 const u16 bit = 1 << (15 - i);
49 switch (format[i]) {
50 case '0':
51 mask |= bit;
52 break;
53 case '1':
54 bits |= bit;
55 mask |= bit;
56 break;
57 default:
58 // Do nothing
59 break;
60 }
61 }
62 }
GenerateThumbInstGen63 u16 Generate() const {
64 u16 inst;
65
66 do {
67 const u16 random = RandInt<u16>(0, 0xFFFF);
68 inst = bits | (random & ~mask);
69 } while (!is_valid(inst));
70
71 ASSERT((inst & mask) == bits);
72
73 return inst;
74 }
75 private:
76 u16 bits = 0;
77 u16 mask = 0;
78 std::function<bool(u16)> is_valid;
79 };
80
DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv> & uni,const A32::Jit & jit,const WriteRecords & interp_write_records,const WriteRecords & jit_write_records)81 static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Jit& jit,
82 const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
83 const auto interp_regs = uni.GetRegisters();
84 const auto jit_regs = jit.Regs();
85
86 return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) &&
87 uni.GetCpsr() == jit.Cpsr() &&
88 interp_write_records == jit_write_records;
89 }
90
RunInstance(size_t run_number,ThumbTestEnv & test_env,A32Unicorn<ThumbTestEnv> & uni,A32::Jit & jit,const ThumbTestEnv::RegisterArray & initial_regs,size_t instruction_count,size_t instructions_to_execute_count)91 static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs,
92 size_t instruction_count, size_t instructions_to_execute_count) {
93 uni.ClearPageCache();
94 jit.ClearCache();
95
96 // Setup initial state
97
98 uni.SetCpsr(0x000001F0);
99 uni.SetRegisters(initial_regs);
100 jit.SetCpsr(0x000001F0);
101 jit.Regs() = initial_regs;
102
103 // Run interpreter
104 test_env.modified_memory.clear();
105 test_env.ticks_left = instructions_to_execute_count;
106 uni.SetPC(uni.GetPC() | 1);
107 uni.Run();
108 const bool uni_code_memory_modified = test_env.code_mem_modified_by_guest;
109 const auto interp_write_records = test_env.modified_memory;
110
111 // Run jit
112 test_env.code_mem_modified_by_guest = false;
113 test_env.modified_memory.clear();
114 test_env.ticks_left = instructions_to_execute_count;
115 jit.Run();
116 const bool jit_code_memory_modified = test_env.code_mem_modified_by_guest;
117 const auto jit_write_records = test_env.modified_memory;
118 test_env.code_mem_modified_by_guest = false;
119
120 REQUIRE(uni_code_memory_modified == jit_code_memory_modified);
121 if (uni_code_memory_modified) {
122 return;
123 }
124
125 // Compare
126 if (!DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)) {
127 printf("Failed at execution number %zu\n", run_number);
128
129 printf("\nInstruction Listing: \n");
130 for (size_t i = 0; i < instruction_count; i++) {
131 printf("%04x %s\n", test_env.code_mem[i], A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
132 }
133
134 printf("\nInitial Register Listing: \n");
135 for (size_t i = 0; i < initial_regs.size(); i++) {
136 printf("%4zu: %08x\n", i, initial_regs[i]);
137 }
138
139 printf("\nFinal Register Listing: \n");
140 printf(" unicorn jit\n");
141 const auto uni_registers = uni.GetRegisters();
142 for (size_t i = 0; i < uni_registers.size(); i++) {
143 printf("%4zu: %08x %08x %s\n", i, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : "");
144 }
145 printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
146
147 printf("\nUnicorn Write Records:\n");
148 for (const auto& record : interp_write_records) {
149 printf("[%08x] = %02x\n", record.first, record.second);
150 }
151
152 printf("\nJIT Write Records:\n");
153 for (const auto& record : jit_write_records) {
154 printf("[%08x] = %02x\n", record.first, record.second);
155 }
156
157 A32::PSR cpsr;
158 cpsr.T(true);
159
160 size_t num_insts = 0;
161 while (num_insts < instructions_to_execute_count) {
162 A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
163 IR::Block ir_block = A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); }, {});
164 Optimization::A32GetSetElimination(ir_block);
165 Optimization::DeadCodeElimination(ir_block);
166 Optimization::A32ConstantMemoryReads(ir_block, &test_env);
167 Optimization::ConstantPropagation(ir_block);
168 Optimization::DeadCodeElimination(ir_block);
169 Optimization::VerificationPass(ir_block);
170 printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
171 printf("\n\nx86_64:\n%s", jit.Disassemble().c_str());
172 num_insts += ir_block.CycleCount();
173 }
174
175 #ifdef _MSC_VER
176 __debugbreak();
177 #endif
178 FAIL();
179 }
180 }
181
FuzzJitThumb(const size_t instruction_count,const size_t instructions_to_execute_count,const size_t run_count,const std::function<u16 ()> instruction_generator)182 void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
183 ThumbTestEnv test_env;
184
185 // Prepare memory.
186 test_env.code_mem.resize(instruction_count + 1);
187 test_env.code_mem.back() = 0xE7FE; // b +#0
188
189 // Prepare test subjects
190 A32Unicorn uni{test_env};
191 A32::Jit jit{GetUserConfig(&test_env)};
192
193 for (size_t run_number = 0; run_number < run_count; run_number++) {
194 ThumbTestEnv::RegisterArray initial_regs;
195 std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
196 initial_regs[15] = 0;
197
198 std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
199
200 RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
201 }
202 }
203
204 TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
205 const std::array instructions = {
206 ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
207 ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
208 ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
209 ThumbInstGen("000110oxxxxxxxxx"), // ADD/SUB_reg
210 ThumbInstGen("000111oxxxxxxxxx"), // ADD/SUB_imm
211 ThumbInstGen("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
212 ThumbInstGen("010000ooooxxxxxx"), // Data Processing
213 ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
214 ThumbInstGen("0100010101xxxxxx", // CMP (high registers)
__anonae0dd4400502()215 [](u16 inst){ return Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
216 ThumbInstGen("0100010110xxxxxx", // CMP (high registers)
__anonae0dd4400602()217 [](u16 inst){ return Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
218 ThumbInstGen("010001100hxxxxxx"), // MOV (high registers)
219 ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
220 ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
221 ThumbInstGen("1011101000xxxxxx"), // REV
222 ThumbInstGen("1011101001xxxxxx"), // REV16
223 ThumbInstGen("1011101011xxxxxx"), // REVSH
224 ThumbInstGen("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
225 ThumbInstGen("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
226 ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
227 ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
228 ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
229 ThumbInstGen("1011010xxxxxxxxx", // PUSH
__anonae0dd4400702()230 [](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
231 ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
__anonae0dd4400802()232 [](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
233 ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA
__anonae0dd4400902() 234 [](u16 inst) {
235 // Ensure that the architecturally undefined case of
236 // the base register being within the list isn't hit.
237 const u32 rn = Common::Bits<8, 10>(inst);
238 return (inst & (1U << rn)) == 0 && Common::Bits<0, 7>(inst) != 0;
239 }),
240 // TODO: We should properly test against swapped
241 // endianness cases, however Unicorn doesn't
242 // expose the intended endianness of a load/store
243 // operation to memory through its hooks.
244 #if 0
245 ThumbInstGen("101101100101x000"), // SETEND
246 #endif
247 };
248
__anonae0dd4400a02() 249 const auto instruction_select = [&]() -> u16 {
250 size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
251
252 return instructions[inst_index].Generate();
253 };
254
255 SECTION("single instructions") {
256 FuzzJitThumb(1, 2, 10000, instruction_select);
257 }
258
259 SECTION("short blocks") {
260 FuzzJitThumb(5, 6, 3000, instruction_select);
261 }
262
263 // TODO: Test longer blocks when Unicorn can consistently
264 // run these without going into an infinite loop.
265 #if 0
266 SECTION("long blocks") {
267 FuzzJitThumb(1024, 1025, 1000, instruction_select);
268 }
269 #endif
270 }
271
272 TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
273 const std::array instructions = {
274 // TODO: We currently can't test BX/BLX as we have
275 // no way of preventing the unpredictable
276 // condition from occurring with the current interface.
277 // (bits zero and one within the specified register
278 // must not be address<1:0> == '10'.
279 #if 0
280 ThumbInstGen("01000111xmmmm000", // BLX/BX
281 [](u16 inst){
282 const u32 Rm = Common::Bits<3, 6>(inst);
283 return Rm != 15;
284 }),
285 #endif
286 ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
287 ThumbInstGen("11100xxxxxxxxxxx"), // B
288 ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
289 ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
290 ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
__anonae0dd4400b02()291 [](u16 inst){
292 const u32 c = Common::Bits<9, 12>(inst);
293 return c < 0b1110; // Don't want SWI or undefined instructions.
294 }),
295 ThumbInstGen("1011o0i1iiiiinnn"), // CBZ/CBNZ
296 ThumbInstGen("10110110011x0xxx"), // CPS
297
298 // TODO: We currently have no control over the generated
299 // values when creating new pages, so we can't
300 // reliably test this yet.
301 #if 0
302 ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1)
303 #endif
304 };
305
__anonae0dd4400c02() 306 const auto instruction_select = [&]() -> u16 {
307 size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
308
309 return instructions[inst_index].Generate();
310 };
311
312 FuzzJitThumb(1, 1, 10000, instruction_select);
313 }
314
315 TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
316 ThumbTestEnv test_env;
317
318 // Prepare test subjects
319 A32Unicorn<ThumbTestEnv> uni{test_env};
320 A32::Jit jit{GetUserConfig(&test_env)};
321
322 constexpr ThumbTestEnv::RegisterArray initial_regs {
323 0xe90ecd70,
324 0x3e3b73c3,
325 0x571616f9,
326 0x0b1ef45a,
327 0xb3a829f2,
328 0x915a7a6a,
329 0x579c38f4,
330 0xd9ffe391,
331 0x55b6682b,
332 0x458d8f37,
333 0x8f3eb3dc,
334 0xe18c0e7d,
335 0x6752657a,
336 0x00001766,
337 0xdbbf23e3,
338 0x00000000,
339 };
340
341 test_env.code_mem = {
342 0x40B8, // lsls r0, r7, #0
343 0x01CA, // lsls r2, r1, #7
344 0x83A1, // strh r1, [r4, #28]
345 0x708A, // strb r2, [r1, #2]
346 0xBCC4, // pop {r2, r6, r7}
347 0xE7FE, // b +#0
348 };
349
350 RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
351 }
352