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