Re: [PATCH v10 12/21] gpu: nova-core: mm: Add unified page table entry wrapper enums

From: Eliot Courtney

Date: Thu Apr 02 2026 - 01:44:20 EST


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 <ndjukic@xxxxxxxxxx>
> Signed-off-by: Joel Fernandes <joelagnelf@xxxxxxxxxx>
> ---
> 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.