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