Skip to content

Commit 69f5cd6

Browse files
GnurouDanilo Krummrich
authored andcommitted
gpu: nova-core: add falcon register definitions and base code
Booting the GSP on Ampere requires an intricate dance between the GSP and SEC2 falcons, where the GSP starts by running the FWSEC firmware to create the WPR2 region , and then SEC2 loads the actual RISC-V firmware into the GSP. Add the common Falcon code and HAL for Ampere GPUs, and instantiate the GSP and SEC2 Falcons that will be required to perform that dance and boot the GSP. Thanks to Ben Skeggs for pointing out an important bug in the memory scrubbing code that could lead to a race condition and ultimately a failure to boot the GSP! Reviewed-by: Lyude Paul <lyude@redhat.com> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://lore.kernel.org/r/20250619-nova-frts-v6-15-ecf41ef99252@nvidia.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 6554ad6 commit 69f5cd6

8 files changed

Lines changed: 916 additions & 0 deletions

File tree

drivers/gpu/nova-core/falcon.rs

Lines changed: 551 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use crate::{
4+
driver::Bar0,
5+
falcon::{Falcon, FalconEngine},
6+
regs,
7+
};
8+
9+
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
10+
pub(crate) struct Gsp(());
11+
12+
impl FalconEngine for Gsp {
13+
const BASE: usize = 0x00110000;
14+
}
15+
16+
impl Falcon<Gsp> {
17+
/// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
18+
/// allow GSP to signal CPU for processing new messages in message queue.
19+
pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) {
20+
regs::NV_PFALCON_FALCON_IRQSCLR::default()
21+
.set_swgen0(true)
22+
.write(bar, Gsp::BASE);
23+
}
24+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::prelude::*;
4+
5+
use crate::driver::Bar0;
6+
use crate::falcon::{Falcon, FalconBromParams, FalconEngine};
7+
use crate::gpu::Chipset;
8+
9+
mod ga102;
10+
11+
/// Hardware Abstraction Layer for Falcon cores.
12+
///
13+
/// Implements chipset-specific low-level operations. The trait is generic against [`FalconEngine`]
14+
/// so its `BASE` parameter can be used in order to avoid runtime bound checks when accessing
15+
/// registers.
16+
pub(crate) trait FalconHal<E: FalconEngine>: Sync {
17+
/// Activates the Falcon core if the engine is a risvc/falcon dual engine.
18+
fn select_core(&self, _falcon: &Falcon<E>, _bar: &Bar0) -> Result {
19+
Ok(())
20+
}
21+
22+
/// Returns the fused version of the signature to use in order to run a HS firmware on this
23+
/// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
24+
fn signature_reg_fuse_version(
25+
&self,
26+
falcon: &Falcon<E>,
27+
bar: &Bar0,
28+
engine_id_mask: u16,
29+
ucode_id: u8,
30+
) -> Result<u32>;
31+
32+
/// Program the boot ROM registers prior to starting a secure firmware.
33+
fn program_brom(&self, falcon: &Falcon<E>, bar: &Bar0, params: &FalconBromParams) -> Result;
34+
}
35+
36+
/// Returns a boxed falcon HAL adequate for `chipset`.
37+
///
38+
/// We use a heap-allocated trait object instead of a statically defined one because the
39+
/// generic `FalconEngine` argument makes it difficult to define all the combinations
40+
/// statically.
41+
pub(super) fn falcon_hal<E: FalconEngine + 'static>(
42+
chipset: Chipset,
43+
) -> Result<KBox<dyn FalconHal<E>>> {
44+
use Chipset::*;
45+
46+
let hal = match chipset {
47+
GA102 | GA103 | GA104 | GA106 | GA107 => {
48+
KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
49+
}
50+
_ => return Err(ENOTSUPP),
51+
};
52+
53+
Ok(hal)
54+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use core::marker::PhantomData;
4+
use core::time::Duration;
5+
6+
use kernel::device;
7+
use kernel::prelude::*;
8+
9+
use crate::driver::Bar0;
10+
use crate::falcon::{
11+
Falcon, FalconBromParams, FalconEngine, FalconModSelAlgo, PeregrineCoreSelect,
12+
};
13+
use crate::regs;
14+
use crate::util;
15+
16+
use super::FalconHal;
17+
18+
fn select_core_ga102<E: FalconEngine>(bar: &Bar0) -> Result {
19+
let bcr_ctrl = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
20+
if bcr_ctrl.core_select() != PeregrineCoreSelect::Falcon {
21+
regs::NV_PRISCV_RISCV_BCR_CTRL::default()
22+
.set_core_select(PeregrineCoreSelect::Falcon)
23+
.write(bar, E::BASE);
24+
25+
// TIMEOUT: falcon core should take less than 10ms to report being enabled.
26+
util::wait_on(Duration::from_millis(10), || {
27+
let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
28+
if r.valid() {
29+
Some(())
30+
} else {
31+
None
32+
}
33+
})?;
34+
}
35+
36+
Ok(())
37+
}
38+
39+
fn signature_reg_fuse_version_ga102(
40+
dev: &device::Device,
41+
bar: &Bar0,
42+
engine_id_mask: u16,
43+
ucode_id: u8,
44+
) -> Result<u32> {
45+
// TODO: The ucode fuse versions are contained in the FUSE_OPT_FPF_<ENGINE>_UCODE<X>_VERSION
46+
// registers, which are an array. Our register definition macros do not allow us to manage them
47+
// properly, so we need to hardcode their addresses for now. Clean this up once we support
48+
// register arrays.
49+
50+
// Each engine has 16 ucode version registers numbered from 1 to 16.
51+
if ucode_id == 0 || ucode_id > 16 {
52+
dev_err!(dev, "invalid ucode id {:#x}", ucode_id);
53+
return Err(EINVAL);
54+
}
55+
56+
// Base address of the FUSE registers array corresponding to the engine.
57+
let reg_fuse_base = if engine_id_mask & 0x0001 != 0 {
58+
regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::OFFSET
59+
} else if engine_id_mask & 0x0004 != 0 {
60+
regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::OFFSET
61+
} else if engine_id_mask & 0x0400 != 0 {
62+
regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::OFFSET
63+
} else {
64+
dev_err!(dev, "unexpected engine_id_mask {:#x}", engine_id_mask);
65+
return Err(EINVAL);
66+
};
67+
68+
// Read `reg_fuse_base[ucode_id - 1]`.
69+
let reg_fuse_version =
70+
bar.read32(reg_fuse_base + ((ucode_id - 1) as usize * core::mem::size_of::<u32>()));
71+
72+
// TODO: replace with `last_set_bit` once it lands.
73+
Ok(u32::BITS - reg_fuse_version.leading_zeros())
74+
}
75+
76+
fn program_brom_ga102<E: FalconEngine>(bar: &Bar0, params: &FalconBromParams) -> Result {
77+
regs::NV_PFALCON2_FALCON_BROM_PARAADDR::default()
78+
.set_value(params.pkc_data_offset)
79+
.write(bar, E::BASE);
80+
regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::default()
81+
.set_value(params.engine_id_mask as u32)
82+
.write(bar, E::BASE);
83+
regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::default()
84+
.set_ucode_id(params.ucode_id)
85+
.write(bar, E::BASE);
86+
regs::NV_PFALCON2_FALCON_MOD_SEL::default()
87+
.set_algo(FalconModSelAlgo::Rsa3k)
88+
.write(bar, E::BASE);
89+
90+
Ok(())
91+
}
92+
93+
pub(super) struct Ga102<E: FalconEngine>(PhantomData<E>);
94+
95+
impl<E: FalconEngine> Ga102<E> {
96+
pub(super) fn new() -> Self {
97+
Self(PhantomData)
98+
}
99+
}
100+
101+
impl<E: FalconEngine> FalconHal<E> for Ga102<E> {
102+
fn select_core(&self, _falcon: &Falcon<E>, bar: &Bar0) -> Result {
103+
select_core_ga102::<E>(bar)
104+
}
105+
106+
fn signature_reg_fuse_version(
107+
&self,
108+
falcon: &Falcon<E>,
109+
bar: &Bar0,
110+
engine_id_mask: u16,
111+
ucode_id: u8,
112+
) -> Result<u32> {
113+
signature_reg_fuse_version_ga102(&falcon.dev, bar, engine_id_mask, ucode_id)
114+
}
115+
116+
fn program_brom(&self, _falcon: &Falcon<E>, bar: &Bar0, params: &FalconBromParams) -> Result {
117+
program_brom_ga102::<E>(bar, params)
118+
}
119+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use crate::falcon::FalconEngine;
4+
5+
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
6+
pub(crate) struct Sec2(());
7+
8+
impl FalconEngine for Sec2 {
9+
const BASE: usize = 0x00840000;
10+
}

drivers/gpu/nova-core/gpu.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use kernel::{device, devres::Devres, error::code::*, pci, prelude::*};
44

55
use crate::driver::Bar0;
6+
use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
67
use crate::fb::SysmemFlush;
78
use crate::firmware::{Firmware, FIRMWARE_VERSION};
89
use crate::gfw;
@@ -203,6 +204,16 @@ impl Gpu {
203204
// System memory page required for sysmembar to properly flush into system memory.
204205
let sysmem_flush = SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?;
205206

207+
let gsp_falcon = Falcon::<Gsp>::new(
208+
pdev.as_ref(),
209+
spec.chipset,
210+
bar,
211+
spec.chipset > Chipset::GA100,
212+
)?;
213+
gsp_falcon.clear_swgen0_intr(bar);
214+
215+
let _sec2_falcon = Falcon::<Sec2>::new(pdev.as_ref(), spec.chipset, bar, true)?;
216+
206217
Ok(pin_init!(Self {
207218
spec,
208219
bar: devres_bar,

drivers/gpu/nova-core/nova_core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
mod dma;
66
mod driver;
7+
mod falcon;
78
mod fb;
89
mod firmware;
910
mod gfw;

0 commit comments

Comments
 (0)