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