xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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     dma::{
10         Coherent,
11         CoherentBox,
12         DmaAddress, //
13     },
14     io::{
15         io_project,
16         poll::read_poll_timeout,
17         register::{
18             RegisterBase,
19             WithBase, //
20         },
21         Io,
22     },
23     prelude::*,
24     time::Delta,
25 };
26 
27 use crate::{
28     bounded_enum,
29     driver::Bar0,
30     falcon::hal::LoadMethod,
31     gpu::Chipset,
32     num::{
33         self,
34         FromSafeCast, //
35     },
36     regs,
37 };
38 
39 pub(crate) mod fsp;
40 pub(crate) mod gsp;
41 mod hal;
42 pub(crate) mod sec2;
43 
44 /// Alignment (in bytes) of falcon memory blocks.
45 pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256;
46 
47 bounded_enum! {
48     /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
49     /// register.
50     #[derive(Debug, Copy, Clone)]
51     pub(crate) enum FalconCoreRev with TryFrom<Bounded<u32, 4>> {
52         Rev1 = 1,
53         Rev2 = 2,
54         Rev3 = 3,
55         Rev4 = 4,
56         Rev5 = 5,
57         Rev6 = 6,
58         Rev7 = 7,
59     }
60 }
61 
62 bounded_enum! {
63     /// Revision subversion number of a falcon core, used in the
64     /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
65     #[derive(Debug, Copy, Clone)]
66     pub(crate) enum FalconCoreRevSubversion with From<Bounded<u32, 2>> {
67         Subversion0 = 0,
68         Subversion1 = 1,
69         Subversion2 = 2,
70         Subversion3 = 3,
71     }
72 }
73 
74 bounded_enum! {
75     /// Security mode of the Falcon microprocessor.
76     ///
77     /// See `falcon.rst` for more details.
78     #[derive(Debug, Copy, Clone)]
79     pub(crate) enum FalconSecurityModel with TryFrom<Bounded<u32, 2>> {
80         /// Non-Secure: runs unsigned code without privileges.
81         None = 0,
82         /// Light-Secured (LS): Runs signed code with some privileges.
83         /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the
84         /// code's signature.
85         ///
86         /// Also known as Low-Secure, Privilege Level 2 or PL2.
87         Light = 2,
88         /// Heavy-Secured (HS): Runs signed code with full privileges.
89         /// The code's signature is verified by the Falcon Boot ROM (BROM).
90         ///
91         /// Also known as High-Secure, Privilege Level 3 or PL3.
92         Heavy = 3,
93     }
94 }
95 
96 bounded_enum! {
97     /// Signing algorithm for a given firmware, used in the
98     /// [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`] register. It is passed to the Falcon Boot ROM
99     /// (BROM) as a parameter.
100     #[derive(Debug, Copy, Clone)]
101     pub(crate) enum FalconModSelAlgo with TryFrom<Bounded<u32, 8>> {
102         /// AES.
103         Aes = 0,
104         /// RSA3K.
105         Rsa3k = 1,
106     }
107 }
108 
109 bounded_enum! {
110     /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`]
111     /// register.
112     #[derive(Debug, Copy, Clone)]
113     pub(crate) enum DmaTrfCmdSize with TryFrom<Bounded<u32, 3>> {
114         /// 256 bytes transfer.
115         Size256B = 0x6,
116     }
117 }
118 
119 bounded_enum! {
120     /// Currently active core on a dual falcon/riscv (Peregrine) controller.
121     #[derive(Debug, Copy, Clone, PartialEq, Eq)]
122     pub(crate) enum PeregrineCoreSelect with From<Bounded<u32, 1>> {
123         /// Falcon core is active.
124         Falcon = 0,
125         /// RISC-V core is active.
126         Riscv = 1,
127     }
128 }
129 
130 /// Different types of memory present in a falcon core.
131 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
132 pub(crate) enum FalconMem {
133     /// Secure Instruction Memory.
134     ImemSecure,
135     /// Non-Secure Instruction Memory.
136     #[expect(unused)]
137     ImemNonSecure,
138     /// Data Memory.
139     Dmem,
140 }
141 
142 bounded_enum! {
143     /// Defines the Framebuffer Interface (FBIF) aperture type.
144     /// This determines the memory type for external memory access during a DMA transfer, which is
145     /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
146     #[derive(Debug, Copy, Clone)]
147     pub(crate) enum FalconFbifTarget with TryFrom<Bounded<u32, 2>> {
148         /// Local Framebuffer (GPU's VRAM memory).
149         LocalFb = 0,
150         /// Coherent system memory (System DRAM).
151         CoherentSysmem = 1,
152         /// Non-coherent system memory (System DRAM).
153         NoncoherentSysmem = 2,
154     }
155 }
156 
157 bounded_enum! {
158     /// Type of memory addresses to use.
159     #[derive(Debug, Copy, Clone)]
160     pub(crate) enum FalconFbifMemType with From<Bounded<u32, 1>> {
161         /// Virtual memory addresses.
162         Virtual = 0,
163         /// Physical memory addresses.
164         Physical = 1,
165     }
166 }
167 
168 /// Type used to represent the `PFALCON` registers address base for a given falcon engine.
169 pub(crate) struct PFalconBase(());
170 
171 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
172 pub(crate) struct PFalcon2Base(());
173 
174 /// Trait defining the parameters of a given Falcon engine.
175 ///
176 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers.
177 pub(crate) trait FalconEngine:
178     Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized
179 {
180 }
181 
182 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM)
183 /// using DMA.
184 #[derive(Debug, Clone)]
185 pub(crate) struct FalconDmaLoadTarget {
186     /// Offset from the start of the source object to copy from.
187     pub(crate) src_start: u32,
188     /// Offset from the start of the destination memory to copy into.
189     pub(crate) dst_start: u32,
190     /// Number of bytes to copy.
191     pub(crate) len: u32,
192 }
193 
194 /// Parameters for the falcon boot ROM.
195 #[derive(Debug, Clone)]
196 pub(crate) struct FalconBromParams {
197     /// Offset in `DMEM`` of the firmware's signature.
198     pub(crate) pkc_data_offset: u32,
199     /// Mask of engines valid for this firmware.
200     pub(crate) engine_id_mask: u16,
201     /// ID of the ucode used to infer a fuse register to validate the signature.
202     pub(crate) ucode_id: u8,
203 }
204 
205 /// Trait implemented by falcon firmwares that can be loaded using DMA.
206 pub(crate) trait FalconDmaLoadable {
207     /// Returns the firmware data as a slice of bytes.
208     fn as_slice(&self) -> &[u8];
209 
210     /// Returns the load parameters for Secure `IMEM`.
211     fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
212 
213     /// Returns the load parameters for Non-Secure `IMEM`,
214     /// used only on Turing and GA100.
215     fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
216 
217     /// Returns the load parameters for `DMEM`.
218     fn dmem_load_params(&self) -> FalconDmaLoadTarget;
219 
220     /// Returns an adapter that provides the required parameter to load this firmware using PIO.
221     ///
222     /// This can only fail if some `u32` fields cannot be converted to `u16`, or if the indices in
223     /// the headers are invalid.
224     fn try_as_pio_loadable(&self) -> Result<FalconDmaFirmwarePioAdapter<'_, Self>> {
225         let new_pio_imem = |params: FalconDmaLoadTarget, secure| {
226             let start = usize::from_safe_cast(params.src_start);
227             let end = start + usize::from_safe_cast(params.len);
228             let data = self.as_slice().get(start..end).ok_or(EINVAL)?;
229 
230             let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?;
231 
232             Ok::<_, Error>(FalconPioImemLoadTarget {
233                 data,
234                 dst_start,
235                 secure,
236                 start_tag: dst_start >> 8,
237             })
238         };
239 
240         let imem_sec = new_pio_imem(self.imem_sec_load_params(), true)?;
241 
242         let imem_ns = if let Some(params) = self.imem_ns_load_params() {
243             Some(new_pio_imem(params, false)?)
244         } else {
245             None
246         };
247 
248         let dmem = {
249             let params = self.dmem_load_params();
250             let start = usize::from_safe_cast(params.src_start);
251             let end = start + usize::from_safe_cast(params.len);
252             let data = self.as_slice().get(start..end).ok_or(EINVAL)?;
253 
254             let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?;
255 
256             FalconPioDmemLoadTarget { data, dst_start }
257         };
258 
259         Ok(FalconDmaFirmwarePioAdapter {
260             fw: self,
261             imem_sec,
262             imem_ns,
263             dmem,
264         })
265     }
266 }
267 
268 /// Represents a portion of the firmware to be loaded into IMEM using PIO.
269 #[derive(Clone)]
270 pub(crate) struct FalconPioImemLoadTarget<'a> {
271     pub(crate) data: &'a [u8],
272     pub(crate) dst_start: u16,
273     pub(crate) secure: bool,
274     pub(crate) start_tag: u16,
275 }
276 
277 /// Represents a portion of the firmware to be loaded into DMEM using PIO.
278 #[derive(Clone)]
279 pub(crate) struct FalconPioDmemLoadTarget<'a> {
280     pub(crate) data: &'a [u8],
281     pub(crate) dst_start: u16,
282 }
283 
284 /// Trait for providing PIO load parameters of falcon firmwares.
285 pub(crate) trait FalconPioLoadable {
286     /// Returns the load parameters for Secure `IMEM`, if any.
287     fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>;
288 
289     /// Returns the load parameters for Non-Secure `IMEM`, if any.
290     fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>;
291 
292     /// Returns the load parameters for `DMEM`.
293     fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_>;
294 }
295 
296 /// Adapter type that makes any DMA-loadable firmware also loadable via PIO.
297 ///
298 /// Created using [`FalconDmaLoadable::try_as_pio_loadable`].
299 pub(crate) struct FalconDmaFirmwarePioAdapter<'a, T: FalconDmaLoadable + ?Sized> {
300     /// Reference to the DMA firmware.
301     fw: &'a T,
302     /// Validated secure IMEM parameters.
303     imem_sec: FalconPioImemLoadTarget<'a>,
304     /// Validated non-secure IMEM parameters.
305     imem_ns: Option<FalconPioImemLoadTarget<'a>>,
306     /// Validated DMEM parameters.
307     dmem: FalconPioDmemLoadTarget<'a>,
308 }
309 
310 impl<'a, T> FalconPioLoadable for FalconDmaFirmwarePioAdapter<'a, T>
311 where
312     T: FalconDmaLoadable + ?Sized,
313 {
314     fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
315         Some(self.imem_sec.clone())
316     }
317 
318     fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
319         self.imem_ns.clone()
320     }
321 
322     fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
323         self.dmem.clone()
324     }
325 }
326 
327 impl<'a, T> FalconFirmware for FalconDmaFirmwarePioAdapter<'a, T>
328 where
329     T: FalconDmaLoadable + FalconFirmware + ?Sized,
330 {
331     type Target = <T as FalconFirmware>::Target;
332 
333     fn brom_params(&self) -> FalconBromParams {
334         self.fw.brom_params()
335     }
336 
337     fn boot_addr(&self) -> u32 {
338         self.fw.boot_addr()
339     }
340 }
341 
342 /// Trait for a falcon firmware.
343 ///
344 /// A falcon firmware can be loaded on a given engine.
345 pub(crate) trait FalconFirmware {
346     /// Engine on which this firmware is to be loaded.
347     type Target: FalconEngine;
348 
349     /// Returns the parameters to write into the BROM registers.
350     fn brom_params(&self) -> FalconBromParams;
351 
352     /// Returns the start address of the firmware.
353     fn boot_addr(&self) -> u32;
354 }
355 
356 /// Contains the base parameters common to all Falcon instances.
357 pub(crate) struct Falcon<'a, E: FalconEngine> {
358     hal: KBox<dyn FalconHal<E>>,
359     dev: &'a device::Device<device::Bound>,
360     bar: Bar0<'a>,
361 }
362 
363 impl<'a, E: FalconEngine + 'static> Falcon<'a, E> {
364     /// Create a new falcon instance.
365     pub(crate) fn new(
366         dev: &'a device::Device<device::Bound>,
367         chipset: Chipset,
368         bar: Bar0<'a>,
369     ) -> Result<Self> {
370         Ok(Self {
371             hal: hal::falcon_hal(chipset)?,
372             dev,
373             bar,
374         })
375     }
376 
377     /// Resets DMA-related registers.
378     pub(crate) fn dma_reset(&self) {
379         self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
380             v.with_allow_phys_no_ctx(true)
381         });
382 
383         self.bar.write(
384             WithBase::of::<E>(),
385             regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
386         );
387     }
388 
389     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
390     pub(crate) fn reset(&self) -> Result {
391         self.hal.reset_eng(self)?;
392         self.hal.select_core(self)?;
393         self.hal.reset_wait_mem_scrubbing(self)?;
394 
395         self.bar.write(
396             WithBase::of::<E>(),
397             regs::NV_PFALCON_FALCON_RM::from(self.bar.read(regs::NV_PMC_BOOT_0).into_raw()),
398         );
399 
400         Ok(())
401     }
402 
403     /// Falcons supports up to four ports, but we only ever use one, so just hard-code it.
404     const PIO_PORT: usize = 0;
405 
406     /// Write a slice to Falcon IMEM memory using programmed I/O (PIO).
407     ///
408     /// Returns `EINVAL` if `img.len()` is not a multiple of 4.
409     fn pio_wr_imem_slice(&self, load_offsets: FalconPioImemLoadTarget<'_>) -> Result {
410         // Rejecting misaligned images here allows us to avoid checking
411         // inside the loops.
412         if load_offsets.data.len() % 4 != 0 {
413             return Err(EINVAL);
414         }
415 
416         self.bar.write(
417             WithBase::of::<E>().at(Self::PIO_PORT),
418             regs::NV_PFALCON_FALCON_IMEMC::zeroed()
419                 .with_secure(load_offsets.secure)
420                 .with_aincw(true)
421                 .with_offs(load_offsets.dst_start),
422         );
423 
424         for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() {
425             let n = u16::try_from(n)?;
426             let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?;
427             self.bar.write(
428                 WithBase::of::<E>().at(Self::PIO_PORT),
429                 regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag),
430             );
431             for word in block.chunks_exact(4) {
432                 let w = [word[0], word[1], word[2], word[3]];
433                 self.bar.write(
434                     WithBase::of::<E>().at(Self::PIO_PORT),
435                     regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)),
436                 );
437             }
438         }
439 
440         Ok(())
441     }
442 
443     /// Write a slice to Falcon DMEM memory using programmed I/O (PIO).
444     ///
445     /// Returns `EINVAL` if `img.len()` is not a multiple of 4.
446     fn pio_wr_dmem_slice(&self, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result {
447         // Rejecting misaligned images here allows us to avoid checking
448         // inside the loops.
449         if load_offsets.data.len() % 4 != 0 {
450             return Err(EINVAL);
451         }
452 
453         self.bar.write(
454             WithBase::of::<E>().at(Self::PIO_PORT),
455             regs::NV_PFALCON_FALCON_DMEMC::zeroed()
456                 .with_aincw(true)
457                 .with_offs(load_offsets.dst_start),
458         );
459 
460         for word in load_offsets.data.chunks_exact(4) {
461             let w = [word[0], word[1], word[2], word[3]];
462             self.bar.write(
463                 WithBase::of::<E>().at(Self::PIO_PORT),
464                 regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)),
465             );
466         }
467 
468         Ok(())
469     }
470 
471     /// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
472     pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
473         &self,
474         fw: &F,
475     ) -> Result {
476         self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
477             v.with_allow_phys_no_ctx(true)
478         });
479 
480         self.bar.write(
481             WithBase::of::<E>(),
482             regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
483         );
484 
485         if let Some(imem_ns) = fw.imem_ns_load_params() {
486             self.pio_wr_imem_slice(imem_ns)?;
487         }
488         if let Some(imem_sec) = fw.imem_sec_load_params() {
489             self.pio_wr_imem_slice(imem_sec)?;
490         }
491         self.pio_wr_dmem_slice(fw.dmem_load_params())?;
492 
493         self.hal.program_brom(self, &fw.brom_params());
494 
495         self.bar.write(
496             WithBase::of::<E>(),
497             regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
498         );
499 
500         Ok(())
501     }
502 
503     /// Perform a DMA write according to `load_offsets` from `dma_obj` into the falcon's
504     /// `target_mem`.
505     ///
506     /// `sec` is set if the loaded firmware is expected to run in secure mode.
507     fn dma_wr(
508         &self,
509         dma_obj: &Coherent<[u8]>,
510         target_mem: FalconMem,
511         load_offsets: FalconDmaLoadTarget,
512     ) -> Result {
513         const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
514 
515         // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we
516         // need to perform.
517         let num_transfers = load_offsets.len.div_ceil(DMA_LEN);
518 
519         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
520         // code addresses in the firmware (and the boot vector) are virtual.
521         //
522         // For DMEM, the start offset is folded into the DMA address.
523         let (src_start, dma_start) = match target_mem {
524             FalconMem::ImemSecure | FalconMem::ImemNonSecure => (load_offsets.src_start, 0),
525             FalconMem::Dmem => (0, usize::from_safe_cast(load_offsets.src_start)),
526         };
527 
528         let dma_address = {
529             // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`.
530             let dma_end = num_transfers
531                 .checked_mul(DMA_LEN)
532                 .and_then(|size| size.checked_add(load_offsets.src_start))
533                 .map(usize::from_safe_cast)
534                 .ok_or(EOVERFLOW)?;
535 
536             io_project!(dma_obj, [try: dma_start..dma_end]).dma_address()
537         };
538 
539         if dma_address % DmaAddress::from(DMA_LEN) > 0 {
540             dev_err!(
541                 self.dev,
542                 "DMA transfer start addresses must be a multiple of {}\n",
543                 DMA_LEN
544             );
545             return Err(EINVAL);
546         }
547 
548         // Set up the base source DMA address.
549 
550         self.bar.write(
551             WithBase::of::<E>(),
552             regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base(
553                 // CAST: `as u32` is used on purpose since we do want to strip the upper bits,
554                 // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
555                 (dma_address >> 8) as u32,
556             ),
557         );
558         self.bar.write(
559             WithBase::of::<E>(),
560             regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_address >> 40)?,
561         );
562 
563         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::zeroed()
564             .with_size(DmaTrfCmdSize::Size256B)
565             .with_falcon_mem(target_mem);
566 
567         for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
568             // Perform a transfer of size `DMA_LEN`.
569             self.bar.write(
570                 WithBase::of::<E>(),
571                 regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed()
572                     .try_with_offs(load_offsets.dst_start + pos)?,
573             );
574             self.bar.write(
575                 WithBase::of::<E>(),
576                 regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos),
577             );
578 
579             self.bar.write(WithBase::of::<E>(), cmd);
580 
581             // Wait for the transfer to complete.
582             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
583             // should ever take that long.
584             read_poll_timeout(
585                 || Ok(self.bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
586                 |r| r.idle(),
587                 Delta::ZERO,
588                 Delta::from_secs(2),
589             )?;
590         }
591 
592         Ok(())
593     }
594 
595     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
596     fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) -> Result {
597         // DMA object with firmware content as the source of the DMA engine.
598         let dma_obj = {
599             let fw_slice = fw.as_slice();
600 
601             // DMA copies are done in chunks of `MEM_BLOCK_ALIGNMENT`, so pad the length
602             // accordingly and fill with `0`.
603             let mut dma_obj = CoherentBox::zeroed_slice(
604                 self.dev,
605                 fw_slice.len().next_multiple_of(MEM_BLOCK_ALIGNMENT),
606                 GFP_KERNEL,
607             )?;
608 
609             // PANIC: `dma_obj` has been created with a length equal to or larger than
610             // `fw_slice.len()`, so the range `..fw_slice.len()` is valid.
611             dma_obj[..fw_slice.len()].copy_from_slice(fw_slice);
612 
613             dma_obj.into()
614         };
615 
616         self.dma_reset();
617         self.bar
618             .update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| {
619                 v.with_target(FalconFbifTarget::CoherentSysmem)
620                     .with_mem_type(FalconFbifMemType::Physical)
621             });
622 
623         self.dma_wr(&dma_obj, FalconMem::ImemSecure, fw.imem_sec_load_params())?;
624         self.dma_wr(&dma_obj, FalconMem::Dmem, fw.dmem_load_params())?;
625 
626         self.hal.program_brom(self, &fw.brom_params());
627 
628         // Set `BootVec` to start of non-secure code.
629         self.bar.write(
630             WithBase::of::<E>(),
631             regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
632         );
633 
634         Ok(())
635     }
636 
637     /// Wait until the falcon CPU is halted.
638     pub(crate) fn wait_till_halted(&self) -> Result<()> {
639         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
640         read_poll_timeout(
641             || Ok(self.bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())),
642             |r| r.halted(),
643             Delta::ZERO,
644             Delta::from_secs(2),
645         )?;
646 
647         Ok(())
648     }
649 
650     /// Start the falcon CPU.
651     pub(crate) fn start(&self) -> Result<()> {
652         match self
653             .bar
654             .read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())
655             .alias_en()
656         {
657             true => self.bar.write(
658                 WithBase::of::<E>(),
659                 regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true),
660             ),
661             false => self.bar.write(
662                 WithBase::of::<E>(),
663                 regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true),
664             ),
665         }
666 
667         Ok(())
668     }
669 
670     /// Writes values to the mailbox registers if provided.
671     pub(crate) fn write_mailboxes(&self, mbox0: Option<u32>, mbox1: Option<u32>) {
672         if let Some(mbox0) = mbox0 {
673             self.bar.write(
674                 WithBase::of::<E>(),
675                 regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0),
676             );
677         }
678 
679         if let Some(mbox1) = mbox1 {
680             self.bar.write(
681                 WithBase::of::<E>(),
682                 regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1),
683             );
684         }
685     }
686 
687     /// Reads the value from `mbox0` register.
688     pub(crate) fn read_mailbox0(&self) -> u32 {
689         self.bar
690             .read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>())
691             .value()
692     }
693 
694     /// Reads the value from `mbox1` register.
695     pub(crate) fn read_mailbox1(&self) -> u32 {
696         self.bar
697             .read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>())
698             .value()
699     }
700 
701     /// Reads values from both mailbox registers.
702     pub(crate) fn read_mailboxes(&self) -> (u32, u32) {
703         let mbox0 = self.read_mailbox0();
704         let mbox1 = self.read_mailbox1();
705 
706         (mbox0, mbox1)
707     }
708 
709     /// Start running the loaded firmware.
710     ///
711     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
712     /// prior to running.
713     ///
714     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
715     /// the `MBOX0` and `MBOX1` registers.
716     pub(crate) fn boot(&self, mbox0: Option<u32>, mbox1: Option<u32>) -> Result<(u32, u32)> {
717         self.write_mailboxes(mbox0, mbox1);
718         self.start()?;
719         self.wait_till_halted()?;
720         Ok(self.read_mailboxes())
721     }
722 
723     /// Returns the fused version of the signature to use in order to run a HS firmware on this
724     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
725     pub(crate) fn signature_reg_fuse_version(
726         &self,
727         engine_id_mask: u16,
728         ucode_id: u8,
729     ) -> Result<u32> {
730         self.hal
731             .signature_reg_fuse_version(self, engine_id_mask, ucode_id)
732     }
733 
734     /// Check if the RISC-V core is active.
735     ///
736     /// Note that this does not guarantee that the RISC-V core is halted if it returns `false`.
737     ///
738     /// Returns `true` if the RISC-V core is active, `false` otherwise.
739     pub(crate) fn is_riscv_active(&self) -> bool {
740         self.hal.is_riscv_active(self)
741     }
742 
743     /// Checks whether the RISC-V core is halted.
744     ///
745     /// Note that this does not guarantee that the RISC-V core is active if it returns `false`.
746     ///
747     /// Returns [`ENOTSUPP`] if the status is not available.
748     pub(crate) fn is_riscv_halted(&self) -> Result<bool> {
749         self.hal.is_riscv_halted(self)
750     }
751 
752     /// Load a firmware image into Falcon memory, using the preferred method for the current
753     /// chipset.
754     pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) -> Result {
755         match self.hal.load_method() {
756             LoadMethod::Dma => self.dma_load(fw),
757             LoadMethod::Pio => self.pio_load(&fw.try_as_pio_loadable()?),
758         }
759     }
760 
761     /// Write the application version to the OS register.
762     pub(crate) fn write_os_version(&self, app_version: u32) {
763         self.bar.write(
764             WithBase::of::<E>(),
765             regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version),
766         );
767     }
768 }
769