Linux Documentation
 help / color / mirror / Atom feed
* [PATCH 00/12] gpu: nova-core: add PRAMIN window support
@ 2026-08-05  5:44 Eliot Courtney
  2026-08-05  5:44 ` [PATCH 01/12] rust: io: add Region::try_subregion Eliot Courtney
                   ` (11 more replies)
  0 siblings, 12 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

This series adds support for the PRAMIN window, plus some documentation.
Some of these patches were originally written by Joel Fernandes [1]. I
have updated them with changelog notes in each patch. For example, I
updated them for the newer IO machinery from Gary Guo.

This series adds a Pramin structure which deals with updating the PRAMIN
window and handing out typed MMIO views. The PRAMIN window is a method
that lets arbitrary VRAM be written over BAR0 by configuring a 1 MiB
window through GPU registers. That 1 MiB window can point anywhere in
VRAM. This is useful for bootstrapping e.g. GPU side page tables.

The Pramin structure hands out typed MMIO views to do writes through
rather than letting callers write directly through it. A follow up
series will add support for GPU side page table entry writing through
this mechanism. This abstraction is important for later since we will
eventually want to write the PTEs via BAR2 not PRAMIN - using a view
that we grab from the Pramin structure can abstract this out.

[1]: https://lore.kernel.org/all/20260518180342.2387845-1-joelagnelf@nvidia.com/

This is based on drm-rust-next.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Eliot Courtney (6):
      rust: io: add Region::try_subregion
      rust: num: reject Bounded::shr overshifts at build time
      rust: num: add Bounded::shr_exact
      gpu: nova-core: mm: Implement Alignable and Debug for VramAddress
      gpu: nova-core: mm: Add the memory management HAL
      gpu: nova-core: Add self-test assertion macros and config option

Joel Fernandes (6):
      gpu: nova-core: mm: Add VramAddress type
      gpu: nova-core: mm: Add PRAMIN window registers
      gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
      docs: gpu: nova-core: Document the PRAMIN aperture mechanism
      gpu: nova-core: mm: Add GpuMm centralized memory manager
      gpu: nova-core: mm: Add PRAMIN aperture self-tests

 Documentation/gpu/nova/core/pramin.rst   | 128 ++++++++++++++
 Documentation/gpu/nova/index.rst         |   1 +
 drivers/gpu/nova-core/Kconfig            |   9 +
 drivers/gpu/nova-core/driver.rs          |   3 +
 drivers/gpu/nova-core/gpu.rs             |  30 +++-
 drivers/gpu/nova-core/gsp/commands.rs    |   4 +
 drivers/gpu/nova-core/gsp/fw/commands.rs |   5 +
 drivers/gpu/nova-core/mm.rs              | 163 ++++++++++++++++++
 drivers/gpu/nova-core/mm/hal.rs          |  56 ++++++
 drivers/gpu/nova-core/mm/hal/gb100.rs    |  35 ++++
 drivers/gpu/nova-core/mm/hal/gh100.rs    |  35 ++++
 drivers/gpu/nova-core/mm/hal/tu102.rs    |  37 ++++
 drivers/gpu/nova-core/mm/pramin.rs       | 285 +++++++++++++++++++++++++++++++
 drivers/gpu/nova-core/mm/regs.rs         |  57 +++++++
 drivers/gpu/nova-core/nova_core.rs       |   3 +
 drivers/gpu/nova-core/selftest.rs        |  64 +++++++
 rust/kernel/io.rs                        |  31 +++-
 rust/kernel/num/bounded.rs               |  31 ++++
 18 files changed, 975 insertions(+), 2 deletions(-)
---
base-commit: 44e7e7f7cffb10a93bb88e7cb59b7b8b3e2deb1c
change-id: 20260804-pramin-split-950e210b6e3f

Best regards,
--  
Eliot Courtney <ecourtney@nvidia.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 01/12] rust: io: add Region::try_subregion
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05 10:43   ` Gary Guo
  2026-08-05  5:44 ` [PATCH 02/12] rust: num: reject Bounded::shr overshifts at build time Eliot Courtney
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

Add a helper to get a subregion of an IO view fallibly.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 rust/kernel/io.rs | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 95f46bb75f9e..85fbdcc50c8f 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -6,7 +6,8 @@
 
 use core::{
     marker::PhantomData,
-    mem::MaybeUninit, //
+    mem::MaybeUninit,
+    ops::Range, //
 };
 
 use crate::{
@@ -80,6 +81,34 @@ pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Sel
 
         Ok(Self::ptr_from_raw_parts_mut(base, size))
     }
+
+    /// Try to create a subregion of `io` at the given range.
+    ///
+    /// Runtime checks that `range` is within this region, is at least as large as the given new
+    /// minimum size `NEW_SIZE`, and that [`Region`]'s alignment requirements are satisfied.
+    #[inline]
+    pub fn try_subregion<'a, const NEW_SIZE: usize, IO>(
+        io: IO,
+        range: Range<usize>,
+    ) -> Result<<IO::Backend as IoBackend>::View<'a, Region<NEW_SIZE>>>
+    where
+        IO: IoBase<'a, Target = Self>,
+    {
+        let view = io.as_view();
+        let ptr = IO::Backend::as_ptr(view);
+
+        let size = KnownSize::size(ptr);
+        if range.start > size || range.end > size {
+            return Err(EINVAL);
+        }
+        let region = Region::ptr_try_from_raw_parts_mut(
+            ptr.cast::<u8>().wrapping_add(range.start),
+            range.len(),
+        )?;
+
+        // SAFETY: We have checked bounds and alignment, so this is a valid projection.
+        Ok(unsafe { IO::Backend::project_view(view, region) })
+    }
 }
 
 impl<const SIZE: usize> KnownSize for Region<SIZE> {

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/12] rust: num: reject Bounded::shr overshifts at build time
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
  2026-08-05  5:44 ` [PATCH 01/12] rust: io: add Region::try_subregion Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 03/12] rust: num: add Bounded::shr_exact Eliot Courtney
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

Make `shr` reject shifts of at least the type's bit width at build
time, instead of panicking or masking the shift amount at runtime.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 rust/kernel/num/bounded.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index dafe77782d79..f263107f001e 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -485,6 +485,7 @@ pub fn cast<U>(self) -> Bounded<U, N>
     /// assert_eq!(v_shifted.get(), 0xff);
     /// ```
     pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+        const { assert!(SHIFT < T::BITS) }
         const { assert!(RES + SHIFT >= N) }
 
         // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/12] rust: num: add Bounded::shr_exact
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
  2026-08-05  5:44 ` [PATCH 01/12] rust: io: add Region::try_subregion Eliot Courtney
  2026-08-05  5:44 ` [PATCH 02/12] rust: num: reject Bounded::shr overshifts at build time Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 04/12] gpu: nova-core: mm: Add VramAddress type Eliot Courtney
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

Add `shr_exact` in the vein of `try_shrink` which shifts a bounded right
only if it loses no set bits. This is useful for getting a shifted down
integer while simultaneously checking that it's aligned.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 rust/kernel/num/bounded.rs | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index f263107f001e..2695a7858d8a 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -493,6 +493,36 @@ pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
         unsafe { Bounded::__new(self.0 >> SHIFT) }
     }
 
+    /// Right-shifts `self` by `SHIFT` if that loses no set bits, and returns the result as a
+    /// `Bounded<_, RES>`, where `RES >= N - SHIFT`.
+    ///
+    /// Returns [`None`] if any of the `SHIFT` least significant bits of `self` is set.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::num::Bounded;
+    ///
+    /// let v = Bounded::<u32, 16>::new::<0xff00>();
+    /// let v_shifted: Option<Bounded<u32, 8>> = v.shr_exact::<8, _>();
+    ///
+    /// assert_eq!(v_shifted.map(|v| v.get()), Some(0xff));
+    ///
+    /// // A set bit would be shifted out.
+    /// let v = Bounded::<u32, 16>::new::<0xff01>();
+    /// let v_shifted: Option<Bounded<u32, 8>> = v.shr_exact::<8, _>();
+    ///
+    /// assert!(v_shifted.is_none());
+    /// ```
+    pub fn shr_exact<const SHIFT: u32, const RES: u32>(self) -> Option<Bounded<T, RES>> {
+        let shifted = self.shr::<SHIFT, RES>();
+        if shifted.get() << SHIFT == self.0 {
+            Some(shifted)
+        } else {
+            None
+        }
+    }
+
     /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=
     /// N + SHIFT`.
     ///

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/12] gpu: nova-core: mm: Add VramAddress type
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (2 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 03/12] rust: num: add Bounded::shr_exact Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 05/12] gpu: nova-core: mm: Implement Alignable and Debug for VramAddress Eliot Courtney
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

Add the `VramAddress` type representing a physical address in VRAM. Also
add an arithmetic helper, comparison, and operator overloads which are
required in later patches for address arithmetic.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: create mm.rs here, squashing in the arithmetic patch]
[ecourtney: splice the two commit bodies]
[ecourtney: drop the Pfn fields, open-coding what bitfield! generated]
[ecourtney: drop align_down and the IntoVramOffset/IntoVramRange traits]
[ecourtney: make checked_add() const over a plain u64, derive the ordering]
[ecourtney: doc wording, header, import, and signature cleanups]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/mm.rs        | 60 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs |  1 +
 2 files changed, 61 insertions(+)

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
new file mode 100644
index 000000000000..dcd5e5e919bf
--- /dev/null
+++ b/drivers/gpu/nova-core/mm.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Memory management subsystems.
+
+#![expect(dead_code)]
+
+use core::{
+    fmt::LowerHex,
+    ops, //
+};
+
+use kernel::fmt;
+
+/// Physical VRAM address in GPU video memory.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(transparent)]
+pub(crate) struct VramAddress(u64);
+
+impl VramAddress {
+    /// Creates an address from a raw value.
+    pub(crate) const fn from_raw(addr: u64) -> Self {
+        Self(addr)
+    }
+
+    /// Returns the address as a raw value.
+    pub(crate) const fn into_raw(self) -> u64 {
+        self.0
+    }
+
+    /// Adds `rhs` to this address, returning [`None`] on overflow.
+    pub(crate) const fn checked_add(self, rhs: u64) -> Option<Self> {
+        match self.into_raw().checked_add(rhs) {
+            Some(addr) => Some(Self::from_raw(addr)),
+            None => None,
+        }
+    }
+}
+
+impl LowerHex for VramAddress {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        LowerHex::fmt(&self.into_raw(), f)
+    }
+}
+
+impl ops::Add<u64> for VramAddress {
+    type Output = Self;
+
+    fn add(self, rhs: u64) -> Self::Output {
+        Self::from_raw(self.into_raw() + rhs)
+    }
+}
+
+impl ops::Sub for VramAddress {
+    type Output = u64;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        self.into_raw() - rhs.into_raw()
+    }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 35a8b1214b0e..8f59cfa97017 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -18,6 +18,7 @@
 mod gpu;
 mod gsp;
 mod mctp;
+mod mm;
 #[macro_use]
 mod num;
 mod regs;

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/12] gpu: nova-core: mm: Implement Alignable and Debug for VramAddress
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (3 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 04/12] gpu: nova-core: mm: Add VramAddress type Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 06/12] gpu: nova-core: mm: Add PRAMIN window registers Eliot Courtney
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

Later patches align VRAM addresses down to the PRAMIN window. Implement
`Alignable` trait for `VramAddress` and plus add a `ZERO` constant.

Also print the address in hex under `{:?}`, so it reads well in debug
output.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/mm.rs | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index dcd5e5e919bf..f9d80ffbe01d 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -10,7 +10,14 @@
     ops, //
 };
 
-use kernel::fmt;
+use kernel::{
+    fmt,
+    prelude::*,
+    ptr::{
+        Alignable,
+        Alignment, //
+    },
+};
 
 /// Physical VRAM address in GPU video memory.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -18,6 +25,9 @@
 pub(crate) struct VramAddress(u64);
 
 impl VramAddress {
+    /// The zero address.
+    pub(crate) const ZERO: Self = Self::from_raw(0);
+
     /// Creates an address from a raw value.
     pub(crate) const fn from_raw(addr: u64) -> Self {
         Self(addr)
@@ -37,12 +47,28 @@ pub(crate) const fn checked_add(self, rhs: u64) -> Option<Self> {
     }
 }
 
+impl Alignable for VramAddress {
+    fn align_down(self, alignment: Alignment) -> Self {
+        Self::from_raw(self.into_raw().align_down(alignment))
+    }
+
+    fn align_up(self, alignment: Alignment) -> Option<Self> {
+        self.into_raw().align_up(alignment).map(Self::from_raw)
+    }
+}
+
 impl LowerHex for VramAddress {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         LowerHex::fmt(&self.into_raw(), f)
     }
 }
 
+impl fmt::Debug for VramAddress {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_fmt(fmt!("{:#x}", self))
+    }
+}
+
 impl ops::Add<u64> for VramAddress {
     type Output = Self;
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/12] gpu: nova-core: mm: Add PRAMIN window registers
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (4 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 05/12] gpu: nova-core: mm: Implement Alignable and Debug for VramAddress Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 07/12] gpu: nova-core: mm: Add the memory management HAL Eliot Courtney
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

PRAMIN apertures are a crucial mechanism for direct CPU read/write to
VRAM. Add the BAR0 window registers that position the PRAMIN aperture
on all supported GPU architectures: Turing, Ampere, Ada (via
`NV_PBUS_BAR0_WINDOW`), Hopper (via `gh100::NV_XAL_EP_BAR0_WINDOW`),
and Blackwell (via `gb100::NV_XAL_EP_BAR0_WINDOW`).

Hopper/Blackwell window-base registers are based on Eliot Courtney's
offlist reference patch.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: split the registers out of the PRAMIN patch into mm/regs.rs]
[ecourtney: drop the register read path]
[ecourtney: reword the message for the split, narrow visibility to mm]
[ecourtney: plain base fields, as the bitfield cast+shift patch is dropped]
[ecourtney: rename the target to VidMem, fix derives, redo the target docs]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/mm.rs      |  2 ++
 drivers/gpu/nova-core/mm/regs.rs | 57 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index f9d80ffbe01d..7d24ad790310 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -19,6 +19,8 @@
     },
 };
 
+mod regs;
+
 /// Physical VRAM address in GPU video memory.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(transparent)]
diff --git a/drivers/gpu/nova-core/mm/regs.rs b/drivers/gpu/nova-core/mm/regs.rs
new file mode 100644
index 000000000000..541941918add
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/regs.rs
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Registers used by the memory management subsystems: the BAR0 PRAMIN window.
+
+use kernel::io::register;
+
+use crate::bounded_enum;
+
+// PRAMIN window
+
+bounded_enum! {
+    /// Target memory type for the BAR0 window register.
+    ///
+    /// Only VRAM is needed by the driver. Pre-Hopper window registers also define
+    /// system-memory targets that are unused here; Hopper+ uses a separate register
+    /// without a target field.
+    #[derive(Debug, Copy, Clone)]
+    pub(super) enum Bar0WindowTarget with TryFrom<Bounded<u32, 2>> {
+        /// Video memory (GPU framebuffer memory).
+        VidMem = 0,
+    }
+}
+
+register! {
+    /// BAR0 window control for PRAMIN access.
+    pub(super) NV_PBUS_BAR0_WINDOW(u32) @ 0x00001700 {
+        /// Target memory aperture for the window.
+        25:24   target ?=> Bar0WindowTarget;
+        /// PRAMIN window base bits 39:16.
+        23:0    base;
+    }
+}
+
+pub(super) mod gh100 {
+    use kernel::io::register;
+
+    register! {
+        /// Hopper register for PRAMIN window.
+        pub(crate) NV_XAL_EP_BAR0_WINDOW(u32) @ 0x0010fd40 {
+            /// PRAMIN window base bits 37:16.
+            21:0    base;
+        }
+    }
+}
+
+pub(super) mod gb100 {
+    use kernel::io::register;
+
+    register! {
+        /// Blackwell GB10x/GB20x register for PRAMIN window.
+        pub(crate) NV_XAL_EP_BAR0_WINDOW(u32) @ 0x0010fd40 {
+            /// PRAMIN window base bits 38:16.
+            22:0    base;
+        }
+    }
+}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/12] gpu: nova-core: mm: Add the memory management HAL
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (5 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 06/12] gpu: nova-core: mm: Add PRAMIN window registers Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM Eliot Courtney
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

Positioning the PRAMIN window requires writing an architecture-specific
register: `NV_PBUS_BAR0_WINDOW` on Turing, Ampere and Ada, and
`NV_XAL_EP_BAR0_WINDOW` with a different field width on Hopper and on
Blackwell.

A `MmHal` trait with one implementation per hardware family hides the
register choice from the rest of the mm code, matching the layout of
the driver's other HALs.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/mm.rs           |  1 +
 drivers/gpu/nova-core/mm/hal.rs       | 56 +++++++++++++++++++++++++++++++++++
 drivers/gpu/nova-core/mm/hal/gb100.rs | 35 ++++++++++++++++++++++
 drivers/gpu/nova-core/mm/hal/gh100.rs | 35 ++++++++++++++++++++++
 drivers/gpu/nova-core/mm/hal/tu102.rs | 37 +++++++++++++++++++++++
 5 files changed, 164 insertions(+)

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 7d24ad790310..07dce4ce2473 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -19,6 +19,7 @@
     },
 };
 
+mod hal;
 mod regs;
 
 /// Physical VRAM address in GPU video memory.
diff --git a/drivers/gpu/nova-core/mm/hal.rs b/drivers/gpu/nova-core/mm/hal.rs
new file mode 100644
index 000000000000..e7fd1e38bd38
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/hal.rs
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Memory management HAL.
+
+use kernel::{
+    num::Bounded,
+    prelude::*, //
+};
+
+use crate::{
+    driver::Bar0,
+    gpu::{
+        Architecture,
+        Chipset, //
+    },
+    mm::VramAddress, //
+};
+
+mod gb100;
+mod gh100;
+mod tu102;
+
+/// Trait implemented by per-architecture MM HALs.
+///
+/// `Sync` is required so that the `&'static dyn MmHal` references can be stored in `Send`
+/// structures.
+pub(super) trait MmHal: Sync {
+    /// Positions the PRAMIN window at `base`.
+    ///
+    /// This fails if `base` is not aligned to the 64 KiB window alignment or is too large for
+    /// the receiving register.
+    fn write_pramin_window_base(&self, bar: Bar0<'_>, base: VramAddress) -> Result;
+}
+
+/// Returns the HAL corresponding to `chipset`.
+pub(super) fn mm_hal(chipset: Chipset) -> &'static dyn MmHal {
+    match chipset.arch() {
+        Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
+        Architecture::Hopper => gh100::GH100_HAL,
+        Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => gb100::GB100_HAL,
+    }
+}
+
+/// Converts `base` into the value of the window-base register field.
+///
+/// Fails with [`EINVAL`] if `base` is not aligned to the window alignment required by the register
+/// field's shift, or if the shifted value does not fit within `RES` bits.
+fn window_base<const RES: u32>(base: VramAddress) -> Result<Bounded<u64, RES>> {
+    const WINDOW_BASE_SHIFT: u32 = 16;
+
+    Bounded::<u64, 64>::from(base.into_raw())
+        .shr_exact::<WINDOW_BASE_SHIFT, { 64 - WINDOW_BASE_SHIFT }>()
+        .and_then(Bounded::try_shrink)
+        .ok_or(EINVAL)
+}
diff --git a/drivers/gpu/nova-core/mm/hal/gb100.rs b/drivers/gpu/nova-core/mm/hal/gb100.rs
new file mode 100644
index 000000000000..3781e143dea7
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/hal/gb100.rs
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Blackwell GB10x/GB20x memory management HAL.
+
+use kernel::{
+    io::Io,
+    prelude::*, //
+};
+
+use crate::{
+    driver::Bar0,
+    mm::{
+        hal::{
+            window_base,
+            MmHal, //
+        },
+        regs,
+        VramAddress, //
+    },
+};
+
+struct Gb100;
+
+impl MmHal for Gb100 {
+    fn write_pramin_window_base(&self, bar: Bar0<'_>, base: VramAddress) -> Result {
+        bar.write_reg(
+            regs::gb100::NV_XAL_EP_BAR0_WINDOW::zeroed().with_base(window_base(base)?.cast()),
+        );
+        Ok(())
+    }
+}
+
+const GB100: Gb100 = Gb100;
+pub(super) const GB100_HAL: &dyn MmHal = &GB100;
diff --git a/drivers/gpu/nova-core/mm/hal/gh100.rs b/drivers/gpu/nova-core/mm/hal/gh100.rs
new file mode 100644
index 000000000000..8af384db2921
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/hal/gh100.rs
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Hopper memory management HAL.
+
+use kernel::{
+    io::Io,
+    prelude::*, //
+};
+
+use crate::{
+    driver::Bar0,
+    mm::{
+        hal::{
+            window_base,
+            MmHal, //
+        },
+        regs,
+        VramAddress, //
+    },
+};
+
+struct Gh100;
+
+impl MmHal for Gh100 {
+    fn write_pramin_window_base(&self, bar: Bar0<'_>, base: VramAddress) -> Result {
+        bar.write_reg(
+            regs::gh100::NV_XAL_EP_BAR0_WINDOW::zeroed().with_base(window_base(base)?.cast()),
+        );
+        Ok(())
+    }
+}
+
+const GH100: Gh100 = Gh100;
+pub(super) const GH100_HAL: &dyn MmHal = &GH100;
diff --git a/drivers/gpu/nova-core/mm/hal/tu102.rs b/drivers/gpu/nova-core/mm/hal/tu102.rs
new file mode 100644
index 000000000000..e4fe7561223c
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/hal/tu102.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Turing, Ampere and Ada memory management HAL.
+
+use kernel::{
+    io::Io,
+    prelude::*, //
+};
+
+use crate::{
+    driver::Bar0,
+    mm::{
+        hal::{
+            window_base,
+            MmHal, //
+        },
+        regs,
+        VramAddress, //
+    },
+};
+
+struct Tu102;
+
+impl MmHal for Tu102 {
+    fn write_pramin_window_base(&self, bar: Bar0<'_>, base: VramAddress) -> Result {
+        bar.write_reg(
+            regs::NV_PBUS_BAR0_WINDOW::zeroed()
+                .with_target(regs::Bar0WindowTarget::VidMem)
+                .with_base(window_base(base)?.cast()),
+        );
+        Ok(())
+    }
+}
+
+const TU102: Tu102 = Tu102;
+pub(super) const TU102_HAL: &dyn MmHal = &TU102;

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (6 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 07/12] gpu: nova-core: mm: Add the memory management HAL Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 09/12] docs: gpu: nova-core: Document the PRAMIN aperture mechanism Eliot Courtney
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

PRAMIN apertures are a crucial mechanism for direct CPU read/write to
VRAM. Add a `Pramin` manager whose `window_at()` returns a typed MMIO
view of VRAM through the 1 MiB PRAMIN aperture in BAR0, validating the
view against the VRAM region and repositioning the window as needed for
the accessed address.

A view borrows `Pramin` mutably, so the window cannot move while
the view is in use, and it inserts an ordering point on Drop.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: split the registers and HAL into the two preceding patches]
[ecourtney: rebase w.r.t. Bar0 lifetime changes]
[ecourtney: drop the window guard and mutex, use &mut self]
[ecourtney: position at init to avoid reads, reposition in window_offset]
[ecourtney: return typed MMIO views instead of read/write accessors]
[ecourtney: insert an ordering read when a view drops]
[ecourtney: rename the aperture constants, drop the doc examples]
[ecourtney: add the copyright header, doc and naming cleanups]
[ecourtney: the pramin module is mm-internal]
Co-developed-by: Eliot Courtney <ecourtney@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/mm.rs        |   1 +
 drivers/gpu/nova-core/mm/pramin.rs | 156 +++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+)

diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 07dce4ce2473..ef5b1cad56c3 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -20,6 +20,7 @@
 };
 
 mod hal;
+mod pramin;
 mod regs;
 
 /// Physical VRAM address in GPU video memory.
diff --git a/drivers/gpu/nova-core/mm/pramin.rs b/drivers/gpu/nova-core/mm/pramin.rs
new file mode 100644
index 000000000000..2aa1bca22fa6
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/pramin.rs
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Utilities for accessing VRAM through the PRAMIN window.
+
+use core::ops::Range;
+
+use kernel::{
+    io::{
+        Io,
+        Mmio,
+        Region, //
+    },
+    prelude::*,
+    ptr::{
+        Alignable,
+        Alignment, //
+    },
+    sizes::{
+        SZ_1M,
+        SZ_64K, //
+    },
+};
+
+use crate::{
+    driver::Bar0,
+    gpu::Chipset,
+    mm::{
+        hal::{
+            self,
+            MmHal, //
+        },
+        VramAddress, //
+    },
+    num::IntoSafeCast, //
+};
+
+/// Size of the PRAMIN window (1 MiB).
+const WINDOW_SIZE: usize = SZ_1M;
+
+/// Owner of the PRAMIN window state.
+///
+/// [`Pramin::window_at()`] repositions the window as needed and returns a typed MMIO view into
+/// it, holding the manager borrowed for the lifetime of the view.
+pub(super) struct Pramin<'gpu> {
+    bar: Bar0<'gpu>,
+    hal: &'static dyn MmHal,
+    /// MMIO view of the PRAMIN window in BAR0.
+    window: Mmio<'gpu, Region<WINDOW_SIZE>>,
+    /// VRAM range to keep the PRAMIN window inside.
+    vram_range: Range<VramAddress>,
+    /// Cached window position.
+    window_range: Range<VramAddress>,
+}
+
+/// Typed view of VRAM through the PRAMIN window.
+///
+/// Inserts an ordering point after previous writes through the window on drop. Views returned
+/// by [`PraminWindow::view()`] cannot outlive this access, so the ordering point covers every
+/// write made through them.
+pub(super) struct PraminWindow<'a, T> {
+    view: Mmio<'a, T>,
+    window: Mmio<'a, Region<WINDOW_SIZE>>,
+}
+
+impl<T> PraminWindow<'_, T> {
+    /// Returns the MMIO view of the accessed location.
+    pub(super) fn view(&self) -> Mmio<'_, T> {
+        self.view
+    }
+}
+
+impl<T> Drop for PraminWindow<'_, T> {
+    fn drop(&mut self) {
+        // Insert an ordering point after previous writes through this window.
+        self.window.read32(0);
+    }
+}
+
+impl<'gpu> Pramin<'gpu> {
+    /// Location of the window inside BAR0.
+    const BAR0_OFFSET: usize = 0x700000;
+
+    /// Alignment required by the PRAMIN window.
+    const BASE_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+
+    /// Creates the window manager for the given VRAM region.
+    pub(super) fn new(
+        bar: Bar0<'gpu>,
+        chipset: Chipset,
+        vram_range: Range<VramAddress>,
+    ) -> Result<Self> {
+        let hal = hal::mm_hal(chipset);
+        let window =
+            Region::try_subregion(bar, Self::BAR0_OFFSET..Self::BAR0_OFFSET + WINDOW_SIZE)?;
+        let base = vram_range.start.align_down(Self::BASE_ALIGN);
+        hal.write_pramin_window_base(bar, base)?;
+
+        Ok(Self {
+            bar,
+            hal,
+            window,
+            vram_range,
+            window_range: base..base + WINDOW_SIZE.into_safe_cast(),
+        })
+    }
+
+    /// Check the window covers `len` bytes at `addr`, moving it if needed.
+    ///
+    /// Returns the window offset at which to perform the access.
+    fn window_offset(&mut self, addr: VramAddress, len: usize) -> Result<usize> {
+        let end = addr.checked_add(len.into_safe_cast()).ok_or(EINVAL)?;
+
+        let inside = |r: &Range<VramAddress>| r.contains(&addr) && end <= r.end;
+        if !inside(&self.vram_range) {
+            return Err(EINVAL);
+        }
+
+        // Reposition the window if the access falls outside it.
+        if !inside(&self.window_range) {
+            let base = addr.align_down(Self::BASE_ALIGN);
+            let window_range = base..base + WINDOW_SIZE.into_safe_cast();
+            if !inside(&window_range) {
+                return Err(EINVAL);
+            }
+            self.hal.write_pramin_window_base(self.bar, base)?;
+            self.window_range = window_range;
+        }
+
+        Ok((addr - self.window_range.start).into_safe_cast())
+    }
+
+    /// Return a typed MMIO view of a `T` at `vram_addr`.
+    ///
+    /// The size of `T` must be a multiple of 4, as required by [`Region`]'s type invariant.
+    /// Returns an error if `vram_addr` is not aligned to 4 bytes and to `T`'s alignment, or if
+    /// a `T` at `vram_addr` does not fit within the VRAM region.
+    pub(super) fn window_at<'a, T>(
+        &'a mut self,
+        vram_addr: VramAddress,
+    ) -> Result<PraminWindow<'a, T>>
+    where
+        T: FromBytes + IntoBytes,
+    {
+        const_assert!(size_of::<T>() % 4 == 0);
+
+        let offset = self.window_offset(vram_addr, size_of::<T>())?;
+        let view: Mmio<'_, Region> =
+            Region::try_subregion(self.window, offset..offset + size_of::<T>())?;
+
+        Ok(PraminWindow {
+            view: view.try_cast()?,
+            window: self.window,
+        })
+    }
+}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/12] docs: gpu: nova-core: Document the PRAMIN aperture mechanism
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (7 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 10/12] gpu: nova-core: mm: Add GpuMm centralized memory manager Eliot Courtney
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

Add documentation for the PRAMIN aperture mechanism used by nova-core
for direct VRAM access.

Nova only uses TARGET=VRAM for VRAM access. The SYS_MEM target values
are documented for completeness but not used by the driver.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: fix the TARGET encodings, KiB/MiB units, wording nits]
[ecourtney: wrap at 80 columns]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 Documentation/gpu/nova/core/pramin.rst | 128 +++++++++++++++++++++++++++++++++
 Documentation/gpu/nova/index.rst       |   1 +
 2 files changed, 129 insertions(+)

diff --git a/Documentation/gpu/nova/core/pramin.rst b/Documentation/gpu/nova/core/pramin.rst
new file mode 100644
index 000000000000..f50b052d73ba
--- /dev/null
+++ b/Documentation/gpu/nova/core/pramin.rst
@@ -0,0 +1,128 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+PRAMIN aperture mechanism
+=========================
+
+.. note::
+   The following description is approximate and current as of the Ampere
+   family. It may change for future generations and is intended to assist in
+   understanding the driver code.
+
+Introduction
+============
+
+PRAMIN is a hardware aperture mechanism that provides CPU access to GPU Video
+RAM (VRAM) before the GPU's Memory Management Unit (MMU) and page tables are
+initialized. This 1 MiB sliding window, located at a fixed offset within BAR0,
+is essential for setting up page tables and other critical GPU data structures
+without relying on the GPU's MMU.
+
+Architecture Overview
+=====================
+
+The PRAMIN aperture mechanism is logically implemented by the GPU's PBUS (PCIe
+Bus Controller Unit) and provides a CPU-accessible window into VRAM through the
+PCIe interface::
+
+    +-----------------+    PCIe     +------------------------------+
+    |      CPU        |<----------->|           GPU                |
+    +-----------------+             |                              |
+                                    |  +----------------------+    |
+                                    |  |       PBUS           |    |
+                                    |  |  (Bus Controller)    |    |
+                                    |  |                      |    |
+                                    |  |  +--------------+ <------------ [1]
+                                    |  |  |   PRAMIN     |    |    |
+                                    |  |  |   Window     |    |    |
+                                    |  |  |   (1 MiB)    |    |    |
+                                    |  |  +--------------+    |    |
+                                    |  |         |            |    |
+                                    |  +---------|------------+    |
+                                    |            |                 |
+                                    |            v                 |
+                                    |  +----------------------+ <------- [2]
+                                    |  |       VRAM           |    |
+                                    |  |    (Several GiB)     |    |
+                                    |  |                      |    |
+                                    |  |   FB[0x0000000000]   |    |
+                                    |  |          ...         |    |
+                                    |  |   FB[0xFFFFFFFFFF]   |    |
+                                    |  +----------------------+    |
+                                    +------------------------------+
+
+    [1] Window starts at BAR0 + 0x700000.
+    [2] Program PRAMIN to any 64 KiB-aligned VRAM boundary.
+
+PBUS is responsible for, among other things, handling MMIO
+accesses to the BAR registers.
+
+PRAMIN Window Operation
+=======================
+
+The PRAMIN window provides a 1 MiB sliding aperture that can be repositioned
+over the entire VRAM address space using the ``NV_PBUS_BAR0_WINDOW`` register.
+
+Window Control Mechanism
+-------------------------
+
+::
+
+    NV_PBUS_BAR0_WINDOW Register (0x1700):
+    +-------+--------+--------------------------------------+
+    | 31:26 | 25:24  |               23:0                   |
+    | RSVD  | TARGET |            BASE_ADDR                 |
+    |       |        |        (bits 39:16 of VRAM address)  |
+    +-------+--------+--------------------------------------+
+
+    The 24-bit BASE_ADDR field encodes bits [39:16] of the target VRAM address,
+    providing 40-bit (1 TiB) address space coverage with 64 KiB alignment.
+
+    TARGET field (bits 25:24):
+    - 0x0: VRAM (Video Memory)
+    - 0x1: Reserved (unused)
+    - 0x2: SYS_MEM_COH (Coherent System Memory)
+    - 0x3: SYS_MEM_NONCOH (Non-coherent System Memory)
+
+.. note::
+   Nova only uses TARGET=VRAM (0x0) for video memory access. The SYS_MEM
+   target values are documented here for hardware completeness but are
+   not used by the driver.
+
+64 KiB Alignment Requirement
+----------------------------
+
+The PRAMIN window must be aligned to 64 KiB boundaries in VRAM. This is enforced
+by the ``BASE_ADDR`` field representing bits [39:16] of the target address::
+
+    VRAM Address Calculation:
+    actual_vram_addr = (BASE_ADDR << 16) + pramin_offset
+    Where:
+    - BASE_ADDR: 24-bit value from NV_PBUS_BAR0_WINDOW[23:0]
+    - pramin_offset: 20-bit offset within the PRAMIN window [0x00000-0xFFFFF]
+
+    Example Window Positioning:
+    +---------------------------------------------------------+
+    |                    VRAM Space                           |
+    |                                                         |
+    |  0x0000000000 +-----------------+ <-- 64 KiB aligned    |
+    |               | PRAMIN Window   |                       |
+    |               |    (1 MiB)      |                       |
+    |  0x00000FFFFF +-----------------+                       |
+    |                                                         |
+    |       |              ^                                  |
+    |       |              | Window can slide                 |
+    |       v              | to any 64 KiB-aligned boundary   |
+    |                                                         |
+    |  0x0123400000 +-----------------+ <-- 64 KiB aligned    |
+    |               | PRAMIN Window   |                       |
+    |               |    (1 MiB)      |                       |
+    |  0x01234FFFFF +-----------------+                       |
+    |                                                         |
+    |                       ...                               |
+    |                                                         |
+    |  0xFFFFF00000 +-----------------+ <-- 64 KiB aligned    |
+    |               | PRAMIN Window   |                       |
+    |               |    (1 MiB)      |                       |
+    |  0xFFFFFFFFFF +-----------------+                       |
+    +---------------------------------------------------------+
diff --git a/Documentation/gpu/nova/index.rst b/Documentation/gpu/nova/index.rst
index 1783513cbd05..5ebbfd76fdac 100644
--- a/Documentation/gpu/nova/index.rst
+++ b/Documentation/gpu/nova/index.rst
@@ -33,3 +33,4 @@ vGPU manager VFIO driver and the nova-drm driver.
    core/fsp
    core/fwsec
    core/falcon
+   core/pramin

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/12] gpu: nova-core: mm: Add GpuMm centralized memory manager
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (8 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 09/12] docs: gpu: nova-core: Document the PRAMIN aperture mechanism Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 11/12] gpu: nova-core: Add self-test assertion macros and config option Eliot Courtney
  2026-08-05  5:44 ` [PATCH 12/12] gpu: nova-core: mm: Add PRAMIN aperture self-tests Eliot Courtney
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

Introduce GpuMm as the centralized GPU memory manager. At this point in
the series, GpuMm only owns the PRAMIN window for direct VRAM access;
the buddy allocator and TLB manager are added later when those backing
types become available.

This provides a clean ownership model where GpuMm provides accessor
methods for its components that can be used for memory management
operations, and lets follow-on patches (such as the PRAMIN aperture
self-tests) reference `pramin_mut()` cleanly.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: squash the total VRAM end patch, drop its dev_info print]
[ecourtney: take the maximum FB limit before adding one, fail with EINVAL]
[ecourtney: rebase for the Bar0 lifetime and ownership changes, drop Arcs]
[ecourtney: source the chipset through gsp_resources]
[ecourtney: build the VRAM region in new(), pramin() becomes pramin_mut()]
[ecourtney: declare mm before gsp_resources, doc wording cleanups]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs             | 18 +++++++++++++++-
 drivers/gpu/nova-core/gsp/commands.rs    |  4 ++++
 drivers/gpu/nova-core/gsp/fw/commands.rs |  5 +++++
 drivers/gpu/nova-core/mm.rs              | 35 ++++++++++++++++++++++++++++++++
 4 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 42a4cd7971fa..e29e07488e78 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -29,6 +29,10 @@
         Gsp,
         GspBootContext, //
     },
+    mm::{
+        GpuMm,
+        VramAddress, //
+    },
     regs,
     vgpu::VgpuManager, //
 };
@@ -283,6 +287,11 @@ pub(crate) struct Gpu<'gpu> {
     spec: Spec,
     /// Static GPU information as provided by the GSP.
     gsp_static_info: GetGspStaticInfoReply,
+    /// GPU memory manager owning memory management resources.
+    ///
+    /// Must be kept declared *before* `gsp_resources`, so that its components are dropped while
+    /// the GSP is still operational.
+    mm: GpuMm<'gpu>,
     /// GSP and its resources.
     #[pin]
     gsp_resources: GspResources<'gpu>,
@@ -410,7 +419,14 @@ pub(crate) fn new(
                 }
 
                 info
-            }
+            },
+
+            // Create GPU memory manager owning memory management resources.
+            mm: GpuMm::new(
+                bar,
+                gsp_resources.spec.chipset,
+                VramAddress::from_raw(gsp_static_info.total_fb_end),
+            )?,
         })
     }
 }
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index ffc25fd8c47b..eea1c9ed4684 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -216,6 +216,8 @@ pub(crate) struct GetGspStaticInfoReply {
     gpu_name: [u8; 64],
     /// Usable FB (VRAM) regions for driver memory allocation.
     pub(crate) usable_fb_regions: KVec<Range<u64>>,
+    /// Exclusive end of the FB physical address space.
+    pub(crate) total_fb_end: u64,
 }
 
 impl MessageFromGsp for GetGspStaticInfoReply {
@@ -231,10 +233,12 @@ fn read(
         for region in msg.usable_fb_regions() {
             usable_fb_regions.push(region, GFP_KERNEL)?;
         }
+        let total_fb_end = msg.total_fb_end().ok_or(EINVAL)?;
 
         Ok(GetGspStaticInfoReply {
             gpu_name: msg.gpu_name_str(),
             usable_fb_regions,
+            total_fb_end,
         })
     }
 }
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 6dc31d1bf5ae..6e85442a4b13 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -165,6 +165,11 @@ pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_
             }
         })
     }
+
+    /// Computes the exclusive end of the FB physical address space.
+    pub(crate) fn total_fb_end(&self) -> Option<u64> {
+        self.fb_regions().map(|reg| reg.limit).max()?.checked_add(1)
+    }
 }
 
 // SAFETY: Padding is explicit and will not contain uninitialized data.
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index ef5b1cad56c3..58dce211a337 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -19,10 +19,45 @@
     },
 };
 
+use crate::{
+    driver::Bar0,
+    gpu::Chipset, //
+};
+
 mod hal;
 mod pramin;
 mod regs;
 
+/// GPU Memory Manager - owns all core MM components.
+///
+/// Provides centralized ownership of memory management resources:
+/// - [`pramin::Pramin`] for direct VRAM access.
+pub(crate) struct GpuMm<'gpu> {
+    pramin: pramin::Pramin<'gpu>,
+}
+
+impl<'gpu> GpuMm<'gpu> {
+    /// Creates the GPU memory manager.
+    pub(crate) fn new(
+        bar: Bar0<'gpu>,
+        chipset: Chipset,
+        total_fb_end: VramAddress,
+    ) -> Result<Self> {
+        // PRAMIN covers all physical VRAM (including GSP-reserved areas
+        // above the usable region, e.g. the BAR1 page directory).
+        let vram_region = VramAddress::ZERO..total_fb_end;
+
+        Ok(Self {
+            pramin: pramin::Pramin::new(bar, chipset, vram_region)?,
+        })
+    }
+
+    /// Access the [`pramin::Pramin`].
+    fn pramin_mut(&mut self) -> &mut pramin::Pramin<'gpu> {
+        &mut self.pramin
+    }
+}
+
 /// Physical VRAM address in GPU video memory.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(transparent)]

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/12] gpu: nova-core: Add self-test assertion macros and config option
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (9 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 10/12] gpu: nova-core: mm: Add GpuMm centralized memory manager Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  2026-08-05  5:44 ` [PATCH 12/12] gpu: nova-core: mm: Add PRAMIN aperture self-tests Eliot Courtney
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney

The existing assert! and assert_eq! macros cause a panic. For self tests
in nova-core, it's inconvenient to cause a panic since these need to be
run on actual hardware. Instead, define similar macros that log an error
then return an Err.

Also add the NOVA_CORE_SELFTESTS Kconfig option that gates the driver
self-tests.

Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/Kconfig      |  9 ++++++
 drivers/gpu/nova-core/nova_core.rs |  2 ++
 drivers/gpu/nova-core/selftest.rs  | 64 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index f918f69e0599..cb7f0b00f796 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -15,3 +15,12 @@ config NOVA_CORE
 	  This driver is work in progress and may not be functional.
 
 	  If M is selected, the module will be called nova-core.
+
+config NOVA_CORE_SELFTESTS
+	bool "Nova Core driver self-tests"
+	depends on NOVA_CORE
+	default n
+	help
+	  Build the driver self-tests and run them when the GPU is probed.
+
+	  If unsure, say N.
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 8f59cfa97017..1133c6ce5c55 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -23,6 +23,8 @@
 mod num;
 mod regs;
 mod sbuffer;
+#[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
+mod selftest;
 mod vbios;
 mod vgpu;
 
diff --git a/drivers/gpu/nova-core/selftest.rs b/drivers/gpu/nova-core/selftest.rs
new file mode 100644
index 000000000000..f5b5965b7e6a
--- /dev/null
+++ b/drivers/gpu/nova-core/selftest.rs
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Assertion macros for driver self-tests.
+//!
+//! Self-tests run against live hardware during probe, so a failed assertion should not panic. These
+//! macros log the failure on the device and fail the enclosing test by returning
+//! [`EIO`](kernel::error::code::EIO) instead.
+
+/// Like [`assert!`], but logs the failure via `dev` and fails the enclosing test instead of
+/// panicking.
+///
+/// As with [`assert!`], a custom message with format arguments can follow the condition.
+#[macro_export]
+macro_rules! selftest_assert {
+    ($dev:expr, $cond:expr $(,)?) => {
+        $crate::selftest_assert!($dev, $cond, "assertion failed: {}", ::core::stringify!($cond))
+    };
+    ($dev:expr, $cond:expr, $($arg:tt)+) => {{
+        if !$cond {
+            ::kernel::dev_err!(
+                $dev,
+                "Selftest: {}:{}: {}\n",
+                ::core::file!(),
+                ::core::line!(),
+                ::kernel::prelude::fmt!($($arg)+)
+            );
+            return Err(::kernel::error::code::EIO);
+        }
+    }};
+}
+
+/// Like [`assert_eq!`], but logs the failure via `dev` and fails the enclosing test instead of
+/// panicking.
+///
+/// As with [`assert_eq!`], a custom message with format arguments can follow the compared values.
+#[macro_export]
+macro_rules! selftest_assert_eq {
+    ($dev:expr, $left:expr, $right:expr $(,)?) => {
+        match (&$left, &$right) {
+            (left, right) => $crate::selftest_assert!(
+                $dev,
+                left == right,
+                "assertion `{} == {}` failed: left {:?}, right {:?}",
+                ::core::stringify!($left),
+                ::core::stringify!($right),
+                left,
+                right
+            ),
+        }
+    };
+    ($dev:expr, $left:expr, $right:expr, $($arg:tt)+) => {
+        match (&$left, &$right) {
+            (left, right) => $crate::selftest_assert!(
+                $dev,
+                left == right,
+                "assertion `left == right` failed: {}: left {:?}, right {:?}",
+                ::kernel::prelude::fmt!($($arg)+),
+                left,
+                right
+            ),
+        }
+    };
+}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/12] gpu: nova-core: mm: Add PRAMIN aperture self-tests
  2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
                   ` (10 preceding siblings ...)
  2026-08-05  5:44 ` [PATCH 11/12] gpu: nova-core: Add self-test assertion macros and config option Eliot Courtney
@ 2026-08-05  5:44 ` Eliot Courtney
  11 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-05  5:44 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, Eliot Courtney, Joel Fernandes

From: Joel Fernandes <joelagnelf@nvidia.com>

Add self-tests for the PRAMIN aperture mechanism to verify correct
operation during GPU probe. The tests validate various alignment
requirements and corner cases.

The tests are default disabled and behind CONFIG_NOVA_CORE_SELFTESTS.
When enabled, tests run after GSP boot during probe.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[ecourtney: convert the tests to window_at(), macros, and the new types]
[ecourtney: cfg-gate the tests and expect(dead_code), not a runtime no-op]
[ecourtney: run the self-tests on all architectures, drop the chipset arg]
[ecourtney: test within a usable FB region, skip when none is large enough]
[ecourtney: report failures without failing probe, start banner at dev_dbg]
[ecourtney: removed the mm-specific Kconfig option]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/driver.rs    |   3 +
 drivers/gpu/nova-core/gpu.rs       |  12 ++++
 drivers/gpu/nova-core/mm.rs        |  40 +++++++++++-
 drivers/gpu/nova-core/mm/pramin.rs | 129 +++++++++++++++++++++++++++++++++++++
 4 files changed, 183 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 5738d4ac521b..2c11eeee92d2 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -86,6 +86,9 @@ fn probe<'bound>(
                 // (`try_pin_init!()` initializes fields in declaration order), lives at a pinned
                 // stable address, and is dropped after `gpu` (struct field drop order).
                 gpu <- Gpu::new(pdev, unsafe { &*core::ptr::from_ref(bar) }),
+                // Run optional GPU selftests.
+                #[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
+                _: { gpu.run_selftests(pdev) },
                 _reg: auxiliary::Registration::new(
                     pdev.as_ref(),
                     c"nova-drm",
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index e29e07488e78..04e1e30eeed7 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -429,4 +429,16 @@ pub(crate) fn new(
             )?,
         })
     }
+
+    /// Runs self-tests on the constructed [`Gpu`], logging failures without failing probe.
+    #[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
+    pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Bound>) {
+        let this = self.project();
+        let dev = pdev.as_ref();
+        let regions = &this.gsp_static_info.usable_fb_regions;
+
+        if let Err(err) = crate::mm::selftest::run(dev, this.mm, regions) {
+            dev_err!(dev, "self-tests failed: {:?}\n", err);
+        }
+    }
 }
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 58dce211a337..9e4338c7c393 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -3,7 +3,7 @@
 
 //! Memory management subsystems.
 
-#![expect(dead_code)]
+#![cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))]
 
 use core::{
     fmt::LowerHex,
@@ -123,3 +123,41 @@ fn sub(self, rhs: Self) -> Self::Output {
         self.into_raw() - rhs.into_raw()
     }
 }
+
+#[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
+pub(crate) mod selftest {
+    use core::ops::Range;
+
+    use kernel::{
+        device,
+        sizes::SizeConstants, //
+    };
+
+    use super::*;
+
+    /// Run MM subsystem self-tests during probe.
+    pub(crate) fn run(
+        dev: &device::Device<device::Bound>,
+        mm: &mut GpuMm<'_>,
+        usable_fb_regions: &[Range<u64>],
+    ) -> Result {
+        // VRAM span the self-tests are free to overwrite, from the chosen test base.
+        const SELFTEST_SPAN: u64 = u64::SZ_64M;
+
+        let base = usable_fb_regions.iter().find_map(|region| {
+            // Tests rely on this being 8 byte aligned for checking misalignment handling.
+            let base = region.start.align_up(Alignment::new::<8>())?;
+            (base.checked_add(SELFTEST_SPAN)? <= region.end).then_some(base)
+        });
+        let Some(base) = base else {
+            dev_warn!(
+                dev,
+                "PRAMIN: skipping self-tests, no usable VRAM region of {:#x} bytes\n",
+                SELFTEST_SPAN
+            );
+            return Ok(());
+        };
+
+        pramin::selftest::run(dev, mm.pramin_mut(), VramAddress::from_raw(base))
+    }
+}
diff --git a/drivers/gpu/nova-core/mm/pramin.rs b/drivers/gpu/nova-core/mm/pramin.rs
index 2aa1bca22fa6..1019a6e57e26 100644
--- a/drivers/gpu/nova-core/mm/pramin.rs
+++ b/drivers/gpu/nova-core/mm/pramin.rs
@@ -154,3 +154,132 @@ pub(super) fn window_at<'a, T>(
         })
     }
 }
+
+#[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
+pub(super) mod selftest {
+    use kernel::{
+        device,
+        io::{
+            io_read,
+            io_write, //
+        },
+        sizes::SizeConstants, //
+    };
+
+    use super::*;
+    use crate::{
+        selftest_assert,
+        selftest_assert_eq, //
+    };
+
+    /// Test read/write at byte granularity.
+    fn test_byte_readwrite(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        base: VramAddress,
+    ) -> Result {
+        {
+            let window = pramin.window_at::<[u8; 4]>(base)?;
+            for (i, val) in (0xA0u8..0xA4).enumerate() {
+                io_write!(window.view(), [build: i], val);
+            }
+        }
+
+        let window = pramin.window_at::<[u8; 4]>(base)?;
+        for (i, val) in (0xA0u8..0xA4).enumerate() {
+            selftest_assert_eq!(dev, io_read!(window.view(), [build: i]), val);
+        }
+        Ok(())
+    }
+
+    /// Test writing a `u32` and reading back as individual `u8`s.
+    fn test_u32_as_bytes(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        base: VramAddress,
+    ) -> Result {
+        let addr = base + 0x10;
+        let val: u32 = 0xDEADBEEF;
+        pramin.window_at::<u32>(addr)?.view().write_val(val);
+
+        let window = pramin.window_at::<[u8; 4]>(addr)?;
+        for (i, &expected) in val.to_le_bytes().iter().enumerate() {
+            selftest_assert_eq!(dev, io_read!(window.view(), [build: i]), expected);
+        }
+        Ok(())
+    }
+
+    /// Test window repositioning across 1 MiB boundaries.
+    fn test_window_reposition(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        base: VramAddress,
+    ) -> Result {
+        let addr_a = base;
+        let addr_b = base + u64::SZ_2M; // base + 2 MiB (different 1 MiB region).
+        let val_a: u32 = 0x11111111;
+        let val_b: u32 = 0x22222222;
+
+        pramin.window_at::<u32>(addr_a)?.view().write_val(val_a);
+        pramin.window_at::<u32>(addr_b)?.view().write_val(val_b);
+
+        selftest_assert_eq!(
+            dev,
+            pramin.window_at::<u32>(addr_a)?.view().read_val(),
+            val_a
+        );
+        selftest_assert_eq!(
+            dev,
+            pramin.window_at::<u32>(addr_b)?.view().read_val(),
+            val_b
+        );
+        Ok(())
+    }
+
+    /// Test that offsets outside the VRAM region are rejected.
+    fn test_invalid_offset(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        vram_end: VramAddress,
+    ) -> Result {
+        selftest_assert!(dev, pramin.window_at::<u32>(vram_end).is_err());
+        Ok(())
+    }
+
+    /// Test that misaligned accesses are rejected.
+    fn test_misaligned_access(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        base: VramAddress,
+    ) -> Result {
+        // Misaligned `window_at()` start (`Region` bases must be 4-byte aligned).
+        selftest_assert!(dev, pramin.window_at::<u32>(base + 2).is_err());
+
+        // `u64` at a 4-byte-aligned (not 8-byte-aligned) address.
+        selftest_assert!(dev, pramin.window_at::<u64>(base + 0x44).is_err());
+        Ok(())
+    }
+
+    /// Run PRAMIN self-tests during probe.
+    ///
+    /// `base` is the start of a driver-usable VRAM span that the tests are free to
+    /// overwrite.
+    pub(crate) fn run(
+        dev: &device::Device<device::Bound>,
+        pramin: &mut Pramin<'_>,
+        base: VramAddress,
+    ) -> Result {
+        dev_dbg!(dev, "PRAMIN: starting self-tests\n");
+
+        let vram_end = pramin.vram_range.end;
+
+        test_byte_readwrite(dev, pramin, base)?;
+        test_u32_as_bytes(dev, pramin, base)?;
+        test_window_reposition(dev, pramin, base)?;
+        test_invalid_offset(dev, pramin, vram_end)?;
+        test_misaligned_access(dev, pramin, base)?;
+
+        dev_info!(dev, "PRAMIN: self-tests passed\n");
+        Ok(())
+    }
+}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 01/12] rust: io: add Region::try_subregion
  2026-08-05  5:44 ` [PATCH 01/12] rust: io: add Region::try_subregion Eliot Courtney
@ 2026-08-05 10:43   ` Gary Guo
  2026-08-07  5:39     ` Eliot Courtney
  0 siblings, 1 reply; 15+ messages in thread
From: Gary Guo @ 2026-08-05 10:43 UTC (permalink / raw)
  To: Eliot Courtney, Danilo Krummrich, Alice Ryhl, Daniel Almeida,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc

On Wed Aug 5, 2026 at 6:44 AM BST, Eliot Courtney wrote:
> Add a helper to get a subregion of an IO view fallibly.
>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> ---
>  rust/kernel/io.rs | 31 ++++++++++++++++++++++++++++++-
>  1 file changed, 30 insertions(+), 1 deletion(-)

Looks like the case where you need this don't actually require a `Region`
(dynamically sized type) but rather a fixed size window?

In that case the next version of
https://lore.kernel.org/rust-for-linux/20260721-typed_register-v1-0-452d72b60262@garyguo.net/
will contain what you need.

Best,
Gary

>
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 95f46bb75f9e..85fbdcc50c8f 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -6,7 +6,8 @@
>  
>  use core::{
>      marker::PhantomData,
> -    mem::MaybeUninit, //
> +    mem::MaybeUninit,
> +    ops::Range, //
>  };
>  
>  use crate::{
> @@ -80,6 +81,34 @@ pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Sel
>  
>          Ok(Self::ptr_from_raw_parts_mut(base, size))
>      }
> +
> +    /// Try to create a subregion of `io` at the given range.
> +    ///
> +    /// Runtime checks that `range` is within this region, is at least as large as the given new
> +    /// minimum size `NEW_SIZE`, and that [`Region`]'s alignment requirements are satisfied.
> +    #[inline]
> +    pub fn try_subregion<'a, const NEW_SIZE: usize, IO>(
> +        io: IO,
> +        range: Range<usize>,
> +    ) -> Result<<IO::Backend as IoBackend>::View<'a, Region<NEW_SIZE>>>
> +    where
> +        IO: IoBase<'a, Target = Self>,
> +    {
> +        let view = io.as_view();
> +        let ptr = IO::Backend::as_ptr(view);
> +
> +        let size = KnownSize::size(ptr);
> +        if range.start > size || range.end > size {
> +            return Err(EINVAL);
> +        }
> +        let region = Region::ptr_try_from_raw_parts_mut(
> +            ptr.cast::<u8>().wrapping_add(range.start),
> +            range.len(),
> +        )?;
> +
> +        // SAFETY: We have checked bounds and alignment, so this is a valid projection.
> +        Ok(unsafe { IO::Backend::project_view(view, region) })
> +    }
>  }
>  
>  impl<const SIZE: usize> KnownSize for Region<SIZE> {



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 01/12] rust: io: add Region::try_subregion
  2026-08-05 10:43   ` Gary Guo
@ 2026-08-07  5:39     ` Eliot Courtney
  0 siblings, 0 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-08-07  5:39 UTC (permalink / raw)
  To: Gary Guo, Eliot Courtney, Danilo Krummrich, Alice Ryhl,
	Daniel Almeida, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Yury Norov, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Jonathan Corbet, Shuah Khan
  Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
	linux-doc, dri-devel

On Wed Aug 5, 2026 at 7:43 PM JST, Gary Guo wrote:
> On Wed Aug 5, 2026 at 6:44 AM BST, Eliot Courtney wrote:
>> Add a helper to get a subregion of an IO view fallibly.
>>
>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>> ---
>>  rust/kernel/io.rs | 31 ++++++++++++++++++++++++++++++-
>>  1 file changed, 30 insertions(+), 1 deletion(-)
>
> Looks like the case where you need this don't actually require a `Region`
> (dynamically sized type) but rather a fixed size window?
>
> In that case the next version of
> https://lore.kernel.org/rust-for-linux/20260721-typed_register-v1-0-452d72b60262@garyguo.net/
> will contain what you need.

Yeah you are right. I think a previous version I was working on needed
runtime lengths. But yes it should work with your compile time
subregion stuff.

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-08-07  5:39 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05  5:44 [PATCH 00/12] gpu: nova-core: add PRAMIN window support Eliot Courtney
2026-08-05  5:44 ` [PATCH 01/12] rust: io: add Region::try_subregion Eliot Courtney
2026-08-05 10:43   ` Gary Guo
2026-08-07  5:39     ` Eliot Courtney
2026-08-05  5:44 ` [PATCH 02/12] rust: num: reject Bounded::shr overshifts at build time Eliot Courtney
2026-08-05  5:44 ` [PATCH 03/12] rust: num: add Bounded::shr_exact Eliot Courtney
2026-08-05  5:44 ` [PATCH 04/12] gpu: nova-core: mm: Add VramAddress type Eliot Courtney
2026-08-05  5:44 ` [PATCH 05/12] gpu: nova-core: mm: Implement Alignable and Debug for VramAddress Eliot Courtney
2026-08-05  5:44 ` [PATCH 06/12] gpu: nova-core: mm: Add PRAMIN window registers Eliot Courtney
2026-08-05  5:44 ` [PATCH 07/12] gpu: nova-core: mm: Add the memory management HAL Eliot Courtney
2026-08-05  5:44 ` [PATCH 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM Eliot Courtney
2026-08-05  5:44 ` [PATCH 09/12] docs: gpu: nova-core: Document the PRAMIN aperture mechanism Eliot Courtney
2026-08-05  5:44 ` [PATCH 10/12] gpu: nova-core: mm: Add GpuMm centralized memory manager Eliot Courtney
2026-08-05  5:44 ` [PATCH 11/12] gpu: nova-core: Add self-test assertion macros and config option Eliot Courtney
2026-08-05  5:44 ` [PATCH 12/12] gpu: nova-core: mm: Add PRAMIN aperture self-tests Eliot Courtney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox