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