* [PATCH 1/7] mm: rust: add pgprot_noncached helper
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 2/7] mm: rust: add VMA page offset helper Deborah Brouwer
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
From: Daniel Almeida <daniel.almeida@collabora.com>
Expose pgprot_noncached() to Rust code through a small helper wrapper.
Rust drivers that insert raw PFN mappings into userspace MMIO VMAs need
to derive an appropriate non-cached pgprot before calling helpers such
as vmf_insert_pfn_prot(). pgprot_noncached() is currently inaccessible
from Rust because it is provided through C helpers/macros rather than a
normal exported symbol.
Add a Rust helper wrapper so Rust MMIO mmap paths can construct
non-cached page protections for device mappings.
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
rust/helpers/mm.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rust/helpers/mm.c b/rust/helpers/mm.c
index b5540997bd20..22194d3196d2 100644
--- a/rust/helpers/mm.c
+++ b/rust/helpers/mm.c
@@ -48,3 +48,8 @@ __rust_helper void rust_helper_vma_end_read(struct vm_area_struct *vma)
{
vma_end_read(vma);
}
+
+__rust_helper pgprot_t rust_helper_pgprot_noncached(pgprot_t prot)
+{
+ return pgprot_noncached(prot);
+}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/7] mm: rust: add VMA page offset helper
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
2026-05-07 23:32 ` [PATCH 1/7] mm: rust: add pgprot_noncached helper Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 3/7] drm/rust: add File::device() helper Deborah Brouwer
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
From: Daniel Almeida <daniel.almeida@collabora.com>
Add VmaRef::pgoff() to expose vm_area_struct::vm_pgoff through the Rust
VMA abstractions.
vm_pgoff stores the mmap file offset in PAGE_SIZE units and can be used to
distinguish different mmap regions and dispatch mmap or fault handling
logic based on the requested offset.
This matches the existing start()/end()/flags() helpers and avoids
open-coded raw vm_area_struct access in Rust drivers.
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
rust/kernel/mm/virt.rs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/rust/kernel/mm/virt.rs b/rust/kernel/mm/virt.rs
index 63eb730b0b05..cb670e9d5fd9 100644
--- a/rust/kernel/mm/virt.rs
+++ b/rust/kernel/mm/virt.rs
@@ -92,6 +92,16 @@ pub fn end(&self) -> usize {
unsafe { (*self.as_ptr()).__bindgen_anon_1.__bindgen_anon_1.vm_end }
}
+ /// Returns the page offset of the virtual memory area.
+ ///
+ /// This is the offset in pages from the start of the file being mapped.
+ #[inline]
+ pub fn pgoff(&self) -> usize {
+ // SAFETY: By the type invariants, the caller holds at least the mmap read lock, so this
+ // access is not a data race.
+ unsafe { (*self.as_ptr()).vm_pgoff }
+ }
+
/// Zap pages in the given page range.
///
/// This clears page table mappings for the range at the leaf level, leaving all other page
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 3/7] drm/rust: add File::device() helper
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
2026-05-07 23:32 ` [PATCH 1/7] mm: rust: add pgprot_noncached helper Deborah Brouwer
2026-05-07 23:32 ` [PATCH 2/7] mm: rust: add VMA page offset helper Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 4/7] drm/rust: allow drivers to override file operations Deborah Brouwer
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
Add a helper to retrieve the DRM device associated with an open
`drm::file::File<T>`.
This wraps the common pattern of traversing `drm_file.minor->dev` and
returning the corresponding `drm::Device<T::Driver>`.
This is useful for DRM file operation callbacks that need access to the
owning DRM device from a `File<T>` reference, avoiding repeated open-coded
raw pointer traversal in drivers.
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
rust/kernel/drm/file.rs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/rust/kernel/drm/file.rs b/rust/kernel/drm/file.rs
index 10160601ce5a..5af28a9558e9 100644
--- a/rust/kernel/drm/file.rs
+++ b/rust/kernel/drm/file.rs
@@ -45,6 +45,18 @@ pub(super) fn as_raw(&self) -> *mut bindings::drm_file {
self.0.get()
}
+ /// Returns the DRM device associated with this open file.
+ pub fn device(&self) -> &drm::Device<T::Driver> {
+ // SAFETY: By the type invariant, `self.as_raw()` points to a valid open
+ // `struct drm_file`. DRM initializes `minor` for open DRM files, and
+ // `minor->dev` points to the registered DRM device associated with this
+ // file.
+ unsafe {
+ let minor = (*self.as_raw()).minor;
+ drm::Device::from_raw((*minor).dev)
+ }
+ }
+
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()
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 4/7] drm/rust: allow drivers to override file operations
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
` (2 preceding siblings ...)
2026-05-07 23:32 ` [PATCH 3/7] drm/rust: add File::device() helper Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 5/7] drm/tyr: add USER register page definitions Deborah Brouwer
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
Add a Driver::FOPS associated constant so Rust DRM drivers can override
or extend the default file operations table.
Use the driver's FOPS in drm::Device instead of always constructing the
default GEM file operations internally.
Also make drm::gem::create_fops() public so drivers can build on top of
the default GEM handlers while overriding selected operations such as
mmap.
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
rust/kernel/drm/device.rs | 4 ++--
rust/kernel/drm/driver.rs | 6 ++++++
rust/kernel/drm/gem/mod.rs | 3 ++-
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index adbafe8db54d..82760a9a426e 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -108,10 +108,10 @@ impl<T: drm::Driver> Device<T> {
driver_features: drm::driver::FEAT_GEM,
ioctls: T::IOCTLS.as_ptr(),
num_ioctls: T::IOCTLS.len() as i32,
- fops: &Self::GEM_FOPS,
+ fops: &Self::FOPS,
};
- const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+ const FOPS: bindings::file_operations = T::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>> {
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 5233bdebc9fc..79218c482fce 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -115,6 +115,12 @@ pub trait Driver {
/// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
+
+ /// File operations for this driver.
+ ///
+ /// Override to replace or extend the default GEM file operations. The default
+ /// provides the standard GEM mmap handler via [`drm::gem::create_fops`].
+ const FOPS: bindings::file_operations = drm::gem::create_fops();
}
/// The registration type of a `drm::Device`.
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
index 75acda7ba500..de7222ee0542 100644
--- a/rust/kernel/drm/gem/mod.rs
+++ b/rust/kernel/drm/gem/mod.rs
@@ -348,7 +348,8 @@ impl<T: DriverObject> AllocImpl for Object<T> {
};
}
-pub(super) const fn create_fops() -> bindings::file_operations {
+/// Returns the default GEM [`bindings::file_operations`] for a DRM driver.
+pub const fn create_fops() -> bindings::file_operations {
let mut fops: bindings::file_operations = pin_init::zeroed();
fops.owner = core::ptr::null_mut();
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 5/7] drm/tyr: add USER register page definitions
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
` (3 preceding siblings ...)
2026-05-07 23:32 ` [PATCH 4/7] drm/rust: allow drivers to override file operations Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 6/7] drm/tyr: store MMIO physical base address Deborah Brouwer
2026-05-07 23:32 ` [PATCH 7/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
Add the USER register page definition and the LATEST_FLUSH register used
to track GPU cache flush completion.
The USER register page is read-only and user-accessible. Tyr will use
these definitions to support the Panthor userspace-MMIO mmap interface,
which exposes the flush tracking register directly to userspace.
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
drivers/gpu/drm/tyr/regs.rs | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs
index 562023e5df2f..98d5625f5bb1 100644
--- a/drivers/gpu/drm/tyr/regs.rs
+++ b/drivers/gpu/drm/tyr/regs.rs
@@ -1633,6 +1633,24 @@ fn from(sh: PtwShareability) -> Self {
}
}
+/// This register corresponds to the USER register page.
+///
+/// This register page is read-only and user-mode accessible.
+/// It exposes cache flush tracking to userspace.
+pub(crate) mod user {
+ use kernel::register;
+
+ register! {
+ /// Latest flush ID register. Read-only.
+ pub(crate) LATEST_FLUSH(u32) @ 0x10000 {
+ /// Incremented when a full GPU cache clean-and-invalidate completes.
+ 23:0 flush_id;
+ /// Indicates that a cache flush operation is currently pending.
+ 31:31 active => bool;
+ }
+ }
+}
+
/// This module corresponds to the DOORBELL_BLOCK_n[0-63] register pages.
pub(crate) mod doorbell_block {
use kernel::register;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 6/7] drm/tyr: store MMIO physical base address
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
` (4 preceding siblings ...)
2026-05-07 23:32 ` [PATCH 5/7] drm/tyr: add USER register page definitions Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
2026-05-07 23:32 ` [PATCH 7/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
Store the physical base address of the GPU MMIO region in
TyrDrmDeviceData.
This will be used by the Panthor userspace-MMIO mmap path to derive the
physical PFNs inserted into userspace VMAs for USER register page
mappings.
Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
drivers/gpu/drm/tyr/driver.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index e20a5978eed6..2ba0a22a225b 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -19,7 +19,8 @@
drm::ioctl,
io::{
poll,
- Io, //
+ Io,
+ PhysAddr, //
},
new_mutex,
of,
@@ -60,6 +61,9 @@ pub(crate) struct TyrPlatformDriverData {
pub(crate) struct TyrDrmDeviceData {
pub(crate) pdev: ARef<platform::Device>,
+ /// Physical base address of the MMIO region.
+ pub(crate) mmio_phys_addr: PhysAddr,
+
#[pin]
clks: Mutex<Clocks>,
@@ -119,6 +123,7 @@ fn probe(
let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?;
let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
+ let mmio_phys_addr = pdev.resource_by_index(0).ok_or(ENODEV)?.start();
let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?;
@@ -140,6 +145,7 @@ fn probe(
let data = try_pin_init!(TyrDrmDeviceData {
pdev: platform.clone(),
+ mmio_phys_addr,
clks <- new_mutex!(Clocks {
core: core_clk,
stacks: stacks_clk,
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 7/7] drm/tyr: add userspace MMIO mmap support
2026-05-07 23:32 [PATCH 0/7] drm/tyr: add userspace MMIO mmap support Deborah Brouwer
` (5 preceding siblings ...)
2026-05-07 23:32 ` [PATCH 6/7] drm/tyr: store MMIO physical base address Deborah Brouwer
@ 2026-05-07 23:32 ` Deborah Brouwer
6 siblings, 0 replies; 8+ messages in thread
From: Deborah Brouwer @ 2026-05-07 23:32 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, David Airlie,
Simona Vetter, Daniel Almeida
Cc: linux-mm, rust-for-linux, linux-kernel, dri-devel,
boris.brezillon, beata.michalska, lyude, acourbot, work,
alvin.sun, Laura Nao, Deborah Brouwer
Add a Tyr-specific mmap handler for Panthor-style userspace MMIO mappings.
Build Tyr's file operations from the default GEM fops and override only
the mmap callback. The callback intercepts the reserved user MMIO mmap
range and falls back to drm_gem_mmap() for normal GEM mappings.
For now, support the USER_FLUSH_ID mapping. The mapping must be shared,
read-only, non-executable, and exactly one page. Faults insert the page
frame number (PFN) for the LATEST_FLUSH MMIO register with a non-cached
page protection.
Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
drivers/gpu/drm/tyr/driver.rs | 4 +
drivers/gpu/drm/tyr/mmap.rs | 247 ++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/tyr/tyr.rs | 1 +
3 files changed, 252 insertions(+)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 2ba0a22a225b..0d2965e3c9ad 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 or MIT
use kernel::{
+ bindings,
clk::{
Clk,
OptionalClk, //
@@ -42,6 +43,7 @@
gem::BoData,
gpu,
gpu::GpuInfo,
+ mmap::FOPS,
regs::gpu_control::*, //
};
@@ -196,6 +198,8 @@ impl drm::Driver for TyrDrmDriver {
kernel::declare_drm_ioctls! {
(PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query),
}
+
+ const FOPS: bindings::file_operations = FOPS;
}
struct Clocks {
diff --git a/drivers/gpu/drm/tyr/mmap.rs b/drivers/gpu/drm/tyr/mmap.rs
new file mode 100644
index 000000000000..5698656faa8a
--- /dev/null
+++ b/drivers/gpu/drm/tyr/mmap.rs
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! Memory-mapping support for the Tyr DRM driver.
+//!
+//! This module implements Tyr's DRM `mmap` file operation. Normal GEM buffer
+//! mappings are handled by the standard DRM GEM mmap path
+//! (`bindings::drm_gem_mmap`), while special Panthor userspace-MMIO mappings
+//! are handled directly by Tyr.
+//!
+//! # Userspace MMIO mappings
+//!
+//! Panthor defines reserved DRM mmap offsets for exposing selected GPU MMIO
+//! pages directly to userspace. These are special uAPI marker offsets, not
+//! hardware register offsets.
+//!
+//! Currently the only supported userspace-MMIO mapping is the flush-ID register
+//! page ([`DRM_PANTHOR_USER_FLUSH_ID_MMIO_OFFSET`]). This mapping exposes a
+//! read-only, non-cached MMIO page that userspace can poll to observe GPU cache
+//! flush completion without requiring a kernel round-trip.
+//!
+//! The VMA for this mapping is configured in [`mmap_user_mmio`]. Physical MMIO
+//! pages are inserted lazily through [`VM_OPS`] and [`vm_fault_handler`] when
+//! userspace first accesses the mapping.
+
+// TODO: When runtime PM support is added, userspace-MMIO mappings must be
+// integrated with suspend/resume. Accessing this MMIO page while the register
+// bank clock is disabled can hang the device. Runtime suspend will need to invalidate
+// existing userspace-MMIO mappings, and mmap/fault handling while suspended
+// should expose a dummy zero page instead of the real MMIO PFN.
+
+use kernel::{
+ bindings,
+ drm,
+ error::code::*,
+ io::register::Register,
+ mm::virt::{
+ flags as vma_flags,
+ VmaNew, //
+ },
+ page::{
+ PAGE_SHIFT,
+ PAGE_SIZE, //
+ },
+ prelude::*, //
+};
+
+use crate::{
+ driver::TyrDrmDevice,
+ file::TyrDrmFileData,
+ regs::user::LATEST_FLUSH, //
+};
+
+// Reserved Panthor mmap offsets for userspace-MMIO mappings.
+//
+// These offsets are special uAPI marker values used to distinguish
+// userspace-MMIO mappings from normal GEM object mappings in the DRM mmap
+// path. They must match uapi/drm/panthor_drm.h exactly.
+//
+// These are not GPU register offsets or physical MMIO addresses.
+//
+// Tyr currently supports only the 64-bit Panthor mmap offsets. Panthor also
+// defines 32-bit compat offsets, but that path is not implemented yet.
+const DRM_PANTHOR_USER_MMIO_OFFSET_64BIT: u64 = 1u64 << 56;
+const DRM_PANTHOR_USER_FLUSH_ID_MMIO_OFFSET: u64 = DRM_PANTHOR_USER_MMIO_OFFSET_64BIT;
+
+// `vm_pgoff` stores mmap offsets in PAGE_SIZE units rather than byte offsets,
+// so convert the Panthor uAPI mmap marker offsets into page offsets before
+// comparing them against `vm_pgoff`.
+const DRM_PANTHOR_USER_MMIO_PGOFF_64BIT: u64 = DRM_PANTHOR_USER_MMIO_OFFSET_64BIT >> PAGE_SHIFT;
+const DRM_PANTHOR_USER_FLUSH_ID_MMIO_PGOFF: u64 =
+ DRM_PANTHOR_USER_FLUSH_ID_MMIO_OFFSET >> PAGE_SHIFT;
+
+pub(crate) const FOPS: bindings::file_operations = {
+ let mut fops: bindings::file_operations = drm::gem::create_fops();
+ fops.mmap = Some(mmap_callback);
+ fops
+};
+
+/// C-callable mmap file operation entry point for Tyr.
+///
+/// Handles the special Panthor flush-ID mmap offset directly and falls back to
+/// [`bindings::drm_gem_mmap`] for normal GEM object mappings.
+///
+/// # Safety
+///
+/// `filp` and `vma` must be valid pointers provided by the kernel mmap path.
+unsafe extern "C" fn mmap_callback(
+ filp: *mut bindings::file,
+ vma: *mut bindings::vm_area_struct,
+) -> core::ffi::c_int {
+ // SAFETY: `filp` is a valid DRM file provided by the mmap callback. DRM open
+ // initializes `filp->private_data` to point to the associated `struct drm_file`,
+ // and the object remains valid until the file is released.
+ let drm_file_ptr = unsafe { (*filp).private_data.cast::<bindings::drm_file>() };
+ if drm_file_ptr.is_null() {
+ return bindings::EINVAL as i32;
+ }
+
+ // SAFETY: `drm_file_ptr` is a valid pointer to the DRM file associated with
+ // this open file descriptor, obtained from `filp->private_data`.
+ let drm_file = unsafe { drm::file::File::<TyrDrmFileData>::from_raw(drm_file_ptr) };
+
+ let device = drm_file.device();
+
+ // SAFETY: This is called from the DRM mmap file operation, so `vma` is
+ // undergoing initial setup for the duration of the callback.
+ let vma_new = unsafe { VmaNew::from_raw(vma) };
+
+ match mmap_user_mmio(device, vma_new) {
+ Some(Ok(())) => 0,
+ Some(Err(e)) => e.to_errno(),
+ // Fall back to the standard DRM GEM mmap path for non-userspace-MMIO
+ // mappings. This matches the default GEM fops created by
+ // `drm::gem::create_fops()`, whose `.mmap` callback is
+ // `bindings::drm_gem_mmap`.
+ //
+ // SAFETY: Called from mmap context with valid `filp` and `vma`.
+ None => unsafe { bindings::drm_gem_mmap(filp, vma) },
+ }
+}
+
+/// Handles Panthor userspace-MMIO mmap requests.
+///
+/// If the VMA offset does not refer to the Panthor flush-ID mmap offset, returns
+/// [`None`] so the caller can fall back to the normal DRM GEM mmap path.
+///
+/// If the offset refers to a userspace-MMIO mapping, validates the requested
+/// mapping parameters and configures the VMA for MMIO access. Returns
+/// `Some(Err(...))` for invalid MMIO mappings and `Some(Ok(()))` on success.
+fn mmap_user_mmio(device: &TyrDrmDevice, vma: &VmaNew) -> Option<Result> {
+ let pgoff = vma.pgoff() as u64;
+ if pgoff < DRM_PANTHOR_USER_MMIO_PGOFF_64BIT {
+ return None;
+ }
+
+ if pgoff != DRM_PANTHOR_USER_FLUSH_ID_MMIO_PGOFF {
+ return Some(Err(EINVAL));
+ }
+
+ if (vma.flags() & vma_flags::SHARED) == 0 {
+ return Some(Err(EINVAL));
+ }
+
+ if vma.end() - vma.start() != PAGE_SIZE {
+ return Some(Err(EINVAL));
+ }
+
+ if (vma.flags() & (vma_flags::WRITE | vma_flags::EXEC)) != 0 {
+ return Some(Err(EINVAL));
+ }
+
+ if vma.try_clear_maywrite().is_err() {
+ return Some(Err(EINVAL));
+ }
+
+ vma.set_io();
+ vma.set_dontcopy();
+ vma.set_dontexpand();
+ vma.set_dontdump();
+
+ let vma_ptr = vma.as_ptr();
+
+ // SAFETY: The VmaNew invariant guarantees the VMA pointer is valid and that the
+ // mapping is undergoing initial setup, so mutating VMA fields is safe.
+ // vm_private_data stores the DRM device pointer for the fault handler; the DRM
+ // device outlives the file and associated VMA.
+ unsafe {
+ // This VMA maps raw MMIO PFNs rather than normal memory pages.
+ (*vma_ptr).__bindgen_anon_2.vm_flags |= vma_flags::PFNMAP | vma_flags::NORESERVE;
+
+ // Store the device pointer so the fault handler can recover it later.
+ (*vma_ptr).vm_private_data = core::ptr::from_ref(device).cast_mut().cast();
+
+ // Install the VMA operations for handling MMIO page faults.
+ (*vma_ptr).vm_ops = core::ptr::from_ref(&VM_OPS);
+ }
+
+ Some(Ok(()))
+}
+
+static VM_OPS: bindings::vm_operations_struct = bindings::vm_operations_struct {
+ fault: Some(vm_fault_handler),
+ // SAFETY: Zero-initializing vm_operations_struct is valid because unset
+ // callback fields are represented as NULL function pointers.
+ ..unsafe { core::mem::zeroed() }
+};
+
+/// Page fault handler for userspace-MMIO VMAs.
+///
+/// Used for lazy insertion of the MMIO page into the VMA on first access.
+///
+/// Handles first access to the userspace-MMIO mapping by inserting the
+/// corresponding physical MMIO page into the VMA with non-cached protections
+/// via [`bindings::vmf_insert_pfn_prot`].
+///
+/// # Safety
+///
+/// Must only be called by the kernel fault handling machinery with a valid
+/// `vmf` pointer.
+unsafe extern "C" fn vm_fault_handler(vmf: *mut bindings::vm_fault) -> bindings::vm_fault_t {
+ const VM_FAULT_SIGBUS: bindings::vm_fault_t = bindings::vm_fault_reason_VM_FAULT_SIGBUS;
+
+ // SAFETY: `vmf` is a valid fault descriptor provided by the kernel for this
+ // vm_ops.fault callback. Its embedded `vma` pointer and fault address are
+ // initialized for the duration of the fault handling operation.
+ let (vma, address) = unsafe { ((*vmf).__bindgen_anon_1.vma, (*vmf).__bindgen_anon_1.address) };
+
+ // SAFETY: `vma` comes from a valid fault descriptor, and `vm_private_data`
+ // was initialized by mmap_user_mmio() to store the associated Tyr DRM device
+ // pointer for this VMA.
+ let ddev_ptr = unsafe { (*vma).vm_private_data as *const TyrDrmDevice };
+ if ddev_ptr.is_null() {
+ return VM_FAULT_SIGBUS;
+ }
+
+ // SAFETY: ddev_ptr is non-null, properly aligned, and was originally a shared reference,
+ // so the pointee is valid for the lifetime of the device.
+ let ddev = unsafe { &*ddev_ptr };
+
+ // SAFETY: vma is valid (obtained from a valid vmf).
+ let pgoff = unsafe { (*vma).vm_pgoff } as u64;
+
+ if pgoff != DRM_PANTHOR_USER_FLUSH_ID_MMIO_PGOFF {
+ return VM_FAULT_SIGBUS;
+ }
+
+ // `vm_start` and `vm_end` are behind bindgen-generated anonymous fields.
+ // SAFETY: vma is valid (obtained from a valid vmf).
+ let vm_start = unsafe { (*vma).__bindgen_anon_1.__bindgen_anon_1.vm_start };
+ // SAFETY: vma is valid (obtained from a valid vmf).
+ let vm_end = unsafe { (*vma).__bindgen_anon_1.__bindgen_anon_1.vm_end };
+
+ if address < vm_start || address >= vm_end {
+ return VM_FAULT_SIGBUS;
+ }
+
+ let phys_addr = ddev.mmio_phys_addr + LATEST_FLUSH::OFFSET as u64;
+ let pfn = (phys_addr >> PAGE_SHIFT) as usize;
+
+ // SAFETY: vma is valid (obtained from a valid vmf).
+ let pgprot = unsafe { bindings::pgprot_noncached((*vma).vm_page_prot) };
+
+ // SAFETY: vma, address, pfn, and pgprot are all valid.
+ let ret = unsafe { bindings::vmf_insert_pfn_prot(vma, address, pfn, pgprot) };
+
+ ret as bindings::vm_fault_t
+}
diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs
index 9432ddd6b5b8..f1c7c46dfc3e 100644
--- a/drivers/gpu/drm/tyr/tyr.rs
+++ b/drivers/gpu/drm/tyr/tyr.rs
@@ -11,6 +11,7 @@
mod file;
mod gem;
mod gpu;
+mod mmap;
mod regs;
kernel::module_platform_driver! {
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread