Skip to main content

starnix_modules_magma/
ffi.rs

1// Copyright 2022 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
5use crate::file::{
6    BufferInfo, ConnectionMap, MagmaBuffer, MagmaConnection, MagmaDevice, MagmaSemaphore,
7};
8use crate::image_file::{ImageFile, ImageInfo};
9use bstr::BString;
10use magma::{
11    MAGMA_QUERY_VENDOR_ID, MAGMA_STATUS_OK, MAGMA_VENDOR_ID_INTEL, MAGMA_VENDOR_ID_MALI,
12    magma_buffer_export, magma_buffer_get_handle, magma_buffer_t, magma_command_descriptor,
13    magma_connection_execute_command, magma_connection_execute_inline_commands,
14    magma_connection_flush, magma_connection_import_semaphore2,
15    magma_connection_read_notification_channel, magma_connection_t, magma_device_create_connection,
16    magma_device_import, magma_device_query, magma_device_t, magma_exec_command_buffer,
17    magma_exec_resource, magma_handle_t, magma_inline_command_buffer, magma_query_t,
18    magma_status_t, virtio_magma_buffer_export_ctrl_t, virtio_magma_buffer_export_resp_t,
19    virtio_magma_buffer_get_handle_ctrl_t, virtio_magma_buffer_get_handle_resp_t,
20    virtio_magma_connection_execute_command_ctrl_t,
21    virtio_magma_connection_execute_inline_commands_ctrl_t, virtio_magma_connection_flush_ctrl_t,
22    virtio_magma_connection_flush_resp_t, virtio_magma_connection_read_notification_channel_ctrl_t,
23    virtio_magma_connection_read_notification_channel_resp_t,
24    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT,
25    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE,
26    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH,
27    virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL,
28    virtio_magma_device_import_ctrl_t, virtio_magma_device_query_resp_t,
29    virtmagma_command_descriptor,
30};
31use starnix_core::mm::memory::MemoryObject;
32use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
33use starnix_core::task::CurrentTask;
34use starnix_core::vfs::{Anon, FdFlags, FsNodeInfo, MemoryRegularFile};
35use starnix_logging::track_stub;
36use starnix_sync::{Locked, Unlocked};
37use starnix_types::user_buffer::UserBuffer;
38use starnix_uapi::errors::Errno;
39use starnix_uapi::file_mode::FileMode;
40use starnix_uapi::open_flags::OpenFlags;
41use starnix_uapi::user_address::{UserAddress, UserRef};
42use starnix_uapi::{errno, error};
43use std::mem::ManuallyDrop;
44use std::sync::Arc;
45use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
46
47/// Reads a sequence of objects starting at `addr`, ensuring at least one element is in the returned
48/// Vec.
49///
50/// # Parameters
51///   - `current_task`: The task from which to read the objects.
52///   - `addr`: The address of the first item to read.
53///   - `item_count`: The number of items to read. If 0, a 1-item vector will be returned to make
54///                   sure that the calling code can safely pass `&mut vec[0]` to libmagma.
55fn read_objects<T>(
56    current_task: &CurrentTask,
57    addr: UserAddress,
58    item_count: usize,
59) -> Result<Vec<T>, Errno>
60where
61    T: Default + Clone + FromBytes,
62{
63    Ok(if item_count > 0 {
64        let user_ref: UserRef<T> = addr.into();
65        current_task.read_objects_to_vec(user_ref, item_count)?
66    } else {
67        vec![T::default()]
68    })
69}
70
71/// Creates a connection for a given device.
72///
73/// # Parameters
74///   - `control`: The control struct containing the device to create a connection to.
75///   - `response`: The struct that will be filled out to contain the response. This struct can be
76///                 written back to userspace.
77///
78/// SAFETY: Makes an FFI call to populate the fields of `response`.
79pub fn create_connection(device: magma_device_t) -> Result<MagmaConnection, magma_status_t> {
80    let mut connection_out: magma_connection_t = 0;
81    let result = {
82        #[allow(
83            clippy::undocumented_unsafe_blocks,
84            reason = "Force documented unsafe blocks in Starnix"
85        )]
86        unsafe {
87            magma_device_create_connection(device, &mut connection_out)
88        }
89    };
90    if result != MAGMA_STATUS_OK {
91        return Err(result);
92    }
93
94    return Ok(MagmaConnection { handle: connection_out });
95}
96
97/// Attempts to open a device at a path. Fails if the device is not a supported one.
98///
99/// # Parameters
100///   - 'path': The filesystem path to open.
101///
102/// SAFETY: Makes FFI calls to import and query devices.
103fn attempt_open_path(
104    path: std::path::PathBuf,
105    supported_vendor_list: &Vec<u16>,
106) -> Result<MagmaDevice, Errno> {
107    let path = path.into_os_string().into_string().map_err(|_| errno!(EINVAL))?;
108    let (client_channel, server_channel) = zx::Channel::create();
109
110    fdio::service_connect(&path, server_channel).map_err(|_| errno!(EINVAL))?;
111    // `magma_device_import` takes ownership of the channel, so don't drop it again.
112    let client_channel = ManuallyDrop::new(client_channel);
113    let device_channel = client_channel.raw_handle();
114
115    let mut device_out: u64 = 0;
116    #[allow(
117        clippy::undocumented_unsafe_blocks,
118        reason = "Force documented unsafe blocks in Starnix"
119    )]
120    let result = unsafe { magma_device_import(device_channel, &mut device_out as *mut u64) };
121
122    if result != MAGMA_STATUS_OK {
123        return error!(EINVAL);
124    }
125    let magma_device = MagmaDevice { handle: device_out };
126
127    let mut result_out = 0;
128    let mut result_buffer_out = 0;
129    #[allow(
130        clippy::undocumented_unsafe_blocks,
131        reason = "Force documented unsafe blocks in Starnix"
132    )]
133    let query_result = unsafe {
134        magma_device_query(
135            device_out,
136            MAGMA_QUERY_VENDOR_ID,
137            &mut result_buffer_out,
138            &mut result_out,
139        )
140    };
141    if query_result != MAGMA_STATUS_OK {
142        return error!(EINVAL);
143    }
144
145    let vendor_id = result_out as u16;
146    if !supported_vendor_list.contains(&vendor_id) {
147        return error!(EINVAL);
148    }
149    Ok(magma_device)
150}
151
152/// Imports a device to magma.
153///
154/// # Parameters
155///   - `control`: The control struct containing the device channel to import from.
156///   - `response`: The struct that will be filled out to contain the response. This struct can be
157///                 written back to userspace.
158///
159/// SAFETY: Makes an FFI call to populate the fields of `response`.
160pub fn device_import(
161    supported_vendors: &Vec<u16>,
162    _control: virtio_magma_device_import_ctrl_t,
163) -> Result<MagmaDevice, Errno> {
164    let entries = std::fs::read_dir("/svc/fuchsia.gpu.magma.Service")
165        .map_err(|_| errno!(EINVAL))?
166        .filter_map(|x| x.ok());
167
168    let mut magma_devices = entries
169        .filter_map(|entry| attempt_open_path(entry.path().join("device"), supported_vendors).ok());
170    let magma_device = magma_devices.next().ok_or_else(|| errno!(EINVAL))?;
171
172    if magma_devices.next().is_some() {
173        track_stub!(TODO("https://fxbug.dev/297445280"), "expose multiple magma devices");
174    }
175
176    Ok(magma_device)
177}
178
179fn get_magma_vendor_id(supported_vendor_list: &Vec<u16>) -> Result<u64, Errno> {
180    let entries = std::fs::read_dir("/svc/fuchsia.gpu.magma.Service")
181        .map_err(|_| errno!(EINVAL))?
182        .filter_map(|x| x.ok());
183
184    let mut magma_devices = entries.filter_map(|entry| {
185        attempt_open_path(entry.path().join("device"), supported_vendor_list).ok()
186    });
187    let magma_device = magma_devices.next().ok_or_else(|| errno!(EINVAL))?;
188    let mut result_out = 0;
189    let mut result_buffer_out = 0;
190    #[allow(
191        clippy::undocumented_unsafe_blocks,
192        reason = "Force documented unsafe blocks in Starnix"
193    )]
194    let query_result = unsafe {
195        magma_device_query(
196            magma_device.handle,
197            MAGMA_QUERY_VENDOR_ID,
198            &mut result_buffer_out,
199            &mut result_out,
200        )
201    };
202    if query_result != MAGMA_STATUS_OK {
203        return error!(EINVAL);
204    }
205    return Ok(result_out);
206}
207
208/// Returns a command-line that helps Android select the correct GPU driver.
209pub fn get_magma_params(supported_vendor_list: &Vec<u16>) -> BString {
210    if let Some(magma_vendor_id) = get_magma_vendor_id(supported_vendor_list).ok() {
211        const MAGMA_VENDOR_ID_MALI_U64: u64 = MAGMA_VENDOR_ID_MALI as u64;
212        const MAGMA_VENDOR_ID_INTEL_U64: u64 = MAGMA_VENDOR_ID_INTEL as u64;
213        let gpu_type = match magma_vendor_id {
214            MAGMA_VENDOR_ID_MALI_U64 => "mali",
215            MAGMA_VENDOR_ID_INTEL_U64 => "intel",
216            _ => "default",
217        };
218        return BString::from(
219            "androidboot.vendor.apex.com.fuchsia.vulkan=com.fuchsia.vulkan.".to_owned() + gpu_type,
220        );
221    }
222    return b"androidboot.vendor.apex.com.fuchsia.vulkan=com.fuchsia.vulkan.default".into();
223}
224
225/// `WireDescriptor` matches the struct used by libmagma_virt to encode some fields of the magma
226/// command descriptor.
227#[repr(C)]
228#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Default, Debug)]
229struct WireDescriptor {
230    resource_count: u32,
231    command_buffer_count: u32,
232    wait_semaphore_count: u32,
233    signal_semaphore_count: u32,
234    flags: u64,
235}
236
237/// Executes a magma command.
238///
239/// This function bridges between the virtmagma structs and the magma structures. It also copies the
240/// data into starnix in order to be able to pass pointers to the resources, command buffers, and
241/// semaphore ids to magma.
242///
243/// SAFETY: Makes an FFI call to magma_connection_execute_command().
244pub fn execute_command<F>(
245    current_task: &CurrentTask,
246    control: virtio_magma_connection_execute_command_ctrl_t,
247    connection: &Arc<MagmaConnection>,
248    get_semaphore: F,
249) -> Result<i32, Errno>
250where
251    F: Fn(u64) -> Result<Arc<MagmaSemaphore>, i32>,
252{
253    let virtmagma_command_descriptor_addr =
254        UserRef::<virtmagma_command_descriptor>::new(control.descriptor.into());
255    let command_descriptor = current_task.read_object(virtmagma_command_descriptor_addr)?;
256
257    // Read the struct that contains the counts and flags for the magma command descriptor.
258    let wire_descriptor: WireDescriptor =
259        current_task.read_object(UserAddress::from(command_descriptor.descriptor).into())?;
260
261    let semaphore_count =
262        wire_descriptor.wait_semaphore_count + wire_descriptor.signal_semaphore_count;
263
264    let mut status: i32 = MAGMA_STATUS_OK;
265    let mut wait_semaphore_count: u32 = 0;
266    let mut signal_semaphore_count: u32 = 0;
267    let mut child_semaphore_ids: Vec<u64> = vec![];
268
269    if semaphore_count > 0 {
270        let semaphore_ids: Vec<u64> = read_objects(
271            current_task,
272            command_descriptor.semaphores.into(),
273            semaphore_count as usize,
274        )?;
275
276        for (i, id) in semaphore_ids.iter().enumerate() {
277            match get_semaphore(*id) {
278                Ok(semaphore) => {
279                    let ids_ref = &semaphore.ids;
280                    for id in ids_ref {
281                        child_semaphore_ids.push(*id);
282                    }
283                    if i < wire_descriptor.wait_semaphore_count as usize {
284                        wait_semaphore_count += ids_ref.len() as u32;
285                    } else {
286                        signal_semaphore_count += ids_ref.len() as u32;
287                    }
288                }
289                Err(s) => {
290                    status = s;
291                    break;
292                }
293            }
294        }
295    } else {
296        // So we can dereference index 0 below
297        child_semaphore_ids.push(0);
298    }
299
300    if status == MAGMA_STATUS_OK {
301        // This is the command descriptor that will be populated from the virtmagma
302        // descriptor and subsequently passed to magma_execute_command.
303        let mut magma_command_descriptor = magma_command_descriptor {
304            resource_count: wire_descriptor.resource_count,
305            command_buffer_count: wire_descriptor.command_buffer_count,
306            wait_semaphore_count,
307            signal_semaphore_count,
308            flags: wire_descriptor.flags,
309            ..Default::default()
310        };
311
312        // Read all the passed in resources, commands, and semaphore ids.
313        let mut resources: Vec<magma_exec_resource> = read_objects(
314            current_task,
315            command_descriptor.resources.into(),
316            wire_descriptor.resource_count as usize,
317        )?;
318        let mut command_buffers: Vec<magma_exec_command_buffer> = read_objects(
319            current_task,
320            command_descriptor.command_buffers.into(),
321            wire_descriptor.command_buffer_count as usize,
322        )?;
323
324        // Make sure the command descriptor contains valid pointers for the resources, command buffers,
325        // and semaphore ids.
326        magma_command_descriptor.resources = &mut resources[0] as *mut magma_exec_resource;
327        magma_command_descriptor.command_buffers =
328            &mut command_buffers[0] as *mut magma_exec_command_buffer;
329        magma_command_descriptor.semaphore_ids = &mut child_semaphore_ids[0] as *mut u64;
330
331        status = {
332            #[allow(
333                clippy::undocumented_unsafe_blocks,
334                reason = "Force documented unsafe blocks in Starnix"
335            )]
336            unsafe {
337                magma_connection_execute_command(
338                    connection.handle,
339                    control.context_id,
340                    &mut magma_command_descriptor as *mut magma_command_descriptor,
341                )
342            }
343        };
344    }
345
346    Ok(status)
347}
348
349/// Executes magma immediate commands.
350///
351/// This function bridges between the virtmagma structs and the magma structures. It also copies the
352/// data into starnix in order to be able to pass pointers to the resources, command buffers, and
353/// semaphore ids to magma.
354///
355/// SAFETY: Makes an FFI call to magma_execute_inline_commands().
356pub fn execute_inline_commands<F>(
357    current_task: &CurrentTask,
358    control: virtio_magma_connection_execute_inline_commands_ctrl_t,
359    connection: &Arc<MagmaConnection>,
360    get_semaphore: F,
361) -> Result<magma_status_t, Errno>
362where
363    F: Fn(u64) -> Result<Arc<MagmaSemaphore>, i32>,
364{
365    let command_buffers_addr = UserAddress::from(control.command_buffers);
366
367    // For virtio-magma, "command_buffers" is an array of virtmagma_command_descriptor instead of
368    // magma_inline_command_buffer.
369    let descriptors: Vec<virtmagma_command_descriptor> =
370        read_objects(current_task, command_buffers_addr, control.command_count as usize)?;
371
372    let mut commands =
373        vec![magma_inline_command_buffer::default(); std::cmp::max(descriptors.len(), 1)];
374
375    let mut commands_vec = Vec::<Vec<u8>>::with_capacity(control.command_count as usize);
376    let mut semaphore_ids_vec = Vec::<Vec<u64>>::with_capacity(control.command_count as usize);
377
378    let mut status: i32 = MAGMA_STATUS_OK;
379
380    for i in 0..control.command_count as usize {
381        let size = descriptors[i].command_buffer_size;
382        let data = current_task.read_buffer(&UserBuffer {
383            address: UserAddress::from(descriptors[i].command_buffers),
384            length: size as usize,
385        })?;
386        commands_vec.push(data);
387        commands[i].size = size;
388
389        let semaphore_count =
390            (descriptors[i].semaphore_size / core::mem::size_of::<u64>() as u64) as u32;
391
392        let mut child_semaphore_ids: Vec<u64> = vec![];
393        if semaphore_count > 0 {
394            let semaphore_ids = read_objects(
395                current_task,
396                UserAddress::from(descriptors[i].semaphores),
397                semaphore_count as usize,
398            )?;
399
400            for id in semaphore_ids.iter() {
401                match get_semaphore(*id) {
402                    Ok(semaphore) => {
403                        let child_ids_ref = &semaphore.ids;
404                        for child_id in child_ids_ref {
405                            child_semaphore_ids.push(*child_id);
406                        }
407                    }
408                    Err(s) => {
409                        status = s;
410                        break;
411                    }
412                }
413            }
414        }
415        commands[i].semaphore_count = child_semaphore_ids.len() as u32;
416        semaphore_ids_vec.push(child_semaphore_ids);
417    }
418
419    if status == MAGMA_STATUS_OK {
420        status = {
421            #[allow(
422                clippy::undocumented_unsafe_blocks,
423                reason = "Force documented unsafe blocks in Starnix"
424            )]
425            unsafe {
426                for i in 0..control.command_count as usize {
427                    commands[i].data = &mut commands_vec[i][0] as *mut u8 as *mut std::ffi::c_void;
428                    commands[i].semaphore_ids = if commands[i].semaphore_count == 0 {
429                        std::ptr::null_mut()
430                    } else {
431                        &mut semaphore_ids_vec[i][0]
432                    };
433                }
434                magma_connection_execute_inline_commands(
435                    connection.handle,
436                    control.context_id,
437                    control.command_count,
438                    &mut commands[0],
439                )
440            }
441        };
442    }
443
444    Ok(status)
445}
446
447/// Exports the provided magma buffer into a `zx::Vmo`, which is then wrapped in a file and added
448/// to `current_task`'s files.
449///
450/// The file's `fd` is then written to the response object, which allows the client to interact with
451/// the exported buffer.
452///
453/// # Parameters
454///   - `current_task`: The task that is exporting the buffer.
455///   - `control`: The control message that contains the buffer to export.
456///   - `response`: The response message that will be updated to write back to user space.
457///
458/// Returns an error if adding the file to `current_task` fails.
459///
460/// SAFETY: Makes an FFI call to populate the fields of `response`, and creates a `zx::Vmo` from
461/// a raw handle provided by magma.
462pub fn export_buffer(
463    locked: &mut Locked<Unlocked>,
464    current_task: &CurrentTask,
465    _control: virtio_magma_buffer_export_ctrl_t,
466    response: &mut virtio_magma_buffer_export_resp_t,
467    buffer: &Arc<MagmaBuffer>,
468    connections: &ConnectionMap,
469) -> Result<(), Errno> {
470    let mut buffer_handle_out = 0;
471    #[allow(
472        clippy::undocumented_unsafe_blocks,
473        reason = "Force documented unsafe blocks in Starnix"
474    )]
475    let status = unsafe {
476        magma_buffer_export(
477            buffer.handle as magma_buffer_t,
478            &mut buffer_handle_out as *mut magma_handle_t,
479        )
480    };
481    if status == MAGMA_STATUS_OK {
482        #[allow(
483            clippy::undocumented_unsafe_blocks,
484            reason = "Force documented unsafe blocks in Starnix"
485        )]
486        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
487        let memory = MemoryObject::from(vmo);
488
489        let mut image_info_opt: Option<ImageInfo> = None;
490        'outer: for image_map in connections.values() {
491            for (image, info) in image_map.buffer_map.iter() {
492                if *image == buffer.handle {
493                    if let BufferInfo::Image(image_info) = info.clone() {
494                        image_info_opt = Some(image_info);
495                        break 'outer;
496                    }
497                }
498            }
499        }
500
501        let file = {
502            if let Some(image_info) = image_info_opt {
503                ImageFile::new_file(locked, current_task, image_info, memory)
504            } else {
505                // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
506                Anon::new_private_file(
507                    locked,
508                    current_task,
509                    Box::new(MemoryRegularFile::new(Arc::new(memory))),
510                    OpenFlags::RDWR,
511                    "[fuchsia:magma_export_buffer]",
512                )
513            }
514        };
515
516        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
517        response.buffer_handle_out = fd.raw() as u64;
518    }
519
520    response.result_return = status as u64;
521    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT as u32;
522
523    Ok(())
524}
525
526/// Calls flush on the provided `control.connection`.
527///
528/// SAFETY: Makes an FFI call to magma, which is expected to handle invalid connection parameters.
529pub fn flush(
530    _control: virtio_magma_connection_flush_ctrl_t,
531    response: &mut virtio_magma_connection_flush_resp_t,
532    connection: &Arc<MagmaConnection>,
533) {
534    response.result_return = {
535        #[allow(
536            clippy::undocumented_unsafe_blocks,
537            reason = "Force documented unsafe blocks in Starnix"
538        )]
539        unsafe {
540            magma_connection_flush(connection.handle) as u64
541        }
542    };
543    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH as u32;
544}
545
546/// Fetches a VMO handles from magma wraps it in a file, then adds that file to `current_task`.
547///
548/// # Parameters
549///   - `current_task`: The task that the created file is added to, in `anon_fs`.
550///   - `control`: The control message containing the image handle.
551///   - `response`: The response which will contain the file descriptor for the created file.
552///
553/// SAFETY: Makes an FFI call to fetch a VMO handle. The VMO handle is expected to be valid if the
554/// FFI call succeeds. Either way, creating a `zx::Vmo` with an invalid handle is safe.
555pub fn get_buffer_handle(
556    locked: &mut Locked<Unlocked>,
557    current_task: &CurrentTask,
558    _control: virtio_magma_buffer_get_handle_ctrl_t,
559    response: &mut virtio_magma_buffer_get_handle_resp_t,
560    buffer: &Arc<MagmaBuffer>,
561) -> Result<(), Errno> {
562    let mut buffer_handle_out = 0;
563    #[allow(
564        clippy::undocumented_unsafe_blocks,
565        reason = "Force documented unsafe blocks in Starnix"
566    )]
567    let status = unsafe {
568        magma_buffer_get_handle(
569            buffer.handle as magma_buffer_t,
570            &mut buffer_handle_out as *mut magma_handle_t,
571        )
572    };
573
574    if status != MAGMA_STATUS_OK {
575        response.result_return = status as u64;
576    } else {
577        #[allow(
578            clippy::undocumented_unsafe_blocks,
579            reason = "Force documented unsafe blocks in Starnix"
580        )]
581        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
582        let memory = MemoryObject::from(vmo);
583        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
584        let file = Anon::new_private_file(
585            locked,
586            current_task,
587            Box::new(MemoryRegularFile::new(Arc::new(memory))),
588            OpenFlags::RDWR,
589            "[fuchsia:magma_buffer]",
590        );
591        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
592        response.handle_out = fd.raw() as u64;
593        response.result_return = MAGMA_STATUS_OK as u64;
594    }
595
596    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE as u32;
597
598    Ok(())
599}
600
601/// Runs a magma query.
602///
603/// This function will create a new file in `current_task.files` if the magma query returns a VMO
604/// handle. The file takes ownership of the VMO handle, and the file descriptor of the file is
605/// returned to the client via `response`.
606///
607/// SAFETY: Makes an FFI call to populate the fields of `response`.
608pub fn query(
609    locked: &mut Locked<Unlocked>,
610    current_task: &CurrentTask,
611    device: magma_device_t,
612    id: magma_query_t,
613    response: &mut virtio_magma_device_query_resp_t,
614) -> Result<(), Errno> {
615    let mut result_buffer_out = 0;
616    let mut result_out = 0;
617    response.result_return = {
618        #[allow(
619            clippy::undocumented_unsafe_blocks,
620            reason = "Force documented unsafe blocks in Starnix"
621        )]
622        unsafe {
623            magma_device_query(device, id, &mut result_buffer_out, &mut result_out) as u64
624        }
625    };
626
627    if result_buffer_out != zx::sys::ZX_HANDLE_INVALID {
628        #[allow(
629            clippy::undocumented_unsafe_blocks,
630            reason = "Force documented unsafe blocks in Starnix"
631        )]
632        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(result_buffer_out)) };
633        let memory = MemoryObject::from(vmo);
634        let memory_size = memory.get_size();
635        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
636        let mut info = FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred());
637        // Enable seek for file size discovery.
638        info.size = memory_size as usize;
639        let file = Anon::new_private_file_extended(
640            locked,
641            current_task,
642            Box::new(MemoryRegularFile::new(Arc::new(memory))),
643            OpenFlags::RDWR,
644            "[fuchsia:magma_vmo]",
645            info,
646        );
647        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
648        response.result_buffer_out = fd.raw() as u64;
649    } else {
650        response.result_buffer_out = u64::MAX;
651    }
652
653    response.result_out = result_out;
654
655    Ok(())
656}
657
658/// Reads a notification from the connection channel and writes it to `control.buffer`.
659///
660/// Upon completion, `response.more_data_out` will be true if there is more data waiting to be read.
661/// `response.buffer_size_out` contains the size of the returned buffer.
662///
663/// SAFETY: Makes an FFI call to magma with a buffer that is populated with data. The passed in
664/// buffer pointer always points to a valid vector, even if the provided buffer length is 0.
665pub fn read_notification_channel(
666    current_task: &CurrentTask,
667    control: virtio_magma_connection_read_notification_channel_ctrl_t,
668    response: &mut virtio_magma_connection_read_notification_channel_resp_t,
669    connection: &Arc<MagmaConnection>,
670) -> Result<(), Errno> {
671    // Buffer has a min length of 1 to make sure the call to
672    // `magma_read_notification_channel2` uses a valid reference.
673    let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)];
674    let mut buffer_size_out = 0;
675    let mut more_data_out: u8 = 0;
676
677    response.result_return = {
678        #[allow(
679            clippy::undocumented_unsafe_blocks,
680            reason = "Force documented unsafe blocks in Starnix"
681        )]
682        unsafe {
683            magma_connection_read_notification_channel(
684                connection.handle,
685                &mut buffer[0] as *mut u8 as *mut std::ffi::c_void,
686                control.buffer_size,
687                &mut buffer_size_out,
688                &mut more_data_out as *mut u8,
689            ) as u64
690        }
691    };
692
693    response.more_data_out = more_data_out as u64;
694    response.buffer_size_out = buffer_size_out;
695    response.hdr.type_ =
696        virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL as u32;
697
698    current_task.write_memory(UserAddress::from(control.buffer), &buffer)?;
699
700    Ok(())
701}
702
703pub fn import_semaphore2(
704    connection: &Arc<MagmaConnection>,
705    counter: zx::Counter,
706    flags: u64,
707) -> (i32, u64, u64) {
708    let mut semaphore = 0;
709    let mut semaphore_id = 0;
710    #[allow(
711        clippy::undocumented_unsafe_blocks,
712        reason = "Force documented unsafe blocks in Starnix"
713    )]
714    let status = unsafe {
715        magma_connection_import_semaphore2(
716            connection.handle,
717            counter.into_raw(),
718            flags,
719            &mut semaphore,
720            &mut semaphore_id,
721        )
722    };
723    (status, semaphore, semaphore_id)
724}