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::bindings; 8 use kernel::device; 9 use kernel::prelude::*; 10 use kernel::time::Delta; 11 use kernel::types::ARef; 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_u32 { 26 ($enum_type:ty) => { 27 impl From<$enum_type> for u32 { 28 fn from(value: $enum_type) -> Self { 29 value as u32 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_u32!(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_u32!(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_u32!(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_u32!(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_u32!(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 impl_from_enum_to_u32!(PeregrineCoreSelect); 206 207 impl From<bool> for PeregrineCoreSelect { 208 fn from(value: bool) -> Self { 209 match value { 210 false => PeregrineCoreSelect::Falcon, 211 true => PeregrineCoreSelect::Riscv, 212 } 213 } 214 } 215 216 /// Different types of memory present in a falcon core. 217 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 218 pub(crate) enum FalconMem { 219 /// Instruction Memory. 220 Imem, 221 /// Data Memory. 222 Dmem, 223 } 224 225 /// Defines the Framebuffer Interface (FBIF) aperture type. 226 /// This determines the memory type for external memory access during a DMA transfer, which is 227 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details. 228 #[derive(Debug, Clone, Default)] 229 pub(crate) enum FalconFbifTarget { 230 /// VRAM. 231 #[default] 232 /// Local Framebuffer (GPU's VRAM memory). 233 LocalFb = 0, 234 /// Coherent system memory (System DRAM). 235 CoherentSysmem = 1, 236 /// Non-coherent system memory (System DRAM). 237 NoncoherentSysmem = 2, 238 } 239 impl_from_enum_to_u32!(FalconFbifTarget); 240 241 // TODO[FPRI]: replace with `FromPrimitive`. 242 impl TryFrom<u8> for FalconFbifTarget { 243 type Error = Error; 244 245 fn try_from(value: u8) -> Result<Self> { 246 let res = match value { 247 0 => Self::LocalFb, 248 1 => Self::CoherentSysmem, 249 2 => Self::NoncoherentSysmem, 250 _ => return Err(EINVAL), 251 }; 252 253 Ok(res) 254 } 255 } 256 257 /// Type of memory addresses to use. 258 #[derive(Debug, Clone, Default)] 259 pub(crate) enum FalconFbifMemType { 260 /// Virtual memory addresses. 261 #[default] 262 Virtual = 0, 263 /// Physical memory addresses. 264 Physical = 1, 265 } 266 impl_from_enum_to_u32!(FalconFbifMemType); 267 268 /// Conversion from a single-bit register field. 269 impl From<bool> for FalconFbifMemType { 270 fn from(value: bool) -> Self { 271 match value { 272 false => Self::Virtual, 273 true => Self::Physical, 274 } 275 } 276 } 277 278 /// Type used to represent the `PFALCON` registers address base for a given falcon engine. 279 pub(crate) struct PFalconBase(()); 280 281 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine. 282 pub(crate) struct PFalcon2Base(()); 283 284 /// Trait defining the parameters of a given Falcon engine. 285 /// 286 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used 287 /// to identify a given Falcon instance with register I/O methods. 288 pub(crate) trait FalconEngine: 289 Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized 290 { 291 /// Singleton of the engine, used to identify it with register I/O methods. 292 const ID: Self; 293 } 294 295 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM). 296 #[derive(Debug)] 297 pub(crate) struct FalconLoadTarget { 298 /// Offset from the start of the source object to copy from. 299 pub(crate) src_start: u32, 300 /// Offset from the start of the destination memory to copy into. 301 pub(crate) dst_start: u32, 302 /// Number of bytes to copy. 303 pub(crate) len: u32, 304 } 305 306 /// Parameters for the falcon boot ROM. 307 #[derive(Debug)] 308 pub(crate) struct FalconBromParams { 309 /// Offset in `DMEM`` of the firmware's signature. 310 pub(crate) pkc_data_offset: u32, 311 /// Mask of engines valid for this firmware. 312 pub(crate) engine_id_mask: u16, 313 /// ID of the ucode used to infer a fuse register to validate the signature. 314 pub(crate) ucode_id: u8, 315 } 316 317 /// Trait for providing load parameters of falcon firmwares. 318 pub(crate) trait FalconLoadParams { 319 /// Returns the load parameters for `IMEM`. 320 fn imem_load_params(&self) -> FalconLoadTarget; 321 322 /// Returns the load parameters for `DMEM`. 323 fn dmem_load_params(&self) -> FalconLoadTarget; 324 325 /// Returns the parameters to write into the BROM registers. 326 fn brom_params(&self) -> FalconBromParams; 327 328 /// Returns the start address of the firmware. 329 fn boot_addr(&self) -> u32; 330 } 331 332 /// Trait for a falcon firmware. 333 /// 334 /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA 335 /// object. 336 pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> { 337 /// Engine on which this firmware is to be loaded. 338 type Target: FalconEngine; 339 } 340 341 /// Contains the base parameters common to all Falcon instances. 342 pub(crate) struct Falcon<E: FalconEngine> { 343 hal: KBox<dyn FalconHal<E>>, 344 dev: ARef<device::Device>, 345 } 346 347 impl<E: FalconEngine + 'static> Falcon<E> { 348 /// Create a new falcon instance. 349 /// 350 /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv 351 /// controller. 352 pub(crate) fn new( 353 dev: &device::Device, 354 chipset: Chipset, 355 bar: &Bar0, 356 need_riscv: bool, 357 ) -> Result<Self> { 358 let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, &E::ID); 359 // Check that the revision and security model contain valid values. 360 let _ = hwcfg1.core_rev()?; 361 let _ = hwcfg1.security_model()?; 362 363 if need_riscv { 364 let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); 365 if !hwcfg2.riscv() { 366 dev_err!( 367 dev, 368 "riscv support requested on a controller that does not support it\n" 369 ); 370 return Err(EINVAL); 371 } 372 } 373 374 Ok(Self { 375 hal: hal::falcon_hal(chipset)?, 376 dev: dev.into(), 377 }) 378 } 379 380 /// Wait for memory scrubbing to complete. 381 fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { 382 // TIMEOUT: memory scrubbing should complete in less than 20ms. 383 util::wait_on(Delta::from_millis(20), || { 384 if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID).mem_scrubbing_done() { 385 Some(()) 386 } else { 387 None 388 } 389 }) 390 } 391 392 /// Reset the falcon engine. 393 fn reset_eng(&self, bar: &Bar0) -> Result { 394 let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); 395 396 // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set 397 // RESET_READY so a non-failing timeout is used. 398 let _ = util::wait_on(Delta::from_micros(150), || { 399 let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); 400 if r.reset_ready() { 401 Some(()) 402 } else { 403 None 404 } 405 }); 406 407 regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(true)); 408 409 // TODO[DLAY]: replace with udelay() or equivalent once available. 410 // TIMEOUT: falcon engine should not take more than 10us to reset. 411 let _: Result = util::wait_on(Delta::from_micros(10), || None); 412 413 regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(false)); 414 415 self.reset_wait_mem_scrubbing(bar)?; 416 417 Ok(()) 418 } 419 420 /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. 421 pub(crate) fn reset(&self, bar: &Bar0) -> Result { 422 self.reset_eng(bar)?; 423 self.hal.select_core(self, bar)?; 424 self.reset_wait_mem_scrubbing(bar)?; 425 426 regs::NV_PFALCON_FALCON_RM::default() 427 .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) 428 .write(bar, &E::ID); 429 430 Ok(()) 431 } 432 433 /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's 434 /// `target_mem`. 435 /// 436 /// `sec` is set if the loaded firmware is expected to run in secure mode. 437 fn dma_wr<F: FalconFirmware<Target = E>>( 438 &self, 439 bar: &Bar0, 440 fw: &F, 441 target_mem: FalconMem, 442 load_offsets: FalconLoadTarget, 443 sec: bool, 444 ) -> Result { 445 const DMA_LEN: u32 = 256; 446 447 // For IMEM, we want to use the start offset as a virtual address tag for each page, since 448 // code addresses in the firmware (and the boot vector) are virtual. 449 // 450 // For DMEM we can fold the start offset into the DMA handle. 451 let (src_start, dma_start) = match target_mem { 452 FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()), 453 FalconMem::Dmem => ( 454 0, 455 fw.dma_handle_with_offset(load_offsets.src_start as usize)?, 456 ), 457 }; 458 if dma_start % bindings::dma_addr_t::from(DMA_LEN) > 0 { 459 dev_err!( 460 self.dev, 461 "DMA transfer start addresses must be a multiple of {}", 462 DMA_LEN 463 ); 464 return Err(EINVAL); 465 } 466 if load_offsets.len % DMA_LEN > 0 { 467 dev_err!( 468 self.dev, 469 "DMA transfer length must be a multiple of {}", 470 DMA_LEN 471 ); 472 return Err(EINVAL); 473 } 474 475 // Set up the base source DMA address. 476 477 regs::NV_PFALCON_FALCON_DMATRFBASE::default() 478 .set_base((dma_start >> 8) as u32) 479 .write(bar, &E::ID); 480 regs::NV_PFALCON_FALCON_DMATRFBASE1::default() 481 .set_base((dma_start >> 40) as u16) 482 .write(bar, &E::ID); 483 484 let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() 485 .set_size(DmaTrfCmdSize::Size256B) 486 .set_imem(target_mem == FalconMem::Imem) 487 .set_sec(if sec { 1 } else { 0 }); 488 489 for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) { 490 // Perform a transfer of size `DMA_LEN`. 491 regs::NV_PFALCON_FALCON_DMATRFMOFFS::default() 492 .set_offs(load_offsets.dst_start + pos) 493 .write(bar, &E::ID); 494 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default() 495 .set_offs(src_start + pos) 496 .write(bar, &E::ID); 497 cmd.write(bar, &E::ID); 498 499 // Wait for the transfer to complete. 500 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 501 // should ever take that long. 502 util::wait_on(Delta::from_secs(2), || { 503 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID); 504 if r.idle() { 505 Some(()) 506 } else { 507 None 508 } 509 })?; 510 } 511 512 Ok(()) 513 } 514 515 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 516 pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { 517 regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); 518 regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); 519 regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| { 520 v.set_target(FalconFbifTarget::CoherentSysmem) 521 .set_mem_type(FalconFbifMemType::Physical) 522 }); 523 524 self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?; 525 self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?; 526 527 self.hal.program_brom(self, bar, &fw.brom_params())?; 528 529 // Set `BootVec` to start of non-secure code. 530 regs::NV_PFALCON_FALCON_BOOTVEC::default() 531 .set_value(fw.boot_addr()) 532 .write(bar, &E::ID); 533 534 Ok(()) 535 } 536 537 /// Runs the loaded firmware and waits for its completion. 538 /// 539 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 540 /// prior to running. 541 /// 542 /// Wait up to two seconds for the firmware to complete, and return its exit status read from 543 /// the `MBOX0` and `MBOX1` registers. 544 pub(crate) fn boot( 545 &self, 546 bar: &Bar0, 547 mbox0: Option<u32>, 548 mbox1: Option<u32>, 549 ) -> Result<(u32, u32)> { 550 if let Some(mbox0) = mbox0 { 551 regs::NV_PFALCON_FALCON_MAILBOX0::default() 552 .set_value(mbox0) 553 .write(bar, &E::ID); 554 } 555 556 if let Some(mbox1) = mbox1 { 557 regs::NV_PFALCON_FALCON_MAILBOX1::default() 558 .set_value(mbox1) 559 .write(bar, &E::ID); 560 } 561 562 match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { 563 true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() 564 .set_startcpu(true) 565 .write(bar, &E::ID), 566 false => regs::NV_PFALCON_FALCON_CPUCTL::default() 567 .set_startcpu(true) 568 .write(bar, &E::ID), 569 } 570 571 // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 572 util::wait_on(Delta::from_secs(2), || { 573 let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID); 574 if r.halted() { 575 Some(()) 576 } else { 577 None 578 } 579 })?; 580 581 let (mbox0, mbox1) = ( 582 regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(), 583 regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(), 584 ); 585 586 Ok((mbox0, mbox1)) 587 } 588 589 /// Returns the fused version of the signature to use in order to run a HS firmware on this 590 /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header. 591 pub(crate) fn signature_reg_fuse_version( 592 &self, 593 bar: &Bar0, 594 engine_id_mask: u16, 595 ucode_id: u8, 596 ) -> Result<u32> { 597 self.hal 598 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 599 } 600 } 601