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