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