xref: /linux/drivers/gpu/nova-core/mm.rs (revision e6a2c988ed96a5a3af51ed97ecba980521bf2fc0)
1 // SPDX-License-Identifier: GPL-2.0
2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 //! Memory management subsystems.
5 
6 #![cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))]
7 
8 use core::{
9     fmt::LowerHex,
10     ops, //
11 };
12 
13 use kernel::{
14     fmt,
15     prelude::*,
16     ptr::{
17         Alignable,
18         Alignment, //
19     },
20 };
21 
22 use crate::{
23     driver::Bar0,
24     gpu::Chipset, //
25 };
26 
27 mod hal;
28 mod pramin;
29 mod regs;
30 
31 /// GPU Memory Manager - owns all core MM components.
32 ///
33 /// Provides centralized ownership of memory management resources:
34 /// - [`pramin::Pramin`] for direct VRAM access.
35 pub(crate) struct GpuMm<'gpu> {
36     pramin: pramin::Pramin<'gpu>,
37 }
38 
39 impl<'gpu> GpuMm<'gpu> {
40     /// Creates the GPU memory manager.
41     pub(crate) fn new(
42         bar: Bar0<'gpu>,
43         chipset: Chipset,
44         total_fb_end: VramAddress,
45     ) -> Result<Self> {
46         // PRAMIN covers all physical VRAM (including GSP-reserved areas
47         // above the usable region, e.g. the BAR1 page directory).
48         let vram_region = VramAddress::ZERO..total_fb_end;
49 
50         Ok(Self {
51             pramin: pramin::Pramin::new(bar, chipset, vram_region)?,
52         })
53     }
54 
55     /// Access the [`pramin::Pramin`].
56     fn pramin_mut(&mut self) -> &mut pramin::Pramin<'gpu> {
57         &mut self.pramin
58     }
59 }
60 
61 /// Physical VRAM address in GPU video memory.
62 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
63 #[repr(transparent)]
64 pub(crate) struct VramAddress(u64);
65 
66 impl VramAddress {
67     /// The zero address.
68     pub(crate) const ZERO: Self = Self::from_raw(0);
69 
70     /// Creates an address from a raw value.
71     pub(crate) const fn from_raw(addr: u64) -> Self {
72         Self(addr)
73     }
74 
75     /// Returns the address as a raw value.
76     pub(crate) const fn into_raw(self) -> u64 {
77         self.0
78     }
79 
80     /// Adds `rhs` to this address, returning [`None`] on overflow.
81     pub(crate) const fn checked_add(self, rhs: u64) -> Option<Self> {
82         match self.into_raw().checked_add(rhs) {
83             Some(addr) => Some(Self::from_raw(addr)),
84             None => None,
85         }
86     }
87 }
88 
89 impl Alignable for VramAddress {
90     fn align_down(self, alignment: Alignment) -> Self {
91         Self::from_raw(self.into_raw().align_down(alignment))
92     }
93 
94     fn align_up(self, alignment: Alignment) -> Option<Self> {
95         self.into_raw().align_up(alignment).map(Self::from_raw)
96     }
97 }
98 
99 impl LowerHex for VramAddress {
100     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101         LowerHex::fmt(&self.into_raw(), f)
102     }
103 }
104 
105 impl fmt::Debug for VramAddress {
106     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107         f.write_fmt(fmt!("{:#x}", self))
108     }
109 }
110 
111 impl ops::Add<u64> for VramAddress {
112     type Output = Self;
113 
114     fn add(self, rhs: u64) -> Self::Output {
115         Self::from_raw(self.into_raw() + rhs)
116     }
117 }
118 
119 impl ops::Sub for VramAddress {
120     type Output = u64;
121 
122     fn sub(self, rhs: Self) -> Self::Output {
123         self.into_raw() - rhs.into_raw()
124     }
125 }
126 
127 #[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
128 pub(crate) mod selftest {
129     use core::ops::Range;
130 
131     use kernel::{
132         device,
133         sizes::SizeConstants, //
134     };
135 
136     use super::*;
137 
138     /// Run MM subsystem self-tests during probe.
139     pub(crate) fn run(
140         dev: &device::Device<device::Bound>,
141         mm: &mut GpuMm<'_>,
142         usable_fb_regions: &[Range<u64>],
143     ) -> Result {
144         // VRAM span the self-tests are free to overwrite, from the chosen test base.
145         const SELFTEST_SPAN: u64 = u64::SZ_64M;
146 
147         let base = usable_fb_regions.iter().find_map(|region| {
148             // Tests rely on this being 8 byte aligned for checking misalignment handling.
149             let base = region.start.align_up(Alignment::new::<8>())?;
150             (base.checked_add(SELFTEST_SPAN)? <= region.end).then_some(base)
151         });
152         let Some(base) = base else {
153             dev_warn!(
154                 dev,
155                 "PRAMIN: skipping self-tests, no usable VRAM region of {:#x} bytes\n",
156                 SELFTEST_SPAN
157             );
158             return Ok(());
159         };
160 
161         pramin::selftest::run(dev, mm.pramin_mut(), VramAddress::from_raw(base))
162     }
163 }
164