use crate::types::ValidationError;
use core::fmt;
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SecurityState {
NonSecure = 0b00,
Secure = 0b01,
Realm = 0b10,
Root = 0b11,
}
impl SecurityState {
#[inline]
#[must_use]
pub const fn is_secure(self) -> bool {
matches!(self, Self::Secure)
}
#[inline]
#[must_use]
pub const fn is_non_secure(self) -> bool {
matches!(self, Self::NonSecure)
}
#[inline]
#[must_use]
pub const fn is_realm(self) -> bool {
matches!(self, Self::Realm)
}
#[inline]
#[must_use]
pub const fn const_is_secure(self) -> bool {
self.is_secure()
}
#[inline]
#[must_use]
pub const fn const_is_non_secure(self) -> bool {
self.is_non_secure()
}
#[inline]
#[must_use]
pub const fn const_is_realm(self) -> bool {
self.is_realm()
}
#[inline]
#[must_use]
pub const fn is_root(self) -> bool {
matches!(self, Self::Root)
}
#[inline]
#[must_use]
pub const fn const_is_root(self) -> bool {
self.is_root()
}
pub fn transition_to(self, target: Self) -> Result<Self, ValidationError> {
if self == target {
return Ok(target);
}
Err(ValidationError::InvalidStateTransition {
from: format!("{self}"),
to: format!("{target}"),
})
}
#[inline]
#[must_use]
pub const fn can_access(self, target: Self) -> bool {
matches!(
(self, target),
(Self::Root, _)
| (Self::Secure, Self::Secure | Self::NonSecure)
| (Self::NonSecure, Self::NonSecure)
| (Self::Realm, Self::Realm)
)
}
pub fn validate_access(self, target: Self) -> Result<(), ValidationError> {
if self.can_access(target) {
Ok(())
} else {
Err(ValidationError::SecurityViolation {
from_state: format!("{self}"),
to_state: format!("{target}"),
})
}
}
#[inline]
#[must_use]
pub const fn to_bits(self) -> u8 {
self as u8
}
pub const fn from_bits(bits: u8) -> Result<Self, ValidationError> {
match bits {
0b00 => Ok(Self::NonSecure),
0b01 => Ok(Self::Secure),
0b10 => Ok(Self::Realm),
0b11 => Ok(Self::Root),
_ => Err(ValidationError::InvalidSecurityState { bits }),
}
}
}
impl fmt::Display for SecurityState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Secure => "Secure",
Self::NonSecure => "NonSecure",
Self::Realm => "Realm",
Self::Root => "Root",
};
write!(f, "{s}")
}
}
impl Default for SecurityState {
fn default() -> Self {
Self::NonSecure
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_security_states() {
assert!(SecurityState::Secure.is_secure());
assert!(SecurityState::NonSecure.is_non_secure());
assert!(SecurityState::Realm.is_realm());
}
#[test]
fn test_access_rules() {
assert!(SecurityState::Secure.can_access(SecurityState::Secure));
assert!(SecurityState::NonSecure.can_access(SecurityState::NonSecure));
assert!(SecurityState::Realm.can_access(SecurityState::Realm));
assert!(SecurityState::Secure.can_access(SecurityState::NonSecure));
assert!(!SecurityState::NonSecure.can_access(SecurityState::Secure));
assert!(!SecurityState::Secure.can_access(SecurityState::Realm));
assert!(!SecurityState::NonSecure.can_access(SecurityState::Realm));
}
#[test]
fn test_encoding() {
assert_eq!(SecurityState::NonSecure.to_bits(), 0b00);
assert_eq!(SecurityState::Secure.to_bits(), 0b01);
assert_eq!(SecurityState::Realm.to_bits(), 0b10);
}
}