Re: [PATCH v2 08/12] gpu: nova-core: mm: Add support to use PRAMIN windows to write to VRAM
Alistair Popple <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On 2026-08-10 at 23:55 +1000, Eliot Courtney <[email protected]> wrote... > 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 and register projections] > [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: declare the window location, 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 | 178 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 179 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..20be3fc471ba > --- /dev/null > +++ b/drivers/gpu/nova-core/mm/pramin.rs > @@ -0,0 +1,178 @@ > +// 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_project, > + register, > + register::OffsetLoc, > + Io, > + Mmio, // > + }, > + prelude::*, > + ptr::{ > + Alignable, > + Alignment, // > + }, > + sizes::{ > + SZ_1M, > + SZ_64K, // > + }, > +}; > + > +use crate::{ > + driver::{ > + Bar0, > + NovaRegisters, // > + }, > + gpu::Chipset, > + mm::{ > + hal::{ > + self, > + MmHal, // > + }, > + VramAddress, // > + }, > + num::IntoSafeCast, // > +}; > + > +/// Size of the PRAMIN window (1 MiB). > +const WINDOW_SIZE: usize = SZ_1M; > + > +/// The PRAMIN window, which is a 1 MiB window into VRAM at a fixed BAR0 offset. > +#[derive(FromBytes, IntoBytes)] > +struct PraminWindow([u8; WINDOW_SIZE]); > + > +register! { > + base: NovaRegisters; I tried building this on drm-rust-next against the base commit (4c9ba407018e8deb06dbc643112bac8f40404f95) but am getting some build errors: error: no rules expected `:` --> /data/source/linux/drivers/gpu/nova-core/mm/pramin.rs:51:9 | 51 | base: NovaRegisters; | ^ no rules expected this token in macro call | note: while trying to match `(` --> /data/source/linux/rust/kernel/io/register.rs:809:52 | 809 | $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) | ^ I'm guessing maybe because this depends on some other series that isn't in drm-rust-next or something in drm-rust-next has changed things here. There are a couple of others with this series applied as well: error: no rules expected `build` --> /data/source/linux/drivers/gpu/nova-core/mm/pramin.rs:115:39 | 115 | let window = io_project!(bar, build: PRAMIN); | ^^^^^ no rules expected this token in macro call | note: while trying to match `,` --> /data/source/linux/rust/kernel/ptr/projection.rs:319:21 | 319 | (@gen $ptr:ident, ) => {}; | ^ error: no rules expected reserved keyword `try` --> /data/source/linux/drivers/gpu/nova-core/mm/pramin.rs:174:45 | 174 | let view = io_project!(self.window, try: OffsetLoc::new(offset)); | ^^^ no rules expected this token in macro call | note: while trying to match `,` --> /data/source/linux/rust/kernel/ptr/projection.rs:319:21 | 319 | (@gen $ptr:ident, ) => {}; | ^ error: no rules expected `:` --> /data/source/linux/drivers/gpu/nova-core/mm/regs.rs:29:9 | 29 | base: NovaRegisters; | ^ no rules expected this token in macro call | note: while trying to match `(` --> /data/source/linux/rust/kernel/io/register.rs:809:52 | 809 | $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) | ^ error: no rules expected `:` --> /data/source/linux/drivers/gpu/nova-core/mm/regs.rs:46:13 | 46 | base: NovaRegisters; | ^ no rules expected this token in macro call | note: while trying to match `(` --> /data/source/linux/rust/kernel/io/register.rs:809:52 | 809 | $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) | ^ error: no rules expected `:` --> /data/source/linux/drivers/gpu/nova-core/mm/regs.rs:62:13 | 62 | base: NovaRegisters; | ^ no rules expected this token in macro call | note: while trying to match `(` --> /data/source/linux/rust/kernel/io/register.rs:809:52 | 809 | $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) | ^ error: aborting due to 6 previous errors - Alistair > + > + /// Location of the window inside BAR0. > + PRAMIN: PraminWindow @ 0x700000; > +} > + > +/// 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, PraminWindow>, > + /// 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 [`PraminAccess::view()`] cannot outlive this access, so the ordering point covers every > +/// write made through them. > +pub(super) struct PraminAccess<'a, T> > +where > + T: FromBytes + IntoBytes, > +{ > + view: Mmio<'a, T>, > +} > + > +impl<T> PraminAccess<'_, T> > +where > + T: FromBytes + IntoBytes, > +{ > + /// Returns the MMIO view of the accessed location. > + pub(super) fn view(&self) -> Mmio<'_, T> { > + self.view > + } > +} > + > +impl<T> Drop for PraminAccess<'_, T> > +where > + T: FromBytes + IntoBytes, > +{ > + fn drop(&mut self) { > + // Insert an ordering point after previous writes through this window. > + self.view.cast::<u8>().read_val(); > + } > +} > + > +impl<'gpu> Pramin<'gpu> { > + /// 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 = io_project!(bar, build: PRAMIN); > + let base = vram_range.start.align_down(Self::BASE_ALIGN); > + let window_range = Self::window_range(base)?; > + hal.write_pramin_window_base(bar, base)?; > + > + Ok(Self { > + bar, > + hal, > + window, > + vram_range, > + window_range, > + }) > + } > + > + /// Returns the VRAM range a window based at `base` exposes. > + fn window_range(base: VramAddress) -> Result<Range<VramAddress>> { > + let end = base > + .checked_add(WINDOW_SIZE.into_safe_cast()) > + .ok_or(EINVAL)?; > + Ok(base..end) > + } > + > + /// 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 = Self::window_range(base)?; > + 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`. > + /// > + /// Returns an error if `vram_addr` is not aligned 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<PraminAccess<'a, T>> > + where > + T: FromBytes + IntoBytes, > + { > + let offset = self.window_offset(vram_addr, size_of::<T>())?; > + let view = io_project!(self.window, try: OffsetLoc::new(offset)); > + > + Ok(PraminAccess { view }) > + } > +} > > -- > 2.55.0 >