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