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