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