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