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 virtmagma-internal struct that contains the counts and flags for the magma command
258    // descriptor.
259    let wire_descriptor: WireDescriptor =
260        current_task.read_object(UserAddress::from(command_descriptor.descriptor).into())?;
261
262    let semaphore_count =
263        wire_descriptor.wait_semaphore_count + wire_descriptor.signal_semaphore_count;
264
265    let mut status: i32 = MAGMA_STATUS_OK;
266    let mut wait_semaphore_count: u32 = 0;
267    let mut signal_semaphore_count: u32 = 0;
268    let mut child_semaphore_ids: Vec<u64> = vec![];
269
270    if semaphore_count > 0 {
271        let semaphore_ids: Vec<u64> = read_objects(
272            current_task,
273            command_descriptor.semaphores.into(),
274            semaphore_count as usize,
275        )?;
276
277        for (i, id) in semaphore_ids.iter().enumerate() {
278            match get_semaphore(*id) {
279                Ok(semaphore) => {
280                    let ids_ref = &semaphore.ids;
281                    for id in ids_ref {
282                        child_semaphore_ids.push(*id);
283                    }
284                    if i < wire_descriptor.wait_semaphore_count as usize {
285                        wait_semaphore_count += ids_ref.len() as u32;
286                    } else {
287                        signal_semaphore_count += ids_ref.len() as u32;
288                    }
289                }
290                Err(s) => {
291                    status = s;
292                    break;
293                }
294            }
295        }
296    } else {
297        // So we can dereference index 0 below
298        child_semaphore_ids.push(0);
299    }
300
301    if status == MAGMA_STATUS_OK {
302        // This is the command descriptor that will be populated from the virtmagma
303        // descriptor and subsequently passed to magma_execute_command.
304        let mut magma_command_descriptor = magma_command_descriptor {
305            resource_count: wire_descriptor.resource_count,
306            command_buffer_count: wire_descriptor.command_buffer_count,
307            wait_semaphore_count,
308            signal_semaphore_count,
309            flags: wire_descriptor.flags,
310            ..Default::default()
311        };
312
313        // Read all the passed in resources, commands, and semaphore ids.
314        let mut resources: Vec<magma_exec_resource> = read_objects(
315            current_task,
316            command_descriptor.resources.into(),
317            wire_descriptor.resource_count as usize,
318        )?;
319        let mut command_buffers: Vec<magma_exec_command_buffer> = read_objects(
320            current_task,
321            command_descriptor.command_buffers.into(),
322            wire_descriptor.command_buffer_count as usize,
323        )?;
324
325        // Make sure the command descriptor contains valid pointers for the resources, command buffers,
326        // and semaphore ids.
327        magma_command_descriptor.resources = &mut resources[0] as *mut magma_exec_resource;
328        magma_command_descriptor.command_buffers =
329            &mut command_buffers[0] as *mut magma_exec_command_buffer;
330        magma_command_descriptor.semaphore_ids = &mut child_semaphore_ids[0] as *mut u64;
331
332        status = {
333            #[allow(
334                clippy::undocumented_unsafe_blocks,
335                reason = "Force documented unsafe blocks in Starnix"
336            )]
337            unsafe {
338                magma_connection_execute_command(
339                    connection.handle,
340                    control.context_id,
341                    &mut magma_command_descriptor as *mut magma_command_descriptor,
342                )
343            }
344        };
345    }
346
347    Ok(status)
348}
349
350/// Executes magma immediate commands.
351///
352/// This function bridges between the virtmagma structs and the magma structures. It also copies the
353/// data into starnix in order to be able to pass pointers to the resources, command buffers, and
354/// semaphore ids to magma.
355///
356/// SAFETY: Makes an FFI call to magma_execute_inline_commands().
357pub fn execute_inline_commands<F>(
358    current_task: &CurrentTask,
359    control: virtio_magma_connection_execute_inline_commands_ctrl_t,
360    connection: &Arc<MagmaConnection>,
361    get_semaphore: F,
362) -> Result<magma_status_t, Errno>
363where
364    F: Fn(u64) -> Result<Arc<MagmaSemaphore>, i32>,
365{
366    let command_buffers_addr = UserAddress::from(control.command_buffers);
367
368    // For virtio-magma, "command_buffers" is an array of virtmagma_command_descriptor instead of
369    // magma_inline_command_buffer.
370    let descriptors: Vec<virtmagma_command_descriptor> =
371        read_objects(current_task, command_buffers_addr, control.command_count as usize)?;
372
373    let mut commands =
374        vec![magma_inline_command_buffer::default(); std::cmp::max(descriptors.len(), 1)];
375
376    let mut commands_vec = Vec::<Vec<u8>>::with_capacity(control.command_count as usize);
377    let mut semaphore_ids_vec = Vec::<Vec<u64>>::with_capacity(control.command_count as usize);
378
379    let mut status: i32 = MAGMA_STATUS_OK;
380
381    for i in 0..control.command_count as usize {
382        let size = descriptors[i].command_buffer_size;
383        let data = current_task.read_buffer(&UserBuffer {
384            address: UserAddress::from(descriptors[i].command_buffers),
385            length: size as usize,
386        })?;
387        commands_vec.push(data);
388        commands[i].size = size;
389
390        let semaphore_count =
391            (descriptors[i].semaphore_size / core::mem::size_of::<u64>() as u64) as u32;
392
393        let mut child_semaphore_ids: Vec<u64> = vec![];
394        if semaphore_count > 0 {
395            let semaphore_ids = read_objects(
396                current_task,
397                UserAddress::from(descriptors[i].semaphores),
398                semaphore_count as usize,
399            )?;
400
401            for id in semaphore_ids.iter() {
402                match get_semaphore(*id) {
403                    Ok(semaphore) => {
404                        let child_ids_ref = &semaphore.ids;
405                        for child_id in child_ids_ref {
406                            child_semaphore_ids.push(*child_id);
407                        }
408                    }
409                    Err(s) => {
410                        status = s;
411                        break;
412                    }
413                }
414            }
415        }
416        commands[i].semaphore_count = child_semaphore_ids.len() as u32;
417        semaphore_ids_vec.push(child_semaphore_ids);
418    }
419
420    if status == MAGMA_STATUS_OK {
421        status = {
422            #[allow(
423                clippy::undocumented_unsafe_blocks,
424                reason = "Force documented unsafe blocks in Starnix"
425            )]
426            unsafe {
427                for i in 0..control.command_count as usize {
428                    commands[i].data = &mut commands_vec[i][0] as *mut u8 as *mut std::ffi::c_void;
429                    commands[i].semaphore_ids = if commands[i].semaphore_count == 0 {
430                        std::ptr::null_mut()
431                    } else {
432                        &mut semaphore_ids_vec[i][0]
433                    };
434                }
435                magma_connection_execute_inline_commands(
436                    connection.handle,
437                    control.context_id,
438                    control.command_count,
439                    &mut commands[0],
440                )
441            }
442        };
443    }
444
445    Ok(status)
446}
447
448/// Exports the provided magma buffer into a `zx::Vmo`, which is then wrapped in a file and added
449/// to `current_task`'s files.
450///
451/// The file's `fd` is then written to the response object, which allows the client to interact with
452/// the exported buffer.
453///
454/// # Parameters
455///   - `current_task`: The task that is exporting the buffer.
456///   - `control`: The control message that contains the buffer to export.
457///   - `response`: The response message that will be updated to write back to user space.
458///
459/// Returns an error if adding the file to `current_task` fails.
460///
461/// SAFETY: Makes an FFI call to populate the fields of `response`, and creates a `zx::Vmo` from
462/// a raw handle provided by magma.
463pub fn export_buffer(
464    locked: &mut Locked<Unlocked>,
465    current_task: &CurrentTask,
466    _control: virtio_magma_buffer_export_ctrl_t,
467    response: &mut virtio_magma_buffer_export_resp_t,
468    buffer: &Arc<MagmaBuffer>,
469    connections: &ConnectionMap,
470) -> Result<(), Errno> {
471    let mut buffer_handle_out = 0;
472    #[allow(
473        clippy::undocumented_unsafe_blocks,
474        reason = "Force documented unsafe blocks in Starnix"
475    )]
476    let status = unsafe {
477        magma_buffer_export(
478            buffer.handle as magma_buffer_t,
479            &mut buffer_handle_out as *mut magma_handle_t,
480        )
481    };
482    if status == MAGMA_STATUS_OK {
483        #[allow(
484            clippy::undocumented_unsafe_blocks,
485            reason = "Force documented unsafe blocks in Starnix"
486        )]
487        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
488        let memory = MemoryObject::from(vmo);
489
490        let mut image_info_opt: Option<ImageInfo> = None;
491        'outer: for image_map in connections.values() {
492            for (image, info) in image_map.buffer_map.iter() {
493                if *image == buffer.handle {
494                    if let BufferInfo::Image(image_info) = info.clone() {
495                        image_info_opt = Some(image_info);
496                        break 'outer;
497                    }
498                }
499            }
500        }
501
502        let file = {
503            if let Some(image_info) = image_info_opt {
504                ImageFile::new_file(locked, current_task, image_info, memory)
505            } else {
506                // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
507                Anon::new_private_file(
508                    locked,
509                    current_task,
510                    Box::new(MemoryRegularFile::new(Arc::new(memory))),
511                    OpenFlags::RDWR,
512                    "[fuchsia:magma_export_buffer]",
513                )
514            }
515        };
516
517        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
518        response.buffer_handle_out = fd.raw() as u64;
519    }
520
521    response.result_return = status as u64;
522    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT as u32;
523
524    Ok(())
525}
526
527/// Calls flush on the provided `control.connection`.
528///
529/// SAFETY: Makes an FFI call to magma, which is expected to handle invalid connection parameters.
530pub fn flush(
531    _control: virtio_magma_connection_flush_ctrl_t,
532    response: &mut virtio_magma_connection_flush_resp_t,
533    connection: &Arc<MagmaConnection>,
534) {
535    response.result_return = {
536        #[allow(
537            clippy::undocumented_unsafe_blocks,
538            reason = "Force documented unsafe blocks in Starnix"
539        )]
540        unsafe {
541            magma_connection_flush(connection.handle) as u64
542        }
543    };
544    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH as u32;
545}
546
547/// Fetches a VMO handles from magma wraps it in a file, then adds that file to `current_task`.
548///
549/// # Parameters
550///   - `current_task`: The task that the created file is added to, in `anon_fs`.
551///   - `control`: The control message containing the image handle.
552///   - `response`: The response which will contain the file descriptor for the created file.
553///
554/// SAFETY: Makes an FFI call to fetch a VMO handle. The VMO handle is expected to be valid if the
555/// FFI call succeeds. Either way, creating a `zx::Vmo` with an invalid handle is safe.
556pub fn get_buffer_handle(
557    locked: &mut Locked<Unlocked>,
558    current_task: &CurrentTask,
559    _control: virtio_magma_buffer_get_handle_ctrl_t,
560    response: &mut virtio_magma_buffer_get_handle_resp_t,
561    buffer: &Arc<MagmaBuffer>,
562) -> Result<(), Errno> {
563    let mut buffer_handle_out = 0;
564    #[allow(
565        clippy::undocumented_unsafe_blocks,
566        reason = "Force documented unsafe blocks in Starnix"
567    )]
568    let status = unsafe {
569        magma_buffer_get_handle(
570            buffer.handle as magma_buffer_t,
571            &mut buffer_handle_out as *mut magma_handle_t,
572        )
573    };
574
575    if status != MAGMA_STATUS_OK {
576        response.result_return = status as u64;
577    } else {
578        #[allow(
579            clippy::undocumented_unsafe_blocks,
580            reason = "Force documented unsafe blocks in Starnix"
581        )]
582        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
583        let memory = MemoryObject::from(vmo);
584        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
585        let file = Anon::new_private_file(
586            locked,
587            current_task,
588            Box::new(MemoryRegularFile::new(Arc::new(memory))),
589            OpenFlags::RDWR,
590            "[fuchsia:magma_buffer]",
591        );
592        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
593        response.handle_out = fd.raw() as u64;
594        response.result_return = MAGMA_STATUS_OK as u64;
595    }
596
597    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE as u32;
598
599    Ok(())
600}
601
602/// Runs a magma query.
603///
604/// This function will create a new file in `current_task.files` if the magma query returns a VMO
605/// handle. The file takes ownership of the VMO handle, and the file descriptor of the file is
606/// returned to the client via `response`.
607///
608/// SAFETY: Makes an FFI call to populate the fields of `response`.
609pub fn query(
610    locked: &mut Locked<Unlocked>,
611    current_task: &CurrentTask,
612    device: magma_device_t,
613    id: magma_query_t,
614    response: &mut virtio_magma_device_query_resp_t,
615) -> Result<(), Errno> {
616    let mut result_buffer_out = 0;
617    let mut result_out = 0;
618    response.result_return = {
619        #[allow(
620            clippy::undocumented_unsafe_blocks,
621            reason = "Force documented unsafe blocks in Starnix"
622        )]
623        unsafe {
624            magma_device_query(device, id, &mut result_buffer_out, &mut result_out) as u64
625        }
626    };
627
628    if result_buffer_out != zx::sys::ZX_HANDLE_INVALID {
629        #[allow(
630            clippy::undocumented_unsafe_blocks,
631            reason = "Force documented unsafe blocks in Starnix"
632        )]
633        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(result_buffer_out)) };
634        let memory = MemoryObject::from(vmo);
635        let memory_size = memory.get_size();
636        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
637        let mut info = FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred());
638        // Enable seek for file size discovery.
639        info.size = memory_size as usize;
640        let file = Anon::new_private_file_extended(
641            locked,
642            current_task,
643            Box::new(MemoryRegularFile::new(Arc::new(memory))),
644            OpenFlags::RDWR,
645            "[fuchsia:magma_vmo]",
646            info,
647        );
648        let fd = current_task.add_file(locked, file, FdFlags::empty())?;
649        response.result_buffer_out = fd.raw() as u64;
650    } else {
651        response.result_buffer_out = u64::MAX;
652    }
653
654    response.result_out = result_out;
655
656    Ok(())
657}
658
659/// Reads a notification from the connection channel and writes it to `control.buffer`.
660///
661/// Upon completion, `response.more_data_out` will be true if there is more data waiting to be read.
662/// `response.buffer_size_out` contains the size of the returned buffer.
663///
664/// SAFETY: Makes an FFI call to magma with a buffer that is populated with data. The passed in
665/// buffer pointer always points to a valid vector, even if the provided buffer length is 0.
666pub fn read_notification_channel(
667    current_task: &CurrentTask,
668    control: virtio_magma_connection_read_notification_channel_ctrl_t,
669    response: &mut virtio_magma_connection_read_notification_channel_resp_t,
670    connection: &Arc<MagmaConnection>,
671) -> Result<(), Errno> {
672    // Buffer has a min length of 1 to make sure the call to
673    // `magma_read_notification_channel2` uses a valid reference.
674    let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)];
675    let mut buffer_size_out = 0;
676    let mut more_data_out: u8 = 0;
677
678    response.result_return = {
679        #[allow(
680            clippy::undocumented_unsafe_blocks,
681            reason = "Force documented unsafe blocks in Starnix"
682        )]
683        unsafe {
684            magma_connection_read_notification_channel(
685                connection.handle,
686                &mut buffer[0] as *mut u8 as *mut std::ffi::c_void,
687                control.buffer_size,
688                &mut buffer_size_out,
689                &mut more_data_out as *mut u8,
690            ) as u64
691        }
692    };
693
694    response.more_data_out = more_data_out as u64;
695    response.buffer_size_out = buffer_size_out;
696    response.hdr.type_ =
697        virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL as u32;
698
699    current_task.write_memory(UserAddress::from(control.buffer), &buffer)?;
700
701    Ok(())
702}
703
704pub fn import_semaphore2(
705    connection: &Arc<MagmaConnection>,
706    counter: zx::Counter,
707    flags: u64,
708) -> (i32, u64, u64) {
709    let mut semaphore = 0;
710    let mut semaphore_id = 0;
711    #[allow(
712        clippy::undocumented_unsafe_blocks,
713        reason = "Force documented unsafe blocks in Starnix"
714    )]
715    let status = unsafe {
716        magma_connection_import_semaphore2(
717            connection.handle,
718            counter.into_raw(),
719            flags,
720            &mut semaphore,
721            &mut semaphore_id,
722        )
723    };
724    (status, semaphore, semaphore_id)
725}