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::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_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 467 // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we 468 // need to perform. 469 let num_transfers = load_offsets.len.div_ceil(DMA_LEN); 470 471 // Check that the area we are about to transfer is within the bounds of the DMA object. 472 // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`. 473 match num_transfers 474 .checked_mul(DMA_LEN) 475 .and_then(|size| size.checked_add(load_offsets.src_start)) 476 { 477 None => { 478 dev_err!(self.dev, "DMA transfer length overflow"); 479 return Err(EOVERFLOW); 480 } 481 Some(upper_bound) if upper_bound as usize > fw.size() => { 482 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object"); 483 return Err(EINVAL); 484 } 485 Some(_) => (), 486 }; 487 488 // Set up the base source DMA address. 489 490 regs::NV_PFALCON_FALCON_DMATRFBASE::default() 491 .set_base((dma_start >> 8) as u32) 492 .write(bar, &E::ID); 493 regs::NV_PFALCON_FALCON_DMATRFBASE1::default() 494 .set_base((dma_start >> 40) as u16) 495 .write(bar, &E::ID); 496 497 let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() 498 .set_size(DmaTrfCmdSize::Size256B) 499 .set_imem(target_mem == FalconMem::Imem) 500 .set_sec(if sec { 1 } else { 0 }); 501 502 for pos in (0..num_transfers).map(|i| i * DMA_LEN) { 503 // Perform a transfer of size `DMA_LEN`. 504 regs::NV_PFALCON_FALCON_DMATRFMOFFS::default() 505 .set_offs(load_offsets.dst_start + pos) 506 .write(bar, &E::ID); 507 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default() 508 .set_offs(src_start + pos) 509 .write(bar, &E::ID); 510 cmd.write(bar, &E::ID); 511 512 // Wait for the transfer to complete. 513 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 514 // should ever take that long. 515 util::wait_on(Delta::from_secs(2), || { 516 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID); 517 if r.idle() { 518 Some(()) 519 } else { 520 None 521 } 522 })?; 523 } 524 525 Ok(()) 526 } 527 528 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 529 pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { 530 regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); 531 regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); 532 regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| { 533 v.set_target(FalconFbifTarget::CoherentSysmem) 534 .set_mem_type(FalconFbifMemType::Physical) 535 }); 536 537 self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?; 538 self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?; 539 540 self.hal.program_brom(self, bar, &fw.brom_params())?; 541 542 // Set `BootVec` to start of non-secure code. 543 regs::NV_PFALCON_FALCON_BOOTVEC::default() 544 .set_value(fw.boot_addr()) 545 .write(bar, &E::ID); 546 547 Ok(()) 548 } 549 550 /// Runs the loaded firmware and waits for its completion. 551 /// 552 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 553 /// prior to running. 554 /// 555 /// Wait up to two seconds for the firmware to complete, and return its exit status read from 556 /// the `MBOX0` and `MBOX1` registers. 557 pub(crate) fn boot( 558 &self, 559 bar: &Bar0, 560 mbox0: Option<u32>, 561 mbox1: Option<u32>, 562 ) -> Result<(u32, u32)> { 563 if let Some(mbox0) = mbox0 { 564 regs::NV_PFALCON_FALCON_MAILBOX0::default() 565 .set_value(mbox0) 566 .write(bar, &E::ID); 567 } 568 569 if let Some(mbox1) = mbox1 { 570 regs::NV_PFALCON_FALCON_MAILBOX1::default() 571 .set_value(mbox1) 572 .write(bar, &E::ID); 573 } 574 575 match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { 576 true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() 577 .set_startcpu(true) 578 .write(bar, &E::ID), 579 false => regs::NV_PFALCON_FALCON_CPUCTL::default() 580 .set_startcpu(true) 581 .write(bar, &E::ID), 582 } 583 584 // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 585 util::wait_on(Delta::from_secs(2), || { 586 let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID); 587 if r.halted() { 588 Some(()) 589 } else { 590 None 591 } 592 })?; 593 594 let (mbox0, mbox1) = ( 595 regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(), 596 regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(), 597 ); 598 599 Ok((mbox0, mbox1)) 600 } 601 602 /// Returns the fused version of the signature to use in order to run a HS firmware on this 603 /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header. 604 pub(crate) fn signature_reg_fuse_version( 605 &self, 606 bar: &Bar0, 607 engine_id_mask: u16, 608 ucode_id: u8, 609 ) -> Result<u32> { 610 self.hal 611 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 612 } 613 } 614