From OSDev Wiki
Jump to navigation Jump to search

This article is a stub! This page or section is a stub. You can help the wiki by accurately contributing to it.

This page is under construction! This page or section is a work in progress and may thus be incomplete. Its content may be changed in the near future.

The RISC-V SBI (Supervisor Binary Interface) defines a common interface for RISC-V platforms for OSes. It hides platform-specific implementation details such that OSes are more portable.

Notably, it adds methods to facilitate IPI (Inter-Processor Interrupts).

The reference implementation is OpenSBI.


To run OpenSBI on QEMU, download the source and compile with make PLATFORM=generic CROSS_COMPILE=riscv64-your-os-. Then add -bios path/to/opensbi/build/platform/generic/firmware/fw_jump.bin

fw_jump vs fw_payload vs fw_dynamic

  • fw_jump should be used in conjunction with QEMU's -kernel option
  • fw_payload should directly include the kernel binary.
  • fw_dynamic should be used if you use another bootloader that passes more information to OpenSBI.


The calling convention is the RISC-V ELF psABI. This means you'll need to save caller-saved registers!

The main difference is that the EID (Extension ID) goes into a7 and FID (Function ID) into a6.


Avoid the legacy extensions (EID 0x00 - 0xFF) as those are deprecated.

Timer (0x54494D45)

The timer extension has a single function sbi_set_timer with FID 0x00. It takes a single uint64_t argument.

To reset the timer, pass UINT64_MAX. This clears the STIP bit and effectively disables the timer.

QEMU as of 6.1.0-rc3 does not handle UINT64_MAX properly. To fix this, apply the following patch:

diff --git a/hw/intc/sifive_clint.c b/hw/intc/sifive_clint.c
index 0f41e5ea1c..e65e71e5ec 100644
--- a/hw/intc/sifive_clint.c
+++ b/hw/intc/sifive_clint.c
@@ -61,6 +61,8 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value,
     /* back to ns (note args switched in muldiv64) */
     next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
         muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
+    /* ensure next does not overflow, as timer_mod takes a signed value */
+    next = MIN(next, INT64_MAX);
     timer_mod(cpu->env.timer, next);

See Also

External Links