Browse Source

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

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

Signed-off-by: Zhouqi Jiang <[email protected]>
Luo Jia / Zhouqi Jiang 1 year ago
parent
commit
d14e28a8a9

+ 1 - 0
kernel/Cargo.toml

@@ -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" }
 
 
 # 构建时依赖项

+ 2 - 1
kernel/src/arch/riscv64/cpu.rs

@@ -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 ...")
 }

+ 24 - 0
kernel/src/arch/riscv64/driver/sbi.rs

@@ -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);
+    }
+}

+ 0 - 220
kernel/src/arch/riscv64/driver/sbi/ecall.rs

@@ -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)),
-    }
-}

+ 0 - 192
kernel/src/arch/riscv64/driver/sbi/legacy.rs

@@ -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指令
-///
-/// 仅针对指定的地址空间ID(ASID)执行虚拟内存范围指定的
-/// 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)
-        );
-    }
-}

+ 0 - 94
kernel/src/arch/riscv64/driver/sbi/mod.rs

@@ -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) };
-    }
-}

+ 0 - 85
kernel/src/arch/riscv64/driver/sbi/reset.rs

@@ -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),
-    }
-}

+ 1 - 1
kernel/src/driver/tty/serial/serial8250/mod.rs

@@ -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);
         }
     }
 }