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