Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing

On Turing and GA100, a new firmware image called the Generic Bootloader
(gen_bootloader) must be used to load FWSEC into Falcon memory. The
driver loads the generic bootloader into Falcon IMEM, passes a
descriptor that points to FWSEC using DMEM, and then boots the generic
bootloader. The bootloader will then load FWSEC into IMEM and boot it.

Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patch.msgid.link/20260306-turing_prep-v11-12-8f0042c5d026@nvidia.com

authored by

Timur Tabi and committed by
Alexandre Courbot
50b3e0c7 e9224168

+365 -3
+6
drivers/gpu/nova-core/firmware/fwsec.rs
··· 10 10 //! - The command to be run, as this firmware can perform several tasks ; 11 11 //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode. 12 12 13 + pub(crate) mod bootloader; 14 + 13 15 use core::marker::PhantomData; 14 16 15 17 use kernel::{ ··· 387 385 } 388 386 389 387 /// Loads the FWSEC firmware into `falcon` and execute it. 388 + /// 389 + /// This must only be called on chipsets that do not need the FWSEC bootloader (i.e., where 390 + /// [`Chipset::needs_fwsec_bootloader()`](crate::gpu::Chipset::needs_fwsec_bootloader) returns 391 + /// `false`). On chipsets that do, use [`bootloader::FwsecFirmwareWithBl`] instead. 390 392 pub(crate) fn run( 391 393 &self, 392 394 dev: &Device<device::Bound>,
+347
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Bootloader support for the FWSEC firmware. 4 + //! 5 + //! On Turing, the FWSEC firmware is not loaded directly, but is instead loaded through a small 6 + //! bootloader program that performs the required DMA operations. This bootloader itself needs to 7 + //! be loaded using PIO. 8 + 9 + use kernel::{ 10 + alloc::KVec, 11 + device::{ 12 + self, 13 + Device, // 14 + }, 15 + prelude::*, 16 + ptr::{ 17 + Alignable, 18 + Alignment, // 19 + }, 20 + sizes, 21 + transmute::{ 22 + AsBytes, 23 + FromBytes, // 24 + }, 25 + }; 26 + 27 + use crate::{ 28 + dma::DmaObject, 29 + driver::Bar0, 30 + falcon::{ 31 + self, 32 + gsp::Gsp, 33 + Falcon, 34 + FalconBromParams, 35 + FalconDmaLoadable, 36 + FalconEngine, 37 + FalconFbifMemType, 38 + FalconFbifTarget, 39 + FalconFirmware, 40 + FalconPioDmemLoadTarget, 41 + FalconPioImemLoadTarget, 42 + FalconPioLoadable, // 43 + }, 44 + firmware::{ 45 + fwsec::FwsecFirmware, 46 + request_firmware, 47 + BinHdr, 48 + FIRMWARE_VERSION, // 49 + }, 50 + gpu::Chipset, 51 + num::FromSafeCast, 52 + regs, 53 + }; 54 + 55 + /// Descriptor used by RM to figure out the requirements of the boot loader. 56 + /// 57 + /// Most of its fields appear to be legacy and carry incorrect values, so they are left unused. 58 + #[repr(C)] 59 + #[derive(Debug, Clone)] 60 + struct BootloaderDesc { 61 + /// Starting tag of bootloader. 62 + start_tag: u32, 63 + /// DMEM load offset - unused here as we always load at offset `0`. 64 + _dmem_load_off: u32, 65 + /// Offset of code section in the image. Unused as there is only one section in the bootloader 66 + /// binary. 67 + _code_off: u32, 68 + /// Size of code section in the image. 69 + code_size: u32, 70 + /// Offset of data section in the image. Unused as we build the data section ourselves. 71 + _data_off: u32, 72 + /// Size of data section in the image. Unused as we build the data section ourselves. 73 + _data_size: u32, 74 + } 75 + // SAFETY: any byte sequence is valid for this struct. 76 + unsafe impl FromBytes for BootloaderDesc {} 77 + 78 + /// Structure used by the boot-loader to load the rest of the code. 79 + /// 80 + /// This has to be filled by the GPU driver and copied into DMEM at offset 81 + /// [`BootloaderDesc.dmem_load_off`]. 82 + #[repr(C, packed)] 83 + #[derive(Debug, Clone)] 84 + struct BootloaderDmemDescV2 { 85 + /// Reserved, should always be first element. 86 + reserved: [u32; 4], 87 + /// 16B signature for secure code, 0s if no secure code. 88 + signature: [u32; 4], 89 + /// DMA context used by the bootloader while loading code/data. 90 + ctx_dma: u32, 91 + /// 256B-aligned physical FB address where code is located. 92 + code_dma_base: u64, 93 + /// Offset from `code_dma_base` where the non-secure code is located. 94 + /// 95 + /// Also used as destination IMEM offset of non-secure code as the DMA firmware object is 96 + /// expected to be a mirror image of its loaded state. 97 + /// 98 + /// Must be multiple of 256. 99 + non_sec_code_off: u32, 100 + /// Size of the non-secure code part. 101 + non_sec_code_size: u32, 102 + /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256). 103 + /// 104 + /// Also used as destination IMEM offset of secure code as the DMA firmware object is expected 105 + /// to be a mirror image of its loaded state. 106 + /// 107 + /// Must be multiple of 256. 108 + sec_code_off: u32, 109 + /// Size of the secure code part. 110 + sec_code_size: u32, 111 + /// Code entry point invoked by the bootloader after code is loaded. 112 + code_entry_point: u32, 113 + /// 256B-aligned physical FB address where data is located. 114 + data_dma_base: u64, 115 + /// Size of data block (should be multiple of 256B). 116 + data_size: u32, 117 + /// Number of arguments to be passed to the target firmware being loaded. 118 + argc: u32, 119 + /// Arguments to be passed to the target firmware being loaded. 120 + argv: u32, 121 + } 122 + // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 123 + unsafe impl AsBytes for BootloaderDmemDescV2 {} 124 + 125 + /// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load 126 + /// operation. 127 + pub(crate) struct FwsecFirmwareWithBl { 128 + /// DMA object the bootloader will copy the firmware from. 129 + _firmware_dma: DmaObject, 130 + /// Code of the bootloader to be loaded into non-secure IMEM. 131 + ucode: KVec<u8>, 132 + /// Descriptor to be loaded into DMEM for the bootloader to read. 133 + dmem_desc: BootloaderDmemDescV2, 134 + /// Range-validated start offset of the firmware code in IMEM. 135 + imem_dst_start: u16, 136 + /// BROM parameters of the loaded firmware. 137 + brom_params: FalconBromParams, 138 + /// Range-validated `desc.start_tag`. 139 + start_tag: u16, 140 + } 141 + 142 + impl FwsecFirmwareWithBl { 143 + /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be 144 + /// loaded using it. 145 + pub(crate) fn new( 146 + firmware: FwsecFirmware, 147 + dev: &Device<device::Bound>, 148 + chipset: Chipset, 149 + ) -> Result<Self> { 150 + let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?; 151 + let hdr = fw 152 + .data() 153 + .get(0..size_of::<BinHdr>()) 154 + .and_then(BinHdr::from_bytes_copy) 155 + .ok_or(EINVAL)?; 156 + 157 + let desc = { 158 + let desc_offset = usize::from_safe_cast(hdr.header_offset); 159 + 160 + fw.data() 161 + .get(desc_offset..) 162 + .and_then(BootloaderDesc::from_bytes_copy_prefix) 163 + .ok_or(EINVAL)? 164 + .0 165 + }; 166 + 167 + let ucode = { 168 + let ucode_start = usize::from_safe_cast(hdr.data_offset); 169 + let code_size = usize::from_safe_cast(desc.code_size); 170 + // Align to falcon block size (256 bytes). 171 + let aligned_code_size = code_size 172 + .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>()) 173 + .ok_or(EINVAL)?; 174 + 175 + let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?; 176 + ucode.extend_from_slice( 177 + fw.data() 178 + .get(ucode_start..ucode_start + code_size) 179 + .ok_or(EINVAL)?, 180 + GFP_KERNEL, 181 + )?; 182 + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?; 183 + 184 + ucode 185 + }; 186 + 187 + // `BootloaderDmemDescV2` expects the source to be a mirror image of the destination and 188 + // uses the same offset parameter for both. 189 + // 190 + // Thus, the start of the source object needs to be padded with the difference between the 191 + // destination and source offsets. 192 + // 193 + // In practice, this is expected to always be zero but is required for code correctness. 194 + let (align_padding, firmware_dma) = { 195 + let align_padding = { 196 + let imem_sec = firmware.imem_sec_load_params(); 197 + 198 + imem_sec 199 + .dst_start 200 + .checked_sub(imem_sec.src_start) 201 + .map(usize::from_safe_cast) 202 + .ok_or(EOVERFLOW)? 203 + }; 204 + 205 + let mut firmware_obj = KVVec::new(); 206 + firmware_obj.extend_with(align_padding, 0u8, GFP_KERNEL)?; 207 + firmware_obj.extend_from_slice(firmware.ucode.0.as_slice(), GFP_KERNEL)?; 208 + 209 + ( 210 + align_padding, 211 + DmaObject::from_data(dev, firmware_obj.as_slice())?, 212 + ) 213 + }; 214 + 215 + let dmem_desc = { 216 + // Bootloader payload is in non-coherent system memory. 217 + const FALCON_DMAIDX_PHYS_SYS_NCOH: u32 = 4; 218 + 219 + let imem_sec = firmware.imem_sec_load_params(); 220 + let imem_ns = firmware.imem_ns_load_params().ok_or(EINVAL)?; 221 + let dmem = firmware.dmem_load_params(); 222 + 223 + // The bootloader does not have a data destination offset field and copies the data at 224 + // the start of DMEM, so it can only be used if the destination offset of the firmware 225 + // is 0. 226 + if dmem.dst_start != 0 { 227 + return Err(EINVAL); 228 + } 229 + 230 + BootloaderDmemDescV2 { 231 + reserved: [0; 4], 232 + signature: [0; 4], 233 + ctx_dma: FALCON_DMAIDX_PHYS_SYS_NCOH, 234 + code_dma_base: firmware_dma.dma_handle(), 235 + // `dst_start` is also valid as the source offset since the firmware DMA object is 236 + // a mirror image of the target IMEM layout. 237 + non_sec_code_off: imem_ns.dst_start, 238 + non_sec_code_size: imem_ns.len, 239 + // `dst_start` is also valid as the source offset since the firmware DMA object is 240 + // a mirror image of the target IMEM layout. 241 + sec_code_off: imem_sec.dst_start, 242 + sec_code_size: imem_sec.len, 243 + code_entry_point: 0, 244 + // Start of data section is the added padding + the DMEM `src_start` field. 245 + data_dma_base: firmware_dma 246 + .dma_handle() 247 + .checked_add(u64::from_safe_cast(align_padding)) 248 + .and_then(|offset| offset.checked_add(dmem.src_start.into())) 249 + .ok_or(EOVERFLOW)?, 250 + data_size: dmem.len, 251 + argc: 0, 252 + argv: 0, 253 + } 254 + }; 255 + 256 + // The bootloader's code must be loaded in the area right below the first 64K of IMEM. 257 + const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K; 258 + let imem_dst_start = BOOTLOADER_LOAD_CEILING 259 + .checked_sub(ucode.len()) 260 + .ok_or(EOVERFLOW)?; 261 + 262 + Ok(Self { 263 + _firmware_dma: firmware_dma, 264 + ucode, 265 + dmem_desc, 266 + brom_params: firmware.brom_params(), 267 + imem_dst_start: u16::try_from(imem_dst_start)?, 268 + start_tag: u16::try_from(desc.start_tag)?, 269 + }) 270 + } 271 + 272 + /// Loads the bootloader into `falcon` and execute it. 273 + /// 274 + /// The bootloader will load the FWSEC firmware and then execute it. This function returns 275 + /// after FWSEC has reached completion. 276 + pub(crate) fn run( 277 + &self, 278 + dev: &Device<device::Bound>, 279 + falcon: &Falcon<Gsp>, 280 + bar: &Bar0, 281 + ) -> Result<()> { 282 + // Reset falcon, load the firmware, and run it. 283 + falcon 284 + .reset(bar) 285 + .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; 286 + falcon 287 + .pio_load(bar, self) 288 + .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; 289 + 290 + // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory. 291 + regs::NV_PFALCON_FBIF_TRANSCFG::try_update( 292 + bar, 293 + &Gsp::ID, 294 + usize::from_safe_cast(self.dmem_desc.ctx_dma), 295 + |v| { 296 + v.set_target(FalconFbifTarget::CoherentSysmem) 297 + .set_mem_type(FalconFbifMemType::Physical) 298 + }, 299 + )?; 300 + 301 + let (mbox0, _) = falcon 302 + .boot(bar, Some(0), None) 303 + .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?; 304 + if mbox0 != 0 { 305 + dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0); 306 + Err(EIO) 307 + } else { 308 + Ok(()) 309 + } 310 + } 311 + } 312 + 313 + impl FalconFirmware for FwsecFirmwareWithBl { 314 + type Target = Gsp; 315 + 316 + fn brom_params(&self) -> FalconBromParams { 317 + self.brom_params.clone() 318 + } 319 + 320 + fn boot_addr(&self) -> u32 { 321 + // On V2 platforms, the boot address is extracted from the generic bootloader, because the 322 + // gbl is what actually copies FWSEC into memory, so that is what needs to be booted. 323 + u32::from(self.start_tag) << 8 324 + } 325 + } 326 + 327 + impl FalconPioLoadable for FwsecFirmwareWithBl { 328 + fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 329 + None 330 + } 331 + 332 + fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 333 + Some(FalconPioImemLoadTarget { 334 + data: self.ucode.as_ref(), 335 + dst_start: self.imem_dst_start, 336 + secure: false, 337 + start_tag: self.start_tag, 338 + }) 339 + } 340 + 341 + fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> { 342 + FalconPioDmemLoadTarget { 343 + data: self.dmem_desc.as_bytes(), 344 + dst_start: 0, 345 + } 346 + } 347 + }
+12 -3
drivers/gpu/nova-core/gsp/boot.rs
··· 24 24 BooterKind, // 25 25 }, 26 26 fwsec::{ 27 + bootloader::FwsecFirmwareWithBl, 27 28 FwsecCommand, 28 29 FwsecFirmware, // 29 30 }, ··· 49 48 /// created the WPR2 region. 50 49 fn run_fwsec_frts( 51 50 dev: &device::Device<device::Bound>, 51 + chipset: Chipset, 52 52 falcon: &Falcon<Gsp>, 53 53 bar: &Bar0, 54 54 bios: &Vbios, ··· 65 63 return Err(EBUSY); 66 64 } 67 65 66 + // FWSEC-FRTS will create the WPR2 region. 68 67 let fwsec_frts = FwsecFirmware::new( 69 68 dev, 70 69 falcon, ··· 77 74 }, 78 75 )?; 79 76 80 - // Run FWSEC-FRTS to create the WPR2 region. 81 - fwsec_frts.run(dev, falcon, bar)?; 77 + if chipset.needs_fwsec_bootloader() { 78 + let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?; 79 + // Load and run the bootloader, which will load FWSEC-FRTS and run it. 80 + fwsec_frts_bl.run(dev, falcon, bar)?; 81 + } else { 82 + // Load and run FWSEC-FRTS directly. 83 + fwsec_frts.run(dev, falcon, bar)?; 84 + } 82 85 83 86 // SCRATCH_E contains the error code for FWSEC-FRTS. 84 87 let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code(); ··· 153 144 let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; 154 145 dev_dbg!(dev, "{:#x?}\n", fb_layout); 155 146 156 - Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; 147 + Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?; 157 148 158 149 let booter_loader = BooterFirmware::new( 159 150 dev,