Skip to main content

starnix_modules_fastrpc/
fastrpc.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
53// Performance data capability not supported.
54const PERF_CAPABILITY_SUPPORT: u32 = 0;
55
56// Newer error version.
57const KERNEL_ERROR_CODE_V1_SUPPORT: u32 = 0;
58
59// Userspace allocation supported through dma-heap.
60const USERSPACE_ALLOCATION_SUPPORT: u32 = 1;
61
62// No signaling support.
63const 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
83// Scalars:
84// These are how we designate the number of various elements inside an rpc method.
85// It comes in a u32 with the bit format:
86//
87// aaam mmmm    iiii iiii    oooo oooo    xxxx yyyy
88//
89// a = attribute (3 bits)
90// m = method (5 bits)
91// i = inbuf (8 bits)
92// o = outbuf (8 bits)
93// x = in handle (4 bits)
94// y = out handle (4 bits)
95//
96// Currently we only support buffers and not handles in this implementation.
97bitfield! {
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
117// All fidl transport errors should be considered as error, and converted to IO error.
118fn 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    // Log at most once every 5 seconds for PEER_CLOSED errors which can spam if the driver
125    // has crashed.
126    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
136// zx.Status errors from fidl domain errors can be converted into fdio-like errnos.
137fn 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
141// zx.Status errors from syscalls can be converted into fdio-like errnos.
142fn zx_status_to_errno(info: &str, error: zx::Status) -> starnix_uapi::errors::Errno {
143    starnix_uapi::from_status_like_fdio!(error, info)
144}
145
146// Directly passthrough retval errors from the driver to the user.
147fn 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    // 128 is the memory alignment within the fastrpc framework.
155    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        // 0th capability is not filled by the driver.
413        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        // OnceLock's get_or_try_init is a nightly feature so we end up with this which might call
432        // get_capabilities_from_device unnecessarily.
433        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                // Collect the vmos for our fds, as well as a mapping to use locally to check
461                // if an entry is mapped or not.
462                let mut fd_vmos = vec![];
463                for fd in fds {
464                    // A non-postive fd signifies a non-mapped entry.
465                    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        // Get the indices for the buffers since we will be shuffling them around.
504        let mut indexed_buffers = remote_bufs
505            .iter()
506            .enumerate()
507            .map(|(index, buf_ref)| (index, buf_ref))
508            .collect::<Vec<_>>();
509
510        // Sort them by start address, if equal start address we sort by reverse of end address.
511        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        // This is used to track the current merge region's endpoint. We don't need to track
523        // a start as we have already sorted them using the start address.
524        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            // The merge_contribution signifies the unique memory that needs to be used to represent
531            // this buffer in memory.
532            let merge_contribution;
533
534            // The merge offset is used to get the actual start of a buffer given a merge point,
535            // this is a negative offset on the current_merge_end.
536            let merge_offset;
537
538            if Self::is_buffer_mapped(fd_vmos, original_idx) {
539                // Ignore buffers that are mapped in our overlap calculations.
540                merge_contribution = 0;
541                merge_offset = 0;
542            } else if start < current_merge_end && end <= current_merge_end {
543                // Buffer lives entirely in the current merged region.
544                merge_contribution = 0;
545                merge_offset = current_merge_end - start;
546            } else if start < current_merge_end {
547                // Buffer lives partially in the current merged region.
548                merge_contribution = end - current_merge_end;
549                merge_offset = current_merge_end - start;
550
551                // Extend the merge region.
552                current_merge_end = end;
553            } else {
554                // Buffer does not live anywhere in the current merged region.
555                merge_contribution = end - start;
556                merge_offset = 0;
557
558                // Start a new merged region.
559                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            // Include in payload if not mapped.
583            if !Self::is_buffer_mapped(fd_vmos, buffer_index) {
584                if merged_buffers[i].merge_offset == 0 {
585                    // Align each new merged region.
586                    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        // Construct these with the usize buffer_index so we can sort them after.
656        //
657        // Output is specified twice, once for the fidl invocation, the other
658        // to be used after the invocation is done since we want to copy data back
659        // to the user.
660        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                // Write data and flush for non-empty, non-mapped input buffers.
699                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                // The reply value indicates to the user whether the smmu
821                // is enabled for this session. On Fuchsia currently we enable the smmu in a
822                // passthrough mode and hardcode a stream id. Eventually when we fully enable the
823                // smmu we will need to allocate and use specific context banks for sessions so
824                // this value will need to come from the driver.
825                current_task.write_object(user_info, &(1u32))?;
826                Ok(SUCCESS)
827            }
828            linux_uapi::FASTRPC_IOCTL_GET_DSP_INFO => {
829                // UserRef note:
830                // fastrpc_ioctl_capability is checked for check_arch_independent_layout.
831                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    // This is called the "system" dma heap, but as of now the fastrpc client is its only client.
962    // Because fastrpc needs to be aware of the fds from this, we are putting the implementation
963    // in this module.
964    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 }, // index 0
1049            remote_buf { pv: uaddr { addr: 100 }, len: 50 }, // index 1
1050        ];
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 }, // 500-600, index 0
1260            remote_buf { pv: uaddr { addr: 100 }, len: 100 }, // 100-200, index 1
1261            remote_buf { pv: uaddr { addr: 150 }, len: 100 }, // 150-250, index 2
1262            remote_buf { pv: uaddr { addr: 400 }, len: 50 },  // 400-450, index 3
1263            remote_buf { pv: uaddr { addr: 180 }, len: 20 },  // 180-200, index 4 (contained)
1264        ];
1265        let fd_vmos = None;
1266        let results = FastRPCFile::merge_buffers(&fd_vmos, &remote_bufs).expect("merge to succeed");
1267        let expected = vec![
1268            // First merge region (100 -> 200 -> 250)
1269            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            // Second merge region (400 -> 450)
1291            BufferWithMergeInfo {
1292                start: 400,
1293                end: 450,
1294                buffer_index: 3,
1295                merge_contribution: 50,
1296                merge_offset: 0,
1297            },
1298            // Third merge region (500 -> 600)
1299            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(&current_task, UserAddress::from_ptr(100 as usize), 500);
1315
1316            // Use the same buffers as merge_buffers_test_complex but just offset them in the
1317            // memory we got mapped above.
1318            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            // This variant of the test puts range based values into the user memory.
1327            let mut writer = UserMemoryWriter::new(&current_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(&current_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(&current_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(&current_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(&current_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                &current_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            // Tests that the input buffers have been correctly setup in the payload.
1388            //
1389            // Since the buffer at 256 is part of the output, it will not be copied into the vmo as
1390            // part of the setup. But because 80-100 is already included as part of the input buffer
1391            // from 0-100 and 50-150 the data appears in here just as a side effect.
1392            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(&current_task, UserAddress::from_ptr(100 as usize), 500);
1425
1426            // Use the same buffers as merge_buffers_test_complex but just offset them in the
1427            // memory we got mapped above.
1428            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            // This variant of the test puts single values based on the buffer index
1437            // into the user memory.
1438            let mut writer = UserMemoryWriter::new(&current_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(&current_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(&current_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(&current_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(&current_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                &current_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            // Tests that the input buffers have been correctly setup in the payload.
1499            //
1500            // Since the buffer at 256 is part of the output, it will not be copied into the vmo as
1501            // part of the setup. But because 80-100 is already included as part of the input buffer
1502            // from 0-100 and 50-150 the data appears in here just as a side effect.
1503            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(&current_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            // Use the same buffers as merge_buffers_test_complex but just offset them in the
1546            // memory we got mapped above.
1547            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            // This variant of the test puts single values based on the buffer index
1556            // into the user memory.
1557            let mut writer = UserMemoryWriter::new(&current_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(&current_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(&current_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(&current_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(&current_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                &current_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            // Tests that the input buffers have been correctly setup in the payload.
1637            //
1638            // The buffers at 500 is mapped so it should not appear here.
1639            //
1640            // Since the buffer at 256 is part of the output, it will not be copied into the vmo as
1641            // part of the setup. But because 80-100 is already included as part of the input buffer
1642            // from 0-100 and 50-150 the data appears in here just as a side effect.
1643            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}