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