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