Skip to content

Commit e7c9698

Browse files
committed
gpu: nova-core: move GSP boot code to its own module
Right now the GSP boot code is very incomplete and limited to running FRTS, so having it in `Gpu::new` is not a big constraint. However, this will change as we add more steps of the GSP boot process, and not all GPU families follow the same procedure, so having these steps in a dedicated method is the logical construct. There is also the fact the GSP will require its own runtime data, and while it won't immediately need to be pinned, we want to be ready for the time where it will - most likely when it starts using mutexes. Thus, add an empty `Gsp` type that is pinned inside `Gpu` and initialized using a pin initializer. This sets the constraint we need to observe from the start, and could spare us some costly refactoring down the road. Then, move the code related to GSP boot to the `gsp::boot` module, as part of the `Gsp` implementation. Doing so allows us to make `Gpu::new` return a fallible `impl PinInit` instead of a `Result.` This is more idiomatic when working with pinned objects, and sets up the pinned initialization pattern we want to preserve as the code grows more complex. Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/r/20250913-nova_firmware-v6-2-9007079548b0@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
1 parent f0fbbff commit e7c9698

5 files changed

Lines changed: 184 additions & 120 deletions

File tree

drivers/gpu/nova-core/driver.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,19 @@ impl pci::Driver for NovaCore {
3434
pdev.enable_device_mem()?;
3535
pdev.set_master();
3636

37-
let bar = Arc::pin_init(
37+
let devres_bar = Arc::pin_init(
3838
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
3939
GFP_KERNEL,
4040
)?;
4141

42+
// Used to provided a `&Bar0` to `Gpu::new` without tying it to the lifetime of
43+
// `devres_bar`.
44+
let bar_clone = Arc::clone(&devres_bar);
45+
let bar = bar_clone.access(pdev.as_ref())?;
46+
4247
let this = KBox::pin_init(
4348
try_pin_init!(Self {
44-
gpu <- Gpu::new(pdev, bar)?,
49+
gpu <- Gpu::new(pdev, devres_bar, bar),
4550
_reg: auxiliary::Registration::new(
4651
pdev.as_ref(),
4752
c_str!("nova-drm"),

drivers/gpu/nova-core/gpu.rs

Lines changed: 41 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc};
44

55
use crate::driver::Bar0;
6-
use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
7-
use crate::fb::FbLayout;
6+
use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon};
87
use crate::fb::SysmemFlush;
9-
use crate::firmware::fwsec::{FwsecCommand, FwsecFirmware};
108
use crate::firmware::{Firmware, FIRMWARE_VERSION};
119
use crate::gfw;
10+
use crate::gsp::Gsp;
1211
use crate::regs;
1312
use crate::util;
14-
use crate::vbios::Vbios;
1513
use core::fmt;
1614

1715
macro_rules! define_chipset {
@@ -172,133 +170,58 @@ pub(crate) struct Gpu {
172170
/// System memory page required for flushing all pending GPU-side memory writes done through
173171
/// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
174172
sysmem_flush: SysmemFlush,
173+
/// GSP falcon instance, used for GSP boot up and cleanup.
174+
gsp_falcon: Falcon<GspFalcon>,
175+
/// SEC2 falcon instance, used for GSP boot up and cleanup.
176+
sec2_falcon: Falcon<Sec2Falcon>,
177+
/// GSP runtime data. Temporarily an empty placeholder.
178+
#[pin]
179+
gsp: Gsp,
175180
}
176181

177182
impl Gpu {
178-
/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
179-
/// created the WPR2 region.
180-
///
181-
/// TODO: this needs to be moved into a larger type responsible for booting the whole GSP
182-
/// (`GspBooter`?).
183-
fn run_fwsec_frts(
184-
dev: &device::Device<device::Bound>,
185-
falcon: &Falcon<Gsp>,
186-
bar: &Bar0,
187-
bios: &Vbios,
188-
fb_layout: &FbLayout,
189-
) -> Result<()> {
190-
// Check that the WPR2 region does not already exists - if it does, we cannot run
191-
// FWSEC-FRTS until the GPU is reset.
192-
if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 {
193-
dev_err!(
194-
dev,
195-
"WPR2 region already exists - GPU needs to be reset to proceed\n"
196-
);
197-
return Err(EBUSY);
198-
}
199-
200-
let fwsec_frts = FwsecFirmware::new(
201-
dev,
202-
falcon,
203-
bar,
204-
bios,
205-
FwsecCommand::Frts {
206-
frts_addr: fb_layout.frts.start,
207-
frts_size: fb_layout.frts.end - fb_layout.frts.start,
208-
},
209-
)?;
210-
211-
// Run FWSEC-FRTS to create the WPR2 region.
212-
fwsec_frts.run(dev, falcon, bar)?;
213-
214-
// SCRATCH_E contains the error code for FWSEC-FRTS.
215-
let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
216-
if frts_status != 0 {
217-
dev_err!(
218-
dev,
219-
"FWSEC-FRTS returned with error code {:#x}",
220-
frts_status
221-
);
222-
223-
return Err(EIO);
224-
}
225-
226-
// Check that the WPR2 region has been created as we requested.
227-
let (wpr2_lo, wpr2_hi) = (
228-
regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(),
229-
regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(),
230-
);
231-
232-
match (wpr2_lo, wpr2_hi) {
233-
(_, 0) => {
234-
dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
235-
236-
Err(EIO)
237-
}
238-
(wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
239-
dev_err!(
240-
dev,
241-
"WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
242-
wpr2_lo,
243-
fb_layout.frts.start,
244-
);
245-
246-
Err(EIO)
247-
}
248-
(wpr2_lo, wpr2_hi) => {
249-
dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
250-
dev_dbg!(dev, "GPU instance built\n");
251-
252-
Ok(())
253-
}
254-
}
255-
}
256-
257-
pub(crate) fn new(
258-
pdev: &pci::Device<device::Bound>,
183+
pub(crate) fn new<'a>(
184+
pdev: &'a pci::Device<device::Bound>,
259185
devres_bar: Arc<Devres<Bar0>>,
260-
) -> Result<impl PinInit<Self>> {
261-
let bar = devres_bar.access(pdev.as_ref())?;
262-
let spec = Spec::new(bar)?;
263-
let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?;
264-
265-
dev_info!(
266-
pdev.as_ref(),
267-
"NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
268-
spec.chipset,
269-
spec.chipset.arch(),
270-
spec.revision
271-
);
186+
bar: &'a Bar0,
187+
) -> impl PinInit<Self, Error> + 'a {
188+
try_pin_init!(Self {
189+
spec: Spec::new(bar).inspect(|spec| {
190+
dev_info!(
191+
pdev.as_ref(),
192+
"NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
193+
spec.chipset,
194+
spec.chipset.arch(),
195+
spec.revision
196+
);
197+
})?,
272198

273-
// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
274-
gfw::wait_gfw_boot_completion(bar)
275-
.inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
199+
// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
200+
_: {
201+
gfw::wait_gfw_boot_completion(bar)
202+
.inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
203+
},
276204

277-
let sysmem_flush = SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?;
205+
fw <- Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?,
278206

279-
let gsp_falcon = Falcon::<Gsp>::new(
280-
pdev.as_ref(),
281-
spec.chipset,
282-
bar,
283-
spec.chipset > Chipset::GA100,
284-
)?;
285-
gsp_falcon.clear_swgen0_intr(bar);
207+
sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
286208

287-
let _sec2_falcon = Falcon::<Sec2>::new(pdev.as_ref(), spec.chipset, bar, true)?;
209+
gsp_falcon: Falcon::new(
210+
pdev.as_ref(),
211+
spec.chipset,
212+
bar,
213+
spec.chipset > Chipset::GA100,
214+
)
215+
.inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
288216

289-
let fb_layout = FbLayout::new(spec.chipset, bar)?;
290-
dev_dbg!(pdev.as_ref(), "{:#x?}\n", fb_layout);
217+
sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar, true)?,
291218

292-
let bios = Vbios::new(pdev.as_ref(), bar)?;
219+
gsp <- Gsp::new(),
293220

294-
Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
221+
_: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
295222

296-
Ok(pin_init!(Self {
297-
spec,
298223
bar: devres_bar,
299-
fw,
300-
sysmem_flush,
301-
}))
224+
})
302225
}
303226

304227
/// Called when the corresponding [`Device`](device::Device) is unbound.

drivers/gpu/nova-core/gsp.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
mod boot;
4+
5+
use kernel::prelude::*;
6+
7+
/// GSP runtime data.
8+
///
9+
/// This is an empty pinned placeholder for now.
10+
#[pin_data]
11+
pub(crate) struct Gsp {}
12+
13+
impl Gsp {
14+
pub(crate) fn new() -> impl PinInit<Self> {
15+
pin_init!(Self {})
16+
}
17+
}

drivers/gpu/nova-core/gsp/boot.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::device;
4+
use kernel::pci;
5+
use kernel::prelude::*;
6+
7+
use crate::driver::Bar0;
8+
use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
9+
use crate::fb::FbLayout;
10+
use crate::firmware::fwsec::{FwsecCommand, FwsecFirmware};
11+
use crate::gpu::Chipset;
12+
use crate::regs;
13+
use crate::vbios::Vbios;
14+
15+
impl super::Gsp {
16+
/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
17+
/// created the WPR2 region.
18+
fn run_fwsec_frts(
19+
dev: &device::Device<device::Bound>,
20+
falcon: &Falcon<Gsp>,
21+
bar: &Bar0,
22+
bios: &Vbios,
23+
fb_layout: &FbLayout,
24+
) -> Result<()> {
25+
// Check that the WPR2 region does not already exists - if it does, we cannot run
26+
// FWSEC-FRTS until the GPU is reset.
27+
if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 {
28+
dev_err!(
29+
dev,
30+
"WPR2 region already exists - GPU needs to be reset to proceed\n"
31+
);
32+
return Err(EBUSY);
33+
}
34+
35+
let fwsec_frts = FwsecFirmware::new(
36+
dev,
37+
falcon,
38+
bar,
39+
bios,
40+
FwsecCommand::Frts {
41+
frts_addr: fb_layout.frts.start,
42+
frts_size: fb_layout.frts.end - fb_layout.frts.start,
43+
},
44+
)?;
45+
46+
// Run FWSEC-FRTS to create the WPR2 region.
47+
fwsec_frts.run(dev, falcon, bar)?;
48+
49+
// SCRATCH_E contains the error code for FWSEC-FRTS.
50+
let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
51+
if frts_status != 0 {
52+
dev_err!(
53+
dev,
54+
"FWSEC-FRTS returned with error code {:#x}",
55+
frts_status
56+
);
57+
58+
return Err(EIO);
59+
}
60+
61+
// Check that the WPR2 region has been created as we requested.
62+
let (wpr2_lo, wpr2_hi) = (
63+
regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(),
64+
regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(),
65+
);
66+
67+
match (wpr2_lo, wpr2_hi) {
68+
(_, 0) => {
69+
dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
70+
71+
Err(EIO)
72+
}
73+
(wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
74+
dev_err!(
75+
dev,
76+
"WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
77+
wpr2_lo,
78+
fb_layout.frts.start,
79+
);
80+
81+
Err(EIO)
82+
}
83+
(wpr2_lo, wpr2_hi) => {
84+
dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
85+
dev_dbg!(dev, "GPU instance built\n");
86+
87+
Ok(())
88+
}
89+
}
90+
}
91+
92+
/// Attempt to boot the GSP.
93+
///
94+
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
95+
/// user-space, patching them with signatures, and building firmware-specific intricate data
96+
/// structures that the GSP will use at runtime.
97+
///
98+
/// Upon return, the GSP is up and running, and its runtime object given as return value.
99+
pub(crate) fn boot(
100+
self: Pin<&mut Self>,
101+
pdev: &pci::Device<device::Bound>,
102+
bar: &Bar0,
103+
chipset: Chipset,
104+
gsp_falcon: &Falcon<Gsp>,
105+
_sec2_falcon: &Falcon<Sec2>,
106+
) -> Result {
107+
let dev = pdev.as_ref();
108+
109+
let bios = Vbios::new(dev, bar)?;
110+
111+
let fb_layout = FbLayout::new(chipset, bar)?;
112+
dev_dbg!(dev, "{:#x?}\n", fb_layout);
113+
114+
Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
115+
116+
Ok(())
117+
}
118+
}

drivers/gpu/nova-core/nova_core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod fb;
99
mod firmware;
1010
mod gfw;
1111
mod gpu;
12+
mod gsp;
1213
mod regs;
1314
mod util;
1415
mod vbios;

0 commit comments

Comments
 (0)