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