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