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