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