1use 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
47fn 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
71pub 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
97fn 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 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
152pub 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
208pub 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#[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
237pub 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 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 child_semaphore_ids.push(0);
298 }
299
300 if status == MAGMA_STATUS_OK {
301 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 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 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
349pub 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 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
447pub fn export_buffer(
463 locked: &mut Locked<Unlocked>,
464 current_task: &CurrentTask,
465 _control: virtio_magma_buffer_export_ctrl_t,
466 response: &mut virtio_magma_buffer_export_resp_t,
467 buffer: &Arc<MagmaBuffer>,
468 connections: &ConnectionMap,
469) -> Result<(), Errno> {
470 let mut buffer_handle_out = 0;
471 #[allow(
472 clippy::undocumented_unsafe_blocks,
473 reason = "Force documented unsafe blocks in Starnix"
474 )]
475 let status = unsafe {
476 magma_buffer_export(
477 buffer.handle as magma_buffer_t,
478 &mut buffer_handle_out as *mut magma_handle_t,
479 )
480 };
481 if status == MAGMA_STATUS_OK {
482 #[allow(
483 clippy::undocumented_unsafe_blocks,
484 reason = "Force documented unsafe blocks in Starnix"
485 )]
486 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
487 let memory = MemoryObject::from(vmo);
488
489 let mut image_info_opt: Option<ImageInfo> = None;
490 'outer: for image_map in connections.values() {
491 for (image, info) in image_map.buffer_map.iter() {
492 if *image == buffer.handle {
493 if let BufferInfo::Image(image_info) = info.clone() {
494 image_info_opt = Some(image_info);
495 break 'outer;
496 }
497 }
498 }
499 }
500
501 let file = {
502 if let Some(image_info) = image_info_opt {
503 ImageFile::new_file(locked, current_task, image_info, memory)
504 } else {
505 Anon::new_private_file(
507 locked,
508 current_task,
509 Box::new(MemoryRegularFile::new(Arc::new(memory))),
510 OpenFlags::RDWR,
511 "[fuchsia:magma_export_buffer]",
512 )
513 }
514 };
515
516 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
517 response.buffer_handle_out = fd.raw() as u64;
518 }
519
520 response.result_return = status as u64;
521 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT as u32;
522
523 Ok(())
524}
525
526pub fn flush(
530 _control: virtio_magma_connection_flush_ctrl_t,
531 response: &mut virtio_magma_connection_flush_resp_t,
532 connection: &Arc<MagmaConnection>,
533) {
534 response.result_return = {
535 #[allow(
536 clippy::undocumented_unsafe_blocks,
537 reason = "Force documented unsafe blocks in Starnix"
538 )]
539 unsafe {
540 magma_connection_flush(connection.handle) as u64
541 }
542 };
543 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH as u32;
544}
545
546pub fn get_buffer_handle(
556 locked: &mut Locked<Unlocked>,
557 current_task: &CurrentTask,
558 _control: virtio_magma_buffer_get_handle_ctrl_t,
559 response: &mut virtio_magma_buffer_get_handle_resp_t,
560 buffer: &Arc<MagmaBuffer>,
561) -> Result<(), Errno> {
562 let mut buffer_handle_out = 0;
563 #[allow(
564 clippy::undocumented_unsafe_blocks,
565 reason = "Force documented unsafe blocks in Starnix"
566 )]
567 let status = unsafe {
568 magma_buffer_get_handle(
569 buffer.handle as magma_buffer_t,
570 &mut buffer_handle_out as *mut magma_handle_t,
571 )
572 };
573
574 if status != MAGMA_STATUS_OK {
575 response.result_return = status as u64;
576 } else {
577 #[allow(
578 clippy::undocumented_unsafe_blocks,
579 reason = "Force documented unsafe blocks in Starnix"
580 )]
581 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
582 let memory = MemoryObject::from(vmo);
583 let file = Anon::new_private_file(
585 locked,
586 current_task,
587 Box::new(MemoryRegularFile::new(Arc::new(memory))),
588 OpenFlags::RDWR,
589 "[fuchsia:magma_buffer]",
590 );
591 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
592 response.handle_out = fd.raw() as u64;
593 response.result_return = MAGMA_STATUS_OK as u64;
594 }
595
596 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE as u32;
597
598 Ok(())
599}
600
601pub fn query(
609 locked: &mut Locked<Unlocked>,
610 current_task: &CurrentTask,
611 device: magma_device_t,
612 id: magma_query_t,
613 response: &mut virtio_magma_device_query_resp_t,
614) -> Result<(), Errno> {
615 let mut result_buffer_out = 0;
616 let mut result_out = 0;
617 response.result_return = {
618 #[allow(
619 clippy::undocumented_unsafe_blocks,
620 reason = "Force documented unsafe blocks in Starnix"
621 )]
622 unsafe {
623 magma_device_query(device, id, &mut result_buffer_out, &mut result_out) as u64
624 }
625 };
626
627 if result_buffer_out != zx::sys::ZX_HANDLE_INVALID {
628 #[allow(
629 clippy::undocumented_unsafe_blocks,
630 reason = "Force documented unsafe blocks in Starnix"
631 )]
632 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(result_buffer_out)) };
633 let memory = MemoryObject::from(vmo);
634 let memory_size = memory.get_size();
635 let mut info = FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred());
637 info.size = memory_size as usize;
639 let file = Anon::new_private_file_extended(
640 locked,
641 current_task,
642 Box::new(MemoryRegularFile::new(Arc::new(memory))),
643 OpenFlags::RDWR,
644 "[fuchsia:magma_vmo]",
645 info,
646 );
647 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
648 response.result_buffer_out = fd.raw() as u64;
649 } else {
650 response.result_buffer_out = u64::MAX;
651 }
652
653 response.result_out = result_out;
654
655 Ok(())
656}
657
658pub fn read_notification_channel(
666 current_task: &CurrentTask,
667 control: virtio_magma_connection_read_notification_channel_ctrl_t,
668 response: &mut virtio_magma_connection_read_notification_channel_resp_t,
669 connection: &Arc<MagmaConnection>,
670) -> Result<(), Errno> {
671 let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)];
674 let mut buffer_size_out = 0;
675 let mut more_data_out: u8 = 0;
676
677 response.result_return = {
678 #[allow(
679 clippy::undocumented_unsafe_blocks,
680 reason = "Force documented unsafe blocks in Starnix"
681 )]
682 unsafe {
683 magma_connection_read_notification_channel(
684 connection.handle,
685 &mut buffer[0] as *mut u8 as *mut std::ffi::c_void,
686 control.buffer_size,
687 &mut buffer_size_out,
688 &mut more_data_out as *mut u8,
689 ) as u64
690 }
691 };
692
693 response.more_data_out = more_data_out as u64;
694 response.buffer_size_out = buffer_size_out;
695 response.hdr.type_ =
696 virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL as u32;
697
698 current_task.write_memory(UserAddress::from(control.buffer), &buffer)?;
699
700 Ok(())
701}
702
703pub fn import_semaphore2(
704 connection: &Arc<MagmaConnection>,
705 counter: zx::Counter,
706 flags: u64,
707) -> (i32, u64, u64) {
708 let mut semaphore = 0;
709 let mut semaphore_id = 0;
710 #[allow(
711 clippy::undocumented_unsafe_blocks,
712 reason = "Force documented unsafe blocks in Starnix"
713 )]
714 let status = unsafe {
715 magma_connection_import_semaphore2(
716 connection.handle,
717 counter.into_raw(),
718 flags,
719 &mut semaphore,
720 &mut semaphore_id,
721 )
722 };
723 (status, semaphore, semaphore_id)
724}