Skip to main content

starnix_modules_magma/
file.rs

1// Copyright 2021 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
5#![allow(non_upper_case_globals)]
6
7use crate::ffi::{
8    create_connection, device_import, execute_command, execute_inline_commands, export_buffer,
9    flush, get_buffer_handle, import_semaphore2, query, read_notification_channel,
10};
11use crate::image_file::{ImageFile, ImageInfo};
12use crate::magma::{StarnixPollItem, read_control_and_response, read_magma_command_and_type};
13
14use magma::{
15    MAGMA_CACHE_POLICY_CACHED, MAGMA_IMPORT_SEMAPHORE_ONE_SHOT, MAGMA_POLL_CONDITION_SIGNALED,
16    MAGMA_POLL_TYPE_SEMAPHORE, MAGMA_PRIORITY_MEDIUM, MAGMA_STATUS_INVALID_ARGS,
17    MAGMA_STATUS_MEMORY_ERROR, MAGMA_STATUS_OK, MAGMA_STATUS_TIMED_OUT, magma_buffer_clean_cache,
18    magma_buffer_get_cache_policy, magma_buffer_get_info, magma_buffer_id_t, magma_buffer_info_t,
19    magma_buffer_set_cache_policy, magma_buffer_set_name, magma_buffer_t, magma_cache_operation_t,
20    magma_cache_policy_t, magma_connection_create_buffer, magma_connection_create_context,
21    magma_connection_create_context2, magma_connection_get_error,
22    magma_connection_get_notification_channel_handle, magma_connection_import_buffer,
23    magma_connection_map_buffer, magma_connection_perform_buffer_op, magma_connection_release,
24    magma_connection_release_buffer, magma_connection_release_context,
25    magma_connection_release_semaphore, magma_connection_t, magma_connection_unmap_buffer,
26    magma_device_release, magma_device_t, magma_initialize_logging, magma_poll, magma_poll_item,
27    magma_poll_item_t, magma_semaphore_export, magma_semaphore_id_t, magma_semaphore_reset,
28    magma_semaphore_signal, magma_semaphore_t, virtio_magma_buffer_clean_cache_ctrl_t,
29    virtio_magma_buffer_clean_cache_resp_t, virtio_magma_buffer_export_ctrl_t,
30    virtio_magma_buffer_export_resp_t, virtio_magma_buffer_get_cache_policy_ctrl_t,
31    virtio_magma_buffer_get_cache_policy_resp_t, virtio_magma_buffer_get_handle_ctrl_t,
32    virtio_magma_buffer_get_handle_resp_t, virtio_magma_buffer_get_info_ctrl_t,
33    virtio_magma_buffer_get_info_resp_t, virtio_magma_buffer_set_cache_policy_ctrl_t,
34    virtio_magma_buffer_set_cache_policy_resp_t, virtio_magma_buffer_set_name_ctrl_t,
35    virtio_magma_buffer_set_name_resp_t, virtio_magma_connection_create_buffer_ctrl_t,
36    virtio_magma_connection_create_buffer_resp_t, virtio_magma_connection_create_context_ctrl_t,
37    virtio_magma_connection_create_context_resp_t, virtio_magma_connection_create_context2_ctrl_t,
38    virtio_magma_connection_create_context2_resp_t,
39    virtio_magma_connection_create_semaphore_ctrl_t,
40    virtio_magma_connection_create_semaphore_resp_t,
41    virtio_magma_connection_execute_command_ctrl_t, virtio_magma_connection_execute_command_resp_t,
42    virtio_magma_connection_execute_inline_commands_ctrl_t,
43    virtio_magma_connection_execute_inline_commands_resp_t, virtio_magma_connection_flush_ctrl_t,
44    virtio_magma_connection_flush_resp_t, virtio_magma_connection_get_error_ctrl_t,
45    virtio_magma_connection_get_error_resp_t,
46    virtio_magma_connection_get_notification_channel_handle_ctrl_t,
47    virtio_magma_connection_get_notification_channel_handle_resp_t,
48    virtio_magma_connection_import_buffer_ctrl_t, virtio_magma_connection_import_buffer_resp_t,
49    virtio_magma_connection_import_semaphore2_ctrl_t,
50    virtio_magma_connection_import_semaphore2_resp_t, virtio_magma_connection_map_buffer_ctrl_t,
51    virtio_magma_connection_map_buffer_resp_t, virtio_magma_connection_perform_buffer_op_ctrl_t,
52    virtio_magma_connection_perform_buffer_op_resp_t,
53    virtio_magma_connection_read_notification_channel_ctrl_t,
54    virtio_magma_connection_read_notification_channel_resp_t,
55    virtio_magma_connection_release_buffer_ctrl_t, virtio_magma_connection_release_buffer_resp_t,
56    virtio_magma_connection_release_context_ctrl_t, virtio_magma_connection_release_context_resp_t,
57    virtio_magma_connection_release_ctrl_t, virtio_magma_connection_release_resp_t,
58    virtio_magma_connection_release_semaphore_ctrl_t,
59    virtio_magma_connection_release_semaphore_resp_t, virtio_magma_connection_unmap_buffer_ctrl_t,
60    virtio_magma_connection_unmap_buffer_resp_t,
61    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_CLEAN_CACHE,
62    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_EXPORT,
63    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_CACHE_POLICY,
64    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_HANDLE,
65    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_INFO,
66    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_SET_CACHE_POLICY,
67    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_SET_NAME,
68    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_BUFFER,
69    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_CONTEXT,
70    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_CONTEXT2,
71    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_SEMAPHORE,
72    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_COMMAND,
73    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_INLINE_COMMANDS,
74    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_FLUSH,
75    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_GET_ERROR,
76    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_GET_NOTIFICATION_CHANNEL_HANDLE,
77    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_IMPORT_BUFFER,
78    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_IMPORT_SEMAPHORE2,
79    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_MAP_BUFFER,
80    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_PERFORM_BUFFER_OP,
81    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_READ_NOTIFICATION_CHANNEL,
82    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE,
83    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_BUFFER,
84    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_CONTEXT,
85    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_SEMAPHORE,
86    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_UNMAP_BUFFER,
87    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_CREATE_CONNECTION,
88    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_IMPORT,
89    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_QUERY,
90    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_RELEASE,
91    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_POLL,
92    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_EXPORT,
93    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_RESET,
94    virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_SIGNAL,
95    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_CLEAN_CACHE,
96    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_CACHE_POLICY,
97    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_INFO,
98    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_SET_CACHE_POLICY,
99    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_SET_NAME,
100    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_BUFFER,
101    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_CONTEXT,
102    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_CONTEXT2,
103    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_SEMAPHORE,
104    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_EXECUTE_COMMAND,
105    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_EXECUTE_INLINE_COMMANDS,
106    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_GET_ERROR,
107    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_GET_NOTIFICATION_CHANNEL_HANDLE,
108    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_IMPORT_BUFFER,
109    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_IMPORT_SEMAPHORE2,
110    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_MAP_BUFFER,
111    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_PERFORM_BUFFER_OP,
112    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE,
113    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_BUFFER,
114    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_CONTEXT,
115    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_SEMAPHORE,
116    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_UNMAP_BUFFER,
117    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_CREATE_CONNECTION,
118    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_IMPORT,
119    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_QUERY,
120    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_RELEASE,
121    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_POLL,
122    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_EXPORT,
123    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_RESET,
124    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_SIGNAL,
125    virtio_magma_device_create_connection_ctrl, virtio_magma_device_create_connection_resp_t,
126    virtio_magma_device_import_ctrl_t, virtio_magma_device_import_resp_t,
127    virtio_magma_device_query_ctrl_t, virtio_magma_device_query_resp_t,
128    virtio_magma_device_release_ctrl_t, virtio_magma_device_release_resp_t,
129    virtio_magma_poll_ctrl_t, virtio_magma_poll_resp_t, virtio_magma_semaphore_export_ctrl_t,
130    virtio_magma_semaphore_export_resp_t, virtio_magma_semaphore_reset_ctrl_t,
131    virtio_magma_semaphore_reset_resp_t, virtio_magma_semaphore_signal_ctrl_t,
132    virtio_magma_semaphore_signal_resp_t, virtmagma_buffer_set_name_wrapper,
133};
134use starnix_core::fileops_impl_nonseekable;
135use starnix_core::fs::fuchsia::sync_file::{SyncFence, SyncFile, SyncPoint, Timeline};
136use starnix_core::fs::fuchsia::{
137    AnonymousRemoteFileObject, RemoteFileObject, RemoteZxioFileObject,
138};
139use starnix_core::mm::memory::MemoryObject;
140use starnix_core::mm::{MemoryAccessorExt, ProtectionFlags};
141use starnix_core::task::CurrentTask;
142use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
143use starnix_core::vfs::{
144    FdFlags, FdNumber, FileObject, FileOps, FsNode, MemoryRegularFile, fileops_impl_noop_sync,
145};
146use starnix_lifecycle::AtomicCounter;
147use starnix_logging::{impossible_error, log_error, log_warn, track_stub};
148use starnix_sync::{
149    LockDepMutex, MagmaBuffersLock, MagmaConnectionsLock, MagmaDevicesLock, MagmaSemaphoresLock,
150};
151use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
152use starnix_types::user_buffer::UserBuffer;
153use starnix_uapi::device_id::DeviceId;
154use starnix_uapi::errors::Errno;
155use starnix_uapi::open_flags::OpenFlags;
156use starnix_uapi::user_address::{UserAddress, UserRef};
157use starnix_uapi::{errno, error};
158use std::collections::HashMap;
159use std::sync::{Arc, Once};
160use zerocopy::IntoBytes;
161
162#[derive(Clone)]
163pub enum BufferInfo {
164    Default,
165    Image(ImageInfo),
166}
167
168/// A `MagmaConnection` is an RAII wrapper around a `magma_connection_t`.
169pub struct MagmaConnection {
170    pub handle: magma_connection_t,
171}
172
173impl Drop for MagmaConnection {
174    fn drop(&mut self) {
175        #[allow(clippy::undocumented_unsafe_blocks)]
176        unsafe {
177            magma_connection_release(self.handle)
178        }
179    }
180}
181
182/// A `MagmaDevice` is an RAII wrapper around a `magma_device_t`.
183pub struct MagmaDevice {
184    pub handle: magma_device_t,
185}
186
187impl Drop for MagmaDevice {
188    /// SAFETY: Makes an FFI call to release a handle that was imported using `magma_device_import`.
189    fn drop(&mut self) {
190        #[allow(clippy::undocumented_unsafe_blocks)]
191        unsafe {
192            magma_device_release(self.handle)
193        }
194    }
195}
196
197/// A `MagmaBuffer` is an RAII wrapper around a `magma_buffer_t`.
198pub struct MagmaBuffer {
199    // This reference is needed to release the buffer.
200    pub connection: Arc<MagmaConnection>,
201    pub handle: magma_buffer_t,
202}
203
204impl Drop for MagmaBuffer {
205    /// SAFETY: Makes an FFI call to release a a `magma_buffer_t` handle. `connection.handle` must
206    /// be valid because `connection` is refcounted.
207    fn drop(&mut self) {
208        #[allow(clippy::undocumented_unsafe_blocks)]
209        unsafe {
210            magma_connection_release_buffer(self.connection.handle, self.handle)
211        }
212    }
213}
214
215/// A `MagmaSemaphore` is an RAII wrapper around one or more `magma_semaphore_t`.  Multiple are
216/// supported because a sync file may be imported.
217pub struct MagmaSemaphore {
218    // This reference is needed to release the semaphores.
219    pub connection: Arc<MagmaConnection>,
220    pub handles: Vec<magma_semaphore_t>,
221    pub ids: Vec<magma_semaphore_id_t>,
222}
223
224impl Drop for MagmaSemaphore {
225    /// SAFETY: Makes an FFI call to release the `magma_semaphore_t` handles. `connection.handle` must
226    /// be valid because `connection` is refcounted.
227    fn drop(&mut self) {
228        for handle in &self.handles {
229            #[allow(clippy::undocumented_unsafe_blocks)]
230            unsafe {
231                magma_connection_release_semaphore(self.connection.handle, *handle)
232            }
233        }
234    }
235}
236
237/// A `BufferMap` stores all the magma buffers for a given connection.
238type BufferMap = HashMap<magma_buffer_id_t, BufferInfo>;
239
240/// A `ConnectionMap` stores the `ConnectionInfo`s associated with each magma connection.
241pub type ConnectionMap = HashMap<u64, ConnectionInfo>;
242
243pub struct ConnectionInfo {
244    pub connection: Arc<MagmaConnection>,
245    pub buffer_map: BufferMap,
246}
247
248impl ConnectionInfo {
249    pub fn new(connection: Arc<MagmaConnection>) -> Self {
250        Self { connection, buffer_map: HashMap::new() }
251    }
252}
253
254pub type DeviceMap = HashMap<u64, Arc<MagmaDevice>>;
255
256pub struct MagmaFile {
257    supported_vendors: Vec<u16>,
258    devices: Arc<LockDepMutex<DeviceMap, MagmaDevicesLock>>,
259    connections: Arc<LockDepMutex<ConnectionMap, MagmaConnectionsLock>>,
260    buffers: Arc<LockDepMutex<HashMap<magma_buffer_id_t, Arc<MagmaBuffer>>, MagmaBuffersLock>>,
261    semaphores:
262        Arc<LockDepMutex<HashMap<magma_semaphore_t, Arc<MagmaSemaphore>>, MagmaSemaphoresLock>>,
263    semaphore_id_generator: AtomicCounter<u64>,
264}
265
266impl MagmaFile {
267    pub fn init() {
268        // Enable the magma client library to emit logs for debug and error cases.
269        let (server_end, client_end) = zx::Channel::create();
270
271        let result = fuchsia_component::client::connect_channel_to_protocol::<
272            fidl_fuchsia_logger::LogSinkMarker,
273        >(server_end);
274
275        if result.is_ok() {
276            #[allow(clippy::undocumented_unsafe_blocks)]
277            unsafe {
278                magma_initialize_logging(client_end.into_raw());
279            }
280        }
281    }
282
283    pub fn new_file(
284        _current_task: &CurrentTask,
285        _dev: DeviceId,
286        _node: &FsNode,
287        _flags: OpenFlags,
288        supported_vendors: Vec<u16>,
289    ) -> Result<Box<dyn FileOps>, Errno> {
290        static INIT: Once = Once::new();
291        INIT.call_once(|| {
292            Self::init();
293        });
294
295        Ok(Box::new(Self {
296            supported_vendors,
297            devices: Default::default(),
298            connections: Default::default(),
299            buffers: Default::default(),
300            semaphores: Default::default(),
301            semaphore_id_generator: AtomicCounter::new(1),
302        }))
303    }
304
305    /// Returns a duplicate of the VMO associated with the file at `fd`, as well as a `BufferInfo`
306    /// of the correct type for that file.
307    ///
308    /// Returns an error if the file does not contain a buffer.
309    fn get_memory_and_magma_buffer(
310        current_task: &CurrentTask,
311        fd: FdNumber,
312    ) -> Result<(MemoryObject, BufferInfo), Errno> {
313        let file = current_task.files().get(fd)?;
314        if let Some(file) = file.downcast_file::<ImageFile>() {
315            let buffer = BufferInfo::Image(file.info.clone());
316            Ok((
317                file.memory.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?,
318                buffer,
319            ))
320        } else if let Some(file) = file.downcast_file::<MemoryRegularFile>() {
321            let buffer = BufferInfo::Default;
322            Ok((
323                file.memory.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?,
324                buffer,
325            ))
326        } else if file.downcast_file::<RemoteFileObject>().is_some()
327            || file.downcast_file::<RemoteZxioFileObject>().is_some()
328            || file.downcast_file::<AnonymousRemoteFileObject>().is_some()
329        {
330            // TODO: Currently this does not preserve BufferInfo::Image fields across allocation via
331            // HIDL/AIDL gralloc followed by import here. If that turns out to be needed, we can add
332            // to system the ability to get ImageFormatConstraints from a sysmem VMO, which could be
333            // used instead of ImageFile. Or if not needed, maybe we can remove ImageFile without
334            // any replacement.
335            //
336            // TODO: Consider if we can have binder related code in starnix use MemoryRegularFile for
337            // any FD wrapping a VMO, or if that's not workable, we may want to have magma related
338            // code use RemoteFileObject.
339            let buffer = BufferInfo::Default;
340            // Map any failure to EINVAL; any failure here is most likely to be an FD that isn't
341            // a gralloc buffer.
342            let memory = file
343                .get_memory(current_task, None, ProtectionFlags::READ | ProtectionFlags::WRITE)
344                .map_err(|_| errno!(EINVAL))?;
345            Ok((
346                memory.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?,
347                buffer,
348            ))
349        } else {
350            error!(EINVAL)
351        }
352    }
353
354    /// Adds a `BufferInfo` for the given `magma_buffer_t`, associated with the specified
355    /// connection.  Note, the hashmaps key type is magma_device_t and the values we use for this
356    /// type are actually the magma buffer ids, because we don't want to expose object-pointers
357    /// to userspace.
358    fn add_buffer_info(
359        &self,
360        connection_id: u64,
361        connection: Arc<MagmaConnection>,
362        buffer: magma_buffer_t,
363        buffer_id: magma_buffer_id_t,
364        buffer_info: BufferInfo,
365    ) {
366        let arc_buffer = Arc::new(MagmaBuffer { connection, handle: buffer });
367        self.connections
368            .lock()
369            .get_mut(&connection_id)
370            .map(|connection_info| connection_info.buffer_map.insert(buffer_id, buffer_info));
371        self.buffers.lock().insert(buffer_id, arc_buffer);
372    }
373
374    fn get_device(&self, device_id: u64) -> Result<Arc<MagmaDevice>, Errno> {
375        Ok(self.devices.lock().get(&device_id).ok_or_else(|| errno!(EINVAL))?.clone())
376    }
377
378    fn get_connection(&self, connection: u64) -> Result<Arc<MagmaConnection>, Errno> {
379        Ok(self
380            .connections
381            .lock()
382            .get(&connection)
383            .ok_or_else(|| errno!(EINVAL))?
384            .connection
385            .clone())
386    }
387
388    fn get_buffer(&self, buffer: magma_buffer_t) -> Result<Arc<MagmaBuffer>, Errno> {
389        Ok(self.buffers.lock().get(&buffer).ok_or_else(|| errno!(EINVAL))?.clone())
390    }
391
392    fn get_semaphore(&self, semaphore: magma_semaphore_t) -> Result<Arc<MagmaSemaphore>, i32> {
393        Ok(self.semaphores.lock().get(&semaphore).ok_or(MAGMA_STATUS_INVALID_ARGS)?.clone())
394    }
395
396    fn import_semaphore2(
397        &self,
398        current_task: &CurrentTask,
399        control: &virtio_magma_connection_import_semaphore2_ctrl_t,
400        response: &mut virtio_magma_connection_import_semaphore2_resp_t,
401    ) {
402        let mut status: i32 = MAGMA_STATUS_OK;
403
404        let fd = FdNumber::from_raw(control.semaphore_handle as i32);
405        let mut result_semaphore_id = 0;
406
407        if let (Ok(connection), Ok(file)) =
408            (self.get_connection(control.connection), current_task.files().get(fd))
409        {
410            let mut handles: Vec<magma_semaphore_t> = vec![];
411            let mut ids: Vec<magma_semaphore_id_t> = vec![];
412
413            if let Some(sync_file) = file.downcast_file::<SyncFile>() {
414                for sync_point in &sync_file.fence.sync_points {
415                    if let Ok(counter) =
416                        sync_point.counter.duplicate_handle(zx::Rights::SAME_RIGHTS)
417                    {
418                        if control.flags & MAGMA_IMPORT_SEMAPHORE_ONE_SHOT == 0 {
419                            // For most non-test cases, one shot should be specified.
420                            log_warn!(
421                                "Importing magma semaphore without MAGMA_IMPORT_SEMAPHORE_ONE_SHOT"
422                            );
423                        }
424                        let semaphore;
425                        let semaphore_id;
426                        (status, semaphore, semaphore_id) =
427                            import_semaphore2(&connection, counter, control.flags);
428                        if status != MAGMA_STATUS_OK {
429                            break;
430                        }
431                        handles.push(semaphore as magma_semaphore_t);
432                        ids.push(semaphore_id as magma_semaphore_id_t);
433                    } else {
434                        status = MAGMA_STATUS_MEMORY_ERROR;
435                        break;
436                    }
437                }
438            } else {
439                status = MAGMA_STATUS_INVALID_ARGS;
440            }
441
442            if status == MAGMA_STATUS_OK {
443                result_semaphore_id = self.semaphore_id_generator.next();
444
445                self.semaphores.lock().insert(
446                    result_semaphore_id,
447                    Arc::new(MagmaSemaphore { connection, handles, ids }),
448                );
449            }
450        } else {
451            status = MAGMA_STATUS_INVALID_ARGS;
452        }
453
454        // Import is expected to close the file that was imported.
455        let _ = current_task.files().close(fd);
456
457        response.result_return = status as u64;
458        response.semaphore_out = result_semaphore_id;
459        response.id_out = result_semaphore_id;
460        response.hdr.type_ =
461            virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_IMPORT_SEMAPHORE2 as u32;
462    }
463}
464
465impl FileOps for MagmaFile {
466    fileops_impl_nonseekable!();
467    fileops_impl_noop_sync!();
468
469    fn ioctl(
470        &self,
471        _file: &FileObject,
472        current_task: &CurrentTask,
473        _request: u32,
474        arg: SyscallArg,
475    ) -> Result<SyscallResult, Errno> {
476        let user_addr = UserAddress::from(arg);
477        let (command, command_type) = read_magma_command_and_type(current_task, user_addr)?;
478        let response_address = UserAddress::from(command.response_address);
479
480        match command_type {
481            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_IMPORT => {
482                let (control, mut response): (
483                    virtio_magma_device_import_ctrl_t,
484                    virtio_magma_device_import_resp_t,
485                ) = read_control_and_response(current_task, &command)?;
486
487                let device = device_import(&self.supported_vendors, control)?;
488
489                {
490                    let mut device_id = 0u64;
491                    let mut retry_count = 0u64;
492                    let mut device_map = self.devices.lock();
493
494                    while device_id == 0 || device_map.contains_key(&device_id) {
495                        starnix_crypto::cprng_draw(device_id.as_mut_bytes());
496                        retry_count += 1;
497                        if retry_count % 10 == 0 {
498                            log_warn!("Too many retries generating device id: {}", retry_count);
499                        }
500                    }
501
502                    device_map.insert(device_id, Arc::new(device));
503                    response.device_out = device_id;
504                }
505
506                response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_IMPORT as u32;
507
508                current_task.write_object(UserRef::new(response_address), &response)
509            }
510            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_CREATE_CONNECTION => {
511                let (control, mut response): (
512                    virtio_magma_device_create_connection_ctrl,
513                    virtio_magma_device_create_connection_resp_t,
514                ) = read_control_and_response(current_task, &command)?;
515
516                let device = self.get_device(control.device)?;
517
518                if let Ok(connection) = create_connection(device.handle) {
519                    let mut connection_id = 0u64;
520                    let mut retry_count = 0u64;
521                    let mut connection_map = self.connections.lock();
522
523                    while connection_id == 0 || connection_map.contains_key(&connection_id) {
524                        starnix_crypto::cprng_draw(connection_id.as_mut_bytes());
525                        retry_count += 1;
526                        if retry_count % 10 == 0 {
527                            log_warn!("Too many retries generating connection id: {}", retry_count);
528                        }
529                    }
530
531                    connection_map.insert(connection_id, ConnectionInfo::new(Arc::new(connection)));
532                    response.connection_out = connection_id;
533                    response.result_return = MAGMA_STATUS_OK as u64;
534                } else {
535                    response.result_return = MAGMA_STATUS_MEMORY_ERROR as u64;
536                }
537                response.hdr.type_ =
538                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_CREATE_CONNECTION as u32;
539
540                current_task.write_object(UserRef::new(response_address), &response)
541            }
542            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE => {
543                let (control, mut response): (
544                    virtio_magma_connection_release_ctrl_t,
545                    virtio_magma_connection_release_resp_t,
546                ) = read_control_and_response(current_task, &command)?;
547
548                let connection_id = control.connection;
549                // Dropping the MagmaConnection will call magma_connection_release via FFI.
550                self.connections.lock().remove(&connection_id);
551
552                response.hdr.type_ =
553                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE as u32;
554
555                current_task.write_object(UserRef::new(response_address), &response)
556            }
557            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_RELEASE => {
558                let (control, mut response): (
559                    virtio_magma_device_release_ctrl_t,
560                    virtio_magma_device_release_resp_t,
561                ) = read_control_and_response(current_task, &command)?;
562
563                let device_id = control.device;
564                // Dropping the MagmaDevice will call magma_device_release via FFI.
565                self.devices.lock().remove(&device_id);
566                response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_RELEASE as u32;
567
568                current_task.write_object(UserRef::new(response_address), &response)
569            }
570            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_FLUSH => {
571                let (control, mut response): (
572                    virtio_magma_connection_flush_ctrl_t,
573                    virtio_magma_connection_flush_resp_t,
574                ) = read_control_and_response(current_task, &command)?;
575                let connection = self.get_connection(control.connection)?;
576
577                flush(control, &mut response, &connection);
578
579                current_task.write_object(UserRef::new(response_address), &response)
580            }
581            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_READ_NOTIFICATION_CHANNEL => {
582                let (control, mut response): (
583                    virtio_magma_connection_read_notification_channel_ctrl_t,
584                    virtio_magma_connection_read_notification_channel_resp_t,
585                ) = read_control_and_response(current_task, &command)?;
586                let connection = self.get_connection(control.connection)?;
587
588                read_notification_channel(current_task, control, &mut response, &connection)?;
589
590                current_task.write_object(UserRef::new(response_address), &response)
591            }
592            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_HANDLE => {
593                let (control, mut response): (
594                    virtio_magma_buffer_get_handle_ctrl_t,
595                    virtio_magma_buffer_get_handle_resp_t,
596                ) = read_control_and_response(current_task, &command)?;
597                let buffer = self.get_buffer(control.buffer)?;
598
599                get_buffer_handle(current_task, control, &mut response, &buffer)?;
600
601                current_task.write_object(UserRef::new(response_address), &response)
602            }
603            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_BUFFER => {
604                let (control, mut response): (
605                    virtio_magma_connection_release_buffer_ctrl_t,
606                    virtio_magma_connection_release_buffer_resp_t,
607                ) = read_control_and_response(current_task, &command)?;
608
609                if let Some(buffers) = self.connections.lock().get_mut(&{ control.connection }) {
610                    match buffers.buffer_map.remove(&{ control.buffer }) {
611                        Some(_) => (),
612                        _ => {
613                            log_error!("Calling magma_release_buffer with an invalid buffer.");
614                        }
615                    };
616                }
617                self.buffers.lock().remove(&{ control.buffer });
618
619                response.hdr.type_ =
620                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_BUFFER as u32;
621
622                current_task.write_object(UserRef::new(response_address), &response)
623            }
624            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_EXPORT => {
625                let (control, mut response): (
626                    virtio_magma_buffer_export_ctrl_t,
627                    virtio_magma_buffer_export_resp_t,
628                ) = read_control_and_response(current_task, &command)?;
629                let buffer = self.get_buffer(control.buffer)?;
630
631                export_buffer(
632                    current_task,
633                    control,
634                    &mut response,
635                    &buffer,
636                    &self.connections.lock(),
637                )?;
638
639                current_task.write_object(UserRef::new(response_address), &response)
640            }
641            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_IMPORT_BUFFER => {
642                let (control, mut response): (
643                    virtio_magma_connection_import_buffer_ctrl_t,
644                    virtio_magma_connection_import_buffer_resp_t,
645                ) = read_control_and_response(current_task, &command)?;
646                let connection_id = control.connection;
647                let connection = self.get_connection(connection_id)?;
648
649                let buffer_fd = FdNumber::from_raw(control.buffer_handle as i32);
650                let (memory, buffer) =
651                    MagmaFile::get_memory_and_magma_buffer(current_task, buffer_fd)?;
652                let vmo = memory.into_vmo().ok_or_else(|| errno!(EINVAL))?;
653
654                let mut buffer_out = magma_buffer_t::default();
655                let mut size_out = 0u64;
656                let mut id_out = magma_buffer_id_t::default();
657                response.result_return = {
658                    #[allow(clippy::undocumented_unsafe_blocks)]
659                    unsafe {
660                        magma_connection_import_buffer(
661                            connection.handle,
662                            vmo.into_raw(),
663                            &mut size_out,
664                            &mut buffer_out,
665                            &mut id_out,
666                        ) as u64
667                    }
668                };
669
670                // Store the information for the newly imported buffer.
671                self.add_buffer_info(connection_id, connection, buffer_out, id_out, buffer);
672                // Import is expected to close the file that was imported.
673                let _ = current_task.files().close(buffer_fd);
674
675                response.buffer_out = id_out;
676                response.id_out = id_out;
677                response.size_out = size_out;
678                response.hdr.type_ =
679                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_IMPORT_BUFFER as u32;
680                current_task.write_object(UserRef::new(response_address), &response)
681            }
682            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_GET_NOTIFICATION_CHANNEL_HANDLE => {
683                let (control, mut response): (
684                    virtio_magma_connection_get_notification_channel_handle_ctrl_t,
685                    virtio_magma_connection_get_notification_channel_handle_resp_t,
686                ) = read_control_and_response(current_task, &command)?;
687                let connection = self.get_connection(control.connection)?;
688
689                response.result_return = {
690                    #[allow(clippy::undocumented_unsafe_blocks)]
691                    unsafe {
692                        magma_connection_get_notification_channel_handle(connection.handle)
693                    }
694                };
695
696                response.hdr.type_ =
697                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_GET_NOTIFICATION_CHANNEL_HANDLE as u32;
698                current_task.write_object(UserRef::new(response_address), &response)
699            }
700            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_CONTEXT => {
701                let (control, mut response): (
702                    virtio_magma_connection_create_context_ctrl_t,
703                    virtio_magma_connection_create_context_resp_t,
704                ) = read_control_and_response(current_task, &command)?;
705                let connection = self.get_connection(control.connection)?;
706
707                let mut context_id_out = 0;
708                response.result_return = {
709                    #[allow(clippy::undocumented_unsafe_blocks)]
710                    unsafe {
711                        magma_connection_create_context(connection.handle, &mut context_id_out)
712                            as u64
713                    }
714                };
715                response.context_id_out = context_id_out as u64;
716
717                response.hdr.type_ =
718                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_CONTEXT as u32;
719                current_task.write_object(UserRef::new(response_address), &response)
720            }
721            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_CONTEXT2 => {
722                let (control, mut response): (
723                    virtio_magma_connection_create_context2_ctrl_t,
724                    virtio_magma_connection_create_context2_resp_t,
725                ) = read_control_and_response(current_task, &command)?;
726                let connection = self.get_connection(control.connection)?;
727
728                // TODO(b/402461734) - remove this workaround when trusted clients are supported.
729                let priority = if control.priority > MAGMA_PRIORITY_MEDIUM {
730                    MAGMA_PRIORITY_MEDIUM
731                } else {
732                    control.priority
733                };
734
735                let mut context_id_out = 0;
736                response.result_return = {
737                    #[allow(clippy::undocumented_unsafe_blocks)]
738                    unsafe {
739                        magma_connection_create_context2(
740                            connection.handle,
741                            priority,
742                            &mut context_id_out,
743                        ) as u64
744                    }
745                };
746                response.context_id_out = context_id_out as u64;
747
748                response.hdr.type_ =
749                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_CONTEXT2 as u32;
750                current_task.write_object(UserRef::new(response_address), &response)
751            }
752            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_CONTEXT => {
753                let (control, mut response): (
754                    virtio_magma_connection_release_context_ctrl_t,
755                    virtio_magma_connection_release_context_resp_t,
756                ) = read_control_and_response(current_task, &command)?;
757                let connection = self.get_connection(control.connection)?;
758
759                #[allow(clippy::undocumented_unsafe_blocks)]
760                unsafe {
761                    magma_connection_release_context(connection.handle, control.context_id);
762                }
763
764                response.hdr.type_ =
765                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_CONTEXT as u32;
766                current_task.write_object(UserRef::new(response_address), &response)
767            }
768            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_BUFFER => {
769                let (control, mut response): (
770                    virtio_magma_connection_create_buffer_ctrl_t,
771                    virtio_magma_connection_create_buffer_resp_t,
772                ) = read_control_and_response(current_task, &command)?;
773                let connection_id = control.connection;
774                let connection = self.get_connection(connection_id)?;
775
776                let mut size_out = 0;
777                let mut buffer_out = 0;
778                let mut id_out = 0;
779                response.result_return = {
780                    #[allow(clippy::undocumented_unsafe_blocks)]
781                    unsafe {
782                        magma_connection_create_buffer(
783                            connection.handle,
784                            control.size,
785                            &mut size_out,
786                            &mut buffer_out,
787                            &mut id_out,
788                        ) as u64
789                    }
790                };
791                response.size_out = size_out;
792                response.buffer_out = id_out;
793                response.id_out = id_out;
794
795                self.add_buffer_info(
796                    connection_id,
797                    connection,
798                    buffer_out,
799                    id_out,
800                    BufferInfo::Default,
801                );
802
803                response.hdr.type_ =
804                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_BUFFER as u32;
805                current_task.write_object(UserRef::new(response_address), &response)
806            }
807            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_CREATE_SEMAPHORE => {
808                let (control, mut response): (
809                    virtio_magma_connection_create_semaphore_ctrl_t,
810                    virtio_magma_connection_create_semaphore_resp_t,
811                ) = read_control_and_response(current_task, &command)?;
812                let connection = self.get_connection(control.connection)?;
813                let status: i32;
814                let mut result_semaphore_id = 0;
815
816                // Use counter semaphores for compatibility with sync files, which need timestamps.
817                let counter = zx::Counter::create();
818                let flags: u64 = 0;
819                let semaphore;
820                let semaphore_id;
821                (status, semaphore, semaphore_id) = import_semaphore2(&connection, counter, flags);
822                if status == MAGMA_STATUS_OK {
823                    result_semaphore_id = self.semaphore_id_generator.next();
824
825                    self.semaphores.lock().insert(
826                        result_semaphore_id,
827                        Arc::new(MagmaSemaphore {
828                            connection,
829                            handles: vec![semaphore; 1],
830                            ids: vec![semaphore_id; 1],
831                        }),
832                    );
833                }
834                response.result_return = status as u64;
835                response.semaphore_out = result_semaphore_id;
836                response.id_out = result_semaphore_id;
837                response.hdr.type_ =
838                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_CREATE_SEMAPHORE as u32;
839                current_task.write_object(UserRef::new(response_address), &response)
840            }
841            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_GET_ERROR => {
842                let (control, mut response): (
843                    virtio_magma_connection_get_error_ctrl_t,
844                    virtio_magma_connection_get_error_resp_t,
845                ) = read_control_and_response(current_task, &command)?;
846                let connection = self.get_connection(control.connection)?;
847
848                response.result_return = {
849                    #[allow(clippy::undocumented_unsafe_blocks)]
850                    unsafe {
851                        magma_connection_get_error(connection.handle) as u64
852                    }
853                };
854
855                response.hdr.type_ =
856                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_GET_ERROR as u32;
857                current_task.write_object(UserRef::new(response_address), &response)
858            }
859            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_IMPORT_SEMAPHORE2 => {
860                let (control, mut response): (
861                    virtio_magma_connection_import_semaphore2_ctrl_t,
862                    virtio_magma_connection_import_semaphore2_resp_t,
863                ) = read_control_and_response(current_task, &command)?;
864
865                self.import_semaphore2(current_task, &control, &mut response);
866
867                current_task.write_object(UserRef::new(response_address), &response)
868            }
869            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_RELEASE_SEMAPHORE => {
870                let (control, mut response): (
871                    virtio_magma_connection_release_semaphore_ctrl_t,
872                    virtio_magma_connection_release_semaphore_resp_t,
873                ) = read_control_and_response(current_task, &command)?;
874
875                self.semaphores.lock().remove(&{ control.semaphore });
876
877                response.hdr.type_ =
878                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_RELEASE_SEMAPHORE as u32;
879                current_task.write_object(UserRef::new(response_address), &response)
880            }
881            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_EXPORT => {
882                let (control, mut response): (
883                    virtio_magma_semaphore_export_ctrl_t,
884                    virtio_magma_semaphore_export_resp_t,
885                ) = read_control_and_response(current_task, &command)?;
886                let mut status: i32 = MAGMA_STATUS_OK;
887
888                let mut sync_points: Vec<SyncPoint> = vec![];
889                let mut sync_file_fd: i32 = -1;
890
891                match self.get_semaphore(control.semaphore) {
892                    Ok(semaphore) => {
893                        for handle in &semaphore.handles {
894                            let mut raw_handle = 0;
895                            status = {
896                                #[allow(clippy::undocumented_unsafe_blocks)]
897                                unsafe {
898                                    magma_semaphore_export(*handle, &mut raw_handle)
899                                }
900                            };
901                            if status != MAGMA_STATUS_OK {
902                                break;
903                            }
904                            let handle = {
905                                #[allow(clippy::undocumented_unsafe_blocks)]
906                                unsafe {
907                                    zx::NullableHandle::from_raw(raw_handle)
908                                }
909                            };
910
911                            sync_points.push(SyncPoint::new(Timeline::Magma, handle.into()));
912                        }
913                    }
914                    Err(s) => status = s,
915                }
916
917                if status == MAGMA_STATUS_OK {
918                    let sync_file_name: &[u8; 32] =
919                        b"magma semaphore\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
920                    let file = SyncFile::new_file(
921                        current_task,
922                        *sync_file_name,
923                        SyncFence { sync_points },
924                    )?;
925
926                    let fd = current_task.add_file(file, FdFlags::empty())?;
927                    sync_file_fd = fd.raw();
928                }
929
930                response.result_return = status as u64;
931                response.semaphore_handle_out = sync_file_fd as u64;
932                response.hdr.type_ =
933                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_EXPORT as u32;
934                current_task.write_object(UserRef::new(response_address), &response)
935            }
936            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_RESET => {
937                let (control, mut response): (
938                    virtio_magma_semaphore_reset_ctrl_t,
939                    virtio_magma_semaphore_reset_resp_t,
940                ) = read_control_and_response(current_task, &command)?;
941
942                if let Ok(semaphore) = self.get_semaphore(control.semaphore) {
943                    for handle in &semaphore.handles {
944                        #[allow(clippy::undocumented_unsafe_blocks)]
945                        unsafe {
946                            magma_semaphore_reset(*handle);
947                        }
948                    }
949                }
950                response.hdr.type_ =
951                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_RESET as u32;
952                current_task.write_object(UserRef::new(response_address), &response)
953            }
954            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SEMAPHORE_SIGNAL => {
955                let (control, mut response): (
956                    virtio_magma_semaphore_signal_ctrl_t,
957                    virtio_magma_semaphore_signal_resp_t,
958                ) = read_control_and_response(current_task, &command)?;
959
960                if let Ok(semaphore) = self.get_semaphore(control.semaphore) {
961                    for handle in &semaphore.handles {
962                        #[allow(clippy::undocumented_unsafe_blocks)]
963                        unsafe {
964                            magma_semaphore_signal(*handle);
965                        }
966                    }
967                }
968                response.hdr.type_ =
969                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SEMAPHORE_SIGNAL as u32;
970                current_task.write_object(UserRef::new(response_address), &response)
971            }
972            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_MAP_BUFFER => {
973                let (control, mut response): (
974                    virtio_magma_connection_map_buffer_ctrl_t,
975                    virtio_magma_connection_map_buffer_resp_t,
976                ) = read_control_and_response(current_task, &command)?;
977                let connection = self.get_connection(control.connection)?;
978                let buffer = self.get_buffer(control.buffer)?;
979
980                response.result_return = {
981                    #[allow(clippy::undocumented_unsafe_blocks)]
982                    unsafe {
983                        magma_connection_map_buffer(
984                            connection.handle,
985                            control.hw_va,
986                            buffer.handle,
987                            control.offset,
988                            control.length,
989                            control.map_flags,
990                        ) as u64
991                    }
992                };
993
994                response.hdr.type_ =
995                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_MAP_BUFFER as u32;
996                current_task.write_object(UserRef::new(response_address), &response)
997            }
998            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_POLL => {
999                let (control, mut response): (virtio_magma_poll_ctrl_t, virtio_magma_poll_resp_t) =
1000                    read_control_and_response(current_task, &command)?;
1001
1002                let num_items = control.count as usize / std::mem::size_of::<StarnixPollItem>();
1003                let items_ref = UserRef::<StarnixPollItem>::new(UserAddress::from(control.items));
1004                // Read the poll items as `StarnixPollItem`, since they contain a union. Also note
1005                // that the minimum length of the vector is 1, to always have a valid reference for
1006                // `magma_poll`.
1007                let starnix_items =
1008                    current_task.read_objects_to_vec(items_ref, std::cmp::max(num_items, 1))?;
1009                // Then convert each item "manually" into `magma_poll_item_t`.
1010                let mut magma_items: Vec<magma_poll_item_t> =
1011                    starnix_items.iter().map(|item| item.as_poll_item()).collect();
1012
1013                // Expand semaphores.
1014                let mut status: i32 = MAGMA_STATUS_OK;
1015                let mut child_semaphore_items: Vec<magma_poll_item_t> = vec![];
1016
1017                for i in 0..magma_items.len() {
1018                    magma_items[i].result = 0;
1019
1020                    if magma_items[i].type_ == MAGMA_POLL_TYPE_SEMAPHORE
1021                        && magma_items[i].condition == MAGMA_POLL_CONDITION_SIGNALED
1022                    {
1023                        match self.get_semaphore(starnix_items[i].semaphore_or_handle) {
1024                            Ok(semaphore) => {
1025                                magma_items[i].condition = 0; // magma_poll must ignore this item
1026
1027                                // Store the number of expanded semaphores to be signaled
1028                                let handles_ref = &semaphore.handles;
1029                                let child_count = handles_ref.len() as u32;
1030                                magma_items[i].unused = child_count;
1031
1032                                for handle in handles_ref {
1033                                    child_semaphore_items.push(
1034                                        StarnixPollItem {
1035                                            semaphore_or_handle: *handle,
1036                                            type_: MAGMA_POLL_TYPE_SEMAPHORE,
1037                                            condition: MAGMA_POLL_CONDITION_SIGNALED,
1038                                            result: 0,
1039                                            // Points back to the parent item
1040                                            unused: i as u32,
1041                                        }
1042                                        .as_poll_item(),
1043                                    );
1044                                }
1045                            }
1046                            Err(s) => status = s,
1047                        }
1048                    }
1049                }
1050
1051                if status == MAGMA_STATUS_OK {
1052                    magma_items.append(&mut child_semaphore_items);
1053
1054                    let abs_timeout_ns = if control.timeout_ns == u64::MAX {
1055                        0
1056                    } else {
1057                        zx::MonotonicInstant::get().into_nanos() as u64 + control.timeout_ns
1058                    };
1059
1060                    'outer: while status == MAGMA_STATUS_OK {
1061                        // Iterate from the end to process child semaphores first
1062                        for i in (0..magma_items.len()).rev() {
1063                            if i < num_items && magma_items[i].result > 0 {
1064                                // A handle or parent semaphore is signaled, we're done
1065                                break 'outer;
1066                            } else if magma_items[i].result > 0 {
1067                                // A child semaphore is signaled
1068                                assert_eq!(magma_items[i].condition, MAGMA_POLL_CONDITION_SIGNALED);
1069                                let parent_index = magma_items[i].unused as usize;
1070                                assert_ne!(magma_items[parent_index].unused, 0);
1071                                magma_items[parent_index].unused -= 1;
1072                                if magma_items[parent_index].unused == 0 {
1073                                    // All children signaled, signal the parent
1074                                    magma_items[parent_index].result = magma_items[i].result;
1075                                }
1076                                // Don't poll on this child again
1077                                magma_items[i].condition = 0;
1078                            }
1079                        }
1080
1081                        let current_time_ns = zx::MonotonicInstant::get().into_nanos() as u64;
1082                        let rel_timeout_ns = if abs_timeout_ns == 0 {
1083                            u64::MAX
1084                        } else if abs_timeout_ns > current_time_ns {
1085                            abs_timeout_ns - current_time_ns
1086                        } else {
1087                            0
1088                        };
1089
1090                        // Force EINTR every second to allow signals to interrupt the wait.
1091                        // TODO(https://fxbug.dev/42080364): Only interrupt the wait when needed.
1092                        let capped_rel_timeout_ns = std::cmp::min(rel_timeout_ns, 1_000_000_000);
1093
1094                        status = {
1095                            #[allow(clippy::undocumented_unsafe_blocks)]
1096                            unsafe {
1097                                magma_poll(
1098                                    &mut magma_items[0] as *mut magma_poll_item,
1099                                    magma_items.len() as u32,
1100                                    capped_rel_timeout_ns,
1101                                )
1102                            }
1103                        };
1104                        let current_time = zx::MonotonicInstant::get().into_nanos();
1105
1106                        // Check if the wait timed out before the user-requested timeout.
1107                        if status == MAGMA_STATUS_TIMED_OUT
1108                            && (control.timeout_ns == u64::MAX
1109                                || (current_time as u64) < abs_timeout_ns)
1110                        {
1111                            if control.timeout_ns != u64::MAX {
1112                                // Update relative deadline.
1113                                let mut control = control;
1114                                control.timeout_ns = abs_timeout_ns - (current_time as u64);
1115                                let request_address = UserAddress::from(command.request_address);
1116                                let _ = current_task
1117                                    .write_object(UserRef::new(request_address), &control);
1118                            }
1119                            return error!(EINTR);
1120                        }
1121                    }
1122                }
1123
1124                // Walk child items to restore modified parent items
1125                for i in num_items..magma_items.len() {
1126                    assert_eq!(magma_items[i].type_, MAGMA_POLL_TYPE_SEMAPHORE);
1127                    let parent_index = magma_items[i].unused as usize;
1128                    magma_items[parent_index].condition = MAGMA_POLL_CONDITION_SIGNALED;
1129                    magma_items[parent_index].unused = 0;
1130                }
1131
1132                // Remove any child semaphores
1133                magma_items.truncate(num_items);
1134
1135                // Convert the poll items back to a serializable version after the `magma_poll`
1136                // call.
1137                let starnix_items: Vec<StarnixPollItem> =
1138                    magma_items.iter().map(StarnixPollItem::new).collect();
1139                current_task.write_objects(items_ref, &starnix_items)?;
1140
1141                response.result_return = status as u64;
1142                response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_POLL as u32;
1143                current_task.write_object(UserRef::new(response_address), &response)
1144            }
1145            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_COMMAND => {
1146                let (control, mut response): (
1147                    virtio_magma_connection_execute_command_ctrl_t,
1148                    virtio_magma_connection_execute_command_resp_t,
1149                ) = read_control_and_response(current_task, &command)?;
1150                let connection = self.get_connection(control.connection)?;
1151
1152                let status = execute_command(current_task, control, &connection, |semaphore_id| {
1153                    self.get_semaphore(semaphore_id)
1154                })?;
1155
1156                response.result_return = status as u64;
1157                response.hdr.type_ =
1158                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_EXECUTE_COMMAND as u32;
1159                current_task.write_object(UserRef::new(response_address), &response)
1160            }
1161            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_EXECUTE_INLINE_COMMANDS => {
1162                let (control, mut response): (
1163                    virtio_magma_connection_execute_inline_commands_ctrl_t,
1164                    virtio_magma_connection_execute_inline_commands_resp_t,
1165                ) = read_control_and_response(current_task, &command)?;
1166                let connection = self.get_connection(control.connection)?;
1167
1168                let status =
1169                    execute_inline_commands(current_task, control, &connection, |semaphore_id| {
1170                        self.get_semaphore(semaphore_id)
1171                    })?;
1172
1173                response.hdr.type_ =
1174                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_EXECUTE_INLINE_COMMANDS
1175                        as u32;
1176                response.result_return = status as u64;
1177
1178                current_task.write_object(UserRef::new(response_address), &response)
1179            }
1180            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_QUERY => {
1181                let (control, mut response): (
1182                    virtio_magma_device_query_ctrl_t,
1183                    virtio_magma_device_query_resp_t,
1184                ) = read_control_and_response(current_task, &command)?;
1185
1186                let device = self.get_device(control.device)?;
1187
1188                query(current_task, device.handle, control.id, &mut response)?;
1189
1190                response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_QUERY as u32;
1191                current_task.write_object(UserRef::new(response_address), &response)
1192            }
1193            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_UNMAP_BUFFER => {
1194                let (control, mut response): (
1195                    virtio_magma_connection_unmap_buffer_ctrl_t,
1196                    virtio_magma_connection_unmap_buffer_resp_t,
1197                ) = read_control_and_response(current_task, &command)?;
1198                let connection = self.get_connection(control.connection)?;
1199                let buffer = self.get_buffer(control.buffer)?;
1200
1201                #[allow(clippy::undocumented_unsafe_blocks)]
1202                unsafe {
1203                    magma_connection_unmap_buffer(connection.handle, control.hw_va, buffer.handle)
1204                };
1205
1206                response.hdr.type_ =
1207                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_UNMAP_BUFFER as u32;
1208
1209                current_task.write_object(UserRef::new(response_address), &response)
1210            }
1211            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CONNECTION_PERFORM_BUFFER_OP => {
1212                let (control, mut response): (
1213                    virtio_magma_connection_perform_buffer_op_ctrl_t,
1214                    virtio_magma_connection_perform_buffer_op_resp_t,
1215                ) = read_control_and_response(current_task, &command)?;
1216                let connection = self.get_connection(control.connection)?;
1217                let buffer = self.get_buffer(control.buffer)?;
1218
1219                response.result_return = {
1220                    #[allow(clippy::undocumented_unsafe_blocks)]
1221                    unsafe {
1222                        magma_connection_perform_buffer_op(
1223                            connection.handle,
1224                            buffer.handle,
1225                            control.options,
1226                            control.start_offset,
1227                            control.length,
1228                        ) as u64
1229                    }
1230                };
1231
1232                response.hdr.type_ =
1233                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_PERFORM_BUFFER_OP as u32;
1234
1235                current_task.write_object(UserRef::new(response_address), &response)
1236            }
1237            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_INFO => {
1238                let (control, mut response): (
1239                    virtio_magma_buffer_get_info_ctrl_t,
1240                    virtio_magma_buffer_get_info_resp_t,
1241                ) = read_control_and_response(current_task, &command)?;
1242                let buffer = self.get_buffer(control.buffer)?;
1243
1244                let mut buffer_info = magma_buffer_info_t { committed_byte_count: 0, size: 0 };
1245
1246                let status = {
1247                    #[allow(clippy::undocumented_unsafe_blocks)]
1248                    unsafe {
1249                        magma_buffer_get_info(buffer.handle, &mut buffer_info)
1250                    }
1251                };
1252
1253                if status == MAGMA_STATUS_OK {
1254                    current_task.write_object(
1255                        UserRef::<magma_buffer_info_t>::new(UserAddress::from(control.info_out)),
1256                        &buffer_info,
1257                    )?;
1258                }
1259
1260                response.hdr.type_ =
1261                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_INFO as u32;
1262
1263                current_task.write_object(UserRef::new(response_address), &response)
1264            }
1265            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_SET_CACHE_POLICY => {
1266                let (control, mut response): (
1267                    virtio_magma_buffer_set_cache_policy_ctrl_t,
1268                    virtio_magma_buffer_set_cache_policy_resp_t,
1269                ) = read_control_and_response(current_task, &command)?;
1270                let buffer = self.get_buffer(control.buffer)?;
1271
1272                response.result_return = {
1273                    #[allow(clippy::undocumented_unsafe_blocks)]
1274                    unsafe {
1275                        magma_buffer_set_cache_policy(
1276                            buffer.handle,
1277                            control.policy as magma_cache_policy_t,
1278                        ) as u64
1279                    }
1280                };
1281
1282                response.hdr.type_ =
1283                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_SET_CACHE_POLICY as u32;
1284
1285                current_task.write_object(UserRef::new(response_address), &response)
1286            }
1287            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_GET_CACHE_POLICY => {
1288                let (control, mut response): (
1289                    virtio_magma_buffer_get_cache_policy_ctrl_t,
1290                    virtio_magma_buffer_get_cache_policy_resp_t,
1291                ) = read_control_and_response(current_task, &command)?;
1292                let buffer = self.get_buffer(control.buffer)?;
1293
1294                let mut policy: magma_cache_policy_t = MAGMA_CACHE_POLICY_CACHED;
1295
1296                let status = {
1297                    #[allow(clippy::undocumented_unsafe_blocks)]
1298                    unsafe {
1299                        magma_buffer_get_cache_policy(buffer.handle, &mut policy)
1300                    }
1301                };
1302
1303                if status == MAGMA_STATUS_OK {
1304                    response.cache_policy_out = policy as u64;
1305                }
1306                response.result_return = status as u64;
1307                response.hdr.type_ =
1308                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_CACHE_POLICY as u32;
1309
1310                current_task.write_object(UserRef::new(response_address), &response)
1311            }
1312            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_CLEAN_CACHE => {
1313                let (control, mut response): (
1314                    virtio_magma_buffer_clean_cache_ctrl_t,
1315                    virtio_magma_buffer_clean_cache_resp_t,
1316                ) = read_control_and_response(current_task, &command)?;
1317                let buffer = self.get_buffer(control.buffer)?;
1318
1319                response.result_return = {
1320                    #[allow(clippy::undocumented_unsafe_blocks)]
1321                    unsafe {
1322                        magma_buffer_clean_cache(
1323                            buffer.handle,
1324                            control.offset,
1325                            control.size,
1326                            control.operation as magma_cache_operation_t,
1327                        ) as u64
1328                    }
1329                };
1330
1331                response.hdr.type_ =
1332                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_CLEAN_CACHE as u32;
1333
1334                current_task.write_object(UserRef::new(response_address), &response)
1335            }
1336            virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_BUFFER_SET_NAME => {
1337                let (control, mut response): (
1338                    virtio_magma_buffer_set_name_ctrl_t,
1339                    virtio_magma_buffer_set_name_resp_t,
1340                ) = read_control_and_response(current_task, &command)?;
1341                let buffer = self.get_buffer(control.buffer)?;
1342
1343                let wrapper_ref = UserRef::<virtmagma_buffer_set_name_wrapper>::new(
1344                    UserAddress::from(control.name),
1345                );
1346                let wrapper = current_task.read_object(wrapper_ref)?;
1347
1348                let name = current_task.read_buffer(&UserBuffer {
1349                    address: UserAddress::from(wrapper.name_address),
1350                    length: wrapper.name_size as usize, // name_size includes null terminate byte
1351                })?;
1352
1353                response.result_return = {
1354                    #[allow(clippy::undocumented_unsafe_blocks)]
1355                    unsafe {
1356                        let name_ptr = &name[0] as *const u8 as *const std::os::raw::c_char;
1357                        magma_buffer_set_name(buffer.handle, name_ptr) as u64
1358                    }
1359                };
1360
1361                response.hdr.type_ =
1362                    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_SET_NAME as u32;
1363
1364                current_task.write_object(UserRef::new(response_address), &response)
1365            }
1366            t => {
1367                track_stub!(TODO("https://fxbug.dev/322874166"), "virtio magma ioctl", t);
1368                error!(ENOSYS)
1369            }
1370        }?;
1371
1372        Ok(SUCCESS)
1373    }
1374
1375    fn read(
1376        &self,
1377        _file: &FileObject,
1378        _current_task: &CurrentTask,
1379        offset: usize,
1380        _data: &mut dyn OutputBuffer,
1381    ) -> Result<usize, Errno> {
1382        debug_assert!(offset == 0);
1383        error!(EINVAL)
1384    }
1385
1386    fn write(
1387        &self,
1388        _file: &FileObject,
1389        _current_task: &CurrentTask,
1390        offset: usize,
1391        _data: &mut dyn InputBuffer,
1392    ) -> Result<usize, Errno> {
1393        debug_assert!(offset == 0);
1394        error!(EINVAL)
1395    }
1396}