1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 //! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware 5 //! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon 6 //! (and optionally unload it through a separate firmware image). 7 8 use core::marker::PhantomData; 9 10 use kernel::{ 11 device, 12 dma::Coherent, 13 prelude::*, 14 transmute::FromBytes, // 15 }; 16 17 use crate::{ 18 driver::Bar0, 19 falcon::{ 20 sec2::Sec2, 21 Falcon, 22 FalconBromParams, 23 FalconDmaLoadTarget, 24 FalconDmaLoadable, 25 FalconFirmware, // 26 }, 27 firmware::{ 28 BinFirmware, 29 FirmwareObject, 30 FirmwareSignature, 31 Signed, 32 Unsigned, // 33 }, 34 gpu::Chipset, 35 num::{ 36 FromSafeCast, 37 IntoSafeCast, // 38 }, 39 }; 40 41 /// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at 42 /// `offset` in `slice`. 43 fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> { 44 let end = offset.checked_add(size_of::<S>()).ok_or(EINVAL)?; 45 slice 46 .get(offset..end) 47 .and_then(S::from_bytes_copy) 48 .ok_or(EINVAL) 49 } 50 51 /// Heavy-Secured firmware header. 52 /// 53 /// Such firmwares have an application-specific payload that needs to be patched with a given 54 /// signature. 55 #[repr(C)] 56 #[derive(Debug, Clone)] 57 struct HsHeaderV2 { 58 /// Offset to the start of the signatures. 59 sig_prod_offset: u32, 60 /// Size in bytes of the signatures. 61 sig_prod_size: u32, 62 /// Offset to a `u32` containing the location at which to patch the signature in the microcode 63 /// image. 64 patch_loc_offset: u32, 65 /// Offset to a `u32` containing the index of the signature to patch. 66 patch_sig_offset: u32, 67 /// Start offset to the signature metadata. 68 meta_data_offset: u32, 69 /// Size in bytes of the signature metadata. 70 meta_data_size: u32, 71 /// Offset to a `u32` containing the number of signatures in the signatures section. 72 num_sig_offset: u32, 73 /// Offset of the application-specific header. 74 header_offset: u32, 75 /// Size in bytes of the application-specific header. 76 header_size: u32, 77 } 78 79 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. 80 unsafe impl FromBytes for HsHeaderV2 {} 81 82 /// Heavy-Secured Firmware image container. 83 /// 84 /// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to 85 /// read from in the firmware data. 86 struct HsFirmwareV2<'a> { 87 hdr: HsHeaderV2, 88 fw: &'a [u8], 89 } 90 91 impl<'a> HsFirmwareV2<'a> { 92 /// Interprets the header of `bin_fw` as a [`HsHeaderV2`] and returns an instance of 93 /// `HsFirmwareV2` for further parsing. 94 /// 95 /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image. 96 fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> { 97 frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast()) 98 .map(|hdr| Self { hdr, fw: bin_fw.fw }) 99 } 100 101 /// Returns the location at which the signatures should be patched in the microcode image. 102 /// 103 /// Fails if the offset of the patch location is outside the bounds of the firmware 104 /// image. 105 fn patch_location(&self) -> Result<u32> { 106 frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast()) 107 } 108 109 /// Returns an iterator to the signatures of the firmware. The iterator can be empty if the 110 /// firmware is unsigned. 111 /// 112 /// Fails if the pointed signatures are outside the bounds of the firmware image. 113 fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> { 114 let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?; 115 let iter = match self.hdr.sig_prod_size.checked_div(num_sig) { 116 // If there are no signatures, return an iterator that will yield zero elements. 117 None => (&[] as &[u8]).chunks_exact(1), 118 Some(sig_size) => { 119 let patch_sig = 120 frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?; 121 122 let signatures_start = self 123 .hdr 124 .sig_prod_offset 125 .checked_add(patch_sig) 126 .map(usize::from_safe_cast) 127 .ok_or(EINVAL)?; 128 129 let signatures_end = signatures_start 130 .checked_add(usize::from_safe_cast(self.hdr.sig_prod_size)) 131 .ok_or(EINVAL)?; 132 133 self.fw 134 // Get signatures range. 135 .get(signatures_start..signatures_end) 136 .ok_or(EINVAL)? 137 .chunks_exact(sig_size.into_safe_cast()) 138 } 139 }; 140 141 // Map the byte slices into signatures. 142 Ok(iter.map(BooterSignature)) 143 } 144 } 145 146 /// Signature parameters, as defined in the firmware. 147 #[repr(C)] 148 struct HsSignatureParams { 149 /// Fuse version to use. 150 fuse_ver: u32, 151 /// Mask of engine IDs this firmware applies to. 152 engine_id_mask: u32, 153 /// ID of the microcode. 154 ucode_id: u32, 155 } 156 157 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. 158 unsafe impl FromBytes for HsSignatureParams {} 159 160 impl HsSignatureParams { 161 /// Returns the signature parameters contained in `hs_fw`. 162 /// 163 /// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or 164 /// if its size doesn't match that of [`HsSignatureParams`]. 165 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> { 166 let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset); 167 let end = start 168 .checked_add(hs_fw.hdr.meta_data_size.into_safe_cast()) 169 .ok_or(EINVAL)?; 170 171 hs_fw 172 .fw 173 .get(start..end) 174 .and_then(Self::from_bytes_copy) 175 .ok_or(EINVAL) 176 } 177 } 178 179 /// Header for code and data load offsets. 180 #[repr(C)] 181 #[derive(Debug, Clone)] 182 struct HsLoadHeaderV2 { 183 // Offset at which the code starts. 184 os_code_offset: u32, 185 // Total size of the code, for all apps. 186 os_code_size: u32, 187 // Offset at which the data starts. 188 os_data_offset: u32, 189 // Size of the data. 190 os_data_size: u32, 191 // Number of apps following this header. Each app is described by a [`HsLoadHeaderV2App`]. 192 num_apps: u32, 193 } 194 195 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. 196 unsafe impl FromBytes for HsLoadHeaderV2 {} 197 198 impl HsLoadHeaderV2 { 199 /// Returns the load header contained in `hs_fw`. 200 /// 201 /// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image. 202 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> { 203 frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast()) 204 } 205 } 206 207 /// Header for app code loader. 208 #[repr(C)] 209 #[derive(Debug, Clone)] 210 struct HsLoadHeaderV2App { 211 /// Offset at which to load the app code. 212 offset: u32, 213 /// Length in bytes of the app code. 214 len: u32, 215 } 216 217 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. 218 unsafe impl FromBytes for HsLoadHeaderV2App {} 219 220 impl HsLoadHeaderV2App { 221 /// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`. 222 /// 223 /// Fails if `idx` is larger than the number of apps declared in `hs_fw`, or if the header is 224 /// not within the bounds of the firmware image. 225 fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> { 226 let load_hdr = HsLoadHeaderV2::new(hs_fw)?; 227 if idx >= load_hdr.num_apps { 228 Err(EINVAL) 229 } else { 230 frombytes_at::<Self>( 231 hs_fw.fw, 232 usize::from_safe_cast(hs_fw.hdr.header_offset) 233 // Skip the load header... 234 .checked_add(size_of::<HsLoadHeaderV2>()) 235 // ... and jump to app header `idx`. 236 .and_then(|offset| { 237 offset 238 .checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?) 239 }) 240 .ok_or(EINVAL)?, 241 ) 242 } 243 } 244 } 245 246 /// Signature for Booter firmware. Their size is encoded into the header and not known a compile 247 /// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`]. 248 struct BooterSignature<'a>(&'a [u8]); 249 250 impl<'a> AsRef<[u8]> for BooterSignature<'a> { 251 fn as_ref(&self) -> &[u8] { 252 self.0 253 } 254 } 255 256 impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {} 257 258 /// The `Booter` loader firmware, responsible for loading the GSP. 259 pub(crate) struct BooterFirmware { 260 // Load parameters for Secure `IMEM` falcon memory. 261 imem_sec_load_target: FalconDmaLoadTarget, 262 // Load parameters for Non-Secure `IMEM` falcon memory, 263 // used only on Turing and GA100 264 imem_ns_load_target: Option<FalconDmaLoadTarget>, 265 // Load parameters for `DMEM` falcon memory. 266 dmem_load_target: FalconDmaLoadTarget, 267 // BROM falcon parameters. 268 brom_params: FalconBromParams, 269 // Device-mapped firmware image. 270 ucode: FirmwareObject<Self, Signed>, 271 } 272 273 impl FirmwareObject<BooterFirmware, Unsigned> { 274 fn new_booter(data: &[u8]) -> Result<Self> { 275 let mut ucode = KVVec::new(); 276 ucode.extend_from_slice(data, GFP_KERNEL)?; 277 278 Ok(Self(ucode, PhantomData)) 279 } 280 } 281 282 #[derive(Copy, Clone, Debug, PartialEq)] 283 pub(crate) enum BooterKind { 284 Loader, 285 Unloader, 286 } 287 288 impl BooterFirmware { 289 /// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is 290 /// ready to be loaded and run on `falcon`. 291 pub(crate) fn new( 292 dev: &device::Device<device::Bound>, 293 kind: BooterKind, 294 chipset: Chipset, 295 ver: &str, 296 falcon: &Falcon<<Self as FalconFirmware>::Target>, 297 bar: &Bar0, 298 ) -> Result<Self> { 299 let fw_name = match kind { 300 BooterKind::Loader => "booter_load", 301 BooterKind::Unloader => "booter_unload", 302 }; 303 let fw = super::request_firmware(dev, chipset, fw_name, ver)?; 304 let bin_fw = BinFirmware::new(&fw)?; 305 306 // The binary firmware embeds a Heavy-Secured firmware. 307 let hs_fw = HsFirmwareV2::new(&bin_fw)?; 308 309 // The Heavy-Secured firmware embeds a firmware load descriptor. 310 let load_hdr = HsLoadHeaderV2::new(&hs_fw)?; 311 312 // Offset in `ucode` where to patch the signature. 313 let patch_loc = hs_fw.patch_location()?; 314 315 let sig_params = HsSignatureParams::new(&hs_fw)?; 316 let brom_params = FalconBromParams { 317 // `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the 318 // signature patch location. 319 pkc_data_offset: patch_loc 320 .checked_sub(load_hdr.os_data_offset) 321 .ok_or(EINVAL)?, 322 engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?, 323 ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?, 324 }; 325 let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?; 326 327 // Object containing the firmware microcode to be signature-patched. 328 let ucode = bin_fw 329 .data() 330 .ok_or(EINVAL) 331 .and_then(FirmwareObject::<Self, _>::new_booter)?; 332 333 let ucode_signed = { 334 let mut signatures = hs_fw.signatures_iter()?.peekable(); 335 336 if signatures.peek().is_none() { 337 // If there are no signatures, then the firmware is unsigned. 338 ucode.no_patch_signature() 339 } else { 340 // Obtain the version from the fuse register, and extract the corresponding 341 // signature. 342 let reg_fuse_version = falcon.signature_reg_fuse_version( 343 bar, 344 brom_params.engine_id_mask, 345 brom_params.ucode_id, 346 )?; 347 348 // `0` means the last signature should be used. 349 const FUSE_VERSION_USE_LAST_SIG: u32 = 0; 350 let signature = match reg_fuse_version { 351 FUSE_VERSION_USE_LAST_SIG => signatures.last(), 352 // Otherwise hardware fuse version needs to be subtracted to obtain the index. 353 reg_fuse_version => { 354 let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else { 355 dev_err!(dev, "invalid fuse version for Booter firmware\n"); 356 return Err(EINVAL); 357 }; 358 signatures.nth(idx.into_safe_cast()) 359 } 360 } 361 .ok_or(EINVAL)?; 362 363 ucode.patch_signature(&signature, patch_loc.into_safe_cast())? 364 } 365 }; 366 367 // There are two versions of Booter, one for Turing/GA100, and another for 368 // GA102+. The extraction of the IMEM sections differs between the two 369 // versions. Unfortunately, the file names are the same, and the headers 370 // don't indicate the versions. The only way to differentiate is by the Chipset. 371 let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 { 372 ( 373 app0.offset, 374 Some(FalconDmaLoadTarget { 375 src_start: 0, 376 dst_start: load_hdr.os_code_offset, 377 len: load_hdr.os_code_size, 378 }), 379 ) 380 } else { 381 (0, None) 382 }; 383 384 Ok(Self { 385 imem_sec_load_target: FalconDmaLoadTarget { 386 src_start: app0.offset, 387 dst_start: imem_sec_dst_start, 388 len: app0.len, 389 }, 390 imem_ns_load_target, 391 dmem_load_target: FalconDmaLoadTarget { 392 src_start: load_hdr.os_data_offset, 393 dst_start: 0, 394 len: load_hdr.os_data_size, 395 }, 396 brom_params, 397 ucode: ucode_signed, 398 }) 399 } 400 401 /// Load and run the booter firmware on SEC2. 402 /// 403 /// Resets SEC2, loads this firmware image, then boots with the WPR metadata 404 /// address passed via the SEC2 mailboxes. 405 pub(crate) fn run<T>( 406 &self, 407 dev: &device::Device<device::Bound>, 408 bar: &Bar0, 409 sec2_falcon: &Falcon<Sec2>, 410 wpr_meta: &Coherent<T>, 411 ) -> Result { 412 sec2_falcon.reset(bar)?; 413 sec2_falcon.load(dev, bar, self)?; 414 let wpr_handle = wpr_meta.dma_handle(); 415 let (mbox0, mbox1) = sec2_falcon.boot( 416 bar, 417 Some(wpr_handle as u32), 418 Some((wpr_handle >> 32) as u32), 419 )?; 420 dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); 421 422 if mbox0 != 0 { 423 dev_err!(dev, "Booter-load failed with error {:#x}\n", mbox0); 424 return Err(ENODEV); 425 } 426 427 Ok(()) 428 } 429 } 430 431 impl FalconDmaLoadable for BooterFirmware { 432 fn as_slice(&self) -> &[u8] { 433 self.ucode.0.as_slice() 434 } 435 436 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { 437 self.imem_sec_load_target.clone() 438 } 439 440 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { 441 self.imem_ns_load_target.clone() 442 } 443 444 fn dmem_load_params(&self) -> FalconDmaLoadTarget { 445 self.dmem_load_target.clone() 446 } 447 } 448 449 impl FalconFirmware for BooterFirmware { 450 type Target = Sec2; 451 452 fn brom_params(&self) -> FalconBromParams { 453 self.brom_params.clone() 454 } 455 456 fn boot_addr(&self) -> u32 { 457 if let Some(ns_target) = &self.imem_ns_load_target { 458 ns_target.dst_start 459 } else { 460 self.imem_sec_load_target.src_start 461 } 462 } 463 } 464