Skip to content

Commit 54e6baf

Browse files
author
Danilo Krummrich
committed
gpu: nova-core: add initial driver stub
Add the initial nova-core driver stub. nova-core is intended to serve as a common base for nova-drm (the corresponding DRM driver) and the vGPU manager VFIO driver, serving as a hard- and firmware abstraction layer for GSP-based NVIDIA GPUs. The Nova project, including nova-core and nova-drm, in the long term, is intended to serve as the successor of Nouveau for all GSP-based GPUs. The motivation for both, starting a successor project for Nouveau and doing so using the Rust programming language, is documented in detail through a previous post on the mailing list [1], an LWN article [2] and a talk from LPC '24. In order to avoid the chicken and egg problem to require a user to upstream Rust abstractions, but at the same time require the Rust abstractions to implement the driver, nova-core kicks off as a driver stub and is subsequently developed upstream. Link: https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u [1] Link: https://lwn.net/Articles/990736/ [2] Link: https://youtu.be/3Igmx28B3BQ?si=sBdSEer4tAPKGpOs [3] Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://lore.kernel.org/r/20250306222336.23482-5-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 1d121a3 commit 54e6baf

File tree

11 files changed

+416
-0
lines changed

11 files changed

+416
-0
lines changed

MAINTAINERS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7449,6 +7449,16 @@ T: git https://gitlab.freedesktop.org/drm/nouveau.git
74497449
F: drivers/gpu/drm/nouveau/
74507450
F: include/uapi/drm/nouveau_drm.h
74517451

7452+
CORE DRIVER FOR NVIDIA GPUS [RUST]
7453+
M: Danilo Krummrich <dakr@kernel.org>
7454+
L: nouveau@lists.freedesktop.org
7455+
S: Supported
7456+
Q: https://patchwork.freedesktop.org/project/nouveau/
7457+
B: https://gitlab.freedesktop.org/drm/nova/-/issues
7458+
C: irc://irc.oftc.net/nouveau
7459+
T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
7460+
F: drivers/gpu/nova-core/
7461+
74527462
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
74537463
M: Stefan Mavrodiev <stefan@olimex.com>
74547464
S: Maintained

drivers/gpu/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
obj-y += host1x/ drm/ vga/
66
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
77
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
8+
obj-$(CONFIG_NOVA_CORE) += nova-core/

drivers/gpu/nova-core/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
config NOVA_CORE
2+
tristate "Nova Core GPU driver"
3+
depends on PCI
4+
depends on RUST
5+
depends on RUST_FW_LOADER_ABSTRACTIONS
6+
default n
7+
help
8+
Choose this if you want to build the Nova Core driver for Nvidia
9+
GPUs based on the GPU System Processor (GSP). This is true for Turing
10+
and later GPUs.
11+
12+
This driver is work in progress and may not be functional.
13+
14+
If M is selected, the module will be called nova_core.

drivers/gpu/nova-core/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
obj-$(CONFIG_NOVA_CORE) += nova_core.o

drivers/gpu/nova-core/driver.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::{bindings, c_str, pci, prelude::*};
4+
5+
use crate::gpu::Gpu;
6+
7+
#[pin_data]
8+
pub(crate) struct NovaCore {
9+
#[pin]
10+
pub(crate) gpu: Gpu,
11+
}
12+
13+
const BAR0_SIZE: usize = 8;
14+
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
15+
16+
kernel::pci_device_table!(
17+
PCI_TABLE,
18+
MODULE_PCI_TABLE,
19+
<NovaCore as pci::Driver>::IdInfo,
20+
[(
21+
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _),
22+
()
23+
)]
24+
);
25+
26+
impl pci::Driver for NovaCore {
27+
type IdInfo = ();
28+
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
29+
30+
fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
31+
dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
32+
33+
pdev.enable_device_mem()?;
34+
pdev.set_master();
35+
36+
let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0"))?;
37+
38+
let this = KBox::pin_init(
39+
try_pin_init!(Self {
40+
gpu <- Gpu::new(pdev, bar)?,
41+
}),
42+
GFP_KERNEL,
43+
)?;
44+
45+
Ok(this)
46+
}
47+
}

drivers/gpu/nova-core/firmware.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use crate::gpu;
4+
use kernel::firmware;
5+
6+
pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
7+
8+
impl<const N: usize> ModInfoBuilder<N> {
9+
const VERSION: &'static str = "535.113.01";
10+
11+
const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
12+
ModInfoBuilder(
13+
self.0
14+
.new_entry()
15+
.push("nvidia/")
16+
.push(chipset)
17+
.push("/gsp/")
18+
.push(fw)
19+
.push("-")
20+
.push(Self::VERSION)
21+
.push(".bin"),
22+
)
23+
}
24+
25+
const fn make_entry_chipset(self, chipset: &str) -> Self {
26+
self.make_entry_file(chipset, "booter_load")
27+
.make_entry_file(chipset, "booter_unload")
28+
.make_entry_file(chipset, "bootloader")
29+
.make_entry_file(chipset, "gsp")
30+
}
31+
32+
pub(crate) const fn create(
33+
module_name: &'static kernel::str::CStr,
34+
) -> firmware::ModInfoBuilder<N> {
35+
let mut this = Self(firmware::ModInfoBuilder::new(module_name));
36+
let mut i = 0;
37+
38+
while i < gpu::Chipset::NAMES.len() {
39+
this = this.make_entry_chipset(gpu::Chipset::NAMES[i]);
40+
i += 1;
41+
}
42+
43+
this.0
44+
}
45+
}

drivers/gpu/nova-core/gpu.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::{
4+
device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString,
5+
};
6+
7+
use crate::driver::Bar0;
8+
use crate::regs;
9+
use crate::util;
10+
use core::fmt;
11+
12+
macro_rules! define_chipset {
13+
({ $($variant:ident = $value:expr),* $(,)* }) =>
14+
{
15+
/// Enum representation of the GPU chipset.
16+
#[derive(fmt::Debug)]
17+
pub(crate) enum Chipset {
18+
$($variant = $value),*,
19+
}
20+
21+
impl Chipset {
22+
pub(crate) const ALL: &'static [Chipset] = &[
23+
$( Chipset::$variant, )*
24+
];
25+
26+
pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [
27+
$( util::const_bytes_to_str(
28+
util::to_lowercase_bytes::<{ stringify!($variant).len() }>(
29+
stringify!($variant)
30+
).as_slice()
31+
), )*
32+
];
33+
}
34+
35+
// TODO replace with something like derive(FromPrimitive)
36+
impl TryFrom<u32> for Chipset {
37+
type Error = kernel::error::Error;
38+
39+
fn try_from(value: u32) -> Result<Self, Self::Error> {
40+
match value {
41+
$( $value => Ok(Chipset::$variant), )*
42+
_ => Err(ENODEV),
43+
}
44+
}
45+
}
46+
}
47+
}
48+
49+
define_chipset!({
50+
// Turing
51+
TU102 = 0x162,
52+
TU104 = 0x164,
53+
TU106 = 0x166,
54+
TU117 = 0x167,
55+
TU116 = 0x168,
56+
// Ampere
57+
GA102 = 0x172,
58+
GA103 = 0x173,
59+
GA104 = 0x174,
60+
GA106 = 0x176,
61+
GA107 = 0x177,
62+
// Ada
63+
AD102 = 0x192,
64+
AD103 = 0x193,
65+
AD104 = 0x194,
66+
AD106 = 0x196,
67+
AD107 = 0x197,
68+
});
69+
70+
impl Chipset {
71+
pub(crate) fn arch(&self) -> Architecture {
72+
match self {
73+
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
74+
Architecture::Turing
75+
}
76+
Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
77+
Architecture::Ampere
78+
}
79+
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
80+
Architecture::Ada
81+
}
82+
}
83+
}
84+
}
85+
86+
// TODO
87+
//
88+
// The resulting strings are used to generate firmware paths, hence the
89+
// generated strings have to be stable.
90+
//
91+
// Hence, replace with something like strum_macros derive(Display).
92+
//
93+
// For now, redirect to fmt::Debug for convenience.
94+
impl fmt::Display for Chipset {
95+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96+
write!(f, "{:?}", self)
97+
}
98+
}
99+
100+
/// Enum representation of the GPU generation.
101+
#[derive(fmt::Debug)]
102+
pub(crate) enum Architecture {
103+
Turing,
104+
Ampere,
105+
Ada,
106+
}
107+
108+
pub(crate) struct Revision {
109+
major: u8,
110+
minor: u8,
111+
}
112+
113+
impl Revision {
114+
fn from_boot0(boot0: regs::Boot0) -> Self {
115+
Self {
116+
major: boot0.major_rev(),
117+
minor: boot0.minor_rev(),
118+
}
119+
}
120+
}
121+
122+
impl fmt::Display for Revision {
123+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124+
write!(f, "{:x}.{:x}", self.major, self.minor)
125+
}
126+
}
127+
128+
/// Structure holding the metadata of the GPU.
129+
pub(crate) struct Spec {
130+
chipset: Chipset,
131+
/// The revision of the chipset.
132+
revision: Revision,
133+
}
134+
135+
impl Spec {
136+
fn new(bar: &Devres<Bar0>) -> Result<Spec> {
137+
let bar = bar.try_access().ok_or(ENXIO)?;
138+
let boot0 = regs::Boot0::read(&bar);
139+
140+
Ok(Self {
141+
chipset: boot0.chipset().try_into()?,
142+
revision: Revision::from_boot0(boot0),
143+
})
144+
}
145+
}
146+
147+
/// Structure encapsulating the firmware blobs required for the GPU to operate.
148+
#[expect(dead_code)]
149+
pub(crate) struct Firmware {
150+
booter_load: firmware::Firmware,
151+
booter_unload: firmware::Firmware,
152+
bootloader: firmware::Firmware,
153+
gsp: firmware::Firmware,
154+
}
155+
156+
impl Firmware {
157+
fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> {
158+
let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?;
159+
chip_name.make_ascii_lowercase();
160+
161+
let request = |name_| {
162+
CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
163+
.and_then(|path| firmware::Firmware::request(&path, dev))
164+
};
165+
166+
Ok(Firmware {
167+
booter_load: request("booter_load")?,
168+
booter_unload: request("booter_unload")?,
169+
bootloader: request("bootloader")?,
170+
gsp: request("gsp")?,
171+
})
172+
}
173+
}
174+
175+
/// Structure holding the resources required to operate the GPU.
176+
#[pin_data]
177+
pub(crate) struct Gpu {
178+
spec: Spec,
179+
/// MMIO mapping of PCI BAR 0
180+
bar: Devres<Bar0>,
181+
fw: Firmware,
182+
}
183+
184+
impl Gpu {
185+
pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> {
186+
let spec = Spec::new(&bar)?;
187+
let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
188+
189+
dev_info!(
190+
pdev.as_ref(),
191+
"NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
192+
spec.chipset,
193+
spec.chipset.arch(),
194+
spec.revision
195+
);
196+
197+
Ok(pin_init!(Self { spec, bar, fw }))
198+
}
199+
}

drivers/gpu/nova-core/nova_core.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Nova Core GPU Driver
4+
5+
mod driver;
6+
mod firmware;
7+
mod gpu;
8+
mod regs;
9+
mod util;
10+
11+
kernel::module_pci_driver! {
12+
type: driver::NovaCore,
13+
name: "NovaCore",
14+
author: "Danilo Krummrich",
15+
description: "Nova Core GPU driver",
16+
license: "GPL v2",
17+
firmware: [],
18+
}
19+
20+
kernel::module_firmware!(firmware::ModInfoBuilder);

0 commit comments

Comments
 (0)