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