0.1.4 |
|
---|---|
0.1.3 |
|
0.1.2 |
|
0.1.1 |
|
0.1.0 |
|
#6 in #sbi
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
上。参数应从a0
到a5
放置,首先放入a0
,然后放入a1
,等等。未使用的参数可以设置为任何值或保持不变。
在寄存器就绪后,调用一个名为ecall
的指令。然后,返回值将被放置在a0
和a1
寄存器中。错误值可以从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