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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.