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