arm_ffa/
partition_info.rs

1// SPDX-FileCopyrightText: Copyright The arm-ffa Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Implementation of FF-A partition discovery data structures.
5
6use thiserror::Error;
7use uuid::Uuid;
8use zerocopy::{transmute, FromBytes, IntoBytes};
9
10// This module uses FF-A v1.1 types by default.
11// FF-A v1.2 specified some previously reserved bits in the partition info properties field, but
12// this doesn't change the descriptor format.
13use crate::{
14    ffa_v1_1::partition_info_descriptor, PartitionInfoGetFlags, SuccessArgs, UuidHelper, Version,
15};
16
17// Sanity check to catch if the descriptor format is changed.
18const _: () = assert!(
19    size_of::<crate::ffa_v1_1::partition_info_descriptor>()
20        == size_of::<crate::ffa_v1_2::partition_info_descriptor>()
21);
22
23/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
24/// with the `FFA_ERROR` interface.
25#[derive(Debug, Error)]
26pub enum Error {
27    #[error("Invalid buffer size")]
28    InvalidBufferSize,
29    #[error("Malformed descriptor")]
30    MalformedDescriptor,
31}
32
33impl From<Error> for crate::FfaError {
34    fn from(_value: Error) -> Self {
35        Self::InvalidParameters
36    }
37}
38
39/// Type of partition identified by the partition ID.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum PartitionIdType {
42    /// Partition ID is a PE endpoint ID. Contains the number of execution contexts implemented by
43    /// this partition.
44    PeEndpoint { execution_ctx_count: u16 },
45    /// Partition ID is a SEPID for an independent peripheral device.
46    SepidIndep,
47    /// Partition ID is a SEPID for an dependent peripheral device. Contains the ID of the proxy
48    /// endpoint for a dependent peripheral device.
49    SepidDep { proxy_endpoint_id: u16 },
50    /// Partition ID is an auxiliary ID.
51    Aux,
52}
53
54impl PartitionIdType {
55    const SHIFT: usize = 4;
56    const MASK: u32 = 0b11;
57    const PE_ENDPOINT: u32 = 0b00;
58    const SEPID_INDEP: u32 = 0b01;
59    const SEPID_DEP: u32 = 0b10;
60    const AUX: u32 = 0b11;
61}
62
63/// Properties of a partition.
64#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
65pub struct PartitionProperties {
66    /// The partition supports receipt of direct requests.
67    pub support_direct_req_rec: bool,
68    /// The partition can send direct requests.
69    pub support_direct_req_send: bool,
70    /// The partition supports receipt of direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
71    /// Added in FF-A v1.2
72    pub support_direct_req2_rec: Option<bool>,
73    /// The partition can send direct requests via the FFA_MSG_SEND_DIRECT_REQ2 ABI.
74    /// Added in FF-A v1.2
75    pub support_direct_req2_send: Option<bool>,
76    /// The partition can send and receive indirect messages.
77    pub support_indirect_msg: bool,
78    /// The partition supports receipt of notifications.
79    pub support_notif_rec: bool,
80    /// The partition must be informed about each VM that is created by the Hypervisor.
81    pub subscribe_vm_created: bool,
82    /// The partition must be informed about each VM that is destroyed by the Hypervisor.
83    pub subscribe_vm_destroyed: bool,
84    /// The partition runs in the AArch64 execution state.
85    pub is_aarch64: bool,
86}
87
88impl PartitionProperties {
89    const SUPPORT_DIRECT_REQ_REC_SHIFT: usize = 0;
90    const SUPPORT_DIRECT_REQ_SEND_SHIFT: usize = 1;
91    const SUPPORT_INDIRECT_MSG_SHIFT: usize = 2;
92    const SUPPORT_NOTIF_REC_SHIFT: usize = 3;
93    const SUBSCRIBE_VM_CREATED_SHIFT: usize = 6;
94    const SUBSCRIBE_VM_DESTROYED_SHIFT: usize = 7;
95    const IS_AARCH64_SHIFT: usize = 8;
96    const SUPPORT_DIRECT_REQ2_REC_SHIFT: usize = 9;
97    const SUPPORT_DIRECT_REQ2_SEND_SHIFT: usize = 10;
98}
99
100fn create_partition_properties(
101    version: Version,
102    id_type: PartitionIdType,
103    properties: PartitionProperties,
104) -> (u32, u16) {
105    let exec_ctx_count_or_proxy_id = match id_type {
106        PartitionIdType::PeEndpoint {
107            execution_ctx_count,
108        } => execution_ctx_count,
109        PartitionIdType::SepidIndep => 0,
110        PartitionIdType::SepidDep { proxy_endpoint_id } => proxy_endpoint_id,
111        PartitionIdType::Aux => 0,
112    };
113
114    let mut prop_bits = match id_type {
115        PartitionIdType::PeEndpoint { .. } => {
116            let mut p = PartitionIdType::PE_ENDPOINT << PartitionIdType::SHIFT;
117
118            if properties.support_direct_req_rec {
119                p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT;
120                if properties.subscribe_vm_created {
121                    // TODO: how to handle if ABI is invoked at NS phys instance?
122                    p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
123                }
124                if properties.subscribe_vm_destroyed {
125                    // TODO: how to handle if ABI is invoked at NS phys instance?
126                    p |= 1 << PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT
127                }
128            }
129
130            if properties.support_direct_req_send {
131                p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT
132            }
133
134            // For v1.2 and later it's mandatory to specify these properties
135            if version >= Version(1, 2) {
136                if properties.support_direct_req2_rec.unwrap() {
137                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT
138                }
139
140                if properties.support_direct_req2_send.unwrap() {
141                    p |= 1 << PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT
142                }
143            }
144
145            if properties.support_indirect_msg {
146                p |= 1 << PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT
147            }
148
149            if properties.support_notif_rec {
150                p |= 1 << PartitionProperties::SUPPORT_NOTIF_REC_SHIFT
151            }
152
153            p
154        }
155        PartitionIdType::SepidIndep => PartitionIdType::SEPID_INDEP << PartitionIdType::SHIFT,
156        PartitionIdType::SepidDep { .. } => PartitionIdType::SEPID_DEP << PartitionIdType::SHIFT,
157        PartitionIdType::Aux => PartitionIdType::AUX << PartitionIdType::SHIFT,
158    };
159
160    if properties.is_aarch64 {
161        prop_bits |= 1 << PartitionProperties::IS_AARCH64_SHIFT
162    }
163
164    (prop_bits, exec_ctx_count_or_proxy_id)
165}
166
167fn parse_partition_properties(
168    version: Version,
169    prop_bits: u32,
170    id_type: u16,
171) -> (PartitionIdType, PartitionProperties) {
172    let part_id_type = match (prop_bits >> PartitionIdType::SHIFT) & PartitionIdType::MASK {
173        PartitionIdType::PE_ENDPOINT => PartitionIdType::PeEndpoint {
174            execution_ctx_count: id_type,
175        },
176        PartitionIdType::SEPID_INDEP => PartitionIdType::SepidIndep,
177        PartitionIdType::SEPID_DEP => PartitionIdType::SepidDep {
178            proxy_endpoint_id: id_type,
179        },
180        PartitionIdType::AUX => PartitionIdType::Aux,
181        _ => panic!(), // The match is exhaustive for a 2-bit value
182    };
183
184    let mut part_props = PartitionProperties::default();
185
186    if matches!(part_id_type, PartitionIdType::PeEndpoint { .. }) {
187        if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_REC_SHIFT) & 0b1 == 1 {
188            part_props.support_direct_req_rec = true;
189
190            if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT) & 0b1 == 1 {
191                part_props.subscribe_vm_created = true;
192            }
193
194            if (prop_bits >> PartitionProperties::SUBSCRIBE_VM_DESTROYED_SHIFT) & 0b1 == 1 {
195                part_props.subscribe_vm_destroyed = true;
196            }
197        }
198
199        if (prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ_SEND_SHIFT) & 0b1 == 1 {
200            part_props.support_direct_req_send = true;
201        }
202
203        if version >= Version(1, 2) {
204            part_props.support_direct_req2_rec =
205                Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_REC_SHIFT) & 0b1 == 1);
206            part_props.support_direct_req2_send =
207                Some((prop_bits >> PartitionProperties::SUPPORT_DIRECT_REQ2_SEND_SHIFT) & 0b1 == 1);
208        }
209
210        if (prop_bits >> PartitionProperties::SUPPORT_INDIRECT_MSG_SHIFT) & 0b1 == 1 {
211            part_props.support_indirect_msg = true;
212        }
213
214        if (prop_bits >> PartitionProperties::SUPPORT_NOTIF_REC_SHIFT) & 0b1 == 1 {
215            part_props.support_notif_rec = true;
216        }
217    }
218
219    if (prop_bits >> PartitionProperties::IS_AARCH64_SHIFT) & 0b1 == 1 {
220        part_props.is_aarch64 = true;
221    }
222
223    (part_id_type, part_props)
224}
225
226/// Partition information descriptor, returned by the `FFA_PARTITION_INFO_GET` interface.
227#[derive(Clone, Copy, Debug, PartialEq, Eq)]
228pub struct PartitionInfo {
229    pub uuid: Uuid,
230    pub partition_id: u16,
231    pub partition_id_type: PartitionIdType,
232    pub props: PartitionProperties,
233}
234
235impl PartitionInfo {
236    pub const DESC_SIZE: usize = size_of::<partition_info_descriptor>();
237
238    /// Serialize a list of partition information descriptors into a buffer. The `fill_uuid`
239    /// parameter controls whether the UUID field of the descriptor will be filled.
240    pub fn pack(version: Version, descriptors: &[PartitionInfo], buf: &mut [u8], fill_uuid: bool) {
241        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
242
243        let mut offset = 0;
244
245        for desc in descriptors {
246            let mut desc_raw = partition_info_descriptor {
247                partition_id: desc.partition_id,
248                ..Default::default()
249            };
250
251            (
252                desc_raw.partition_props,
253                desc_raw.exec_ctx_count_or_proxy_id,
254            ) = create_partition_properties(version, desc.partition_id_type, desc.props);
255
256            if fill_uuid {
257                desc_raw
258                    .uuid
259                    .copy_from_slice(&UuidHelper::to_bytes(desc.uuid));
260            }
261
262            desc_raw.write_to_prefix(&mut buf[offset..]).unwrap();
263            offset += Self::DESC_SIZE;
264        }
265    }
266}
267
268/// `FFA_PARTITION_INFO_GET` specific success args structure.
269#[derive(Debug, Eq, PartialEq, Clone, Copy)]
270pub struct SuccessArgsPartitionInfoGet {
271    pub count: u32,
272    pub size: Option<u32>,
273}
274
275impl From<SuccessArgsPartitionInfoGet> for SuccessArgs {
276    fn from(value: SuccessArgsPartitionInfoGet) -> Self {
277        SuccessArgs::Args32([value.count, value.size.unwrap_or(0), 0, 0, 0, 0])
278    }
279}
280
281impl TryFrom<(PartitionInfoGetFlags, SuccessArgs)> for SuccessArgsPartitionInfoGet {
282    type Error = crate::Error;
283
284    fn try_from(value: (PartitionInfoGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
285        let (flags, value) = value;
286        let args = value.try_get_args32()?;
287
288        let size = if !flags.count_only {
289            Some(args[1])
290        } else {
291            None
292        };
293
294        Ok(Self {
295            count: args[0],
296            size,
297        })
298    }
299}
300
301/// `FFA_PARTITION_INFO_GET_REGS` specific success args structure.
302#[derive(Debug, Eq, PartialEq, Clone, Copy)]
303pub struct SuccessArgsPartitionInfoGetRegs {
304    pub last_index: u16,
305    pub current_index: u16,
306    pub info_tag: u16,
307    pub descriptor_data: [u8; Self::DESCRIPTOR_REG_COUNT * 8],
308}
309
310impl SuccessArgsPartitionInfoGetRegs {
311    const DESCRIPTOR_REG_COUNT: usize = 15;
312    const LAST_INDEX_SHIFT: usize = 0;
313    const CURRENT_INDEX_SHIFT: usize = 16;
314    const INFO_TAG_SHIFT: usize = 32;
315    const SIZE_SHIFT: usize = 48;
316}
317
318impl From<SuccessArgsPartitionInfoGetRegs> for SuccessArgs {
319    fn from(value: SuccessArgsPartitionInfoGetRegs) -> Self {
320        let mut args = [0; 16];
321
322        args[0] = ((value.last_index as u64) << SuccessArgsPartitionInfoGetRegs::LAST_INDEX_SHIFT)
323            | ((value.current_index as u64)
324                << SuccessArgsPartitionInfoGetRegs::CURRENT_INDEX_SHIFT)
325            | ((value.info_tag as u64) << SuccessArgsPartitionInfoGetRegs::INFO_TAG_SHIFT)
326            | ((PartitionInfo::DESC_SIZE as u64) << SuccessArgsPartitionInfoGetRegs::SIZE_SHIFT);
327
328        let descriptor_regs: [u64; SuccessArgsPartitionInfoGetRegs::DESCRIPTOR_REG_COUNT] =
329            transmute!(value.descriptor_data);
330        args[1..].copy_from_slice(&descriptor_regs);
331
332        Self::Args64_2(args)
333    }
334}
335
336impl TryFrom<SuccessArgs> for SuccessArgsPartitionInfoGetRegs {
337    type Error = crate::Error;
338
339    fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
340        let args = value.try_get_args64_2()?;
341
342        // Validate size
343        let size = (args[0] >> Self::SIZE_SHIFT) as u16;
344        if size as usize != PartitionInfo::DESC_SIZE {
345            return Err(Self::Error::InvalidPartitionInfoGetRegsResponse);
346        }
347
348        // Validate inidices
349        let last_index = (args[0] >> Self::LAST_INDEX_SHIFT) as u16;
350        let current_index = (args[0] >> Self::CURRENT_INDEX_SHIFT) as u16;
351        if last_index < current_index {
352            return Err(Self::Error::InvalidPartitionInfoGetRegsResponse);
353        }
354
355        let info_tag = (args[0] >> Self::INFO_TAG_SHIFT) as u16;
356
357        // Convert registers into byte array
358        let descriptor_regs: [u64; SuccessArgsPartitionInfoGetRegs::DESCRIPTOR_REG_COUNT] =
359            args[1..].try_into().unwrap();
360        let descriptor_data = transmute!(descriptor_regs);
361
362        Ok(Self {
363            last_index,
364            current_index,
365            info_tag,
366            descriptor_data,
367        })
368    }
369}
370
371/// Iterator of partition information descriptors.
372pub struct PartitionInfoIterator<'a> {
373    version: Version,
374    buf: &'a [u8],
375    offset: usize,
376    count: usize,
377}
378
379impl<'a> PartitionInfoIterator<'a> {
380    /// Create an iterator of partition information descriptors from a buffer.
381    pub fn new(version: Version, buf: &'a [u8], count: usize) -> Result<Self, Error> {
382        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
383
384        let Some(total_size) = count.checked_mul(PartitionInfo::DESC_SIZE) else {
385            return Err(Error::InvalidBufferSize);
386        };
387
388        if buf.len() < total_size {
389            return Err(Error::InvalidBufferSize);
390        }
391
392        Ok(Self {
393            version,
394            buf,
395            offset: 0,
396            count,
397        })
398    }
399}
400
401impl Iterator for PartitionInfoIterator<'_> {
402    type Item = Result<PartitionInfo, Error>;
403
404    fn next(&mut self) -> Option<Self::Item> {
405        if self.count > 0 {
406            let offset = self.offset;
407            self.offset += PartitionInfo::DESC_SIZE;
408            self.count -= 1;
409
410            let Ok(desc_raw) = partition_info_descriptor::ref_from_bytes(
411                &self.buf[offset..offset + PartitionInfo::DESC_SIZE],
412            ) else {
413                return Some(Err(Error::MalformedDescriptor));
414            };
415
416            let partition_id = desc_raw.partition_id;
417
418            let (partition_id_type, props) = parse_partition_properties(
419                self.version,
420                desc_raw.partition_props,
421                desc_raw.exec_ctx_count_or_proxy_id,
422            );
423
424            let uuid = UuidHelper::from_bytes(desc_raw.uuid);
425
426            let desc = PartitionInfo {
427                uuid,
428                partition_id,
429                partition_id_type,
430                props,
431            };
432
433            return Some(Ok(desc));
434        }
435
436        None
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443    use uuid::uuid;
444
445    // TODO: add tests with a known correct partition info blob
446
447    #[test]
448    fn part_info() {
449        let desc1 = PartitionInfo {
450            uuid: uuid!("12345678-1234-1234-1234-123456789abc"),
451            partition_id: 0x8001,
452            partition_id_type: PartitionIdType::PeEndpoint {
453                execution_ctx_count: 1,
454            },
455            props: PartitionProperties {
456                support_direct_req_rec: true,
457                support_direct_req_send: true,
458                support_indirect_msg: false,
459                support_notif_rec: false,
460                subscribe_vm_created: true,
461                subscribe_vm_destroyed: true,
462                is_aarch64: true,
463                support_direct_req2_rec: Some(true),
464                support_direct_req2_send: Some(true),
465            },
466        };
467
468        let desc2 = PartitionInfo {
469            uuid: uuid!("abcdef00-abcd-dcba-1234-abcdef012345"),
470            partition_id: 0x8002,
471            partition_id_type: PartitionIdType::SepidIndep,
472            props: PartitionProperties {
473                support_direct_req_rec: false,
474                support_direct_req_send: false,
475                support_indirect_msg: false,
476                support_notif_rec: false,
477                subscribe_vm_created: false,
478                subscribe_vm_destroyed: false,
479                is_aarch64: true,
480                support_direct_req2_rec: None,
481                support_direct_req2_send: None,
482            },
483        };
484
485        let mut buf = [0u8; 0xff];
486        PartitionInfo::pack(Version(1, 2), &[desc1, desc2], &mut buf, true);
487
488        let mut descriptors = PartitionInfoIterator::new(Version(1, 2), &buf, 2).unwrap();
489        let desc1_check = descriptors.next().unwrap().unwrap();
490        let desc2_check = descriptors.next().unwrap().unwrap();
491
492        assert_eq!(desc1, desc1_check);
493        assert_eq!(desc2, desc2_check);
494
495        let expected = [
496            // Desc1
497            0x01, 0x80, 0x01, 0x00, 0xc3, 0x07, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34,
498            0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // End of Desc1
499            // Desc2
500            0x02, 0x80, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0xab, 0xcd, 0xef, 0x00, 0xab, 0xcd,
501            0xdc, 0xba, 0x12, 0x34, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, // End of Desc2
502        ];
503
504        assert_eq!(buf[0..expected.len()], expected);
505    }
506}