Skip to content

Commit 68789e3

Browse files
committed
RISC-V: minimal SBI runtime; PolarFire SoC boots 4-CPU SMP Yocto Linux
1 parent b8628f5 commit 68789e3

5 files changed

Lines changed: 1082 additions & 20 deletions

File tree

src/boot_riscv.c

Lines changed: 226 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ extern void (* const IV[])(void);
5656
extern void main(void);
5757
extern 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
6177
static 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. */
136158
unsigned 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)
218389
int 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)
279486
void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset)
280487
#else
281488
void 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. */

src/boot_riscv_start.S

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,13 @@ _copy_params:
159159
sd zero, 48(s11)
160160
sd zero, 56(s11)
161161

162-
/* Wait for E51 to signal HLS_MAIN_HART_STARTED */
162+
/* Wait for E51 to signal HLS_MAIN_HART_STARTED. Poll the DTIM copy
163+
* of the flag, not the L2-scratch HLS: an E51 store to the cacheable
164+
* scratchpad can be lost on dirty-line eviction (layout-dependent),
165+
* which left the secondaries parked here until the kernel's HSM
166+
* hart_start IPI arrived -- too late for its 1s online window. */
163167
li t3, 0x12344321
164-
la t1, _main_hart_hls
168+
li t1, MPFS_DTIM_MAIN_STARTED_ADDR
165169
.L_wait_main_hart:
166170
lwu t2, 0(t1)
167171
bne t3, t2, .L_wait_main_hart
@@ -187,6 +191,14 @@ _copy_params:
187191
fence iorw, iorw
188192
fence.i
189193

194+
/* The C code on this hart (secondary_hart_entry and the M->S release
195+
* path) addresses small globals gp-relative; only the E51's
196+
* .L_sram_entry path sets gp, so set it here too. */
197+
.option push
198+
.option norelax
199+
la gp, __global_pointer$
200+
.option pop
201+
190202
csrr a0, mhartid
191203
mv a1, s11
192204
la t0, secondary_hart_entry

0 commit comments

Comments
 (0)