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