FF-A message interface refactor

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: I6e24ac897ab3609d84449532e272313a35e1b825
diff --git a/src/lib.rs b/src/lib.rs
index efd5256..6f353f3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,7 +13,30 @@
 pub mod memory_management;
 pub mod partition_info;
 
-pub const FFA_PAGE_SIZE: usize = 4096;
+// On many occasions the FF-A spec defines memory size as count of 4K pages,
+// regardless of the current translation granule
+pub const FFA_PAGE_SIZE_4K: usize = 4096;
+
+#[derive(Debug, Error)]
+pub enum Error {
+    #[error("Unrecognised FF-A function ID {0}")]
+    UnrecognisedFunctionId(u32),
+    #[error("Unrecognised FF-A feature ID {0}")]
+    UnrecognisedFeatureId(u8),
+    #[error("Unrecognised FF-A error code {0}")]
+    UnrecognisedErrorCode(i32),
+}
+
+impl From<Error> for FfaError {
+    fn from(value: Error) -> Self {
+        match value {
+            Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
+                Self::NotSupported
+            }
+            Error::UnrecognisedErrorCode(_) => Self::InvalidParameters,
+        }
+    }
+}
 
 #[derive(PartialEq, Clone, Copy)]
 pub enum Instance {
@@ -21,38 +44,9 @@
     SecureVirtual(u16),
 }
 
-/// FF-A v1.1, Table 12.2: Error status codes
-#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
-#[repr(i32)]
-pub enum Error {
-    #[error("Not supported")]
-    NotSupported = -1,
-    #[error("Invalid parameters")]
-    InvalidParameters = -2,
-    #[error("No memory")]
-    NoMemory = -3,
-    #[error("Busy")]
-    Busy = -4,
-    #[error("Interrupted")]
-    Interrupted = -5,
-    #[error("Denied")]
-    Denied = -6,
-    #[error("Retry")]
-    Retry = -7,
-    #[error("Aborted")]
-    Aborted = -8,
-    #[error("No data")]
-    NoData = -9,
-}
-
-/// An integer couldn't be converted to a [`FuncId`] because it is not a recognised function.
-#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
-#[error("Unrecognised function ID {0} for FF-A")]
-pub struct UnrecognisedFunctionIdError(u32);
-
 /// FF-A v1.1: Function IDs
 #[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
-#[num_enum(error_type(name = UnrecognisedFunctionIdError, constructor = UnrecognisedFunctionIdError))]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
 #[repr(u32)]
 pub enum FuncId {
     Error = 0x84000060,
@@ -97,813 +91,56 @@
     ConsoleLog64 = 0xc400008a,
 }
 
+/// FF-A v1.1, Table 12.2: Error status codes
+#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
+#[repr(i32)]
+pub enum FfaError {
+    #[error("Not supported")]
+    NotSupported = -1,
+    #[error("Invalid parameters")]
+    InvalidParameters = -2,
+    #[error("No memory")]
+    NoMemory = -3,
+    #[error("Busy")]
+    Busy = -4,
+    #[error("Interrupted")]
+    Interrupted = -5,
+    #[error("Denied")]
+    Denied = -6,
+    #[error("Retry")]
+    Retry = -7,
+    #[error("Aborted")]
+    Aborted = -8,
+    #[error("No data")]
+    NoData = -9,
+}
+
 #[derive(Debug, Eq, PartialEq, Clone, Copy)]
-pub enum Interface {
-    Error {
-        target_info: u32,
-        error_code: Error,
-    },
-    Success {
-        target_info: u32,
-        result_regs: [u64; 6],
-        is_32bit: bool,
-    },
-    Interrupt {
-        endpoint_id: u32,
-        interrupt_id: u32,
-    },
-    Version {
-        input_version: u32,
-    },
-    VersionOut {
-        output_version: u32,
-    },
-    Features {
-        feat_id: u32,
-        input_properties: u32,
-    },
-    RxAcquire {
-        vm_id: u32,
-    },
-    RxRelease {
-        vm_id: u32,
-    },
-    RxTxMap {
-        tx_addr: u64,
-        rx_addr: u64,
-        page_cnt: u32,
-        is_32bit: bool,
-    },
-    RxTxUnmap {
-        id: u32,
-    },
-    PartitionInfoGet {
-        uuid: Uuid,
-        flags: u32,
-    },
-    IdGet,
-    SpmIdGet,
-    MsgWait,
-    Yield,
-    Run {
-        target_info: u32,
-    },
-    NormalWorldResume,
-    MsgSend2 {
-        sender_vm_id: u32,
-        flags: u32,
-    },
-    MsgSendDirectReq {
-        src_id: u16,
-        dst_id: u16,
-        flags: u32,
-        args: [u64; 5],
-        is_32bit: bool,
-    },
-    MsgSendDirectResp {
-        src_id: u16,
-        dst_id: u16,
-        flags: u32,
-        args: [u64; 5],
-        is_32bit: bool,
-    },
-    MemDonate {
-        total_len: u32,
-        frag_len: u32,
-        address: u64,
-        page_cnt: u32,
-        is_32bit: bool,
-    },
-    MemLend {
-        total_len: u32,
-        frag_len: u32,
-        address: u64,
-        page_cnt: u32,
-        is_32bit: bool,
-    },
-    MemShare {
-        total_len: u32,
-        frag_len: u32,
-        address: u64,
-        page_cnt: u32,
-        is_32bit: bool,
-    },
-    MemRetrieveReq {
-        total_len: u32,
-        frag_len: u32,
-        address: u64,
-        page_cnt: u32,
-        is_32bit: bool,
-    },
-    MemRetrieveResp {
-        total_len: u32,
-        frag_len: u32,
-    },
-    MemRelinquish,
-    MemReclaim {
-        handle: memory_management::Handle,
-        flags: u32,
-    },
-    MemPermGet {
-        base_addr: u64,
-        is_32bit: bool,
-    },
-    MemPermSet {
-        base_addr: u64,
-        page_cnt: u32,
-        mem_perm: u32,
-        is_32bit: bool,
-    },
-    ConsoleLog {
-        char_cnt: u32,
-        char_lists: [u64; 6],
-        is_32bit: bool,
-    },
+pub struct TargetInfo {
+    pub endpoint_id: u16,
+    pub vcpu_id: u16,
 }
 
-impl TryFrom<[u64; 8]> for Interface {
-    type Error = UnrecognisedFunctionIdError;
-
-    fn try_from(regs: [u64; 8]) -> Result<Self, UnrecognisedFunctionIdError> {
-        let fid = FuncId::try_from(regs[0] as u32)?;
-
-        let msg = match fid {
-            FuncId::Error => Self::Error {
-                target_info: regs[1] as u32,
-                error_code: Error::try_from(regs[2] as i32).unwrap(),
-            },
-            FuncId::Success32 | FuncId::Success64 => {
-                let target_info = regs[1] as u32;
-                let mut result_regs = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
-                let mut is_32bit = false;
-
-                if fid == FuncId::Success32 {
-                    result_regs[0] &= u32::MAX as u64;
-                    result_regs[1] &= u32::MAX as u64;
-                    result_regs[2] &= u32::MAX as u64;
-                    result_regs[3] &= u32::MAX as u64;
-                    result_regs[4] &= u32::MAX as u64;
-                    result_regs[5] &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::Success {
-                    target_info,
-                    result_regs,
-                    is_32bit,
-                }
-            }
-            FuncId::Interrupt => Self::Interrupt {
-                endpoint_id: regs[1] as u32,
-                interrupt_id: regs[2] as u32,
-            },
-            FuncId::Version => Self::Version {
-                input_version: regs[1] as u32,
-            },
-            FuncId::Features => Self::Features {
-                feat_id: regs[1] as u32,
-                input_properties: regs[2] as u32,
-            },
-            FuncId::RxAcquire => Self::RxAcquire {
-                vm_id: regs[1] as u32,
-            },
-            FuncId::RxRelease => Self::RxRelease {
-                vm_id: regs[1] as u32,
-            },
-            FuncId::RxTxMap32 | FuncId::RxTxMap64 => {
-                let mut tx_addr = regs[1];
-                let mut rx_addr = regs[2];
-                let page_cnt = regs[3] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::RxTxMap32 {
-                    tx_addr &= u32::MAX as u64;
-                    rx_addr &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::RxTxMap {
-                    tx_addr,
-                    rx_addr,
-                    page_cnt,
-                    is_32bit,
-                }
-            }
-            FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u32 },
-            FuncId::PartitionInfoGet => {
-                let uuid_words = [
-                    regs[1] as u32,
-                    regs[2] as u32,
-                    regs[3] as u32,
-                    regs[4] as u32,
-                ];
-                let mut bytes: [u8; 16] = [0; 16];
-                for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
-                    bytes[i] = b;
-                }
-                Self::PartitionInfoGet {
-                    uuid: Uuid::from_bytes(bytes),
-                    flags: regs[5] as u32,
-                }
-            }
-            FuncId::IdGet => Self::IdGet,
-            FuncId::SpmIdGet => Self::SpmIdGet,
-            FuncId::MsgWait => Self::MsgWait,
-            FuncId::Yield => Self::Yield,
-            FuncId::Run => Self::Run {
-                target_info: regs[1] as u32,
-            },
-            FuncId::NormalWorldResume => Self::NormalWorldResume,
-            FuncId::MsgSend2 => Self::MsgSend2 {
-                sender_vm_id: regs[1] as u32,
-                flags: regs[2] as u32,
-            },
-            FuncId::MsgSendDirectReq32 | FuncId::MsgSendDirectReq64 => {
-                let src_id = (regs[1] >> 16) as u16;
-                let dst_id = regs[1] as u16;
-                let flags = regs[2] as u32;
-                let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
-                let mut is_32bit = false;
-
-                if fid == FuncId::MsgSendDirectReq32 {
-                    args[0] &= u32::MAX as u64;
-                    args[1] &= u32::MAX as u64;
-                    args[2] &= u32::MAX as u64;
-                    args[3] &= u32::MAX as u64;
-                    args[4] &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MsgSendDirectReq {
-                    src_id,
-                    dst_id,
-                    flags,
-                    args,
-                    is_32bit,
-                }
-            }
-            FuncId::MsgSendDirectResp32 | FuncId::MsgSendDirectResp64 => {
-                let src_id = (regs[1] >> 16) as u16;
-                let dst_id = regs[1] as u16;
-                let flags = regs[2] as u32;
-                let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
-                let mut is_32bit = false;
-
-                if fid == FuncId::MsgSendDirectResp32 {
-                    args[0] &= u32::MAX as u64;
-                    args[1] &= u32::MAX as u64;
-                    args[2] &= u32::MAX as u64;
-                    args[3] &= u32::MAX as u64;
-                    args[4] &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MsgSendDirectResp {
-                    src_id,
-                    dst_id,
-                    flags,
-                    args,
-                    is_32bit,
-                }
-            }
-            FuncId::MemDonate32 | FuncId::MemDonate64 => {
-                let total_len = regs[1] as u32;
-                let frag_len = regs[2] as u32;
-                let mut address = regs[3];
-                let page_cnt = regs[4] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemDonate32 {
-                    address &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemDonate {
-                    total_len,
-                    frag_len,
-                    address,
-                    page_cnt,
-                    is_32bit,
-                }
-            }
-            FuncId::MemLend32 | FuncId::MemLend64 => {
-                let total_len = regs[1] as u32;
-                let frag_len = regs[2] as u32;
-                let mut address = regs[3];
-                let page_cnt = regs[4] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemLend32 {
-                    address &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemLend {
-                    total_len,
-                    frag_len,
-                    address,
-                    page_cnt,
-                    is_32bit,
-                }
-            }
-            FuncId::MemShare32 | FuncId::MemShare64 => {
-                let total_len = regs[1] as u32;
-                let frag_len = regs[2] as u32;
-                let mut address = regs[3];
-                let page_cnt = regs[4] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemShare32 {
-                    address &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemShare {
-                    total_len,
-                    frag_len,
-                    address,
-                    page_cnt,
-                    is_32bit,
-                }
-            }
-            FuncId::MemRetrieveReq32 | FuncId::MemRetrieveReq64 => {
-                let total_len = regs[1] as u32;
-                let frag_len = regs[2] as u32;
-                let mut address = regs[3];
-                let page_cnt = regs[4] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemRetrieveReq32 {
-                    address &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemRetrieveReq {
-                    total_len,
-                    frag_len,
-                    address,
-                    page_cnt,
-                    is_32bit,
-                }
-            }
-            FuncId::MemRetrieveResp => Self::MemRetrieveResp {
-                total_len: regs[1] as u32,
-                frag_len: regs[2] as u32,
-            },
-            FuncId::MemRelinquish => Self::MemRelinquish,
-            FuncId::MemReclaim => Self::MemReclaim {
-                handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
-                flags: regs[3] as u32,
-            },
-            FuncId::MemPermGet32 | FuncId::MemPermGet64 => {
-                let mut base_addr = regs[1];
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemPermGet32 {
-                    base_addr &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemPermGet {
-                    base_addr,
-                    is_32bit,
-                }
-            }
-            FuncId::MemPermSet32 | FuncId::MemPermSet64 => {
-                let mut base_addr = regs[1];
-                let page_cnt = regs[2] as u32;
-                let mem_perm = regs[3] as u32;
-                let mut is_32bit = false;
-
-                if fid == FuncId::MemPermSet32 {
-                    base_addr &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::MemPermSet {
-                    base_addr,
-                    page_cnt,
-                    mem_perm,
-                    is_32bit,
-                }
-            }
-            FuncId::ConsoleLog32 | FuncId::ConsoleLog64 => {
-                let char_cnt = regs[1] as u32;
-                let mut char_lists = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
-                let mut is_32bit = false;
-
-                if fid == FuncId::ConsoleLog32 {
-                    char_lists[0] &= u32::MAX as u64;
-                    char_lists[1] &= u32::MAX as u64;
-                    char_lists[2] &= u32::MAX as u64;
-                    char_lists[3] &= u32::MAX as u64;
-                    char_lists[4] &= u32::MAX as u64;
-                    char_lists[5] &= u32::MAX as u64;
-                    is_32bit = true;
-                }
-
-                Self::ConsoleLog {
-                    char_cnt,
-                    char_lists,
-                    is_32bit,
-                }
-            }
-        };
-
-        Ok(msg)
+impl From<u32> for TargetInfo {
+    fn from(value: u32) -> Self {
+        Self {
+            endpoint_id: (value >> 16) as u16,
+            vcpu_id: value as u16,
+        }
     }
 }
 
-impl Interface {
-    /// Returns the function ID for the call, if it has one.
-    pub fn function_id(&self) -> Option<FuncId> {
-        match self {
-            Interface::Error { .. } => Some(FuncId::Error),
-            Interface::Success {
-                is_32bit: false, ..
-            } => Some(FuncId::Success64),
-            Interface::Success { is_32bit: true, .. } => Some(FuncId::Success32),
-            Interface::Interrupt { .. } => Some(FuncId::Interrupt),
-            Interface::Version { .. } => Some(FuncId::Version),
-            Interface::VersionOut { .. } => None,
-            Interface::Features { .. } => Some(FuncId::Features),
-            Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
-            Interface::RxRelease { .. } => Some(FuncId::RxRelease),
-            Interface::RxTxMap {
-                is_32bit: false, ..
-            } => Some(FuncId::RxTxMap64),
-            Interface::RxTxMap { is_32bit: true, .. } => Some(FuncId::RxTxMap32),
-            Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
-            Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
-            Interface::IdGet => Some(FuncId::IdGet),
-            Interface::SpmIdGet => Some(FuncId::SpmIdGet),
-            Interface::MsgWait => Some(FuncId::MsgWait),
-            Interface::Yield => Some(FuncId::Yield),
-            Interface::Run { .. } => Some(FuncId::Run),
-            Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
-            Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
-            Interface::MsgSendDirectReq {
-                is_32bit: false, ..
-            } => Some(FuncId::MsgSendDirectReq64),
-            Interface::MsgSendDirectReq { is_32bit: true, .. } => Some(FuncId::MsgSendDirectReq32),
-            Interface::MsgSendDirectResp {
-                is_32bit: false, ..
-            } => Some(FuncId::MsgSendDirectResp64),
-            Interface::MsgSendDirectResp { is_32bit: true, .. } => {
-                Some(FuncId::MsgSendDirectResp32)
-            }
-            Interface::MemDonate {
-                is_32bit: false, ..
-            } => Some(FuncId::MemDonate64),
-            Interface::MemDonate { is_32bit: true, .. } => Some(FuncId::MemDonate32),
-            Interface::MemLend {
-                is_32bit: false, ..
-            } => Some(FuncId::MemLend64),
-            Interface::MemLend { is_32bit: true, .. } => Some(FuncId::MemLend32),
-            Interface::MemShare {
-                is_32bit: false, ..
-            } => Some(FuncId::MemShare64),
-            Interface::MemShare { is_32bit: true, .. } => Some(FuncId::MemShare32),
-            Interface::MemRetrieveReq {
-                is_32bit: false, ..
-            } => Some(FuncId::MemRetrieveReq64),
-            Interface::MemRetrieveReq { is_32bit: true, .. } => Some(FuncId::MemRetrieveReq32),
-            Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
-            Interface::MemRelinquish => Some(FuncId::MemRelinquish),
-            Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
-            Interface::MemPermGet {
-                is_32bit: false, ..
-            } => Some(FuncId::MemPermGet64),
-            Interface::MemPermGet { is_32bit: true, .. } => Some(FuncId::MemPermGet32),
-            Interface::MemPermSet {
-                is_32bit: false, ..
-            } => Some(FuncId::MemPermSet64),
-            Interface::MemPermSet { is_32bit: true, .. } => Some(FuncId::MemPermSet32),
-            Interface::ConsoleLog {
-                is_32bit: false, ..
-            } => Some(FuncId::ConsoleLog64),
-            Interface::ConsoleLog { is_32bit: true, .. } => Some(FuncId::ConsoleLog32),
-        }
+impl From<TargetInfo> for u32 {
+    fn from(value: TargetInfo) -> Self {
+        (value.endpoint_id as u32) << 16 | value.vcpu_id as u32
     }
+}
 
-    /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
-    pub fn is_32bit(&self) -> bool {
-        match self {
-            Interface::Error { .. }
-            | Interface::Interrupt { .. }
-            | Interface::Version { .. }
-            | Interface::VersionOut { .. }
-            | Interface::Features { .. }
-            | Interface::RxAcquire { .. }
-            | Interface::RxRelease { .. }
-            | Interface::RxTxUnmap { .. }
-            | Interface::PartitionInfoGet { .. }
-            | Interface::IdGet
-            | Interface::SpmIdGet
-            | Interface::MsgWait
-            | Interface::Yield
-            | Interface::Run { .. }
-            | Interface::NormalWorldResume
-            | Interface::MsgSend2 { .. }
-            | Interface::MemRetrieveResp { .. }
-            | Interface::MemRelinquish
-            | Interface::MemReclaim { .. } => true,
-            Interface::Success { is_32bit, .. }
-            | Interface::RxTxMap { is_32bit, .. }
-            | Interface::MsgSendDirectReq { is_32bit, .. }
-            | Interface::MsgSendDirectResp { is_32bit, .. }
-            | Interface::MemDonate { is_32bit, .. }
-            | Interface::MemLend { is_32bit, .. }
-            | Interface::MemShare { is_32bit, .. }
-            | Interface::MemRetrieveReq { is_32bit, .. }
-            | Interface::MemPermGet { is_32bit, .. }
-            | Interface::MemPermSet { is_32bit, .. }
-            | Interface::ConsoleLog { is_32bit, .. } => *is_32bit,
-        }
-    }
-
-    pub fn copy_to_array(&self, a: &mut [u64; 8]) {
-        a.fill(0);
-        if let Some(function_id) = self.function_id() {
-            a[0] = function_id as u64;
-        }
-
-        match *self {
-            Interface::Error {
-                target_info,
-                error_code,
-            } => {
-                a[1] = target_info as u64;
-                a[2] = error_code as u32 as u64;
-            }
-            Interface::Success {
-                target_info,
-                result_regs,
-                is_32bit,
-            } => {
-                a[1] = target_info as u64;
-                if is_32bit {
-                    a[2] = result_regs[0] & u32::MAX as u64;
-                    a[3] = result_regs[1] & u32::MAX as u64;
-                    a[4] = result_regs[2] & u32::MAX as u64;
-                    a[5] = result_regs[3] & u32::MAX as u64;
-                    a[6] = result_regs[4] & u32::MAX as u64;
-                    a[7] = result_regs[5] & u32::MAX as u64;
-                } else {
-                    a[2] = result_regs[0];
-                    a[3] = result_regs[1];
-                    a[4] = result_regs[2];
-                    a[5] = result_regs[3];
-                    a[6] = result_regs[4];
-                    a[7] = result_regs[5];
-                }
-            }
-            Interface::Interrupt {
-                endpoint_id,
-                interrupt_id,
-            } => {
-                a[1] = endpoint_id as u64;
-                a[2] = interrupt_id as u64;
-            }
-            Interface::Version { input_version } => {
-                a[1] = input_version as u64;
-            }
-            Interface::VersionOut { output_version } => {
-                a[0] = output_version as u64;
-            }
-            Interface::Features {
-                feat_id,
-                input_properties,
-            } => {
-                a[1] = feat_id as u64;
-                a[2] = input_properties as u64;
-            }
-            Interface::RxAcquire { vm_id } => {
-                a[1] = vm_id as u64;
-            }
-            Interface::RxRelease { vm_id } => {
-                a[1] = vm_id as u64;
-            }
-            Interface::RxTxMap {
-                tx_addr,
-                rx_addr,
-                page_cnt,
-                is_32bit,
-            } => {
-                a[3] = page_cnt as u64;
-                if is_32bit {
-                    a[1] = tx_addr & u32::MAX as u64;
-                    a[2] = rx_addr & u32::MAX as u64;
-                } else {
-                    a[1] = tx_addr;
-                    a[2] = rx_addr;
-                }
-            }
-            Interface::RxTxUnmap { id } => {
-                a[1] = id as u64;
-            }
-            Interface::PartitionInfoGet { uuid, flags } => {
-                let bytes = uuid.to_bytes_le();
-                a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64;
-                a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64;
-                a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as u64;
-                a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as u64;
-                a[5] = flags as u64;
-            }
-            Interface::IdGet | Interface::SpmIdGet | Interface::MsgWait | Interface::Yield => {}
-            Interface::Run { target_info } => {
-                a[1] = target_info as u64;
-            }
-            Interface::NormalWorldResume => {}
-            Interface::MsgSend2 {
-                sender_vm_id,
-                flags,
-            } => {
-                a[1] = sender_vm_id as u64;
-                a[2] = flags as u64;
-            }
-            Interface::MsgSendDirectReq {
-                src_id,
-                dst_id,
-                flags,
-                args,
-                is_32bit,
-            } => {
-                a[1] = (src_id as u64) << 16 | dst_id as u64;
-                a[2] = flags as u64;
-                if is_32bit {
-                    a[3] = args[0] & u32::MAX as u64;
-                    a[4] = args[1] & u32::MAX as u64;
-                    a[5] = args[2] & u32::MAX as u64;
-                    a[6] = args[3] & u32::MAX as u64;
-                    a[7] = args[4] & u32::MAX as u64;
-                } else {
-                    a[3] = args[0];
-                    a[4] = args[1];
-                    a[5] = args[2];
-                    a[6] = args[3];
-                    a[7] = args[4];
-                }
-            }
-            Interface::MsgSendDirectResp {
-                src_id,
-                dst_id,
-                flags,
-                args,
-                is_32bit,
-            } => {
-                a[1] = (src_id as u64) << 16 | dst_id as u64;
-                a[2] = flags as u64;
-                if is_32bit {
-                    a[3] = args[0] & u32::MAX as u64;
-                    a[4] = args[1] & u32::MAX as u64;
-                    a[5] = args[2] & u32::MAX as u64;
-                    a[6] = args[3] & u32::MAX as u64;
-                    a[7] = args[4] & u32::MAX as u64;
-                } else {
-                    a[3] = args[0];
-                    a[4] = args[1];
-                    a[5] = args[2];
-                    a[6] = args[3];
-                    a[7] = args[4];
-                }
-            }
-            Interface::MemDonate {
-                total_len,
-                frag_len,
-                address,
-                page_cnt,
-                is_32bit,
-            } => {
-                a[1] = total_len as u64;
-                a[2] = frag_len as u64;
-                a[4] = page_cnt as u64;
-                if is_32bit {
-                    a[3] = address & u32::MAX as u64;
-                } else {
-                    a[3] = address;
-                }
-            }
-            Interface::MemLend {
-                total_len,
-                frag_len,
-                address,
-                page_cnt,
-                is_32bit,
-            } => {
-                a[1] = total_len as u64;
-                a[2] = frag_len as u64;
-                a[4] = page_cnt as u64;
-                if is_32bit {
-                    a[3] = address & u32::MAX as u64;
-                } else {
-                    a[3] = address;
-                }
-            }
-            Interface::MemShare {
-                total_len,
-                frag_len,
-                address,
-                page_cnt,
-                is_32bit,
-            } => {
-                a[1] = total_len as u64;
-                a[2] = frag_len as u64;
-                a[4] = page_cnt as u64;
-                if is_32bit {
-                    a[3] = address & u32::MAX as u64;
-                } else {
-                    a[3] = address;
-                }
-            }
-            Interface::MemRetrieveReq {
-                total_len,
-                frag_len,
-                address,
-                page_cnt,
-                is_32bit,
-            } => {
-                a[1] = total_len as u64;
-                a[2] = frag_len as u64;
-                a[4] = page_cnt as u64;
-                if is_32bit {
-                    a[3] = address & u32::MAX as u64;
-                } else {
-                    a[3] = address;
-                }
-            }
-            Interface::MemRetrieveResp {
-                total_len,
-                frag_len,
-            } => {
-                a[1] = total_len as u64;
-                a[2] = frag_len as u64;
-            }
-            Interface::MemRelinquish => {}
-            Interface::MemReclaim { handle, flags } => {
-                let handle_regs: [u32; 2] = handle.into();
-                a[1] = handle_regs[0] as u64;
-                a[2] = handle_regs[1] as u64;
-                a[3] = flags as u64;
-            }
-            Interface::MemPermGet {
-                base_addr,
-                is_32bit,
-            } => {
-                if is_32bit {
-                    a[1] = base_addr & u32::MAX as u64;
-                } else {
-                    a[1] = base_addr;
-                }
-            }
-            Interface::MemPermSet {
-                base_addr,
-                page_cnt,
-                mem_perm,
-                is_32bit,
-            } => {
-                a[2] = page_cnt as u64;
-                a[3] = mem_perm as u64;
-
-                if is_32bit {
-                    a[1] = base_addr & u32::MAX as u64;
-                } else {
-                    a[1] = base_addr;
-                }
-            }
-            Interface::ConsoleLog {
-                char_cnt,
-                char_lists,
-                is_32bit,
-            } => {
-                a[1] = char_cnt as u64;
-                if is_32bit {
-                    a[2] = char_lists[0] & u32::MAX as u64;
-                    a[3] = char_lists[1] & u32::MAX as u64;
-                    a[4] = char_lists[2] & u32::MAX as u64;
-                    a[5] = char_lists[3] & u32::MAX as u64;
-                    a[6] = char_lists[4] & u32::MAX as u64;
-                    a[7] = char_lists[5] & u32::MAX as u64;
-                } else {
-                    a[2] = char_lists[0];
-                    a[3] = char_lists[1];
-                    a[4] = char_lists[2];
-                    a[5] = char_lists[3];
-                    a[6] = char_lists[4];
-                    a[7] = char_lists[5];
-                }
-            }
-        }
-    }
-
-    /// Helper function to create an FFA_SUCCESS interface without any arguments
-    pub fn success32_noargs() -> Self {
-        Self::Success {
-            target_info: 0,
-            result_regs: [0, 0, 0, 0, 0, 0],
-            is_32bit: true,
-        }
-    }
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum SuccessArgs {
+    Result32([u32; 6]),
+    Result64([u64; 6]),
 }
 
 #[derive(Clone, Copy, Eq, PartialEq)]
@@ -933,31 +170,882 @@
     }
 }
 
-pub const CONSOLE_LOG_32_MAX_MSG_LEN: usize = 24;
-pub const CONSOLE_LOG_64_MAX_MSG_LEN: usize = 48;
+#[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,
+}
 
-pub fn parse_console_log(
-    char_cnt: u32,
-    char_lists: &[u64; 6],
-    is_32bit: bool,
-    log_bytes: &mut [u8],
-) -> Result<(), Error> {
-    const CHAR_COUNT_MASK: u32 = 0xff;
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Feature {
+    FuncId(FuncId),
+    FeatureId(FeatureId),
+}
 
-    let char_count = (char_cnt & CHAR_COUNT_MASK) as usize;
-    let (max_length, reg_size) = if is_32bit {
-        (CONSOLE_LOG_32_MAX_MSG_LEN, 4)
-    } else {
-        (CONSOLE_LOG_64_MAX_MSG_LEN, 8)
-    };
+impl TryFrom<u32> for Feature {
+    type Error = Error;
 
-    if char_count < 1 || char_count > max_length {
-        return Err(Error::InvalidParameters);
+    fn try_from(value: u32) -> Result<Self, Self::Error> {
+        let res = if (value >> 31) & 1 == 1 {
+            Self::FuncId(value.try_into()?)
+        } else {
+            Self::FeatureId((value as u8).try_into()?)
+        };
+
+        Ok(res)
+    }
+}
+
+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,
+        }
+    }
+}
+
+#[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 enum DirectMsgArgs {
+    Args32([u32; 5]),
+    Args64([u64; 5]),
+}
+
+#[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),
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum ConsoleLogChars {
+    Reg32([u32; 6]),
+    Reg64([u64; 6]),
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Interface {
+    Error {
+        target_info: TargetInfo,
+        error_code: FfaError,
+    },
+    Success {
+        target_info: u32,
+        args: SuccessArgs,
+    },
+    Interrupt {
+        target_info: TargetInfo,
+        interrupt_id: u32,
+    },
+    Version {
+        input_version: Version,
+    },
+    VersionOut {
+        output_version: Version,
+    },
+    Features {
+        feat_id: Feature,
+        input_properties: u32,
+    },
+    RxAcquire {
+        vm_id: u16,
+    },
+    RxRelease {
+        vm_id: u16,
+    },
+    RxTxMap {
+        addr: RxTxAddr,
+        page_cnt: u32,
+    },
+    RxTxUnmap {
+        id: u16,
+    },
+    PartitionInfoGet {
+        uuid: Uuid,
+        flags: u32,
+    },
+    IdGet,
+    SpmIdGet,
+    MsgWait,
+    Yield,
+    Run {
+        target_info: TargetInfo,
+    },
+    NormalWorldResume,
+    MsgSend2 {
+        sender_vm_id: u16,
+        flags: u32,
+    },
+    MsgSendDirectReq {
+        src_id: u16,
+        dst_id: u16,
+        flags: u32,
+        args: DirectMsgArgs,
+    },
+    MsgSendDirectResp {
+        src_id: u16,
+        dst_id: u16,
+        flags: u32,
+        args: DirectMsgArgs,
+    },
+    MemDonate {
+        total_len: u32,
+        frag_len: u32,
+        buf: Option<MemOpBuf>,
+    },
+    MemLend {
+        total_len: u32,
+        frag_len: u32,
+        buf: Option<MemOpBuf>,
+    },
+    MemShare {
+        total_len: u32,
+        frag_len: u32,
+        buf: Option<MemOpBuf>,
+    },
+    MemRetrieveReq {
+        total_len: u32,
+        frag_len: u32,
+        buf: Option<MemOpBuf>,
+    },
+    MemRetrieveResp {
+        total_len: u32,
+        frag_len: u32,
+    },
+    MemRelinquish,
+    MemReclaim {
+        handle: memory_management::Handle,
+        flags: u32,
+    },
+    MemPermGet {
+        addr: MemAddr,
+    },
+    MemPermSet {
+        addr: MemAddr,
+        page_cnt: u32,
+        mem_perm: u32,
+    },
+    ConsoleLog {
+        char_cnt: u8,
+        char_lists: ConsoleLogChars,
+    },
+}
+
+impl TryFrom<[u64; 8]> for Interface {
+    type Error = Error;
+
+    fn try_from(regs: [u64; 8]) -> Result<Self, Error> {
+        let fid = FuncId::try_from(regs[0] as u32)?;
+
+        let msg = match fid {
+            FuncId::Error => Self::Error {
+                target_info: (regs[1] as u32).into(),
+                error_code: FfaError::try_from(regs[2] as i32)?,
+            },
+            FuncId::Success32 => Self::Success {
+                target_info: regs[1] as u32,
+                args: SuccessArgs::Result32([
+                    regs[2] as u32,
+                    regs[3] as u32,
+                    regs[4] as u32,
+                    regs[5] as u32,
+                    regs[6] as u32,
+                    regs[7] as u32,
+                ]),
+            },
+            FuncId::Success64 => Self::Success {
+                target_info: regs[1] as u32,
+                args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
+            },
+            FuncId::Interrupt => Self::Interrupt {
+                target_info: (regs[1] as u32).into(),
+                interrupt_id: regs[2] as u32,
+            },
+            FuncId::Version => Self::Version {
+                input_version: (regs[1] as u32).into(),
+            },
+            FuncId::Features => Self::Features {
+                feat_id: (regs[1] as u32).try_into()?,
+                input_properties: regs[2] as u32,
+            },
+            FuncId::RxAcquire => Self::RxAcquire {
+                vm_id: regs[1] as u16,
+            },
+            FuncId::RxRelease => Self::RxRelease {
+                vm_id: regs[1] as u16,
+            },
+            FuncId::RxTxMap32 => {
+                let addr = RxTxAddr::Addr32 {
+                    rx: regs[2] as u32,
+                    tx: regs[1] as u32,
+                };
+                let page_cnt = regs[3] as u32;
+
+                Self::RxTxMap { addr, page_cnt }
+            }
+            FuncId::RxTxMap64 => {
+                let addr = RxTxAddr::Addr64 {
+                    rx: regs[2],
+                    tx: regs[1],
+                };
+                let page_cnt = regs[3] as u32;
+
+                Self::RxTxMap { addr, page_cnt }
+            }
+            FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
+            FuncId::PartitionInfoGet => {
+                let uuid_words = [
+                    regs[1] as u32,
+                    regs[2] as u32,
+                    regs[3] as u32,
+                    regs[4] as u32,
+                ];
+                let mut bytes: [u8; 16] = [0; 16];
+                for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
+                    bytes[i] = b;
+                }
+                Self::PartitionInfoGet {
+                    uuid: Uuid::from_bytes(bytes),
+                    flags: regs[5] as u32,
+                }
+            }
+            FuncId::IdGet => Self::IdGet,
+            FuncId::SpmIdGet => Self::SpmIdGet,
+            FuncId::MsgWait => Self::MsgWait,
+            FuncId::Yield => Self::Yield,
+            FuncId::Run => Self::Run {
+                target_info: (regs[1] as u32).into(),
+            },
+            FuncId::NormalWorldResume => Self::NormalWorldResume,
+            FuncId::MsgSend2 => Self::MsgSend2 {
+                sender_vm_id: regs[1] as u16,
+                flags: regs[2] as u32,
+            },
+            FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                flags: regs[2] as u32,
+                args: DirectMsgArgs::Args32([
+                    regs[3] as u32,
+                    regs[4] as u32,
+                    regs[5] as u32,
+                    regs[6] as u32,
+                    regs[7] as u32,
+                ]),
+            },
+            FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                flags: regs[2] as u32,
+                args: DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]]),
+            },
+            FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                flags: regs[2] as u32,
+                args: DirectMsgArgs::Args32([
+                    regs[3] as u32,
+                    regs[4] as u32,
+                    regs[5] as u32,
+                    regs[6] as u32,
+                    regs[7] as u32,
+                ]),
+            },
+            FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                flags: regs[2] as u32,
+                args: DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]]),
+            },
+            FuncId::MemDonate32 => Self::MemDonate {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf32 {
+                        addr: regs[3] as u32,
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemDonate64 => Self::MemDonate {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf64 {
+                        addr: regs[3],
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemLend32 => Self::MemLend {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf32 {
+                        addr: regs[3] as u32,
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemLend64 => Self::MemLend {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf64 {
+                        addr: regs[3],
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemShare32 => Self::MemShare {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf32 {
+                        addr: regs[3] as u32,
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemShare64 => Self::MemShare {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf64 {
+                        addr: regs[3],
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf32 {
+                        addr: regs[3] as u32,
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+                buf: if regs[3] != 0 && regs[4] != 0 {
+                    Some(MemOpBuf::Buf64 {
+                        addr: regs[3],
+                        page_cnt: regs[4] as u32,
+                    })
+                } else {
+                    None
+                },
+            },
+            FuncId::MemRetrieveResp => Self::MemRetrieveResp {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+            },
+            FuncId::MemRelinquish => Self::MemRelinquish,
+            FuncId::MemReclaim => Self::MemReclaim {
+                handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
+                flags: regs[3] as u32,
+            },
+            FuncId::MemPermGet32 => Self::MemPermGet {
+                addr: MemAddr::Addr32(regs[1] as u32),
+            },
+            FuncId::MemPermGet64 => Self::MemPermGet {
+                addr: MemAddr::Addr64(regs[1]),
+            },
+            FuncId::MemPermSet32 => Self::MemPermSet {
+                addr: MemAddr::Addr32(regs[1] as u32),
+                page_cnt: regs[2] as u32,
+                mem_perm: regs[3] as u32,
+            },
+            FuncId::MemPermSet64 => Self::MemPermSet {
+                addr: MemAddr::Addr64(regs[1]),
+                page_cnt: regs[2] as u32,
+                mem_perm: regs[3] as u32,
+            },
+            FuncId::ConsoleLog32 => Self::ConsoleLog {
+                char_cnt: regs[1] as u8,
+                char_lists: ConsoleLogChars::Reg32([
+                    regs[2] as u32,
+                    regs[3] as u32,
+                    regs[4] as u32,
+                    regs[5] as u32,
+                    regs[6] as u32,
+                    regs[7] as u32,
+                ]),
+            },
+            FuncId::ConsoleLog64 => Self::ConsoleLog {
+                char_cnt: regs[1] as u8,
+                char_lists: ConsoleLogChars::Reg64([
+                    regs[2], regs[3], regs[4], regs[5], regs[6], regs[7],
+                ]),
+            },
+        };
+
+        Ok(msg)
+    }
+}
+
+impl Interface {
+    /// Returns the function ID for the call, if it has one.
+    pub fn function_id(&self) -> Option<FuncId> {
+        match self {
+            Interface::Error { .. } => Some(FuncId::Error),
+            Interface::Success { args, .. } => match args {
+                SuccessArgs::Result32(..) => Some(FuncId::Success32),
+                SuccessArgs::Result64(..) => Some(FuncId::Success64),
+            },
+            Interface::Interrupt { .. } => Some(FuncId::Interrupt),
+            Interface::Version { .. } => Some(FuncId::Version),
+            Interface::VersionOut { .. } => None,
+            Interface::Features { .. } => Some(FuncId::Features),
+            Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
+            Interface::RxRelease { .. } => Some(FuncId::RxRelease),
+            Interface::RxTxMap { addr, .. } => match addr {
+                RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
+                RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
+            },
+            Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
+            Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
+            Interface::IdGet => Some(FuncId::IdGet),
+            Interface::SpmIdGet => Some(FuncId::SpmIdGet),
+            Interface::MsgWait => Some(FuncId::MsgWait),
+            Interface::Yield => Some(FuncId::Yield),
+            Interface::Run { .. } => Some(FuncId::Run),
+            Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
+            Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
+            Interface::MsgSendDirectReq { args, .. } => match args {
+                DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
+                DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
+            },
+            Interface::MsgSendDirectResp { args, .. } => match args {
+                DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
+                DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
+            },
+            Interface::MemDonate { buf, .. } => match buf {
+                Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
+                _ => Some(FuncId::MemDonate32),
+            },
+            Interface::MemLend { buf, .. } => match buf {
+                Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
+                _ => Some(FuncId::MemLend32),
+            },
+            Interface::MemShare { buf, .. } => match buf {
+                Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
+                _ => Some(FuncId::MemShare32),
+            },
+            Interface::MemRetrieveReq { buf, .. } => match buf {
+                Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
+                _ => Some(FuncId::MemRetrieveReq32),
+            },
+            Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
+            Interface::MemRelinquish => Some(FuncId::MemRelinquish),
+            Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
+            Interface::MemPermGet { addr, .. } => match addr {
+                MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
+                MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
+            },
+            Interface::MemPermSet { addr, .. } => match addr {
+                MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
+                MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
+            },
+            Interface::ConsoleLog { char_lists, .. } => match char_lists {
+                ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
+                ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
+            },
+        }
     }
 
-    for i in 0..=5 {
-        log_bytes[reg_size * i..reg_size * (i + 1)]
-            .copy_from_slice(&char_lists[i].to_le_bytes()[0..reg_size]);
+    /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
+    pub fn is_32bit(&self) -> bool {
+        match self {
+            Interface::Error { .. }
+            | Interface::Interrupt { .. }
+            | Interface::Version { .. }
+            | Interface::VersionOut { .. }
+            | Interface::Features { .. }
+            | Interface::RxAcquire { .. }
+            | Interface::RxRelease { .. }
+            | Interface::RxTxUnmap { .. }
+            | Interface::PartitionInfoGet { .. }
+            | Interface::IdGet
+            | Interface::SpmIdGet
+            | Interface::MsgWait
+            | Interface::Yield
+            | Interface::Run { .. }
+            | Interface::NormalWorldResume
+            | Interface::MsgSend2 { .. }
+            | Interface::MemRetrieveResp { .. }
+            | Interface::MemRelinquish
+            | Interface::MemReclaim { .. } => true,
+            Interface::Success {
+                args: SuccessArgs::Result32(..),
+                ..
+            } => true,
+            Interface::RxTxMap {
+                addr: RxTxAddr::Addr32 { .. },
+                ..
+            } => true,
+            Interface::MsgSendDirectReq { args, .. }
+            | Interface::MsgSendDirectResp { args, .. }
+                if matches!(args, DirectMsgArgs::Args32(_)) =>
+            {
+                true
+            }
+            Interface::MemDonate { buf, .. }
+            | Interface::MemLend { buf, .. }
+            | Interface::MemShare { buf, .. }
+            | Interface::MemRetrieveReq { buf, .. }
+                if buf.is_none() || matches!(buf, Some(MemOpBuf::Buf32 { .. })) =>
+            {
+                true
+            }
+            Interface::MemPermGet { addr, .. } | Interface::MemPermSet { addr, .. }
+                if matches!(addr, MemAddr::Addr32(_)) =>
+            {
+                true
+            }
+            Interface::ConsoleLog {
+                char_lists: ConsoleLogChars::Reg32(_),
+                ..
+            } => true,
+            _ => false,
+        }
+    }
+
+    pub fn copy_to_array(&self, a: &mut [u64; 8]) {
+        a.fill(0);
+        if let Some(function_id) = self.function_id() {
+            a[0] = function_id as u64;
+        }
+
+        match *self {
+            Interface::Error {
+                target_info,
+                error_code,
+            } => {
+                a[1] = u32::from(target_info).into();
+                a[2] = (error_code as u32).into();
+            }
+            Interface::Success { target_info, args } => {
+                a[1] = target_info.into();
+                match args {
+                    SuccessArgs::Result32(regs) => {
+                        a[2] = regs[0].into();
+                        a[3] = regs[1].into();
+                        a[4] = regs[2].into();
+                        a[5] = regs[3].into();
+                        a[6] = regs[4].into();
+                        a[7] = regs[5].into();
+                    }
+                    SuccessArgs::Result64(regs) => {
+                        a[2] = regs[0];
+                        a[3] = regs[1];
+                        a[4] = regs[2];
+                        a[5] = regs[3];
+                        a[6] = regs[4];
+                        a[7] = regs[5];
+                    }
+                }
+            }
+            Interface::Interrupt {
+                target_info,
+                interrupt_id,
+            } => {
+                a[1] = u32::from(target_info).into();
+                a[2] = interrupt_id.into();
+            }
+            Interface::Version { input_version } => {
+                a[1] = u32::from(input_version).into();
+            }
+            Interface::VersionOut { output_version } => {
+                a[0] = u32::from(output_version).into();
+            }
+            Interface::Features {
+                feat_id,
+                input_properties,
+            } => {
+                a[1] = u32::from(feat_id).into();
+                a[2] = input_properties.into();
+            }
+            Interface::RxAcquire { vm_id } => {
+                a[1] = vm_id.into();
+            }
+            Interface::RxRelease { vm_id } => {
+                a[1] = vm_id.into();
+            }
+            Interface::RxTxMap { addr, page_cnt } => {
+                match addr {
+                    RxTxAddr::Addr32 { rx, tx } => {
+                        a[1] = tx.into();
+                        a[2] = rx.into();
+                    }
+                    RxTxAddr::Addr64 { rx, tx } => {
+                        a[1] = tx;
+                        a[2] = rx;
+                    }
+                }
+                a[3] = page_cnt.into();
+            }
+            Interface::RxTxUnmap { id } => {
+                a[1] = id.into();
+            }
+            Interface::PartitionInfoGet { uuid, flags } => {
+                let bytes = uuid.into_bytes();
+                a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
+                a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
+                a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
+                a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
+                a[5] = flags.into();
+            }
+            Interface::IdGet | Interface::SpmIdGet | Interface::MsgWait | Interface::Yield => {}
+            Interface::Run { target_info } => {
+                a[1] = u32::from(target_info).into();
+            }
+            Interface::NormalWorldResume => {}
+            Interface::MsgSend2 {
+                sender_vm_id,
+                flags,
+            } => {
+                a[1] = sender_vm_id.into();
+                a[2] = flags.into();
+            }
+            Interface::MsgSendDirectReq {
+                src_id,
+                dst_id,
+                flags,
+                args,
+            } => {
+                a[1] = (src_id as u64) << 16 | dst_id as u64;
+                a[2] = flags.into();
+                match args {
+                    DirectMsgArgs::Args32(args) => {
+                        a[3] = args[0].into();
+                        a[4] = args[1].into();
+                        a[5] = args[2].into();
+                        a[6] = args[3].into();
+                        a[7] = args[4].into();
+                    }
+                    DirectMsgArgs::Args64(args) => {
+                        a[3] = args[0];
+                        a[4] = args[1];
+                        a[5] = args[2];
+                        a[6] = args[3];
+                        a[7] = args[4];
+                    }
+                }
+            }
+            Interface::MsgSendDirectResp {
+                src_id,
+                dst_id,
+                flags,
+                args,
+            } => {
+                a[1] = (src_id as u64) << 16 | dst_id as u64;
+                a[2] = flags.into();
+                match args {
+                    DirectMsgArgs::Args32(args) => {
+                        a[3] = args[0].into();
+                        a[4] = args[1].into();
+                        a[5] = args[2].into();
+                        a[6] = args[3].into();
+                        a[7] = args[4].into();
+                    }
+                    DirectMsgArgs::Args64(args) => {
+                        a[3] = args[0];
+                        a[4] = args[1];
+                        a[5] = args[2];
+                        a[6] = args[3];
+                        a[7] = args[4];
+                    }
+                }
+            }
+            Interface::MemDonate {
+                total_len,
+                frag_len,
+                buf,
+            } => {
+                a[1] = total_len.into();
+                a[2] = frag_len.into();
+                (a[3], a[4]) = match buf {
+                    Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
+                    Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
+                    None => (0, 0),
+                };
+            }
+            Interface::MemLend {
+                total_len,
+                frag_len,
+                buf,
+            } => {
+                a[1] = total_len.into();
+                a[2] = frag_len.into();
+                (a[3], a[4]) = match buf {
+                    Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
+                    Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
+                    None => (0, 0),
+                };
+            }
+            Interface::MemShare {
+                total_len,
+                frag_len,
+                buf,
+            } => {
+                a[1] = total_len.into();
+                a[2] = frag_len.into();
+                (a[3], a[4]) = match buf {
+                    Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
+                    Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
+                    None => (0, 0),
+                };
+            }
+            Interface::MemRetrieveReq {
+                total_len,
+                frag_len,
+                buf,
+            } => {
+                a[1] = total_len.into();
+                a[2] = frag_len.into();
+                (a[3], a[4]) = match buf {
+                    Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
+                    Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
+                    None => (0, 0),
+                };
+            }
+            Interface::MemRetrieveResp {
+                total_len,
+                frag_len,
+            } => {
+                a[1] = total_len.into();
+                a[2] = frag_len.into();
+            }
+            Interface::MemRelinquish => {}
+            Interface::MemReclaim { handle, flags } => {
+                let handle_regs: [u32; 2] = handle.into();
+                a[1] = handle_regs[0].into();
+                a[2] = handle_regs[1].into();
+                a[3] = flags.into();
+            }
+            Interface::MemPermGet { addr } => {
+                a[1] = match addr {
+                    MemAddr::Addr32(addr) => addr.into(),
+                    MemAddr::Addr64(addr) => addr,
+                };
+            }
+            Interface::MemPermSet {
+                addr,
+                page_cnt,
+                mem_perm,
+            } => {
+                a[1] = match addr {
+                    MemAddr::Addr32(addr) => addr.into(),
+                    MemAddr::Addr64(addr) => addr,
+                };
+                a[2] = page_cnt.into();
+                a[3] = mem_perm.into();
+            }
+            Interface::ConsoleLog {
+                char_cnt,
+                char_lists,
+            } => {
+                a[1] = char_cnt.into();
+                match char_lists {
+                    ConsoleLogChars::Reg32(regs) => {
+                        a[2] = regs[0].into();
+                        a[3] = regs[1].into();
+                        a[4] = regs[2].into();
+                        a[5] = regs[3].into();
+                        a[6] = regs[4].into();
+                        a[7] = regs[5].into();
+                    }
+                    ConsoleLogChars::Reg64(regs) => {
+                        a[2] = regs[0];
+                        a[3] = regs[1];
+                        a[4] = regs[2];
+                        a[5] = regs[3];
+                        a[6] = regs[4];
+                        a[7] = regs[5];
+                    }
+                }
+            }
+        }
+    }
+
+    /// Helper function to create an FFA_SUCCESS interface without any arguments
+    pub fn success32_noargs() -> Self {
+        Self::Success {
+            target_info: 0,
+            args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
+        }
+    }
+
+    /// Helper function to create an FFA_ERROR interface without any arguments
+    pub fn error(error_code: FfaError) -> Self {
+        Self::Error {
+            target_info: TargetInfo {
+                endpoint_id: 0,
+                vcpu_id: 0,
+            },
+            error_code,
+        }
+    }
+}
+
+pub const CONSOLE_LOG_32_MAX_MSG_LEN: u8 = 24;
+pub const CONSOLE_LOG_64_MAX_MSG_LEN: u8 = 48;
+
+pub fn parse_console_log(
+    char_cnt: u8,
+    char_lists: &ConsoleLogChars,
+    log_bytes: &mut [u8],
+) -> Result<(), FfaError> {
+    match char_lists {
+        ConsoleLogChars::Reg32(regs) => {
+            if !(1..=CONSOLE_LOG_32_MAX_MSG_LEN).contains(&char_cnt) {
+                return Err(FfaError::InvalidParameters);
+            }
+            for (i, reg) in regs.iter().enumerate() {
+                log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
+            }
+        }
+        ConsoleLogChars::Reg64(regs) => {
+            if !(1..=CONSOLE_LOG_64_MAX_MSG_LEN).contains(&char_cnt) {
+                return Err(FfaError::InvalidParameters);
+            }
+            for (i, reg) in regs.iter().enumerate() {
+                log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
+            }
+        }
     }
 
     Ok(())