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