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