Add Notification related interfaces

Change-Id: I913242440c9081d8ee960592fc98a7e460518811
Signed-off-by: Tomás González <tomasagustin.gonzalezorlando@arm.com>
diff --git a/src/lib.rs b/src/lib.rs
index 92da318..308760a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -43,6 +43,10 @@
     InvalidVersion(u32),
     #[error("Invalid Information Tag {0}")]
     InvalidInformationTag(u16),
+    #[error("Invalid Flag for Notification Set")]
+    InvalidNotificationSetFlag(u32),
+    #[error("Invalid Vm ID")]
+    InvalidVmId(u32),
 }
 
 impl From<Error> for FfaError {
@@ -57,6 +61,8 @@
             | Error::InvalidVersion(_)
             | Error::InvalidMsgWaitFlag(_)
             | Error::UnrecognisedVmAvailabilityStatus(_)
+            | Error::InvalidNotificationSetFlag(_)
+            | Error::InvalidVmId(_)
             | Error::UnrecognisedWarmBootType(_) => Self::InvalidParameters,
         }
     }
@@ -477,6 +483,135 @@
     Reg64([u64; 16]),
 }
 
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct NotificationBindFlags {
+    per_vcpu_notification: bool,
+}
+
+impl NotificationBindFlags {
+    const PER_VCPU_NOTIFICATION: u32 = 1;
+}
+
+impl From<NotificationBindFlags> for u32 {
+    fn from(flags: NotificationBindFlags) -> Self {
+        let mut bits: u32 = 0;
+        if flags.per_vcpu_notification {
+            bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
+        }
+        bits
+    }
+}
+
+impl From<u32> for NotificationBindFlags {
+    fn from(flags: u32) -> Self {
+        Self {
+            per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
+        }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct NotificationSetFlags {
+    delay_schedule_receiver: bool,
+    vcpu_id: Option<u16>,
+}
+
+impl NotificationSetFlags {
+    const PER_VCP_NOTIFICATION: u32 = 1 << 0;
+    const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
+    const VCPU_ID_SHIFT: u32 = 16;
+
+    const MBZ_BITS: u32 = 0xfffc;
+}
+
+impl From<NotificationSetFlags> for u32 {
+    fn from(flags: NotificationSetFlags) -> Self {
+        let mut bits: u32 = 0;
+
+        if flags.delay_schedule_receiver {
+            bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
+        }
+        if let Some(vcpu_id) = flags.vcpu_id {
+            bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
+            bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
+        }
+
+        bits
+    }
+}
+
+impl TryFrom<u32> for NotificationSetFlags {
+    type Error = Error;
+
+    fn try_from(flags: u32) -> Result<Self, Self::Error> {
+        if (flags & Self::MBZ_BITS) != 0 {
+            return Err(Error::InvalidNotificationSetFlag(flags));
+        }
+
+        let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
+
+        let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
+            Some(tentative_vcpu_id)
+        } else {
+            if tentative_vcpu_id != 0 {
+                return Err(Error::InvalidNotificationSetFlag(flags));
+            }
+            None
+        };
+
+        Ok(Self {
+            delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
+            vcpu_id,
+        })
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct NotificationGetFlags {
+    sp_bitmap_id: bool,
+    vm_bitmap_id: bool,
+    spm_bitmap_id: bool,
+    hyp_bitmap_id: bool,
+}
+
+impl NotificationGetFlags {
+    const SP_BITMAP_ID: u32 = 1;
+    const VM_BITMAP_ID: u32 = 1 << 1;
+    const SPM_BITMAP_ID: u32 = 1 << 2;
+    const HYP_BITMAP_ID: u32 = 1 << 3;
+}
+
+impl From<NotificationGetFlags> for u32 {
+    fn from(flags: NotificationGetFlags) -> Self {
+        let mut bits: u32 = 0;
+        if flags.sp_bitmap_id {
+            bits |= NotificationGetFlags::SP_BITMAP_ID;
+        }
+        if flags.vm_bitmap_id {
+            bits |= NotificationGetFlags::VM_BITMAP_ID;
+        }
+        if flags.spm_bitmap_id {
+            bits |= NotificationGetFlags::SPM_BITMAP_ID;
+        }
+        if flags.hyp_bitmap_id {
+            bits |= NotificationGetFlags::HYP_BITMAP_ID;
+        }
+        bits
+    }
+}
+
+impl From<u32> for NotificationGetFlags {
+    // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
+    fn from(flags: u32) -> Self {
+        Self {
+            sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
+            vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
+            spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
+            hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
+        }
+    }
+}
+
 /// FF-A "message types", the terminology used by the spec is "interfaces".
 ///
 /// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
@@ -607,6 +742,38 @@
         char_cnt: u8,
         char_lists: ConsoleLogChars,
     },
+    NotificationBitmapCreate {
+        vm_id: u16,
+        vcpu_cnt: u32,
+    },
+    NotificationBitmapDestroy {
+        vm_id: u16,
+    },
+    NotificationBind {
+        sender_id: u16,
+        receiver_id: u16,
+        flags: NotificationBindFlags,
+        bitmap: u64,
+    },
+    NotificationUnBind {
+        sender_id: u16,
+        receiver_id: u16,
+        bitmap: u64,
+    },
+    NotificationSet {
+        sender_id: u16,
+        receiver_id: u16,
+        flags: NotificationSetFlags,
+        bitmap: u64,
+    },
+    NotificationGet {
+        vcpu_id: u16,
+        endpoint_id: u16,
+        flags: NotificationGetFlags,
+    },
+    NotificationInfoGet {
+        is_32bit: bool,
+    },
 }
 
 impl Interface {
@@ -695,6 +862,16 @@
                 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
                 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
             },
+            Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
+            Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
+            Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
+            Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
+            Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
+            Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
+            Interface::NotificationInfoGet { is_32bit } => match is_32bit {
+                true => Some(FuncId::NotificationInfoGet32),
+                false => Some(FuncId::NotificationInfoGet64),
+            },
         }
     }
 
@@ -1080,6 +1257,49 @@
                     regs[7] as u32,
                 ]),
             },
+            FuncId::NotificationBitmapCreate => {
+                let tentative_vm_id = regs[1] as u32;
+                if (tentative_vm_id >> 16) != 0 {
+                    return Err(Error::InvalidVmId(tentative_vm_id));
+                }
+                Self::NotificationBitmapCreate {
+                    vm_id: tentative_vm_id as u16,
+                    vcpu_cnt: regs[2] as u32,
+                }
+            }
+            FuncId::NotificationBitmapDestroy => {
+                let tentative_vm_id = regs[1] as u32;
+                if (tentative_vm_id >> 16) != 0 {
+                    return Err(Error::InvalidVmId(tentative_vm_id));
+                }
+                Self::NotificationBitmapDestroy {
+                    vm_id: tentative_vm_id as u16,
+                }
+            }
+            FuncId::NotificationBind => Self::NotificationBind {
+                sender_id: (regs[1] >> 16) as u16,
+                receiver_id: regs[1] as u16,
+                flags: (regs[2] as u32).into(),
+                bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
+            },
+            FuncId::NotificationUnbind => Self::NotificationUnBind {
+                sender_id: (regs[1] >> 16) as u16,
+                receiver_id: regs[1] as u16,
+                bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
+            },
+            FuncId::NotificationSet => Self::NotificationSet {
+                sender_id: (regs[1] >> 16) as u16,
+                receiver_id: regs[1] as u16,
+                flags: (regs[2] as u32).try_into()?,
+                bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
+            },
+            FuncId::NotificationGet => Self::NotificationGet {
+                vcpu_id: (regs[1] >> 16) as u16,
+                endpoint_id: regs[1] as u16,
+                flags: (regs[2] as u32).into(),
+            },
+            FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
+            FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
             _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
         };
 
@@ -1485,6 +1705,53 @@
                     _ => panic!("{:#x?} requires 18 registers", char_lists),
                 }
             }
+            Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
+                a[1] = vm_id.into();
+                a[2] = vcpu_cnt.into();
+            }
+            Interface::NotificationBitmapDestroy { vm_id } => {
+                a[1] = vm_id.into();
+            }
+            Interface::NotificationBind {
+                sender_id,
+                receiver_id,
+                flags,
+                bitmap,
+            } => {
+                a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
+                a[2] = u32::from(flags).into();
+                a[3] = bitmap & 0xffff_ffff;
+                a[4] = bitmap >> 32;
+            }
+            Interface::NotificationUnBind {
+                sender_id,
+                receiver_id,
+                bitmap,
+            } => {
+                a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
+                a[3] = bitmap & 0xffff_ffff;
+                a[4] = bitmap >> 32;
+            }
+            Interface::NotificationSet {
+                sender_id,
+                receiver_id,
+                flags,
+                bitmap,
+            } => {
+                a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
+                a[2] = u32::from(flags).into();
+                a[3] = bitmap & 0xffff_ffff;
+                a[4] = bitmap >> 32;
+            }
+            Interface::NotificationGet {
+                vcpu_id,
+                endpoint_id,
+                flags,
+            } => {
+                a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
+                a[2] = u32::from(flags).into();
+            }
+            Interface::NotificationInfoGet { .. } => {}
             _ => panic!("{:#x?} requires 18 registers", self),
         }
     }