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