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