Skip to main content

arm_ffa/
notification.rs

1// SPDX-FileCopyrightText: Copyright The arm-ffa Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! FF-A notification data structures and functions.
5
6use crate::interface_args::SuccessArgs;
7use thiserror::Error;
8use zerocopy::transmute;
9
10/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
11/// with the `FFA_ERROR` interface.
12#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
13pub enum Error {
14    #[error("Invalid Flag for Notification Set")]
15    InvalidNotificationSetFlag(u32),
16    #[error("Invalid notification count")]
17    InvalidNotificationCount,
18}
19
20/// Flags field of the `FFA_NOTIFICATION_BIND` interface.
21#[derive(Debug, Eq, PartialEq, Clone, Copy)]
22pub struct NotificationBindFlags {
23    pub per_vcpu_notification: bool,
24}
25
26impl NotificationBindFlags {
27    const PER_VCPU_NOTIFICATION: u32 = 1;
28}
29
30impl From<NotificationBindFlags> for u32 {
31    fn from(flags: NotificationBindFlags) -> Self {
32        let mut bits: u32 = 0;
33        if flags.per_vcpu_notification {
34            bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
35        }
36        bits
37    }
38}
39
40impl From<u32> for NotificationBindFlags {
41    fn from(flags: u32) -> Self {
42        Self {
43            per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
44        }
45    }
46}
47
48/// Flags field of the `FFA_NOTIFICATION_SET` interface.
49#[derive(Debug, Eq, PartialEq, Clone, Copy)]
50pub struct NotificationSetFlags {
51    pub delay_schedule_receiver: bool,
52    pub vcpu_id: Option<u16>,
53}
54
55impl NotificationSetFlags {
56    const PER_VCP_NOTIFICATION: u32 = 1 << 0;
57    const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
58    const VCPU_ID_SHIFT: u32 = 16;
59
60    const MBZ_BITS: u32 = 0xfffc;
61}
62
63impl From<NotificationSetFlags> for u32 {
64    fn from(flags: NotificationSetFlags) -> Self {
65        let mut bits: u32 = 0;
66
67        if flags.delay_schedule_receiver {
68            bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
69        }
70        if let Some(vcpu_id) = flags.vcpu_id {
71            bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
72            bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
73        }
74
75        bits
76    }
77}
78
79impl TryFrom<u32> for NotificationSetFlags {
80    type Error = Error;
81
82    fn try_from(flags: u32) -> Result<Self, Self::Error> {
83        if (flags & Self::MBZ_BITS) != 0 {
84            return Err(Error::InvalidNotificationSetFlag(flags));
85        }
86
87        let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
88
89        let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
90            Some(tentative_vcpu_id)
91        } else {
92            if tentative_vcpu_id != 0 {
93                return Err(Error::InvalidNotificationSetFlag(flags));
94            }
95            None
96        };
97
98        Ok(Self {
99            delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
100            vcpu_id,
101        })
102    }
103}
104
105/// Flags field of the `FFA_NOTIFICATION_GET` interface.
106#[derive(Debug, Eq, PartialEq, Clone, Copy)]
107pub struct NotificationGetFlags {
108    pub sp_bitmap_id: bool,
109    pub vm_bitmap_id: bool,
110    pub spm_bitmap_id: bool,
111    pub hyp_bitmap_id: bool,
112}
113
114impl NotificationGetFlags {
115    const SP_BITMAP_ID: u32 = 1;
116    const VM_BITMAP_ID: u32 = 1 << 1;
117    const SPM_BITMAP_ID: u32 = 1 << 2;
118    const HYP_BITMAP_ID: u32 = 1 << 3;
119}
120
121impl From<NotificationGetFlags> for u32 {
122    fn from(flags: NotificationGetFlags) -> Self {
123        let mut bits: u32 = 0;
124        if flags.sp_bitmap_id {
125            bits |= NotificationGetFlags::SP_BITMAP_ID;
126        }
127        if flags.vm_bitmap_id {
128            bits |= NotificationGetFlags::VM_BITMAP_ID;
129        }
130        if flags.spm_bitmap_id {
131            bits |= NotificationGetFlags::SPM_BITMAP_ID;
132        }
133        if flags.hyp_bitmap_id {
134            bits |= NotificationGetFlags::HYP_BITMAP_ID;
135        }
136        bits
137    }
138}
139
140impl From<u32> for NotificationGetFlags {
141    // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
142    fn from(flags: u32) -> Self {
143        Self {
144            sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
145            vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
146            spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
147            hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
148        }
149    }
150}
151
152/// `FFA_NOTIFICATION_GET` specific success argument structure.
153#[derive(Debug, Eq, PartialEq, Clone, Copy)]
154pub struct SuccessArgsNotificationGet {
155    pub sp_notifications: Option<u64>,
156    pub vm_notifications: Option<u64>,
157    pub spm_notifications: Option<u32>,
158    pub hypervisor_notifications: Option<u32>,
159}
160
161impl From<SuccessArgsNotificationGet> for SuccessArgs {
162    fn from(value: SuccessArgsNotificationGet) -> Self {
163        let mut args = [0; 6];
164
165        if let Some(bitmap) = value.sp_notifications {
166            args[0] = bitmap as u32;
167            args[1] = (bitmap >> 32) as u32;
168        }
169
170        if let Some(bitmap) = value.vm_notifications {
171            args[2] = bitmap as u32;
172            args[3] = (bitmap >> 32) as u32;
173        }
174
175        if let Some(bitmap) = value.spm_notifications {
176            args[4] = bitmap;
177        }
178
179        if let Some(bitmap) = value.hypervisor_notifications {
180            args[5] = bitmap;
181        }
182
183        Self::Args32(args)
184    }
185}
186
187impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
188    type Error = crate::Error;
189
190    fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
191        let (flags, value) = value;
192        let args = value.try_get_args32()?;
193
194        let sp_notifications = if flags.sp_bitmap_id {
195            Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
196        } else {
197            None
198        };
199
200        let vm_notifications = if flags.vm_bitmap_id {
201            Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
202        } else {
203            None
204        };
205
206        let spm_notifications = if flags.spm_bitmap_id {
207            Some(args[4])
208        } else {
209            None
210        };
211
212        let hypervisor_notifications = if flags.hyp_bitmap_id {
213            Some(args[5])
214        } else {
215            None
216        };
217
218        Ok(Self {
219            sp_notifications,
220            vm_notifications,
221            spm_notifications,
222            hypervisor_notifications,
223        })
224    }
225}
226
227/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
228/// depends on the 32-bit or 64-bit packing.
229#[derive(Debug, Eq, PartialEq, Clone, Copy)]
230pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
231    pub more_pending_notifications: bool,
232    pub(crate) list_count: usize,
233    pub(crate) id_counts: [u8; MAX_COUNT],
234    pub(crate) ids: [u16; MAX_COUNT],
235}
236
237impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
238    fn default() -> Self {
239        Self {
240            more_pending_notifications: false,
241            list_count: 0,
242            id_counts: [0; MAX_COUNT],
243            ids: [0; MAX_COUNT],
244        }
245    }
246}
247
248impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
249    const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
250    const LIST_COUNT_SHIFT: usize = 7;
251    const LIST_COUNT_MASK: u64 = 0x1f;
252    const ID_COUNT_SHIFT: usize = 12;
253    const ID_COUNT_MASK: u64 = 0x03;
254    const ID_COUNT_BITS: usize = 2;
255
256    pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
257        if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
258            return Err(Error::InvalidNotificationCount);
259        }
260
261        // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
262        // of vCPU IDs is recorded in `id_counts`.
263        let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
264        if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
265            // The new list does not fit into the available space for IDs.
266            return Err(Error::InvalidNotificationCount);
267        }
268
269        self.id_counts[self.list_count] = vcpu_ids.len() as u8;
270        self.list_count += 1;
271
272        // The first ID is the endpoint ID.
273        self.ids[current_id_index] = endpoint;
274        current_id_index += 1;
275
276        // Insert the vCPU IDs.
277        self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
278
279        Ok(())
280    }
281
282    pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
283        NotificationInfoGetIterator {
284            list_index: 0,
285            id_index: 0,
286            id_count: &self.id_counts[0..self.list_count],
287            ids: &self.ids,
288        }
289    }
290
291    /// Pack flags field and IDs.
292    fn pack(self) -> (u64, [u16; MAX_COUNT]) {
293        let mut flags = if self.more_pending_notifications {
294            Self::MORE_PENDING_NOTIFICATIONS_FLAG
295        } else {
296            0
297        };
298
299        flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
300        for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
301            (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
302                .step_by(Self::ID_COUNT_BITS),
303        ) {
304            flags |= u64::from(*count) << shift;
305        }
306
307        (flags, self.ids)
308    }
309
310    /// Unpack flags field and IDs.
311    fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
312        let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
313
314        if count_of_lists > MAX_COUNT {
315            return Err(Error::InvalidNotificationCount);
316        }
317
318        let mut count_of_ids = [0; MAX_COUNT];
319        let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
320
321        for id in count_of_ids.iter_mut().take(count_of_lists) {
322            *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
323            count_of_ids_bits >>= Self::ID_COUNT_BITS;
324        }
325
326        let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
327        if id_field_count > MAX_COUNT {
328            return Err(Error::InvalidNotificationCount);
329        }
330
331        Ok(Self {
332            more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
333            list_count: count_of_lists,
334            id_counts: count_of_ids,
335            ids,
336        })
337    }
338}
339
340/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
341pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
342
343impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
344    fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
345        let (flags, ids) = value.pack();
346        let id_regs: [u32; 5] = transmute!(ids);
347
348        let mut args = [0; 6];
349        args[0] = flags as u32;
350        args[1..6].copy_from_slice(&id_regs);
351
352        SuccessArgs::Args32(args)
353    }
354}
355
356impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
357    type Error = crate::Error;
358
359    fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
360        let args = value.try_get_args32()?;
361        let flags = args[0].into();
362        let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
363        Self::unpack(flags, transmute!(id_regs)).map_err(|e| e.into())
364    }
365}
366
367/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
368pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
369
370impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
371    fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
372        let (flags, ids) = value.pack();
373        let id_regs: [u64; 5] = transmute!(ids);
374
375        let mut args = [0; 16];
376        args[0] = flags;
377        args[1..6].copy_from_slice(&id_regs);
378
379        SuccessArgs::Args64(args)
380    }
381}
382
383impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
384    type Error = crate::Error;
385
386    fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
387        let args = value.try_get_args64()?;
388        let flags = args[0];
389        let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
390        Self::unpack(flags, transmute!(id_regs)).map_err(|e| e.into())
391    }
392}
393
394/// Iterator implementation for parsing the (partition ID, vCPU ID list) pairs of the `FFA_SUCCESS`
395/// of an `FFA_NOTIFICATION_INFO_GET` call.
396pub struct NotificationInfoGetIterator<'a> {
397    list_index: usize,
398    id_index: usize,
399    id_count: &'a [u8],
400    ids: &'a [u16],
401}
402
403impl<'a> Iterator for NotificationInfoGetIterator<'a> {
404    type Item = (u16, &'a [u16]);
405
406    fn next(&mut self) -> Option<Self::Item> {
407        if self.list_index < self.id_count.len() {
408            let partition_id = self.ids[self.id_index];
409            let id_range =
410                (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
411
412            self.id_index += 1 + self.id_count[self.list_index] as usize;
413            self.list_index += 1;
414
415            Some((partition_id, &self.ids[id_range]))
416        } else {
417            None
418        }
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    #[test]
427    fn success_args_notification_info_get32() {
428        let mut notifications = SuccessArgsNotificationInfoGet32::default();
429
430        // 16.7.1.1 Example usage
431        notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
432        notifications.add_list(0x0000, &[4, 6]).unwrap();
433        notifications.add_list(0x0002, &[]).unwrap();
434        notifications.add_list(0x0003, &[1]).unwrap();
435
436        let args: SuccessArgs = notifications.into();
437        assert_eq!(
438            SuccessArgs::Args32([
439                0x0004_b200,
440                0x0000_0000,
441                0x0003_0002,
442                0x0004_0000,
443                0x0002_0006,
444                0x0001_0003
445            ]),
446            args
447        );
448
449        let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
450        let mut iter = notifications.iter();
451        assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
452        assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
453        assert_eq!(Some((0x0002, &[][..])), iter.next());
454        assert_eq!(Some((0x0003, &[1][..])), iter.next());
455    }
456
457    #[test]
458    fn success_args_notification_info_get64() {
459        let mut notifications = SuccessArgsNotificationInfoGet64::default();
460
461        // 16.7.1.1 Example usage
462        notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
463        notifications.add_list(0x0000, &[4, 6]).unwrap();
464        notifications.add_list(0x0002, &[]).unwrap();
465        notifications.add_list(0x0003, &[1]).unwrap();
466
467        let args: SuccessArgs = notifications.into();
468        assert_eq!(
469            SuccessArgs::Args64([
470                0x0004_b200,
471                0x0003_0002_0000_0000,
472                0x0002_0006_0004_0000,
473                0x0000_0000_0001_0003,
474                0x0000_0000_0000_0000,
475                0x0000_0000_0000_0000,
476                0,
477                0,
478                0,
479                0,
480                0,
481                0,
482                0,
483                0,
484                0,
485                0
486            ]),
487            args
488        );
489
490        let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
491        let mut iter = notifications.iter();
492        assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
493        assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
494        assert_eq!(Some((0x0002, &[][..])), iter.next());
495        assert_eq!(Some((0x0003, &[1][..])), iter.next());
496    }
497}