1use thiserror::Error;
7use uuid::Uuid;
8use zerocopy::{FromBytes, IntoBytes, transmute};
9
10use crate::{
14 UuidHelper, Version, ffa_v1_1::partition_info_descriptor, interface_args::SuccessArgs,
15};
16
17const _: () = assert!(
19 size_of::<crate::ffa_v1_1::partition_info_descriptor>()
20 == size_of::<crate::ffa_v1_2::partition_info_descriptor>()
21);
22
23#[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum PartitionIdType {
46 PeEndpoint { execution_ctx_count: u16 },
49 SepidIndep,
51 SepidDep { proxy_endpoint_id: u16 },
54 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
69pub struct PartitionProperties {
70 pub support_direct_req_rec: bool,
72 pub support_direct_req_send: bool,
74 pub support_direct_req2_rec: Option<bool>,
77 pub support_direct_req2_send: Option<bool>,
80 pub support_indirect_msg: bool,
82 pub support_notif_rec: bool,
84 pub subscribe_vm_created: bool,
86 pub subscribe_vm_destroyed: bool,
88 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 p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
127 }
128 if properties.subscribe_vm_destroyed {
129 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 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!(), };
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#[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 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#[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#[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#[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 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 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 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
410pub struct PartitionInfoIterator<'a> {
412 version: Version,
413 buf: &'a [u8],
414 offset: usize,
415 count: usize,
416}
417
418impl<'a> PartitionInfoIterator<'a> {
419 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 #[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 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, 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, ];
542
543 assert_eq!(buf[0..expected.len()], expected);
544 }
545}