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