xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 82ed3243219d160601fdb98742633bee7dc6f360)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Falcon microprocessor base support
4 
5 use core::ops::Deref;
6 
7 use hal::FalconHal;
8 
9 use kernel::{
10     device,
11     dma::{
12         DmaAddress,
13         DmaMask, //
14     },
15     io::poll::read_poll_timeout,
16     prelude::*,
17     sync::aref::ARef,
18     time::{
19         Delta, //
20     },
21 };
22 
23 use crate::{
24     dma::DmaObject,
25     driver::Bar0,
26     gpu::Chipset,
27     num::{
28         FromSafeCast,
29         IntoSafeCast, //
30     },
31     regs,
32     regs::macros::RegisterBase, //
33 };
34 
35 pub(crate) mod gsp;
36 mod hal;
37 pub(crate) mod sec2;
38 
39 // TODO[FPRI]: Replace with `ToPrimitive`.
40 macro_rules! impl_from_enum_to_u8 {
41     ($enum_type:ty) => {
42         impl From<$enum_type> for u8 {
43             fn from(value: $enum_type) -> Self {
44                 value as u8
45             }
46         }
47     };
48 }
49 
50 /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
51 /// register.
52 #[repr(u8)]
53 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
54 pub(crate) enum FalconCoreRev {
55     #[default]
56     Rev1 = 1,
57     Rev2 = 2,
58     Rev3 = 3,
59     Rev4 = 4,
60     Rev5 = 5,
61     Rev6 = 6,
62     Rev7 = 7,
63 }
64 impl_from_enum_to_u8!(FalconCoreRev);
65 
66 // TODO[FPRI]: replace with `FromPrimitive`.
67 impl TryFrom<u8> for FalconCoreRev {
68     type Error = Error;
69 
70     fn try_from(value: u8) -> Result<Self> {
71         use FalconCoreRev::*;
72 
73         let rev = match value {
74             1 => Rev1,
75             2 => Rev2,
76             3 => Rev3,
77             4 => Rev4,
78             5 => Rev5,
79             6 => Rev6,
80             7 => Rev7,
81             _ => return Err(EINVAL),
82         };
83 
84         Ok(rev)
85     }
86 }
87 
88 /// Revision subversion number of a falcon core, used in the
89 /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
90 #[repr(u8)]
91 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
92 pub(crate) enum FalconCoreRevSubversion {
93     #[default]
94     Subversion0 = 0,
95     Subversion1 = 1,
96     Subversion2 = 2,
97     Subversion3 = 3,
98 }
99 impl_from_enum_to_u8!(FalconCoreRevSubversion);
100 
101 // TODO[FPRI]: replace with `FromPrimitive`.
102 impl TryFrom<u8> for FalconCoreRevSubversion {
103     type Error = Error;
104 
105     fn try_from(value: u8) -> Result<Self> {
106         use FalconCoreRevSubversion::*;
107 
108         let sub_version = match value & 0b11 {
109             0 => Subversion0,
110             1 => Subversion1,
111             2 => Subversion2,
112             3 => Subversion3,
113             _ => return Err(EINVAL),
114         };
115 
116         Ok(sub_version)
117     }
118 }
119 
120 /// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
121 /// register.
122 #[repr(u8)]
123 #[derive(Debug, Default, Copy, Clone)]
124 /// Security mode of the Falcon microprocessor.
125 ///
126 /// See `falcon.rst` for more details.
127 pub(crate) enum FalconSecurityModel {
128     /// Non-Secure: runs unsigned code without privileges.
129     #[default]
130     None = 0,
131     /// Light-Secured (LS): Runs signed code with some privileges.
132     /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the code's
133     /// signature.
134     ///
135     /// Also known as Low-Secure, Privilege Level 2 or PL2.
136     Light = 2,
137     /// Heavy-Secured (HS): Runs signed code with full privileges.
138     /// The code's signature is verified by the Falcon Boot ROM (BROM).
139     ///
140     /// Also known as High-Secure, Privilege Level 3 or PL3.
141     Heavy = 3,
142 }
143 impl_from_enum_to_u8!(FalconSecurityModel);
144 
145 // TODO[FPRI]: replace with `FromPrimitive`.
146 impl TryFrom<u8> for FalconSecurityModel {
147     type Error = Error;
148 
149     fn try_from(value: u8) -> Result<Self> {
150         use FalconSecurityModel::*;
151 
152         let sec_model = match value {
153             0 => None,
154             2 => Light,
155             3 => Heavy,
156             _ => return Err(EINVAL),
157         };
158 
159         Ok(sec_model)
160     }
161 }
162 
163 /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
164 /// register. It is passed to the Falcon Boot ROM (BROM) as a parameter.
165 #[repr(u8)]
166 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
167 pub(crate) enum FalconModSelAlgo {
168     /// AES.
169     #[expect(dead_code)]
170     Aes = 0,
171     /// RSA3K.
172     #[default]
173     Rsa3k = 1,
174 }
175 impl_from_enum_to_u8!(FalconModSelAlgo);
176 
177 // TODO[FPRI]: replace with `FromPrimitive`.
178 impl TryFrom<u8> for FalconModSelAlgo {
179     type Error = Error;
180 
181     fn try_from(value: u8) -> Result<Self> {
182         match value {
183             1 => Ok(FalconModSelAlgo::Rsa3k),
184             _ => Err(EINVAL),
185         }
186     }
187 }
188 
189 /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
190 #[repr(u8)]
191 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
192 pub(crate) enum DmaTrfCmdSize {
193     /// 256 bytes transfer.
194     #[default]
195     Size256B = 0x6,
196 }
197 impl_from_enum_to_u8!(DmaTrfCmdSize);
198 
199 // TODO[FPRI]: replace with `FromPrimitive`.
200 impl TryFrom<u8> for DmaTrfCmdSize {
201     type Error = Error;
202 
203     fn try_from(value: u8) -> Result<Self> {
204         match value {
205             0x6 => Ok(Self::Size256B),
206             _ => Err(EINVAL),
207         }
208     }
209 }
210 
211 /// Currently active core on a dual falcon/riscv (Peregrine) controller.
212 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
213 pub(crate) enum PeregrineCoreSelect {
214     /// Falcon core is active.
215     #[default]
216     Falcon = 0,
217     /// RISC-V core is active.
218     Riscv = 1,
219 }
220 
221 impl From<bool> for PeregrineCoreSelect {
222     fn from(value: bool) -> Self {
223         match value {
224             false => PeregrineCoreSelect::Falcon,
225             true => PeregrineCoreSelect::Riscv,
226         }
227     }
228 }
229 
230 impl From<PeregrineCoreSelect> for bool {
231     fn from(value: PeregrineCoreSelect) -> Self {
232         match value {
233             PeregrineCoreSelect::Falcon => false,
234             PeregrineCoreSelect::Riscv => true,
235         }
236     }
237 }
238 
239 /// Different types of memory present in a falcon core.
240 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
241 pub(crate) enum FalconMem {
242     /// Secure Instruction Memory.
243     ImemSecure,
244     /// Non-Secure Instruction Memory.
245     #[expect(unused)]
246     ImemNonSecure,
247     /// Data Memory.
248     Dmem,
249 }
250 
251 /// Defines the Framebuffer Interface (FBIF) aperture type.
252 /// This determines the memory type for external memory access during a DMA transfer, which is
253 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
254 #[derive(Debug, Clone, Default)]
255 pub(crate) enum FalconFbifTarget {
256     /// VRAM.
257     #[default]
258     /// Local Framebuffer (GPU's VRAM memory).
259     LocalFb = 0,
260     /// Coherent system memory (System DRAM).
261     CoherentSysmem = 1,
262     /// Non-coherent system memory (System DRAM).
263     NoncoherentSysmem = 2,
264 }
265 impl_from_enum_to_u8!(FalconFbifTarget);
266 
267 // TODO[FPRI]: replace with `FromPrimitive`.
268 impl TryFrom<u8> for FalconFbifTarget {
269     type Error = Error;
270 
271     fn try_from(value: u8) -> Result<Self> {
272         let res = match value {
273             0 => Self::LocalFb,
274             1 => Self::CoherentSysmem,
275             2 => Self::NoncoherentSysmem,
276             _ => return Err(EINVAL),
277         };
278 
279         Ok(res)
280     }
281 }
282 
283 /// Type of memory addresses to use.
284 #[derive(Debug, Clone, Default)]
285 pub(crate) enum FalconFbifMemType {
286     /// Virtual memory addresses.
287     #[default]
288     Virtual = 0,
289     /// Physical memory addresses.
290     Physical = 1,
291 }
292 
293 /// Conversion from a single-bit register field.
294 impl From<bool> for FalconFbifMemType {
295     fn from(value: bool) -> Self {
296         match value {
297             false => Self::Virtual,
298             true => Self::Physical,
299         }
300     }
301 }
302 
303 impl From<FalconFbifMemType> for bool {
304     fn from(value: FalconFbifMemType) -> Self {
305         match value {
306             FalconFbifMemType::Virtual => false,
307             FalconFbifMemType::Physical => true,
308         }
309     }
310 }
311 
312 /// Type used to represent the `PFALCON` registers address base for a given falcon engine.
313 pub(crate) struct PFalconBase(());
314 
315 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
316 pub(crate) struct PFalcon2Base(());
317 
318 /// Trait defining the parameters of a given Falcon engine.
319 ///
320 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used
321 /// to identify a given Falcon instance with register I/O methods.
322 pub(crate) trait FalconEngine:
323     Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized
324 {
325     /// Singleton of the engine, used to identify it with register I/O methods.
326     const ID: Self;
327 }
328 
329 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
330 #[derive(Debug, Clone)]
331 pub(crate) struct FalconLoadTarget {
332     /// Offset from the start of the source object to copy from.
333     pub(crate) src_start: u32,
334     /// Offset from the start of the destination memory to copy into.
335     pub(crate) dst_start: u32,
336     /// Number of bytes to copy.
337     pub(crate) len: u32,
338 }
339 
340 /// Parameters for the falcon boot ROM.
341 #[derive(Debug, Clone)]
342 pub(crate) struct FalconBromParams {
343     /// Offset in `DMEM`` of the firmware's signature.
344     pub(crate) pkc_data_offset: u32,
345     /// Mask of engines valid for this firmware.
346     pub(crate) engine_id_mask: u16,
347     /// ID of the ucode used to infer a fuse register to validate the signature.
348     pub(crate) ucode_id: u8,
349 }
350 
351 /// Trait for providing load parameters of falcon firmwares.
352 pub(crate) trait FalconLoadParams {
353     /// Returns the load parameters for Secure `IMEM`.
354     fn imem_sec_load_params(&self) -> FalconLoadTarget;
355 
356     /// Returns the load parameters for Non-Secure `IMEM`,
357     /// used only on Turing and GA100.
358     fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>;
359 
360     /// Returns the load parameters for `DMEM`.
361     fn dmem_load_params(&self) -> FalconLoadTarget;
362 
363     /// Returns the parameters to write into the BROM registers.
364     fn brom_params(&self) -> FalconBromParams;
365 
366     /// Returns the start address of the firmware.
367     fn boot_addr(&self) -> u32;
368 }
369 
370 /// Trait for a falcon firmware.
371 ///
372 /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
373 /// object.
374 pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
375     /// Engine on which this firmware is to be loaded.
376     type Target: FalconEngine;
377 }
378 
379 /// Contains the base parameters common to all Falcon instances.
380 pub(crate) struct Falcon<E: FalconEngine> {
381     hal: KBox<dyn FalconHal<E>>,
382     dev: ARef<device::Device>,
383 }
384 
385 impl<E: FalconEngine + 'static> Falcon<E> {
386     /// Create a new falcon instance.
387     pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> {
388         Ok(Self {
389             hal: hal::falcon_hal(chipset)?,
390             dev: dev.into(),
391         })
392     }
393 
394     /// Resets DMA-related registers.
395     pub(crate) fn dma_reset(&self, bar: &Bar0) {
396         regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
397         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
398     }
399 
400     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
401     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
402         self.hal.reset_eng(bar)?;
403         self.hal.select_core(self, bar)?;
404         self.hal.reset_wait_mem_scrubbing(bar)?;
405 
406         regs::NV_PFALCON_FALCON_RM::default()
407             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
408             .write(bar, &E::ID);
409 
410         Ok(())
411     }
412 
413     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
414     /// `target_mem`.
415     ///
416     /// `sec` is set if the loaded firmware is expected to run in secure mode.
417     fn dma_wr<F: FalconFirmware<Target = E>>(
418         &self,
419         bar: &Bar0,
420         fw: &F,
421         target_mem: FalconMem,
422         load_offsets: FalconLoadTarget,
423     ) -> Result {
424         const DMA_LEN: u32 = 256;
425 
426         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
427         // code addresses in the firmware (and the boot vector) are virtual.
428         //
429         // For DMEM we can fold the start offset into the DMA handle.
430         let (src_start, dma_start) = match target_mem {
431             FalconMem::ImemSecure | FalconMem::ImemNonSecure => {
432                 (load_offsets.src_start, fw.dma_handle())
433             }
434             FalconMem::Dmem => (
435                 0,
436                 fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?,
437             ),
438         };
439         if dma_start % DmaAddress::from(DMA_LEN) > 0 {
440             dev_err!(
441                 self.dev,
442                 "DMA transfer start addresses must be a multiple of {}\n",
443                 DMA_LEN
444             );
445             return Err(EINVAL);
446         }
447 
448         // The DMATRFBASE/1 register pair only supports a 49-bit address.
449         if dma_start > DmaMask::new::<49>().value() {
450             dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start);
451             return Err(ERANGE);
452         }
453 
454         // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we
455         // need to perform.
456         let num_transfers = load_offsets.len.div_ceil(DMA_LEN);
457 
458         // Check that the area we are about to transfer is within the bounds of the DMA object.
459         // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`.
460         match num_transfers
461             .checked_mul(DMA_LEN)
462             .and_then(|size| size.checked_add(load_offsets.src_start))
463         {
464             None => {
465                 dev_err!(self.dev, "DMA transfer length overflow\n");
466                 return Err(EOVERFLOW);
467             }
468             Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => {
469                 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n");
470                 return Err(EINVAL);
471             }
472             Some(_) => (),
473         };
474 
475         // Set up the base source DMA address.
476 
477         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
478             // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which
479             // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
480             .set_base((dma_start >> 8) as u32)
481             .write(bar, &E::ID);
482         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
483             // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit
484             // within a `u16`.
485             .set_base((dma_start >> 40) as u16)
486             .write(bar, &E::ID);
487 
488         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
489             .set_size(DmaTrfCmdSize::Size256B)
490             .with_falcon_mem(target_mem);
491 
492         for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
493             // Perform a transfer of size `DMA_LEN`.
494             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
495                 .set_offs(load_offsets.dst_start + pos)
496                 .write(bar, &E::ID);
497             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
498                 .set_offs(src_start + pos)
499                 .write(bar, &E::ID);
500             cmd.write(bar, &E::ID);
501 
502             // Wait for the transfer to complete.
503             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
504             // should ever take that long.
505             read_poll_timeout(
506                 || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)),
507                 |r| r.idle(),
508                 Delta::ZERO,
509                 Delta::from_secs(2),
510             )?;
511         }
512 
513         Ok(())
514     }
515 
516     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
517     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
518         // The Non-Secure section only exists on firmware used by Turing and GA100, and
519         // those platforms do not use DMA.
520         if fw.imem_ns_load_params().is_some() {
521             debug_assert!(false);
522             return Err(EINVAL);
523         }
524 
525         self.dma_reset(bar);
526         regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| {
527             v.set_target(FalconFbifTarget::CoherentSysmem)
528                 .set_mem_type(FalconFbifMemType::Physical)
529         });
530 
531         self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?;
532         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?;
533 
534         self.hal.program_brom(self, bar, &fw.brom_params())?;
535 
536         // Set `BootVec` to start of non-secure code.
537         regs::NV_PFALCON_FALCON_BOOTVEC::default()
538             .set_value(fw.boot_addr())
539             .write(bar, &E::ID);
540 
541         Ok(())
542     }
543 
544     /// Wait until the falcon CPU is halted.
545     pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> {
546         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
547         read_poll_timeout(
548             || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)),
549             |r| r.halted(),
550             Delta::ZERO,
551             Delta::from_secs(2),
552         )?;
553 
554         Ok(())
555     }
556 
557     /// Start the falcon CPU.
558     pub(crate) fn start(&self, bar: &Bar0) -> Result<()> {
559         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() {
560             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
561                 .set_startcpu(true)
562                 .write(bar, &E::ID),
563             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
564                 .set_startcpu(true)
565                 .write(bar, &E::ID),
566         }
567 
568         Ok(())
569     }
570 
571     /// Writes values to the mailbox registers if provided.
572     pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) {
573         if let Some(mbox0) = mbox0 {
574             regs::NV_PFALCON_FALCON_MAILBOX0::default()
575                 .set_value(mbox0)
576                 .write(bar, &E::ID);
577         }
578 
579         if let Some(mbox1) = mbox1 {
580             regs::NV_PFALCON_FALCON_MAILBOX1::default()
581                 .set_value(mbox1)
582                 .write(bar, &E::ID);
583         }
584     }
585 
586     /// Reads the value from `mbox0` register.
587     pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 {
588         regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value()
589     }
590 
591     /// Reads the value from `mbox1` register.
592     pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 {
593         regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value()
594     }
595 
596     /// Reads values from both mailbox registers.
597     pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) {
598         let mbox0 = self.read_mailbox0(bar);
599         let mbox1 = self.read_mailbox1(bar);
600 
601         (mbox0, mbox1)
602     }
603 
604     /// Start running the loaded firmware.
605     ///
606     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
607     /// prior to running.
608     ///
609     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
610     /// the `MBOX0` and `MBOX1` registers.
611     pub(crate) fn boot(
612         &self,
613         bar: &Bar0,
614         mbox0: Option<u32>,
615         mbox1: Option<u32>,
616     ) -> Result<(u32, u32)> {
617         self.write_mailboxes(bar, mbox0, mbox1);
618         self.start(bar)?;
619         self.wait_till_halted(bar)?;
620         Ok(self.read_mailboxes(bar))
621     }
622 
623     /// Returns the fused version of the signature to use in order to run a HS firmware on this
624     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
625     pub(crate) fn signature_reg_fuse_version(
626         &self,
627         bar: &Bar0,
628         engine_id_mask: u16,
629         ucode_id: u8,
630     ) -> Result<u32> {
631         self.hal
632             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
633     }
634 
635     /// Check if the RISC-V core is active.
636     ///
637     /// Returns `true` if the RISC-V core is active, `false` otherwise.
638     pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
639         self.hal.is_riscv_active(bar)
640     }
641 
642     /// Write the application version to the OS register.
643     pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) {
644         regs::NV_PFALCON_FALCON_OS::default()
645             .set_value(app_version)
646             .write(bar, &E::ID);
647     }
648 }
649