xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 7937dca770393d34d1ad217580c5446d2e45417f)
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::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_u32 {
26     ($enum_type:ty) => {
27         impl From<$enum_type> for u32 {
28             fn from(value: $enum_type) -> Self {
29                 value as u32
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_u32!(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_u32!(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_u32!(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_u32!(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_u32!(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 impl_from_enum_to_u32!(PeregrineCoreSelect);
206 
207 impl From<bool> for PeregrineCoreSelect {
208     fn from(value: bool) -> Self {
209         match value {
210             false => PeregrineCoreSelect::Falcon,
211             true => PeregrineCoreSelect::Riscv,
212         }
213     }
214 }
215 
216 /// Different types of memory present in a falcon core.
217 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
218 pub(crate) enum FalconMem {
219     /// Instruction Memory.
220     Imem,
221     /// Data Memory.
222     Dmem,
223 }
224 
225 /// Defines the Framebuffer Interface (FBIF) aperture type.
226 /// This determines the memory type for external memory access during a DMA transfer, which is
227 /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
228 #[derive(Debug, Clone, Default)]
229 pub(crate) enum FalconFbifTarget {
230     /// VRAM.
231     #[default]
232     /// Local Framebuffer (GPU's VRAM memory).
233     LocalFb = 0,
234     /// Coherent system memory (System DRAM).
235     CoherentSysmem = 1,
236     /// Non-coherent system memory (System DRAM).
237     NoncoherentSysmem = 2,
238 }
239 impl_from_enum_to_u32!(FalconFbifTarget);
240 
241 // TODO[FPRI]: replace with `FromPrimitive`.
242 impl TryFrom<u8> for FalconFbifTarget {
243     type Error = Error;
244 
245     fn try_from(value: u8) -> Result<Self> {
246         let res = match value {
247             0 => Self::LocalFb,
248             1 => Self::CoherentSysmem,
249             2 => Self::NoncoherentSysmem,
250             _ => return Err(EINVAL),
251         };
252 
253         Ok(res)
254     }
255 }
256 
257 /// Type of memory addresses to use.
258 #[derive(Debug, Clone, Default)]
259 pub(crate) enum FalconFbifMemType {
260     /// Virtual memory addresses.
261     #[default]
262     Virtual = 0,
263     /// Physical memory addresses.
264     Physical = 1,
265 }
266 impl_from_enum_to_u32!(FalconFbifMemType);
267 
268 /// Conversion from a single-bit register field.
269 impl From<bool> for FalconFbifMemType {
270     fn from(value: bool) -> Self {
271         match value {
272             false => Self::Virtual,
273             true => Self::Physical,
274         }
275     }
276 }
277 
278 /// Type used to represent the `PFALCON` registers address base for a given falcon engine.
279 pub(crate) struct PFalconBase(());
280 
281 /// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
282 pub(crate) struct PFalcon2Base(());
283 
284 /// Trait defining the parameters of a given Falcon engine.
285 ///
286 /// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The `ID` constant is used
287 /// to identify a given Falcon instance with register I/O methods.
288 pub(crate) trait FalconEngine:
289     Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized
290 {
291     /// Singleton of the engine, used to identify it with register I/O methods.
292     const ID: Self;
293 }
294 
295 /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
296 #[derive(Debug)]
297 pub(crate) struct FalconLoadTarget {
298     /// Offset from the start of the source object to copy from.
299     pub(crate) src_start: u32,
300     /// Offset from the start of the destination memory to copy into.
301     pub(crate) dst_start: u32,
302     /// Number of bytes to copy.
303     pub(crate) len: u32,
304 }
305 
306 /// Parameters for the falcon boot ROM.
307 #[derive(Debug)]
308 pub(crate) struct FalconBromParams {
309     /// Offset in `DMEM`` of the firmware's signature.
310     pub(crate) pkc_data_offset: u32,
311     /// Mask of engines valid for this firmware.
312     pub(crate) engine_id_mask: u16,
313     /// ID of the ucode used to infer a fuse register to validate the signature.
314     pub(crate) ucode_id: u8,
315 }
316 
317 /// Trait for providing load parameters of falcon firmwares.
318 pub(crate) trait FalconLoadParams {
319     /// Returns the load parameters for `IMEM`.
320     fn imem_load_params(&self) -> FalconLoadTarget;
321 
322     /// Returns the load parameters for `DMEM`.
323     fn dmem_load_params(&self) -> FalconLoadTarget;
324 
325     /// Returns the parameters to write into the BROM registers.
326     fn brom_params(&self) -> FalconBromParams;
327 
328     /// Returns the start address of the firmware.
329     fn boot_addr(&self) -> u32;
330 }
331 
332 /// Trait for a falcon firmware.
333 ///
334 /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
335 /// object.
336 pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
337     /// Engine on which this firmware is to be loaded.
338     type Target: FalconEngine;
339 }
340 
341 /// Contains the base parameters common to all Falcon instances.
342 pub(crate) struct Falcon<E: FalconEngine> {
343     hal: KBox<dyn FalconHal<E>>,
344     dev: ARef<device::Device>,
345 }
346 
347 impl<E: FalconEngine + 'static> Falcon<E> {
348     /// Create a new falcon instance.
349     ///
350     /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
351     /// controller.
352     pub(crate) fn new(
353         dev: &device::Device,
354         chipset: Chipset,
355         bar: &Bar0,
356         need_riscv: bool,
357     ) -> Result<Self> {
358         let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, &E::ID);
359         // Check that the revision and security model contain valid values.
360         let _ = hwcfg1.core_rev()?;
361         let _ = hwcfg1.security_model()?;
362 
363         if need_riscv {
364             let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
365             if !hwcfg2.riscv() {
366                 dev_err!(
367                     dev,
368                     "riscv support requested on a controller that does not support it\n"
369                 );
370                 return Err(EINVAL);
371             }
372         }
373 
374         Ok(Self {
375             hal: hal::falcon_hal(chipset)?,
376             dev: dev.into(),
377         })
378     }
379 
380     /// Wait for memory scrubbing to complete.
381     fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
382         // TIMEOUT: memory scrubbing should complete in less than 20ms.
383         util::wait_on(Delta::from_millis(20), || {
384             if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID).mem_scrubbing_done() {
385                 Some(())
386             } else {
387                 None
388             }
389         })
390     }
391 
392     /// Reset the falcon engine.
393     fn reset_eng(&self, bar: &Bar0) -> Result {
394         let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
395 
396         // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
397         // RESET_READY so a non-failing timeout is used.
398         let _ = util::wait_on(Delta::from_micros(150), || {
399             let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
400             if r.reset_ready() {
401                 Some(())
402             } else {
403                 None
404             }
405         });
406 
407         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(true));
408 
409         // TODO[DLAY]: replace with udelay() or equivalent once available.
410         // TIMEOUT: falcon engine should not take more than 10us to reset.
411         let _: Result = util::wait_on(Delta::from_micros(10), || None);
412 
413         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(false));
414 
415         self.reset_wait_mem_scrubbing(bar)?;
416 
417         Ok(())
418     }
419 
420     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
421     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
422         self.reset_eng(bar)?;
423         self.hal.select_core(self, bar)?;
424         self.reset_wait_mem_scrubbing(bar)?;
425 
426         regs::NV_PFALCON_FALCON_RM::default()
427             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
428             .write(bar, &E::ID);
429 
430         Ok(())
431     }
432 
433     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
434     /// `target_mem`.
435     ///
436     /// `sec` is set if the loaded firmware is expected to run in secure mode.
437     fn dma_wr<F: FalconFirmware<Target = E>>(
438         &self,
439         bar: &Bar0,
440         fw: &F,
441         target_mem: FalconMem,
442         load_offsets: FalconLoadTarget,
443         sec: bool,
444     ) -> Result {
445         const DMA_LEN: u32 = 256;
446 
447         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
448         // code addresses in the firmware (and the boot vector) are virtual.
449         //
450         // For DMEM we can fold the start offset into the DMA handle.
451         let (src_start, dma_start) = match target_mem {
452             FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
453             FalconMem::Dmem => (
454                 0,
455                 fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
456             ),
457         };
458         if dma_start % bindings::dma_addr_t::from(DMA_LEN) > 0 {
459             dev_err!(
460                 self.dev,
461                 "DMA transfer start addresses must be a multiple of {}",
462                 DMA_LEN
463             );
464             return Err(EINVAL);
465         }
466 
467         // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we
468         // need to perform.
469         let num_transfers = load_offsets.len.div_ceil(DMA_LEN);
470 
471         // Check that the area we are about to transfer is within the bounds of the DMA object.
472         // Upper limit of transfer is `(num_transfers * DMA_LEN) + load_offsets.src_start`.
473         match num_transfers
474             .checked_mul(DMA_LEN)
475             .and_then(|size| size.checked_add(load_offsets.src_start))
476         {
477             None => {
478                 dev_err!(self.dev, "DMA transfer length overflow");
479                 return Err(EOVERFLOW);
480             }
481             Some(upper_bound) if upper_bound as usize > fw.size() => {
482                 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object");
483                 return Err(EINVAL);
484             }
485             Some(_) => (),
486         };
487 
488         // Set up the base source DMA address.
489 
490         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
491             .set_base((dma_start >> 8) as u32)
492             .write(bar, &E::ID);
493         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
494             .set_base((dma_start >> 40) as u16)
495             .write(bar, &E::ID);
496 
497         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
498             .set_size(DmaTrfCmdSize::Size256B)
499             .set_imem(target_mem == FalconMem::Imem)
500             .set_sec(if sec { 1 } else { 0 });
501 
502         for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
503             // Perform a transfer of size `DMA_LEN`.
504             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
505                 .set_offs(load_offsets.dst_start + pos)
506                 .write(bar, &E::ID);
507             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
508                 .set_offs(src_start + pos)
509                 .write(bar, &E::ID);
510             cmd.write(bar, &E::ID);
511 
512             // Wait for the transfer to complete.
513             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
514             // should ever take that long.
515             util::wait_on(Delta::from_secs(2), || {
516                 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID);
517                 if r.idle() {
518                     Some(())
519                 } else {
520                     None
521                 }
522             })?;
523         }
524 
525         Ok(())
526     }
527 
528     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
529     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
530         regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
531         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
532         regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| {
533             v.set_target(FalconFbifTarget::CoherentSysmem)
534                 .set_mem_type(FalconFbifMemType::Physical)
535         });
536 
537         self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
538         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
539 
540         self.hal.program_brom(self, bar, &fw.brom_params())?;
541 
542         // Set `BootVec` to start of non-secure code.
543         regs::NV_PFALCON_FALCON_BOOTVEC::default()
544             .set_value(fw.boot_addr())
545             .write(bar, &E::ID);
546 
547         Ok(())
548     }
549 
550     /// Runs the loaded firmware and waits for its completion.
551     ///
552     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
553     /// prior to running.
554     ///
555     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
556     /// the `MBOX0` and `MBOX1` registers.
557     pub(crate) fn boot(
558         &self,
559         bar: &Bar0,
560         mbox0: Option<u32>,
561         mbox1: Option<u32>,
562     ) -> Result<(u32, u32)> {
563         if let Some(mbox0) = mbox0 {
564             regs::NV_PFALCON_FALCON_MAILBOX0::default()
565                 .set_value(mbox0)
566                 .write(bar, &E::ID);
567         }
568 
569         if let Some(mbox1) = mbox1 {
570             regs::NV_PFALCON_FALCON_MAILBOX1::default()
571                 .set_value(mbox1)
572                 .write(bar, &E::ID);
573         }
574 
575         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() {
576             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
577                 .set_startcpu(true)
578                 .write(bar, &E::ID),
579             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
580                 .set_startcpu(true)
581                 .write(bar, &E::ID),
582         }
583 
584         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
585         util::wait_on(Delta::from_secs(2), || {
586             let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID);
587             if r.halted() {
588                 Some(())
589             } else {
590                 None
591             }
592         })?;
593 
594         let (mbox0, mbox1) = (
595             regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(),
596             regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(),
597         );
598 
599         Ok((mbox0, mbox1))
600     }
601 
602     /// Returns the fused version of the signature to use in order to run a HS firmware on this
603     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
604     pub(crate) fn signature_reg_fuse_version(
605         &self,
606         bar: &Bar0,
607         engine_id_mask: u16,
608         ucode_id: u8,
609     ) -> Result<u32> {
610         self.hal
611             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
612     }
613 }
614