|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
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"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. | |
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.
Definition in file emu_seam_div0.c.
| 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.
| 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).
| [in,out] | uc | Unicorn engine whose memory is patched. |
uc permits uc_mem_write to the (host-side) code image. 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().
| 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.
| [in] | elf | In-memory ELF image (still alive at call time). |
elf is a 32-bit ARM ELF (already validated by load_elf). 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().
| 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).
| [in,out] | uc | Unicorn engine. |
| [in] | vtor_base | Fallback vector base if VTOR reads as 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().
| 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().
| 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().
| 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().
| 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().
| 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().
| 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.
| [in,out] | uc | Unicorn engine. |
| [in] | pc | Address of the trapping instruction. |
| [in] | code | The 4 bytes at pc (the UDF, unused – the original encoding comes from s_div0_site). |
| true | pc was a patched divide; register/PC state (or the fault latch) was updated and the caller should stop + resume. |
| false | pc is not a patched divide; try the next handler. |
pc; else Rd and PC are advanced. 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().
|
static |
Compute a UDIV/SDIV quotient with the Arm div-by-zero + overflow rules.
| [in] | vn | Dividend value. |
| [in] | vm | Divisor value (already known non-trapping: non-zero, or DIV_0_TRP clear so a zero divisor yields the Arm default 0). |
| [in] | is_signed | True for SDIV, false for UDIV. |
| 0 | When vm is zero (Arm default divide-by-zero result). |
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().
|
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.
| [in] | seg | Segment to scan. |
| [in] | ctx | Unused; the site table is file-scope state. |
| true | Scanning may continue with the next executable segment. |
| false | The fixed site cap or a source read stopped scanning. |
seg is non-NULL. < 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().
|
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.
| [in] | hw1 | First (low-address) instruction halfword. |
| [in] | hw2 | Second instruction halfword. |
| [out] | out_rn | Dividend register index [0, 15] on a match. |
| [out] | out_rd | Destination register index [0, 15] on a match. |
| [out] | out_rm | Divisor register index [0, 15] on a match. |
| [out] | out_signed | True for SDIV, false for UDIV, on a match. |
hw1 / hw2 encode a UDIV or SDIV. | true | A divide was decoded; all outputs are valid. |
| false | Not a divide; the outputs are untouched. |
hw1 / hw2 are the two halfwords of one candidate site. 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().
|
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().
|
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().
|
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().
|
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().
|
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().
|
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().
|
static |
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().