Add support for handling multiple FF-A versions

This is required because a single FF-A component (i.e. an SPMC) must be
able to support various FF-A versions at runtime. Also, implement some
of the new message types from FF-A v1.2 and add support for handling 18
registers instead of 8.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ia46dc40204698265dc4ea9ed3731275baad94949
diff --git a/src/lib.rs b/src/lib.rs
index 466ec0a..53c1cb8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,7 @@
 
 pub mod boot_info;
 mod ffa_v1_1;
+mod ffa_v1_2;
 pub mod memory_management;
 pub mod partition_info;
 
@@ -78,8 +79,10 @@
     MsgSend2 = 0x84000086,
     MsgSendDirectReq32 = 0x8400006f,
     MsgSendDirectReq64 = 0xc400006f,
+    MsgSendDirectReq64_2 = 0xc400008d,
     MsgSendDirectResp32 = 0x84000070,
     MsgSendDirectResp64 = 0xc4000070,
+    MsgSendDirectResp64_2 = 0xc400008e,
     MemDonate32 = 0x84000071,
     MemDonate64 = 0xc4000071,
     MemLend32 = 0x84000072,
@@ -99,6 +102,13 @@
     ConsoleLog64 = 0xc400008a,
 }
 
+impl FuncId {
+    /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
+    pub fn is_32bit(&self) -> bool {
+        u32::from(*self) & (1 << 30) != 0
+    }
+}
+
 /// Error status codes used by the `FFA_ERROR` interface.
 #[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
 #[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
@@ -151,10 +161,11 @@
 pub enum SuccessArgs {
     Result32([u32; 6]),
     Result64([u64; 6]),
+    Result64_2([u64; 16]),
 }
 
 /// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
-#[derive(Clone, Copy, Eq, PartialEq)]
+#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
 pub struct Version(pub u16, pub u16);
 
 impl From<u32> for Version {
@@ -235,6 +246,10 @@
     Args64([u64; 5]),
 }
 
+/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct DirectMsg2Args([u64; 14]);
+
 /// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
 /// descriptor. Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX
 /// buffer is not used to transmit the transaction descriptor.
@@ -251,11 +266,11 @@
     Addr64(u64),
 }
 
-/// Argument for the `FFA_CONSOLE_LOG` interface. Currently only supports x0..x7 instead of x0..x17.
+/// Argument for the `FFA_CONSOLE_LOG` interface.
 #[derive(Debug, Eq, PartialEq, Clone, Copy)]
 pub enum ConsoleLogChars {
     Reg32([u32; 6]),
-    Reg64([u64; 6]),
+    Reg64([u64; 16]),
 }
 
 /// FF-A "message types", the terminology used by the spec is "interfaces". The interfaces are used
@@ -326,6 +341,17 @@
         flags: u32,
         args: DirectMsgArgs,
     },
+    MsgSendDirectReq2 {
+        src_id: u16,
+        dst_id: u16,
+        uuid: Uuid,
+        args: DirectMsg2Args,
+    },
+    MsgSendDirectResp2 {
+        src_id: u16,
+        dst_id: u16,
+        args: DirectMsg2Args,
+    },
     MemDonate {
         total_len: u32,
         frag_len: u32,
@@ -357,6 +383,7 @@
     },
     MemPermGet {
         addr: MemAddr,
+        page_cnt: Option<u32>,
     },
     MemPermSet {
         addr: MemAddr,
@@ -369,10 +396,116 @@
     },
 }
 
-impl TryFrom<[u64; 8]> for Interface {
-    type Error = Error;
+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(..) | SuccessArgs::Result64_2(..) => 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::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
+            Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
+            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),
+            },
+        }
+    }
 
-    fn try_from(regs: [u64; 8]) -> Result<Self, Error> {
+    /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
+    pub fn is_32bit(&self) -> bool {
+        // TODO: self should always have a function ID?
+        self.function_id().unwrap().is_32bit()
+    }
+
+    /// Parse interface from register contents. The caller must ensure that the `regs` argument has
+    /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
+    pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
+        let reg_cnt = regs.len();
+
+        let msg = match reg_cnt {
+            8 => {
+                assert!(version <= Version(1, 1));
+                Interface::unpack_regs8(version, regs.try_into().unwrap())?
+            }
+            18 => {
+                assert!(version >= Version(1, 2));
+                match FuncId::try_from(regs[0] as u32)? {
+                    FuncId::ConsoleLog64
+                    | FuncId::Success64
+                    | FuncId::MsgSendDirectReq64_2
+                    | FuncId::MsgSendDirectResp64_2 => {
+                        Interface::unpack_regs18(version, regs.try_into().unwrap())?
+                    }
+                    _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
+                }
+            }
+            _ => panic!(
+                "Invalid number of registers ({}) for FF-A version {}",
+                reg_cnt, version
+            ),
+        };
+
+        Ok(msg)
+    }
+
+    fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
         let fid = FuncId::try_from(regs[0] as u32)?;
 
         let msg = match fid {
@@ -602,9 +735,19 @@
             },
             FuncId::MemPermGet32 => Self::MemPermGet {
                 addr: MemAddr::Addr32(regs[1] as u32),
+                page_cnt: if version >= Version(1, 3) {
+                    Some(regs[2] as u32)
+                } else {
+                    None
+                },
             },
             FuncId::MemPermGet64 => Self::MemPermGet {
                 addr: MemAddr::Addr64(regs[1]),
+                page_cnt: if version >= Version(1, 3) {
+                    Some(regs[2] as u32)
+                } else {
+                    None
+                },
             },
             FuncId::MemPermSet32 => Self::MemPermSet {
                 addr: MemAddr::Addr32(regs[1] as u32),
@@ -627,147 +770,78 @@
                     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],
-                ]),
-            },
+            _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
         };
 
         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),
-            },
-        }
-    }
+    fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
+        assert!(version >= Version(1, 2));
 
-    /// 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,
-        }
+        let fid = FuncId::try_from(regs[0] as u32)?;
+
+        let msg = match fid {
+            FuncId::Success64 => Self::Success {
+                target_info: regs[1] as u32,
+                args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
+            },
+            FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                uuid: Uuid::from_u64_pair(regs[2], regs[3]),
+                args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
+            },
+            FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
+                src_id: (regs[1] >> 16) as u16,
+                dst_id: regs[1] as u16,
+                args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
+            },
+            FuncId::ConsoleLog64 => Self::ConsoleLog {
+                char_cnt: regs[1] as u8,
+                char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
+            },
+            _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
+        };
+
+        Ok(msg)
     }
 
     /// Create register contents for an interface.
-    pub fn copy_to_array(&self, a: &mut [u64; 8]) {
+    pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
+        let reg_cnt = regs.len();
+
+        match reg_cnt {
+            8 => {
+                assert!(version <= Version(1, 1));
+                self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
+            }
+            18 => {
+                assert!(version >= Version(1, 2));
+
+                match self {
+                    Interface::ConsoleLog {
+                        char_lists: ConsoleLogChars::Reg64(_),
+                        ..
+                    }
+                    | Interface::Success {
+                        args: SuccessArgs::Result64_2(_),
+                        ..
+                    }
+                    | Interface::MsgSendDirectReq2 { .. }
+                    | Interface::MsgSendDirectResp2 { .. } => {
+                        self.pack_regs18(version, regs.try_into().unwrap());
+                    }
+                    _ => {
+                        self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
+                    }
+                }
+            }
+            _ => panic!("Invalid number of registers {}", reg_cnt),
+        }
+    }
+
+    fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
         a.fill(0);
         if let Some(function_id) = self.function_id() {
             a[0] = function_id as u64;
@@ -800,6 +874,7 @@
                         a[6] = regs[4];
                         a[7] = regs[5];
                     }
+                    _ => panic!("{:#x?} requires 18 registers", args),
                 }
             }
             Interface::Interrupt {
@@ -980,11 +1055,17 @@
                 a[2] = handle_regs[1].into();
                 a[3] = flags.into();
             }
-            Interface::MemPermGet { addr } => {
+            Interface::MemPermGet { addr, page_cnt } => {
                 a[1] = match addr {
                     MemAddr::Addr32(addr) => addr.into(),
                     MemAddr::Addr64(addr) => addr,
                 };
+                a[2] = if version >= Version(1, 3) {
+                    page_cnt.unwrap().into()
+                } else {
+                    assert!(page_cnt.is_none());
+                    0
+                }
             }
             Interface::MemPermSet {
                 addr,
@@ -1012,16 +1093,60 @@
                         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];
-                    }
+                    _ => panic!("{:#x?} requires 18 registers", char_lists),
                 }
             }
+            _ => panic!("{:#x?} requires 18 registers", self),
+        }
+    }
+
+    fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
+        assert!(version >= Version(1, 2));
+
+        a.fill(0);
+        if let Some(function_id) = self.function_id() {
+            a[0] = function_id as u64;
+        }
+
+        match *self {
+            Interface::Success { target_info, args } => {
+                a[1] = target_info.into();
+                match args {
+                    SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
+                    _ => panic!("{:#x?} requires 8 registers", args),
+                }
+            }
+            Interface::MsgSendDirectReq2 {
+                src_id,
+                dst_id,
+                uuid,
+                args,
+            } => {
+                a[1] = ((src_id as u64) << 16) | dst_id as u64;
+                (a[2], a[3]) = uuid.as_u64_pair();
+                a[4..18].copy_from_slice(&args.0[..14]);
+            }
+            Interface::MsgSendDirectResp2 {
+                src_id,
+                dst_id,
+                args,
+            } => {
+                a[1] = ((src_id as u64) << 16) | dst_id as u64;
+                a[2] = 0;
+                a[3] = 0;
+                a[4..18].copy_from_slice(&args.0[..14]);
+            }
+            Interface::ConsoleLog {
+                char_cnt,
+                char_lists,
+            } => {
+                a[1] = char_cnt.into();
+                match char_lists {
+                    ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
+                    _ => panic!("{:#x?} requires 8 registers", char_lists),
+                }
+            }
+            _ => panic!("{:#x?} requires 8 registers", self),
         }
     }
 
@@ -1047,10 +1172,8 @@
 
 /// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
 pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
-/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message. Note: this
-/// value currently differs from the spec because the library currently only supports parsing 8
-/// registers instead of 18.
-pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 48;
+/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
+pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
 
 /// Helper function to convert the "Tightly packed list of characters" format used by the
 /// `FFA_CONSOLE_LOG` interface into a byte slice.