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_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 dmem_load_params(&self) -> FalconLoadTarget { 236 FalconLoadTarget { 237 src_start: self.desc.imem_load_size, 238 dst_start: self.desc.dmem_phys_base, 239 len: self.desc.dmem_load_size, 240 } 241 } 242 243 fn brom_params(&self) -> FalconBromParams { 244 FalconBromParams { 245 pkc_data_offset: self.desc.pkc_data_offset, 246 engine_id_mask: self.desc.engine_id_mask, 247 ucode_id: self.desc.ucode_id, 248 } 249 } 250 251 fn boot_addr(&self) -> u32 { 252 0 253 } 254 } 255 256 impl Deref for FwsecFirmware { 257 type Target = DmaObject; 258 259 fn deref(&self) -> &Self::Target { 260 &self.ucode.0 261 } 262 } 263 264 impl FalconFirmware for FwsecFirmware { 265 type Target = Gsp; 266 } 267 268 impl FirmwareDmaObject<FwsecFirmware, Unsigned> { 269 fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> { 270 let desc = bios.fwsec_image().header()?; 271 let ucode = bios.fwsec_image().ucode(desc)?; 272 let mut dma_object = DmaObject::from_data(dev, ucode)?; 273 274 let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset); 275 // SAFETY: we have exclusive access to `dma_object`. 276 let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?; 277 278 if hdr.version != 1 { 279 return Err(EINVAL); 280 } 281 282 // Find the DMEM mapper section in the firmware. 283 for i in 0..usize::from(hdr.entry_count) { 284 // SAFETY: we have exclusive access to `dma_object`. 285 let app: &FalconAppifV1 = unsafe { 286 transmute( 287 &dma_object, 288 hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size), 289 ) 290 }?; 291 292 if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { 293 continue; 294 } 295 let dmem_base = app.dmem_base; 296 297 // SAFETY: we have exclusive access to `dma_object`. 298 let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe { 299 transmute_mut( 300 &mut dma_object, 301 (desc.imem_load_size + dmem_base).into_safe_cast(), 302 ) 303 }?; 304 305 dmem_mapper.init_cmd = match cmd { 306 FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS, 307 FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB, 308 }; 309 let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset; 310 311 // SAFETY: we have exclusive access to `dma_object`. 312 let frts_cmd: &mut FrtsCmd = unsafe { 313 transmute_mut( 314 &mut dma_object, 315 (desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(), 316 ) 317 }?; 318 319 frts_cmd.read_vbios = ReadVbios { 320 ver: 1, 321 hdr: u32::try_from(size_of::<ReadVbios>())?, 322 addr: 0, 323 size: 0, 324 flags: 2, 325 }; 326 if let FwsecCommand::Frts { 327 frts_addr, 328 frts_size, 329 } = cmd 330 { 331 frts_cmd.frts_region = FrtsRegion { 332 ver: 1, 333 hdr: u32::try_from(size_of::<FrtsRegion>())?, 334 addr: u32::try_from(frts_addr >> 12)?, 335 size: u32::try_from(frts_size >> 12)?, 336 ftype: NVFW_FRTS_CMD_REGION_TYPE_FB, 337 }; 338 } 339 340 // Return early as we found and patched the DMEMMAPPER region. 341 return Ok(Self(dma_object, PhantomData)); 342 } 343 344 Err(ENOTSUPP) 345 } 346 } 347 348 impl FwsecFirmware { 349 /// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd` 350 /// command. 351 pub(crate) fn new( 352 dev: &Device<device::Bound>, 353 falcon: &Falcon<Gsp>, 354 bar: &Bar0, 355 bios: &Vbios, 356 cmd: FwsecCommand, 357 ) -> Result<Self> { 358 let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?; 359 360 // Patch signature if needed. 361 let desc = bios.fwsec_image().header()?; 362 let ucode_signed = if desc.signature_count != 0 { 363 let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset); 364 let desc_sig_versions = u32::from(desc.signature_versions); 365 let reg_fuse_version = 366 falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?; 367 dev_dbg!( 368 dev, 369 "desc_sig_versions: {:#x}, reg_fuse_version: {}\n", 370 desc_sig_versions, 371 reg_fuse_version 372 ); 373 let signature_idx = { 374 let reg_fuse_version_bit = 1 << reg_fuse_version; 375 376 // Check if the fuse version is supported by the firmware. 377 if desc_sig_versions & reg_fuse_version_bit == 0 { 378 dev_err!( 379 dev, 380 "no matching signature: {:#x} {:#x}\n", 381 reg_fuse_version_bit, 382 desc_sig_versions, 383 ); 384 return Err(EINVAL); 385 } 386 387 // `desc_sig_versions` has one bit set per included signature. Thus, the index of 388 // the signature to patch is the number of bits in `desc_sig_versions` set to `1` 389 // before `reg_fuse_version_bit`. 390 391 // Mask of the bits of `desc_sig_versions` to preserve. 392 let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1); 393 394 usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones()) 395 }; 396 397 dev_dbg!(dev, "patching signature with index {}\n", signature_idx); 398 let signature = bios 399 .fwsec_image() 400 .sigs(desc) 401 .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?; 402 403 ucode_dma.patch_signature(signature, sig_base_img)? 404 } else { 405 ucode_dma.no_patch_signature() 406 }; 407 408 Ok(FwsecFirmware { 409 desc: desc.clone(), 410 ucode: ucode_signed, 411 }) 412 } 413 414 /// Loads the FWSEC firmware into `falcon` and execute it. 415 pub(crate) fn run( 416 &self, 417 dev: &Device<device::Bound>, 418 falcon: &Falcon<Gsp>, 419 bar: &Bar0, 420 ) -> Result<()> { 421 // Reset falcon, load the firmware, and run it. 422 falcon 423 .reset(bar) 424 .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; 425 falcon 426 .dma_load(bar, self) 427 .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; 428 let (mbox0, _) = falcon 429 .boot(bar, Some(0), None) 430 .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?; 431 if mbox0 != 0 { 432 dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0); 433 Err(EIO) 434 } else { 435 Ok(()) 436 } 437 } 438 } 439