blob: 6f9a44845b0b737fc38be783655b0ce7984c96ba [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fdio::service_connect;
use kgsl_libmagma::{Device, initialize_logging};
use kgsl_strings::{ioctl_kgsl, kgsl_prop};
use magma::{MAGMA_QUERY_DEVICE_ID, MAGMA_QUERY_VENDOR_ID};
use starnix_core::mm::MemoryAccessorExt;
use starnix_core::task::CurrentTask;
use starnix_core::vfs::{FileObject, FileOps, FsNode};
use starnix_core::{fileops_impl_dataless, fileops_impl_nonseekable, fileops_impl_noop_sync};
use starnix_logging::{log_error, log_info, log_warn};
use starnix_sync::{Locked, Unlocked};
use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
use starnix_uapi::device_type::DeviceType;
use starnix_uapi::errors::Errno;
use starnix_uapi::open_flags::OpenFlags;
use starnix_uapi::user_address::{UserAddress, UserRef};
use starnix_uapi::{
IOCTL_KGSL_DEVICE_GETPROPERTY, KGSL_PROP_DEVICE_INFO, errno, error, kgsl_device_getproperty,
kgsl_devinfo,
};
use std::sync::Once;
pub struct KgslFile {
device: Device,
}
impl KgslFile {
pub fn init() {
match Self::init_magma_logging() {
Ok(()) => log_info!("kgsl: magma logging enabled"),
Err(()) => log_warn!("kgsl: magma logging failed to initialize"),
};
}
fn init_magma_logging() -> Result<(), ()> {
let (client, server) = zx::Channel::create();
service_connect("/svc/fuchsia.logger.LogSink", server).map_err(|_| ())?;
return initialize_logging(client);
}
fn import_device(path: &str) -> Result<Device, zx::Status> {
let (client, server) = zx::Channel::create();
service_connect(&path, server)?;
let device = Device::from_channel(client).map_err(|_| zx::Status::INTERNAL)?;
let vendor_id =
device.query_value(MAGMA_QUERY_VENDOR_ID).map_err(|_| zx::Status::INTERNAL)?;
log_info!("kgsl: magma device at {} is vendor {:#04x}", path, vendor_id);
Ok(device)
}
pub fn new_file(
_current_task: &CurrentTask,
_dev: DeviceType,
_node: &FsNode,
_flags: OpenFlags,
) -> Result<Box<dyn FileOps>, Errno> {
static INIT: Once = Once::new();
INIT.call_once(|| {
Self::init();
});
let mut devices = std::fs::read_dir("/svc/fuchsia.gpu.magma.Service")
.map_err(|_| errno!(ENXIO))?
.filter_map(|x| x.ok())
.filter_map(|entry| entry.path().join("device").into_os_string().into_string().ok())
.filter_map(|path| Self::import_device(&path).ok());
let device = devices.next().ok_or_else(|| errno!(ENXIO))?;
Ok(Box::new(Self { device }))
}
#[allow(clippy::unwrap_in_result, reason = "Force clippy rule in Starnix")]
fn kgsl_device_getproperty(
&self,
current_task: &CurrentTask,
arg: SyscallArg,
) -> Result<SyscallResult, Errno> {
let params = current_task.read_object(UserRef::<kgsl_device_getproperty>::from(arg))?;
let result = UserRef::from(UserAddress::from(params.value));
match params.type_ {
KGSL_PROP_DEVICE_INFO => {
let device_id =
self.device.query_value(MAGMA_QUERY_DEVICE_ID).map_err(|_| errno!(ENOTTY))?;
let devinfo =
kgsl_devinfo { device_id: device_id.try_into().unwrap(), ..Default::default() };
current_task.write_object(result, &devinfo)?;
Ok(SUCCESS)
}
_ => {
log_error!("kgsl: unimplemented GetProperty type {}", kgsl_prop(params.type_));
error!(ENOTSUP)
}
}
}
}
impl Drop for KgslFile {
fn drop(&mut self) {}
}
impl FileOps for KgslFile {
fileops_impl_dataless!();
fileops_impl_nonseekable!();
fileops_impl_noop_sync!();
fn ioctl(
&self,
_locked: &mut Locked<Unlocked>,
_file: &FileObject,
current_task: &CurrentTask,
request: u32,
arg: SyscallArg,
) -> Result<SyscallResult, Errno> {
// Special ioctl to signal container to use kgsl.
// TODO(b/429239527): remove after transitioned
const IOCTL_KGSL_ENABLE: u32 = 42;
if request == IOCTL_KGSL_ENABLE {
if cfg!(not(feature = "starnix-kgsl-enable")) {
log_info!("kgsl: suppressing further use of kgsl");
return error!(ENXIO);
}
return Ok(SUCCESS);
}
match request {
IOCTL_KGSL_DEVICE_GETPROPERTY => self.kgsl_device_getproperty(current_task, arg),
_ => {
log_error!("kgsl: unimplemented ioctl {}", ioctl_kgsl(request));
error!(ENOTSUP)
}
}
}
}