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