kernel/arch/riscv64/instruction/
sbi.rs

1use core::arch::asm;
2
3pub enum Extension {
4    Base = 0x10,
5    SetTimer = 0x00,
6    ConsolePutChar = 0x01,
7    ConsoleGetChar = 0x02,
8    DebugConsole = 0x4442434e,
9    Timer = 0x54494d45,
10    Ipi = 0x735049,
11    Rfence = 0x52464e43,
12    Hsm = 0x48534d,
13    Srst = 0x53525354,
14    Pmu = 0x504d55,
15}
16
17pub struct SbiRet {
18    pub error: usize,
19    pub value: usize,
20}
21
22pub enum SbiError {
23    Failed = -1,
24    NotSupported = -2,
25    InvalidParam = -3,
26    Denied = -4,
27    InvalidAddress = -5,
28    AlreadyAvailable = -6,
29    AlreadyStarted = -7,
30    AlreadyStopped = -8,
31}
32
33impl SbiError {
34    pub fn from_error(error: usize) -> SbiError {
35        let error = error as isize;
36        match error {
37            -1 => SbiError::Failed,
38            -2 => SbiError::NotSupported,
39            -3 => SbiError::InvalidParam,
40            -4 => SbiError::Denied,
41            -5 => SbiError::InvalidAddress,
42            -6 => SbiError::AlreadyAvailable,
43            -7 => SbiError::AlreadyStarted,
44            -8 => SbiError::AlreadyStopped,
45            _ => panic!("Invalid SBI error code"),
46        }
47    }
48}
49
50/// More robust SBI call implementation with additional safety measures
51#[inline(never)]
52#[unsafe(no_mangle)]
53pub fn sbi_call(
54    extension: Extension,
55    function: usize,
56    arg0: usize,
57    arg1: usize,
58) -> Result<usize, SbiError> {
59    let error: usize;
60    let ret: usize;
61
62    unsafe {
63        asm!(
64            "ecall",
65            inout("a0") arg0 => error,
66            inout("a1") arg1 => ret,
67            inout("a2") 0 => _,
68            inout("a3") 0 => _,
69            inout("a4") 0 => _,
70            inout("a5") 0 => _,
71            inout("a6") function => _,
72            inout("a7") extension as usize => _,
73            clobber_abi("C"),
74            options(nostack),
75        );
76    }
77
78    match error {
79        0 => Ok(ret),
80        error_code if error_code <= 8 => Err(SbiError::from_error(error_code)),
81        _ => Err(SbiError::Failed),
82    }
83}
84
85pub fn sbi_console_putchar(c: char) {
86    let _ = sbi_call(Extension::ConsolePutChar, 0, c as usize, 0);
87}
88
89pub fn sbi_console_getchar() -> char {
90    let ret = sbi_call(Extension::ConsoleGetChar, 0, 0, 0);
91    match ret {
92        Ok(c) => c as u8 as char,
93        Err(_) => '\0',
94    }
95}
96
97pub fn sbi_debug_console_write_byte(c: char) {
98    let _ = sbi_call(Extension::DebugConsole, 0x2, c as usize, 0);
99}
100
101pub fn sbi_set_timer(stime_value: u64) {
102    let _ = sbi_call(Extension::Timer, 0, stime_value as usize, 0);
103}
104
105pub fn sbi_system_reset(reset_type: u32, reset_reason: u32) -> ! {
106    let _ = sbi_call(
107        Extension::Srst,
108        0,
109        reset_type as usize,
110        reset_reason as usize,
111    );
112    loop {}
113}