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