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
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
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.
© 2016 - 2025 Red Hat, Inc.