@@ -56,6 +56,22 @@ extern void (* const IV[])(void);
5656extern void main (void );
5757extern void reloc_trap_vector (const uint32_t * address );
5858
59+ #if defined(WOLFBOOT_RISCV_MMODE ) && defined(WOLFBOOT_MMODE_SMODE_BOOT )
60+ /* Minimal SBI runtime (src/riscv_sbi.c): services S-mode ecalls and the
61+ * M-mode timer/software interrupts that back the S-mode timer and IPIs. */
62+ extern unsigned long sbi_handle_ecall (unsigned long * regs , unsigned long epc );
63+ extern void sbi_timer_irq (void );
64+ extern void sbi_ipi_irq (unsigned long hartid );
65+ extern unsigned long sbi_illegal_insn (unsigned long * regs , unsigned long epc ,
66+ unsigned long tval );
67+ extern unsigned long sbi_misaligned_ldst (unsigned long * regs ,
68+ unsigned long epc ,
69+ unsigned long tval ,
70+ unsigned long cause );
71+ extern void sbi_mscratch_init (unsigned long hartid );
72+ extern void sbi_hart_mark_started (unsigned long hartid );
73+ #endif
74+
5975/* Trap state saved for debugging */
6076#if __riscv_xlen == 64
6177static uint64_t last_cause = 0 , last_epc = 0 , last_tval = 0 ;
@@ -133,20 +149,100 @@ static void handle_external_interrupt(void)
133149}
134150#endif /* PLIC_BASE */
135151
152+ /* Legacy 3-arg weak hook. The asm trap entry now calls handle_trap_ex (the
153+ * real dispatcher); it forwards here for interrupts it does not handle itself
154+ * (after the in-tree SBI/PLIC handling) and for synchronous exceptions before
155+ * halting. An out-of-tree override that returns a resume epc different from
156+ * the faulting one keeps the extension point pre-dispatcher wolfBoot had; the
157+ * weak default returns epc unchanged, so the in-tree path prints and halts. */
136158unsigned long WEAKFUNCTION handle_trap (unsigned long cause , unsigned long epc ,
137159 unsigned long tval )
160+ {
161+ (void )cause ;
162+ (void )tval ;
163+ return epc ;
164+ }
165+
166+ /* Regs-aware trap dispatch -- called directly from src/vector_riscv.S.
167+ * Override this (also weak) to take full control including the saved
168+ * register frame. Falls through to weak handle_trap so legacy 3-arg
169+ * overrides still run. */
170+ unsigned long WEAKFUNCTION handle_trap_ex (unsigned long cause , unsigned long epc ,
171+ unsigned long tval , unsigned long * regs )
138172{
139173 last_cause = cause ;
140174 last_epc = epc ;
141175 last_tval = tval ;
142176
177+ #if defined(WOLFBOOT_RISCV_MMODE ) && defined(WOLFBOOT_MMODE_SMODE_BOOT )
178+ /* SBI runtime: service the S-mode environment calls and the M-mode
179+ * timer/software interrupts that back the S-mode timer and IPIs. All
180+ * other traps fall through to the fault handler below. */
181+ {
182+ unsigned long ec = cause & MCAUSE_CAUSE ;
183+ if ((cause & MCAUSE_INT ) != 0UL ) {
184+ if (ec == (unsigned long )IRQ_M_TIMER ) {
185+ sbi_timer_irq ();
186+ return epc ;
187+ }
188+ if (ec == (unsigned long )IRQ_M_SOFT ) {
189+ unsigned long self ;
190+ __asm__ volatile ("csrr %0, mhartid" : "=r" (self ));
191+ sbi_ipi_irq (self );
192+ return epc ;
193+ }
194+ }
195+ else if (ec == 9UL ) { /* environment call from S-mode */
196+ return sbi_handle_ecall (regs , epc );
197+ }
198+ else if (ec == 2UL ) {
199+ /* Illegal instruction from S-mode OR U-mode: try the SBI
200+ * emulation path (rdtime -- these harts have no time CSR, and
201+ * userspace reaches it via the vDSO clock_gettime path). */
202+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
203+ if (mpp != 3UL ) { /* any non-M context */
204+ unsigned long nepc = sbi_illegal_insn (regs , epc , tval );
205+ if (nepc != 0UL ) {
206+ return nepc ;
207+ }
208+ }
209+ /* not handled: fall through to the fatal dump below */
210+ }
211+ else if (ec == 4UL || ec == 6UL ) {
212+ /* Misaligned load/store from S/U mode: these harts cannot
213+ * delegate misaligned traps; firmware emulates them byte-wise
214+ * (OpenSBI parity). */
215+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
216+ if (mpp != 3UL ) {
217+ unsigned long nepc = sbi_misaligned_ldst (regs , epc , tval ,
218+ ec );
219+ if (nepc != 0UL ) {
220+ return nepc ;
221+ }
222+ }
223+ /* not handled: fall through to the fatal dump below */
224+ }
225+ }
226+ #endif
227+
143228 /* Always print and halt on synchronous exceptions to prevent
144229 * infinite trap-mret loops that appear as silent hangs.
145230 * NOTE: keep each printf SIMPLE (few args) to minimize the risk of
146231 * recursive traps if wolfBoot's state is corrupted. */
147232 if (!(cause & MCAUSE_INT )) {
148- wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx\n" ,
149- cause , epc , tval );
233+ /* Offer the synchronous exception to the legacy 3-arg hook so an
234+ * out-of-tree platform override can service it (returning a resume
235+ * epc different from the faulting one). The weak default returns
236+ * epc unchanged, treated as "unhandled" -> fall through to print +
237+ * halt (a bare resume-at-epc would spin in a silent trap-mret loop). */
238+ {
239+ unsigned long resume = handle_trap (cause , epc , tval );
240+ if (resume != epc ) {
241+ return resume ;
242+ }
243+ }
244+ wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx mstatus=%lx\n" ,
245+ cause , epc , tval , csr_read (mstatus ));
150246#if defined(DEBUG_BOOT )
151247 unsigned long sp_now ;
152248 __asm__ volatile ("mv %0, sp" : "=r" (sp_now ));
@@ -161,9 +257,83 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
161257 wolfBoot_printf ("STACK OVERFLOW: under by %lu\n" ,
162258 bottom - sp_now );
163259 }
260+ /* Dump saved register frame from trap_entry. Each slot is
261+ * 8 bytes (REGBYTES); slot[N] = xN. See vector_riscv.S. */
262+ if (regs != NULL ) {
263+ wolfBoot_printf (
264+ " ra=%lx sp=%lx gp=%lx tp=%lx\n" ,
265+ regs [1 ], regs [2 ], regs [3 ], regs [4 ]);
266+ wolfBoot_printf (
267+ " t0=%lx t1=%lx t2=%lx\n" ,
268+ regs [5 ], regs [6 ], regs [7 ]);
269+ wolfBoot_printf (
270+ " s0=%lx s1=%lx\n" ,
271+ regs [8 ], regs [9 ]);
272+ wolfBoot_printf (
273+ " a0=%lx a1=%lx a2=%lx a3=%lx\n" ,
274+ regs [10 ], regs [11 ], regs [12 ], regs [13 ]);
275+ wolfBoot_printf (
276+ " a4=%lx a5=%lx a6=%lx a7=%lx\n" ,
277+ regs [14 ], regs [15 ], regs [16 ], regs [17 ]);
278+ wolfBoot_printf (
279+ " s2=%lx s3=%lx s4=%lx s5=%lx\n" ,
280+ regs [18 ], regs [19 ], regs [20 ], regs [21 ]);
281+ wolfBoot_printf (
282+ " s6=%lx s7=%lx s8=%lx s9=%lx\n" ,
283+ regs [22 ], regs [23 ], regs [24 ], regs [25 ]);
284+ wolfBoot_printf (
285+ " s10=%lx s11=%lx\n" ,
286+ regs [26 ], regs [27 ]);
287+ wolfBoot_printf (
288+ " t3=%lx t4=%lx t5=%lx t6=%lx\n" ,
289+ regs [28 ], regs [29 ], regs [30 ], regs [31 ]);
290+ /* Dump stack memory above the trap frame. Trap frame is
291+ * 256 bytes; above it is the trapping function's own frame
292+ * containing its saved ra values from sub-call chains. */
293+ {
294+ unsigned long * stk = (unsigned long * )(regs + 32 );
295+ int j ;
296+ wolfBoot_printf (" stack from caller sp=%lx:\n" ,
297+ (unsigned long )stk );
298+ for (j = 0 ; j < 24 ; j += 4 ) {
299+ wolfBoot_printf (
300+ " +%x: %lx %lx %lx %lx\n" ,
301+ j * 8 , stk [j ], stk [j + 1 ], stk [j + 2 ], stk [j + 3 ]);
302+ }
303+ }
304+ }
305+ /* Phase A canary check. bot expected 0xC0DEC0DE x 4 at
306+ * _main_hart_stack_bottom; mid expected 0xCA11AB1E x 4 at
307+ * 0x0A030000. If both intact but stack frame is zeroed,
308+ * cache-aliasing scrubbed the specific frame slots. If both
309+ * zero, the whole scratchpad was wiped. If bot zero but mid
310+ * intact, the stack overflowed past the bottom. */
311+ {
312+ volatile uint32_t * cb = (volatile uint32_t * )bottom ;
313+ volatile uint32_t * cm = (volatile uint32_t * )0x0A030000UL ;
314+ wolfBoot_printf (
315+ " canary bot[%p]=%lx %lx %lx %lx (want C0DEC0DE)\n" ,
316+ (void * )cb , (unsigned long )cb [0 ], (unsigned long )cb [1 ],
317+ (unsigned long )cb [2 ], (unsigned long )cb [3 ]);
318+ wolfBoot_printf (
319+ " canary mid[0A030000]=%lx %lx %lx %lx (want CA11AB1E)\n" ,
320+ (unsigned long )cm [0 ], (unsigned long )cm [1 ],
321+ (unsigned long )cm [2 ], (unsigned long )cm [3 ]);
322+ }
164323#endif
165324#endif /* DEBUG_BOOT */
166- while (1 ) ; /* halt to prevent infinite trap-mret loop */
325+ /* Halt and pet MSS WDT so GDB can inspect the trap state
326+ * indefinitely without the chip cycling. Same WDT addresses
327+ * as in wolfBoot_panic. */
328+ while (1 ) {
329+ #if defined(TARGET_mpfs250 )
330+ * (volatile uint32_t * )0x20001000UL = 0xDEADC0DEU ;
331+ * (volatile uint32_t * )0x20101000UL = 0xDEADC0DEU ;
332+ * (volatile uint32_t * )0x20103000UL = 0xDEADC0DEU ;
333+ * (volatile uint32_t * )0x20105000UL = 0xDEADC0DEU ;
334+ * (volatile uint32_t * )0x20107000UL = 0xDEADC0DEU ;
335+ #endif
336+ }
167337 }
168338
169339#ifdef PLIC_BASE
@@ -180,7 +350,8 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
180350 /* Synchronous exceptions are not handled - just record them */
181351#endif
182352
183- return epc ;
353+ /* Forward to the legacy 3-arg hook so out-of-tree overrides still run */
354+ return handle_trap (cause , epc , tval );
184355}
185356
186357/* ============================================================================
@@ -214,7 +385,7 @@ uint64_t hal_get_timer_us(void)
214385 return (ticks * 1000 ) / (rate / 1000 );
215386}
216387
217- #ifdef MMU
388+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
218389int WEAKFUNCTION hal_dts_fixup (void * dts_addr )
219390{
220391 (void )dts_addr ;
@@ -263,6 +434,42 @@ static void __attribute__((noreturn)) enter_smode(unsigned long entry,
263434 );
264435 __builtin_unreachable ();
265436}
437+
438+ /* Public M->S handoff entry point. Sets up PMP, delegates S-mode traps,
439+ * then transitions the calling hart to S-mode at entry. Used both by the
440+ * default do_boot path and by HAL overrides that release a different hart. */
441+ void __attribute__((noreturn ))
442+ riscv_mmode_to_smode (unsigned long entry , unsigned long hartid ,
443+ unsigned long dtb )
444+ {
445+ setup_pmp_for_smode ();
446+ delegate_traps_to_smode ();
447+ #if defined(WOLFBOOT_MMODE_SMODE_BOOT )
448+ /* Install the wolfBoot SBI trap vector on this hart so S-mode ecalls and
449+ * the M-timer/M-soft IRQs are serviced in M-mode here, and arm this
450+ * hart's dedicated M-mode trap stack (the S-mode sp is virtual once the
451+ * OS enables paging, so the trap entry must not store through it).
452+ * Keep illegal-instruction traps in M-mode: rdtime is emulated there
453+ * (no time CSR on these harts). mcounteren still exposes cycle/instret
454+ * to S-mode. Enable M software interrupts for IPI delivery. */
455+ csr_write (mtvec , (unsigned long )trap_vector_table );
456+ sbi_mscratch_init (hartid );
457+ sbi_hart_mark_started (hartid );
458+ csr_write (medeleg , csr_read (medeleg ) & ~(1UL << 2 ));
459+ csr_write (mcounteren , 0x7UL );
460+ csr_write (mie , csr_read (mie ) | MIE_MSIE );
461+ #endif
462+ enter_smode (entry , hartid , dtb );
463+ }
464+
465+ /* Weak default: hand the kernel off in S-mode on the current hart. Platforms
466+ * that need a different topology (e.g. the MPFS E51 must release a U54
467+ * because cpu@0 is disabled in the DTB) override this in their HAL. */
468+ void __attribute__((weak , noreturn ))
469+ hal_smode_boot (unsigned long entry , unsigned long hartid , unsigned long dtb )
470+ {
471+ riscv_mmode_to_smode (entry , hartid , dtb );
472+ }
266473#endif /* WOLFBOOT_RISCV_MMODE */
267474
268475#if __riscv_xlen == 64
@@ -275,7 +482,7 @@ unsigned long get_boot_hartid(void)
275482}
276483#endif
277484
278- #ifdef MMU
485+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
279486void do_boot (const uint32_t * app_offset , const uint32_t * dts_offset )
280487#else
281488void do_boot (const uint32_t * app_offset )
@@ -284,9 +491,13 @@ void do_boot(const uint32_t *app_offset)
284491#if __riscv_xlen == 64
285492 unsigned long hartid ;
286493#endif
287- #ifdef MMU
494+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
288495 unsigned long dts_addr ;
289- hal_dts_fixup ((uint32_t * )dts_offset );
496+ /* dts_offset is NULL when the loaded image was not a FIT (or had no
497+ * flat_dt): skip the fixup and hand off with dtb=0 rather than deref. */
498+ if (dts_offset != NULL ) {
499+ hal_dts_fixup ((uint32_t * )dts_offset );
500+ }
290501 dts_addr = (unsigned long )dts_offset ;
291502#elif defined(WOLFBOOT_RISCV_MMODE ) || __riscv_xlen == 64
292503 unsigned long dts_addr = 0 ;
@@ -301,7 +512,7 @@ void do_boot(const uint32_t *app_offset)
301512#if __riscv_xlen == 64
302513 wolfBoot_printf (", hartid=%lu" , hartid );
303514#endif
304- #ifdef MMU
515+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
305516 wolfBoot_printf (", dts=0x%lx" , dts_addr );
306517#endif
307518 wolfBoot_printf ("\n" );
@@ -312,12 +523,13 @@ void do_boot(const uint32_t *app_offset)
312523
313524#ifdef WOLFBOOT_RISCV_MMODE
314525#ifdef WOLFBOOT_MMODE_SMODE_BOOT
315- /* M-mode -> S-mode transition for Linux boot */
316- wolfBoot_printf ("M->S transition: entry=0x%lx\n" , (unsigned long )app_offset );
317- setup_pmp_for_smode ();
318- delegate_traps_to_smode ();
526+ /* M-mode -> S-mode transition for Linux boot. Default: hand off on the
527+ * current hart. HAL may override hal_smode_boot to release a different
528+ * hart and self-park (see hal/mpfs250.c when MPFS_DDR_INIT is set). */
529+ wolfBoot_printf ("M->S handoff: entry=0x%lx hart=%lu dtb=0x%lx\n" ,
530+ (unsigned long )app_offset , hartid , dts_addr );
319531 /* This never returns */
320- enter_smode ((unsigned long )app_offset , hartid , dts_addr );
532+ hal_smode_boot ((unsigned long )app_offset , hartid , dts_addr );
321533#else
322534 /* Direct M-mode jump for bare-metal payloads.
323535 * Define WOLFBOOT_MMODE_SMODE_BOOT to boot Linux via S-mode transition. */
0 commit comments