Browse Source

Use fat pointer cell to support asynchronous hart state monitor module

luojia65 3 years ago
parent
commit
b17455cf16
4 changed files with 112 additions and 14 deletions
  1. 1 0
      CHANGELOG.md
  2. 14 14
      src/hsm.rs
  3. 3 0
      src/lib.rs
  4. 94 0
      src/util.rs

+ 1 - 0
CHANGELOG.md

@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Support device tree binary in K210 platform
 - Support SBI v0.3 hart suspend function
 - Support PMU extension trait and init function
+- Use fat pointer cell to support asynchronous hart state monitor module
 
 ### Modified
 - Reform RustSBI project into a library

+ 14 - 14
src/hsm.rs

@@ -75,7 +75,7 @@ pub trait Hsm: Send {
     /// | `sstatus.SIE` | 0
     /// | a0            | hartid
     /// | a1            | `opaque` parameter
-    fn hart_start(&mut self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;
+    fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;
     /// Request the SBI implementation to stop executing the calling hart in supervisor-mode 
     /// and return it’s ownership to the SBI implementation. 
     ///
@@ -89,7 +89,7 @@ pub trait Hsm: Send {
     /// | Error code  | Description 
     /// |:------------|:------------
     /// | SBI_ERR_FAILED | Failed to stop execution of the current hart 
-    fn hart_stop(&mut self, hartid: usize) -> SbiRet;
+    fn hart_stop(&self, hartid: usize) -> SbiRet;
     /// Get the current status (or HSM state) of the given hart.
     ///
     /// The harts may transition HSM states at any time due to any concurrent `sbi_hart_start`
@@ -187,46 +187,46 @@ pub trait Hsm: Send {
 }
 
 use alloc::boxed::Box;
-use spin::Mutex;
+use crate::util::OnceFatBox;
 
-lazy_static::lazy_static! {
-    static ref HSM: Mutex<Option<Box<dyn Hsm>>> =
-        Mutex::new(None);
-}
+static HSM: OnceFatBox<dyn Hsm + Sync + 'static> = OnceFatBox::new();
 
 #[doc(hidden)] // use through a macro or a call from implementation
-pub fn init_hsm<T: Hsm + Send + 'static>(hsm: T) {
-    *HSM.lock() = Some(Box::new(hsm));
+pub fn init_hsm<T: Hsm + Sync + 'static>(hsm: T) {
+    let result = HSM.set(Box::new(hsm));
+    if result.is_err() {
+        panic!("load sbi module when already loaded")
+    }
 }
 
 #[inline]
 pub(crate) fn probe_hsm() -> bool {
-    HSM.lock().as_ref().is_some()
+    HSM.get().is_some()
 }
 
 pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
-    if let Some(obj) = &mut *HSM.lock() {
+    if let Some(obj) = HSM.get() {
         return obj.hart_start(hartid, start_addr, opaque);
     }
     SbiRet::not_supported()
 }
 
 pub(crate) fn hart_stop(hartid: usize) -> SbiRet {
-    if let Some(obj) = &mut *HSM.lock() {
+    if let Some(obj) = HSM.get() {
         return obj.hart_stop(hartid);
     }
     SbiRet::not_supported()
 }
 
 pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
-    if let Some(obj) = &mut *HSM.lock() {
+    if let Some(obj) = HSM.get() {
         return obj.hart_get_status(hartid);
     }
     SbiRet::not_supported()
 }
 
 pub(crate) fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
-    if let Some(obj) = &mut *HSM.lock() {
+    if let Some(obj) = HSM.get() {
         let suspend_type = suspend_type as u32;
         return obj.hart_suspend(suspend_type, resume_addr, opaque);
     }

+ 3 - 0
src/lib.rs

@@ -138,6 +138,7 @@
 
 #![no_std]
 #![feature(asm)]
+#![feature(ptr_metadata)]
 
 extern crate alloc;
 
@@ -157,6 +158,8 @@ mod timer;
 mod rfence;
 mod pmu;
 
+mod util;
+
 const SBI_SPEC_MAJOR: usize = 0;
 const SBI_SPEC_MINOR: usize = 2;
 

+ 94 - 0
src/util.rs

@@ -0,0 +1,94 @@
+//! useful structures
+
+// Ref: once_cell
+
+use core::{
+    fmt::{self, Debug}, 
+    marker::PhantomData, 
+    mem::MaybeUninit, 
+    ptr::{self, Pointee}, 
+    sync::atomic::{AtomicPtr, Ordering}
+};
+use alloc::boxed::Box;
+
+/// A thread-safe fat pointer cell which can be written to only once.
+pub struct OnceFatBox<T: ?Sized> {
+    thin_ptr: AtomicPtr<()>,
+    meta: MaybeUninit<<T as Pointee>::Metadata>,
+    _marker: PhantomData<Option<Box<T>>>,
+}
+
+impl<T: ?Sized> Default for OnceFatBox<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<T: ?Sized> Drop for OnceFatBox<T> {
+    fn drop(&mut self) {
+        let data_address = *self.thin_ptr.get_mut();
+        if !data_address.is_null() {
+            let metadata = unsafe { self.meta.assume_init() };
+            let fat_ptr: *mut T = ptr::from_raw_parts_mut(data_address, metadata);
+            drop(unsafe { Box::from_raw(fat_ptr) })
+        }
+    }
+}
+
+impl<T: ?Sized + Debug> Debug for OnceFatBox<T> 
+where 
+    <T as Pointee>::Metadata: Debug 
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("OnceFatBox")
+            .field("data_address", &self.thin_ptr)
+            .field("meta", &self.meta)
+            .finish()
+    }
+}
+
+impl<T: ?Sized> OnceFatBox<T> {
+    /// Creates a new empty cell.
+    pub const fn new() -> OnceFatBox<T> {
+        OnceFatBox { 
+            thin_ptr: AtomicPtr::new(ptr::null_mut()), 
+            meta: MaybeUninit::uninit(), // value meaning ignored when thin_ptr is null
+            _marker: PhantomData 
+        }
+    }
+
+    /// Gets a reference to the underlying value.
+    pub fn get(&self) -> Option<&T> {
+        let data_address = self.thin_ptr.load(Ordering::Acquire);
+        if data_address.is_null() {
+            return None;
+        }
+        let metadata = unsafe { self.meta.assume_init() };
+        let fat_ptr: *const T = ptr::from_raw_parts(data_address, metadata);
+        Some(unsafe { &*fat_ptr })
+    }
+
+    /// Sets the contents of this cell to `value`.
+    ///
+    /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was full.
+    pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> {
+        let fat_ptr = Box::into_raw(value);
+        let data_address = fat_ptr as *mut ();
+        let exchange = self.thin_ptr.compare_exchange(
+            ptr::null_mut(),
+            data_address,
+            Ordering::AcqRel,
+            Ordering::Acquire,
+        );
+        if let Err(_) = exchange {
+            let value = unsafe { Box::from_raw(fat_ptr) };
+            return Err(value);
+        }
+        unsafe {
+            *(self.meta.as_ptr() as *mut _) = ptr::metadata(fat_ptr);
+        }
+        Ok(())
+    }
+}
+
+unsafe impl<T: Sync + Send + ?Sized> Sync for OnceFatBox<T> {}