Skip to content

Latest commit

 

History

History
116 lines (64 loc) · 8.9 KB

20220617-design.md

File metadata and controls

116 lines (64 loc) · 8.9 KB

可退出式 SBI 和基于 SBI 引导的思路

背景

u740-unmatched

u740 是具有 4 个 u74 核(RV64GC)及一个 s7 核(RV64IMAC)的 RISCV 多核通用计算机。

参考:

清华操作系统实验在 RISCV 硬件使用中产生的问题

清华操作系统实验中,对 u740 和 d1 的使用暴露了 2 个问题:

  1. SBI 软件无法服务于没有 S 态的 hart

    目前 SBI 的实现,无论初次引导还是处理陷入完成,都会回到特权态。这对于有 M、S、U 三个态的硬件来说没有什么问题,但如果对于只有两个态的硬件,如果 SBI 仍然要独占一个特权级,又只能提供标准指定那些功能,无疑就太弱了,但如果直接放弃在这样的硬件上提供服务,就意味着对于 u740 这样异构多核的系统,特权软件开发者不得不在两个态上开发,在 u74 核上得到 SBI 服务,在 s7 核上则没有 SBI 软件。这也意味着这些开发者不得不重复开发已经由 SBI 屏蔽的 HSM、IPI 和 TIMER 等功能,从而反过来导致 u74 上 SBI 软件必要性的动摇。

  2. RISCV 硬件引导方式种类多、不统一

    d1 单核、文档稀烂,各方面都不如 u740,唯独 fel 模式直接 dram 启动相当惊艳,给开发带来极大方便。反观 u740,sd 卡引导非常麻烦,不但需要制作适合 sd 卡的镜像、每次调试都需要拔插 sd 卡,而且 sd 卡还有寿命问题,可能直接物理损坏。

    实际上对于嵌入式软件开发,尤其是内核开发来说,开发阶段并不是很需要离线运行能力,相反烧写方便、快速才是首要需求。d1 的 fel 模式就通过 b0 固化的 usb 驱动实现了直接从 usb 到 sram 的引导,再结合 xfel 工具提供的 flash 烧写和 dram 初始化功能,支持 usb->dram 一步引导,极大简化了操作系统开发。

    清华操作系统课程设计团队中,FreeRTOS 组同学使用 d1,效率很高;而 zCore 多核调度组的同学由于需要多核,只能使用 u740,于是遇到了 sd 卡损坏无法引导的问题。而他们最终调通网络引导,解决了这个问题。

    这件事产生了一些启发。也许,网络引导才是最具通用性的引导方式。

    从现存的 RISCV 开发板来看,低至对标 stm32 的 esp32 微控制器,高至 u740 这样需要数百瓦电源、16GB 内存、4+1 核的正经 PC 级计算机,没有不带网络外设/网卡的,兼容性有保障。同时,xfel 工具由于需要连接 usb,在微软公布 wsl2 转接 usb 外设的方式之前,是无法用于 wsl2 的,对于其他虚拟机也都有适配性问题;而网络,无论是部署服务还是适配虚拟机,或者转发、代理,工具生态都十分丰富。还方便实现单服务器对多设备的分发部署。此外,相比 usb 为了想要支持的复杂功能,网络协议栈更纯粹,也就更容易实现高速。实际使用中,xfel 最快也不会超过 500kbps,而网络做到几个 Mbps 应该是轻而易举的事。

要解决这些问题,本文试图提出一系列 RISCV SBI 的通用扩展(比如,用于所有 RustSBI 实现),使 SBI 软件在不损失通用性、甚至提高通用性的情况下增加功能。

设计

可退出式 SBI

本节提出一种扩展,使 SBI 软件如非 RISCV 架构上的引导软件那样,自我清理,然后将控制权转移给同特权级的其他软件。讨论从 u740 上的 SBI 设计展开:

u740 上 u74 核和 s7 核的差异——比如 s7 核没有 mmu,对一些指令的支持也不完整——从根源上断绝了在 u74 核和 s7 核复用一套软件的可能性:将 u74 核限制到兼容 s7 核是没有意义的。

然而,SBI 软件若想服务于 u740 平台,就意味着必须同时服务于 u74 核和 s7 核。甚至,根据 s7 核的官方设定(监控核 Monitor Core,见 u74-mc),似乎从 s7 核通过 hsm 和 ipi 干预大核(或者说 worker hart)才是正统的设计。因此,u740 上的 SBI 软件可以以两种方式提供服务:

传统方式

在传统方式下,四个 u74 hart 组成一个 SMPx4 系统,然后再与 s7 组成 4+1 AMP 系统。s7 hart 完全独立于 u74x4,作为协处理器运行完全不同的软件(比如一个 M 态实时内核)。

初次引导时,SBI 指定一个 u74 为引导核,进入 S 态软件,其他所有核进入 HSM::STOPPED 状态,由引导核特权软件通过 HSM::hart_start SBI 调用控制启动。且在每个核上都存在 M 态的 SBI 作为执行环境(EE),对于 u74 为 SEE,而对于 s7,M 态 SBI 软件直接作为 UEE 向用户态提供服务。

作为补充,s7 上 SBI 构成的 UEE 除了标准指定的 SBI 功能,还需要提供一个扩展,实现将自身以 M 态重启。一旦收到此调用,s7 上的 SBI 通过 jr(而不是 mret)将控制权转交给指定软件,并在跳转前清理自己。

这样,开发者既可以将 s7 作为一个一般的协处理器使用,比如控制外设,或者执行 CPU 密集型用户程序,尤其是代数运算,比如结合 DSP 实现定点浮点混合运算。此外,s7 上的 SBI 还实现了向开发者让渡完整硬件——开发者仍可以在 s7 上运行一个有特权级的完整操作系统(如某种 RTOS),利用 AMP 构造实时系统。

正统方式

在正统方式下,s7 才是主核(primary hart),构成了一个 SMPx1,而 u74 是 s7 管理下的协处理器。

这种“小带大”式的结构也确实是更现代的:GPU 比 CPU 强大的 PC 并不罕见。归根结底,“主”“次”的划分是基于功能的而不是基于能力的。

初次引导时,SBI 固定从 s7 引导一个用户态软件,并提供包括 SBI 调用在内的执行环境。这个用户态软件看似用户态,实际上由于完全被 SBI 软件隔离^*^,完全可以执行任意安全性的代码,比如操作系统内核。此时,4 个 强大的 u74 hart 构成一个硬件线程池,s7 软件则负责将工作调度到到线程池上运行。如此,四个 u74 核可以真正对等,而且充分发挥计算性能;SBI 软件也不再需要特殊性^2^。

此外,两种方式可以通过通用的 SBI 调用互相转化,SBI 选择的主核启动正式的主核后关闭自己即可。

  • 甚至可以在所有 u74 hart 上通过 pmp 屏蔽其所在空间,实现对除 SBI 外所有软件的彻底隔离。

SBI 退出

对于传统方式,SBI 必然要提供一个退出扩展,以在 s7 核让出 M 态。即使以正统方式启动,也不必失掉这个能力:万一开发者对 M 态另有安排呢?以下是对这个扩展 SBI 一种可能的设计的描述:

fn sbi_exit(start_addr: usize, opaque: usize) -> SbiRet;

这个 SBI 调用用于退出调用者所在的核的 SBI。SBI 软件收到此调用后释放所有资源,以 SBI 所在的特权态跳转到 start_addr 并将 a1 设为 opaque

fn hart_start_no_sbi(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;

这个 SBI 调用用于直接以无 SBI 的形式启动 hartid 指示的 hart,当且仅当对方状态是 HSM::STOPPED 才会成功。效果如同 HSM::hart_start 但以 SBI 所在的特权态跳转到 start_addr

为了防止意外调用 sbi_exit 导致 SBI 不可恢复地退出,另外提供一个

fn sbi_keep() -> SbiRet;

用于禁止在这个核调用 sbi_exit,直到核由于关闭而复位。

集成引导功能的 SBI

大概这个工作仍会从 qemu 开始,初步的计划是:

  1. 解决现在 rustsbi-qemu 造成错误示范的问题

    sbi 是一套接口,rustsbi 只是 sbi 接口的 rust 映射。即使是运行时的 sbi 也仍然是接口,不应该认为存在一个 sbi 软件,而应该认为一个 ee 软件提供了 sbi 服务。

    因此,正确的 qemu 示范是另开一个名为 qemu-see 之类名字的仓库,实现一个集成了 bootloader 并提供 see 的软件,sbi 接口只是 see 软件的一个部分。

  2. 丰富 bootloader 功能

    比如为 qemu 提供网络等多样的引导方式。

  3. 添加 sbi 退出扩展实现纯 bootloader

一些有用的库

  • defmt:轻量日志及格式化
  • qemu-exit:qemu 退出机制的封装