diff --git a/articles/20230612-introduction-to-riscv-sbi.md b/articles/20230612-introduction-to-riscv-sbi.md new file mode 100644 index 0000000000000000000000000000000000000000..8a93ffb9b06adf64b3d7370c8f7d9a4e71e00e6f --- /dev/null +++ b/articles/20230612-introduction-to-riscv-sbi.md @@ -0,0 +1,203 @@ +> Author:  groot [gr00t@foxmail.com](gr00t@foxmail.com) +> +>Date:    2023/06/12 +> +>Revisor: Falcon falcon@tinylab.org[falcon@tinylab.org] +> +>Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) +> +>Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) +> +>Sponsor: PLCT Lab, ISCAS + + + +# RISC-V SBI概述 + + + +## 什么是SBI? + +SBI 全称 Supervisor Binary Interface,是 RISC-V execution environment interface ( EEI ) 之一[[1]](#fn1),目的是使处于 supervisor-sode ( S-mode 或者 VS-mode ) 的程序能够很方便的移植到实现不同扩展指令集的 RISC-V 架构的处理器上。提供SBI接口给监管模式软件的更高特权软件被称为SBI实现或Supervisor Execution Environmen(监管执行环境SEE)。 + + +## 为什么要有SBI + +![图一](https://cdn.nlark.com/yuque/0/2023/svg/25893182/1686552812284-f1eed519-e7e0-4808-9c91-0797bc21a6c0.svg#clientId=u578a3445-851c-4&from=ui&id=SD6FL&originHeight=191&originWidth=761&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=27785&status=done&style=none&taskId=ua4caa884-eded-47dc-b8a8-0e12204c465&title=%E5%9B%BE%E4%B8%80 "图一")
如果没有 SBI(如图一右侧),针对实现的扩展指令集不同的 RISC-V 微架构,可能要采用不同的方式才能够使操作系统内核触发 M-mode 的动作。而有了 SBI 之后,只要在扩展指令集不同的 RISC-V 微架构中实现统一的向上的 SBI 接口,上层的操作系统就可以不再关注具体的微架构细节,而是专注实现 SBI 接口提供的功能即可,大大提升了处于 supervisor-mode 的程序的可移植性。
这其实就是计算机中的一个很重要的哲学——抽象。通过将底层的具体实现屏蔽,向上提供统一的接口,使上层应用不需关注过多底层细节,大大简化了程序的开发难度。
通俗地,我们将 SBI 比作手机充电器接口。曾经市场上可能有非常多种类的充电器接口,如果 A 的手机接口和B的手机接口不一样,那么他们没办法互相使用对方的充电器,但是如果我们将充电器接口全部统一为 type-c 接口,这种尴尬地场景就不会再发生了,大大的方便了用户。 + + + +## SBI的作用 + +![图二](https://cdn.nlark.com/yuque/0/2023/svg/25893182/1686552867532-d8b1da30-bc00-4956-a64d-bc41d46685ec.svg#clientId=u578a3445-851c-4&from=ui&id=ub1f31844&originHeight=311&originWidth=411&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=25182&status=done&style=none&taskId=uccd8d207-78c3-4d3b-90c5-26061e72a24&title=%E5%9B%BE%E4%BA%8C "图二") + +SBI 的第一个作用我们开头已经讲过了(图一左)。
除此之外,如上图所示,SBI 也可能在 hypervisor-mode(HS-mode)下作为虚拟机管理程序实现。
从更高一级的特权模式来看,SBI implementation 为 supervisor-mode 软件分配物理执行单元(HARTs)。
因此,从 SBI implementation 的角度来看,S-mode 的 HART 被称为虚拟 HART(图一)。而如果实现是一个虚拟机管理程序(图二),那么虚拟HART则表示 VS-mode 的虚拟HART。 + + +## SBI Implementations + +理论上说,因为 SBI spec 是开源的,只要能够按照spec说明实现其功能就可以称为 BI Implementation。
不过当前经过 riscv 官方认证的 Implementation 有如下几个[[2]](#fn2): + +| ImplementationID | Name | +| ---------------- | -------------------------- | +| 0 | Berkeley Boot Loader (BBL) | +| 1 | OpenSBI | +| 2 | Xvisor | +| 3 | KVM | +| 4 | RustSBI | +| 5 | Diosix | +| 6 | Coffer | + + + +# OpenSBI + + + +## 什么是OpenSBI + +OpenSBI 是 SBI spec 的一个 RISC-V SBI C 语言参考实现。 它由 Western Digital 公司发起,并且在 2019 年进行了开源。 + + +## 编译openSBI + +> 这里已经默认用户安装好 qemu 和 u-boot + +1. 下载 OpenSBI 源码 + +```shell +git clone https://github.com/riscv/opensbi.git +``` + +2. 进入 OpenSBI 文件夹 + +```shell +cd qemu-opensbi +``` + +3. 新建文件夹并进入 + +```shell +mkdir build +cd build +``` + +3. 编译 + +```shell +make -C $(pwd)/.. PLATFORM=generic CROSS_COMPILE=riscv64-Linux-gnu- FW_PAYLOAD_PATH=path/to/u-boot.bin +``` + + + +## 启动 OpenSBI + +1. 在 `qemu-opensbi` 文件夹中执行下面的命令 + +```shell +qemu-system-riscv64 -M virt -m 256 -nographic -bios build/platform/generic/firmware/fw_payload.el +``` + +2. 显示输出
+ +![图三](https://cdn.nlark.com/yuque/0/2023/png/25893182/1686552932179-85c23a0a-cb0c-4a36-8067-5e752492ebf2.png#averageHue=%23436041&clientId=u578a3445-851c-4&from=ui&id=JPuUw&originHeight=1467&originWidth=1073&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=264141&status=done&style=none&taskId=u024b6a09-101b-4cc4-917d-34b63c9c3ff&title=%E5%9B%BE%E4%B8%89 "图三") + + +# Linux Kernel SBI代码分析引导 + +下面分析Linux源码中的SBI代码: + + +## ECALL指令 + +`ECALL` 指令用于向执行环境发出请求,在不同的特权等级中执行 `ECALL` 指令有不同的效果:在 User Mode 中会引发 environment-call-from-U-mode 异常,在Supervisor Mode 中会引发 environment-call-from-S-mode 异常, 而在Machine Mode 中会引发 environment-call-from-M-mode 异常[[3]](#fn3)。 + + +### Linux内核SBI代码 + +`ECALL` 指令在Linux中用于系统调用,如下为 `arch/riscv/kernel/sbi.c` 中的部分代码。
`sbi_ecall` 指令接受8个参数,分别是 + +- `ext` : SBI extension ID (EID) +- `fid` : SBI function ID (FID) +- `arg0-arg5` : SBI函数调用参数 + +```c +struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5) +{ + struct sbiret ret; + + register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); + register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); + register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); + register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); + register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4); + register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5); + register uintptr_t a6 asm ("a6") = (uintptr_t)(fid); + register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); + asm volatile ("ecall" + : "+r" (a0), "+r" (a1) + : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7) + : "memory"); + ret.error = a0; + ret.value = a1; + + return ret; +} +``` + +> 使用 `ECALL` 指令时,将异常类型写在a7寄存器, 参数写在a0-a5寄存器,后面会根据异常类型的不同调用不同的异常处理函数
`register` 关键字表明后面的变量直接存储在寄存器中
`asm ("ax")` 表明将后面的变量与 `ax` 寄存器进行绑定
`asm volatile` 表明嵌入汇编代码进入C代码中,并且将 `a0` 和 `a1` 寄存器既作为输入寄存器又作为输出寄存器传给 `ECALL` 指令,而 `a2` - `a6` 寄存器作为输入寄存器传递给 `ECALL`
`ECALL` 函数返回两个值 `a0` 和 `a1` ,`sbi_ecall` 函数将这两个值作为错误和值返回给调用它的函数 + + +比如实现一个putchar函数用于打印一个字符到系统控制台上,就如下通过调用 `sbi_ecall` 来实现: + +```c +void sbi_console_putchar(int ch) +{ + sbi_ecall(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, ch, 0, 0, 0, 0, 0); +} +``` + +然后我们进入 `arch/riscv/include/sbi.h` ,观察宏定义: + +```c +enum sbi_ext_id { +#ifdef CONFIG_RISCV_SBI_V01 + SBI_EXT_0_1_SET_TIMER = 0x0, + SBI_EXT_0_1_CONSOLE_PUTCHAR = 0x1, + SBI_EXT_0_1_CONSOLE_GETCHAR = 0x2, + SBI_EXT_0_1_CLEAR_IPI = 0x3, + SBI_EXT_0_1_SEND_IPI = 0x4, + SBI_EXT_0_1_REMOTE_FENCE_I = 0x5, + SBI_EXT_0_1_REMOTE_SFENCE_VMA = 0x6, + SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID = 0x7, + SBI_EXT_0_1_SHUTDOWN = 0x8, +#endif + SBI_EXT_BASE = 0x10, + SBI_EXT_TIME = 0x54494D45, + SBI_EXT_IPI = 0x735049, + SBI_EXT_RFENCE = 0x52464E43, + SBI_EXT_HSM = 0x48534D, + SBI_EXT_SRST = 0x53525354, + SBI_EXT_PMU = 0x504D55, + + /* Experimentals extensions must lie within this range */ + SBI_EXT_EXPERIMENTAL_START = 0x08000000, + SBI_EXT_EXPERIMENTAL_END = 0x08FFFFFF, + + /* Vendor extensions must lie within this range */ + SBI_EXT_VENDOR_START = 0x09000000, + SBI_EXT_VENDOR_END = 0x09FFFFFF, +}; +``` + +观察到 `SBI_EXT_0_1_CONSOLE_PUTCHAR` 定义为 `0x1` 。 + +--- + + +1. Volume I: RISC-V Unprivileged ISA V20191214-draft [↩︎](#fnref1) +2. RISC-V Supervisor Binary Interface Specification Version -v2.0-rc1, 2023-06-01: Draft [↩︎](#fnref2) +3. Volume II: RISC-V Privileged Architectures V1.12-draft [↩︎](#fnref3) \ No newline at end of file