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