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