On Wed Apr 1, 2026 at 6:20 AM JST, Joel Fernandes wrote:
> Add unified Pte, Pde, and DualPde wrapper enums that abstract over
> MMU v2 and v3 page table entry formats. These enums allow the page
> table walker and VMM to work with both MMU versions.
>
> Each unified type:
> - Takes MmuVersion parameter in constructors
> - Wraps both ver2 and ver3 variants
> - Delegates method calls to the appropriate variant
>
> This enables version-agnostic page table operations while keeping
> version-specific implementation details encapsulated in the ver2
> and ver3 modules.
>
> Cc: Nikola Djukic <[email protected]>
> Signed-off-by: Joel Fernandes <[email protected]>
> ---
>  drivers/gpu/nova-core/mm/pagetable.rs | 330 ++++++++++++++++++++++++++
>  1 file changed, 330 insertions(+)
>
> diff --git a/drivers/gpu/nova-core/mm/pagetable.rs 
> b/drivers/gpu/nova-core/mm/pagetable.rs
> index 6e01a1af5222..909df37c3ee8 100644
> --- a/drivers/gpu/nova-core/mm/pagetable.rs
> +++ b/drivers/gpu/nova-core/mm/pagetable.rs
> @@ -12,6 +12,13 @@
>  pub(crate) mod ver3;
>  
>  use crate::gpu::Architecture;
> +use crate::mm::{
> +    pramin,
> +    Pfn,
> +    VirtualAddress,
> +    VramAddress, //
> +};
> +use kernel::prelude::*;
>  
>  /// Extracts the page table index at a given level from a virtual address.
>  pub(crate) trait VaLevelIndex {
> @@ -84,6 +91,96 @@ pub(crate) const fn as_index(&self) -> u64 {
>      }
>  }
>  
> +impl MmuVersion {
> +    /// Get the `PDE` levels (excluding PTE level) for page table walking.
> +    pub(crate) fn pde_levels(&self) -> &'static [PageTableLevel] {
> +        match self {
> +            Self::V2 => ver2::PDE_LEVELS,
> +            Self::V3 => ver3::PDE_LEVELS,
> +        }
> +    }
> +
> +    /// Get the PTE level for this MMU version.
> +    pub(crate) fn pte_level(&self) -> PageTableLevel {
> +        match self {
> +            Self::V2 => ver2::PTE_LEVEL,
> +            Self::V3 => ver3::PTE_LEVEL,
> +        }
> +    }
> +
> +    /// Get the dual PDE level (128-bit entries) for this MMU version.
> +    pub(crate) fn dual_pde_level(&self) -> PageTableLevel {
> +        match self {
> +            Self::V2 => ver2::DUAL_PDE_LEVEL,
> +            Self::V3 => ver3::DUAL_PDE_LEVEL,
> +        }
> +    }
> +
> +    /// Get the number of PDE levels for this MMU version.
> +    pub(crate) fn pde_level_count(&self) -> usize {
> +        self.pde_levels().len()
> +    }
> +
> +    /// Get the entry size in bytes for a given level.
> +    pub(crate) fn entry_size(&self, level: PageTableLevel) -> usize {
> +        if level == self.dual_pde_level() {
> +            16 // 128-bit dual PDE
> +        } else {
> +            8 // 64-bit PDE/PTE
> +        }
> +    }
> +
> +    /// Get the number of entries per page table page for a given level.
> +    pub(crate) fn entries_per_page(&self, level: PageTableLevel) -> usize {
> +        match self {
> +            Self::V2 => match level {
> +                // TODO: Calculate these values from the bitfield dynamically
> +                // instead of hardcoding them.
> +                PageTableLevel::Pdb => 4, // PD3 root: bits [48:47] = 2 bits
> +                PageTableLevel::L3 => 256, // PD0 dual: bits [28:21] = 8 bits
> +                _ => 512,                 // PD2, PD1, PT: 9 bits each
> +            },
> +            Self::V3 => match level {
> +                PageTableLevel::Pdb => 2,  // PDE4 root: bit [56] = 1 bit, 2 
> entries
> +                PageTableLevel::L4 => 256, // PDE0 dual: bits [28:21] = 8 
> bits
> +                _ => 512,                  // PDE3, PDE2, PDE1, PT: 9 bits 
> each
> +            },
> +        }
> +    }
> +
> +    /// Extract the page table index at `level` from `va` for this MMU 
> version.
> +    pub(crate) fn level_index(&self, va: VirtualAddress, level: u64) -> u64 {
> +        match self {
> +            Self::V2 => ver2::VirtualAddressV2::new(va).level_index(level),
> +            Self::V3 => ver3::VirtualAddressV3::new(va).level_index(level),
> +        }
> +    }
> +
> +    /// Compute upper bound on page table pages needed for `num_virt_pages`.
> +    ///
> +    /// Walks from PTE level up through PDE levels, accumulating the tree.
> +    pub(crate) fn pt_pages_upper_bound(&self, num_virt_pages: usize) -> 
> usize {
> +        let mut total = 0;
> +
> +        // PTE pages at the leaf level.
> +        let pte_epp = self.entries_per_page(self.pte_level());
> +        let mut pages_at_level = num_virt_pages.div_ceil(pte_epp);
> +        total += pages_at_level;
> +
> +        // Walk PDE levels bottom-up (reverse of pde_levels()).
> +        for &level in self.pde_levels().iter().rev() {
> +            let epp = self.entries_per_page(level);
> +
> +            // How many pages at this level do we need to point to
> +            // the previous pages_at_level?
> +            pages_at_level = pages_at_level.div_ceil(epp);
> +            total += pages_at_level;
> +        }
> +
> +        total
> +    }
> +}
> +

We have a lot of matches on the MMU version here (and below in Pte, Pde,
DualPde). What about making MmuVersion into a trait (e.g. Mmu) with
associated types for Pte, Pde, DualPde which can implement traits
defining their common operations too? Then you can parameterise
Vmm/PtWalk on this type.

Reply via email to