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 io::Io, 10 num::Bounded, 11 pci, 12 prelude::*, // 13 }; 14 15 use crate::{ 16 bounded_enum, 17 driver::Bar0, 18 falcon::{ 19 gsp::Gsp as GspFalcon, 20 sec2::Sec2 as Sec2Falcon, 21 Falcon, // 22 }, 23 fb::SysmemFlush, 24 gsp::{ 25 self, 26 Gsp, // 27 }, 28 regs, 29 }; 30 31 mod hal; 32 33 macro_rules! define_chipset { 34 ({ $($variant:ident = $value:expr),* $(,)* }) => 35 { 36 /// Enum representation of the GPU chipset. 37 #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 38 pub(crate) enum Chipset { 39 $($variant = $value),*, 40 } 41 42 impl Chipset { 43 pub(crate) const ALL: &'static [Chipset] = &[ 44 $( Chipset::$variant, )* 45 ]; 46 47 ::kernel::macros::paste!( 48 /// Returns the name of this chipset, in lowercase. 49 /// 50 /// # Examples 51 /// 52 /// ``` 53 /// let chipset = Chipset::GA102; 54 /// assert_eq!(chipset.name(), "ga102"); 55 /// ``` 56 pub(crate) const fn name(&self) -> &'static str { 57 match *self { 58 $( 59 Chipset::$variant => stringify!([<$variant:lower>]), 60 )* 61 } 62 } 63 ); 64 } 65 66 // TODO[FPRI]: replace with something like derive(FromPrimitive) 67 impl TryFrom<u32> for Chipset { 68 type Error = kernel::error::Error; 69 70 fn try_from(value: u32) -> Result<Self, Self::Error> { 71 match value { 72 $( $value => Ok(Chipset::$variant), )* 73 _ => Err(ENODEV), 74 } 75 } 76 } 77 } 78 } 79 80 define_chipset!({ 81 // Turing 82 TU102 = 0x162, 83 TU104 = 0x164, 84 TU106 = 0x166, 85 TU117 = 0x167, 86 TU116 = 0x168, 87 // Ampere 88 GA100 = 0x170, 89 GA102 = 0x172, 90 GA103 = 0x173, 91 GA104 = 0x174, 92 GA106 = 0x176, 93 GA107 = 0x177, 94 // Hopper 95 GH100 = 0x180, 96 // Ada 97 AD102 = 0x192, 98 AD103 = 0x193, 99 AD104 = 0x194, 100 AD106 = 0x196, 101 AD107 = 0x197, 102 // Blackwell GB10x 103 GB100 = 0x1a0, 104 GB102 = 0x1a2, 105 // Blackwell GB20x 106 GB202 = 0x1b2, 107 GB203 = 0x1b3, 108 GB205 = 0x1b5, 109 GB206 = 0x1b6, 110 GB207 = 0x1b7, 111 }); 112 113 impl Chipset { 114 pub(crate) const fn arch(self) -> Architecture { 115 match self { 116 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 117 Architecture::Turing 118 } 119 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 120 Architecture::Ampere 121 } 122 Self::GH100 => Architecture::Hopper, 123 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 124 Architecture::Ada 125 } 126 Self::GB100 | Self::GB102 => Architecture::BlackwellGB10x, 127 Self::GB202 | Self::GB203 | Self::GB205 | Self::GB206 | Self::GB207 => { 128 Architecture::BlackwellGB20x 129 } 130 } 131 } 132 133 /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC. 134 /// 135 /// This includes all chipsets < GA102. 136 pub(crate) const fn needs_fwsec_bootloader(self) -> bool { 137 matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100) 138 } 139 140 /// Returns the address range of the PCI config mirror space. 141 pub(crate) fn pci_config_mirror_range(self) -> Range<u32> { 142 hal::gpu_hal(self).pci_config_mirror_range() 143 } 144 } 145 146 // TODO 147 // 148 // The resulting strings are used to generate firmware paths, hence the 149 // generated strings have to be stable. 150 // 151 // Hence, replace with something like strum_macros derive(Display). 152 // 153 // For now, redirect to fmt::Debug for convenience. 154 impl fmt::Display for Chipset { 155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 156 write!(f, "{self:?}") 157 } 158 } 159 160 bounded_enum! { 161 /// Enum representation of the GPU generation. 162 #[derive(fmt::Debug, Copy, Clone)] 163 pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> { 164 Turing = 0x16, 165 Ampere = 0x17, 166 Hopper = 0x18, 167 Ada = 0x19, 168 BlackwellGB10x = 0x1a, 169 BlackwellGB20x = 0x1b, 170 } 171 } 172 173 #[derive(Clone, Copy)] 174 pub(crate) struct Revision { 175 major: Bounded<u8, 4>, 176 minor: Bounded<u8, 4>, 177 } 178 179 impl From<regs::NV_PMC_BOOT_42> for Revision { 180 fn from(boot0: regs::NV_PMC_BOOT_42) -> Self { 181 Self { 182 major: boot0.major_revision().cast(), 183 minor: boot0.minor_revision().cast(), 184 } 185 } 186 } 187 188 impl fmt::Display for Revision { 189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 190 write!(f, "{:x}.{:x}", self.major, self.minor) 191 } 192 } 193 194 /// Structure holding a basic description of the GPU: `Chipset` and `Revision`. 195 #[derive(Clone, Copy)] 196 pub(crate) struct Spec { 197 chipset: Chipset, 198 revision: Revision, 199 } 200 201 impl Spec { 202 fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> { 203 // Some brief notes about boot0 and boot42, in chronological order: 204 // 205 // NV04 through NV50: 206 // 207 // Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs. 208 // boot42 may not even exist on some of these GPUs. 209 // 210 // Fermi through Volta: 211 // 212 // Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42 213 // is also guaranteed to be both present and accurate. 214 // 215 // Turing and later: 216 // 217 // Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not 218 // from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU. 219 // Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs. 220 221 let boot0 = bar.read(regs::NV_PMC_BOOT_0); 222 223 if boot0.is_older_than_fermi() { 224 return Err(ENODEV); 225 } 226 227 let boot42 = bar.read(regs::NV_PMC_BOOT_42); 228 Spec::try_from(boot42).inspect_err(|_| { 229 dev_err!(dev, "Unsupported chipset: {}\n", boot42); 230 }) 231 } 232 } 233 234 impl TryFrom<regs::NV_PMC_BOOT_42> for Spec { 235 type Error = Error; 236 237 fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> { 238 Ok(Self { 239 chipset: boot42.chipset()?, 240 revision: boot42.into(), 241 }) 242 } 243 } 244 245 impl fmt::Display for Spec { 246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 247 f.write_fmt(fmt!( 248 "Chipset: {}, Architecture: {:?}, Revision: {}", 249 self.chipset, 250 self.chipset.arch(), 251 self.revision 252 )) 253 } 254 } 255 256 /// Structure holding the resources required to operate the GPU. 257 #[pin_data(PinnedDrop)] 258 pub(crate) struct Gpu<'gpu> { 259 /// Device owning the GPU. 260 device: &'gpu device::Device<device::Bound>, 261 spec: Spec, 262 /// MMIO mapping of PCI BAR 0. 263 bar: &'gpu Bar0, 264 /// System memory page required for flushing all pending GPU-side memory writes done through 265 /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). 266 sysmem_flush: SysmemFlush<'gpu>, 267 /// GSP falcon instance, used for GSP boot up and cleanup. 268 gsp_falcon: Falcon<GspFalcon>, 269 /// SEC2 falcon instance, used for GSP boot up and cleanup. 270 sec2_falcon: Falcon<Sec2Falcon>, 271 /// GSP runtime data. Temporarily an empty placeholder. 272 #[pin] 273 gsp: Gsp, 274 /// GSP unload firmware bundle, if any. 275 unload_bundle: Option<gsp::UnloadBundle>, 276 } 277 278 impl<'gpu> Gpu<'gpu> { 279 pub(crate) fn new( 280 pdev: &'gpu pci::Device<device::Core<'_>>, 281 bar: &'gpu Bar0, 282 ) -> impl PinInit<Self, Error> + 'gpu { 283 try_pin_init!(Self { 284 device: pdev.as_ref(), 285 spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| { 286 dev_info!(pdev,"NVIDIA ({})\n", spec); 287 })?, 288 289 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. 290 _: { 291 let hal = hal::gpu_hal(spec.chipset); 292 let dma_mask = hal.dma_mask(); 293 294 // SAFETY: `Gpu` owns all DMA allocations for this device, and we are 295 // still constructing it, so no concurrent DMA allocations can exist. 296 unsafe { pdev.dma_set_mask_and_coherent(dma_mask)? }; 297 298 hal.wait_gfw_boot_completion(bar) 299 .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?; 300 }, 301 302 bar, 303 304 sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, 305 306 gsp_falcon: Falcon::new( 307 pdev.as_ref(), 308 spec.chipset, 309 ) 310 .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, 311 312 sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, 313 314 gsp <- Gsp::new(pdev), 315 316 // This member must be initialized last, so the `UnloadBundle` can never be dropped from 317 // outside of the constructed `Gpu`, ensuring that the unload sequence is properly run 318 // in case of failure. 319 unload_bundle: gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)?, 320 }) 321 } 322 } 323 324 #[pinned_drop] 325 impl PinnedDrop for Gpu<'_> { 326 fn drop(self: Pin<&mut Self>) { 327 let this = self.project(); 328 let device = *this.device; 329 let bar = *this.bar; 330 let bundle = this.unload_bundle.take(); 331 332 let _ = this 333 .gsp 334 .as_ref() 335 .get_ref() 336 .unload(device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle) 337 .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e)); 338 } 339 } 340