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