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