rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC] nova-core: mm: Add initial structures for page table management
@ 2025-10-06 21:09 Joel Fernandes
  2025-10-06 21:11 ` Joel Fernandes
  2025-10-15  2:08 ` John Hubbard
  0 siblings, 2 replies; 4+ messages in thread
From: Joel Fernandes @ 2025-10-06 21:09 UTC (permalink / raw)
  To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
  Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
	Timur Tabi, joel, Elle Rhumsaa, Daniel Almeida, Andrea Righi,
	nouveau

Add initial structures and helpers for page table management. Will
expand and build on this in later patches. Uses bitstruct for clean
code.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
Rebased on rust-drm-next + bitfield patches [1].

[1] https://lore.kernel.org/all/20251003154748.1687160-1-joelagnelf@nvidia.com/

 drivers/gpu/nova-core/falcon/gsp.rs |   2 +-
 drivers/gpu/nova-core/mm/mod.rs     |   3 +
 drivers/gpu/nova-core/mm/types.rs   | 382 ++++++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs  |   1 +
 4 files changed, 387 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/nova-core/mm/mod.rs
 create mode 100644 drivers/gpu/nova-core/mm/types.rs

diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
index cd4960e997c8..839c803001b5 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -5,7 +5,7 @@
 use crate::{
     driver::Bar0,
     falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
-    regs::self,
+    regs,
 };
 
 /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
diff --git a/drivers/gpu/nova-core/mm/mod.rs b/drivers/gpu/nova-core/mm/mod.rs
new file mode 100644
index 000000000000..4cebfa3cf184
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/mod.rs
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+
+pub(crate) mod types;
diff --git a/drivers/gpu/nova-core/mm/types.rs b/drivers/gpu/nova-core/mm/types.rs
new file mode 100644
index 000000000000..6eca8bcb24a6
--- /dev/null
+++ b/drivers/gpu/nova-core/mm/types.rs
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0
+//! Page table data management for NVIDIA GPUs
+
+#![expect(dead_code)]
+
+use kernel::bitfield;
+
+/// Memory size constants
+pub(crate) const KB: usize = 1024;
+pub(crate) const MB: usize = KB * 1024;
+
+/// Page size: 4 KiB
+pub(crate) const PAGE_SIZE: usize = 4 * KB;
+
+/// Page Table Level hierarchy
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub(crate) enum PageTableLevel {
+    Pdb, // Level 0 - Page Directory Base
+    L1,  // Level 1
+    L2,  // Level 2
+    L3,  // Level 3 - Dual PDE (128-bit entries)
+    L4,  // Level 4 - PTEs
+}
+
+impl PageTableLevel {
+    /// Get the entry size for this level
+    pub(crate) fn entry_size(&self) -> usize {
+        match self {
+            Self::L3 => 16, // 128-bit dual PDE
+            _ => 8,         // 64-bit PDE/PTE
+        }
+    }
+
+    /// PDE levels constant array for iteration
+    const PDE_LEVELS: [PageTableLevel; 4] = [
+        PageTableLevel::Pdb,
+        PageTableLevel::L1,
+        PageTableLevel::L2,
+        PageTableLevel::L3,
+    ];
+
+    /// Get iterator over PDE levels
+    pub(crate) fn pde_levels() -> impl Iterator<Item = PageTableLevel> {
+        Self::PDE_LEVELS.into_iter()
+    }
+}
+
+/// Memory aperture for Page Directory Entries (PDEs)
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Default)]
+pub(crate) enum AperturePde {
+    #[default]
+    Invalid = 0,
+    VideoMemory = 1,
+    SystemCoherent = 2,
+    SystemNonCoherent = 3,
+}
+
+impl From<u8> for AperturePde {
+    fn from(val: u8) -> Self {
+        match val {
+            1 => Self::VideoMemory,
+            2 => Self::SystemCoherent,
+            3 => Self::SystemNonCoherent,
+            _ => Self::Invalid,
+        }
+    }
+}
+
+impl From<AperturePde> for u8 {
+    fn from(val: AperturePde) -> Self {
+        val as u8
+    }
+}
+
+impl From<AperturePde> for u64 {
+    fn from(val: AperturePde) -> Self {
+        val as u64
+    }
+}
+
+/// Memory aperture for Page Table Entries (PTEs)
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Default)]
+pub(crate) enum AperturePte {
+    #[default]
+    VideoMemory = 0,
+    PeerVideoMemory = 1,
+    SystemCoherent = 2,
+    SystemNonCoherent = 3,
+}
+
+impl From<u8> for AperturePte {
+    fn from(val: u8) -> Self {
+        match val {
+            0 => Self::VideoMemory,
+            1 => Self::PeerVideoMemory,
+            2 => Self::SystemCoherent,
+            3 => Self::SystemNonCoherent,
+            _ => Self::VideoMemory,
+        }
+    }
+}
+
+impl From<AperturePte> for u8 {
+    fn from(val: AperturePte) -> Self {
+        val as u8
+    }
+}
+
+impl From<AperturePte> for u64 {
+    fn from(val: AperturePte) -> Self {
+        val as u64
+    }
+}
+
+/// Common trait for address types
+pub(crate) trait Address {
+    /// Get raw u64 value
+    fn raw(&self) -> u64;
+
+    /// Get the frame number (upper 52 bits)
+    fn frame_number(&self) -> u64 {
+        self.raw() >> 12
+    }
+
+    /// Get the offset within the frame (lower 12 bits)
+    fn frame_offset(&self) -> u16 {
+        (self.raw() & 0xFFF) as u16
+    }
+
+    /// Convert to usize
+    fn to_usize(&self) -> usize {
+        self.raw() as usize
+    }
+}
+
+// VRAM address representation - used with Pfn for GPU video memory allocation
+// and page table entries.
+bitfield! {
+    pub(crate) struct VramAddress(u64) {
+        11:0    offset          as u16;    // Offset within 4KB page
+        63:12   frame_number    as u64;    // Frame number (52 bits)
+    }
+}
+
+impl Address for VramAddress {
+    fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+impl From<u64> for VramAddress {
+    fn from(val: u64) -> Self {
+        VramAddress(val)
+    }
+}
+
+impl From<usize> for VramAddress {
+    fn from(val: usize) -> Self {
+        VramAddress(val as u64)
+    }
+}
+
+impl From<Pfn> for VramAddress {
+    fn from(pfn: Pfn) -> VramAddress {
+        VramAddress::default().set_frame_number(pfn.raw())
+    }
+}
+
+// Virtual address
+bitfield! {
+    pub(crate) struct VirtualAddress(u64) {
+        11:0    offset      as u16;    // Offset within 4KB page
+        20:12   l4_index    as u16;    // Level 4 index (PTE)
+        29:21   l3_index    as u16;    // Level 3 index
+        38:30   l2_index    as u16;    // Level 2 index
+        47:39   l1_index    as u16;    // Level 1 index
+        56:48   l0_index    as u16;    // Level 0 index (PDB)
+    }
+}
+
+impl VirtualAddress {
+    /// Get index for a specific page table level
+    pub(crate) fn level_index(&self, level: PageTableLevel) -> u16 {
+        match level {
+            PageTableLevel::Pdb => self.l0_index(),
+            PageTableLevel::L1 => self.l1_index(),
+            PageTableLevel::L2 => self.l2_index(),
+            PageTableLevel::L3 => self.l3_index(),
+            PageTableLevel::L4 => self.l4_index(),
+        }
+    }
+}
+
+impl Address for VirtualAddress {
+    fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+impl From<u64> for VirtualAddress {
+    fn from(addr: u64) -> Self {
+        Self(addr)
+    }
+}
+
+impl From<Vfn> for VirtualAddress {
+    fn from(vfn: Vfn) -> VirtualAddress {
+        VirtualAddress::from(vfn.raw() << 12)
+    }
+}
+
+// Page Table Entry (PTE) - 64-bit entry at level 4
+// Note: PTE has V=1 for valid, and includes aperture bits
+bitfield! {
+    pub(crate) struct Pte(u64) {
+        0:0     valid           as bool;    // (1 = valid for PTEs)
+        1:1     privilege       as bool;    // P - Privileged/kernel-only access
+        2:2     read_only       as bool;    // RO - Write protection
+        3:3     atomic_disable  as bool;    // AD - Disable atomic ops
+        4:4     encrypted       as bool;    // E - Encryption enabled
+        39:8    frame_number    as u64;     // PA[39:8] - Physical frame number (32 bits)
+        41:40   aperture        as u8 => AperturePte;   // Memory aperture type.
+        42:42   volatile        as bool;    // VOL - Volatile flag
+        50:43   kind            as u8;      // K[7:0] - Compression/tiling kind
+        63:51   comptag_line    as u16;     // CTL[12:0] - Compression tag line (partial, shared with PA)
+    }
+}
+
+impl Pte {
+    /// Set the address for this PTE
+    pub(crate) fn set_address(&mut self, addr: VramAddress) {
+        self.set_frame_number(addr.frame_number());
+    }
+
+    /// Get the address from this PTE
+    pub(crate) fn address(&self) -> VramAddress {
+        VramAddress::default().set_frame_number(self.frame_number())
+    }
+
+    /// Get raw u64 value
+    pub(crate) fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+// Page Directory Entry (PDE) - 64-bit entry at levels 0-2
+// Note: V=0 means valid for PDEs (inverted), aperture=0 means invalid
+bitfield! {
+    pub(crate) struct Pde(u64) {
+        0:0     valid_inverted       as bool;    // V - Valid bit (0=valid for PDEs)
+        2:1     aperture             as u8 => AperturePde;      // Memory aperture type
+        3:3     volatile             as bool;    // VOL - Volatile flag
+        39:8    table_frame_number   as u64;     // PA[39:8] - Table frame number (32 bits)
+    }
+}
+
+impl Pde {
+    /// Check if PDE is valid
+    pub(crate) fn is_valid(&self) -> bool {
+        !self.valid_inverted() && self.aperture() != AperturePde::Invalid
+    }
+
+    /// The valid bit is inverted so add an accessor to flip it
+    pub(crate) fn set_valid(&self, value: bool) -> Pde {
+        self.set_valid_inverted(!value)
+    }
+
+    /// Set the table address for this PDE
+    pub(crate) fn set_table_address(&mut self, addr: VramAddress) {
+        self.set_table_frame_number(addr.frame_number());
+    }
+
+    /// Get the table address from this PDE
+    pub(crate) fn table_address(&self) -> VramAddress {
+        VramAddress::default().set_frame_number(self.table_frame_number())
+    }
+
+    /// Get raw u64 value
+    pub(crate) fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+/// Dual PDE at Level 3 - 128-bit entry containing both LPT and SPT pointers
+/// Lower 64 bits = big/large page, upper 64 bits = small page
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct DualPde {
+    pub lpt: Pde, // Large/Big Page Table pointer (2MB pages) - bits 63:0 (lower)
+    pub spt: Pde, // Small Page Table pointer (4KB pages) - bits 127:64 (upper)
+}
+
+impl DualPde {
+    /// Create a new empty dual PDE
+    pub(crate) fn new() -> Self {
+        Self {
+            spt: Pde::default(),
+            lpt: Pde::default(),
+        }
+    }
+
+    /// Set the Small Page Table address with aperture
+    pub(crate) fn set_small_pt_address(&mut self, addr: VramAddress, aperture: AperturePde) {
+        self.spt = Pde::default()
+            .set_valid(true)
+            .set_table_frame_number(addr.frame_number())
+            .set_aperture(aperture);
+    }
+
+    /// Set the Large Page Table address with aperture
+    pub(crate) fn set_large_pt_address(&mut self, addr: VramAddress, aperture: AperturePde) {
+        self.lpt = Pde::default()
+            .set_valid(true)
+            .set_table_frame_number(addr.frame_number())
+            .set_aperture(aperture);
+    }
+
+    /// Check if has valid Small Page Table
+    pub(crate) fn has_small_pt_address(&self) -> bool {
+        self.spt.is_valid()
+    }
+
+    /// Check if has valid Large Page Table
+    pub(crate) fn has_large_pt_address(&self) -> bool {
+        self.lpt.is_valid()
+    }
+
+    /// Set SPT (Small Page Table) using Pfn
+    pub(crate) fn set_spt(&mut self, pfn: Pfn, aperture: AperturePde) {
+        self.spt = Pde::default()
+            .set_valid(true)
+            .set_aperture(aperture)
+            .set_table_frame_number(pfn.raw());
+    }
+}
+
+/// Virtual Frame Number - virtual address divided by 4KB
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct Vfn(u64);
+
+impl Vfn {
+    /// Create a new VFN from a frame number
+    pub(crate) const fn new(frame_number: u64) -> Self {
+        Self(frame_number)
+    }
+
+    /// Get raw frame number
+    pub(crate) const fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+impl From<VirtualAddress> for Vfn {
+    fn from(vaddr: VirtualAddress) -> Self {
+        Self(vaddr.frame_number())
+    }
+}
+
+/// Physical Frame Number - physical address divided by 4KB
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct Pfn(u64);
+
+impl Pfn {
+    /// Create a new PFN from a frame number
+    pub(crate) const fn new(frame_number: u64) -> Self {
+        Self(frame_number)
+    }
+
+    /// Get raw frame number
+    pub(crate) const fn raw(&self) -> u64 {
+        self.0
+    }
+}
+
+impl From<VramAddress> for Pfn {
+    fn from(addr: VramAddress) -> Self {
+        let frame = addr.frame_number();
+        Self(frame)
+    }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index fffcaee2249f..ff994f1ec9b9 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -10,6 +10,7 @@
 mod gfw;
 mod gpu;
 mod gsp;
+mod mm;
 mod regs;
 mod util;
 mod vbios;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH RFC] nova-core: mm: Add initial structures for page table management
  2025-10-06 21:09 [PATCH RFC] nova-core: mm: Add initial structures for page table management Joel Fernandes
@ 2025-10-06 21:11 ` Joel Fernandes
  2025-10-15  2:08 ` John Hubbard
  1 sibling, 0 replies; 4+ messages in thread
From: Joel Fernandes @ 2025-10-06 21:11 UTC (permalink / raw)
  To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
  Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
	Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau

On 10/6/2025 5:09 PM, Joel Fernandes wrote:
> Add initial structures and helpers for page table management. Will
> expand and build on this in later patches. Uses bitstruct for clean
> code.

Sorry this commit message needs an update, bitstruct is now called bitfield
(this is just an initial RFC).

Thanks.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH RFC] nova-core: mm: Add initial structures for page table management
  2025-10-06 21:09 [PATCH RFC] nova-core: mm: Add initial structures for page table management Joel Fernandes
  2025-10-06 21:11 ` Joel Fernandes
@ 2025-10-15  2:08 ` John Hubbard
  2025-10-15 20:34   ` Joel Fernandes
  1 sibling, 1 reply; 4+ messages in thread
From: John Hubbard @ 2025-10-15  2:08 UTC (permalink / raw)
  To: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
	acourbot
  Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Timur Tabi, joel, Elle Rhumsaa,
	Daniel Almeida, Andrea Righi, nouveau

On 10/6/25 2:09 PM, Joel Fernandes wrote:
> Add initial structures and helpers for page table management. Will
> expand and build on this in later patches. Uses bitstruct for clean
> code.
> 
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> Rebased on rust-drm-next + bitfield patches [1].
> 
> [1] https://lore.kernel.org/all/20251003154748.1687160-1-joelagnelf@nvidia.com/
> 
>  drivers/gpu/nova-core/falcon/gsp.rs |   2 +-
>  drivers/gpu/nova-core/mm/mod.rs     |   3 +
>  drivers/gpu/nova-core/mm/types.rs   | 382 ++++++++++++++++++++++++++++
>  drivers/gpu/nova-core/nova_core.rs  |   1 +
>  4 files changed, 387 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/nova-core/mm/mod.rs
>  create mode 100644 drivers/gpu/nova-core/mm/types.rs
> 

Hi Joel,

This will be easier to provide a meaningful review for, once there are
some callers to look at. Structures alone are very difficult to review,
in the absence of behaviors (callers).


thanks,
John Hubbard

> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
> index cd4960e997c8..839c803001b5 100644
> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> @@ -5,7 +5,7 @@
>  use crate::{
>      driver::Bar0,
>      falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
> -    regs::self,
> +    regs,
>  };
>  
>  /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
> diff --git a/drivers/gpu/nova-core/mm/mod.rs b/drivers/gpu/nova-core/mm/mod.rs
> new file mode 100644
> index 000000000000..4cebfa3cf184
> --- /dev/null
> +++ b/drivers/gpu/nova-core/mm/mod.rs
> @@ -0,0 +1,3 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +pub(crate) mod types;
> diff --git a/drivers/gpu/nova-core/mm/types.rs b/drivers/gpu/nova-core/mm/types.rs
> new file mode 100644
> index 000000000000..6eca8bcb24a6
> --- /dev/null
> +++ b/drivers/gpu/nova-core/mm/types.rs
> @@ -0,0 +1,382 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//! Page table data management for NVIDIA GPUs
> +
> +#![expect(dead_code)]
> +
> +use kernel::bitfield;
> +
> +/// Memory size constants
> +pub(crate) const KB: usize = 1024;
> +pub(crate) const MB: usize = KB * 1024;
> +
> +/// Page size: 4 KiB
> +pub(crate) const PAGE_SIZE: usize = 4 * KB;
> +
> +/// Page Table Level hierarchy
> +#[derive(Debug, Clone, Copy, PartialEq)]
> +pub(crate) enum PageTableLevel {
> +    Pdb, // Level 0 - Page Directory Base
> +    L1,  // Level 1
> +    L2,  // Level 2
> +    L3,  // Level 3 - Dual PDE (128-bit entries)
> +    L4,  // Level 4 - PTEs
> +}
> +
> +impl PageTableLevel {
> +    /// Get the entry size for this level
> +    pub(crate) fn entry_size(&self) -> usize {
> +        match self {
> +            Self::L3 => 16, // 128-bit dual PDE
> +            _ => 8,         // 64-bit PDE/PTE
> +        }
> +    }
> +
> +    /// PDE levels constant array for iteration
> +    const PDE_LEVELS: [PageTableLevel; 4] = [
> +        PageTableLevel::Pdb,
> +        PageTableLevel::L1,
> +        PageTableLevel::L2,
> +        PageTableLevel::L3,
> +    ];
> +
> +    /// Get iterator over PDE levels
> +    pub(crate) fn pde_levels() -> impl Iterator<Item = PageTableLevel> {
> +        Self::PDE_LEVELS.into_iter()
> +    }
> +}
> +
> +/// Memory aperture for Page Directory Entries (PDEs)
> +#[repr(u8)]
> +#[derive(Debug, Clone, Copy, PartialEq, Default)]
> +pub(crate) enum AperturePde {
> +    #[default]
> +    Invalid = 0,
> +    VideoMemory = 1,
> +    SystemCoherent = 2,
> +    SystemNonCoherent = 3,
> +}
> +
> +impl From<u8> for AperturePde {
> +    fn from(val: u8) -> Self {
> +        match val {
> +            1 => Self::VideoMemory,
> +            2 => Self::SystemCoherent,
> +            3 => Self::SystemNonCoherent,
> +            _ => Self::Invalid,
> +        }
> +    }
> +}
> +
> +impl From<AperturePde> for u8 {
> +    fn from(val: AperturePde) -> Self {
> +        val as u8
> +    }
> +}
> +
> +impl From<AperturePde> for u64 {
> +    fn from(val: AperturePde) -> Self {
> +        val as u64
> +    }
> +}
> +
> +/// Memory aperture for Page Table Entries (PTEs)
> +#[repr(u8)]
> +#[derive(Debug, Clone, Copy, PartialEq, Default)]
> +pub(crate) enum AperturePte {
> +    #[default]
> +    VideoMemory = 0,
> +    PeerVideoMemory = 1,
> +    SystemCoherent = 2,
> +    SystemNonCoherent = 3,
> +}
> +
> +impl From<u8> for AperturePte {
> +    fn from(val: u8) -> Self {
> +        match val {
> +            0 => Self::VideoMemory,
> +            1 => Self::PeerVideoMemory,
> +            2 => Self::SystemCoherent,
> +            3 => Self::SystemNonCoherent,
> +            _ => Self::VideoMemory,
> +        }
> +    }
> +}
> +
> +impl From<AperturePte> for u8 {
> +    fn from(val: AperturePte) -> Self {
> +        val as u8
> +    }
> +}
> +
> +impl From<AperturePte> for u64 {
> +    fn from(val: AperturePte) -> Self {
> +        val as u64
> +    }
> +}
> +
> +/// Common trait for address types
> +pub(crate) trait Address {
> +    /// Get raw u64 value
> +    fn raw(&self) -> u64;
> +
> +    /// Get the frame number (upper 52 bits)
> +    fn frame_number(&self) -> u64 {
> +        self.raw() >> 12
> +    }
> +
> +    /// Get the offset within the frame (lower 12 bits)
> +    fn frame_offset(&self) -> u16 {
> +        (self.raw() & 0xFFF) as u16
> +    }
> +
> +    /// Convert to usize
> +    fn to_usize(&self) -> usize {
> +        self.raw() as usize
> +    }
> +}
> +
> +// VRAM address representation - used with Pfn for GPU video memory allocation
> +// and page table entries.
> +bitfield! {
> +    pub(crate) struct VramAddress(u64) {
> +        11:0    offset          as u16;    // Offset within 4KB page
> +        63:12   frame_number    as u64;    // Frame number (52 bits)
> +    }
> +}
> +
> +impl Address for VramAddress {
> +    fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +impl From<u64> for VramAddress {
> +    fn from(val: u64) -> Self {
> +        VramAddress(val)
> +    }
> +}
> +
> +impl From<usize> for VramAddress {
> +    fn from(val: usize) -> Self {
> +        VramAddress(val as u64)
> +    }
> +}
> +
> +impl From<Pfn> for VramAddress {
> +    fn from(pfn: Pfn) -> VramAddress {
> +        VramAddress::default().set_frame_number(pfn.raw())
> +    }
> +}
> +
> +// Virtual address
> +bitfield! {
> +    pub(crate) struct VirtualAddress(u64) {
> +        11:0    offset      as u16;    // Offset within 4KB page
> +        20:12   l4_index    as u16;    // Level 4 index (PTE)
> +        29:21   l3_index    as u16;    // Level 3 index
> +        38:30   l2_index    as u16;    // Level 2 index
> +        47:39   l1_index    as u16;    // Level 1 index
> +        56:48   l0_index    as u16;    // Level 0 index (PDB)
> +    }
> +}
> +
> +impl VirtualAddress {
> +    /// Get index for a specific page table level
> +    pub(crate) fn level_index(&self, level: PageTableLevel) -> u16 {
> +        match level {
> +            PageTableLevel::Pdb => self.l0_index(),
> +            PageTableLevel::L1 => self.l1_index(),
> +            PageTableLevel::L2 => self.l2_index(),
> +            PageTableLevel::L3 => self.l3_index(),
> +            PageTableLevel::L4 => self.l4_index(),
> +        }
> +    }
> +}
> +
> +impl Address for VirtualAddress {
> +    fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +impl From<u64> for VirtualAddress {
> +    fn from(addr: u64) -> Self {
> +        Self(addr)
> +    }
> +}
> +
> +impl From<Vfn> for VirtualAddress {
> +    fn from(vfn: Vfn) -> VirtualAddress {
> +        VirtualAddress::from(vfn.raw() << 12)
> +    }
> +}
> +
> +// Page Table Entry (PTE) - 64-bit entry at level 4
> +// Note: PTE has V=1 for valid, and includes aperture bits
> +bitfield! {
> +    pub(crate) struct Pte(u64) {
> +        0:0     valid           as bool;    // (1 = valid for PTEs)
> +        1:1     privilege       as bool;    // P - Privileged/kernel-only access
> +        2:2     read_only       as bool;    // RO - Write protection
> +        3:3     atomic_disable  as bool;    // AD - Disable atomic ops
> +        4:4     encrypted       as bool;    // E - Encryption enabled
> +        39:8    frame_number    as u64;     // PA[39:8] - Physical frame number (32 bits)
> +        41:40   aperture        as u8 => AperturePte;   // Memory aperture type.
> +        42:42   volatile        as bool;    // VOL - Volatile flag
> +        50:43   kind            as u8;      // K[7:0] - Compression/tiling kind
> +        63:51   comptag_line    as u16;     // CTL[12:0] - Compression tag line (partial, shared with PA)
> +    }
> +}
> +
> +impl Pte {
> +    /// Set the address for this PTE
> +    pub(crate) fn set_address(&mut self, addr: VramAddress) {
> +        self.set_frame_number(addr.frame_number());
> +    }
> +
> +    /// Get the address from this PTE
> +    pub(crate) fn address(&self) -> VramAddress {
> +        VramAddress::default().set_frame_number(self.frame_number())
> +    }
> +
> +    /// Get raw u64 value
> +    pub(crate) fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +// Page Directory Entry (PDE) - 64-bit entry at levels 0-2
> +// Note: V=0 means valid for PDEs (inverted), aperture=0 means invalid
> +bitfield! {
> +    pub(crate) struct Pde(u64) {
> +        0:0     valid_inverted       as bool;    // V - Valid bit (0=valid for PDEs)
> +        2:1     aperture             as u8 => AperturePde;      // Memory aperture type
> +        3:3     volatile             as bool;    // VOL - Volatile flag
> +        39:8    table_frame_number   as u64;     // PA[39:8] - Table frame number (32 bits)
> +    }
> +}
> +
> +impl Pde {
> +    /// Check if PDE is valid
> +    pub(crate) fn is_valid(&self) -> bool {
> +        !self.valid_inverted() && self.aperture() != AperturePde::Invalid
> +    }
> +
> +    /// The valid bit is inverted so add an accessor to flip it
> +    pub(crate) fn set_valid(&self, value: bool) -> Pde {
> +        self.set_valid_inverted(!value)
> +    }
> +
> +    /// Set the table address for this PDE
> +    pub(crate) fn set_table_address(&mut self, addr: VramAddress) {
> +        self.set_table_frame_number(addr.frame_number());
> +    }
> +
> +    /// Get the table address from this PDE
> +    pub(crate) fn table_address(&self) -> VramAddress {
> +        VramAddress::default().set_frame_number(self.table_frame_number())
> +    }
> +
> +    /// Get raw u64 value
> +    pub(crate) fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +/// Dual PDE at Level 3 - 128-bit entry containing both LPT and SPT pointers
> +/// Lower 64 bits = big/large page, upper 64 bits = small page
> +#[repr(C)]
> +#[derive(Debug, Clone, Copy)]
> +pub(crate) struct DualPde {
> +    pub lpt: Pde, // Large/Big Page Table pointer (2MB pages) - bits 63:0 (lower)
> +    pub spt: Pde, // Small Page Table pointer (4KB pages) - bits 127:64 (upper)
> +}
> +
> +impl DualPde {
> +    /// Create a new empty dual PDE
> +    pub(crate) fn new() -> Self {
> +        Self {
> +            spt: Pde::default(),
> +            lpt: Pde::default(),
> +        }
> +    }
> +
> +    /// Set the Small Page Table address with aperture
> +    pub(crate) fn set_small_pt_address(&mut self, addr: VramAddress, aperture: AperturePde) {
> +        self.spt = Pde::default()
> +            .set_valid(true)
> +            .set_table_frame_number(addr.frame_number())
> +            .set_aperture(aperture);
> +    }
> +
> +    /// Set the Large Page Table address with aperture
> +    pub(crate) fn set_large_pt_address(&mut self, addr: VramAddress, aperture: AperturePde) {
> +        self.lpt = Pde::default()
> +            .set_valid(true)
> +            .set_table_frame_number(addr.frame_number())
> +            .set_aperture(aperture);
> +    }
> +
> +    /// Check if has valid Small Page Table
> +    pub(crate) fn has_small_pt_address(&self) -> bool {
> +        self.spt.is_valid()
> +    }
> +
> +    /// Check if has valid Large Page Table
> +    pub(crate) fn has_large_pt_address(&self) -> bool {
> +        self.lpt.is_valid()
> +    }
> +
> +    /// Set SPT (Small Page Table) using Pfn
> +    pub(crate) fn set_spt(&mut self, pfn: Pfn, aperture: AperturePde) {
> +        self.spt = Pde::default()
> +            .set_valid(true)
> +            .set_aperture(aperture)
> +            .set_table_frame_number(pfn.raw());
> +    }
> +}
> +
> +/// Virtual Frame Number - virtual address divided by 4KB
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +pub(crate) struct Vfn(u64);
> +
> +impl Vfn {
> +    /// Create a new VFN from a frame number
> +    pub(crate) const fn new(frame_number: u64) -> Self {
> +        Self(frame_number)
> +    }
> +
> +    /// Get raw frame number
> +    pub(crate) const fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +impl From<VirtualAddress> for Vfn {
> +    fn from(vaddr: VirtualAddress) -> Self {
> +        Self(vaddr.frame_number())
> +    }
> +}
> +
> +/// Physical Frame Number - physical address divided by 4KB
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +pub(crate) struct Pfn(u64);
> +
> +impl Pfn {
> +    /// Create a new PFN from a frame number
> +    pub(crate) const fn new(frame_number: u64) -> Self {
> +        Self(frame_number)
> +    }
> +
> +    /// Get raw frame number
> +    pub(crate) const fn raw(&self) -> u64 {
> +        self.0
> +    }
> +}
> +
> +impl From<VramAddress> for Pfn {
> +    fn from(addr: VramAddress) -> Self {
> +        let frame = addr.frame_number();
> +        Self(frame)
> +    }
> +}
> diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
> index fffcaee2249f..ff994f1ec9b9 100644
> --- a/drivers/gpu/nova-core/nova_core.rs
> +++ b/drivers/gpu/nova-core/nova_core.rs
> @@ -10,6 +10,7 @@
>  mod gfw;
>  mod gpu;
>  mod gsp;
> +mod mm;
>  mod regs;
>  mod util;
>  mod vbios;



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH RFC] nova-core: mm: Add initial structures for page table management
  2025-10-15  2:08 ` John Hubbard
@ 2025-10-15 20:34   ` Joel Fernandes
  0 siblings, 0 replies; 4+ messages in thread
From: Joel Fernandes @ 2025-10-15 20:34 UTC (permalink / raw)
  To: John Hubbard, linux-kernel, rust-for-linux, dri-devel, dakr,
	acourbot
  Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Timur Tabi, joel, Elle Rhumsaa,
	Daniel Almeida, Andrea Righi, nouveau



On 10/14/2025 10:08 PM, John Hubbard wrote:
> On 10/6/25 2:09 PM, Joel Fernandes wrote:
>> Add initial structures and helpers for page table management. Will
>> expand and build on this in later patches. Uses bitstruct for clean
>> code.
>>
>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
>> ---
>> Rebased on rust-drm-next + bitfield patches [1].
>>
>> [1] https://lore.kernel.org/all/20251003154748.1687160-1-joelagnelf@nvidia.com/
>>
>>  drivers/gpu/nova-core/falcon/gsp.rs |   2 +-
>>  drivers/gpu/nova-core/mm/mod.rs     |   3 +
>>  drivers/gpu/nova-core/mm/types.rs   | 382 ++++++++++++++++++++++++++++
>>  drivers/gpu/nova-core/nova_core.rs  |   1 +
>>  4 files changed, 387 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/gpu/nova-core/mm/mod.rs
>>  create mode 100644 drivers/gpu/nova-core/mm/types.rs
>>
>
> Hi Joel,
>
> This will be easier to provide a meaningful review for, once there are
> some callers to look at. Structures alone are very difficult to review,
> in the absence of behaviors (callers).

John, true. For next posting of this particular patch, I will post the page
table walker patches as well. There are still several prerequisites that PT
walking needs though (PRAMIN, bitfield, buddy allocator), which I will post in
advance before that. This patch was mainly to show how bitfield (formerly
bitstruct) will be used.

Thanks.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-10-15 20:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-06 21:09 [PATCH RFC] nova-core: mm: Add initial structures for page table management Joel Fernandes
2025-10-06 21:11 ` Joel Fernandes
2025-10-15  2:08 ` John Hubbard
2025-10-15 20:34   ` Joel Fernandes

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).