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