summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDanilo Krummrich <dakr@redhat.com>2023-11-22 22:37:18 +0100
committerDanilo Krummrich <dakr@kernel.org>2024-08-13 15:54:19 +0200
commitb54176537b79224f4591018610015aab700853ad (patch)
tree9e8160e57fa3327daea96ef48ffd3ca854afa269 /drivers
parentac3b90930f007514defbf06ec63a07122b02ba8a (diff)
nova: add initial driver stub
Add the initial driver stub of Nova, a Rust-based GSP-only driver for Nvidia GPUs. Nova, in the long term, is intended to serve as the successor of Nouveau for GSP-firmware-based GPUs. [1] As a stub driver Nova's focus is to make use of the most basic device / driver infrastructure required to build a DRM driver on the PCI bus and serve as demonstration example and justification for this infrastructure. In further consequence, the idea is to develop Nova continuously upstream, using those increments to lift further Rust abstractions and infrastructure upstream. Link: https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u [1] Signed-off-by: Danilo Krummrich <dakr@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/nova/Kconfig12
-rw-r--r--drivers/gpu/drm/nova/Makefile3
-rw-r--r--drivers/gpu/drm/nova/driver.rs85
-rw-r--r--drivers/gpu/drm/nova/file.rs70
-rw-r--r--drivers/gpu/drm/nova/gem.rs50
-rw-r--r--drivers/gpu/drm/nova/gpu.rs173
-rw-r--r--drivers/gpu/drm/nova/nova.rs18
9 files changed, 414 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 6b2c6b91f962..1ba3bbe36d3e 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -323,6 +323,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig"
source "drivers/gpu/drm/nouveau/Kconfig"
+source "drivers/gpu/drm/nova/Kconfig"
+
source "drivers/gpu/drm/i915/Kconfig"
source "drivers/gpu/drm/xe/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 68cc9258ffc4..b7f9fc5ccc97 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -172,6 +172,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VGEM) += vgem/
obj-$(CONFIG_DRM_VKMS) += vkms/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-$(CONFIG_DRM_NOVA_STUB) += nova/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
diff --git a/drivers/gpu/drm/nova/Kconfig b/drivers/gpu/drm/nova/Kconfig
new file mode 100644
index 000000000000..3c15593e054b
--- /dev/null
+++ b/drivers/gpu/drm/nova/Kconfig
@@ -0,0 +1,12 @@
+config DRM_NOVA_STUB
+ tristate "Nova GPU driver stub"
+ depends on DRM
+ depends on PCI
+ depends on RUST
+ depends on RUST_FW_LOADER_ABSTRACTIONS
+ default n
+ help
+ Choose this if you want to build the Nova stub driver for Nvidia
+ GSP-based GPUs.
+
+ If M is selected, the module will be called nova.
diff --git a/drivers/gpu/drm/nova/Makefile b/drivers/gpu/drm/nova/Makefile
new file mode 100644
index 000000000000..733ac5fb9f4f
--- /dev/null
+++ b/drivers/gpu/drm/nova/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_NOVA_STUB) += nova.o
diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs
new file mode 100644
index 000000000000..69d0efeb125e
--- /dev/null
+++ b/drivers/gpu/drm/nova/driver.rs
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+ bindings, c_str, drm,
+ drm::{drv, ioctl},
+ pci,
+ pci::define_pci_id_table,
+ prelude::*,
+ sync::Arc,
+};
+
+use crate::{file::File, gpu::Gpu};
+
+pub(crate) struct NovaDriver;
+
+/// Convienence type alias for the DRM device type for this driver
+pub(crate) type NovaDevice = drm::device::Device<NovaDriver>;
+
+#[allow(dead_code)]
+pub(crate) struct NovaData {
+ pub(crate) gpu: Arc<Gpu>,
+ pub(crate) pdev: pci::Device,
+}
+
+const BAR0_SIZE: usize = 8;
+pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
+
+const INFO: drm::drv::DriverInfo = drm::drv::DriverInfo {
+ major: 0,
+ minor: 0,
+ patchlevel: 0,
+ name: c_str!("nova"),
+ desc: c_str!("Nvidia Graphics"),
+ date: c_str!("20240227"),
+};
+
+impl pci::Driver for NovaDriver {
+ type Data = Arc<NovaData>;
+
+ define_pci_id_table! {
+ (),
+ [ (pci::DeviceId::new(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as u32), None) ]
+ }
+
+ fn probe(pdev: &mut pci::Device, _id_info: Option<&Self::IdInfo>) -> Result<Self::Data> {
+ dev_dbg!(pdev.as_ref(), "Probe Nova GPU driver.\n");
+
+ pdev.enable_device_mem()?;
+ pdev.set_master();
+
+ let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova"))?;
+ let data = Arc::new(
+ NovaData {
+ gpu: Gpu::new(pdev, bar)?,
+ pdev: pdev.clone(),
+ },
+ GFP_KERNEL,
+ )?;
+
+ let drm = drm::device::Device::<NovaDriver>::new(pdev.as_ref(), data.clone())?;
+ drm::drv::Registration::new_foreign_owned(drm, 0)?;
+
+ Ok(data)
+ }
+
+ fn remove(data: &Self::Data) {
+ dev_dbg!(data.pdev.as_ref(), "Remove Nova GPU driver.\n");
+ }
+}
+
+#[vtable]
+impl drm::drv::Driver for NovaDriver {
+ type Data = Arc<NovaData>;
+ type File = File;
+ type Object = crate::gem::Object;
+
+ const INFO: drm::drv::DriverInfo = INFO;
+ const FEATURES: u32 = drv::FEAT_GEM;
+
+ kernel::declare_drm_ioctls! {
+ (NOVA_GETPARAM, drm_nova_getparam, ioctl::RENDER_ALLOW, File::get_param),
+ (NOVA_GEM_CREATE, drm_nova_gem_create, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_create),
+ (NOVA_GEM_INFO, drm_nova_gem_info, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_info),
+ }
+}
diff --git a/drivers/gpu/drm/nova/file.rs b/drivers/gpu/drm/nova/file.rs
new file mode 100644
index 000000000000..4fa9df536f78
--- /dev/null
+++ b/drivers/gpu/drm/nova/file.rs
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::driver::{NovaDevice, NovaDriver};
+use crate::gem;
+use kernel::{
+ alloc::flags::*,
+ drm::{self, device::Device as DrmDevice, gem::BaseObject},
+ prelude::*,
+ uapi,
+};
+
+pub(crate) struct File();
+
+/// Convenience type alias for our DRM `File` type
+pub(crate) type DrmFile = drm::file::File<File>;
+
+impl drm::file::DriverFile for File {
+ type Driver = NovaDriver;
+
+ fn open(dev: &DrmDevice<Self::Driver>) -> Result<Pin<Box<Self>>> {
+ dev_dbg!(dev.as_ref(), "drm::device::Device::open\n");
+
+ Ok(Box::into_pin(Box::new(Self(), GFP_KERNEL)?))
+ }
+}
+
+impl File {
+ /// IOCTL: get_param: Query GPU / driver metadata.
+ pub(crate) fn get_param(
+ dev: &NovaDevice,
+ getparam: &mut uapi::drm_nova_getparam,
+ _file: &DrmFile,
+ ) -> Result<u32> {
+ let pdev = &dev.data().pdev;
+
+ getparam.value = match getparam.param as u32 {
+ uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?,
+ _ => return Err(EINVAL),
+ };
+
+ Ok(0)
+ }
+
+ /// IOCTL: gem_create: Create a new DRM GEM object.
+ pub(crate) fn gem_create(
+ dev: &NovaDevice,
+ req: &mut uapi::drm_nova_gem_create,
+ file: &DrmFile,
+ ) -> Result<u32> {
+ let obj = gem::object_new(dev, req.size.try_into()?)?;
+
+ let handle = obj.create_handle(file)?;
+ req.handle = handle;
+
+ Ok(0)
+ }
+
+ /// IOCTL: gem_info: Query GEM metadata.
+ pub(crate) fn gem_info(
+ _dev: &NovaDevice,
+ req: &mut uapi::drm_nova_gem_info,
+ file: &DrmFile,
+ ) -> Result<u32> {
+ let bo = gem::lookup_handle(file, req.handle)?;
+
+ req.size = bo.size().try_into()?;
+
+ Ok(0)
+ }
+}
diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs
new file mode 100644
index 000000000000..51bc30c226e2
--- /dev/null
+++ b/drivers/gpu/drm/nova/gem.rs
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+ drm::{
+ gem,
+ gem::{BaseObject, ObjectRef},
+ },
+ prelude::*,
+};
+
+use crate::{
+ driver::{NovaDevice, NovaDriver},
+ file::DrmFile,
+};
+
+/// GEM Object inner driver data
+#[pin_data]
+pub(crate) struct DriverObject {}
+
+/// Type alias for the GEM object tyoe for this driver.
+pub(crate) type Object = gem::Object<DriverObject>;
+
+impl gem::BaseDriverObject<Object> for DriverObject {
+ fn new(dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> {
+ dev_dbg!(dev.as_ref(), "DriverObject::new\n");
+ DriverObject {}
+ }
+}
+
+impl gem::DriverObject for DriverObject {
+ type Driver = NovaDriver;
+}
+
+/// Create a new DRM GEM object.
+pub(crate) fn object_new(dev: &NovaDevice, size: usize) -> Result<ObjectRef<Object>> {
+ let aligned_size = size.next_multiple_of(1 << 12);
+
+ if size == 0 || size > aligned_size {
+ return Err(EINVAL);
+ }
+
+ let gem = Object::new(dev, aligned_size)?;
+
+ Ok(ObjectRef::from_pinned_unique(gem))
+}
+
+/// Look up a GEM object handle for a `File` and return an `ObjectRef` for it.
+pub(crate) fn lookup_handle(file: &DrmFile, handle: u32) -> Result<ObjectRef<Object>> {
+ Object::lookup_handle(file, handle)
+}
diff --git a/drivers/gpu/drm/nova/gpu.rs b/drivers/gpu/drm/nova/gpu.rs
new file mode 100644
index 000000000000..d2cc45b6b636
--- /dev/null
+++ b/drivers/gpu/drm/nova/gpu.rs
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+ device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString, sync::Arc,
+};
+
+use crate::driver::Bar0;
+use core::fmt::Debug;
+
+/// Enum representing the GPU chipset.
+#[derive(Debug)]
+pub(crate) enum Chipset {
+ TU102 = 0x162,
+ TU104 = 0x164,
+ TU106 = 0x166,
+ TU117 = 0x167,
+ TU116 = 0x168,
+ GA102 = 0x172,
+ GA103 = 0x173,
+ GA104 = 0x174,
+ GA106 = 0x176,
+ GA107 = 0x177,
+ AD102 = 0x192,
+ AD103 = 0x193,
+ AD104 = 0x194,
+ AD106 = 0x196,
+ AD107 = 0x197,
+}
+
+/// Enum representing the GPU generation.
+#[derive(Debug)]
+pub(crate) enum CardType {
+ /// Turing
+ TU100 = 0x160,
+ /// Ampere
+ GA100 = 0x170,
+ /// Ada Lovelace
+ AD100 = 0x190,
+}
+
+/// Structure holding the metadata of the GPU.
+#[allow(dead_code)]
+pub(crate) struct GpuSpec {
+ /// Contents of the boot0 register.
+ boot0: u64,
+ card_type: CardType,
+ chipset: Chipset,
+ /// The revision of the chipset.
+ chiprev: u8,
+}
+
+/// Structure encapsulating the firmware blobs required for the GPU to operate.
+#[allow(dead_code)]
+pub(crate) struct Firmware {
+ booter_load: firmware::Firmware,
+ booter_unload: firmware::Firmware,
+ gsp: firmware::Firmware,
+}
+
+/// Structure holding the resources required to operate the GPU.
+#[allow(dead_code)]
+#[pin_data]
+pub(crate) struct Gpu {
+ spec: GpuSpec,
+ /// MMIO mapping of PCI BAR 0
+ bar: Devres<Bar0>,
+ fw: Firmware,
+}
+
+// TODO replace with something like derive(FromPrimitive)
+impl Chipset {
+ fn from_u32(value: u32) -> Option<Chipset> {
+ match value {
+ 0x162 => Some(Chipset::TU102),
+ 0x164 => Some(Chipset::TU104),
+ 0x166 => Some(Chipset::TU106),
+ 0x167 => Some(Chipset::TU117),
+ 0x168 => Some(Chipset::TU116),
+ 0x172 => Some(Chipset::GA102),
+ 0x173 => Some(Chipset::GA103),
+ 0x174 => Some(Chipset::GA104),
+ 0x176 => Some(Chipset::GA106),
+ 0x177 => Some(Chipset::GA107),
+ 0x192 => Some(Chipset::AD102),
+ 0x193 => Some(Chipset::AD103),
+ 0x194 => Some(Chipset::AD104),
+ 0x196 => Some(Chipset::AD106),
+ 0x197 => Some(Chipset::AD107),
+ _ => None,
+ }
+ }
+}
+
+// TODO replace with something like derive(FromPrimitive)
+impl CardType {
+ fn from_u32(value: u32) -> Option<CardType> {
+ match value {
+ 0x160 => Some(CardType::TU100),
+ 0x170 => Some(CardType::GA100),
+ 0x190 => Some(CardType::AD100),
+ _ => None,
+ }
+ }
+}
+
+impl GpuSpec {
+ fn new(bar: &Devres<Bar0>) -> Result<GpuSpec> {
+ let bar = bar.try_access().ok_or(ENXIO)?;
+ let boot0 = u64::from_le(bar.readq(0));
+ let chip = ((boot0 & 0x1ff00000) >> 20) as u32;
+
+ if boot0 & 0x1f000000 == 0 {
+ return Err(ENODEV);
+ }
+
+ let chipset = match Chipset::from_u32(chip) {
+ Some(x) => x,
+ None => return Err(ENODEV),
+ };
+
+ let card_type = match CardType::from_u32(chip & 0x1f0) {
+ Some(x) => x,
+ None => return Err(ENODEV),
+ };
+
+ Ok(Self {
+ boot0,
+ card_type,
+ chipset,
+ chiprev: (boot0 & 0xff) as u8,
+ })
+ }
+}
+
+impl Firmware {
+ fn new(dev: &device::Device, spec: &GpuSpec, ver: &str) -> Result<Firmware> {
+ let mut chip_name = CString::try_from_fmt(fmt!("{:?}", spec.chipset))?;
+ chip_name.make_ascii_lowercase();
+
+ let fw_booter_load_path =
+ CString::try_from_fmt(fmt!("nvidia/{}/gsp/booter_load-{}.bin", &*chip_name, ver,))?;
+ let fw_booter_unload_path =
+ CString::try_from_fmt(fmt!("nvidia/{}/gsp/booter_unload-{}.bin", &*chip_name, ver))?;
+ let fw_gsp_path =
+ CString::try_from_fmt(fmt!("nvidia/{}/gsp/gsp-{}.bin", &*chip_name, ver))?;
+
+ let booter_load = firmware::Firmware::request(&fw_booter_load_path, dev)?;
+ let booter_unload = firmware::Firmware::request(&fw_booter_unload_path, dev)?;
+ let gsp = firmware::Firmware::request(&fw_gsp_path, dev)?;
+
+ Ok(Firmware {
+ booter_load,
+ booter_unload,
+ gsp,
+ })
+ }
+}
+
+impl Gpu {
+ pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<Arc<Gpu>> {
+ let spec = GpuSpec::new(&bar)?;
+ let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
+
+ dev_info!(
+ pdev.as_ref(),
+ "NVIDIA {:?} ({:#x})",
+ spec.chipset,
+ spec.boot0
+ );
+
+ Arc::pin_init(try_pin_init!(Self { spec, bar, fw }), GFP_KERNEL)
+ }
+}
diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs
new file mode 100644
index 000000000000..c675be404d9b
--- /dev/null
+++ b/drivers/gpu/drm/nova/nova.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Nova GPU Driver
+
+mod driver;
+mod file;
+mod gem;
+mod gpu;
+
+use crate::driver::NovaDriver;
+
+kernel::module_pci_driver! {
+ type: NovaDriver,
+ name: "Nova",
+ author: "Danilo Krummrich",
+ description: "Nova GPU driver",
+ license: "GPL v2",
+}