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