浏览代码

feat: 整理 ecall handle,解决 legacy a6 问题

临时方案,最终将移除 legacy

Signed-off-by: YdrMaster <[email protected]>
YdrMaster 2 年之前
父节点
当前提交
2aeecdf6cf
共有 22 个文件被更改,包括 303 次插入699 次删除
  1. 1 1
      Cargo.toml
  2. 0 1
      src/base.rs
  3. 0 208
      src/ecall.rs
  4. 26 72
      src/ecall/base.rs
  5. 14 36
      src/ecall/hsm.rs
  6. 0 19
      src/ecall/ipi.rs
  7. 0 73
      src/ecall/legacy.rs
  8. 139 0
      src/ecall/mod.rs
  9. 26 102
      src/ecall/pmu.rs
  10. 0 113
      src/ecall/rfence.rs
  11. 25 0
      src/ecall/rfnc.rs
  12. 12 0
      src/ecall/spi.rs
  13. 8 10
      src/ecall/srst.rs
  14. 36 0
      src/ecall/time.rs
  15. 0 45
      src/ecall/timer.rs
  16. 0 5
      src/guest.rs
  17. 1 1
      src/hsm.rs
  18. 2 1
      src/ipi.rs
  19. 4 8
      src/lib.rs
  20. 2 2
      src/pmu.rs
  21. 3 1
      src/reset.rs
  22. 4 1
      src/rfence.rs

+ 1 - 1
Cargo.toml

@@ -19,7 +19,7 @@ edition = "2021"
 
 [dependencies]
 riscv = "0.8"
-sbi-spec = { git = "https://github.com/rustsbi/sbi-spec.git", rev = "a0fd76b" }
+sbi-spec = { git = "https://github.com/rustsbi/sbi-spec.git", rev = "9d728bb" }
 
 [features]
 default = []

+ 0 - 1
src/extension.rs → src/base.rs

@@ -9,7 +9,6 @@ pub fn probe_extension(extension: usize) -> bool {
         srst::EID_SRST => crate::reset::probe_reset(),
         hsm::EID_HSM => crate::hsm::probe_hsm(),
         pmu::EID_PMU => crate::pmu::probe_pmu(),
-        // new extensions should be added here to be probed
         _ => false,
     }
 }

+ 0 - 208
src/ecall.rs

@@ -1,208 +0,0 @@
-//! 这个模块将会处理所有的SBI调用陷入
-// 你应该在riscv-rt或其它中断处理函数里,调用这个模块的内容
-
-mod base;
-mod hsm;
-mod ipi;
-mod legacy;
-mod pmu;
-mod rfence;
-mod srst;
-mod timer;
-
-use sbi_spec as spec;
-
-/// Supervisor environment call handler function
-///
-/// This function is used by platform runtime to handle environment call `ecall` instruction.
-///
-/// You should call this function in your runtime's exception handler.
-/// If the incoming exception is caused by supervisor `ecall`,
-/// call this function with parameters extracted from trap frame.
-/// After this function returns, store the return value into `a0` and `a1` parameters.
-///
-/// This function also adapts to the legacy functions.
-/// If the supervisor called any of legacy function, the `a0` and `a1` parameter
-/// is transferred to error and value field of `SbiRet` respectively.
-/// In this case, implementations should always store the result into `a0` and `a1` in
-/// any environment call functions including legacy functions.
-///
-/// # Example
-///
-/// A typical usage:
-///
-/// ```no_run
-/// # use riscv::register::{mepc, mcause::{self, Trap, Exception}};
-/// # struct TrapFrame { a0: usize, a1: usize, a2: usize, a3: usize,
-/// # a4: usize, a5: usize, a6: usize, a7: usize }
-/// extern "C" fn rust_handle_exception(ctx: &mut TrapFrame) {
-///     if mcause::read().cause() == Trap::Exception(Exception::SupervisorEnvCall) {
-///         let params = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
-///         let ans = rustsbi::ecall(ctx.a7, ctx.a6, params);
-///         ctx.a0 = ans.error;
-///         ctx.a1 = ans.value;
-///         mepc::write(mepc::read().wrapping_add(4));
-///     }
-///     // other conditions..
-/// }
-/// ```
-///
-/// Do not forget to advance `mepc` by 4 after an ecall is handled.
-/// This skips the `ecall` instruction itself which is 4-byte long in all conditions.
-#[inline]
-pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
-    // RISC-V SBI requires SBI extension IDs (EIDs) and SBI function IDs (FIDs)
-    // are encoded as signed 32-bit integers
-    #[cfg(not(target_pointer_width = "32"))]
-    if u32::try_from(extension).is_err() {
-        return SbiRet::not_supported();
-    }
-    let function = match u32::try_from(function) {
-        Ok(f) => f,
-        Err(_) => return SbiRet::not_supported(),
-    };
-    // process actual environment calls
-    match extension {
-        spec::rfnc::EID_RFNC => {
-            rfence::handle_ecall_rfence(function, param[0], param[1], param[2], param[3], param[4])
-        }
-        spec::time::EID_TIME => match () {
-            #[cfg(target_pointer_width = "64")]
-            () => timer::handle_ecall_timer_64(function, param[0]),
-            #[cfg(target_pointer_width = "32")]
-            () => timer::handle_ecall_timer_32(function, param[0], param[1]),
-        },
-        spec::spi::EID_SPI => ipi::handle_ecall_ipi(function, param[0], param[1]),
-        spec::base::EID_BASE => base::handle_ecall_base(function, param[0]),
-        spec::hsm::EID_HSM => hsm::handle_ecall_hsm(function, param[0], param[1], param[2]),
-        spec::srst::EID_SRST => srst::handle_ecall_srst(function, param[0], param[1]),
-        spec::pmu::EID_PMU => match () {
-            #[cfg(target_pointer_width = "64")]
-            () => {
-                pmu::handle_ecall_pmu_64(function, param[0], param[1], param[2], param[3], param[4])
-            }
-            #[cfg(target_pointer_width = "32")]
-            () => pmu::handle_ecall_pmu_32(
-                function, param[0], param[1], param[2], param[3], param[4], param[5],
-            ),
-        },
-        spec::legacy::LEGACY_SET_TIMER => match () {
-            #[cfg(target_pointer_width = "64")]
-            () => legacy::set_timer_64(param[0]),
-            #[cfg(target_pointer_width = "32")]
-            () => legacy::set_timer_32(param[0], param[1]),
-        }
-        .legacy_void(param[0], param[1]),
-        spec::legacy::LEGACY_CONSOLE_PUTCHAR => {
-            legacy::console_putchar(param[0]).legacy_void(param[0], param[1])
-        }
-        spec::legacy::LEGACY_CONSOLE_GETCHAR => legacy::console_getchar().legacy_return(param[1]),
-        spec::legacy::LEGACY_SEND_IPI => legacy::send_ipi(param[0]).legacy_void(param[0], param[1]),
-        spec::legacy::LEGACY_SHUTDOWN => legacy::shutdown().legacy_void(param[0], param[1]),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-/// Call result returned by SBI
-///
-/// After `handle_ecall` finished, you should save returned `error` in `a0`, and `value` in `a1`.
-#[repr(C)] // ensure that return value follows RISC-V SBI calling convention
-pub struct SbiRet {
-    /// Error number
-    pub error: usize,
-    /// Result value
-    pub value: usize,
-}
-
-impl SbiRet {
-    /// Return success SBI state with given value.
-    #[inline]
-    pub fn ok(value: usize) -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_SUCCESS,
-            value,
-        }
-    }
-    /// The SBI call request failed for unknown reasons.
-    #[inline]
-    pub fn failed() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_FAILED,
-            value: 0,
-        }
-    }
-    /// SBI call failed due to not supported by target ISA, operation type not supported,
-    /// or target operation type not implemented on purpose.
-    #[inline]
-    pub fn not_supported() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_NOT_SUPPORTED,
-            value: 0,
-        }
-    }
-    /// SBI call failed due to invalid hart mask parameter, invalid target hart id, invalid operation type
-    /// or invalid resource index.
-    #[inline]
-    pub fn invalid_param() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_INVALID_PARAM,
-            value: 0,
-        }
-    }
-    /// SBI call failed for invalid mask start address, not a valid physical address parameter,
-    /// or the target address is prohibited by PMP to run in supervisor mode.
-    #[inline]
-    pub fn invalid_address() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_INVALID_ADDRESS,
-            value: 0,
-        }
-    }
-    /// SBI call failed for the target resource is already available, e.g. the target hart is already
-    /// started when caller still request it to start.
-    #[inline]
-    pub fn already_available() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_ALREADY_AVAILABLE,
-            value: 0,
-        }
-    }
-    /// SBI call failed for the target resource is already started, e.g. target performance counter is started.
-    #[inline]
-    pub fn already_started() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_ALREADY_STARTED,
-            value: 0,
-        }
-    }
-    /// SBI call failed for the target resource is already stopped, e.g. target performance counter is stopped.
-    #[inline]
-    pub fn already_stopped() -> SbiRet {
-        SbiRet {
-            error: spec::binary::RET_ERR_ALREADY_STOPPED,
-            value: 0,
-        }
-    }
-    #[inline]
-    pub(crate) fn legacy_ok(legacy_value: usize) -> SbiRet {
-        SbiRet {
-            error: legacy_value,
-            value: 0,
-        }
-    }
-    // only used for legacy where a0, a1 return value is not modified
-    #[inline]
-    pub(crate) fn legacy_void(self, a0: usize, a1: usize) -> SbiRet {
-        SbiRet {
-            error: a0,
-            value: a1,
-        }
-    }
-    #[inline]
-    pub(crate) fn legacy_return(self, a1: usize) -> SbiRet {
-        SbiRet {
-            error: self.error,
-            value: a1,
-        }
-    }
-}

+ 26 - 72
src/ecall/base.rs

@@ -1,73 +1,27 @@
-//! base extension
-use super::SbiRet;
-use crate::{SBI_SPEC_MAJOR, SBI_SPEC_MINOR};
-use riscv::register::{marchid, mimpid, mvendorid};
-
-const FUNCTION_BASE_GET_SPEC_VERSION: u32 = 0x0;
-const FUNCTION_BASE_GET_SBI_IMPL_ID: u32 = 0x1;
-const FUNCTION_BASE_GET_SBI_IMPL_VERSION: u32 = 0x2;
-const FUNCTION_BASE_PROBE_EXTENSION: u32 = 0x3;
-const FUNCTION_BASE_GET_MVENDORID: u32 = 0x4;
-const FUNCTION_BASE_GET_MARCHID: u32 = 0x5;
-const FUNCTION_BASE_GET_MIMPID: u32 = 0x6;
-
-#[inline]
-pub fn handle_ecall_base(function: u32, param0: usize) -> SbiRet {
-    match function {
-        FUNCTION_BASE_GET_SPEC_VERSION => get_spec_version(),
-        FUNCTION_BASE_GET_SBI_IMPL_ID => get_sbi_impl_id(),
-        FUNCTION_BASE_GET_SBI_IMPL_VERSION => get_sbi_impl_version(),
-        FUNCTION_BASE_PROBE_EXTENSION => probe_extension(param0),
-        FUNCTION_BASE_GET_MVENDORID => get_mvendorid(),
-        FUNCTION_BASE_GET_MARCHID => get_marchid(),
-        FUNCTION_BASE_GET_MIMPID => get_mimpid(),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[inline]
-fn get_spec_version() -> SbiRet {
-    let spec_version = (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR);
-    SbiRet::ok(spec_version)
-}
-
-#[inline]
-fn get_sbi_impl_id() -> SbiRet {
-    let sbi_impl_id = crate::IMPL_ID_RUSTSBI;
-    SbiRet::ok(sbi_impl_id)
-}
-
-#[inline]
-fn get_sbi_impl_version() -> SbiRet {
-    let sbi_impl_version = crate::RUSTSBI_VERSION;
-    SbiRet::ok(sbi_impl_version)
-}
-
-#[inline]
-fn probe_extension(extension_id: usize) -> SbiRet {
-    const NO_EXTENSION: usize = 0;
-    const HAS_EXTENSION: usize = 1;
-    // SBI extension IDs (EIDs) and SBI function IDs (FIDs) are encoded as signed 32-bit integers.
-    // When passed in registers these follow the standard above calling convention rules.
-    #[cfg(not(target_pointer_width = "32"))]
-    if extension_id > u32::MAX as usize {
-        return SbiRet::ok(NO_EXTENSION);
-    }
-    let ans = crate::extension::probe_extension(extension_id);
-    SbiRet::ok(if ans { HAS_EXTENSION } else { NO_EXTENSION })
-}
-
-#[inline]
-fn get_mvendorid() -> SbiRet {
-    SbiRet::ok(mvendorid::read().map(|r| r.bits()).unwrap_or(0))
-}
-
-#[inline]
-fn get_marchid() -> SbiRet {
-    SbiRet::ok(marchid::read().map(|r| r.bits()).unwrap_or(0))
-}
-
-#[inline]
-fn get_mimpid() -> SbiRet {
-    SbiRet::ok(mimpid::read().map(|r| r.bits()).unwrap_or(0))
+use sbi_spec::binary::SbiRet;
+
+#[inline]
+pub(super) fn handle_ecall(function: usize, param0: usize) -> SbiRet {
+    use crate::base::*;
+    use crate::{IMPL_ID_RUSTSBI, RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR};
+    use riscv::register::{marchid, mimpid, mvendorid};
+    use sbi_spec::base::*;
+
+    let value = match function {
+        GET_SPEC_VERSION => (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR),
+        GET_SBI_IMPL_ID => IMPL_ID_RUSTSBI,
+        GET_SBI_IMPL_VERSION => RUSTSBI_VERSION,
+        PROBE_EXTENSION => {
+            if probe_extension(param0) {
+                UNAVAILABLE_EXTENSION.wrapping_add(1)
+            } else {
+                UNAVAILABLE_EXTENSION
+            }
+        }
+        GET_MVENDORID => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
+        GET_MARCHID => marchid::read().map(|r| r.bits()).unwrap_or(0),
+        GET_MIMPID => mimpid::read().map(|r| r.bits()).unwrap_or(0),
+        _ => return SbiRet::not_supported(),
+    };
+    SbiRet::ok(value)
 }

+ 14 - 36
src/ecall/hsm.rs

@@ -1,42 +1,20 @@
-//! hsm extension
-use super::SbiRet;
-
-const FUNCTION_HSM_HART_START: u32 = 0x0;
-const FUNCTION_HSM_HART_STOP: u32 = 0x1;
-const FUNCTION_HSM_HART_GET_STATUS: u32 = 0x2;
-const FUNCTION_HSM_HART_SUSPEND: u32 = 0x3;
+use sbi_spec::binary::SbiRet;
 
 #[inline]
-pub fn handle_ecall_hsm(function: u32, param0: usize, param1: usize, param2: usize) -> SbiRet {
+pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize, param2: usize) -> SbiRet {
+    use crate::hsm::*;
+    use sbi_spec::hsm::*;
     match function {
-        FUNCTION_HSM_HART_START => hart_start(param0, param1, param2),
-        FUNCTION_HSM_HART_STOP => hart_stop(),
-        FUNCTION_HSM_HART_GET_STATUS => hart_get_status(param0),
-        FUNCTION_HSM_HART_SUSPEND => hart_suspend(param0, param1, param2),
+        HART_START => hart_start(param0, param1, param2),
+        HART_STOP => hart_stop(),
+        HART_GET_STATUS => hart_get_status(param0),
+        HART_SUSPEND => {
+            if let Ok(suspend_type) = u32::try_from(param0) {
+                hart_suspend(suspend_type, param1, param2)
+            } else {
+                SbiRet::invalid_param()
+            }
+        }
         _ => SbiRet::not_supported(),
     }
 }
-
-#[inline]
-fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
-    crate::hsm::hart_start(hartid, start_addr, opaque)
-}
-
-#[inline]
-fn hart_stop() -> SbiRet {
-    crate::hsm::hart_stop()
-}
-
-#[inline]
-fn hart_get_status(hartid: usize) -> SbiRet {
-    crate::hsm::hart_get_status(hartid)
-}
-
-#[inline]
-fn hart_suspend(suspend_type: usize, resume_addr: usize, opaque: usize) -> SbiRet {
-    if suspend_type > u32::MAX as usize {
-        // valid suspend type should be a `u32` typed value
-        return SbiRet::invalid_param();
-    }
-    crate::hsm::hart_suspend(suspend_type as u32, resume_addr, opaque)
-}

+ 0 - 19
src/ecall/ipi.rs

@@ -1,19 +0,0 @@
-use super::SbiRet;
-use crate::hart_mask::HartMask;
-use crate::ipi::send_ipi_many;
-
-const FUNCTION_IPI_SEND_IPI: u32 = 0x0;
-
-#[inline]
-pub fn handle_ecall_ipi(function: u32, param0: usize, param1: usize) -> SbiRet {
-    match function {
-        FUNCTION_IPI_SEND_IPI => send_ipi(param0, param1),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[inline]
-fn send_ipi(hart_mask: usize, hart_mask_base: usize) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    send_ipi_many(hart_mask)
-}

+ 0 - 73
src/ecall/legacy.rs

@@ -1,73 +0,0 @@
-use super::SbiRet;
-use crate::hart_mask::HartMask;
-use crate::ipi::send_ipi_many;
-use crate::legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
-use riscv::register::{mie, mip};
-
-#[inline]
-pub fn console_putchar(param0: usize) -> SbiRet {
-    let ch = (param0 & 0xff) as u8;
-    legacy_stdio_putchar(ch);
-    SbiRet::ok(0) // the return value 0 is ignored in legacy
-}
-
-// Returns a character, or -1 when failed.
-#[inline]
-pub fn console_getchar() -> SbiRet {
-    let ch = legacy_stdio_getchar();
-    SbiRet::legacy_ok(ch)
-}
-
-#[inline]
-pub fn send_ipi(hart_mask_addr: usize) -> SbiRet {
-    // note(unsafe): if any load fault, should be handled by machine level;
-    // see docs at `legacy_from_addr`.
-    let hart_mask = unsafe { HartMask::legacy_from_addr(hart_mask_addr) };
-    send_ipi_many(hart_mask)
-}
-
-#[inline]
-#[cfg(target_pointer_width = "64")]
-pub fn set_timer_64(time_value: usize) -> SbiRet {
-    crate::timer::set_timer(time_value as u64);
-
-    let mtip = mip::read().mtimer();
-    if mtip {
-        unsafe {
-            mie::clear_mtimer();
-            mip::set_stimer();
-        }
-    } else {
-        unsafe {
-            mie::set_mtimer();
-            mip::clear_stimer();
-        }
-    }
-    SbiRet::ok(0)
-}
-
-#[inline]
-#[cfg(target_pointer_width = "32")]
-pub fn set_timer_32(arg0: usize, arg1: usize) -> SbiRet {
-    let time_value = (arg0 as u64) + ((arg1 as u64) << 32);
-    crate::timer::set_timer(time_value as u64);
-
-    let mtip = mip::read().mtimer();
-    if mtip {
-        unsafe {
-            mie::clear_mtimer();
-            mip::set_stimer();
-        }
-    } else {
-        unsafe {
-            mie::set_mtimer();
-            mip::clear_stimer();
-        }
-    }
-    SbiRet::ok(0)
-}
-
-#[inline]
-pub fn shutdown() -> SbiRet {
-    crate::reset::legacy_reset()
-}

+ 139 - 0
src/ecall/mod.rs

@@ -0,0 +1,139 @@
+//! 这个模块将会处理所有的 SBI 调用陷入,你应该在中断处理函数里调用 `handle_ecall`
+
+// §4
+mod base;
+// §6
+mod time;
+// §7
+mod spi;
+// §8
+mod rfnc;
+// §9
+mod hsm;
+// §10
+mod srst;
+// §11
+mod pmu;
+
+use crate::{
+    ipi::send_ipi_many, legacy_stdio_getchar, legacy_stdio_putchar, reset::legacy_reset, HartMask,
+};
+use sbi_spec::{self as spec, binary::SbiRet};
+
+/// Supervisor environment call handler function
+///
+/// This function is used by platform runtime to handle environment call `ecall` instruction.
+///
+/// You should call this function in your runtime's exception handler.
+/// If the incoming exception is caused by supervisor `ecall`,
+/// call this function with parameters extracted from trap frame.
+/// After this function returns, store the return value into `a0` and `a1` parameters.
+///
+/// This function also adapts to the legacy functions.
+/// If the supervisor called any of legacy function, the `a0` and `a1` parameter
+/// is transferred to error and value field of `SbiRet` respectively.
+/// In this case, implementations should always store the result into `a0` and `a1` in
+/// any environment call functions including legacy functions.
+///
+/// # Example
+///
+/// A typical usage:
+///
+/// ```no_run
+/// # use riscv::register::{mepc, mcause::{self, Trap, Exception}};
+/// # struct TrapFrame { a0: usize, a1: usize, a2: usize, a3: usize,
+/// # a4: usize, a5: usize, a6: usize, a7: usize }
+/// extern "C" fn rust_handle_exception(ctx: &mut TrapFrame) {
+///     if mcause::read().cause() == Trap::Exception(Exception::SupervisorEnvCall) {
+///         let params = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
+///         let ans = rustsbi::ecall(ctx.a7, ctx.a6, params);
+///         ctx.a0 = ans.error;
+///         ctx.a1 = ans.value;
+///         mepc::write(mepc::read().wrapping_add(4));
+///     }
+///     // other conditions..
+/// }
+/// ```
+///
+/// Do not forget to advance `mepc` by 4 after an ecall is handled.
+/// This skips the `ecall` instruction itself which is 4-byte long in all conditions.
+#[inline]
+pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
+    match extension {
+        spec::rfnc::EID_RFNC => {
+            rfnc::handle_ecall(function, param[0], param[1], param[2], param[3], param[4])
+        }
+        spec::time::EID_TIME => match () {
+            #[cfg(target_pointer_width = "64")]
+            () => time::handle_ecall(function, param[0]),
+            #[cfg(target_pointer_width = "32")]
+            () => time::handle_ecall(function, param[0], param[1]),
+        },
+        spec::spi::EID_SPI => spi::handle_ecall(function, param[0], param[1]),
+        spec::base::EID_BASE => base::handle_ecall(function, param[0]),
+        spec::hsm::EID_HSM => hsm::handle_ecall(function, param[0], param[1], param[2]),
+        spec::srst::EID_SRST => srst::handle_ecall(function, param[0], param[1]),
+        spec::pmu::EID_PMU => match () {
+            #[cfg(target_pointer_width = "64")]
+            () => pmu::handle_ecall(function, param[0], param[1], param[2], param[3], param[4]),
+            #[cfg(target_pointer_width = "32")]
+            () => pmu::handle_ecall(
+                fu32, param[0], param[1], param[2], param[3], param[4], param[5],
+            ),
+        },
+        // handle legacy callings.
+        //
+        // legacy 调用不使用 a1 返回值,总把 a1 填回 `SbiRet::value` 来模拟非 legacy 的行为。
+        spec::legacy::LEGACY_SET_TIMER => {
+            match () {
+                #[cfg(target_pointer_width = "64")]
+                () => crate::timer::set_timer(param[0] as _),
+                #[cfg(target_pointer_width = "32")]
+                () => crate::timer::set_timer_32(param[0] as _, param[1] as _),
+            };
+            use riscv::register::{mie, mip};
+            let mtip = mip::read().mtimer();
+            if mtip {
+                unsafe {
+                    mie::clear_mtimer();
+                    mip::set_stimer();
+                }
+            } else {
+                unsafe {
+                    mie::set_mtimer();
+                    mip::clear_stimer();
+                }
+            }
+            SbiRet {
+                error: param[0],
+                value: param[1],
+            }
+        }
+        spec::legacy::LEGACY_CONSOLE_PUTCHAR => {
+            legacy_stdio_putchar(param[0] as _);
+            SbiRet {
+                error: param[0],
+                value: param[1],
+            }
+        }
+        spec::legacy::LEGACY_CONSOLE_GETCHAR => SbiRet {
+            error: legacy_stdio_getchar(),
+            value: param[1],
+        },
+        spec::legacy::LEGACY_SEND_IPI => {
+            send_ipi_many(unsafe { HartMask::legacy_from_addr(param[0]) });
+            SbiRet {
+                error: param[0],
+                value: param[1],
+            }
+        }
+        spec::legacy::LEGACY_SHUTDOWN => legacy_reset(),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline]
+const fn concat_u32(h: usize, l: usize) -> u64 {
+    ((h as u64) << 32) | (l as u64)
+}

+ 26 - 102
src/ecall/pmu.rs

@@ -1,41 +1,34 @@
-//! pmu extension
-use super::SbiRet;
-use crate::pmu;
+use sbi_spec::binary::SbiRet;
 
-const FUNCTION_PMU_NUM_COUNTERS: u32 = 0x0;
-const FUNCTION_PMU_COUNTER_GET_INFO: u32 = 0x1;
-const FUNCTION_PMU_COUNTER_CONFIG_MATCHING: u32 = 0x2;
-const FUNCTION_PMU_COUNTER_START: u32 = 0x3;
-const FUNCTION_PMU_COUNTER_STOP: u32 = 0x4;
-const FUNCTION_PMU_COUNTER_FW_READ: u32 = 0x5;
-
-#[inline]
 #[cfg(target_pointer_width = "64")]
-pub fn handle_ecall_pmu_64(
-    function: u32,
+#[inline]
+pub fn handle_ecall(
+    function: usize,
     param0: usize,
     param1: usize,
     param2: usize,
     param3: usize,
     param4: usize,
 ) -> SbiRet {
+    use crate::pmu::*;
+    use sbi_spec::pmu::*;
     match function {
-        FUNCTION_PMU_NUM_COUNTERS => pmu::num_counters(),
-        FUNCTION_PMU_COUNTER_GET_INFO => pmu::counter_get_info(param0),
-        FUNCTION_PMU_COUNTER_CONFIG_MATCHING => {
-            counter_config_matching_64(param0, param1, param2, param3, param4)
+        PMU_NUM_COUNTERS => num_counters(),
+        PMU_COUNTER_GET_INFO => counter_get_info(param0),
+        PMU_COUNTER_CONFIG_MATCHING => {
+            counter_config_matching(param0, param1, param2, param3, param4 as _)
         }
-        FUNCTION_PMU_COUNTER_START => counter_start_64(param0, param1, param2, param3),
-        FUNCTION_PMU_COUNTER_STOP => pmu::counter_stop(param0, param1, param2),
-        FUNCTION_PMU_COUNTER_FW_READ => pmu::counter_fw_read(param0),
+        PMU_COUNTER_START => counter_start(param0, param1, param2, param3 as _),
+        PMU_COUNTER_STOP => counter_stop(param0, param1, param2),
+        PMU_COUNTER_FW_READ => counter_fw_read(param0),
         _ => SbiRet::not_supported(),
     }
 }
 
-#[inline]
 #[cfg(target_pointer_width = "32")]
-pub fn handle_ecall_pmu_32(
-    function: u32,
+#[inline]
+pub fn handle_ecall(
+    function: usize,
     param0: usize,
     param1: usize,
     param2: usize,
@@ -43,87 +36,18 @@ pub fn handle_ecall_pmu_32(
     param4: usize,
     param5: usize,
 ) -> SbiRet {
+    use super::concat_u32;
+    use crate::pmu::*;
+    use sbi_spec::pmu::*;
     match function {
-        FUNCTION_PMU_NUM_COUNTERS => pmu::num_counters(),
-        FUNCTION_PMU_COUNTER_GET_INFO => pmu::counter_get_info(param0),
-        FUNCTION_PMU_COUNTER_CONFIG_MATCHING => {
-            counter_config_matching_32(param0, param1, param2, param3, param4, param5)
+        PMU_NUM_COUNTERS => num_counters(),
+        PMU_COUNTER_GET_INFO => counter_get_info(param0),
+        PMU_COUNTER_CONFIG_MATCHING => {
+            counter_config_matching(param0, param1, param2, param3, concat_u32(param4, param5))
         }
-        FUNCTION_PMU_COUNTER_START => counter_start_32(param0, param1, param2, param3, param4),
-        FUNCTION_PMU_COUNTER_STOP => pmu::counter_stop(param0, param1, param2),
-        FUNCTION_PMU_COUNTER_FW_READ => pmu::counter_fw_read(param0),
+        PMU_COUNTER_START => counter_start(param0, param1, param2, concat_u32(param3, param4)),
+        PMU_COUNTER_STOP => counter_stop(param0, param1, param2),
+        PMU_COUNTER_FW_READ => counter_fw_read(param0),
         _ => SbiRet::not_supported(),
     }
 }
-
-#[cfg(target_pointer_width = "64")]
-#[inline]
-fn counter_config_matching_64(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    config_flags: usize,
-    event_idx: usize,
-    event_data: usize,
-) -> SbiRet {
-    pmu::counter_config_matching(
-        counter_idx_base,
-        counter_idx_mask,
-        config_flags,
-        event_idx,
-        event_data as u64,
-    )
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-fn counter_config_matching_32(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    config_flags: usize,
-    event_idx: usize,
-    event_data_lo: usize,
-    event_data_hi: usize,
-) -> SbiRet {
-    let event_data = (event_data_lo as u64) + ((event_data_hi as u64) << 32);
-    pmu::counter_config_matching(
-        counter_idx_base,
-        counter_idx_mask,
-        config_flags,
-        event_idx,
-        event_data,
-    )
-}
-
-#[cfg(target_pointer_width = "64")]
-#[inline]
-fn counter_start_64(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    start_flags: usize,
-    initial_value: usize,
-) -> SbiRet {
-    pmu::counter_start(
-        counter_idx_base,
-        counter_idx_mask,
-        start_flags,
-        initial_value as u64,
-    )
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-fn counter_start_32(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    start_flags: usize,
-    initial_value_lo: usize,
-    initial_value_hi: usize,
-) -> SbiRet {
-    let initial_value = (initial_value_lo as u64) + ((initial_value_hi as u64) << 32);
-    pmu::counter_start(
-        counter_idx_base,
-        counter_idx_mask,
-        start_flags,
-        initial_value,
-    )
-}

+ 0 - 113
src/ecall/rfence.rs

@@ -1,113 +0,0 @@
-use super::SbiRet;
-use crate::hart_mask::HartMask;
-use crate::rfence;
-
-const FUNCTION_RFENCE_REMOTE_FENCE_I: u32 = 0x0;
-const FUNCTION_RFENCE_REMOTE_SFENCE_VMA: u32 = 0x1;
-const FUNCTION_RFENCE_REMOTE_SFENCE_VMA_ASID: u32 = 0x2;
-const FUNCTION_RFENCE_REMOTE_HFENCE_GVMA_VMID: u32 = 0x3;
-const FUNCTION_RFENCE_REMOTE_HFENCE_GVMA: u32 = 0x4;
-const FUNCTION_RFENCE_REMOTE_HFENCE_VVMA_ASID: u32 = 0x5;
-const FUNCTION_RFENCE_REMOTE_HFENCE_VVMA: u32 = 0x6;
-
-#[inline]
-pub fn handle_ecall_rfence(
-    function: u32,
-    param0: usize,
-    param1: usize,
-    param2: usize,
-    param3: usize,
-    param4: usize,
-) -> SbiRet {
-    match function {
-        FUNCTION_RFENCE_REMOTE_FENCE_I => remote_fence_i(param0, param1),
-        FUNCTION_RFENCE_REMOTE_SFENCE_VMA => remote_sfence_vma(param0, param1, param2, param3),
-        FUNCTION_RFENCE_REMOTE_SFENCE_VMA_ASID => {
-            remote_sfence_vma_asid(param0, param1, param2, param3, param4)
-        }
-        FUNCTION_RFENCE_REMOTE_HFENCE_GVMA_VMID => {
-            remote_hfence_gvma_vmid(param0, param1, param2, param3, param4)
-        }
-        FUNCTION_RFENCE_REMOTE_HFENCE_GVMA => remote_hfence_gvma(param0, param1, param2, param3),
-        FUNCTION_RFENCE_REMOTE_HFENCE_VVMA_ASID => {
-            remote_hfence_vvma_asid(param0, param1, param2, param3, param4)
-        }
-        FUNCTION_RFENCE_REMOTE_HFENCE_VVMA => remote_hfence_vvma(param0, param1, param2, param3),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[inline]
-fn remote_fence_i(hart_mask: usize, hart_mask_base: usize) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_fence_i(hart_mask)
-}
-
-#[inline]
-fn remote_sfence_vma(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_sfence_vma(hart_mask, start_addr, size)
-}
-
-#[inline]
-fn remote_sfence_vma_asid(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-    asid: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_sfence_vma_asid(hart_mask, start_addr, size, asid)
-}
-
-#[inline]
-fn remote_hfence_gvma_vmid(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-    vmid: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_hfence_gvma_vmid(hart_mask, start_addr, size, vmid)
-}
-
-#[inline]
-fn remote_hfence_gvma(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_hfence_gvma(hart_mask, start_addr, size)
-}
-
-#[inline]
-fn remote_hfence_vvma_asid(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-    asid: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_hfence_vvma_asid(hart_mask, start_addr, size, asid)
-}
-
-#[inline]
-fn remote_hfence_vvma(
-    hart_mask: usize,
-    hart_mask_base: usize,
-    start_addr: usize,
-    size: usize,
-) -> SbiRet {
-    let hart_mask = HartMask::from_mask_base(hart_mask, hart_mask_base);
-    rfence::remote_hfence_vvma(hart_mask, start_addr, size)
-}

+ 25 - 0
src/ecall/rfnc.rs

@@ -0,0 +1,25 @@
+use sbi_spec::binary::SbiRet;
+
+#[inline]
+pub fn handle_ecall(
+    function: usize,
+    param0: usize,
+    param1: usize,
+    param2: usize,
+    param3: usize,
+    param4: usize,
+) -> SbiRet {
+    use crate::rfence::*;
+    use sbi_spec::rfnc::*;
+    let hart_mask = crate::HartMask::from_mask_base(param0, param1);
+    match function {
+        REMOTE_FENCE_I => remote_fence_i(hart_mask),
+        REMOTE_SFENCE_VMA => remote_sfence_vma(hart_mask, param2, param3),
+        REMOTE_SFENCE_VMA_ASID => remote_sfence_vma_asid(hart_mask, param2, param3, param4),
+        REMOTE_HFENCE_GVMA_VMID => remote_hfence_gvma_vmid(hart_mask, param2, param3, param4),
+        REMOTE_HFENCE_GVMA => remote_hfence_gvma(hart_mask, param2, param3),
+        REMOTE_HFENCE_VVMA_ASID => remote_hfence_vvma_asid(hart_mask, param2, param3, param4),
+        REMOTE_HFENCE_VVMA => remote_hfence_vvma(hart_mask, param2, param3),
+        _ => SbiRet::not_supported(),
+    }
+}

+ 12 - 0
src/ecall/spi.rs

@@ -0,0 +1,12 @@
+use sbi_spec::binary::SbiRet;
+
+#[inline]
+pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize) -> SbiRet {
+    use crate::hart_mask::HartMask;
+    use crate::ipi::*;
+    use sbi_spec::spi::*;
+    match function {
+        SEND_IPI => send_ipi_many(HartMask::from_mask_base(param0, param1)),
+        _ => SbiRet::not_supported(),
+    }
+}

+ 8 - 10
src/ecall/srst.rs

@@ -1,16 +1,14 @@
-use super::SbiRet;
-
-const FUNCTION_SYSTEM_RESET: u32 = 0x0;
+use sbi_spec::binary::SbiRet;
 
 #[inline]
-pub fn handle_ecall_srst(function: u32, param0: usize, param1: usize) -> SbiRet {
+pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize) -> SbiRet {
+    use crate::reset::*;
+    use sbi_spec::srst::*;
     match function {
-        FUNCTION_SYSTEM_RESET => system_reset(param0 as _, param1 as _),
+        SYSTEM_RESET => match (u32::try_from(param0), u32::try_from(param1)) {
+            (Ok(reset_type), Ok(reset_reason)) => system_reset(reset_type, reset_reason),
+            (_, _) => SbiRet::invalid_param(),
+        },
         _ => SbiRet::not_supported(),
     }
 }
-
-#[inline]
-fn system_reset(reset_type: u32, reset_reason: u32) -> SbiRet {
-    crate::reset::system_reset(reset_type, reset_reason)
-}

+ 36 - 0
src/ecall/time.rs

@@ -0,0 +1,36 @@
+use sbi_spec::binary::SbiRet;
+
+#[cfg(target_pointer_width = "64")]
+#[inline]
+pub(super) fn handle_ecall(function: usize, param0: usize) -> SbiRet {
+    use crate::timer::*;
+    use sbi_spec::time::*;
+    match function {
+        SET_TIMER => {
+            if set_timer(param0 as _) {
+                SbiRet::ok(0)
+            } else {
+                SbiRet::not_supported()
+            }
+        }
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline]
+pub(super) fn handle_ecall_timer(function: usize, param0: usize, param1: usize) -> SbiRet {
+    use super::concat_u32;
+    use crate::timer::*;
+    use sbi_spec::time::*;
+    match function {
+        SET_TIMER => {
+            if set_timer(concat_u32(a1, a0)) {
+                SbiRet::ok(0)
+            } else {
+                SbiRet::not_supported()
+            }
+        }
+        _ => SbiRet::not_supported(),
+    }
+}

+ 0 - 45
src/ecall/timer.rs

@@ -1,45 +0,0 @@
-use super::SbiRet;
-
-const FUNCTION_TIMER_SET_TIMER: u32 = 0x0;
-
-#[inline]
-#[cfg(target_pointer_width = "64")]
-pub fn handle_ecall_timer_64(function: u32, param0: usize) -> SbiRet {
-    match function {
-        FUNCTION_TIMER_SET_TIMER => set_timer(param0),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[inline]
-#[cfg(target_pointer_width = "32")]
-pub fn handle_ecall_timer_32(function: u32, param0: usize, param1: usize) -> SbiRet {
-    match function {
-        FUNCTION_TIMER_SET_TIMER => set_timer(param0, param1),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-fn set_timer(arg0: usize, arg1: usize) -> SbiRet {
-    let time_value = (arg0 as u64) + ((arg1 as u64) << 32);
-    if crate::timer::set_timer(time_value) {
-        SbiRet::ok(0)
-    } else {
-        // should be probed with probe_extension
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(target_pointer_width = "64")]
-#[inline]
-fn set_timer(arg0: usize) -> SbiRet {
-    let time_value = arg0 as u64;
-    if crate::timer::set_timer(time_value) {
-        SbiRet::ok(0)
-    } else {
-        // should be probed with probe_extension
-        SbiRet::not_supported()
-    }
-}

+ 0 - 5
src/guest.rs

@@ -1,5 +0,0 @@
-//! Guest pointer width which differs from host pointer width
-//!
-//! Useful for developing hypervisors.
-
-// TODO: design this module

+ 1 - 1
src/hsm.rs

@@ -1,4 +1,4 @@
-use crate::ecall::SbiRet;
+use sbi_spec::binary::SbiRet;
 
 /// Hart State Management Extension
 ///

+ 2 - 1
src/ipi.rs

@@ -1,4 +1,5 @@
-use crate::{ecall::SbiRet, hart_mask::HartMask, util::AmoOnceRef};
+use crate::{hart_mask::HartMask, util::AmoOnceRef};
+use sbi_spec::binary::SbiRet;
 
 /// Inter-processor interrupt support
 pub trait Ipi: Send + Sync {

+ 4 - 8
src/lib.rs

@@ -163,8 +163,8 @@
 #[doc(hidden)]
 #[macro_use]
 pub mod legacy_stdio;
+mod base;
 mod ecall;
-mod extension;
 mod hart_mask;
 mod hsm;
 mod ipi;
@@ -203,12 +203,9 @@ const SBI_SPEC_MINOR: usize = 0;
 /// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
 const IMPL_ID_RUSTSBI: usize = 4;
 
-const RUSTSBI_VERSION_MAJOR: usize =
-    (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as usize;
-const RUSTSBI_VERSION_MINOR: usize =
-    (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as usize;
-const RUSTSBI_VERSION_PATCH: usize =
-    (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as usize;
+const RUSTSBI_VERSION_MAJOR: usize = (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as _;
+const RUSTSBI_VERSION_MINOR: usize = (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as _;
+const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as _;
 const RUSTSBI_VERSION: usize =
     (RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
 
@@ -217,7 +214,6 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
 pub use ecall::handle_ecall as ecall;
-pub use ecall::SbiRet;
 pub use hart_mask::HartMask;
 pub use hsm::{init_hsm, Hsm};
 pub use ipi::{init_ipi, Ipi};

+ 2 - 2
src/pmu.rs

@@ -1,4 +1,4 @@
-use crate::{ecall::SbiRet, util::AmoOnceRef};
+use sbi_spec::binary::SbiRet;
 
 /// Performance Monitoring Unit Extension
 ///
@@ -196,7 +196,7 @@ pub trait Pmu: Send + Sync {
     fn counter_fw_read(&self, counter_idx: usize) -> SbiRet;
 }
 
-// TODO: all the events here
+use crate::util::AmoOnceRef;
 
 static PMU: AmoOnceRef<dyn Pmu> = AmoOnceRef::new();
 

+ 3 - 1
src/reset.rs

@@ -1,4 +1,4 @@
-use crate::{ecall::SbiRet, util::AmoOnceRef};
+use sbi_spec::binary::SbiRet;
 
 /// System Reset Extension
 ///
@@ -49,6 +49,8 @@ pub trait Reset: Send + Sync {
     }
 }
 
+use crate::util::AmoOnceRef;
+
 static RESET: AmoOnceRef<dyn Reset> = AmoOnceRef::new();
 
 #[doc(hidden)]

+ 4 - 1
src/rfence.rs

@@ -1,4 +1,5 @@
-use crate::{ecall::SbiRet, hart_mask::HartMask, util::AmoOnceRef};
+use crate::hart_mask::HartMask;
+use sbi_spec::binary::SbiRet;
 
 /// Remote fence support
 ///
@@ -134,6 +135,8 @@ pub trait Rfence: Send + Sync {
     }
 }
 
+use crate::util::AmoOnceRef;
+
 static RFENCE: AmoOnceRef<dyn Rfence> = AmoOnceRef::new();
 
 pub fn init_rfence(rfence: &'static dyn Rfence) {