1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Falcon microprocessor base support 4 5 use hal::FalconHal; 6 7 use kernel::{ 8 device::{ 9 self, 10 Device, // 11 }, 12 dma::{ 13 Coherent, 14 CoherentBox, 15 DmaAddress, 16 DmaMask, // 17 }, 18 io::{ 19 poll::read_poll_timeout, 20 register::{ 21 RegisterBase, 22 WithBase, // 23 }, 24 Io, 25 }, 26 prelude::*, 27 sync::aref::ARef, 28 time::Delta, 29 }; 30 31 use crate::{ 32 bounded_enum, 33 driver::Bar0, 34 falcon::hal::LoadMethod, 35 gpu::Chipset, 36 num::{ 37 self, 38 FromSafeCast, // 39 }, 40 regs, 41 }; 42 43 pub(crate) mod fsp; 44 pub(crate) mod gsp; 45 mod hal; 46 pub(crate) mod sec2; 47 48 /// Alignment (in bytes) of falcon memory blocks. 49 pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256; 50 51 bounded_enum! { 52 /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] 53 /// register. 54 #[derive(Debug, Copy, Clone)] 55 pub(crate) enum FalconCoreRev with TryFrom<Bounded<u32, 4>> { 56 Rev1 = 1, 57 Rev2 = 2, 58 Rev3 = 3, 59 Rev4 = 4, 60 Rev5 = 5, 61 Rev6 = 6, 62 Rev7 = 7, 63 } 64 } 65 66 bounded_enum! { 67 /// Revision subversion number of a falcon core, used in the 68 /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register. 69 #[derive(Debug, Copy, Clone)] 70 pub(crate) enum FalconCoreRevSubversion with From<Bounded<u32, 2>> { 71 Subversion0 = 0, 72 Subversion1 = 1, 73 Subversion2 = 2, 74 Subversion3 = 3, 75 } 76 } 77 78 bounded_enum! { 79 /// Security mode of the Falcon microprocessor. 80 /// 81 /// See `falcon.rst` for more details. 82 #[derive(Debug, Copy, Clone)] 83 pub(crate) enum FalconSecurityModel with TryFrom<Bounded<u32, 2>> { 84 /// Non-Secure: runs unsigned code without privileges. 85 None = 0, 86 /// Light-Secured (LS): Runs signed code with some privileges. 87 /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the 88 /// code's signature. 89 /// 90 /// Also known as Low-Secure, Privilege Level 2 or PL2. 91 Light = 2, 92 /// Heavy-Secured (HS): Runs signed code with full privileges. 93 /// The code's signature is verified by the Falcon Boot ROM (BROM). 94 /// 95 /// Also known as High-Secure, Privilege Level 3 or PL3. 96 Heavy = 3, 97 } 98 } 99 100 bounded_enum! { 101 /// Signing algorithm for a given firmware, used in the 102 /// [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`] register. It is passed to the Falcon Boot ROM 103 /// (BROM) as a parameter. 104 #[derive(Debug, Copy, Clone)] 105 pub(crate) enum FalconModSelAlgo with TryFrom<Bounded<u32, 8>> { 106 /// AES. 107 Aes = 0, 108 /// RSA3K. 109 Rsa3k = 1, 110 } 111 } 112 113 bounded_enum! { 114 /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] 115 /// register. 116 #[derive(Debug, Copy, Clone)] 117 pub(crate) enum DmaTrfCmdSize with TryFrom<Bounded<u32, 3>> { 118 /// 256 bytes transfer. 119 Size256B = 0x6, 120 } 121 } 122 123 bounded_enum! { 124 /// Currently active core on a dual falcon/riscv (Peregrine) controller. 125 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 126 pub(crate) enum PeregrineCoreSelect with From<Bounded<u32, 1>> { 127 /// Falcon core is active. 128 Falcon = 0, 129 /// RISC-V core is active. 130 Riscv = 1, 131 } 132 } 133 134 /// Different types of memory present in a falcon core. 135 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 136 pub(crate) enum FalconMem { 137 /// Secure Instruction Memory. 138 ImemSecure, 139 /// Non-Secure Instruction Memory. 140 #[expect(unused)] 141 ImemNonSecure, 142 /// Data Memory. 143 Dmem, 144 } 145 146 bounded_enum! { 147 /// Defines the Framebuffer Interface (FBIF) aperture type. 148 /// This determines the memory type for external memory access during a DMA transfer, which is 149 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details. 150 #[derive(Debug, Copy, Clone)] 151 pub(crate) enum FalconFbifTarget with TryFrom<Bounded<u32, 2>> { 152 /// Local Framebuffer (GPU's VRAM memory). 153 LocalFb = 0, 154 /// Coherent system memory (System DRAM). 155 CoherentSysmem = 1, 156 /// Non-coherent system memory (System DRAM). 157 NoncoherentSysmem = 2, 158 } 159 } 160 161 bounded_enum! { 162 /// Type of memory addresses to use. 163 #[derive(Debug, Copy, Clone)] 164 pub(crate) enum FalconFbifMemType with From<Bounded<u32, 1>> { 165 /// Virtual memory addresses. 166 Virtual = 0, 167 /// Physical memory addresses. 168 Physical = 1, 169 } 170 } 171 172 /// Type used to represent the `PFALCON` registers address base for a given falcon engine. 173 pub(crate) struct PFalconBase(()); 174 175 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine. 176 pub(crate) struct PFalcon2Base(()); 177 178 /// Trait defining the parameters of a given Falcon engine. 179 /// 180 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. 181 pub(crate) trait FalconEngine: 182 Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized 183 { 184 } 185 186 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM) 187 /// using DMA. 188 #[derive(Debug, Clone)] 189 pub(crate) struct FalconDmaLoadTarget { 190 /// Offset from the start of the source object to copy from. 191 pub(crate) src_start: u32, 192 /// Offset from the start of the destination memory to copy into. 193 pub(crate) dst_start: u32, 194 /// Number of bytes to copy. 195 pub(crate) len: u32, 196 } 197 198 /// Parameters for the falcon boot ROM. 199 #[derive(Debug, Clone)] 200 pub(crate) struct FalconBromParams { 201 /// Offset in `DMEM`` of the firmware's signature. 202 pub(crate) pkc_data_offset: u32, 203 /// Mask of engines valid for this firmware. 204 pub(crate) engine_id_mask: u16, 205 /// ID of the ucode used to infer a fuse register to validate the signature. 206 pub(crate) ucode_id: u8, 207 } 208 209 /// Trait implemented by falcon firmwares that can be loaded using DMA. 210 pub(crate) trait FalconDmaLoadable { 211 /// Returns the firmware data as a slice of bytes. 212 fn as_slice(&self) -> &[u8]; 213 214 /// Returns the load parameters for Secure `IMEM`. 215 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget; 216 217 /// Returns the load parameters for Non-Secure `IMEM`, 218 /// used only on Turing and GA100. 219 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>; 220 221 /// Returns the load parameters for `DMEM`. 222 fn dmem_load_params(&self) -> FalconDmaLoadTarget; 223 224 /// Returns an adapter that provides the required parameter to load this firmware using PIO. 225 /// 226 /// This can only fail if some `u32` fields cannot be converted to `u16`, or if the indices in 227 /// the headers are invalid. 228 fn try_as_pio_loadable(&self) -> Result<FalconDmaFirmwarePioAdapter<'_, Self>> { 229 let new_pio_imem = |params: FalconDmaLoadTarget, secure| { 230 let start = usize::from_safe_cast(params.src_start); 231 let end = start + usize::from_safe_cast(params.len); 232 let data = self.as_slice().get(start..end).ok_or(EINVAL)?; 233 234 let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?; 235 236 Ok::<_, Error>(FalconPioImemLoadTarget { 237 data, 238 dst_start, 239 secure, 240 start_tag: dst_start >> 8, 241 }) 242 }; 243 244 let imem_sec = new_pio_imem(self.imem_sec_load_params(), true)?; 245 246 let imem_ns = if let Some(params) = self.imem_ns_load_params() { 247 Some(new_pio_imem(params, false)?) 248 } else { 249 None 250 }; 251 252 let dmem = { 253 let params = self.dmem_load_params(); 254 let start = usize::from_safe_cast(params.src_start); 255 let end = start + usize::from_safe_cast(params.len); 256 let data = self.as_slice().get(start..end).ok_or(EINVAL)?; 257 258 let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?; 259 260 FalconPioDmemLoadTarget { data, dst_start } 261 }; 262 263 Ok(FalconDmaFirmwarePioAdapter { 264 fw: self, 265 imem_sec, 266 imem_ns, 267 dmem, 268 }) 269 } 270 } 271 272 /// Represents a portion of the firmware to be loaded into IMEM using PIO. 273 #[derive(Clone)] 274 pub(crate) struct FalconPioImemLoadTarget<'a> { 275 pub(crate) data: &'a [u8], 276 pub(crate) dst_start: u16, 277 pub(crate) secure: bool, 278 pub(crate) start_tag: u16, 279 } 280 281 /// Represents a portion of the firmware to be loaded into DMEM using PIO. 282 #[derive(Clone)] 283 pub(crate) struct FalconPioDmemLoadTarget<'a> { 284 pub(crate) data: &'a [u8], 285 pub(crate) dst_start: u16, 286 } 287 288 /// Trait for providing PIO load parameters of falcon firmwares. 289 pub(crate) trait FalconPioLoadable { 290 /// Returns the load parameters for Secure `IMEM`, if any. 291 fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>; 292 293 /// Returns the load parameters for Non-Secure `IMEM`, if any. 294 fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>; 295 296 /// Returns the load parameters for `DMEM`. 297 fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_>; 298 } 299 300 /// Adapter type that makes any DMA-loadable firmware also loadable via PIO. 301 /// 302 /// Created using [`FalconDmaLoadable::try_as_pio_loadable`]. 303 pub(crate) struct FalconDmaFirmwarePioAdapter<'a, T: FalconDmaLoadable + ?Sized> { 304 /// Reference to the DMA firmware. 305 fw: &'a T, 306 /// Validated secure IMEM parameters. 307 imem_sec: FalconPioImemLoadTarget<'a>, 308 /// Validated non-secure IMEM parameters. 309 imem_ns: Option<FalconPioImemLoadTarget<'a>>, 310 /// Validated DMEM parameters. 311 dmem: FalconPioDmemLoadTarget<'a>, 312 } 313 314 impl<'a, T> FalconPioLoadable for FalconDmaFirmwarePioAdapter<'a, T> 315 where 316 T: FalconDmaLoadable + ?Sized, 317 { 318 fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 319 Some(self.imem_sec.clone()) 320 } 321 322 fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 323 self.imem_ns.clone() 324 } 325 326 fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> { 327 self.dmem.clone() 328 } 329 } 330 331 impl<'a, T> FalconFirmware for FalconDmaFirmwarePioAdapter<'a, T> 332 where 333 T: FalconDmaLoadable + FalconFirmware + ?Sized, 334 { 335 type Target = <T as FalconFirmware>::Target; 336 337 fn brom_params(&self) -> FalconBromParams { 338 self.fw.brom_params() 339 } 340 341 fn boot_addr(&self) -> u32 { 342 self.fw.boot_addr() 343 } 344 } 345 346 /// Trait for a falcon firmware. 347 /// 348 /// A falcon firmware can be loaded on a given engine. 349 pub(crate) trait FalconFirmware { 350 /// Engine on which this firmware is to be loaded. 351 type Target: FalconEngine; 352 353 /// Returns the parameters to write into the BROM registers. 354 fn brom_params(&self) -> FalconBromParams; 355 356 /// Returns the start address of the firmware. 357 fn boot_addr(&self) -> u32; 358 } 359 360 /// Contains the base parameters common to all Falcon instances. 361 pub(crate) struct Falcon<E: FalconEngine> { 362 hal: KBox<dyn FalconHal<E>>, 363 dev: ARef<device::Device>, 364 } 365 366 impl<E: FalconEngine + 'static> Falcon<E> { 367 /// Create a new falcon instance. 368 pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> { 369 Ok(Self { 370 hal: hal::falcon_hal(chipset)?, 371 dev: dev.into(), 372 }) 373 } 374 375 /// Resets DMA-related registers. 376 pub(crate) fn dma_reset(&self, bar: &Bar0) { 377 bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| { 378 v.with_allow_phys_no_ctx(true) 379 }); 380 381 bar.write( 382 WithBase::of::<E>(), 383 regs::NV_PFALCON_FALCON_DMACTL::zeroed(), 384 ); 385 } 386 387 /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. 388 pub(crate) fn reset(&self, bar: &Bar0) -> Result { 389 self.hal.reset_eng(bar)?; 390 self.hal.select_core(self, bar)?; 391 self.hal.reset_wait_mem_scrubbing(bar)?; 392 393 bar.write( 394 WithBase::of::<E>(), 395 regs::NV_PFALCON_FALCON_RM::from(bar.read(regs::NV_PMC_BOOT_0).into_raw()), 396 ); 397 398 Ok(()) 399 } 400 401 /// Falcons supports up to four ports, but we only ever use one, so just hard-code it. 402 const PIO_PORT: usize = 0; 403 404 /// Write a slice to Falcon IMEM memory using programmed I/O (PIO). 405 /// 406 /// Returns `EINVAL` if `img.len()` is not a multiple of 4. 407 fn pio_wr_imem_slice(&self, bar: &Bar0, load_offsets: FalconPioImemLoadTarget<'_>) -> Result { 408 // Rejecting misaligned images here allows us to avoid checking 409 // inside the loops. 410 if load_offsets.data.len() % 4 != 0 { 411 return Err(EINVAL); 412 } 413 414 bar.write( 415 WithBase::of::<E>().at(Self::PIO_PORT), 416 regs::NV_PFALCON_FALCON_IMEMC::zeroed() 417 .with_secure(load_offsets.secure) 418 .with_aincw(true) 419 .with_offs(load_offsets.dst_start), 420 ); 421 422 for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() { 423 let n = u16::try_from(n)?; 424 let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?; 425 bar.write( 426 WithBase::of::<E>().at(Self::PIO_PORT), 427 regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag), 428 ); 429 for word in block.chunks_exact(4) { 430 let w = [word[0], word[1], word[2], word[3]]; 431 bar.write( 432 WithBase::of::<E>().at(Self::PIO_PORT), 433 regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)), 434 ); 435 } 436 } 437 438 Ok(()) 439 } 440 441 /// Write a slice to Falcon DMEM memory using programmed I/O (PIO). 442 /// 443 /// Returns `EINVAL` if `img.len()` is not a multiple of 4. 444 fn pio_wr_dmem_slice(&self, bar: &Bar0, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result { 445 // Rejecting misaligned images here allows us to avoid checking 446 // inside the loops. 447 if load_offsets.data.len() % 4 != 0 { 448 return Err(EINVAL); 449 } 450 451 bar.write( 452 WithBase::of::<E>().at(Self::PIO_PORT), 453 regs::NV_PFALCON_FALCON_DMEMC::zeroed() 454 .with_aincw(true) 455 .with_offs(load_offsets.dst_start), 456 ); 457 458 for word in load_offsets.data.chunks_exact(4) { 459 let w = [word[0], word[1], word[2], word[3]]; 460 bar.write( 461 WithBase::of::<E>().at(Self::PIO_PORT), 462 regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)), 463 ); 464 } 465 466 Ok(()) 467 } 468 469 /// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 470 pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>( 471 &self, 472 bar: &Bar0, 473 fw: &F, 474 ) -> Result { 475 bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| { 476 v.with_allow_phys_no_ctx(true) 477 }); 478 479 bar.write( 480 WithBase::of::<E>(), 481 regs::NV_PFALCON_FALCON_DMACTL::zeroed(), 482 ); 483 484 if let Some(imem_ns) = fw.imem_ns_load_params() { 485 self.pio_wr_imem_slice(bar, imem_ns)?; 486 } 487 if let Some(imem_sec) = fw.imem_sec_load_params() { 488 self.pio_wr_imem_slice(bar, imem_sec)?; 489 } 490 self.pio_wr_dmem_slice(bar, fw.dmem_load_params())?; 491 492 self.hal.program_brom(self, bar, &fw.brom_params()); 493 494 bar.write( 495 WithBase::of::<E>(), 496 regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), 497 ); 498 499 Ok(()) 500 } 501 502 /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's 503 /// `target_mem`. 504 /// 505 /// `sec` is set if the loaded firmware is expected to run in secure mode. 506 fn dma_wr( 507 &self, 508 bar: &Bar0, 509 dma_obj: &Coherent<[u8]>, 510 target_mem: FalconMem, 511 load_offsets: FalconDmaLoadTarget, 512 ) -> Result { 513 const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>(); 514 515 // For IMEM, we want to use the start offset as a virtual address tag for each page, since 516 // code addresses in the firmware (and the boot vector) are virtual. 517 // 518 // For DMEM we can fold the start offset into the DMA handle. 519 let (src_start, dma_start) = match target_mem { 520 FalconMem::ImemSecure | FalconMem::ImemNonSecure => { 521 (load_offsets.src_start, dma_obj.dma_handle()) 522 } 523 FalconMem::Dmem => ( 524 0, 525 dma_obj.dma_handle() + DmaAddress::from(load_offsets.src_start), 526 ), 527 }; 528 if dma_start % DmaAddress::from(DMA_LEN) > 0 { 529 dev_err!( 530 self.dev, 531 "DMA transfer start addresses must be a multiple of {}\n", 532 DMA_LEN 533 ); 534 return Err(EINVAL); 535 } 536 537 // The DMATRFBASE/1 register pair only supports a 49-bit address. 538 if dma_start > DmaMask::new::<49>().value() { 539 dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start); 540 return Err(ERANGE); 541 } 542 543 // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we 544 // need to perform. 545 let num_transfers = load_offsets.len.div_ceil(DMA_LEN); 546 547 // Check that the area we are about to transfer is within the bounds of the DMA object. 548 // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`. 549 match num_transfers 550 .checked_mul(DMA_LEN) 551 .and_then(|size| size.checked_add(load_offsets.src_start)) 552 { 553 None => { 554 dev_err!(self.dev, "DMA transfer length overflow\n"); 555 return Err(EOVERFLOW); 556 } 557 Some(upper_bound) if usize::from_safe_cast(upper_bound) > dma_obj.size() => { 558 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); 559 return Err(EINVAL); 560 } 561 Some(_) => (), 562 }; 563 564 // Set up the base source DMA address. 565 566 bar.write( 567 WithBase::of::<E>(), 568 regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base( 569 // CAST: `as u32` is used on purpose since we do want to strip the upper bits, 570 // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. 571 (dma_start >> 8) as u32, 572 ), 573 ); 574 bar.write( 575 WithBase::of::<E>(), 576 regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_start >> 40)?, 577 ); 578 579 let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::zeroed() 580 .with_size(DmaTrfCmdSize::Size256B) 581 .with_falcon_mem(target_mem); 582 583 for pos in (0..num_transfers).map(|i| i * DMA_LEN) { 584 // Perform a transfer of size `DMA_LEN`. 585 bar.write( 586 WithBase::of::<E>(), 587 regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed() 588 .try_with_offs(load_offsets.dst_start + pos)?, 589 ); 590 bar.write( 591 WithBase::of::<E>(), 592 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos), 593 ); 594 595 bar.write(WithBase::of::<E>(), cmd); 596 597 // Wait for the transfer to complete. 598 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 599 // should ever take that long. 600 read_poll_timeout( 601 || Ok(bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())), 602 |r| r.idle(), 603 Delta::ZERO, 604 Delta::from_secs(2), 605 )?; 606 } 607 608 Ok(()) 609 } 610 611 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 612 fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>( 613 &self, 614 dev: &Device<device::Bound>, 615 bar: &Bar0, 616 fw: &F, 617 ) -> Result { 618 // DMA object with firmware content as the source of the DMA engine. 619 let dma_obj = { 620 let fw_slice = fw.as_slice(); 621 622 // DMA copies are done in chunks of `MEM_BLOCK_ALIGNMENT`, so pad the length 623 // accordingly and fill with `0`. 624 let mut dma_obj = CoherentBox::zeroed_slice( 625 dev, 626 fw_slice.len().next_multiple_of(MEM_BLOCK_ALIGNMENT), 627 GFP_KERNEL, 628 )?; 629 630 // PANIC: `dma_obj` has been created with a length equal to or larger than 631 // `fw_slice.len()`, so the range `..fw_slice.len()` is valid. 632 dma_obj[..fw_slice.len()].copy_from_slice(fw_slice); 633 634 dma_obj.into() 635 }; 636 637 self.dma_reset(bar); 638 bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| { 639 v.with_target(FalconFbifTarget::CoherentSysmem) 640 .with_mem_type(FalconFbifMemType::Physical) 641 }); 642 643 self.dma_wr( 644 bar, 645 &dma_obj, 646 FalconMem::ImemSecure, 647 fw.imem_sec_load_params(), 648 )?; 649 self.dma_wr(bar, &dma_obj, FalconMem::Dmem, fw.dmem_load_params())?; 650 651 self.hal.program_brom(self, bar, &fw.brom_params()); 652 653 // Set `BootVec` to start of non-secure code. 654 bar.write( 655 WithBase::of::<E>(), 656 regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), 657 ); 658 659 Ok(()) 660 } 661 662 /// Wait until the falcon CPU is halted. 663 pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> { 664 // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 665 read_poll_timeout( 666 || Ok(bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())), 667 |r| r.halted(), 668 Delta::ZERO, 669 Delta::from_secs(2), 670 )?; 671 672 Ok(()) 673 } 674 675 /// Start the falcon CPU. 676 pub(crate) fn start(&self, bar: &Bar0) -> Result<()> { 677 match bar 678 .read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>()) 679 .alias_en() 680 { 681 true => bar.write( 682 WithBase::of::<E>(), 683 regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true), 684 ), 685 false => bar.write( 686 WithBase::of::<E>(), 687 regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true), 688 ), 689 } 690 691 Ok(()) 692 } 693 694 /// Writes values to the mailbox registers if provided. 695 pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) { 696 if let Some(mbox0) = mbox0 { 697 bar.write( 698 WithBase::of::<E>(), 699 regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0), 700 ); 701 } 702 703 if let Some(mbox1) = mbox1 { 704 bar.write( 705 WithBase::of::<E>(), 706 regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1), 707 ); 708 } 709 } 710 711 /// Reads the value from `mbox0` register. 712 pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 { 713 bar.read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>()) 714 .value() 715 } 716 717 /// Reads the value from `mbox1` register. 718 pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 { 719 bar.read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>()) 720 .value() 721 } 722 723 /// Reads values from both mailbox registers. 724 pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) { 725 let mbox0 = self.read_mailbox0(bar); 726 let mbox1 = self.read_mailbox1(bar); 727 728 (mbox0, mbox1) 729 } 730 731 /// Start running the loaded firmware. 732 /// 733 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 734 /// prior to running. 735 /// 736 /// Wait up to two seconds for the firmware to complete, and return its exit status read from 737 /// the `MBOX0` and `MBOX1` registers. 738 pub(crate) fn boot( 739 &self, 740 bar: &Bar0, 741 mbox0: Option<u32>, 742 mbox1: Option<u32>, 743 ) -> Result<(u32, u32)> { 744 self.write_mailboxes(bar, mbox0, mbox1); 745 self.start(bar)?; 746 self.wait_till_halted(bar)?; 747 Ok(self.read_mailboxes(bar)) 748 } 749 750 /// Returns the fused version of the signature to use in order to run a HS firmware on this 751 /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header. 752 pub(crate) fn signature_reg_fuse_version( 753 &self, 754 bar: &Bar0, 755 engine_id_mask: u16, 756 ucode_id: u8, 757 ) -> Result<u32> { 758 self.hal 759 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 760 } 761 762 /// Check if the RISC-V core is active. 763 /// 764 /// Returns `true` if the RISC-V core is active, `false` otherwise. 765 pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { 766 self.hal.is_riscv_active(bar) 767 } 768 769 /// Load a firmware image into Falcon memory, using the preferred method for the current 770 /// chipset. 771 pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>( 772 &self, 773 dev: &Device<device::Bound>, 774 bar: &Bar0, 775 fw: &F, 776 ) -> Result { 777 match self.hal.load_method() { 778 LoadMethod::Dma => self.dma_load(dev, bar, fw), 779 LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?), 780 } 781 } 782 783 /// Write the application version to the OS register. 784 pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) { 785 bar.write( 786 WithBase::of::<E>(), 787 regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version), 788 ); 789 } 790 } 791