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

Divide-by-zero UsageFault (CCR.DIV_0_TRP) CPU-model seam. More...

#include <stdio.h>
#include <string.h>
#include "emu_elf.h"
#include "emu_elf_source_internal.h"
#include "emu_engine.h"
#include "emu_exc.h"
#include "emu_host_io_internal.h"
#include "emu_seams.h"
Include dependency graph for emu_seam_div0.c:

Go to the source code of this file.

Data Structures

struct  div0_site_t
 One tracked UDIV/SDIV site: its address and original encoding halfwords. More...

Enumerations

enum  : uint32_t { k_div0_sites_max = 4096U }
 Div-0 seam sizing: max tracked divide sites (real counts tiny). More...

Functions

static RA8_INTERNAL bool internal_udiv_sdiv_decode (uint16_t hw1, uint16_t hw2, uint32_t *out_rn, uint32_t *out_rd, uint32_t *out_rm, bool *out_signed)
 Decode a Thumb-2 halfword pair as UDIV/SDIV, recovering all operands.
static RA8_INTERNAL uint32_t internal_div0_quotient (uint32_t vn, uint32_t vm, bool is_signed)
 Compute a UDIV/SDIV quotient with the Arm div-by-zero + overflow rules.
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.
void div0_patch_sites (uc_engine *uc)
 Overwrite every tracked divide with UDF so divide-by-zero can trap.
static RA8_INTERNAL bool internal_div0_scan_segment (const elf_exec_segment_t *seg, void *ctx)
 Record every UDIV/SDIV site in one executable segment.
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_synth_usagefault (uc_engine *uc, uint32_t vtor_base)
 Synthesise a UsageFault (#6) for a trapped divide-by-zero.
bool emu_div0_fault_pending (void)
 Implementation of emu_div0_fault_pending() – plain flag read.
void emu_div0_clear_fault (void)
 Implementation of emu_div0_clear_fault() – plain flag clear.
uint32_t emu_div0_fault_pc (void)
 Implementation of emu_div0_fault_pc() – plain state read.
void emu_div0_count_trap (void)
 Implementation of emu_div0_count_trap() – run-end telemetry bump.
void emu_div0_disarm (void)
 Implementation of emu_div0_disarm() – warm reboot un-patches sites.

Variables

static bool s_div0_fault
 Trapping div-0 latched (the run loop synthesises the UsageFault).
static uint32_t s_div0_fault_pc
 PC of the divide that trapped (stacked by the synthesised fault).
static uint64_t s_div0_traps
 Count of divide-by-zero UsageFaults synthesised this run.
static const uint8_t s_k_div0_udf [k_div0_insn_len]
 UDF.W #0 – a permanently-undefined 32-bit Thumb-2 instruction (LE bytes).
static div0_site_t s_div0_site [k_div0_sites_max]
 Tracked divide sites.
static uint32_t s_div0_site_n
 Count of tracked sites.
static bool s_div0_armed
 Armed sites overwritten UDF.

Detailed Description

Divide-by-zero UsageFault (CCR.DIV_0_TRP) CPU-model seam.

Unicorn's Cortex-M core executes UDIV/SDIV with the Arm default divide-by-zero result (quotient 0) and never raises the UsageFault real silicon takes when CCR.DIV_0_TRP is set. This seam tracks every UDIV/SDIV site at setup and – only once the firmware opts in by setting DIV_0_TRP – overwrites those sites with UDF so each divide traps through the invalid-instruction hook, which either latches the decoded UsageFault (zero divisor) or emulates the divide in software and continues. Moved verbatim out of the ra8_emulator main translation unit.

Since
0.1.0

Definition in file emu_seam_div0.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum : uint32_t

Div-0 seam sizing: max tracked divide sites (real counts tiny).

Enumerator
k_div0_sites_max 

Div0 sites maximum.

Definition at line 86 of file emu_seam_div0.c.

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

Implementation of emu_div0_clear_fault() – plain flag clear.

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 )

Implementation of emu_div0_count_trap() – run-end telemetry bump.

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 )

Implementation of emu_div0_disarm() – warm reboot un-patches sites.

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 )

Implementation of emu_div0_fault_pc() – plain state read.

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 )

Implementation of emu_div0_fault_pending() – plain flag read.

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

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

◆ internal_div0_quotient()

RA8_INTERNAL uint32_t internal_div0_quotient ( uint32_t vn,
uint32_t vm,
bool is_signed )
static

Compute a UDIV/SDIV quotient with the Arm div-by-zero + overflow rules.

Parameters
[in]vnDividend value.
[in]vmDivisor value (already known non-trapping: non-zero, or DIV_0_TRP clear so a zero divisor yields the Arm default 0).
[in]is_signedTrue for SDIV, false for UDIV.
Returns
The 32-bit quotient the core would have produced.
Return values
0When vm is zero (Arm default divide-by-zero result).
Precondition
None.
None.
Postcondition
No state is mutated (pure).
Signed INT32_MIN / -1 saturates to INT32_MIN (Arm SDIV overflow rule).
Note
Thread safety: pure; thread-safe.
Since
0.1.0

Compute a udiv/sdiv quotient with the arm div-by-zero + overflow rules; this step is contained within the emu seam div0 model and uses bounded caller or module-owned storage.

Definition at line 187 of file emu_seam_div0.c.

References k_div0_int32_min, and RA8_INTERNAL.

Referenced by emulate_div0_patched().

◆ internal_div0_scan_segment()

RA8_INTERNAL bool internal_div0_scan_segment ( const elf_exec_segment_t * seg,
void * ctx )
static

Record every UDIV/SDIV site in one executable segment.

Steps the segment a halfword at a time, since Thumb-2 instructions are halfword-aligned and a 32-bit encoding may start at any even offset. Sites are only recorded here; arming (the UDF overwrite) happens when the firmware sets CCR.DIV_0_TRP.

Parameters
[in]segSegment to scan.
[in]ctxUnused; the site table is file-scope state.
Returns
True to continue with the next segment, false once the site cap is reached (there is no point scanning further).
Return values
trueScanning may continue with the next executable segment.
falseThe fixed site cap or a source read stopped scanning.
Precondition
seg is non-NULL.
The segment source remains open and its file range is bounded.
Postcondition
s_div0_site_n is bounded by k_div0_sites_max.
Each recorded site carries its virtual address and both halfwords.
Note
Not thread-safe; the emulator is single-threaded host-side.
Since
0.1.0

< Transient instruction-scan bytes.

Definition at line 339 of file emu_seam_div0.c.

References elf_exec_segment_t::filesz, internal_udiv_sdiv_decode(), k_div0_insn_len, k_div0_sites_max, k_emu_elf_io_ok, elf_exec_segment_t::offset, priv_emu_elf_read(), priv_emu_io_errf(), RA8_INTERNAL, s_div0_site, s_div0_site_n, elf_exec_segment_t::source, emu_elf_io_result_t::status, and elf_exec_segment_t::vaddr.

Referenced by div0_seam_install().

◆ internal_udiv_sdiv_decode()

RA8_INTERNAL bool internal_udiv_sdiv_decode ( uint16_t hw1,
uint16_t hw2,
uint32_t * out_rn,
uint32_t * out_rd,
uint32_t * out_rm,
bool * out_signed )
static

Decode a Thumb-2 halfword pair as UDIV/SDIV, recovering all operands.

Matches the T1 encodings 1111 1011 1011 Rn : 1111 Rd 1111 Rm (UDIV) and 1111 1011 1001 Rn : 1111 Rd 1111 Rm (SDIV) from the Armv8-M Architecture Reference Manual, recovering Rn (dividend), Rd (destination), Rm (divisor) and whether the divide is signed. The two fixed nibbles in hw2 ([15:12] and [7:4] == 1111) are verified so a scan false-positive on data cannot be taken as a divide.

The architecture makes UDIV/SDIV with d, n, or m == 13 (SP) or 15 (PC) UNPREDICTABLE, so a real compiler never emits those encodings; a match with any of Rn/Rd/Rm in {13, 15} is therefore a scan false-positive. This matters because the sweep runs on 2-byte boundaries: the high nibbles of an adjacent BL / B.W pair (each halfword 0xFxxx) read as "SDIV ..., ..., r15", and if that window were armed, div0_patch_sites would overwrite the two straddled real branches with UDF – corrupting executed code, not an unreachable divide. Rejecting the reserved register operands keeps the seam matching only the encodings the core can actually take.

Parameters
[in]hw1First (low-address) instruction halfword.
[in]hw2Second instruction halfword.
[out]out_rnDividend register index [0, 15] on a match.
[out]out_rdDestination register index [0, 15] on a match.
[out]out_rmDivisor register index [0, 15] on a match.
[out]out_signedTrue for SDIV, false for UDIV, on a match.
Returns
Whether hw1 / hw2 encode a UDIV or SDIV.
Return values
trueA divide was decoded; all outputs are valid.
falseNot a divide; the outputs are untouched.
Precondition
All output pointers are non-NULL.
hw1 / hw2 are the two halfwords of one candidate site.
Postcondition
The outputs are written only when true is returned.
No engine or global state is mutated (pure).
Note
Thread safety: pure; thread-safe.
Since
0.1.0

Definition at line 132 of file emu_seam_div0.c.

References k_div0_hw1_mask, k_div0_hw1_sdiv, k_div0_hw1_udiv, k_div0_hw2_fixed, k_div0_hw2_mask, k_div0_rd_shift, k_div0_reg_mask, k_div0_reg_pc, k_div0_reg_sp, and RA8_INTERNAL.

Referenced by emulate_div0_patched(), and internal_div0_scan_segment().

Variable Documentation

◆ s_div0_armed

bool s_div0_armed
static

Armed sites overwritten UDF.

Definition at line 91 of file emu_seam_div0.c.

Referenced by div0_patch_sites(), div0_seam_install(), and emu_div0_disarm().

◆ s_div0_fault

bool s_div0_fault
static

Trapping div-0 latched (the run loop synthesises the UsageFault).

Definition at line 31 of file emu_seam_div0.c.

Referenced by emu_div0_clear_fault(), emu_div0_fault_pending(), and emulate_div0_patched().

◆ s_div0_fault_pc

uint32_t s_div0_fault_pc
static

PC of the divide that trapped (stacked by the synthesised fault).

Definition at line 34 of file emu_seam_div0.c.

Referenced by emu_div0_fault_pc(), and emulate_div0_patched().

◆ s_div0_site

div0_site_t s_div0_site[k_div0_sites_max]
static

Tracked divide sites.

Definition at line 89 of file emu_seam_div0.c.

Referenced by div0_patch_sites(), emulate_div0_patched(), and internal_div0_scan_segment().

◆ s_div0_site_n

uint32_t s_div0_site_n
static

Count of tracked sites.

Definition at line 90 of file emu_seam_div0.c.

Referenced by div0_patch_sites(), div0_seam_install(), emulate_div0_patched(), and internal_div0_scan_segment().

◆ s_div0_traps

uint64_t s_div0_traps
static

Count of divide-by-zero UsageFaults synthesised this run.

Definition at line 37 of file emu_seam_div0.c.

Referenced by emu_div0_count_trap().

◆ s_k_div0_udf

const uint8_t s_k_div0_udf[k_div0_insn_len]
static
Initial value:
= {
(uint8_t)k_div0_udf_b0,
(uint8_t)k_div0_udf_b1,
0U,
(uint8_t)k_div0_udf_b3,
}
@ k_div0_udf_b0
UDF.W #0 little-endian byte 0.
Definition emu_seams.h:243
@ k_div0_udf_b1
UDF.W #0 little-endian byte 1.
Definition emu_seams.h:244
@ k_div0_udf_b3
UDF.W #0 little-endian byte 3.
Definition emu_seams.h:245

UDF.W #0 – a permanently-undefined 32-bit Thumb-2 instruction (LE bytes).

Armv8-M UDF.W #0 is 0xF7F0A000; stored little-endian it is the byte sequence written over an armed divide so its execution raises the undefined-instruction trap that emulate_div0_patched services. Byte 2 is 0.

Definition at line 66 of file emu_seam_div0.c.

Referenced by div0_patch_sites().