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