1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 //! Contains structures and functions dedicated to the parsing, building and patching of firmwares 5 //! to be loaded into a given execution unit. 6 7 use core::marker::PhantomData; 8 use core::ops::Deref; 9 10 use kernel::{ 11 firmware, 12 prelude::*, // 13 }; 14 15 use crate::{ 16 falcon::{ 17 FalconDmaLoadTarget, 18 FalconFirmware, // 19 }, 20 gpu, 21 gsp::boot_firmware_files, 22 num::IntoSafeCast, // 23 }; 24 25 pub(crate) mod booter; 26 pub(crate) mod fsp; 27 pub(crate) mod fwsec; 28 pub(crate) mod gsp; 29 pub(crate) mod riscv; 30 pub(crate) mod tlv; 31 32 pub(crate) const FIRMWARE_VERSION: &str = "570.144"; 33 34 /// Structure used to describe some firmwares, notably FWSEC-FRTS. 35 #[repr(C)] 36 #[derive(Debug, Clone, FromBytes)] 37 pub(crate) struct FalconUCodeDescV2 { 38 /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM. 39 hdr: u32, 40 /// Stored size of the ucode after the header, compressed or uncompressed 41 stored_size: u32, 42 /// Uncompressed size of the ucode. If store_size == uncompressed_size, then the ucode 43 /// is not compressed. 44 pub(crate) uncompressed_size: u32, 45 /// Code entry point 46 pub(crate) virtual_entry: u32, 47 /// Offset after the code segment at which the Application Interface Table headers are located. 48 pub(crate) interface_offset: u32, 49 /// Base address at which to load the code segment into 'IMEM'. 50 pub(crate) imem_phys_base: u32, 51 /// Size in bytes of the code to copy into 'IMEM' (includes both secure and non-secure 52 /// segments). 53 pub(crate) imem_load_size: u32, 54 /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start. 55 pub(crate) imem_virt_base: u32, 56 /// Virtual address of secure IMEM segment. 57 pub(crate) imem_sec_base: u32, 58 /// Size of secure IMEM segment. 59 pub(crate) imem_sec_size: u32, 60 /// Offset into stored (uncompressed) image at which DMEM begins. 61 pub(crate) dmem_offset: u32, 62 /// Base address at which to load the data segment into 'DMEM'. 63 pub(crate) dmem_phys_base: u32, 64 /// Size in bytes of the data to copy into 'DMEM'. 65 pub(crate) dmem_load_size: u32, 66 /// "Alternate" Size of data to load into IMEM. 67 pub(crate) alt_imem_load_size: u32, 68 /// "Alternate" Size of data to load into DMEM. 69 pub(crate) alt_dmem_load_size: u32, 70 } 71 72 /// Structure used to describe some firmwares, notably FWSEC-FRTS. 73 #[repr(C)] 74 #[derive(Debug, Clone, FromBytes)] 75 pub(crate) struct FalconUCodeDescV3 { 76 /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM. 77 hdr: u32, 78 /// Stored size of the ucode after the header. 79 stored_size: u32, 80 /// Offset in `DMEM` at which the signature is expected to be found. 81 pub(crate) pkc_data_offset: u32, 82 /// Offset after the code segment at which the app headers are located. 83 pub(crate) interface_offset: u32, 84 /// Base address at which to load the code segment into `IMEM`. 85 pub(crate) imem_phys_base: u32, 86 /// Size in bytes of the code to copy into `IMEM`. 87 pub(crate) imem_load_size: u32, 88 /// Virtual `IMEM` address (i.e. `tag`) at which the code should start. 89 pub(crate) imem_virt_base: u32, 90 /// Base address at which to load the data segment into `DMEM`. 91 pub(crate) dmem_phys_base: u32, 92 /// Size in bytes of the data to copy into `DMEM`. 93 pub(crate) dmem_load_size: u32, 94 /// Mask of the falcon engines on which this firmware can run. 95 pub(crate) engine_id_mask: u16, 96 /// ID of the ucode used to infer a fuse register to validate the signature. 97 pub(crate) ucode_id: u8, 98 /// Number of signatures in this firmware. 99 pub(crate) signature_count: u8, 100 /// Versions of the signatures, used to infer a valid signature to use. 101 pub(crate) signature_versions: u16, 102 _reserved: u16, 103 } 104 105 /// Enum wrapping the different versions of Falcon microcode descriptors. 106 /// 107 /// This allows handling both V2 and V3 descriptor formats through a 108 /// unified type, providing version-agnostic access to firmware metadata 109 /// via the [`FalconUCodeDescriptor`] trait. 110 #[derive(Debug, Clone)] 111 pub(crate) enum FalconUCodeDesc { 112 V2(FalconUCodeDescV2), 113 V3(FalconUCodeDescV3), 114 } 115 116 impl Deref for FalconUCodeDesc { 117 type Target = dyn FalconUCodeDescriptor; 118 119 fn deref(&self) -> &Self::Target { 120 match self { 121 FalconUCodeDesc::V2(v2) => v2, 122 FalconUCodeDesc::V3(v3) => v3, 123 } 124 } 125 } 126 127 /// Trait providing a common interface for accessing Falcon microcode descriptor fields. 128 /// 129 /// This trait abstracts over the different descriptor versions ([`FalconUCodeDescV2`] and 130 /// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata without needing to 131 /// know the specific descriptor version. Fields not present return zero. 132 pub(crate) trait FalconUCodeDescriptor { 133 fn hdr(&self) -> u32; 134 fn imem_load_size(&self) -> u32; 135 fn interface_offset(&self) -> u32; 136 fn dmem_load_size(&self) -> u32; 137 fn pkc_data_offset(&self) -> u32; 138 fn engine_id_mask(&self) -> u16; 139 fn ucode_id(&self) -> u8; 140 fn signature_count(&self) -> u8; 141 fn signature_versions(&self) -> u16; 142 143 /// Returns the size in bytes of the header. 144 fn size(&self) -> usize { 145 let hdr = self.hdr(); 146 147 const HDR_SIZE_SHIFT: u32 = 16; 148 const HDR_SIZE_MASK: u32 = 0xffff0000; 149 ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() 150 } 151 152 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget; 153 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>; 154 fn dmem_load_params(&self) -> FalconDmaLoadTarget; 155 } 156 157 impl FalconUCodeDescriptor for FalconUCodeDescV2 { 158 fn hdr(&self) -> u32 { 159 self.hdr 160 } 161 fn imem_load_size(&self) -> u32 { 162 self.imem_load_size 163 } 164 fn interface_offset(&self) -> u32 { 165 self.interface_offset 166 } 167 fn dmem_load_size(&self) -> u32 { 168 self.dmem_load_size 169 } 170 fn pkc_data_offset(&self) -> u32 { 171 0 172 } 173 fn engine_id_mask(&self) -> u16 { 174 0 175 } 176 fn ucode_id(&self) -> u8 { 177 0 178 } 179 fn signature_count(&self) -> u8 { 180 0 181 } 182 fn signature_versions(&self) -> u16 { 183 0 184 } 185 186 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { 187 // `imem_sec_base` is the *virtual* start address of the secure IMEM segment, so subtract 188 // `imem_virt_base` to get its physical offset. 189 let imem_sec_start = self.imem_sec_base.saturating_sub(self.imem_virt_base); 190 191 FalconDmaLoadTarget { 192 src_start: imem_sec_start, 193 dst_start: self.imem_phys_base.saturating_add(imem_sec_start), 194 len: self.imem_sec_size, 195 } 196 } 197 198 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { 199 Some(FalconDmaLoadTarget { 200 // Non-secure code always starts at offset 0. 201 src_start: 0, 202 dst_start: self.imem_phys_base, 203 // `imem_load_size` includes the size of the secure segment, so subtract it to 204 // get the correct amount of data to copy. 205 len: self.imem_load_size.saturating_sub(self.imem_sec_size), 206 }) 207 } 208 209 fn dmem_load_params(&self) -> FalconDmaLoadTarget { 210 FalconDmaLoadTarget { 211 src_start: self.dmem_offset, 212 dst_start: self.dmem_phys_base, 213 len: self.dmem_load_size, 214 } 215 } 216 } 217 218 impl FalconUCodeDescriptor for FalconUCodeDescV3 { 219 fn hdr(&self) -> u32 { 220 self.hdr 221 } 222 fn imem_load_size(&self) -> u32 { 223 self.imem_load_size 224 } 225 fn interface_offset(&self) -> u32 { 226 self.interface_offset 227 } 228 fn dmem_load_size(&self) -> u32 { 229 self.dmem_load_size 230 } 231 fn pkc_data_offset(&self) -> u32 { 232 self.pkc_data_offset 233 } 234 fn engine_id_mask(&self) -> u16 { 235 self.engine_id_mask 236 } 237 fn ucode_id(&self) -> u8 { 238 self.ucode_id 239 } 240 fn signature_count(&self) -> u8 { 241 self.signature_count 242 } 243 fn signature_versions(&self) -> u16 { 244 self.signature_versions 245 } 246 247 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { 248 FalconDmaLoadTarget { 249 // IMEM segment always starts at offset 0. 250 src_start: 0, 251 dst_start: self.imem_phys_base, 252 len: self.imem_load_size, 253 } 254 } 255 256 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { 257 // Not used on V3 platforms 258 None 259 } 260 261 fn dmem_load_params(&self) -> FalconDmaLoadTarget { 262 FalconDmaLoadTarget { 263 // DMEM segment starts right after the IMEM one. 264 src_start: self.imem_load_size, 265 dst_start: self.dmem_phys_base, 266 len: self.dmem_load_size, 267 } 268 } 269 } 270 271 /// Trait implemented by types defining the signed state of a firmware. 272 trait SignedState {} 273 274 /// Type indicating that the firmware must be signed before it can be used. 275 struct Unsigned; 276 impl SignedState for Unsigned {} 277 278 /// Type indicating that the firmware is signed and ready to be loaded. 279 struct Signed; 280 impl SignedState for Signed {} 281 282 /// Microcode to be loaded into a specific falcon. 283 /// 284 /// This is module-local and meant for sub-modules to use internally. 285 /// 286 /// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature 287 /// before it can be loaded (with an exception for development hardware). The 288 /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the 289 /// firmware to its [`Signed`] state. 290 // TODO: Consider replacing this with a coherent memory object once `CoherentAllocation` supports 291 // temporary CPU-exclusive access to the object without unsafe methods. 292 struct FirmwareObject<F: FalconFirmware, S: SignedState>(KVVec<u8>, PhantomData<(F, S)>); 293 294 /// Trait for signatures to be patched directly into a given firmware. 295 /// 296 /// This is module-local and meant for sub-modules to use internally. 297 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {} 298 299 impl<F: FalconFirmware> FirmwareObject<F, Unsigned> { 300 /// Patches the firmware at offset `signature_start` with `signature`. 301 fn patch_signature<S: FirmwareSignature<F>>( 302 mut self, 303 signature: &S, 304 signature_start: usize, 305 ) -> Result<FirmwareObject<F, Signed>> { 306 let signature_bytes = signature.as_ref(); 307 let signature_end = signature_start 308 .checked_add(signature_bytes.len()) 309 .ok_or(EOVERFLOW)?; 310 let dst = self 311 .0 312 .get_mut(signature_start..signature_end) 313 .ok_or(EINVAL)?; 314 315 // PANIC: `dst` and `signature_bytes` have the same length. 316 dst.copy_from_slice(signature_bytes); 317 318 Ok(FirmwareObject(self.0, PhantomData)) 319 } 320 321 /// Mark the firmware as signed without patching it. 322 /// 323 /// This method is used to explicitly confirm that we do not need to sign the firmware, while 324 /// allowing us to continue as if it was. This is typically only needed for development 325 /// hardware. 326 fn no_patch_signature(self) -> FirmwareObject<F, Signed> { 327 FirmwareObject(self.0, PhantomData) 328 } 329 } 330 331 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>); 332 333 impl<const N: usize> ModInfoBuilder<N> { 334 const fn make_entry_file(self, chipset: &str, fw: &str) -> Self { 335 ModInfoBuilder( 336 self.0 337 .new_entry() 338 .push("nvidia/") 339 .push(chipset) 340 .push("/gsp/") 341 .push(fw) 342 .push("-") 343 .push(FIRMWARE_VERSION) 344 .push(".bin"), 345 ) 346 } 347 348 const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self { 349 let name = chipset.name(); 350 351 // GSP firmware files are always present. 352 let mut this = self 353 .make_entry_file(name, "bootloader") 354 .make_entry_file(name, "gsp"); 355 356 // Add the firmware files specific to the GSP boot method of `chipset`. 357 let boot_files = boot_firmware_files(chipset); 358 let mut i = 0; 359 while i < boot_files.len() { 360 this = this.make_entry_file(name, boot_files[i]); 361 i += 1; 362 } 363 364 this 365 } 366 367 pub(crate) const fn create( 368 module_name: &'static core::ffi::CStr, 369 ) -> firmware::ModInfoBuilder<N> { 370 let mut this = Self(firmware::ModInfoBuilder::new(module_name)); 371 let mut i = 0; 372 373 while i < gpu::Chipset::ALL.len() { 374 this = this.make_entry_chipset(gpu::Chipset::ALL[i]); 375 i += 1; 376 } 377 378 this.0 379 } 380 } 381