sbi-spec 0.0.8

Definitions and constants in the RISC-V Supervisor Binary Interface (RISC-V SBI)
Documentation
//! Chapter 4. Base Extension (EID #0x10).

/// Extension ID for RISC-V SBI Base extension.
pub const EID_BASE: usize = 0x10;
pub use fid::*;

/// Default probe value for the target SBI extension is unavailable.
pub const UNAVAILABLE_EXTENSION: usize = 0;

/// SBI specification version.
///
/// In RISC-V SBI specification, the bit 31 must be 0 and is reserved for future expansion.
///
/// Not to be confused with 'implementation version'.
///
/// Declared in §4.1.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, Hash)]
#[repr(transparent)]
pub struct Version {
    raw: usize,
}

impl Version {
    /// Converts raw extension value into Version structure.
    #[inline]
    pub const fn from_raw(raw: usize) -> Self {
        Self { raw }
    }

    /// Reads the major version of RISC-V SBI specification.
    #[inline]
    pub const fn major(self) -> usize {
        (self.raw >> 24) & ((1 << 7) - 1)
    }

    /// Reads the minor version of RISC-V SBI specification.
    #[inline]
    pub const fn minor(self) -> usize {
        self.raw & ((1 << 24) - 1)
    }
}

impl core::fmt::Display for Version {
    #[inline]
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}.{}", self.major(), self.minor())
    }
}

impl core::cmp::PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.major()
            .partial_cmp(&other.major())
            .map(|ordering| ordering.then_with(|| self.minor().cmp(&other.minor())))
    }
}

/// Declared in §4.8
mod fid {
    /// Function ID to get the current SBI specification version.
    ///
    /// Declared in §4.1.
    pub const GET_SBI_SPEC_VERSION: usize = 0x0;
    /// Function ID to get the current SBI implementation ID.
    ///
    /// Declared in §4.2.
    pub const GET_SBI_IMPL_ID: usize = 0x1;
    /// Function ID to get the current SBI implementation version.
    ///
    /// Declared in §4.3.
    pub const GET_SBI_IMPL_VERSION: usize = 0x2;
    /// Function ID to probe information about one SBI extension from the current environment.
    ///
    /// Declared in §4.4.
    pub const PROBE_EXTENSION: usize = 0x3;
    /// Function ID to get the value of `mvendorid` register in the current environment.
    ///
    /// Declared in §4.5.
    pub const GET_MVENDORID: usize = 0x4;
    /// Function ID to get the value of `marchid` register in the current environment.
    ///
    /// Declared in §4.6.
    pub const GET_MARCHID: usize = 0x5;
    /// Function ID to get the value of `mimpid` register in the current environment.
    ///
    /// Declared in §4.7.
    pub const GET_MIMPID: usize = 0x6;
}

/// SBI Implementation IDs.
///
/// Declared in §4.9.
pub mod impl_id {
    /// Berkley Bootloader.
    pub const BBL: usize = 0;
    /// OpenSBI.
    pub const OPEN_SBI: usize = 1;
    /// Xvisor.
    pub const XVISOR: usize = 2;
    /// KVM.
    pub const KVM: usize = 3;
    /// RustSBI.
    pub const RUST_SBI: usize = 4;
    /// Diosix.
    pub const DIOSIX: usize = 5;
    /// Coffer.
    pub const COFFER: usize = 6;
    /// Xen Project.
    pub const XEN: usize = 7;
    /// PolarFire Hart Software Services.
    pub const POLARFIRE_HSS: usize = 8;
    /// Coreboot.
    pub const COREBOOT: usize = 9;
    /// Oreboot.
    pub const OREBOOT: usize = 10;
}

#[cfg(test)]
mod tests {
    use super::Version;

    #[test]
    fn version_parse() {
        let v1_0 = Version::from_raw(0x100_0000);
        assert_eq!(v1_0.major(), 1);
        assert_eq!(v1_0.minor(), 0);

        let v2_0 = Version::from_raw(0x200_0000);
        assert_eq!(v2_0.major(), 2);
        assert_eq!(v2_0.minor(), 0);

        let v2_1 = Version::from_raw(0x200_0001);
        assert_eq!(v2_1.major(), 2);
        assert_eq!(v2_1.minor(), 1);

        let v2_max = Version::from_raw(0x2ff_ffff);
        assert_eq!(v2_max.major(), 2);
        assert_eq!(v2_max.minor(), 16777215);

        let vmax_3 = Version::from_raw(0x7f00_0003);
        assert_eq!(vmax_3.major(), 127);
        assert_eq!(vmax_3.minor(), 3);

        let vmax_max = Version::from_raw(0x7fff_ffff);
        assert_eq!(vmax_max.major(), 127);
        assert_eq!(vmax_max.minor(), 16777215);
    }

    #[test]
    fn version_display() {
        extern crate alloc;
        use alloc::string::ToString;

        assert_eq!("0.0", &Version::from_raw(0).to_string());
        assert_eq!("0.1", &Version::from_raw(0x1).to_string());
        assert_eq!("1.0", &Version::from_raw(0x100_0000).to_string());
        assert_eq!("1.1", &Version::from_raw(0x100_0001).to_string());
        assert_eq!("2.0", &Version::from_raw(0x200_0000).to_string());
        assert_eq!("127.0", &Version::from_raw(0x7f00_0000).to_string());
        assert_eq!("2.16777215", &Version::from_raw(0x2ff_ffff).to_string());
        assert_eq!("127.16777215", &Version::from_raw(0x7fff_ffff).to_string());
    }

    #[test]
    fn version_ordering() {
        use core::cmp::Ordering;
        let v0_0 = Version::from_raw(0x0);
        let v0_3 = Version::from_raw(0x3);
        let v1_0 = Version::from_raw(0x100_0000);
        let v2_0 = Version::from_raw(0x200_0000);
        let v2_1 = Version::from_raw(0x200_0001);
        let v2_max = Version::from_raw(0x2ff_ffff);
        let vmax_3 = Version::from_raw(0x7f00_0003);
        let vmax_max = Version::from_raw(0x7fff_ffff);

        assert!(v0_3 != v0_0);
        assert!(!(v0_3 == v0_0));
        assert!(v0_0 == v0_0);
        assert!(vmax_max == vmax_max);

        assert!(v0_3 > v0_0);
        assert!(v0_3 >= v0_0);
        assert!(v0_0 < v0_3);
        assert!(v0_0 <= v0_3);
        assert!(v0_0 >= v0_0);
        assert!(v0_0 <= v0_0);

        assert!(v0_3 > v0_0);
        assert!(v1_0 > v0_3);
        assert!(v2_0 > v1_0);
        assert!(v2_1 > v2_0);
        assert!(v2_max > v2_1);
        assert!(vmax_3 > v2_max);
        assert!(vmax_max > vmax_3);

        assert_eq!(Version::partial_cmp(&v1_0, &v0_0), Some(Ordering::Greater));
        assert_eq!(Version::partial_cmp(&v0_0, &v1_0), Some(Ordering::Less));
        assert_eq!(Version::partial_cmp(&v0_0, &v0_0), Some(Ordering::Equal));

        assert_eq!(Version::max(v0_0, v0_0), v0_0);
        assert_eq!(Version::max(v1_0, v0_0), v1_0);
        assert_eq!(Version::max(v0_0, v1_0), v1_0);
        assert_eq!(Version::min(v0_0, v0_0), v0_0);
        assert_eq!(Version::min(v1_0, v0_0), v0_0);
        assert_eq!(Version::min(v0_0, v1_0), v0_0);

        assert_eq!(v0_0.clamp(v0_3, v2_0), v0_3);
        assert_eq!(v0_3.clamp(v0_3, v2_0), v0_3);
        assert_eq!(v1_0.clamp(v0_3, v2_0), v1_0);
        assert_eq!(v2_0.clamp(v0_3, v2_0), v2_0);
        assert_eq!(v2_1.clamp(v0_3, v2_0), v2_0);
    }
}