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}