1use thiserror::Error;
7use uuid::Uuid;
8use zerocopy::{transmute, FromBytes, IntoBytes};
9
10use crate::{
14 ffa_v1_1::partition_info_descriptor, PartitionInfoGetFlags, SuccessArgs, UuidHelper, Version,
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)]
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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum PartitionIdType {
42 PeEndpoint { execution_ctx_count: u16 },
45 SepidIndep,
47 SepidDep { proxy_endpoint_id: u16 },
50 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
65pub struct PartitionProperties {
66 pub support_direct_req_rec: bool,
68 pub support_direct_req_send: bool,
70 pub support_direct_req2_rec: Option<bool>,
73 pub support_direct_req2_send: Option<bool>,
76 pub support_indirect_msg: bool,
78 pub support_notif_rec: bool,
80 pub subscribe_vm_created: bool,
82 pub subscribe_vm_destroyed: bool,
84 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 p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
123 }
124 if properties.subscribe_vm_destroyed {
125 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 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!(), };
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#[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 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#[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#[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 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 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 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
371pub struct PartitionInfoIterator<'a> {
373 version: Version,
374 buf: &'a [u8],
375 offset: usize,
376 count: usize,
377}
378
379impl<'a> PartitionInfoIterator<'a> {
380 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 #[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 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, 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, ];
503
504 assert_eq!(buf[0..expected.len()], expected);
505 }
506}