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