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