Browse Source

rt: add DBTR extension support to SBI implementation (#117)

* rt: add DBTR extension support to SBI implementation
This commit implements the SBI Debug Triggers Extension (EID #0x44425452 "DBTR"),
as defined in the RISC-V SBI Specification chapter 19

Part of the tasks listed in #106

Signed-off-by: Jiefeng Li <[email protected]>

* fix: Use text in doc comments in to pass CI

Signed-off-by: Jiefeng Li <[email protected]>

* fix(docs): Fixed incorrect doc format and adapted parameter description.

Signed-off-by: Jiefeng Li <[email protected]>

* rt: Pruned constant error code in return value and changed return type

Signed-off-by: Jiefeng Li <[email protected]>

---------

Signed-off-by: Jiefeng Li <[email protected]>
Jiefeng Li 1 week ago
parent
commit
74b18f4295
3 changed files with 256 additions and 0 deletions
  1. 1 0
      library/sbi-rt/CHANGELOG.md
  2. 252 0
      library/sbi-rt/src/dbtr.rs
  3. 3 0
      library/sbi-rt/src/lib.rs

+ 1 - 0
library/sbi-rt/CHANGELOG.md

@@ -14,6 +14,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - lib: re-export `sbi_spec::base::CounterMask` on crate root.
 - rt: add FWFT extension support to SBI implementation.
 - Add C language naming alias tags to all functions of the sbi-rt library
+- rt: add DBTR extension support to SBI implementation.
 
 ### Modified
 

+ 252 - 0
library/sbi-rt/src/dbtr.rs

@@ -0,0 +1,252 @@
+//! Debug Triggers Extension (EID #0x44425452 "DBTR")
+//!
+//! The RISC-V Sdtrig extension allows machine-mode software to directly
+//! configure debug triggers which in-turn allows native (or hosted) debugging in machine-mode
+//! without any external debugger. Unfortunately, the debug triggers are only accessible to
+//! machine-mode.
+//!
+//! The SBI debug trigger extension defines a SBI based abstraction to provide native debugging
+//! for supervisor-mode software such that it is:
+//! 1. Suitable for the rich operating systems and hypervisors running in supervisor-mode.
+//! 2. Allows Guest (VS-mode) and Hypervisor (HS-mode) to share debug triggers on a hart.
+//!
+//! Each hart on a RISC-V platform has a fixed number of debug triggers which is referred
+//! to as `trig_max` in this SBI extension. Each debug trigger is assigned a logical index
+//! called `trig_idx` by the SBI implementation where `-1 < trig_idx < trig_max`.
+
+use crate::binary::{sbi_call_1, sbi_call_2, sbi_call_3};
+use sbi_spec::binary::{SbiRet, SharedPtr};
+use sbi_spec::dbtr::*;
+
+/// Get the number of debug triggers on the calling hart which can support the trigger
+/// configuration specified by `trig_tdata1` parameter.
+///
+/// This function always returns `SbiRet::success()` in `SbiRet.error`. It will return `trig_max`
+/// in `SbiRet.value` when `trig_tdata1 == 0` otherwise it will return the number of matching
+/// debug triggers in `SbiRet.value`.
+#[doc(alias = "sbi_debug_num_triggers")]
+#[inline]
+pub fn debug_num_triggers(trig_tdata1: usize) -> usize {
+    sbi_call_1(EID_DBTR, NUM_TRIGGERS, trig_tdata1).value
+}
+
+/// Set and enable the shared memory for debug trigger configuration on the calling hart.
+///
+/// If `shmem` is not all-ones bitwise then `shmem` specifies the bits of the shared memory physical base address.
+/// The `shmem` MUST be `(XLEN / 8)` bytes aligned and the size of shared
+/// memory is assumed to be `trig_max * (XLEN / 2)` bytes.
+///
+/// If `shmem` is all-ones bitwise then shared memory for debug trigger configuration is disabled
+///
+/// The `flags` parameter is reserved for future use and MUST be zero.
+///
+/// # Return value
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Shared memory was set or cleared successfully.
+/// | `SbiRet::invalid_param()`     | The `flags` parameter is not zero or the `shmem.phys_addr_lo` parameter is not `(XLEN / 8)` bytes aligned.
+/// | `SbiRet::invalid_address()`   | The shared memory pointed to by the `shmem` parameter does not satisfy the requirements.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_debug_set_shmem")]
+#[inline]
+pub fn debug_set_shmem(shmem: SharedPtr<u8>, flags: usize) -> SbiRet {
+    sbi_call_3(
+        EID_DBTR,
+        SET_SHMEM,
+        shmem.phys_addr_lo(),
+        shmem.phys_addr_hi(),
+        flags,
+    )
+}
+
+/// Read the debug trigger state and configuration into shared memory for a range of
+/// debug triggers specified by the `trig_idx_base` and `trig_count` parameters on the calling hart.
+///
+/// For each debug trigger with index `trig_idx_base + i` where `-1 < i < trig_count`, the
+/// debug trigger state and configuration consisting of four XLEN-bit words are written in
+/// little-endian format at `offset = i * (XLEN / 2)` of the shared memory as follows:
+///
+/// ```text
+/// word[0] = `trig_state` written by the SBI implementation
+/// word[1] = `trig_tdata1` written by the SBI implementation
+/// word[2] = `trig_tdata2` written by the SBI implementation
+/// word[3] = `trig_tdata3` written by the SBI implementation
+/// ```
+/// # Return value
+///
+/// | Error code              | Description
+/// |:------------------------|:---------------------------------
+/// | `SbiRet::success()`     | State and configuration of triggers read successfully.
+/// | `SbiRet::no_shmem()`    | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`   | Either `trig_idx_base >= trig_max` or `trig_idx_base + trig_count >= trig_max`.
+#[doc(alias = "sbi_debug_read_triggers")]
+#[inline]
+pub fn debug_read_triggers(trig_idx_base: usize, trig_count: usize) -> SbiRet {
+    sbi_call_2(EID_DBTR, READ_TRIGGERS, trig_idx_base, trig_count)
+}
+
+/// Install debug triggers based on an array of trigger configurations in the shared memory
+/// of the calling hart. The `trig_idx` assigned to each installed trigger configuration is
+/// written back in the shared memory.
+///
+/// The `trig_count` parameter represents the number of trigger configuration entries in
+/// the shared memory at offset `0x0`.
+///
+/// The i'th trigger configuration at `offset = i * (XLEN / 2)` in the shared memory
+/// consists of four consecutive XLEN-bit words in little-endian format which are
+/// organized as follows:
+///
+/// ```text
+/// word[0] = `trig_idx` written back by the SBI implementation
+/// word[1] = `trig_tdata1` read by the SBI implementation
+/// word[2] = `trig_tdata2` read by the SBI implementation
+/// word[3] = `trig_tdata3` read by the SBI implementation
+/// ```
+///
+/// Upon success, `SbiRet.value` is set to zero. Upon failure, `SbiRet.value` is set to the
+/// array index of the failing trigger configuration.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers installed successfully.
+/// | `SbiRet::no_shmem()`        | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`       | `trig_count >= trig_max`.
+/// | `SbiRet::invalid_param()`   | One of the trigger configuration words `trig_tdata1`, `trig_tdata2`, or `trig_tdata3` has an invalid value.
+/// | `SbiRet::failed()`          | Failed to assign `trig_idx` or HW debug trigger for one of the trigger configurations.
+/// | `SbiRet::not_supported()`   | One of the trigger configuration can't be programmed due to unimplemented optional bits in `tdata1`, `tdata2`, or `tdata3` CSRs.
+#[doc(alias = "sbi_debug_install_triggers")]
+#[inline]
+pub fn debug_install_triggers(trig_count: usize) -> SbiRet {
+    sbi_call_1(EID_DBTR, INSTALL_TRIGGERS, trig_count)
+}
+
+/// Update already installed debug triggers based on a trigger configuration array in the
+/// shared memory of the calling hart.
+///
+/// The `trig_count` parameter represents the number of trigger configuration entries in
+/// the shared memory at offset `0x0`.
+///
+/// The i'th trigger configuration at `offset = i * (XLEN / 2)` in the shared memory
+/// consists of four consecutive XLEN-bit words in little-endian format as follows:
+///
+/// ```text
+/// word[0] = `trig_idx` read by the SBI implementation
+/// word[1] = `trig_tdata1` read by the SBI implementation
+/// word[2] = `trig_tdata2` read by the SBI implementation
+/// word[3] = `trig_tdata3` read by the SBI implementation
+/// ```
+/// The SBI implementation MUST consider trigger configurations in the increasing order of
+/// the array index and starting with array index `0`. To install a debug trigger for the
+/// trigger configuration at array index `i` in the shared memory, the SBI implementation
+/// MUST do the following:
+///
+/// - Map an unused HW debug trigger which matches the trigger configuration to an
+///   an unused `trig_idx`.
+/// - Save a copy of the `trig_tdata1.vs`, `trig_tdata1.vu`, `trig_tdata1.s`, and
+///   `trig_tdata.u` bits in `trig_state`.
+/// - Update the `tdata1`, `tdata2`, and `tdata3` CSRs of the HW debug trigger.
+/// - Write `trig_idx` at `offset = i * (XLEN / 2)` in the shared memory.
+///
+/// Additionally for each trigger configuration chain in the shared memory, the SBI
+/// implementation MUST assign contiguous `trig_idx` values and contiguous HW debug
+/// triggers when installing the trigger configuration chain.
+///
+/// The last trigger configuration in the shared memory MUST not have `trig_tdata1.chain == 1`
+/// for `trig_tdata1.type = 2 or 6` to prevent incomplete trigger configuration chain
+/// in the shared memory.
+///
+/// The `SbiRet.value` is set to zero upon success or if shared memory is disabled whereas
+/// `SbiRet.value` is set to the array index `i` of the failing trigger configuration upon
+/// other failures.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers updated successfully.
+/// | `SbiRet::no_shmem()`        | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`       | `trig_count >= trig_max`.
+/// | `SbiRet::invalid_param()`   | One of the trigger configuration in the shared memory has an invalid of `trig_idx` (i.e. `trig_idx >= trig_max`), `trig_tdata1`, `trig_tdata2`, or `trig_tdata3`.
+/// | `SbiRet::failed()`          | One of the trigger configurations has valid `trig_idx` but the corresponding debug trigger is not mapped to any HW debug trigger.
+/// | `SbiRet::not_supported()`   | One of the trigger configuration can't be programmed due to unimplemented optional bits in `tdata1`, `tdata2`, or `tdata3` CSRs.
+#[doc(alias = "sbi_debug_update_triggers")]
+#[inline]
+pub fn debug_update_triggers(trig_count: usize) -> SbiRet {
+    sbi_call_1(EID_DBTR, UPDATE_TRIGGERS, trig_count)
+}
+
+/// Uninstall a set of debug triggers specified by the `trig_idx_base` and `trig_idx_mask`
+/// parameters on the calling hart.
+///
+/// The `trig_idx_base` specifies the starting trigger index, while the `trig_idx_mask` is a
+/// bitmask indicating which triggers, relative to the base, are to be uninstalled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// For each debug trigger in the specified set of debug triggers, the SBI implementation MUST:
+/// 1. Clear the `tdata1`, `tdata2`, and `tdata3` CSRs of the mapped HW debug trigger.
+/// 2. Clear the `trig_state` of the debug trigger.
+/// 3. Unmap and free the HW debug trigger and corresponding `trig_idx` for re-use in
+///    the future trigger installations.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers uninstalled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_uninstall_triggers")]
+#[inline]
+pub fn debug_uninstall_triggers(trig_idx_base: usize, trig_idx_mask: usize) -> SbiRet {
+    sbi_call_2(EID_DBTR, UNINSTALL_TRIGGERS, trig_idx_base, trig_idx_mask)
+}
+
+/// Enable a set of debug triggers specified by the `trig_idx_base` and `trig_idx_mask`
+/// parameters on the calling hart.
+///
+/// The `trig_idx_base` specifies the starting trigger index, while the `trig_idx_mask` is a
+/// bitmask indicating which triggers, relative to the base, are to be enabled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// To enable a debug trigger in the specified set of debug triggers, the SBI implementation
+/// MUST restore the `vs`, `vu`, `s`, and `u` bits of the mapped HW debug trigger from their
+/// saved copy in `trig_state`.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers enabled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_enable_triggers")]
+#[inline]
+pub fn debug_enable_triggers(trig_idx_base: usize, trig_idx_mask: usize) -> SbiRet {
+    sbi_call_2(EID_DBTR, ENABLE_TRIGGERS, trig_idx_base, trig_idx_mask)
+}
+
+/// Disable a set of debug triggers specified by the `trig_idx_base` and `trig_idx_mask`
+/// parameters on the calling hart.
+///
+/// The `trig_idx_base` specifies the starting trigger index, while the `trig_idx_mask` is a
+/// bitmask indicating which triggers, relative to the base, are to be disabled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// To disable a debug trigger in the specified set of debug triggers, the SBI implementation
+/// MUST clear the `vs`, `vu`, `s`, and `u` bits of the mapped HW debug trigger.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers disabled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_disable_triggers")]
+#[inline]
+pub fn debug_disable_triggers(trig_idx_base: usize, trig_idx_mask: usize) -> SbiRet {
+    sbi_call_2(EID_DBTR, DISABLE_TRIGGERS, trig_idx_base, trig_idx_mask)
+}

+ 3 - 0
library/sbi-rt/src/lib.rs

@@ -42,6 +42,8 @@ mod nacl;
 mod sta;
 // §18
 mod fwft;
+// §19
+mod dbtr;
 
 pub use sbi_spec::{
     base::Version,
@@ -54,6 +56,7 @@ pub use sbi_spec::{
 pub use base::*;
 pub use cppc::*;
 pub use dbcn::*;
+pub use dbtr::*;
 pub use fwft::*;
 pub use hsm::*;
 pub use nacl::*;