diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 62cb65e1..fe7179f2 100644 --- a/kernel/Cargo.toml +++ b/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" } # 构建时依赖项 diff --git a/kernel/src/arch/riscv64/cpu.rs b/kernel/src/arch/riscv64/cpu.rs index a3dee48f..be943990 100644 --- a/kernel/src/arch/riscv64/cpu.rs +++ b/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 ...") } diff --git a/kernel/src/arch/riscv64/driver/sbi.rs b/kernel/src/arch/riscv64/driver/sbi.rs new file mode 100644 index 00000000..ac1ce6be --- /dev/null +++ b/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); + } +} diff --git a/kernel/src/arch/riscv64/driver/sbi/ecall.rs b/kernel/src/arch/riscv64/driver/sbi/ecall.rs deleted file mode 100644 index 0dc0ce47..00000000 --- a/kernel/src/arch/riscv64/driver/sbi/ecall.rs +++ /dev/null @@ -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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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)), - } -} diff --git a/kernel/src/arch/riscv64/driver/sbi/legacy.rs b/kernel/src/arch/riscv64/driver/sbi/legacy.rs deleted file mode 100644 index eda07dad..00000000 --- a/kernel/src/arch/riscv64/driver/sbi/legacy.rs +++ /dev/null @@ -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 { - 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`。 -#[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」。 -#[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`。 -/// - `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`。 -/// - `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) - ); - } -} diff --git a/kernel/src/arch/riscv64/driver/sbi/mod.rs b/kernel/src/arch/riscv64/driver/sbi/mod.rs deleted file mode 100644 index 15f117cb..00000000 --- a/kernel/src/arch/riscv64/driver/sbi/mod.rs +++ /dev/null @@ -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) }; - } -} diff --git a/kernel/src/arch/riscv64/driver/sbi/reset.rs b/kernel/src/arch/riscv64/driver/sbi/reset.rs deleted file mode 100644 index f8b5b3d1..00000000 --- a/kernel/src/arch/riscv64/driver/sbi/reset.rs +++ /dev/null @@ -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 { - 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), - } -} diff --git a/kernel/src/driver/tty/serial/serial8250/mod.rs b/kernel/src/driver/tty/serial/serial8250/mod.rs index 4a07b18d..5245c767 100644 --- a/kernel/src/driver/tty/serial/serial8250/mod.rs +++ b/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); } } }