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 <[email protected]> --- Rebased on rust-drm-next + bitfield patches [1]. [1] https://lore.kernel.org/all/[email protected]/ 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
