sbi_spec/
binary.rs

1//! Chapter 3. Binary Encoding.
2
3use core::marker::PhantomData;
4
5/// SBI functions return type.
6///
7/// > SBI functions must return a pair of values in a0 and a1,
8/// > with a0 returning an error code.
9/// > This is analogous to returning the C structure `SbiRet`.
10///
11/// Note: if this structure is used in function return on conventional
12/// Rust code, it would not require pinning memory representation as
13/// extern C. The `repr(C)` is set in case that some users want to use
14/// this structure in FFI code.
15#[derive(Clone, Copy, PartialEq, Eq)]
16#[repr(C)]
17pub struct SbiRet {
18    /// Error number.
19    pub error: usize,
20    /// Result value.
21    pub value: usize,
22}
23
24/// SBI success state return value.
25pub const RET_SUCCESS: usize = 0;
26/// Error for SBI call failed for unknown reasons.
27pub const RET_ERR_FAILED: usize = -1isize as _;
28/// Error for target operation not supported.
29pub const RET_ERR_NOT_SUPPORTED: usize = -2isize as _;
30/// Error for invalid parameter.
31pub const RET_ERR_INVALID_PARAM: usize = -3isize as _;
32/// Error for denied.
33pub const RET_ERR_DENIED: usize = -4isize as _;
34/// Error for invalid address.
35pub const RET_ERR_INVALID_ADDRESS: usize = -5isize as _;
36/// Error for resource already available.
37pub const RET_ERR_ALREADY_AVAILABLE: usize = -6isize as _;
38/// Error for resource already started.
39pub const RET_ERR_ALREADY_STARTED: usize = -7isize as _;
40/// Error for resource already stopped.
41pub const RET_ERR_ALREADY_STOPPED: usize = -8isize as _;
42/// Error for shared memory not available.
43pub const RET_ERR_NO_SHMEM: usize = -9isize as _;
44
45impl core::fmt::Debug for SbiRet {
46    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47        match self.error {
48            RET_SUCCESS => self.value.fmt(f),
49            RET_ERR_FAILED => write!(f, "<SBI call failed>"),
50            RET_ERR_NOT_SUPPORTED => write!(f, "<SBI feature not supported>"),
51            RET_ERR_INVALID_PARAM => write!(f, "<SBI invalid parameter>"),
52            RET_ERR_DENIED => write!(f, "<SBI denied>"),
53            RET_ERR_INVALID_ADDRESS => write!(f, "<SBI invalid address>"),
54            RET_ERR_ALREADY_AVAILABLE => write!(f, "<SBI already available>"),
55            RET_ERR_ALREADY_STARTED => write!(f, "<SBI already started>"),
56            RET_ERR_ALREADY_STOPPED => write!(f, "<SBI already stopped>"),
57            RET_ERR_NO_SHMEM => write!(f, "<SBI shared memory not available>"),
58            unknown => write!(f, "[SBI Unknown error: {unknown:#x}]"),
59        }
60    }
61}
62
63/// RISC-V SBI error in enumeration.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum Error {
66    /// Error for SBI call failed for unknown reasons.
67    Failed,
68    /// Error for target operation not supported.
69    NotSupported,
70    /// Error for invalid parameter.
71    InvalidParam,
72    /// Error for denied.
73    Denied,
74    /// Error for invalid address.
75    InvalidAddress,
76    /// Error for resource already available.
77    AlreadyAvailable,
78    /// Error for resource already started.
79    AlreadyStarted,
80    /// Error for resource already stopped.
81    AlreadyStopped,
82    /// Error for shared memory not available.
83    NoShmem,
84    /// Custom error code.
85    Custom(isize),
86}
87
88impl SbiRet {
89    /// Returns success SBI state with given `value`.
90    #[inline]
91    pub const fn success(value: usize) -> Self {
92        Self {
93            error: RET_SUCCESS,
94            value,
95        }
96    }
97
98    /// The SBI call request failed for unknown reasons.
99    #[inline]
100    pub const fn failed() -> Self {
101        Self {
102            error: RET_ERR_FAILED,
103            value: 0,
104        }
105    }
106
107    /// SBI call failed due to not supported by target ISA,
108    /// operation type not supported,
109    /// or target operation type not implemented on purpose.
110    #[inline]
111    pub const fn not_supported() -> Self {
112        Self {
113            error: RET_ERR_NOT_SUPPORTED,
114            value: 0,
115        }
116    }
117
118    /// SBI call failed due to invalid hart mask parameter,
119    /// invalid target hart id,
120    /// invalid operation type,
121    /// or invalid resource index.
122    #[inline]
123    pub const fn invalid_param() -> Self {
124        Self {
125            error: RET_ERR_INVALID_PARAM,
126            value: 0,
127        }
128    }
129    /// SBI call denied for unsatisfied entry criteria, or insufficient access
130    /// permission to debug console or CPPC register.
131    #[inline]
132    pub const fn denied() -> Self {
133        Self {
134            error: RET_ERR_DENIED,
135            value: 0,
136        }
137    }
138
139    /// SBI call failed for invalid mask start address,
140    /// not a valid physical address parameter,
141    /// or the target address is prohibited by PMP to run in supervisor mode.
142    #[inline]
143    pub const fn invalid_address() -> Self {
144        Self {
145            error: RET_ERR_INVALID_ADDRESS,
146            value: 0,
147        }
148    }
149
150    /// SBI call failed for the target resource is already available,
151    /// e.g., the target hart is already started when caller still requests it to start.
152    #[inline]
153    pub const fn already_available() -> Self {
154        Self {
155            error: RET_ERR_ALREADY_AVAILABLE,
156            value: 0,
157        }
158    }
159
160    /// SBI call failed for the target resource is already started,
161    /// e.g., target performance counter is started.
162    #[inline]
163    pub const fn already_started() -> Self {
164        Self {
165            error: RET_ERR_ALREADY_STARTED,
166            value: 0,
167        }
168    }
169
170    /// SBI call failed for the target resource is already stopped,
171    /// e.g., target performance counter is stopped.
172    #[inline]
173    pub const fn already_stopped() -> Self {
174        Self {
175            error: RET_ERR_ALREADY_STOPPED,
176            value: 0,
177        }
178    }
179
180    /// SBI call failed for shared memory is not available,
181    /// e.g. nested acceleration shared memory is not available.
182    #[inline]
183    pub const fn no_shmem() -> Self {
184        Self {
185            error: RET_ERR_NO_SHMEM,
186            value: 0,
187        }
188    }
189}
190
191impl SbiRet {
192    /// Converts to a [`Result`] of value and error.
193    #[inline]
194    pub const fn into_result(self) -> Result<usize, Error> {
195        match self.error {
196            RET_SUCCESS => Ok(self.value),
197            RET_ERR_FAILED => Err(Error::Failed),
198            RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
199            RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
200            RET_ERR_DENIED => Err(Error::Denied),
201            RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
202            RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
203            RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
204            RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
205            RET_ERR_NO_SHMEM => Err(Error::NoShmem),
206            unknown => Err(Error::Custom(unknown as _)),
207        }
208    }
209
210    /// Returns `true` if current SBI return succeeded.
211    ///
212    /// # Examples
213    ///
214    /// Basic usage:
215    ///
216    /// ```
217    /// # use sbi_spec::binary::SbiRet;
218    /// let x = SbiRet::success(0);
219    /// assert_eq!(x.is_ok(), true);
220    ///
221    /// let x = SbiRet::failed();
222    /// assert_eq!(x.is_ok(), false);
223    /// ```
224    #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"]
225    #[inline]
226    pub const fn is_ok(&self) -> bool {
227        matches!(self.error, RET_SUCCESS)
228    }
229
230    /// Returns `true` if the SBI call succeeded and the value inside of it matches a predicate.
231    ///
232    /// # Examples
233    ///
234    /// Basic usage:
235    ///
236    /// ```
237    /// # use sbi_spec::binary::SbiRet;
238    /// let x = SbiRet::success(2);
239    /// assert_eq!(x.is_ok_and(|x| x > 1), true);
240    ///
241    /// let x = SbiRet::success(0);
242    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
243    ///
244    /// let x = SbiRet::no_shmem();
245    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
246    /// ```
247    #[must_use]
248    #[inline]
249    pub fn is_ok_and(self, f: impl FnOnce(usize) -> bool) -> bool {
250        self.into_result().is_ok_and(f)
251    }
252
253    /// Returns `true` if current SBI return is an error.
254    ///
255    /// # Examples
256    ///
257    /// Basic usage:
258    ///
259    /// ```
260    /// # use sbi_spec::binary::SbiRet;
261    /// let x = SbiRet::success(0);
262    /// assert_eq!(x.is_err(), false);
263    ///
264    /// let x = SbiRet::not_supported();
265    /// assert_eq!(x.is_err(), true);
266    /// ```
267    #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"]
268    #[inline]
269    pub const fn is_err(&self) -> bool {
270        !self.is_ok()
271    }
272
273    /// Returns `true` if the result is an error and the value inside of it matches a predicate.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// # use sbi_spec::binary::{SbiRet, Error};
279    /// let x = SbiRet::denied();
280    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), true);
281    ///
282    /// let x = SbiRet::invalid_address();
283    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
284    ///
285    /// let x = SbiRet::success(0);
286    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
287    /// ```
288    #[must_use]
289    #[inline]
290    pub fn is_err_and(self, f: impl FnOnce(Error) -> bool) -> bool {
291        self.into_result().is_err_and(f)
292    }
293
294    /// Converts from `SbiRet` to [`Option<usize>`].
295    ///
296    /// Converts `self` into an [`Option<usize>`], consuming `self`,
297    /// and discarding the error, if any.
298    ///
299    /// # Examples
300    ///
301    /// Basic usage:
302    ///
303    /// ```
304    /// # use sbi_spec::binary::SbiRet;
305    /// let x = SbiRet::success(2);
306    /// assert_eq!(x.ok(), Some(2));
307    ///
308    /// let x = SbiRet::invalid_param();
309    /// assert_eq!(x.ok(), None);
310    /// ```
311    // fixme: should be pub const fn once this function in Result is stablized in constant
312    #[inline]
313    pub fn ok(self) -> Option<usize> {
314        self.into_result().ok()
315    }
316
317    /// Converts from `SbiRet` to [`Option<Error>`].
318    ///
319    /// Converts `self` into an [`Option<Error>`], consuming `self`,
320    /// and discarding the success value, if any.
321    ///
322    /// # Examples
323    ///
324    /// Basic usage:
325    ///
326    /// ```
327    /// # use sbi_spec::binary::{SbiRet, Error};
328    /// let x = SbiRet::success(2);
329    /// assert_eq!(x.err(), None);
330    ///
331    /// let x = SbiRet::denied();
332    /// assert_eq!(x.err(), Some(Error::Denied));
333    /// ```
334    // fixme: should be pub const fn once this function in Result is stablized in constant
335    #[inline]
336    pub fn err(self) -> Option<Error> {
337        self.into_result().err()
338    }
339
340    /// Maps a `SbiRet` to `Result<U, Error>` by applying a function to a
341    /// contained success value, leaving an error value untouched.
342    ///
343    /// This function can be used to compose the results of two functions.
344    ///
345    /// # Examples
346    ///
347    /// Gets detail of a PMU counter and judge if it is a firmware counter.
348    ///
349    /// ```
350    /// # use sbi_spec::binary::SbiRet;
351    /// # use core::mem::size_of;
352    /// # mod sbi_rt {
353    /// #     use sbi_spec::binary::SbiRet;
354    /// #     const TYPE_MASK: usize = 1 << (core::mem::size_of::<usize>() - 1);
355    /// #     pub fn pmu_counter_get_info(_: usize) -> SbiRet { SbiRet::success(TYPE_MASK) }
356    /// # }
357    /// // We assume that counter index 42 is a firmware counter.
358    /// let counter_idx = 42;
359    /// // Masks PMU counter type by setting highest bit in `usize`.
360    /// const TYPE_MASK: usize = 1 << (size_of::<usize>() - 1);
361    /// // Highest bit of returned `counter_info` represents whether it's
362    /// // a firmware counter or a hardware counter.
363    /// let is_firmware_counter = sbi_rt::pmu_counter_get_info(counter_idx)
364    ///     .map(|counter_info| counter_info & TYPE_MASK != 0);
365    /// // If that bit is set, it is a firmware counter.
366    /// assert_eq!(is_firmware_counter, Ok(true));
367    /// ```
368    #[inline]
369    pub fn map<U, F: FnOnce(usize) -> U>(self, op: F) -> Result<U, Error> {
370        self.into_result().map(op)
371    }
372
373    /// Returns the provided default (if error),
374    /// or applies a function to the contained value (if success).
375    ///
376    /// Arguments passed to `map_or` are eagerly evaluated;
377    /// if you are passing the result of a function call,
378    /// it is recommended to use [`map_or_else`],
379    /// which is lazily evaluated.
380    ///
381    /// [`map_or_else`]: SbiRet::map_or_else
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// # use sbi_spec::binary::SbiRet;
387    /// let x = SbiRet::success(3);
388    /// assert_eq!(x.map_or(42, |v| v & 0b1), 1);
389    ///
390    /// let x = SbiRet::invalid_address();
391    /// assert_eq!(x.map_or(42, |v| v & 0b1), 42);
392    /// ```
393    #[inline]
394    pub fn map_or<U, F: FnOnce(usize) -> U>(self, default: U, f: F) -> U {
395        self.into_result().map_or(default, f)
396    }
397
398    /// Maps a `SbiRet` to `usize` value by applying fallback function `default` to
399    /// a contained error, or function `f` to a contained success value.
400    ///
401    /// This function can be used to unpack a successful result
402    /// while handling an error.
403    ///
404    /// # Examples
405    ///
406    /// Basic usage:
407    ///
408    /// ```
409    /// # use sbi_spec::binary::SbiRet;
410    /// let k = 21;
411    ///
412    /// let x = SbiRet::success(3);
413    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 1);
414    ///
415    /// let x = SbiRet::already_available();
416    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 42);
417    /// ```
418    #[inline]
419    pub fn map_or_else<U, D: FnOnce(Error) -> U, F: FnOnce(usize) -> U>(
420        self,
421        default: D,
422        f: F,
423    ) -> U {
424        self.into_result().map_or_else(default, f)
425    }
426
427    /// Maps a `SbiRet` to `Result<T, F>` by applying a function to a
428    /// contained error as [`Error`] struct, leaving success value untouched.
429    ///
430    /// This function can be used to pass through a successful result while handling
431    /// an error.
432    ///
433    /// # Examples
434    ///
435    /// Basic usage:
436    ///
437    /// ```
438    /// # use sbi_spec::binary::{SbiRet, Error};
439    /// fn stringify(x: Error) -> String {
440    ///     if x == Error::AlreadyStarted {
441    ///         "error: already started!".to_string()
442    ///     } else {
443    ///         "error: other error!".to_string()
444    ///     }
445    /// }
446    ///
447    /// let x = SbiRet::success(2);
448    /// assert_eq!(x.map_err(stringify), Ok(2));
449    ///
450    /// let x = SbiRet::already_started();
451    /// assert_eq!(x.map_err(stringify), Err("error: already started!".to_string()));
452    /// ```
453    #[inline]
454    pub fn map_err<F, O: FnOnce(Error) -> F>(self, op: O) -> Result<usize, F> {
455        self.into_result().map_err(op)
456    }
457
458    /// Calls a function with a reference to the contained value if current SBI call succeeded.
459    ///
460    /// Returns the original result.
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// # use sbi_spec::binary::SbiRet;
466    /// // Assume that SBI debug console have read 512 bytes into a buffer.
467    /// let ret = SbiRet::success(512);
468    /// // Inspect the SBI DBCN call result.
469    /// let idx = ret
470    ///     .inspect(|x| println!("bytes written: {x}"))
471    ///     .map(|x| x - 1)
472    ///     .expect("SBI DBCN call failed");
473    /// assert_eq!(idx, 511);
474    /// ```
475    #[inline]
476    pub fn inspect<F: FnOnce(&usize)>(self, f: F) -> Self {
477        if let Ok(ref t) = self.into_result() {
478            f(t);
479        }
480
481        self
482    }
483
484    /// Calls a function with a reference to the contained value if current SBI result is an error.
485    ///
486    /// Returns the original result.
487    ///
488    /// # Examples
489    ///
490    /// ```
491    /// # use sbi_spec::binary::SbiRet;
492    /// // Assume that SBI debug console write operation failed for invalid parameter.
493    /// let ret = SbiRet::invalid_param();
494    /// // Print the error if SBI DBCN call failed.
495    /// let ret = ret.inspect_err(|e| eprintln!("failed to read from SBI console: {e:?}"));
496    /// ```
497    #[inline]
498    pub fn inspect_err<F: FnOnce(&Error)>(self, f: F) -> Self {
499        if let Err(ref e) = self.into_result() {
500            f(e);
501        }
502
503        self
504    }
505
506    /// Returns the contained success value, consuming the `self` value.
507    ///
508    /// # Panics
509    ///
510    /// Panics if self is an SBI error with a panic message including the
511    /// passed message, and the content of the SBI state.
512    ///
513    /// # Examples
514    ///
515    /// Basic usage:
516    ///
517    /// ```should_panic
518    /// # use sbi_spec::binary::SbiRet;
519    /// let x = SbiRet::already_stopped();
520    /// x.expect("Testing expect"); // panics with `Testing expect`
521    /// ```
522    #[inline]
523    pub fn expect(self, msg: &str) -> usize {
524        self.into_result().expect(msg)
525    }
526
527    /// Returns the contained success value, consuming the `self` value.
528    ///
529    /// # Panics
530    ///
531    /// Panics if self is an SBI error, with a panic message provided by the
532    /// SBI error converted into [`Error`] struct.
533    ///
534    /// # Examples
535    ///
536    /// Basic usage:
537    ///
538    /// ```
539    /// # use sbi_spec::binary::SbiRet;
540    /// let x = SbiRet::success(2);
541    /// assert_eq!(x.unwrap(), 2);
542    /// ```
543    ///
544    /// ```should_panic
545    /// # use sbi_spec::binary::SbiRet;
546    /// let x = SbiRet::failed();
547    /// x.unwrap(); // panics
548    /// ```
549    #[inline]
550    pub fn unwrap(self) -> usize {
551        self.into_result().unwrap()
552    }
553
554    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
555    ///
556    /// # Panics
557    ///
558    /// Panics if the self is SBI success value, with a panic message
559    /// including the passed message, and the content of the success value.
560    ///
561    /// # Examples
562    ///
563    /// Basic usage:
564    ///
565    /// ```should_panic
566    /// # use sbi_spec::binary::SbiRet;
567    /// let x = SbiRet::success(10);
568    /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err`
569    /// ```
570    #[inline]
571    pub fn expect_err(self, msg: &str) -> Error {
572        self.into_result().expect_err(msg)
573    }
574
575    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
576    ///
577    /// # Panics
578    ///
579    /// Panics if the self is SBI success value, with a custom panic message provided
580    /// by the success value.
581    ///
582    /// # Examples
583    ///
584    /// ```should_panic
585    /// # use sbi_spec::binary::SbiRet;
586    /// let x = SbiRet::success(2);
587    /// x.unwrap_err(); // panics with `2`
588    /// ```
589    ///
590    /// ```
591    /// # use sbi_spec::binary::{SbiRet, Error};
592    /// let x = SbiRet::not_supported();
593    /// assert_eq!(x.unwrap_err(), Error::NotSupported);
594    /// ```
595    #[inline]
596    pub fn unwrap_err(self) -> Error {
597        self.into_result().unwrap_err()
598    }
599
600    /// Returns `res` if self is success value, otherwise otherwise returns the contained error
601    /// of `self` as [`Error`] struct.
602    ///
603    /// Arguments passed to `and` are eagerly evaluated; if you are passing the
604    /// result of a function call, it is recommended to use [`and_then`], which is
605    /// lazily evaluated.
606    ///
607    /// [`and_then`]: SbiRet::and_then
608    ///
609    /// # Examples
610    ///
611    /// Basic usage:
612    ///
613    /// ```
614    /// # use sbi_spec::binary::{SbiRet, Error};
615    /// let x = SbiRet::success(2);
616    /// let y = SbiRet::invalid_param().into_result();
617    /// assert_eq!(x.and(y), Err(Error::InvalidParam));
618    ///
619    /// let x = SbiRet::denied();
620    /// let y = SbiRet::success(3).into_result();
621    /// assert_eq!(x.and(y), Err(Error::Denied));
622    ///
623    /// let x = SbiRet::invalid_address();
624    /// let y = SbiRet::already_available().into_result();
625    /// assert_eq!(x.and(y), Err(Error::InvalidAddress));
626    ///
627    /// let x = SbiRet::success(4);
628    /// let y = SbiRet::success(5).into_result();
629    /// assert_eq!(x.and(y), Ok(5));
630    /// ```
631    // fixme: should be pub const fn once this function in Result is stablized in constant
632    // fixme: should parameter be `res: SbiRet`?
633    #[inline]
634    pub fn and<U>(self, res: Result<U, Error>) -> Result<U, Error> {
635        self.into_result().and(res)
636    }
637
638    /// Calls `op` if self is success value, otherwise returns the contained error
639    /// as [`Error`] struct.
640    ///
641    /// This function can be used for control flow based on `SbiRet` values.
642    ///
643    /// # Examples
644    ///
645    /// ```
646    /// # use sbi_spec::binary::{SbiRet, Error};
647    /// fn sq_then_to_string(x: usize) -> Result<String, Error> {
648    ///     x.checked_mul(x).map(|sq| sq.to_string()).ok_or(Error::Failed)
649    /// }
650    ///
651    /// assert_eq!(SbiRet::success(2).and_then(sq_then_to_string), Ok(4.to_string()));
652    /// assert_eq!(SbiRet::success(1_000_000_000_000).and_then(sq_then_to_string), Err(Error::Failed));
653    /// assert_eq!(SbiRet::invalid_param().and_then(sq_then_to_string), Err(Error::InvalidParam));
654    /// ```
655    #[inline]
656    pub fn and_then<U, F: FnOnce(usize) -> Result<U, Error>>(self, op: F) -> Result<U, Error> {
657        self.into_result().and_then(op)
658    }
659
660    /// Returns `res` if self is SBI error, otherwise returns the success value of `self`.
661    ///
662    /// Arguments passed to `or` are eagerly evaluated; if you are passing the
663    /// result of a function call, it is recommended to use [`or_else`], which is
664    /// lazily evaluated.
665    ///
666    /// [`or_else`]: Result::or_else
667    ///
668    /// # Examples
669    ///
670    /// Basic usage:
671    ///
672    /// ```
673    /// # use sbi_spec::binary::{SbiRet, Error};
674    /// let x = SbiRet::success(2);
675    /// let y = SbiRet::invalid_param().into_result();
676    /// assert_eq!(x.or(y), Ok(2));
677    ///
678    /// let x = SbiRet::denied();
679    /// let y = SbiRet::success(3).into_result();
680    /// assert_eq!(x.or(y), Ok(3));
681    ///
682    /// let x = SbiRet::invalid_address();
683    /// let y = SbiRet::already_available().into_result();
684    /// assert_eq!(x.or(y), Err(Error::AlreadyAvailable));
685    ///
686    /// let x = SbiRet::success(4);
687    /// let y = SbiRet::success(100).into_result();
688    /// assert_eq!(x.or(y), Ok(4));
689    /// ```
690    // fixme: should be pub const fn once this function in Result is stablized in constant
691    // fixme: should parameter be `res: SbiRet`?
692    #[inline]
693    pub fn or<F>(self, res: Result<usize, F>) -> Result<usize, F> {
694        self.into_result().or(res)
695    }
696
697    /// Calls `op` if self is SBI error, otherwise returns the success value of `self`.
698    ///
699    /// This function can be used for control flow based on result values.
700    ///
701    ///
702    /// # Examples
703    ///
704    /// Basic usage:
705    ///
706    /// ```
707    /// # use sbi_spec::binary::{SbiRet, Error};
708    /// fn is_failed(x: Error) -> Result<usize, bool> { Err(x == Error::Failed) }
709    ///
710    /// assert_eq!(SbiRet::success(2).or_else(is_failed), Ok(2));
711    /// assert_eq!(SbiRet::failed().or_else(is_failed), Err(true));
712    /// ```
713    #[inline]
714    pub fn or_else<F, O: FnOnce(Error) -> Result<usize, F>>(self, op: O) -> Result<usize, F> {
715        self.into_result().or_else(op)
716    }
717
718    /// Returns the contained success value or a provided default.
719    ///
720    /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
721    /// the result of a function call, it is recommended to use [`unwrap_or_else`],
722    /// which is lazily evaluated.
723    ///
724    /// [`unwrap_or_else`]: SbiRet::unwrap_or_else
725    ///
726    /// # Examples
727    ///
728    /// Basic usage:
729    ///
730    /// ```
731    /// # use sbi_spec::binary::SbiRet;
732    /// let default = 2;
733    /// let x = SbiRet::success(9);
734    /// assert_eq!(x.unwrap_or(default), 9);
735    ///
736    /// let x = SbiRet::invalid_param();
737    /// assert_eq!(x.unwrap_or(default), default);
738    /// ```
739    // fixme: should be pub const fn once this function in Result is stablized in constant
740    #[inline]
741    pub fn unwrap_or(self, default: usize) -> usize {
742        self.into_result().unwrap_or(default)
743    }
744
745    /// Returns the contained success value or computes it from a closure.
746    ///
747    /// # Examples
748    ///
749    /// Basic usage:
750    ///
751    /// ```
752    /// # use sbi_spec::binary::{SbiRet, Error};
753    /// fn invalid_use_zero(x: Error) -> usize { if x == Error::InvalidParam { 0 } else { 3 } }
754    ///
755    /// assert_eq!(SbiRet::success(2).unwrap_or_else(invalid_use_zero), 2);
756    /// assert_eq!(SbiRet::invalid_param().unwrap_or_else(invalid_use_zero), 0);
757    /// ```
758    #[inline]
759    pub fn unwrap_or_else<F: FnOnce(Error) -> usize>(self, op: F) -> usize {
760        self.into_result().unwrap_or_else(op)
761    }
762}
763
764/// Check if the implementation contains the provided `bit`.
765///
766/// ## Parameters
767///
768/// - `mask`: bitmask defining the range of bits.
769/// - `base`: the starting bit index. (default: `0`)
770/// - `ignore`: if `base` is equal to this value, ignore the `mask` parameter, and consider all `bit`s set.
771/// - `bit`: the bit index to check for membership in the `mask`.
772#[inline]
773pub(crate) const fn has_bit(mask: usize, base: usize, ignore: usize, bit: usize) -> bool {
774    if base == ignore {
775        // ignore the `mask`, consider all `bit`s as set.
776        true
777    } else if bit < base {
778        // invalid index, under minimum range.
779        false
780    } else if (bit - base) >= usize::BITS as usize {
781        // invalid index, over max range.
782        false
783    } else {
784        // index is in range, check if it is set in the mask.
785        mask & (1 << (bit - base)) != 0
786    }
787}
788
789/// Hart mask structure in SBI function calls.
790#[repr(C)]
791#[derive(Debug, Copy, Clone, Eq, PartialEq)]
792pub struct HartMask {
793    hart_mask: usize,
794    hart_mask_base: usize,
795}
796
797impl HartMask {
798    /// Special value to ignore the `mask`, and consider all `bit`s as set.
799    pub const IGNORE_MASK: usize = usize::MAX;
800
801    /// Construct a [HartMask] from mask value and base hart id.
802    #[inline]
803    pub const fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> Self {
804        Self {
805            hart_mask,
806            hart_mask_base,
807        }
808    }
809
810    /// Gets the special value for ignoring the `mask` parameter.
811    #[inline]
812    pub const fn ignore_mask(&self) -> usize {
813        Self::IGNORE_MASK
814    }
815
816    /// Returns `mask` and `base` parameters from the [HartMask].
817    #[inline]
818    pub const fn into_inner(self) -> (usize, usize) {
819        (self.hart_mask, self.hart_mask_base)
820    }
821
822    /// Returns whether the [HartMask] contains the provided `hart_id`.
823    #[inline]
824    pub const fn has_bit(self, hart_id: usize) -> bool {
825        has_bit(
826            self.hart_mask,
827            self.hart_mask_base,
828            Self::IGNORE_MASK,
829            hart_id,
830        )
831    }
832}
833
834/// Counter index mask structure in SBI function calls for the `PMU` extension ยง11.
835#[repr(C)]
836#[derive(Debug, Copy, Clone, Eq, PartialEq)]
837pub struct CounterMask {
838    counter_idx_mask: usize,
839    counter_idx_base: usize,
840}
841
842impl CounterMask {
843    /// Special value to ignore the `mask`, and consider all `bit`s as set.
844    pub const IGNORE_MASK: usize = usize::MAX;
845
846    /// Construct a [CounterMask] from mask value and base counter index.
847    #[inline]
848    pub const fn from_mask_base(counter_idx_mask: usize, counter_idx_base: usize) -> Self {
849        Self {
850            counter_idx_mask,
851            counter_idx_base,
852        }
853    }
854
855    /// Gets the special value for ignoring the `mask` parameter.
856    #[inline]
857    pub const fn ignore_mask(&self) -> usize {
858        Self::IGNORE_MASK
859    }
860
861    /// Returns `mask` and `base` parameters from the [CounterMask].
862    #[inline]
863    pub const fn into_inner(self) -> (usize, usize) {
864        (self.counter_idx_mask, self.counter_idx_base)
865    }
866
867    /// Returns whether the [CounterMask] contains the provided `counter`.
868    #[inline]
869    pub const fn has_bit(self, counter: usize) -> bool {
870        has_bit(
871            self.counter_idx_mask,
872            self.counter_idx_base,
873            Self::IGNORE_MASK,
874            counter,
875        )
876    }
877}
878
879/// Physical slice wrapper with type annotation.
880///
881/// This struct wraps slices in RISC-V physical memory by low and high part of the
882/// physical base address as well as its length. It is usually used by SBI extensions
883/// as parameter types to pass base address and length parameters on physical memory
884/// other than a virtual one.
885///
886/// Generic parameter `P` represents a hint of how this physical slice would be used.
887/// For example, `Physical<&[u8]>` represents an immutable reference to physical byte slice,
888/// while `Physical<&mut [u8]>` represents a mutable one.
889///
890/// An SBI implementation should load or store memory using both `phys_addr_lo` and
891/// `phys_addr_hi` combined as base address. A supervisor program (kernels etc.)
892/// should provide continuous physical memory, wrapping its reference using this structure
893/// before passing into SBI runtime.
894#[derive(Clone, Copy)]
895pub struct Physical<P> {
896    num_bytes: usize,
897    phys_addr_lo: usize,
898    phys_addr_hi: usize,
899    _marker: PhantomData<P>,
900}
901
902impl<P> Physical<P> {
903    /// Create a physical memory slice by length and physical address.
904    #[inline]
905    pub const fn new(num_bytes: usize, phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
906        Self {
907            num_bytes,
908            phys_addr_lo,
909            phys_addr_hi,
910            _marker: core::marker::PhantomData,
911        }
912    }
913
914    /// Returns length of the physical memory slice.
915    #[inline]
916    pub const fn num_bytes(&self) -> usize {
917        self.num_bytes
918    }
919
920    /// Returns low-part base address of physical memory slice.
921    #[inline]
922    pub const fn phys_addr_lo(&self) -> usize {
923        self.phys_addr_lo
924    }
925
926    /// Returns high-part base address of physical memory slice.
927    #[inline]
928    pub const fn phys_addr_hi(&self) -> usize {
929        self.phys_addr_hi
930    }
931}
932
933/// Shared memory physical address raw pointer with type annotation.
934///
935/// This is a structure wrapping a raw pointer to the value of the type `T` without
936/// a pointer metadata. `SharedPtr`'s are _thin_; they won't include metadata
937/// as RISC-V SBI does not provide an approach to pass them via SBI calls,
938/// thus the length of type `T` should be decided independently of raw
939/// pointer structure.
940///
941/// `SharedPtr` can be used as a parameter to pass the shared memory physical pointer
942///  with a given base address in RISC-V SBI calls. For example, a `SharedPtr<[u8; 64]>`
943/// would represent a fixed-size 64 byte array on a RISC-V SBI function argument
944/// type.
945///
946/// This structure cannot be dereferenced directly with physical addresses,
947/// because on RISC-V systems the physical address space could be larger than the
948/// virtual ones. Hence, this structure describes the physical memory range by
949/// two `usize` values: the upper `phys_addr_hi` and lower `phys_addr_lo`.
950///
951/// RISC-V SBI extensions may declare special pointer values for shared memory
952/// raw pointers. For example, SBI STA declares that steal-time information
953/// should stop from reporting when the SBI call is invoked using all-ones
954/// bitwise shared pointer, i.e. `phys_addr_hi` and `phys_addr_lo` both equals
955/// `usize::MAX`. `SharedPtr` can be constructed using such special values
956/// by providing them to the `SharedPtr::new` function.
957///
958/// # Requirements
959///
960/// If an SBI function needs to pass a shared memory physical address range to
961/// the SBI implementation (or higher privilege mode), then this physical memory
962/// address range MUST satisfy the following requirements:
963///
964/// * The SBI implementation MUST check that the supervisor-mode software is
965///   allowed to access the specified physical memory range with the access
966///   type requested (read and/or write).
967/// * The SBI implementation MUST access the specified physical memory range
968///   using the PMA attributes.
969/// * The data in the shared memory MUST follow little-endian byte ordering.
970///
971/// *NOTE:* If the supervisor-mode software accesses the same physical memory
972/// range using a memory type different from the PMA, then a loss of coherence
973/// or unexpected memory ordering may occur. The invoking software should
974/// follow the rules and sequences defined in the RISC-V Svpbmt specification
975/// to prevent the loss of coherence and memory ordering.
976///
977/// It is recommended that a memory physical address passed to an SBI function
978/// should use at least two `usize` parameters to support platforms
979/// which have memory physical addresses wider than `XLEN` bits.
980// FIXME: should constrain with `T: Thin` once ptr_metadata feature is stabled;
981// RISC-V SBI does not provide an approach to pass pointer metadata by SBI calls.
982pub struct SharedPtr<T> {
983    phys_addr_lo: usize,
984    phys_addr_hi: usize,
985    _marker: PhantomData<*mut T>,
986}
987
988// FIXME: we should consider strict provenance rules for this pointer-like structure
989// once feature strict_provenance is stabled.
990impl<T> SharedPtr<T> {
991    /// Create a shared physical memory pointer by physical address.
992    #[inline]
993    pub const fn new(phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
994        Self {
995            phys_addr_lo,
996            phys_addr_hi,
997            _marker: PhantomData,
998        }
999    }
1000
1001    /// Returns low-part physical address of the shared physical memory pointer.
1002    #[inline]
1003    pub const fn phys_addr_lo(self) -> usize {
1004        self.phys_addr_lo
1005    }
1006
1007    /// Returns high-part physical address of the shared physical memory pointer.
1008    #[inline]
1009    pub const fn phys_addr_hi(self) -> usize {
1010        self.phys_addr_hi
1011    }
1012}
1013
1014impl<T> Clone for SharedPtr<T> {
1015    #[inline(always)]
1016    fn clone(&self) -> Self {
1017        *self
1018    }
1019}
1020
1021impl<T> Copy for SharedPtr<T> {}
1022
1023#[cfg(test)]
1024mod tests {
1025    use super::*;
1026
1027    #[test]
1028    fn rustsbi_hart_mask() {
1029        let mask = HartMask::from_mask_base(0b1, 400);
1030        assert!(!mask.has_bit(0));
1031        assert!(mask.has_bit(400));
1032        assert!(!mask.has_bit(401));
1033        let mask = HartMask::from_mask_base(0b110, 500);
1034        assert!(!mask.has_bit(0));
1035        assert!(!mask.has_bit(500));
1036        assert!(mask.has_bit(501));
1037        assert!(mask.has_bit(502));
1038        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
1039        let max_bit = 1 << (usize::BITS - 1);
1040        let mask = HartMask::from_mask_base(max_bit, 600);
1041        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
1042        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
1043        let mask = HartMask::from_mask_base(0b11, usize::MAX - 1);
1044        assert!(!mask.has_bit(usize::MAX - 2));
1045        assert!(mask.has_bit(usize::MAX - 1));
1046        assert!(mask.has_bit(usize::MAX));
1047        assert!(!mask.has_bit(0));
1048        // hart_mask_base == usize::MAX is special, it means hart_mask should be ignored
1049        // and this hart mask contains all harts available
1050        let mask = HartMask::from_mask_base(0, usize::MAX);
1051        for i in 0..5 {
1052            assert!(mask.has_bit(i));
1053        }
1054        assert!(mask.has_bit(usize::MAX));
1055    }
1056
1057    #[test]
1058    fn rustsbi_counter_index_mask() {
1059        let mask = CounterMask::from_mask_base(0b1, 400);
1060        assert!(!mask.has_bit(0));
1061        assert!(mask.has_bit(400));
1062        assert!(!mask.has_bit(401));
1063        let mask = CounterMask::from_mask_base(0b110, 500);
1064        assert!(!mask.has_bit(0));
1065        assert!(!mask.has_bit(500));
1066        assert!(mask.has_bit(501));
1067        assert!(mask.has_bit(502));
1068        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
1069        let max_bit = 1 << (usize::BITS - 1);
1070        let mask = CounterMask::from_mask_base(max_bit, 600);
1071        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
1072        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
1073        let mask = CounterMask::from_mask_base(0b11, usize::MAX - 1);
1074        assert!(!mask.has_bit(usize::MAX - 2));
1075        assert!(mask.has_bit(usize::MAX - 1));
1076        assert!(mask.has_bit(usize::MAX));
1077        assert!(!mask.has_bit(0));
1078        let mask = CounterMask::from_mask_base(0, usize::MAX);
1079        let null_mask = CounterMask::from_mask_base(0, 0);
1080        (0..=usize::BITS as usize).for_each(|i| {
1081            assert!(mask.has_bit(i));
1082            assert!(!null_mask.has_bit(i));
1083        });
1084        assert!(mask.has_bit(usize::MAX));
1085    }
1086}