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