1 // SPDX-License-Identifier: GPL-2.0 2 3 //! FWSEC is a High Secure firmware that is extracted from the BIOS and performs the first step of 4 //! the GSP startup by creating the WPR2 memory region and copying critical areas of the VBIOS into 5 //! it after authenticating them, ensuring they haven't been tampered with. It runs on the GSP 6 //! falcon. 7 //! 8 //! Before being run, it needs to be patched in two areas: 9 //! 10 //! - The command to be run, as this firmware can perform several tasks ; 11 //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode. 12 13 pub(crate) mod bootloader; 14 15 use core::marker::PhantomData; 16 17 use kernel::{ 18 device::{ 19 self, 20 Device, // 21 }, 22 prelude::*, 23 transmute::{ 24 AsBytes, 25 FromBytes, // 26 }, 27 }; 28 29 use crate::{ 30 falcon::{ 31 gsp::Gsp, 32 Falcon, 33 FalconBromParams, 34 FalconDmaLoadTarget, 35 FalconDmaLoadable, 36 FalconFirmware, // 37 }, 38 firmware::{ 39 FalconUCodeDesc, 40 FirmwareObject, 41 FirmwareSignature, 42 Signed, 43 Unsigned, // 44 }, 45 num::FromSafeCast, 46 vbios::Vbios, 47 }; 48 49 const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4; 50 51 #[repr(C)] 52 #[derive(Debug)] 53 struct FalconAppifHdrV1 { 54 version: u8, 55 header_size: u8, 56 entry_size: u8, 57 entry_count: u8, 58 } 59 // SAFETY: Any byte sequence is valid for this struct. 60 unsafe impl FromBytes for FalconAppifHdrV1 {} 61 62 #[repr(C, packed)] 63 #[derive(Debug)] 64 struct FalconAppifV1 { 65 id: u32, 66 dmem_base: u32, 67 } 68 // SAFETY: Any byte sequence is valid for this struct. 69 unsafe impl FromBytes for FalconAppifV1 {} 70 71 #[derive(Debug)] 72 #[repr(C, packed)] 73 struct FalconAppifDmemmapperV3 { 74 signature: u32, 75 version: u16, 76 size: u16, 77 cmd_in_buffer_offset: u32, 78 cmd_in_buffer_size: u32, 79 cmd_out_buffer_offset: u32, 80 cmd_out_buffer_size: u32, 81 nvf_img_data_buffer_offset: u32, 82 nvf_img_data_buffer_size: u32, 83 printf_buffer_hdr: u32, 84 ucode_build_time_stamp: u32, 85 ucode_signature: u32, 86 init_cmd: u32, 87 ucode_feature: u32, 88 ucode_cmd_mask0: u32, 89 ucode_cmd_mask1: u32, 90 multi_tgt_tbl: u32, 91 } 92 // SAFETY: Any byte sequence is valid for this struct. 93 unsafe impl FromBytes for FalconAppifDmemmapperV3 {} 94 // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 95 unsafe impl AsBytes for FalconAppifDmemmapperV3 {} 96 97 #[derive(Debug)] 98 #[repr(C, packed)] 99 struct ReadVbios { 100 ver: u32, 101 hdr: u32, 102 addr: u64, 103 size: u32, 104 flags: u32, 105 } 106 // SAFETY: Any byte sequence is valid for this struct. 107 unsafe impl FromBytes for ReadVbios {} 108 // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 109 unsafe impl AsBytes for ReadVbios {} 110 111 #[derive(Debug)] 112 #[repr(C, packed)] 113 struct FrtsRegion { 114 ver: u32, 115 hdr: u32, 116 addr: u32, 117 size: u32, 118 ftype: u32, 119 } 120 // SAFETY: Any byte sequence is valid for this struct. 121 unsafe impl FromBytes for FrtsRegion {} 122 // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 123 unsafe impl AsBytes for FrtsRegion {} 124 125 const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2; 126 127 #[repr(C, packed)] 128 struct FrtsCmd { 129 read_vbios: ReadVbios, 130 frts_region: FrtsRegion, 131 } 132 // SAFETY: Any byte sequence is valid for this struct. 133 unsafe impl FromBytes for FrtsCmd {} 134 // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 135 unsafe impl AsBytes for FrtsCmd {} 136 137 const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15; 138 const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19; 139 140 /// Command for the [`FwsecFirmware`] to execute. 141 pub(crate) enum FwsecCommand { 142 /// Asks [`FwsecFirmware`] to carve out the WPR2 area and place a verified copy of the VBIOS 143 /// image into it. 144 Frts { frts_addr: u64, frts_size: u64 }, 145 /// Asks [`FwsecFirmware`] to load pre-OS apps on the PMU. 146 Sb, 147 } 148 149 /// Size of the signatures used in FWSEC. 150 const BCRT30_RSA3K_SIG_SIZE: usize = 384; 151 152 /// A single signature that can be patched into a FWSEC image. 153 #[repr(transparent)] 154 pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]); 155 156 /// SAFETY: A signature is just an array of bytes. 157 unsafe impl FromBytes for Bcrt30Rsa3kSignature {} 158 159 impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature { 160 fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self { 161 Self(sig) 162 } 163 } 164 165 impl AsRef<[u8]> for Bcrt30Rsa3kSignature { 166 fn as_ref(&self) -> &[u8] { 167 &self.0 168 } 169 } 170 171 impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {} 172 173 /// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon. 174 /// 175 /// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow. 176 pub(crate) struct FwsecFirmware { 177 /// Descriptor of the firmware. 178 desc: FalconUCodeDesc, 179 /// Object containing the firmware binary. 180 ucode: FirmwareObject<Self, Signed>, 181 } 182 183 impl FalconDmaLoadable for FwsecFirmware { 184 fn as_slice(&self) -> &[u8] { 185 self.ucode.0.as_slice() 186 } 187 188 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { 189 self.desc.imem_sec_load_params() 190 } 191 192 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { 193 self.desc.imem_ns_load_params() 194 } 195 196 fn dmem_load_params(&self) -> FalconDmaLoadTarget { 197 self.desc.dmem_load_params() 198 } 199 } 200 201 impl FalconFirmware for FwsecFirmware { 202 type Target = Gsp; 203 204 fn brom_params(&self) -> FalconBromParams { 205 FalconBromParams { 206 pkc_data_offset: self.desc.pkc_data_offset(), 207 engine_id_mask: self.desc.engine_id_mask(), 208 ucode_id: self.desc.ucode_id(), 209 } 210 } 211 212 fn boot_addr(&self) -> u32 { 213 0 214 } 215 } 216 217 impl FirmwareObject<FwsecFirmware, Unsigned> { 218 fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> { 219 let desc = bios.fwsec_image().header()?; 220 let mut ucode = KVVec::new(); 221 ucode.extend_from_slice(bios.fwsec_image().ucode(&desc)?, GFP_KERNEL)?; 222 223 let hdr_offset = desc 224 .imem_load_size() 225 .checked_add(desc.interface_offset()) 226 .map(usize::from_safe_cast) 227 .ok_or(EINVAL)?; 228 229 let hdr = ucode 230 .get(hdr_offset..) 231 .and_then(FalconAppifHdrV1::from_bytes_prefix) 232 .ok_or(EINVAL)? 233 .0; 234 235 if hdr.version != 1 { 236 return Err(EINVAL); 237 } 238 239 // Find the DMEM mapper section in the firmware. 240 for i in 0..usize::from(hdr.entry_count) { 241 // CALC: hdr_offset + header_size + i * entry_size. 242 let entry_offset = hdr_offset 243 .checked_add(usize::from(hdr.header_size)) 244 .and_then(|o| o.checked_add(i.checked_mul(usize::from(hdr.entry_size))?)) 245 .ok_or(EINVAL)?; 246 247 let app = ucode 248 .get(entry_offset..) 249 .and_then(FalconAppifV1::from_bytes_prefix) 250 .ok_or(EINVAL)? 251 .0; 252 253 if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { 254 continue; 255 } 256 let dmem_base = app.dmem_base; 257 258 let dmem_mapper_offset = desc 259 .imem_load_size() 260 .checked_add(dmem_base) 261 .map(usize::from_safe_cast) 262 .ok_or(EINVAL)?; 263 264 let dmem_mapper = ucode 265 .get_mut(dmem_mapper_offset..) 266 .and_then(FalconAppifDmemmapperV3::from_bytes_mut_prefix) 267 .ok_or(EINVAL)? 268 .0; 269 270 dmem_mapper.init_cmd = match cmd { 271 FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS, 272 FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB, 273 }; 274 let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset; 275 276 let frts_cmd_offset = desc 277 .imem_load_size() 278 .checked_add(cmd_in_buffer_offset) 279 .map(usize::from_safe_cast) 280 .ok_or(EINVAL)?; 281 282 let frts_cmd = ucode 283 .get_mut(frts_cmd_offset..) 284 .and_then(FrtsCmd::from_bytes_mut_prefix) 285 .ok_or(EINVAL)? 286 .0; 287 288 frts_cmd.read_vbios = ReadVbios { 289 ver: 1, 290 hdr: u32::try_from(size_of::<ReadVbios>())?, 291 addr: 0, 292 size: 0, 293 flags: 2, 294 }; 295 if let FwsecCommand::Frts { 296 frts_addr, 297 frts_size, 298 } = cmd 299 { 300 frts_cmd.frts_region = FrtsRegion { 301 ver: 1, 302 hdr: u32::try_from(size_of::<FrtsRegion>())?, 303 addr: u32::try_from(frts_addr >> 12)?, 304 size: u32::try_from(frts_size >> 12)?, 305 ftype: NVFW_FRTS_CMD_REGION_TYPE_FB, 306 }; 307 } 308 309 // Return early as we found and patched the DMEMMAPPER region. 310 return Ok(Self(ucode, PhantomData)); 311 } 312 313 Err(ENOTSUPP) 314 } 315 } 316 317 impl FwsecFirmware { 318 /// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd` 319 /// command. 320 pub(crate) fn new( 321 dev: &Device<device::Bound>, 322 falcon: &Falcon<'_, Gsp>, 323 bios: &Vbios, 324 cmd: FwsecCommand, 325 ) -> Result<Self> { 326 let ucode_dma = FirmwareObject::<Self, _>::new_fwsec(bios, cmd)?; 327 328 // Patch signature if needed. 329 let desc = bios.fwsec_image().header()?; 330 let ucode_signed = if desc.signature_count() != 0 { 331 let sig_base_img = desc 332 .imem_load_size() 333 .checked_add(desc.pkc_data_offset()) 334 .map(usize::from_safe_cast) 335 .ok_or(EINVAL)?; 336 let desc_sig_versions = u32::from(desc.signature_versions()); 337 let reg_fuse_version = 338 falcon.signature_reg_fuse_version(desc.engine_id_mask(), desc.ucode_id())?; 339 dev_dbg!( 340 dev, 341 "desc_sig_versions: {:#x}, reg_fuse_version: {}\n", 342 desc_sig_versions, 343 reg_fuse_version 344 ); 345 let signature_idx = { 346 let reg_fuse_version_bit = 1 << reg_fuse_version; 347 348 // Check if the fuse version is supported by the firmware. 349 if desc_sig_versions & reg_fuse_version_bit == 0 { 350 dev_err!( 351 dev, 352 "no matching signature: {:#x} {:#x}\n", 353 reg_fuse_version_bit, 354 desc_sig_versions, 355 ); 356 return Err(EINVAL); 357 } 358 359 // `desc_sig_versions` has one bit set per included signature. Thus, the index of 360 // the signature to patch is the number of bits in `desc_sig_versions` set to `1` 361 // before `reg_fuse_version_bit`. 362 363 // Mask of the bits of `desc_sig_versions` to preserve. 364 let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1); 365 366 usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones()) 367 }; 368 369 dev_dbg!(dev, "patching signature with index {}\n", signature_idx); 370 let signature = bios 371 .fwsec_image() 372 .sigs(&desc) 373 .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?; 374 375 ucode_dma.patch_signature(signature, sig_base_img)? 376 } else { 377 ucode_dma.no_patch_signature() 378 }; 379 380 Ok(FwsecFirmware { 381 desc, 382 ucode: ucode_signed, 383 }) 384 } 385 386 /// Loads the FWSEC firmware into `falcon` and execute it. 387 /// 388 /// This must only be called on chipsets that do not need the FWSEC bootloader. On chipsets 389 /// where the bootloader is required, use [`bootloader::FwsecFirmwareWithBl`] instead. 390 pub(crate) fn run(&self, dev: &Device<device::Bound>, falcon: &Falcon<'_, Gsp>) -> Result<()> { 391 // Reset falcon, load the firmware, and run it. 392 falcon 393 .reset() 394 .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; 395 falcon 396 .load(self) 397 .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; 398 let (mbox0, _) = falcon 399 .boot(Some(0), None) 400 .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?; 401 if mbox0 != 0 { 402 dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0); 403 Err(EIO) 404 } else { 405 Ok(()) 406 } 407 } 408 } 409