File Index
Files and directories
Sign in
FILE

falcon.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/falcon.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! Falcon microprocessor base support
  4
  5use core::ops::Deref;
  6
  7use hal::FalconHal;
  8
  9use 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
 24use 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
 36pub(crate) mod gsp;
 37mod hal;
 38pub(crate) mod sec2;
 39
 40// TODO[FPRI]: Replace with `ToPrimitive`.
 41macro_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)]
 55pub(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}
 65impl_from_enum_to_u8!(FalconCoreRev);
 66
 67// TODO[FPRI]: replace with `FromPrimitive`.
 68impl 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)]
 93pub(crate) enum FalconCoreRevSubversion {
 94    #[default]
 95    Subversion0 = 0,
 96    Subversion1 = 1,
 97    Subversion2 = 2,
 98    Subversion3 = 3,
 99}
100impl_from_enum_to_u8!(FalconCoreRevSubversion);
101
102// TODO[FPRI]: replace with `FromPrimitive`.
103impl 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.
128pub(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}
144impl_from_enum_to_u8!(FalconSecurityModel);
145
146// TODO[FPRI]: replace with `FromPrimitive`.
147impl 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)]
168pub(crate) enum FalconModSelAlgo {
169    /// AES.
170    #[expect(dead_code)]
171    Aes = 0,
172    /// RSA3K.
173    #[default]
174    Rsa3k = 1,
175}
176impl_from_enum_to_u8!(FalconModSelAlgo);
177
178// TODO[FPRI]: replace with `FromPrimitive`.
179impl 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)]
193pub(crate) enum DmaTrfCmdSize {
194    /// 256 bytes transfer.
195    #[default]
196    Size256B = 0x6,
197}
198impl_from_enum_to_u8!(DmaTrfCmdSize);
199
200// TODO[FPRI]: replace with `FromPrimitive`.
201impl 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)]
214pub(crate) enum PeregrineCoreSelect {
215    /// Falcon core is active.
216    #[default]
217    Falcon = 0,
218    /// RISC-V core is active.
219    Riscv = 1,
220}
221
222impl 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
231impl 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)]
242pub(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)]
253pub(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}
263impl_from_enum_to_u8!(FalconFbifTarget);
264
265// TODO[FPRI]: replace with `FromPrimitive`.
266impl 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)]
283pub(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.
292impl 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
301impl 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.
311pub(crate) struct PFalconBase(());
312
313/// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
314pub(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.
320pub(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)]
329pub(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)]
340pub(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.
350pub(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.
368pub(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.
374pub(crate) struct Falcon<E: FalconEngine> {
375    hal: KBox<dyn FalconHal<E>>,
376    dev: ARef<device::Device>,
377}
378
379impl<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 {}",
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");
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");
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}
~/ Read-only file preview.