John Hubbard
2025-Oct-29 03:03 UTC
[PATCH v3 2/2] 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 be zeroed out.
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.
Signed-off-by: John Hubbard <jhubbard at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 72 +++++++++++++++++++++++++++++++----
drivers/gpu/nova-core/regs.rs | 27 +++++++++++++
2 files changed, 92 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 6f1486d4e9c6..6762493206ec 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -134,6 +134,34 @@ pub(crate) struct Revision {
minor: u8,
}
+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 {
+ major: boot0.major_revision(),
+ minor: boot0.minor_revision(),
+ },
+ })
+ }
+}
+
+impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
+ type Error = Error;
+
+ fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
+ Ok(Self {
+ chipset: boot42.chipset()?,
+ revision: Revision {
+ major: boot42.major_revision(),
+ minor: boot42.minor_revision(),
+ },
+ })
+ }
+}
+
impl fmt::Display for Revision {
fn fmt(&self, f: &mut fmt::Formatter<'_>) ->
fmt::Result {
write!(f, "{:x}.{:x}", self.major, self.minor)
@@ -151,13 +179,43 @@ impl Spec {
fn new(bar: &Bar0) -> Result<Spec> {
let boot0 = regs::NV_PMC_BOOT_0::read(bar);
- Ok(Self {
- chipset: boot0.chipset()?,
- revision: Revision {
- major: boot0.major_revision(),
- minor: boot0.minor_revision(),
- },
- })
+ // "next-gen" GPUs (some time after Blackwell) will zero out
boot0, and put the architecture
+ // details in boot42 instead. Avoid reading boot42 unless we are in
that case.
+ let boot42 = if boot0.is_next_gen() {
+ Some(regs::NV_PMC_BOOT_42::read(bar))
+ } else {
+ None
+ };
+
+ // Some brief notes about boot0 and boot42, in chronological order:
+ //
+ // NV04 through Volta:
+ //
+ // Not supported by Nova. boot0 is necessary and sufficient to
identify these GPUs.
+ // boot42 may not even exist on some of these GPUs.boot42
+ //
+ // Turing through Blackwell:
+ //
+ // Supported by both Nouveau and Nova. boot0 is still necessary and
sufficient to
+ // identify these GPUs. boot42 exists on these GPUs but we
don't need to use it.
+ //
+ // Future "next-gen" GPUs:
+ //
+ // Only supported by Nova. boot42 has the architecture details,
boot0 is zeroed out.
+
+ // NV04, the very first NVIDIA GPU to be supported on Linux, is
identified by a specific bit
+ // pattern in boot0. Although Nova does not support NV04 (see above),
it is possible to
+ // confuse NV04 with a "next-gen" GPU. Therefore, return
early if we specifically detect
+ // NV04, thus simplifying the remaining selection logic.
+ if boot0.is_nv04() {
+ Err(ENODEV)?
+ }
+
+ // Now that we know it is something more recent than NV04, use boot42
if we previously
+ // determined that boot42 was both valid and relevant, and boot0
otherwise.
+ boot42
+ .map(Spec::try_from)
+ .unwrap_or_else(|| Spec::try_from(boot0))
}
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 206dab2e1335..ed3a2c39edbc 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -25,6 +25,18 @@
});
impl NV_PMC_BOOT_0 {
+ pub(crate) fn is_nv04(self) -> bool {
+ // The very first supported GPU was NV04, and it is identified by a
specific bit pattern in
+ // boot0. This provides a way to check for that, which in turn is
required in order to avoid
+ // confusing future "next-gen" GPUs with NV04.
+ self.architecture_0() == 0 && (self.0 & 0xff00fff0) ==
0x20004000
+ }
+
+ pub(crate) fn is_next_gen(self) -> bool {
+ // "next-gen" GPUs (some time after Blackwell) will set
`architecture_0` to 0, and put the
+ // architecture details in boot42 instead.
+ self.architecture_0() == 0 && !self.is_nv04()
+ }
/// Combines `architecture_0` and `architecture_1` to obtain the
architecture of the chip.
pub(crate) fn architecture(self) -> Result<Architecture> {
Architecture::try_from(
@@ -43,6 +55,21 @@ pub(crate) fn chipset(self) -> Result<Chipset> {
}
}
+register!(NV_PMC_BOOT_42 @ 0x00000108, "Extended architecture
information" {
+ 7:0 implementation as u8, "Implementation version of the
architecture";
+ 15:8 architecture as u8, "Architecture value";
+ 19:16 minor_revision as u8, "Minor revision of the chip";
+ 23:20 major_revision as u8, "Major revision of the chip";
+});
+
+impl NV_PMC_BOOT_42 {
+ pub(crate) fn chipset(self) -> Result<Chipset> {
+ let arch = Architecture::try_from(self.architecture())?;
+ let chipset_value = ((arch as u32) << 8) |
u32::from(self.implementation());
+ Chipset::try_from(chipset_value)
+ }
+}
+
// PBUS
register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {});
--
2.51.2
Danilo Krummrich
2025-Oct-29 11:26 UTC
[PATCH v3 2/2] gpu: nova-core: add boot42 support for next-gen GPUs
On Wed Oct 29, 2025 at 4:03 AM CET, John Hubbard wrote:> diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs > index 6f1486d4e9c6..6762493206ec 100644 > --- a/drivers/gpu/nova-core/gpu.rs > +++ b/drivers/gpu/nova-core/gpu.rs > @@ -134,6 +134,34 @@ pub(crate) struct Revision { > minor: u8, > } > > +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 { > + major: boot0.major_revision(), > + minor: boot0.minor_revision(), > + },Actually, would be nice to handle this just like chipset for consistency, i.e. revision: boot0.revision()> + }) > + } > +} > + > +impl TryFrom<regs::NV_PMC_BOOT_42> for Spec { > + type Error = Error; > + > + fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> { > + Ok(Self { > + chipset: boot42.chipset()?, > + revision: Revision { > + major: boot42.major_revision(), > + minor: boot42.minor_revision(), > + },Same here, could be revision: boot42.revision()> + }) > + } > +} > + > impl fmt::Display for Revision { > fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { > write!(f, "{:x}.{:x}", self.major, self.minor) > @@ -151,13 +179,43 @@ impl Spec { > fn new(bar: &Bar0) -> Result<Spec> { > let boot0 = regs::NV_PMC_BOOT_0::read(bar); > > - Ok(Self { > - chipset: boot0.chipset()?, > - revision: Revision { > - major: boot0.major_revision(), > - minor: boot0.minor_revision(), > - }, > - }) > + // "next-gen" GPUs (some time after Blackwell) will zero out boot0, and put the architecture > + // details in boot42 instead. Avoid reading boot42 unless we are in that case. > + let boot42 = if boot0.is_next_gen() { > + Some(regs::NV_PMC_BOOT_42::read(bar)) > + } else { > + None > + }; > + > + // Some brief notes about boot0 and boot42, in chronological order: > + // > + // NV04 through Volta: > + // > + // Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs. > + // boot42 may not even exist on some of these GPUs.boot42 > + // > + // Turing through Blackwell: > + // > + // Supported by both Nouveau and Nova. boot0 is still necessary and sufficient to > + // identify these GPUs. boot42 exists on these GPUs but we don't need to use it. > + // > + // Future "next-gen" GPUs: > + // > + // Only supported by Nova. boot42 has the architecture details, boot0 is zeroed out. > + > + // NV04, the very first NVIDIA GPU to be supported on Linux, is identified by a specific bit > + // pattern in boot0. Although Nova does not support NV04 (see above), it is possible to > + // confuse NV04 with a "next-gen" GPU. Therefore, return early if we specifically detect > + // NV04, thus simplifying the remaining selection logic. > + if boot0.is_nv04() { > + Err(ENODEV)? > + } > + > + // Now that we know it is something more recent than NV04, use boot42 if we previously > + // determined that boot42 was both valid and relevant, and boot0 otherwise. > + boot42 > + .map(Spec::try_from) > + .unwrap_or_else(|| Spec::try_from(boot0)) > } > }Without the comments this currently is: let boot42 = if boot0.is_next_gen() { Some(regs::NV_PMC_BOOT_42::read(bar)) } else { None }; if boot0.is_nv04() { Err(ENODEV)? } boot42 .map(Spec::try_from) .unwrap_or_else(|| Spec::try_from(boot0)) Which I think is a bit heavy-handed. Let's simplify this a bit: let boot0 = regs::NV_PMC_BOOT_0::read(bar); if boot0.is_nv04() { return Err(ENODEV); } Spec::try_from( if boot0.is_next_gen() { regs::NV_PMC_BOOT_42::read(bar) } else { boot0 } )
Alexandre Courbot
2025-Oct-29 14:05 UTC
[PATCH v3 2/2] gpu: nova-core: add boot42 support for next-gen GPUs
On Wed Oct 29, 2025 at 12:03 PM JST, John Hubbard wrote: <snip>> diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs > index 206dab2e1335..ed3a2c39edbc 100644 > --- a/drivers/gpu/nova-core/regs.rs > +++ b/drivers/gpu/nova-core/regs.rs > @@ -25,6 +25,18 @@ > }); > > impl NV_PMC_BOOT_0 { > + pub(crate) fn is_nv04(self) -> bool { > + // The very first supported GPU was NV04, and it is identified by a specific bit pattern in > + // boot0. This provides a way to check for that, which in turn is required in order to avoid > + // confusing future "next-gen" GPUs with NV04. > + self.architecture_0() == 0 && (self.0 & 0xff00fff0) == 0x20004000 > + } > + > + pub(crate) fn is_next_gen(self) -> bool { > + // "next-gen" GPUs (some time after Blackwell) will set `architecture_0` to 0, and put the > + // architecture details in boot42 instead. > + self.architecture_0() == 0 && !self.is_nv04() > + } > /// Combines `architecture_0` and `architecture_1` to obtain the architecture of the chip. > pub(crate) fn architecture(self) -> Result<Architecture> { > Architecture::try_from( > @@ -43,6 +55,21 @@ pub(crate) fn chipset(self) -> Result<Chipset> { > } > } > > +register!(NV_PMC_BOOT_42 @ 0x00000108, "Extended architecture information" { > + 7:0 implementation as u8, "Implementation version of the architecture"; > + 15:8 architecture as u8, "Architecture value";Here you can directly return the `Architecture` type and spare callers the labor of doing the conversion themselves: 15:8 architecture as u8 ?=> Architecture, "Architecture value"; This allows the implementation of `NV_PMC_BOOT_42` to mirror that of `NV_PMC_BOOT_0`: impl NV_PMC_BOOT_42 { pub(crate) fn chipset(self) -> Result<Chipset> { self.architecture() .map(|arch| { ((arch as u32) << Self::IMPLEMENTATION_RANGE.len()) | u32::from(self.implementation()) }) .and_then(Chipset::try_from) } } ... but also requires `Architecture` to be `repr(u8)` and to implement `Default` and `Into<u8>` so it can be used in the `register` macro: /// Enum representation of the GPU generation. #[derive(fmt::Debug, Default)] #[repr(u8)] pub(crate) enum Architecture { #[default] Turing = 0x16, Ampere = 0x17, Ada = 0x19, } impl From<Architecture> for u8 { fn from(value: Architecture) -> Self { // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless. value as u8 } } These implementations were not needed for `NV_PMC_BOOT_0` because its `architecture` method is manually implemented as it depends on two distinct fields.
Timur Tabi
2025-Oct-29 15:02 UTC
[PATCH v3 2/2] gpu: nova-core: add boot42 support for next-gen GPUs
On Tue, 2025-10-28 at 20:03 -0700, John Hubbard wrote:> 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 be zeroed out.I don't think this is correct. ARCH_1 in PMC_BOOT_0 will be set to 1 in future GPUs.