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