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 <cstdio>
9 #include <functional>
10 #include <tuple>
11 #include <vector>
12 
13 #include <catch.hpp>
14 #include <dynarmic/A32/a32.h>
15 
16 #include "common/common_types.h"
17 #include "common/fp/fpcr.h"
18 #include "common/fp/fpsr.h"
19 #include "common/scope_exit.h"
20 #include "frontend/A32/disassembler/disassembler.h"
21 #include "frontend/A32/location_descriptor.h"
22 #include "frontend/A32/translate/translate.h"
23 #include "frontend/A32/types.h"
24 #include "frontend/ir/basic_block.h"
25 #include "frontend/ir/location_descriptor.h"
26 #include "frontend/ir/opcodes.h"
27 #include "fuzz_util.h"
28 #include "rand_int.h"
29 #include "testenv.h"
30 #include "unicorn_emu/a32_unicorn.h"
31 
32 // Must be declared last for all necessary operator<< to be declared prior to this.
33 #include <fmt/format.h>
34 #include <fmt/ostream.h>
35 
36 namespace {
37 using namespace Dynarmic;
38 
ShouldTestInst(u32 instruction,u32 pc,bool is_last_inst)39 bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
40     const A32::LocationDescriptor location{pc, {}, {}};
41     IR::Block block{location};
42     const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
43 
44     if (!should_continue && !is_last_inst) {
45         return false;
46     }
47 
48     if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal)) {
49         return false;
50     }
51 
52     for (const auto& ir_inst : block) {
53         switch (ir_inst.GetOpcode()) {
54         case IR::Opcode::A32ExceptionRaised:
55         case IR::Opcode::A32CallSupervisor:
56         case IR::Opcode::A32CoprocInternalOperation:
57         case IR::Opcode::A32CoprocSendOneWord:
58         case IR::Opcode::A32CoprocSendTwoWords:
59         case IR::Opcode::A32CoprocGetOneWord:
60         case IR::Opcode::A32CoprocGetTwoWords:
61         case IR::Opcode::A32CoprocLoadWords:
62         case IR::Opcode::A32CoprocStoreWords:
63             return false;
64         // Currently unimplemented in Unicorn
65         case IR::Opcode::FPVectorRecipEstimate16:
66         case IR::Opcode::FPVectorRSqrtEstimate16:
67         case IR::Opcode::VectorPolynomialMultiplyLong64:
68             return false;
69         default:
70             continue;
71         }
72     }
73 
74     return true;
75 }
76 
GenRandomInst(u32 pc,bool is_last_inst)77 u32 GenRandomInst(u32 pc, bool is_last_inst) {
78     static const struct InstructionGeneratorInfo {
79         std::vector<InstructionGenerator> generators;
80         std::vector<InstructionGenerator> invalid;
81     } instructions = []{
82         const std::vector<std::tuple<std::string, const char*>> list {
83 #define INST(fn, name, bitstring) {#fn, bitstring},
84 #include "frontend/A32/decoder/arm.inc"
85 #include "frontend/A32/decoder/asimd.inc"
86 #include "frontend/A32/decoder/vfp.inc"
87 #undef INST
88         };
89 
90         std::vector<InstructionGenerator> generators;
91         std::vector<InstructionGenerator> invalid;
92 
93         // List of instructions not to test
94         static constexpr std::array do_not_test {
95             // Translating load/stores
96             "arm_LDRBT", "arm_LDRBT", "arm_LDRHT", "arm_LDRHT", "arm_LDRSBT", "arm_LDRSBT", "arm_LDRSHT", "arm_LDRSHT", "arm_LDRT", "arm_LDRT",
97             "arm_STRBT", "arm_STRBT", "arm_STRHT", "arm_STRHT", "arm_STRT", "arm_STRT",
98             // Exclusive load/stores
99             "arm_LDREXB", "arm_LDREXD", "arm_LDREXH", "arm_LDREX", "arm_LDAEXB", "arm_LDAEXD", "arm_LDAEXH", "arm_LDAEX",
100             "arm_STREXB", "arm_STREXD", "arm_STREXH", "arm_STREX", "arm_STLEXB", "arm_STLEXD", "arm_STLEXH", "arm_STLEX",
101             "arm_SWP", "arm_SWPB",
102             // Elevated load/store multiple instructions.
103             "arm_LDM_eret", "arm_LDM_usr",
104             "arm_STM_usr",
105             // Hint instructions
106             "arm_NOP", "arm_PLD_imm", "arm_PLD_reg", "arm_SEV",
107             "arm_WFE", "arm_WFI", "arm_YIELD",
108             // E, T, J
109             "arm_BLX_reg", "arm_BLX_imm", "arm_BXJ", "arm_SETEND",
110             // Coprocessor
111             "arm_CDP", "arm_LDC", "arm_MCR", "arm_MCRR", "arm_MRC", "arm_MRRC", "arm_STC",
112             // System
113             "arm_CPS", "arm_RFE", "arm_SRS",
114             // Undefined
115             "arm_UDF",
116             // FPSCR is inaccurate
117             "vfp_VMRS",
118             // Incorrect Unicorn implementations
119             "asimd_VRECPS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP.
120             "asimd_VRSQRTS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP.
121             "vfp_VCVT_from_fixed", // Unicorn does not do round-to-nearest-even for this instruction correctly.
122         };
123 
124         for (const auto& [fn, bitstring] : list) {
125             if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
126                 invalid.emplace_back(InstructionGenerator{bitstring});
127                 continue;
128             }
129             generators.emplace_back(InstructionGenerator{bitstring});
130         }
131         return InstructionGeneratorInfo{generators, invalid};
132     }();
133 
134     while (true) {
135         const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
136         const u32 inst = instructions.generators[index].Generate();
137 
138         if ((instructions.generators[index].Mask() & 0xF0000000) == 0 && (inst & 0xF0000000) == 0xF0000000) {
139             continue;
140         }
141 
142         if (ShouldTestInst(inst, pc, is_last_inst)) {
143             return inst;
144         }
145     }
146 }
147 
GetUserConfig(ArmTestEnv & testenv)148 Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
149     Dynarmic::A32::UserConfig user_config;
150     user_config.optimizations &= ~OptimizationFlag::FastDispatch;
151     user_config.callbacks = &testenv;
152     user_config.always_little_endian = true;
153     return user_config;
154 }
155 
RunTestInstance(Dynarmic::A32::Jit & jit,A32Unicorn<ArmTestEnv> & uni,ArmTestEnv & jit_env,ArmTestEnv & uni_env,const A32Unicorn<ArmTestEnv>::RegisterArray & regs,const A32Unicorn<ArmTestEnv>::ExtRegArray & vecs,const std::vector<u32> & instructions,const u32 cpsr,const u32 fpscr)156 static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni,
157                             ArmTestEnv& jit_env, ArmTestEnv& uni_env,
158                             const A32Unicorn<ArmTestEnv>::RegisterArray& regs,
159                             const A32Unicorn<ArmTestEnv>::ExtRegArray& vecs,
160                             const std::vector<u32>& instructions, const u32 cpsr, const u32 fpscr) {
161     const u32 initial_pc = regs[15];
162     const u32 num_words = initial_pc / sizeof(u32);
163     const u32 code_mem_size = num_words + static_cast<u32>(instructions.size());
164 
165     jit_env.code_mem.resize(code_mem_size + 1);
166     uni_env.code_mem.resize(code_mem_size + 1);
167 
168     std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words);
169     std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words);
170     jit_env.code_mem.back() = 0xEAFFFFFE; // B .
171     uni_env.code_mem.back() = 0xEAFFFFFE; // B .
172     jit_env.modified_memory.clear();
173     uni_env.modified_memory.clear();
174     jit_env.interrupts.clear();
175     uni_env.interrupts.clear();
176 
177     jit.Regs() = regs;
178     jit.ExtRegs() = vecs;
179     jit.SetFpscr(fpscr);
180     jit.SetCpsr(cpsr);
181     jit.ClearCache();
182     uni.SetRegisters(regs);
183     uni.SetExtRegs(vecs);
184     uni.SetFpscr(fpscr);
185     uni.EnableFloatingPointAccess();
186     uni.SetCpsr(cpsr);
187     uni.ClearPageCache();
188 
189     jit_env.ticks_left = instructions.size();
190     jit.Run();
191 
192     uni_env.ticks_left = instructions.size();
193     uni.Run();
194 
195     SCOPE_FAIL {
196         fmt::print("Instruction Listing:\n");
197         for (u32 instruction : instructions) {
198             fmt::print("{:08x} {}\n", instruction, A32::DisassembleArm(instruction));
199         }
200         fmt::print("\n");
201 
202         fmt::print("Initial register listing:\n");
203         for (size_t i = 0; i < regs.size(); ++i) {
204             fmt::print("{:3s}: {:08x}\n", static_cast<A32::Reg>(i), regs[i]);
205         }
206         for (size_t i = 0; i < vecs.size(); ++i) {
207             fmt::print("{:3s}: {:08x}\n", static_cast<A32::ExtReg>(i), vecs[i]);
208         }
209         fmt::print("cpsr {:08x}\n", cpsr);
210         fmt::print("fpcr {:08x}\n", fpscr);
211         fmt::print("fpcr.AHP   {}\n", FP::FPCR{fpscr}.AHP());
212         fmt::print("fpcr.DN    {}\n", FP::FPCR{fpscr}.DN());
213         fmt::print("fpcr.FZ    {}\n", FP::FPCR{fpscr}.FZ());
214         fmt::print("fpcr.RMode {}\n", static_cast<size_t>(FP::FPCR{fpscr}.RMode()));
215         fmt::print("fpcr.FZ16  {}\n", FP::FPCR{fpscr}.FZ16());
216         fmt::print("\n");
217 
218         fmt::print("Final register listing:\n");
219         fmt::print("     unicorn  dynarmic\n");
220         const auto uni_regs = uni.GetRegisters();
221         for (size_t i = 0; i < regs.size(); ++i) {
222             fmt::print("{:3s}: {:08x} {:08x} {}\n", static_cast<A32::Reg>(i), uni_regs[i], jit.Regs()[i], uni_regs[i] != jit.Regs()[i] ? "*" : "");
223         }
224         const auto uni_ext_regs = uni.GetExtRegs();
225         for (size_t i = 0; i < vecs.size(); ++i) {
226             fmt::print("s{:2d}: {:08x} {:08x} {}\n", static_cast<size_t>(i), uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : "");
227         }
228         fmt::print("cpsr {:08x} {:08x} {}\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
229         fmt::print("fpsr {:08x} {:08x} {}\n", uni.GetFpscr(), jit.Fpscr(), (uni.GetFpscr() & 0xF0000000) != (jit.Fpscr() & 0xF0000000) ? "*" : "");
230         fmt::print("\n");
231 
232         fmt::print("Modified memory:\n");
233         fmt::print("                 uni dyn\n");
234         auto uni_iter = uni_env.modified_memory.begin();
235         auto jit_iter = jit_env.modified_memory.begin();
236         while (uni_iter != uni_env.modified_memory.end() || jit_iter != jit_env.modified_memory.end()) {
237             if (uni_iter == uni_env.modified_memory.end() || (jit_iter != jit_env.modified_memory.end() && uni_iter->first > jit_iter->first)) {
238                 fmt::print("{:08x}:    {:02x} *\n", jit_iter->first, jit_iter->second);
239                 jit_iter++;
240             } else if (jit_iter == jit_env.modified_memory.end() || jit_iter->first > uni_iter->first) {
241                 fmt::print("{:08x}: {:02x}    *\n", uni_iter->first, uni_iter->second);
242                 uni_iter++;
243             } else if (uni_iter->first == jit_iter->first) {
244                 fmt::print("{:08x}: {:02x} {:02x} {}\n", uni_iter->first, uni_iter->second, jit_iter->second, uni_iter->second != jit_iter->second ? "*" : "");
245                 uni_iter++;
246                 jit_iter++;
247             }
248         }
249         fmt::print("\n");
250 
251         fmt::print("x86_64:\n");
252         fmt::print("{}\n", jit.Disassemble());
253 
254         fmt::print("Interrupts:\n");
255         for (const auto& i : uni_env.interrupts) {
256             std::puts(i.c_str());
257         }
258     };
259 
260     REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest);
261     if (uni_env.code_mem_modified_by_guest) {
262         return;
263     }
264 
265     // Qemu doesn't do Thumb transitions??
266     {
267         const u32 uni_pc = uni.GetPC();
268         const bool is_thumb = (jit.Cpsr() & (1 << 5)) != 0;
269         const u32 new_uni_pc = uni_pc & (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC);
270         uni.SetPC(new_uni_pc);
271     }
272 
273     REQUIRE(uni.GetRegisters() == jit.Regs());
274     REQUIRE(uni.GetExtRegs() == jit.ExtRegs());
275     REQUIRE((uni.GetCpsr() & 0xFFFFFDDF) == (jit.Cpsr() & 0xFFFFFDDF));
276     REQUIRE((uni.GetFpscr() & 0xF0000000) == (jit.Fpscr() & 0xF0000000));
277     REQUIRE(uni_env.modified_memory == jit_env.modified_memory);
278     REQUIRE(uni_env.interrupts.empty());
279 }
280 } // Anonymous namespace
281 
282 TEST_CASE("A32: Single random instruction", "[arm]") {
283     ArmTestEnv jit_env{};
284     ArmTestEnv uni_env{};
285 
286     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
287     A32Unicorn<ArmTestEnv> uni{uni_env};
288 
289     A32Unicorn<ArmTestEnv>::RegisterArray regs;
290     A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg;
291     std::vector<u32> instructions(1);
292 
293     for (size_t iteration = 0; iteration < 100000; ++iteration) {
__anond32abe400302null294         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
__anond32abe400402null295         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
296 
297         instructions[0] = GenRandomInst(0, true);
298 
299         const u32 start_address = 100;
300         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
301         const u32 fpcr = RandomFpcr();
302 
303         INFO("Instruction: 0x" << std::hex << instructions[0]);
304 
305         regs[15] = start_address;
306         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
307     }
308 }
309 
310 TEST_CASE("A32: Small random block", "[arm]") {
311     ArmTestEnv jit_env{};
312     ArmTestEnv uni_env{};
313 
314     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
315     A32Unicorn<ArmTestEnv> uni{uni_env};
316 
317     A32Unicorn<ArmTestEnv>::RegisterArray regs;
318     A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg;
319     std::vector<u32> instructions(5);
320 
321     for (size_t iteration = 0; iteration < 100000; ++iteration) {
__anond32abe400502null322         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
__anond32abe400602null323         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
324 
325         instructions[0] = GenRandomInst(0, false);
326         instructions[1] = GenRandomInst(4, false);
327         instructions[2] = GenRandomInst(8, false);
328         instructions[3] = GenRandomInst(12, false);
329         instructions[4] = GenRandomInst(16, true);
330 
331         const u32 start_address = 100;
332         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
333         const u32 fpcr = RandomFpcr();
334 
335         INFO("Instruction 1: 0x" << std::hex << instructions[0]);
336         INFO("Instruction 2: 0x" << std::hex << instructions[1]);
337         INFO("Instruction 3: 0x" << std::hex << instructions[2]);
338         INFO("Instruction 4: 0x" << std::hex << instructions[3]);
339         INFO("Instruction 5: 0x" << std::hex << instructions[4]);
340 
341         regs[15] = start_address;
342         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
343     }
344 }
345 
346 TEST_CASE("A32: Large random block", "[arm]") {
347     ArmTestEnv jit_env{};
348     ArmTestEnv uni_env{};
349 
350     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
351     A32Unicorn<ArmTestEnv> uni{uni_env};
352 
353     A32Unicorn<ArmTestEnv>::RegisterArray regs;
354     A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg;
355 
356     constexpr size_t instruction_count = 100;
357     std::vector<u32> instructions(instruction_count);
358 
359     for (size_t iteration = 0; iteration < 10000; ++iteration) {
__anond32abe400702null360         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
__anond32abe400802null361         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
362 
363         for (size_t j = 0; j < instruction_count; ++j) {
364             instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1);
365         }
366 
367         const u64 start_address = 100;
368         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
369         const u32 fpcr = RandomFpcr();
370 
371         regs[15] = start_address;
372         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
373     }
374 }
375