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/README.md b/README.md
index e969932..691c7c2 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,8 @@
 [FF-A Memory Management Protocol specification](https://developer.arm.com/documentation/den0140/latest/)
 
 Library for handling common FF-A related functionality, create and parse interfaces and descriptors
-defined by FF-A.
+defined by FF-A. Starting from FF-A v1.2 the memory management related parts of the specification
+have been moved to a separate document (link above).
 
 ## Design goals
   * Keep the code exception level agnostic by default. If exception level specific parts are
@@ -27,6 +28,17 @@
       the spec (i.e. Table x.y or chapter x.y.z)
     * The data structures should derive the necessary `zerocopy` traits.
 
+## FF-A version handling
+
+The FF-A specification allows different components of a system to use different versions of the
+specification. The version used at a specific FF-A instance (i.e. an interface between two FF-A
+components) is discovered at runtime, either by parsing FF-A manifests or using `FFA_VERSION`. An
+FF-A component might have to use multiple versions at runtime on its different interfaces, therefore
+this library must be able to support this and having a compile time fixed version is not possible.
+Because of this, most of the functions to create or parse FF-A messages and data structures require
+passing the FF-A version used at the instance where the serialized data was received from or will be
+sent to.
+
 ## Implemented features
 
   * Supports converting FF-A interface types between Rust types and the FF-A register ABI.
@@ -38,6 +50,7 @@
 
   * Implement missing features from FF-A v1.1 and later. Implementing FF-A v1.0 features that are
     deprecated by v1.1 are low priority for now.
+  * Increase test coverage.
   * Create more detailed documentation to capture which parts of FF-A are currently supported.
 
 ## License
diff --git a/src/boot_info.rs b/src/boot_info.rs
index c9c45fb..9d902cf 100644
--- a/src/boot_info.rs
+++ b/src/boot_info.rs
@@ -16,15 +16,18 @@
 //!   - S-EL2 SPMC and a S-EL0 SP.
 //!   - S-EL1 SPMC and a S-EL0 SP.
 
-use crate::{
-    ffa_v1_1::{boot_info_descriptor, boot_info_header},
-    Version,
-};
 use core::ffi::CStr;
 use thiserror::Error;
 use uuid::Uuid;
 use zerocopy::{FromBytes, IntoBytes};
 
+// This module uses FF-A v1.1 types by default.
+// FF-A v1.2 didn't introduce any changes to the data stuctures used by this module.
+use crate::{
+    ffa_v1_1::{boot_info_descriptor, boot_info_header},
+    Version,
+};
+
 /// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
 /// with the `FFA_ERROR` interface.
 #[derive(Debug, Error)]
@@ -244,7 +247,14 @@
     /// virtual address where the buffer is mapped to). This is necessary since there are
     /// self-references within the serialized data structure which must be described with an
     /// absolute address according to the FF-A spec.
-    pub fn pack(descriptors: &[BootInfo], buf: &mut [u8], mapped_addr: Option<usize>) {
+    pub fn pack(
+        version: Version,
+        descriptors: &[BootInfo],
+        buf: &mut [u8],
+        mapped_addr: Option<usize>,
+    ) {
+        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
+
         // Offset from the base of the header to the first element in the boot info descriptor array
         // Must be 8 byte aligned, but otherwise we're free to choose any value here.
         // Let's just pack the array right after the header.
@@ -365,7 +375,7 @@
 
         let header_raw = boot_info_header {
             signature: 0x0ffa,
-            version: Version(1, 1).into(),
+            version: version.into(),
             boot_info_blob_size: total_offset as u32,
             boot_info_desc_size: DESC_SIZE as u32,
             boot_info_desc_count: desc_cnt as u32,
@@ -377,7 +387,7 @@
     }
 
     /// Validate and return the boot information header
-    fn get_header(buf: &[u8]) -> Result<&boot_info_header, Error> {
+    fn get_header(version: Version, buf: &[u8]) -> Result<&boot_info_header, Error> {
         let (header_raw, _) =
             boot_info_header::ref_from_prefix(buf).map_err(|_| Error::InvalidHeader)?;
 
@@ -385,9 +395,9 @@
             return Err(Error::InvalidSignature);
         }
 
-        let version = Version::from(header_raw.version);
-        if version != Version(1, 1) {
-            return Err(Error::InvalidVersion(version));
+        let header_version = header_raw.version.into();
+        if header_version != version {
+            return Err(Error::InvalidVersion(header_version));
         }
 
         Ok(header_raw)
@@ -397,8 +407,12 @@
     /// consumer to map all of the boot information blob in its translation regime or copy it to
     /// another memory location without parsing each element in the boot information descriptor
     /// array.
-    pub fn get_blob_size(buf: &[u8]) -> Result<usize, Error> {
-        let header_raw = Self::get_header(buf)?;
+    pub fn get_blob_size(version: Version, buf: &[u8]) -> Result<usize, Error> {
+        if !(Version(1, 1)..=Version(1, 2)).contains(&version) {
+            return Err(Error::InvalidVersion(version));
+        }
+
+        let header_raw = Self::get_header(version, buf)?;
 
         Ok(header_raw.boot_info_blob_size as usize)
     }
@@ -414,8 +428,8 @@
 
 impl<'a> BootInfoIterator<'a> {
     /// Create an iterator of boot information descriptors from a buffer.
-    pub fn new(buf: &'a [u8]) -> Result<Self, Error> {
-        let header_raw = BootInfo::get_header(buf)?;
+    pub fn new(version: Version, buf: &'a [u8]) -> Result<Self, Error> {
+        let header_raw = BootInfo::get_header(version, buf)?;
 
         if buf.len() < header_raw.boot_info_blob_size as usize {
             return Err(Error::InvalidBufferSize);
@@ -551,8 +565,13 @@
 
         let mut buf = [0u8; 0x1ff];
         let buf_addr = buf.as_ptr() as usize;
-        BootInfo::pack(&[desc1.clone(), desc2.clone()], &mut buf, Some(buf_addr));
-        let mut descriptors = BootInfoIterator::new(&buf).unwrap();
+        BootInfo::pack(
+            Version(1, 1),
+            &[desc1.clone(), desc2.clone()],
+            &mut buf,
+            Some(buf_addr),
+        );
+        let mut descriptors = BootInfoIterator::new(Version(1, 1), &buf).unwrap();
         let desc1_check = descriptors.next().unwrap().unwrap();
         let desc2_check = descriptors.next().unwrap().unwrap();
 
diff --git a/src/ffa_v1_2.rs b/src/ffa_v1_2.rs
new file mode 100644
index 0000000..2d40344
--- /dev/null
+++ b/src/ffa_v1_2.rs
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+#![allow(non_camel_case_types)]
+
+use crate::ffa_v1_1;
+use zerocopy_derive::*;
+
+/// Table 6.1: Partition information descriptor
+/// Table 6.2: Partition properties descriptor
+/// The following changes are introduced by FF-A v1.2 to the partition properties field:
+/// - bit\[10:9\]: Has the following encoding if Bits\[5:4\] = b’00. Reserved (MBZ) otherwise.
+///   + bit\[9\] has the following encoding:
+///     * b’0: Cannot receive Direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+///     * b’1: Can receive Direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+///   + bit\[10\] has the following encoding:
+///     * b’0: Cannot send Direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+///     * b’1: Can send Direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+/// - bit\[31:11\]: Reserved (MBZ).
+///
+/// This doesn't change the descriptor format so we can just use an alias.
+#[allow(unused)]
+pub(crate) type partition_info_descriptor = ffa_v1_1::partition_info_descriptor;
+
+/// FF-A Memory Management Protocol Table 1.16: Endpoint memory access descriptor
+#[derive(Default, FromBytes, IntoBytes, KnownLayout, Immutable)]
+#[repr(C, packed)]
+pub(crate) struct endpoint_memory_access_descriptor {
+    /// Offset 0, length 4: Memory access permissions descriptor as specified in Table 10.15
+    pub(crate) access_perm_desc: ffa_v1_1::memory_access_permission_descriptor,
+    /// Offset 4, length 4: Offset to the composite memory region descriptor to which the endpoint
+    /// access permissions apply. Offset must be calculated from the base address of the data
+    /// structure this descriptor is included in. An offset value of 0 indicates that the endpoint
+    /// access permissions apply to a memory region description identified by the Handle parameter
+    /// specified in the data structure that includes this one.
+    pub(crate) composite_offset: u32,
+    /// Offset 8, length 16: Implementation defined information
+    pub(crate) impdef_info: [u8; 16],
+    /// Offset 24, length 8: Reserved (MBZ)
+    pub(crate) reserved: u64,
+}
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.
diff --git a/src/partition_info.rs b/src/partition_info.rs
index 781f299..23e4c06 100644
--- a/src/partition_info.rs
+++ b/src/partition_info.rs
@@ -3,11 +3,21 @@
 
 //! Implementation of FF-A partition discovery data structures.
 
-use crate::ffa_v1_1::partition_info_descriptor;
 use thiserror::Error;
 use uuid::Uuid;
 use zerocopy::{FromBytes, IntoBytes};
 
+// This module uses FF-A v1.1 types by default.
+// FF-A v1.2 specified some previously reserved bits in the partition info properties field, but
+// this doesn't change the descriptor format.
+use crate::{ffa_v1_1::partition_info_descriptor, Version};
+
+// Sanity check to catch if the descriptor format is changed.
+const _: () = assert!(
+    size_of::<crate::ffa_v1_1::partition_info_descriptor>()
+        == size_of::<crate::ffa_v1_2::partition_info_descriptor>()
+);
+
 /// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
 /// with the `FFA_ERROR` interface.
 #[derive(Debug, Error)]
@@ -55,6 +65,12 @@
     pub support_direct_req_rec: bool,
     /// The partition can send direct requests.
     pub support_direct_req_send: bool,
+    /// The partition supports receipt of direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+    /// Added in FF-A v1.2
+    pub support_direct_req2_rec: Option<bool>,
+    /// The partition can send direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
+    /// Added in FF-A v1.2
+    pub support_direct_req2_send: Option<bool>,
     /// The partition can send and receive indirect messages.
     pub support_indirect_msg: bool,
     /// The partition supports receipt of notifications.
@@ -75,113 +91,134 @@
     const SUBSCRIBE_VM_CREATED_SHIFT: usize = 6;
     const SUBSCRIBE_VM_DESTROYED_SHIFT: usize = 7;
     const IS_AARCH64_SHIFT: usize = 8;
+    const SUPPORT_DIRECT_REQ2_REC_SHIFT: usize = 9;
+    const SUPPORT_DIRECT_REQ2_SEND_SHIFT: usize = 10;
 }
 
-struct PartPropWrapper(PartitionIdType, PartitionProperties);
+fn create_partition_properties(
+    version: Version,
+    id_type: PartitionIdType,
+    properties: PartitionProperties,
+) -> (u32, u16) {
+    let exec_ctx_count_or_proxy_id = match id_type {
+        PartitionIdType::PeEndpoint {
+            execution_ctx_count,
+        } => execution_ctx_count,
+        PartitionIdType::SepidIndep => 0,
+        PartitionIdType::SepidDep { proxy_endpoint_id } => proxy_endpoint_id,
+        PartitionIdType::Aux => 0,
+    };
 
-impl From<PartPropWrapper> for (u32, u16) {
-    fn from(value: PartPropWrapper) -> Self {
-        let exec_ctx_count_or_proxy_id = match value.0 {
-            PartitionIdType::PeEndpoint {
-                execution_ctx_count,
-            } => execution_ctx_count,
-            PartitionIdType::SepidIndep => 0,
-            PartitionIdType::SepidDep { proxy_endpoint_id } => proxy_endpoint_id,
-            PartitionIdType::Aux => 0,
-        };
+    let mut prop_bits = match id_type {
+        PartitionIdType::PeEndpoint { .. } => {
+            let mut p = PartitionIdType::PE_ENDPOINT << PartitionIdType::SHIFT;
 
-        let mut props = match value.0 {
-            PartitionIdType::PeEndpoint { .. } => {
-                let mut p = PartitionIdType::PE_ENDPOINT << PartitionIdType::SHIFT;
-
-                if value.1.support_direct_req_rec {
-                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT;
-                    if value.1.subscribe_vm_created {
-                        // TODO: how to handle if ABI is invoked at NS phys instance?
-                        p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
-                    }
-                    if value.1.subscribe_vm_destroyed {
-                        // TODO: how to handle if ABI is invoked at NS phys instance?
-                        p |= 1 << PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT
-                    }
+            if properties.support_direct_req_rec {
+                p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT;
+                if properties.subscribe_vm_created {
+                    // TODO: how to handle if ABI is invoked at NS phys instance?
+                    p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
                 }
-                if value.1.support_direct_req_send {
-                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT
+                if properties.subscribe_vm_destroyed {
+                    // TODO: how to handle if ABI is invoked at NS phys instance?
+                    p |= 1 << PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT
                 }
-                if value.1.support_indirect_msg {
-                    p |= 1 << PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT
-                }
-                if value.1.support_notif_rec {
-                    p |= 1 << PartitionProperties::SUPPORT_NOTIF_REC_SHIFT
-                }
-
-                p
             }
-            PartitionIdType::SepidIndep => PartitionIdType::SEPID_INDEP << PartitionIdType::SHIFT,
-            PartitionIdType::SepidDep { .. } => {
-                PartitionIdType::SEPID_DEP << PartitionIdType::SHIFT
-            }
-            PartitionIdType::Aux => PartitionIdType::AUX << PartitionIdType::SHIFT,
-        };
 
-        if value.1.is_aarch64 {
-            props |= 1 << PartitionProperties::IS_AARCH64_SHIFT
+            if properties.support_direct_req_send {
+                p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT
+            }
+
+            // For v1.2 and later it's mandatory to specify these properties
+            if version >= Version(1, 2) {
+                if properties.support_direct_req2_rec.unwrap() {
+                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT
+                }
+
+                if properties.support_direct_req2_send.unwrap() {
+                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT
+                }
+            }
+
+            if properties.support_indirect_msg {
+                p |= 1 << PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT
+            }
+
+            if properties.support_notif_rec {
+                p |= 1 << PartitionProperties::SUPPORT_NOTIF_REC_SHIFT
+            }
+
+            p
         }
+        PartitionIdType::SepidIndep => PartitionIdType::SEPID_INDEP << PartitionIdType::SHIFT,
+        PartitionIdType::SepidDep { .. } => PartitionIdType::SEPID_DEP << PartitionIdType::SHIFT,
+        PartitionIdType::Aux => PartitionIdType::AUX << PartitionIdType::SHIFT,
+    };
 
-        (props, exec_ctx_count_or_proxy_id)
+    if properties.is_aarch64 {
+        prop_bits |= 1 << PartitionProperties::IS_AARCH64_SHIFT
     }
+
+    (prop_bits, exec_ctx_count_or_proxy_id)
 }
 
-impl From<(u32, u16)> for PartPropWrapper {
-    fn from(value: (u32, u16)) -> Self {
-        let part_id_type = match (value.0 >> PartitionIdType::SHIFT) & PartitionIdType::MASK {
-            PartitionIdType::PE_ENDPOINT => PartitionIdType::PeEndpoint {
-                execution_ctx_count: value.1,
-            },
-            PartitionIdType::SEPID_INDEP => PartitionIdType::SepidIndep,
-            PartitionIdType::SEPID_DEP => PartitionIdType::SepidDep {
-                proxy_endpoint_id: value.1,
-            },
-            PartitionIdType::AUX => PartitionIdType::Aux,
-            _ => panic!(), // The match is exhaustive for a 2-bit value
-        };
+fn parse_partition_properties(
+    version: Version,
+    prop_bits: u32,
+    id_type: u16,
+) -> (PartitionIdType, PartitionProperties) {
+    let part_id_type = match (prop_bits >> PartitionIdType::SHIFT) & PartitionIdType::MASK {
+        PartitionIdType::PE_ENDPOINT => PartitionIdType::PeEndpoint {
+            execution_ctx_count: id_type,
+        },
+        PartitionIdType::SEPID_INDEP => PartitionIdType::SepidIndep,
+        PartitionIdType::SEPID_DEP => PartitionIdType::SepidDep {
+            proxy_endpoint_id: id_type,
+        },
+        PartitionIdType::AUX => PartitionIdType::Aux,
+        _ => panic!(), // The match is exhaustive for a 2-bit value
+    };
 
-        let mut part_props = PartitionProperties::default();
+    let mut part_props = PartitionProperties::default();
 
-        if (value.0 >> PartitionIdType::SHIFT) & PartitionIdType::MASK
-            == PartitionIdType::PE_ENDPOINT
-        {
-            if (value.0 >> PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT) & 0b1 == 1 {
-                part_props.support_direct_req_rec = true;
+    if matches!(part_id_type, PartitionIdType::PeEndpoint { .. }) {
+        if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT) & 0b1 == 1 {
+            part_props.support_direct_req_rec = true;
 
-                if (value.0 >> PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT) & 0b1 == 1 {
-                    part_props.subscribe_vm_created = true;
-                }
-
-                if (value.0 >> PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT) & 0b1 == 1 {
-                    part_props.subscribe_vm_destroyed = true;
-                }
+            if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT) & 0b1 == 1 {
+                part_props.subscribe_vm_created = true;
             }
 
-            if (value.0 >> PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT) & 0b1 == 1 {
-                part_props.support_direct_req_send = true;
-            }
-
-            if (value.0 >> PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT) & 0b1 == 1 {
-                part_props.support_indirect_msg = true;
-            }
-
-            if (value.0 >> PartitionProperties::SUPPORT_NOTIF_REC_SHIFT) & 0b1 == 1 {
-                part_props.support_notif_rec = true;
+            if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT) & 0b1 == 1 {
+                part_props.subscribe_vm_destroyed = true;
             }
         }
 
-        if (value.0 >> PartitionProperties::IS_AARCH64_SHIFT) & 0b1 == 1 {
-            part_props.is_aarch64 = true;
+        if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT) & 0b1 == 1 {
+            part_props.support_direct_req_send = true;
         }
 
-        PartPropWrapper(part_id_type, part_props)
+        if version >= Version(1, 2) {
+            part_props.support_direct_req2_rec =
+                Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT) & 0b1 == 1);
+            part_props.support_direct_req2_send =
+                Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT) & 0b1 == 1);
+        }
+
+        if (prop_bits >> PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT) & 0b1 == 1 {
+            part_props.support_indirect_msg = true;
+        }
+
+        if (prop_bits >> PartitionProperties::SUPPORT_NOTIF_REC_SHIFT) & 0b1 == 1 {
+            part_props.support_notif_rec = true;
+        }
     }
+
+    if (prop_bits >> PartitionProperties::IS_AARCH64_SHIFT) & 0b1 == 1 {
+        part_props.is_aarch64 = true;
+    }
+
+    (part_id_type, part_props)
 }
 
 /// Partition information descriptor, returned by the `FFA_PARTITION_INFO_GET` interface.
@@ -194,11 +231,13 @@
 }
 
 impl PartitionInfo {
-    const DESC_SIZE: usize = size_of::<partition_info_descriptor>();
+    pub const DESC_SIZE: usize = size_of::<partition_info_descriptor>();
 
     /// Serialize a list of partition information descriptors into a buffer. The `fill_uuid`
     /// parameter controls whether the UUID field of the descriptor will be filled.
-    pub fn pack(descriptors: &[PartitionInfo], buf: &mut [u8], fill_uuid: bool) {
+    pub fn pack(version: Version, descriptors: &[PartitionInfo], buf: &mut [u8], fill_uuid: bool) {
+        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
+
         let mut offset = 0;
 
         for desc in descriptors {
@@ -210,7 +249,7 @@
             (
                 desc_raw.partition_props,
                 desc_raw.exec_ctx_count_or_proxy_id,
-            ) = PartPropWrapper(desc.partition_id_type, desc.props).into();
+            ) = create_partition_properties(version, desc.partition_id_type, desc.props);
 
             if fill_uuid {
                 desc_raw.uuid.copy_from_slice(desc.uuid.as_bytes());
@@ -224,6 +263,7 @@
 
 /// Iterator of partition information descriptors.
 pub struct PartitionInfoIterator<'a> {
+    version: Version,
     buf: &'a [u8],
     offset: usize,
     count: usize,
@@ -231,7 +271,9 @@
 
 impl<'a> PartitionInfoIterator<'a> {
     /// Create an iterator of partition information descriptors from a buffer.
-    pub fn new(buf: &'a [u8], count: usize) -> Result<Self, Error> {
+    pub fn new(version: Version, buf: &'a [u8], count: usize) -> Result<Self, Error> {
+        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
+
         let Some(total_size) = count.checked_mul(PartitionInfo::DESC_SIZE) else {
             return Err(Error::InvalidBufferSize);
         };
@@ -241,6 +283,7 @@
         }
 
         Ok(Self {
+            version,
             buf,
             offset: 0,
             count,
@@ -265,18 +308,19 @@
 
             let partition_id = desc_raw.partition_id;
 
-            let wrapper = PartPropWrapper::from((
+            let (partition_id_type, props) = parse_partition_properties(
+                self.version,
                 desc_raw.partition_props,
                 desc_raw.exec_ctx_count_or_proxy_id,
-            ));
+            );
 
             let uuid = Uuid::from_bytes(desc_raw.uuid);
 
             let desc = PartitionInfo {
                 uuid,
                 partition_id,
-                partition_id_type: wrapper.0,
-                props: wrapper.1,
+                partition_id_type,
+                props,
             };
 
             return Some(Ok(desc));
@@ -309,6 +353,8 @@
                 subscribe_vm_created: true,
                 subscribe_vm_destroyed: true,
                 is_aarch64: true,
+                support_direct_req2_rec: Some(true),
+                support_direct_req2_send: Some(true),
             },
         };
 
@@ -324,13 +370,15 @@
                 subscribe_vm_created: false,
                 subscribe_vm_destroyed: false,
                 is_aarch64: true,
+                support_direct_req2_rec: None,
+                support_direct_req2_send: None,
             },
         };
 
         let mut buf = [0u8; 0xff];
-        PartitionInfo::pack(&[desc1, desc2], &mut buf, true);
+        PartitionInfo::pack(Version(1, 2), &[desc1, desc2], &mut buf, true);
 
-        let mut descriptors = PartitionInfoIterator::new(&buf, 2).unwrap();
+        let mut descriptors = PartitionInfoIterator::new(Version(1, 2), &buf, 2).unwrap();
         let desc1_check = descriptors.next().unwrap().unwrap();
         let desc2_check = descriptors.next().unwrap().unwrap();