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