rustsbi 0.3.0-rc.2

Minimal RISC-V's SBI implementation library in Rust
Documentation
//! 这个模块将会处理所有的 SBI 调用陷入,你应该在中断处理函数里调用 `handle_ecall`

// §4
mod base;
// §6
mod time;
// §7
mod spi;
// §8
mod rfnc;
// §9
mod hsm;
// §10
mod srst;
// §11
mod pmu;

#[cfg(feature = "legacy")]
use crate::{
    ipi::send_ipi, legacy_stdio_getchar, legacy_stdio_putchar, reset::legacy_reset, HartMask,
};
use sbi_spec::{self as spec, binary::SbiRet};

/// Supervisor environment call handler function
///
/// This function is used by platform runtime to handle environment call `ecall` instruction.
///
/// You should call this function in your runtime's exception handler.
/// If the incoming exception is caused by supervisor `ecall`,
/// call this function with parameters extracted from trap frame.
/// After this function returns, store the return value into `a0` and `a1` parameters.
///
/// This function also adapts to the legacy functions.
/// If the supervisor called any of legacy function, the `a0` and `a1` parameter
/// is transferred to error and value field of `SbiRet` respectively.
/// In this case, implementations should always store the result into `a0` and `a1` in
/// any environment call functions including legacy functions.
///
/// # Example
///
/// A typical usage:
///
/// ```no_run
/// # use riscv::register::{mepc, mcause::{self, Trap, Exception}};
/// # struct TrapFrame { a0: usize, a1: usize, a2: usize, a3: usize,
/// # a4: usize, a5: usize, a6: usize, a7: usize }
/// extern "C" fn rust_handle_exception(ctx: &mut TrapFrame) {
///     if mcause::read().cause() == Trap::Exception(Exception::SupervisorEnvCall) {
///         let params = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
///         let ans = rustsbi::ecall(ctx.a7, ctx.a6, params);
///         ctx.a0 = ans.error;
///         ctx.a1 = ans.value;
///         mepc::write(mepc::read().wrapping_add(4));
///     }
///     // other conditions..
/// }
/// ```
///
/// Do not forget to advance `mepc` by 4 after an ecall is handled.
/// This skips the `ecall` instruction itself which is 4-byte long in all conditions.
#[inline]
pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
    match extension {
        spec::rfnc::EID_RFNC => {
            rfnc::handle_ecall(function, param[0], param[1], param[2], param[3], param[4])
        }
        spec::time::EID_TIME => match () {
            #[cfg(target_pointer_width = "64")]
            () => time::handle_ecall(function, param[0]),
            #[cfg(target_pointer_width = "32")]
            () => time::handle_ecall(function, param[0], param[1]),
        },
        spec::spi::EID_SPI => spi::handle_ecall(function, param[0], param[1]),
        spec::base::EID_BASE => base::handle_ecall(function, param[0]),
        spec::hsm::EID_HSM => hsm::handle_ecall(function, param[0], param[1], param[2]),
        spec::srst::EID_SRST => srst::handle_ecall(function, param[0], param[1]),
        spec::pmu::EID_PMU => match () {
            #[cfg(target_pointer_width = "64")]
            () => pmu::handle_ecall(function, param[0], param[1], param[2], param[3], param[4]),
            #[cfg(target_pointer_width = "32")]
            () => pmu::handle_ecall(
                function, param[0], param[1], param[2], param[3], param[4], param[5],
            ),
        },
        // handle legacy callings.
        //
        // legacy 调用不使用 a1 返回值,总把 a1 填回 `SbiRet::value` 来模拟非 legacy 的行为。
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_SET_TIMER => {
            match () {
                #[cfg(target_pointer_width = "64")]
                () => crate::timer::set_timer(param[0] as _),
                #[cfg(target_pointer_width = "32")]
                () => crate::timer::set_timer(concat_u32(param[1] as _, param[0] as _)),
            };
            SbiRet {
                error: param[0],
                value: param[1],
            }
        }
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_CONSOLE_PUTCHAR => {
            legacy_stdio_putchar(param[0] as _);
            SbiRet {
                error: param[0],
                value: param[1],
            }
        }
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_CONSOLE_GETCHAR => SbiRet {
            error: legacy_stdio_getchar(),
            value: param[1],
        },
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_SEND_IPI => {
            send_ipi(unsafe { HartMask::legacy_from_addr(param[0]) });
            SbiRet {
                error: param[0],
                value: param[1],
            }
        }
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_CLEAR_IPI => {
            unsafe {
                riscv::register::mip::clear_ssoft();
            }
            SbiRet {
                error: param[0],
                value: param[1],
            }
        }
        #[cfg(feature = "legacy")]
        spec::legacy::LEGACY_SHUTDOWN => legacy_reset(),
        _ => SbiRet::not_supported(),
    }
}

#[cfg(target_pointer_width = "32")]
#[inline]
const fn concat_u32(h: usize, l: usize) -> u64 {
    ((h as u64) << 32) | (l as u64)
}