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