1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Falcon microprocessor base support 4 5 use core::ops::Deref; 6 7 use hal::FalconHal; 8 9 use kernel::{ 10 device, 11 dma::{ 12 DmaAddress, 13 DmaMask, // 14 }, 15 io::poll::read_poll_timeout, 16 prelude::*, 17 sync::aref::ARef, 18 time::{ 19 Delta, // 20 }, 21 }; 22 23 use crate::{ 24 dma::DmaObject, 25 driver::Bar0, 26 falcon::hal::LoadMethod, 27 gpu::Chipset, 28 num::{ 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 // TODO[FPRI]: Replace with `ToPrimitive`. 41 macro_rules! impl_from_enum_to_u8 { 42 ($enum_type:ty) => { 43 impl From<$enum_type> for u8 { 44 fn from(value: $enum_type) -> Self { 45 value as u8 46 } 47 } 48 }; 49 } 50 51 /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] 52 /// register. 53 #[repr(u8)] 54 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 55 pub(crate) enum FalconCoreRev { 56 #[default] 57 Rev1 = 1, 58 Rev2 = 2, 59 Rev3 = 3, 60 Rev4 = 4, 61 Rev5 = 5, 62 Rev6 = 6, 63 Rev7 = 7, 64 } 65 impl_from_enum_to_u8!(FalconCoreRev); 66 67 // TODO[FPRI]: replace with `FromPrimitive`. 68 impl TryFrom<u8> for FalconCoreRev { 69 type Error = Error; 70 71 fn try_from(value: u8) -> Result<Self> { 72 use FalconCoreRev::*; 73 74 let rev = match value { 75 1 => Rev1, 76 2 => Rev2, 77 3 => Rev3, 78 4 => Rev4, 79 5 => Rev5, 80 6 => Rev6, 81 7 => Rev7, 82 _ => return Err(EINVAL), 83 }; 84 85 Ok(rev) 86 } 87 } 88 89 /// Revision subversion number of a falcon core, used in the 90 /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register. 91 #[repr(u8)] 92 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 93 pub(crate) enum FalconCoreRevSubversion { 94 #[default] 95 Subversion0 = 0, 96 Subversion1 = 1, 97 Subversion2 = 2, 98 Subversion3 = 3, 99 } 100 impl_from_enum_to_u8!(FalconCoreRevSubversion); 101 102 // TODO[FPRI]: replace with `FromPrimitive`. 103 impl TryFrom<u8> for FalconCoreRevSubversion { 104 type Error = Error; 105 106 fn try_from(value: u8) -> Result<Self> { 107 use FalconCoreRevSubversion::*; 108 109 let sub_version = match value & 0b11 { 110 0 => Subversion0, 111 1 => Subversion1, 112 2 => Subversion2, 113 3 => Subversion3, 114 _ => return Err(EINVAL), 115 }; 116 117 Ok(sub_version) 118 } 119 } 120 121 /// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] 122 /// register. 123 #[repr(u8)] 124 #[derive(Debug, Default, Copy, Clone)] 125 /// Security mode of the Falcon microprocessor. 126 /// 127 /// See `falcon.rst` for more details. 128 pub(crate) enum FalconSecurityModel { 129 /// Non-Secure: runs unsigned code without privileges. 130 #[default] 131 None = 0, 132 /// Light-Secured (LS): Runs signed code with some privileges. 133 /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the code's 134 /// signature. 135 /// 136 /// Also known as Low-Secure, Privilege Level 2 or PL2. 137 Light = 2, 138 /// Heavy-Secured (HS): Runs signed code with full privileges. 139 /// The code's signature is verified by the Falcon Boot ROM (BROM). 140 /// 141 /// Also known as High-Secure, Privilege Level 3 or PL3. 142 Heavy = 3, 143 } 144 impl_from_enum_to_u8!(FalconSecurityModel); 145 146 // TODO[FPRI]: replace with `FromPrimitive`. 147 impl TryFrom<u8> for FalconSecurityModel { 148 type Error = Error; 149 150 fn try_from(value: u8) -> Result<Self> { 151 use FalconSecurityModel::*; 152 153 let sec_model = match value { 154 0 => None, 155 2 => Light, 156 3 => Heavy, 157 _ => return Err(EINVAL), 158 }; 159 160 Ok(sec_model) 161 } 162 } 163 164 /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`] 165 /// register. It is passed to the Falcon Boot ROM (BROM) as a parameter. 166 #[repr(u8)] 167 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] 168 pub(crate) enum FalconModSelAlgo { 169 /// AES. 170 #[expect(dead_code)] 171 Aes = 0, 172 /// RSA3K. 173 #[default] 174 Rsa3k = 1, 175 } 176 impl_from_enum_to_u8!(FalconModSelAlgo); 177 178 // TODO[FPRI]: replace with `FromPrimitive`. 179 impl TryFrom<u8> for FalconModSelAlgo { 180 type Error = Error; 181 182 fn try_from(value: u8) -> Result<Self> { 183 match value { 184 1 => Ok(FalconModSelAlgo::Rsa3k), 185 _ => Err(EINVAL), 186 } 187 } 188 } 189 190 /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register. 191 #[repr(u8)] 192 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] 193 pub(crate) enum DmaTrfCmdSize { 194 /// 256 bytes transfer. 195 #[default] 196 Size256B = 0x6, 197 } 198 impl_from_enum_to_u8!(DmaTrfCmdSize); 199 200 // TODO[FPRI]: replace with `FromPrimitive`. 201 impl TryFrom<u8> for DmaTrfCmdSize { 202 type Error = Error; 203 204 fn try_from(value: u8) -> Result<Self> { 205 match value { 206 0x6 => Ok(Self::Size256B), 207 _ => Err(EINVAL), 208 } 209 } 210 } 211 212 /// Currently active core on a dual falcon/riscv (Peregrine) controller. 213 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 214 pub(crate) enum PeregrineCoreSelect { 215 /// Falcon core is active. 216 #[default] 217 Falcon = 0, 218 /// RISC-V core is active. 219 Riscv = 1, 220 } 221 222 impl From<bool> for PeregrineCoreSelect { 223 fn from(value: bool) -> Self { 224 match value { 225 false => PeregrineCoreSelect::Falcon, 226 true => PeregrineCoreSelect::Riscv, 227 } 228 } 229 } 230 231 impl From<PeregrineCoreSelect> for bool { 232 fn from(value: PeregrineCoreSelect) -> Self { 233 match value { 234 PeregrineCoreSelect::Falcon => false, 235 PeregrineCoreSelect::Riscv => true, 236 } 237 } 238 } 239 240 /// Different types of memory present in a falcon core. 241 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 242 pub(crate) enum FalconMem { 243 /// Secure Instruction Memory. 244 ImemSecure, 245 /// Non-Secure Instruction Memory. 246 #[expect(unused)] 247 ImemNonSecure, 248 /// Data Memory. 249 Dmem, 250 } 251 252 /// Defines the Framebuffer Interface (FBIF) aperture type. 253 /// This determines the memory type for external memory access during a DMA transfer, which is 254 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details. 255 #[derive(Debug, Clone, Default)] 256 pub(crate) enum FalconFbifTarget { 257 /// VRAM. 258 #[default] 259 /// Local Framebuffer (GPU's VRAM memory). 260 LocalFb = 0, 261 /// Coherent system memory (System DRAM). 262 CoherentSysmem = 1, 263 /// Non-coherent system memory (System DRAM). 264 NoncoherentSysmem = 2, 265 } 266 impl_from_enum_to_u8!(FalconFbifTarget); 267 268 // TODO[FPRI]: replace with `FromPrimitive`. 269 impl TryFrom<u8> for FalconFbifTarget { 270 type Error = Error; 271 272 fn try_from(value: u8) -> Result<Self> { 273 let res = match value { 274 0 => Self::LocalFb, 275 1 => Self::CoherentSysmem, 276 2 => Self::NoncoherentSysmem, 277 _ => return Err(EINVAL), 278 }; 279 280 Ok(res) 281 } 282 } 283 284 /// Type of memory addresses to use. 285 #[derive(Debug, Clone, Default)] 286 pub(crate) enum FalconFbifMemType { 287 /// Virtual memory addresses. 288 #[default] 289 Virtual = 0, 290 /// Physical memory addresses. 291 Physical = 1, 292 } 293 294 /// Conversion from a single-bit register field. 295 impl From<bool> for FalconFbifMemType { 296 fn from(value: bool) -> Self { 297 match value { 298 false => Self::Virtual, 299 true => Self::Physical, 300 } 301 } 302 } 303 304 impl From<FalconFbifMemType> for bool { 305 fn from(value: FalconFbifMemType) -> Self { 306 match value { 307 FalconFbifMemType::Virtual => false, 308 FalconFbifMemType::Physical => true, 309 } 310 } 311 } 312 313 /// Type used to represent the `PFALCON` registers address base for a given falcon engine. 314 pub(crate) struct PFalconBase(()); 315 316 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine. 317 pub(crate) struct PFalcon2Base(()); 318 319 /// Trait defining the parameters of a given Falcon engine. 320 /// 321 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used 322 /// to identify a given Falcon instance with register I/O methods. 323 pub(crate) trait FalconEngine: 324 Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized 325 { 326 /// Singleton of the engine, used to identify it with register I/O methods. 327 const ID: Self; 328 } 329 330 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM). 331 #[derive(Debug, Clone)] 332 pub(crate) struct FalconLoadTarget { 333 /// Offset from the start of the source object to copy from. 334 pub(crate) src_start: u32, 335 /// Offset from the start of the destination memory to copy into. 336 pub(crate) dst_start: u32, 337 /// Number of bytes to copy. 338 pub(crate) len: u32, 339 } 340 341 /// Parameters for the falcon boot ROM. 342 #[derive(Debug, Clone)] 343 pub(crate) struct FalconBromParams { 344 /// Offset in `DMEM`` of the firmware's signature. 345 pub(crate) pkc_data_offset: u32, 346 /// Mask of engines valid for this firmware. 347 pub(crate) engine_id_mask: u16, 348 /// ID of the ucode used to infer a fuse register to validate the signature. 349 pub(crate) ucode_id: u8, 350 } 351 352 /// Trait for providing load parameters of falcon firmwares. 353 pub(crate) trait FalconLoadParams { 354 /// Returns the load parameters for Secure `IMEM`. 355 fn imem_sec_load_params(&self) -> FalconLoadTarget; 356 357 /// Returns the load parameters for Non-Secure `IMEM`, 358 /// used only on Turing and GA100. 359 fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>; 360 361 /// Returns the load parameters for `DMEM`. 362 fn dmem_load_params(&self) -> FalconLoadTarget; 363 364 /// Returns the parameters to write into the BROM registers. 365 fn brom_params(&self) -> FalconBromParams; 366 367 /// Returns the start address of the firmware. 368 fn boot_addr(&self) -> u32; 369 } 370 371 /// Trait for a falcon firmware. 372 /// 373 /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA 374 /// object. 375 pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> { 376 /// Engine on which this firmware is to be loaded. 377 type Target: FalconEngine; 378 } 379 380 /// Contains the base parameters common to all Falcon instances. 381 pub(crate) struct Falcon<E: FalconEngine> { 382 hal: KBox<dyn FalconHal<E>>, 383 dev: ARef<device::Device>, 384 } 385 386 impl<E: FalconEngine + 'static> Falcon<E> { 387 /// Create a new falcon instance. 388 pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> { 389 Ok(Self { 390 hal: hal::falcon_hal(chipset)?, 391 dev: dev.into(), 392 }) 393 } 394 395 /// Resets DMA-related registers. 396 pub(crate) fn dma_reset(&self, bar: &Bar0) { 397 regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); 398 regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); 399 } 400 401 /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. 402 pub(crate) fn reset(&self, bar: &Bar0) -> Result { 403 self.hal.reset_eng(bar)?; 404 self.hal.select_core(self, bar)?; 405 self.hal.reset_wait_mem_scrubbing(bar)?; 406 407 regs::NV_PFALCON_FALCON_RM::default() 408 .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) 409 .write(bar, &E::ID); 410 411 Ok(()) 412 } 413 414 /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's 415 /// `target_mem`. 416 /// 417 /// `sec` is set if the loaded firmware is expected to run in secure mode. 418 fn dma_wr<F: FalconFirmware<Target = E>>( 419 &self, 420 bar: &Bar0, 421 fw: &F, 422 target_mem: FalconMem, 423 load_offsets: FalconLoadTarget, 424 ) -> Result { 425 const DMA_LEN: u32 = 256; 426 427 // For IMEM, we want to use the start offset as a virtual address tag for each page, since 428 // code addresses in the firmware (and the boot vector) are virtual. 429 // 430 // For DMEM we can fold the start offset into the DMA handle. 431 let (src_start, dma_start) = match target_mem { 432 FalconMem::ImemSecure | FalconMem::ImemNonSecure => { 433 (load_offsets.src_start, fw.dma_handle()) 434 } 435 FalconMem::Dmem => ( 436 0, 437 fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, 438 ), 439 }; 440 if dma_start % DmaAddress::from(DMA_LEN) > 0 { 441 dev_err!( 442 self.dev, 443 "DMA transfer start addresses must be a multiple of {}\n", 444 DMA_LEN 445 ); 446 return Err(EINVAL); 447 } 448 449 // The DMATRFBASE/1 register pair only supports a 49-bit address. 450 if dma_start > DmaMask::new::<49>().value() { 451 dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start); 452 return Err(ERANGE); 453 } 454 455 // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we 456 // need to perform. 457 let num_transfers = load_offsets.len.div_ceil(DMA_LEN); 458 459 // Check that the area we are about to transfer is within the bounds of the DMA object. 460 // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`. 461 match num_transfers 462 .checked_mul(DMA_LEN) 463 .and_then(|size| size.checked_add(load_offsets.src_start)) 464 { 465 None => { 466 dev_err!(self.dev, "DMA transfer length overflow\n"); 467 return Err(EOVERFLOW); 468 } 469 Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => { 470 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); 471 return Err(EINVAL); 472 } 473 Some(_) => (), 474 }; 475 476 // Set up the base source DMA address. 477 478 regs::NV_PFALCON_FALCON_DMATRFBASE::default() 479 // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which 480 // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. 481 .set_base((dma_start >> 8) as u32) 482 .write(bar, &E::ID); 483 regs::NV_PFALCON_FALCON_DMATRFBASE1::default() 484 // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit 485 // within a `u16`. 486 .set_base((dma_start >> 40) as u16) 487 .write(bar, &E::ID); 488 489 let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() 490 .set_size(DmaTrfCmdSize::Size256B) 491 .with_falcon_mem(target_mem); 492 493 for pos in (0..num_transfers).map(|i| i * DMA_LEN) { 494 // Perform a transfer of size `DMA_LEN`. 495 regs::NV_PFALCON_FALCON_DMATRFMOFFS::default() 496 .set_offs(load_offsets.dst_start + pos) 497 .write(bar, &E::ID); 498 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default() 499 .set_offs(src_start + pos) 500 .write(bar, &E::ID); 501 cmd.write(bar, &E::ID); 502 503 // Wait for the transfer to complete. 504 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 505 // should ever take that long. 506 read_poll_timeout( 507 || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)), 508 |r| r.idle(), 509 Delta::ZERO, 510 Delta::from_secs(2), 511 )?; 512 } 513 514 Ok(()) 515 } 516 517 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 518 fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { 519 // The Non-Secure section only exists on firmware used by Turing and GA100, and 520 // those platforms do not use DMA. 521 if fw.imem_ns_load_params().is_some() { 522 debug_assert!(false); 523 return Err(EINVAL); 524 } 525 526 self.dma_reset(bar); 527 regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { 528 v.set_target(FalconFbifTarget::CoherentSysmem) 529 .set_mem_type(FalconFbifMemType::Physical) 530 }); 531 532 self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?; 533 self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?; 534 535 self.hal.program_brom(self, bar, &fw.brom_params())?; 536 537 // Set `BootVec` to start of non-secure code. 538 regs::NV_PFALCON_FALCON_BOOTVEC::default() 539 .set_value(fw.boot_addr()) 540 .write(bar, &E::ID); 541 542 Ok(()) 543 } 544 545 /// Wait until the falcon CPU is halted. 546 pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> { 547 // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 548 read_poll_timeout( 549 || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)), 550 |r| r.halted(), 551 Delta::ZERO, 552 Delta::from_secs(2), 553 )?; 554 555 Ok(()) 556 } 557 558 /// Start the falcon CPU. 559 pub(crate) fn start(&self, bar: &Bar0) -> Result<()> { 560 match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { 561 true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() 562 .set_startcpu(true) 563 .write(bar, &E::ID), 564 false => regs::NV_PFALCON_FALCON_CPUCTL::default() 565 .set_startcpu(true) 566 .write(bar, &E::ID), 567 } 568 569 Ok(()) 570 } 571 572 /// Writes values to the mailbox registers if provided. 573 pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) { 574 if let Some(mbox0) = mbox0 { 575 regs::NV_PFALCON_FALCON_MAILBOX0::default() 576 .set_value(mbox0) 577 .write(bar, &E::ID); 578 } 579 580 if let Some(mbox1) = mbox1 { 581 regs::NV_PFALCON_FALCON_MAILBOX1::default() 582 .set_value(mbox1) 583 .write(bar, &E::ID); 584 } 585 } 586 587 /// Reads the value from `mbox0` register. 588 pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 { 589 regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value() 590 } 591 592 /// Reads the value from `mbox1` register. 593 pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 { 594 regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value() 595 } 596 597 /// Reads values from both mailbox registers. 598 pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) { 599 let mbox0 = self.read_mailbox0(bar); 600 let mbox1 = self.read_mailbox1(bar); 601 602 (mbox0, mbox1) 603 } 604 605 /// Start running the loaded firmware. 606 /// 607 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 608 /// prior to running. 609 /// 610 /// Wait up to two seconds for the firmware to complete, and return its exit status read from 611 /// the `MBOX0` and `MBOX1` registers. 612 pub(crate) fn boot( 613 &self, 614 bar: &Bar0, 615 mbox0: Option<u32>, 616 mbox1: Option<u32>, 617 ) -> Result<(u32, u32)> { 618 self.write_mailboxes(bar, mbox0, mbox1); 619 self.start(bar)?; 620 self.wait_till_halted(bar)?; 621 Ok(self.read_mailboxes(bar)) 622 } 623 624 /// Returns the fused version of the signature to use in order to run a HS firmware on this 625 /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header. 626 pub(crate) fn signature_reg_fuse_version( 627 &self, 628 bar: &Bar0, 629 engine_id_mask: u16, 630 ucode_id: u8, 631 ) -> Result<u32> { 632 self.hal 633 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 634 } 635 636 /// Check if the RISC-V core is active. 637 /// 638 /// Returns `true` if the RISC-V core is active, `false` otherwise. 639 pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { 640 self.hal.is_riscv_active(bar) 641 } 642 643 // Load a firmware image into Falcon memory 644 pub(crate) fn load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { 645 match self.hal.load_method() { 646 LoadMethod::Dma => self.dma_load(bar, fw), 647 LoadMethod::Pio => Err(ENOTSUPP), 648 } 649 } 650 651 /// Write the application version to the OS register. 652 pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) { 653 regs::NV_PFALCON_FALCON_OS::default() 654 .set_value(app_version) 655 .write(bar, &E::ID); 656 } 657 } 658