Fix page count handling in FFA_MEM_PERM_GET

FF-A v1.2 and lower doesn't support passing a page count argument in the
FFA_MEM_PERM_GET interface. The corresponding register value MBZ, but
the actual number of pages affected by the call is always 1. Starting
from FF-A v1.3 an arbitrary page count is allowed, but to keep backwards
compatibility the actual register value is offset by -1. Add this logic
to the Interface type.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ia4539210544e34016472bf7b069cc20bcd761151
diff --git a/src/lib.rs b/src/lib.rs
index ee3ed71..e96eb41 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1338,7 +1338,11 @@
     },
     MemPermGet {
         addr: MemAddr,
-        page_cnt: Option<u32>,
+        /// The actual number of pages queried by the call.  It is calculated by adding one to the
+        /// corresponding register's value, i.e. zero in the register means one page. For FF-A v1.2
+        /// and lower the register value MBZ, so the page count is always 1. For higher versions the
+        /// page count can be any nonzero value.
+        page_cnt: u32,
     },
     MemPermSet {
         addr: MemAddr,
@@ -1846,22 +1850,34 @@
                 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
                 flags: (regs[3] as u32).try_into()?,
             },
-            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::MemPermGet32 => {
+                if (version <= Version(1, 2) && regs[2] != 0)
+                    || (regs[2] as u32).checked_add(1).is_none()
+                {
+                    return Err(Error::MemoryManagementError(
+                        memory_management::Error::InvalidPageCount,
+                    ));
+                }
+
+                Self::MemPermGet {
+                    addr: MemAddr::Addr32(regs[1] as u32),
+                    page_cnt: regs[2] as u32 + 1,
+                }
+            }
+            FuncId::MemPermGet64 => {
+                if (version <= Version(1, 2) && regs[2] != 0)
+                    || (regs[2] as u32).checked_add(1).is_none()
+                {
+                    return Err(Error::MemoryManagementError(
+                        memory_management::Error::InvalidPageCount,
+                    ));
+                }
+
+                Self::MemPermGet {
+                    addr: MemAddr::Addr64(regs[1]),
+                    page_cnt: regs[2] as u32 + 1,
+                }
+            }
             FuncId::MemPermSet32 => Self::MemPermSet {
                 addr: MemAddr::Addr32(regs[1] as u32),
                 page_cnt: regs[2] as u32,
@@ -2320,11 +2336,12 @@
                     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());
+                a[2] = if version <= Version(1, 2) {
+                    assert_eq!(page_cnt, 1);
                     0
+                } else {
+                    assert_ne!(page_cnt, 0);
+                    (page_cnt - 1).into()
                 }
             }
             Interface::MemPermSet {
@@ -2751,4 +2768,97 @@
         assert_eq!(Some((0x0002, &[][..])), iter.next());
         assert_eq!(Some((0x0003, &[1][..])), iter.next());
     }
+
+    #[test]
+    fn mem_perm_get_pack() {
+        let mut expected_regs = [0u64; 18];
+        let mut out_regs = [0u64; 18];
+
+        expected_regs[0] = u32::from(FuncId::MemPermGet32).into();
+        expected_regs[1] = 0xabcd;
+        expected_regs[2] = 5;
+
+        Interface::MemPermGet {
+            addr: MemAddr::Addr32(0xabcd),
+            page_cnt: 6,
+        }
+        .to_regs(Version(1, 3), &mut out_regs);
+
+        assert_eq!(expected_regs, out_regs);
+
+        expected_regs[2] = 0;
+
+        Interface::MemPermGet {
+            addr: MemAddr::Addr32(0xabcd),
+            page_cnt: 1,
+        }
+        .to_regs(Version(1, 2), &mut out_regs);
+
+        assert_eq!(expected_regs, out_regs);
+    }
+
+    #[test]
+    #[should_panic]
+    fn mem_perm_get_pack_fail1() {
+        let mut out_regs = [0u64; 18];
+        Interface::MemPermGet {
+            addr: MemAddr::Addr32(0xabcd),
+            page_cnt: 2,
+        }
+        .to_regs(Version(1, 2), &mut out_regs);
+    }
+
+    #[test]
+    #[should_panic]
+    fn mem_perm_get_pack_fail2() {
+        let mut out_regs = [0u64; 18];
+        Interface::MemPermGet {
+            addr: MemAddr::Addr32(0xabcd),
+            page_cnt: 0,
+        }
+        .to_regs(Version(1, 3), &mut out_regs);
+    }
+
+    #[test]
+    fn mem_perm_get_unpack() {
+        let mut in_regs = [0u64; 18];
+
+        in_regs[0] = u32::from(FuncId::MemPermGet32).into();
+        in_regs[1] = 0xabcd;
+        in_regs[2] = 5;
+
+        assert_eq!(
+            Interface::from_regs(Version(1, 3), &in_regs),
+            Ok(Interface::MemPermGet {
+                addr: MemAddr::Addr32(0xabcd),
+                page_cnt: 6,
+            }),
+        );
+
+        assert_eq!(
+            Interface::from_regs(Version(1, 2), &in_regs),
+            Err(Error::MemoryManagementError(
+                memory_management::Error::InvalidPageCount
+            )),
+        );
+
+        in_regs[2] = 0;
+
+        assert_eq!(
+            Interface::from_regs(Version(1, 2), &in_regs),
+            Ok(Interface::MemPermGet {
+                addr: MemAddr::Addr32(0xabcd),
+                page_cnt: 1,
+            }),
+        );
+
+        in_regs[2] = u32::MAX.into();
+
+        assert_eq!(
+            Interface::from_regs(Version(1, 3), &in_regs),
+            Err(Error::MemoryManagementError(
+                memory_management::Error::InvalidPageCount
+            )),
+        );
+    }
 }