Linux Documentation
 help / color / mirror / Atom feed
From: Joel Fernandes <joelagnelf@nvidia.com>
To: linux-kernel@vger.kernel.org
Cc: Miguel Ojeda <ojeda@kernel.org>, Boqun Feng <boqun@kernel.org>,
	Gary Guo <gary@garyguo.net>,
	Bjorn Roy Baron <bjorn3_gh@protonmail.com>,
	Benno Lossin <lossin@kernel.org>,
	Andreas Hindborg <a.hindborg@kernel.org>,
	Alice Ryhl <aliceryhl@google.com>,
	Trevor Gross <tmgross@umich.edu>,
	Danilo Krummrich <dakr@kernel.org>,
	Dave Airlie <airlied@redhat.com>,
	Daniel Almeida <daniel.almeida@collabora.com>,
	dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org,
	nova-gpu@lists.linux.dev, Nikola Djukic <ndjukic@nvidia.com>,
	David Airlie <airlied@gmail.com>,
	Boqun Feng <boqun.feng@gmail.com>,
	John Hubbard <jhubbard@nvidia.com>,
	Alistair Popple <apopple@nvidia.com>,
	Timur Tabi <ttabi@nvidia.com>, Edwin Peer <epeer@nvidia.com>,
	Alexandre Courbot <acourbot@nvidia.com>,
	Andrea Righi <arighi@nvidia.com>,
	Andy Ritger <aritger@nvidia.com>, Zhi Wang <zhiw@nvidia.com>,
	Balbir Singh <balbirs@nvidia.com>,
	Philipp Stanner <phasta@kernel.org>,
	alexeyi@nvidia.com, Eliot Courtney <ecourtney@nvidia.com>,
	joel@joelfernandes.org, linux-doc@vger.kernel.org,
	Joel Fernandes <joelagnelf@nvidia.com>
Subject: [PATCH v1 12/12] gpu: nova-core: mm: Add PRAMIN aperture self-tests
Date: Mon, 18 May 2026 14:03:42 -0400	[thread overview]
Message-ID: <20260518180342.2387845-13-joelagnelf@nvidia.com> (raw)
In-Reply-To: <20260518180342.2387845-1-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_MM_SELFTESTS.
When enabled, tests run after GSP boot during probe.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/Kconfig      |  10 ++
 drivers/gpu/nova-core/driver.rs    |   2 +
 drivers/gpu/nova-core/gpu.rs       |   9 ++
 drivers/gpu/nova-core/mm.rs        |  16 +++
 drivers/gpu/nova-core/mm/pramin.rs | 214 +++++++++++++++++++++++++++++
 5 files changed, 251 insertions(+)

diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index f918f69e0599..abf10e82647b 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -15,3 +15,13 @@ 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_MM_SELFTESTS
+	bool "Memory management self-tests"
+	depends on NOVA_CORE
+	help
+	  Enable self-tests for the memory management subsystem. When enabled,
+	  tests are run during GPU probe to verify PRAMIN aperture access,
+	  page table walking, and BAR1 virtual memory mapping functionality.
+
+	  This is a testing option and is default-disabled.
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 84b0e1703150..77746d6949d7 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -96,6 +96,8 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, E
 
             Ok(try_pin_init!(Self {
                 gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?),
+                // Run optional GPU 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 38544c38d660..aa047fe91054 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -342,4 +342,13 @@ pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
             .inspect(|bar| self.sysmem_flush.unregister(bar))
             .is_err());
     }
+
+    /// Run selftests on the constructed [`Gpu`].
+    pub(crate) fn run_selftests(
+        self: Pin<&mut Self>,
+        pdev: &pci::Device<device::Bound>,
+    ) -> Result {
+        crate::mm::run_mm_selftests(pdev, &self.mm, self.spec.chipset)?;
+        Ok(())
+    }
 }
diff --git a/drivers/gpu/nova-core/mm.rs b/drivers/gpu/nova-core/mm.rs
index 5c1941d20d1b..08d74710f790 100644
--- a/drivers/gpu/nova-core/mm.rs
+++ b/drivers/gpu/nova-core/mm.rs
@@ -40,6 +40,7 @@ macro_rules! impl_pfn_bounded {
     device,
     devres::Devres,
     num::Bounded,
+    pci,
     prelude::*,
     sync::Arc, //
 };
@@ -83,6 +84,21 @@ pub(crate) fn pramin(&self) -> &pramin::Pramin {
     }
 }
 
+/// Run MM subsystem self-tests during probe.
+///
+/// No-op when `CONFIG_NOVA_MM_SELFTESTS` is not enabled.
+#[cfg_attr(not(CONFIG_NOVA_MM_SELFTESTS), allow(unused_variables))]
+pub(crate) fn run_mm_selftests(
+    pdev: &pci::Device<device::Bound>,
+    mm: &Arc<GpuMm>,
+    chipset: Chipset,
+) -> Result {
+    #[cfg(CONFIG_NOVA_MM_SELFTESTS)]
+    pramin::run_self_test(pdev.as_ref(), mm.pramin(), chipset)?;
+
+    Ok(())
+}
+
 bitfield! {
     /// Physical VRAM address in GPU video memory.
     pub(crate) struct VramAddress(u64) {
diff --git a/drivers/gpu/nova-core/mm/pramin.rs b/drivers/gpu/nova-core/mm/pramin.rs
index 38758ca971be..73d516c91c15 100644
--- a/drivers/gpu/nova-core/mm/pramin.rs
+++ b/drivers/gpu/nova-core/mm/pramin.rs
@@ -296,3 +296,217 @@ fn compute_window(
     define_pramin_write!(try_write32, u32);
     define_pramin_write!(try_write64, u64);
 }
+
+#[cfg(CONFIG_NOVA_MM_SELFTESTS)]
+mod selftest {
+    use super::*;
+    use crate::{
+        mm::VramAddress,
+        num::IntoSafeCast, //
+    };
+    use kernel::{
+        device,
+        prelude::*, //
+    };
+
+    /// Offset within the VRAM region to use as the self-test area.
+    const SELFTEST_REGION_OFFSET: u64 = 0x1000;
+
+    /// Test read/write at byte-aligned locations.
+    fn test_byte_readwrite(
+        dev: &kernel::device::Device,
+        win: &mut PraminWindow<'_>,
+        base: VramAddress,
+    ) -> Result {
+        for i in 0u8..4 {
+            let offset = base + 1 + u64::from(i);
+            let val = 0xA0 + i;
+            win.try_write8(offset, val)?;
+            let read_val = win.try_read8(offset)?;
+            if read_val != val {
+                dev_err!(
+                    dev,
+                    "PRAMIN: FAIL - offset {:#x}: wrote {:#x}, read {:#x}\n",
+                    offset,
+                    val,
+                    read_val
+                );
+                return Err(EIO);
+            }
+        }
+        Ok(())
+    }
+
+    /// Test writing a `u32` and reading back as individual `u8`s.
+    fn test_u32_as_bytes(
+        dev: &kernel::device::Device,
+        win: &mut PraminWindow<'_>,
+        base: VramAddress,
+    ) -> Result {
+        let offset = base + 0x10;
+        let val: u32 = 0xDEADBEEF;
+        win.try_write32(offset, val)?;
+
+        // Read back as individual bytes (little-endian: EF BE AD DE).
+        let expected_bytes: [u8; 4] = [0xEF, 0xBE, 0xAD, 0xDE];
+        for (i, &expected) in expected_bytes.iter().enumerate() {
+            let i_u64: u64 = i.into_safe_cast();
+            let read_val = win.try_read8(offset + i_u64)?;
+            if read_val != expected {
+                dev_err!(
+                    dev,
+                    "PRAMIN: FAIL - offset {:#x}: expected {:#x}, read {:#x}\n",
+                    offset + i_u64,
+                    expected,
+                    read_val
+                );
+                return Err(EIO);
+            }
+        }
+        Ok(())
+    }
+
+    /// Test window repositioning across 1MB boundaries.
+    fn test_window_reposition(
+        dev: &kernel::device::Device,
+        win: &mut PraminWindow<'_>,
+        base: VramAddress,
+    ) -> Result {
+        let offset_a = base;
+        let offset_b = base + 0x200000; // base + 2MB (different 1MB region).
+        let val_a: u32 = 0x11111111;
+        let val_b: u32 = 0x22222222;
+
+        win.try_write32(offset_a, val_a)?;
+        win.try_write32(offset_b, val_b)?;
+
+        let read_b = win.try_read32(offset_b)?;
+        if read_b != val_b {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - offset {:#x}: expected {:#x}, read {:#x}\n",
+                offset_b,
+                val_b,
+                read_b
+            );
+            return Err(EIO);
+        }
+
+        let read_a = win.try_read32(offset_a)?;
+        if read_a != val_a {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - offset {:#x}: expected {:#x}, read {:#x}\n",
+                offset_a,
+                val_a,
+                read_a
+            );
+            return Err(EIO);
+        }
+        Ok(())
+    }
+
+    /// Test that offsets outside the VRAM region are rejected.
+    fn test_invalid_offset(
+        dev: &kernel::device::Device,
+        win: &mut PraminWindow<'_>,
+        vram_end: VramAddress,
+    ) -> Result {
+        let result = win.try_read32(vram_end);
+        if result.is_ok() {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - read at invalid offset {:#x} should have failed\n",
+                vram_end
+            );
+            return Err(EIO);
+        }
+        Ok(())
+    }
+
+    /// Test that misaligned multi-byte accesses are rejected.
+    fn test_misaligned_access(
+        dev: &kernel::device::Device,
+        win: &mut PraminWindow<'_>,
+        base: VramAddress,
+    ) -> Result {
+        // `u16` at odd offset (not 2-byte aligned).
+        let offset_u16 = base + 0x21;
+        if win.try_write16(offset_u16, 0xABCD).is_ok() {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - misaligned u16 write at {:#x} should have failed\n",
+                offset_u16
+            );
+            return Err(EIO);
+        }
+
+        // `u32` at 2-byte-aligned (not 4-byte-aligned) offset.
+        let offset_u32 = base + 0x32;
+        if win.try_write32(offset_u32, 0x12345678).is_ok() {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - misaligned u32 write at {:#x} should have failed\n",
+                offset_u32
+            );
+            return Err(EIO);
+        }
+
+        // `u64` read at 4-byte-aligned (not 8-byte-aligned) offset.
+        let offset_u64 = base + 0x44;
+        if win.try_read64(offset_u64).is_ok() {
+            dev_err!(
+                dev,
+                "PRAMIN: FAIL - misaligned u64 read at {:#x} should have failed\n",
+                offset_u64
+            );
+            return Err(EIO);
+        }
+        Ok(())
+    }
+
+    /// Run PRAMIN self-tests during boot if self-tests are enabled.
+    pub(crate) fn run_self_test(
+        pdev: &device::Device<device::Bound>,
+        pramin: &Pramin,
+        chipset: crate::gpu::Chipset,
+    ) -> Result {
+        use crate::gpu::Architecture;
+
+        let dev = pdev;
+
+        // PRAMIN uses NV_PBUS_BAR0_WINDOW which is only available on pre-Hopper GPUs.
+        // Hopper+ uses NV_XAL_EP_BAR0_WINDOW instead, requiring a separate HAL that
+        // has not been implemented yet.
+        if !matches!(
+            chipset.arch(),
+            Architecture::Turing | Architecture::Ampere | Architecture::Ada
+        ) {
+            dev_info!(
+                dev,
+                "PRAMIN: Skipping self-tests for {:?} (only pre-Hopper supported)\n",
+                chipset
+            );
+            return Ok(());
+        }
+
+        dev_info!(dev, "PRAMIN: Starting self-test...\n");
+
+        let vram_region = pramin.vram_region();
+        let base = vram_region.start + SELFTEST_REGION_OFFSET;
+        let vram_end = vram_region.end;
+        let mut win = pramin.get_window(pdev)?;
+
+        test_byte_readwrite(dev, &mut win, base)?;
+        test_u32_as_bytes(dev, &mut win, base)?;
+        test_window_reposition(dev, &mut win, base)?;
+        test_invalid_offset(dev, &mut win, vram_end)?;
+        test_misaligned_access(dev, &mut win, base)?;
+
+        dev_info!(dev, "PRAMIN: All self-tests PASSED\n");
+        Ok(())
+    }
+}
+
+#[cfg(CONFIG_NOVA_MM_SELFTESTS)]
+pub(crate) use selftest::run_self_test;
-- 
2.34.1


      parent reply	other threads:[~2026-05-18 18:04 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-18 18:03 [PATCH v1 00/12] Introduce nova-core mm prerequisites Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 01/12] rust: pci: add resource_flags accessor Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 02/12] rust: bitfield: support cast+shift accessor syntax Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 03/12] gpu: nova-core: gsp: Return GspStaticInfo from boot() Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 04/12] gpu: nova-core: gsp: Extract usable FB region from GSP Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 05/12] gpu: nova-core: gsp: Expose total physical VRAM end from FB region info Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 06/12] gpu: nova-core: mm: Add Pfn (Physical Frame Number) type Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 07/12] gpu: nova-core: mm: Add VramAddress type and conversion traits Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 08/12] gpu: nova-core: mm: Add VramAddress arithmetic and ordering Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 09/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 10/12] docs: gpu: nova-core: Document the PRAMIN aperture mechanism Joel Fernandes
2026-05-18 18:03 ` [PATCH v1 11/12] gpu: nova-core: mm: Add GpuMm centralized memory manager Joel Fernandes
2026-05-18 18:03 ` Joel Fernandes [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260518180342.2387845-13-joelagnelf@nvidia.com \
    --to=joelagnelf@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=airlied@redhat.com \
    --cc=alexeyi@nvidia.com \
    --cc=aliceryhl@google.com \
    --cc=apopple@nvidia.com \
    --cc=arighi@nvidia.com \
    --cc=aritger@nvidia.com \
    --cc=balbirs@nvidia.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=ecourtney@nvidia.com \
    --cc=epeer@nvidia.com \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=joel@joelfernandes.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ndjukic@nvidia.com \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=phasta@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=ttabi@nvidia.com \
    --cc=zhiw@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox