Skip to content

Instantly share code, notes, and snippets.

@luojia65
Last active November 22, 2021 14:40
Show Gist options
  • Save luojia65/e6a31563d99aa566a8c61ffd6bbc2d70 to your computer and use it in GitHub Desktop.
Save luojia65/e6a31563d99aa566a8c61ffd6bbc2d70 to your computer and use it in GitHub Desktop.
sbicall calling convention
// -- 调用者(操作系统内核)
extern "sbicall" fn sbi_call(param: [usize; 6], a6: usize, a7: usize) -> (usize, usize);
// 最后两个参数必须是两个usize类型,a6: usize和a7: usize
// 除了最后两个参数,前面的参数必须是usize、[usize; N]或(usize, usize, ..)类型
// 依次会被填写到a0, a1, ..., a5寄存器中。
// 返回值可以是(usize, usize), [usize; 2]或者SbiRet(包含两个usize的结构体)
fn kernel() {
let (err, val) = sbi_call(SBI_POWER, POWER_RESET);
println!("err = {}, val = {}", err, val);
}
// 编译结果:
// ; let (err, val) = sbi_call(SBI_POWER, POWER_RESET);
// li a6, SBI_POWER
// li a7, POWER_RESET
// ecall
// ; println!("err = {}, val = {}", err, val);
// mv {param_1}, a0
// mv {param_2}, a1
// jal {println_funct}
// -- 用户(SBI实现)
extern "sbicall" fn handle_ecall(param: [usize; 6], module: usize, funct: usize) -> SbiRet {
// 最终得到a0到a7的值,并且能返回到SbiRet里
if module == 1 && funct == 2 {
return SbiRet::ok(0)
}
SbiRet::unsupported()
}
// 调用约定:
// 被调用者保存:保存所有所有用到的临时变量到栈上,填写返回值到a0-a1寄存器
// 调用者保存:所有的参数填写到a0-a7寄存器,不操作栈
extern "sbicall" fn handle_ecall(param: [usize; 6], module: usize, funct: usize) -> SbiRet {
let a = param[0] + param[1];
let b = param[2] + param[3];
let c = param[4] + param[5];
hint::black_box((a, b, c)); // 不优化掉未使用的变量
SbiRet::unsupported()
}
// 编译结果:
// ; extern "sbicall" fn handle_ecall(...) -> ... {
// addi sp, sp, -3*8
// sd t0, 0(sp)
// sd t1, 8(sp)
// sd t2, 16(sp)
// ; let a = param[0] + param[1];
// add t0, a0, a1
// ; let b = param[2] + param[3];
// add t1, a2, a3
// ; let c = param[4] + param[5];
// add t2, a4, a5
// ; hint::black_box((a, b, c));
// ; SbiRet::unsupported()
// li a0, SBI_UNSUPPORTED
// li a1, 0
// ; }
// ld t0, 0(sp)
// ld t1, 8(sp)
// ld t2, 16(sp)
// addi sp, sp, 3*8
// ret
// 实现方法1:ecall调用,mret返回
extern "sbicall" fn handle_ecall() {
// 直接开始调用
}
// 但是这种方法会破坏内核的栈
// 可以用attribute解决这个问题
use core::arch::riscv::{swap_stack, interrupt};
#[swap_stack(supervisor)]
#[interrupt(machine)]
extern "sbicall" fn handle_ecall() {
// ...
}
// 实现方法2:ecall调用,ret返回
// 缺点:需要下面这一大块代码才能用,而且这个调用约定是不完整的,ret返回后函数仍然没有返回
#[naked]
unsafe fn handle_trap() {
asm!(
"csrrw sp, sscratch, sp",
"jal {}",
"csrrw sp, sscratch, sp",
"mret",
sym handle_ecall,
)
}
// 实现方法3:call调用,ret返回
// 这种方法是调用约定是完整的,缺点是需要中断处理代码才能使用
// 运行时
fn execute() {
let mut rt = Runtime::new();
rt.set_handler(handle_ecall);
rt.run();
}
struct Runtime {
// 所有的 extern "sbicall" fn(PARAMETERS) -> RETURNS类型都能安全地强制转换
handle_ecall: extern "sbicall" fn([usize; 6], usize, usize) -> SbiRet,
}
impl Runtime {
fn run(&mut self) {
loop {
asm!(
"csrrw sp, sscratch, sp",
"mret", // 回去了
"csrrw sp, sscratch, sp",
"jal {}",
sym self.handle_ecall,
);
}
}
fn set_handler(&mut self, f: impl Into<extern "sbicall" fn(..) -> ..>) {
self.handle_ecall = f.into() as extern "sbicall" fn([usize; 6], usize, usize) -> SbiRet;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment