1use crate::canonicalize_ioctl_request;
6use crate::dma_heap::{Alloc, dma_heap_device_register};
7use bitfield::bitfield;
8use bstr::ByteSlice;
9use fidl_fuchsia_hardware_qualcomm_fastrpc as frpc;
10use starnix_core::device::DeviceOps;
11use starnix_core::mm::memory::MemoryObject;
12use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt, ProtectionFlags};
13use starnix_core::task::{CurrentTask, ThreadGroupKey};
14use starnix_core::vfs::{
15 Anon, FdFlags, FdNumber, FileObject, FileObjectState, FileOps, NamespaceNode,
16 call_fidl_and_await_close, default_ioctl,
17};
18use starnix_core::{
19 fileops_impl_dataless, fileops_impl_memory, fileops_impl_noop_sync, fileops_impl_seekless,
20};
21use starnix_logging::{log_debug, log_error, log_warn};
22use starnix_sync::{FastrpcInnerState, FileOpsCore, Locked, OrderedMutex, Unlocked};
23use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
24use starnix_types::user_buffer::UserBuffer;
25use starnix_uapi::device_type::DeviceType;
26use starnix_uapi::errors::{Errno, ErrnoCode};
27use starnix_uapi::open_flags::OpenFlags;
28use starnix_uapi::user_address::{MultiArchUserRef, UserCString, UserRef};
29use starnix_uapi::{errno, error};
30use std::collections::VecDeque;
31use std::sync::atomic::{AtomicI64, Ordering};
32use std::sync::{Arc, OnceLock};
33use zx::HandleBased;
34
35type IoctlInvokeFdPtr = MultiArchUserRef<
36 linux_uapi::fastrpc_ioctl_invoke_fd,
37 linux_uapi::arch32::fastrpc_ioctl_invoke_fd,
38>;
39
40type IoctlInvoke2Ptr =
41 MultiArchUserRef<linux_uapi::fastrpc_ioctl_invoke2, linux_uapi::arch32::fastrpc_ioctl_invoke2>;
42
43type IoctlInitPtr =
44 MultiArchUserRef<linux_uapi::fastrpc_ioctl_init, linux_uapi::arch32::fastrpc_ioctl_init>;
45
46type IoctlInvokePtr =
47 MultiArchUserRef<linux_uapi::fastrpc_ioctl_invoke, linux_uapi::arch32::fastrpc_ioctl_invoke>;
48
49type RemoteBufPtr = MultiArchUserRef<linux_uapi::remote_buf, linux_uapi::arch32::remote_buf>;
50
51const FASTRPC_MAX_DSP_ATTRIBUTES: usize = 256;
52const FASTRPC_MAX_ATTRIBUTES: usize = 260;
53
54const PERF_CAPABILITY_SUPPORT: u32 = 0;
56
57const KERNEL_ERROR_CODE_V1_SUPPORT: u32 = 0;
59
60const USERSPACE_ALLOCATION_SUPPORT: u32 = 1;
62
63const DSPSIGNAL_SUPPORT: u32 = 0;
65
66const KERNEL_CAPABILITIES: [u32; FASTRPC_MAX_ATTRIBUTES - FASTRPC_MAX_DSP_ATTRIBUTES] = [
67 PERF_CAPABILITY_SUPPORT,
68 KERNEL_ERROR_CODE_V1_SUPPORT,
69 USERSPACE_ALLOCATION_SUPPORT,
70 DSPSIGNAL_SUPPORT,
71];
72
73const ASYNC_FASTRPC_CAP: usize = 9;
74const DMA_HANDLE_REVERSE_RPC_CAP: usize = 129;
75
76const INVOKE2_MAX: u32 = 4;
77
78const FASTRPC_INIT_ATTACH: u32 = 0;
79const FASTRPC_INIT_CREATE_STATIC: u32 = 2;
80
81const INIT_FILELEN_MAX: u32 = 2 * 1024 * 1024;
82const INIT_MEMLEN_MAX: u32 = 8 * 1024 * 1024;
83
84bitfield! {
99 pub struct Scalar(u32);
100 impl Debug;
101
102 pub method_id, _: 28, 24;
103 pub inbuffs, _: 23, 16;
104 pub outbuffs, _: 15, 8;
105 pub inhandles, _: 7, 4;
106 pub outhandles, _: 3, 0;
107}
108
109impl Scalar {
110 fn len(&self) -> u32 {
111 self.inbuffs() as u32
112 + self.outbuffs() as u32
113 + self.inhandles() as u32
114 + self.outhandles() as u32
115 }
116}
117
118fn fidl_error_to_errno(info: &str, error: fidl::Error) -> starnix_uapi::errors::Errno {
120 if !error.is_closed() {
121 log_error!("{}: {:?}", info, error);
122 return errno!(EIO);
123 }
124
125 static LAST_LOG_TIME: AtomicI64 = AtomicI64::new(0);
128 let now = zx::MonotonicInstant::get().into_nanos();
129 let last = LAST_LOG_TIME.load(Ordering::Relaxed);
130 if now - last > 5_000_000_000 {
131 LAST_LOG_TIME.store(now, Ordering::Relaxed);
132 log_error!("{}: {:?}", info, error);
133 }
134 errno!(EIO)
135}
136
137fn zx_i32_to_errno(info: &str, error: i32) -> starnix_uapi::errors::Errno {
139 starnix_uapi::from_status_like_fdio!(zx::Status::from_raw(error), info)
140}
141
142fn zx_status_to_errno(info: &str, error: zx::Status) -> starnix_uapi::errors::Errno {
144 starnix_uapi::from_status_like_fdio!(error, info)
145}
146
147fn retval_i32_to_errno(info: &str, error: i32) -> starnix_uapi::errors::Errno {
149 let code = ErrnoCode::from_return_value(error as u64);
150 log_debug!("{}: {:?}", info, code);
151 Errno::with_context(code, info)
152}
153
154fn fastrpc_align(size: u64) -> Result<u64, Errno> {
155 size.checked_next_multiple_of(128).ok_or_else(|| errno!(EOVERFLOW))
157}
158
159struct DmaBufFile {
160 memory: Arc<MemoryObject>,
161}
162
163impl DmaBufFile {
164 fn new(memory: Arc<MemoryObject>) -> Box<Self> {
165 Box::new(Self { memory })
166 }
167}
168
169impl FileOps for DmaBufFile {
170 fileops_impl_memory!(self, &self.memory);
171 fileops_impl_noop_sync!();
172
173 fn ioctl(
174 &self,
175 locked: &mut Locked<Unlocked>,
176 file: &FileObject,
177 current_task: &CurrentTask,
178 request: u32,
179 arg: SyscallArg,
180 ) -> Result<SyscallResult, Errno> {
181 match canonicalize_ioctl_request(current_task, request) {
182 linux_uapi::DMA_BUF_SET_NAME_B => {
183 let name = current_task.read_c_string_to_vec(
184 UserCString::new(current_task, arg),
185 linux_uapi::DMA_BUF_NAME_LEN as usize,
186 )?;
187 log_debug!(
188 "dma buf file with koid {:?} got ioctl set name: {}",
189 self.memory.get_koid(),
190 name
191 );
192 self.memory.set_zx_name(&name);
193 Ok(SUCCESS)
194 }
195 _ => default_ioctl(file, locked, current_task, request, arg),
196 }
197 }
198}
199
200struct SystemHeap {
201 device: Arc<frpc::SecureFastRpcSynchronousProxy>,
202}
203
204impl Alloc for SystemHeap {
205 fn alloc(
206 &self,
207 locked: &mut Locked<Unlocked>,
208 current_task: &CurrentTask,
209 size: u64,
210 fd_flags: FdFlags,
211 ) -> Result<FdNumber, Errno> {
212 let vmo = self
213 .device
214 .allocate(size, zx::MonotonicInstant::INFINITE)
215 .map_err(|e| fidl_error_to_errno("allocate call", e))?
216 .map_err(|e| zx_i32_to_errno("allocate", e))?;
217
218 log_debug!("allocated vmo with koid {:?}", vmo.koid());
219
220 let memory = Arc::new(MemoryObject::from(vmo));
221
222 let file = Anon::new_private_file(
223 locked,
224 current_task,
225 DmaBufFile::new(memory),
226 OpenFlags::RDWR,
227 "[fastrpc:buffer]",
228 );
229
230 current_task.add_file(locked, file, fd_flags)
231 }
232}
233
234#[derive(Default)]
235struct FastRPCFileState {
236 session: Option<Arc<frpc::RemoteDomainSynchronousProxy>>,
237 payload_vmos: VecDeque<frpc::SharedPayloadBuffer>,
238 cid: Option<i32>,
239 pid: Option<ThreadGroupKey>,
240}
241
242struct ParsedInvoke {
243 invoke: linux_uapi::fastrpc_ioctl_invoke,
244 scalar: Scalar,
245 fd_vmos: Option<Vec<Option<zx::Vmo>>>,
246}
247
248#[derive(PartialEq)]
249struct BufferWithMergeInfo {
250 start: u64,
251 end: u64,
252 buffer_index: usize,
253 merge_contribution: u64,
254 merge_offset: u64,
255}
256
257impl std::fmt::Debug for BufferWithMergeInfo {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 f.debug_struct("BufferWithMergeInfo")
260 .field("start", &format_args!("{:#x}", self.start))
261 .field("end", &format_args!("{:#x}", self.end))
262 .field("buffer_index", &self.buffer_index)
263 .field("merge_contribution", &format_args!("{:#x}", self.merge_contribution))
264 .field("merge_offset", &self.merge_offset)
265 .finish()
266 }
267}
268
269struct OutputArgumentInfo {
270 mapped: bool,
271 offset: u64,
272 length: u64,
273}
274
275struct PayloadInformation {
276 payload_buffer: Option<frpc::SharedPayloadBuffer>,
277 input_args: Vec<frpc::ArgumentEntry>,
278 output_args: Vec<frpc::ArgumentEntry>,
279 output_info: Vec<OutputArgumentInfo>,
280}
281
282struct FastRPCFile {
283 pid_open: ThreadGroupKey,
284 device: Arc<frpc::SecureFastRpcSynchronousProxy>,
285 cached_capabilities: Arc<OnceLock<[u32; FASTRPC_MAX_DSP_ATTRIBUTES]>>,
286 inner_state: OrderedMutex<FastRPCFileState, FastrpcInnerState>,
287}
288
289impl FastRPCFile {
290 fn new(
291 pid_open: ThreadGroupKey,
292 device: Arc<frpc::SecureFastRpcSynchronousProxy>,
293 cached_capabilities: Arc<OnceLock<[u32; FASTRPC_MAX_DSP_ATTRIBUTES]>>,
294 ) -> Self {
295 Self {
296 pid_open,
297 device,
298 cached_capabilities,
299 inner_state: OrderedMutex::new(FastRPCFileState::default()),
300 }
301 }
302
303 fn invoke(
304 &self,
305 current_task: &CurrentTask,
306 locked: &mut Locked<Unlocked>,
307 request: u32,
308 arg: SyscallArg,
309 ) -> Result<SyscallResult, Errno> {
310 let parsed_invoke = Self::parse_invoke_request(current_task, request, arg)?;
311 let ParsedInvoke { invoke: info, scalar, mut fd_vmos } = parsed_invoke;
312
313 log_debug!(
314 "FastRPC ioctl invoke, scalar {} ({}, {}, {}), handle {}",
315 info.sc,
316 scalar.method_id(),
317 scalar.inbuffs(),
318 scalar.outbuffs(),
319 info.handle
320 );
321
322 let length = scalar.len();
323 let inbufs = scalar.inbuffs() as u32;
324
325 if scalar.inhandles() != 0 || scalar.outhandles() != 0 {
326 log_error!("handles in scalar not supported.");
327 return error!(ENOSYS);
328 }
329
330 let remote_bufs = current_task.read_multi_arch_objects_to_vec(
331 RemoteBufPtr::new(current_task, info.pra),
332 length as usize,
333 )?;
334 let merged_buffers = Self::merge_buffers(&fd_vmos, &remote_bufs)?;
335 let payload = Self::get_payload_info(
336 current_task,
337 locked,
338 &self.inner_state,
339 &merged_buffers,
340 &remote_bufs,
341 &mut fd_vmos,
342 inbufs,
343 )?;
344
345 let payload_buffer_id = match &payload.payload_buffer {
346 Some(buffer) => buffer.id,
347 None => 0,
348 };
349
350 let session = self.get_session(locked)?;
351 let invoke_res = session.invoke(
352 current_task.get_tid(),
353 info.handle,
354 scalar.method_id() as u32,
355 payload_buffer_id,
356 payload.input_args,
357 payload.output_args,
358 zx::MonotonicInstant::INFINITE,
359 );
360
361 let buffer_after_invoke = |success: bool| -> Result<(), Errno> {
362 if success {
363 if let Some(buffer) = &payload.payload_buffer {
364 self.process_out_bufs(
365 current_task,
366 &remote_bufs,
367 &buffer.vmo,
368 &payload.output_info,
369 inbufs,
370 )?;
371 }
372 }
373
374 if let Some(buffer) = payload.payload_buffer {
375 log_debug!("returning payload buffer {}", buffer.id);
376 self.inner_state.lock(locked).payload_vmos.push_back(buffer);
377 };
378
379 Ok(())
380 };
381
382 match invoke_res {
383 Ok(Ok(())) => {
384 buffer_after_invoke(true)?;
385 Ok(SUCCESS)
386 }
387 Ok(Err(e)) => {
388 buffer_after_invoke(false)?;
389 Err(retval_i32_to_errno("invoke", e))
390 }
391 Err(e) => {
392 buffer_after_invoke(false)?;
393 Err(fidl_error_to_errno("invoke call", e))
394 }
395 }
396 }
397
398 fn get_session(
399 &self,
400 locked: &mut Locked<Unlocked>,
401 ) -> Result<Arc<frpc::RemoteDomainSynchronousProxy>, Errno> {
402 let inner = self.inner_state.lock(locked);
403 Ok(inner.session.as_ref().ok_or_else(|| errno!(ENOENT))?.clone())
404 }
405
406 fn get_capabilities_from_device(
407 &self,
408 _domain: u32,
409 ) -> Result<[u32; FASTRPC_MAX_DSP_ATTRIBUTES], Errno> {
410 let capabilities = self
411 .device
412 .get_capabilities(zx::MonotonicInstant::INFINITE)
413 .map_err(|e| fidl_error_to_errno("get_capabilities call", e))?
414 .map_err(|e| retval_i32_to_errno("get_capabilities", e))?;
415
416 let mut res: [u32; FASTRPC_MAX_DSP_ATTRIBUTES] = [0; FASTRPC_MAX_DSP_ATTRIBUTES];
417 let attribute_buffer_length = FASTRPC_MAX_DSP_ATTRIBUTES - 1;
418 res[0] = 0;
420 res[1..(attribute_buffer_length + 1)]
421 .copy_from_slice(&capabilities[..attribute_buffer_length]);
422
423 log_debug!("ASYNC_FASTRPC_CAP: {}", res[ASYNC_FASTRPC_CAP]);
424 log_debug!("DMA_HANDLE_REVERSE_RPC_CAP: {}", res[DMA_HANDLE_REVERSE_RPC_CAP]);
425 Ok(res)
426 }
427
428 fn get_capabilities(&self, domain: u32, attr: usize) -> Result<u32, Errno> {
429 if attr >= FASTRPC_MAX_ATTRIBUTES {
430 return error!(EOVERFLOW);
431 }
432
433 if attr >= FASTRPC_MAX_DSP_ATTRIBUTES {
434 return Ok(KERNEL_CAPABILITIES[(attr) - FASTRPC_MAX_DSP_ATTRIBUTES]);
435 }
436
437 let caps = self.cached_capabilities.get();
440 match caps {
441 Some(caps) => Ok(caps[attr]),
442 None => {
443 let from_device = self.get_capabilities_from_device(domain)?;
444 let caps = self.cached_capabilities.get_or_init(|| from_device);
445 Ok(caps[attr])
446 }
447 }
448 }
449
450 fn parse_invoke_request(
451 current_task: &CurrentTask,
452 request: u32,
453 arg: SyscallArg,
454 ) -> Result<ParsedInvoke, Errno> {
455 match canonicalize_ioctl_request(current_task, request) {
456 linux_uapi::FASTRPC_IOCTL_INVOKE_FD => {
457 let info = current_task
458 .read_multi_arch_object(IoctlInvokeFdPtr::new(current_task, arg))?;
459 log_debug!("FastRPC ioctl invoke_fd {:?}", info);
460
461 let scalar = Scalar(info.inv.sc);
462
463 let fds = current_task
464 .read_objects_to_vec::<i32>(info.fds.into(), scalar.len() as usize)?;
465
466 let mut fd_vmos = vec![];
469 for fd in fds {
470 if fd > 0 {
472 let file = current_task.get_file(FdNumber::from_raw(fd))?;
473 let dma_buf =
474 file.downcast_file::<DmaBufFile>().ok_or_else(|| errno!(EBADF))?;
475
476 let fd_vmo = dma_buf
477 .memory
478 .as_vmo()
479 .ok_or_else(|| errno!(EBADF))?
480 .duplicate_handle(fidl::Rights::SAME_RIGHTS)
481 .map_err(|e| {
482 zx_status_to_errno("parse_invoke_request duplicate_handle", e)
483 })?;
484
485 fd_vmos.push(Some(fd_vmo));
486 } else {
487 fd_vmos.push(None);
488 }
489 }
490
491 Ok(ParsedInvoke { invoke: info.inv, scalar, fd_vmos: Some(fd_vmos) })
492 }
493 linux_uapi::FASTRPC_IOCTL_INVOKE => {
494 let info =
495 current_task.read_multi_arch_object(IoctlInvokePtr::new(current_task, arg))?;
496 let scalar = Scalar(info.sc);
497 Ok(ParsedInvoke { invoke: info, scalar, fd_vmos: None })
498 }
499 _ => {
500 error!(ENOSYS)
501 }
502 }
503 }
504
505 fn merge_buffers(
506 fd_vmos: &Option<Vec<Option<zx::Vmo>>>,
507 remote_bufs: &[linux_uapi::remote_buf],
508 ) -> Result<Vec<BufferWithMergeInfo>, Errno> {
509 let mut indexed_buffers = remote_bufs
511 .iter()
512 .enumerate()
513 .map(|(index, buf_ref)| (index, buf_ref))
514 .collect::<Vec<_>>();
515
516 indexed_buffers.sort_by(|(_, b1), (_, b2)| {
518 let start_comparison = b1.pv.cmp(&b2.pv);
519 let end_reverse_comparison = (b2.pv.addr + b2.len).cmp(&(b1.pv.addr + b1.len));
520 match start_comparison {
521 std::cmp::Ordering::Equal => end_reverse_comparison,
522 std::cmp::Ordering::Greater | std::cmp::Ordering::Less => start_comparison,
523 }
524 });
525
526 let mut results = Vec::new();
527
528 let mut current_merge_end: u64 = 0;
531
532 for (original_idx, buffer) in indexed_buffers.into_iter() {
533 let start = buffer.pv.addr;
534 let end = buffer.pv.addr.checked_add(buffer.len).ok_or_else(|| errno!(EOVERFLOW))?;
535
536 let merge_contribution;
539
540 let merge_offset;
543
544 if Self::is_buffer_mapped(fd_vmos, original_idx) {
545 merge_contribution = 0;
547 merge_offset = 0;
548 } else if start < current_merge_end && end <= current_merge_end {
549 merge_contribution = 0;
551 merge_offset = current_merge_end - start;
552 } else if start < current_merge_end {
553 merge_contribution = end - current_merge_end;
555 merge_offset = current_merge_end - start;
556
557 current_merge_end = end;
559 } else {
560 merge_contribution = end - start;
562 merge_offset = 0;
563
564 current_merge_end = end;
566 }
567
568 results.push(BufferWithMergeInfo {
569 start,
570 end,
571 buffer_index: original_idx,
572 merge_contribution,
573 merge_offset,
574 });
575 }
576
577 Ok(results)
578 }
579
580 fn get_payload_size(
581 fd_vmos: &Option<Vec<Option<zx::Vmo>>>,
582 merged_buffers: &Vec<BufferWithMergeInfo>,
583 ) -> Result<u64, Errno> {
584 let mut size: u64 = 0;
585 for i in 0..merged_buffers.len() {
586 let buffer_index = merged_buffers[i].buffer_index;
587
588 if !Self::is_buffer_mapped(fd_vmos, buffer_index) {
590 if merged_buffers[i].merge_offset == 0 {
591 size = fastrpc_align(size)?;
593 }
594
595 size = size
596 .checked_add(merged_buffers[i].merge_contribution)
597 .ok_or_else(|| errno!(EOVERFLOW))?;
598 }
599 }
600
601 Ok(size)
602 }
603
604 fn is_buffer_mapped(fd_vmos: &Option<Vec<Option<zx::Vmo>>>, idx: usize) -> bool {
605 match fd_vmos {
606 None => false,
607 Some(vmos) => vmos[idx].is_some(),
608 }
609 }
610
611 fn get_mapped_memory_and_offset(
612 current_task: &CurrentTask,
613 buf: &linux_uapi::remote_buf,
614 fd_vmos: &mut Option<Vec<Option<zx::Vmo>>>,
615 idx: usize,
616 ) -> Result<(u64, zx::Vmo), Errno> {
617 let (mm_vmo, mm_offset) = current_task
618 .mm()?
619 .get_mapping_memory(buf.pv.into(), ProtectionFlags::READ | ProtectionFlags::WRITE)?;
620
621 if let Some(fd_vmo) =
622 fd_vmos.as_deref_mut().and_then(|v| v.get_mut(idx)).and_then(|o| o.take())
623 {
624 if mm_vmo.get_koid()
625 == fd_vmo
626 .basic_info()
627 .map_err(|e| zx_status_to_errno("get_mapped_memory_and_offset basic_info", e))?
628 .koid
629 {
630 log_debug!(
631 "FastRPC ioctl invoke found allocated vmo for user address. koid: {:?}. User pointer: {:#x} offset in vmo: {}",
632 mm_vmo.get_koid(),
633 buf.pv.addr,
634 mm_offset
635 );
636 return Ok((mm_offset, fd_vmo));
637 }
638 }
639
640 error!(ENOSYS)
641 }
642
643 fn get_payload_info(
644 current_task: &CurrentTask,
645 locked: &mut Locked<Unlocked>,
646 inner_state: &OrderedMutex<FastRPCFileState, FastrpcInnerState>,
647 merged_buffers: &Vec<BufferWithMergeInfo>,
648 remote_bufs: &Vec<linux_uapi::remote_buf>,
649 fd_vmos: &mut Option<Vec<Option<zx::Vmo>>>,
650 inbufs: u32,
651 ) -> Result<PayloadInformation, Errno> {
652 let payload_size = Self::get_payload_size(fd_vmos, &merged_buffers)?;
653 let payload_buffer = if payload_size == 0 {
654 None
655 } else {
656 let payload_buffer =
657 inner_state.lock(locked).payload_vmos.pop_front().ok_or_else(|| errno!(ENOBUFS))?;
658 log_debug!("selected payload buffer {}", payload_buffer.id);
659 Some(payload_buffer)
660 };
661
662 let mut input_args: Vec<(usize, frpc::ArgumentEntry)> = vec![];
668 let mut output_args: Vec<(usize, frpc::ArgumentEntry)> = vec![];
669 let mut output_info: Vec<(usize, OutputArgumentInfo)> = vec![];
670 let mut curr_merge_point = 0;
671
672 for merged_buffer in merged_buffers {
673 let buf =
674 remote_bufs.get(merged_buffer.buffer_index).expect("to have index in remote bufs");
675 let is_mapped = Self::is_buffer_mapped(fd_vmos, merged_buffer.buffer_index);
676
677 let (entry, offset) = if is_mapped {
678 let (offset, vmo) = Self::get_mapped_memory_and_offset(
679 current_task,
680 buf,
681 fd_vmos,
682 merged_buffer.buffer_index,
683 )?;
684 (
685 frpc::ArgumentEntry::VmoArgument(frpc::VmoArgument {
686 vmo,
687 offset,
688 length: buf.len,
689 }),
690 offset,
691 )
692 } else {
693 if merged_buffer.merge_offset == 0 {
694 curr_merge_point = fastrpc_align(curr_merge_point)?;
695 }
696
697 let offset = curr_merge_point - merged_buffer.merge_offset;
698 curr_merge_point = curr_merge_point
699 .checked_add(merged_buffer.merge_contribution)
700 .ok_or_else(|| errno!(EOVERFLOW))?;
701 (frpc::ArgumentEntry::Argument(frpc::Argument { offset, length: buf.len }), offset)
702 };
703
704 if merged_buffer.buffer_index < inbufs as usize {
705 if !is_mapped && buf.len > 0 {
707 let buf_data = current_task.read_buffer(&UserBuffer {
708 address: buf.pv.into(),
709 length: buf.len as usize,
710 })?;
711
712 let vmo = &payload_buffer.as_ref().expect("payload buffer").vmo;
713 vmo.write(buf_data.as_slice(), offset)
714 .map_err(|e| zx_status_to_errno("get_payload_info write", e))?;
715 }
716
717 input_args.push((merged_buffer.buffer_index, entry));
718 } else {
719 output_args.push((merged_buffer.buffer_index, entry));
720 output_info.push((
721 merged_buffer.buffer_index,
722 OutputArgumentInfo { mapped: is_mapped, offset, length: buf.len },
723 ));
724 }
725 }
726
727 input_args.sort_by_key(|e| e.0);
728 output_args.sort_by_key(|e| e.0);
729 output_info.sort_by_key(|e| e.0);
730
731 let input_args = input_args.into_iter().map(|e| e.1).collect();
732 let output_args = output_args.into_iter().map(|e| e.1).collect();
733 let output_info = output_info.into_iter().map(|e| e.1).collect();
734
735 Ok(PayloadInformation { payload_buffer, input_args, output_args, output_info })
736 }
737
738 fn process_out_bufs(
739 &self,
740 current_task: &CurrentTask,
741 remote_bufs: &Vec<linux_uapi::remote_buf>,
742 payload_vmo: &zx::Vmo,
743 output_infos: &Vec<OutputArgumentInfo>,
744 inbufs: u32,
745 ) -> Result<(), Errno> {
746 let max_len = output_infos.iter().filter(|i| !i.mapped).map(|i| i.length).max();
747 let Some(max_len) = max_len else {
748 return Ok(());
749 };
750
751 let mut read_vec = vec![0; max_len as usize];
752
753 for (output_index, output_info) in output_infos.iter().enumerate() {
754 if output_info.mapped {
755 continue;
756 }
757 if output_info.length == 0 {
758 continue;
759 }
760
761 let buf = &remote_bufs[output_index + inbufs as usize];
762
763 assert_eq!(buf.len, output_info.length);
764
765 payload_vmo
766 .read(&mut read_vec[0..output_info.length as usize], output_info.offset)
767 .map_err(|e| zx_status_to_errno("process_out_bufs read", e))?;
768
769 let _ = current_task
770 .write_memory(buf.pv.into(), &read_vec[0..output_info.length as usize])?;
771 }
772 Ok(())
773 }
774}
775
776impl FileOps for FastRPCFile {
777 fileops_impl_noop_sync!();
778 fileops_impl_seekless!();
779 fileops_impl_dataless!();
780
781 fn close(
782 self: Box<Self>,
783 locked: &mut Locked<FileOpsCore>,
784 _file: &FileObjectState,
785 _current_task: &CurrentTask,
786 ) {
787 let inner = self.inner_state.lock(locked);
788 if let Some(ref session) = inner.session {
789 call_fidl_and_await_close(frpc::RemoteDomainSynchronousProxy::close, session.as_ref());
790 }
791 }
792
793 fn ioctl(
794 &self,
795 locked: &mut Locked<Unlocked>,
796 file: &FileObject,
797 current_task: &CurrentTask,
798 request: u32,
799 arg: SyscallArg,
800 ) -> Result<SyscallResult, Errno> {
801 let pid = current_task.thread_group_key.clone();
802 if pid != self.pid_open {
803 return error!(EPERM);
804 }
805
806 match canonicalize_ioctl_request(current_task, request) {
807 linux_uapi::FASTRPC_IOCTL_INVOKE | linux_uapi::FASTRPC_IOCTL_INVOKE_FD => {
808 self.invoke(current_task, locked, request, arg)
809 }
810 linux_uapi::FASTRPC_IOCTL_GETINFO => {
811 let user_info = UserRef::<u32>::from(arg);
812 let channel_id = current_task.read_object(user_info)?;
813 let device_channel_id = self
814 .device
815 .get_channel_id(zx::MonotonicInstant::INFINITE)
816 .map_err(|e| fidl_error_to_errno("get_channel_id call", e))?
817 .map_err(|e| zx_i32_to_errno("get_channel_id", e))?;
818
819 if device_channel_id != channel_id {
820 return error!(EPERM);
821 }
822
823 let mut inner = self.inner_state.lock(locked);
824 if inner.session.is_some() {
825 return error!(EEXIST);
826 }
827
828 inner.pid = Some(pid);
829 inner.cid = Some(channel_id as i32);
830
831 log_debug!("FastRPC ioctl getinfo for channel_id {}", channel_id);
832
833 current_task.write_object(user_info, &(1u32))?;
839 Ok(SUCCESS)
840 }
841 linux_uapi::FASTRPC_IOCTL_GET_DSP_INFO => {
842 let user_ref = UserRef::<linux_uapi::fastrpc_ioctl_capability>::new(arg.into());
845 let mut info = current_task.read_object(user_ref)?;
846 log_debug!(
847 "FastRPC ioctl get dsp info domain {} attribute {}",
848 info.domain,
849 info.attribute_ID
850 );
851 info.capability = self.get_capabilities(info.domain, info.attribute_ID as usize)?;
852 current_task.write_object(user_ref, &info)?;
853 Ok(SUCCESS)
854 }
855 linux_uapi::FASTRPC_IOCTL_INVOKE2 => {
856 let info =
857 current_task.read_multi_arch_object(IoctlInvoke2Ptr::new(current_task, arg))?;
858 if info.req > INVOKE2_MAX {
859 log_debug!("FastRPC ioctl invoke2 out of bounds req number {}", info.req);
860 return error!(ENOTTY);
861 }
862
863 log_debug!("FastRPC ioctl invoke2 {:?}", info);
864 error!(ENOSYS)
865 }
866 linux_uapi::FASTRPC_IOCTL_INIT => {
867 let info =
868 current_task.read_multi_arch_object(IoctlInitPtr::new(current_task, arg))?;
869
870 if info.filelen >= INIT_FILELEN_MAX || info.memlen >= INIT_MEMLEN_MAX {
871 return error!(EFBIG);
872 }
873
874 let mut inner = self.inner_state.lock(locked);
875 if inner.session.is_some() {
876 return error!(EEXIST);
877 }
878
879 match info.flags {
880 FASTRPC_INIT_ATTACH => {
881 log_debug!("FastRPC ioctl init FASTRPC_INIT_ATTACH {:?}", info);
882
883 let (client, server) =
884 fidl::endpoints::create_sync_proxy::<frpc::RemoteDomainMarker>();
885
886 self.device
887 .attach_root_domain(server, zx::MonotonicInstant::INFINITE)
888 .map_err(|e| fidl_error_to_errno("attach_root_domain call", e))?
889 .map_err(|e| retval_i32_to_errno("attach_root_domain", e))?;
890
891 inner.payload_vmos = client
892 .get_payload_buffer_set(3, zx::MonotonicInstant::INFINITE)
893 .map_err(|e| fidl_error_to_errno("get_payload_buffer_set call", e))?
894 .map_err(|e| zx_i32_to_errno("get_payload_buffer_set", e))?
895 .into();
896
897 inner.session = Some(Arc::new(client));
898 Ok(SUCCESS)
899 }
900 FASTRPC_INIT_CREATE_STATIC => {
901 log_debug!("FastRPC ioctl init FASTRPC_INIT_CREATE_STATIC {:?}", info);
902 let file_name = current_task.read_c_string_to_vec(
903 UserCString::new(current_task, info.file),
904 info.filelen as usize,
905 )?;
906
907 let (client, server) =
908 fidl::endpoints::create_sync_proxy::<frpc::RemoteDomainMarker>();
909
910 self.device
911 .create_static_domain(
912 file_name.to_str().map_err(|_| errno!(EINVAL))?,
913 info.memlen,
914 server,
915 zx::MonotonicInstant::INFINITE,
916 )
917 .map_err(|e| fidl_error_to_errno("create_static_domain call", e))?
918 .map_err(|e| retval_i32_to_errno("create_static_domain", e))?;
919
920 inner.payload_vmos = client
921 .get_payload_buffer_set(3, zx::MonotonicInstant::INFINITE)
922 .map_err(|e| fidl_error_to_errno("get_payload_buffer_set call", e))?
923 .map_err(|e| zx_i32_to_errno("get_payload_buffer_set", e))?
924 .into();
925
926 inner.session = Some(Arc::new(client));
927 Ok(SUCCESS)
928 }
929 _ => {
930 log_warn!("FastRPC ioctl init with unsupported flag {:?}", info);
931 error!(ENOSYS)
932 }
933 }
934 }
935 _ => default_ioctl(file, locked, current_task, request, arg),
936 }
937 }
938}
939
940#[derive(Clone)]
941struct FastRPCDevice {
942 device: Arc<frpc::SecureFastRpcSynchronousProxy>,
943 cached_capabilities: Arc<OnceLock<[u32; FASTRPC_MAX_DSP_ATTRIBUTES]>>,
944}
945
946impl FastRPCDevice {
947 fn new(device: Arc<frpc::SecureFastRpcSynchronousProxy>) -> Self {
948 Self { device, cached_capabilities: Arc::new(OnceLock::new()) }
949 }
950}
951
952impl DeviceOps for FastRPCDevice {
953 fn open(
954 &self,
955 _locked: &mut Locked<FileOpsCore>,
956 current_task: &CurrentTask,
957 _id: DeviceType,
958 _node: &NamespaceNode,
959 _flags: OpenFlags,
960 ) -> Result<Box<dyn FileOps>, Errno> {
961 Ok(Box::new(FastRPCFile::new(
962 current_task.thread_group_key.clone(),
963 self.device.clone(),
964 self.cached_capabilities.clone(),
965 )))
966 }
967}
968
969pub fn fastrpc_device_init(locked: &mut Locked<Unlocked>, system_task: &CurrentTask) {
970 let device = fuchsia_component::client::connect_to_protocol_sync::<frpc::SecureFastRpcMarker>()
971 .expect("Failed to connect to fuchsia.hardware.qualcomm.fastrpc.SecureFastRpc");
972
973 let device = Arc::new(device);
974
975 dma_heap_device_register(locked, system_task, "system", SystemHeap { device: device.clone() });
979
980 let device = FastRPCDevice::new(device);
981 let registry = &system_task.kernel().device_registry;
982 registry
983 .register_dyn_device(
984 locked,
985 system_task,
986 "adsprpc-smd-secure".into(),
987 registry.objects.get_or_create_class("fastrpc".into(), registry.objects.virtual_bus()),
988 device,
989 )
990 .expect("Can register heap device");
991}
992
993#[cfg(test)]
994pub mod tests {
995 use crate::fastrpc::{BufferWithMergeInfo, FastRPCFile, FastRPCFileState};
996 use fidl_fuchsia_hardware_qualcomm_fastrpc::{
997 Argument, ArgumentEntry, SharedPayloadBuffer, VmoArgument,
998 };
999 use linux_uapi::{remote_buf, uaddr};
1000 use starnix_core::mm::ProtectionFlags;
1001 use starnix_core::testing::{UserMemoryWriter, map_memory, spawn_kernel_and_run};
1002 use starnix_sync::OrderedMutex;
1003 use starnix_types::PAGE_SIZE;
1004 use starnix_uapi::user_address::UserAddress;
1005 use zx::HandleBased;
1006
1007 #[fuchsia::test]
1008 fn merge_buffers_test_empty_input() {
1009 let remote_bufs: Vec<remote_buf> = vec![];
1010 let fd_vmos = None;
1011 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1012 assert!(results.is_empty());
1013 }
1014
1015 #[fuchsia::test]
1016 fn merge_buffers_test_single_buffer() {
1017 let remote_bufs = vec![remote_buf { pv: uaddr { addr: 100 }, len: 50 }];
1018 let fd_vmos = None;
1019 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1020 assert_eq!(
1021 results,
1022 vec![BufferWithMergeInfo {
1023 start: 100,
1024 end: 150,
1025 buffer_index: 0,
1026 merge_contribution: 50,
1027 merge_offset: 0,
1028 }]
1029 );
1030 }
1031
1032 #[fuchsia::test]
1033 fn merge_buffers_test_disjoint_buffers_sorted_input() {
1034 let remote_bufs = vec![
1035 remote_buf { pv: uaddr { addr: 100 }, len: 50 },
1036 remote_buf { pv: uaddr { addr: 200 }, len: 50 },
1037 ];
1038 let fd_vmos = None;
1039 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1040 assert_eq!(
1041 results,
1042 vec![
1043 BufferWithMergeInfo {
1044 start: 100,
1045 end: 150,
1046 buffer_index: 0,
1047 merge_contribution: 50,
1048 merge_offset: 0
1049 },
1050 BufferWithMergeInfo {
1051 start: 200,
1052 end: 250,
1053 buffer_index: 1,
1054 merge_contribution: 50,
1055 merge_offset: 0
1056 },
1057 ]
1058 );
1059 }
1060
1061 #[fuchsia::test]
1062 fn merge_buffers_test_disjoint_buffers_unsorted_input() {
1063 let remote_bufs = vec![
1064 remote_buf { pv: uaddr { addr: 200 }, len: 50 }, remote_buf { pv: uaddr { addr: 100 }, len: 50 }, ];
1067 let fd_vmos = None;
1068 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1069 assert_eq!(
1070 results,
1071 vec![
1072 BufferWithMergeInfo {
1073 start: 100,
1074 end: 150,
1075 buffer_index: 1,
1076 merge_contribution: 50,
1077 merge_offset: 0
1078 },
1079 BufferWithMergeInfo {
1080 start: 200,
1081 end: 250,
1082 buffer_index: 0,
1083 merge_contribution: 50,
1084 merge_offset: 0
1085 },
1086 ]
1087 );
1088 }
1089
1090 #[fuchsia::test]
1091 fn merge_buffers_test_touching_buffers() {
1092 let remote_bufs = vec![
1093 remote_buf { pv: uaddr { addr: 100 }, len: 50 },
1094 remote_buf { pv: uaddr { addr: 150 }, len: 50 },
1095 ];
1096 let fd_vmos = None;
1097 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1098 assert_eq!(
1099 results,
1100 vec![
1101 BufferWithMergeInfo {
1102 start: 100,
1103 end: 150,
1104 buffer_index: 0,
1105 merge_contribution: 50,
1106 merge_offset: 0
1107 },
1108 BufferWithMergeInfo {
1109 start: 150,
1110 end: 200,
1111 buffer_index: 1,
1112 merge_contribution: 50,
1113 merge_offset: 0
1114 },
1115 ]
1116 );
1117 }
1118
1119 #[fuchsia::test]
1120 fn merge_buffers_test_touching_buffers_one_mapped() {
1121 let remote_bufs = vec![
1122 remote_buf { pv: uaddr { addr: 100 }, len: 50 },
1123 remote_buf { pv: uaddr { addr: 150 }, len: 50 },
1124 ];
1125 let fd_vmos = Some(vec![None, Some(zx::Vmo::create(1).expect("vmo"))]);
1126 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1127 assert_eq!(
1128 results,
1129 vec![
1130 BufferWithMergeInfo {
1131 start: 100,
1132 end: 150,
1133 buffer_index: 0,
1134 merge_contribution: 50,
1135 merge_offset: 0
1136 },
1137 BufferWithMergeInfo {
1138 start: 150,
1139 end: 200,
1140 buffer_index: 1,
1141 merge_contribution: 00,
1142 merge_offset: 0
1143 },
1144 ]
1145 );
1146 }
1147
1148 #[fuchsia::test]
1149 fn merge_buffers_test_partial_overlap() {
1150 let remote_bufs = vec![
1151 remote_buf { pv: uaddr { addr: 100 }, len: 100 },
1152 remote_buf { pv: uaddr { addr: 150 }, len: 100 },
1153 ];
1154 let fd_vmos = None;
1155 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1156 assert_eq!(
1157 results,
1158 vec![
1159 BufferWithMergeInfo {
1160 start: 100,
1161 end: 200,
1162 buffer_index: 0,
1163 merge_contribution: 100,
1164 merge_offset: 0
1165 },
1166 BufferWithMergeInfo {
1167 start: 150,
1168 end: 250,
1169 buffer_index: 1,
1170 merge_contribution: 50,
1171 merge_offset: 50
1172 },
1173 ]
1174 );
1175 }
1176
1177 #[fuchsia::test]
1178 fn merge_buffers_test_full_containment() {
1179 let remote_bufs = vec![
1180 remote_buf { pv: uaddr { addr: 100 }, len: 100 },
1181 remote_buf { pv: uaddr { addr: 120 }, len: 50 },
1182 ];
1183 let fd_vmos = None;
1184 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1185 assert_eq!(
1186 results,
1187 vec![
1188 BufferWithMergeInfo {
1189 start: 100,
1190 end: 200,
1191 buffer_index: 0,
1192 merge_contribution: 100,
1193 merge_offset: 0
1194 },
1195 BufferWithMergeInfo {
1196 start: 120,
1197 end: 170,
1198 buffer_index: 1,
1199 merge_contribution: 0,
1200 merge_offset: 80
1201 },
1202 ]
1203 );
1204 }
1205
1206 #[fuchsia::test]
1207 fn merge_buffers_test_same_start_address() {
1208 let remote_bufs = vec![
1209 remote_buf { pv: uaddr { addr: 100 }, len: 50 },
1210 remote_buf { pv: uaddr { addr: 100 }, len: 100 },
1211 ];
1212 let fd_vmos = None;
1213 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1214 assert_eq!(
1215 results,
1216 vec![
1217 BufferWithMergeInfo {
1218 start: 100,
1219 end: 200,
1220 buffer_index: 1,
1221 merge_contribution: 100,
1222 merge_offset: 0
1223 },
1224 BufferWithMergeInfo {
1225 start: 100,
1226 end: 150,
1227 buffer_index: 0,
1228 merge_contribution: 0,
1229 merge_offset: 100
1230 },
1231 ]
1232 );
1233 }
1234
1235 #[fuchsia::test]
1236 fn merge_buffers_test_zero_length_buffers() {
1237 let remote_bufs = vec![
1238 remote_buf { pv: uaddr { addr: 100 }, len: 50 },
1239 remote_buf { pv: uaddr { addr: 120 }, len: 0 },
1240 remote_buf { pv: uaddr { addr: 200 }, len: 0 },
1241 ];
1242 let fd_vmos = None;
1243 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1244 assert_eq!(
1245 results,
1246 vec![
1247 BufferWithMergeInfo {
1248 start: 100,
1249 end: 150,
1250 buffer_index: 0,
1251 merge_contribution: 50,
1252 merge_offset: 0
1253 },
1254 BufferWithMergeInfo {
1255 start: 120,
1256 end: 120,
1257 buffer_index: 1,
1258 merge_contribution: 0,
1259 merge_offset: 30
1260 },
1261 BufferWithMergeInfo {
1262 start: 200,
1263 end: 200,
1264 buffer_index: 2,
1265 merge_contribution: 0,
1266 merge_offset: 0
1267 },
1268 ]
1269 );
1270 }
1271
1272 #[fuchsia::test]
1273 fn merge_buffers_test_complex() {
1274 let remote_bufs = vec![
1275 remote_buf { pv: uaddr { addr: 500 }, len: 100 }, remote_buf { pv: uaddr { addr: 100 }, len: 100 }, remote_buf { pv: uaddr { addr: 150 }, len: 100 }, remote_buf { pv: uaddr { addr: 400 }, len: 50 }, remote_buf { pv: uaddr { addr: 180 }, len: 20 }, ];
1281 let fd_vmos = None;
1282 let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1283 let expected = vec![
1284 BufferWithMergeInfo {
1286 start: 100,
1287 end: 200,
1288 buffer_index: 1,
1289 merge_contribution: 100,
1290 merge_offset: 0,
1291 },
1292 BufferWithMergeInfo {
1293 start: 150,
1294 end: 250,
1295 buffer_index: 2,
1296 merge_contribution: 50,
1297 merge_offset: 50,
1298 },
1299 BufferWithMergeInfo {
1300 start: 180,
1301 end: 200,
1302 buffer_index: 4,
1303 merge_contribution: 0,
1304 merge_offset: 70,
1305 },
1306 BufferWithMergeInfo {
1308 start: 400,
1309 end: 450,
1310 buffer_index: 3,
1311 merge_contribution: 50,
1312 merge_offset: 0,
1313 },
1314 BufferWithMergeInfo {
1316 start: 500,
1317 end: 600,
1318 buffer_index: 0,
1319 merge_contribution: 100,
1320 merge_offset: 0,
1321 },
1322 ];
1323
1324 assert_eq!(results, expected);
1325 }
1326
1327 #[fuchsia::test]
1328 async fn get_payload_info_test_complex_range_values() {
1329 spawn_kernel_and_run(async |locked, current_task| {
1330 let addr = map_memory(locked, ¤t_task, UserAddress::from_ptr(100 as usize), 500);
1331
1332 let remote_bufs = vec![
1335 remote_buf { pv: uaddr { addr: (addr + 500u64).expect("add").into() }, len: 100 },
1336 remote_buf { pv: uaddr { addr: (addr + 100u64).expect("add").into() }, len: 100 },
1337 remote_buf { pv: uaddr { addr: (addr + 150u64).expect("add").into() }, len: 100 },
1338 remote_buf { pv: uaddr { addr: (addr + 400u64).expect("add").into() }, len: 50 },
1339 remote_buf { pv: uaddr { addr: (addr + 180u64).expect("add").into() }, len: 20 },
1340 ];
1341
1342 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[0].pv.into());
1344 let data = (0..remote_bufs[0].len as u8).collect::<Vec<_>>();
1345 writer.write(&data);
1346
1347 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[1].pv.into());
1348 let data = (0..remote_bufs[1].len as u8).collect::<Vec<_>>();
1349 writer.write(&data);
1350
1351 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[2].pv.into());
1352 let data = (0..remote_bufs[2].len as u8).collect::<Vec<_>>();
1353 writer.write(&data);
1354
1355 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[3].pv.into());
1356 let data = (0..remote_bufs[3].len as u8).collect::<Vec<_>>();
1357 writer.write(&data);
1358
1359 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[4].pv.into());
1360 let data = (0..remote_bufs[4].len as u8).collect::<Vec<_>>();
1361 writer.write(&data);
1362
1363 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("vmo create");
1364 let vmo_dup = vmo.duplicate_handle(fidl::Rights::SAME_RIGHTS).expect("dup");
1365
1366 let state = OrderedMutex::new(FastRPCFileState {
1367 session: None,
1368 payload_vmos: vec![SharedPayloadBuffer { id: 1, vmo: vmo }].into(),
1369 cid: None,
1370 pid: None,
1371 });
1372 let mut fd_vmos = None;
1373
1374 let merged_buffers =
1375 FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1376 let payload_info = FastRPCFile::get_payload_info(
1377 ¤t_task,
1378 locked,
1379 &state,
1380 &merged_buffers,
1381 &remote_bufs,
1382 &mut fd_vmos,
1383 3,
1384 )
1385 .expect("get_payload_info");
1386
1387 assert_eq!(
1388 payload_info.input_args,
1389 vec![
1390 ArgumentEntry::Argument(Argument { offset: 384, length: 100 }),
1391 ArgumentEntry::Argument(Argument { offset: 0, length: 100 }),
1392 ArgumentEntry::Argument(Argument { offset: 50, length: 100 })
1393 ]
1394 );
1395
1396 assert_eq!(
1397 payload_info.output_args,
1398 vec![
1399 ArgumentEntry::Argument(Argument { offset: 256, length: 50 }),
1400 ArgumentEntry::Argument(Argument { offset: 80, length: 20 }),
1401 ]
1402 );
1403
1404 let data = vmo_dup.read_to_vec::<u8>(0, 484).expect("read");
1410 let expected_vmo = vec![
1411 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1412 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1413 44, 45, 46, 47, 48, 49, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1414 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1415 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
1416 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
1417 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0, 0, 0, 0,
1418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1419 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1420 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1421 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1422 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1423 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1426 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
1427 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1428 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
1429 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
1430 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1431 ];
1432
1433 assert_eq!(expected_vmo, data);
1434 })
1435 .await;
1436 }
1437
1438 #[fuchsia::test]
1439 async fn get_payload_info_test_complex_single_values() {
1440 spawn_kernel_and_run(async |locked, current_task| {
1441 let addr = map_memory(locked, ¤t_task, UserAddress::from_ptr(100 as usize), 500);
1442
1443 let remote_bufs = vec![
1446 remote_buf { pv: uaddr { addr: (addr + 500u64).expect("add").into() }, len: 100 },
1447 remote_buf { pv: uaddr { addr: (addr + 100u64).expect("add").into() }, len: 100 },
1448 remote_buf { pv: uaddr { addr: (addr + 150u64).expect("add").into() }, len: 100 },
1449 remote_buf { pv: uaddr { addr: (addr + 400u64).expect("add").into() }, len: 50 },
1450 remote_buf { pv: uaddr { addr: (addr + 180u64).expect("add").into() }, len: 20 },
1451 ];
1452
1453 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[0].pv.into());
1456 let data = vec![10; remote_bufs[0].len as usize];
1457 writer.write(&data);
1458
1459 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[1].pv.into());
1460 let data = vec![11; remote_bufs[1].len as usize];
1461 writer.write(&data);
1462
1463 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[2].pv.into());
1464 let data = vec![12; remote_bufs[2].len as usize];
1465 writer.write(&data);
1466
1467 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[3].pv.into());
1468 let data = vec![13; remote_bufs[3].len as usize];
1469 writer.write(&data);
1470
1471 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[4].pv.into());
1472 let data = vec![14; remote_bufs[4].len as usize];
1473 writer.write(&data);
1474
1475 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("vmo create");
1476 let vmo_dup = vmo.duplicate_handle(fidl::Rights::SAME_RIGHTS).expect("dup");
1477
1478 let state = OrderedMutex::new(FastRPCFileState {
1479 session: None,
1480 payload_vmos: vec![SharedPayloadBuffer { id: 1, vmo: vmo }].into(),
1481 cid: None,
1482 pid: None,
1483 });
1484 let mut fd_vmos = None;
1485
1486 let merged_buffers =
1487 FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1488 let payload_info = FastRPCFile::get_payload_info(
1489 ¤t_task,
1490 locked,
1491 &state,
1492 &merged_buffers,
1493 &remote_bufs,
1494 &mut fd_vmos,
1495 3,
1496 )
1497 .expect("get_payload_info");
1498
1499 assert_eq!(
1500 payload_info.input_args,
1501 vec![
1502 ArgumentEntry::Argument(Argument { offset: 384, length: 100 }),
1503 ArgumentEntry::Argument(Argument { offset: 0, length: 100 }),
1504 ArgumentEntry::Argument(Argument { offset: 50, length: 100 })
1505 ]
1506 );
1507
1508 assert_eq!(
1509 payload_info.output_args,
1510 vec![
1511 ArgumentEntry::Argument(Argument { offset: 256, length: 50 }),
1512 ArgumentEntry::Argument(Argument { offset: 80, length: 20 }),
1513 ]
1514 );
1515
1516 let data = vmo_dup.read_to_vec::<u8>(0, 484).expect("read");
1522 let expected_vmo = vec![
1523 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
1524 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
1525 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1526 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14,
1527 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 12,
1528 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1529 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1530 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1531 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1532 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1533 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1535 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1536 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1537 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1538 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1539 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1540 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1541 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1542 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1543 10, 10, 10, 10, 10, 10,
1544 ];
1545
1546 assert_eq!(expected_vmo, data);
1547 })
1548 .await;
1549 }
1550
1551 #[fuchsia::test]
1552 async fn get_payload_info_test_complex_single_values_with_one_mapped() {
1553 spawn_kernel_and_run(async |locked, current_task| {
1554 let addr = map_memory(locked, ¤t_task, UserAddress::from_ptr(100 as usize), 400);
1555
1556 let mapped_addr = starnix_core::testing::map_memory_anywhere(locked, current_task, 100);
1557 let (mm_vmo, _mm_offset) = current_task
1558 .mm()
1559 .unwrap()
1560 .get_mapping_memory(mapped_addr, ProtectionFlags::READ | ProtectionFlags::WRITE)
1561 .expect("mem");
1562
1563 let remote_bufs = vec![
1566 remote_buf { pv: uaddr { addr: mapped_addr.into() }, len: 100 },
1567 remote_buf { pv: uaddr { addr: (addr + 100u64).expect("add").into() }, len: 100 },
1568 remote_buf { pv: uaddr { addr: (addr + 150u64).expect("add").into() }, len: 100 },
1569 remote_buf { pv: uaddr { addr: (addr + 400u64).expect("add").into() }, len: 50 },
1570 remote_buf { pv: uaddr { addr: (addr + 180u64).expect("add").into() }, len: 20 },
1571 ];
1572
1573 let mut writer = UserMemoryWriter::new(¤t_task, mapped_addr.into());
1576 let data = vec![10; remote_bufs[0].len as usize];
1577 writer.write(&data);
1578
1579 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[1].pv.into());
1580 let data = vec![11; remote_bufs[1].len as usize];
1581 writer.write(&data);
1582
1583 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[2].pv.into());
1584 let data = vec![12; remote_bufs[2].len as usize];
1585 writer.write(&data);
1586
1587 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[3].pv.into());
1588 let data = vec![13; remote_bufs[3].len as usize];
1589 writer.write(&data);
1590
1591 let mut writer = UserMemoryWriter::new(¤t_task, remote_bufs[4].pv.into());
1592 let data = vec![14; remote_bufs[4].len as usize];
1593 writer.write(&data);
1594
1595 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("vmo create");
1596 let vmo_dup = vmo.duplicate_handle(fidl::Rights::SAME_RIGHTS).expect("dup");
1597
1598 let state = OrderedMutex::new(FastRPCFileState {
1599 session: None,
1600 payload_vmos: vec![SharedPayloadBuffer { id: 1, vmo: vmo }].into(),
1601 cid: None,
1602 pid: None,
1603 });
1604 let mut fd_vmos = Some(vec![
1605 Some(
1606 mm_vmo
1607 .as_vmo()
1608 .unwrap()
1609 .duplicate_handle(fidl::Rights::SAME_RIGHTS)
1610 .expect("dup"),
1611 ),
1612 None,
1613 None,
1614 None,
1615 None,
1616 ]);
1617
1618 let merged_buffers =
1619 FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1620 let payload_info = FastRPCFile::get_payload_info(
1621 ¤t_task,
1622 locked,
1623 &state,
1624 &merged_buffers,
1625 &remote_bufs,
1626 &mut fd_vmos,
1627 3,
1628 )
1629 .expect("get_payload_info");
1630
1631 let ArgumentEntry::VmoArgument(VmoArgument { vmo: _vmo, offset: _offset, length }) =
1632 &payload_info.input_args[0]
1633 else {
1634 panic!("wrong type")
1635 };
1636
1637 assert_eq!(length, &100u64);
1638
1639 assert_eq!(
1640 payload_info.input_args[1..3],
1641 vec![
1642 ArgumentEntry::Argument(Argument { offset: 0, length: 100 }),
1643 ArgumentEntry::Argument(Argument { offset: 50, length: 100 })
1644 ]
1645 );
1646
1647 assert_eq!(
1648 payload_info.output_args,
1649 vec![
1650 ArgumentEntry::Argument(Argument { offset: 256, length: 50 }),
1651 ArgumentEntry::Argument(Argument { offset: 80, length: 20 }),
1652 ]
1653 );
1654
1655 let data = vmo_dup.read_to_vec::<u8>(0, 484).expect("read");
1663 let expected_vmo = vec![
1664 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
1665 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
1666 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1667 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14,
1668 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 12,
1669 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1670 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1671 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1673 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1674 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1675 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1677 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1678 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1679 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1680 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1681 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1682 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1683 0, 0,
1684 ];
1685
1686 assert_eq!(expected_vmo, data);
1687 })
1688 .await;
1689 }
1690}