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