Skip to main content

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