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;
36
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    current_task: &CurrentTask,
464    _control: virtio_magma_buffer_export_ctrl_t,
465    response: &mut virtio_magma_buffer_export_resp_t,
466    buffer: &Arc<MagmaBuffer>,
467    connections: &ConnectionMap,
468) -> Result<(), Errno> {
469    let mut buffer_handle_out = 0;
470    #[allow(
471        clippy::undocumented_unsafe_blocks,
472        reason = "Force documented unsafe blocks in Starnix"
473    )]
474    let status = unsafe {
475        magma_buffer_export(
476            buffer.handle as magma_buffer_t,
477            &mut buffer_handle_out as *mut magma_handle_t,
478        )
479    };
480    if status == MAGMA_STATUS_OK {
481        #[allow(
482            clippy::undocumented_unsafe_blocks,
483            reason = "Force documented unsafe blocks in Starnix"
484        )]
485        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
486        let memory = MemoryObject::from(vmo);
487
488        let mut image_info_opt: Option<ImageInfo> = None;
489        'outer: for image_map in connections.values() {
490            for (image, info) in image_map.buffer_map.iter() {
491                if *image == buffer.handle {
492                    if let BufferInfo::Image(image_info) = info.clone() {
493                        image_info_opt = Some(image_info);
494                        break 'outer;
495                    }
496                }
497            }
498        }
499
500        let file = {
501            if let Some(image_info) = image_info_opt {
502                ImageFile::new_file(current_task, image_info, memory)
503            } else {
504                // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
505                Anon::new_private_file(
506                    current_task,
507                    Box::new(MemoryRegularFile::new(Arc::new(memory))),
508                    OpenFlags::RDWR,
509                    "[fuchsia:magma_export_buffer]",
510                )
511            }
512        };
513
514        let fd = current_task.add_file(file, FdFlags::empty())?;
515        response.buffer_handle_out = fd.raw() as u64;
516    }
517
518    response.result_return = status as u64;
519    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT as u32;
520
521    Ok(())
522}
523
524/// Calls flush on the provided `control.connection`.
525///
526/// SAFETY: Makes an FFI call to magma, which is expected to handle invalid connection parameters.
527pub fn flush(
528    _control: virtio_magma_connection_flush_ctrl_t,
529    response: &mut virtio_magma_connection_flush_resp_t,
530    connection: &Arc<MagmaConnection>,
531) {
532    response.result_return = {
533        #[allow(
534            clippy::undocumented_unsafe_blocks,
535            reason = "Force documented unsafe blocks in Starnix"
536        )]
537        unsafe {
538            magma_connection_flush(connection.handle) as u64
539        }
540    };
541    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH as u32;
542}
543
544/// Fetches a VMO handles from magma wraps it in a file, then adds that file to `current_task`.
545///
546/// # Parameters
547///   - `current_task`: The task that the created file is added to, in `anon_fs`.
548///   - `control`: The control message containing the image handle.
549///   - `response`: The response which will contain the file descriptor for the created file.
550///
551/// SAFETY: Makes an FFI call to fetch a VMO handle. The VMO handle is expected to be valid if the
552/// FFI call succeeds. Either way, creating a `zx::Vmo` with an invalid handle is safe.
553pub fn get_buffer_handle(
554    current_task: &CurrentTask,
555    _control: virtio_magma_buffer_get_handle_ctrl_t,
556    response: &mut virtio_magma_buffer_get_handle_resp_t,
557    buffer: &Arc<MagmaBuffer>,
558) -> Result<(), Errno> {
559    let mut buffer_handle_out = 0;
560    #[allow(
561        clippy::undocumented_unsafe_blocks,
562        reason = "Force documented unsafe blocks in Starnix"
563    )]
564    let status = unsafe {
565        magma_buffer_get_handle(
566            buffer.handle as magma_buffer_t,
567            &mut buffer_handle_out as *mut magma_handle_t,
568        )
569    };
570
571    if status != MAGMA_STATUS_OK {
572        response.result_return = status as u64;
573    } else {
574        #[allow(
575            clippy::undocumented_unsafe_blocks,
576            reason = "Force documented unsafe blocks in Starnix"
577        )]
578        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
579        let memory = MemoryObject::from(vmo);
580        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
581        let file = Anon::new_private_file(
582            current_task,
583            Box::new(MemoryRegularFile::new(Arc::new(memory))),
584            OpenFlags::RDWR,
585            "[fuchsia:magma_buffer]",
586        );
587        let fd = current_task.add_file(file, FdFlags::empty())?;
588        response.handle_out = fd.raw() as u64;
589        response.result_return = MAGMA_STATUS_OK as u64;
590    }
591
592    response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE as u32;
593
594    Ok(())
595}
596
597/// Runs a magma query.
598///
599/// This function will create a new file in `current_task.files` if the magma query returns a VMO
600/// handle. The file takes ownership of the VMO handle, and the file descriptor of the file is
601/// returned to the client via `response`.
602///
603/// SAFETY: Makes an FFI call to populate the fields of `response`.
604pub fn query(
605    current_task: &CurrentTask,
606    device: magma_device_t,
607    id: magma_query_t,
608    response: &mut virtio_magma_device_query_resp_t,
609) -> Result<(), Errno> {
610    let mut result_buffer_out = 0;
611    let mut result_out = 0;
612    response.result_return = {
613        #[allow(
614            clippy::undocumented_unsafe_blocks,
615            reason = "Force documented unsafe blocks in Starnix"
616        )]
617        unsafe {
618            magma_device_query(device, id, &mut result_buffer_out, &mut result_out) as u64
619        }
620    };
621
622    if result_buffer_out != zx::sys::ZX_HANDLE_INVALID {
623        #[allow(
624            clippy::undocumented_unsafe_blocks,
625            reason = "Force documented unsafe blocks in Starnix"
626        )]
627        let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(result_buffer_out)) };
628        let memory = MemoryObject::from(vmo);
629        let memory_size = memory.get_size();
630        // TODO: https://fxbug.dev/404739824 - Confirm whether to handle this as a "private" node.
631        let mut info = FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred());
632        // Enable seek for file size discovery.
633        info.size = memory_size as usize;
634        let file = Anon::new_private_file_extended(
635            current_task,
636            Box::new(MemoryRegularFile::new(Arc::new(memory))),
637            OpenFlags::RDWR,
638            "[fuchsia:magma_vmo]",
639            info,
640        );
641        let fd = current_task.add_file(file, FdFlags::empty())?;
642        response.result_buffer_out = fd.raw() as u64;
643    } else {
644        response.result_buffer_out = u64::MAX;
645    }
646
647    response.result_out = result_out;
648
649    Ok(())
650}
651
652/// Reads a notification from the connection channel and writes it to `control.buffer`.
653///
654/// Upon completion, `response.more_data_out` will be true if there is more data waiting to be read.
655/// `response.buffer_size_out` contains the size of the returned buffer.
656///
657/// SAFETY: Makes an FFI call to magma with a buffer that is populated with data. The passed in
658/// buffer pointer always points to a valid vector, even if the provided buffer length is 0.
659pub fn read_notification_channel(
660    current_task: &CurrentTask,
661    control: virtio_magma_connection_read_notification_channel_ctrl_t,
662    response: &mut virtio_magma_connection_read_notification_channel_resp_t,
663    connection: &Arc<MagmaConnection>,
664) -> Result<(), Errno> {
665    // Buffer has a min length of 1 to make sure the call to
666    // `magma_read_notification_channel2` uses a valid reference.
667    let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)];
668    let mut buffer_size_out = 0;
669    let mut more_data_out: u8 = 0;
670
671    response.result_return = {
672        #[allow(
673            clippy::undocumented_unsafe_blocks,
674            reason = "Force documented unsafe blocks in Starnix"
675        )]
676        unsafe {
677            magma_connection_read_notification_channel(
678                connection.handle,
679                &mut buffer[0] as *mut u8 as *mut std::ffi::c_void,
680                control.buffer_size,
681                &mut buffer_size_out,
682                &mut more_data_out as *mut u8,
683            ) as u64
684        }
685    };
686
687    response.more_data_out = more_data_out as u64;
688    response.buffer_size_out = buffer_size_out;
689    response.hdr.type_ =
690        virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL as u32;
691
692    current_task.write_memory(UserAddress::from(control.buffer), &buffer)?;
693
694    Ok(())
695}
696
697pub fn import_semaphore2(
698    connection: &Arc<MagmaConnection>,
699    counter: zx::Counter,
700    flags: u64,
701) -> (i32, u64, u64) {
702    let mut semaphore = 0;
703    let mut semaphore_id = 0;
704    #[allow(
705        clippy::undocumented_unsafe_blocks,
706        reason = "Force documented unsafe blocks in Starnix"
707    )]
708    let status = unsafe {
709        magma_connection_import_semaphore2(
710            connection.handle,
711            counter.into_raw(),
712            flags,
713            &mut semaphore,
714            &mut semaphore_id,
715        )
716    };
717    (status, semaphore, semaphore_id)
718}