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