mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
gpu: nova-core: mm: Add PRAMIN aperture self-tests
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> Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://patch.msgid.link/20260827-pramin-split-v3-10-24b24d7afc52@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
parent
2c76c4ec9e
commit
e6a2c988ed
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -429,6 +429,18 @@ pub(crate) fn new<'a>(
|
|||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the boot0 register and returns its raw value.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,3 +176,137 @@ pub(super) fn window_at<'a, T>(
|
|||
Ok(PraminAccess { view })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
|
||||
pub(super) mod selftest {
|
||||
use kernel::{
|
||||
device,
|
||||
io::io_read,
|
||||
sizes::SizeConstants, //
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
selftest_assert,
|
||||
selftest_assert_eq, //
|
||||
};
|
||||
|
||||
/// Test read/write at byte granularity, at unaligned addresses.
|
||||
fn test_byte_readwrite(
|
||||
dev: &device::Device<device::Bound>,
|
||||
pramin: &mut Pramin<'_>,
|
||||
base: VramAddress,
|
||||
) -> Result {
|
||||
for i in 0u8..4 {
|
||||
let addr = base + 1 + u64::from(i);
|
||||
pramin.window_at::<u8>(addr)?.view().write_val(0xA0 + i);
|
||||
}
|
||||
|
||||
for i in 0u8..4 {
|
||||
let addr = base + 1 + u64::from(i);
|
||||
selftest_assert_eq!(
|
||||
dev,
|
||||
pramin.window_at::<u8>(addr)?.view().read_val(),
|
||||
0xA0 + i
|
||||
);
|
||||
}
|
||||
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 {
|
||||
// `u16` at odd offset (not 2-byte aligned).
|
||||
selftest_assert!(dev, pramin.window_at::<u16>(base + 0x21).is_err());
|
||||
|
||||
// `u32` at 2-byte-aligned (not 4-byte-aligned) offset.
|
||||
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());
|
||||
|
||||
// A `u16` view at an even address is allowed.
|
||||
pramin.window_at::<u16>(base + 0x22)?;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user