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