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