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