projects/linux·blob master · refs

768 lines · 24.8 KiB

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