@@ -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,88 @@ 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 only for interrupts it does not handle
154+ * itself, and only after the in-tree SBI/PLIC handling. Synchronous
155+ * exceptions are serviced (or halted) in handle_trap_ex and never reach this
156+ * hook, so an out-of-tree override no longer sees them. */
136157unsigned long WEAKFUNCTION handle_trap (unsigned long cause , unsigned long epc ,
137158 unsigned long tval )
159+ {
160+ (void )cause ;
161+ (void )tval ;
162+ return epc ;
163+ }
164+
165+ /* Regs-aware trap dispatch -- called directly from src/vector_riscv.S.
166+ * Override this (also weak) to take full control including the saved
167+ * register frame. Falls through to weak handle_trap so legacy 3-arg
168+ * overrides still run. */
169+ unsigned long WEAKFUNCTION handle_trap_ex (unsigned long cause , unsigned long epc ,
170+ unsigned long tval , unsigned long * regs )
138171{
139172 last_cause = cause ;
140173 last_epc = epc ;
141174 last_tval = tval ;
142175
176+ #if defined(WOLFBOOT_RISCV_MMODE ) && defined(WOLFBOOT_MMODE_SMODE_BOOT )
177+ /* SBI runtime: service the S-mode environment calls and the M-mode
178+ * timer/software interrupts that back the S-mode timer and IPIs. All
179+ * other traps fall through to the fault handler below. */
180+ {
181+ unsigned long ec = cause & MCAUSE_CAUSE ;
182+ if ((cause & MCAUSE_INT ) != 0UL ) {
183+ if (ec == (unsigned long )IRQ_M_TIMER ) {
184+ sbi_timer_irq ();
185+ return epc ;
186+ }
187+ if (ec == (unsigned long )IRQ_M_SOFT ) {
188+ unsigned long self ;
189+ __asm__ volatile ("csrr %0, mhartid" : "=r" (self ));
190+ sbi_ipi_irq (self );
191+ return epc ;
192+ }
193+ }
194+ else if (ec == 9UL ) { /* environment call from S-mode */
195+ return sbi_handle_ecall (regs , epc );
196+ }
197+ else if (ec == 2UL ) {
198+ /* Illegal instruction from S-mode OR U-mode: try the SBI
199+ * emulation path (rdtime -- these harts have no time CSR, and
200+ * userspace reaches it via the vDSO clock_gettime path). */
201+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
202+ if (mpp != 3UL ) { /* any non-M context */
203+ unsigned long nepc = sbi_illegal_insn (regs , epc , tval );
204+ if (nepc != 0UL ) {
205+ return nepc ;
206+ }
207+ }
208+ /* not handled: fall through to the fatal dump below */
209+ }
210+ else if (ec == 4UL || ec == 6UL ) {
211+ /* Misaligned load/store from S/U mode: these harts cannot
212+ * delegate misaligned traps; firmware emulates them byte-wise
213+ * (OpenSBI parity). */
214+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
215+ if (mpp != 3UL ) {
216+ unsigned long nepc = sbi_misaligned_ldst (regs , epc , tval ,
217+ ec );
218+ if (nepc != 0UL ) {
219+ return nepc ;
220+ }
221+ }
222+ /* not handled: fall through to the fatal dump below */
223+ }
224+ }
225+ #endif
226+
143227 /* Always print and halt on synchronous exceptions to prevent
144228 * infinite trap-mret loops that appear as silent hangs.
145229 * NOTE: keep each printf SIMPLE (few args) to minimize the risk of
146230 * recursive traps if wolfBoot's state is corrupted. */
147231 if (!(cause & MCAUSE_INT )) {
148- wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx\n" ,
149- cause , epc , tval );
232+ wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx mstatus=%lx \n" ,
233+ cause , epc , tval , csr_read ( mstatus ) );
150234#if defined(DEBUG_BOOT )
151235 unsigned long sp_now ;
152236 __asm__ volatile ("mv %0, sp" : "=r" (sp_now ));
@@ -161,9 +245,83 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
161245 wolfBoot_printf ("STACK OVERFLOW: under by %lu\n" ,
162246 bottom - sp_now );
163247 }
248+ /* Dump saved register frame from trap_entry. Each slot is
249+ * 8 bytes (REGBYTES); slot[N] = xN. See vector_riscv.S. */
250+ if (regs != NULL ) {
251+ wolfBoot_printf (
252+ " ra=%lx sp=%lx gp=%lx tp=%lx\n" ,
253+ regs [1 ], regs [2 ], regs [3 ], regs [4 ]);
254+ wolfBoot_printf (
255+ " t0=%lx t1=%lx t2=%lx\n" ,
256+ regs [5 ], regs [6 ], regs [7 ]);
257+ wolfBoot_printf (
258+ " s0=%lx s1=%lx\n" ,
259+ regs [8 ], regs [9 ]);
260+ wolfBoot_printf (
261+ " a0=%lx a1=%lx a2=%lx a3=%lx\n" ,
262+ regs [10 ], regs [11 ], regs [12 ], regs [13 ]);
263+ wolfBoot_printf (
264+ " a4=%lx a5=%lx a6=%lx a7=%lx\n" ,
265+ regs [14 ], regs [15 ], regs [16 ], regs [17 ]);
266+ wolfBoot_printf (
267+ " s2=%lx s3=%lx s4=%lx s5=%lx\n" ,
268+ regs [18 ], regs [19 ], regs [20 ], regs [21 ]);
269+ wolfBoot_printf (
270+ " s6=%lx s7=%lx s8=%lx s9=%lx\n" ,
271+ regs [22 ], regs [23 ], regs [24 ], regs [25 ]);
272+ wolfBoot_printf (
273+ " s10=%lx s11=%lx\n" ,
274+ regs [26 ], regs [27 ]);
275+ wolfBoot_printf (
276+ " t3=%lx t4=%lx t5=%lx t6=%lx\n" ,
277+ regs [28 ], regs [29 ], regs [30 ], regs [31 ]);
278+ /* Dump stack memory above the trap frame. Trap frame is
279+ * 256 bytes; above it is the trapping function's own frame
280+ * containing its saved ra values from sub-call chains. */
281+ {
282+ unsigned long * stk = (unsigned long * )(regs + 32 );
283+ int j ;
284+ wolfBoot_printf (" stack from caller sp=%lx:\n" ,
285+ (unsigned long )stk );
286+ for (j = 0 ; j < 24 ; j += 4 ) {
287+ wolfBoot_printf (
288+ " +%x: %lx %lx %lx %lx\n" ,
289+ j * 8 , stk [j ], stk [j + 1 ], stk [j + 2 ], stk [j + 3 ]);
290+ }
291+ }
292+ }
293+ /* Phase A canary check. bot expected 0xC0DEC0DE x 4 at
294+ * _main_hart_stack_bottom; mid expected 0xCA11AB1E x 4 at
295+ * 0x0A030000. If both intact but stack frame is zeroed,
296+ * cache-aliasing scrubbed the specific frame slots. If both
297+ * zero, the whole scratchpad was wiped. If bot zero but mid
298+ * intact, the stack overflowed past the bottom. */
299+ {
300+ volatile uint32_t * cb = (volatile uint32_t * )bottom ;
301+ volatile uint32_t * cm = (volatile uint32_t * )0x0A030000UL ;
302+ wolfBoot_printf (
303+ " canary bot[%p]=%lx %lx %lx %lx (want C0DEC0DE)\n" ,
304+ (void * )cb , (unsigned long )cb [0 ], (unsigned long )cb [1 ],
305+ (unsigned long )cb [2 ], (unsigned long )cb [3 ]);
306+ wolfBoot_printf (
307+ " canary mid[0A030000]=%lx %lx %lx %lx (want CA11AB1E)\n" ,
308+ (unsigned long )cm [0 ], (unsigned long )cm [1 ],
309+ (unsigned long )cm [2 ], (unsigned long )cm [3 ]);
310+ }
164311#endif
165312#endif /* DEBUG_BOOT */
166- while (1 ) ; /* halt to prevent infinite trap-mret loop */
313+ /* Halt and pet MSS WDT so GDB can inspect the trap state
314+ * indefinitely without the chip cycling. Same WDT addresses
315+ * as in wolfBoot_panic. */
316+ while (1 ) {
317+ #if defined(TARGET_mpfs250 )
318+ * (volatile uint32_t * )0x20001000UL = 0xDEADC0DEU ;
319+ * (volatile uint32_t * )0x20101000UL = 0xDEADC0DEU ;
320+ * (volatile uint32_t * )0x20103000UL = 0xDEADC0DEU ;
321+ * (volatile uint32_t * )0x20105000UL = 0xDEADC0DEU ;
322+ * (volatile uint32_t * )0x20107000UL = 0xDEADC0DEU ;
323+ #endif
324+ }
167325 }
168326
169327#ifdef PLIC_BASE
@@ -180,7 +338,8 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
180338 /* Synchronous exceptions are not handled - just record them */
181339#endif
182340
183- return epc ;
341+ /* Forward to the legacy 3-arg hook so out-of-tree overrides still run */
342+ return handle_trap (cause , epc , tval );
184343}
185344
186345/* ============================================================================
@@ -214,7 +373,7 @@ uint64_t hal_get_timer_us(void)
214373 return (ticks * 1000 ) / (rate / 1000 );
215374}
216375
217- #ifdef MMU
376+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
218377int WEAKFUNCTION hal_dts_fixup (void * dts_addr )
219378{
220379 (void )dts_addr ;
@@ -263,6 +422,42 @@ static void __attribute__((noreturn)) enter_smode(unsigned long entry,
263422 );
264423 __builtin_unreachable ();
265424}
425+
426+ /* Public M->S handoff entry point. Sets up PMP, delegates S-mode traps,
427+ * then transitions the calling hart to S-mode at entry. Used both by the
428+ * default do_boot path and by HAL overrides that release a different hart. */
429+ void __attribute__((noreturn ))
430+ riscv_mmode_to_smode (unsigned long entry , unsigned long hartid ,
431+ unsigned long dtb )
432+ {
433+ setup_pmp_for_smode ();
434+ delegate_traps_to_smode ();
435+ #if defined(WOLFBOOT_MMODE_SMODE_BOOT )
436+ /* Install the wolfBoot SBI trap vector on this hart so S-mode ecalls and
437+ * the M-timer/M-soft IRQs are serviced in M-mode here, and arm this
438+ * hart's dedicated M-mode trap stack (the S-mode sp is virtual once the
439+ * OS enables paging, so the trap entry must not store through it).
440+ * Keep illegal-instruction traps in M-mode: rdtime is emulated there
441+ * (no time CSR on these harts). mcounteren still exposes cycle/instret
442+ * to S-mode. Enable M software interrupts for IPI delivery. */
443+ csr_write (mtvec , (unsigned long )trap_vector_table );
444+ sbi_mscratch_init (hartid );
445+ sbi_hart_mark_started (hartid );
446+ csr_write (medeleg , csr_read (medeleg ) & ~(1UL << 2 ));
447+ csr_write (mcounteren , 0x7UL );
448+ csr_write (mie , csr_read (mie ) | MIE_MSIE );
449+ #endif
450+ enter_smode (entry , hartid , dtb );
451+ }
452+
453+ /* Weak default: hand the kernel off in S-mode on the current hart. Platforms
454+ * that need a different topology (e.g. the MPFS E51 must release a U54
455+ * because cpu@0 is disabled in the DTB) override this in their HAL. */
456+ void __attribute__((weak , noreturn ))
457+ hal_smode_boot (unsigned long entry , unsigned long hartid , unsigned long dtb )
458+ {
459+ riscv_mmode_to_smode (entry , hartid , dtb );
460+ }
266461#endif /* WOLFBOOT_RISCV_MMODE */
267462
268463#if __riscv_xlen == 64
@@ -275,7 +470,7 @@ unsigned long get_boot_hartid(void)
275470}
276471#endif
277472
278- #ifdef MMU
473+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
279474void do_boot (const uint32_t * app_offset , const uint32_t * dts_offset )
280475#else
281476void do_boot (const uint32_t * app_offset )
@@ -284,9 +479,13 @@ void do_boot(const uint32_t *app_offset)
284479#if __riscv_xlen == 64
285480 unsigned long hartid ;
286481#endif
287- #ifdef MMU
482+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
288483 unsigned long dts_addr ;
289- hal_dts_fixup ((uint32_t * )dts_offset );
484+ /* dts_offset is NULL when the loaded image was not a FIT (or had no
485+ * flat_dt): skip the fixup and hand off with dtb=0 rather than deref. */
486+ if (dts_offset != NULL ) {
487+ hal_dts_fixup ((uint32_t * )dts_offset );
488+ }
290489 dts_addr = (unsigned long )dts_offset ;
291490#elif defined(WOLFBOOT_RISCV_MMODE ) || __riscv_xlen == 64
292491 unsigned long dts_addr = 0 ;
@@ -301,7 +500,7 @@ void do_boot(const uint32_t *app_offset)
301500#if __riscv_xlen == 64
302501 wolfBoot_printf (", hartid=%lu" , hartid );
303502#endif
304- #ifdef MMU
503+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
305504 wolfBoot_printf (", dts=0x%lx" , dts_addr );
306505#endif
307506 wolfBoot_printf ("\n" );
@@ -312,12 +511,13 @@ void do_boot(const uint32_t *app_offset)
312511
313512#ifdef WOLFBOOT_RISCV_MMODE
314513#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 ();
514+ /* M-mode -> S-mode transition for Linux boot. Default: hand off on the
515+ * current hart. HAL may override hal_smode_boot to release a different
516+ * hart and self-park (see hal/mpfs250.c when MPFS_DDR_INIT is set). */
517+ wolfBoot_printf ("M->S handoff: entry=0x%lx hart=%lu dtb=0x%lx\n" ,
518+ (unsigned long )app_offset , hartid , dts_addr );
319519 /* This never returns */
320- enter_smode ((unsigned long )app_offset , hartid , dts_addr );
520+ hal_smode_boot ((unsigned long )app_offset , hartid , dts_addr );
321521#else
322522 /* Direct M-mode jump for bare-metal payloads.
323523 * Define WOLFBOOT_MMODE_SMODE_BOOT to boot Linux via S-mode transition. */
0 commit comments