diff options
Diffstat (limited to 'drivers/gpu/nova-core/gpu.rs')
| -rw-r--r-- | drivers/gpu/nova-core/gpu.rs | 195 |
1 files changed, 132 insertions, 63 deletions
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index b3c91731db45..fd1414004dd0 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -9,7 +9,8 @@ use kernel::{ io::Io, num::Bounded, pci, - prelude::*, // + prelude::*, + sizes::SizeConstants, // }; use crate::{ @@ -21,11 +22,15 @@ use crate::{ Falcon, // }, fb::SysmemFlush, + fsp::Fsp, gsp::{ self, - Gsp, // + commands::GetGspStaticInfoReply, + Gsp, + GspBootContext, // }, regs, + vgpu::VgpuManager, // }; mod hal; @@ -130,22 +135,6 @@ impl Chipset { } } - /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC. - /// - /// This includes all chipsets < GA102. - pub(crate) const fn needs_fwsec_bootloader(self) -> bool { - matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100) - } - - /// Returns `true` if this chipset boots via FSP (Hopper and later), which requires the FMC - /// firmware image. - pub(crate) const fn uses_fsp(self) -> bool { - matches!( - self.arch(), - Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x - ) - } - /// Returns the address range of the PCI config mirror space. pub(crate) fn pci_config_mirror_range(self) -> Range<u32> { hal::gpu_hal(self).pci_config_mirror_range() @@ -262,37 +251,87 @@ impl fmt::Display for Spec { } } -/// Structure holding the resources required to operate the GPU. +/// Self-contained resources to operate and drop the GSP. #[pin_data(PinnedDrop)] -pub(crate) struct Gpu<'gpu> { +struct GspResources<'gpu> { /// Device owning the GPU. - device: &'gpu device::Device<device::Bound>, + device: &'gpu pci::Device<device::Bound>, + /// Details about the chipset. spec: Spec, /// MMIO mapping of PCI BAR 0. bar: Bar0<'gpu>, - /// System memory page required for flushing all pending GPU-side memory writes done through - /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). - sysmem_flush: SysmemFlush<'gpu>, /// GSP falcon instance, used for GSP boot up and cleanup. - gsp_falcon: Falcon<GspFalcon>, + gsp_falcon: Falcon<'gpu, GspFalcon>, /// SEC2 falcon instance, used for GSP boot up and cleanup. - sec2_falcon: Falcon<Sec2Falcon>, - /// GSP runtime data. Temporarily an empty placeholder. + sec2_falcon: Falcon<'gpu, Sec2Falcon>, + /// FSP instance, if on an arch that supports it. + // TODO: use different resource types for each boot method, and make the relevant Gsp methods + // generic against them. + fsp: Option<Fsp<'gpu>>, + /// vGPU state detected before GSP boot. + vgpu: VgpuManager, + /// GSP runtime data. #[pin] gsp: Gsp, /// GSP unload firmware bundle, if any. unload_bundle: Option<gsp::UnloadBundle>, } +/// Structure holding the resources required to operate the GPU. +#[pin_data] +pub(crate) struct Gpu<'gpu> { + spec: Spec, + /// Static GPU information as provided by the GSP. + gsp_static_info: GetGspStaticInfoReply, + /// GSP and its resources. + #[pin] + gsp_resources: GspResources<'gpu>, + /// System memory page required for flushing all pending GPU-side memory writes done through + /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). + /// + /// Must be kept declared *after* `gsp_resources`, as the latter's `PinnedDrop` implementation + /// requires the sysmem flush page to be in place. + sysmem_flush: SysmemFlush<'gpu>, +} + +#[pinned_drop] +impl PinnedDrop for GspResources<'_> { + fn drop(self: Pin<&mut Self>) { + let this = self.project(); + let device = *this.device; + let bar = *this.bar; + let bundle = this.unload_bundle.take(); + + let _ = this + .gsp + .as_ref() + .get_ref() + .unload( + GspBootContext { + pdev: device, + bar, + chipset: this.spec.chipset, + gsp_falcon: &*this.gsp_falcon, + sec2_falcon: &*this.sec2_falcon, + fsp: this.fsp.as_mut(), + vgpu: &*this.vgpu, + }, + bundle, + ) + .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e)); + } +} + impl<'gpu> Gpu<'gpu> { - pub(crate) fn new( - pdev: &'gpu pci::Device<device::Core<'_>>, + pub(crate) fn new<'a>( + pdev: &'gpu pci::Device<device::Core<'a>>, bar: Bar0<'gpu>, - ) -> impl PinInit<Self, Error> + 'gpu { + ) -> impl PinInit<Self, Error> + use<'gpu, 'a> { + let dev = pdev.as_ref(); + try_pin_init!(Self { - device: pdev.as_ref(), - spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| { - dev_info!(pdev,"NVIDIA ({})\n", spec); + spec: Spec::new(dev, bar).inspect(|spec| { + dev_info!(dev,"NVIDIA ({})\n", spec); })?, // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. @@ -305,43 +344,73 @@ impl<'gpu> Gpu<'gpu> { unsafe { pdev.dma_set_mask_and_coherent(dma_mask)? }; hal.wait_gfw_boot_completion(bar) - .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?; + .inspect_err(|_| dev_err!(dev, "GFW boot did not complete\n"))?; }, - sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, + // Initialize this early because `gsp_resources` depends on it. + sysmem_flush: SysmemFlush::register(dev, bar, spec.chipset)?, - gsp_falcon: Falcon::new( - pdev.as_ref(), - spec.chipset, - ) - .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, + gsp_resources <- try_pin_init!(GspResources { + device: pdev, - sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, + spec: *spec, - gsp <- Gsp::new(pdev), + bar, - // This member must be initialized last, so the `UnloadBundle` can never be dropped from - // outside of the constructed `Gpu`, ensuring that the unload sequence is properly run - // in case of failure. - unload_bundle: gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)?, - bar, - }) - } -} + gsp_falcon: Falcon::new( + dev, + spec.chipset, + bar + ) + .inspect(|falcon| falcon.clear_swgen0_intr())?, -#[pinned_drop] -impl PinnedDrop for Gpu<'_> { - fn drop(self: Pin<&mut Self>) { - let this = self.project(); - let device = *this.device; - let bar = *this.bar; - let bundle = this.unload_bundle.take(); + sec2_falcon: Falcon::new(dev, spec.chipset, bar)?, - let _ = this - .gsp - .as_ref() - .get_ref() - .unload(device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle) - .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e)); + fsp: Fsp::try_new(dev, bar, spec.chipset)?, + + vgpu: VgpuManager::new(pdev, spec.chipset, fsp.as_mut()), + + gsp <- Gsp::new(pdev), + + // This member must be initialized last, so the `UnloadBundle` can never be dropped + // from outside of the constructed `GspResources`, ensuring that the unload sequence + // is properly run in case of failure. + unload_bundle: gsp.boot(GspBootContext { + pdev, + bar, + chipset: spec.chipset, + gsp_falcon, + sec2_falcon, + fsp: fsp.as_mut(), + vgpu, + })?, + }), + + gsp_static_info: { + // Obtain and display basic GPU information. + let info = gsp_resources.gsp.get_static_info(bar)?; + match info.gpu_name() { + Ok(name) => dev_info!(dev, "GPU name: {}\n", name), + Err(e) => dev_warn!(dev, "GPU name unavailable: {:?}\n", e), + } + + if !info.usable_fb_regions.is_empty() { + dev_dbg!(dev, "Usable FB regions:\n"); + for region in &info.usable_fb_regions { + dev_dbg!(dev, " - {:#x?}\n", region); + } + + dev_dbg!( + dev, + "Total usable VRAM: {} MiB\n", + info.usable_fb_regions.iter().fold(0u64, |res, region| res + .saturating_add(region.end - region.start)) + / u64::SZ_1M + ); + } + + info + } + }) } } |
