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