@@ -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,22 +149,102 @@ 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 )
138160{
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 )
172+ {
173+ #if defined(WOLFBOOT_RISCV_MMODE ) && defined(WOLFBOOT_MMODE_SMODE_BOOT )
174+ unsigned long ec ;
175+ #endif
139176 last_cause = cause ;
140177 last_epc = epc ;
141178 last_tval = tval ;
142179
180+ #if defined(WOLFBOOT_RISCV_MMODE ) && defined(WOLFBOOT_MMODE_SMODE_BOOT )
181+ /* SBI runtime: service the S-mode environment calls and the M-mode
182+ * timer/software interrupts that back the S-mode timer and IPIs. All
183+ * other traps fall through to the fault handler below. */
184+ ec = cause & MCAUSE_CAUSE ;
185+ if ((cause & MCAUSE_INT ) != 0UL ) {
186+ if (ec == (unsigned long )IRQ_M_TIMER ) {
187+ sbi_timer_irq ();
188+ return epc ;
189+ }
190+ if (ec == (unsigned long )IRQ_M_SOFT ) {
191+ unsigned long self ;
192+ __asm__ volatile ("csrr %0, mhartid" : "=r" (self ));
193+ sbi_ipi_irq (self );
194+ return epc ;
195+ }
196+ }
197+ else if (ec == 9UL ) { /* environment call from S-mode */
198+ return sbi_handle_ecall (regs , epc );
199+ }
200+ else if (ec == 2UL ) {
201+ /* Illegal instruction from S-mode OR U-mode: try the SBI
202+ * emulation path (rdtime -- these harts have no time CSR, and
203+ * userspace reaches it via the vDSO clock_gettime path). */
204+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
205+ if (mpp != 3UL ) { /* any non-M context */
206+ unsigned long nepc = sbi_illegal_insn (regs , epc , tval );
207+ if (nepc != 0UL ) {
208+ return nepc ;
209+ }
210+ }
211+ /* not handled: fall through to the fatal dump below */
212+ }
213+ else if (ec == 4UL || ec == 6UL ) {
214+ /* Misaligned load/store from S/U mode: these harts cannot
215+ * delegate misaligned traps; firmware emulates them byte-wise
216+ * (OpenSBI parity). */
217+ unsigned long mpp = (csr_read (mstatus ) >> 11 ) & 3UL ;
218+ if (mpp != 3UL ) {
219+ unsigned long nepc = sbi_misaligned_ldst (regs , epc , tval ,
220+ ec );
221+ if (nepc != 0UL ) {
222+ return nepc ;
223+ }
224+ }
225+ /* not handled: fall through to the fatal dump below */
226+ }
227+ #endif
228+
143229 /* Always print and halt on synchronous exceptions to prevent
144230 * infinite trap-mret loops that appear as silent hangs.
145231 * NOTE: keep each printf SIMPLE (few args) to minimize the risk of
146232 * recursive traps if wolfBoot's state is corrupted. */
147233 if (!(cause & MCAUSE_INT )) {
148- wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx\n" ,
149- cause , epc , tval );
234+ unsigned long resume ;
235+ /* Offer the synchronous exception to the legacy 3-arg hook so an
236+ * out-of-tree platform override can service it (returning a resume
237+ * epc different from the faulting one). The weak default returns
238+ * epc unchanged, treated as "unhandled" -> fall through to print +
239+ * halt (a bare resume-at-epc would spin in a silent trap-mret loop). */
240+ resume = handle_trap (cause , epc , tval );
241+ if (resume != epc ) {
242+ return resume ;
243+ }
150244#if defined(DEBUG_BOOT )
151245 unsigned long sp_now ;
246+ wolfBoot_printf ("TRAP: cause=%lx epc=%lx tval=%lx mstatus=%lx\n" ,
247+ cause , epc , tval , csr_read (mstatus ));
152248 __asm__ volatile ("mv %0, sp" : "=r" (sp_now ));
153249 wolfBoot_printf (" sp=%lx\n" , sp_now );
154250#if defined(WOLFBOOT_RISCV_MMODE ) && defined(TARGET_mpfs250 )
@@ -161,9 +257,56 @@ 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+ unsigned long * stk ;
264+ int j ;
265+ wolfBoot_printf (
266+ " ra=%lx sp=%lx gp=%lx tp=%lx\n" ,
267+ regs [1 ], regs [2 ], regs [3 ], regs [4 ]);
268+ wolfBoot_printf (
269+ " t0=%lx t1=%lx t2=%lx\n" ,
270+ regs [5 ], regs [6 ], regs [7 ]);
271+ wolfBoot_printf (
272+ " s0=%lx s1=%lx\n" ,
273+ regs [8 ], regs [9 ]);
274+ wolfBoot_printf (
275+ " a0=%lx a1=%lx a2=%lx a3=%lx\n" ,
276+ regs [10 ], regs [11 ], regs [12 ], regs [13 ]);
277+ wolfBoot_printf (
278+ " a4=%lx a5=%lx a6=%lx a7=%lx\n" ,
279+ regs [14 ], regs [15 ], regs [16 ], regs [17 ]);
280+ wolfBoot_printf (
281+ " s2=%lx s3=%lx s4=%lx s5=%lx\n" ,
282+ regs [18 ], regs [19 ], regs [20 ], regs [21 ]);
283+ wolfBoot_printf (
284+ " s6=%lx s7=%lx s8=%lx s9=%lx\n" ,
285+ regs [22 ], regs [23 ], regs [24 ], regs [25 ]);
286+ wolfBoot_printf (
287+ " s10=%lx s11=%lx\n" ,
288+ regs [26 ], regs [27 ]);
289+ wolfBoot_printf (
290+ " t3=%lx t4=%lx t5=%lx t6=%lx\n" ,
291+ regs [28 ], regs [29 ], regs [30 ], regs [31 ]);
292+ /* Dump stack memory above the trap frame. Trap frame is
293+ * 256 bytes; above it is the trapping function's own frame
294+ * containing its saved ra values from sub-call chains. */
295+ stk = (unsigned long * )(regs + 32 );
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+ }
164304#endif
165305#endif /* DEBUG_BOOT */
166- while (1 ) ; /* halt to prevent infinite trap-mret loop */
306+ /* Halt: spin so the watchdog fires and resets the chip (recovery).
307+ * Do NOT pet the WDT here -- production must reset out of a fault. */
308+ while (1 )
309+ ;
167310 }
168311
169312#ifdef PLIC_BASE
@@ -180,7 +323,8 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
180323 /* Synchronous exceptions are not handled - just record them */
181324#endif
182325
183- return epc ;
326+ /* Forward to the legacy 3-arg hook so out-of-tree overrides still run */
327+ return handle_trap (cause , epc , tval );
184328}
185329
186330/* ============================================================================
@@ -214,7 +358,7 @@ uint64_t hal_get_timer_us(void)
214358 return (ticks * 1000 ) / (rate / 1000 );
215359}
216360
217- #ifdef MMU
361+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
218362int WEAKFUNCTION hal_dts_fixup (void * dts_addr )
219363{
220364 (void )dts_addr ;
@@ -263,6 +407,42 @@ static void __attribute__((noreturn)) enter_smode(unsigned long entry,
263407 );
264408 __builtin_unreachable ();
265409}
410+
411+ /* Public M->S handoff entry point. Sets up PMP, delegates S-mode traps,
412+ * then transitions the calling hart to S-mode at entry. Used both by the
413+ * default do_boot path and by HAL overrides that release a different hart. */
414+ void __attribute__((noreturn ))
415+ riscv_mmode_to_smode (unsigned long entry , unsigned long hartid ,
416+ unsigned long dtb )
417+ {
418+ setup_pmp_for_smode ();
419+ delegate_traps_to_smode ();
420+ #if defined(WOLFBOOT_MMODE_SMODE_BOOT )
421+ /* Install the wolfBoot SBI trap vector on this hart so S-mode ecalls and
422+ * the M-timer/M-soft IRQs are serviced in M-mode here, and arm this
423+ * hart's dedicated M-mode trap stack (the S-mode sp is virtual once the
424+ * OS enables paging, so the trap entry must not store through it).
425+ * Keep illegal-instruction traps in M-mode: rdtime is emulated there
426+ * (no time CSR on these harts). mcounteren still exposes cycle/instret
427+ * to S-mode. Enable M software interrupts for IPI delivery. */
428+ csr_write (mtvec , (unsigned long )trap_vector_table );
429+ sbi_mscratch_init (hartid );
430+ sbi_hart_mark_started (hartid );
431+ csr_write (medeleg , csr_read (medeleg ) & ~(1UL << 2 ));
432+ csr_write (mcounteren , 0x7UL );
433+ csr_write (mie , csr_read (mie ) | MIE_MSIE );
434+ #endif
435+ enter_smode (entry , hartid , dtb );
436+ }
437+
438+ /* Weak default: hand the kernel off in S-mode on the current hart. Platforms
439+ * that need a different topology (e.g. the MPFS E51 must release a U54
440+ * because cpu@0 is disabled in the DTB) override this in their HAL. */
441+ void __attribute__((weak , noreturn ))
442+ hal_smode_boot (unsigned long entry , unsigned long hartid , unsigned long dtb )
443+ {
444+ riscv_mmode_to_smode (entry , hartid , dtb );
445+ }
266446#endif /* WOLFBOOT_RISCV_MMODE */
267447
268448#if __riscv_xlen == 64
@@ -275,7 +455,7 @@ unsigned long get_boot_hartid(void)
275455}
276456#endif
277457
278- #ifdef MMU
458+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
279459void do_boot (const uint32_t * app_offset , const uint32_t * dts_offset )
280460#else
281461void do_boot (const uint32_t * app_offset )
@@ -284,9 +464,13 @@ void do_boot(const uint32_t *app_offset)
284464#if __riscv_xlen == 64
285465 unsigned long hartid ;
286466#endif
287- #ifdef MMU
467+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
288468 unsigned long dts_addr ;
289- hal_dts_fixup ((uint32_t * )dts_offset );
469+ /* dts_offset is NULL when the loaded image was not a FIT (or had no
470+ * flat_dt): skip the fixup and hand off with dtb=0 rather than deref. */
471+ if (dts_offset != NULL ) {
472+ hal_dts_fixup ((uint32_t * )dts_offset );
473+ }
290474 dts_addr = (unsigned long )dts_offset ;
291475#elif defined(WOLFBOOT_RISCV_MMODE ) || __riscv_xlen == 64
292476 unsigned long dts_addr = 0 ;
@@ -301,7 +485,7 @@ void do_boot(const uint32_t *app_offset)
301485#if __riscv_xlen == 64
302486 wolfBoot_printf (", hartid=%lu" , hartid );
303487#endif
304- #ifdef MMU
488+ #if defined( MMU ) || defined( WOLFBOOT_FDT )
305489 wolfBoot_printf (", dts=0x%lx" , dts_addr );
306490#endif
307491 wolfBoot_printf ("\n" );
@@ -312,12 +496,13 @@ void do_boot(const uint32_t *app_offset)
312496
313497#ifdef WOLFBOOT_RISCV_MMODE
314498#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 ();
499+ /* M-mode -> S-mode transition for Linux boot. Default: hand off on the
500+ * current hart. HAL may override hal_smode_boot to release a different
501+ * hart and self-park (see hal/mpfs250.c when MPFS_DDR_INIT is set). */
502+ wolfBoot_printf ("M->S handoff: entry=0x%lx hart=%lu dtb=0x%lx\n" ,
503+ (unsigned long )app_offset , hartid , dts_addr );
319504 /* This never returns */
320- enter_smode ((unsigned long )app_offset , hartid , dts_addr );
505+ hal_smode_boot ((unsigned long )app_offset , hartid , dts_addr );
321506#else
322507 /* Direct M-mode jump for bare-metal payloads.
323508 * Define WOLFBOOT_MMODE_SMODE_BOOT to boot Linux via S-mode transition. */
0 commit comments