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