xref: /linux/drivers/gpu/nova-core/falcon.rs (revision b72cb7bcc20c3a0d7fc29590a6e85de3dfbe9eff)
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     /// Secure Instruction Memory.
244     ImemSecure,
245     /// Non-Secure Instruction Memory.
246     #[expect(unused)]
247     ImemNonSecure,
248     /// Data Memory.
249     Dmem,
250 }
251 
252 /// Defines the Framebuffer Interface (FBIF) aperture type.
253 /// This determines the memory type for external memory access during a DMA transfer, which is
254 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
255 #[derive(Debug, Clone, Default)]
256 pub(crate) enum FalconFbifTarget {
257     /// VRAM.
258     #[default]
259     /// Local Framebuffer (GPU's VRAM memory).
260     LocalFb = 0,
261     /// Coherent system memory (System DRAM).
262     CoherentSysmem = 1,
263     /// Non-coherent system memory (System DRAM).
264     NoncoherentSysmem = 2,
265 }
266 impl_from_enum_to_u8!(FalconFbifTarget);
267 
268 // TODO[FPRI]: replace with `FromPrimitive`.
269 impl TryFrom<u8> for FalconFbifTarget {
270     type Error = Error;
271 
272     fn try_from(value: u8) -> Result<Self> {
273         let res = match value {
274             0 => Self::LocalFb,
275             1 => Self::CoherentSysmem,
276             2 => Self::NoncoherentSysmem,
277             _ => return Err(EINVAL),
278         };
279 
280         Ok(res)
281     }
282 }
283 
284 /// Type of memory addresses to use.
285 #[derive(Debug, Clone, Default)]
286 pub(crate) enum FalconFbifMemType {
287     /// Virtual memory addresses.
288     #[default]
289     Virtual = 0,
290     /// Physical memory addresses.
291     Physical = 1,
292 }
293 
294 /// Conversion from a single-bit register field.
295 impl From<bool> for FalconFbifMemType {
296     fn from(value: bool) -> Self {
297         match value {
298             false => Self::Virtual,
299             true => Self::Physical,
300         }
301     }
302 }
303 
304 impl From<FalconFbifMemType> for bool {
305     fn from(value: FalconFbifMemType) -> Self {
306         match value {
307             FalconFbifMemType::Virtual => false,
308             FalconFbifMemType::Physical => true,
309         }
310     }
311 }
312 
313 /// Type used to represent the `PFALCON` registers address base for a given falcon engine.
314 pub(crate) struct PFalconBase(());
315 
316 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
317 pub(crate) struct PFalcon2Base(());
318 
319 /// Trait defining the parameters of a given Falcon engine.
320 ///
321 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used
322 /// to identify a given Falcon instance with register I/O methods.
323 pub(crate) trait FalconEngine:
324     Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized
325 {
326     /// Singleton of the engine, used to identify it with register I/O methods.
327     const ID: Self;
328 }
329 
330 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
331 #[derive(Debug, Clone)]
332 pub(crate) struct FalconLoadTarget {
333     /// Offset from the start of the source object to copy from.
334     pub(crate) src_start: u32,
335     /// Offset from the start of the destination memory to copy into.
336     pub(crate) dst_start: u32,
337     /// Number of bytes to copy.
338     pub(crate) len: u32,
339 }
340 
341 /// Parameters for the falcon boot ROM.
342 #[derive(Debug, Clone)]
343 pub(crate) struct FalconBromParams {
344     /// Offset in `DMEM`` of the firmware's signature.
345     pub(crate) pkc_data_offset: u32,
346     /// Mask of engines valid for this firmware.
347     pub(crate) engine_id_mask: u16,
348     /// ID of the ucode used to infer a fuse register to validate the signature.
349     pub(crate) ucode_id: u8,
350 }
351 
352 /// Trait for providing load parameters of falcon firmwares.
353 pub(crate) trait FalconLoadParams {
354     /// Returns the load parameters for Secure `IMEM`.
355     fn imem_sec_load_params(&self) -> FalconLoadTarget;
356 
357     /// Returns the load parameters for Non-Secure `IMEM`,
358     /// used only on Turing and GA100.
359     fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>;
360 
361     /// Returns the load parameters for `DMEM`.
362     fn dmem_load_params(&self) -> FalconLoadTarget;
363 
364     /// Returns the parameters to write into the BROM registers.
365     fn brom_params(&self) -> FalconBromParams;
366 
367     /// Returns the start address of the firmware.
368     fn boot_addr(&self) -> u32;
369 }
370 
371 /// Trait for a falcon firmware.
372 ///
373 /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
374 /// object.
375 pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
376     /// Engine on which this firmware is to be loaded.
377     type Target: FalconEngine;
378 }
379 
380 /// Contains the base parameters common to all Falcon instances.
381 pub(crate) struct Falcon<E: FalconEngine> {
382     hal: KBox<dyn FalconHal<E>>,
383     dev: ARef<device::Device>,
384 }
385 
386 impl<E: FalconEngine + 'static> Falcon<E> {
387     /// Create a new falcon instance.
388     pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> {
389         Ok(Self {
390             hal: hal::falcon_hal(chipset)?,
391             dev: dev.into(),
392         })
393     }
394 
395     /// Resets DMA-related registers.
396     pub(crate) fn dma_reset(&self, bar: &Bar0) {
397         regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
398         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
399     }
400 
401     /// Wait for memory scrubbing to complete.
402     fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
403         // TIMEOUT: memory scrubbing should complete in less than 20ms.
404         read_poll_timeout(
405             || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)),
406             |r| r.mem_scrubbing_done(),
407             Delta::ZERO,
408             Delta::from_millis(20),
409         )
410         .map(|_| ())
411     }
412 
413     /// Reset the falcon engine.
414     fn reset_eng(&self, bar: &Bar0) -> Result {
415         let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
416 
417         // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
418         // RESET_READY so a non-failing timeout is used.
419         let _ = read_poll_timeout(
420             || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)),
421             |r| r.reset_ready(),
422             Delta::ZERO,
423             Delta::from_micros(150),
424         );
425 
426         regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true));
427 
428         // TIMEOUT: falcon engine should not take more than 10us to reset.
429         fsleep(Delta::from_micros(10));
430 
431         regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false));
432 
433         self.reset_wait_mem_scrubbing(bar)?;
434 
435         Ok(())
436     }
437 
438     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
439     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
440         self.reset_eng(bar)?;
441         self.hal.select_core(self, bar)?;
442         self.reset_wait_mem_scrubbing(bar)?;
443 
444         regs::NV_PFALCON_FALCON_RM::default()
445             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
446             .write(bar, &E::ID);
447 
448         Ok(())
449     }
450 
451     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
452     /// `target_mem`.
453     ///
454     /// `sec` is set if the loaded firmware is expected to run in secure mode.
455     fn dma_wr<F: FalconFirmware<Target = E>>(
456         &self,
457         bar: &Bar0,
458         fw: &F,
459         target_mem: FalconMem,
460         load_offsets: FalconLoadTarget,
461         sec: bool,
462     ) -> Result {
463         const DMA_LEN: u32 = 256;
464 
465         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
466         // code addresses in the firmware (and the boot vector) are virtual.
467         //
468         // For DMEM we can fold the start offset into the DMA handle.
469         let (src_start, dma_start) = match target_mem {
470             FalconMem::ImemSecure | FalconMem::ImemNonSecure => {
471                 (load_offsets.src_start, fw.dma_handle())
472             }
473             FalconMem::Dmem => (
474                 0,
475                 fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?,
476             ),
477         };
478         if dma_start % DmaAddress::from(DMA_LEN) > 0 {
479             dev_err!(
480                 self.dev,
481                 "DMA transfer start addresses must be a multiple of {}\n",
482                 DMA_LEN
483             );
484             return Err(EINVAL);
485         }
486 
487         // The DMATRFBASE/1 register pair only supports a 49-bit address.
488         if dma_start > DmaMask::new::<49>().value() {
489             dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start);
490             return Err(ERANGE);
491         }
492 
493         // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we
494         // need to perform.
495         let num_transfers = load_offsets.len.div_ceil(DMA_LEN);
496 
497         // Check that the area we are about to transfer is within the bounds of the DMA object.
498         // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`.
499         match num_transfers
500             .checked_mul(DMA_LEN)
501             .and_then(|size| size.checked_add(load_offsets.src_start))
502         {
503             None => {
504                 dev_err!(self.dev, "DMA transfer length overflow\n");
505                 return Err(EOVERFLOW);
506             }
507             Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => {
508                 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n");
509                 return Err(EINVAL);
510             }
511             Some(_) => (),
512         };
513 
514         // Set up the base source DMA address.
515 
516         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
517             // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which
518             // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
519             .set_base((dma_start >> 8) as u32)
520             .write(bar, &E::ID);
521         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
522             // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit
523             // within a `u16`.
524             .set_base((dma_start >> 40) as u16)
525             .write(bar, &E::ID);
526 
527         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
528             .set_size(DmaTrfCmdSize::Size256B)
529             .set_imem(target_mem != FalconMem::Dmem)
530             .set_sec(if sec { 1 } else { 0 });
531 
532         for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
533             // Perform a transfer of size `DMA_LEN`.
534             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
535                 .set_offs(load_offsets.dst_start + pos)
536                 .write(bar, &E::ID);
537             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
538                 .set_offs(src_start + pos)
539                 .write(bar, &E::ID);
540             cmd.write(bar, &E::ID);
541 
542             // Wait for the transfer to complete.
543             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
544             // should ever take that long.
545             read_poll_timeout(
546                 || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)),
547                 |r| r.idle(),
548                 Delta::ZERO,
549                 Delta::from_secs(2),
550             )?;
551         }
552 
553         Ok(())
554     }
555 
556     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
557     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
558         // The Non-Secure section only exists on firmware used by Turing and GA100, and
559         // those platforms do not use DMA.
560         if fw.imem_ns_load_params().is_some() {
561             debug_assert!(false);
562             return Err(EINVAL);
563         }
564 
565         self.dma_reset(bar);
566         regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| {
567             v.set_target(FalconFbifTarget::CoherentSysmem)
568                 .set_mem_type(FalconFbifMemType::Physical)
569         });
570 
571         self.dma_wr(
572             bar,
573             fw,
574             FalconMem::ImemSecure,
575             fw.imem_sec_load_params(),
576             true,
577         )?;
578         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
579 
580         self.hal.program_brom(self, bar, &fw.brom_params())?;
581 
582         // Set `BootVec` to start of non-secure code.
583         regs::NV_PFALCON_FALCON_BOOTVEC::default()
584             .set_value(fw.boot_addr())
585             .write(bar, &E::ID);
586 
587         Ok(())
588     }
589 
590     /// Wait until the falcon CPU is halted.
591     pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> {
592         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
593         read_poll_timeout(
594             || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)),
595             |r| r.halted(),
596             Delta::ZERO,
597             Delta::from_secs(2),
598         )?;
599 
600         Ok(())
601     }
602 
603     /// Start the falcon CPU.
604     pub(crate) fn start(&self, bar: &Bar0) -> Result<()> {
605         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() {
606             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
607                 .set_startcpu(true)
608                 .write(bar, &E::ID),
609             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
610                 .set_startcpu(true)
611                 .write(bar, &E::ID),
612         }
613 
614         Ok(())
615     }
616 
617     /// Writes values to the mailbox registers if provided.
618     pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) {
619         if let Some(mbox0) = mbox0 {
620             regs::NV_PFALCON_FALCON_MAILBOX0::default()
621                 .set_value(mbox0)
622                 .write(bar, &E::ID);
623         }
624 
625         if let Some(mbox1) = mbox1 {
626             regs::NV_PFALCON_FALCON_MAILBOX1::default()
627                 .set_value(mbox1)
628                 .write(bar, &E::ID);
629         }
630     }
631 
632     /// Reads the value from `mbox0` register.
633     pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 {
634         regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value()
635     }
636 
637     /// Reads the value from `mbox1` register.
638     pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 {
639         regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value()
640     }
641 
642     /// Reads values from both mailbox registers.
643     pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) {
644         let mbox0 = self.read_mailbox0(bar);
645         let mbox1 = self.read_mailbox1(bar);
646 
647         (mbox0, mbox1)
648     }
649 
650     /// Start running the loaded firmware.
651     ///
652     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
653     /// prior to running.
654     ///
655     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
656     /// the `MBOX0` and `MBOX1` registers.
657     pub(crate) fn boot(
658         &self,
659         bar: &Bar0,
660         mbox0: Option<u32>,
661         mbox1: Option<u32>,
662     ) -> Result<(u32, u32)> {
663         self.write_mailboxes(bar, mbox0, mbox1);
664         self.start(bar)?;
665         self.wait_till_halted(bar)?;
666         Ok(self.read_mailboxes(bar))
667     }
668 
669     /// Returns the fused version of the signature to use in order to run a HS firmware on this
670     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
671     pub(crate) fn signature_reg_fuse_version(
672         &self,
673         bar: &Bar0,
674         engine_id_mask: u16,
675         ucode_id: u8,
676     ) -> Result<u32> {
677         self.hal
678             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
679     }
680 
681     /// Check if the RISC-V core is active.
682     ///
683     /// Returns `true` if the RISC-V core is active, `false` otherwise.
684     pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
685         let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
686         cpuctl.active_stat()
687     }
688 
689     /// Write the application version to the OS register.
690     pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) {
691         regs::NV_PFALCON_FALCON_OS::default()
692             .set_value(app_version)
693             .write(bar, &E::ID);
694     }
695 }
696