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