Skip to content

Commit 776474f

Browse files
committed
RISC-V: minimal SBI runtime; PolarFire SoC boots 4-CPU SMP Yocto Linux
1 parent f43cec6 commit 776474f

5 files changed

Lines changed: 1061 additions & 20 deletions

File tree

src/boot_riscv.c

Lines changed: 214 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,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. */
136157
unsigned 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)
218377
int 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)
279474
void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset)
280475
#else
281476
void 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. */

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)