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