Skip to content

Commit 036fee2

Browse files
committed
RISC-V: minimal SBI runtime; PolarFire SoC boots 4-CPU SMP Yocto Linux
1 parent e265502 commit 036fee2

5 files changed

Lines changed: 1060 additions & 20 deletions

File tree

src/boot_riscv.c

Lines changed: 199 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,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. */
136158
unsigned 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)
218362
int 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)
279459
void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset)
280460
#else
281461
void 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. */

src/boot_riscv_start.S

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,19 @@ _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. On MPFS250 poll the
163+
* DTIM copy of the flag, not the L2-scratch HLS: an E51 store to the
164+
* cacheable scratchpad can be lost on dirty-line eviction (layout-
165+
* dependent), which left the secondaries parked here until the kernel's
166+
* HSM hart_start IPI arrived -- too late for its 1s online window.
167+
* Other RISCV64 M-mode targets poll the generic _main_hart_hls flag
168+
* (MPFS_DTIM_MAIN_STARTED_ADDR is MPFS250-specific). */
163169
li t3, 0x12344321
170+
#ifdef TARGET_mpfs250
171+
li t1, MPFS_DTIM_MAIN_STARTED_ADDR
172+
#else
164173
la t1, _main_hart_hls
174+
#endif
165175
.L_wait_main_hart:
166176
lwu t2, 0(t1)
167177
bne t3, t2, .L_wait_main_hart
@@ -187,6 +197,14 @@ _copy_params:
187197
fence iorw, iorw
188198
fence.i
189199

200+
/* The C code on this hart (secondary_hart_entry and the M->S release
201+
* path) addresses small globals gp-relative; only the E51's
202+
* .L_sram_entry path sets gp, so set it here too. */
203+
.option push
204+
.option norelax
205+
la gp, __global_pointer$
206+
.option pop
207+
190208
csrr a0, mhartid
191209
mv a1, s11
192210
la t0, secondary_hart_entry

0 commit comments

Comments
 (0)