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