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