blob: a05264917a0b2dd390b924729c4f17f135c43d3b [file]
// 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<Self> {
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<T: AsBytes>(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::<T>().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<Pin<KBox<Self>>> {
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<Registered>,
_reg_data: &DrmRegData<'_>,
req: &mut uapi::drm_nova_gem_create,
file: &drm::File<File>,
) -> Result<u32> {
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<Registered>,
_reg_data: &DrmRegData<'_>,
req: &mut uapi::drm_nova_gem_info,
file: &drm::File<File>,
) -> Result<u32> {
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<Registered>,
reg_data: &DrmRegData<'_>,
info: &mut uapi::drm_nova_info,
_file: &drm::File<File>,
) -> Result<u32> {
match info.id {
uapi::DRM_NOVA_INFO_GPU => write_info(info, GpuInfo::new(reg_data)?)?,
_ => return Err(EINVAL),
}
Ok(0)
}
}