John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 0/6] gpu: nova: add boot42 support for next-gen GPUs
Changes in v8: 1) Added two new patches: convert the remaining ENODEV cases to ENOTSUPP, and add dev_err!() report for some (most) cases of unsupported GPUs. 2) Corrected some comments. 3) Added Joel's Reviewed-by, but not yet to the two new patches. Changes in v7: 1) Added a TODO to eventually remove the Default trait from Architecture. 2) Further refined the logic and comments for reading boot0, then boot42. Changes in v6: 1) Split out a separate patch for implementing Display for Spec. 2) Moved Spec implementation code to its proper location. 3) Significantly changed the use_boot42_instead() logic, and updated the comments accordingly. 4) Fixed the boot42 register and field values, which were wrong (and had not been exercised before). 5) Imported Revision, to save a few ::'s. 6) Rebased to the very latest drm-rust-next, which now includes the new "one 'use' item per line" updates. Changes in v5: Two fixes, both from Timur's review feedback (thanks!): 1) Updated both the cover letter, and patch 3 commit description, with the correct description of the future contents of NV_PMC_BOOT_0. 2) Removed a trailing "boot42" typo from a comment in the code. Changes in v4: 1) Simplified and improved the decision logic: reads both arch_0 and arch_1 fields in boot0, and skips the unnecessary is_nv04() logic as well. Thanks to Timur Tabi and Danilo for noticing these issues. 2) Added a patch to represent Architecture as a u8. This simplifies a few things. (Thanks to Alex Courbot. I added your Suggested-by to that patch.) 3) Enhanced the Revision type to do more, which simplifies the callers. (Thanks to Danilo.) Changes in v3: 1) Restored the Revision type as recommended by Danilo, but decoupled it from boot0. 2) Applied Alex Courbot's suggestion to use TryFrom<NV_PMC_BOOT_0/42> for Spec. 3) Reflowed the new comment documentation to 100 cols, to avoid wasting a few vertical lines. Changes in v2: 1) Restored the Spec type, and used that to encapsulate the subsequent boot42 enhancements. Thanks to Danilo Krummrich's feedback for that improvement. v1 cover letter (with typos fixed) NVIDIA GPUs are moving away from using NV_PMC_BOOT_0 to contain architecture and revision details, and will instead use NV_PMC_BOOT_42 in the future. NV_PMC_BOOT_0 will contain a specific set of values that will mean "go read NV_PMC_BOOT_42 instead". Change the selection logic in Nova so that it will claim Turing and later GPUs. This will work for the foreseeable future, without any further code changes here, because all NVIDIA GPUs are considered, from the oldest supported on Linux (NV04), through the future GPUs. Add some comment documentation to explain, chronologically, how boot0 and boot42 change with the GPU eras, and how that affects the selection logic. Also, remove the Revision type, because Revision is no longer valuable as a stand-alone type, because we only ever want the full information that Spec provides. This is based on today's drm-rust-next, which in turn is based on Linux 6.18-rc2. John Hubbard (6): gpu: nova-core: implement Display for Spec gpu: nova-core: prepare Spec and Revision types for boot0/boot42 gpu: nova-core: make Architecture behave as a u8 type gpu: nova-core: use ENOTSUPP for unsupported GPUs, in all cases gpu: nova-core: add boot42 support for next-gen GPUs gpu: nova-core: provide a clear error report for unsupported GPUs drivers/gpu/nova-core/gpu.rs | 94 +++++++++++++++++++++++++---------- drivers/gpu/nova-core/regs.rs | 51 ++++++++++++++++--- 2 files changed, 112 insertions(+), 33 deletions(-) base-commit: e54ad0cd3673c93cdafda58505eaa81610fe3aef -- 2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 1/6] gpu: nova-core: implement Display for Spec
Implement Display for Spec. This simplifies the dev_info!() code for
printing banners such as:
NVIDIA (Chipset: GA104, Architecture: Ampere, Revision: a.1)
Cc: Alexandre Courbot <acourbot at nvidia.com>
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Reviewed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 802e71e4f97d..7fd9e91771a6 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -180,6 +180,18 @@ fn new(bar: &Bar0) -> Result<Spec> {
}
}
+impl fmt::Display for Spec {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) ->
fmt::Result {
+ write!(
+ f,
+ "Chipset: {}, Architecture: {:?}, Revision: {}",
+ self.chipset,
+ self.chipset.arch(),
+ self.revision
+ )
+ }
+}
+
/// Structure holding the resources required to operate the GPU.
#[pin_data]
pub(crate) struct Gpu {
@@ -206,13 +218,7 @@ pub(crate) fn new<'a>(
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(Self {
spec: Spec::new(bar).inspect(|spec| {
- dev_info!(
- pdev.as_ref(),
- "NVIDIA (Chipset: {}, Architecture: {:?}, Revision:
{})\n",
- spec.chipset,
- spec.chipset.arch(),
- spec.revision
- );
+ dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
})?,
// We must wait for GFW_BOOT completion before doing any
significant setup on the GPU.
--
2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 2/6] gpu: nova-core: prepare Spec and Revision types for boot0/boot42
1) Decouple Revision from boot0.
2) Enhance Revision, which in turn simplifies Spec::new().
3) Also, slightly enhance the comment about Spec, to be more precise.
Cc: Alexandre Courbot <acourbot at nvidia.com>
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Reviewed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 26 ++++++++++++--------------
drivers/gpu/nova-core/regs.rs | 11 ++++++++++-
2 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 7fd9e91771a6..8f438188fc03 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -143,17 +143,8 @@ fn try_from(value: u8) -> Result<Self> {
}
pub(crate) struct Revision {
- major: u8,
- minor: u8,
-}
-
-impl Revision {
- fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
- Self {
- major: boot0.major_revision(),
- minor: boot0.minor_revision(),
- }
- }
+ pub(crate) major: u8,
+ pub(crate) minor: u8,
}
impl fmt::Display for Revision {
@@ -162,10 +153,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>)
-> fmt::Result {
}
}
-/// Structure holding the metadata of the GPU.
+/// Structure holding a basic description of the GPU: `Architecture`, `Chipset`
and `Revision`.
pub(crate) struct Spec {
chipset: Chipset,
- /// The revision of the chipset.
revision: Revision,
}
@@ -173,9 +163,17 @@ impl Spec {
fn new(bar: &Bar0) -> Result<Spec> {
let boot0 = regs::NV_PMC_BOOT_0::read(bar);
+ Spec::try_from(boot0)
+ }
+}
+
+impl TryFrom<regs::NV_PMC_BOOT_0> for Spec {
+ type Error = Error;
+
+ fn try_from(boot0: regs::NV_PMC_BOOT_0) -> Result<Self> {
Ok(Self {
chipset: boot0.chipset()?,
- revision: Revision::from_boot0(boot0),
+ revision: boot0.revision(),
})
}
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 934003cab8a8..8c9af3c59708 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -24,7 +24,8 @@
},
gpu::{
Architecture,
- Chipset, //
+ Chipset,
+ Revision, //
},
num::FromSafeCast,
};
@@ -56,6 +57,14 @@ pub(crate) fn chipset(self) -> Result<Chipset> {
})
.and_then(Chipset::try_from)
}
+
+ /// Returns the revision information of the chip.
+ pub(crate) fn revision(self) -> Revision {
+ Revision {
+ major: self.major_revision(),
+ minor: self.minor_revision(),
+ }
+ }
}
// PBUS
--
2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 3/6] gpu: nova-core: make Architecture behave as a u8 type
This allows Architecture to be passed into register!() and bitfield!()
macro calls. That in turn requires a default implementation for
Architecture.
This simplifies transforming BOOT0 (and later, BOOT42) register values
into GPU architectures.
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Suggested-by: Alexandre Courbot <acourbot at nvidia.com>
Reviewed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 8f438188fc03..c1eca052968b 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -122,8 +122,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>)
-> fmt::Result {
}
/// Enum representation of the GPU generation.
-#[derive(fmt::Debug)]
+///
+/// TODO: remove the `Default` trait implementation, and the `#[default]`
+/// attribute, once the register!() macro (which creates Architecture items) no
+/// longer requires it for read-only fields.
+#[derive(fmt::Debug, Default, Copy, Clone)]
+#[repr(u8)]
pub(crate) enum Architecture {
+ #[default]
Turing = 0x16,
Ampere = 0x17,
Ada = 0x19,
@@ -142,6 +148,13 @@ fn try_from(value: u8) -> Result<Self> {
}
}
+impl From<Architecture> for u8 {
+ fn from(value: Architecture) -> Self {
+ // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
+ value as u8
+ }
+}
+
pub(crate) struct Revision {
pub(crate) major: u8,
pub(crate) minor: u8,
--
2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 4/6] gpu: nova-core: use ENOTSUPP for unsupported GPUs, in all cases
Some places in the driver use ENODEV for unsupported GPUs, while others
use ENOTSUPP. ENOTSUPP is more accurate, so change the ENODEV instances
to ENOTSUPP.
Cc: Alexandre Courbot <acourbot at nvidia.com>
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Cc: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index c1eca052968b..aa8e881dd474 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -62,7 +62,7 @@ impl TryFrom<u32> for Chipset {
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
$( $value => Ok(Chipset::$variant), )*
- _ => Err(ENODEV),
+ _ => Err(ENOTSUPP),
}
}
}
@@ -143,7 +143,7 @@ fn try_from(value: u8) -> Result<Self> {
0x16 => Ok(Self::Turing),
0x17 => Ok(Self::Ampere),
0x19 => Ok(Self::Ada),
- _ => Err(ENODEV),
+ _ => Err(ENOTSUPP),
}
}
}
--
2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 5/6] gpu: nova-core: add boot42 support for next-gen GPUs
NVIDIA GPUs are moving away from using NV_PMC_BOOT_0 to contain
architecture and revision details, and will instead use NV_PMC_BOOT_42
in the future. NV_PMC_BOOT_0 will contain a specific set of values
that will mean "go read NV_PMC_BOOT_42 instead".
Change the selection logic in Nova so that it will claim Turing and
later GPUs. This will work for the foreseeable future, without any
further code changes here, because all NVIDIA GPUs are considered, from
the oldest supported on Linux (NV04), through the future GPUs.
Add some comment documentation to explain, chronologically, how boot0
and boot42 change with the GPU eras, and how that affects the selection
logic.
Cc: Alexandre Courbot <acourbot at nvidia.com>
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Reviewed-by: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 32 +++++++++++++++++++++++++++-----
drivers/gpu/nova-core/regs.rs | 22 ++++++++++++++++------
2 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index aa8e881dd474..9bc94811741c 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -174,19 +174,41 @@ pub(crate) struct Spec {
impl Spec {
fn new(bar: &Bar0) -> Result<Spec> {
+ // Some brief notes about boot0 and boot42, in chronological order:
+ //
+ // NV04 through NV50:
+ //
+ // Not supported by Nova. boot0 is necessary and sufficient to
identify these GPUs.
+ // boot42 may not even exist on some of these GPUs.
+ //
+ // Fermi through Volta:
+ //
+ // Not supported by Nova. boot0 is still sufficient to identify
these GPUs, but boot42
+ // is also guaranteed to be both present and accurate.
+ //
+ // Turing and later:
+ //
+ // Supported by Nova. Identified by first checking boot0 to ensure
that the GPU is not
+ // from an earlier (pre-Fermi) era, and then using boot42 to
precisely identify the GPU.
+ // Somewhere in the Rubin timeframe, boot0 will no longer have
space to add new GPU IDs.
+
let boot0 = regs::NV_PMC_BOOT_0::read(bar);
- Spec::try_from(boot0)
+ if boot0.is_older_than_fermi() {
+ return Err(ENOTSUPP);
+ }
+
+ Spec::try_from(regs::NV_PMC_BOOT_42::read(bar))
}
}
-impl TryFrom<regs::NV_PMC_BOOT_0> for Spec {
+impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
type Error = Error;
- fn try_from(boot0: regs::NV_PMC_BOOT_0) -> Result<Self> {
+ fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
Ok(Self {
- chipset: boot0.chipset()?,
- revision: boot0.revision(),
+ chipset: boot42.chipset()?,
+ revision: boot42.revision(),
})
}
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 8c9af3c59708..81097e83c276 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -41,14 +41,24 @@
});
impl NV_PMC_BOOT_0 {
- /// Combines `architecture_0` and `architecture_1` to obtain the
architecture of the chip.
- pub(crate) fn architecture(self) -> Result<Architecture> {
- Architecture::try_from(
- self.architecture_0() | (self.architecture_1() <<
Self::ARCHITECTURE_0_RANGE.len()),
- )
+ pub(crate) fn is_older_than_fermi(self) -> bool {
+ // From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals :
+ const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 = 0xc;
+
+ // Older chips left arch1 zeroed out. That, combined with an arch0
value that is less than
+ // GF100, means "older than Fermi".
+ self.architecture_1() == 0 && self.architecture_0() <
NV_PMC_BOOT_0_ARCHITECTURE_GF100
}
+}
+
+register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture
information" {
+ 15:12 minor_revision as u8, "Minor revision of the chip";
+ 19:16 major_revision as u8, "Major revision of the chip";
+ 23:20 implementation as u8, "Implementation version of the
architecture";
+ 29:24 architecture as u8 ?=> Architecture, "Architecture
value";
+});
- /// Combines `architecture` and `implementation` to obtain a code unique to
the chipset.
+impl NV_PMC_BOOT_42 {
pub(crate) fn chipset(self) -> Result<Chipset> {
self.architecture()
.map(|arch| {
--
2.51.2
John Hubbard
2025-Nov-14 02:41 UTC
[PATCH v8 6/6] gpu: nova-core: provide a clear error report for unsupported GPUs
Pass in a PCI device to Spec::new(), and provide a Display
implementation for boot42, in order to provide a clear, concise report
of what happened: the driver read NV_PMC_BOOT42, and found that the GPU
is not supported.
For very old GPUs (older than Fermi), the driver still returns ENOTSUPP,
but it does so without a driver-specific dmesg report. That is exactly
appropriate, because if such a GPU is installed, it can only be
supported by Nouveau. And if so, the user is not helped by additional
error messages from Nova.
Here's the full dmesg output for a Blackwell (not yet supported) GPU:
NovaCore 0000:01:00.0: Probe Nova Core GPU driver.
NovaCore 0000:01:00.0: Unsupported chipset: boot42 = 0x1b2a1000 (architecture
0x1b, implementation 0x2)
NovaCore 0000:01:00.0: probe with driver NovaCore failed with error -524
Cc: Alexandre Courbot <acourbot at nvidia.com>
Cc: Danilo Krummrich <dakr at kernel.org>
Cc: Timur Tabi <ttabi at nvidia.com>
Cc: Joel Fernandes <joelagnelf at nvidia.com>
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 9 ++++++---
drivers/gpu/nova-core/regs.rs | 18 ++++++++++++++++++
2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 9bc94811741c..7f8a5c1ed9f1 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -173,7 +173,7 @@ pub(crate) struct Spec {
}
impl Spec {
- fn new(bar: &Bar0) -> Result<Spec> {
+ fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> {
// Some brief notes about boot0 and boot42, in chronological order:
//
// NV04 through NV50:
@@ -198,7 +198,10 @@ fn new(bar: &Bar0) -> Result<Spec> {
return Err(ENOTSUPP);
}
- Spec::try_from(regs::NV_PMC_BOOT_42::read(bar))
+ let boot42 = regs::NV_PMC_BOOT_42::read(bar);
+ Spec::try_from(boot42).inspect_err(|_| {
+ dev_err!(dev, "Unsupported chipset: {}\n", boot42);
+ })
}
}
@@ -250,7 +253,7 @@ pub(crate) fn new<'a>(
bar: &'a Bar0,
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(Self {
- spec: Spec::new(bar).inspect(|spec| {
+ spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
})?,
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 81097e83c276..d6da8bc2e242 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -68,6 +68,12 @@ pub(crate) fn chipset(self) -> Result<Chipset> {
.and_then(Chipset::try_from)
}
+ /// Returns the raw architecture value from the register.
+ fn architecture_raw(self) -> u8 {
+ ((self.0 >> Self::ARCHITECTURE_RANGE.start()) & ((1 <<
Self::ARCHITECTURE_RANGE.len()) - 1))
+ as u8
+ }
+
/// Returns the revision information of the chip.
pub(crate) fn revision(self) -> Revision {
Revision {
@@ -77,6 +83,18 @@ pub(crate) fn revision(self) -> Revision {
}
}
+impl kernel::fmt::Display for NV_PMC_BOOT_42 {
+ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) ->
kernel::fmt::Result {
+ write!(
+ f,
+ "boot42 = 0x{:08x} (architecture 0x{:x}, implementation
0x{:x})",
+ self.0,
+ self.architecture_raw(),
+ self.implementation()
+ )
+ }
+}
+
// PBUS
register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {});
--
2.51.2