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