#sbi #risc-v

已删除 riscvsbi

最小化RISC-V的SBI实现库

0.1.4 2021年5月20日
0.1.3 2021年5月19日
0.1.2 2021年5月16日
0.1.1 2021年5月16日
0.1.0 2021年5月16日

#6 in #sbi

MulanPSL-2.0 OR MIT

52KB
687

riscvsbi

RISC-V监控器二进制接口(SBI)在Rust中的实现;在M模式下运行。SBI版本0.2 SBI版本0.3


lib.rs:

一个最小化的RISC-V的SBI实现。

如何使用RustSBI

SBI功能包括引导序列和内核环境。要将内核引导起来,将内核放置到RustSBI实现定义的地址,然后RustSBI将准备一个环境并跳转到此地址。

调用SBI环境

要使用内核环境,您可以使用SBI调用或模拟指令。SBI调用类似于操作系统的syscall。RISC-V SBI定义了许多SBI模块,在每一个模块中都有不同的函数,您在调用之前应该选择一个函数。然后,您需要准备一些参数,这些参数的定义在不同的函数中并不相同。

现在您有一个模块号,一个函数号和一些SBI调用参数。您在监控器级别上调用一个特殊的ecall指令,它将陷入机器级别的SBI实现。它将处理您的ecall,类似于内核处理用户级别的系统调用。

SBI函数返回两个值,除了一个之外。第一个值将是错误号,它将告诉SBI调用是否成功,或者发生了哪种错误。第二个值是实际的返回值,其含义根据您调用的函数而异。

在不同编程语言中调用SBI

调用SBI与调用系统调用类似。

模块号需要放在寄存器a7上,函数号在a6上。参数应从a0a5放置,首先放入a0,然后放入a1,等等。未使用的参数可以设置为任何值或保持不变。

在寄存器就绪后,调用一个名为ecall的指令。然后,返回值将被放置在a0a1寄存器中。错误值可以从a0中读取,返回值将被放置在a1中。

在Rust中,这里有一个使用内联汇编调用SBI函数的示例

#[inline(always)]
fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
    let (error, value);
    match () {
        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
        () => unsafe { asm!(
            "ecall",
            in("a0") arg0, in("a1") arg1,
            in("a6") function, in("a7") extension,
            lateout("a0") error, lateout("a1") value,
        ) },
        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
        () => {
            drop((extension, function, arg0, arg1));
            unimplemented!("not RISC-V instruction set architecture")
        }
    };
    SbiRet { error, value }
}

#[inline]
pub fn get_spec_version() -> usize {
    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0).value
}

复杂的SBI函数可能会失败。在这个示例中,我们只取值,但在完整的设计中,我们应该处理从SbiRet返回的错误值。

依赖关系

~270KB