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