use crate::{Error, FfaError, FuncId, Version, memory_management};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use zerocopy::{FromBytes, Immutable, IntoBytes};
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct TargetInfo {
pub endpoint_id: u16,
pub vcpu_id: u16,
}
impl From<u32> for TargetInfo {
fn from(value: u32) -> Self {
Self {
endpoint_id: (value >> 16) as u16,
vcpu_id: value as u16,
}
}
}
impl From<TargetInfo> for u32 {
fn from(value: TargetInfo) -> Self {
((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SuccessArgs {
Args32([u32; 6]),
Args64([u64; 16]),
}
impl SuccessArgs {
pub(crate) fn try_get_args32(self) -> Result<[u32; 6], Error> {
match self {
SuccessArgs::Args32(args) => Ok(args),
SuccessArgs::Args64(_) => Err(Error::InvalidSuccessArgsVariant),
}
}
pub(crate) fn try_get_args64(self) -> Result<[u64; 16], Error> {
match self {
SuccessArgs::Args64(args) => Ok(args),
SuccessArgs::Args32(_) => Err(Error::InvalidSuccessArgsVariant),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SecondaryEpRegisterAddr {
Addr32(u32),
Addr64(u64),
}
#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
#[repr(u8)]
pub enum FeatureId {
NotificationPendingInterrupt = 0x1,
ScheduleReceiverInterrupt = 0x2,
ManagedExitInterrupt = 0x3,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Feature {
FuncId(FuncId),
FeatureId(FeatureId),
Unknown(u32),
}
impl From<u32> for Feature {
fn from(value: u32) -> Self {
if let Ok(func_id) = value.try_into() {
Self::FuncId(func_id)
} else if let Ok(feat_id) = (value as u8).try_into() {
Self::FeatureId(feat_id)
} else {
Self::Unknown(value)
}
}
}
impl From<Feature> for u32 {
fn from(value: Feature) -> Self {
match value {
Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
Feature::FeatureId(feature_id) => feature_id as u32,
Feature::Unknown(id) => id,
}
}
}
#[derive(Debug, Eq, Default, PartialEq, Clone, Copy)]
pub struct SuccessArgsFeatures {
pub properties: [u32; 2],
}
impl From<SuccessArgsFeatures> for SuccessArgs {
fn from(value: SuccessArgsFeatures) -> Self {
Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
}
}
impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
type Error = Error;
fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
let args = value.try_get_args32()?;
Ok(Self {
properties: [args[0], args[1]],
})
}
}
#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = Error, constructor = Error::InvalidVersionQueryType))]
#[repr(u8)]
pub enum VersionQueryType {
Negotiate = 0b00,
QueryCompatibility = 0b01,
QueryNegotiated = 0b10,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VersionFlags {
pub query_type: VersionQueryType,
}
impl VersionFlags {
const QUERY_TYPE_MASK: u32 = 0b11;
const MBZ_BITS: u32 = 0xffff_fffc;
}
impl TryFrom<u32> for VersionFlags {
type Error = Error;
fn try_from(val: u32) -> Result<Self, Self::Error> {
if (val & Self::MBZ_BITS) != 0 {
Err(Error::InvalidVersionFlags(val))
} else {
Ok(Self {
query_type: VersionQueryType::try_from((val & Self::QUERY_TYPE_MASK) as u8)?,
})
}
}
}
impl From<VersionFlags> for u32 {
fn from(flags: VersionFlags) -> Self {
flags.query_type as u32
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum RxTxAddr {
Addr32 { rx: u32, tx: u32 },
Addr64 { rx: u64, tx: u64 },
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct SuccessArgsIdGet {
pub id: u16,
}
impl From<SuccessArgsIdGet> for SuccessArgs {
fn from(value: SuccessArgsIdGet) -> Self {
SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
}
}
impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
type Error = Error;
fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
let args = value.try_get_args32()?;
Ok(Self { id: args[0] as u16 })
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct SuccessArgsSpmIdGet {
pub id: u16,
}
impl From<SuccessArgsSpmIdGet> for SuccessArgs {
fn from(value: SuccessArgsSpmIdGet) -> Self {
SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
}
}
impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
type Error = Error;
fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
let args = value.try_get_args32()?;
Ok(Self { id: args[0] as u16 })
}
}
#[derive(Debug, Eq, Default, PartialEq, Clone, Copy)]
pub struct MsgSend2Flags {
pub delay_schedule_receiver: bool,
}
impl MsgSend2Flags {
const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
const MBZ_BITS: u32 = 0xffff_fffd;
}
impl TryFrom<u32> for MsgSend2Flags {
type Error = Error;
fn try_from(val: u32) -> Result<Self, Self::Error> {
if (val & Self::MBZ_BITS) != 0 {
Err(Error::InvalidMsgSend2Flag(val))
} else {
Ok(MsgSend2Flags {
delay_schedule_receiver: val & Self::DELAY_SCHEDULE_RECEIVER != 0,
})
}
}
}
impl From<MsgSend2Flags> for u32 {
fn from(flags: MsgSend2Flags) -> Self {
let mut bits: u32 = 0;
if flags.delay_schedule_receiver {
bits |= MsgSend2Flags::DELAY_SCHEDULE_RECEIVER;
}
bits
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum VmAvailabilityStatus {
Success,
Error(FfaError),
}
impl TryFrom<i32> for VmAvailabilityStatus {
type Error = Error;
fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
Ok(match value {
0 => Self::Success,
error_code => Self::Error(FfaError::try_from(error_code)?),
})
}
}
impl From<VmAvailabilityStatus> for i32 {
fn from(value: VmAvailabilityStatus) -> Self {
match value {
VmAvailabilityStatus::Success => 0,
VmAvailabilityStatus::Error(error_code) => error_code.into(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
#[repr(u32)]
pub enum WarmBootType {
ExitFromSuspendToRam = 0,
ExitFromLowPower = 1,
}
impl WarmBootType {
#[allow(non_upper_case_globals)]
#[deprecated = "Ambiguous name. Please use ExitFromSuspendToRam."]
pub const ExitFromSuspend: WarmBootType = WarmBootType::ExitFromSuspendToRam;
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum DirectMsgArgs {
Args32([u32; 5]),
Args64([u64; 15]),
VersionReq {
version: Version,
flags: VersionFlags,
},
VersionResp {
version: Option<Version>,
},
PowerPsciReq32 {
params: [u32; 4],
},
PowerPsciReq64 {
params: [u64; 4],
},
PowerWarmBootReq {
boot_type: WarmBootType,
},
PowerPsciResp {
psci_status: i32,
},
VmCreated {
handle: memory_management::Handle,
vm_id: u16,
},
VmCreatedAck {
sp_status: VmAvailabilityStatus,
},
VmDestructed {
handle: memory_management::Handle,
vm_id: u16,
},
VmDestructedAck {
sp_status: VmAvailabilityStatus,
},
}
impl DirectMsgArgs {
pub(crate) const FWK_MSG_BITS: u32 = 1 << 31;
pub(crate) const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
pub(crate) const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
pub(crate) const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
pub(crate) const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
pub(crate) const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
pub(crate) const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
pub(crate) const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
pub(crate) const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
pub(crate) const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct DirectMsg2Args(pub [u64; 14]);
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct MsgWaitFlags {
pub retain_rx_buffer: bool,
}
impl MsgWaitFlags {
const RETAIN_RX_BUFFER: u32 = 0x01;
const MBZ_BITS: u32 = 0xfffe;
}
impl TryFrom<u32> for MsgWaitFlags {
type Error = Error;
fn try_from(val: u32) -> Result<Self, Self::Error> {
if (val & Self::MBZ_BITS) != 0 {
Err(Error::InvalidMsgWaitFlag(val))
} else {
Ok(MsgWaitFlags {
retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
})
}
}
}
impl From<MsgWaitFlags> for u32 {
fn from(flags: MsgWaitFlags) -> Self {
let mut bits: u32 = 0;
if flags.retain_rx_buffer {
bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
}
bits
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MemOpBuf {
Buf32 { addr: u32, page_cnt: u32 },
Buf64 { addr: u64, page_cnt: u32 },
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MemAddr {
Addr32(u32),
Addr64(u64),
}
impl MemAddr {
pub fn address(&self) -> u64 {
match self {
MemAddr::Addr32(a) => (*a).into(),
MemAddr::Addr64(a) => *a,
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum ConsoleLogChars {
Chars32(ConsoleLogChars32),
Chars64(ConsoleLogChars64),
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct LogChars<T>
where
T: IntoBytes + FromBytes + Immutable,
{
pub(crate) char_cnt: u8,
pub(crate) char_lists: T,
}
impl<T> LogChars<T>
where
T: IntoBytes + FromBytes + Immutable,
{
pub(crate) const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
pub fn empty(&self) -> bool {
self.char_cnt == 0
}
pub fn full(&self) -> bool {
self.char_cnt as usize >= core::mem::size_of::<T>()
}
pub fn bytes(&self) -> &[u8] {
&self.char_lists.as_bytes()[..self.char_cnt as usize]
}
pub fn push(&mut self, source: &[u8]) -> usize {
let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
let len = empty_area.len().min(source.len());
empty_area[..len].copy_from_slice(&source[..len]);
self.char_cnt += len as u8;
len
}
}
pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn log_chars_empty() {
assert!(
ConsoleLogChars64 {
char_cnt: 0,
char_lists: [0; 16]
}
.empty()
)
}
#[test]
fn log_chars_push() {
let mut console = ConsoleLogChars64 {
char_cnt: 0,
char_lists: [0; 16],
};
assert_eq!(console.push("hello world!".as_bytes()), 12);
assert_eq!(console.char_cnt, 12);
assert_eq!(&console.bytes()[0..12], "hello world!".as_bytes());
assert!(!console.empty());
}
#[test]
fn log_chars_full() {
let mut console = ConsoleLogChars64 {
char_cnt: 0,
char_lists: [0; 16],
};
assert_eq!(console.push(&[97; 128]), 128);
assert!(console.full());
}
#[test]
fn success_args_invalid_variants() {
assert!(SuccessArgs::Args32([0; 6]).try_get_args64().is_err());
assert!(SuccessArgs::Args64([0; 16]).try_get_args32().is_err());
}
}