summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core/mm/pagetable/ver2.rs
blob: d7169a0fcff978eae0fd09a0d27dff8d5c939720 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
// SPDX-License-Identifier: GPL-2.0

//! MMU v2 page table types for Turing, Ampere and Ada GPUs.
//!
//! This module defines MMU version 2 specific types (Turing, Ampere and Ada GPUs).
//!
//! Bit field layouts derived from the NVIDIA OpenRM documentation:
//! `open-gpu-kernel-modules/src/common/inc/swref/published/turing/tu102/dev_mmu.h`

#![allow(dead_code)]

use kernel::{
    bitfield,
    num::Bounded, //
};

use pin_init::Zeroable;

use super::{
    AperturePde,
    AperturePte,
    DualPdeOps,
    PageTableLevel,
    PdeOps,
    PteOps,
    VaLevelIndex, //
};

use crate::mm::{
    Pfn,
    VirtualAddress,
    VramAddress, //
};

// Bounded to version 2 Pfn bitfield conversions:
// 25 bits for video memory frame numbers (bits 32:8).
impl_pfn_bounded!(25);
// 46 bits for system memory frame numbers (bits 53:8).
impl_pfn_bounded!(46);

bitfield! {
    /// MMU v2 49-bit virtual address layout.
    pub(super) struct VirtualAddressV2(u64) {
        /// Page offset [11:0].
        11:0    offset;
        /// PT index [20:12].
        20:12   pt_idx;
        /// PDE0 index [28:21].
        28:21   pde0_idx;
        /// PDE1 index [37:29].
        37:29   pde1_idx;
        /// PDE2 index [46:38].
        46:38   pde2_idx;
        /// PDE3 index [48:47].
        48:47   pde3_idx;
    }
}

impl VirtualAddressV2 {
    /// Create a [`VirtualAddressV2`] from a [`VirtualAddress`].
    pub(super) fn new(va: VirtualAddress) -> Self {
        Self::from_raw(va.into_raw())
    }
}

impl VaLevelIndex for VirtualAddressV2 {
    fn level_index(&self, level: u64) -> u64 {
        match level {
            0 => *self.pde3_idx(),
            1 => *self.pde2_idx(),
            2 => *self.pde1_idx(),
            3 => *self.pde0_idx(),
            4 => *self.pt_idx(),
            _ => 0,
        }
    }
}

/// `PDE` levels for MMU v2 (5-level hierarchy: `PDB` -> `L1` -> `L2` -> `L3` -> `L4`).
pub(super) const PDE_LEVELS: &[PageTableLevel] = &[
    PageTableLevel::Pdb,
    PageTableLevel::L1,
    PageTableLevel::L2,
    PageTableLevel::L3,
];

/// `PTE` level for MMU v2.
pub(super) const PTE_LEVEL: PageTableLevel = PageTableLevel::L4;

/// Dual `PDE` level for MMU v2 (128-bit entries).
pub(super) const DUAL_PDE_LEVEL: PageTableLevel = PageTableLevel::L3;

// Page Table Entry (PTE) for MMU v2 - 64-bit entry at level 4.
bitfield! {
    /// Page Table Entry for MMU v2.
    pub(in crate::mm) struct Pte(u64) {
        /// Entry is valid.
        0:0     valid;
        /// Memory aperture type.
        2:1     aperture => AperturePte;
        /// Volatile (bypass L2 cache).
        3:3     volatile;
        /// Encryption enabled (Confidential Computing).
        4:4     encrypted;
        /// Privileged access only.
        5:5     privilege;
        /// Write protection.
        6:6     read_only;
        /// Atomic operations disabled.
        7:7     atomic_disable;
        /// Frame number for system memory.
        53:8    frame_number_sys => Pfn;
        /// Frame number for video memory.
        32:8    frame_number_vid => Pfn;
        /// Peer GPU ID for peer memory (0-7).
        35:33   peer_id;
        /// Compression tag line bits.
        53:36   comptagline;
        /// Surface kind/format.
        63:56   kind;
    }
}

impl PteOps for Pte {
    fn from_raw(val: u64) -> Self {
        Self::from_raw(val)
    }

    fn invalid() -> Self {
        Self::zeroed()
    }

    fn new(aperture: AperturePte, pfn: Pfn, writable: bool) -> Self {
        let base = Self::zeroed()
            .with_valid(true)
            .with_aperture(aperture)
            .with_read_only(!writable);
        match aperture {
            AperturePte::VideoMemory => base.with_frame_number_vid(pfn),
            // Sysmem PTEs use VOL=1 to bypass L2 for cache coherency.
            AperturePte::SystemCoherent => base.with_frame_number_sys(pfn).with_volatile(true),
            AperturePte::PeerMemory | AperturePte::SystemNonCoherent => {
                kernel::pr_warn!("MMU v2 PTE aperture {:?} not supported\n", aperture);
                Self::invalid()
            }
        }
    }

    fn is_valid(&self) -> bool {
        self.valid().into_bool()
    }

    fn frame_number(&self) -> Pfn {
        match self.aperture() {
            AperturePte::VideoMemory => self.frame_number_vid(),
            _ => self.frame_number_sys(),
        }
    }
}

// Page Directory Entry (PDE) for MMU v2 - 64-bit entry at levels 0-2.
bitfield! {
    /// Page Directory Entry for MMU v2.
    pub(in crate::mm) struct Pde(u64) {
        /// Valid bit (inverted logic).
        0:0     valid_inverted;
        /// Memory aperture type.
        2:1     aperture => AperturePde;
        /// Volatile (bypass L2 cache).
        3:3     volatile;
        /// Disable Address Translation Services.
        5:5     no_ats;
        /// Table frame number for system memory.
        53:8    table_frame_sys => Pfn;
        /// Table frame number for video memory.
        32:8    table_frame_vid => Pfn;
        /// Peer GPU ID (0-7).
        35:33   peer_id;
    }
}

impl PdeOps for Pde {
    fn from_raw(val: u64) -> Self {
        Self::from_raw(val)
    }

    fn new(aperture: AperturePde, table_pfn: Pfn) -> Self {
        let base = Self::zeroed()
            .with_valid_inverted(false) // 0 = valid
            .with_aperture(aperture);
        match aperture {
            AperturePde::VideoMemory => base.with_table_frame_vid(table_pfn),
            // Sysmem PTEs use VOL=1 to bypass L2 for cache coherency.
            AperturePde::SystemCoherent => base.with_table_frame_sys(table_pfn).with_volatile(true),
            AperturePde::Invalid | AperturePde::SystemNonCoherent => {
                kernel::pr_warn!("MMU v2 PDE aperture {:?} not supported\n", aperture);
                Self::invalid()
            }
        }
    }

    fn invalid() -> Self {
        Self::zeroed()
            .with_valid_inverted(true)
            .with_aperture(AperturePde::Invalid)
    }

    fn is_valid(&self) -> bool {
        !self.valid_inverted().into_bool() && self.aperture() != AperturePde::Invalid
    }

    fn aperture(&self) -> AperturePde {
        Pde::aperture(*self)
    }

    fn table_vram_address(&self) -> VramAddress {
        debug_assert!(
            Pde::aperture(*self) == AperturePde::VideoMemory,
            "table_vram_address called on non-VRAM PDE (aperture: {:?})",
            Pde::aperture(*self)
        );
        VramAddress::from(self.table_frame_vid())
    }
}

/// Dual `PDE` at Level 3 - 128-bit entry of Large/Small Page Table pointers.
///
/// The dual `PDE` supports both large (64KB) and small (4KB) page tables.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(in crate::mm) struct DualPde {
    /// Large/Big Page Table pointer (lower 64 bits).
    pub(super) big: Pde,
    /// Small Page Table pointer (upper 64 bits).
    pub(super) small: Pde,
}

impl DualPde {
    /// Check if the big page table pointer is valid.
    fn has_big(&self) -> bool {
        PdeOps::is_valid(&self.big)
    }
}

impl DualPdeOps for DualPde {
    fn from_raw(big: u64, small: u64) -> Self {
        Self {
            big: PdeOps::from_raw(big),
            small: PdeOps::from_raw(small),
        }
    }

    fn new_small(table_pfn: Pfn) -> Self {
        Self {
            big: PdeOps::from_raw(0),
            small: PdeOps::new(AperturePde::VideoMemory, table_pfn),
        }
    }

    fn has_small(&self) -> bool {
        PdeOps::is_valid(&self.small)
    }

    fn small_vram_address(&self) -> VramAddress {
        PdeOps::table_vram_address(&self.small)
    }

    fn big_raw_u64(&self) -> u64 {
        self.big.into_raw()
    }

    fn small_raw_u64(&self) -> u64 {
        self.small.into_raw()
    }
}