xref: /linux/drivers/gpu/nova-core/gpu.rs (revision 9cb3f5d56e81fd97a82ef361b210bc2322daaaa7)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use core::ops::Range;
4 
5 use kernel::{
6     device,
7     dma::Device,
8     fmt,
9     gpu::buddy::GpuBuddyParams,
10     io::Io,
11     num::Bounded,
12     pci,
13     prelude::*,
14     ptr::Alignment,
15     sizes::{
16         SizeConstants,
17         SZ_4K, //
18     },
19     uapi, //
20 };
21 
22 use crate::{
23     bounded_enum,
24     driver::{
25         Bar0,
26         Bar1, //
27     },
28     falcon::{
29         gsp::Gsp as GspFalcon,
30         sec2::Sec2 as Sec2Falcon,
31         Falcon, //
32     },
33     fb::SysmemFlush,
34     fsp::Fsp,
35     gsp::{
36         self,
37         commands::GetGspStaticInfoReply,
38         Gsp,
39         GspBootContext, //
40     },
41     mm::{
42         bar_user::BarUser,
43         pagetable::MmuVersion,
44         GpuMm,
45         VramAddress, //
46     },
47     vgpu::VgpuManager, //
48 };
49 
50 mod hal;
51 mod regs;
52 
53 macro_rules! define_chipset {
54     ({ $($variant:ident),* $(,)* }) =>
55     {
56         ::kernel::macros::paste!(
57         /// Enum representation of the GPU chipset.
58         #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
59         #[repr(u32)]
60         #[allow(missing_docs)]
61         pub enum Chipset {
62             $($variant = uapi::[<drm_nova_chipid_NOVA_DRM_CHIPID_ $variant:upper>]),*,
63         }
64 
65         impl Chipset {
66             /// All chipsets known to the driver.
67             pub const ALL: &'static [Chipset] = &[
68                 $( Chipset::$variant, )*
69             ];
70 
71             /// Returns the name of this chipset, in lowercase.
72             ///
73             /// # Examples
74             ///
75             /// ```
76             /// let chipset = Chipset::GA102;
77             /// assert_eq!(chipset.name(), "ga102");
78             /// ```
79             pub(crate) const fn name(&self) -> &'static str {
80                 match *self {
81                 $(
82                     Chipset::$variant => stringify!([<$variant:lower>]),
83                 )*
84                 }
85             }
86         }
87 
88         // TODO[FPRI]: replace with something like derive(FromPrimitive)
89         impl TryFrom<u32> for Chipset {
90             type Error = kernel::error::Error;
91 
92             fn try_from(value: u32) -> Result<Self, Self::Error> {
93                 match value {
94                     $(
95                         uapi::[<drm_nova_chipid_NOVA_DRM_CHIPID_ $variant:upper>] => {
96                             Ok(Chipset::$variant)
97                         }
98                     )*
99                     _ => Err(ENODEV),
100                 }
101             }
102         }
103     );
104     }
105 }
106 
107 define_chipset!({
108     // Turing
109     TU102,
110     TU104,
111     TU106,
112     TU117,
113     TU116,
114     // Ampere
115     GA100,
116     GA102,
117     GA103,
118     GA104,
119     GA106,
120     GA107,
121     // Hopper
122     GH100,
123     // Ada
124     AD102,
125     AD103,
126     AD104,
127     AD106,
128     AD107,
129     // Blackwell GB10x
130     GB100,
131     GB102,
132     // Blackwell GB20x
133     GB202,
134     GB203,
135     GB205,
136     GB206,
137     GB207,
138 });
139 
140 impl Chipset {
141     /// Returns the [`Architecture`] generation of this chipset.
142     pub const fn arch(self) -> Architecture {
143         match self {
144             Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
145                 Architecture::Turing
146             }
147             Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
148                 Architecture::Ampere
149             }
150             Self::GH100 => Architecture::Hopper,
151             Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
152                 Architecture::Ada
153             }
154             Self::GB100 | Self::GB102 => Architecture::BlackwellGB10x,
155             Self::GB202 | Self::GB203 | Self::GB205 | Self::GB206 | Self::GB207 => {
156                 Architecture::BlackwellGB20x
157             }
158         }
159     }
160 
161     /// Returns the address range of the PCI config mirror space.
162     pub(crate) fn pci_config_mirror_range(self) -> Range<u32> {
163         hal::gpu_hal(self).pci_config_mirror_range()
164     }
165 
166     /// Returns the MMU version for this chipset.
167     pub(crate) fn mmu_version(self) -> MmuVersion {
168         MmuVersion::from(self.arch())
169     }
170 }
171 
172 impl From<Chipset> for u32 {
173     #[inline]
174     fn from(value: Chipset) -> Self {
175         // CAST: `Chipset` is `repr(u32)` and can thus be cast losslessly.
176         value as u32
177     }
178 }
179 
180 // TODO
181 //
182 // The resulting strings are used to generate firmware paths, hence the
183 // generated strings have to be stable.
184 //
185 // Hence, replace with something like strum_macros derive(Display).
186 //
187 // For now, redirect to fmt::Debug for convenience.
188 impl fmt::Display for Chipset {
189     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190         write!(f, "{self:?}")
191     }
192 }
193 
194 bounded_enum! {
195     /// Enum representation of the GPU generation.
196     #[derive(fmt::Debug, Copy, Clone)]
197     #[repr(u32)]
198     pub enum Architecture with TryFrom<Bounded<u32, 6>> {
199         /// Turing (TU1xx).
200         Turing = uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_TURING,
201         /// Ampere (GA10x).
202         Ampere = uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_AMPERE,
203         /// Hopper (GH100).
204         Hopper = uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_HOPPER,
205         /// Ada Lovelace (AD10x).
206         Ada = uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_ADA,
207         /// Blackwell (GB10x).
208         BlackwellGB10x =
209             uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_BLACKWELL_GB10X,
210         /// Blackwell (GB20x).
211         BlackwellGB20x =
212             uapi::drm_nova_architecture_NOVA_DRM_ARCHITECTURE_BLACKWELL_GB20X,
213     }
214 }
215 
216 impl From<Architecture> for u32 {
217     #[inline]
218     fn from(value: Architecture) -> Self {
219         // CAST: `Architecture` is `repr(u32)` and can thus be cast losslessly.
220         value as u32
221     }
222 }
223 
224 #[derive(Clone, Copy)]
225 pub(crate) struct Revision {
226     major: Bounded<u8, 4>,
227     minor: Bounded<u8, 4>,
228 }
229 
230 impl From<regs::NV_PMC_BOOT_42> for Revision {
231     fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
232         Self {
233             major: boot0.major_revision().cast(),
234             minor: boot0.minor_revision().cast(),
235         }
236     }
237 }
238 
239 impl fmt::Display for Revision {
240     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241         write!(f, "{:x}.{:x}", self.major, self.minor)
242     }
243 }
244 
245 /// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
246 #[derive(Clone, Copy)]
247 pub struct Spec {
248     /// The GPU chipset.
249     pub chipset: Chipset,
250     revision: Revision,
251 }
252 
253 impl Spec {
254     pub(crate) fn new(dev: &device::Device, bar: Bar0<'_>) -> Result<Spec> {
255         // Some brief notes about boot0 and boot42, in chronological order:
256         //
257         // NV04 through NV50:
258         //
259         //    Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs.
260         //    boot42 may not even exist on some of these GPUs.
261         //
262         // Fermi through Volta:
263         //
264         //     Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42
265         //     is also guaranteed to be both present and accurate.
266         //
267         // Turing and later:
268         //
269         //     Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not
270         //     from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU.
271         //     Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs.
272 
273         let boot0 = bar.read(regs::NV_PMC_BOOT_0);
274 
275         if boot0.is_older_than_fermi() {
276             return Err(ENODEV);
277         }
278 
279         let boot42 = bar.read(regs::NV_PMC_BOOT_42);
280         Spec::try_from(boot42).inspect_err(|_| {
281             dev_err!(dev, "Unsupported chipset: {}\n", boot42);
282         })
283     }
284 }
285 
286 impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
287     type Error = Error;
288 
289     fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
290         Ok(Self {
291             chipset: boot42.chipset()?,
292             revision: boot42.into(),
293         })
294     }
295 }
296 
297 impl fmt::Display for Spec {
298     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299         f.write_fmt(fmt!(
300             "Chipset: {}, Architecture: {:?}, Revision: {}",
301             self.chipset,
302             self.chipset.arch(),
303             self.revision
304         ))
305     }
306 }
307 
308 /// Self-contained resources to operate and drop the GSP.
309 #[pin_data(PinnedDrop)]
310 struct GspResources<'gpu> {
311     /// Device owning the GPU.
312     device: &'gpu pci::Device<device::Bound>,
313     /// Details about the chipset.
314     spec: Spec,
315     /// MMIO mapping of PCI BAR 0.
316     bar: Bar0<'gpu>,
317     /// GSP falcon instance, used for GSP boot up and cleanup.
318     gsp_falcon: Falcon<'gpu, GspFalcon>,
319     /// SEC2 falcon instance, used for GSP boot up and cleanup.
320     sec2_falcon: Falcon<'gpu, Sec2Falcon>,
321     /// FSP instance, if on an arch that supports it.
322     // TODO: use different resource types for each boot method, and make the relevant Gsp methods
323     // generic against them.
324     fsp: Option<Fsp<'gpu>>,
325     /// vGPU state detected before GSP boot.
326     vgpu: VgpuManager,
327     /// GSP runtime data.
328     #[pin]
329     gsp: Gsp<'gpu>,
330     /// GSP unload firmware bundle, if any.
331     unload_bundle: Option<gsp::UnloadBundle<'gpu>>,
332 }
333 
334 /// Structure holding the resources required to operate the GPU.
335 #[pin_data]
336 pub(crate) struct Gpu<'gpu> {
337     pub(crate) spec: Spec,
338     /// Static GPU information as provided by the GSP.
339     pub(crate) gsp_static_info: GetGspStaticInfoReply,
340     /// GPU memory manager owning memory management resources.
341     ///
342     /// Must be kept declared *before* `gsp_resources`, so that its components are dropped while
343     /// the GSP is still operational.
344     mm: GpuMm<'gpu>,
345     /// BAR1 user interface for CPU access to GPU virtual memory.
346     #[pin]
347     bar_user: BarUser<'gpu>,
348     /// GSP and its resources.
349     #[pin]
350     gsp_resources: GspResources<'gpu>,
351     /// System memory page required for flushing all pending GPU-side memory writes done through
352     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
353     ///
354     /// Must be kept declared *after* `gsp_resources`, as the latter's `PinnedDrop` implementation
355     /// requires the sysmem flush page to be in place.
356     sysmem_flush: SysmemFlush<'gpu>,
357 }
358 
359 #[pinned_drop]
360 impl PinnedDrop for GspResources<'_> {
361     fn drop(self: Pin<&mut Self>) {
362         let this = self.project();
363         let device = *this.device;
364         let bar = *this.bar;
365         let bundle = this.unload_bundle.take();
366 
367         let _ = this
368             .gsp
369             .as_ref()
370             .get_ref()
371             .unload(
372                 GspBootContext {
373                     pdev: device,
374                     bar,
375                     chipset: this.spec.chipset,
376                     gsp_falcon: &*this.gsp_falcon,
377                     sec2_falcon: &*this.sec2_falcon,
378                     fsp: this.fsp.as_mut(),
379                     vgpu: &*this.vgpu,
380                 },
381                 bundle,
382             )
383             .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e));
384     }
385 }
386 
387 impl<'gpu> Gpu<'gpu> {
388     pub(crate) fn new<'a>(
389         pdev: &'gpu pci::Device<device::Core<'a>>,
390         bar: Bar0<'gpu>,
391         bar1: Bar1<'gpu>,
392     ) -> impl PinInit<Self, Error> + use<'gpu, 'a> {
393         let dev = pdev.as_ref();
394 
395         try_pin_init!(Self {
396             spec: Spec::new(dev, bar).inspect(|spec| {
397                 dev_info!(dev,"NVIDIA ({})\n", spec);
398             })?,
399 
400             _: {
401                 let dma_mask = hal::gpu_hal(spec.chipset).dma_mask();
402 
403                 // SAFETY: `Gpu` owns all DMA allocations for this device, and we are
404                 // still constructing it, so no concurrent DMA allocations can exist.
405                 unsafe { pdev.dma_set_mask_and_coherent(dma_mask)? };
406 
407                 // Nova walks SG segments to build page tables, so their length is
408                 // irrelevant to the device.
409                 //
410                 // SAFETY: `Gpu` owns all DMA allocations for this device, and we are
411                 // still constructing it, so no concurrent DMA allocations can exist.
412                 unsafe { pdev.dma_set_max_seg_size(u32::MAX) };
413 
414                 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
415                 Self::wait_gfw_boot_completion(dev, bar, spec.chipset)?;
416             },
417 
418             // Initialize this early because `gsp_resources` depends on it.
419             sysmem_flush: SysmemFlush::register(dev, bar, spec.chipset)?,
420 
421             gsp_resources <- try_pin_init!(GspResources {
422                 device: pdev,
423 
424                 spec: *spec,
425 
426                 bar,
427 
428                 gsp_falcon: Falcon::new(
429                     dev,
430                     spec.chipset,
431                     bar
432                 )
433                 .inspect(|falcon| falcon.clear_swgen0_intr())?,
434 
435                 sec2_falcon: Falcon::new(dev, spec.chipset, bar)?,
436 
437                 fsp: Fsp::try_new(dev, bar, spec.chipset)?,
438 
439                 vgpu: VgpuManager::new(pdev, spec.chipset, fsp.as_mut()),
440 
441                 gsp <- Gsp::new(pdev, bar),
442 
443                 // This member must be initialized last, so the `UnloadBundle` can never be dropped
444                 // from outside of the constructed `GspResources`, ensuring that the unload sequence
445                 // is properly run in case of failure.
446                 unload_bundle: gsp.boot(GspBootContext {
447                     pdev,
448                     bar,
449                     chipset: spec.chipset,
450                     gsp_falcon,
451                     sec2_falcon,
452                     fsp: fsp.as_mut(),
453                     vgpu,
454                 })?,
455             }),
456 
457             gsp_static_info: {
458                 // Obtain and display basic GPU information.
459                 let info = gsp_resources.gsp.get_static_info()?;
460                 match info.gpu_name() {
461                     Ok(name) => dev_info!(dev, "GPU name: {}\n", name),
462                     Err(e) => dev_warn!(dev, "GPU name unavailable: {:?}\n", e),
463                 }
464 
465                 if !info.usable_fb_regions.is_empty() {
466                     dev_dbg!(dev, "Usable FB regions:\n");
467                     for region in &info.usable_fb_regions {
468                         dev_dbg!(dev, "  - {:#x?}\n", region);
469                     }
470 
471                     dev_dbg!(
472                         dev,
473                         "Total usable VRAM: {} MiB\n",
474                         info.vram_size() / u64::SZ_1M
475                     );
476                 }
477 
478                 info
479             },
480 
481             // Create GPU memory manager owning memory management resources.
482             mm: {
483                 let usable_vram = gsp_static_info.usable_fb_regions.first().ok_or(ENODEV)?;
484                 let buddy_params = GpuBuddyParams {
485                     base_offset: usable_vram.start,
486                     size: usable_vram.end - usable_vram.start,
487                     chunk_size: Alignment::new::<SZ_4K>(),
488                 };
489 
490                 GpuMm::new(
491                     bar,
492                     gsp_resources.spec.chipset,
493                     buddy_params,
494                     VramAddress::from_raw(gsp_static_info.total_fb_end),
495                 )?
496             },
497 
498             // Create BAR1 user interface for CPU access to GPU virtual memory.
499             bar_user <- {
500                 let pdb_addr = VramAddress::from_raw(gsp_static_info.bar1_pde_base);
501                 let bar1_idx = crate::driver::bar1_resource_index(pdev)?;
502                 let bar1_size = pdev.resource_len(bar1_idx)?;
503 
504                 BarUser::new(
505                     pdb_addr,
506                     gsp_resources.spec.chipset,
507                     bar1_size,
508                     bar1,
509                 )
510             },
511         })
512     }
513 
514     /// Waits for GFW, the GPU's boot firmware, to report completion.
515     ///
516     /// The driver must not program the GPU before then.
517     ///
518     /// # Errors
519     ///
520     /// `ETIMEDOUT` if GFW does not report completion in time.
521     pub(crate) fn wait_gfw_boot_completion(
522         dev: &device::Device<device::Bound>,
523         bar: Bar0<'_>,
524         chipset: Chipset,
525     ) -> Result {
526         hal::gpu_hal(chipset)
527             .wait_gfw_boot_completion(bar)
528             .inspect_err(|_| dev_err!(dev, "GFW boot did not complete\n"))
529     }
530 
531     /// Runs self-tests on the constructed [`Gpu`], logging failures without failing probe.
532     #[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
533     pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Bound>) {
534         let this = self.project();
535         let dev = pdev.as_ref();
536         let regions = &this.gsp_static_info.usable_fb_regions;
537 
538         if let Err(err) = crate::mm::selftest::run(
539             dev,
540             this.mm,
541             regions,
542             &this.bar_user,
543             this.gsp_static_info.bar1_pde_base,
544             this.spec.chipset,
545         ) {
546             dev_err!(dev, "self-tests failed: {:?}\n", err);
547         }
548     }
549 }
550 
551 /// Reads the boot0 register and returns its raw value.
552 pub(crate) fn boot_0_raw(bar: Bar0<'_>) -> u32 {
553     bar.read(regs::NV_PMC_BOOT_0).into_raw()
554 }
555