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