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 =
260 current_task.read_object(UserAddress::from(command_descriptor.descriptor).into())?;
261
262 let semaphore_count =
263 wire_descriptor.wait_semaphore_count + wire_descriptor.signal_semaphore_count;
264
265 let mut status: i32 = MAGMA_STATUS_OK;
266 let mut wait_semaphore_count: u32 = 0;
267 let mut signal_semaphore_count: u32 = 0;
268 let mut child_semaphore_ids: Vec<u64> = vec![];
269
270 if semaphore_count > 0 {
271 let semaphore_ids: Vec<u64> = read_objects(
272 current_task,
273 command_descriptor.semaphores.into(),
274 semaphore_count as usize,
275 )?;
276
277 for (i, id) in semaphore_ids.iter().enumerate() {
278 match get_semaphore(*id) {
279 Ok(semaphore) => {
280 let ids_ref = &semaphore.ids;
281 for id in ids_ref {
282 child_semaphore_ids.push(*id);
283 }
284 if i < wire_descriptor.wait_semaphore_count as usize {
285 wait_semaphore_count += ids_ref.len() as u32;
286 } else {
287 signal_semaphore_count += ids_ref.len() as u32;
288 }
289 }
290 Err(s) => {
291 status = s;
292 break;
293 }
294 }
295 }
296 } else {
297 child_semaphore_ids.push(0);
299 }
300
301 if status == MAGMA_STATUS_OK {
302 let mut magma_command_descriptor = magma_command_descriptor {
305 resource_count: wire_descriptor.resource_count,
306 command_buffer_count: wire_descriptor.command_buffer_count,
307 wait_semaphore_count,
308 signal_semaphore_count,
309 flags: wire_descriptor.flags,
310 ..Default::default()
311 };
312
313 let mut resources: Vec<magma_exec_resource> = read_objects(
315 current_task,
316 command_descriptor.resources.into(),
317 wire_descriptor.resource_count as usize,
318 )?;
319 let mut command_buffers: Vec<magma_exec_command_buffer> = read_objects(
320 current_task,
321 command_descriptor.command_buffers.into(),
322 wire_descriptor.command_buffer_count as usize,
323 )?;
324
325 magma_command_descriptor.resources = &mut resources[0] as *mut magma_exec_resource;
328 magma_command_descriptor.command_buffers =
329 &mut command_buffers[0] as *mut magma_exec_command_buffer;
330 magma_command_descriptor.semaphore_ids = &mut child_semaphore_ids[0] as *mut u64;
331
332 status = {
333 #[allow(
334 clippy::undocumented_unsafe_blocks,
335 reason = "Force documented unsafe blocks in Starnix"
336 )]
337 unsafe {
338 magma_connection_execute_command(
339 connection.handle,
340 control.context_id,
341 &mut magma_command_descriptor as *mut magma_command_descriptor,
342 )
343 }
344 };
345 }
346
347 Ok(status)
348}
349
350pub fn execute_inline_commands<F>(
358 current_task: &CurrentTask,
359 control: virtio_magma_connection_execute_inline_commands_ctrl_t,
360 connection: &Arc<MagmaConnection>,
361 get_semaphore: F,
362) -> Result<magma_status_t, Errno>
363where
364 F: Fn(u64) -> Result<Arc<MagmaSemaphore>, i32>,
365{
366 let command_buffers_addr = UserAddress::from(control.command_buffers);
367
368 let descriptors: Vec<virtmagma_command_descriptor> =
371 read_objects(current_task, command_buffers_addr, control.command_count as usize)?;
372
373 let mut commands =
374 vec![magma_inline_command_buffer::default(); std::cmp::max(descriptors.len(), 1)];
375
376 let mut commands_vec = Vec::<Vec<u8>>::with_capacity(control.command_count as usize);
377 let mut semaphore_ids_vec = Vec::<Vec<u64>>::with_capacity(control.command_count as usize);
378
379 let mut status: i32 = MAGMA_STATUS_OK;
380
381 for i in 0..control.command_count as usize {
382 let size = descriptors[i].command_buffer_size;
383 let data = current_task.read_buffer(&UserBuffer {
384 address: UserAddress::from(descriptors[i].command_buffers),
385 length: size as usize,
386 })?;
387 commands_vec.push(data);
388 commands[i].size = size;
389
390 let semaphore_count =
391 (descriptors[i].semaphore_size / core::mem::size_of::<u64>() as u64) as u32;
392
393 let mut child_semaphore_ids: Vec<u64> = vec![];
394 if semaphore_count > 0 {
395 let semaphore_ids = read_objects(
396 current_task,
397 UserAddress::from(descriptors[i].semaphores),
398 semaphore_count as usize,
399 )?;
400
401 for id in semaphore_ids.iter() {
402 match get_semaphore(*id) {
403 Ok(semaphore) => {
404 let child_ids_ref = &semaphore.ids;
405 for child_id in child_ids_ref {
406 child_semaphore_ids.push(*child_id);
407 }
408 }
409 Err(s) => {
410 status = s;
411 break;
412 }
413 }
414 }
415 }
416 commands[i].semaphore_count = child_semaphore_ids.len() as u32;
417 semaphore_ids_vec.push(child_semaphore_ids);
418 }
419
420 if status == MAGMA_STATUS_OK {
421 status = {
422 #[allow(
423 clippy::undocumented_unsafe_blocks,
424 reason = "Force documented unsafe blocks in Starnix"
425 )]
426 unsafe {
427 for i in 0..control.command_count as usize {
428 commands[i].data = &mut commands_vec[i][0] as *mut u8 as *mut std::ffi::c_void;
429 commands[i].semaphore_ids = if commands[i].semaphore_count == 0 {
430 std::ptr::null_mut()
431 } else {
432 &mut semaphore_ids_vec[i][0]
433 };
434 }
435 magma_connection_execute_inline_commands(
436 connection.handle,
437 control.context_id,
438 control.command_count,
439 &mut commands[0],
440 )
441 }
442 };
443 }
444
445 Ok(status)
446}
447
448pub fn export_buffer(
464 locked: &mut Locked<Unlocked>,
465 current_task: &CurrentTask,
466 _control: virtio_magma_buffer_export_ctrl_t,
467 response: &mut virtio_magma_buffer_export_resp_t,
468 buffer: &Arc<MagmaBuffer>,
469 connections: &ConnectionMap,
470) -> Result<(), Errno> {
471 let mut buffer_handle_out = 0;
472 #[allow(
473 clippy::undocumented_unsafe_blocks,
474 reason = "Force documented unsafe blocks in Starnix"
475 )]
476 let status = unsafe {
477 magma_buffer_export(
478 buffer.handle as magma_buffer_t,
479 &mut buffer_handle_out as *mut magma_handle_t,
480 )
481 };
482 if status == MAGMA_STATUS_OK {
483 #[allow(
484 clippy::undocumented_unsafe_blocks,
485 reason = "Force documented unsafe blocks in Starnix"
486 )]
487 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
488 let memory = MemoryObject::from(vmo);
489
490 let mut image_info_opt: Option<ImageInfo> = None;
491 'outer: for image_map in connections.values() {
492 for (image, info) in image_map.buffer_map.iter() {
493 if *image == buffer.handle {
494 if let BufferInfo::Image(image_info) = info.clone() {
495 image_info_opt = Some(image_info);
496 break 'outer;
497 }
498 }
499 }
500 }
501
502 let file = {
503 if let Some(image_info) = image_info_opt {
504 ImageFile::new_file(locked, current_task, image_info, memory)
505 } else {
506 Anon::new_private_file(
508 locked,
509 current_task,
510 Box::new(MemoryRegularFile::new(Arc::new(memory))),
511 OpenFlags::RDWR,
512 "[fuchsia:magma_export_buffer]",
513 )
514 }
515 };
516
517 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
518 response.buffer_handle_out = fd.raw() as u64;
519 }
520
521 response.result_return = status as u64;
522 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_EXPORT as u32;
523
524 Ok(())
525}
526
527pub fn flush(
531 _control: virtio_magma_connection_flush_ctrl_t,
532 response: &mut virtio_magma_connection_flush_resp_t,
533 connection: &Arc<MagmaConnection>,
534) {
535 response.result_return = {
536 #[allow(
537 clippy::undocumented_unsafe_blocks,
538 reason = "Force documented unsafe blocks in Starnix"
539 )]
540 unsafe {
541 magma_connection_flush(connection.handle) as u64
542 }
543 };
544 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_FLUSH as u32;
545}
546
547pub fn get_buffer_handle(
557 locked: &mut Locked<Unlocked>,
558 current_task: &CurrentTask,
559 _control: virtio_magma_buffer_get_handle_ctrl_t,
560 response: &mut virtio_magma_buffer_get_handle_resp_t,
561 buffer: &Arc<MagmaBuffer>,
562) -> Result<(), Errno> {
563 let mut buffer_handle_out = 0;
564 #[allow(
565 clippy::undocumented_unsafe_blocks,
566 reason = "Force documented unsafe blocks in Starnix"
567 )]
568 let status = unsafe {
569 magma_buffer_get_handle(
570 buffer.handle as magma_buffer_t,
571 &mut buffer_handle_out as *mut magma_handle_t,
572 )
573 };
574
575 if status != MAGMA_STATUS_OK {
576 response.result_return = status as u64;
577 } else {
578 #[allow(
579 clippy::undocumented_unsafe_blocks,
580 reason = "Force documented unsafe blocks in Starnix"
581 )]
582 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(buffer_handle_out)) };
583 let memory = MemoryObject::from(vmo);
584 let file = Anon::new_private_file(
586 locked,
587 current_task,
588 Box::new(MemoryRegularFile::new(Arc::new(memory))),
589 OpenFlags::RDWR,
590 "[fuchsia:magma_buffer]",
591 );
592 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
593 response.handle_out = fd.raw() as u64;
594 response.result_return = MAGMA_STATUS_OK as u64;
595 }
596
597 response.hdr.type_ = virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_BUFFER_GET_HANDLE as u32;
598
599 Ok(())
600}
601
602pub fn query(
610 locked: &mut Locked<Unlocked>,
611 current_task: &CurrentTask,
612 device: magma_device_t,
613 id: magma_query_t,
614 response: &mut virtio_magma_device_query_resp_t,
615) -> Result<(), Errno> {
616 let mut result_buffer_out = 0;
617 let mut result_out = 0;
618 response.result_return = {
619 #[allow(
620 clippy::undocumented_unsafe_blocks,
621 reason = "Force documented unsafe blocks in Starnix"
622 )]
623 unsafe {
624 magma_device_query(device, id, &mut result_buffer_out, &mut result_out) as u64
625 }
626 };
627
628 if result_buffer_out != zx::sys::ZX_HANDLE_INVALID {
629 #[allow(
630 clippy::undocumented_unsafe_blocks,
631 reason = "Force documented unsafe blocks in Starnix"
632 )]
633 let vmo = unsafe { zx::Vmo::from(zx::NullableHandle::from_raw(result_buffer_out)) };
634 let memory = MemoryObject::from(vmo);
635 let memory_size = memory.get_size();
636 let mut info = FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred());
638 info.size = memory_size as usize;
640 let file = Anon::new_private_file_extended(
641 locked,
642 current_task,
643 Box::new(MemoryRegularFile::new(Arc::new(memory))),
644 OpenFlags::RDWR,
645 "[fuchsia:magma_vmo]",
646 info,
647 );
648 let fd = current_task.add_file(locked, file, FdFlags::empty())?;
649 response.result_buffer_out = fd.raw() as u64;
650 } else {
651 response.result_buffer_out = u64::MAX;
652 }
653
654 response.result_out = result_out;
655
656 Ok(())
657}
658
659pub fn read_notification_channel(
667 current_task: &CurrentTask,
668 control: virtio_magma_connection_read_notification_channel_ctrl_t,
669 response: &mut virtio_magma_connection_read_notification_channel_resp_t,
670 connection: &Arc<MagmaConnection>,
671) -> Result<(), Errno> {
672 let mut buffer = vec![0; std::cmp::max(control.buffer_size as usize, 1)];
675 let mut buffer_size_out = 0;
676 let mut more_data_out: u8 = 0;
677
678 response.result_return = {
679 #[allow(
680 clippy::undocumented_unsafe_blocks,
681 reason = "Force documented unsafe blocks in Starnix"
682 )]
683 unsafe {
684 magma_connection_read_notification_channel(
685 connection.handle,
686 &mut buffer[0] as *mut u8 as *mut std::ffi::c_void,
687 control.buffer_size,
688 &mut buffer_size_out,
689 &mut more_data_out as *mut u8,
690 ) as u64
691 }
692 };
693
694 response.more_data_out = more_data_out as u64;
695 response.buffer_size_out = buffer_size_out;
696 response.hdr.type_ =
697 virtio_magma_ctrl_type_VIRTIO_MAGMA_RESP_CONNECTION_READ_NOTIFICATION_CHANNEL as u32;
698
699 current_task.write_memory(UserAddress::from(control.buffer), &buffer)?;
700
701 Ok(())
702}
703
704pub fn import_semaphore2(
705 connection: &Arc<MagmaConnection>,
706 counter: zx::Counter,
707 flags: u64,
708) -> (i32, u64, u64) {
709 let mut semaphore = 0;
710 let mut semaphore_id = 0;
711 #[allow(
712 clippy::undocumented_unsafe_blocks,
713 reason = "Force documented unsafe blocks in Starnix"
714 )]
715 let status = unsafe {
716 magma_connection_import_semaphore2(
717 connection.handle,
718 counter.into_raw(),
719 flags,
720 &mut semaphore,
721 &mut semaphore_id,
722 )
723 };
724 (status, semaphore, semaphore_id)
725}