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