Browse Source

Merge pull request #58 from jakezhu9/feat/nacl-sta

Add support for nacl and sta extension
Luo Jia / Zhouqi Jiang 1 year ago
parent
commit
4b7a8144c2
5 changed files with 262 additions and 15 deletions
  1. 1 1
      Cargo.toml
  2. 14 14
      src/instance.rs
  3. 4 0
      src/lib.rs
  4. 176 0
      src/nacl.rs
  5. 67 0
      src/sta.rs

+ 1 - 1
Cargo.toml

@@ -17,7 +17,7 @@ edition = "2021"
 exclude = ["/.github"]
 
 [dependencies]
-sbi-spec = "0.0.6"
+sbi-spec = "0.0.7-alpha.1"
 riscv = { version = "0.10.1", optional = true }
 
 [features]

+ 14 - 14
src/instance.rs

@@ -287,17 +287,17 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console, SU: Susp,
                     let [param0, param1, param2, param3, param4] =
                         [param[0], param[1], param[2], param[3], param[4]];
                     match function {
-                        spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => {
+                        spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::COUNTER_CONFIG_MATCHING => {
                             pmu.counter_config_matching(param0, param1, param2, param3, param4 as _)
                         }
-                        spec::pmu::PMU_COUNTER_START => {
+                        spec::pmu::COUNTER_START => {
                             pmu.counter_start(param0, param1, param2, param3 as _)
                         }
-                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                        spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
+                        spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
                         _ => SbiRet::not_supported(),
                     }
                 }
@@ -309,21 +309,21 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console, SU: Susp,
                     let [param0, param1, param2, param3, param4, param5] =
                         [param[0], param[1], param[2], param[3], param[4], param[5]];
                     match function {
-                        spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
+                        spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
                             param0,
                             param1,
                             param2,
                             param3,
                             concat_u32(param5, param4),
                         ),
-                        spec::pmu::PMU_COUNTER_START => {
+                        spec::pmu::COUNTER_START => {
                             pmu.counter_start(param0, param1, param2, concat_u32(param4, param3))
                         }
-                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                        spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
+                        spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
                         _ => SbiRet::not_supported(),
                     }
                 }

+ 4 - 0
src/lib.rs

@@ -510,9 +510,11 @@ mod hart_mask;
 mod hsm;
 mod instance;
 mod ipi;
+mod nacl;
 mod pmu;
 mod reset;
 mod rfence;
+mod sta;
 mod susp;
 mod timer;
 
@@ -549,9 +551,11 @@ pub use hart_mask::HartMask;
 pub use hsm::Hsm;
 pub use instance::{Builder, RustSBI};
 pub use ipi::Ipi;
+pub use nacl::Nacl;
 pub use pmu::Pmu;
 pub use reset::Reset;
 pub use rfence::Rfence as Fence;
+pub use sta::Sta;
 pub use susp::Susp;
 pub use timer::Timer;
 

+ 176 - 0
src/nacl.rs

@@ -0,0 +1,176 @@
+use core::mem::size_of;
+
+use spec::binary::{SbiRet, SharedPtr};
+
+/// Nested Acceleration Extension
+///
+/// Nested virtualization is the ability of a hypervisor to run another hypervisor
+/// as a guest. RISC-V nested virtualization requires an L0 hypervisor (running
+/// in hypervisor-mode) to trap-and-emulate the RISC-V H-extension functionality
+/// (such as CSR accesses, HFENCE instructions, HLV/HSV instructions,
+/// etc.) for the L1 hypervisor (running in virtualized supervisor-mode).
+///
+/// The SBI nested acceleration extension defines a shared memory based interface
+/// between the SBI implementation (or L0 hypervisor) and the supervisor software
+/// (or L1 hypervisor) which allows both to collaboratively reduce traps taken
+/// by the L0 hypervisor for emulating RISC-V H-extension functionality. The
+/// nested acceleration shared memory allows the L1 hypervisor to batch multiple
+/// RISC-V H-extension CSR accesses and HFENCE requests which are then emulated
+/// by the L0 hypervisor upon an explicit synchronization SBI call.
+///
+/// *NOTE:* The M-mode firmware should not implement the SBI nested acceleration
+/// extension if the underlying platform has the RISC-V H-extension implemented
+/// in hardware.
+
+pub trait Nacl: Send + Sync {
+    /// Probe nested acceleration feature.
+    ///
+    /// Probe a nested acceleration feature. This is a mandatory function of the
+    /// SBI nested acceleration extension.
+    ///
+    /// # Parameters
+    ///
+    /// The `feature_id` parameter specifies the nested acceleration feature to probe.
+    ///
+    /// # Return Value
+    ///
+    /// This function always returns SBI_SUCCESS in `SbiRet.error`. It returns `0`
+    /// in `SbiRet.value` if the given `feature_id` is not available, or `1` in
+    /// `SbiRet.value` if it is available.
+    fn probe_feature(&self, feature_id: u32) -> SbiRet;
+    /// Set nested acceleration shared memory.
+    ///
+    /// Set and enable the shared memory for nested acceleration on the calling
+    /// hart.
+    ///
+    /// # Parameters
+    ///
+    /// If physical address of `shmem` are not all-ones bitwise, then the `shmem` pointer
+    /// specifies the shared memory physical base address. The physical address of `shmem`
+    /// MUST be 4096-byte aligned. The size of the shared memory must be 4096 + (XLEN * 128) bytes.
+    /// The SBI implementation MUST zero the shared memory before returning from the SBI
+    /// call.
+    ///
+    /// If physical address of `shmem` are all-ones bitwise, then the nested acceleration features
+    /// are disabled.
+    ///
+    /// The `flags` parameter is reserved for future use and must be zero.
+    ///
+    /// # Return Value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code                 | Description
+    /// |:----------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`         | The steal-time shared memory physical base address was set or cleared successfully.
+    /// | `SbiRet::invalid_param()`   | The `flags` parameter is not zero or the `shmem` is not 4096-byte aligned.
+    /// | `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.
+    // fixme: `shmem` should have length of a constant definition like `SharedPtr<[u8; SHMEM_LEN]>`,
+    // where `SHMEM_LEN` is defined in `sbi-spec` crate to be `4096 + (XLEN * 128)`.
+    fn set_shmem(
+        &self,
+        shmem: SharedPtr<[u8; size_of::<usize>() * 128 + 4096]>,
+        flags: usize,
+    ) -> SbiRet;
+    /// Synchronize shared memory CSRs.
+    ///
+    /// Synchronize CSRs in the nested acceleration shared memory. This is an
+    /// optional function which is only available if the SBI_NACL_FEAT_SYNC_CSR
+    /// feature is available.
+    ///
+    /// # Parameters
+    ///
+    /// The parameter `csr_num` specifies the set of RISC-V H-extension CSRs to be synchronized.
+    ///
+    /// If `csr_num` is all-ones bitwise then all RISC-V H-extension CSRs implemented by the SBI
+    /// implementation (or L0 hypervisor) are synchronized.
+    ///
+    /// If `(csr_num & 0x300) == 0x200` and `csr_num < 0x1000` then only a single RISC-V H-extension
+    /// CSR specified by the `csr_num` parameter is synchronized.
+    ///
+    /// # Return Value
+    ///
+    /// The possible error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description
+    /// |:--------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`       | CSRs synchronized successfully.
+    /// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_CSR feature is not available.
+    /// | `SbiRet::invalid_param()` | `csr_num` is not all-ones bitwise and either: `(csr_num & 0x300) != 0x200` or `csr_num >= 0x1000` or `csr_num` is not implemented by the SBI implementation
+    /// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
+    fn sync_csr(&self, csr_num: usize) -> SbiRet;
+    /// Synchronize shared memory HFENCEs.
+    ///
+    /// Synchronize HFENCEs in the nested acceleration shared memory. This is an
+    /// optional function which is only available if the SBI_NACL_FEAT_SYNC_HFENCE
+    /// feature is available.
+    ///
+    /// # Parameters
+    ///
+    /// The parameter `entry_index` specifies the set of nested HFENCE entries to be synchronized.
+    ///
+    /// If `entry_index` is all-ones bitwise then all nested HFENCE entries are
+    /// synchronized.
+    ///
+    /// If `entry_index < (3840 / XLEN)` then only a single nested HFENCE entry
+    /// specified by the `entry_index` parameter is synchronized.
+    ///
+    /// # Return Value
+    ///
+    /// The possible error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description
+    /// |:--------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`       | HFENCEs synchronized successfully.
+    /// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_HFENCE feature is not available.
+    /// | `SbiRet::invalid_param()` | `entry_index` is not all-ones bitwise and `entry_index >= (3840 / XLEN)`.
+    /// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
+    fn sync_hfence(&self, entry_index: usize) -> SbiRet;
+    /// Synchronize shared memory and emulate SRET.
+    ///
+    /// Synchronize CSRs and HFENCEs in the nested acceleration shared memory and
+    /// emulate the SRET instruction. This is an optional function which is only
+    /// available if the SBI_NACL_FEAT_SYNC_SRET feature is available.
+    ///
+    /// This function is used by supervisor software (or L1 hypervisor) to do
+    /// a synchronize SRET request and the SBI implementation (or L0 hypervisor)
+    /// MUST handle it.
+    ///
+    /// # Return Value
+    ///
+    /// This function does not return upon success and the possible error codes returned in
+    /// `SbiRet.error` upon failure are shown in the table below:
+    ///
+    /// | Return code               | Description
+    /// |:--------------------------|:----------------------------------------------
+    /// | `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;
+}
+
+impl<T: Nacl> Nacl for T {
+    #[inline]
+    fn probe_feature(&self, feature_id: u32) -> SbiRet {
+        T::probe_feature(self, feature_id)
+    }
+    #[inline]
+    fn set_shmem(
+        &self,
+        shmem: SharedPtr<[u8; size_of::<usize>() * 128 + 4096]>,
+        flags: usize,
+    ) -> SbiRet {
+        T::set_shmem(self, shmem, flags)
+    }
+    #[inline]
+    fn sync_csr(&self, csr_num: usize) -> SbiRet {
+        T::sync_csr(self, csr_num)
+    }
+    #[inline]
+    fn sync_hfence(&self, entry_index: usize) -> SbiRet {
+        T::sync_hfence(self, entry_index)
+    }
+    #[inline]
+    fn sync_sret(&self) -> SbiRet {
+        T::sync_sret(self)
+    }
+}

+ 67 - 0
src/sta.rs

@@ -0,0 +1,67 @@
+use sbi_spec::binary::{SbiRet, SharedPtr};
+
+/// Steal-time Accounting Extension
+///
+/// SBI implementations may encounter situations where virtual harts are ready to
+/// run, but must be withheld from running. These situations may be, for example,
+/// when multiple SBI domains share processors or when an SBI implementation is a
+/// hypervisor and guest contexts share processors with other guest contexts or
+/// host tasks. When virtual harts are at times withheld from running, observers
+/// within the contexts of the virtual harts may need a way to account for less
+/// progress than would otherwise be expected. The time a virtual hart was ready,
+/// but had to wait, is called "stolen time" and the tracking of it is referred to
+/// as steal-time accounting. The Steal-time Accounting (STA) extension defines the
+/// mechanism in which an SBI implementation provides steal-time and preemption
+/// information, for each virtual hart, to supervisor-mode software.
+
+pub trait Sta: Send + Sync {
+    /// Set Steal-time Shared Memory Address.
+    ///
+    /// Set the shared memory physical base address for steal-time accounting of the
+    /// calling virtual hart and enable the SBI implementation's steal-time information
+    /// reporting.
+    ///
+    /// If physical address of `shmem` are not all-ones bitwise, then the `shmem` pointer
+    /// specifies the shared memory physical base address. The physical address of `shmem`
+    /// MUST be 64-byte aligned. The size of the shared memory must be 64 bytes.
+    /// The SBI implementation MUST zero the shared memory before returning from the SBI
+    /// call.
+    ///
+    /// If physical address of `shmem` are all-ones bitwise, the SBI
+    /// implementation will stop reporting steal-time information for the virtual hart.
+    ///
+    /// The `flags` parameter is reserved for future use and MUST be zero.
+    ///
+    /// It is not expected for the shared memory to be written by the supervisor-mode
+    /// software while it is in use for steal-time accounting. However, the SBI
+    /// implementation MUST not misbehave if a write from supervisor-mode software
+    /// occurs, however, in that case, it MAY leave the shared memory filled with
+    /// inconsistent data.
+    ///
+    /// The SBI implementation MUST stop writing to the shared memory when the
+    /// supervisor-mode software is not runnable, such as upon system reset or system
+    /// suspend.
+    ///
+    /// *NOTE:* Not writing to the shared memory when the supervisor-mode software is
+    /// not runnable avoids unnecessary work and supports repeatable capture of a
+    /// system image while the supervisor-mode software is suspended.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code                 | Description
+    /// |:----------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`         | The steal-time shared memory physical base address was set or cleared successfully.
+    /// | `SbiRet::invalid_param()`   | The `flags` parameter is not zero or the `shmem` is not 64-byte aligned.
+    /// | `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;
+}
+
+impl<T: Sta> Sta for &T {
+    #[inline]
+    fn set_shmem(&self, shmem: SharedPtr<[u8; 64]>, flags: usize) -> SbiRet {
+        T::set_shmem(self, shmem, flags)
+    }
+}