Skip to main content

tg_rcore_tutorial_sbi/
lib.rs

1//! # tg-sbi
2//!
3//! 为 rCore 教学操作系统提供的 SBI (Supervisor Binary Interface) 调用封装。
4//!
5//! ## Features
6//!
7//! - `nobios`: 启用内置的 M-Mode SBI 实现,用于 `-bios none` 启动模式。
8//!   启用此 feature 后,crate 将提供自己的 M-Mode 陷阱处理程序和基本 SBI 服务。
9//!
10//! ## 支持的 SBI 扩展
11//!
12//! - Legacy 控制台 I/O (EID 0x01, 0x02)
13//! - Timer 扩展 (EID 0x54494D45)
14//! - System Reset 扩展 (EID 0x53525354)
15//!
16//! ## Example
17//!
18//! ```ignore
19//! use tg_sbi::{console_putchar, set_timer, shutdown};
20//!
21//! // 输出字符
22//! console_putchar(b'H');
23//!
24//! // 设置定时器中断
25//! set_timer(1000000);
26//!
27//! // 关闭系统
28//! shutdown(false);
29//! ```
30//!
31//! ## 调用链(学习者重点)
32//!
33//! 对于章节内核调用 `tg_sbi` 接口时,整体链路如下:
34//!
35//! 1. `chX` 内核代码调用 `set_timer/console_putchar/shutdown`
36//! 2. 本文件的 `sbi_call()` 用 `ecall` 发起 SBI 请求
37//! 3. 若未开启 `nobios`:请求进入外部 SBI 固件(例如 RustSBI)
38//! 4. 若开启 `nobios`:请求进入本 crate 的 `m_entry.asm + msbi.rs`
39//! 5. M-mode 处理完成后,返回到 S-mode 内核继续执行
40//!
41//! 这使得上层章节代码不需要关心“外部固件”还是“内置最小 SBI”,
42//! 只需面向统一 API 编程即可。
43
44#![no_std]
45#![deny(warnings, missing_docs)]
46
47// M-Mode SBI 实现(用于 -bios none 启动)
48#[cfg(all(feature = "nobios", target_arch = "riscv64"))]
49pub mod msbi;
50// M-Mode SBI 入口点(用于 -bios none 启动)
51#[cfg(all(feature = "nobios", target_arch = "riscv64"))]
52core::arch::global_asm!(include_str!("m_entry.asm"));
53
54// Legacy SBI 调用号(用于兼容性)
55const SBI_CONSOLE_PUTCHAR: usize = 1;
56const SBI_CONSOLE_GETCHAR: usize = 2;
57
58// SBI 扩展 ID
59const SBI_EXT_TIMER: usize = 0x54494D45;
60const SBI_EXT_SRST: usize = 0x53525354;
61
62/// SBI `ecall` 的寄存器约定(RISC-V):
63///
64/// - `x10(a0)`~`x12(a2)`:参数
65/// - `x16(a6)`:功能号 `fid`
66/// - `x17(a7)`:扩展号 `eid`
67/// - 返回值通常在 `x10(a0)`(以及部分实现使用 `x11(a1)`)
68///
69/// 这里封装成统一函数,避免章节代码直接写内联汇编。
70/// 通用 SBI 调用。
71#[cfg(all(target_arch = "riscv64", not(feature = "nobios")))]
72#[inline(always)]
73fn sbi_call(eid: usize, fid: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
74    let ret;
75    // SAFETY: 执行 SBI ecall,这是 S-mode 软件向 SBI 固件请求服务的标准接口。
76    // ecall 指令定义明确,寄存器约定遵循 RISC-V SBI 规范。
77    unsafe {
78        core::arch::asm!(
79            "ecall",
80            inlateout("x10") arg0 => ret,
81            in("x11") arg1,
82            in("x12") arg2,
83            in("x16") fid,
84            in("x17") eid,
85        );
86    }
87    ret
88}
89
90/// 通用 SBI 调用(`nobios` 模式,使用本仓库自带的 M-mode 处理程序)。
91///
92/// 在该模式下,`msbi.rs` 按 `SbiRet { error, value }` 语义返回:
93/// - `a0` 对应 `error`(0 表示成功,负值表示失败)
94/// - `a1` 对应 `value`
95///
96/// 因此这里会先检查 `ret1(error)`,再返回 `ret2(value)`。
97#[cfg(all(target_arch = "riscv64", feature = "nobios"))]
98#[inline(always)]
99fn sbi_call(eid: usize, fid: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
100    let ret1: isize;
101    let ret2: usize;
102    // SAFETY: 执行 ecall 调用自定义的 M-mode SBI 处理程序。
103    // 处理程序在 m_entry.asm 中设置,在 msbi.rs 中实现。
104    // 寄存器约定遵循 RISC-V SBI 规范。
105    unsafe {
106        core::arch::asm!(
107            "ecall",
108            inlateout("x10") arg0 => ret1,
109            inlateout("x11") arg1 => ret2,
110            in("x12") arg2,
111            in("x16") fid,
112            in("x17") eid
113        );
114    }
115    if ret1 < 0 {
116        panic!("SBI call failed: {}", ret1);
117    }
118    ret2
119}
120
121/// 非 riscv64 架构处理。
122#[cfg(not(target_arch = "riscv64"))]
123#[inline(always)]
124fn sbi_call(_eid: usize, _fid: usize, _arg0: usize, _arg1: usize, _arg2: usize) -> usize {
125    unimplemented!("SBI calls are only supported on riscv64")
126}
127
128/// 设置下一次定时器中断的时间。
129///
130/// `timer` 是目标触发时刻(通常是 `time::read64() + interval`)。
131/// 内核章节通常在每次调度前调用它,形成时间片中断。
132pub fn set_timer(timer: u64) {
133    sbi_call(SBI_EXT_TIMER, 0, timer as usize, 0, 0);
134}
135
136/// 向调试控制台输出一个字符。
137///
138/// 章节内 `print!/println!` 最终会落到该接口逐字节输出。
139pub fn console_putchar(c: u8) {
140    sbi_call(SBI_CONSOLE_PUTCHAR, 0, c as usize, 0, 0);
141}
142
143/// 从调试控制台读取一个字符。
144///
145/// 该接口的具体阻塞/非阻塞行为取决于底层 SBI 实现。
146pub fn console_getchar() -> usize {
147    sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0, 0)
148}
149
150/// 关闭系统。
151///
152/// `failure = false` 表示正常退出;`true` 表示异常退出。
153/// 若底层平台未真正关机,最后的 `panic!` 用于防止继续执行未知状态代码。
154pub fn shutdown(failure: bool) -> ! {
155    if failure {
156        sbi_call(SBI_EXT_SRST, 0, 1, 0, 0);
157    } else {
158        sbi_call(SBI_EXT_SRST, 0, 0, 0, 0);
159    }
160    panic!("It should shutdown!");
161}