1use thiserror::Error;
7use uuid::Uuid;
8use zerocopy::{transmute, FromBytes, IntoBytes};
9
10use crate::{ffa_v1_1::partition_info_descriptor, PartitionInfoGetFlags, SuccessArgs, Version};
14
15const _: () = assert!(
17 size_of::<crate::ffa_v1_1::partition_info_descriptor>()
18 == size_of::<crate::ffa_v1_2::partition_info_descriptor>()
19);
20
21#[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum PartitionIdType {
40 PeEndpoint { execution_ctx_count: u16 },
43 SepidIndep,
45 SepidDep { proxy_endpoint_id: u16 },
48 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
63pub struct PartitionProperties {
64 pub support_direct_req_rec: bool,
66 pub support_direct_req_send: bool,
68 pub support_direct_req2_rec: Option<bool>,
71 pub support_direct_req2_send: Option<bool>,
74 pub support_indirect_msg: bool,
76 pub support_notif_rec: bool,
78 pub subscribe_vm_created: bool,
80 pub subscribe_vm_destroyed: bool,
82 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 p |= 1 << PartitionProperties::SUBSCRIBE_VM_CREATED_SHIFT
121 }
122 if properties.subscribe_vm_destroyed {
123 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 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!(), };
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#[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 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#[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#[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 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 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 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
367pub struct PartitionInfoIterator<'a> {
369 version: Version,
370 buf: &'a [u8],
371 offset: usize,
372 count: usize,
373}
374
375impl<'a> PartitionInfoIterator<'a> {
376 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 #[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}