Browse Source

lib: dynamic RustSBI implementation

This patch allows multiple drivers to present in one RustSBI implementation. It will try to use fields one-by-one, suitable for FDT based implementations used on dynamic firmware.

Signed-off-by: Zhouqi Jiang <[email protected]>
Zhouqi Jiang 10 months ago
parent
commit
f3585015cc
15 changed files with 626 additions and 54 deletions
  1. 324 51
      macros/src/lib.rs
  2. 6 0
      src/console.rs
  3. 6 0
      src/cppc.rs
  4. 6 0
      src/hsm.rs
  5. 6 0
      src/ipi.rs
  6. 9 3
      src/lib.rs
  7. 6 0
      src/nacl.rs
  8. 6 0
      src/pmu.rs
  9. 6 0
      src/reset.rs
  10. 76 0
      src/rfence.rs
  11. 6 0
      src/sta.rs
  12. 6 0
      src/susp.rs
  13. 6 0
      src/timer.rs
  14. 66 0
      src/traits.rs
  15. 91 0
      tests/dynamic-priority.rs

+ 324 - 51
macros/src/lib.rs

@@ -6,8 +6,14 @@ use proc_macro::TokenStream;
 use quote::{quote, ToTokens};
 use syn::{parse_macro_input, Data, DeriveInput, Generics, Ident, Member};
 
+#[derive(Clone)]
+enum ParseMode {
+    Static,
+    Dynamic,
+}
+
 #[derive(Clone, Default)]
-struct RustSBIImp {
+struct StaticImpl {
     fence: Option<Member>,
     hsm: Option<Member>,
     ipi: Option<Member>,
@@ -22,55 +28,99 @@ struct RustSBIImp {
     env_info: Option<Member>,
 }
 
+impl StaticImpl {
+    fn replace_sbi_extension_ident(
+        &mut self,
+        extension_name: &str,
+        member: Member,
+    ) -> (bool, Option<Member>) {
+        match extension_name {
+            "rfnc" | "fence" => (true, self.fence.replace(member)),
+            "hsm" => (true, self.hsm.replace(member)),
+            "spi" | "ipi" => (true, self.ipi.replace(member)),
+            "srst" | "reset" => (true, self.reset.replace(member)),
+            "time" | "timer" => (true, self.timer.replace(member)),
+            "pmu" => (true, self.pmu.replace(member)),
+            "dbcn" | "console" => (true, self.console.replace(member)),
+            "susp" => (true, self.susp.replace(member)),
+            "cppc" => (true, self.cppc.replace(member)),
+            "nacl" => (true, self.nacl.replace(member)),
+            "sta" => (true, self.sta.replace(member)),
+            "info" | "env_info" => (true, self.env_info.replace(member)),
+            _ => (false, None),
+        }
+    }
+}
+
+#[derive(Clone, Default)]
+struct DynamicImpl {
+    fence: Vec<Member>,
+    hsm: Vec<Member>,
+    ipi: Vec<Member>,
+    reset: Vec<Member>,
+    timer: Vec<Member>,
+    pmu: Vec<Member>,
+    console: Vec<Member>,
+    susp: Vec<Member>,
+    cppc: Vec<Member>,
+    nacl: Vec<Member>,
+    sta: Vec<Member>,
+    env_info: Option<Member>,
+}
+
+impl DynamicImpl {
+    fn push_sbi_extension_ident(&mut self, extension_name: &str, member: Member) -> bool {
+        match extension_name {
+            "rfnc" | "fence" => self.fence.push(member),
+            "hsm" => self.hsm.push(member),
+            "spi" | "ipi" => self.ipi.push(member),
+            "srst" | "reset" => self.reset.push(member),
+            "time" | "timer" => self.timer.push(member),
+            "pmu" => self.pmu.push(member),
+            "dbcn" | "console" => self.console.push(member),
+            "susp" => self.susp.push(member),
+            "cppc" => self.cppc.push(member),
+            "nacl" => self.nacl.push(member),
+            "sta" => self.sta.push(member),
+            "info" | "env_info" => return self.env_info.replace(member).is_none(),
+            _ => return false,
+        }
+        true
+    }
+}
+
 /// This macro should be used in `rustsbi` crate as `rustsbi::RustSBI`.
 #[proc_macro_derive(RustSBI, attributes(rustsbi))]
 pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
 
-    let Data::Struct(strukt) = input.data else {
+    let Data::Struct(strukt) = &input.data else {
         panic!("#[derive(RustSBI)] must be used on structs");
     };
 
-    let mut imp = RustSBIImp::default();
-
-    let mut replace_sbi_extension_ident =
-        |extension_name: &str, member: Member| match extension_name {
-            "rfnc" | "fence" => (true, imp.fence.replace(member)),
-            "hsm" => (true, imp.hsm.replace(member)),
-            "spi" | "ipi" => (true, imp.ipi.replace(member)),
-            "srst" | "reset" => (true, imp.reset.replace(member)),
-            "time" | "timer" => (true, imp.timer.replace(member)),
-            "pmu" => (true, imp.pmu.replace(member)),
-            "dbcn" | "console" => (true, imp.console.replace(member)),
-            "susp" => (true, imp.susp.replace(member)),
-            "cppc" => (true, imp.cppc.replace(member)),
-            "nacl" => (true, imp.nacl.replace(member)),
-            "sta" => (true, imp.sta.replace(member)),
-            "info" | "env_info" => (true, imp.env_info.replace(member)),
-            _ => (false, None),
-        };
     let mut ans = TokenStream::new();
-    let check_already_exists = |field: &syn::Field,
-                                extension_name: &str,
-                                origin: Option<Member>,
-                                ans: &mut TokenStream| {
-        if let Some(_origin) = origin {
-            // TODO: provide more detailed proc macro error hinting that previous
-            // definition of this extension resides in `origin` once RFC 1566
-            // (Procedural Macro Diagnostics) is stabilized.
-            // Link: https://github.com/rust-lang/rust/issues/54140
-            let error = syn::Error::new_spanned(
-                field,
-                format!(
-                    "more than one field defined SBI extension '{}'. \
-                At most one fields should define the same SBI extension; consider using \
-                #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.",
-                    extension_name
-                ),
-            );
-            ans.extend(TokenStream::from(error.to_compile_error()));
+    let mut parse_mode = ParseMode::Static;
+
+    for attr in &input.attrs {
+        if !attr.path().is_ident("rustsbi") {
+            continue;
         }
-    };
+        let parsed = attr.parse_nested_meta(|meta| {
+            if meta.path.is_ident("dynamic") {
+                parse_mode = ParseMode::Dynamic;
+                Ok(())
+            } else {
+                let path = meta.path.to_token_stream().to_string().replace(' ', "");
+                Err(meta.error(format_args!("unknown RustSBI struct attribute `{}`", path)))
+            }
+        });
+        if let Err(err) = parsed {
+            ans.extend(TokenStream::from(err.to_compile_error()));
+        }
+    }
+
+    let mut static_impl = StaticImpl::default();
+    let mut dynamic_impl = DynamicImpl::default();
 
     for (i, field) in strukt.fields.iter().enumerate() {
         let member = match &field.ident {
@@ -89,11 +139,22 @@ pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
                     current_meta_accepted = true;
                 } else if let Some(meta_path_ident) = meta.path.get_ident() {
                     let extension_name = &meta_path_ident.to_string();
-                    let (replaced, origin) =
-                        replace_sbi_extension_ident(extension_name, member.clone());
-                    if replaced {
-                        check_already_exists(field, extension_name, origin, &mut ans);
-                        current_meta_accepted = true;
+                    match parse_mode {
+                        ParseMode::Static => {
+                            let (replaced, origin) = static_impl
+                                .replace_sbi_extension_ident(extension_name, member.clone());
+                            if replaced {
+                                check_already_exists(field, extension_name, origin, &mut ans);
+                                current_meta_accepted = true;
+                            }
+                        }
+                        ParseMode::Dynamic => {
+                            let replaced = dynamic_impl
+                                .push_sbi_extension_ident(extension_name, member.clone());
+                            if replaced {
+                                current_meta_accepted = true;
+                            }
+                        }
                     }
                 }
                 if current_meta_accepted {
@@ -114,17 +175,59 @@ pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
             continue;
         }
         if let Some(field_ident) = &field.ident {
-            let (_replaced, origin) =
-                replace_sbi_extension_ident(field_ident.to_string().as_str(), member);
-            check_already_exists(field, &field_ident.to_string(), origin, &mut ans);
+            match parse_mode {
+                ParseMode::Static => {
+                    let (_replaced, origin) = static_impl
+                        .replace_sbi_extension_ident(field_ident.to_string().as_str(), member);
+                    check_already_exists(field, &field_ident.to_string(), origin, &mut ans);
+                }
+                ParseMode::Dynamic => {
+                    let _replaced = dynamic_impl
+                        .push_sbi_extension_ident(field_ident.to_string().as_str(), member);
+                }
+            }
         }
     }
-
-    ans.extend(impl_derive_rustsbi(&input.ident, imp, &input.generics));
+    match parse_mode {
+        ParseMode::Static => ans.extend(impl_derive_rustsbi_static(
+            &input.ident,
+            static_impl,
+            &input.generics,
+        )),
+        ParseMode::Dynamic => ans.extend(impl_derive_rustsbi_dynamic(
+            &input.ident,
+            dynamic_impl,
+            &input.generics,
+        )),
+    };
     ans
 }
 
-fn impl_derive_rustsbi(name: &Ident, imp: RustSBIImp, generics: &Generics) -> TokenStream {
+fn check_already_exists(
+    field: &syn::Field,
+    extension_name: &str,
+    origin: Option<Member>,
+    ans: &mut TokenStream,
+) {
+    if let Some(_origin) = origin {
+        // TODO: provide more detailed proc macro error hinting that previous
+        // definition of this extension resides in `origin` once RFC 1566
+        // (Procedural Macro Diagnostics) is stabilized.
+        // Link: https://github.com/rust-lang/rust/issues/54140
+        let error = syn::Error::new_spanned(
+            field,
+            format!(
+                "more than one field defined SBI extension '{}'. \
+                At most one fields should define the same SBI extension; consider using \
+                #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.",
+                extension_name
+            ),
+        );
+        ans.extend(TokenStream::from(error.to_compile_error()));
+    }
+}
+
+fn impl_derive_rustsbi_static(name: &Ident, imp: StaticImpl, generics: &Generics) -> TokenStream {
     let base_probe: usize = 1;
     let fence_probe: usize = if imp.fence.is_some() { 1 } else { 0 };
     let hsm_probe: usize = if imp.hsm.is_some() { 1 } else { 0 };
@@ -244,3 +347,173 @@ fn impl_derive_rustsbi(name: &Ident, imp: RustSBIImp, generics: &Generics) -> To
         };
     gen.into()
 }
+
+fn impl_derive_rustsbi_dynamic(name: &Ident, imp: DynamicImpl, generics: &Generics) -> TokenStream {
+    let mut fence_contents = quote! {};
+    let mut prober_fence = quote! {};
+    for fence in &imp.fence {
+        fence_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_fence_probe(&self.#fence) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_fence(&self.#fence, param, function)
+            }
+        });
+        prober_fence.extend(quote! {
+            let value = ::rustsbi::_rustsbi_fence_probe(&self.0.#fence);
+            if value != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return value
+            }
+        });
+    }
+    let mut timer_contents = quote! {};
+    for timer in &imp.timer {
+        timer_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_timer_probe(&self.#timer) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_timer(&self.#timer, param, function)
+            }
+        })
+    }
+    let mut ipi_contents = quote! {};
+    for ipi in &imp.ipi {
+        ipi_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_ipi_probe(&self.#ipi) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_ipi(&self.#ipi, param, function)
+            }
+        })
+    }
+    let mut hsm_contents = quote! {};
+    for hsm in &imp.hsm {
+        hsm_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_hsm_probe(&self.#hsm) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_hsm(&self.#hsm, param, function)
+            }
+        })
+    }
+    let mut reset_contents = quote! {};
+    for reset in &imp.reset {
+        reset_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_reset_probe(&self.#reset) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_reset(&self.#reset, param, function)
+            }
+        })
+    }
+    let mut pmu_contents = quote! {};
+    for pmu in &imp.pmu {
+        pmu_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_pmu_probe(&self.#pmu) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_pmu(&self.#pmu, param, function)
+            }
+        })
+    }
+    let mut console_contents = quote! {};
+    for console in &imp.console {
+        console_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_console_probe(&self.#console) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_console(&self.#console, param, function)
+            }
+        })
+    }
+    let mut susp_contents = quote! {};
+    for susp in &imp.susp {
+        susp_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_susp_probe(&self.#susp) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_susp(&self.#susp, param, function)
+            }
+        })
+    }
+    let mut cppc_contents = quote! {};
+    for cppc in &imp.cppc {
+        cppc_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_cppc_probe(&self.#cppc) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_cppc(&self.#cppc, param, function)
+            }
+        })
+    }
+    let mut nacl_contents = quote! {};
+    for nacl in &imp.nacl {
+        nacl_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_nacl_probe(&self.#nacl) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_nacl(&self.#nacl, param, function)
+            }
+        })
+    }
+    let mut sta_contents = quote! {};
+    for sta in &imp.sta {
+        sta_contents.extend(quote! {
+            if ::rustsbi::_rustsbi_sta_probe(&self.#sta) != ::rustsbi::spec::base::UNAVAILABLE_EXTENSION {
+                return ::rustsbi::_rustsbi_sta(&self.#sta, param, function)
+            }
+        })
+    }
+
+    let define_prober = quote! {
+        struct _Prober<'a>(&'a #name);
+        impl<'a> ::rustsbi::_ExtensionProbe for _Prober<'a> {
+            #[inline(always)]
+            fn probe_extension(&self, extension: usize) -> usize {
+                match extension {
+                    // ::rustsbi::spec::base::EID_BASE => self.base,
+                    // ::rustsbi::spec::time::EID_TIME => self.timer,
+                    // ::rustsbi::spec::spi::EID_SPI => self.ipi,
+                    ::rustsbi::spec::rfnc::EID_RFNC => { #prober_fence ::rustsbi::spec::base::UNAVAILABLE_EXTENSION },
+                    // ::rustsbi::spec::srst::EID_SRST => self.reset,
+                    // ::rustsbi::spec::hsm::EID_HSM => self.hsm,
+                    // ::rustsbi::spec::pmu::EID_PMU => self.pmu,
+                    // ::rustsbi::spec::dbcn::EID_DBCN => self.console,
+                    // ::rustsbi::spec::susp::EID_SUSP => self.susp,
+                    // ::rustsbi::spec::cppc::EID_CPPC => self.cppc,
+                    // ::rustsbi::spec::nacl::EID_NACL => self.nacl,
+                    // ::rustsbi::spec::sta::EID_STA => self.sta,
+                    _ => ::rustsbi::spec::base::UNAVAILABLE_EXTENSION,
+                }
+            }
+        }
+    };
+    let base_result = if let Some(env_info) = imp.env_info {
+        quote! {
+            ::rustsbi::_rustsbi_base_env_info(param, function, &self.#env_info, prober)
+        }
+    } else {
+        match () {
+            #[cfg(not(feature = "machine"))]
+            () => quote! {
+                compile_error!(
+                    "can't derive RustSBI: #[cfg(feature = \"machine\")] is needed to derive RustSBI with no extra `EnvInfo` provided; \
+            consider adding an `info` parameter to provide machine environment information implementing `rustsbi::EnvInfo`\
+            if RustSBI is not run on machine mode."
+                )
+            },
+            #[cfg(feature = "machine")]
+            () => quote! {
+                ::rustsbi::_rustsbi_base_bare(param, function, prober)
+            },
+        }
+    };
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+    let gen = quote! {
+        impl #impl_generics ::rustsbi::RustSBI for #name #ty_generics #where_clause {
+            #[inline]
+            fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> ::rustsbi::SbiRet {
+                match extension {
+                    ::rustsbi::spec::rfnc::EID_RFNC => { #fence_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::time::EID_TIME => { #timer_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::spi::EID_SPI => { #ipi_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::hsm::EID_HSM => { #hsm_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::srst::EID_SRST => { #reset_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::pmu::EID_PMU => { #pmu_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::dbcn::EID_DBCN => { #console_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::susp::EID_SUSP => { #susp_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::cppc::EID_CPPC => { #cppc_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::nacl::EID_NACL => { #nacl_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::sta::EID_STA => { #sta_contents ::rustsbi::SbiRet::not_supported() },
+                    ::rustsbi::spec::base::EID_BASE => {
+                        #define_prober
+                        let prober = _Prober(&self);
+                        #base_result
+                    }
+                    _ => ::rustsbi::SbiRet::not_supported(),
+                }
+            }
+        }
+    };
+    gen.into()
+}

+ 6 - 0
src/console.rs

@@ -84,6 +84,12 @@ pub trait Console {
     /// | `SbiRet::success()`       | Byte written successfully.
     /// | `SbiRet::failed()`        | Failed to write the byte due to I/O errors.
     fn write_byte(&self, byte: u8) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Console> Console for &T {

+ 6 - 0
src/cppc.rs

@@ -113,6 +113,12 @@ pub trait Cppc {
     /// | `SbiRet::denied()`        | `reg_id` is a read-only register.
     /// | `SbiRet::failed()`        | The write-request failed for unspecified or unknown other reasons.
     fn write(&self, reg_id: u32, val: u64) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Cppc> Cppc for &T {

+ 6 - 0
src/hsm.rs

@@ -201,6 +201,12 @@ pub trait Hsm {
         let _ = (suspend_type, resume_addr, opaque);
         SbiRet::not_supported()
     }
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Hsm> Hsm for &T {

+ 6 - 0
src/ipi.rs

@@ -10,6 +10,12 @@ pub trait Ipi {
     ///
     /// Should return `SbiRet::success()` if IPI was sent to all the targeted harts successfully.
     fn send_ipi(&self, hart_mask: HartMask) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Ipi> Ipi for &T {

+ 9 - 3
src/lib.rs

@@ -1063,7 +1063,13 @@ pub use traits::{EnvInfo, RustSBI};
 pub use traits::_rustsbi_base_bare;
 #[doc(hidden)]
 pub use traits::{
-    _StandardExtensionProbe, _rustsbi_base_env_info, _rustsbi_console, _rustsbi_cppc,
-    _rustsbi_fence, _rustsbi_hsm, _rustsbi_ipi, _rustsbi_nacl, _rustsbi_pmu, _rustsbi_reset,
-    _rustsbi_sta, _rustsbi_susp, _rustsbi_timer,
+    _ExtensionProbe, _StandardExtensionProbe, _rustsbi_base_env_info, _rustsbi_console,
+    _rustsbi_cppc, _rustsbi_fence, _rustsbi_hsm, _rustsbi_ipi, _rustsbi_nacl, _rustsbi_pmu,
+    _rustsbi_reset, _rustsbi_sta, _rustsbi_susp, _rustsbi_timer,
+};
+#[doc(hidden)]
+pub use traits::{
+    _rustsbi_console_probe, _rustsbi_cppc_probe, _rustsbi_fence_probe, _rustsbi_hsm_probe,
+    _rustsbi_ipi_probe, _rustsbi_nacl_probe, _rustsbi_pmu_probe, _rustsbi_reset_probe,
+    _rustsbi_sta_probe, _rustsbi_susp_probe, _rustsbi_timer_probe,
 };

+ 6 - 0
src/nacl.rs

@@ -141,6 +141,12 @@ pub trait Nacl {
     /// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_SRET feature is not available.
     /// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
     fn sync_sret(&self) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Nacl> Nacl for &T {

+ 6 - 0
src/pmu.rs

@@ -224,6 +224,12 @@ pub trait Pmu {
             }
         }
     }
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Pmu> Pmu for &T {

+ 6 - 0
src/reset.rs

@@ -41,6 +41,12 @@ pub trait Reset {
     /// | `SbiRet::not_supported()` | `reset_type` is valid but not implemented.
     /// | `SbiRet::failed()`        | Reset request failed for unknown reasons.
     fn system_reset(&self, reset_type: u32, reset_reason: u32) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Reset> Reset for &T {

+ 76 - 0
src/rfence.rs

@@ -136,6 +136,12 @@ pub trait Rfence {
         let _ = (hart_mask, start_addr, size);
         SbiRet::not_supported()
     }
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Rfence> Rfence for &T {
@@ -186,3 +192,73 @@ impl<T: Rfence> Rfence for &T {
         T::remote_hfence_vvma(self, hart_mask, start_addr, size)
     }
 }
+
+impl<T: Rfence> Rfence for Option<T> {
+    #[inline]
+    fn remote_fence_i(&self, hart_mask: HartMask) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_fence_i(inner, hart_mask))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_sfence_vma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_sfence_vma(inner, hart_mask, start_addr, size))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_sfence_vma_asid(
+        &self,
+        hart_mask: HartMask,
+        start_addr: usize,
+        size: usize,
+        asid: usize,
+    ) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_sfence_vma_asid(inner, hart_mask, start_addr, size, asid))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_hfence_gvma_vmid(
+        &self,
+        hart_mask: HartMask,
+        start_addr: usize,
+        size: usize,
+        vmid: usize,
+    ) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_hfence_gvma_vmid(inner, hart_mask, start_addr, size, vmid))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_hfence_gvma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_hfence_gvma(inner, hart_mask, start_addr, size))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_hfence_vvma_asid(
+        &self,
+        hart_mask: HartMask,
+        start_addr: usize,
+        size: usize,
+        asid: usize,
+    ) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_hfence_vvma_asid(inner, hart_mask, start_addr, size, asid))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn remote_hfence_vvma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        self.as_ref()
+            .map(|inner| T::remote_hfence_vvma(inner, hart_mask, start_addr, size))
+            .unwrap_or(SbiRet::not_supported())
+    }
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        match self {
+            Some(_) => sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1),
+            None => sbi_spec::base::UNAVAILABLE_EXTENSION,
+        }
+    }
+}

+ 6 - 0
src/sta.rs

@@ -57,6 +57,12 @@ pub trait Sta {
     /// | `SbiRet::invalid_address()` | The shared memory pointed to by the `shmem` parameter is not writable or does not satisfy other requirements of shared memory physical address range.
     /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     fn set_shmem(&self, shmem: SharedPtr<[u8; 64]>, flags: usize) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Sta> Sta for &T {

+ 6 - 0
src/susp.rs

@@ -84,6 +84,12 @@ pub trait Susp {
     /// | `SbiRet::failed()`
     /// | The suspend request failed for unspecified or unknown other reasons.
     fn system_suspend(&self, sleep_type: u32, resume_addr: usize, opaque: usize) -> SbiRet;
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Susp> Susp for &T {

+ 6 - 0
src/timer.rs

@@ -8,6 +8,12 @@ pub trait Timer {
     /// it can either request a timer interrupt infinitely far into the future (i.e., (uint64_t)-1),
     /// or it can instead mask the timer interrupt by clearing `sie.STIE` CSR bit.
     fn set_timer(&self, stime_value: u64);
+    /// Function internal to macros. Do not use.
+    #[doc(hidden)]
+    #[inline]
+    fn _rustsbi_probe(&self) -> usize {
+        sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+    }
 }
 
 impl<T: Timer> Timer for &T {

+ 66 - 0
src/traits.rs

@@ -392,3 +392,69 @@ pub fn _rustsbi_sta<T: crate::Sta>(sta: &T, param: [usize; 6], function: usize)
 const fn concat_u32(h: usize, l: usize) -> u64 {
     ((h as u64) << 32) | (l as u64)
 }
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_fence_probe<T: crate::Fence>(fence: &T) -> usize {
+    fence._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_timer_probe<T: crate::Timer>(timer: &T) -> usize {
+    timer._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_ipi_probe<T: crate::Ipi>(ipi: &T) -> usize {
+    ipi._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_hsm_probe<T: crate::Hsm>(hsm: &T) -> usize {
+    hsm._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_reset_probe<T: crate::Reset>(reset: &T) -> usize {
+    reset._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_pmu_probe<T: crate::Pmu>(pmu: &T) -> usize {
+    pmu._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_console_probe<T: crate::Console>(console: &T) -> usize {
+    console._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_susp_probe<T: crate::Susp>(susp: &T) -> usize {
+    susp._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_cppc_probe<T: crate::Cppc>(cppc: &T) -> usize {
+    cppc._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_nacl_probe<T: crate::Nacl>(nacl: &T) -> usize {
+    nacl._rustsbi_probe()
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_sta_probe<T: crate::Sta>(sta: &T) -> usize {
+    sta._rustsbi_probe()
+}

+ 91 - 0
tests/dynamic-priority.rs

@@ -0,0 +1,91 @@
+use rustsbi::{RustSBI, SbiRet};
+use sbi_spec::rfnc::EID_RFNC;
+
+#[derive(RustSBI)]
+#[rustsbi(dynamic)]
+struct MultipleFences {
+    #[rustsbi(fence)]
+    fence_one: Option<FenceOne>,
+    #[rustsbi(fence)]
+    fence_two: Option<FenceTwo>,
+    env_info: DummyEnvInfo,
+}
+
+struct FenceOne;
+
+impl rustsbi::Fence for FenceOne {
+    fn remote_fence_i(&self, _: rustsbi::HartMask) -> SbiRet {
+        SbiRet::success(1)
+    }
+
+    fn remote_sfence_vma(&self, _: rustsbi::HartMask, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(2)
+    }
+
+    fn remote_sfence_vma_asid(&self, _: rustsbi::HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(3)
+    }
+}
+
+struct FenceTwo;
+
+impl rustsbi::Fence for FenceTwo {
+    fn remote_fence_i(&self, _: rustsbi::HartMask) -> SbiRet {
+        SbiRet::success(4)
+    }
+
+    fn remote_sfence_vma(&self, _: rustsbi::HartMask, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(5)
+    }
+
+    fn remote_sfence_vma_asid(&self, _: rustsbi::HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(6)
+    }
+}
+
+#[test]
+fn priority() {
+    let sbi = MultipleFences {
+        fence_one: Some(FenceOne),
+        fence_two: None,
+        env_info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]), SbiRet::success(1));
+    let sbi = MultipleFences {
+        fence_one: None,
+        fence_two: Some(FenceTwo),
+        env_info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]), SbiRet::success(4));
+    let sbi = MultipleFences {
+        fence_one: Some(FenceOne),
+        fence_two: Some(FenceTwo),
+        env_info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]), SbiRet::success(1));
+    let sbi = MultipleFences {
+        fence_one: None,
+        fence_two: None,
+        env_info: DummyEnvInfo,
+    };
+    assert_eq!(
+        sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]),
+        SbiRet::not_supported()
+    );
+}
+
+struct DummyEnvInfo;
+
+impl rustsbi::EnvInfo for DummyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        36
+    }
+
+    fn marchid(&self) -> usize {
+        37
+    }
+
+    fn mimpid(&self) -> usize {
+        38
+    }
+}