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