summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2025-05-21 05:49:31 +1000
committerDave Airlie <airlied@redhat.com>2025-05-21 05:49:31 +1000
commitc4f8ac095fc91084108ec21117eb9c1fff64725d (patch)
treeaebd07463aa88994b92c6653f53ef94d3fcc6710
parent7c1a9408ce5f34ded5a85db81cf80e0975901685 (diff)
parent276c53c66e032c8e7cc0da63555f2742eb1afd69 (diff)
Merge tag 'nova-next-v6.16-2025-05-20' of https://gitlab.freedesktop.org/drm/nova into drm-next
Nova changes for v6.16 auxiliary: - bus abstractions - implementation for driver registration - add sample driver drm: - implement __drm_dev_alloc() - DRM core infrastructure Rust abstractions - device, driver and registration - DRM IOCTL - DRM File - GEM object - IntoGEMObject rework - generically implement AlwaysRefCounted through IntoGEMObject - refactor unsound from_gem_obj() into as_ref() - refactor into_gem_obj() into as_raw() driver-core: - merge topic/device-context-2025-04-17 from driver-core tree - implement Devres::access() - fix: doctest build under `!CONFIG_PCI` - accessor for Device::parent() - fix: conditionally expect `dead_code` for `parent()` - impl TryFrom<&Device> bus devices (PCI, platform) nova-core: - remove completed Vec extentions from task list - register auxiliary device for nova-drm - derive useful traits for Chipset - add missing GA100 chipset - take &Device<Bound> in Gpu::new() - infrastructure to generate register definitions - fix register layout of NV_PMC_BOOT_0 - move Firmware into own (Rust) module - fix: select AUXILIARY_BUS nova-drm: - initial driver skeleton (depends on drm and auxiliary bus abstractions) - fix: select AUXILIARY_BUS Rust (dependencies): - implement Opaque::zeroed() - implement Revocable::try_access_with() - implement Revocable::access() From: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/r/aCxAf3RqQAXLDhAj@cassiopeiae
-rw-r--r--Documentation/gpu/nova/core/todo.rst16
-rw-r--r--MAINTAINERS17
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/drm_drv.c58
-rw-r--r--drivers/gpu/drm/nova/Kconfig14
-rw-r--r--drivers/gpu/drm/nova/Makefile3
-rw-r--r--drivers/gpu/drm/nova/driver.rs69
-rw-r--r--drivers/gpu/drm/nova/file.rs74
-rw-r--r--drivers/gpu/drm/nova/gem.rs49
-rw-r--r--drivers/gpu/drm/nova/nova.rs18
-rw-r--r--drivers/gpu/drm/nova/uapi.rs61
-rw-r--r--drivers/gpu/nova-core/Kconfig1
-rw-r--r--drivers/gpu/nova-core/driver.rs9
-rw-r--r--drivers/gpu/nova-core/firmware.rs44
-rw-r--r--drivers/gpu/nova-core/gpu.rs86
-rw-r--r--drivers/gpu/nova-core/nova_core.rs2
-rw-r--r--drivers/gpu/nova-core/regs.rs82
-rw-r--r--drivers/gpu/nova-core/regs/macros.rs380
-rw-r--r--include/drm/drm_drv.h5
-rw-r--r--include/uapi/drm/nova_drm.h101
-rw-r--r--rust/bindings/bindings_helper.h7
-rw-r--r--rust/helpers/auxiliary.c23
-rw-r--r--rust/helpers/drm.c23
-rw-r--r--rust/helpers/helpers.c2
-rw-r--r--rust/helpers/pci.c5
-rw-r--r--rust/helpers/platform.c5
-rw-r--r--rust/kernel/auxiliary.rs360
-rw-r--r--rust/kernel/device.rs109
-rw-r--r--rust/kernel/devres.rs56
-rw-r--r--rust/kernel/dma.rs14
-rw-r--r--rust/kernel/drm/device.rs200
-rw-r--r--rust/kernel/drm/driver.rs166
-rw-r--r--rust/kernel/drm/file.rs99
-rw-r--r--rust/kernel/drm/gem/mod.rs328
-rw-r--r--rust/kernel/drm/ioctl.rs162
-rw-r--r--rust/kernel/drm/mod.rs19
-rw-r--r--rust/kernel/lib.rs4
-rw-r--r--rust/kernel/pci.rs55
-rw-r--r--rust/kernel/platform.rs54
-rw-r--r--rust/kernel/revocable.rs28
-rw-r--r--rust/kernel/types.rs8
-rw-r--r--rust/uapi/uapi_helper.h2
-rw-r--r--samples/rust/Kconfig12
-rw-r--r--samples/rust/Makefile1
-rw-r--r--samples/rust/rust_driver_auxiliary.rs120
-rw-r--r--samples/rust/rust_driver_pci.rs5
47 files changed, 2762 insertions, 197 deletions
diff --git a/Documentation/gpu/nova/core/todo.rst b/Documentation/gpu/nova/core/todo.rst
index ca08377d3b73..8a459fc08812 100644
--- a/Documentation/gpu/nova/core/todo.rst
+++ b/Documentation/gpu/nova/core/todo.rst
@@ -102,7 +102,13 @@ Usage:
let boot0 = Boot0::read(&bar);
pr_info!("Revision: {}\n", boot0.revision());
+Note: a work-in-progress implementation currently resides in
+`drivers/gpu/nova-core/regs/macros.rs` and is used in nova-core. It would be
+nice to improve it (possibly using proc macros) and move it to the `kernel`
+crate so it can be used by other components as well.
+
| Complexity: Advanced
+| Contact: Alexandre Courbot
Delay / Sleep abstractions
--------------------------
@@ -190,16 +196,6 @@ Rust abstraction for debugfs APIs.
| Reference: Export GSP log buffers
| Complexity: Intermediate
-Vec extensions
---------------
-
-Implement ``Vec::truncate`` and ``Vec::resize``.
-
-Currently this is used for some experimental code to parse the vBIOS.
-
-| Reference vBIOS support
-| Complexity: Beginner
-
GPU (general)
=============
diff --git a/MAINTAINERS b/MAINTAINERS
index 9f3cb7ea2798..d063d711e092 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3880,6 +3880,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
F: Documentation/driver-api/auxiliary_bus.rst
F: drivers/base/auxiliary.c
F: include/linux/auxiliary_bus.h
+F: rust/helpers/auxiliary.c
+F: rust/kernel/auxiliary.rs
+F: samples/rust/rust_driver_auxiliary.rs
AUXILIARY DISPLAY DRIVERS
M: Andy Shevchenko <andy@kernel.org>
@@ -7607,6 +7610,18 @@ T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
F: Documentation/gpu/nova/
F: drivers/gpu/nova-core/
+DRM DRIVER FOR NVIDIA GPUS [RUST]
+M: Danilo Krummrich <dakr@kernel.org>
+L: nouveau@lists.freedesktop.org
+S: Supported
+Q: https://patchwork.freedesktop.org/project/nouveau/
+B: https://gitlab.freedesktop.org/drm/nova/-/issues
+C: irc://irc.oftc.net/nouveau
+T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
+F: Documentation/gpu/nova/
+F: drivers/gpu/drm/nova/
+F: include/uapi/drm/nova_drm.h
+
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
M: Stefan Mavrodiev <stefan@olimex.com>
S: Maintained
@@ -7820,6 +7835,7 @@ F: Documentation/devicetree/bindings/display/
F: Documentation/devicetree/bindings/gpu/
F: Documentation/gpu/
F: drivers/gpu/
+F: rust/kernel/drm/
F: include/drm/
F: include/linux/vga*
F: include/uapi/drm/
@@ -7836,6 +7852,7 @@ F: Documentation/devicetree/bindings/gpu/
F: Documentation/gpu/
F: drivers/gpu/drm/
F: drivers/gpu/vga/
+F: rust/kernel/drm/
F: include/drm/drm
F: include/linux/vga*
F: include/uapi/drm/
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 9488fc01bca3..f094797f3b2b 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -274,6 +274,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 70510620f29c..4199715670b1 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -177,6 +177,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) += nova/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3dc7acd56b1d..4589ab4bb1ec 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -830,36 +830,62 @@ void *__devm_drm_dev_alloc(struct device *parent,
EXPORT_SYMBOL(__devm_drm_dev_alloc);
/**
- * drm_dev_alloc - Allocate new DRM device
- * @driver: DRM driver to allocate device for
+ * __drm_dev_alloc - Allocation of a &drm_device instance
* @parent: Parent device object
+ * @driver: DRM driver
+ * @size: the size of the struct which contains struct drm_device
+ * @offset: the offset of the &drm_device within the container.
*
- * This is the deprecated version of devm_drm_dev_alloc(), which does not support
- * subclassing through embedding the struct &drm_device in a driver private
- * structure, and which does not support automatic cleanup through devres.
+ * This should *NOT* be by any drivers, but is a dedicated interface for the
+ * corresponding Rust abstraction.
*
- * RETURNS:
- * Pointer to new DRM device, or ERR_PTR on failure.
+ * This is the same as devm_drm_dev_alloc(), but without the corresponding
+ * resource management through the parent device, but not the same as
+ * drm_dev_alloc(), since the latter is the deprecated version, which does not
+ * support subclassing.
+ *
+ * Returns: A pointer to new DRM device, or an ERR_PTR on failure.
*/
-struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
- struct device *parent)
+void *__drm_dev_alloc(struct device *parent,
+ const struct drm_driver *driver,
+ size_t size, size_t offset)
{
- struct drm_device *dev;
+ void *container;
+ struct drm_device *drm;
int ret;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ container = kzalloc(size, GFP_KERNEL);
+ if (!container)
return ERR_PTR(-ENOMEM);
- ret = drm_dev_init(dev, driver, parent);
+ drm = container + offset;
+ ret = drm_dev_init(drm, driver, parent);
if (ret) {
- kfree(dev);
+ kfree(container);
return ERR_PTR(ret);
}
+ drmm_add_final_kfree(drm, container);
- drmm_add_final_kfree(dev, dev);
+ return container;
+}
+EXPORT_SYMBOL(__drm_dev_alloc);
- return dev;
+/**
+ * drm_dev_alloc - Allocate new DRM device
+ * @driver: DRM driver to allocate device for
+ * @parent: Parent device object
+ *
+ * This is the deprecated version of devm_drm_dev_alloc(), which does not support
+ * subclassing through embedding the struct &drm_device in a driver private
+ * structure, and which does not support automatic cleanup through devres.
+ *
+ * RETURNS:
+ * Pointer to new DRM device, or ERR_PTR on failure.
+ */
+struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
+ struct device *parent)
+{
+ return __drm_dev_alloc(parent, driver, sizeof(struct drm_device), 0);
}
EXPORT_SYMBOL(drm_dev_alloc);
diff --git a/drivers/gpu/drm/nova/Kconfig b/drivers/gpu/drm/nova/Kconfig
new file mode 100644
index 000000000000..cca6a3fea879
--- /dev/null
+++ b/drivers/gpu/drm/nova/Kconfig
@@ -0,0 +1,14 @@
+config DRM_NOVA
+ tristate "Nova DRM driver"
+ depends on DRM=y
+ depends on PCI
+ depends on RUST
+ select AUXILIARY_BUS
+ default n
+ help
+ Choose this if you want to build the Nova DRM driver for Nvidia
+ GSP-based GPUs.
+
+ This driver is work in progress and may not be functional.
+
+ 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..42019bff3173
--- /dev/null
+++ b/drivers/gpu/drm/nova/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_NOVA) += nova.o
diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs
new file mode 100644
index 000000000000..b28b2e05cc15
--- /dev/null
+++ b/drivers/gpu/drm/nova/driver.rs
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{auxiliary, c_str, device::Core, drm, drm::gem, drm::ioctl, prelude::*, types::ARef};
+
+use crate::file::File;
+use crate::gem::NovaObject;
+
+pub(crate) struct NovaDriver {
+ #[expect(unused)]
+ drm: ARef<drm::Device<Self>>,
+}
+
+/// Convienence type alias for the DRM device type for this driver
+pub(crate) type NovaDevice = drm::Device<NovaDriver>;
+
+#[pin_data]
+pub(crate) struct NovaData {
+ pub(crate) adev: ARef<auxiliary::Device>,
+}
+
+const INFO: drm::DriverInfo = drm::DriverInfo {
+ major: 0,
+ minor: 0,
+ patchlevel: 0,
+ name: c_str!("nova"),
+ desc: c_str!("Nvidia Graphics"),
+};
+
+const NOVA_CORE_MODULE_NAME: &CStr = c_str!("NovaCore");
+const AUXILIARY_NAME: &CStr = c_str!("nova-drm");
+
+kernel::auxiliary_device_table!(
+ AUX_TABLE,
+ MODULE_AUX_TABLE,
+ <NovaDriver as auxiliary::Driver>::IdInfo,
+ [(
+ auxiliary::DeviceId::new(NOVA_CORE_MODULE_NAME, AUXILIARY_NAME),
+ ()
+ )]
+);
+
+impl auxiliary::Driver for NovaDriver {
+ type IdInfo = ();
+ const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
+
+ fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
+ let data = try_pin_init!(NovaData { adev: adev.into() });
+
+ let drm = drm::Device::<Self>::new(adev.as_ref(), data)?;
+ drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?;
+
+ Ok(KBox::new(Self { drm }, GFP_KERNEL)?.into())
+ }
+}
+
+#[vtable]
+impl drm::Driver for NovaDriver {
+ type Data = NovaData;
+ type File = File;
+ type Object = gem::Object<NovaObject>;
+
+ const INFO: drm::DriverInfo = INFO;
+
+ 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..7e59a34b830d
--- /dev/null
+++ b/drivers/gpu/drm/nova/file.rs
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::driver::{NovaDevice, NovaDriver};
+use crate::gem::NovaObject;
+use crate::uapi::{GemCreate, GemInfo, Getparam};
+use kernel::{
+ alloc::flags::*,
+ drm::{self, gem::BaseObject},
+ pci,
+ prelude::*,
+ types::Opaque,
+ uapi,
+};
+
+pub(crate) struct File;
+
+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: get_param: Query GPU / driver metadata.
+ pub(crate) fn get_param(
+ dev: &NovaDevice,
+ getparam: &Opaque<uapi::drm_nova_getparam>,
+ _file: &drm::File<File>,
+ ) -> Result<u32> {
+ let adev = &dev.adev;
+ let parent = adev.parent().ok_or(ENOENT)?;
+ let pdev: &pci::Device = parent.try_into()?;
+ let getparam: &Getparam = getparam.into();
+
+ let value = match getparam.param() as u32 {
+ uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?,
+ _ => return Err(EINVAL),
+ };
+
+ getparam.set_value(value);
+
+ Ok(0)
+ }
+
+ /// IOCTL: gem_create: Create a new DRM GEM object.
+ pub(crate) fn gem_create(
+ dev: &NovaDevice,
+ req: &Opaque<uapi::drm_nova_gem_create>,
+ file: &drm::File<File>,
+ ) -> Result<u32> {
+ let req: &GemCreate = req.into();
+ let obj = NovaObject::new(dev, req.size().try_into()?)?;
+
+ req.set_handle(obj.create_handle(file)?);
+
+ Ok(0)
+ }
+
+ /// IOCTL: gem_info: Query GEM metadata.
+ pub(crate) fn gem_info(
+ _dev: &NovaDevice,
+ req: &Opaque<uapi::drm_nova_gem_info>,
+ file: &drm::File<File>,
+ ) -> Result<u32> {
+ let req: &GemInfo = req.into();
+ let bo = NovaObject::lookup_handle(file, req.handle())?;
+
+ req.set_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..33b62d21400c
--- /dev/null
+++ b/drivers/gpu/drm/nova/gem.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+ drm,
+ drm::{gem, gem::BaseObject},
+ prelude::*,
+ types::ARef,
+};
+
+use crate::{
+ driver::{NovaDevice, NovaDriver},
+ file::File,
+};
+
+/// GEM Object inner driver data
+#[pin_data]
+pub(crate) struct NovaObject {}
+
+impl gem::BaseDriverObject<gem::Object<NovaObject>> for NovaObject {
+ fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> {
+ try_pin_init!(NovaObject {})
+ }
+}
+
+impl gem::DriverObject for NovaObject {
+ type Driver = NovaDriver;
+}
+
+impl NovaObject {
+ /// Create a new DRM GEM object.
+ pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result<ARef<gem::Object<Self>>> {
+ let aligned_size = size.next_multiple_of(1 << 12);
+
+ if size == 0 || size > aligned_size {
+ return Err(EINVAL);
+ }
+
+ gem::Object::new(dev, aligned_size)
+ }
+
+ /// Look up a GEM object handle for a `File` and return an `ObjectRef` for it.
+ #[inline]
+ pub(crate) fn lookup_handle(
+ file: &drm::File<File>,
+ handle: u32,
+ ) -> Result<ARef<gem::Object<Self>>> {
+ gem::Object::lookup_handle(file, handle)
+ }
+}
diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs
new file mode 100644
index 000000000000..902876aa14d1
--- /dev/null
+++ b/drivers/gpu/drm/nova/nova.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Nova DRM Driver
+
+mod driver;
+mod file;
+mod gem;
+mod uapi;
+
+use crate::driver::NovaDriver;
+
+kernel::module_auxiliary_driver! {
+ type: NovaDriver,
+ name: "Nova",
+ author: "Danilo Krummrich",
+ description: "Nova GPU driver",
+ license: "GPL v2",
+}
diff --git a/drivers/gpu/drm/nova/uapi.rs b/drivers/gpu/drm/nova/uapi.rs
new file mode 100644
index 000000000000..eb228a58d423
--- /dev/null
+++ b/drivers/gpu/drm/nova/uapi.rs
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::uapi;
+
+// TODO Work out some common infrastructure to avoid boilerplate code for uAPI abstractions.
+
+macro_rules! define_uapi_abstraction {
+ ($name:ident <= $inner:ty) => {
+ #[repr(transparent)]
+ pub struct $name(::kernel::types::Opaque<$inner>);
+
+ impl ::core::convert::From<&::kernel::types::Opaque<$inner>> for &$name {
+ fn from(value: &::kernel::types::Opaque<$inner>) -> Self {
+ // SAFETY: `Self` is a transparent wrapper of `$inner`.
+ unsafe { ::core::mem::transmute(value) }
+ }
+ }
+ };
+}
+
+define_uapi_abstraction!(Getparam <= uapi::drm_nova_getparam);
+
+impl Getparam {
+ pub fn param(&self) -> u64 {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
+ unsafe { (*self.0.get()).param }
+ }
+
+ pub fn set_value(&self, v: u64) {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
+ unsafe { (*self.0.get()).value = v };
+ }
+}
+
+define_uapi_abstraction!(GemCreate <= uapi::drm_nova_gem_create);
+
+impl GemCreate {
+ pub fn size(&self) -> u64 {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
+ unsafe { (*self.0.get()).size }
+ }
+
+ pub fn set_handle(&self, handle: u32) {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
+ unsafe { (*self.0.get()).handle = handle };
+ }
+}
+
+define_uapi_abstraction!(GemInfo <= uapi::drm_nova_gem_info);
+
+impl GemInfo {
+ pub fn handle(&self) -> u32 {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
+ unsafe { (*self.0.get()).handle }
+ }
+
+ pub fn set_size(&self, size: u64) {
+ // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
+ unsafe { (*self.0.get()).size = size };
+ }
+}
diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index ad0c06756516..8726d80d6ba4 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -3,6 +3,7 @@ config NOVA_CORE
depends on PCI
depends on RUST
depends on RUST_FW_LOADER_ABSTRACTIONS
+ select AUXILIARY_BUS
default n
help
Choose this if you want to build the Nova Core driver for Nvidia
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index a08fb6599267..8c86101c26cb 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::{bindings, c_str, device::Core, pci, prelude::*};
+use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*};
use crate::gpu::Gpu;
@@ -8,6 +8,7 @@ use crate::gpu::Gpu;
pub(crate) struct NovaCore {
#[pin]
pub(crate) gpu: Gpu,
+ _reg: auxiliary::Registration,
}
const BAR0_SIZE: usize = 8;
@@ -38,6 +39,12 @@ impl pci::Driver for NovaCore {
let this = KBox::pin_init(
try_pin_init!(Self {
gpu <- Gpu::new(pdev, bar)?,
+ _reg: auxiliary::Registration::new(
+ pdev.as_ref(),
+ c_str!("nova-drm"),
+ 0, // TODO: Once it lands, use XArray; for now we don't use the ID.
+ crate::MODULE_NAME
+ )?,
}),
GFP_KERNEL,
)?;
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 6e6361c59ca1..4b8a38358a4f 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -1,13 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::gpu;
+//! Contains structures and functions dedicated to the parsing, building and patching of firmwares
+//! to be loaded into a given execution unit.
+
+use kernel::device;
use kernel::firmware;
+use kernel::prelude::*;
+use kernel::str::CString;
+
+use crate::gpu;
+use crate::gpu::Chipset;
+
+pub(crate) const FIRMWARE_VERSION: &str = "535.113.01";
+
+/// Structure encapsulating the firmware blobs required for the GPU to operate.
+#[expect(dead_code)]
+pub(crate) struct Firmware {
+ booter_load: firmware::Firmware,
+ booter_unload: firmware::Firmware,
+ bootloader: firmware::Firmware,
+ gsp: firmware::Firmware,
+}
+
+impl Firmware {
+ pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> {
+ let mut chip_name = CString::try_from_fmt(fmt!("{}", chipset))?;
+ chip_name.make_ascii_lowercase();
+
+ let request = |name_| {
+ CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
+ .and_then(|path| firmware::Firmware::request(&path, dev))
+ };
+
+ Ok(Firmware {
+ booter_load: request("booter_load")?,
+ booter_unload: request("booter_unload")?,
+ bootloader: request("bootloader")?,
+ gsp: request("gsp")?,
+ })
+ }
+}
pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
impl<const N: usize> ModInfoBuilder<N> {
- const VERSION: &'static str = "535.113.01";
-
const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
ModInfoBuilder(
self.0
@@ -17,7 +53,7 @@ impl<const N: usize> ModInfoBuilder<N> {
.push("/gsp/")
.push(fw)
.push("-")
- .push(Self::VERSION)
+ .push(FIRMWARE_VERSION)
.push(".bin"),
)
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 17c9660da450..99c6796e73e9 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -1,10 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::{
- device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString,
-};
+use kernel::{device, devres::Devres, error::code::*, pci, prelude::*};
use crate::driver::Bar0;
+use crate::firmware::{Firmware, FIRMWARE_VERSION};
use crate::regs;
use crate::util;
use core::fmt;
@@ -13,7 +12,7 @@ macro_rules! define_chipset {
({ $($variant:ident = $value:expr),* $(,)* }) =>
{
/// Enum representation of the GPU chipset.
- #[derive(fmt::Debug)]
+ #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) enum Chipset {
$($variant = $value),*,
}
@@ -54,6 +53,7 @@ define_chipset!({
TU117 = 0x167,
TU116 = 0x168,
// Ampere
+ GA100 = 0x170,
GA102 = 0x172,
GA103 = 0x173,
GA104 = 0x174,
@@ -73,7 +73,7 @@ impl Chipset {
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
Architecture::Turing
}
- Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
+ Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
Architecture::Ampere
}
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
@@ -100,9 +100,22 @@ impl fmt::Display for Chipset {
/// Enum representation of the GPU generation.
#[derive(fmt::Debug)]
pub(crate) enum Architecture {
- Turing,
- Ampere,
- Ada,
+ Turing = 0x16,
+ Ampere = 0x17,
+ Ada = 0x19,
+}
+
+impl TryFrom<u8> for Architecture {
+ type Error = Error;
+
+ fn try_from(value: u8) -> Result<Self> {
+ match value {
+ 0x16 => Ok(Self::Turing),
+ 0x17 => Ok(Self::Ampere),
+ 0x19 => Ok(Self::Ada),
+ _ => Err(ENODEV),
+ }
+ }
}
pub(crate) struct Revision {
@@ -111,10 +124,10 @@ pub(crate) struct Revision {
}
impl Revision {
- fn from_boot0(boot0: regs::Boot0) -> Self {
+ fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
Self {
- major: boot0.major_rev(),
- minor: boot0.minor_rev(),
+ major: boot0.major_revision(),
+ minor: boot0.minor_revision(),
}
}
}
@@ -133,45 +146,16 @@ pub(crate) struct Spec {
}
impl Spec {
- fn new(bar: &Devres<Bar0>) -> Result<Spec> {
- let bar = bar.try_access().ok_or(ENXIO)?;
- let boot0 = regs::Boot0::read(&bar);
+ fn new(bar: &Bar0) -> Result<Spec> {
+ let boot0 = regs::NV_PMC_BOOT_0::read(bar);
Ok(Self {
- chipset: boot0.chipset().try_into()?,
+ chipset: boot0.chipset()?,
revision: Revision::from_boot0(boot0),
})
}
}
-/// Structure encapsulating the firmware blobs required for the GPU to operate.
-#[expect(dead_code)]
-pub(crate) struct Firmware {
- booter_load: firmware::Firmware,
- booter_unload: firmware::Firmware,
- bootloader: firmware::Firmware,
- gsp: firmware::Firmware,
-}
-
-impl Firmware {
- fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> {
- let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?;
- chip_name.make_ascii_lowercase();
-
- let request = |name_| {
- CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
- .and_then(|path| firmware::Firmware::request(&path, dev))
- };
-
- Ok(Firmware {
- booter_load: request("booter_load")?,
- booter_unload: request("booter_unload")?,
- bootloader: request("bootloader")?,
- gsp: request("gsp")?,
- })
- }
-}
-
/// Structure holding the resources required to operate the GPU.
#[pin_data]
pub(crate) struct Gpu {
@@ -182,9 +166,13 @@ pub(crate) struct Gpu {
}
impl Gpu {
- pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> {
- let spec = Spec::new(&bar)?;
- let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
+ pub(crate) fn new(
+ pdev: &pci::Device<device::Bound>,
+ devres_bar: Devres<Bar0>,
+ ) -> Result<impl PinInit<Self>> {
+ let bar = devres_bar.access(pdev.as_ref())?;
+ let spec = Spec::new(bar)?;
+ let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?;
dev_info!(
pdev.as_ref(),
@@ -194,6 +182,10 @@ impl Gpu {
spec.revision
);
- Ok(pin_init!(Self { spec, bar, fw }))
+ Ok(pin_init!(Self {
+ spec,
+ bar: devres_bar,
+ fw
+ }))
}
}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index a91cd924054b..618632f0abcc 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -8,6 +8,8 @@ mod gpu;
mod regs;
mod util;
+pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
+
kernel::module_pci_driver! {
type: driver::NovaCore,
name: "NovaCore",
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index b1a25b86ef17..5a1273230306 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -1,55 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::driver::Bar0;
-
-// TODO
-//
-// Create register definitions via generic macros. See task "Generic register
-// abstraction" in Documentation/gpu/nova/core/todo.rst.
-
-const BOOT0_OFFSET: usize = 0x00000000;
-
-// 3:0 - chipset minor revision
-const BOOT0_MINOR_REV_SHIFT: u8 = 0;
-const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f;
-
-// 7:4 - chipset major revision
-const BOOT0_MAJOR_REV_SHIFT: u8 = 4;
-const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0;
-
-// 23:20 - chipset implementation Identifier (depends on architecture)
-const BOOT0_IMPL_SHIFT: u8 = 20;
-const BOOT0_IMPL_MASK: u32 = 0x00f00000;
-
-// 28:24 - chipset architecture identifier
-const BOOT0_ARCH_MASK: u32 = 0x1f000000;
-
-// 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and
-// BOOT0_ARCH)
-const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT;
-const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK;
-
-#[derive(Copy, Clone)]
-pub(crate) struct Boot0(u32);
-
-impl Boot0 {
- #[inline]
- pub(crate) fn read(bar: &Bar0) -> Self {
- Self(bar.read32(BOOT0_OFFSET))
- }
-
- #[inline]
- pub(crate) fn chipset(&self) -> u32 {
- (self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT
- }
-
- #[inline]
- pub(crate) fn minor_rev(&self) -> u8 {
- ((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8
+// Required to retain the original register names used by OpenRM, which are all capital snake case
+// but are mapped to types.
+#![allow(non_camel_case_types)]
+
+#[macro_use]
+mod macros;
+
+use crate::gpu::{Architecture, Chipset};
+use kernel::prelude::*;
+
+/* PMC */
+
+register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" {
+ 3:0 minor_revision as u8, "Minor revision of the chip";
+ 7:4 major_revision as u8, "Major revision of the chip";
+ 8:8 architecture_1 as u8, "MSB of the architecture";
+ 23:20 implementation as u8, "Implementation version of the architecture";
+ 28:24 architecture_0 as u8, "Lower bits of the architecture";
+});
+
+impl NV_PMC_BOOT_0 {
+ /// Combines `architecture_0` and `architecture_1` to obtain the architecture of the chip.
+ pub(crate) fn architecture(self) -> Result<Architecture> {
+ Architecture::try_from(
+ self.architecture_0() | (self.architecture_1() << Self::ARCHITECTURE_0.len()),
+ )
}
- #[inline]
- pub(crate) fn major_rev(&self) -> u8 {
- ((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8
+ /// Combines `architecture` and `implementation` to obtain a code unique to the chipset.
+ pub(crate) fn chipset(self) -> Result<Chipset> {
+ self.architecture()
+ .map(|arch| {
+ ((arch as u32) << Self::IMPLEMENTATION.len()) | self.implementation() as u32
+ })
+ .and_then(Chipset::try_from)
}
}
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
new file mode 100644
index 000000000000..7ecc70efb3cd
--- /dev/null
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Macro to define register layout and accessors.
+//!
+//! A single register typically includes several fields, which are accessed through a combination
+//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
+//! not all possible field values are necessarily valid.
+//!
+//! The macro in this module allow to define, using an intruitive and readable syntax, a dedicated
+//! type for each register with its own field accessors that can return an error is a field's value
+//! is invalid.
+
+/// Defines a dedicated type for a register with an absolute offset, alongside with getter and
+/// setter methods for its fields and methods to read and write it from an `Io` region.
+///
+/// Example:
+///
+/// ```no_run
+/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
+/// 3:0 minor_revision as u8, "Minor revision of the chip";
+/// 7:4 major_revision as u8, "Major revision of the chip";
+/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
+/// });
+/// ```
+///
+/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
+/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 less
+/// significant bits of the register. Each field can be accessed and modified using accessor
+/// methods:
+///
+/// ```no_run
+/// // Read from the register's defined offset (0x100).
+/// let boot0 = BOOT_0::read(&bar);
+/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
+///
+/// // `Chipset::try_from` will be called with the value of the field and returns an error if the
+/// // value is invalid.
+/// let chipset = boot0.chipset()?;
+///
+/// // Update some fields and write the value back.
+/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
+///
+/// // Or just read and update the register in a single step:
+/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
+/// ```
+///
+/// Fields can be defined as follows:
+///
+/// - `as <type>` simply returns the field value casted as the requested integer type, typically
+/// `u32`, `u16`, `u8` or `bool`. Note that `bool` fields must have a range of 1 bit.
+/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
+/// the result.
+/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
+/// and returns the result. This is useful on fields for which not all values are value.
+///
+/// The documentation strings are optional. If present, they will be added to the type's
+/// definition, or the field getter and setter methods they are attached to.
+///
+/// Putting a `+` before the address of the register makes it relative to a base: the `read` and
+/// `write` methods take a `base` argument that is added to the specified address before access,
+/// and `try_read` and `try_write` methods are also created, allowing access with offsets unknown
+/// at compile-time:
+///
+/// ```no_run
+/// register!(CPU_CTL @ +0x0000010, "CPU core control" {
+/// 0:0 start as bool, "Start the CPU core";
+/// });
+///
+/// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.
+/// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
+/// pr_info!("CPU CTL: {:#x}", cpuctl);
+/// cpuctl.set_start(true).write(&bar, CPU_BASE);
+/// ```
+macro_rules! register {
+ // Creates a register at a fixed offset of the MMIO space.
+ (
+ $name:ident @ $offset:literal $(, $comment:literal)? {
+ $($fields:tt)*
+ }
+ ) => {
+ register!(@common $name $(, $comment)?);
+ register!(@field_accessors $name { $($fields)* });
+ register!(@io $name @ $offset);
+ };
+
+ // Creates a register at a relative offset from a base address.
+ (
+ $name:ident @ + $offset:literal $(, $comment:literal)? {
+ $($fields:tt)*
+ }
+ ) => {
+ register!(@common $name $(, $comment)?);
+ register!(@field_accessors $name { $($fields)* });
+ register!(@io$name @ + $offset);
+ };
+
+ // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,
+ // and conversion to regular `u32`).
+ (@common $name:ident $(, $comment:literal)?) => {
+ $(
+ #[doc=$comment]
+ )?
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Default)]
+ pub(crate) struct $name(u32);
+
+ // TODO: display the raw hex value, then the value of all the fields. This requires
+ // matching the fields, which will complexify the syntax considerably...
+ impl ::core::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ f.debug_tuple(stringify!($name))
+ .field(&format_args!("0x{0:x}", &self.0))
+ .finish()
+ }
+ }
+
+ impl core::ops::BitOr for $name {
+ type Output = Self;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl ::core::convert::From<$name> for u32 {
+ fn from(reg: $name) -> u32 {
+ reg.0
+ }
+ }
+ };
+
+ // Defines all the field getter/methods methods for `$name`.
+ (
+ @field_accessors $name:ident {
+ $($hi:tt:$lo:tt $field:ident as $type:tt
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ $(
+ register!(@check_field_bounds $hi:$lo $field as $type);
+ )*
+
+ #[allow(dead_code)]
+ impl $name {
+ $(
+ register!(@field_accessor $name $hi:$lo $field as $type
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ $(, $comment)?
+ ;
+ );
+ )*
+ }
+ };
+
+ // Boolean fields must have `$hi == $lo`.
+ (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
+ #[allow(clippy::eq_op)]
+ const _: () = {
+ kernel::build_assert!(
+ $hi == $lo,
+ concat!("boolean field `", stringify!($field), "` covers more than one bit")
+ );
+ };
+ };
+
+ // Non-boolean fields must have `$hi >= $lo`.
+ (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
+ #[allow(clippy::eq_op)]
+ const _: () = {
+ kernel::build_assert!(
+ $hi >= $lo,
+ concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
+ );
+ };
+ };
+
+ // Catches fields defined as `bool` and convert them into a boolean value.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ register!(
+ @leaf_accessor $name $hi:$lo $field as bool
+ { |f| <$into_type>::from(if f != 0 { true } else { false }) }
+ $into_type => $into_type $(, $comment)?;
+ );
+ };
+
+ // Shortcut for fields defined as `bool` without the `=>` syntax.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
+ ) => {
+ register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
+ };
+
+ // Catches the `?=>` syntax for non-boolean fields.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ register!(@leaf_accessor $name $hi:$lo $field as $type
+ { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
+ ::core::result::Result<
+ $try_into_type,
+ <$try_into_type as ::core::convert::TryFrom<$type>>::Error
+ >
+ $(, $comment)?;);
+ };
+
+ // Catches the `=>` syntax for non-boolean fields.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ register!(@leaf_accessor $name $hi:$lo $field as $type
+ { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
+ };
+
+ // Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
+ $(, $comment:literal)?;
+ ) => {
+ register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
+ };
+
+ // Generates the accessor methods for a single field.
+ (
+ @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
+ { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
+ ) => {
+ kernel::macros::paste!(
+ const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
+ const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
+ const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
+ );
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline]
+ pub(crate) fn $field(self) -> $res_type {
+ kernel::macros::paste!(
+ const MASK: u32 = $name::[<$field:upper _MASK>];
+ const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
+ );
+ let field = ((self.0 & MASK) >> SHIFT);
+
+ $process(field)
+ }
+
+ kernel::macros::paste!(
+ $(
+ #[doc="Sets the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline]
+ pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
+ const MASK: u32 = $name::[<$field:upper _MASK>];
+ const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
+ let value = ((value as u32) << SHIFT) & MASK;
+ self.0 = (self.0 & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Creates the IO accessors for a fixed offset register.
+ (@io $name:ident @ $offset:literal) => {
+ #[allow(dead_code)]
+ impl $name {
+ #[inline]
+ pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ Self(io.read32($offset))
+ }
+
+ #[inline]
+ pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ io.write32(self.0, $offset)
+ }
+
+ #[inline]
+ pub(crate) fn alter<const SIZE: usize, T, F>(
+ io: &T,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io));
+ reg.write(io);
+ }
+ }
+ };
+
+ // Create the IO accessors for a relative offset register.
+ (@io $name:ident @ + $offset:literal) => {
+ #[allow(dead_code)]
+ impl $name {
+ #[inline]
+ pub(crate) fn read<const SIZE: usize, T>(
+ io: &T,
+ base: usize,
+ ) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ Self(io.read32(base + $offset))
+ }
+
+ #[inline]
+ pub(crate) fn write<const SIZE: usize, T>(
+ self,
+ io: &T,
+ base: usize,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ io.write32(self.0, base + $offset)
+ }
+
+ #[inline]
+ pub(crate) fn alter<const SIZE: usize, T, F>(
+ io: &T,
+ base: usize,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io, base));
+ reg.write(io, base);
+ }
+
+ #[inline]
+ pub(crate) fn try_read<const SIZE: usize, T>(
+ io: &T,
+ base: usize,
+ ) -> ::kernel::error::Result<Self> where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ io.try_read32(base + $offset).map(Self)
+ }
+
+ #[inline]
+ pub(crate) fn try_write<const SIZE: usize, T>(
+ self,
+ io: &T,
+ base: usize,
+ ) -> ::kernel::error::Result<()> where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ io.try_write32(self.0, base + $offset)
+ }
+
+ #[inline]
+ pub(crate) fn try_alter<const SIZE: usize, T, F>(
+ io: &T,
+ base: usize,
+ f: F,
+ ) -> ::kernel::error::Result<()> where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::try_read(io, base)?);
+ reg.try_write(io, base)
+ }
+ }
+ };
+}
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index a43d707b5f36..63b51942d606 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -473,6 +473,11 @@ drmm_cgroup_register_region(struct drm_device *dev,
struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
struct device *parent);
+
+void *__drm_dev_alloc(struct device *parent,
+ const struct drm_driver *driver,
+ size_t size, size_t offset);
+
int drm_dev_register(struct drm_device *dev, unsigned long flags);
void drm_dev_unregister(struct drm_device *dev);
diff --git a/include/uapi/drm/nova_drm.h b/include/uapi/drm/nova_drm.h
new file mode 100644
index 000000000000..3ca90ed9d2bb
--- /dev/null
+++ b/include/uapi/drm/nova_drm.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __NOVA_DRM_H__
+#define __NOVA_DRM_H__
+
+#include "drm.h"
+
+/* DISCLAIMER: Do not use, this is not a stable uAPI.
+ *
+ * This uAPI serves only testing purposes as long as this driver is still in
+ * development. It is required to implement and test infrastructure which is
+ * upstreamed in the context of this driver. See also [1].
+ *
+ * [1] https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * NOVA_GETPARAM_VRAM_BAR_SIZE
+ *
+ * Query the VRAM BAR size in bytes.
+ */
+#define NOVA_GETPARAM_VRAM_BAR_SIZE 0x1
+
+/**
+ * struct drm_nova_getparam - query GPU and driver metadata
+ */
+struct drm_nova_getparam {
+ /**
+ * @param: The identifier of the parameter to query.
+ */
+ __u64 param;
+
+ /**
+ * @value: The value for the specified parameter.
+ */
+ __u64 value;
+};
+
+/**
+ * struct drm_nova_gem_create - create a new DRM GEM object
+ */
+struct drm_nova_gem_create {
+ /**
+ * @handle: The handle of the new DRM GEM object.
+ */
+ __u32 handle;
+
+ /**
+ * @pad: 32 bit padding, should be 0.
+ */
+ __u32 pad;
+
+ /**
+ * @size: The size of the new DRM GEM object.
+ */
+ __u64 size;
+};
+
+/**
+ * struct drm_nova_gem_info - query DRM GEM object metadata
+ */
+struct drm_nova_gem_info {
+ /**
+ * @handle: The handle of the DRM GEM object to query.
+ */
+ __u32 handle;
+
+ /**
+ * @pad: 32 bit padding, should be 0.
+ */
+ __u32 pad;
+
+ /**
+ * @size: The size of the DRM GEM obejct.
+ */
+ __u64 size;
+};
+
+#define DRM_NOVA_GETPARAM 0x00
+#define DRM_NOVA_GEM_CREATE 0x01
+#define DRM_NOVA_GEM_INFO 0x02
+
+/* Note: this is an enum so that it can be resolved by Rust bindgen. */
+enum {
+ DRM_IOCTL_NOVA_GETPARAM = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GETPARAM,
+ struct drm_nova_getparam),
+ DRM_IOCTL_NOVA_GEM_CREATE = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_CREATE,
+ struct drm_nova_gem_create),
+ DRM_IOCTL_NOVA_GEM_INFO = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_INFO,
+ struct drm_nova_gem_info),
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __NOVA_DRM_H__ */
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index ab37e1d35c70..e0676ba99d37 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,7 +6,13 @@
* Sorted alphabetically.
*/
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
#include <kunit/test.h>
+#include <linux/auxiliary_bus.h>
#include <linux/blk-mq.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
@@ -55,3 +61,4 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
+const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET = FOP_UNSIGNED_OFFSET;
diff --git a/rust/helpers/auxiliary.c b/rust/helpers/auxiliary.c
new file mode 100644
index 000000000000..0db3860d774e
--- /dev/null
+++ b/rust/helpers/auxiliary.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/auxiliary_bus.h>
+
+void rust_helper_auxiliary_set_drvdata(struct auxiliary_device *adev, void *data)
+{
+ auxiliary_set_drvdata(adev, data);
+}
+
+void *rust_helper_auxiliary_get_drvdata(struct auxiliary_device *adev)
+{
+ return auxiliary_get_drvdata(adev);
+}
+
+void rust_helper_auxiliary_device_uninit(struct auxiliary_device *adev)
+{
+ return auxiliary_device_uninit(adev);
+}
+
+void rust_helper_auxiliary_device_delete(struct auxiliary_device *adev)
+{
+ return auxiliary_device_delete(adev);
+}
diff --git a/rust/helpers/drm.c b/rust/helpers/drm.c
new file mode 100644
index 000000000000..450b406c6f27
--- /dev/null
+++ b/rust/helpers/drm.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_gem.h>
+#include <drm/drm_vma_manager.h>
+
+#ifdef CONFIG_DRM
+
+void rust_helper_drm_gem_object_get(struct drm_gem_object *obj)
+{
+ drm_gem_object_get(obj);
+}
+
+void rust_helper_drm_gem_object_put(struct drm_gem_object *obj)
+{
+ drm_gem_object_put(obj);
+}
+
+__u64 rust_helper_drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
+{
+ return drm_vma_node_offset_addr(node);
+}
+
+#endif
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 1e7c84df7252..c228a5363212 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -7,6 +7,7 @@
* Sorted alphabetically.
*/
+#include "auxiliary.c"
#include "blk.c"
#include "bug.c"
#include "build_assert.c"
@@ -15,6 +16,7 @@
#include "cred.c"
#include "device.c"
#include "dma.c"
+#include "drm.c"
#include "err.c"
#include "fs.c"
#include "io.c"
diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
index 8ba22f911459..cd0e6bf2cc4d 100644
--- a/rust/helpers/pci.c
+++ b/rust/helpers/pci.c
@@ -16,3 +16,8 @@ resource_size_t rust_helper_pci_resource_len(struct pci_dev *pdev, int bar)
{
return pci_resource_len(pdev, bar);
}
+
+bool rust_helper_dev_is_pci(const struct device *dev)
+{
+ return dev_is_pci(dev);
+}
diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
index ab9b9f317301..82171233d12f 100644
--- a/rust/helpers/platform.c
+++ b/rust/helpers/platform.c
@@ -11,3 +11,8 @@ void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
{
platform_set_drvdata(pdev, data);
}
+
+bool rust_helper_dev_is_platform(const struct device *dev)
+{
+ return dev_is_platform(dev);
+}
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
new file mode 100644
index 000000000000..5c072960dee0
--- /dev/null
+++ b/rust/kernel/auxiliary.rs
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the auxiliary bus.
+//!
+//! C header: [`include/linux/auxiliary_bus.h`](srctree/include/linux/auxiliary_bus.h)
+
+use crate::{
+ bindings, container_of, device,
+ device_id::RawDeviceId,
+ driver,
+ error::{to_result, Result},
+ prelude::*,
+ str::CStr,
+ types::{ForeignOwnable, Opaque},
+ ThisModule,
+};
+use core::{
+ marker::PhantomData,
+ ptr::{addr_of_mut, NonNull},
+};
+
+/// An adapter for the registration of auxiliary drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ type RegType = bindings::auxiliary_driver;
+
+ unsafe fn register(
+ adrv: &Opaque<Self::RegType>,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ // SAFETY: It's safe to set the fields of `struct auxiliary_driver` on initialization.
+ unsafe {
+ (*adrv.get()).name = name.as_char_ptr();
+ (*adrv.get()).probe = Some(Self::probe_callback);
+ (*adrv.get()).remove = Some(Self::remove_callback);
+ (*adrv.get()).id_table = T::ID_TABLE.as_ptr();
+ }
+
+ // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
+ to_result(unsafe {
+ bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
+ })
+ }
+
+ unsafe fn unregister(adrv: &Opaque<Self::RegType>) {
+ // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
+ unsafe { bindings::auxiliary_driver_unregister(adrv.get()) }
+ }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+ extern "C" fn probe_callback(
+ adev: *mut bindings::auxiliary_device,
+ id: *const bindings::auxiliary_device_id,
+ ) -> kernel::ffi::c_int {
+ // SAFETY: The auxiliary bus only ever calls the probe callback with a valid pointer to a
+ // `struct auxiliary_device`.
+ //
+ // INVARIANT: `adev` is valid for the duration of `probe_callback()`.
+ let adev = unsafe { &*adev.cast::<Device<device::Core>>() };
+
+ // SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id`
+ // and does not add additional invariants, so it's safe to transmute.
+ let id = unsafe { &*id.cast::<DeviceId>() };
+ let info = T::ID_TABLE.info(id.index());
+
+ match T::probe(adev, info) {
+ Ok(data) => {
+ // Let the `struct auxiliary_device` own a reference of the driver's private data.
+ // SAFETY: By the type invariant `adev.as_raw` returns a valid pointer to a
+ // `struct auxiliary_device`.
+ unsafe { bindings::auxiliary_set_drvdata(adev.as_raw(), data.into_foreign()) };
+ }
+ Err(err) => return Error::to_errno(err),
+ }
+
+ 0
+ }
+
+ extern "C" fn remove_callback(adev: *mut bindings::auxiliary_device) {
+ // SAFETY: The auxiliary bus only ever calls the remove callback with a valid pointer to a
+ // `struct auxiliary_device`.
+ let ptr = unsafe { bindings::auxiliary_get_drvdata(adev) };
+
+ // SAFETY: `remove_callback` is only ever called after a successful call to
+ // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
+ // `KBox<T>` pointer created through `KBox::into_foreign`.
+ drop(unsafe { KBox::<T>::from_foreign(ptr) });
+ }
+}
+
+/// Declares a kernel module that exposes a single auxiliary driver.
+#[macro_export]
+macro_rules! module_auxiliary_driver {
+ ($($f:tt)*) => {
+ $crate::module_driver!(<T>, $crate::auxiliary::Adapter<T>, { $($f)* });
+ };
+}
+
+/// Abstraction for `bindings::auxiliary_device_id`.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::auxiliary_device_id);
+
+impl DeviceId {
+ /// Create a new [`DeviceId`] from name.
+ pub const fn new(modname: &'static CStr, name: &'static CStr) -> Self {
+ let name = name.as_bytes_with_nul();
+ let modname = modname.as_bytes_with_nul();
+
+ // TODO: Replace with `bindings::auxiliary_device_id::default()` once stabilized for
+ // `const`.
+ //
+ // SAFETY: FFI type is valid to be zero-initialized.
+ let mut id: bindings::auxiliary_device_id = unsafe { core::mem::zeroed() };
+
+ let mut i = 0;
+ while i < modname.len() {
+ id.name[i] = modname[i];
+ i += 1;
+ }
+
+ // Reuse the space of the NULL terminator.
+ id.name[i - 1] = b'.';
+
+ let mut j = 0;
+ while j < name.len() {
+ id.name[i] = name[j];
+ i += 1;
+ j += 1;
+ }
+
+ Self(id)
+ }
+}
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)`] wrapper of `auxiliary_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceId for DeviceId {
+ type RawType = bindings::auxiliary_device_id;
+
+ const DRIVER_DATA_OFFSET: usize =
+ core::mem::offset_of!(bindings::auxiliary_device_id, driver_data);
+
+ fn index(&self) -> usize {
+ self.0.driver_data
+ }
+}
+
+/// IdTable type for auxiliary drivers.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a auxiliary `IdTable` with its alias for modpost.
+#[macro_export]
+macro_rules! auxiliary_device_table {
+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+ const $table_name: $crate::device_id::IdArray<
+ $crate::auxiliary::DeviceId,
+ $id_info_type,
+ { $table_data.len() },
+ > = $crate::device_id::IdArray::new($table_data);
+
+ $crate::module_device_table!("auxiliary", $module_table_name, $table_name);
+ };
+}
+
+/// The auxiliary driver trait.
+///
+/// Drivers must implement this trait in order to get an auxiliary driver registered.
+pub trait Driver {
+ /// The type holding information about each device id supported by the driver.
+ ///
+ /// TODO: Use associated_type_defaults once stabilized:
+ ///
+ /// type IdInfo: 'static = ();
+ type IdInfo: 'static;
+
+ /// The table of device ids supported by the driver.
+ const ID_TABLE: IdTable<Self::IdInfo>;
+
+ /// Auxiliary driver probe.
+ ///
+ /// Called when an auxiliary device is matches a corresponding driver.
+ fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
+}
+
+/// The auxiliary device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct auxiliary_device`. The
+/// implementation abstracts the usage of an already existing C `struct auxiliary_device` within
+/// Rust code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct auxiliary_device` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+ Opaque<bindings::auxiliary_device>,
+ PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+ fn as_raw(&self) -> *mut bindings::auxiliary_device {
+ self.0.get()
+ }
+
+ /// Returns the auxiliary device' id.
+ pub fn id(&self) -> u32 {
+ // SAFETY: By the type invariant `self.as_raw()` is a valid pointer to a
+ // `struct auxiliary_device`.
+ unsafe { (*self.as_raw()).id }
+ }
+
+ /// Returns a reference to the parent [`device::Device`], if any.
+ pub fn parent(&self) -> Option<&device::Device> {
+ let ptr: *const Self = self;
+ // CAST: `Device<Ctx: DeviceContext>` types are transparent to each other.
+ let ptr: *const Device = ptr.cast();
+ // SAFETY: `ptr` was derived from `&self`.
+ let this = unsafe { &*ptr };
+
+ this.as_ref().parent()
+ }
+}
+
+impl Device {
+ extern "C" fn release(dev: *mut bindings::device) {
+ // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
+ // embedded in `struct auxiliary_device`.
+ let adev = unsafe { container_of!(dev, bindings::auxiliary_device, dev) }.cast_mut();
+
+ // SAFETY: `adev` points to the memory that has been allocated in `Registration::new`, via
+ // `KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)`.
+ let _ = unsafe { KBox::<Opaque<bindings::auxiliary_device>>::from_raw(adev.cast()) };
+ }
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for Device {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+ unsafe { bindings::get_device(self.as_ref().as_raw()) };
+ }
+
+ unsafe fn dec_ref(obj: NonNull<Self>) {
+ // CAST: `Self` a transparent wrapper of `bindings::auxiliary_device`.
+ let adev: *mut bindings::auxiliary_device = obj.cast().as_ptr();
+
+ // SAFETY: By the type invariant of `Self`, `adev` is a pointer to a valid
+ // `struct auxiliary_device`.
+ let dev = unsafe { addr_of_mut!((*adev).dev) };
+
+ // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+ unsafe { bindings::put_device(dev) }
+ }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &device::Device<Ctx> {
+ // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+ // `struct auxiliary_device`.
+ let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
+
+ // SAFETY: `dev` points to a valid `struct device`.
+ unsafe { device::Device::as_ref(dev) }
+ }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}
+
+/// The registration of an auxiliary device.
+///
+/// This type represents the registration of a [`struct auxiliary_device`]. When an instance of this
+/// type is dropped, its respective auxiliary device will be unregistered from the system.
+///
+/// # Invariants
+///
+/// `self.0` always holds a valid pointer to an initialized and registered
+/// [`struct auxiliary_device`].
+pub struct Registration(NonNull<bindings::auxiliary_device>);
+
+impl Registration {
+ /// Create and register a new auxiliary device.
+ pub fn new(parent: &device::Device, name: &CStr, id: u32, modname: &CStr) -> Result<Self> {
+ let boxed = KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)?;
+ let adev = boxed.get();
+
+ // SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization.
+ unsafe {
+ (*adev).dev.parent = parent.as_raw();
+ (*adev).dev.release = Some(Device::release);
+ (*adev).name = name.as_char_ptr();
+ (*adev).id = id;
+ }
+
+ // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`,
+ // which has not been initialized yet.
+ unsafe { bindings::auxiliary_device_init(adev) };
+
+ // Now that `adev` is initialized, leak the `Box`; the corresponding memory will be freed
+ // by `Device::release` when the last reference to the `struct auxiliary_device` is dropped.
+ let _ = KBox::into_raw(boxed);
+
+ // SAFETY:
+ // - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which has
+ // been initialialized,
+ // - `modname.as_char_ptr()` is a NULL terminated string.
+ let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) };
+ if ret != 0 {
+ // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`,
+ // which has been initialialized.
+ unsafe { bindings::auxiliary_device_uninit(adev) };
+
+ return Err(Error::from_errno(ret));
+ }
+
+ // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated successfully.
+ //
+ // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is called,
+ // which happens in `Self::drop()`.
+ Ok(Self(unsafe { NonNull::new_unchecked(adev) }))
+ }
+}
+
+impl Drop for Registration {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered
+ // `struct auxiliary_device`.
+ unsafe { bindings::auxiliary_device_delete(self.0.as_ptr()) };
+
+ // This drops the reference we acquired through `auxiliary_device_init()`.
+ //
+ // SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered
+ // `struct auxiliary_device`.
+ unsafe { bindings::auxiliary_device_uninit(self.0.as_ptr()) };
+ }
+}
+
+// SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
+unsafe impl Send for Registration {}
+
+// SAFETY: `Registration` does not expose any methods or fields that need synchronization.
+unsafe impl Sync for Registration {}
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 21b343a1dc4d..f08583fa39c9 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -9,7 +9,7 @@ use crate::{
str::CStr,
types::{ARef, Opaque},
};
-use core::{fmt, ptr};
+use core::{fmt, marker::PhantomData, ptr};
#[cfg(CONFIG_PRINTK)]
use crate::c_str;
@@ -42,7 +42,7 @@ use crate::c_str;
/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
/// dropped from any thread.
#[repr(transparent)]
-pub struct Device(Opaque<bindings::device>);
+pub struct Device<Ctx: DeviceContext = Normal>(Opaque<bindings::device>, PhantomData<Ctx>);
impl Device {
/// Creates a new reference-counted abstraction instance of an existing `struct device` pointer.
@@ -59,12 +59,33 @@ impl Device {
// SAFETY: By the safety requirements ptr is valid
unsafe { Self::as_ref(ptr) }.into()
}
+}
+impl<Ctx: DeviceContext> Device<Ctx> {
/// Obtain the raw `struct device *`.
pub(crate) fn as_raw(&self) -> *mut bindings::device {
self.0.get()
}
+ /// Returns a reference to the parent device, if any.
+ #[cfg_attr(not(CONFIG_AUXILIARY_BUS), expect(dead_code))]
+ pub(crate) fn parent(&self) -> Option<&Self> {
+ // SAFETY:
+ // - By the type invariant `self.as_raw()` is always valid.
+ // - The parent device is only ever set at device creation.
+ let parent = unsafe { (*self.as_raw()).parent };
+
+ if parent.is_null() {
+ None
+ } else {
+ // SAFETY:
+ // - Since `parent` is not NULL, it must be a valid pointer to a `struct device`.
+ // - `parent` is valid for the lifetime of `self`, since a `struct device` holds a
+ // reference count of its parent.
+ Some(unsafe { Self::as_ref(parent) })
+ }
+ }
+
/// Convert a raw C `struct device` pointer to a `&'a Device`.
///
/// # Safety
@@ -189,6 +210,11 @@ impl Device {
}
}
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
fn inc_ref(&self) {
@@ -225,16 +251,95 @@ pub struct Normal;
/// any of the bus callbacks, such as `probe()`.
pub struct Core;
+/// The [`Bound`] context is the context of a bus specific device reference when it is guaranteed to
+/// be bound for the duration of its lifetime.
+pub struct Bound;
+
mod private {
pub trait Sealed {}
+ impl Sealed for super::Bound {}
impl Sealed for super::Core {}
impl Sealed for super::Normal {}
}
+impl DeviceContext for Bound {}
impl DeviceContext for Core {}
impl DeviceContext for Normal {}
+/// # Safety
+///
+/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
+/// generic argument of `$device`.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __impl_device_context_deref {
+ (unsafe { $device:ident, $src:ty => $dst:ty }) => {
+ impl ::core::ops::Deref for $device<$src> {
+ type Target = $device<$dst>;
+
+ fn deref(&self) -> &Self::Target {
+ let ptr: *const Self = self;
+
+ // CAST: `$device<$src>` and `$device<$dst>` transparently wrap the same type by the
+ // safety requirement of the macro.
+ let ptr = ptr.cast::<Self::Target>();
+
+ // SAFETY: `ptr` was derived from `&self`.
+ unsafe { &*ptr }
+ }
+ }
+ };
+}
+
+/// Implement [`core::ops::Deref`] traits for allowed [`DeviceContext`] conversions of a (bus
+/// specific) device.
+///
+/// # Safety
+///
+/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
+/// generic argument of `$device`.
+#[macro_export]
+macro_rules! impl_device_context_deref {
+ (unsafe { $device:ident }) => {
+ // SAFETY: This macro has the exact same safety requirement as
+ // `__impl_device_context_deref!`.
+ ::kernel::__impl_device_context_deref!(unsafe {
+ $device,
+ $crate::device::Core => $crate::device::Bound
+ });
+
+ // SAFETY: This macro has the exact same safety requirement as
+ // `__impl_device_context_deref!`.
+ ::kernel::__impl_device_context_deref!(unsafe {
+ $device,
+ $crate::device::Bound => $crate::device::Normal
+ });
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __impl_device_context_into_aref {
+ ($src:ty, $device:tt) => {
+ impl ::core::convert::From<&$device<$src>> for $crate::types::ARef<$device> {
+ fn from(dev: &$device<$src>) -> Self {
+ (&**dev).into()
+ }
+ }
+ };
+}
+
+/// Implement [`core::convert::From`], such that all `&Device<Ctx>` can be converted to an
+/// `ARef<Device>`.
+#[macro_export]
+macro_rules! impl_device_context_into_aref {
+ ($device:tt) => {
+ ::kernel::__impl_device_context_into_aref!($crate::device::Core, $device);
+ ::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device);
+ };
+}
+
#[doc(hidden)]
#[macro_export]
macro_rules! dev_printk {
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index ddb1ce4a78d9..0f79a2ec9474 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -8,7 +8,7 @@
use crate::{
alloc::Flags,
bindings,
- device::Device,
+ device::{Bound, Device},
error::{Error, Result},
ffi::c_void,
prelude::*,
@@ -45,7 +45,7 @@ struct DevresInner<T> {
/// # Example
///
/// ```no_run
-/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}};
+/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}};
/// # use core::ops::Deref;
///
/// // See also [`pci::Bar`] for a real example.
@@ -83,13 +83,10 @@ struct DevresInner<T> {
/// unsafe { Io::from_raw(&self.0) }
/// }
/// }
-/// # fn no_run() -> Result<(), Error> {
-/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
-/// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) };
-///
+/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
/// // SAFETY: Invalid usage for example purposes.
/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
-/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
+/// let devres = Devres::new(dev, iomem, GFP_KERNEL)?;
///
/// let res = devres.try_access().ok_or(ENXIO)?;
/// res.write8(0x42, 0x0);
@@ -99,7 +96,7 @@ struct DevresInner<T> {
pub struct Devres<T>(Arc<DevresInner<T>>);
impl<T> DevresInner<T> {
- fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
+ fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
let inner = Arc::pin_init(
pin_init!( DevresInner {
dev: dev.into(),
@@ -171,7 +168,7 @@ impl<T> DevresInner<T> {
impl<T> Devres<T> {
/// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
/// returned `Devres` instance' `data` will be revoked once the device is detached.
- pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
+ pub fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Self> {
let inner = DevresInner::new(dev, data, flags)?;
Ok(Devres(inner))
@@ -179,11 +176,50 @@ impl<T> Devres<T> {
/// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
/// is owned by devres and will be revoked / dropped, once the device is detached.
- pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
+ pub fn new_foreign_owned(dev: &Device<Bound>, data: T, flags: Flags) -> Result {
let _ = DevresInner::new(dev, data, flags)?;
Ok(())
}
+
+ /// Obtain `&'a T`, bypassing the [`Revocable`].
+ ///
+ /// This method allows to directly obtain a `&'a T`, bypassing the [`Revocable`], by presenting
+ /// a `&'a Device<Bound>` of the same [`Device`] this [`Devres`] instance has been created with.
+ ///
+ /// # Errors
+ ///
+ /// An error is returned if `dev` does not match the same [`Device`] this [`Devres`] instance
+ /// has been created with.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # #![cfg(CONFIG_PCI)]
+ /// # use kernel::{device::Core, devres::Devres, pci};
+ ///
+ /// fn from_core(dev: &pci::Device<Core>, devres: Devres<pci::Bar<0x4>>) -> Result {
+ /// let bar = devres.access(dev.as_ref())?;
+ ///
+ /// let _ = bar.read32(0x0);
+ ///
+ /// // might_sleep()
+ ///
+ /// bar.write32(0x42, 0x0);
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a T> {
+ if self.0.dev.as_raw() != dev.as_raw() {
+ return Err(EINVAL);
+ }
+
+ // SAFETY: `dev` being the same device as the device this `Devres` has been created for
+ // proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as
+ // long as `dev` lives; `dev` lives at least as long as `self`.
+ Ok(unsafe { self.deref().access() })
+ }
}
impl<T> Deref for Devres<T> {
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 8cdc76043ee7..605e01e35715 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -6,7 +6,7 @@
use crate::{
bindings, build_assert,
- device::Device,
+ device::{Bound, Device},
error::code::*,
error::Result,
transmute::{AsBytes, FromBytes},
@@ -22,10 +22,10 @@ use crate::{
/// # Examples
///
/// ```
-/// use kernel::device::Device;
+/// # use kernel::device::{Bound, Device};
/// use kernel::dma::{attrs::*, CoherentAllocation};
///
-/// # fn test(dev: &Device) -> Result {
+/// # fn test(dev: &Device<Bound>) -> Result {
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
/// let c: CoherentAllocation<u64> =
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?;
@@ -143,16 +143,16 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
/// # Examples
///
/// ```
- /// use kernel::device::Device;
+ /// # use kernel::device::{Bound, Device};
/// use kernel::dma::{attrs::*, CoherentAllocation};
///
- /// # fn test(dev: &Device) -> Result {
+ /// # fn test(dev: &Device<Bound>) -> Result {
/// let c: CoherentAllocation<u64> =
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
/// # Ok::<(), Error>(()) }
/// ```
pub fn alloc_attrs(
- dev: &Device,
+ dev: &Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
@@ -194,7 +194,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
/// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the
/// `dma_attrs` is 0 by default.
pub fn alloc_coherent(
- dev: &Device,
+ dev: &Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
) -> Result<CoherentAllocation<T>> {
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
new file mode 100644
index 000000000000..74c9a3dd719e
--- /dev/null
+++ b/rust/kernel/drm/device.rs
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM device.
+//!
+//! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h)
+
+use crate::{
+ bindings, device, drm,
+ drm::driver::AllocImpl,
+ error::from_err_ptr,
+ error::Result,
+ prelude::*,
+ types::{ARef, AlwaysRefCounted, Opaque},
+};
+use core::{mem, ops::Deref, ptr, ptr::NonNull};
+
+#[cfg(CONFIG_DRM_LEGACY)]
+macro_rules! drm_legacy_fields {
+ ( $($field:ident: $val:expr),* $(,)? ) => {
+ bindings::drm_driver {
+ $( $field: $val ),*,
+ firstopen: None,
+ preclose: None,
+ dma_ioctl: None,
+ dma_quiescent: None,
+ context_dtor: None,
+ irq_handler: None,
+ irq_preinstall: None,
+ irq_postinstall: None,
+ irq_uninstall: None,
+ get_vblank_counter: None,
+ enable_vblank: None,
+ disable_vblank: None,
+ dev_priv_size: 0,
+ }
+ }
+}
+
+#[cfg(not(CONFIG_DRM_LEGACY))]
+macro_rules! drm_legacy_fields {
+ ( $($field:ident: $val:expr),* $(,)? ) => {
+ bindings::drm_driver {
+ $( $field: $val ),*
+ }
+ }
+}
+
+/// A typed DRM device with a specific `drm::Driver` implementation.
+///
+/// The device is always reference-counted.
+///
+/// # Invariants
+///
+/// `self.dev` is a valid instance of a `struct device`.
+#[repr(C)]
+#[pin_data]
+pub struct Device<T: drm::Driver> {
+ dev: Opaque<bindings::drm_device>,
+ #[pin]
+ data: T::Data,
+}
+
+impl<T: drm::Driver> Device<T> {
+ const VTABLE: bindings::drm_driver = drm_legacy_fields! {
+ load: None,
+ open: Some(drm::File::<T::File>::open_callback),
+ postclose: Some(drm::File::<T::File>::postclose_callback),
+ unload: None,
+ release: None,
+ master_set: None,
+ master_drop: None,
+ debugfs_init: None,
+ gem_create_object: T::Object::ALLOC_OPS.gem_create_object,
+ prime_handle_to_fd: T::Object::ALLOC_OPS.prime_handle_to_fd,
+ prime_fd_to_handle: T::Object::ALLOC_OPS.prime_fd_to_handle,
+ gem_prime_import: T::Object::ALLOC_OPS.gem_prime_import,
+ gem_prime_import_sg_table: T::Object::ALLOC_OPS.gem_prime_import_sg_table,
+ dumb_create: T::Object::ALLOC_OPS.dumb_create,
+ dumb_map_offset: T::Object::ALLOC_OPS.dumb_map_offset,
+ show_fdinfo: None,
+ fbdev_probe: None,
+
+ major: T::INFO.major,
+ minor: T::INFO.minor,
+ patchlevel: T::INFO.patchlevel,
+ name: T::INFO.name.as_char_ptr() as *mut _,
+ desc: T::INFO.desc.as_char_ptr() as *mut _,
+
+ driver_features: drm::driver::FEAT_GEM,
+ ioctls: T::IOCTLS.as_ptr(),
+ num_ioctls: T::IOCTLS.len() as i32,
+ fops: &Self::GEM_FOPS as _,
+ };
+
+ const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+
+ /// Create a new `drm::Device` for a `drm::Driver`.
+ pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
+ // SAFETY:
+ // - `VTABLE`, as a `const` is pinned to the read-only section of the compilation,
+ // - `dev` is valid by its type invarants,
+ let raw_drm: *mut Self = unsafe {
+ bindings::__drm_dev_alloc(
+ dev.as_raw(),
+ &Self::VTABLE,
+ mem::size_of::<Self>(),
+ mem::offset_of!(Self, dev),
+ )
+ }
+ .cast();
+ let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?;
+
+ // SAFETY: `raw_drm` is a valid pointer to `Self`.
+ let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) };
+
+ // SAFETY:
+ // - `raw_data` is a valid pointer to uninitialized memory.
+ // - `raw_data` will not move until it is dropped.
+ unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
+ // SAFETY: `__drm_dev_alloc()` was successful, hence `raw_drm` must be valid and the
+ // refcount must be non-zero.
+ unsafe { bindings::drm_dev_put(ptr::addr_of_mut!((*raw_drm.as_ptr()).dev).cast()) };
+ })?;
+
+ // SAFETY: The reference count is one, and now we take ownership of that reference as a
+ // `drm::Device`.
+ Ok(unsafe { ARef::from_raw(raw_drm) })
+ }
+
+ pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
+ self.dev.get()
+ }
+
+ /// # Safety
+ ///
+ /// `ptr` must be a valid pointer to a `struct device` embedded in `Self`.
+ unsafe fn from_drm_device(ptr: *const bindings::drm_device) -> *mut Self {
+ // SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
+ // `struct drm_device` embedded in `Self`.
+ unsafe { crate::container_of!(ptr, Self, dev) }.cast_mut()
+ }
+
+ /// Not intended to be called externally, except via declare_drm_ioctls!()
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
+ /// i.e. it must be ensured that the reference count of the C `struct drm_device` `ptr` points
+ /// to can't drop to zero, for the duration of this function call and the entire duration when
+ /// the returned reference exists.
+ ///
+ /// Additionally, callers must ensure that the `struct device`, `ptr` is pointing to, is
+ /// embedded in `Self`.
+ #[doc(hidden)]
+ pub unsafe fn as_ref<'a>(ptr: *const bindings::drm_device) -> &'a Self {
+ // SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
+ // `struct drm_device` embedded in `Self`.
+ let ptr = unsafe { Self::from_drm_device(ptr) };
+
+ // SAFETY: `ptr` is valid by the safety requirements of this function.
+ unsafe { &*ptr.cast() }
+ }
+}
+
+impl<T: drm::Driver> Deref for Device<T> {
+ type Target = T::Data;
+
+ fn deref(&self) -> &Self::Target {
+ &self.data
+ }
+}
+
+// SAFETY: DRM device objects are always reference counted and the get/put functions
+// satisfy the requirements.
+unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+ unsafe { bindings::drm_dev_get(self.as_raw()) };
+ }
+
+ unsafe fn dec_ref(obj: NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+ unsafe { bindings::drm_dev_put(obj.cast().as_ptr()) };
+ }
+}
+
+impl<T: drm::Driver> AsRef<device::Device> for Device<T> {
+ fn as_ref(&self) -> &device::Device {
+ // SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid,
+ // which is guaranteed by the type invariant.
+ unsafe { device::Device::as_ref((*self.as_raw()).dev) }
+ }
+}
+
+// SAFETY: A `drm::Device` can be released from any thread.
+unsafe impl<T: drm::Driver> Send for Device<T> {}
+
+// SAFETY: A `drm::Device` can be shared among threads because all immutable methods are protected
+// by the synchronization in `struct drm_device`.
+unsafe impl<T: drm::Driver> Sync for Device<T> {}
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
new file mode 100644
index 000000000000..acb638086131
--- /dev/null
+++ b/rust/kernel/drm/driver.rs
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM driver core.
+//!
+//! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h)
+
+use crate::{
+ bindings, device,
+ devres::Devres,
+ drm,
+ error::{to_result, Result},
+ prelude::*,
+ str::CStr,
+ types::ARef,
+};
+use macros::vtable;
+
+/// Driver use the GEM memory manager. This should be set for all modern drivers.
+pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM;
+
+/// Information data for a DRM Driver.
+pub struct DriverInfo {
+ /// Driver major version.
+ pub major: i32,
+ /// Driver minor version.
+ pub minor: i32,
+ /// Driver patchlevel version.
+ pub patchlevel: i32,
+ /// Driver name.
+ pub name: &'static CStr,
+ /// Driver description.
+ pub desc: &'static CStr,
+}
+
+/// Internal memory management operation set, normally created by memory managers (e.g. GEM).
+pub struct AllocOps {
+ pub(crate) gem_create_object: Option<
+ unsafe extern "C" fn(
+ dev: *mut bindings::drm_device,
+ size: usize,
+ ) -> *mut bindings::drm_gem_object,
+ >,
+ pub(crate) prime_handle_to_fd: Option<
+ unsafe extern "C" fn(
+ dev: *mut bindings::drm_device,
+ file_priv: *mut bindings::drm_file,
+ handle: u32,
+ flags: u32,
+ prime_fd: *mut core::ffi::c_int,
+ ) -> core::ffi::c_int,
+ >,
+ pub(crate) prime_fd_to_handle: Option<
+ unsafe extern "C" fn(
+ dev: *mut bindings::drm_device,
+ file_priv: *mut bindings::drm_file,
+ prime_fd: core::ffi::c_int,
+ handle: *mut u32,
+ ) -> core::ffi::c_int,
+ >,
+ pub(crate) gem_prime_import: Option<
+ unsafe extern "C" fn(
+ dev: *mut bindings::drm_device,
+ dma_buf: *mut bindings::dma_buf,
+ ) -> *mut bindings::drm_gem_object,
+ >,
+ pub(crate) gem_prime_import_sg_table: Option<
+ unsafe extern "C" fn(
+ dev: *mut bindings::drm_device,
+ attach: *mut bindings::dma_buf_attachment,
+ sgt: *mut bindings::sg_table,
+ ) -> *mut bindings::drm_gem_object,
+ >,
+ pub(crate) dumb_create: Option<
+ unsafe extern "C" fn(
+ file_priv: *mut bindings::drm_file,
+ dev: *mut bindings::drm_device,
+ args: *mut bindings::drm_mode_create_dumb,
+ ) -> core::ffi::c_int,
+ >,
+ pub(crate) dumb_map_offset: Option<
+ unsafe extern "C" fn(
+ file_priv: *mut bindings::drm_file,
+ dev: *mut bindings::drm_device,
+ handle: u32,
+ offset: *mut u64,
+ ) -> core::ffi::c_int,
+ >,
+}
+
+/// Trait for memory manager implementations. Implemented internally.
+pub trait AllocImpl: super::private::Sealed + drm::gem::IntoGEMObject {
+ /// The C callback operations for this memory manager.
+ const ALLOC_OPS: AllocOps;
+}
+
+/// The DRM `Driver` trait.
+///
+/// This trait must be implemented by drivers in order to create a `struct drm_device` and `struct
+/// drm_driver` to be registered in the DRM subsystem.
+#[vtable]
+pub trait Driver {
+ /// Context data associated with the DRM driver
+ type Data: Sync + Send;
+
+ /// The type used to manage memory for this driver.
+ type Object: AllocImpl;
+
+ /// The type used to represent a DRM File (client)
+ type File: drm::file::DriverFile;
+
+ /// Driver metadata
+ const INFO: DriverInfo;
+
+ /// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
+ const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
+}
+
+/// The registration type of a `drm::Device`.
+///
+/// Once the `Registration` structure is dropped, the device is unregistered.
+pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
+
+impl<T: Driver> Registration<T> {
+ /// Creates a new [`Registration`] and registers it.
+ fn new(drm: &drm::Device<T>, flags: usize) -> Result<Self> {
+ // SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`.
+ to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;
+
+ Ok(Self(drm.into()))
+ }
+
+ /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to
+ /// [`Devres`].
+ pub fn new_foreign_owned(
+ drm: &drm::Device<T>,
+ dev: &device::Device<device::Bound>,
+ flags: usize,
+ ) -> Result {
+ if drm.as_ref().as_raw() != dev.as_raw() {
+ return Err(EINVAL);
+ }
+
+ let reg = Registration::<T>::new(drm, flags)?;
+ Devres::new_foreign_owned(dev, reg, GFP_KERNEL)
+ }
+
+ /// Returns a reference to the `Device` instance for this registration.
+ pub fn device(&self) -> &drm::Device<T> {
+ &self.0
+ }
+}
+
+// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between
+// threads, hence it's safe to share it.
+unsafe impl<T: Driver> Sync for Registration<T> {}
+
+// SAFETY: Registration with and unregistration from the DRM subsystem can happen from any thread.
+unsafe impl<T: Driver> Send for Registration<T> {}
+
+impl<T: Driver> Drop for Registration<T> {
+ fn drop(&mut self) {
+ // SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
+ // `Registration` also guarantees the this `drm::Device` is actually registered.
+ unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
+ }
+}
diff --git a/rust/kernel/drm/file.rs b/rust/kernel/drm/file.rs
new file mode 100644
index 000000000000..b9527705e551
--- /dev/null
+++ b/rust/kernel/drm/file.rs
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM File objects.
+//!
+//! C header: [`include/linux/drm/drm_file.h`](srctree/include/linux/drm/drm_file.h)
+
+use crate::{bindings, drm, error::Result, prelude::*, types::Opaque};
+use core::marker::PhantomData;
+use core::pin::Pin;
+
+/// Trait that must be implemented by DRM drivers to represent a DRM File (a client instance).
+pub trait DriverFile {
+ /// The parent `Driver` implementation for this `DriverFile`.
+ type Driver: drm::Driver;
+
+ /// Open a new file (called when a client opens the DRM device).
+ fn open(device: &drm::Device<Self::Driver>) -> Result<Pin<KBox<Self>>>;
+}
+
+/// An open DRM File.
+///
+/// # Invariants
+///
+/// `self.0` is a valid instance of a `struct drm_file`.
+#[repr(transparent)]
+pub struct File<T: DriverFile>(Opaque<bindings::drm_file>, PhantomData<T>);
+
+impl<T: DriverFile> File<T> {
+ #[doc(hidden)]
+ /// Not intended to be called externally, except via declare_drm_ioctls!()
+ ///
+ /// # Safety
+ ///
+ /// `raw_file` must be a valid pointer to an open `struct drm_file`, opened through `T::open`.
+ pub unsafe fn as_ref<'a>(ptr: *mut bindings::drm_file) -> &'a File<T> {
+ // SAFETY: `raw_file` is valid by the safety requirements of this function.
+ unsafe { &*ptr.cast() }
+ }
+
+ pub(super) fn as_raw(&self) -> *mut bindings::drm_file {
+ self.0.get()
+ }
+
+ fn driver_priv(&self) -> *mut T {
+ // SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
+ unsafe { (*self.as_raw()).driver_priv }.cast()
+ }
+
+ /// Return a pinned reference to the driver file structure.
+ pub fn inner(&self) -> Pin<&T> {
+ // SAFETY: By the type invariant the pointer `self.as_raw()` points to a valid and opened
+ // `struct drm_file`, hence `driver_priv` has been properly initialized by `open_callback`.
+ unsafe { Pin::new_unchecked(&*(self.driver_priv())) }
+ }
+
+ /// The open callback of a `struct drm_file`.
+ pub(crate) extern "C" fn open_callback(
+ raw_dev: *mut bindings::drm_device,
+ raw_file: *mut bindings::drm_file,
+ ) -> core::ffi::c_int {
+ // SAFETY: A callback from `struct drm_driver::open` guarantees that
+ // - `raw_dev` is valid pointer to a `struct drm_device`,
+ // - the corresponding `struct drm_device` has been registered.
+ let drm = unsafe { drm::Device::as_ref(raw_dev) };
+
+ // SAFETY: `raw_file` is a valid pointer to a `struct drm_file`.
+ let file = unsafe { File::<T>::as_ref(raw_file) };
+
+ let inner = match T::open(drm) {
+ Err(e) => {
+ return e.to_errno();
+ }
+ Ok(i) => i,
+ };
+
+ // SAFETY: This pointer is treated as pinned, and the Drop guarantee is upheld in
+ // `postclose_callback()`.
+ let driver_priv = KBox::into_raw(unsafe { Pin::into_inner_unchecked(inner) });
+
+ // SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
+ unsafe { (*file.as_raw()).driver_priv = driver_priv.cast() };
+
+ 0
+ }
+
+ /// The postclose callback of a `struct drm_file`.
+ pub(crate) extern "C" fn postclose_callback(
+ _raw_dev: *mut bindings::drm_device,
+ raw_file: *mut bindings::drm_file,
+ ) {
+ // SAFETY: This reference won't escape this function
+ let file = unsafe { File::<T>::as_ref(raw_file) };
+
+ // SAFETY: `file.driver_priv` has been created in `open_callback` through `KBox::into_raw`.
+ let _ = unsafe { KBox::from_raw(file.driver_priv()) };
+ }
+}
+
+impl<T: DriverFile> super::private::Sealed for File<T> {}
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
new file mode 100644
index 000000000000..d8765e61c6c2
--- /dev/null
+++ b/rust/kernel/drm/gem/mod.rs
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM GEM API
+//!
+//! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h)
+
+use crate::{
+ alloc::flags::*,
+ bindings, drm,
+ drm::driver::{AllocImpl, AllocOps},
+ error::{to_result, Result},
+ prelude::*,
+ types::{ARef, AlwaysRefCounted, Opaque},
+};
+use core::{mem, ops::Deref, ptr::NonNull};
+
+/// GEM object functions, which must be implemented by drivers.
+pub trait BaseDriverObject<T: BaseObject>: Sync + Send + Sized {
+ /// Create a new driver data object for a GEM object of a given size.
+ fn new(dev: &drm::Device<T::Driver>, size: usize) -> impl PinInit<Self, Error>;
+
+ /// Open a new handle to an existing object, associated with a File.
+ fn open(
+ _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object,
+ _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>,
+ ) -> Result {
+ Ok(())
+ }
+
+ /// Close a handle to an existing object, associated with a File.
+ fn close(
+ _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object,
+ _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>,
+ ) {
+ }
+}
+
+/// Trait that represents a GEM object subtype
+pub trait IntoGEMObject: Sized + super::private::Sealed + AlwaysRefCounted {
+ /// Owning driver for this type
+ type Driver: drm::Driver;
+
+ /// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as
+ /// this owning object is valid.
+ fn as_raw(&self) -> *mut bindings::drm_gem_object;
+
+ /// Converts a pointer to a `struct drm_gem_object` into a reference to `Self`.
+ ///
+ /// # Safety
+ ///
+ /// - `self_ptr` must be a valid pointer to `Self`.
+ /// - The caller promises that holding the immutable reference returned by this function does
+ /// not violate rust's data aliasing rules and remains valid throughout the lifetime of `'a`.
+ unsafe fn as_ref<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self;
+}
+
+// SAFETY: All gem objects are refcounted.
+unsafe impl<T: IntoGEMObject> AlwaysRefCounted for T {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+ unsafe { bindings::drm_gem_object_get(self.as_raw()) };
+ }
+
+ unsafe fn dec_ref(obj: NonNull<Self>) {
+ // SAFETY: We either hold the only refcount on `obj`, or one of many - meaning that no one
+ // else could possibly hold a mutable reference to `obj` and thus this immutable reference
+ // is safe.
+ let obj = unsafe { obj.as_ref() }.as_raw();
+
+ // SAFETY:
+ // - The safety requirements guarantee that the refcount is non-zero.
+ // - We hold no references to `obj` now, making it safe for us to potentially deallocate it.
+ unsafe { bindings::drm_gem_object_put(obj) };
+ }
+}
+
+/// Trait which must be implemented by drivers using base GEM objects.
+pub trait DriverObject: BaseDriverObject<Object<Self>> {
+ /// Parent `Driver` for this object.
+ type Driver: drm::Driver;
+}
+
+extern "C" fn open_callback<T: BaseDriverObject<U>, U: BaseObject>(
+ raw_obj: *mut bindings::drm_gem_object,
+ raw_file: *mut bindings::drm_file,
+) -> core::ffi::c_int {
+ // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`.
+ let file = unsafe {
+ drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file)
+ };
+ // SAFETY: `open_callback` is specified in the AllocOps structure for `Object<T>`, ensuring that
+ // `raw_obj` is indeed contained within a `Object<T>`.
+ let obj = unsafe {
+ <<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::as_ref(raw_obj)
+ };
+
+ match T::open(obj, file) {
+ Err(e) => e.to_errno(),
+ Ok(()) => 0,
+ }
+}
+
+extern "C" fn close_callback<T: BaseDriverObject<U>, U: BaseObject>(
+ raw_obj: *mut bindings::drm_gem_object,
+ raw_file: *mut bindings::drm_file,
+) {
+ // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`.
+ let file = unsafe {
+ drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file)
+ };
+ // SAFETY: `close_callback` is specified in the AllocOps structure for `Object<T>`, ensuring
+ // that `raw_obj` is indeed contained within a `Object<T>`.
+ let obj = unsafe {
+ <<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::as_ref(raw_obj)
+ };
+
+ T::close(obj, file);
+}
+
+impl<T: DriverObject> IntoGEMObject for Object<T> {
+ type Driver = T::Driver;
+
+ fn as_raw(&self) -> *mut bindings::drm_gem_object {
+ self.obj.get()
+ }
+
+ unsafe fn as_ref<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self {
+ // SAFETY: `obj` is guaranteed to be in an `Object<T>` via the safety contract of this
+ // function
+ unsafe { &*crate::container_of!(self_ptr, Object<T>, obj) }
+ }
+}
+
+/// Base operations shared by all GEM object classes
+pub trait BaseObject: IntoGEMObject {
+ /// Returns the size of the object in bytes.
+ fn size(&self) -> usize {
+ // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `struct drm_gem_object`.
+ unsafe { (*self.as_raw()).size }
+ }
+
+ /// Creates a new handle for the object associated with a given `File`
+ /// (or returns an existing one).
+ fn create_handle(
+ &self,
+ file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>,
+ ) -> Result<u32> {
+ let mut handle: u32 = 0;
+ // SAFETY: The arguments are all valid per the type invariants.
+ to_result(unsafe {
+ bindings::drm_gem_handle_create(file.as_raw().cast(), self.as_raw(), &mut handle)
+ })?;
+ Ok(handle)
+ }
+
+ /// Looks up an object by its handle for a given `File`.
+ fn lookup_handle(
+ file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>,
+ handle: u32,
+ ) -> Result<ARef<Self>> {
+ // SAFETY: The arguments are all valid per the type invariants.
+ let ptr = unsafe { bindings::drm_gem_object_lookup(file.as_raw().cast(), handle) };
+ if ptr.is_null() {
+ return Err(ENOENT);
+ }
+
+ // SAFETY:
+ // - A `drm::Driver` can only have a single `File` implementation.
+ // - `file` uses the same `drm::Driver` as `Self`.
+ // - Therefore, we're guaranteed that `ptr` must be a gem object embedded within `Self`.
+ // - And we check if the pointer is null befoe calling as_ref(), ensuring that `ptr` is a
+ // valid pointer to an initialized `Self`.
+ let obj = unsafe { Self::as_ref(ptr) };
+
+ // SAFETY:
+ // - We take ownership of the reference of `drm_gem_object_lookup()`.
+ // - Our `NonNull` comes from an immutable reference, thus ensuring it is a valid pointer to
+ // `Self`.
+ Ok(unsafe { ARef::from_raw(obj.into()) })
+ }
+
+ /// Creates an mmap offset to map the object from userspace.
+ fn create_mmap_offset(&self) -> Result<u64> {
+ // SAFETY: The arguments are valid per the type invariant.
+ to_result(unsafe { bindings::drm_gem_create_mmap_offset(self.as_raw()) })?;
+
+ // SAFETY: The arguments are valid per the type invariant.
+ Ok(unsafe { bindings::drm_vma_node_offset_addr(&raw mut (*self.as_raw()).vma_node) })
+ }
+}
+
+impl<T: IntoGEMObject> BaseObject for T {}
+
+/// A base GEM object.
+///
+/// Invariants
+///
+/// - `self.obj` is a valid instance of a `struct drm_gem_object`.
+/// - `self.dev` is always a valid pointer to a `struct drm_device`.
+#[repr(C)]
+#[pin_data]
+pub struct Object<T: DriverObject + Send + Sync> {
+ obj: Opaque<bindings::drm_gem_object>,
+ dev: NonNull<drm::Device<T::Driver>>,
+ #[pin]
+ data: T,
+}
+
+impl<T: DriverObject> Object<T> {
+ /// The size of this object's structure.
+ pub const SIZE: usize = mem::size_of::<Self>();
+
+ const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs {
+ free: Some(Self::free_callback),
+ open: Some(open_callback::<T, Object<T>>),
+ close: Some(close_callback::<T, Object<T>>),
+ print_info: None,
+ export: None,
+ pin: None,
+ unpin: None,
+ get_sg_table: None,
+ vmap: None,
+ vunmap: None,
+ mmap: None,
+ status: None,
+ vm_ops: core::ptr::null_mut(),
+ evict: None,
+ rss: None,
+ };
+
+ /// Create a new GEM object.
+ pub fn new(dev: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> {
+ let obj: Pin<KBox<Self>> = KBox::pin_init(
+ try_pin_init!(Self {
+ obj: Opaque::new(bindings::drm_gem_object::default()),
+ data <- T::new(dev, size),
+ // INVARIANT: The drm subsystem guarantees that the `struct drm_device` will live
+ // as long as the GEM object lives.
+ dev: dev.into(),
+ }),
+ GFP_KERNEL,
+ )?;
+
+ // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above.
+ unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS };
+
+ // SAFETY: The arguments are all valid per the type invariants.
+ to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?;
+
+ // SAFETY: We never move out of `Self`.
+ let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) });
+
+ // SAFETY: `ptr` comes from `KBox::into_raw` and hence can't be NULL.
+ let ptr = unsafe { NonNull::new_unchecked(ptr) };
+
+ // SAFETY: We take over the initial reference count from `drm_gem_object_init()`.
+ Ok(unsafe { ARef::from_raw(ptr) })
+ }
+
+ /// Returns the `Device` that owns this GEM object.
+ pub fn dev(&self) -> &drm::Device<T::Driver> {
+ // SAFETY: The DRM subsystem guarantees that the `struct drm_device` will live as long as
+ // the GEM object lives, hence the pointer must be valid.
+ unsafe { self.dev.as_ref() }
+ }
+
+ fn as_raw(&self) -> *mut bindings::drm_gem_object {
+ self.obj.get()
+ }
+
+ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
+ // SAFETY: All of our objects are of type `Object<T>`.
+ let this = unsafe { crate::container_of!(obj, Self, obj) }.cast_mut();
+
+ // SAFETY: The C code only ever calls this callback with a valid pointer to a `struct
+ // drm_gem_object`.
+ unsafe { bindings::drm_gem_object_release(obj) };
+
+ // SAFETY: All of our objects are allocated via `KBox`, and we're in the
+ // free callback which guarantees this object has zero remaining references,
+ // so we can drop it.
+ let _ = unsafe { KBox::from_raw(this) };
+ }
+}
+
+impl<T: DriverObject> super::private::Sealed for Object<T> {}
+
+impl<T: DriverObject> Deref for Object<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.data
+ }
+}
+
+impl<T: DriverObject> AllocImpl for Object<T> {
+ const ALLOC_OPS: AllocOps = AllocOps {
+ gem_create_object: None,
+ prime_handle_to_fd: None,
+ prime_fd_to_handle: None,
+ gem_prime_import: None,
+ gem_prime_import_sg_table: None,
+ dumb_create: None,
+ dumb_map_offset: None,
+ };
+}
+
+pub(super) const fn create_fops() -> bindings::file_operations {
+ // SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations`
+ // zeroed.
+ let mut fops: bindings::file_operations = unsafe { core::mem::zeroed() };
+
+ fops.owner = core::ptr::null_mut();
+ fops.open = Some(bindings::drm_open);
+ fops.release = Some(bindings::drm_release);
+ fops.unlocked_ioctl = Some(bindings::drm_ioctl);
+ #[cfg(CONFIG_COMPAT)]
+ {
+ fops.compat_ioctl = Some(bindings::drm_compat_ioctl);
+ }
+ fops.poll = Some(bindings::drm_poll);
+ fops.read = Some(bindings::drm_read);
+ fops.llseek = Some(bindings::noop_llseek);
+ fops.mmap = Some(bindings::drm_gem_mmap);
+ fops.fop_flags = bindings::FOP_UNSIGNED_OFFSET;
+
+ fops
+}
diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs
new file mode 100644
index 000000000000..445639404fb7
--- /dev/null
+++ b/rust/kernel/drm/ioctl.rs
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM IOCTL definitions.
+//!
+//! C header: [`include/linux/drm/drm_ioctl.h`](srctree/include/linux/drm/drm_ioctl.h)
+
+use crate::ioctl;
+
+const BASE: u32 = uapi::DRM_IOCTL_BASE as u32;
+
+/// Construct a DRM ioctl number with no argument.
+#[allow(non_snake_case)]
+#[inline(always)]
+pub const fn IO(nr: u32) -> u32 {
+ ioctl::_IO(BASE, nr)
+}
+
+/// Construct a DRM ioctl number with a read-only argument.
+#[allow(non_snake_case)]
+#[inline(always)]
+pub const fn IOR<T>(nr: u32) -> u32 {
+ ioctl::_IOR::<T>(BASE, nr)
+}
+
+/// Construct a DRM ioctl number with a write-only argument.
+#[allow(non_snake_case)]
+#[inline(always)]
+pub const fn IOW<T>(nr: u32) -> u32 {
+ ioctl::_IOW::<T>(BASE, nr)
+}
+
+/// Construct a DRM ioctl number with a read-write argument.
+#[allow(non_snake_case)]
+#[inline(always)]
+pub const fn IOWR<T>(nr: u32) -> u32 {
+ ioctl::_IOWR::<T>(BASE, nr)
+}
+
+/// Descriptor type for DRM ioctls. Use the `declare_drm_ioctls!{}` macro to construct them.
+pub type DrmIoctlDescriptor = bindings::drm_ioctl_desc;
+
+/// This is for ioctl which are used for rendering, and require that the file descriptor is either
+/// for a render node, or if it’s a legacy/primary node, then it must be authenticated.
+pub const AUTH: u32 = bindings::drm_ioctl_flags_DRM_AUTH;
+
+/// This must be set for any ioctl which can change the modeset or display state. Userspace must
+/// call the ioctl through a primary node, while it is the active master.
+///
+/// Note that read-only modeset ioctl can also be called by unauthenticated clients, or when a
+/// master is not the currently active one.
+pub const MASTER: u32 = bindings::drm_ioctl_flags_DRM_MASTER;
+
+/// Anything that could potentially wreak a master file descriptor needs to have this flag set.
+///
+/// Current that’s only for the SETMASTER and DROPMASTER ioctl, which e.g. logind can call to
+/// force a non-behaving master (display compositor) into compliance.
+///
+/// This is equivalent to callers with the SYSADMIN capability.
+pub const ROOT_ONLY: u32 = bindings::drm_ioctl_flags_DRM_ROOT_ONLY;
+
+/// This is used for all ioctl needed for rendering only, for drivers which support render nodes.
+/// This should be all new render drivers, and hence it should be always set for any ioctl with
+/// `AUTH` set. Note though that read-only query ioctl might have this set, but have not set
+/// DRM_AUTH because they do not require authentication.
+pub const RENDER_ALLOW: u32 = bindings::drm_ioctl_flags_DRM_RENDER_ALLOW;
+
+/// Internal structures used by the `declare_drm_ioctls!{}` macro. Do not use directly.
+#[doc(hidden)]
+pub mod internal {
+ pub use bindings::drm_device;
+ pub use bindings::drm_file;
+ pub use bindings::drm_ioctl_desc;
+}
+
+/// Declare the DRM ioctls for a driver.
+///
+/// Each entry in the list should have the form:
+///
+/// `(ioctl_number, argument_type, flags, user_callback),`
+///
+/// `argument_type` is the type name within the `bindings` crate.
+/// `user_callback` should have the following prototype:
+///
+/// ```ignore
+/// fn foo(device: &kernel::drm::Device<Self>,
+/// data: &Opaque<uapi::argument_type>,
+/// file: &kernel::drm::File<Self::File>,
+/// ) -> Result<u32>
+/// ```
+/// where `Self` is the drm::drv::Driver implementation these ioctls are being declared within.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::declare_drm_ioctls! {
+/// (FOO_GET_PARAM, drm_foo_get_param, ioctl::RENDER_ALLOW, my_get_param_handler),
+/// }
+/// ```
+///
+#[macro_export]
+macro_rules! declare_drm_ioctls {
+ ( $(($cmd:ident, $struct:ident, $flags:expr, $func:expr)),* $(,)? ) => {
+ const IOCTLS: &'static [$crate::drm::ioctl::DrmIoctlDescriptor] = {
+ use $crate::uapi::*;
+ const _:() = {
+ let i: u32 = $crate::uapi::DRM_COMMAND_BASE;
+ // Assert that all the IOCTLs are in the right order and there are no gaps,
+ // and that the size of the specified type is correct.
+ $(
+ let cmd: u32 = $crate::macros::concat_idents!(DRM_IOCTL_, $cmd);
+ ::core::assert!(i == $crate::ioctl::_IOC_NR(cmd));
+ ::core::assert!(core::mem::size_of::<$crate::uapi::$struct>() ==
+ $crate::ioctl::_IOC_SIZE(cmd));
+ let i: u32 = i + 1;
+ )*
+ };
+
+ let ioctls = &[$(
+ $crate::drm::ioctl::internal::drm_ioctl_desc {
+ cmd: $crate::macros::concat_idents!(DRM_IOCTL_, $cmd) as u32,
+ func: {
+ #[allow(non_snake_case)]
+ unsafe extern "C" fn $cmd(
+ raw_dev: *mut $crate::drm::ioctl::internal::drm_device,
+ raw_data: *mut ::core::ffi::c_void,
+ raw_file: *mut $crate::drm::ioctl::internal::drm_file,
+ ) -> core::ffi::c_int {
+ // SAFETY:
+ // - The DRM core ensures the device lives while callbacks are being
+ // called.
+ // - The DRM device must have been registered when we're called through
+ // an IOCTL.
+ //
+ // FIXME: Currently there is nothing enforcing that the types of the
+ // dev/file match the current driver these ioctls are being declared
+ // for, and it's not clear how to enforce this within the type system.
+ let dev = $crate::drm::device::Device::as_ref(raw_dev);
+ // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we
+ // asserted above matches the size of this type, and all bit patterns of
+ // UAPI structs must be valid.
+ let data = unsafe {
+ &*(raw_data as *const $crate::types::Opaque<$crate::uapi::$struct>)
+ };
+ // SAFETY: This is just the DRM file structure
+ let file = unsafe { $crate::drm::File::as_ref(raw_file) };
+
+ match $func(dev, data, file) {
+ Err(e) => e.to_errno(),
+ Ok(i) => i.try_into()
+ .unwrap_or($crate::error::code::ERANGE.to_errno()),
+ }
+ }
+ Some($cmd)
+ },
+ flags: $flags,
+ name: $crate::c_str!(::core::stringify!($cmd)).as_char_ptr(),
+ }
+ ),*];
+ ioctls
+ };
+ };
+}
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
new file mode 100644
index 000000000000..1b82b6945edf
--- /dev/null
+++ b/rust/kernel/drm/mod.rs
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM subsystem abstractions.
+
+pub mod device;
+pub mod driver;
+pub mod file;
+pub mod gem;
+pub mod ioctl;
+
+pub use self::device::Device;
+pub use self::driver::Driver;
+pub use self::driver::DriverInfo;
+pub use self::driver::Registration;
+pub use self::file::File;
+
+pub(crate) mod private {
+ pub trait Sealed {}
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index de07aadd1ff5..ab0286857061 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -38,6 +38,8 @@ extern crate self as kernel;
pub use ffi;
pub mod alloc;
+#[cfg(CONFIG_AUXILIARY_BUS)]
+pub mod auxiliary;
#[cfg(CONFIG_BLOCK)]
pub mod block;
#[doc(hidden)]
@@ -48,6 +50,8 @@ pub mod device_id;
pub mod devres;
pub mod dma;
pub mod driver;
+#[cfg(CONFIG_DRM = "y")]
+pub mod drm;
pub mod error;
pub mod faux;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index c97d6d470b28..38fc8d5ffbf9 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -6,7 +6,7 @@
use crate::{
alloc::flags::*,
- bindings, device,
+ bindings, container_of, device,
device_id::RawDeviceId,
devres::Devres,
driver,
@@ -360,11 +360,13 @@ impl<const SIZE: usize> Deref for Bar<SIZE> {
}
}
-impl Device {
+impl<Ctx: device::DeviceContext> Device<Ctx> {
fn as_raw(&self) -> *mut bindings::pci_dev {
self.0.get()
}
+}
+impl Device {
/// Returns the PCI vendor ID.
pub fn vendor_id(&self) -> u16 {
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
@@ -388,7 +390,9 @@ impl Device {
// - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`.
Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
}
+}
+impl Device<device::Bound> {
/// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
/// can be performed on compile time for offsets (plus the requested type size) < SIZE.
pub fn iomap_region_sized<const SIZE: usize>(
@@ -422,25 +426,10 @@ impl Device<device::Core> {
}
}
-impl Deref for Device<device::Core> {
- type Target = Device;
-
- fn deref(&self) -> &Self::Target {
- let ptr: *const Self = self;
-
- // CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::pci_dev>`.
- let ptr = ptr.cast::<Device>();
-
- // SAFETY: `ptr` was derived from `&self`.
- unsafe { &*ptr }
- }
-}
-
-impl From<&Device<device::Core>> for ARef<Device> {
- fn from(dev: &Device<device::Core>) -> Self {
- (&**dev).into()
- }
-}
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
@@ -455,8 +444,8 @@ unsafe impl crate::types::AlwaysRefCounted for Device {
}
}
-impl AsRef<device::Device> for Device {
- fn as_ref(&self) -> &device::Device {
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &device::Device<Ctx> {
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct pci_dev`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
@@ -466,6 +455,26 @@ impl AsRef<device::Device> for Device {
}
}
+impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &Device<Ctx> {
+ type Error = kernel::error::Error;
+
+ fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
+ // SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
+ // `struct device`.
+ if !unsafe { bindings::dev_is_pci(dev.as_raw()) } {
+ return Err(EINVAL);
+ }
+
+ // SAFETY: We've just verified that the bus type of `dev` equals `bindings::pci_bus_type`,
+ // hence `dev` must be embedded in a valid `struct pci_dev` as guaranteed by the
+ // corresponding C code.
+ let pdev = unsafe { container_of!(dev.as_raw(), bindings::pci_dev, dev) };
+
+ // SAFETY: `pdev` is a valid pointer to a `struct pci_dev`.
+ Ok(unsafe { &*pdev.cast() })
+ }
+}
+
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
unsafe impl Send for Device {}
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 4917cb34e2fe..08849d92c074 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -5,18 +5,17 @@
//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
use crate::{
- bindings, device, driver,
+ bindings, container_of, device, driver,
error::{to_result, Result},
of,
prelude::*,
str::CStr,
- types::{ARef, ForeignOwnable, Opaque},
+ types::{ForeignOwnable, Opaque},
ThisModule,
};
use core::{
marker::PhantomData,
- ops::Deref,
ptr::{addr_of_mut, NonNull},
};
@@ -184,31 +183,16 @@ pub struct Device<Ctx: device::DeviceContext = device::Normal>(
PhantomData<Ctx>,
);
-impl Device {
+impl<Ctx: device::DeviceContext> Device<Ctx> {
fn as_raw(&self) -> *mut bindings::platform_device {
self.0.get()
}
}
-impl Deref for Device<device::Core> {
- type Target = Device;
-
- fn deref(&self) -> &Self::Target {
- let ptr: *const Self = self;
-
- // CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::platform_device>`.
- let ptr = ptr.cast::<Device>();
-
- // SAFETY: `ptr` was derived from `&self`.
- unsafe { &*ptr }
- }
-}
-
-impl From<&Device<device::Core>> for ARef<Device> {
- fn from(dev: &Device<device::Core>) -> Self {
- (&**dev).into()
- }
-}
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
@@ -223,8 +207,8 @@ unsafe impl crate::types::AlwaysRefCounted for Device {
}
}
-impl AsRef<device::Device> for Device {
- fn as_ref(&self) -> &device::Device {
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &device::Device<Ctx> {
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct platform_device`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
@@ -234,6 +218,26 @@ impl AsRef<device::Device> for Device {
}
}
+impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &Device<Ctx> {
+ type Error = kernel::error::Error;
+
+ fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
+ // SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
+ // `struct device`.
+ if !unsafe { bindings::dev_is_platform(dev.as_raw()) } {
+ return Err(EINVAL);
+ }
+
+ // SAFETY: We've just verified that the bus type of `dev` equals
+ // `bindings::platform_bus_type`, hence `dev` must be embedded in a valid
+ // `struct platform_device` as guaranteed by the corresponding C code.
+ let pdev = unsafe { container_of!(dev.as_raw(), bindings::platform_device, dev) };
+
+ // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
+ Ok(unsafe { &*pdev.cast() })
+ }
+}
+
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
unsafe impl Send for Device {}
diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
index 1e5a9d25c21b..db4aa46bb121 100644
--- a/rust/kernel/revocable.rs
+++ b/rust/kernel/revocable.rs
@@ -123,6 +123,34 @@ impl<T> Revocable<T> {
}
}
+ /// Tries to access the wrapped object and run a closure on it while the guard is held.
+ ///
+ /// This is a convenience method to run short non-sleepable code blocks while ensuring the
+ /// guard is dropped afterwards. [`Self::try_access`] carries the risk that the caller will
+ /// forget to explicitly drop that returned guard before calling sleepable code; this method
+ /// adds an extra safety to make sure it doesn't happen.
+ ///
+ /// Returns [`None`] if the object has been revoked and is therefore no longer accessible, or
+ /// the result of the closure wrapped in [`Some`]. If the closure returns a [`Result`] then the
+ /// return type becomes `Option<Result<>>`, which can be inconvenient. Users are encouraged to
+ /// define their own macro that turns the [`Option`] into a proper error code and flattens the
+ /// inner result into it if it makes sense within their subsystem.
+ pub fn try_access_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
+ self.try_access().map(|t| f(&*t))
+ }
+
+ /// Directly access the revocable wrapped object.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure this [`Revocable`] instance hasn't been revoked and won't be revoked
+ /// as long as the returned `&T` lives.
+ pub unsafe fn access(&self) -> &T {
+ // SAFETY: By the safety requirement of this function it is guaranteed that
+ // `self.data.get()` is a valid pointer to an instance of `T`.
+ unsafe { &*self.data.get() }
+ }
+
/// # Safety
///
/// Callers must ensure that there are no more concurrent users of the revocable object.
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 9d0471afc964..eee387727d1a 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -329,6 +329,14 @@ impl<T> Opaque<T> {
}
}
+ /// Creates a new zeroed opaque value.
+ pub const fn zeroed() -> Self {
+ Self {
+ value: UnsafeCell::new(MaybeUninit::zeroed()),
+ _pin: PhantomPinned,
+ }
+ }
+
/// Create an opaque pin-initializer from the given pin-initializer.
pub fn pin_init(slot: impl PinInit<T>) -> impl PinInit<Self> {
Self::ffi_init(|ptr: *mut T| {
diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h
index 76d3f103e764..1409441359f5 100644
--- a/rust/uapi/uapi_helper.h
+++ b/rust/uapi/uapi_helper.h
@@ -7,6 +7,8 @@
*/
#include <uapi/asm-generic/ioctl.h>
+#include <uapi/drm/drm.h>
+#include <uapi/drm/nova_drm.h>
#include <uapi/linux/mdio.h>
#include <uapi/linux/mii.h>
#include <uapi/linux/ethtool.h>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index cad52b7120b5..b1006ab4bc3c 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -82,6 +82,18 @@ config SAMPLE_RUST_DRIVER_FAUX
If unsure, say N.
+config SAMPLE_RUST_DRIVER_AUXILIARY
+ tristate "Auxiliary Driver"
+ depends on PCI
+ select AUXILIARY_BUS
+ help
+ This option builds the Rust auxiliary driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_driver_auxiliary.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index c6a2479f7d9c..6a466afd2a21 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) += rust_driver_auxiliary.o
rust_print-y := rust_print_main.o rust_print_events.o
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
new file mode 100644
index 000000000000..3e15e6d002bb
--- /dev/null
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust auxiliary driver sample (based on a PCI driver for QEMU's `pci-testdev`).
+//!
+//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
+
+use kernel::{
+ auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr,
+ InPlaceModule,
+};
+
+use pin_init::PinInit;
+
+const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
+const AUXILIARY_NAME: &CStr = c_str!("auxiliary");
+
+struct AuxiliaryDriver;
+
+kernel::auxiliary_device_table!(
+ AUX_TABLE,
+ MODULE_AUX_TABLE,
+ <AuxiliaryDriver as auxiliary::Driver>::IdInfo,
+ [(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())]
+);
+
+impl auxiliary::Driver for AuxiliaryDriver {
+ type IdInfo = ();
+
+ const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
+
+ fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
+ dev_info!(
+ adev.as_ref(),
+ "Probing auxiliary driver for auxiliary device with id={}\n",
+ adev.id()
+ );
+
+ ParentDriver::connect(adev)?;
+
+ let this = KBox::new(Self, GFP_KERNEL)?;
+
+ Ok(this.into())
+ }
+}
+
+struct ParentDriver {
+ _reg: [auxiliary::Registration; 2],
+}
+
+kernel::pci_device_table!(
+ PCI_TABLE,
+ MODULE_PCI_TABLE,
+ <ParentDriver as pci::Driver>::IdInfo,
+ [(
+ pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
+ ()
+ )]
+);
+
+impl pci::Driver for ParentDriver {
+ type IdInfo = ();
+
+ const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
+
+ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
+ let this = KBox::new(
+ Self {
+ _reg: [
+ auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?,
+ auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?,
+ ],
+ },
+ GFP_KERNEL,
+ )?;
+
+ Ok(this.into())
+ }
+}
+
+impl ParentDriver {
+ fn connect(adev: &auxiliary::Device) -> Result<()> {
+ let parent = adev.parent().ok_or(EINVAL)?;
+ let pdev: &pci::Device = parent.try_into()?;
+
+ dev_info!(
+ adev.as_ref(),
+ "Connect auxiliary {} with parent: VendorID={:#x}, DeviceID={:#x}\n",
+ adev.id(),
+ pdev.vendor_id(),
+ pdev.device_id()
+ );
+
+ Ok(())
+ }
+}
+
+#[pin_data]
+struct SampleModule {
+ #[pin]
+ _pci_driver: driver::Registration<pci::Adapter<ParentDriver>>,
+ #[pin]
+ _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
+}
+
+impl InPlaceModule for SampleModule {
+ fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ _pci_driver <- driver::Registration::new(MODULE_NAME, module),
+ _aux_driver <- driver::Registration::new(MODULE_NAME, module),
+ })
+ }
+}
+
+module! {
+ type: SampleModule,
+ name: "rust_driver_auxiliary",
+ author: "Danilo Krummrich",
+ description: "Rust auxiliary driver",
+ license: "GPL v2",
+}
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 2bb260aebc9e..15147e4401b2 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -83,12 +83,11 @@ impl pci::Driver for SampleDriver {
GFP_KERNEL,
)?;
- let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
-
+ let bar = drvdata.bar.access(pdev.as_ref())?;
dev_info!(
pdev.as_ref(),
"pci-testdev data-match count: {}\n",
- Self::testdev(info, &bar)?
+ Self::testdev(info, bar)?
);
Ok(drvdata.into())