33typedef enum : uint32_t {
134 wr32(uc, (uint64_t)sp + 0U,
reg_get(uc, UC_ARM_REG_R0));
135 wr32(uc, (uint64_t)sp + 4U,
reg_get(uc, UC_ARM_REG_R1));
136 wr32(uc, (uint64_t)sp + 8U,
reg_get(uc, UC_ARM_REG_R2));
138 wr32(uc, (uint64_t)sp + 16U,
reg_get(uc, UC_ARM_REG_R12));
146 for (uint32_t i = 0U; i < (uint32_t)
k_fp_s_words; i++) {
149 reg_get(uc, UC_ARM_REG_S0 + (
int)i));
172 }
else if (use_psp) {
210void exc_enter(uc_engine* uc, uint32_t exc_num, uint32_t handler)
212 const uint32_t xpsr_in =
reg_get(uc, UC_ARM_REG_XPSR);
213 const uint32_t control =
reg_get(uc, UC_ARM_REG_CONTROL);
215 const bool use_psp = in_thread && ((control & (uint32_t)
k_control_spsel) != 0U);
216 const bool fp_active = (control & (uint32_t)
k_control_fpca) != 0U;
218 const int sp_reg = use_psp ? UC_ARM_REG_PSP : UC_ARM_REG_MSP;
219 uint32_t sp =
reg_get(uc, sp_reg);
223 uint32_t frame_xpsr = xpsr_in;
224 if ((sp & 0x4U) != 0U) {
246 uint32_t handler_xpsr =
249 reg_set(uc, UC_ARM_REG_XPSR, handler_xpsr);
250 reg_set(uc, UC_ARM_REG_LR, exc_ret);
251 reg_set(uc, UC_ARM_REG_PC, handler & ~1U);
272 for (uint32_t i = 0U; i < (uint32_t)
k_fp_s_words; i++) {
275 UC_ARM_REG_S0 + (
int)i,
296 uint32_t control =
reg_get(uc, UC_ARM_REG_CONTROL);
297 if (to_thread && to_psp) {
302 reg_set(uc, UC_ARM_REG_CONTROL, control);
308 reg_set(uc, UC_ARM_REG_XPSR, new_xpsr);
338 const bool to_thread = (exc_ret & (uint32_t)
k_exc_ret_mode) != 0U;
340 const int sp_reg = to_psp ? UC_ARM_REG_PSP : UC_ARM_REG_MSP;
341 uint32_t sp =
reg_get(uc, sp_reg);
343 const uint32_t r0 =
rd32(uc, (uint64_t)sp + 0U);
344 const uint32_t r1 =
rd32(uc, (uint64_t)sp + 4U);
345 const uint32_t r2 =
rd32(uc, (uint64_t)sp + 8U);
347 const uint32_t r12 =
rd32(uc, (uint64_t)sp + 16U);
365 reg_set(uc, UC_ARM_REG_R0, r0);
366 reg_set(uc, UC_ARM_REG_R1, r1);
367 reg_set(uc, UC_ARM_REG_R2, r2);
368 reg_set(uc, UC_ARM_REG_R3, r3);
369 reg_set(uc, UC_ARM_REG_R12, r12);
370 reg_set(uc, UC_ARM_REG_LR, lr);
375 reg_set(uc, UC_ARM_REG_SP,
reg_get(uc, to_psp && to_thread ? UC_ARM_REG_PSP : UC_ARM_REG_MSP));
376 reg_set(uc, UC_ARM_REG_PC, pc & ~1U);
417uint32_t
exc_vector(uc_engine* uc, uint32_t vtor_base, uint32_t exc_num)
423 const uint32_t handler =
rd32(uc, (uint64_t)vtor + ((uint64_t)exc_num * 4U)) & ~1U;
464 const uint32_t prio =
466 if (prio >= active) {
474 const uint64_t ispr_word = (uint64_t)
k_nvic_ispr_base + ((uint64_t)(irq / 32U) * 4U);
475 uint32_t ispr =
rd32(uc, ispr_word);
476 ispr &= ~(1U << (irq % 32U));
477 wr32(uc, ispr_word, ispr);
532 const uint32_t primask =
reg_get(uc, UC_ARM_REG_PRIMASK);
533 if ((primask & 1U) != 0U) {
591 if (type == UC_MEM_READ_UNMAPPED) {
594 if (type == UC_MEM_WRITE_UNMAPPED) {
630 (void)uc_emu_stop(uc);
633 (void)
priv_emu_io_errf(
" UNMAPPED %s @ 0x%08llX (extend the memory/peripheral map)\n",
635 (
unsigned long long)addr);
668 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
669 (void)uc_emu_stop(uc);
674 (void)uc_reg_read(uc, UC_ARM_REG_SP, &sp);
677 const uint32_t zero = 0U;
683 (void)uc_reg_write(uc, UC_ARM_REG_SP, &sp);
684 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
685 (void)uc_emu_stop(uc);
717 (void)uc_emu_stop(uc);
756 (void)uc_reg_read(uc, UC_ARM_REG_PC, &pc);
767 (void)uc_emu_stop(uc);
774 (void)uc_emu_stop(uc);
788 (void)
emu_mem_read(uc, (uint64_t)pc, &insn,
sizeof(insn));
810 (void)uc_emu_stop(uc);
865 uint32_t primask = 0U;
866 (void)uc_reg_read(uc, UC_ARM_REG_PRIMASK, &primask);
867 if ((primask & 1U) != 0U) {
880 (void)uc_reg_read(uc, UC_ARM_REG_PC, &pc);
885 uint32_t next = pc + step;
886 (void)uc_reg_write(uc, UC_ARM_REG_PC, &next);
894 (void)uc_emu_stop(uc);
900 static uc_hook s_h_unmapped;
901 static uc_hook s_h_intr;
902 static uc_hook s_h_icsr;
903 (void)uc_hook_add(uc,
905 UC_HOOK_MEM_UNMAPPED,
912 (void)uc_hook_add(uc, &s_h_intr, UC_HOOK_INTR, (
void*)
internal_on_intr,
nullptr, 1, 0);
916 (void)uc_hook_add(uc,
Register-accurate peripheral-model framework for the board emulator.
void board_periph_note_irq_taken(uint32_t irq)
Record that NVIC IRQ irq was actually taken (for the summary).
bool board_periph_next_irq(uint32_t *out_irq)
Pop the next pending, enabled NVIC IRQ number the ICU has queued.
Emulator text-console surfaces: UART echo, ITM/SWO echo, escapes.
Shared Unicorn engine access utilities for the board emulator.
static uint32_t reg_get(uc_engine *uc, int reg)
Read a Unicorn 32-bit register by its UC_ARM_REG_* id.
static uint32_t rd32(uc_engine *uc, uint64_t addr)
Read a 32-bit little-endian word from emulated memory.
static void wr32(uc_engine *uc, uint64_t addr, uint32_t v)
Write a 32-bit little-endian word to emulated memory.
static void reg_set(uc_engine *uc, int reg, uint32_t v)
Write a Unicorn 32-bit register by its UC_ARM_REG_* id.
emu_exc_decode_t
Thumb decode + idle-spin scan constants for this exception engine.
bool emu_exc_take_exc_return(uint64_t *out_pc)
Implementation of emu_exc_take_exc_return() – read + clear latch.
void emu_exc_arm_systick(void)
Implementation of emu_exc_arm_systick() – pend the periodic tick.
void emu_exc_clear_pendsv_stop(void)
Implementation of emu_exc_clear_pendsv_stop() – per-launch clear.
static RA8_INTERNAL void internal_exc_restore_fp_frame(uc_engine *uc, uint32_t sp)
Restore the Armv8-M FP extended frame (S0-S15 + FPSCR) from sp.
static RA8_INTERNAL bool internal_on_intr_bkpt(uc_engine *uc, uint32_t pc, uint32_t insn)
Model a firmware BKPT as a halt (record the site and stop).
static RA8_INTERNAL bool internal_on_intr_sec_insn(uc_engine *uc, uint32_t pc, uint32_t insn)
Model the Armv8-M Security-Extension opcodes Unicorn's M33 lacks.
static uint32_t s_exc_depth
Number of active handlers.
static RA8_INTERNAL uint32_t internal_exc_active_prio(void)
Priority value (lower = higher) of the active handler, or sentinel.
static uint32_t s_svc_takes
SVCall exceptions taken.
static uint64_t s_exc_return_pc
Pending EXC_RETURN to unstack.
static RA8_INTERNAL bool internal_on_unmapped(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user)
Hook fired on access to unmapped memory (peripheral surface gap).
static RA8_INTERNAL bool internal_is_exc_return(uint64_t pc)
True if pc is an EXC_RETURN magic value.
bool emu_exc_bkpt_hit(void)
Implementation of emu_exc_bkpt_hit() – plain flag read.
static uint32_t s_exc_stack[k_exc_nest_max]
Active-handler priorities.
static RA8_INTERNAL void internal_exc_stack_frame(uc_engine *uc, uint32_t sp, bool fp_active, uint32_t frame_xpsr)
Stack the basic {R0-R3,R12,LR,PC,xPSR} frame + optional FP frame at sp.
uint32_t exc_vector(uc_engine *uc, uint32_t vtor_base, uint32_t exc_num)
Read the handler address for an exception from the vector table.
static RA8_INTERNAL bool internal_exc_take_periph_irq(uc_engine *uc, uint32_t vtor_base, uint32_t active)
Take one pending peripheral NVIC IRQ the ICU has queued, if allowed.
static RA8_INTERNAL const char * internal_unmapped_access_kind(uc_mem_type type)
Name the access kind that hit an unmapped address.
bool emu_exc_pendsv_stop(void)
Implementation of emu_exc_pendsv_stop() – plain flag read.
static bool s_pendsv_stop
Chunk ended on a PENDSVSET.
static RA8_INTERNAL uint32_t internal_exc_return_value(bool in_thread, bool use_psp, bool fp_active)
Select the EXC_RETURN magic for the outgoing mode/stack/FP state.
static bool s_systick_pending
SysTick exception is pended.
uint32_t emu_exc_pendsv_takes(void)
Implementation of emu_exc_pendsv_takes() – plain counter read.
void exc_enter(uc_engine *uc, uint32_t exc_num, uint32_t handler)
Enter a Cortex-M exception: stack the basic frame and vector in.
static uint32_t s_pendsv_takes
PendSV exceptions taken.
uint32_t emu_exc_bkpt_pc(void)
Implementation of emu_exc_bkpt_pc() – plain state read.
static RA8_INTERNAL void internal_on_intr(uc_engine *uc, uint32_t int_no, void *user_data)
UC_HOOK_INTR handler: take the SVCall exception on an svc opcode.
void exc_return(uc_engine *uc, uint32_t exc_ret)
Perform a Cortex-M exception return for an observed EXC_RETURN branch.
static RA8_INTERNAL void internal_exc_restore_mode(uc_engine *uc, uint32_t xpsr, bool to_thread, bool to_psp)
Restore CONTROL.SPSEL + xPSR/IPSR for the returned-to context.
void emu_exc_install_core(uc_engine *uc)
Implementation of emu_exc_install_core() – unmapped/INTR/ICSR hooks.
static RA8_INTERNAL uint32_t internal_exc_priority(uc_engine *uc, uint32_t exc_num)
Read a system-handler priority byte from an SHPR register.
uint32_t emu_exc_svc_takes(void)
Implementation of emu_exc_svc_takes() – plain counter read.
bool exc_take_pending(uc_engine *uc, uint32_t vtor_base, bool allow_systick)
Take the highest-priority pending exception, if one may activate now.
void emu_exc_reset(void)
Implementation of emu_exc_reset() – warm-reboot exception state.
static RA8_INTERNAL void internal_on_icsr_write(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user)
UC_HOOK_MEM_WRITE handler for SCB ICSR – take PendSV promptly.
uint32_t emu_exc_systick_fires(void)
Implementation of emu_exc_systick_fires() – plain counter read.
static uint32_t s_systick_fires
static bool s_exc_return_hit
An EXC_RETURN branch was seen.
static uint32_t s_bkpt_pc
PC of the BKPT that halted.
static bool s_bkpt_hit
Firmware executed a BKPT.
Cortex-M exception model constants and interfaces for ra8_emulator.
@ k_fp_s_words
S0-S15 saved in the FP extended frame.
@ k_xpsr_t_bit
xPSR.T (Thumb) – must stay set.
@ k_exc_ret_spsel
EXC_RETURN bit2: return stack = PSP.
@ k_thumb2_insn_bytes
32-bit Thumb-2 instruction width.
@ k_frame_off_xpsr
Basic exception-frame offset of xPSR.
@ k_control_spsel
CONTROL.SPSEL: thread SP = PSP.
@ k_exc_ret_mode
EXC_RETURN bit3: return to Thread.
@ k_xpsr_ipsr_mask
xPSR[8:0] = IPSR (active exception).
@ k_fpcxtns_pop
VLDR FPCXTNS,[sp],#4 (LE word).
@ k_lo4_mask
Low nibble (register / cond field).
@ k_frame_off_lr
Basic exception-frame offset of LR.
@ k_exc_ret_ftype
EXC_RETURN bit4: 1 = basic, 0 = FP.
@ k_exc_prio_max
Lowest configurable priority value.
@ k_frame_off_pc
Basic exception-frame offset of PC.
@ k_frame_off_fpscr
FP-frame offset of FPSCR (32 + 16*4).
@ k_exc_frame_bytes
8 words * 4 bytes.
@ k_lo16_mask
Low halfword of a 32-bit fetch.
@ k_exc_ret_handler
Return to Handler mode, MSP.
@ k_exc_ret_psp
Return to Thread mode, PSP.
@ k_fpcxtns_push
VSTR FPCXTNS,[sp,#-4]! (LE word).
@ k_exc_ret_v8_mask
Armv8-M EXC_RETURN prefix: bits[31:7].
@ k_nvic_prio_shift
Implemented priority is the 4 MSBs.
@ k_vector_erased
Erased-flash / invalid vector word.
@ k_exc_ret_msp
Return to Thread mode, MSP.
@ k_frame_off_r3
Basic exception-frame offset of R3.
@ k_fp_frame_extra
FP ext frame above basic: S0-15+FPSCR.
@ k_bkpt_hw_base
BKPT #imm8 halfword (imm free).
@ k_exc_nest_max
Tracked active-exception nesting cap.
@ k_byte_bits
Bits per byte (SHPR field width).
@ k_word_bytes
One stacked word.
@ k_frame_off_s0
FP-frame offset of S0 (above basic).
@ k_control_fpca
CONTROL.FPCA: FP context is active.
@ k_armv8m_sg_opcode
Armv8-M SG secure-gateway opcode.
@ k_bkpt_hw_mask
Mask isolating the BKPT opcode.
@ k_xpsr_align9
xPSR bit9: stack-frame realignment.
@ k_exc_prio_none
Sentinel "no handler active" prio.
@ k_syst_csr
SysTick control/status (SYST_CSR).
@ k_nvic_ipr_base
NVIC IPR priority bytes (one per IRQ).
@ k_scb_shpr2
System handler priority 2 (SVC=b3).
@ k_scb_vtor
Vector table offset register.
@ k_icsr_pendsvset
ICSR.PENDSVSET bit (request PendSV).
@ k_syst_csr_run
ENABLE | TICKINT both set.
@ k_scb_shpr3
System handler priority 3 (PSV/SYT).
@ k_exc_svcall
SVCall exception / vector index.
@ k_exc_pendsv
PendSV exception / vector index.
@ k_exc_systick
SysTick exception / vector index.
@ k_scb_icsr
Interrupt control/state (ICSR).
@ k_exc_irq_vec0
Vector index of IRQ0 (16 + IRQn).
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.
Shared aliased-memory backing and Unicorn memory-map bindings.
uint64_t emu_memmap_mram_base(void)
Return the MRAM boot-vector base.
uc_err emu_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t count)
Read guest memory through the central access seam.
uc_err emu_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t count)
Write guest memory through the central access seam.
@ k_op_wfi
Thumb wfi (wait-for-interrupt).
@ k_idle_loop_max
Largest idle loop (bytes) that may hold PC.
@ k_idle_scan_fwd
Halfwords scanned ahead for a loop edge.
@ k_bn_imm_sext_shl
Shift imm11 bit10 up to bit31 (sign bit).
@ k_bn_imm_sext_shr
Arith >> sign-extends and scales imm by 2.
@ k_op_bn_mask
Mask selecting a Thumb T2 b.n opcode.
@ k_op_branch_self
Thumb "b ." (branch-to-self idle loop).
@ k_op_bn_base
Thumb T2 unconditional b.n base value.
@ k_op_cpsie_i
Thumb cpsie i (re-enable IRQ in a poll).
@ k_op_bn_imm
Thumb T2 b.n imm11 field mask.
@ k_thumb32_op5_min
op5 >= this -> 32-bit instruction.
@ k_thumb_op5_mask
5-bit op5 field.
@ k_thumb_op5_shift
op5 = hw0[15:11].
Armv8.1-M instruction-emulation seams (M85 ops on Unicorn's M33).
bool emu_mve_nocp_emulate(uc_engine *uc, uint32_t pc)
Emulate an MVE contiguous load/store from the NoCP UsageFault.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_nvic_ispr_base
NVIC Interrupt Set-Pending Register array.