Skip to main content

starnix_modules_kgsl/
file.rs

1// Copyright 2025 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::util::maur::{self, TaskWritable};
6use crate::util::{KgslCmdBatchFlags, KgslContextFlags, KgslMemFlags};
7use fdio::service_connect;
8use kgsl_libmagma::{
9    Buffer, Connection, Context, Device, QueryOutput, Semaphore, initialize_logging,
10};
11use kgsl_magma_params::{AdrenoKgslParams, MAGMA_QCOM_ADRENO_QUERY_KGSL_PARAMS};
12use kgsl_strings::{ioctl_kgsl, kgsl_prop};
13use magma::{
14    MAGMA_MAP_FLAG_READ, MAGMA_MAP_FLAG_WRITE, MAGMA_QUERY_DEVICE_ID, MAGMA_QUERY_VENDOR_ID,
15};
16use range_alloc::RangeAllocator;
17use starnix_core::mm::memory::MemoryObject;
18use starnix_core::mm::{MappingName, MemoryAccessorExt, PAGE_SIZE};
19use starnix_core::task::CurrentTask;
20use starnix_core::vfs::{FileObject, FileOps, FsNode};
21use starnix_core::{fileops_impl_dataless, fileops_impl_nonseekable, fileops_impl_noop_sync};
22use starnix_logging::{log_error, log_info, log_warn, track_stub};
23use starnix_sync::{
24    KgslAllocatorLock, KgslContextsLock, KgslGpuObjsLock, KgslSyncSourcesLock, LockDepMutex,
25};
26use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
27use starnix_uapi::device_id::DeviceId;
28use starnix_uapi::errors::Errno;
29use starnix_uapi::open_flags::OpenFlags;
30use starnix_uapi::user_address::{UserAddress, UserRef};
31use starnix_uapi::{errno, error, kgsl_command_object, kgsl_command_syncpoint, uapi};
32use std::collections::HashMap;
33use std::collections::hash_map::Entry;
34use std::sync::atomic::{AtomicU32, Ordering};
35use std::sync::{Arc, Once};
36
37#[cfg(feature = "starnix-kgsl-debug")]
38#[macro_export]
39macro_rules! kgsl_debug {
40    ($fmt:expr $(, $arg:expr)*) => {
41        log_info!("kgsl: {}:{}: {}", file!(), line!(), format_args!($fmt $(, $arg)*));
42    };
43}
44
45#[cfg(not(feature = "starnix-kgsl-debug"))]
46#[macro_export]
47macro_rules! kgsl_debug {
48    ($($arg:tt)*) => {};
49}
50
51/// The maximum alignment of any buffer that can be allocated. The additional address space used by
52/// many small buffers that only need page alignment is not a concern, as the total amount of
53/// address space is very large.
54const BUFFER_ALIGNMENT: u64 = 65536;
55
56/// Helper trait to manage allocations in units of BUFFER_ALIGNMENT.
57trait RangeAllocatorExt {
58    fn create(size: u64) -> Self;
59    fn allocate(&mut self, size: u64) -> Option<u64>;
60    fn free(&mut self, gpuaddr: u64, size: u64);
61}
62
63impl RangeAllocatorExt for RangeAllocator<u64> {
64    fn create(size: u64) -> Self {
65        RangeAllocator::new(0..(size / BUFFER_ALIGNMENT))
66    }
67
68    fn allocate(&mut self, size: u64) -> Option<u64> {
69        self.allocate_range(size.div_ceil(BUFFER_ALIGNMENT))
70            .ok()
71            .map(|r| r.start * BUFFER_ALIGNMENT)
72    }
73
74    fn free(&mut self, gpuaddr: u64, size: u64) {
75        let start_unit = gpuaddr / BUFFER_ALIGNMENT;
76        let units = size.div_ceil(BUFFER_ALIGNMENT);
77        self.free_range(start_unit..(start_unit + units));
78    }
79}
80
81pub struct KgslFile {
82    // This member will be read in a future change. A linter attribute is used
83    // instead of an underscore prefix so that field init shorthand still works.
84    #[expect(dead_code)]
85    device: Device,
86    connection: Connection,
87    adreno_kgsl_params: AdrenoKgslParams,
88    // TODO(b/481419355): transition to id-map container once available
89    syncsources: LockDepMutex<HashMap<u32, Semaphore>, KgslSyncSourcesLock>,
90    next_syncsource_id: AtomicU32,
91    // TODO(b/481419355): transition to id-map container once available
92    gpuobjs: LockDepMutex<HashMap<u32, GpuObject>, KgslGpuObjsLock>,
93    next_gpuobj_id: AtomicU32,
94    allocator: LockDepMutex<RangeAllocator<u64>, KgslAllocatorLock>,
95    shadow_properties: uapi::kgsl_shadowprop,
96    // TODO(b/481419355): transition to id-map container once available
97    contexts: LockDepMutex<HashMap<u32, Context>, KgslContextsLock>,
98    next_context_id: AtomicU32,
99}
100
101struct GpuObject {
102    buffer: Buffer,
103    flags: u64,
104    size: u64,
105    mmapsize: u64,
106    gpuaddr: u64,
107}
108
109fn map_flags(flags: KgslMemFlags) -> Result<u64, Errno> {
110    match (flags.gpu_read_only(), flags.gpu_write_only()) {
111        (true, false) => Ok(MAGMA_MAP_FLAG_READ),
112        (false, true) => Ok(MAGMA_MAP_FLAG_WRITE),
113        (false, false) => Ok(MAGMA_MAP_FLAG_READ | MAGMA_MAP_FLAG_WRITE),
114        (true, true) => Err(errno!(EINVAL)),
115    }
116}
117
118impl KgslFile {
119    pub fn init() {
120        match Self::init_magma_logging() {
121            Ok(()) => log_info!("kgsl: magma logging enabled"),
122            Err(()) => log_warn!("kgsl: magma logging failed to initialize"),
123        };
124    }
125
126    fn init_magma_logging() -> Result<(), ()> {
127        let (client, server) = zx::Channel::create();
128        service_connect("/svc/fuchsia.logger.LogSink", server).map_err(|_| ())?;
129        return initialize_logging(client);
130    }
131
132    fn import_device(path: &str) -> Result<Device, zx::Status> {
133        let (client, server) = zx::Channel::create();
134        service_connect(&path, server)?;
135        let device = Device::from_channel(client).map_err(|_| zx::Status::INTERNAL)?;
136        let QueryOutput::Value(vendor_id) =
137            device.query(MAGMA_QUERY_VENDOR_ID).map_err(|_| zx::Status::INTERNAL)?
138        else {
139            return Err(zx::Status::INTERNAL);
140        };
141        let QueryOutput::Value(device_id) =
142            device.query(MAGMA_QUERY_DEVICE_ID).map_err(|_| zx::Status::INTERNAL)?
143        else {
144            return Err(zx::Status::INTERNAL);
145        };
146
147        log_info!(
148            "kgsl: magma device at {} is vendor {:#04x} device {:#04x}",
149            path,
150            vendor_id,
151            device_id
152        );
153        Ok(device)
154    }
155
156    pub fn new_file(
157        _current_task: &CurrentTask,
158        _dev: DeviceId,
159        _node: &FsNode,
160        _flags: OpenFlags,
161    ) -> Result<Box<dyn FileOps>, Errno> {
162        static INIT: Once = Once::new();
163        INIT.call_once(|| {
164            Self::init();
165        });
166        let mut devices = std::fs::read_dir("/svc/fuchsia.gpu.magma.Service")
167            .map_err(|_| errno!(ENXIO))?
168            .filter_map(|x| x.ok())
169            .filter_map(|entry| entry.path().join("device").into_os_string().into_string().ok())
170            .filter_map(|path| Self::import_device(&path).ok());
171        let device = devices.next().ok_or_else(|| errno!(ENXIO))?;
172        let QueryOutput::Buffer(adreno_kgsl_params_vmo) =
173            device.query(MAGMA_QCOM_ADRENO_QUERY_KGSL_PARAMS).map_err(|_| errno!(ENXIO))?
174        else {
175            return Err(errno!(ENXIO));
176        };
177
178        let adreno_kgsl_params = adreno_kgsl_params_vmo
179            .read_to_object::<AdrenoKgslParams>(0)
180            .map_err(|_| errno!(ENXIO))?;
181
182        let connection = device.create_connection().map_err(|_| errno!(ENXIO))?;
183
184        let mut allocator = RangeAllocator::create(adreno_kgsl_params.gpu_va64_size);
185
186        // Reserve GPU secure VA space.
187        allocator.allocate(adreno_kgsl_params.gpu_secure_va_size).ok_or_else(|| errno!(ENOMEM))?;
188
189        // Shadow buffer must immediately follow the secure VA space, which spans 0..gpu_secure_va_size.
190        let shadow_size = adreno_kgsl_params.device_shadow_size;
191        let shadow_buffer = connection.create_buffer(shadow_size).map_err(|_| errno!(ENOMEM))?;
192        // Reserve space for the shadow buffer, but assign it the end of the secure VA space.
193        // This is necessary as the shadow must immediately follow the secure VA space which may
194        // not be aligned to the overly-conservative BUFFER_ALIGNMENT.
195        allocator.allocate(shadow_size).ok_or_else(|| errno!(ENOMEM))?;
196        let shadow_gpuaddr = adreno_kgsl_params.gpu_secure_va_size;
197        shadow_buffer
198            .map(shadow_gpuaddr, 0, shadow_size, MAGMA_MAP_FLAG_READ | MAGMA_MAP_FLAG_WRITE)
199            .map_err(|_| errno!(ENOMEM))?;
200        let shadow = GpuObject {
201            buffer: shadow_buffer,
202            flags: adreno_kgsl_params.device_shadow_flags.into(),
203            size: shadow_size,
204            mmapsize: shadow_size,
205            gpuaddr: shadow_gpuaddr,
206        };
207        let shadow_properties = uapi::kgsl_shadowprop {
208            gpuaddr: shadow_gpuaddr.try_into().map_err(|_| errno!(ENXIO))?,
209            size: shadow_size.try_into().map_err(|_| errno!(ENXIO))?,
210            flags: adreno_kgsl_params.device_shadow_flags,
211            ..Default::default()
212        };
213
214        let shadow_id = 1;
215        let mut gpuobjs = HashMap::new();
216        gpuobjs.insert(shadow_id, shadow);
217
218        Ok(Box::new(Self {
219            device,
220            connection,
221            adreno_kgsl_params,
222            syncsources: Default::default(),
223            next_syncsource_id: AtomicU32::new(1),
224            gpuobjs: gpuobjs.into(),
225            next_gpuobj_id: AtomicU32::new(shadow_id + 1),
226            allocator: allocator.into(),
227            shadow_properties,
228            contexts: Default::default(),
229            next_context_id: AtomicU32::new(1),
230        }))
231    }
232
233    fn kgsl_device_getproperty(
234        &self,
235        current_task: &CurrentTask,
236        arg: SyscallArg,
237    ) -> Result<SyscallResult, Errno> {
238        let params_ref = maur::kgsl_device_getproperty::new(current_task, arg);
239        let params = current_task.read_multi_arch_object(params_ref)?;
240        kgsl_debug!("kgsl_device_getproperty {:?}", params);
241
242        let params_size: usize = params.sizebytes.try_into().map_err(|_| errno!(EINVAL))?;
243        // TODO(b/393160668): check params_size against all property types
244
245        match params.type_ {
246            uapi::KGSL_PROP_DEVICE_INFO => {
247                let prop_value = uapi::kgsl_devinfo {
248                    device_id: self.adreno_kgsl_params.device_id,
249                    chip_id: self.adreno_kgsl_params.chip_id,
250                    mmu_enabled: self.adreno_kgsl_params.mmu_enabled,
251                    gmem_gpubaseaddr: 0, // This field is unused by the driver.
252                    gpu_id: self.adreno_kgsl_params.gpu_id,
253                    gmem_sizebytes: self.adreno_kgsl_params.gmem_sizebytes,
254                    ..Default::default()
255                };
256                kgsl_debug!("KGSL_PROP_DEVICE_INFO: {:?}", prop_value);
257                prop_value.write(&current_task, params.value)
258            }
259            uapi::KGSL_PROP_DEVICE_SHADOW => {
260                let prop_value = self.shadow_properties;
261                kgsl_debug!("KGSL_PROP_DEVICE_SHADOW: {:?}", prop_value);
262                prop_value.write(&current_task, params.value)
263            }
264            uapi::KGSL_PROP_UCHE_GMEM_VADDR => {
265                let prop_value = 0u32; // Unified cache unsupported.
266                kgsl_debug!("KGSL_PROP_UCHE_GMEM_VADDR: {:?}", prop_value);
267                prop_value.write(&current_task, params.value)
268            }
269            uapi::KGSL_PROP_UCODE_VERSION => {
270                let prop_value = uapi::kgsl_ucode_version {
271                    pfp: self.adreno_kgsl_params.ucode_version_pfp,
272                    pm4: self.adreno_kgsl_params.ucode_version_pm4,
273                    ..Default::default()
274                };
275                kgsl_debug!("KGSL_PROP_UCODE_VERSION: {:?}", prop_value);
276                prop_value.write(&current_task, params.value)
277            }
278            uapi::KGSL_PROP_HIGHEST_BANK_BIT => {
279                let prop_value = self.adreno_kgsl_params.highest_bank_bit;
280                kgsl_debug!("KGSL_PROP_HIGHEST_BANK_BIT: {:?}", prop_value);
281                prop_value.write(&current_task, params.value)
282            }
283            uapi::KGSL_PROP_DEVICE_BITNESS => {
284                let prop_value = self.adreno_kgsl_params.device_bitness;
285                kgsl_debug!("KGSL_PROP_DEVICE_BITNESS: {:?}", prop_value);
286                prop_value.write(&current_task, params.value)
287            }
288            uapi::KGSL_PROP_DEVICE_QDSS_STM => {
289                // This is intentionally zero-sized to indicate lack of support.
290                let prop_value =
291                    uapi::kgsl_qdss_stm_prop { gpuaddr: 0, size: 0, ..Default::default() };
292                kgsl_debug!("KGSL_PROP_DEVICE_QDSS_STM: {:?}", prop_value);
293                prop_value.write(&current_task, params.value)
294            }
295            uapi::KGSL_PROP_MIN_ACCESS_LENGTH => {
296                let prop_value = self.adreno_kgsl_params.min_access_length;
297                kgsl_debug!("KGSL_PROP_MIN_ACCESS_LENGTH: {:?}", prop_value);
298                prop_value.write(&current_task, params.value)
299            }
300            uapi::KGSL_PROP_UBWC_MODE => {
301                let prop_value = self.adreno_kgsl_params.ubwc_mode;
302                kgsl_debug!("KGSL_PROP_UBWC_MODE: {:?}", prop_value);
303                prop_value.write(&current_task, params.value)
304            }
305            uapi::KGSL_PROP_DEVICE_QTIMER => {
306                // This is intentionally zero-sized to indicate lack of support.
307                let prop_value =
308                    uapi::kgsl_qtimer_prop { gpuaddr: 0, size: 0, ..Default::default() };
309                kgsl_debug!("KGSL_PROP_DEVICE_QTIMER: {:?}", prop_value);
310                prop_value.write(&current_task, params.value)
311            }
312            uapi::KGSL_PROP_SECURE_BUFFER_ALIGNMENT => {
313                let prop_value = self.adreno_kgsl_params.secure_buf_alignment;
314                kgsl_debug!("KGSL_PROP_SECURE_BUFFER_ALIGNMENT: {:?}", prop_value);
315                prop_value.write(&current_task, params.value)
316            }
317            uapi::KGSL_PROP_SECURE_CTXT_SUPPORT => {
318                let prop_value = self.adreno_kgsl_params.secure_ctxt_support;
319                kgsl_debug!("KGSL_PROP_SECURE_CTXT_SUPPORT: {:?}", prop_value);
320                prop_value.write(&current_task, params.value)
321            }
322            uapi::KGSL_PROP_SPEED_BIN => {
323                let prop_value = 0u64; // Default speed bin.
324                kgsl_debug!("KGSL_PROP_SPEED_BIN: {:?}", prop_value);
325                prop_value.write(&current_task, params.value)
326            }
327            uapi::KGSL_PROP_GAMING_BIN => {
328                kgsl_debug!("KGSL_PROP_GAMING_BIN returning EINVAL");
329                error!(EINVAL, "gaming bin unsupported")
330            }
331            uapi::KGSL_PROP_GPU_MODEL => {
332                if params_size < self.adreno_kgsl_params.gpu_model.len() {
333                    return error!(EINVAL);
334                }
335                let prop_value = self.adreno_kgsl_params.gpu_model;
336                kgsl_debug!("KGSL_PROP_GPU_MODEL: {:?}", prop_value);
337                let result_ref = UserRef::from(UserAddress::from(params.value));
338                current_task.write_object(result_ref, &prop_value)?;
339                Ok(SUCCESS)
340            }
341            uapi::KGSL_PROP_VK_DEVICE_ID => {
342                let prop_value = self.adreno_kgsl_params.vk_device_id;
343                kgsl_debug!("KGSL_PROP_VK_DEVICE_ID: {:?}", prop_value);
344                prop_value.write(&current_task, params.value)
345            }
346            uapi::KGSL_PROP_IS_LPAC_ENABLED => {
347                let prop_value = 0u32; // Asynchronous compute unsupported.
348                kgsl_debug!("KGSL_PROP_IS_LPAC_ENABLED: {:?}", prop_value);
349                prop_value.write(&current_task, params.value)
350            }
351            uapi::KGSL_PROP_GPU_VA64_SIZE => {
352                let prop_value = self.adreno_kgsl_params.gpu_va64_size;
353                kgsl_debug!("KGSL_PROP_GPU_VA64_SIZE: {:?}", prop_value);
354                prop_value.write(&current_task, params.value)
355            }
356            uapi::KGSL_PROP_IS_RAYTRACING_ENABLED => {
357                let prop_value = 0u32; // Raytracing unsupported.
358                kgsl_debug!("KGSL_PROP_IS_RAYTRACING_ENABLED: {:?}", prop_value);
359                prop_value.write(&current_task, params.value)
360            }
361            uapi::KGSL_PROP_IS_FASTBLEND_ENABLED => {
362                let prop_value = 0u32; // Fast blend unsupported.
363                kgsl_debug!("KGSL_PROP_IS_FASTBLEND_ENABLED: {:?}", prop_value);
364                prop_value.write(&current_task, params.value)
365            }
366            uapi::KGSL_PROP_UCHE_TRAP_BASE => {
367                kgsl_debug!("KGSL_PROP_UCHE_TRAP_BASE returning EINVAL");
368                error!(EINVAL, "uche_trap_base unset")
369            }
370            uapi::KGSL_PROP_GPU_SECURE_VA_SIZE => {
371                let prop_value = self.adreno_kgsl_params.gpu_secure_va_size;
372                kgsl_debug!("KGSL_PROP_GPU_SECURE_VA_SIZE: {:?}", prop_value);
373                prop_value.write(&current_task, params.value)
374            }
375            _ => {
376                track_stub!(TODO("https://fxbug.dev/393160668"), "kgsl property", params.type_);
377                log_error!("kgsl: unimplemented GetProperty type {}", kgsl_prop(params.type_));
378                error!(ENOTSUP)
379            }
380        }
381    }
382
383    fn kgsl_gpuobj_alloc(
384        &self,
385        current_task: &CurrentTask,
386        arg: SyscallArg,
387    ) -> Result<SyscallResult, Errno> {
388        let params_ref = maur::kgsl_gpuobj_alloc::new(current_task, arg);
389        let mut params = current_task.read_multi_arch_object(params_ref)?;
390        kgsl_debug!("kgsl_gpuobj_alloc {:?}", params);
391
392        let flags = KgslMemFlags::try_from(params.flags).map_err(|bits| {
393            log_error!("kgsl: unknown memory flags {:#x}", bits);
394            errno!(EINVAL)
395        })?;
396        if BUFFER_ALIGNMENT % (1 << flags.align_bits()) != 0 {
397            log_error!("kgsl: unsupported alignment {}", flags.align_bits());
398            return error!(ENOTSUP);
399        }
400
401        let buffer = self.connection.create_buffer(params.size).map_err(|_| errno!(ENOMEM))?;
402        let size = buffer.size();
403
404        let gpuaddr = self.allocator.lock().allocate(size).ok_or_else(|| errno!(ENOMEM))?;
405        buffer.map(gpuaddr, 0, size, map_flags(flags)?).map_err(|_| errno!(ENOMEM))?;
406
407        let id = self.next_gpuobj_id.fetch_add(1, Ordering::Relaxed);
408        if id == 0 {
409            log_error!("kgsl: gpuobj ids exhausted");
410            return error!(ENOMEM);
411        }
412
413        let gpuobj = GpuObject { buffer, flags: params.flags, size, mmapsize: size, gpuaddr };
414
415        self.gpuobjs.lock().insert(id, gpuobj);
416
417        params.size = size;
418        params.mmapsize = size;
419        params.id = id;
420
421        current_task.write_multi_arch_object(params_ref, params)?;
422        Ok(SUCCESS)
423    }
424
425    fn kgsl_gpuobj_free(
426        &self,
427        current_task: &CurrentTask,
428        arg: SyscallArg,
429    ) -> Result<SyscallResult, Errno> {
430        let params_ref = maur::kgsl_gpuobj_free::new(current_task, arg);
431        let params = current_task.read_multi_arch_object(params_ref)?;
432        kgsl_debug!("kgsl_gpuobj_free {:?}", params);
433
434        if let Entry::Occupied(entry) = self.gpuobjs.lock().entry(params.id) {
435            self.allocator.lock().free(entry.get().gpuaddr, entry.get().size);
436            entry.remove();
437            Ok(SUCCESS)
438        } else {
439            error!(EINVAL)
440        }
441    }
442
443    fn kgsl_gpuobj_info(
444        &self,
445        current_task: &CurrentTask,
446        arg: SyscallArg,
447    ) -> Result<SyscallResult, Errno> {
448        let params_ref = maur::kgsl_gpuobj_info::new(current_task, arg);
449        let mut params = current_task.read_multi_arch_object(params_ref)?;
450        kgsl_debug!("kgsl_gpuobj_info {:?}", params);
451
452        let gpuobjs = self.gpuobjs.lock();
453        let gpuobj = gpuobjs.get(&params.id).ok_or_else(|| errno!(EINVAL))?;
454
455        params.gpuaddr = gpuobj.gpuaddr;
456        params.size = gpuobj.size;
457        params.flags = gpuobj.flags;
458        params.va_len = gpuobj.size;
459        params.va_addr = 0;
460
461        current_task.write_multi_arch_object(params_ref, params)?;
462        Ok(SUCCESS)
463    }
464
465    fn kgsl_syncsource_create(
466        &self,
467        current_task: &CurrentTask,
468        arg: SyscallArg,
469    ) -> Result<SyscallResult, Errno> {
470        let params_ref = maur::kgsl_syncsource_create::new(current_task, arg);
471        let mut params = current_task.read_multi_arch_object(params_ref)?;
472        kgsl_debug!("kgsl_syncsource_create {:?}", params);
473
474        let semaphore = self.connection.create_semaphore().map_err(|_| errno!(ENOMEM))?;
475        let id = self.next_syncsource_id.fetch_add(1, Ordering::Relaxed);
476        if id == 0 {
477            // fetch_add wraps to zero on overflow. Transitioning to id-map container
478            // will avoid this issue.
479            log_error!("kgsl: ids exhausted");
480            return error!(ENOMEM);
481        }
482        self.syncsources.lock().insert(id, semaphore);
483
484        params.id = id;
485
486        current_task.write_multi_arch_object(params_ref, params)?;
487        Ok(SUCCESS)
488    }
489
490    fn kgsl_syncsource_destroy(
491        &self,
492        current_task: &CurrentTask,
493        arg: SyscallArg,
494    ) -> Result<SyscallResult, Errno> {
495        let params_ref = maur::kgsl_syncsource_destroy::new(current_task, arg);
496        let params = current_task.read_multi_arch_object(params_ref)?;
497        kgsl_debug!("kgsl_syncsource_destroy {:?}", params);
498
499        if self.syncsources.lock().remove(&params.id).is_some() {
500            Ok(SUCCESS)
501        } else {
502            error!(EINVAL)
503        }
504    }
505
506    fn kgsl_drawctxt_create(
507        &self,
508        current_task: &CurrentTask,
509        arg: SyscallArg,
510    ) -> Result<SyscallResult, Errno> {
511        let params_ref = maur::kgsl_drawctxt_create::new(current_task, arg);
512        let mut params = current_task.read_multi_arch_object(params_ref)?;
513        kgsl_debug!("kgsl_drawctxt_create {:?}", params);
514        let flags = KgslContextFlags::try_from(params.flags).map_err(|bits| {
515            log_error!("kgsl: unknown context flags {:#x}", bits);
516            errno!(EINVAL)
517        })?;
518        let context =
519            self.connection.create_context(flags.priority().into()).map_err(|_| errno!(ENXIO))?;
520        let id = self.next_context_id.fetch_add(1, Ordering::Relaxed);
521        if id == 0 {
522            // fetch_add wraps to zero on overflow. Transitioning to id-map container
523            // will avoid this issue.
524            log_error!("kgsl: ids exhausted");
525            return error!(ENOMEM);
526        }
527        self.contexts.lock().insert(id, context);
528        params.drawctxt_id = id;
529        current_task.write_multi_arch_object(params_ref, params)?;
530        Ok(SUCCESS)
531    }
532
533    fn kgsl_drawctxt_destroy(
534        &self,
535        current_task: &CurrentTask,
536        arg: SyscallArg,
537    ) -> Result<SyscallResult, Errno> {
538        let params_ref = maur::kgsl_drawctxt_destroy::new(current_task, arg);
539        let params = current_task.read_multi_arch_object(params_ref)?;
540        kgsl_debug!("kgsl_drawctxt_destroy {:?}", params);
541        if self.contexts.lock().remove(&params.drawctxt_id).is_some() {
542            Ok(SUCCESS)
543        } else {
544            error!(EINVAL)
545        }
546    }
547
548    fn kgsl_gpu_command(
549        &self,
550        current_task: &CurrentTask,
551        arg: SyscallArg,
552    ) -> Result<SyscallResult, Errno> {
553        let params_ref = maur::kgsl_gpu_command::new(current_task, arg);
554        let params = current_task.read_multi_arch_object(params_ref)?;
555        kgsl_debug!("kgsl_gpu_command {:?}", params);
556        let contexts = self.contexts.lock();
557        let context = contexts.get(&params.context_id).ok_or_else(|| errno!(EINVAL))?;
558
559        let cmds = current_task.read_objects_to_vec::<kgsl_command_object>(
560            UserRef::from(UserAddress::from(params.cmdlist)),
561            params.numcmds as usize,
562        )?;
563        kgsl_debug!("kgsl_gpu_command cmds {:?}", cmds);
564        let objs = current_task.read_objects_to_vec::<kgsl_command_object>(
565            UserRef::from(UserAddress::from(params.objlist)),
566            params.numobjs as usize,
567        )?;
568        kgsl_debug!("kgsl_gpu_command objs {:?}", objs);
569        let syncs = current_task.read_objects_to_vec::<kgsl_command_syncpoint>(
570            UserRef::from(UserAddress::from(params.synclist)),
571            params.numsyncs as usize,
572        )?;
573        kgsl_debug!("kgsl_gpu_command syncs {:?}", syncs);
574        if syncs.len() > 0 {
575            // TODO(b/359831679): implement syncpoints
576            log_error!("kgsl: syncs not supported yet");
577            return error!(ENOTSUP);
578        }
579
580        if params.flags != 0 {
581            let flags = KgslCmdBatchFlags::try_from(params.flags).map_err(|bits| {
582                log_error!("kgsl: unknown command flags {:#x}", bits);
583                errno!(EINVAL)
584            })?;
585            log_warn!("kgsl: unsupported flags {:?}", flags);
586        }
587
588        let gpuobjs = self.gpuobjs.lock();
589
590        let to_exec_resources = |objects: &[kgsl_command_object],
591                                 kind: &str|
592         -> Result<Vec<kgsl_libmagma::ExecResource>, Errno> {
593            // The caller does not always set the id field of the command object, and the gpuaddr
594            // may not be the start of the buffer, so we have to search for the object manually.
595            // If this turns out to be a bottleneck, we may want to try an initial base address
596            // search before falling back to a full search and/or cache the results of the search.
597            // TODO(b/393160668): profile and optimize as necessary
598            objects
599                .iter()
600                .map(|obj| {
601                    let gpuobj = gpuobjs
602                        .values()
603                        .find(|o| obj.gpuaddr >= o.gpuaddr && obj.gpuaddr < o.gpuaddr + o.size)
604                        .ok_or_else(|| {
605                            log_error!("kgsl: {} gpuaddr {:#x} not found", kind, obj.gpuaddr);
606                            errno!(EINVAL)
607                        })?;
608                    Ok(kgsl_libmagma::ExecResource {
609                        buffer: gpuobj.buffer.clone(),
610                        offset: (obj.gpuaddr - gpuobj.gpuaddr) + obj.offset,
611                        length: obj.size,
612                    })
613                })
614                .collect()
615        };
616
617        let magma_resources = to_exec_resources(&objs, "resource")?;
618        let magma_command_buffers = to_exec_resources(&cmds, "command")?;
619
620        context
621            .execute_command(magma_command_buffers, magma_resources, vec![], vec![], 0)
622            .map_err(|_| errno!(EINVAL))?;
623
624        Ok(SUCCESS)
625    }
626}
627
628impl Drop for KgslFile {
629    fn drop(&mut self) {}
630}
631
632impl FileOps for KgslFile {
633    fileops_impl_dataless!();
634    fileops_impl_nonseekable!();
635    fileops_impl_noop_sync!();
636
637    fn ioctl(
638        &self,
639        _file: &FileObject,
640        current_task: &CurrentTask,
641        request: u32,
642        arg: SyscallArg,
643    ) -> Result<SyscallResult, Errno> {
644        // Special ioctl to signal container to use kgsl.
645        // TODO(b/429239527): remove after transitioned
646        const IOCTL_KGSL_ENABLE: u32 = 42;
647        if request == IOCTL_KGSL_ENABLE {
648            if cfg!(not(feature = "starnix-kgsl-enable")) {
649                log_info!("kgsl: suppressing further use of kgsl");
650                return error!(ENXIO);
651            }
652            return Ok(SUCCESS);
653        }
654        match crate::util::canonicalize_ioctl_request(current_task, request) {
655            uapi::IOCTL_KGSL_DEVICE_GETPROPERTY => self.kgsl_device_getproperty(current_task, arg),
656            uapi::IOCTL_KGSL_GPUOBJ_ALLOC => self.kgsl_gpuobj_alloc(current_task, arg),
657            uapi::IOCTL_KGSL_GPUOBJ_FREE => self.kgsl_gpuobj_free(current_task, arg),
658            uapi::IOCTL_KGSL_GPUOBJ_INFO => self.kgsl_gpuobj_info(current_task, arg),
659            uapi::IOCTL_KGSL_SYNCSOURCE_CREATE => self.kgsl_syncsource_create(current_task, arg),
660            uapi::IOCTL_KGSL_SYNCSOURCE_DESTROY => self.kgsl_syncsource_destroy(current_task, arg),
661            uapi::IOCTL_KGSL_DRAWCTXT_CREATE => self.kgsl_drawctxt_create(current_task, arg),
662            uapi::IOCTL_KGSL_DRAWCTXT_DESTROY => self.kgsl_drawctxt_destroy(current_task, arg),
663            uapi::IOCTL_KGSL_GPU_COMMAND => self.kgsl_gpu_command(current_task, arg),
664            _ => {
665                track_stub!(TODO("https://fxbug.dev/393160668"), "kgsl ioctl", request);
666                log_error!("kgsl: unimplemented ioctl {}", ioctl_kgsl(request));
667                error!(ENOTSUP)
668            }
669        }
670    }
671
672    fn mmap(
673        &self,
674        file: &FileObject,
675        current_task: &CurrentTask,
676        addr: starnix_core::mm::DesiredAddress,
677        memory_offset: u64, // Callers encode the buffer ID using this field.
678        length: usize,
679        prot_flags: starnix_core::mm::ProtectionFlags,
680        mapping_options: starnix_core::mm::MappingOptions,
681        _filename: starnix_core::vfs::NamespaceNode,
682    ) -> Result<UserAddress, Errno> {
683        kgsl_debug!("mmap {:?} {:?} {:?} {:?}", addr, memory_offset, length, prot_flags);
684        let id = (memory_offset / *PAGE_SIZE) as u32;
685        let gpuobjs = self.gpuobjs.lock();
686        let gpuobj = gpuobjs.get(&id).ok_or_else(|| errno!(EINVAL))?;
687        if length as u64 > gpuobj.mmapsize {
688            return error!(EINVAL);
689        }
690        let handle = gpuobj.buffer.get_handle().map_err(|_| errno!(ENXIO))?;
691        let vmo = zx::Vmo::from(handle);
692        let memory = Arc::new(MemoryObject::from(vmo).with_zx_name(b"starnix:kgsl"));
693        // The memory manager persists the memory object until the client exits or calls munmap.
694        current_task.mm()?.map_memory(
695            addr,
696            memory,
697            0,
698            length,
699            prot_flags,
700            file.max_access_for_memory_mapping(),
701            mapping_options,
702            MappingName::None,
703        )
704    }
705}