// SPDX-License-Identifier: GPL-2.0 use crate::driver::{ DrmRegData, NovaDevice, NovaDriver, // }; use crate::gem::NovaObject; use kernel::{ alloc::flags::*, drm::{ self, gem::BaseObject, Registered, // }, num::casts::{ arch::IntoSafeCastArch, IntoSafeCast, // }, prelude::*, transmute::AsBytes, uaccess::UserSlice, uapi, }; pub(crate) struct File; /// GPU information returned to userspace. #[repr(transparent)] struct GpuInfo(uapi::drm_nova_info_gpu); /// Copies `name` into the zero initialised, fixed size uAPI buffer `dst`, keeping it /// NUL-terminated. /// /// Fails with [`ENAMETOOLONG`] if `name` does not fit in `dst` with room for the terminator. fn copy_name(dst: &mut [u8], name: &str) -> Result { let bytes = name.as_bytes(); if bytes.len() >= dst.len() { return Err(ENAMETOOLONG); } dst[..bytes.len()].copy_from_slice(bytes); Ok(()) } impl GpuInfo { /// Collects the GPU information reported to userspace. /// /// This is fallible so that unexpected GSP behaviour, such as a malformed name string, is /// reported to userspace instead of being silently replaced with a made up value. When adding /// a field, take care that a value the GSP simply does not provide, for example because the /// firmware predates it, does not cause a failure. Such fields must fall back to zero or /// another documented default instead. fn new(reg_data: &DrmRegData<'_>) -> Result { let spec = reg_data.api.with(|api| api.get_ref().spec()); let gsp_static_info = reg_data.api.with(|api| api.get_ref().gsp_static_info()); let mut info = uapi::drm_nova_info_gpu { architecture: spec.chipset.arch().into(), chipid: spec.chipset.into(), vram_size: gsp_static_info.vram_size(), vram_bar_size: reg_data.api.with(|api| api.bar1_size())?, gpu_gid: gsp_static_info.gpu_gid, ..pin_init::zeroed() }; copy_name( &mut info.gpu_name, gsp_static_info.gpu_name().map_err(|_| EINVAL)?, )?; copy_name( &mut info.gpu_short_name, gsp_static_info.gpu_short_name().map_err(|_| EINVAL)?, )?; Ok(Self(info)) } } // SAFETY: `GpuInfo` has no interior mutability, and there are no uninitialized bytes. unsafe impl AsBytes for GpuInfo {} fn write_info(info: &mut uapi::drm_nova_info, value: T) -> Result { // A NULL data pointer requests the size of the information structure. if info.data == 0 { info.size = size_of::().try_into()?; return Ok(()); } let mut writer = UserSlice::new( UserPtr::from_addr(info.data.into_safe_cast_arch()), info.size.into_safe_cast(), ) .writer(); info.size = writer.write_truncated(&value)?.try_into()?; Ok(()) } impl drm::file::DriverFile for File { type Driver = NovaDriver; fn open(_dev: &NovaDevice) -> Result>> { Ok(KBox::new(Self, GFP_KERNEL)?.into()) } } impl File { /// IOCTL: gem_create: Create a new DRM GEM object. pub(crate) fn gem_create( dev: &NovaDevice, _reg_data: &DrmRegData<'_>, req: &mut uapi::drm_nova_gem_create, file: &drm::File, ) -> Result { let obj = NovaObject::new(dev, req.size.try_into()?)?; req.handle = obj.create_handle(file)?; Ok(0) } /// IOCTL: gem_info: Query GEM metadata. pub(crate) fn gem_info( _dev: &NovaDevice, _reg_data: &DrmRegData<'_>, req: &mut uapi::drm_nova_gem_info, file: &drm::File, ) -> Result { let bo = NovaObject::lookup_handle(file, req.handle)?; req.size = bo.size().try_into()?; Ok(0) } /// IOCTL: info: Query device information. pub(crate) fn info( _dev: &NovaDevice, reg_data: &DrmRegData<'_>, info: &mut uapi::drm_nova_info, _file: &drm::File, ) -> Result { match info.id { uapi::DRM_NOVA_INFO_GPU => write_info(info, GpuInfo::new(reg_data)?)?, _ => return Err(EINVAL), } Ok(0) } }