[PATCH 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
Eliot Courtney <[email protected]> Wed, 05 Aug 2026 14:44:55 +0900
| Newsgroups | gmane.linux.documentation,gmane.linux.kernel.rust,gmane.linux.kernel,gmane.comp.video.dri.devel |
|---|---|
| Message-ID | <[email protected]> |
From: Joel Fernandes <[email protected]> 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 <[email protected]> [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 <[email protected]> Signed-off-by: Eliot Courtney <[email protected]> --- 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