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