riscv: 使用sbi-rt库完成SBI操作 (#510)

未来的其它SBI操作也将使用sbi-rt

Signed-off-by: Zhouqi Jiang <luojia@hust.edu.cn>
This commit is contained in:
Luo Jia / Zhouqi Jiang 2024-02-07 11:38:15 +08:00 committed by GitHub
parent fccbe87dca
commit d14e28a8a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 28 additions and 593 deletions

View File

@ -62,6 +62,7 @@ x86_64 = "0.14.10"
# target为riscv64时使用下面的依赖
[target.'cfg(target_arch = "riscv64")'.dependencies]
riscv = { version = "0.11.0", features = [ "s-mode" ] }
sbi-rt = { git = "https://github.com/rustsbi/rustsbi" }
# 构建时依赖项

View File

@ -6,5 +6,6 @@ pub fn current_cpu_id() -> u32 {
/// 重置cpu
pub unsafe fn cpu_reset() -> ! {
unimplemented!("RiscV64 cpu_reset")
sbi_rt::system_reset(sbi_rt::WarmReboot, sbi_rt::NoReason);
unimplemented!("RiscV64 reset failed, manual override expected ...")
}

View File

@ -0,0 +1,24 @@
/// 向控制台打印字符串。
///
/// 该函数接受一个字节切片 `s` 作为输入,并迭代切片中的每个字节 `c`。
/// 然后调用 `sbi_rt::console_write_byte` 函数,将 `c` 的值作为参数传递给它。
///
/// # 安全性
///
/// 这个函数是安全的因为对SBI环境的操作不涉及不安全内存的访问操作。
///
/// # 参数
///
/// * `s` - 表示要打印的字符串的字节切片。
///
/// # 示例
///
/// ```
/// let message = b"Hello, World!";
/// console_putstr(message);
/// ```
pub fn console_putstr(s: &[u8]) {
for c in s {
sbi_rt::console_write_byte(*c);
}
}

View File

@ -1,220 +0,0 @@
#![allow(dead_code)]
use super::SbiError;
/// 使用给定的扩展和函数 ID 进行零参数的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 不接受任何参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall0(extension_id: usize, function_id: usize) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
in("a6") function_id,
in("a7") extension_id,
lateout("a0") error,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行单参数的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall1(
arg: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg => error,
in("a6") function_id,
in("a7") extension_id,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 一个带有给定扩展和函数ID的两参数`ecall`。
///
/// # 安全性
/// 只有在给定的函数ID接受两个参数时才安全调用此函数。否则行为将是未定义的
/// 因为将额外的中断寄存器传递给SBI实现时其内容将是未定义的。
#[inline]
pub unsafe fn ecall2(
arg0: usize,
arg1: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 3参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall3(
arg0: usize,
arg1: usize,
arg2: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 4参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall4(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 5参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall5(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 6参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
#[allow(clippy::too_many_arguments)]
pub unsafe fn ecall6(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
arg5: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a5") arg5,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}

View File

@ -1,192 +0,0 @@
#![allow(dead_code)]
use crate::{arch::driver::sbi::ecall::ecall1, mm::VirtAddr};
use core::arch::asm;
/// `sbi_set_timer` extension ID
pub const SET_TIMER_EID: usize = 0x00;
/// `sbi_console_putchar` extension ID
pub const CONSOLE_PUTCHAR_EID: usize = 0x01;
/// `sbi_console_getchar` extension ID
pub const CONSOLE_GETCHAR_EID: usize = 0x02;
/// `sbi_clear_ipi` extension ID
pub const CLEAR_IPI_EID: usize = 0x03;
/// `sbi_send_ipi` extension ID
pub const SEND_IPI_EID: usize = 0x04;
/// `sbi_remote_fence_i` extension ID
pub const REMOTE_FENCE_I_EID: usize = 0x05;
/// `sbi_remote_sfence_vma` extension ID
pub const REMOTE_SFENCE_VMA_EID: usize = 0x06;
/// `sbi_remote_sfence_vma_asid` extension ID
pub const REMOTE_SFENCE_VMA_ASID_EID: usize = 0x07;
/// `sbi_shutdown` extension ID
pub const SHUTDOWN_EID: usize = 0x08;
/// 计划在未来的某个时间触发中断。
///
/// ## 参数
///
/// - `stime`:要触发中断的绝对时间,以滴答为单位。如果`stime`小于当前时间,则不会触发中断。
///
/// ## 详情
///
/// 要清除计时器中断而不预约另一个计时器事件,可以将时间设置为无限远(`u64::MAX`)或
/// mask `sie` CSR的`STIE` 位。此函数将清除待处理计时器中断位。
///
/// 注意:`time` 是一个绝对时间,不是从调用时刻开始的偏移量。这意味着如果您想要设置一个未来`n`和tick之后
/// 触发的时钟,您需要首先读取 `time` CSR然后将滴答数添加到该值。关于如何确定每个滴答的时间
/// 这是平台依赖的,而时钟频率应在 CPU 节点的 `timebase-frequency` 属性中表达,如果可用的话。
#[inline]
pub unsafe fn set_timer(stime: u64) {
#[cfg(target_arch = "riscv64")]
unsafe {
ecall1(stime as usize, SET_TIMER_EID, 0).ok();
}
#[cfg(target_arch = "riscv32")]
unsafe {
asm!(
"ecall",
inout ("a0") stime as usize => _,
inout ("a1") (stime >> 32) as usize => _,
in("a7") SET_TIMER_EID,
);
}
}
/// 将字符写入调试控制台。如果仍有待处理的控制台输出,此调用将阻塞。如果不存在控制台,则不会执行任何操作。
#[inline]
pub unsafe fn console_putchar(c: u8) {
unsafe {
ecall1(c.into(), CONSOLE_PUTCHAR_EID, 0).ok();
}
}
/// 尝试从调试控制台获取一个字符。
/// 如果没有任何字符等待阅读,或者没有调试控制台设备,则此函数将返回[`None`]。
#[inline]
pub unsafe fn console_getchar() -> Option<u8> {
let mut ret: i8;
unsafe {
asm!(
"ecall",
lateout("a0") ret,
in("a7") CONSOLE_GETCHAR_EID,
);
}
match ret {
-1 => None,
_ => Some(ret as u8),
}
}
/// 清除current核心的待处理中断IPIs
#[inline]
#[deprecated = "S模式可以直接清除`sip.SSIP` CSR位因此无需调用此函数。"]
pub unsafe fn clear_ipi() {
unsafe {
asm!(
"ecall",
in("a7") CLEAR_IPI_EID,
lateout("a0") _,
);
}
}
/// 向所有由`hart_mask`位掩码指定的核心发送中断IPI。接收到的中断表示为监视器软件中断。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制位向量,向上取整到下一个`usize`。
#[inline]
pub unsafe fn send_ipi(hart_mask: &[usize]) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a7") SEND_IPI_EID,
);
}
}
/// 对指定的心脏hart执行 `FENCE.I` 指令
///
/// ## 参数
/// - `hart_mask`: 一个长度为 `n_harts / size_of::<usize>()` 的位矢量,
/// 向上取整到下一个 `usize」。
#[inline]
pub unsafe fn remote_fence_i(hart_mask: &[usize]) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a7") REMOTE_FENCE_I_EID,
);
}
}
/// 在指定的hart上执行`SFENCE.VMA`指令
/// 为指定的虚拟内存范围(由`start`和`size`指定)执行。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量,
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如,要失效一个
/// 包含2个4-KiB页面的区域您会为`size`传递`8192`。
///
/// 如果`start`和`size`都为`0`,或者如果`size`为[`usize::MAX`],则将执行完整的
/// `SFENCE.VMA`,而不仅仅是一个或多个页面大小的`SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma(hart_mask: &[usize], start: VirtAddr, size: usize) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a1") start.data(),
in("a2") size,
in("a7") REMOTE_SFENCE_VMA_EID,
);
}
}
/// 在指定的hart上执行SFENCE.VMA指令
///
/// 仅针对指定的地址空间IDASID执行虚拟内存范围指定的
/// start和size的hart_mask位掩码。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量,
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如,要失效一个
/// 包含2个4-KiB页面的区域您会为`size`传递`8192`。
/// - `asid`: 要执行`SFENCE.VMA`的地址空间ID。
///
/// 如果start和size都为0或者如果size为[usize::MAX],则将执行全
/// 部SFENCE.VMA而不是多个页面大小的SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma_asid(hart_mask: &[usize], start: usize, size: usize, asid: usize) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a1") start,
in("a2") size,
in("a3") asid,
in("a7") REMOTE_SFENCE_VMA_ASID_EID,
);
}
}
/// 将所有核心置于关闭状态,此时处理器的执行模式比当前监督模式具有更高的特权。此调用不会返回。
#[inline]
pub unsafe fn shutdown() -> ! {
unsafe {
asm!(
"ecall",
in("a7") SHUTDOWN_EID,
options(noreturn)
);
}
}

View File

@ -1,94 +0,0 @@
use self::legacy::console_putchar;
/// The SBI S-mode driver.
///
/// Some code takes from `https://github.com/repnop/sbi.git`
mod ecall;
pub mod legacy;
pub mod reset;
/// Error codes returned by SBI calls
///
/// note: `SBI_SUCCESS` is not represented here since this is to be used as the
/// error type in a `Result`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SbiError {
/// The SBI call failed
Failed,
/// The SBI call is not implemented or the functionality is not available
NotSupported,
/// An invalid parameter was passed
InvalidParameter,
/// The SBI implementation has denied execution of the call functionality
Denied,
/// An invalid address was passed
InvalidAddress,
/// The resource is already available
AlreadyAvailable,
/// The resource was previously started
AlreadyStarted,
/// The resource was previously stopped
AlreadyStopped,
}
impl SbiError {
#[inline]
fn new(n: isize) -> Self {
match n {
-1 => SbiError::Failed,
-2 => SbiError::NotSupported,
-3 => SbiError::InvalidParameter,
-4 => SbiError::Denied,
-5 => SbiError::InvalidAddress,
-6 => SbiError::AlreadyAvailable,
-7 => SbiError::AlreadyStarted,
-8 => SbiError::AlreadyStopped,
n => unreachable!("bad SBI error return value: {}", n),
}
}
}
impl core::fmt::Display for SbiError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}",
match self {
SbiError::AlreadyAvailable => "resource is already available",
SbiError::Denied => "SBI implementation denied execution",
SbiError::Failed => "call to SBI failed",
SbiError::InvalidAddress => "invalid address passed",
SbiError::InvalidParameter => "invalid parameter passed",
SbiError::NotSupported => "SBI call not implemented or functionality not available",
SbiError::AlreadyStarted => "resource was already started",
SbiError::AlreadyStopped => "resource was already stopped",
}
)
}
}
/// 向控制台打印字符串。
///
/// 该函数接受一个字节切片 `s` 作为输入,并迭代切片中的每个字节 `c`。
/// 然后调用 `console_putchar` 函数,将 `c` 的值作为参数传递给它。
///
/// # 安全性
/// 该函数被标记为 `unsafe`,因为它调用了 `console_putchar` 函数,
/// 而假设该函数执行可能有副作用或违反内存安全的底层操作。
/// 调用者有责任确保 `s` 切片是有效的并且正确终止的。
///
/// # 参数
///
/// * `s` - 表示要打印的字符串的字节切片。
///
/// # 示例
///
/// ```
/// let message = b"Hello, World!";
/// console_putstr(message);
/// ```
pub unsafe fn console_putstr(s: &[u8]) {
for c in s {
unsafe { console_putchar(*c) };
}
}

View File

@ -1,85 +0,0 @@
#![allow(dead_code)]
use super::{ecall::ecall2, SbiError};
/// System reset extension ID
pub const EXTENSION_ID: usize = 0x53525354;
/// The type of reset to perform
#[derive(Debug, Clone, Copy)]
pub enum ResetType {
/// Shutdown the system
Shutdown,
/// Power off all hardware and perform a cold boot
ColdReboot,
/// Reset processors and some hardware
WarmReboot,
/// Platform specific reset type. The variant value is a value within the
/// range `0x00000000..=0x0FFFFFFF`. A value outside of that range will be
/// clamped to the maximum possible valid value for this reset type.
PlatformSpecific(u32),
}
impl ResetType {
fn to_u32(self) -> u32 {
match self {
ResetType::Shutdown => 0,
ResetType::ColdReboot => 1,
ResetType::WarmReboot => 2,
ResetType::PlatformSpecific(n) => n.min(0x0FFFFFFF) + 0xF0000000,
}
}
}
/// The reason for performing the reset
#[derive(Debug, Clone, Copy)]
pub enum ResetReason {
/// No reason for reset
NoReason,
/// System failure
SystemFailure,
/// SBI implementation specific reset reason. The variant value is a value
/// within the range `0x00000000..=0x0FFFFFFF`. A value outside of that
/// range will be clamped to the maximum possible valid value for this reset
/// reason type.
SbiSpecific(u32),
/// Platform specific reset reason. The variant value is a value within the
/// range `0x00000000..=0x0FFFFFFF`. A value outside of that range will be
/// clamped to the maximum possible valid value for this reset reason type.
PlatformSpecific(u32),
}
impl ResetReason {
fn to_u32(self) -> u32 {
match self {
ResetReason::NoReason => 0,
ResetReason::SystemFailure => 1,
ResetReason::SbiSpecific(n) => n.min(0x0FFFFFFF) + 0xE0000000,
ResetReason::PlatformSpecific(n) => n.min(0x0FFFFFFF) + 0xF0000000,
}
}
}
/// Attempt to reset the system in the provided method, with a reason for the
/// reset.
///
/// ### Possible errors
///
/// [`SbiError::NotSupported`]: The [`ResetType`] is valid but not implemented.
///
/// [`SbiError::Failed`]: The system reset request failed for an unknown reason.
pub fn system_reset(
kind: ResetType,
reason: ResetReason,
) -> Result<core::convert::Infallible, SbiError> {
match unsafe {
ecall2(
kind.to_u32() as usize,
reason.to_u32() as usize,
EXTENSION_ID,
0,
)
} {
Ok(_) => unreachable!("SBI returned `Ok` after a system reset call"),
Err(e) => Err(e),
}
}

View File

@ -564,7 +564,7 @@ pub fn send_to_default_serial8250_port(s: &[u8]) {
if unsafe { INITIALIZED } {
todo!("riscv64: send_to_default_serial8250_port")
} else {
unsafe { crate::arch::driver::sbi::console_putstr(s) };
crate::arch::driver::sbi::console_putstr(s);
}
}
}