| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![allow(non_upper_case_globals)] |
| |
| use fuchsia_zircon as zx; |
| use fuchsia_zircon::HandleBased; |
| use magma::*; |
| use std::collections::HashMap; |
| use std::sync::Arc; |
| |
| use super::ffi::*; |
| use super::magma::*; |
| use crate::device::wayland::image_file::*; |
| use crate::fs::*; |
| use crate::lock::Mutex; |
| use crate::logging::impossible_error; |
| use crate::syscalls::*; |
| use crate::task::CurrentTask; |
| use crate::types::*; |
| |
| #[derive(Clone)] |
| pub enum BufferInfo { |
| Default, |
| Image(ImageInfo), |
| } |
| |
| /// A `MagmaConnection` is an internal representation of a `magma_connection_t`. |
| type MagmaConnection = u64; |
| |
| /// A `BufferMap` stores all the magma buffers for a given connection. |
| type BufferMap = HashMap<magma_buffer_t, BufferInfo>; |
| |
| /// A `ConnectionMap` stores the `BufferMap`s associated with each magma connection. |
| type ConnectionMap = HashMap<MagmaConnection, BufferMap>; |
| pub struct MagmaFile { |
| // TODO(fxbug.dev/12731): The lifecycle of the device channels should be handled by magma. |
| devices: Arc<Mutex<Vec<zx::Channel>>>, |
| connections: Arc<Mutex<ConnectionMap>>, |
| } |
| |
| impl MagmaFile { |
| pub fn new() -> Result<Box<dyn FileOps>, Errno> { |
| Ok(Box::new(Self { |
| devices: Arc::new(Mutex::new(vec![])), |
| connections: Arc::new(Mutex::new(HashMap::new())), |
| })) |
| } |
| |
| /// Returns a duplicate of the VMO associated with the file at `fd`, as well as a `BufferInfo` |
| /// of the correct type for that file. |
| /// |
| /// Returns an error if the file does not contain a buffer. |
| fn get_vmo_and_magma_buffer( |
| current_task: &CurrentTask, |
| fd: FdNumber, |
| ) -> Result<(zx::Vmo, BufferInfo), Errno> { |
| let file = current_task.files.get(fd)?; |
| if let Some(file) = file.downcast_file::<ImageFile>() { |
| let buffer = BufferInfo::Image(file.info.clone()); |
| Ok(( |
| file.vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?, |
| buffer, |
| )) |
| } else if let Some(file) = file.downcast_file::<VmoFileObject>() { |
| let buffer = BufferInfo::Default; |
| Ok(( |
| file.vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?, |
| buffer, |
| )) |
| } else { |
| error!(EINVAL) |
| } |
| } |
| |
| /// Adds a `BufferInfo` for the given `magma_buffer_t`, associated with the specified |
| /// connection. |
| fn add_buffer_info( |
| &self, |
| connection: magma_connection_t, |
| buffer: magma_buffer_t, |
| buffer_info: BufferInfo, |
| ) { |
| self.connections |
| .lock() |
| .entry(connection as u64) |
| .or_insert_with(HashMap::new) |
| .insert(buffer, buffer_info); |
| } |
| |
| /// Returns a `BufferInfo` for the given `magma_buffer_t`, if one exists for the given |
| /// `connection`. Otherwise returns `None`. |
| fn get_buffer_info( |
| &self, |
| connection: magma_connection_t, |
| buffer: magma_buffer_t, |
| ) -> Option<BufferInfo> { |
| match self.connections.lock().get(&(connection as u64)) { |
| Some(buffers) => buffers.get(&buffer).map(|buffer| buffer.clone()), |
| _ => None, |
| } |
| } |
| } |
| |
| impl FileOps for MagmaFile { |
| fileops_impl_nonseekable!(); |
| fileops_impl_nonblocking!(); |
| |
| fn ioctl( |
| &self, |
| _file: &FileObject, |
| current_task: &CurrentTask, |
| _request: u32, |
| user_addr: UserAddress, |
| ) -> Result<SyscallResult, Errno> { |
| let (command, command_type) = read_magma_command_and_type(current_task, user_addr)?; |
| let response_address = UserAddress::from(command.response_address); |
| |
| match command_type { |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_IMPORT => { |
| let (control, mut response) = read_control_and_response(current_task, &command)?; |
| (*self.devices.lock()).push(device_import(control, &mut response)?); |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_IMPORT as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CREATE_CONNECTION2 => { |
| let (control, mut response): ( |
| virtio_magma_create_connection2_ctrl, |
| virtio_magma_create_connection2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut connection_out: magma_connection_t = 0; |
| response.result_return = |
| unsafe { magma_create_connection2(control.device, &mut connection_out) as u64 }; |
| |
| response.connection_out = connection_out; |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CREATE_CONNECTION2 as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_RELEASE_CONNECTION => { |
| let (control, mut response): ( |
| virtio_magma_release_connection_ctrl_t, |
| virtio_magma_release_connection_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let connection = control.connection as magma_connection_t; |
| let mut connections = self.connections.lock(); |
| if connections.contains_key(&connection) { |
| unsafe { magma_release_connection(connection) }; |
| connections.remove(&connection); |
| } |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_RELEASE_CONNECTION as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_DEVICE_RELEASE => { |
| let (control, mut response): ( |
| virtio_magma_device_release_ctrl_t, |
| virtio_magma_device_release_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| unsafe { magma_device_release(control.device) }; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_DEVICE_RELEASE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_VIRT_CREATE_IMAGE => { |
| let (control, mut response): ( |
| virtio_magma_virt_create_image_ctrl_t, |
| virtio_magma_virt_create_image_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut create_info_ptr: u64 = 0; |
| let create_info_address = UserAddress::from(control.create_info); |
| current_task |
| .mm |
| .read_object(UserRef::new(create_info_address), &mut create_info_ptr)?; |
| |
| let mut create_info = magma_image_create_info_t::default(); |
| let create_info_address = UserAddress::from(create_info_ptr); |
| current_task.mm.read_object(UserRef::new(create_info_address), &mut create_info)?; |
| |
| let (vmo, token, info) = create_drm_image(0, &create_info).map_err(|e| { |
| tracing::warn!("Error creating drm image: {:?}", e); |
| errno!(EINVAL) |
| })?; |
| |
| let mut buffer_out = magma_buffer_t::default(); |
| response.result_return = unsafe { |
| magma_import( |
| control.connection as magma_connection_t, |
| vmo.into_raw(), |
| &mut buffer_out, |
| ) as u64 |
| }; |
| |
| self.add_buffer_info( |
| control.connection as magma_connection_t, |
| buffer_out, |
| BufferInfo::Image(ImageInfo { info, token }), |
| ); |
| |
| response.image_out = buffer_out; |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_VIRT_CREATE_IMAGE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_VIRT_GET_IMAGE_INFO => { |
| let (control, mut response): ( |
| virtio_magma_virt_get_image_info_ctrl_t, |
| virtio_magma_virt_get_image_info_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let image_info_address_ref = |
| UserRef::new(UserAddress::from(control.image_info_out)); |
| let mut image_info_ptr = UserAddress::default(); |
| current_task.mm.read_object(image_info_address_ref, &mut image_info_ptr)?; |
| |
| match self.get_buffer_info( |
| control.connection as magma_connection_t, |
| control.image as magma_buffer_t, |
| ) { |
| Some(BufferInfo::Image(image_info)) => { |
| let image_info_ref = UserRef::new(image_info_ptr); |
| current_task.mm.write_object(image_info_ref, &image_info.info)?; |
| response.result_return = MAGMA_STATUS_OK as u64; |
| } |
| _ => { |
| tracing::error!("No image info was found for buffer: {:?}", { |
| control.image |
| }); |
| response.result_return = MAGMA_STATUS_INVALID_ARGS as u64; |
| } |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_VIRT_GET_IMAGE_INFO as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_BUFFER_SIZE => { |
| let (control, mut response): ( |
| virtio_magma_get_buffer_size_ctrl_t, |
| virtio_magma_get_buffer_size_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = unsafe { magma_get_buffer_size(control.buffer) }; |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_BUFFER_SIZE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_FLUSH => { |
| let (control, mut response): ( |
| virtio_magma_flush_ctrl_t, |
| virtio_magma_flush_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = |
| unsafe { magma_flush(control.connection as magma_connection_t) as u64 }; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_FLUSH as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_READ_NOTIFICATION_CHANNEL2 => { |
| let (control, mut response): ( |
| virtio_magma_read_notification_channel2_ctrl_t, |
| virtio_magma_read_notification_channel2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| // Buffer has a min length of 1 to make sure the call to |
| // `magma_read_notification_channel2` uses a valid reference. |
| let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)]; |
| let mut buffer_size_out = 0; |
| let mut more_data_out: u8 = 0; |
| |
| response.result_return = unsafe { |
| magma_read_notification_channel2( |
| control.connection as magma_connection_t, |
| &mut buffer[0] as *mut u8 as *mut std::ffi::c_void, |
| control.buffer_size, |
| &mut buffer_size_out, |
| &mut more_data_out as *mut u8, |
| ) as u64 |
| }; |
| |
| response.more_data_out = more_data_out as u64; |
| response.buffer_size_out = buffer_size_out; |
| current_task.mm.write_memory(UserAddress::from(control.buffer), &mut buffer)?; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_READ_NOTIFICATION_CHANNEL2 as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_BUFFER_HANDLE2 => { |
| let (control, mut response): ( |
| virtio_magma_get_buffer_handle2_ctrl_t, |
| virtio_magma_get_buffer_handle2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut buffer_handle_out = 0; |
| let status = unsafe { |
| magma_get_buffer_handle2( |
| control.buffer as magma_buffer_t, |
| &mut buffer_handle_out as *mut magma_handle_t, |
| ) |
| }; |
| if status != MAGMA_STATUS_OK as i32 { |
| response.result_return = status as u64; |
| } else { |
| let vmo = unsafe { zx::Vmo::from(zx::Handle::from_raw(buffer_handle_out)) }; |
| let file = Anon::new_file( |
| anon_fs(current_task.kernel()), |
| Box::new(VmoFileObject::new(Arc::new(vmo))), |
| OpenFlags::RDWR, |
| ); |
| let fd = current_task.files.add_with_flags(file, FdFlags::empty())?; |
| response.handle_out = fd.raw() as u64; |
| response.result_return = MAGMA_STATUS_OK as u64; |
| } |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_BUFFER_HANDLE2 as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_RELEASE_BUFFER => { |
| let (control, mut response): ( |
| virtio_magma_release_buffer_ctrl_t, |
| virtio_magma_release_buffer_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| self.connections.lock().get_mut(&{ control.connection }).map( |
| |buffers| match buffers.remove(&(control.buffer as u64)) { |
| Some(_) => unsafe { |
| magma_release_buffer( |
| control.connection as magma_connection_t, |
| control.buffer as magma_buffer_t, |
| ); |
| }, |
| _ => { |
| tracing::error!("Calling magma_release_buffer with an invalid buffer."); |
| } |
| }, |
| ); |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_RELEASE_BUFFER as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_EXPORT => { |
| let (control, mut response): ( |
| virtio_magma_export_ctrl_t, |
| virtio_magma_export_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut buffer_handle_out = 0; |
| let status = unsafe { |
| magma_export( |
| control.connection as magma_connection_t, |
| control.buffer as magma_buffer_t, |
| &mut buffer_handle_out as *mut magma_handle_t, |
| ) |
| }; |
| |
| if status as u32 == MAGMA_STATUS_OK { |
| let vmo = unsafe { zx::Vmo::from(zx::Handle::from_raw(buffer_handle_out)) }; |
| let file = match self |
| .connections |
| .lock() |
| .get(&{ control.connection }) |
| .and_then(|buffers| buffers.get(&(control.buffer as magma_buffer_t))) |
| { |
| Some(BufferInfo::Image(image_info)) => { |
| ImageFile::new(current_task.kernel(), image_info.clone(), vmo) |
| } |
| _ => Anon::new_file( |
| anon_fs(current_task.kernel()), |
| Box::new(VmoFileObject::new(Arc::new(vmo))), |
| OpenFlags::RDWR, |
| ), |
| }; |
| let fd = current_task.files.add_with_flags(file, FdFlags::empty())?; |
| response.buffer_handle_out = fd.raw() as u64; |
| } |
| |
| response.result_return = status as u64; |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_EXPORT as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_IMPORT => { |
| let (control, mut response): ( |
| virtio_magma_import_ctrl_t, |
| virtio_magma_import_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let buffer_fd = FdNumber::from_raw(control.buffer_handle as i32); |
| let (vmo, buffer) = MagmaFile::get_vmo_and_magma_buffer(current_task, buffer_fd)?; |
| |
| let mut buffer_out = magma_buffer_t::default(); |
| response.result_return = unsafe { |
| magma_import( |
| control.connection as magma_connection_t, |
| vmo.into_raw(), |
| &mut buffer_out, |
| ) as u64 |
| }; |
| |
| // Store the information for the newly imported buffer. |
| self.add_buffer_info(control.connection as magma_connection_t, buffer_out, buffer); |
| // Import is expected to close the file that was imported. |
| let _ = current_task.files.close(buffer_fd); |
| |
| response.buffer_out = buffer_out; |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_IMPORT as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_QUERY2 => { |
| let (control, mut response): ( |
| virtio_magma_query2_ctrl_t, |
| virtio_magma_query2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut value_out = 0; |
| response.result_return = |
| unsafe { magma_query2(control.device, control.id, &mut value_out) as u64 }; |
| response.value_out = value_out; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_QUERY2 as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_NOTIFICATION_CHANNEL_HANDLE => { |
| let (control, mut response): ( |
| virtio_magma_get_notification_channel_handle_ctrl_t, |
| virtio_magma_get_notification_channel_handle_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = unsafe { |
| magma_get_notification_channel_handle(control.connection as magma_connection_t) |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_NOTIFICATION_CHANNEL_HANDLE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CREATE_CONTEXT => { |
| let (control, mut response): ( |
| virtio_magma_create_context_ctrl_t, |
| virtio_magma_create_context_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut context_id_out = 0; |
| response.result_return = unsafe { |
| magma_create_context( |
| control.connection as magma_connection_t, |
| &mut context_id_out, |
| ) as u64 |
| }; |
| response.context_id_out = context_id_out as u64; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CREATE_CONTEXT as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_RELEASE_CONTEXT => { |
| let (control, mut response): ( |
| virtio_magma_release_context_ctrl_t, |
| virtio_magma_release_context_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| unsafe { |
| magma_release_context( |
| control.connection as magma_connection_t, |
| control.context_id as u32, |
| ); |
| } |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_RELEASE_CONTEXT as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CREATE_BUFFER => { |
| let (control, mut response): ( |
| virtio_magma_create_buffer_ctrl_t, |
| virtio_magma_create_buffer_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut size_out = 0; |
| let mut buffer_out = 0; |
| response.result_return = unsafe { |
| magma_create_buffer( |
| control.connection as magma_connection_t, |
| control.size, |
| &mut size_out, |
| &mut buffer_out, |
| ) as u64 |
| }; |
| response.size_out = size_out; |
| response.buffer_out = buffer_out; |
| self.add_buffer_info( |
| control.connection as magma_connection_t, |
| buffer_out, |
| BufferInfo::Default, |
| ); |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CREATE_BUFFER as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_BUFFER_ID => { |
| let (control, mut response): ( |
| virtio_magma_get_buffer_id_ctrl_t, |
| virtio_magma_get_buffer_id_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = |
| unsafe { magma_get_buffer_id(control.buffer as magma_buffer_t) }; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_BUFFER_ID as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_CREATE_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_create_semaphore_ctrl_t, |
| virtio_magma_create_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut semaphore_out = 0; |
| response.result_return = unsafe { |
| magma_create_semaphore( |
| control.connection as magma_connection_t, |
| &mut semaphore_out, |
| ) as u64 |
| }; |
| |
| response.semaphore_out = semaphore_out; |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CREATE_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_QUERY_RETURNS_BUFFER2 => { |
| let (control, mut response): ( |
| virtio_magma_query_returns_buffer2_ctrl_t, |
| virtio_magma_query_returns_buffer2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut handle_out = 0; |
| response.result_return = unsafe { |
| magma_query_returns_buffer2( |
| control.device as magma_device_t, |
| control.id, |
| &mut handle_out, |
| ) as u64 |
| }; |
| let vmo = unsafe { zx::Vmo::from(zx::Handle::from_raw(handle_out)) }; |
| let file = Anon::new_file( |
| anon_fs(current_task.kernel()), |
| Box::new(VmoFileObject::new(Arc::new(vmo))), |
| OpenFlags::RDWR, |
| ); |
| let fd = current_task.files.add_with_flags(file, FdFlags::empty())?; |
| |
| response.handle_out = fd.raw() as u64; |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_QUERY_RETURNS_BUFFER2 as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_ERROR => { |
| let (control, mut response): ( |
| virtio_magma_get_error_ctrl_t, |
| virtio_magma_get_error_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = |
| unsafe { magma_get_error(control.connection as magma_connection_t) as u64 }; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_ERROR as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_IMPORT_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_import_semaphore_ctrl_t, |
| virtio_magma_import_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut semaphore_out = 0; |
| response.result_return = unsafe { |
| magma_import_semaphore( |
| control.connection as magma_connection_t, |
| control.semaphore_handle, |
| &mut semaphore_out, |
| ) as u64 |
| }; |
| response.semaphore_out = semaphore_out; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_IMPORT_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_GET_SEMAPHORE_ID => { |
| let (control, mut response): ( |
| virtio_magma_get_semaphore_id_ctrl_t, |
| virtio_magma_get_semaphore_id_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = unsafe { |
| magma_get_semaphore_id(control.semaphore as magma_semaphore_t) as u64 |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_GET_SEMAPHORE_ID as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_RELEASE_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_release_semaphore_ctrl_t, |
| virtio_magma_release_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| unsafe { |
| magma_release_semaphore( |
| control.connection as magma_connection_t, |
| control.semaphore as magma_semaphore_t, |
| ); |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_RELEASE_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_EXPORT_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_export_semaphore_ctrl_t, |
| virtio_magma_export_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| let mut semaphore_handle_out = 0; |
| response.result_return = unsafe { |
| magma_export_semaphore( |
| control.connection as magma_connection_t, |
| control.semaphore as magma_semaphore_t, |
| &mut semaphore_handle_out, |
| ) as u64 |
| }; |
| response.semaphore_handle_out = semaphore_handle_out as u64; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_EXPORT_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_RESET_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_reset_semaphore_ctrl_t, |
| virtio_magma_reset_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| unsafe { |
| magma_reset_semaphore(control.semaphore as magma_semaphore_t); |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_RESET_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_SIGNAL_SEMAPHORE => { |
| let (control, mut response): ( |
| virtio_magma_signal_semaphore_ctrl_t, |
| virtio_magma_signal_semaphore_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| unsafe { |
| magma_signal_semaphore(control.semaphore as magma_semaphore_t); |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_SIGNAL_SEMAPHORE as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_MAP_BUFFER_GPU => { |
| let (control, mut response): ( |
| virtio_magma_map_buffer_gpu_ctrl_t, |
| virtio_magma_map_buffer_gpu_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| response.result_return = unsafe { |
| magma_map_buffer_gpu( |
| control.connection as magma_connection_t, |
| control.buffer, |
| control.page_offset, |
| control.page_count, |
| control.gpu_va, |
| control.map_flags, |
| ) as u64 |
| }; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_MAP_BUFFER_GPU as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_POLL => { |
| let (control, mut response): (virtio_magma_poll_ctrl_t, virtio_magma_poll_resp_t) = |
| read_control_and_response(current_task, &command)?; |
| |
| let num_items = control.count as usize / std::mem::size_of::<StarnixPollItem>(); |
| let items_ref = UserRef::new(UserAddress::from(control.items)); |
| // Read the poll items as `StarnixPollItem`, since they contain a union. Also note |
| // that the minimum length of the vector is 1, to always have a valid reference for |
| // `magma_poll`. |
| let mut starnix_items = |
| vec![StarnixPollItem::default(); std::cmp::max(num_items, 1)]; |
| current_task.mm.read_objects(items_ref, &mut starnix_items)?; |
| // Then convert each item "manually" into `magma_poll_item_t`. |
| let mut magma_items: Vec<magma_poll_item_t> = |
| starnix_items.iter().map(|item| item.into_poll_item()).collect(); |
| |
| response.result_return = unsafe { |
| magma_poll( |
| &mut magma_items[0] as *mut magma_poll_item, |
| num_items as u32, |
| control.timeout_ns, |
| ) as u64 |
| }; |
| |
| // Convert the poll items back to a serializable version after the `magma_poll` |
| // call. |
| let starnix_items: Vec<StarnixPollItem> = |
| magma_items.iter().map(StarnixPollItem::new).collect(); |
| current_task.mm.write_objects(items_ref, &starnix_items)?; |
| |
| response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_POLL as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_CMD_EXECUTE_COMMAND_BUFFER_WITH_RESOURCES2 => { |
| let (control, mut response): ( |
| virtio_magma_execute_command_buffer_with_resources2_ctrl_t, |
| virtio_magma_execute_command_buffer_with_resources2_resp_t, |
| ) = read_control_and_response(current_task, &command)?; |
| |
| // First read the `virtmagma_command_buffer`, this contains pointers to all the |
| // remaining structures needed by `magma_execute_command_buffer_with_resources2`. |
| let virt_command_buffer_ref = |
| UserRef::new(UserAddress::from(control.command_buffer as u64)); |
| let mut virt_command_buffer = virtmagma_command_buffer::default(); |
| current_task.mm.read_object(virt_command_buffer_ref, &mut virt_command_buffer)?; |
| |
| let command_buffer_ref = UserRef::<magma_command_buffer>::new(UserAddress::from( |
| virt_command_buffer.command_buffer, |
| )); |
| let mut command_buffer = magma_command_buffer::default(); |
| current_task.mm.read_object(command_buffer_ref, &mut command_buffer)?; |
| |
| let resources_ref = UserRef::<magma_exec_resource>::new(UserAddress::from( |
| virt_command_buffer.resources as u64, |
| )); |
| let mut resources = vec![ |
| magma_exec_resource::default(); |
| std::cmp::max(1, command_buffer.resource_count as usize) |
| ]; |
| // The resources vector is of length >= 1, since we need a valid reference to pass |
| // to the magma function even when there is no data. This check is here to prevent |
| // us from reading objects when the length is really 0. |
| if command_buffer.resource_count > 0 { |
| current_task.mm.read_objects(resources_ref, &mut resources)?; |
| } |
| |
| let semaphore_ids_ref = |
| UserRef::<u64>::new(UserAddress::from(virt_command_buffer.semaphores as u64)); |
| let semaphore_count = (command_buffer.wait_semaphore_count |
| + command_buffer.signal_semaphore_count) |
| as usize; |
| let mut semaphore_ids = vec![0; std::cmp::max(1, semaphore_count)]; |
| // This check exists for the same reason as the command_buffer.resource_count check |
| // above (to avoid reading when the actual count is 0). |
| if semaphore_count > 0 { |
| current_task.mm.read_objects(semaphore_ids_ref, &mut semaphore_ids)?; |
| } |
| response.result_return = unsafe { |
| magma_execute_command_buffer_with_resources2( |
| control.connection as magma_connection_t, |
| control.context_id, |
| &mut command_buffer, |
| &mut resources[0] as *mut magma_exec_resource, |
| &mut semaphore_ids[0] as *mut u64, |
| ) as u64 |
| }; |
| |
| response.hdr.type_ = |
| virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_EXECUTE_COMMAND_BUFFER_WITH_RESOURCES2 |
| as u32; |
| current_task.mm.write_object(UserRef::new(response_address), &response) |
| } |
| t => { |
| tracing::warn!("Got unknown request: {:?}", t); |
| error!(ENOSYS) |
| } |
| }?; |
| |
| Ok(SUCCESS) |
| } |
| |
| fn read( |
| &self, |
| _file: &FileObject, |
| _current_task: &CurrentTask, |
| _data: &[UserBuffer], |
| ) -> Result<usize, Errno> { |
| error!(EINVAL) |
| } |
| |
| fn write( |
| &self, |
| _file: &FileObject, |
| _current_task: &CurrentTask, |
| _data: &[UserBuffer], |
| ) -> Result<usize, Errno> { |
| error!(EINVAL) |
| } |
| } |