ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_insn_seams.c
Go to the documentation of this file.
1
19
20#include <capstone/capstone.h>
21#include <stdio.h>
22
23#include "emu_engine.h"
24#include "emu_exc.h"
26#include "emu_seams.h"
27
29typedef enum : uint32_t {
31 k_cs_op_mask = 0x3U,
33
34/* Armv8.1-M conditional-select family (CSEL/CSINC/CSINV/CSNEG). The RA8D2 is
35 * Cortex-M85 (Armv8.1-M) but Unicorn's nearest core is M33 (Armv8-M), which
36 * lacks these. GCC emits them for branchless index/modulo math -- notably in
37 * the firmware's event_post on the touch path -- so the invalid-instruction
38 * hook decodes and executes them and lets the real firmware path continue.
39 * Encoding (T1, two halfwords, little-endian): hw1 = 0xEA5n (n = Rn);
40 * hw2 bit15=1, bit14=0, op=bits[13:12], Rd=bits[11:8], cond=bits[7:4],
41 * Rm=bits[3:0]. Verified against the GNU assembler for armv8.1-m.main. */
42typedef enum : uint32_t {
43 k_cs_hw1_mask = 0xFFF0U,
44 k_cs_hw1_match = 0xEA50U,
45 k_cs_hw2_b15 = 0x8000U,
46 k_cs_hw2_b14 = 0x4000U,
55
56/* APSR condition-flag bit positions inside xPSR. */
57typedef enum : uint32_t {
58 k_apsr_n = 31U,
59 k_apsr_z = 30U,
60 k_apsr_c = 29U,
61 k_apsr_v = 28U,
63
68typedef enum : uint32_t {
69 k_cond_eq = 0x0U,
70 k_cond_ne = 0x1U,
71 k_cond_cs = 0x2U,
72 k_cond_cc = 0x3U,
73 k_cond_mi = 0x4U,
74 k_cond_pl = 0x5U,
75 k_cond_vs = 0x6U,
76 k_cond_vc = 0x7U,
77 k_cond_hi = 0x8U,
78 k_cond_ls = 0x9U,
79 k_cond_ge = 0xAU,
80 k_cond_lt = 0xBU,
81 k_cond_gt = 0xCU,
82 k_cond_le = 0xDU,
83 k_cond_al = 0xEU,
85
99RA8_INTERNAL static bool internal_cond_holds(uint32_t cond, uint32_t xpsr)
100{
101 const bool n = ((xpsr >> (uint32_t)k_apsr_n) & 1U) != 0U;
102 const bool z = ((xpsr >> (uint32_t)k_apsr_z) & 1U) != 0U;
103 const bool c = ((xpsr >> (uint32_t)k_apsr_c) & 1U) != 0U;
104 const bool v = ((xpsr >> (uint32_t)k_apsr_v) & 1U) != 0U;
105 switch (cond & (uint32_t)k_lo4_mask) {
106 case k_cond_eq:
107 return z;
108 case k_cond_ne:
109 return !z;
110 case k_cond_cs:
111 return c;
112 case k_cond_cc:
113 return !c;
114 case k_cond_mi:
115 return n;
116 case k_cond_pl:
117 return !n;
118 case k_cond_vs:
119 return v;
120 case k_cond_vc:
121 return !v;
122 case k_cond_hi:
123 return c && !z;
124 case k_cond_ls:
125 return !c || z;
126 case k_cond_ge:
127 return n == v;
128 case k_cond_lt:
129 return n != v;
130 case k_cond_gt:
131 return !z && (n == v);
132 case k_cond_le:
133 return z || (n != v);
134 default:
135 return true; /* AL (k_cond_al) / 0xF */
136 }
137}
138
157RA8_INTERNAL static bool internal_cs_reserved_reg(uint32_t reg)
158{
159 return (reg == (uint32_t)k_cs_reg_sp) || (reg == (uint32_t)k_cs_reg_pc);
160}
161
182RA8_INTERNAL static bool
183internal_emulate_cond_select(uc_engine* uc, uint32_t pc, const uint8_t* code)
184{
185 const uint16_t hw1 = (uint16_t)(code[0] | ((uint16_t)code[1] << 8));
186 const uint16_t hw2 = (uint16_t)(code[2] | ((uint16_t)code[3] << 8));
187 if (((hw1 & (uint16_t)k_cs_hw1_mask) != (uint16_t)k_cs_hw1_match) ||
188 ((hw2 & (uint16_t)k_cs_hw2_b15) == 0U) || ((hw2 & (uint16_t)k_cs_hw2_b14) != 0U)) {
189 return false;
190 }
191 const uint32_t rn = (uint32_t)(hw1 & (uint32_t)k_lo4_mask);
192 const uint32_t op = (uint32_t)((hw2 >> (uint32_t)k_cs_op_shift) & (uint32_t)k_cs_op_mask);
193 const uint32_t rd = (uint32_t)((hw2 >> 8) & (uint32_t)k_lo4_mask);
194 const uint32_t cond = (uint32_t)((hw2 >> 4) & (uint32_t)k_lo4_mask);
195 const uint32_t rm = (uint32_t)(hw2 & (uint32_t)k_lo4_mask);
196
197 /* The Armv8.1-M long shifts share this family's 0xEA5n prefix and put 0b1101
198 * (SP) or 0b1111 (PC) in what reads here as Rm, so a register-form long shift
199 * such as LSLL r2, r3, r8 (EA52 830D) satisfies every condition above -- it
200 * would execute here as CSEL r3, r2, sp, EQ and silently corrupt r3. Reserved
201 * operands mean this is not a conditional select; fall through and let the
202 * long-shift seam claim it. */
205 return false;
206 }
207
208 uint32_t xpsr = 0U;
209 uint32_t vn = 0U;
210 uint32_t vm = 0U;
211 (void)uc_reg_read(uc, UC_ARM_REG_XPSR, &xpsr);
212 (void)uc_reg_read(uc, k_arm_reg_id[rn], &vn);
213 (void)uc_reg_read(uc, k_arm_reg_id[rm], &vm);
214
215 uint32_t result;
216 if (internal_cond_holds(cond, xpsr)) {
217 result = vn;
218 } else {
219 switch (op) {
220 case (uint32_t)k_cs_op_csinc:
221 result = vm + 1U;
222 break;
223 case (uint32_t)k_cs_op_csinv:
224 result = ~vm;
225 break;
226 case (uint32_t)k_cs_op_csneg:
227 result = (uint32_t)(-(int32_t)vm);
228 break;
229 case (uint32_t)k_cs_op_csel:
230 default:
231 result = vm;
232 break;
233 }
234 }
235 (void)uc_reg_write(uc, k_arm_reg_id[rd], &result);
236 uint32_t next = pc + (uint32_t)k_cs_insn_len;
237 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
238 return true;
239}
240
265RA8_INTERNAL static bool internal_emulate_barrier(uc_engine* uc, uint32_t pc, const uint8_t* code)
266{
267 enum : uint16_t {
268 k_barrier_hw1 = 0xF3BFU,
269 k_barrier_hw2_mask = 0xFF00U,
270 k_barrier_hw2_match = 0x8F00U,
271 k_barrier_op_mask = 0x00F0U,
272 k_barrier_op_dsb = 0x0040U,
273 k_barrier_op_dmb = 0x0050U,
274 k_barrier_op_isb = 0x0060U,
275 k_barrier_len = 0x0004U,
276 };
277 const uint16_t hw1 = (uint16_t)(code[0] | ((uint16_t)code[1] << 8));
278 const uint16_t hw2 = (uint16_t)(code[2] | ((uint16_t)code[3] << 8));
279 if ((hw1 != (uint16_t)k_barrier_hw1) ||
280 ((hw2 & (uint16_t)k_barrier_hw2_mask) != (uint16_t)k_barrier_hw2_match)) {
281 return false;
282 }
283 const uint16_t op = (uint16_t)(hw2 & (uint16_t)k_barrier_op_mask);
284 if ((op != (uint16_t)k_barrier_op_dsb) && (op != (uint16_t)k_barrier_op_dmb) &&
285 (op != (uint16_t)k_barrier_op_isb)) {
286 return false;
287 }
288 const uint32_t next = pc + (uint32_t)k_barrier_len;
289 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
290 return true;
291}
292
315RA8_INTERNAL static bool
316internal_emulate_sec_scrub(uc_engine* uc, uint32_t pc, const uint8_t code[4])
317{
318 const uint16_t hw0 = (uint16_t)(((uint16_t)code[1] << (uint16_t)k_byte_bits) | (uint16_t)code[0]);
319 if ((hw0 != (uint16_t)k_clrm_hw0) && (hw0 != (uint16_t)k_vscclrm_hw0_s) &&
320 (hw0 != (uint16_t)k_vscclrm_hw0_d)) {
321 return false;
322 }
323 const uint32_t next = pc + (uint32_t)k_thumb2_insn_bytes;
324 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
325 return true;
326}
327
328/* ---------------------------------------------------------------------------
329 * Low-Overhead-Branch (LOB) emulation. The M85 has the hardware-loop extension
330 * (DLS/WLS/LE) that GCC's -Og uses for counted loops; the M33 does not, so they
331 * trap as invalid. Handle the two forms the toolchain emits:
332 * DLS lr, Rn -- start a loop: LR = Rn (the iteration count)
333 * LE lr, <label> -- loop end: LR -= 1; branch back to <label> while LR != 0
334 * (the toolchain's <label> offset is (hw2 & 0x7FF) 2-byte units backward from
335 * PC+4, verified against its disassembly). Both sit amid valid code, so the same
336 * stop-then-relaunch contract as the cond-select seam applies. WLS/LETP/DLSTP are
337 * not emitted here and fall through to the normal invalid-instruction report.
338 * ===========================================================================
339 */
340enum : uint32_t {
341 k_lob_dls_hw1 = 0xF040U,
342 k_lob_dls_h1m = 0xFFF0U,
343 k_lob_dls_hw2 = 0xE001U,
344 k_lob_le_hw1 = 0xF00FU,
345 k_lob_le_hw2 = 0xC000U,
346 k_lob_le_h2m = 0xF000U,
347 k_lob_le_imm10 = 0x03FFU,
351};
353static uint64_t s_lob_emulated = 0U;
354
368RA8_INTERNAL static bool internal_emulate_lob(uc_engine* uc, uint32_t pc, const uint8_t code[4])
369{
370 const uint16_t hw1 = (uint16_t)(code[0] | ((uint16_t)code[1] << (uint16_t)k_byte_bits));
371 const uint16_t hw2 = (uint16_t)(code[2] | ((uint16_t)code[3] << (uint16_t)k_byte_bits));
372 /* DLS lr, Rn -> LR = Rn (start the counted loop). */
373 if (((hw1 & (uint16_t)k_lob_dls_h1m) == (uint16_t)k_lob_dls_hw1) &&
374 (hw2 == (uint16_t)k_lob_dls_hw2)) {
375 /* Map Rn via k_arm_reg_id[] -- Unicorn's UC_ARM_REG_* enum is NOT contiguous
376 * (UC_ARM_REG_R0 + n != UC_ARM_REG_Rn), so the prior arithmetic read the
377 * wrong register for the loop count -> bogus iteration count -> over-run and
378 * a wild branch (#233). This is the same table the CSEL/long-shift seams use. */
379 const uint32_t rn = (uint32_t)hw1 & (uint32_t)k_lob_rn_mask;
380 uint32_t v = 0U;
381 (void)uc_reg_read(uc, k_arm_reg_id[rn], &v);
382 (void)uc_reg_write(uc, UC_ARM_REG_LR, &v);
383 const uint32_t next = pc + (uint32_t)k_lob_insn_len;
384 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
386 return true;
387 }
388 /* LE lr, label -> LR -= 1; branch back while LR != 0, else fall through. */
389 if ((hw1 == (uint16_t)k_lob_le_hw1) &&
390 ((hw2 & (uint16_t)k_lob_le_h2m) == (uint16_t)k_lob_le_hw2)) {
391 uint32_t lr = 0U;
392 (void)uc_reg_read(uc, UC_ARM_REG_LR, &lr);
393 lr -= 1U;
394 (void)uc_reg_write(uc, UC_ARM_REG_LR, &lr);
395 uint32_t next = pc + (uint32_t)k_lob_insn_len;
396 if (lr != 0U) {
397 /* Backward branch to the loop top: the 11-bit immediate is scattered --
398 * hw2[10:1] are the high bits and hw2[11] is the offset LSB (2-byte
399 * granularity) -- so offset = (imm10 << 2) | (lsb << 1), taken from PC+4.
400 * Verified against objdump for all 95 LE sites in the -O1 reader image
401 * (#233). The previous (hw2 & 0x7FF) << 1 decode ignored the scatter and
402 * mis-branched at 55 of them, running loops the wrong number of times
403 * (memory corruption + a wild branch to zeros at -O1). */
404 const uint32_t imm10 = ((uint32_t)hw2 >> 1U) & (uint32_t)k_lob_le_imm10;
405 const uint32_t lsb = ((uint32_t)hw2 >> (uint32_t)k_lob_le_lsb) & 1U;
406 next -= ((imm10 << 2U) | (lsb << 1U)); /* backward to loop top. */
407 }
408 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
410 return true;
411 }
412 return false;
413}
414
441RA8_INTERNAL static bool
442internal_dispatch_armv81_seam(uc_engine* uc, uint32_t pc, const uint8_t code[4])
443{
444 /* The RA8D2 firmware is built for Cortex-M85 (Armv8.1-M); the nearest core
445 * Unicorn offers is M33 (Armv8-M), which lacks the conditional-select family.
446 * GCC emits those for branchless index math on the touch path, so
447 * execute them here. internal_emulate_cond_select writes Rd and advances PC past the
448 * 4-byte instruction; then uc_emu_stop so the chunked run loop relaunches from
449 * the new PC -- editing PC and continuing in-place corrupts Unicorn's block /
450 * Thumb state (it then misdecodes the next valid instruction), so the
451 * stop-then-relaunch contract the SysTick / touch stubs use is required here. */
452 if (internal_emulate_cond_select(uc, pc, code)) {
453 return true; /* handled -- run loop resumes at the advanced PC */
454 }
455
456 /* Older Unicorn builds (the runner's 2.0.1) trap DSB/DMB/ISB as invalid; a
457 * barrier is a NOP in this emulator, so advance past it and relaunch. */
458 if (internal_emulate_barrier(uc, pc, code)) {
459 return true; /* handled -- run loop resumes past the barrier */
460 }
461
462 /* MVE (Helium): the M85 has it, Unicorn's M33 does not, so the auto-vectoriser's
463 * Helium ops trap here. Emulate the handled subset and relaunch past it. */
464 if (emulate_mve(uc, pc, code)) {
465 return true; /* handled -- run loop resumes past the MVE instruction */
466 }
467
468 /* Armv8.1-M long shift, REGISTER form (LSLL/ASRL by Rm). Its encoding aliases
469 * to an ORR.W with Rm == SP, which the core refuses rather than
470 * mis-executing, so it lands here as a real trap. (The immediate form aliases
471 * to an ORRS the core happily executes with a wrong result, so that one is
472 * repaired by the code-hook seam in emu_seam_longshift.c instead.) */
473 if (emulate_long_shift_reg(uc, pc, code)) {
474 return true; /* handled -- run loop resumes past the long shift */
475 }
476
477 /* Low-Overhead-Branch (DLS/LE): the M85's hardware-loop ops, absent on the M33.
478 * Emulate the loop counter / branch and relaunch. */
479 if (internal_emulate_lob(uc, pc, code)) {
480 return true; /* handled -- run loop resumes at the loop top or past the loop */
481 }
482 return false;
483}
484
498RA8_INTERNAL static bool
499internal_dispatch_insn_seam(uc_engine* uc, uint32_t pc, const uint8_t code[4])
500{
501 /* Divide-by-zero trap: a UDIV/SDIV overwritten with UDF once the firmware set
502 * CCR.DIV_0_TRP. emulate_div0_patched either latches a UsageFault (zero divisor)
503 * or emulates the divide and advances PC; either way stop + relaunch. Checked
504 * first: it matches only the exact patched addresses, never a real UDF. */
505 if (emulate_div0_patched(uc, pc, code)) {
506 (void)uc_emu_stop(uc);
507 return true;
508 }
509
510 /* Armv8-M Security Extension register scrub (CLRM / VSCCLRM) on a cmse
511 * Non-Secure-Callable return: a NOP in ra8_emulator's single-domain model. */
512 if (internal_emulate_sec_scrub(uc, pc, code)) {
513 (void)uc_emu_stop(uc);
514 return true;
515 }
516
517 if (internal_dispatch_armv81_seam(uc, pc, code)) {
518 (void)uc_emu_stop(uc);
519 return true; /* handled -- run loop resumes past the emulated instruction */
520 }
521 return false;
522}
523
534RA8_INTERNAL static void internal_report_unhandled_insn(uint32_t pc, const uint8_t code[4])
535{
536 (void)priv_emu_io_errf(" INVALID INSN @ 0x%08X: bytes %02X %02X %02X %02X\n",
537 pc,
538 code[0],
539 code[1],
540 code[2],
541 code[3]);
542
543 csh cs;
544 if (cs_open(CS_ARCH_ARM, (cs_mode)(CS_MODE_THUMB | CS_MODE_MCLASS), &cs) == CS_ERR_OK) {
545 cs_insn* insn = nullptr;
546 /* `code` decays to a pointer here, so sizeof(code) would hand capstone the
547 * pointer width instead of the four instruction bytes that are valid. */
548 const size_t n = cs_disasm(cs, code, (size_t)k_cs_insn_len, pc, 1, &insn);
549 if (n > 0U) {
550 (void)priv_emu_io_errf(" disasm: %s %s\n", insn[0].mnemonic, insn[0].op_str);
551 cs_free(insn, n);
552 } else {
553 (void)priv_emu_io_errf(" disasm: capstone could not decode it either\n");
554 }
555 cs_close(&cs);
556 }
557}
558
560bool on_invalid_insn(uc_engine* uc, void* user)
561{
562 (void)user;
563 uint32_t pc = 0U;
564 (void)uc_reg_read(uc, UC_ARM_REG_PC, &pc);
565 uint8_t code[4] = {};
566 (void)emu_mem_read(uc, pc, code, sizeof(code));
567
568 if (internal_dispatch_insn_seam(uc, pc, code)) {
569 return true; /* handled -- run loop resumes at the advanced PC */
570 }
571
572 /* Unicorn re-reports the instruction FOLLOWING a serviced NoCP fault as
573 * invalid even when it is perfectly valid, because the seam wrote PC and
574 * stopped the engine from inside the interrupt hook. Absorb exactly that one
575 * report, at exactly that one address, and relaunch. */
576 if (emu_mve_nocp_spurious(pc)) {
577 (void)uc_emu_stop(uc);
578 return true;
579 }
580
582 return false; /* not handled -> stop emulation with UC_ERR_INSN_INVALID */
583}
584
586void emu_insn_seams_install(uc_engine* uc)
587{
588 static uc_hook s_h_invalid;
589 (void)uc_hook_add(uc, &s_h_invalid, UC_HOOK_INSN_INVALID, (void*)on_invalid_insn, nullptr, 1, 0);
590}
591
594{
595 return s_lob_emulated;
596}
Shared Unicorn engine access utilities for the board emulator.
const int k_arm_reg_id[16]
ARM register index (0..15) -> Unicorn register id.
Definition emu_engine.c:17
Cortex-M exception model constants and interfaces for ra8_emulator.
@ k_thumb2_insn_bytes
32-bit Thumb-2 instruction width.
Definition emu_exc.h:112
@ k_vscclrm_hw0_s
VSCCLRM {s..,VPR} first halfword.
Definition emu_exc.h:117
@ k_lo4_mask
Low nibble (register / cond field).
Definition emu_exc.h:110
@ k_vscclrm_hw0_d
VSCCLRM {d..,VPR} first halfword.
Definition emu_exc.h:118
@ k_byte_bits
Bits per byte (SHPR field width).
Definition emu_exc.h:99
@ k_clrm_hw0
CLRM {regs} first halfword.
Definition emu_exc.h:116
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
static RA8_INTERNAL bool internal_cs_reserved_reg(uint32_t reg)
Report whether reg is reserved as a conditional-select operand.
cs_op_field_t
Thumb halfword-two field decode for the conditional-select family.
static RA8_INTERNAL void internal_report_unhandled_insn(uint32_t pc, const uint8_t code[4])
Report + capstone-disassemble an instruction no seam could decode.
static RA8_INTERNAL bool internal_emulate_sec_scrub(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Emulate the Armv8-M security register-scrub ops as NOPs.
void emu_insn_seams_install(uc_engine *uc)
Implementation of emu_insn_seams_install() – arm the dispatcher.
bool on_invalid_insn(uc_engine *uc, void *user)
Disassemble + report an instruction the core could not decode.
static RA8_INTERNAL bool internal_dispatch_insn_seam(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Try each Armv8.1-M / security seam in turn; true (and stop) if handled.
static RA8_INTERNAL bool internal_emulate_cond_select(uc_engine *uc, uint32_t pc, const uint8_t *code)
Emulate one Armv8.1-M conditional-select instruction if present at PC.
static uint64_t s_lob_emulated
Count of LOB instructions emulated this run (run-end telemetry).
static RA8_INTERNAL bool internal_cond_holds(uint32_t cond, uint32_t xpsr)
Evaluate an ARM condition code against the APSR flags.
@ k_lob_le_hw1
LE lr,label first half-word.
@ k_lob_le_lsb
LE offset LSB scattered to hw2[11].
@ k_lob_rn_mask
Rn field (4 bits) in hw1[3:0].
@ k_lob_insn_len
LOB instructions are 32-bit Thumb-2.
@ k_lob_dls_hw2
DLS second half-word (fully fixed).
@ k_lob_le_imm10
LE offset high bits hw2[10:1] (#233).
@ k_lob_le_h2m
Mask isolating the fixed LE hw2 bits.
@ k_lob_le_hw2
LE second half-word fixed bits.
@ k_lob_dls_h1m
Mask isolating the fixed DLS hw1 bits.
@ k_lob_dls_hw1
DLS lr,Rn first half-word (Rn in [3:0]).
arm_cond_t
ARM/Thumb 4-bit condition-code field encodings (cond[3:0]).
@ k_cond_mi
Negative.
@ k_cond_eq
Equal (Z==1).
@ k_cond_ls
Unsigned lower or same.
@ k_cond_ne
Not equal (Z==0).
@ k_cond_al
Always.
@ k_cond_ge
Signed >=.
@ k_cond_cs
Carry set / unsigned >=.
@ k_cond_vc
Overflow clear.
@ k_cond_gt
Signed >.
@ k_cond_hi
Unsigned higher.
@ k_cond_le
Signed <=.
@ k_cond_pl
Positive or zero.
@ k_cond_cc
Carry clear / unsigned <.
@ k_cond_vs
Overflow set.
@ k_cond_lt
Signed <.
apsr_bit_t
@ k_apsr_c
Carry.
@ k_apsr_z
Zero.
@ k_apsr_n
Negative.
@ k_apsr_v
Overflow.
static RA8_INTERNAL bool internal_dispatch_armv81_seam(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Emulate one Armv8.1-M instruction Unicorn's M33 core does not provide.
static RA8_INTERNAL bool internal_emulate_barrier(uc_engine *uc, uint32_t pc, const uint8_t *code)
Emulate an Armv8-M memory barrier (DSB/DMB/ISB) as a NOP if present.
uint64_t emu_lob_emulated_count(void)
Implementation of emu_lob_emulated_count() – plain counter read.
static RA8_INTERNAL bool internal_emulate_lob(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Emulate a DLS/LE Low-Overhead-Branch instruction; true iff handled.
cond_select_t
@ k_cs_op_csinv
op == 10: Rd = c ?
@ k_cs_op_csel
op == 00: Rd = c ?
@ k_cs_insn_len
Both halfwords: 4 bytes.
@ k_cs_op_csneg
op == 11: Rd = c ?
@ k_cs_op_csinc
op == 01: Rd = c ?
@ k_cs_hw1_match
hw1[15:4] == 0xEA5 for this family.
@ k_cs_hw2_b14
hw2 bit14 must be 0.
@ k_cs_reg_pc
PC: reserved as a CSEL operand.
@ k_cs_hw1_mask
hw1 high 12 bits identify the group.
@ k_cs_reg_sp
SP: reserved as a CSEL operand.
@ k_cs_hw2_b15
hw2 bit15 must be 1.
uc_err emu_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t count)
Read guest memory through the central access seam.
@ k_cs_op_shift
CSEL-family op = hw2[13:12].
Definition emu_run.h:92
@ k_cs_op_mask
2-bit op field.
Definition emu_run.h:93
Armv8.1-M instruction-emulation seams (M85 ops on Unicorn's M33).
bool on_invalid_insn(uc_engine *uc, void *user)
UC_HOOK_INSN_INVALID dispatcher: service or report a trapped opcode.
bool emu_mve_nocp_spurious(uint32_t pc)
Report whether an invalid-instruction trap at pc is the bogus one Unicorn raises just after the MVE s...
bool emulate_div0_patched(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Service an undefined-instruction trap that landed on an armed divide.
bool emulate_long_shift_reg(uc_engine *uc, uint32_t pc, const uint8_t code[4])
Emulate a register-form Armv8.1-M long shift (LSLL/ASRL) that trapped.
bool emulate_mve(uc_engine *uc, uint32_t pc0, const uint8_t code0[4])
Emulate a run of consecutive auto-vectoriser MVE instructions.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).