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
+ )),
+ );
+ }
}