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( 408 &self, 409 bar: Bar0<'_>, 410 load_offsets: FalconPioImemLoadTarget<'_>, 411 ) -> Result { 412 // Rejecting misaligned images here allows us to avoid checking 413 // inside the loops. 414 if load_offsets.data.len() % 4 != 0 { 415 return Err(EINVAL); 416 } 417 418 bar.write( 419 WithBase::of::<E>().at(Self::PIO_PORT), 420 regs::NV_PFALCON_FALCON_IMEMC::zeroed() 421 .with_secure(load_offsets.secure) 422 .with_aincw(true) 423 .with_offs(load_offsets.dst_start), 424 ); 425 426 for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() { 427 let n = u16::try_from(n)?; 428 let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?; 429 bar.write( 430 WithBase::of::<E>().at(Self::PIO_PORT), 431 regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag), 432 ); 433 for word in block.chunks_exact(4) { 434 let w = [word[0], word[1], word[2], word[3]]; 435 bar.write( 436 WithBase::of::<E>().at(Self::PIO_PORT), 437 regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)), 438 ); 439 } 440 } 441 442 Ok(()) 443 } 444 445 /// Write a slice to Falcon DMEM memory using programmed I/O (PIO). 446 /// 447 /// Returns `EINVAL` if `img.len()` is not a multiple of 4. 448 fn pio_wr_dmem_slice( 449 &self, 450 bar: Bar0<'_>, 451 load_offsets: FalconPioDmemLoadTarget<'_>, 452 ) -> Result { 453 // Rejecting misaligned images here allows us to avoid checking 454 // inside the loops. 455 if load_offsets.data.len() % 4 != 0 { 456 return Err(EINVAL); 457 } 458 459 bar.write( 460 WithBase::of::<E>().at(Self::PIO_PORT), 461 regs::NV_PFALCON_FALCON_DMEMC::zeroed() 462 .with_aincw(true) 463 .with_offs(load_offsets.dst_start), 464 ); 465 466 for word in load_offsets.data.chunks_exact(4) { 467 let w = [word[0], word[1], word[2], word[3]]; 468 bar.write( 469 WithBase::of::<E>().at(Self::PIO_PORT), 470 regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)), 471 ); 472 } 473 474 Ok(()) 475 } 476 477 /// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 478 pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>( 479 &self, 480 bar: Bar0<'_>, 481 fw: &F, 482 ) -> Result { 483 bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| { 484 v.with_allow_phys_no_ctx(true) 485 }); 486 487 bar.write( 488 WithBase::of::<E>(), 489 regs::NV_PFALCON_FALCON_DMACTL::zeroed(), 490 ); 491 492 if let Some(imem_ns) = fw.imem_ns_load_params() { 493 self.pio_wr_imem_slice(bar, imem_ns)?; 494 } 495 if let Some(imem_sec) = fw.imem_sec_load_params() { 496 self.pio_wr_imem_slice(bar, imem_sec)?; 497 } 498 self.pio_wr_dmem_slice(bar, fw.dmem_load_params())?; 499 500 self.hal.program_brom(self, bar, &fw.brom_params()); 501 502 bar.write( 503 WithBase::of::<E>(), 504 regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), 505 ); 506 507 Ok(()) 508 } 509 510 /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's 511 /// `target_mem`. 512 /// 513 /// `sec` is set if the loaded firmware is expected to run in secure mode. 514 fn dma_wr( 515 &self, 516 bar: Bar0<'_>, 517 dma_obj: &Coherent<[u8]>, 518 target_mem: FalconMem, 519 load_offsets: FalconDmaLoadTarget, 520 ) -> Result { 521 const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>(); 522 523 // For IMEM, we want to use the start offset as a virtual address tag for each page, since 524 // code addresses in the firmware (and the boot vector) are virtual. 525 // 526 // For DMEM we can fold the start offset into the DMA handle. 527 let (src_start, dma_start) = match target_mem { 528 FalconMem::ImemSecure | FalconMem::ImemNonSecure => { 529 (load_offsets.src_start, dma_obj.dma_handle()) 530 } 531 FalconMem::Dmem => ( 532 0, 533 dma_obj.dma_handle() + DmaAddress::from(load_offsets.src_start), 534 ), 535 }; 536 if dma_start % DmaAddress::from(DMA_LEN) > 0 { 537 dev_err!( 538 self.dev, 539 "DMA transfer start addresses must be a multiple of {}\n", 540 DMA_LEN 541 ); 542 return Err(EINVAL); 543 } 544 545 // The DMATRFBASE/1 register pair only supports a 49-bit address. 546 if dma_start > DmaMask::new::<49>().value() { 547 dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start); 548 return Err(ERANGE); 549 } 550 551 // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we 552 // need to perform. 553 let num_transfers = load_offsets.len.div_ceil(DMA_LEN); 554 555 // Check that the area we are about to transfer is within the bounds of the DMA object. 556 // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`. 557 match num_transfers 558 .checked_mul(DMA_LEN) 559 .and_then(|size| size.checked_add(load_offsets.src_start)) 560 { 561 None => { 562 dev_err!(self.dev, "DMA transfer length overflow\n"); 563 return Err(EOVERFLOW); 564 } 565 Some(upper_bound) if usize::from_safe_cast(upper_bound) > dma_obj.size() => { 566 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); 567 return Err(EINVAL); 568 } 569 Some(_) => (), 570 }; 571 572 // Set up the base source DMA address. 573 574 bar.write( 575 WithBase::of::<E>(), 576 regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base( 577 // CAST: `as u32` is used on purpose since we do want to strip the upper bits, 578 // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. 579 (dma_start >> 8) as u32, 580 ), 581 ); 582 bar.write( 583 WithBase::of::<E>(), 584 regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_start >> 40)?, 585 ); 586 587 let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::zeroed() 588 .with_size(DmaTrfCmdSize::Size256B) 589 .with_falcon_mem(target_mem); 590 591 for pos in (0..num_transfers).map(|i| i * DMA_LEN) { 592 // Perform a transfer of size `DMA_LEN`. 593 bar.write( 594 WithBase::of::<E>(), 595 regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed() 596 .try_with_offs(load_offsets.dst_start + pos)?, 597 ); 598 bar.write( 599 WithBase::of::<E>(), 600 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos), 601 ); 602 603 bar.write(WithBase::of::<E>(), cmd); 604 605 // Wait for the transfer to complete. 606 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 607 // should ever take that long. 608 read_poll_timeout( 609 || Ok(bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())), 610 |r| r.idle(), 611 Delta::ZERO, 612 Delta::from_secs(2), 613 )?; 614 } 615 616 Ok(()) 617 } 618 619 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 620 fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>( 621 &self, 622 dev: &Device<device::Bound>, 623 bar: Bar0<'_>, 624 fw: &F, 625 ) -> Result { 626 // DMA object with firmware content as the source of the DMA engine. 627 let dma_obj = { 628 let fw_slice = fw.as_slice(); 629 630 // DMA copies are done in chunks of `MEM_BLOCK_ALIGNMENT`, so pad the length 631 // accordingly and fill with `0`. 632 let mut dma_obj = CoherentBox::zeroed_slice( 633 dev, 634 fw_slice.len().next_multiple_of(MEM_BLOCK_ALIGNMENT), 635 GFP_KERNEL, 636 )?; 637 638 // PANIC: `dma_obj` has been created with a length equal to or larger than 639 // `fw_slice.len()`, so the range `..fw_slice.len()` is valid. 640 dma_obj[..fw_slice.len()].copy_from_slice(fw_slice); 641 642 dma_obj.into() 643 }; 644 645 self.dma_reset(bar); 646 bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| { 647 v.with_target(FalconFbifTarget::CoherentSysmem) 648 .with_mem_type(FalconFbifMemType::Physical) 649 }); 650 651 self.dma_wr( 652 bar, 653 &dma_obj, 654 FalconMem::ImemSecure, 655 fw.imem_sec_load_params(), 656 )?; 657 self.dma_wr(bar, &dma_obj, FalconMem::Dmem, fw.dmem_load_params())?; 658 659 self.hal.program_brom(self, bar, &fw.brom_params()); 660 661 // Set `BootVec` to start of non-secure code. 662 bar.write( 663 WithBase::of::<E>(), 664 regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()), 665 ); 666 667 Ok(()) 668 } 669 670 /// Wait until the falcon CPU is halted. 671 pub(crate) fn wait_till_halted(&self, bar: Bar0<'_>) -> Result<()> { 672 // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 673 read_poll_timeout( 674 || Ok(bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())), 675 |r| r.halted(), 676 Delta::ZERO, 677 Delta::from_secs(2), 678 )?; 679 680 Ok(()) 681 } 682 683 /// Start the falcon CPU. 684 pub(crate) fn start(&self, bar: Bar0<'_>) -> Result<()> { 685 match bar 686 .read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>()) 687 .alias_en() 688 { 689 true => bar.write( 690 WithBase::of::<E>(), 691 regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true), 692 ), 693 false => bar.write( 694 WithBase::of::<E>(), 695 regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true), 696 ), 697 } 698 699 Ok(()) 700 } 701 702 /// Writes values to the mailbox registers if provided. 703 pub(crate) fn write_mailboxes(&self, bar: Bar0<'_>, mbox0: Option<u32>, mbox1: Option<u32>) { 704 if let Some(mbox0) = mbox0 { 705 bar.write( 706 WithBase::of::<E>(), 707 regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0), 708 ); 709 } 710 711 if let Some(mbox1) = mbox1 { 712 bar.write( 713 WithBase::of::<E>(), 714 regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1), 715 ); 716 } 717 } 718 719 /// Reads the value from `mbox0` register. 720 pub(crate) fn read_mailbox0(&self, bar: Bar0<'_>) -> u32 { 721 bar.read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>()) 722 .value() 723 } 724 725 /// Reads the value from `mbox1` register. 726 pub(crate) fn read_mailbox1(&self, bar: Bar0<'_>) -> u32 { 727 bar.read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>()) 728 .value() 729 } 730 731 /// Reads values from both mailbox registers. 732 pub(crate) fn read_mailboxes(&self, bar: Bar0<'_>) -> (u32, u32) { 733 let mbox0 = self.read_mailbox0(bar); 734 let mbox1 = self.read_mailbox1(bar); 735 736 (mbox0, mbox1) 737 } 738 739 /// Start running the loaded firmware. 740 /// 741 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 742 /// prior to running. 743 /// 744 /// Wait up to two seconds for the firmware to complete, and return its exit status read from 745 /// the `MBOX0` and `MBOX1` registers. 746 pub(crate) fn boot( 747 &self, 748 bar: Bar0<'_>, 749 mbox0: Option<u32>, 750 mbox1: Option<u32>, 751 ) -> Result<(u32, u32)> { 752 self.write_mailboxes(bar, mbox0, mbox1); 753 self.start(bar)?; 754 self.wait_till_halted(bar)?; 755 Ok(self.read_mailboxes(bar)) 756 } 757 758 /// Returns the fused version of the signature to use in order to run a HS firmware on this 759 /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header. 760 pub(crate) fn signature_reg_fuse_version( 761 &self, 762 bar: Bar0<'_>, 763 engine_id_mask: u16, 764 ucode_id: u8, 765 ) -> Result<u32> { 766 self.hal 767 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 768 } 769 770 /// Check if the RISC-V core is active. 771 /// 772 /// Returns `true` if the RISC-V core is active, `false` otherwise. 773 pub(crate) fn is_riscv_active(&self, bar: Bar0<'_>) -> bool { 774 self.hal.is_riscv_active(bar) 775 } 776 777 /// Load a firmware image into Falcon memory, using the preferred method for the current 778 /// chipset. 779 pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>( 780 &self, 781 dev: &Device<device::Bound>, 782 bar: Bar0<'_>, 783 fw: &F, 784 ) -> Result { 785 match self.hal.load_method() { 786 LoadMethod::Dma => self.dma_load(dev, bar, fw), 787 LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?), 788 } 789 } 790 791 /// Write the application version to the OS register. 792 pub(crate) fn write_os_version(&self, bar: Bar0<'_>, app_version: u32) { 793 bar.write( 794 WithBase::of::<E>(), 795 regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version), 796 ); 797 } 798 } 799