|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Minimal MVE (Helium) emulation seam (see emu_seams.h). More...
#include <capstone/capstone.h>#include <stdio.h>#include <string.h>#include "emu_engine.h"#include "emu_exc.h"#include "emu_seams.h"Go to the source code of this file.
Data Structures | |
| struct | mve_mem_op_t |
| One decoded MVE contiguous load/store. More... | |
Enumerations | |
| enum | : uint32_t { k_mve_insn_len = 4U , k_mve_q_bytes = 16U , k_mve_lane_shift = 32U , k_mve_max_run = 4096U } |
| enum | mve_mem_field_t : uint32_t { k_mve_mem_h1_mask = 0xFF40U , k_mve_mem_h1_val = 0xED00U , k_mve_mem_h1_post = 0xEC00U , k_mve_mem_h2_mask = 0x1E00U , k_mve_mem_h2_val = 0x1E00U , k_mve_mem_bit_u = 0x0080U , k_mve_mem_bit_w = 0x0020U , k_mve_mem_bit_l = 0x0010U , k_mve_mem_size_sh = 7U , k_mve_mem_size_msk = 0x3U , k_mve_mem_sz_byte = 0U , k_mve_mem_sz_half = 1U , k_mve_mem_sz_word = 2U , k_mve_mem_scl_byte = 1U , k_mve_mem_scl_half = 2U , k_mve_mem_scl_word = 4U , k_mve_qd_shift = 13U , k_mve_qd_mask = 0x7U , k_mve_rn_mask = 0xFU , k_mve_imm7_mask = 0x7FU } |
Functions | |
| static RA8_INTERNAL bool | internal_mve_mem_decode (uint16_t hw1, uint16_t hw2, mve_mem_op_t *op) |
| Decode an MVE contiguous load/store from its two halfwords. | |
| static RA8_INTERNAL void | internal_mve_mem_exec (uc_engine *uc, const mve_mem_op_t *op) |
| Perform one decoded MVE contiguous load/store against emulated state. | |
| static RA8_INTERNAL bool | internal_mve_mem_try (uc_engine *uc, const uint8_t code[4]) |
Perform the MVE contiguous load/store at code, if that is what it is. | |
| static RA8_INTERNAL int | internal_mve_q_d (unsigned int qreg, bool high) |
Map capstone Q-reg qreg to its Unicorn D-register half (low/high). | |
| static RA8_INTERNAL bool | internal_mve_exec_one (uc_engine *uc, const cs_insn *insn) |
| Execute one decoded MVE instruction (no PC change); true iff handled. | |
| static RA8_INTERNAL csh * | internal_mve_capstone (void) |
| Lazily open the shared Thumb/M-class Capstone handle; nullptr on failure. | |
| bool | emulate_mve (uc_engine *uc, uint32_t pc0, const uint8_t code0[4]) |
| Emulate a run of consecutive auto-vectoriser MVE instructions. | |
| bool | emu_mve_nocp_emulate (uc_engine *uc, uint32_t pc) |
| Emulate an MVE contiguous load/store from the NoCP UsageFault. | |
| 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 seam serviced a NoCP fault. | |
| bool | emu_mve_nocp_take (void) |
| Implementation of emu_mve_nocp_take() – test-and-clear the latch. | |
| uint64_t | emu_mve_emulated_count (void) |
| Implementation of emu_mve_emulated_count() – plain counter read. | |
Variables | |
| static uint64_t | s_mve_emulated = 0U |
| Count of MVE instructions emulated this run (run-end telemetry). | |
| static bool | s_mve_nocp_handled = false |
| Latch: the chunk just ended in a NoCP fault this seam serviced. | |
| static uint32_t | s_mve_resume_pc = 0U |
| Address the seam advanced PC to after servicing the last NoCP fault. | |
| static bool | s_mve_resume_armed = false |
| True while one bogus post-NoCP invalid report is still expected. | |
Minimal MVE (Helium) emulation seam (see emu_seams.h).
The RA8D2 is Cortex-M85 (Armv8.1-M, has MVE) but the closest core Unicorn offers is M33 (Armv8-M, NO MVE), so the Helium instructions GCC's auto-vectoriser emits either trap as invalid (VMOV.I32) or silently decode as legacy coprocessor stores (VSTRW.32). This seam emulates the handled subset – the invalid-instruction path consumes runs of trapped MVE ops, and a one-time image scan hooks every VSTRW.32 site so the store happens before the core reaches the mis-decoding instruction. Moved verbatim out of the ra8_emulator main translation unit.
Definition in file emu_seam_mve.c.
| anonymous enum : uint32_t |
Definition at line 43 of file emu_seam_mve.c.
| enum mve_mem_field_t : uint32_t |
Definition at line 141 of file emu_seam_mve.c.
| uint64_t emu_mve_emulated_count | ( | void | ) |
Implementation of emu_mve_emulated_count() – plain counter read.
Count of MVE instructions emulated this run (run-end telemetry).
Definition at line 490 of file emu_seam_mve.c.
References s_mve_emulated.
Referenced by internal_run_print_stop_summary().
| bool emu_mve_nocp_emulate | ( | uc_engine * | uc, |
| uint32_t | pc ) |
Emulate an MVE contiguous load/store from the NoCP UsageFault.
Armv8.1-M reallocates coprocessor space 0b1110 / 0b1111 to MVE, so VLDRB/VLDRH/VLDRW and VSTRB/VSTRH/VSTRW (immediate offset) reuse the legacy STC/LDC encodings byte for byte – stc p15, c7, [r0, #196] and vstrw.32 q3, [r0, #196] are both ED80 7F31. Unicorn's M33 implements neither MVE nor coprocessor 14/15, so it does not trap them as invalid instructions: it raises a NoCP UsageFault with PC still at the faulting word. This decodes that word straight from its two halfwords (capstone renders the family as a legacy stc, so it cannot be used), moves the 16-byte vector, applies write-back, and advances PC past the instruction. Anything outside the family – including the neighbouring FP stores, which differ only in hw2[12:9] and which Unicorn executes correctly – is rejected so it still faults honestly.
| [in,out] | uc | Unicorn engine. |
| [in] | pc | Address of the faulting instruction. |
| true | The access happened and PC advanced by four bytes. |
| false | Not this family; no engine state changed. |
uc is stopped inside the UC_HOOK_INTR NoCP fault. pc is the faulting instruction address, not an EXC_RETURN magic. Definition at line 452 of file emu_seam_mve.c.
References emu_mem_read(), internal_mve_mem_try(), k_mve_insn_len, s_mve_emulated, s_mve_nocp_handled, s_mve_resume_armed, and s_mve_resume_pc.
Referenced by internal_on_intr().
| 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 seam serviced a NoCP fault.
Servicing the NoCP fault means writing PC and calling uc_emu_stop from inside the interrupt hook. Unicorn then re-decodes at the advanced PC and reports UC_ERR_INSN_INVALID there even though the instruction is perfectly valid – in ereader_cover the address is a plain strd r3, r3, [r7, #8] four bytes past a vstrw.32. A relaunch from that same PC executes it correctly, so the report is noise, but it is indistinguishable from a real one by decode alone: this predicate distinguishes it by address instead.
Exactly one report is absorbed per serviced fault, and only at the address the seam advanced to. A genuinely invalid instruction sitting at that address is therefore delayed by one relaunch, never suppressed: the arming is cleared on the first call, so the second trap reports normally.
| [in] | pc | Address the invalid-instruction hook trapped at. |
| true | Absorb it silently and relaunch; nothing is wrong. |
| false | Report it; the arming (if any) is now cleared. |
Definition at line 470 of file emu_seam_mve.c.
References s_mve_nocp_handled, s_mve_resume_armed, and s_mve_resume_pc.
Referenced by on_invalid_insn().
| bool emu_mve_nocp_take | ( | void | ) |
Implementation of emu_mve_nocp_take() – test-and-clear the latch.
Test and clear the "NoCP fault serviced by the MVE seam" latch.
Definition at line 482 of file emu_seam_mve.c.
References s_mve_nocp_handled.
Referenced by internal_run_inner_take_exception().
| bool emulate_mve | ( | uc_engine * | uc, |
| uint32_t | pc0, | ||
| const uint8_t | code0[4] ) |
Emulate a run of consecutive auto-vectoriser MVE instructions.
Helium ops come in tight back-to-back runs (e.g. one VMOV.I32 then several VSTRW.32 to zero a struct). Emulating only the first and relaunching would land the next launch on another invalid instruction, which Unicorn faults on – so this consumes every consecutive handled MVE op in one trap and sets PC to the first instruction it does NOT handle, exactly mirroring the cond-select seam's "stop, then relaunch on valid code" contract. Bounded by k_mve_max_run.
Definition at line 407 of file emu_seam_mve.c.
References emu_mem_read(), internal_mve_capstone(), internal_mve_exec_one(), internal_mve_mem_try(), k_mve_insn_len, k_mve_max_run, memcpy(), and s_mve_emulated.
Referenced by internal_dispatch_armv81_seam().
|
static |
Lazily open the shared Thumb/M-class Capstone handle; nullptr on failure.
Definition at line 380 of file emu_seam_mve.c.
References RA8_INTERNAL.
Referenced by emulate_mve().
|
static |
Execute one decoded MVE instruction (no PC change); true iff handled.
Execute one decoded mve instruction (no pc change); true iff handled; this step is contained within the emu seam mve model and uses bounded caller or module-owned storage.
| [in,out] | uc | Unicorn engine whose emulated state is read or updated. |
| [in] | insn | Insn input used by the operation. |
| true | The mve exec one condition holds or completed successfully; false otherwise. |
Definition at line 359 of file emu_seam_mve.c.
References internal_mve_q_d(), k_mve_lane_shift, RA8_INTERNAL, and strstr().
Referenced by emulate_mve().
|
static |
Decode an MVE contiguous load/store from its two halfwords.
Rejects anything outside the family, including the reserved size == 0b11 encoding, so an unallocated word is left to fault rather than emulated as some neighbouring instruction.
| [in] | hw1 | First instruction halfword. |
| [in] | hw2 | Second instruction halfword. |
| [out] | op | Decoded operation; untouched unless true is returned. |
hw1 / hw2 encode a member of this family. | true | op holds the decoded fields. |
| false | Not this family; op is unmodified. |
op is non-NULL. hw1 and hw2 are the little-endian halfwords in program order. op is initialised. Definition at line 209 of file emu_seam_mve.c.
References mve_mem_op_t::add, k_mve_imm7_mask, k_mve_mem_bit_l, k_mve_mem_bit_u, k_mve_mem_bit_w, k_mve_mem_h1_mask, k_mve_mem_h1_post, k_mve_mem_h1_val, k_mve_mem_h2_mask, k_mve_mem_h2_val, k_mve_mem_scl_byte, k_mve_mem_scl_half, k_mve_mem_scl_word, k_mve_mem_size_msk, k_mve_mem_size_sh, k_mve_mem_sz_byte, k_mve_mem_sz_half, k_mve_mem_sz_word, k_mve_qd_mask, k_mve_qd_shift, k_mve_rn_mask, mve_mem_op_t::load, mve_mem_op_t::off, mve_mem_op_t::post, mve_mem_op_t::qd, RA8_INTERNAL, mve_mem_op_t::rn, and mve_mem_op_t::wback.
Referenced by internal_mve_mem_try().
|
static |
Perform one decoded MVE contiguous load/store against emulated state.
The Q registers alias the FP D registers (Qn == D[2n]:D[2n+1]) which Unicorn's M33 does have, so the vector operand is read and written through them. UC_ARM_REG_D0..D15 are contiguous in Unicorn's enum (checked), but the core-register ids are NOT – SP, LR and PC sit far from R0..R12 – so the base register is resolved through k_arm_reg_id. Indexing UC_ARM_REG_R0 + rn instead would read an unrelated register whenever Rn is SP, which the toolchain does emit (vstrw.32 q1, [sp, #4] assembles to ED8D 3F01).
| [in,out] | uc | Unicorn engine. |
| [in] | op | Decoded operation from internal_mve_mem_decode. |
op was produced by a successful internal_mve_mem_decode. uc is stopped inside the NoCP fault for this instruction. Definition at line 265 of file emu_seam_mve.c.
References mve_mem_op_t::add, emu_mem_read(), emu_mem_write(), k_arm_reg_id, k_mve_q_bytes, mve_mem_op_t::load, memcpy(), mve_mem_op_t::off, mve_mem_op_t::post, mve_mem_op_t::qd, RA8_INTERNAL, mve_mem_op_t::rn, and mve_mem_op_t::wback.
Referenced by internal_mve_mem_try().
|
static |
Perform the MVE contiguous load/store at code, if that is what it is.
Shared by both arrival paths. Unicorn delivers the FIRST instruction of a run as a NoCP UsageFault through UC_HOOK_INTR, but once that handler has written PC and stopped the engine, the NEXT instruction of the same run arrives at the invalid-instruction hook instead – verified standalone against libunicorn with two consecutive vstrw.32. Both callers therefore need the identical decode-and-access step; only PC bookkeeping differs.
| [in,out] | uc | Unicorn engine. |
| [in] | code | The four instruction bytes to decode. |
code was an MVE contiguous load/store and was performed. | true | The 16-byte access happened; PC is NOT changed. |
| false | Not this family; no state changed. |
code holds four valid instruction bytes. uc is stopped in a hook callback. Definition at line 317 of file emu_seam_mve.c.
References internal_mve_mem_decode(), internal_mve_mem_exec(), k_byte_bits, and RA8_INTERNAL.
Referenced by emu_mve_nocp_emulate(), and emulate_mve().
|
static |
Map capstone Q-reg qreg to its Unicorn D-register half (low/high).
Map capstone q-reg qreg to its unicorn d-register half (low/high); this step is contained within the emu seam mve model and uses bounded caller or module-owned storage.
| [in] | qreg | Qreg input used by the operation. |
| [in] | high | High input used by the operation. |
| value | The operation-specific mve q d value. |
Definition at line 341 of file emu_seam_mve.c.
References RA8_INTERNAL.
Referenced by internal_mve_exec_one().
|
static |
Count of MVE instructions emulated this run (run-end telemetry).
Definition at line 50 of file emu_seam_mve.c.
Referenced by emu_mve_emulated_count(), emu_mve_nocp_emulate(), and emulate_mve().
|
static |
Latch: the chunk just ended in a NoCP fault this seam serviced.
Unicorn returns UC_ERR_EXCEPTION from uc_emu_start for the NoCP UsageFault even when the UC_HOOK_INTR callback fully handled it and stopped the engine, so the status alone cannot distinguish a serviced MVE access from a real unhandled exception. This latch carries that distinction to the run loop, which clears it via emu_mve_nocp_take and relaunches instead of ending the run – the same shape as the MPU and divide-by-zero latches.
Definition at line 67 of file emu_seam_mve.c.
Referenced by emu_mve_nocp_emulate(), emu_mve_nocp_spurious(), and emu_mve_nocp_take().
|
static |
True while one bogus post-NoCP invalid report is still expected.
Set when a NoCP fault is serviced, cleared by the first emu_mve_nocp_spurious call whether or not it matched, so at most one report is ever absorbed per serviced fault.
Definition at line 93 of file emu_seam_mve.c.
Referenced by emu_mve_nocp_emulate(), and emu_mve_nocp_spurious().
|
static |
Address the seam advanced PC to after servicing the last NoCP fault.
Paired with s_mve_resume_armed to absorb the one bogus invalid-instruction report Unicorn emits at this address; see emu_mve_nocp_spurious for why that report happens.
Definition at line 80 of file emu_seam_mve.c.
Referenced by emu_mve_nocp_emulate(), and emu_mve_nocp_spurious().