ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_seams.h File Reference

Armv8.1-M instruction-emulation seams (M85 ops on Unicorn's M33). More...

#include <stdint.h>
#include <unicorn/unicorn.h>
#include "emu_elf.h"
#include "ra8_attributes.h"
Include dependency graph for emu_seams.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Enumerations

enum  div0_field_t : uint32_t {
  k_div0_hw1_mask = 0xFFF0U ,
  k_div0_hw1_udiv = 0xFBB0U ,
  k_div0_hw1_sdiv = 0xFB90U ,
  k_div0_hw2_mask = 0xF0F0U ,
  k_div0_hw2_fixed = 0xF0F0U ,
  k_div0_reg_mask = 0x000FU ,
  k_div0_rd_shift = 8U ,
  k_div0_reg_sp = 13U ,
  k_div0_reg_pc = 15U ,
  k_div0_insn_len = 4U ,
  k_div0_cfsr_divzero = 1U << 25U ,
  k_div0_int32_min = 0x80000000U ,
  k_div0_udf_b0 = 0xF0U ,
  k_div0_udf_b1 = 0xF7U ,
  k_div0_udf_b3 = 0xA0U
}
 UDIV/SDIV decode masks + fault field bits for the div-0 seam. More...

Functions

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_take (void)
 Test and clear the "NoCP fault serviced by the MVE seam" latch.
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.
uint64_t emu_mve_emulated_count (void)
 Count of MVE instructions emulated this run (run-end telemetry).
void long_shift_seam_install (uc_engine *uc, const emu_elf_source_t *elf)
 Scan the loaded image and install a hook at every immediate long-shift.
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.
void div0_seam_install (const emu_elf_source_t *elf)
 Scan the image for UDIV/SDIV sites so the div-0 trap can arm later.
void div0_patch_sites (uc_engine *uc)
 Overwrite every tracked divide with UDF so divide-by-zero can trap.
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 emu_div0_fault_pending (void)
 Whether a trapping divide-by-zero is latched for the run loop.
void emu_div0_clear_fault (void)
 Clear the latched divide-by-zero fault.
uint32_t emu_div0_fault_pc (void)
 PC of the divide that latched the pending fault.
void emu_div0_count_trap (void)
 Count one synthesised divide-by-zero UsageFault (telemetry).
void emu_div0_disarm (void)
 Drop the armed state after a warm reboot re-loads the image.
bool on_invalid_insn (uc_engine *uc, void *user)
 UC_HOOK_INSN_INVALID dispatcher: service or report a trapped opcode.
void emu_insn_seams_install (uc_engine *uc)
 Arm the invalid-instruction dispatcher on the engine.
uint64_t emu_lob_emulated_count (void)
 Count of LOB (DLS/LE) instructions emulated this run (telemetry).
void div0_synth_usagefault (uc_engine *uc, uint32_t vtor_base)
 Synthesise a UsageFault (#6) for a trapped divide-by-zero.
void emu_fast_sd_enable (void)
 Opt in to the –fast-sd block-serving seam for this run.
void fast_sd_seam_install (uc_engine *uc, const emu_elf_source_t *elf)
 Install the --fast-sd block-read hook if opted-in and the symbol exists.

Detailed Description

Armv8.1-M instruction-emulation seams (M85 ops on Unicorn's M33).

The RA8D2's Cortex-M85 executes Armv8.1-M instructions that Unicorn's nearest core (Cortex-M33, Armv8-M) either traps as invalid or silently mis-executes. Each seam closes one gap:

  • MVE (Helium): VMOV.I32 traps invalid and is emulated from the invalid-instruction hook; the contiguous load/store family reuses the legacy coprocessor encodings, so the M33 raises a NoCP UsageFault instead of trapping and those are serviced from the interrupt hook.
  • Long shifts (LSLL/LSRL/ASRL): overlap ORR.W and mis-execute silently; likewise found by image scan and emulated per site.
  • Divide-by-zero trap (CCR.DIV_0_TRP): Unicorn never raises the UsageFault; every UDIV/SDIV site is tracked and, once the firmware opts in, overwritten with UDF so the divide traps through the invalid-instruction hook and is either faulted (zero divisor) or emulated in software.
  • The invalid-instruction dispatcher itself (conditional-select family, barriers on old Unicorn builds, security register scrubs, and the low-overhead-branch DLS/LE loops).

Split out of the ra8_emulator main translation unit; behaviour unchanged.

Since
0.1.0

Definition in file emu_seams.h.

Enumeration Type Documentation

◆ div0_field_t

enum div0_field_t : uint32_t

UDIV/SDIV decode masks + fault field bits for the div-0 seam.

Enumerator
k_div0_hw1_mask 

hw1[15:4] selects the divide opcode.

k_div0_hw1_udiv 

UDIV T1: hw1[15:4] == 0xFBB.

k_div0_hw1_sdiv 

SDIV T1: hw1[15:4] == 0xFB9.

k_div0_hw2_mask 

hw2[15:12] and hw2[7:4] must be 1111.

k_div0_hw2_fixed 

Their required value for a real divide.

k_div0_reg_mask 

4-bit register field (Rn / Rm / Rd).

k_div0_rd_shift 

hw2[11:8] = Rd (destination register).

k_div0_reg_sp 

r13 (SP): UNPREDICTABLE as UDIV d/n/m.

k_div0_reg_pc 

r15 (PC): UNPREDICTABLE as UDIV d/n/m.

k_div0_insn_len 

UDIV/SDIV are 32-bit Thumb-2.

k_div0_cfsr_divzero 

CFSR.UFSR.DIVBYZERO (0x02000000).

k_div0_int32_min 

INT32_MIN: the SDIV / -1 overflow edge.

k_div0_udf_b0 

UDF.W #0 little-endian byte 0.

k_div0_udf_b1 

UDF.W #0 little-endian byte 1.

k_div0_udf_b3 

UDF.W #0 little-endian byte 3.

Definition at line 230 of file emu_seams.h.

Function Documentation

◆ div0_patch_sites()

void div0_patch_sites ( uc_engine * uc)

Overwrite every tracked divide with UDF so divide-by-zero can trap.

Called from the SCB control-register write hook the first time the firmware sets CCR.DIV_0_TRP. The patch is deferred to opt-in so a firmware that never arms the trap keeps its original divides (native, quotient-0 semantics) and pays nothing. Patching to UDF – rather than installing a UC_HOOK_CODE at each site – is deliberate: a code hook disables Unicorn's engine-wide block chaining and would roughly quarter the throughput of any busy loop in a DIV_0_TRP firmware, whereas the already-armed undefined-instruction hook has no such cost. Idempotent; re-applied after a warm reboot re-loads the image (the reboot path calls emu_div0_disarm()).

Parameters
[in,out]ucUnicorn engine whose memory is patched.
Returns
Nothing.
Precondition
The tracked divide sites hold valid addresses.
uc permits uc_mem_write to the code image.
Postcondition
Every tracked site holds the UDF encoding and the seam is armed.
A no-op when already armed.
Note
Not thread-safe (single engine).
Since
0.1.0

Called from ::on_scb_ctrl_write the first time the firmware sets CCR.DIV_0_TRP. The patch is deferred to opt-in so a firmware that never arms the trap keeps its original divides (native, quotient-0 semantics) and pays nothing. Patching to UDF – rather than installing a UC_HOOK_CODE at each site – is deliberate: UC_HOOK_CODE disables Unicorn's engine-wide block chaining and would roughly quarter the throughput of any busy loop in a DIV_0_TRP firmware, whereas the already-armed undefined-instruction hook (on_invalid_insn) has no such cost. Idempotent via s_div0_armed. Re-applied after a warm reboot re-loads the image (see the run loop's reboot paths, which clear s_div0_armed).

Parameters
[in,out]ucUnicorn engine whose memory is patched.
Returns
Nothing.
Precondition
s_div0_site[0 .. s_div0_site_n) hold valid divide-site addresses.
uc permits uc_mem_write to the (host-side) code image.
Postcondition
s_div0_armed is true and each site holds s_k_div0_udf.
A no-op when already armed.
Note
Not thread-safe (single engine).
Since
0.1.0

Definition at line 301 of file emu_seam_div0.c.

References emu_mem_write(), priv_emu_io_errf(), s_div0_armed, s_div0_site, s_div0_site_n, and s_k_div0_udf.

Referenced by internal_on_scb_ctrl_write().

◆ div0_seam_install()

void div0_seam_install ( const emu_elf_source_t * elf)

Scan the image for UDIV/SDIV sites so the div-0 trap can arm later.

Walks the ELF32 PT_LOAD executable segments on 2-byte boundaries for the UDIV/SDIV encoding, recording each site's VMA (p_vaddr based, so a ramfunc is tracked at its execution address) and its original halfwords. Nothing is patched here; the SCB control-write watcher overwrites the sites with UDF via div0_patch_sites() only if the firmware sets CCR.DIV_0_TRP. Tracked for every core (UDIV/SDIV exist on the M85 and the M33 alike). A scan false-positive is harmless: the site is only ever patched after opt-in, and emulate_div0_patched() re-decodes before acting.

Parameters
[in]elfOpen immutable ELF source.
Returns
Nothing.
Precondition
elf is a 32-bit ARM ELF (already validated by load_elf).
elf remains open throughout the bounded segment scan.
Postcondition
Up to the site cap of divide sites are tracked, none patched yet.
The armed flag is cleared (a fresh scan starts un-armed).
Note
Not thread-safe; call once during setup before the run loop.
See also
div0_patch_sites() Arms the tracked sites on firmware opt-in.
Since
0.1.0

Walks the ELF32 PT_LOAD executable segments on 2-byte boundaries for the UDIV/SDIV encoding (internal_udiv_sdiv_decode), recording each site's VMA (p_vaddr based, so a ramfunc is tracked at its execution address) and its original halfwords. Nothing is patched here; the always-on SCB control-write watcher (::on_scb_ctrl_write) overwrites the sites with UDF via div0_patch_sites only if the firmware sets CCR.DIV_0_TRP. Tracked for every core (UDIV/SDIV exist on the M85 and the M33 alike). A scan false-positive is harmless: the site is only ever patched after opt-in, and emulate_div0_patched re-decodes before acting.

Parameters
[in]elfIn-memory ELF image (still alive at call time).
Returns
Nothing.
Precondition
elf is a 32-bit ARM ELF (already validated by load_elf).
Postcondition
s_div0_site holds up to k_div0_sites_max tracked divide sites.
No site is patched yet (armed later by on_scb_ctrl_write).
Note
Not thread-safe; call once during setup before the run loop.
Since
0.1.0

Definition at line 409 of file emu_seam_div0.c.

References elf_foreach_exec_segment(), internal_div0_scan_segment(), priv_emu_io_errf(), s_div0_armed, and s_div0_site_n.

Referenced by internal_main_install_run_seams().

◆ div0_synth_usagefault()

void div0_synth_usagefault ( uc_engine * uc,
uint32_t vtor_base )

Synthesise a UsageFault (#6) for a trapped divide-by-zero.

Called by the run loop after emulate_div0_patched() latched a trapping div-0. Latches CFSR.UFSR.DIVBYZERO (so a fault handler – and the HIL alive probe – see the architectural status), forces PC back to the faulting divide so the exception entry stacks that address, and vectors into the application's UsageFault_Handler. If no handler is installed the trap is dropped (no HardFault escalation is modelled).

Parameters
[in,out]ucUnicorn engine.
[in]vtor_baseFallback vector base if VTOR reads as 0.
Returns
Nothing.
Precondition
A divide-by-zero fault is latched (PC captured at the divide).
The PPB CFSR word and the vector table are mapped as RAM.
Postcondition
On a valid vector, the core is in the UsageFault handler with the basic frame stacked and IPSR == 6.
Note
Faithful to Armv8-M CCR.DIV_0_TRP semantics; no time advances.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

Called by the run loop after emulate_div0_patched latched a trapping div-0. Latches CFSR.UFSR.DIVBYZERO (so a fault handler – and the HIL alive probe – see the architectural status: cfsr == 0x02000000), forces PC back to the faulting divide so exc_enter stacks that address (a real div-0 UsageFault stacks the divide), and vectors into the application's UsageFault_Handler. If no handler is installed the trap is dropped (no HardFault escalation is modelled – the firmware that arms DIV_0_TRP always installs the handler).

Parameters
[in,out]ucUnicorn engine.
[in]vtor_baseFallback vector base if VTOR reads as 0.
Returns
Nothing.
Precondition
s_div0_fault_pc holds the trapping divide's address.
The PPB CFSR word and the vector table are mapped as RAM.
Postcondition
On a valid vector, the core is in the UsageFault handler with the basic frame stacked (stacked PC == the faulting divide) and IPSR == 6.
CFSR.UFSR.DIVBYZERO reads set and s_div0_traps is incremented.
Note
Faithful to Armv8-M CCR.DIV_0_TRP semantics; no time advances (a fault is synchronous).
Since
0.1.0

Definition at line 448 of file emu_seam_div0.c.

References emu_div0_count_trap(), emu_div0_fault_pc(), exc_enter(), exc_vector(), k_div0_cfsr_divzero, k_exc_usagefault, k_scb_cfsr, rd32(), and wr32().

Referenced by internal_run_inner_take_exception().

◆ emu_div0_clear_fault()

void emu_div0_clear_fault ( void )

Clear the latched divide-by-zero fault.

The run loop clears the latch right before synthesising the UsageFault; the warm-reboot path clears it so a rebooted image starts clean.

Returns
Nothing.
Precondition
A fault was latched (or the call is a harmless reset).
None otherwise.
Postcondition
No divide-by-zero is pending.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

Clear the latched divide-by-zero fault.

Definition at line 468 of file emu_seam_div0.c.

References s_div0_fault.

Referenced by internal_run_inner_take_exception(), and warm_reboot().

◆ emu_div0_count_trap()

void emu_div0_count_trap ( void )

Count one synthesised divide-by-zero UsageFault (telemetry).

Returns
Nothing.
Precondition
A UsageFault is being synthesised for a latched div-0.
None otherwise.
Postcondition
The run's div-0 trap count grew by one.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

Count one synthesised divide-by-zero usagefault (telemetry); this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

Count one synthesised divide-by-zero UsageFault (telemetry).

Definition at line 480 of file emu_seam_div0.c.

References s_div0_traps.

Referenced by div0_synth_usagefault().

◆ emu_div0_disarm()

void emu_div0_disarm ( void )

Drop the armed state after a warm reboot re-loads the image.

Re-loading the ELF restores the original divide encodings, so the seam must re-arm on the next CCR.DIV_0_TRP write; this clears the idempotence latch.

Returns
Nothing.
Precondition
A warm reboot just re-wrote the PT_LOAD segments.
None otherwise.
Postcondition
The next div0_patch_sites() call patches again.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

Drop the armed state after a warm reboot re-loads the image.

Definition at line 486 of file emu_seam_div0.c.

References s_div0_armed.

Referenced by warm_reboot().

◆ emu_div0_fault_pc()

uint32_t emu_div0_fault_pc ( void )

PC of the divide that latched the pending fault.

Returns
The trapping divide's address (stacked by the synthesised fault).
Return values
0No fault has ever been latched.
Precondition
A fault was latched this run (else the value is stale/zero).
None otherwise.
Postcondition
No state is modified.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

Pc of the divide that latched the pending fault; this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

PC of the divide that latched the pending fault.

Definition at line 474 of file emu_seam_div0.c.

References s_div0_fault_pc.

Referenced by div0_synth_usagefault().

◆ emu_div0_fault_pending()

bool emu_div0_fault_pending ( void )

Whether a trapping divide-by-zero is latched for the run loop.

Returns
true while a div-0 UsageFault awaits synthesis.
Return values
falseNo divide-by-zero is pending.
Precondition
None.
None.
Postcondition
No state is modified.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

Whether a trapping divide-by-zero is latched for the run loop; this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

Whether a trapping divide-by-zero is latched for the run loop.

Definition at line 462 of file emu_seam_div0.c.

References s_div0_fault.

Referenced by internal_run_inner_take_exception().

◆ emu_fast_sd_enable()

void emu_fast_sd_enable ( void )

Opt in to the –fast-sd block-serving seam for this run.

Returns
Nothing.
Precondition
Called from the CLI parser before the seams are installed.
None otherwise.
Postcondition
fast_sd_seam_install() will arm the block hook when possible.
Note
Not thread-safe; single-threaded setup only.
Since
0.1.0

Opt in to the –fast-sd block-serving seam for this run; this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

Opt in to the –fast-sd block-serving seam for this run.

Definition at line 149 of file emu_seam_sd.c.

References s_fast_sd.

Referenced by internal_args_try_mode().

◆ emu_insn_seams_install()

void emu_insn_seams_install ( uc_engine * uc)

Arm the invalid-instruction dispatcher on the engine.

Returns
Nothing.
Precondition
uc is initialised (no code has run yet).
Called once during setup, before any other hook that relies on the dispatcher's seams.
Postcondition
The UC_HOOK_INSN_INVALID hook is installed for the whole run.
Note
Not thread-safe; call once during single-threaded setup.
Since
0.1.0

Arm the invalid-instruction dispatcher on the engine; this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Parameters
[in,out]ucUnicorn engine whose emulated state is read or updated.
Postcondition
Ownership of caller-supplied storage is unchanged.

Arm the invalid-instruction dispatcher on the engine.

Definition at line 586 of file emu_insn_seams.c.

References on_invalid_insn().

Referenced by internal_main_install_core_seams().

◆ emu_lob_emulated_count()

uint64_t emu_lob_emulated_count ( void )

Count of LOB (DLS/LE) instructions emulated this run (telemetry).

Returns
Emulated low-overhead-branch instruction count.
Return values
0No LOB instruction was emulated.
Precondition
None.
None.
Postcondition
No state is modified.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

Count of lob (dls/le) instructions emulated this run (telemetry); this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

Count of LOB (DLS/LE) instructions emulated this run (telemetry).

Definition at line 593 of file emu_insn_seams.c.

References s_lob_emulated.

Referenced by internal_run_print_stop_summary().

◆ emu_mve_emulated_count()

uint64_t emu_mve_emulated_count ( void )

Count of MVE instructions emulated this run (run-end telemetry).

Returns
Emulated MVE instruction count.
Return values
0No MVE instruction was emulated (e.g. an M33 image).
Precondition
None.
None.
Postcondition
No state is modified.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

Count of mve instructions emulated this run (run-end telemetry); this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.

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().

◆ emu_mve_nocp_emulate()

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.

Parameters
[in,out]ucUnicorn engine.
[in]pcAddress of the faulting instruction.
Returns
true iff an MVE contiguous load/store was decoded and performed.
Return values
trueThe access happened and PC advanced by four bytes.
falseNot this family; no engine state changed.
Precondition
uc is stopped inside the UC_HOOK_INTR NoCP fault.
pc is the faulting instruction address, not an EXC_RETURN magic.
Postcondition
On true, PC points at the next instruction.
On false, no engine or memory state changed.
Note
Not thread-safe; the emulator is single-threaded host-side.
See also
emulate_mve() Handles the MVE forms that do trap as invalid.
Since
0.1.0

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().

◆ emu_mve_nocp_spurious()

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.

Parameters
[in]pcAddress the invalid-instruction hook trapped at.
Returns
true iff this trap is the expected post-NoCP artefact.
Return values
trueAbsorb it silently and relaunch; nothing is wrong.
falseReport it; the arming (if any) is now cleared.
Precondition
Called only after every real seam in the dispatch chain declined.
Called at most once per invalid-instruction trap.
Postcondition
The one-shot arming is clear on return.
Note
Not thread-safe; the emulator is single-threaded host-side.
See also
emu_mve_nocp_emulate() Arms this.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

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().

◆ emu_mve_nocp_take()

bool emu_mve_nocp_take ( void )

Test and clear the "NoCP fault serviced by the MVE seam" latch.

uc_emu_start returns UC_ERR_EXCEPTION for a NoCP UsageFault even when the interrupt hook handled it completely, so the run loop cannot tell a serviced MVE access from a genuine unhandled exception by status alone. The seam sets this latch on every access it performs; the run loop consumes it at the chunk boundary and relaunches rather than ending the run as a fault.

Returns
true iff a NoCP fault was serviced since the last call.
Return values
trueRelaunch from the advanced PC; the access already happened.
falseNothing was serviced; treat the chunk status at face value.
Precondition
Called once per chunk boundary, from the run loop only.
The engine is stopped at a chunk boundary.
Postcondition
The latch is clear on return.
Note
Not thread-safe; the emulator is single-threaded host-side.
See also
emu_mve_nocp_emulate() Sets the latch.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

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().

◆ emulate_div0_patched()

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.

Called from the invalid-instruction dispatcher. When pc is a patched divide site this recovers the original encoding, reads the operands, and either (a) latches the pending div-0 fault when the divisor is zero and DIV_0_TRP is set – so the run loop synthesises the decoded UsageFault with pc stacked – or (b) computes the quotient in software, writes Rd and steps PC past the 4-byte instruction as a zero-time seam relaunch. A trap at any other address is not ours.

Parameters
[in,out]ucUnicorn engine.
[in]pcAddress of the trapping instruction.
[in]codeThe 4 bytes at pc (the UDF, unused – the original encoding comes from the tracked sites).
Returns
Whether the trap was an armed divide this handler serviced.
Return values
truepc was a patched divide; state (or the fault latch) was updated and the caller should stop + resume.
falsepc is not a patched divide; try the next handler.
Precondition
The tracked sites hold the armed divide addresses.
The PPB CCR word is mapped as RAM.
Postcondition
On a trapping div-0 the pending fault is latched with pc; else Rd and PC are advanced.
Note
Not thread-safe (single engine).
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

Called from on_invalid_insn. Divide-by-zero trapping is modelled by overwriting each divide with UDF once the firmware sets CCR.DIV_0_TRP – unlike a per-site UC_HOOK_CODE this does NOT disable Unicorn's translation-block chaining, so a firmware that arms the trap runs its steady state at the baseline rate. When pc is one of those patched sites this recovers the original encoding, reads the operands, and either (a) latches s_div0_fault when the divisor is zero and DIV_0_TRP is set – so the run loop synthesises the decoded UsageFault with pc stacked – or (b) computes the quotient in software (internal_div0_quotient), writes Rd and steps PC past the 4-byte instruction. A trap at any other address is not ours.

Parameters
[in,out]ucUnicorn engine.
[in]pcAddress of the trapping instruction.
[in]codeThe 4 bytes at pc (the UDF, unused – the original encoding comes from s_div0_site).
Returns
Whether the trap was an armed divide this handler serviced.
Return values
truepc was a patched divide; register/PC state (or the fault latch) was updated and the caller should stop + resume.
falsepc is not a patched divide; try the next handler.
Precondition
s_div0_site[0 .. s_div0_site_n) hold the armed sites.
The PPB CCR word is mapped as RAM.
Postcondition
On a trapping div-0 s_div0_fault is set with pc; else Rd and PC are advanced.
Note
Not thread-safe (single engine).
Since
0.1.0

Definition at line 232 of file emu_seam_div0.c.

References emu_seam_request_relaunch(), div0_site_t::hw1, div0_site_t::hw2, internal_div0_quotient(), internal_udiv_sdiv_decode(), k_arm_reg_id, k_ccr_div_0_trp, k_div0_insn_len, k_scb_ccr, rd32(), s_div0_fault, s_div0_fault_pc, s_div0_site, and s_div0_site_n.

Referenced by internal_dispatch_insn_seam().

◆ emulate_long_shift_reg()

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.

The register form (`lsll r0, r1, ip`) aliases to an ORR.W whose Rm field is SP, which the core refuses outright, so unlike the immediate form it arrives here as a genuine undefined-instruction trap rather than silently mis-executing. Decodes the site, applies the shift to the {RdaHi:RdaLo} pair with correct 64-bit host arithmetic – the amount is the SIGNED low byte of Rm, so a negative value shifts the other way – writes the pair back and advances PC past the 4-byte instruction. Flags are untouched because the aliased ORRS never executed.

Parameters
[in,out]ucUnicorn engine.
[in]pcAddress of the trapping instruction.
[in]codeThe 4 instruction bytes at pc.
Returns
true when code was a register-form long shift and was emulated.
Return values
trueRegisters written and PC advanced; caller should stop+relaunch.
falseNot a register-form long shift; try the next handler.
Precondition
code holds the 4 bytes the core failed to decode at pc.
uc is stopped inside the invalid-instruction hook.
Postcondition
On true, PC is pc + 4 and the register pair holds the result.
On false, no engine state is modified.
Note
Not thread-safe; called from the single-threaded run loop.
See also
long_shift_seam_install() Handles the immediate form, which never traps.
Since
0.1.0

Definition at line 511 of file emu_seam_longshift.c.

References long_shift_insn_t::by_reg, internal_long_shift_amount(), internal_long_shift_apply(), internal_long_shift_decode(), k_arm_reg_id, k_byte_bits, k_lsh_insn_len, k_lsh_word_bits, long_shift_insn_t::op, long_shift_insn_t::rdahi, and long_shift_insn_t::rdalo.

Referenced by internal_dispatch_armv81_seam().

◆ emulate_mve()

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.

Parameters
[in,out]ucUnicorn engine.
[in]pc0Address of the trapped instruction.
[in]code0The 4 instruction bytes already read at pc0.
Returns
true iff at least one MVE instruction was emulated.
Return values
falsecode0 is not a handled MVE form (state untouched).
Precondition
code0 holds the 4 bytes at pc0.
The invalid-instruction hook trapped at pc0.
Postcondition
On true, PC points at the first unhandled instruction.
On false, no engine state changed.
Note
Not thread-safe; the emulator is single-threaded host-side.
See also
emu_mve_nocp_emulate() Handles the MVE loads/stores that raise NoCP.
Since
0.1.0

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.

Returns
true iff at least one MVE instruction was emulated.

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().

◆ fast_sd_seam_install()

void fast_sd_seam_install ( uc_engine * uc,
const emu_elf_source_t * elf )

Install the --fast-sd block-read hook if opted-in and the symbol exists.

Parameters
[in,out]ucActive Unicorn engine.
[in]elfOpen ELF source used for symbol resolution.
Returns
Nothing.
Precondition
uc is initialised and elf remains open.
The SD card model is attached when the fast path should serve blocks.
Postcondition
With the opt-in and the symbol present, a UC_HOOK_CODE serves whole 512-byte blocks at ra8_sdmmc_spi_read_block's entry; otherwise nothing is armed.
Note
A firmware without the symbol (no SD path) is reported once and left on the default per-byte MMIO path.
Since
0.1.0

Install the --fast-sd block-read hook if opted-in and the symbol exists; this step is contained within the emu seams model and uses bounded caller or module-owned storage.

Postcondition
Ownership of caller-supplied storage is unchanged.
Parameters
[in,out]ucActive Unicorn engine.
[in]elfLoaded ELF image (for symbol resolution).
Precondition
uc is initialised and elf is a validated loaded image.
Postcondition
With s_fast_sd and the symbol present, a UC_HOOK_CODE fires internal_on_sdmmc_read_block at the function entry; otherwise nothing is armed.
Note
A firmware without ra8_sdmmc_spi_read_block (no SD path) is reported once and left on the default per-byte MMIO path.
Since
0.1.0

Definition at line 124 of file emu_seam_sd.c.

References elf_sym_addr(), internal_on_sdmmc_read_block(), priv_emu_io_errf(), and s_fast_sd.

Referenced by internal_main_install_run_seams().

◆ long_shift_seam_install()

void long_shift_seam_install ( uc_engine * uc,
const emu_elf_source_t * elf )

Scan the loaded image and install a hook at every immediate long-shift.

Walks the ELF32 PT_LOAD program headers, and for each executable segment scans its bytes on 2-byte boundaries for the LSLL/LSRL/ASRL encoding. A targeted UC_HOOK_CODE is installed at each site's VMA so the shift is applied on the host – where 64-bit arithmetic is correct – before the core silently mis-executes the overlapping ORR.W encoding. Matches use the segment's VMA (p_vaddr), so a .sram_text ramfunc region is hooked at its execution address even though it is not yet copied at install time. A scan false-positive is harmless: the core never starts execution there, so the hook never fires. Zero hooks – hence zero steady-state cost – for firmware that contains no long shifts.

Parameters
[in,out]ucUnicorn engine to install the hooks on.
[in]elfOpen immutable ELF source.
Returns
Nothing.
Precondition
elf is a 32-bit ARM ELF (already validated by load_elf).
The M85 profile is selected (the caller gates on the primary core).
Postcondition
One UC_HOOK_CODE per long-shift site is armed (up to the site cap).
One injected error sink summary line is printed when any site was hooked.
Note
Not thread-safe; call once during setup before the run loop.
See also
div0_seam_install() The companion image-scan seam.
Since
0.1.0

Walks the ELF32 PT_LOAD program headers, and for each executable segment scans its bytes on 2-byte boundaries for the long-shift encoding (internal_long_shift_decode). A targeted UC_HOOK_CODE is installed at each site's VMA, and at the following instruction, so internal_on_long_shift can emulate it. Matches use the segment's VMA (p_vaddr), so a .sram_text ramfunc region is hooked at its execution address even though it is not yet copied at install time. A scan false-positive (a halfword pair inside data or mid-instruction that happens to match) is harmless: the core never starts execution there, so the hook never fires, and the seam never rewrites the image. Zero hooks – hence zero steady-state cost – for firmware that contains no long shifts.

Parameters
[in,out]ucUnicorn engine to install the hooks on.
[in]elfIn-memory ELF image (still alive at call time).
Returns
Nothing.
Precondition
elf is a 32-bit ARM ELF (already validated by load_elf).
Postcondition
One UC_HOOK_CODE pair per long-shift site (up to k_lsh_sites_max) is armed.
Note
Not thread-safe; call once during setup before the run loop.
Since
0.1.0

Definition at line 675 of file emu_seam_longshift.c.

References elf_foreach_exec_segment(), internal_long_shift_segment(), memset(), priv_emu_io_errf(), s_lsh_pending, and s_lsh_site_count.

Referenced by internal_main_install_run_seams().

◆ on_invalid_insn()

bool on_invalid_insn ( uc_engine * uc,
void * user )

UC_HOOK_INSN_INVALID dispatcher: service or report a trapped opcode.

Tries each trap-path seam in a fixed order – the armed div-0 UDF service first (it matches only exact patched addresses), then the Armv8-M security scrubs, the conditional-select family, the barrier NOPs, the MVE run consumer and the low-overhead-branch loops. A handled seam edits PC and stops the engine (the run loop relaunches on valid code); anything else is disassembled and reported, then the engine stops with the invalid-opcode error.

Parameters
[in,out]ucUnicorn engine at the trapped instruction.
[in]userHook user pointer (unused; Unicorn ABI).
Returns
true when a seam handled the instruction, false to fault.
Return values
truePC advanced past the emulated instruction; engine stopped.
falseGenuinely invalid: report printed, emulation errors out.
Precondition
The engine trapped an instruction its core cannot decode.
The seams were installed for this run.
Postcondition
On true, engine state reflects the emulated instruction.
Note
Not thread-safe; the emulator is single-threaded host-side.
See also
emu_insn_seams_install() Arms this dispatcher.
Since
0.1.0
Postcondition
Ownership of caller-supplied storage is unchanged.

UC_HOOK_INSN_INVALID dispatcher: service or report a trapped opcode.

Definition at line 560 of file emu_insn_seams.c.

References emu_mem_read(), emu_mve_nocp_spurious(), internal_dispatch_insn_seam(), and internal_report_unhandled_insn().

Referenced by emu_insn_seams_install().