Browse Source

Add remote fence extension; document fixes

luojia65 4 years ago
parent
commit
b9428d3297

+ 1 - 1
CHANGELOG.md

@@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased]
 ### Added
 - S-level Illegal instruction exception is now delegated into S-level software handler
+- Support RFENCE extension in RustSBI framework
 - Added a test kernel to test SBI function on RustSBI implementations
-- Support framework for RFENCE extension
 
 ### Modified
 - Function `rustsbi::ecall` now require 5 input parameters

+ 2 - 1
platform/k210/src/main.rs

@@ -155,7 +155,7 @@ fn main() -> ! {
             fn max_hart_id(&self) -> usize {
                 1
             }
-            fn send_ipi_many(&mut self, hart_mask: rustsbi::HartMask) {
+            fn send_ipi_many(&mut self, hart_mask: rustsbi::HartMask) -> rustsbi::SbiRet {
                 use k210_hal::clint::msip;
                 for i in 0..=1 {
                     if hart_mask.has_bit(i) {
@@ -163,6 +163,7 @@ fn main() -> ! {
                         msip::clear_ipi(i);
                     }
                 }
+                rustsbi::SbiRet::ok(0)
             }
         }
         use rustsbi::init_ipi;

+ 1 - 1
platform/qemu/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "rustsbi-qemu"
-version = "0.1.0"
+version = "0.2.0"
 authors = ["luojia65 <[email protected]>"]
 edition = "2018"
 publish = false

+ 3 - 1
platform/qemu/src/hal/clint.rs

@@ -1,4 +1,5 @@
 // 这部分其实是运行时提供的,不应该做到实现库里面
+use rustsbi::SbiRet;
 
 pub struct Clint {
     base: usize,
@@ -48,12 +49,13 @@ impl Ipi for Clint {
         *crate::MAX_HART_ID.lock()
     }
 
-    fn send_ipi_many(&mut self, hart_mask: HartMask) {
+    fn send_ipi_many(&mut self, hart_mask: HartMask) -> SbiRet {
         for i in 0..=self.max_hart_id() {
             if hart_mask.has_bit(i) {
                 self.send_soft(i);
             }
         }
+        SbiRet::ok(0)
     }
 }
 

+ 2 - 1
rustsbi/src/ecall.rs

@@ -109,7 +109,8 @@ const SBI_ERR_NOT_SUPPORTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-2)
 // const SBI_ERR_ALREADY_AVAILABLE: usize = usize::from_ne_bytes(isize::to_ne_bytes(-6));
 
 impl SbiRet {
-    pub(crate) fn ok(value: usize) -> SbiRet {
+    /// Return success SBI state with given value.
+    pub fn ok(value: usize) -> SbiRet {
         SbiRet {
             error: SBI_SUCCESS,
             value,

+ 7 - 3
rustsbi/src/ecall/ipi.rs

@@ -14,7 +14,11 @@ pub fn handle_ecall_ipi(function: usize, param0: usize, param1: usize) -> SbiRet
 
 #[inline]
 fn send_ipi(hart_mask: usize, hart_mask_base: usize) -> SbiRet {
-    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id()) };
-    send_ipi_many(hart_mask);
-    SbiRet::ok(0)
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    send_ipi_many(hart_mask)
 }

+ 7 - 3
rustsbi/src/ecall/legacy.rs

@@ -19,11 +19,15 @@ pub fn console_getchar() -> SbiRet {
 
 #[inline]
 pub fn send_ipi(hart_mask_addr: usize) -> SbiRet {
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
     // note(unsafe): if any load fault, should be handled by user or supervisor
     // base hart should be 0 on legacy
-    let hart_mask = unsafe { HartMask::from_addr(hart_mask_addr, 0, max_hart_id()) };
-    send_ipi_many(hart_mask);
-    SbiRet::ok(0) // the return value 0 is ignored in legacy
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask_addr, 0, max_hart_id) };
+    send_ipi_many(hart_mask)
 }
 
 #[inline]

+ 57 - 21
rustsbi/src/ecall/rfence.rs

@@ -1,4 +1,7 @@
 use super::SbiRet;
+use crate::rfence;
+use crate::hart_mask::HartMask;
+use crate::ipi::max_hart_id;
 
 const FUNCTION_RFENCE_REMOTE_FENCE_I: usize = 0x0;
 const FUNCTION_RFENCE_REMOTE_SFENCE_VMA: usize = 0x1;
@@ -22,51 +25,84 @@ pub fn handle_ecall_rfence(function: usize, param0: usize, param1: usize, param2
     }
 }
 
+// If None = max_hart_id(), that means IPI extension is not supported.
+// In RustSBI, RFENCE support requires an IPI support is implemented.
+// If platform does not provide IPI support, RustSBI will disable RFENCE
+// interface access from supervisor level.
+
 #[inline]
 fn remote_fence_i(hart_mask: usize, hart_mask_base: usize) -> SbiRet {
-    // todo
-    drop((hart_mask, hart_mask_base));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    rfence::remote_fence_i(hart_mask)
 }
 
 #[inline]
 fn remote_sfence_vma(hart_mask: usize, hart_mask_base: usize, start_addr: usize, size: usize) -> SbiRet {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    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 {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size, asid));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    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 {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size, vmid));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    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 {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    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 {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size, asid));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    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 {
-    // todo
-    drop((hart_mask, hart_mask_base, start_addr, size));
-    SbiRet::not_supported()
+    let max_hart_id = if let Some(id) = max_hart_id() {
+        id
+    } else {
+        return SbiRet::not_supported()
+    };
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask, hart_mask_base, max_hart_id) };
+    rfence::remote_hfence_vvma(hart_mask, start_addr, size)
 }

+ 10 - 7
rustsbi/src/ipi.rs

@@ -1,4 +1,5 @@
 use crate::hart_mask::HartMask;
+use crate::ecall::SbiRet;
 
 /// Inter-processor interrupt support
 pub trait Ipi: Send {
@@ -7,7 +8,7 @@ pub trait Ipi: Send {
     /// Send an inter-processor interrupt to all the harts defined in `hart_mask`.
     ///
     /// Interprocessor interrupts manifest at the receiving harts as the supervisor software interrupts.
-    fn send_ipi_many(&mut self, hart_mask: HartMask);
+    fn send_ipi_many(&mut self, hart_mask: HartMask) -> SbiRet;
 }
 
 use alloc::boxed::Box;
@@ -27,16 +28,18 @@ pub(crate) fn probe_ipi() -> bool {
     IPI.lock().as_ref().is_some()
 }
 
-pub(crate) fn send_ipi_many(hart_mask: HartMask) {
+pub(crate) fn send_ipi_many(hart_mask: HartMask) -> SbiRet {
     if let Some(ipi) = IPI.lock().as_mut() {
         ipi.send_ipi_many(hart_mask)
+    } else {
+        SbiRet::not_supported()
     }
 }
 
-pub(crate) fn max_hart_id() -> usize {
-    loop {
-        if let Some(ipi) = IPI.lock().as_ref() {
-            return ipi.max_hart_id();
-        }
+pub(crate) fn max_hart_id() -> Option<usize> { 
+    if let Some(ipi) = IPI.lock().as_ref() {
+        Some(ipi.max_hart_id())
+    } else {
+        None
     }
 }

+ 8 - 0
rustsbi/src/legacy_stdio.rs

@@ -119,6 +119,10 @@ pub fn _print(args: fmt::Arguments) {
 }
 
 /// Prints to the legacy debug console.
+///
+/// This is only supported when there exists legacy extension; 
+/// otherwise platform caller should use an early kernel input/output device
+/// declared in platform specific hardware.
 #[macro_export(local_inner_macros)]
 macro_rules! print {
     ($($arg:tt)*) => ({
@@ -127,6 +131,10 @@ macro_rules! print {
 }
 
 /// Prints to the legacy debug console, with a newline.
+///
+/// This is only supported when there exists legacy extension; 
+/// otherwise platform caller should use an early kernel input/output device
+/// declared in platform specific hardware.
 #[macro_export(local_inner_macros)]
 macro_rules! println {
     ($fmt: literal $(, $($arg: tt)+)?) => {

+ 1 - 0
rustsbi/src/lib.rs

@@ -58,3 +58,4 @@ pub use logo::LOGO;
 pub use privileged::enter_privileged;
 pub use reset::{init_reset, Reset};
 pub use timer::{init_timer, Timer};
+pub use rfence::{init_rfence as init_remote_fence, Rfence as Fence};

+ 188 - 3
rustsbi/src/rfence.rs

@@ -1,4 +1,189 @@
-pub fn probe_rfence() -> bool {
-    false
+use crate::hart_mask::HartMask;
+use crate::ecall::SbiRet;
+
+/// Remote fence support
+///
+/// In RustSBI, RFENCE support requires an IPI support is implemented.
+/// If your platform does not provide IPI support, RustSBI will disable RFENCE
+/// interface access from supervisor level.
+///
+/// The remote fence function acts as a full TLB flush if
+/// - `start_addr` and `size` are both 0
+/// - `size` is equal to `usize::max_value()`
+pub trait Rfence: Send {
+    /// Instructs remote harts to execute `FENCE.I` instruction.
+    ///
+    /// Returns `SBI_SUCCESS` when remote fence was sent to all the targeted harts successfully.
+    fn remote_fence_i(&mut self, hart_mask: HartMask) -> SbiRet;
+    /// Instructs the remote harts to execute one or more `SFENCE.VMA` instructions, 
+    /// covering the range of virtual addresses between start and size.
+    /// 
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_sfence_vma(&mut self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet;
+    /// Instruct the remote harts to execute one or more `SFENCE.VMA` instructions, 
+    /// covering the range of virtual addresses between start and size. This covers only the given `ASID`.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_sfence_vma_asid(&mut self, hart_mask: HartMask, start_addr: usize, size: usize, asid: usize) -> SbiRet;
+    /// Instruct the remote harts to execute one or more `HFENCE.GVMA` instructions, 
+    /// covering the range of guest physical addresses between start and size only for the given `VMID`. 
+    /// 
+    /// This function call is only valid for harts implementing hypervisor extension.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_NOT_SUPPORTED     | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_hfence_gvma_vmid(&mut self, hart_mask: HartMask, start_addr: usize, size: usize, vmid: usize) -> SbiRet {
+        drop((hart_mask, start_addr, size, vmid));
+        SbiRet::not_supported()
+    }
+    /// Instruct the remote harts to execute one or more `HFENCE.GVMA` instructions, 
+    /// covering the range of guest physical addresses between start and size for all the guests.
+    /// 
+    /// This function call is only valid for harts implementing hypervisor extension.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_NOT_SUPPORTED     | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_hfence_gvma(&mut self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        drop((hart_mask, start_addr, size));
+        SbiRet::not_supported()
+    }
+    /// Instruct the remote harts to execute one or more `HFENCE.VVMA` instructions, 
+    /// covering the range of guest virtual addresses between start and size for the given `ASID` and current `VMID` (in `hgatp` CSR) 
+    /// of calling hart.
+    ///  
+    /// This function call is only valid for harts implementing hypervisor extension.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_NOT_SUPPORTED     | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_hfence_vvma_asid(&mut self, hart_mask: HartMask, start_addr: usize, size: usize, asid: usize) -> SbiRet {
+        drop((hart_mask, start_addr, size, asid));
+        SbiRet::not_supported()
+    }
+    /// Instruct the remote harts to execute one or more `HFENCE.VVMA` instructions, 
+    /// covering the range of guest virtual addresses between start and size for current `VMID` (in `hgatp` CSR) 
+    /// of calling hart. 
+    ///
+    /// This function call is only valid for harts implementing hypervisor extension.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description 
+    /// |:--------------------------|:----------------------------------------------
+    /// | SBI_SUCCESS               | Remote fence was sent to all the targeted harts successfully.
+    /// | SBI_ERR_NOT_SUPPORTED     | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
+    /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
+    fn remote_hfence_vvma(&mut self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        drop((hart_mask, start_addr, size, size));
+        SbiRet::not_supported()
+    }
+}
+
+use alloc::boxed::Box;
+use spin::Mutex;
+
+lazy_static::lazy_static! {
+    static ref RFENCE: Mutex<Option<Box<dyn Rfence>>> = Mutex::new(None);
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_rfence<T: Rfence + Send + 'static>(rfence: T) {
+    *RFENCE.lock() = Some(Box::new(rfence));
+}
+
+#[inline]
+pub(crate) fn probe_rfence() -> bool {
+    RFENCE.lock().as_ref().is_some()
+}
+
+pub(crate) fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_fence_i(hart_mask)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_sfence_vma(hart_mask, start_addr, size)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_sfence_vma_asid(hart_mask: HartMask, start_addr: usize, size: usize, asid: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_sfence_vma_asid(hart_mask, start_addr, size, asid)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_hfence_gvma_vmid(hart_mask: HartMask, start_addr: usize, size: usize, vmid: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_hfence_gvma_vmid(hart_mask, start_addr, size, vmid)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_hfence_gvma(hart_mask, start_addr, size)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_hfence_vvma_asid(hart_mask: HartMask, start_addr: usize, size: usize, asid: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_hfence_vvma_asid(hart_mask, start_addr, size, asid)
+    } else {
+        SbiRet::not_supported()
+    }
+}
+
+pub(crate) fn remote_hfence_vvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+    if let Some(rfence) = RFENCE.lock().as_mut() {
+        rfence.remote_hfence_vvma(hart_mask, start_addr, size)
+    } else {
+        SbiRet::not_supported()
+    }
 }
-// todo