* [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:41 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 02/10] gpu: nova-core: falcon: add constant for memory block alignment Alexandre Courbot
` (9 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
When DMA was the only loading option for falcon firmwares, we decided to
store them in DMA objects as soon as they were loaded from disk and
patch them in-place to avoid having to do an extra copy.
This decision complicates the PIO loading patch considerably, and
actually does not even stand on its own when put into perspective with
the fact that it requires 8 unsafe statements in the code that wouldn't
exist if we stored the firmware into a `KVVec` and copied it into a DMA
object at the last minute.
The cost of the copy is, as can be expected, imperceptible at runtime.
Thus, switch to a lazy DMA object creation model and simplify our code
a bit. This will also have the nice side-effect of being more fit for
PIO loading.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 57 ++++++++++++-------
drivers/gpu/nova-core/firmware.rs | 38 ++++++-------
drivers/gpu/nova-core/firmware/booter.rs | 33 +++++------
drivers/gpu/nova-core/firmware/fwsec.rs | 96 ++++++++++----------------------
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
5 files changed, 99 insertions(+), 127 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 37bfee1d0949..8d444cf9d55c 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -2,12 +2,13 @@
//! Falcon microprocessor base support
-use core::ops::Deref;
-
use hal::FalconHal;
use kernel::{
- device,
+ device::{
+ self,
+ Device, //
+ },
dma::{
DmaAddress,
DmaMask, //
@@ -15,9 +16,7 @@
io::poll::read_poll_timeout,
prelude::*,
sync::aref::ARef,
- time::{
- Delta, //
- },
+ time::Delta,
};
use crate::{
@@ -351,6 +350,9 @@ pub(crate) struct FalconBromParams {
/// Trait for providing load parameters of falcon firmwares.
pub(crate) trait FalconLoadParams {
+ /// Returns the firmware data as a slice of bytes.
+ fn as_slice(&self) -> &[u8];
+
/// Returns the load parameters for Secure `IMEM`.
fn imem_sec_load_params(&self) -> FalconLoadTarget;
@@ -370,9 +372,8 @@ pub(crate) trait FalconLoadParams {
/// Trait for a falcon firmware.
///
-/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
-/// object.
-pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
+/// A falcon firmware can be loaded on a given engine.
+pub(crate) trait FalconFirmware: FalconLoadParams {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
@@ -415,10 +416,10 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result {
/// `target_mem`.
///
/// `sec` is set if the loaded firmware is expected to run in secure mode.
- fn dma_wr<F: FalconFirmware<Target = E>>(
+ fn dma_wr(
&self,
bar: &Bar0,
- fw: &F,
+ dma_obj: &DmaObject,
target_mem: FalconMem,
load_offsets: FalconLoadTarget,
) -> Result {
@@ -430,11 +431,11 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
// For DMEM we can fold the start offset into the DMA handle.
let (src_start, dma_start) = match target_mem {
FalconMem::ImemSecure | FalconMem::ImemNonSecure => {
- (load_offsets.src_start, fw.dma_handle())
+ (load_offsets.src_start, dma_obj.dma_handle())
}
FalconMem::Dmem => (
0,
- fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?,
+ dma_obj.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?,
),
};
if dma_start % DmaAddress::from(DMA_LEN) > 0 {
@@ -466,7 +467,7 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
dev_err!(self.dev, "DMA transfer length overflow\n");
return Err(EOVERFLOW);
}
- Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => {
+ Some(upper_bound) if usize::from_safe_cast(upper_bound) > dma_obj.size() => {
dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n");
return Err(EINVAL);
}
@@ -515,7 +516,12 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
}
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
- fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
+ fn dma_load<F: FalconFirmware<Target = E>>(
+ &self,
+ dev: &Device<device::Bound>,
+ bar: &Bar0,
+ fw: &F,
+ ) -> Result {
// The Non-Secure section only exists on firmware used by Turing and GA100, and
// those platforms do not use DMA.
if fw.imem_ns_load_params().is_some() {
@@ -523,14 +529,22 @@ fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result
return Err(EINVAL);
}
+ // Create DMA object with firmware content as the source of the DMA engine.
+ let dma_obj = DmaObject::from_data(dev, fw.as_slice())?;
+
self.dma_reset(bar);
regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| {
v.set_target(FalconFbifTarget::CoherentSysmem)
.set_mem_type(FalconFbifMemType::Physical)
});
- self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?;
- self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?;
+ self.dma_wr(
+ bar,
+ &dma_obj,
+ FalconMem::ImemSecure,
+ fw.imem_sec_load_params(),
+ )?;
+ self.dma_wr(bar, &dma_obj, FalconMem::Dmem, fw.dmem_load_params())?;
self.hal.program_brom(self, bar, &fw.brom_params())?;
@@ -641,9 +655,14 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
}
// Load a firmware image into Falcon memory
- pub(crate) fn load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
+ pub(crate) fn load<F: FalconFirmware<Target = E>>(
+ &self,
+ dev: &Device<device::Bound>,
+ bar: &Bar0,
+ fw: &F,
+ ) -> Result {
match self.hal.load_method() {
- LoadMethod::Dma => self.dma_load(bar, fw),
+ LoadMethod::Dma => self.dma_load(dev, bar, fw),
LoadMethod::Pio => Err(ENOTSUPP),
}
}
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 815e8000bf81..09b12ad546c2 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -15,7 +15,6 @@
};
use crate::{
- dma::DmaObject,
falcon::{
FalconFirmware,
FalconLoadTarget, //
@@ -292,7 +291,7 @@ impl SignedState for Unsigned {}
struct Signed;
impl SignedState for Signed {}
-/// A [`DmaObject`] containing a specific microcode ready to be loaded into a falcon.
+/// Microcode to be loaded into a specific falcon.
///
/// This is module-local and meant for sub-modules to use internally.
///
@@ -300,34 +299,33 @@ impl SignedState for Signed {}
/// before it can be loaded (with an exception for development hardware). The
/// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the
/// firmware to its [`Signed`] state.
-struct FirmwareDmaObject<F: FalconFirmware, S: SignedState>(DmaObject, PhantomData<(F, S)>);
+struct FirmwareObject<F: FalconFirmware, S: SignedState>(KVVec<u8>, PhantomData<(F, S)>);
/// Trait for signatures to be patched directly into a given firmware.
///
/// This is module-local and meant for sub-modules to use internally.
trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
-impl<F: FalconFirmware> FirmwareDmaObject<F, Unsigned> {
- /// Patches the firmware at offset `sig_base_img` with `signature`.
+impl<F: FalconFirmware> FirmwareObject<F, Unsigned> {
+ /// Patches the firmware at offset `signature_start` with `signature`.
fn patch_signature<S: FirmwareSignature<F>>(
mut self,
signature: &S,
- sig_base_img: usize,
- ) -> Result<FirmwareDmaObject<F, Signed>> {
+ signature_start: usize,
+ ) -> Result<FirmwareObject<F, Signed>> {
let signature_bytes = signature.as_ref();
- if sig_base_img + signature_bytes.len() > self.0.size() {
- return Err(EINVAL);
- }
+ let signature_end = signature_start
+ .checked_add(signature_bytes.len())
+ .ok_or(EOVERFLOW)?;
+ let dst = self
+ .0
+ .get_mut(signature_start..signature_end)
+ .ok_or(EINVAL)?;
- // SAFETY: We are the only user of this object, so there cannot be any race.
- let dst = unsafe { self.0.start_ptr_mut().add(sig_base_img) };
+ // PANIC: `dst` and `signature_bytes` have the same length.
+ dst.copy_from_slice(signature_bytes);
- // SAFETY: `signature` and `dst` are valid, properly aligned, and do not overlap.
- unsafe {
- core::ptr::copy_nonoverlapping(signature_bytes.as_ptr(), dst, signature_bytes.len())
- };
-
- Ok(FirmwareDmaObject(self.0, PhantomData))
+ Ok(FirmwareObject(self.0, PhantomData))
}
/// Mark the firmware as signed without patching it.
@@ -335,8 +333,8 @@ fn patch_signature<S: FirmwareSignature<F>>(
/// This method is used to explicitly confirm that we do not need to sign the firmware, while
/// allowing us to continue as if it was. This is typically only needed for development
/// hardware.
- fn no_patch_signature(self) -> FirmwareDmaObject<F, Signed> {
- FirmwareDmaObject(self.0, PhantomData)
+ fn no_patch_signature(self) -> FirmwareObject<F, Signed> {
+ FirmwareObject(self.0, PhantomData)
}
}
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index ab374026b1f4..2b7166eaf283 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -4,10 +4,7 @@
//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon
//! (and optionally unload it through a separate firmware image).
-use core::{
- marker::PhantomData,
- ops::Deref, //
-};
+use core::marker::PhantomData;
use kernel::{
device,
@@ -16,7 +13,6 @@
};
use crate::{
- dma::DmaObject,
driver::Bar0,
falcon::{
sec2::Sec2,
@@ -28,7 +24,7 @@
},
firmware::{
BinFirmware,
- FirmwareDmaObject,
+ FirmwareObject,
FirmwareSignature,
Signed,
Unsigned, //
@@ -269,12 +265,15 @@ pub(crate) struct BooterFirmware {
// BROM falcon parameters.
brom_params: FalconBromParams,
// Device-mapped firmware image.
- ucode: FirmwareDmaObject<Self, Signed>,
+ ucode: FirmwareObject<Self, Signed>,
}
-impl FirmwareDmaObject<BooterFirmware, Unsigned> {
- fn new_booter(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {
- DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData))
+impl FirmwareObject<BooterFirmware, Unsigned> {
+ fn new_booter(data: &[u8]) -> Result<Self> {
+ let mut ucode = KVVec::new();
+ ucode.extend_from_slice(data, GFP_KERNEL)?;
+
+ Ok(Self(ucode, PhantomData))
}
}
@@ -328,7 +327,7 @@ pub(crate) fn new(
let ucode = bin_fw
.data()
.ok_or(EINVAL)
- .and_then(|data| FirmwareDmaObject::<Self, _>::new_booter(dev, data))?;
+ .and_then(FirmwareObject::<Self, _>::new_booter)?;
let ucode_signed = {
let mut signatures = hs_fw.signatures_iter()?.peekable();
@@ -400,6 +399,10 @@ pub(crate) fn new(
}
impl FalconLoadParams for BooterFirmware {
+ fn as_slice(&self) -> &[u8] {
+ self.ucode.0.as_slice()
+ }
+
fn imem_sec_load_params(&self) -> FalconLoadTarget {
self.imem_sec_load_target.clone()
}
@@ -425,14 +428,6 @@ fn boot_addr(&self) -> u32 {
}
}
-impl Deref for BooterFirmware {
- type Target = DmaObject;
-
- fn deref(&self) -> &Self::Target {
- &self.ucode.0
- }
-}
-
impl FalconFirmware for BooterFirmware {
type Target = Sec2;
}
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index df3d8de14ca1..9349c715a5a4 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -10,10 +10,7 @@
//! - The command to be run, as this firmware can perform several tasks ;
//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
-use core::{
- marker::PhantomData,
- ops::Deref, //
-};
+use core::marker::PhantomData;
use kernel::{
device::{
@@ -28,7 +25,6 @@
};
use crate::{
- dma::DmaObject,
driver::Bar0,
falcon::{
gsp::Gsp,
@@ -40,7 +36,7 @@
},
firmware::{
FalconUCodeDesc,
- FirmwareDmaObject,
+ FirmwareObject,
FirmwareSignature,
Signed,
Unsigned, //
@@ -174,52 +170,21 @@ fn as_ref(&self) -> &[u8] {
impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
-/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement
-/// [`FromBytes`]) and return a reference to it.
-///
-/// # Safety
-///
-/// * Callers must ensure that the device does not read/write to/from memory while the returned
-/// reference is live.
-/// * Callers must ensure that this call does not race with a write to the same region while
-/// the returned reference is live.
-unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {
- // SAFETY: The safety requirements of the function guarantee the device won't read
- // or write to memory while the reference is alive and that this call won't race
- // with writes to the same memory region.
- T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)
-}
-
-/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must
-/// implement [`FromBytes`]) and return a reference to it.
-///
-/// # Safety
-///
-/// * Callers must ensure that the device does not read/write to/from memory while the returned
-/// slice is live.
-/// * Callers must ensure that this call does not race with a read or write to the same region
-/// while the returned slice is live.
-unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
- fw: &mut DmaObject,
- offset: usize,
-) -> Result<&mut T> {
- // SAFETY: The safety requirements of the function guarantee the device won't read
- // or write to memory while the reference is alive and that this call won't race
- // with writes or reads to the same memory region.
- T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)
-}
-
/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.
///
/// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow.
pub(crate) struct FwsecFirmware {
/// Descriptor of the firmware.
desc: FalconUCodeDesc,
- /// GPU-accessible DMA object containing the firmware.
- ucode: FirmwareDmaObject<Self, Signed>,
+ /// Object containing the firmware binary.
+ ucode: FirmwareObject<Self, Signed>,
}
impl FalconLoadParams for FwsecFirmware {
+ fn as_slice(&self) -> &[u8] {
+ self.ucode.0.as_slice()
+ }
+
fn imem_sec_load_params(&self) -> FalconLoadTarget {
self.desc.imem_sec_load_params()
}
@@ -245,23 +210,15 @@ fn boot_addr(&self) -> u32 {
}
}
-impl Deref for FwsecFirmware {
- type Target = DmaObject;
-
- fn deref(&self) -> &Self::Target {
- &self.ucode.0
- }
-}
-
impl FalconFirmware for FwsecFirmware {
type Target = Gsp;
}
-impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
- fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
+impl FirmwareObject<FwsecFirmware, Unsigned> {
+ fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let desc = bios.fwsec_image().header()?;
- let ucode = bios.fwsec_image().ucode(&desc)?;
- let mut dma_object = DmaObject::from_data(dev, ucode)?;
+ let mut ucode = KVVec::new();
+ ucode.extend_from_slice(bios.fwsec_image().ucode(&desc)?, GFP_KERNEL)?;
let hdr_offset = desc
.imem_load_size()
@@ -269,8 +226,9 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
.map(usize::from_safe_cast)
.ok_or(EINVAL)?;
- // SAFETY: we have exclusive access to `dma_object`.
- let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
+ let hdr = FalconAppifHdrV1::from_bytes_prefix(&ucode[hdr_offset..])
+ .ok_or(EINVAL)?
+ .0;
if hdr.version != 1 {
return Err(EINVAL);
@@ -284,8 +242,9 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
.and_then(|o| o.checked_add(i.checked_mul(usize::from(hdr.entry_size))?))
.ok_or(EINVAL)?;
- // SAFETY: we have exclusive access to `dma_object`.
- let app: &FalconAppifV1 = unsafe { transmute(&dma_object, entry_offset) }?;
+ let app = FalconAppifV1::from_bytes_prefix(&ucode[entry_offset..])
+ .ok_or(EINVAL)?
+ .0;
if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {
continue;
@@ -298,9 +257,10 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
.map(usize::from_safe_cast)
.ok_or(EINVAL)?;
- let dmem_mapper: &mut FalconAppifDmemmapperV3 =
- // SAFETY: we have exclusive access to `dma_object`.
- unsafe { transmute_mut(&mut dma_object, dmem_mapper_offset) }?;
+ let dmem_mapper =
+ FalconAppifDmemmapperV3::from_bytes_mut_prefix(&mut ucode[dmem_mapper_offset..])
+ .ok_or(EINVAL)?
+ .0;
dmem_mapper.init_cmd = match cmd {
FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS,
@@ -314,9 +274,9 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
.map(usize::from_safe_cast)
.ok_or(EINVAL)?;
- let frts_cmd: &mut FrtsCmd =
- // SAFETY: we have exclusive access to `dma_object`.
- unsafe { transmute_mut(&mut dma_object, frts_cmd_offset) }?;
+ let frts_cmd = FrtsCmd::from_bytes_mut_prefix(&mut ucode[frts_cmd_offset..])
+ .ok_or(EINVAL)?
+ .0;
frts_cmd.read_vbios = ReadVbios {
ver: 1,
@@ -340,7 +300,7 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
}
// Return early as we found and patched the DMEMMAPPER region.
- return Ok(Self(dma_object, PhantomData));
+ return Ok(Self(ucode, PhantomData));
}
Err(ENOTSUPP)
@@ -357,7 +317,7 @@ pub(crate) fn new(
bios: &Vbios,
cmd: FwsecCommand,
) -> Result<Self> {
- let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?;
+ let ucode_dma = FirmwareObject::<Self, _>::new_fwsec(bios, cmd)?;
// Patch signature if needed.
let desc = bios.fwsec_image().header()?;
@@ -429,7 +389,7 @@ pub(crate) fn run(
.reset(bar)
.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
falcon
- .load(bar, self)
+ .load(dev, bar, self)
.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
let (mbox0, _) = falcon
.boot(bar, Some(0), None)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index c56029f444cb..78957ed8814f 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -178,7 +178,7 @@ pub(crate) fn boot(
);
sec2_falcon.reset(bar)?;
- sec2_falcon.load(bar, &booter_loader)?;
+ sec2_falcon.load(dev, bar, &booter_loader)?;
let wpr_handle = wpr_meta.dma_handle();
let (mbox0, mbox1) = sec2_falcon.boot(
bar,
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily
2026-03-01 14:03 ` [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily Alexandre Courbot
@ 2026-03-06 1:41 ` Eliot Courtney
2026-03-06 4:24 ` Alexandre Courbot
0 siblings, 1 reply; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:41 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> When DMA was the only loading option for falcon firmwares, we decided to
> store them in DMA objects as soon as they were loaded from disk and
> patch them in-place to avoid having to do an extra copy.
>
> This decision complicates the PIO loading patch considerably, and
> actually does not even stand on its own when put into perspective with
> the fact that it requires 8 unsafe statements in the code that wouldn't
> exist if we stored the firmware into a `KVVec` and copied it into a DMA
> object at the last minute.
>
> The cost of the copy is, as can be expected, imperceptible at runtime.
> Thus, switch to a lazy DMA object creation model and simplify our code
> a bit. This will also have the nice side-effect of being more fit for
> PIO loading.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 57 ++++++++++++-------
> drivers/gpu/nova-core/firmware.rs | 38 ++++++-------
> drivers/gpu/nova-core/firmware/booter.rs | 33 +++++------
> drivers/gpu/nova-core/firmware/fwsec.rs | 96 ++++++++++----------------------
> drivers/gpu/nova-core/gsp/boot.rs | 2 +-
> 5 files changed, 99 insertions(+), 127 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 37bfee1d0949..8d444cf9d55c 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -2,12 +2,13 @@
>
> //! Falcon microprocessor base support
>
> -use core::ops::Deref;
> -
> use hal::FalconHal;
>
> use kernel::{
> - device,
> + device::{
> + self,
> + Device, //
> + },
> dma::{
> DmaAddress,
> DmaMask, //
> @@ -15,9 +16,7 @@
> io::poll::read_poll_timeout,
> prelude::*,
> sync::aref::ARef,
> - time::{
> - Delta, //
> - },
> + time::Delta,
nit: Missing // guard here.
> diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
> index df3d8de14ca1..9349c715a5a4 100644
> --- a/drivers/gpu/nova-core/firmware/fwsec.rs
> +++ b/drivers/gpu/nova-core/firmware/fwsec.rs
> @@ -10,10 +10,7 @@
> //! - The command to be run, as this firmware can perform several tasks ;
> //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
>
> -use core::{
> - marker::PhantomData,
> - ops::Deref, //
> -};
> +use core::marker::PhantomData;
>
> use kernel::{
> device::{
> @@ -28,7 +25,6 @@
> };
>
> use crate::{
> - dma::DmaObject,
> driver::Bar0,
> falcon::{
> gsp::Gsp,
> @@ -40,7 +36,7 @@
> },
> firmware::{
> FalconUCodeDesc,
> - FirmwareDmaObject,
> + FirmwareObject,
> FirmwareSignature,
> Signed,
> Unsigned, //
> @@ -174,52 +170,21 @@ fn as_ref(&self) -> &[u8] {
>
> impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
>
> -/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement
> -/// [`FromBytes`]) and return a reference to it.
> -///
> -/// # Safety
> -///
> -/// * Callers must ensure that the device does not read/write to/from memory while the returned
> -/// reference is live.
> -/// * Callers must ensure that this call does not race with a write to the same region while
> -/// the returned reference is live.
> -unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {
> - // SAFETY: The safety requirements of the function guarantee the device won't read
> - // or write to memory while the reference is alive and that this call won't race
> - // with writes to the same memory region.
> - T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)
> -}
> -
> -/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must
> -/// implement [`FromBytes`]) and return a reference to it.
> -///
> -/// # Safety
> -///
> -/// * Callers must ensure that the device does not read/write to/from memory while the returned
> -/// slice is live.
> -/// * Callers must ensure that this call does not race with a read or write to the same region
> -/// while the returned slice is live.
> -unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
> - fw: &mut DmaObject,
> - offset: usize,
> -) -> Result<&mut T> {
> - // SAFETY: The safety requirements of the function guarantee the device won't read
> - // or write to memory while the reference is alive and that this call won't race
> - // with writes or reads to the same memory region.
> - T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)
> -}
> -
> /// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.
> ///
> /// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow.
> pub(crate) struct FwsecFirmware {
> /// Descriptor of the firmware.
> desc: FalconUCodeDesc,
> - /// GPU-accessible DMA object containing the firmware.
> - ucode: FirmwareDmaObject<Self, Signed>,
> + /// Object containing the firmware binary.
> + ucode: FirmwareObject<Self, Signed>,
> }
>
> impl FalconLoadParams for FwsecFirmware {
> + fn as_slice(&self) -> &[u8] {
> + self.ucode.0.as_slice()
> + }
> +
> fn imem_sec_load_params(&self) -> FalconLoadTarget {
> self.desc.imem_sec_load_params()
> }
> @@ -245,23 +210,15 @@ fn boot_addr(&self) -> u32 {
> }
> }
>
> -impl Deref for FwsecFirmware {
> - type Target = DmaObject;
> -
> - fn deref(&self) -> &Self::Target {
> - &self.ucode.0
> - }
> -}
> -
> impl FalconFirmware for FwsecFirmware {
> type Target = Gsp;
> }
>
> -impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
> - fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
> +impl FirmwareObject<FwsecFirmware, Unsigned> {
> + fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
> let desc = bios.fwsec_image().header()?;
> - let ucode = bios.fwsec_image().ucode(&desc)?;
> - let mut dma_object = DmaObject::from_data(dev, ucode)?;
> + let mut ucode = KVVec::new();
> + ucode.extend_from_slice(bios.fwsec_image().ucode(&desc)?, GFP_KERNEL)?;
>
> let hdr_offset = desc
> .imem_load_size()
> @@ -269,8 +226,9 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
> .map(usize::from_safe_cast)
> .ok_or(EINVAL)?;
>
> - // SAFETY: we have exclusive access to `dma_object`.
> - let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
> + let hdr = FalconAppifHdrV1::from_bytes_prefix(&ucode[hdr_offset..])
> + .ok_or(EINVAL)?
> + .0;
Is it worth adding // PANIC: comments like we have in some other areas
of the codebase for each of these indexes into ucode?
Other than those two optional nits,
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily
2026-03-06 1:41 ` Eliot Courtney
@ 2026-03-06 4:24 ` Alexandre Courbot
0 siblings, 0 replies; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-06 4:24 UTC (permalink / raw)
To: Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, nouveau, rust-for-linux, dri-devel, linux-kernel,
dri-devel
On Fri Mar 6, 2026 at 10:41 AM JST, Eliot Courtney wrote:
<snip>
>> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
>> index 37bfee1d0949..8d444cf9d55c 100644
>> --- a/drivers/gpu/nova-core/falcon.rs
>> +++ b/drivers/gpu/nova-core/falcon.rs
>> @@ -2,12 +2,13 @@
>>
>> //! Falcon microprocessor base support
>>
>> -use core::ops::Deref;
>> -
>> use hal::FalconHal;
>>
>> use kernel::{
>> - device,
>> + device::{
>> + self,
>> + Device, //
>> + },
>> dma::{
>> DmaAddress,
>> DmaMask, //
>> @@ -15,9 +16,7 @@
>> io::poll::read_poll_timeout,
>> prelude::*,
>> sync::aref::ARef,
>> - time::{
>> - Delta, //
>> - },
>> + time::Delta,
>
> nit: Missing // guard here.
IIUC the guard is only required to prevent rustfmt from reformatting,
which does not happen here.
<snip>
>> -impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
>> - fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
>> +impl FirmwareObject<FwsecFirmware, Unsigned> {
>> + fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
>> let desc = bios.fwsec_image().header()?;
>> - let ucode = bios.fwsec_image().ucode(&desc)?;
>> - let mut dma_object = DmaObject::from_data(dev, ucode)?;
>> + let mut ucode = KVVec::new();
>> + ucode.extend_from_slice(bios.fwsec_image().ucode(&desc)?, GFP_KERNEL)?;
>>
>> let hdr_offset = desc
>> .imem_load_size()
>> @@ -269,8 +226,9 @@ fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Re
>> .map(usize::from_safe_cast)
>> .ok_or(EINVAL)?;
>>
>> - // SAFETY: we have exclusive access to `dma_object`.
>> - let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
>> + let hdr = FalconAppifHdrV1::from_bytes_prefix(&ucode[hdr_offset..])
>> + .ok_or(EINVAL)?
>> + .0;
>
> Is it worth adding // PANIC: comments like we have in some other areas
> of the codebase for each of these indexes into ucode?
Even better, we can do
let hdr = ucode
.get(hdr_offset..)
.and_then(FalconAppifHdrV1::from_bytes_prefix)
.ok_or(EINVAL)?
.0;
and have the bounds checked at runtime, as they should be.
>
> Other than those two optional nits,
> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
Thanks!
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 02/10] gpu: nova-core: falcon: add constant for memory block alignment
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
2026-03-01 14:03 ` [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:42 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency Alexandre Courbot
` (8 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
Falcon memory blocks are 256 bytes in size. This is a hard constant on
all models.
This value was hardcoded, so turn it into a documented constant. It will
also become useful with the PIO loading code.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 8d444cf9d55c..31217cd3a795 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -25,6 +25,7 @@
falcon::hal::LoadMethod,
gpu::Chipset,
num::{
+ self,
FromSafeCast,
IntoSafeCast, //
},
@@ -36,6 +37,9 @@
mod hal;
pub(crate) mod sec2;
+/// Alignment (in bytes) of falcon memory blocks.
+pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256;
+
// TODO[FPRI]: Replace with `ToPrimitive`.
macro_rules! impl_from_enum_to_u8 {
($enum_type:ty) => {
@@ -423,7 +427,7 @@ fn dma_wr(
target_mem: FalconMem,
load_offsets: FalconLoadTarget,
) -> Result {
- const DMA_LEN: u32 = 256;
+ const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
// For IMEM, we want to use the start offset as a virtual address tag for each page, since
// code addresses in the firmware (and the boot vector) are virtual.
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 02/10] gpu: nova-core: falcon: add constant for memory block alignment
2026-03-01 14:03 ` [PATCH v10 02/10] gpu: nova-core: falcon: add constant for memory block alignment Alexandre Courbot
@ 2026-03-06 1:42 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:42 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> Falcon memory blocks are 256 bytes in size. This is a hard constant on
> all models.
>
> This value was hardcoded, so turn it into a documented constant. It will
> also become useful with the PIO loading code.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 8d444cf9d55c..31217cd3a795 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -25,6 +25,7 @@
> falcon::hal::LoadMethod,
> gpu::Chipset,
> num::{
> + self,
> FromSafeCast,
> IntoSafeCast, //
> },
> @@ -36,6 +37,9 @@
> mod hal;
> pub(crate) mod sec2;
>
> +/// Alignment (in bytes) of falcon memory blocks.
> +pub(crate) const MEM_BLOCK_ALIGNMENT: usize = 256;
> +
> // TODO[FPRI]: Replace with `ToPrimitive`.
> macro_rules! impl_from_enum_to_u8 {
> ($enum_type:ty) => {
> @@ -423,7 +427,7 @@ fn dma_wr(
> target_mem: FalconMem,
> load_offsets: FalconLoadTarget,
> ) -> Result {
> - const DMA_LEN: u32 = 256;
> + const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
>
> // For IMEM, we want to use the start offset as a virtual address tag for each page, since
> // code addresses in the firmware (and the boot vector) are virtual.
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
2026-03-01 14:03 ` [PATCH v10 01/10] gpu: nova-core: create falcon firmware DMA objects lazily Alexandre Courbot
2026-03-01 14:03 ` [PATCH v10 02/10] gpu: nova-core: falcon: add constant for memory block alignment Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-02 4:54 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 04/10] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Alexandre Courbot
` (7 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
The current `FalconLoadParams` and `FalconLoadTarget` types are fit for
DMA loading, but not so much for PIO loading which will require its own
types. Start by renaming them to something that indicates that they are
indeed DMA-related.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 19 ++++++++++---------
drivers/gpu/nova-core/firmware.rs | 30 +++++++++++++++---------------
drivers/gpu/nova-core/firmware/booter.rs | 24 ++++++++++++------------
drivers/gpu/nova-core/firmware/fwsec.rs | 12 ++++++------
4 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 31217cd3a795..9eb827477e5e 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -330,9 +330,10 @@ pub(crate) trait FalconEngine:
const ID: Self;
}
-/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
+/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM)
+/// using DMA.
#[derive(Debug, Clone)]
-pub(crate) struct FalconLoadTarget {
+pub(crate) struct FalconDmaLoadTarget {
/// Offset from the start of the source object to copy from.
pub(crate) src_start: u32,
/// Offset from the start of the destination memory to copy into.
@@ -352,20 +353,20 @@ pub(crate) struct FalconBromParams {
pub(crate) ucode_id: u8,
}
-/// Trait for providing load parameters of falcon firmwares.
-pub(crate) trait FalconLoadParams {
+/// Trait implemented by falcon firmwares that can be loaded using DMA.
+pub(crate) trait FalconDmaLoadable {
/// Returns the firmware data as a slice of bytes.
fn as_slice(&self) -> &[u8];
/// Returns the load parameters for Secure `IMEM`.
- fn imem_sec_load_params(&self) -> FalconLoadTarget;
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
/// Returns the load parameters for Non-Secure `IMEM`,
/// used only on Turing and GA100.
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>;
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
/// Returns the load parameters for `DMEM`.
- fn dmem_load_params(&self) -> FalconLoadTarget;
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget;
/// Returns the parameters to write into the BROM registers.
fn brom_params(&self) -> FalconBromParams;
@@ -377,7 +378,7 @@ pub(crate) trait FalconLoadParams {
/// Trait for a falcon firmware.
///
/// A falcon firmware can be loaded on a given engine.
-pub(crate) trait FalconFirmware: FalconLoadParams {
+pub(crate) trait FalconFirmware: FalconDmaLoadable {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
@@ -425,7 +426,7 @@ fn dma_wr(
bar: &Bar0,
dma_obj: &DmaObject,
target_mem: FalconMem,
- load_offsets: FalconLoadTarget,
+ load_offsets: FalconDmaLoadTarget,
) -> Result {
const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 09b12ad546c2..6d47a6364c79 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -17,7 +17,7 @@
use crate::{
falcon::{
FalconFirmware,
- FalconLoadTarget, //
+ FalconDmaLoadTarget, //
},
gpu,
num::{
@@ -170,9 +170,9 @@ fn size(&self) -> usize {
((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
}
- fn imem_sec_load_params(&self) -> FalconLoadTarget;
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>;
- fn dmem_load_params(&self) -> FalconLoadTarget;
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget;
}
impl FalconUCodeDescriptor for FalconUCodeDescV2 {
@@ -204,24 +204,24 @@ fn signature_versions(&self) -> u16 {
0
}
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
- FalconLoadTarget {
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
+ FalconDmaLoadTarget {
src_start: 0,
dst_start: self.imem_sec_base,
len: self.imem_sec_size,
}
}
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> {
- Some(FalconLoadTarget {
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
+ Some(FalconDmaLoadTarget {
src_start: 0,
dst_start: self.imem_phys_base,
len: self.imem_load_size.checked_sub(self.imem_sec_size)?,
})
}
- fn dmem_load_params(&self) -> FalconLoadTarget {
- FalconLoadTarget {
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget {
+ FalconDmaLoadTarget {
src_start: self.dmem_offset,
dst_start: self.dmem_phys_base,
len: self.dmem_load_size,
@@ -258,21 +258,21 @@ fn signature_versions(&self) -> u16 {
self.signature_versions
}
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
- FalconLoadTarget {
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
+ FalconDmaLoadTarget {
src_start: 0,
dst_start: self.imem_phys_base,
len: self.imem_load_size,
}
}
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> {
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
// Not used on V3 platforms
None
}
- fn dmem_load_params(&self) -> FalconLoadTarget {
- FalconLoadTarget {
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget {
+ FalconDmaLoadTarget {
src_start: self.imem_load_size,
dst_start: self.dmem_phys_base,
len: self.dmem_load_size,
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index 2b7166eaf283..d569151982d1 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -19,8 +19,8 @@
Falcon,
FalconBromParams,
FalconFirmware,
- FalconLoadParams,
- FalconLoadTarget, //
+ FalconDmaLoadable,
+ FalconDmaLoadTarget, //
},
firmware::{
BinFirmware,
@@ -256,12 +256,12 @@ impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
/// The `Booter` loader firmware, responsible for loading the GSP.
pub(crate) struct BooterFirmware {
// Load parameters for Secure `IMEM` falcon memory.
- imem_sec_load_target: FalconLoadTarget,
+ imem_sec_load_target: FalconDmaLoadTarget,
// Load parameters for Non-Secure `IMEM` falcon memory,
// used only on Turing and GA100
- imem_ns_load_target: Option<FalconLoadTarget>,
+ imem_ns_load_target: Option<FalconDmaLoadTarget>,
// Load parameters for `DMEM` falcon memory.
- dmem_load_target: FalconLoadTarget,
+ dmem_load_target: FalconDmaLoadTarget,
// BROM falcon parameters.
brom_params: FalconBromParams,
// Device-mapped firmware image.
@@ -370,7 +370,7 @@ pub(crate) fn new(
let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 {
(
app0.offset,
- Some(FalconLoadTarget {
+ Some(FalconDmaLoadTarget {
src_start: 0,
dst_start: load_hdr.os_code_offset,
len: load_hdr.os_code_size,
@@ -381,13 +381,13 @@ pub(crate) fn new(
};
Ok(Self {
- imem_sec_load_target: FalconLoadTarget {
+ imem_sec_load_target: FalconDmaLoadTarget {
src_start: app0.offset,
dst_start: imem_sec_dst_start,
len: app0.len,
},
imem_ns_load_target,
- dmem_load_target: FalconLoadTarget {
+ dmem_load_target: FalconDmaLoadTarget {
src_start: load_hdr.os_data_offset,
dst_start: 0,
len: load_hdr.os_data_size,
@@ -398,20 +398,20 @@ pub(crate) fn new(
}
}
-impl FalconLoadParams for BooterFirmware {
+impl FalconDmaLoadable for BooterFirmware {
fn as_slice(&self) -> &[u8] {
self.ucode.0.as_slice()
}
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
self.imem_sec_load_target.clone()
}
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> {
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
self.imem_ns_load_target.clone()
}
- fn dmem_load_params(&self) -> FalconLoadTarget {
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.dmem_load_target.clone()
}
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 9349c715a5a4..2ba70e0c5a30 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -31,8 +31,8 @@
Falcon,
FalconBromParams,
FalconFirmware,
- FalconLoadParams,
- FalconLoadTarget, //
+ FalconDmaLoadable,
+ FalconDmaLoadTarget, //
},
firmware::{
FalconUCodeDesc,
@@ -180,20 +180,20 @@ pub(crate) struct FwsecFirmware {
ucode: FirmwareObject<Self, Signed>,
}
-impl FalconLoadParams for FwsecFirmware {
+impl FalconDmaLoadable for FwsecFirmware {
fn as_slice(&self) -> &[u8] {
self.ucode.0.as_slice()
}
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
+ fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
self.desc.imem_sec_load_params()
}
- fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> {
+ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
self.desc.imem_ns_load_params()
}
- fn dmem_load_params(&self) -> FalconLoadTarget {
+ fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.desc.dmem_load_params()
}
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency
2026-03-01 14:03 ` [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency Alexandre Courbot
@ 2026-03-02 4:54 ` Eliot Courtney
2026-03-02 5:24 ` Alexandre Courbot
0 siblings, 1 reply; 27+ messages in thread
From: Eliot Courtney @ 2026-03-02 4:54 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> The current `FalconLoadParams` and `FalconLoadTarget` types are fit for
> DMA loading, but not so much for PIO loading which will require its own
> types. Start by renaming them to something that indicates that they are
> indeed DMA-related.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
> index 2b7166eaf283..d569151982d1 100644
> --- a/drivers/gpu/nova-core/firmware/booter.rs
> +++ b/drivers/gpu/nova-core/firmware/booter.rs
> @@ -19,8 +19,8 @@
> Falcon,
> FalconBromParams,
> FalconFirmware,
> - FalconLoadParams,
> - FalconLoadTarget, //
> + FalconDmaLoadable,
> + FalconDmaLoadTarget, //
> },
I think here but also other files in this patch are not rustfmt'd (e.g.
import ordering here).
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency
2026-03-02 4:54 ` Eliot Courtney
@ 2026-03-02 5:24 ` Alexandre Courbot
0 siblings, 0 replies; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-02 5:24 UTC (permalink / raw)
To: Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Alistair Popple, Joel Fernandes, nouveau, rust-for-linux,
dri-devel, linux-kernel, dri-devel
On Mon Mar 2, 2026 at 1:54 PM JST, Eliot Courtney wrote:
> On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
>> The current `FalconLoadParams` and `FalconLoadTarget` types are fit for
>> DMA loading, but not so much for PIO loading which will require its own
>> types. Start by renaming them to something that indicates that they are
>> indeed DMA-related.
>>
>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> ---
>> diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
>> index 2b7166eaf283..d569151982d1 100644
>> --- a/drivers/gpu/nova-core/firmware/booter.rs
>> +++ b/drivers/gpu/nova-core/firmware/booter.rs
>> @@ -19,8 +19,8 @@
>> Falcon,
>> FalconBromParams,
>> FalconFirmware,
>> - FalconLoadParams,
>> - FalconLoadTarget, //
>> + FalconDmaLoadable,
>> + FalconDmaLoadTarget, //
>> },
>
> I think here but also other files in this patch are not rustfmt'd (e.g.
> import ordering here).
Oopsie, you're right. Sorry about that, that's what happens when I don't
run my checklist script.
And it's even worse, the build breaks in the middle of the series. I'll
respin and think of some penance to atone for my carelessness.
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 04/10] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (2 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 03/10] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:45 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 05/10] gpu: nova-core: falcon: remove unwarranted safety check in dma_load Alexandre Courbot
` (6 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
Not all firmware is necessarily loaded by DMA. Remove the requirement
for `FalconFirmware` to implement `FalconDmaLoadable`, and adapt
`Falcon`'s methods constraints accordingly.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 9eb827477e5e..450431804e1c 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -378,7 +378,7 @@ pub(crate) trait FalconDmaLoadable {
/// Trait for a falcon firmware.
///
/// A falcon firmware can be loaded on a given engine.
-pub(crate) trait FalconFirmware: FalconDmaLoadable {
+pub(crate) trait FalconFirmware {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
@@ -521,7 +521,7 @@ fn dma_wr(
}
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
- fn dma_load<F: FalconFirmware<Target = E>>(
+ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
&self,
dev: &Device<device::Bound>,
bar: &Bar0,
@@ -660,7 +660,7 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
}
// Load a firmware image into Falcon memory
- pub(crate) fn load<F: FalconFirmware<Target = E>>(
+ pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
&self,
dev: &Device<device::Bound>,
bar: &Bar0,
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 04/10] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable
2026-03-01 14:03 ` [PATCH v10 04/10] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Alexandre Courbot
@ 2026-03-06 1:45 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:45 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> Not all firmware is necessarily loaded by DMA. Remove the requirement
> for `FalconFirmware` to implement `FalconDmaLoadable`, and adapt
> `Falcon`'s methods constraints accordingly.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 9eb827477e5e..450431804e1c 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -378,7 +378,7 @@ pub(crate) trait FalconDmaLoadable {
> /// Trait for a falcon firmware.
> ///
> /// A falcon firmware can be loaded on a given engine.
> -pub(crate) trait FalconFirmware: FalconDmaLoadable {
> +pub(crate) trait FalconFirmware {
> /// Engine on which this firmware is to be loaded.
> type Target: FalconEngine;
> }
> @@ -521,7 +521,7 @@ fn dma_wr(
> }
>
> /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
> - fn dma_load<F: FalconFirmware<Target = E>>(
> + fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
> &self,
> dev: &Device<device::Bound>,
> bar: &Bar0,
> @@ -660,7 +660,7 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
> }
>
> // Load a firmware image into Falcon memory
> - pub(crate) fn load<F: FalconFirmware<Target = E>>(
> + pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
> &self,
> dev: &Device<device::Bound>,
> bar: &Bar0,
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 05/10] gpu: nova-core: falcon: remove unwarranted safety check in dma_load
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (3 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 04/10] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:50 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 06/10] gpu: nova-core: move brom_params and boot_addr to FalconFirmware Alexandre Courbot
` (5 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
This safety check was an assumption based on the firmwares we work with
- it is not based on an actual hardware limitation. Thus, remove it.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 7 -------
1 file changed, 7 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 450431804e1c..c02b73b1cfe6 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -527,13 +527,6 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
bar: &Bar0,
fw: &F,
) -> Result {
- // The Non-Secure section only exists on firmware used by Turing and GA100, and
- // those platforms do not use DMA.
- if fw.imem_ns_load_params().is_some() {
- debug_assert!(false);
- return Err(EINVAL);
- }
-
// Create DMA object with firmware content as the source of the DMA engine.
let dma_obj = DmaObject::from_data(dev, fw.as_slice())?;
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 05/10] gpu: nova-core: falcon: remove unwarranted safety check in dma_load
2026-03-01 14:03 ` [PATCH v10 05/10] gpu: nova-core: falcon: remove unwarranted safety check in dma_load Alexandre Courbot
@ 2026-03-06 1:50 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:50 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> This safety check was an assumption based on the firmwares we work with
> - it is not based on an actual hardware limitation. Thus, remove it.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 7 -------
> 1 file changed, 7 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 450431804e1c..c02b73b1cfe6 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -527,13 +527,6 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
> bar: &Bar0,
> fw: &F,
> ) -> Result {
> - // The Non-Secure section only exists on firmware used by Turing and GA100, and
> - // those platforms do not use DMA.
> - if fw.imem_ns_load_params().is_some() {
> - debug_assert!(false);
> - return Err(EINVAL);
> - }
> -
> // Create DMA object with firmware content as the source of the DMA engine.
> let dma_obj = DmaObject::from_data(dev, fw.as_slice())?;
>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 06/10] gpu: nova-core: move brom_params and boot_addr to FalconFirmware
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (4 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 05/10] gpu: nova-core: falcon: remove unwarranted safety check in dma_load Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:52 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
` (4 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
These methods are relevant no matter the loading method used, thus move
them to the common `FalconFirmware` trait.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 12 ++++++------
drivers/gpu/nova-core/firmware/booter.rs | 8 ++++----
drivers/gpu/nova-core/firmware/fwsec.rs | 8 ++++----
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index c02b73b1cfe6..53ee388f88be 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -367,12 +367,6 @@ pub(crate) trait FalconDmaLoadable {
/// Returns the load parameters for `DMEM`.
fn dmem_load_params(&self) -> FalconDmaLoadTarget;
-
- /// Returns the parameters to write into the BROM registers.
- fn brom_params(&self) -> FalconBromParams;
-
- /// Returns the start address of the firmware.
- fn boot_addr(&self) -> u32;
}
/// Trait for a falcon firmware.
@@ -381,6 +375,12 @@ pub(crate) trait FalconDmaLoadable {
pub(crate) trait FalconFirmware {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
+
+ /// Returns the parameters to write into the BROM registers.
+ fn brom_params(&self) -> FalconBromParams;
+
+ /// Returns the start address of the firmware.
+ fn boot_addr(&self) -> u32;
}
/// Contains the base parameters common to all Falcon instances.
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index d569151982d1..7bb732aa4cd6 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -414,6 +414,10 @@ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.dmem_load_target.clone()
}
+}
+
+impl FalconFirmware for BooterFirmware {
+ type Target = Sec2;
fn brom_params(&self) -> FalconBromParams {
self.brom_params.clone()
@@ -427,7 +431,3 @@ fn boot_addr(&self) -> u32 {
}
}
}
-
-impl FalconFirmware for BooterFirmware {
- type Target = Sec2;
-}
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 2ba70e0c5a30..07404164ef12 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -196,6 +196,10 @@ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.desc.dmem_load_params()
}
+}
+
+impl FalconFirmware for FwsecFirmware {
+ type Target = Gsp;
fn brom_params(&self) -> FalconBromParams {
FalconBromParams {
@@ -210,10 +214,6 @@ fn boot_addr(&self) -> u32 {
}
}
-impl FalconFirmware for FwsecFirmware {
- type Target = Gsp;
-}
-
impl FirmwareObject<FwsecFirmware, Unsigned> {
fn new_fwsec(bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
let desc = bios.fwsec_image().header()?;
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 06/10] gpu: nova-core: move brom_params and boot_addr to FalconFirmware
2026-03-01 14:03 ` [PATCH v10 06/10] gpu: nova-core: move brom_params and boot_addr to FalconFirmware Alexandre Courbot
@ 2026-03-06 1:52 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:52 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> These methods are relevant no matter the loading method used, thus move
> them to the common `FalconFirmware` trait.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (5 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 06/10] gpu: nova-core: move brom_params and boot_addr to FalconFirmware Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-05 20:49 ` Timur Tabi
2026-03-01 14:03 ` [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
` (3 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
From: Timur Tabi <ttabi@nvidia.com>
Turing and GA100 use programmed I/O (PIO) instead of DMA to upload
firmware images into Falcon memory.
Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 218 +++++++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/falcon/hal.rs | 6 +-
drivers/gpu/nova-core/regs.rs | 30 +++++
3 files changed, 251 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 53ee388f88be..7097a206ec3c 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -367,6 +367,127 @@ pub(crate) trait FalconDmaLoadable {
/// Returns the load parameters for `DMEM`.
fn dmem_load_params(&self) -> FalconDmaLoadTarget;
+
+ /// Returns an adapter that provides the required parameter to load this firmware using PIO.
+ ///
+ /// This can only fail if some `u32` fields cannot be converted to `u16`, or if the indices in
+ /// the headers are invalid.
+ fn try_as_pio_loadable(&self) -> Result<FalconDmaFirmwarePioAdapter<'_, Self>> {
+ let new_pio_imem = |params: FalconDmaLoadTarget, secure| {
+ let start = usize::from_safe_cast(params.src_start);
+ let end = start + usize::from_safe_cast(params.len);
+ let data = self.as_slice().get(start..end).ok_or(EINVAL)?;
+
+ let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?;
+
+ Ok::<_, Error>(FalconPioImemLoadTarget {
+ data,
+ dst_start,
+ secure,
+ start_tag: dst_start >> 8,
+ })
+ };
+
+ let imem_sec = new_pio_imem(self.imem_sec_load_params(), true)?;
+
+ let imem_ns = if let Some(params) = self.imem_ns_load_params() {
+ Some(new_pio_imem(params, false)?)
+ } else {
+ None
+ };
+
+ let dmem = {
+ let params = self.dmem_load_params();
+ let start = usize::from_safe_cast(params.src_start);
+ let end = start + usize::from_safe_cast(params.len);
+ let data = self.as_slice().get(start..end).ok_or(EINVAL)?;
+
+ let dst_start = u16::try_from(params.dst_start).map_err(|_| EINVAL)?;
+
+ FalconPioDmemLoadTarget { data, dst_start }
+ };
+
+ Ok(FalconDmaFirmwarePioAdapter {
+ fw: self,
+ imem_sec,
+ imem_ns,
+ dmem,
+ })
+ }
+}
+
+/// Represents a portion of the firmware to be loaded into IMEM using PIO.
+#[derive(Clone)]
+pub(crate) struct FalconPioImemLoadTarget<'a> {
+ pub(crate) data: &'a [u8],
+ pub(crate) dst_start: u16,
+ pub(crate) secure: bool,
+ pub(crate) start_tag: u16,
+}
+
+/// Represents a portion of the firmware to be loaded into DMEM using PIO.
+#[derive(Clone)]
+pub(crate) struct FalconPioDmemLoadTarget<'a> {
+ pub(crate) data: &'a [u8],
+ pub(crate) dst_start: u16,
+}
+
+/// Trait for providing PIO load parameters of falcon firmwares.
+pub(crate) trait FalconPioLoadable {
+ /// Returns the load parameters for Secure `IMEM`, if any.
+ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>;
+
+ /// Returns the load parameters for Non-Secure `IMEM`, if any.
+ fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>>;
+
+ /// Returns the load parameters for `DMEM`.
+ fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_>;
+}
+
+/// Adapter type that makes any DMA-loadable firmware also loadable via PIO.
+///
+/// Created using [`FalconDmaLoadable::try_as_pio_loadable`].
+pub(crate) struct FalconDmaFirmwarePioAdapter<'a, T: FalconDmaLoadable + ?Sized> {
+ /// Reference to the DMA firmware.
+ fw: &'a T,
+ /// Validated secure IMEM parameters.
+ imem_sec: FalconPioImemLoadTarget<'a>,
+ /// Validated non-secure IMEM parameters.
+ imem_ns: Option<FalconPioImemLoadTarget<'a>>,
+ /// Validated DMEM parameters.
+ dmem: FalconPioDmemLoadTarget<'a>,
+}
+
+impl<'a, T> FalconPioLoadable for FalconDmaFirmwarePioAdapter<'a, T>
+where
+ T: FalconDmaLoadable + ?Sized,
+{
+ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ Some(self.imem_sec.clone())
+ }
+
+ fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ self.imem_ns.clone()
+ }
+
+ fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
+ self.dmem.clone()
+ }
+}
+
+impl<'a, T> FalconFirmware for FalconDmaFirmwarePioAdapter<'a, T>
+where
+ T: FalconDmaLoadable + FalconFirmware + ?Sized,
+{
+ type Target = <T as FalconFirmware>::Target;
+
+ fn brom_params(&self) -> FalconBromParams {
+ self.fw.brom_params()
+ }
+
+ fn boot_addr(&self) -> u32 {
+ self.fw.boot_addr()
+ }
}
/// Trait for a falcon firmware.
@@ -417,6 +538,98 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result {
Ok(())
}
+ /// Falcons supports up to four ports, but we only ever use one, so just hard-code it.
+ const PIO_PORT: usize = 0;
+
+ /// Write a slice to Falcon IMEM memory using programmed I/O (PIO).
+ ///
+ /// Returns `EINVAL` if `img.len()` is not a multiple of 4.
+ fn pio_wr_imem_slice(&self, bar: &Bar0, load_offsets: FalconPioImemLoadTarget<'_>) -> Result {
+ // Rejecting misaligned images here allows us to avoid checking
+ // inside the loops.
+ if load_offsets.data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ regs::NV_PFALCON_FALCON_IMEMC::default()
+ .set_secure(load_offsets.secure)
+ .set_aincw(true)
+ .set_offs(load_offsets.dst_start)
+ .write(bar, &E::ID, Self::PIO_PORT);
+
+ for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() {
+ let n = u16::try_from(n)?;
+ let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?;
+ regs::NV_PFALCON_FALCON_IMEMT::default().set_tag(tag).write(
+ bar,
+ &E::ID,
+ Self::PIO_PORT,
+ );
+ for word in block.chunks_exact(4) {
+ let w = [word[0], word[1], word[2], word[3]];
+ regs::NV_PFALCON_FALCON_IMEMD::default()
+ .set_data(u32::from_le_bytes(w))
+ .write(bar, &E::ID, Self::PIO_PORT);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Write a slice to Falcon DMEM memory using programmed I/O (PIO).
+ ///
+ /// Returns `EINVAL` if `img.len()` is not a multiple of 4.
+ fn pio_wr_dmem_slice(&self, bar: &Bar0, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result {
+ // Rejecting misaligned images here allows us to avoid checking
+ // inside the loops.
+ if load_offsets.data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ regs::NV_PFALCON_FALCON_DMEMC::default()
+ .set_aincw(true)
+ .set_offs(load_offsets.dst_start)
+ .write(bar, &E::ID, Self::PIO_PORT);
+
+ for word in load_offsets.data.chunks_exact(4) {
+ let w = [word[0], word[1], word[2], word[3]];
+ regs::NV_PFALCON_FALCON_DMEMD::default()
+ .set_data(u32::from_le_bytes(w))
+ .write(bar, &E::ID, Self::PIO_PORT);
+ }
+
+ Ok(())
+ }
+
+ /// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
+ pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
+ &self,
+ bar: &Bar0,
+ fw: &F,
+ ) -> Result {
+ regs::NV_PFALCON_FBIF_CTL::read(bar, &E::ID)
+ .set_allow_phys_no_ctx(true)
+ .write(bar, &E::ID);
+
+ regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
+
+ if let Some(imem_ns) = fw.imem_ns_load_params() {
+ self.pio_wr_imem_slice(bar, imem_ns)?;
+ }
+ if let Some(imem_sec) = fw.imem_sec_load_params() {
+ self.pio_wr_imem_slice(bar, imem_sec)?;
+ }
+ self.pio_wr_dmem_slice(bar, fw.dmem_load_params())?;
+
+ self.hal.program_brom(self, bar, &fw.brom_params())?;
+
+ regs::NV_PFALCON_FALCON_BOOTVEC::default()
+ .set_value(fw.boot_addr())
+ .write(bar, &E::ID);
+
+ Ok(())
+ }
+
/// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
/// `target_mem`.
///
@@ -652,7 +865,8 @@ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
self.hal.is_riscv_active(bar)
}
- // Load a firmware image into Falcon memory
+ /// Load a firmware image into Falcon memory, using the preferred method for the current
+ /// chipset.
pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
&self,
dev: &Device<device::Bound>,
@@ -661,7 +875,7 @@ pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
) -> Result {
match self.hal.load_method() {
LoadMethod::Dma => self.dma_load(dev, bar, fw),
- LoadMethod::Pio => Err(ENOTSUPP),
+ LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?),
}
}
diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs
index 89babd5f9325..a7e5ea8d0272 100644
--- a/drivers/gpu/nova-core/falcon/hal.rs
+++ b/drivers/gpu/nova-core/falcon/hal.rs
@@ -58,7 +58,11 @@ fn signature_reg_fuse_version(
/// Reset the falcon engine.
fn reset_eng(&self, bar: &Bar0) -> Result;
- /// returns the method needed to load data into Falcon memory
+ /// Returns the method used to load data into the falcon's memory.
+ ///
+ /// The only chipsets supporting PIO are those < GA102, and PIO is the preferred method for
+ /// these. For anything above, the PIO registers appear to be masked to the CPU, so DMA is the
+ /// only usable method.
fn load_method(&self) -> LoadMethod;
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index ea0d32f5396c..53f412f0ca32 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -364,6 +364,36 @@ pub(crate) fn with_falcon_mem(self, mem: FalconMem) -> Self {
1:1 startcpu as bool;
});
+// IMEM access control register. Up to 4 ports are available for IMEM access.
+register!(NV_PFALCON_FALCON_IMEMC @ PFalconBase[0x00000180[4; 16]] {
+ 15:0 offs as u16, "IMEM block and word offset";
+ 24:24 aincw as bool, "Auto-increment on write";
+ 28:28 secure as bool, "Access secure IMEM";
+});
+
+// IMEM data register. Reading/writing this register accesses IMEM at the address
+// specified by the corresponding IMEMC register.
+register!(NV_PFALCON_FALCON_IMEMD @ PFalconBase[0x00000184[4; 16]] {
+ 31:0 data as u32;
+});
+
+// IMEM tag register. Used to set the tag for the current IMEM block.
+register!(NV_PFALCON_FALCON_IMEMT @ PFalconBase[0x00000188[4; 16]] {
+ 15:0 tag as u16;
+});
+
+// DMEM access control register. Up to 8 ports are available for DMEM access.
+register!(NV_PFALCON_FALCON_DMEMC @ PFalconBase[0x000001c0[8; 8]] {
+ 15:0 offs as u16, "DMEM block and word offset";
+ 24:24 aincw as bool, "Auto-increment on write";
+});
+
+// DMEM data register. Reading/writing this register accesses DMEM at the address
+// specified by the corresponding DMEMC register.
+register!(NV_PFALCON_FALCON_DMEMD @ PFalconBase[0x000001c4[8; 8]] {
+ 31:0 data as u32;
+});
+
// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the falcon
// instance.
register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] {
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images
2026-03-01 14:03 ` [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
@ 2026-03-05 20:49 ` Timur Tabi
2026-03-06 1:05 ` Alexandre Courbot
0 siblings, 1 reply; 27+ messages in thread
From: Timur Tabi @ 2026-03-05 20:49 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, aliceryhl@google.com,
airlied@gmail.com, simona@ffwll.ch
Cc: Alistair Popple, John Hubbard, Edwin Peer,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
Eliot Courtney, nouveau@lists.freedesktop.org, Joel Fernandes,
linux-kernel@vger.kernel.org
On Sun, 2026-03-01 at 23:03 +0900, Alexandre Courbot wrote:
> ) -> Result {
> match self.hal.load_method() {
> LoadMethod::Dma => self.dma_load(dev, bar, fw),
> - LoadMethod::Pio => Err(ENOTSUPP),
> + LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?),
Wouldn't it be simpler to have pio_load() just extract the IMEM/DMEM values it needs from &fw,
instead of creating an adapter? The adapter just seems like overkill for something that's used only
once.
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images
2026-03-05 20:49 ` Timur Tabi
@ 2026-03-06 1:05 ` Alexandre Courbot
0 siblings, 0 replies; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-06 1:05 UTC (permalink / raw)
To: Timur Tabi
Cc: dakr@kernel.org, aliceryhl@google.com, airlied@gmail.com,
simona@ffwll.ch, Alistair Popple, John Hubbard, Edwin Peer,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
Eliot Courtney, nouveau@lists.freedesktop.org, Joel Fernandes,
linux-kernel@vger.kernel.org
On Fri Mar 6, 2026 at 5:49 AM JST, Timur Tabi wrote:
> On Sun, 2026-03-01 at 23:03 +0900, Alexandre Courbot wrote:
>> ) -> Result {
>> match self.hal.load_method() {
>> LoadMethod::Dma => self.dma_load(dev, bar, fw),
>> - LoadMethod::Pio => Err(ENOTSUPP),
>> + LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?),
>
> Wouldn't it be simpler to have pio_load() just extract the IMEM/DMEM values it needs from &fw,
> instead of creating an adapter? The adapter just seems like overkill for something that's used only
> once.
pio_load's job is to load a firmware, period. If we start doing
something else we distract it from its purpose and end up with a messy
soup doing many different things.
The differences between PIO and DMA are minimal but subtle enough (tag
for IMEM sections, smaller `dst_start`, ...) that we need to have
different parameter types for both and a fallible conversion step. Of
course I'd prefer to do without an adapter but it's not as simple as
that (or please show me with code how to do it).
Actually I'm thinking that we should maybe move all the PIO-related
stuff into its own `pio` submodule so we don't clutter `falcon.rs` with
this.
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (6 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 07/10] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-02 7:22 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 09/10] gpu: nova-core: make Chipset::arch() const Alexandre Courbot
` (2 subsequent siblings)
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
From: Timur Tabi <ttabi@nvidia.com>
On Turing and GA100, a new firmware image called the Generic Bootloader
(gen_bootloader) must be used to load FWSEC into Falcon memory. The
driver loads the generic bootloader into Falcon IMEM, passes a
descriptor that points to FWSEC using DMEM, and then boots the generic
bootloader. The bootloader will then load FWSEC into IMEM and boot it.
Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/firmware/fwsec.rs | 6 +
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 289 +++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 15 +-
3 files changed, 307 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 07404164ef12..afa39f04cc8a 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -10,6 +10,8 @@
//! - The command to be run, as this firmware can perform several tasks ;
//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
+pub(crate) mod bootloader;
+
use core::marker::PhantomData;
use kernel::{
@@ -378,6 +380,10 @@ pub(crate) fn new(
}
/// Loads the FWSEC firmware into `falcon` and execute it.
+ ///
+ /// This must only be called on chipsets that do not need the FWSEC bootloader (i.e., where
+ /// [`Chipset::needs_fwsec_bootloader()`](crate::gpu::Chipset::needs_fwsec_bootloader) returns
+ /// `false`). On chipsets that do, use [`bootloader::FwsecFirmwareWithBl`] instead.
pub(crate) fn run(
&self,
dev: &Device<device::Bound>,
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
new file mode 100644
index 000000000000..dcde2d21dd4e
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bootloader support for the FWSEC firmware.
+//!
+//! On Turing, the FWSEC firmware is not loaded directly, but is instead loaded through a small
+//! bootloader program that performs the required DMA operations. This bootloader itself needs to
+//! be loaded using PIO.
+
+use kernel::{
+ alloc::KVec,
+ device::{
+ self,
+ Device, //
+ },
+ prelude::*,
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ sizes,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ falcon::{
+ self,
+ gsp::Gsp,
+ Falcon,
+ FalconBromParams,
+ FalconDmaLoadable,
+ FalconEngine,
+ FalconFbifMemType,
+ FalconFbifTarget,
+ FalconFirmware,
+ FalconPioDmemLoadTarget,
+ FalconPioImemLoadTarget,
+ FalconPioLoadable, //
+ },
+ firmware::{
+ fwsec::FwsecFirmware,
+ request_firmware,
+ BinHdr,
+ FIRMWARE_VERSION, //
+ },
+ gpu::Chipset,
+ num::FromSafeCast,
+ regs,
+};
+
+/// Descriptor used by RM to figure out the requirements of the boot loader.
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct BootloaderDesc {
+ /// Starting tag of bootloader.
+ start_tag: u32,
+ /// DMEM offset where [`BootloaderDmemDescV2`] is to be loaded.
+ dmem_load_off: u32,
+ /// Offset of code section in the image.
+ code_off: u32,
+ /// Size of code section in the image.
+ code_size: u32,
+ /// Offset of data section in the image.
+ data_off: u32,
+ /// Size of data section in the image.
+ data_size: u32,
+}
+// SAFETY: any byte sequence is valid for this struct.
+unsafe impl FromBytes for BootloaderDesc {}
+
+/// Structure used by the boot-loader to load the rest of the code.
+///
+/// This has to be filled by the GPU driver and copied into DMEM at offset
+/// [`BootloaderDesc.dmem_load_off`].
+#[repr(C, packed)]
+#[derive(Debug, Clone)]
+struct BootloaderDmemDescV2 {
+ /// Reserved, should always be first element.
+ reserved: [u32; 4],
+ /// 16B signature for secure code, 0s if no secure code.
+ signature: [u32; 4],
+ /// DMA context used by the bootloader while loading code/data.
+ ctx_dma: u32,
+ /// 256B-aligned physical FB address where code is located.
+ code_dma_base: u64,
+ /// Offset from `code_dma_base` where the non-secure code is located (must be multiple of 256).
+ non_sec_code_off: u32,
+ /// Size of the non-secure code part.
+ non_sec_code_size: u32,
+ /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256).
+ sec_code_off: u32,
+ /// Size of the secure code part.
+ sec_code_size: u32,
+ /// Code entry point invoked by the bootloader after code is loaded.
+ code_entry_point: u32,
+ /// 256B-aligned physical FB address where data is located.
+ data_dma_base: u64,
+ /// Size of data block (should be multiple of 256B).
+ data_size: u32,
+ /// Number of arguments to be passed to the target firmware being loaded.
+ argc: u32,
+ /// Arguments to be passed to the target firmware being loaded.
+ argv: u32,
+}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for BootloaderDmemDescV2 {}
+
+/// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load
+/// operation.
+pub(crate) struct FwsecFirmwareWithBl {
+ /// DMA object the bootloader will copy the firmware from.
+ _firmware_dma: DmaObject,
+ /// Code of the bootloader to be loaded into non-secure IMEM.
+ ucode: KVec<u8>,
+ /// Descriptor to be loaded into DMEM for the bootloader to read.
+ dmem_desc: BootloaderDmemDescV2,
+ /// Range-validated start offset of the firmware code in IMEM.
+ imem_dst_start: u16,
+ /// BROM parameters of the loaded firmware.
+ brom_params: FalconBromParams,
+ /// Range-validated `desc.start_tag`.
+ start_tag: u16,
+}
+
+impl FwsecFirmwareWithBl {
+ /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be
+ /// loaded using it.
+ pub(crate) fn new(
+ firmware: FwsecFirmware,
+ dev: &Device<device::Bound>,
+ chipset: Chipset,
+ ) -> Result<Self> {
+ let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?;
+ let hdr = fw
+ .data()
+ .get(0..size_of::<BinHdr>())
+ .and_then(BinHdr::from_bytes_copy)
+ .ok_or(EINVAL)?;
+
+ let desc = {
+ let desc_offset = usize::from_safe_cast(hdr.header_offset);
+
+ fw.data()
+ .get(desc_offset..)
+ .and_then(BootloaderDesc::from_bytes_copy_prefix)
+ .ok_or(EINVAL)?
+ .0
+ };
+
+ let ucode = {
+ let ucode_start = usize::from_safe_cast(hdr.data_offset);
+ let code_size = usize::from_safe_cast(desc.code_size);
+ // Align to falcon block size (256 bytes).
+ let aligned_code_size = code_size
+ .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
+ .ok_or(EINVAL)?;
+
+ let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
+ ucode.extend_from_slice(
+ fw.data()
+ .get(ucode_start..ucode_start + code_size)
+ .ok_or(EINVAL)?,
+ GFP_KERNEL,
+ )?;
+ ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
+
+ ucode
+ };
+
+ let firmware_dma = DmaObject::from_data(dev, &firmware.ucode.0)?;
+
+ let dmem_desc = {
+ let imem_sec = FalconDmaLoadable::imem_sec_load_params(&firmware);
+ let imem_ns = FalconDmaLoadable::imem_ns_load_params(&firmware).ok_or(EINVAL)?;
+ let dmem = FalconDmaLoadable::dmem_load_params(&firmware);
+
+ BootloaderDmemDescV2 {
+ reserved: [0; 4],
+ signature: [0; 4],
+ ctx_dma: 4, // FALCON_DMAIDX_PHYS_SYS_NCOH
+ code_dma_base: firmware_dma.dma_handle(),
+ non_sec_code_off: imem_ns.dst_start,
+ non_sec_code_size: imem_ns.len,
+ sec_code_off: imem_sec.dst_start,
+ sec_code_size: imem_sec.len,
+ code_entry_point: 0,
+ data_dma_base: firmware_dma.dma_handle() + u64::from(dmem.src_start),
+ data_size: dmem.len,
+ argc: 0,
+ argv: 0,
+ }
+ };
+
+ // The bootloader's code must be loaded in the area right below the first 64K of IMEM.
+ const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K;
+ let imem_dst_start = BOOTLOADER_LOAD_CEILING
+ .checked_sub(ucode.len())
+ .ok_or(EOVERFLOW)?;
+
+ Ok(Self {
+ _firmware_dma: firmware_dma,
+ ucode,
+ dmem_desc,
+ brom_params: firmware.brom_params(),
+ imem_dst_start: u16::try_from(imem_dst_start)?,
+ start_tag: u16::try_from(desc.start_tag)?,
+ })
+ }
+
+ /// Loads the bootloader into `falcon` and execute it.
+ ///
+ /// The bootloader will load the FWSEC firmware and then execute it. This function returns
+ /// after FWSEC has reached completion.
+ pub(crate) fn run(
+ &self,
+ dev: &Device<device::Bound>,
+ falcon: &Falcon<Gsp>,
+ bar: &Bar0,
+ ) -> Result<()> {
+ // Reset falcon, load the firmware, and run it.
+ falcon
+ .reset(bar)
+ .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
+ falcon
+ .pio_load(bar, self)
+ .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
+
+ // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory.
+ regs::NV_PFALCON_FBIF_TRANSCFG::try_update(
+ bar,
+ &Gsp::ID,
+ usize::from_safe_cast(self.dmem_desc.ctx_dma),
+ |v| {
+ v.set_target(FalconFbifTarget::CoherentSysmem)
+ .set_mem_type(FalconFbifMemType::Physical)
+ },
+ )?;
+
+ let (mbox0, _) = falcon
+ .boot(bar, Some(0), None)
+ .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;
+ if mbox0 != 0 {
+ dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);
+ Err(EIO)
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl FalconFirmware for FwsecFirmwareWithBl {
+ type Target = Gsp;
+
+ fn brom_params(&self) -> FalconBromParams {
+ self.brom_params.clone()
+ }
+
+ fn boot_addr(&self) -> u32 {
+ // On V2 platforms, the boot address is extracted from the generic bootloader, because the
+ // gbl is what actually copies FWSEC into memory, so that is what needs to be booted.
+ u32::from(self.start_tag) << 8
+ }
+}
+
+impl FalconPioLoadable for FwsecFirmwareWithBl {
+ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ None
+ }
+
+ fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ Some(FalconPioImemLoadTarget {
+ data: self.ucode.as_ref(),
+ dst_start: self.imem_dst_start,
+ secure: false,
+ start_tag: self.start_tag,
+ })
+ }
+
+ fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
+ FalconPioDmemLoadTarget {
+ data: self.dmem_desc.as_bytes(),
+ dst_start: 0,
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 78957ed8814f..9a00ddb922ac 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -24,6 +24,7 @@
BooterKind, //
},
fwsec::{
+ bootloader::FwsecFirmwareWithBl,
FwsecCommand,
FwsecFirmware, //
},
@@ -48,6 +49,7 @@ impl super::Gsp {
/// created the WPR2 region.
fn run_fwsec_frts(
dev: &device::Device<device::Bound>,
+ chipset: Chipset,
falcon: &Falcon<Gsp>,
bar: &Bar0,
bios: &Vbios,
@@ -63,6 +65,7 @@ fn run_fwsec_frts(
return Err(EBUSY);
}
+ // FWSEC-FRTS will create the WPR2 region.
let fwsec_frts = FwsecFirmware::new(
dev,
falcon,
@@ -74,8 +77,14 @@ fn run_fwsec_frts(
},
)?;
- // Run FWSEC-FRTS to create the WPR2 region.
- fwsec_frts.run(dev, falcon, bar)?;
+ if chipset.needs_fwsec_bootloader() {
+ let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
+ // Load and run the bootloader, which will load FWSEC-FRTS and run it.
+ fwsec_frts_bl.run(dev, falcon, bar)?;
+ } else {
+ // Load and run FWSEC-FRTS directly.
+ fwsec_frts.run(dev, falcon, bar)?;
+ }
// SCRATCH_E contains the error code for FWSEC-FRTS.
let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
@@ -144,7 +153,7 @@ pub(crate) fn boot(
let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
- Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
+ Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?;
let booter_loader = BooterFirmware::new(
dev,
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-03-01 14:03 ` [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
@ 2026-03-02 7:22 ` Eliot Courtney
2026-03-05 15:09 ` Alexandre Courbot
0 siblings, 1 reply; 27+ messages in thread
From: Eliot Courtney @ 2026-03-02 7:22 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: Alistair Popple, Joel Fernandes, Eliot Courtney, nouveau,
rust-for-linux, dri-devel, linux-kernel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> From: Timur Tabi <ttabi@nvidia.com>
> +impl FwsecFirmwareWithBl {
> + /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be
> + /// loaded using it.
> + pub(crate) fn new(
> + firmware: FwsecFirmware,
> + dev: &Device<device::Bound>,
> + chipset: Chipset,
> + ) -> Result<Self> {
> + let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?;
> + let hdr = fw
> + .data()
> + .get(0..size_of::<BinHdr>())
> + .and_then(BinHdr::from_bytes_copy)
> + .ok_or(EINVAL)?;
> +
> + let desc = {
> + let desc_offset = usize::from_safe_cast(hdr.header_offset);
> +
> + fw.data()
> + .get(desc_offset..)
> + .and_then(BootloaderDesc::from_bytes_copy_prefix)
> + .ok_or(EINVAL)?
> + .0
> + };
> +
> + let ucode = {
> + let ucode_start = usize::from_safe_cast(hdr.data_offset);
> + let code_size = usize::from_safe_cast(desc.code_size);
> + // Align to falcon block size (256 bytes).
> + let aligned_code_size = code_size
> + .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
> + .ok_or(EINVAL)?;
> +
> + let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
> + ucode.extend_from_slice(
> + fw.data()
> + .get(ucode_start..ucode_start + code_size)
> + .ok_or(EINVAL)?,
> + GFP_KERNEL,
> + )?;
> + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
> +
> + ucode
> + };
> +
> + let firmware_dma = DmaObject::from_data(dev, &firmware.ucode.0)?;
> +
> + let dmem_desc = {
> + let imem_sec = FalconDmaLoadable::imem_sec_load_params(&firmware);
> + let imem_ns = FalconDmaLoadable::imem_ns_load_params(&firmware).ok_or(EINVAL)?;
> + let dmem = FalconDmaLoadable::dmem_load_params(&firmware);
> +
> + BootloaderDmemDescV2 {
> + reserved: [0; 4],
> + signature: [0; 4],
> + ctx_dma: 4, // FALCON_DMAIDX_PHYS_SYS_NCOH
> + code_dma_base: firmware_dma.dma_handle(),
> + non_sec_code_off: imem_ns.dst_start,
> + non_sec_code_size: imem_ns.len,
> + sec_code_off: imem_sec.dst_start,
Is it correct here to use `dst_start` for `non_sec_code_off` and `sec_code_off`?
AFAICT, these are meant to be offsets from the base of the DMA memory and
it's meant to copy into the falcon. But the documentation on `dst_start`
says `Offset from the start of the destination memory to copy into.`
Also `ucode_start` is defined as `hdr.data_offset`
but doesn't add `code_off` from `BootloaderDesc` and `code_off` appears
unused. So does `data_off` and `dmem_load_off` for that matter.
I find it hard to follow what's actually required but it seems odd that
none of these are used.
At least for `code_off` should it not be part of the computation of `ucode_start`?
`let ucode_start = usize::from_safe_cast(hdr.data_offset);`
Overall I find the dst/srcs here confusing cos it feels hard to keep
track of which set of memory things are in. So, sorry if these comments
are nonsense.
> +
> + Ok(Self {
> + _firmware_dma: firmware_dma,
> + ucode,
> + dmem_desc,
> + brom_params: firmware.brom_params(),
> + imem_dst_start: u16::try_from(imem_dst_start)?,
> + start_tag: u16::try_from(desc.start_tag)?,
> + })
> + }
> +
> + /// Loads the bootloader into `falcon` and execute it.
> + ///
> + /// The bootloader will load the FWSEC firmware and then execute it. This function returns
> + /// after FWSEC has reached completion.
> + pub(crate) fn run(
> + &self,
> + dev: &Device<device::Bound>,
> + falcon: &Falcon<Gsp>,
> + bar: &Bar0,
> + ) -> Result<()> {
> + // Reset falcon, load the firmware, and run it.
> + falcon
> + .reset(bar)
> + .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
> + falcon
> + .pio_load(bar, self)
> + .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
> +
> + // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory.
> + regs::NV_PFALCON_FBIF_TRANSCFG::try_update(
> + bar,
> + &Gsp::ID,
> + usize::from_safe_cast(self.dmem_desc.ctx_dma),
> + |v| {
> + v.set_target(FalconFbifTarget::CoherentSysmem)
> + .set_mem_type(FalconFbifMemType::Physical)
> + },
> + )?;
> +
> + let (mbox0, _) = falcon
> + .boot(bar, Some(0), None)
> + .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;
> + if mbox0 != 0 {
> + dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);
> + Err(EIO)
> + } else {
> + Ok(())
> + }
> + }
> +}
> +
> +impl FalconFirmware for FwsecFirmwareWithBl {
> + type Target = Gsp;
> +
> + fn brom_params(&self) -> FalconBromParams {
> + self.brom_params.clone()
> + }
> +
> + fn boot_addr(&self) -> u32 {
> + // On V2 platforms, the boot address is extracted from the generic bootloader, because the
> + // gbl is what actually copies FWSEC into memory, so that is what needs to be booted.
> + u32::from(self.start_tag) << 8
> + }
> +}
> +
> +impl FalconPioLoadable for FwsecFirmwareWithBl {
> + fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
> + None
> + }
> +
> + fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
> + Some(FalconPioImemLoadTarget {
> + data: self.ucode.as_ref(),
> + dst_start: self.imem_dst_start,
> + secure: false,
> + start_tag: self.start_tag,
> + })
> + }
> +
> + fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
> + FalconPioDmemLoadTarget {
> + data: self.dmem_desc.as_bytes(),
> + dst_start: 0,
> + }
> + }
> +}
Here in `FwsecFirmwareWithBl::dmem_load_params` it returns dst_start as 0,
except the docs say 'This has to be filled by the GPU driver and copied into DMEM at offset
/// [`BootloaderDesc.dmem_load_off`].'
> diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
> index 78957ed8814f..9a00ddb922ac 100644
> --- a/drivers/gpu/nova-core/gsp/boot.rs
> +++ b/drivers/gpu/nova-core/gsp/boot.rs
> @@ -24,6 +24,7 @@
> BooterKind, //
> },
> fwsec::{
> + bootloader::FwsecFirmwareWithBl,
> FwsecCommand,
> FwsecFirmware, //
> },
> @@ -48,6 +49,7 @@ impl super::Gsp {
> /// created the WPR2 region.
> fn run_fwsec_frts(
> dev: &device::Device<device::Bound>,
> + chipset: Chipset,
> falcon: &Falcon<Gsp>,
> bar: &Bar0,
> bios: &Vbios,
> @@ -63,6 +65,7 @@ fn run_fwsec_frts(
> return Err(EBUSY);
> }
>
> + // FWSEC-FRTS will create the WPR2 region.
> let fwsec_frts = FwsecFirmware::new(
> dev,
> falcon,
> @@ -74,8 +77,14 @@ fn run_fwsec_frts(
> },
> )?;
>
> - // Run FWSEC-FRTS to create the WPR2 region.
> - fwsec_frts.run(dev, falcon, bar)?;
> + if chipset.needs_fwsec_bootloader() {
> + let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
> + // Load and run the bootloader, which will load FWSEC-FRTS and run it.
> + fwsec_frts_bl.run(dev, falcon, bar)?;
> + } else {
> + // Load and run FWSEC-FRTS directly.
> + fwsec_frts.run(dev, falcon, bar)?;
> + }
>
> // SCRATCH_E contains the error code for FWSEC-FRTS.
> let frts_status = regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
> @@ -144,7 +153,7 @@ pub(crate) fn boot(
> let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
> dev_dbg!(dev, "{:#x?}\n", fb_layout);
>
> - Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
> + Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?;
>
> let booter_loader = BooterFirmware::new(
> dev,
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-03-02 7:22 ` Eliot Courtney
@ 2026-03-05 15:09 ` Alexandre Courbot
2026-03-09 4:51 ` Eliot Courtney
0 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-05 15:09 UTC (permalink / raw)
To: Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Alistair Popple, Joel Fernandes, nouveau, rust-for-linux,
dri-devel, linux-kernel
On Mon Mar 2, 2026 at 4:22 PM JST, Eliot Courtney wrote:
> On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
>> From: Timur Tabi <ttabi@nvidia.com>
>> +impl FwsecFirmwareWithBl {
>> + /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be
>> + /// loaded using it.
>> + pub(crate) fn new(
>> + firmware: FwsecFirmware,
>> + dev: &Device<device::Bound>,
>> + chipset: Chipset,
>> + ) -> Result<Self> {
>> + let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?;
>> + let hdr = fw
>> + .data()
>> + .get(0..size_of::<BinHdr>())
>> + .and_then(BinHdr::from_bytes_copy)
>> + .ok_or(EINVAL)?;
>> +
>> + let desc = {
>> + let desc_offset = usize::from_safe_cast(hdr.header_offset);
>> +
>> + fw.data()
>> + .get(desc_offset..)
>> + .and_then(BootloaderDesc::from_bytes_copy_prefix)
>> + .ok_or(EINVAL)?
>> + .0
>> + };
>> +
>> + let ucode = {
>> + let ucode_start = usize::from_safe_cast(hdr.data_offset);
>> + let code_size = usize::from_safe_cast(desc.code_size);
>> + // Align to falcon block size (256 bytes).
>> + let aligned_code_size = code_size
>> + .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
>> + .ok_or(EINVAL)?;
>> +
>> + let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
>> + ucode.extend_from_slice(
>> + fw.data()
>> + .get(ucode_start..ucode_start + code_size)
>> + .ok_or(EINVAL)?,
>> + GFP_KERNEL,
>> + )?;
>> + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
>> +
>> + ucode
>> + };
>> +
>> + let firmware_dma = DmaObject::from_data(dev, &firmware.ucode.0)?;
>> +
>> + let dmem_desc = {
>> + let imem_sec = FalconDmaLoadable::imem_sec_load_params(&firmware);
>> + let imem_ns = FalconDmaLoadable::imem_ns_load_params(&firmware).ok_or(EINVAL)?;
>> + let dmem = FalconDmaLoadable::dmem_load_params(&firmware);
>> +
>> + BootloaderDmemDescV2 {
>> + reserved: [0; 4],
>> + signature: [0; 4],
>> + ctx_dma: 4, // FALCON_DMAIDX_PHYS_SYS_NCOH
>> + code_dma_base: firmware_dma.dma_handle(),
>> + non_sec_code_off: imem_ns.dst_start,
>> + non_sec_code_size: imem_ns.len,
>> + sec_code_off: imem_sec.dst_start,
>
> Is it correct here to use `dst_start` for `non_sec_code_off` and `sec_code_off`?
> AFAICT, these are meant to be offsets from the base of the DMA memory and
> it's meant to copy into the falcon. But the documentation on `dst_start`
> says `Offset from the start of the destination memory to copy into.`
OpenRM does the following:
pUcode->imemNsPa = pDescV2->IMEMPhysBase;
...
blDmemDesc.nonSecureCodeOff = pUcode->imemNsPa;
and
pUcode->imemSecPa = pDescV2->IMEMSecBase - pDescV2->IMEMVirtBase + pDescV2->IMEMPhysBase;
...
blDmemDesc.secureCodeOff = pUcode->imemSecPa;
So it *does* set IMEM addresses (i.e. destination) into these fields as
well. And their documentation is the same as Nova, which is all the more
intriguing.
But the reason for this became clear after I figured out that the FWSEC
firmware was a *mirror* image of what is expected in IMEM. The IMEM
destination offsets are also the offsets from the start of the source
DMA object, hence their use in `BootloaderDmemDescV2` - for the
bootloader, they are the offsets from both the source AND the
destination!
I've found a couple of differences while reviewing the code though.
Nova-core did not do the `- pDescV2->IMEMVirtBase +
pDescV2->IMEMPhysBase` part, and it did not pad the FWSEC start image
with zeroes up to `IMEMPhysBase` like OpenRM does (to have this
source/destination symmetry). This happened to work because these values
are all zero in practice, but we want to align the code to do the
correct thing otherwise we have a theoretical risk of mismatch.
>
> Also `ucode_start` is defined as `hdr.data_offset`
> but doesn't add `code_off` from `BootloaderDesc` and `code_off` appears
> unused. So does `data_off` and `dmem_load_off` for that matter.
> I find it hard to follow what's actually required but it seems odd that
> none of these are used.
>
> At least for `code_off` should it not be part of the computation of `ucode_start`?
> `let ucode_start = usize::from_safe_cast(hdr.data_offset);`
These two appear to be correct, at least if we follow OpenRM. For IMEM:
// Copy bootloader to top of IMEM
virtAddr = pBlUcDesc->blStartTag << 8;
NV_ASSERT_OK_OR_RETURN(
s_imemCopyTo_TU102(pGpu, pKernelFlcn,
imemDstBlk << FALCON_IMEM_BLKSIZE2,
pBlImg, // <-- start of image, no `code_off`.
blSize,
NV_FALSE,
virtAddr));
The `BootloaderDesc` is only used for `code_size` and `start_tag`. And
it's not strange considering that it just contains the code to load into
IMEM - the data being the `BootloaderDmemDescV2` that we construct from
the actual firmware. Since there is only one segment in that binary, it
makes sense that it starts at offset zero.
And here is what OpenRM does for DMEM:
// Copy dmem desc to DMEM offset 0
NV_ASSERT_OK_OR_RETURN(
s_dmemCopyTo_TU102(pGpu, pKernelFlcn,
0,
(NvU8 *) &blDmemDesc,
sizeof(RM_FLCN_BL_DMEM_DESC)));
Here too it makes sense that we load at offset 0, since there is only
one data segment (the `BootloaderDmemDescV2`) that we build ourselves.
There is no other data to place anywhere.
I suspect `BootloaderDesc` (or `RM_FLCN_BL_IMG_HEADER` in OpenRM) was
recycled from some older (pre-OpenRM), more complex firmware and
inherited its documentation only serves a limited purpose here.
The comments on `BootloaderDesc` are incorrect for us though. I'll amend
them.
>
> Overall I find the dst/srcs here confusing cos it feels hard to keep
> track of which set of memory things are in. So, sorry if these comments
> are nonsense.
The documentation clearly sends us on the wrong path, so good thing we
fixed it.
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-03-05 15:09 ` Alexandre Courbot
@ 2026-03-09 4:51 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-09 4:51 UTC (permalink / raw)
To: Alexandre Courbot, Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Alistair Popple, Joel Fernandes, nouveau, rust-for-linux,
dri-devel, linux-kernel, dri-devel
On Fri Mar 6, 2026 at 12:09 AM JST, Alexandre Courbot wrote:
> On Mon Mar 2, 2026 at 4:22 PM JST, Eliot Courtney wrote:
>> On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
>>> From: Timur Tabi <ttabi@nvidia.com>
>>> +impl FwsecFirmwareWithBl {
>>> + /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be
>>> + /// loaded using it.
>>> + pub(crate) fn new(
>>> + firmware: FwsecFirmware,
>>> + dev: &Device<device::Bound>,
>>> + chipset: Chipset,
>>> + ) -> Result<Self> {
>>> + let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?;
>>> + let hdr = fw
>>> + .data()
>>> + .get(0..size_of::<BinHdr>())
>>> + .and_then(BinHdr::from_bytes_copy)
>>> + .ok_or(EINVAL)?;
>>> +
>>> + let desc = {
>>> + let desc_offset = usize::from_safe_cast(hdr.header_offset);
>>> +
>>> + fw.data()
>>> + .get(desc_offset..)
>>> + .and_then(BootloaderDesc::from_bytes_copy_prefix)
>>> + .ok_or(EINVAL)?
>>> + .0
>>> + };
>>> +
>>> + let ucode = {
>>> + let ucode_start = usize::from_safe_cast(hdr.data_offset);
>>> + let code_size = usize::from_safe_cast(desc.code_size);
>>> + // Align to falcon block size (256 bytes).
>>> + let aligned_code_size = code_size
>>> + .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
>>> + .ok_or(EINVAL)?;
>>> +
>>> + let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
>>> + ucode.extend_from_slice(
>>> + fw.data()
>>> + .get(ucode_start..ucode_start + code_size)
>>> + .ok_or(EINVAL)?,
>>> + GFP_KERNEL,
>>> + )?;
>>> + ucode.resize(aligned_code_size, 0, GFP_KERNEL)?;
>>> +
>>> + ucode
>>> + };
>>> +
>>> + let firmware_dma = DmaObject::from_data(dev, &firmware.ucode.0)?;
>>> +
>>> + let dmem_desc = {
>>> + let imem_sec = FalconDmaLoadable::imem_sec_load_params(&firmware);
>>> + let imem_ns = FalconDmaLoadable::imem_ns_load_params(&firmware).ok_or(EINVAL)?;
>>> + let dmem = FalconDmaLoadable::dmem_load_params(&firmware);
>>> +
>>> + BootloaderDmemDescV2 {
>>> + reserved: [0; 4],
>>> + signature: [0; 4],
>>> + ctx_dma: 4, // FALCON_DMAIDX_PHYS_SYS_NCOH
>>> + code_dma_base: firmware_dma.dma_handle(),
>>> + non_sec_code_off: imem_ns.dst_start,
>>> + non_sec_code_size: imem_ns.len,
>>> + sec_code_off: imem_sec.dst_start,
>>
>> Is it correct here to use `dst_start` for `non_sec_code_off` and `sec_code_off`?
>> AFAICT, these are meant to be offsets from the base of the DMA memory and
>> it's meant to copy into the falcon. But the documentation on `dst_start`
>> says `Offset from the start of the destination memory to copy into.`
>
> OpenRM does the following:
>
> pUcode->imemNsPa = pDescV2->IMEMPhysBase;
> ...
> blDmemDesc.nonSecureCodeOff = pUcode->imemNsPa;
>
> and
>
> pUcode->imemSecPa = pDescV2->IMEMSecBase - pDescV2->IMEMVirtBase + pDescV2->IMEMPhysBase;
> ...
> blDmemDesc.secureCodeOff = pUcode->imemSecPa;
>
> So it *does* set IMEM addresses (i.e. destination) into these fields as
> well. And their documentation is the same as Nova, which is all the more
> intriguing.
>
> But the reason for this became clear after I figured out that the FWSEC
> firmware was a *mirror* image of what is expected in IMEM. The IMEM
> destination offsets are also the offsets from the start of the source
> DMA object, hence their use in `BootloaderDmemDescV2` - for the
> bootloader, they are the offsets from both the source AND the
> destination!
>
> I've found a couple of differences while reviewing the code though.
> Nova-core did not do the `- pDescV2->IMEMVirtBase +
> pDescV2->IMEMPhysBase` part, and it did not pad the FWSEC start image
> with zeroes up to `IMEMPhysBase` like OpenRM does (to have this
> source/destination symmetry). This happened to work because these values
> are all zero in practice, but we want to align the code to do the
> correct thing otherwise we have a theoretical risk of mismatch.
Thanks for looking into this - the mirrored firmware layout thing helps
explain a lot why `dst_start` is correct to use here and `src_start`
is incorect.
>
>>
>> Also `ucode_start` is defined as `hdr.data_offset`
>> but doesn't add `code_off` from `BootloaderDesc` and `code_off` appears
>> unused. So does `data_off` and `dmem_load_off` for that matter.
>> I find it hard to follow what's actually required but it seems odd that
>> none of these are used.
>>
>> At least for `code_off` should it not be part of the computation of `ucode_start`?
>> `let ucode_start = usize::from_safe_cast(hdr.data_offset);`
>
> These two appear to be correct, at least if we follow OpenRM. For IMEM:
>
> // Copy bootloader to top of IMEM
> virtAddr = pBlUcDesc->blStartTag << 8;
> NV_ASSERT_OK_OR_RETURN(
> s_imemCopyTo_TU102(pGpu, pKernelFlcn,
> imemDstBlk << FALCON_IMEM_BLKSIZE2,
> pBlImg, // <-- start of image, no `code_off`.
> blSize,
> NV_FALSE,
> virtAddr));
>
> The `BootloaderDesc` is only used for `code_size` and `start_tag`. And
> it's not strange considering that it just contains the code to load into
> IMEM - the data being the `BootloaderDmemDescV2` that we construct from
> the actual firmware. Since there is only one segment in that binary, it
> makes sense that it starts at offset zero.
I looked into this some more and I believe we probably should include
`code_off` here, even though currently it doesn't matter. First, here is
what I found out about the firmware layout, which might be useful
background so let me record it here for posterity:
In OpenRM, the gen_bootloader firmware in [0], and it's split out into:
- ksec2BinArchiveBlUcode_TU102_ucode_image_data
- ksec2BinArchiveBlUcode_TU102_ucode_desc_data
Which are the image data and descriptor of the firmware.
This script creates the gen_bootloader binary that nova uses [1].
The script writes gen_bootloader.bin here[2]. It creates the
`nvfw_bin_hdr` which IIUC corresponds to `BinHdr` in nova. That would
make `BinHdr::data_offset` equal to `24 + descriptor_size` = 48 and
the .bin size `816` (this corresponds exactly to the data
offset of 0x200+data size of 0x100 + the 48 bytes of header, and to the
file size of the binary firmware file, and to the uncompressed size of
the ucode_image + 48 bytes of header). After that it puts in the
descriptor, which corresponds to
ksec2BinArchiveBlUcode_TU102_ucode_desc_data (which is 24 bytes, type is
`RM_FLCN_BL_DESC` in openrm) and includes the code offset field (which
is zero):
```
typedef struct _def_rm_flcn_bl_img_header
{
/*!
* Offset of code section in the image.
*/
NvU32 blCodeOffset;
/*!
* Size of code section in the image.
*/
NvU32 blCodeSize;
/*!
* Offset of data section in the image.
*/
NvU32 blDataOffset;
/*!
* Size of data section in the image.
*/
NvU32 blDataSize;
} RM_FLCN_BL_IMG_HEADER, *PRM_FLCN_BL_IMG_HEADER;
```
So when we read into the firmware in nova, we're looking at a firmware
that is like:
BinHdr
Descriptor (which includes code offset field)
Code Data
Data Data (this is unused)
But, in OpenRM, in
`src/nvidia/src/kernel/gpu/gsp/arch/turing/kernel_gsp_falcon_tu102.c` in
`s_prepareHsFalconWithLoader` it calls `ksec2GetGenericBlUcode_HAL`
which writes into `pBlUcDesc` which is of type `RM_FLCN_BL_DESC*` - that
goes to `ksec2BinArchiveBlUcode_TU102_ucode_desc_data`. It also writes
into `pBlImg`, but that actually points directly to
`ksec2BinArchiveBlUcode_TU102_ucode_image_data`.
In `ksec2GetGenericBlUcode_TU102` it writes just the code section:
`*ppImg = pKernelSec2->pGenericBlUcodeImg;`. That comes from
`bindataArchiveGetStorage` which grabs "ucode_image" in
`__ksec2GetBinArchiveBlUcode_TU102`. That corresponds directly to
`ksec2BinArchiveBlUcode_TU102_ucode_image_data`.
So when it uses `pBlImg` in `s_prepareHsFalconWithLoader`, it actually
doesn't include the header or the descriptor, it just includes the code
data.
[0] src/nvidia/generated/g_bindata_ksec2GetBinArchiveBlUcode_TU102.c
[1] https://github.com/NVIDIA/open-gpu-kernel-modules/blob/main/nouveau/extract-firmware-nouveau.py
[2] https://github.com/NVIDIA/open-gpu-kernel-modules/blob/df1c9a3de2be4b130f4fc7680f7198193d8ac6ad/nouveau/extract-firmware-nouveau.py#L181
With that out of the way, if we just consider the ucode image part, I
think OpenRM code is wrong to not add the code offset, and nouveau code
is correct to. Even though it is currently 0 in the embedded firmware
in openrm. I think this is less of a problem for openrm because the
hardcoded zero is versioned with the actual firmware which is embedded
in the driver, so there's no way it could get broken later.
So yeah, it doesn't matter too much but to be maximally safe and
correct, I think it should have `code_off` added.
>
> And here is what OpenRM does for DMEM:
>
> // Copy dmem desc to DMEM offset 0
> NV_ASSERT_OK_OR_RETURN(
> s_dmemCopyTo_TU102(pGpu, pKernelFlcn,
> 0,
> (NvU8 *) &blDmemDesc,
> sizeof(RM_FLCN_BL_DMEM_DESC)));
>
> Here too it makes sense that we load at offset 0, since there is only
> one data segment (the `BootloaderDmemDescV2`) that we build ourselves.
> There is no other data to place anywhere.
>
> I suspect `BootloaderDesc` (or `RM_FLCN_BL_IMG_HEADER` in OpenRM) was
> recycled from some older (pre-OpenRM), more complex firmware and
> inherited its documentation only serves a limited purpose here.
>
> The comments on `BootloaderDesc` are incorrect for us though. I'll amend
> them.
Thanks. And for dmem_load_offset, it's not used by nouveau either.
I think this one is ok with just a comment and marking it as unused
using _.
>
>>
>> Overall I find the dst/srcs here confusing cos it feels hard to keep
>> track of which set of memory things are in. So, sorry if these comments
>> are nonsense.
>
> The documentation clearly sends us on the wrong path, so good thing we
> fixed it.
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 09/10] gpu: nova-core: make Chipset::arch() const
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (7 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 08/10] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 1:49 ` Eliot Courtney
2026-03-01 14:03 ` [PATCH v10 10/10] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Alexandre Courbot
2026-03-06 10:53 ` [PATCH v10 00/10] gpu: nova-core: add Turing support Danilo Krummrich
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
We will use this method from const context.
Also take `self` by value since it is the size of a primitive type and
implements `Copy`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 60c85fffaeaf..c14d411c6759 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -92,7 +92,7 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
});
impl Chipset {
- pub(crate) fn arch(&self) -> Architecture {
+ pub(crate) const fn arch(self) -> Architecture {
match self {
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
Architecture::Turing
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 09/10] gpu: nova-core: make Chipset::arch() const
2026-03-01 14:03 ` [PATCH v10 09/10] gpu: nova-core: make Chipset::arch() const Alexandre Courbot
@ 2026-03-06 1:49 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 1:49 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> We will use this method from const context.
>
> Also take `self` by value since it is the size of a primitive type and
> implements `Copy`.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/gpu.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
> index 60c85fffaeaf..c14d411c6759 100644
> --- a/drivers/gpu/nova-core/gpu.rs
> +++ b/drivers/gpu/nova-core/gpu.rs
> @@ -92,7 +92,7 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
> });
>
> impl Chipset {
> - pub(crate) fn arch(&self) -> Architecture {
> + pub(crate) const fn arch(self) -> Architecture {
> match self {
> Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
> Architecture::Turing
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v10 10/10] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (8 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 09/10] gpu: nova-core: make Chipset::arch() const Alexandre Courbot
@ 2026-03-01 14:03 ` Alexandre Courbot
2026-03-06 2:19 ` Eliot Courtney
2026-03-06 10:53 ` [PATCH v10 00/10] gpu: nova-core: add Turing support Danilo Krummrich
10 siblings, 1 reply; 27+ messages in thread
From: Alexandre Courbot @ 2026-03-01 14:03 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel
Turing GPUs need an additional firmware file (the FWSEC generic
bootloader) in order to initialize. Add it to `ModInfoBuilder`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 21 +++++++++++++++------
drivers/gpu/nova-core/gpu.rs | 7 +++++++
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 6d47a6364c79..6d97fecefa74 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -415,11 +415,20 @@ const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
)
}
- const fn make_entry_chipset(self, chipset: &str) -> Self {
- self.make_entry_file(chipset, "booter_load")
- .make_entry_file(chipset, "booter_unload")
- .make_entry_file(chipset, "bootloader")
- .make_entry_file(chipset, "gsp")
+ const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
+ let name = chipset.name();
+
+ let this = self
+ .make_entry_file(name, "booter_load")
+ .make_entry_file(name, "booter_unload")
+ .make_entry_file(name, "bootloader")
+ .make_entry_file(name, "gsp");
+
+ if chipset.needs_fwsec_bootloader() {
+ this.make_entry_file(name, "gen_bootloader")
+ } else {
+ this
+ }
}
pub(crate) const fn create(
@@ -429,7 +438,7 @@ pub(crate) const fn create(
let mut i = 0;
while i < gpu::Chipset::ALL.len() {
- this = this.make_entry_chipset(gpu::Chipset::ALL[i].name());
+ this = this.make_entry_chipset(gpu::Chipset::ALL[i]);
i += 1;
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index c14d411c6759..8579d632e717 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -105,6 +105,13 @@ pub(crate) const fn arch(self) -> Architecture {
}
}
}
+
+ /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC.
+ ///
+ /// This includes all chipsets < GA102.
+ pub(crate) const fn needs_fwsec_bootloader(self) -> bool {
+ matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100)
+ }
}
// TODO
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v10 10/10] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder
2026-03-01 14:03 ` [PATCH v10 10/10] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Alexandre Courbot
@ 2026-03-06 2:19 ` Eliot Courtney
0 siblings, 0 replies; 27+ messages in thread
From: Eliot Courtney @ 2026-03-06 2:19 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, Alice Ryhl, David Airlie,
Simona Vetter
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, nouveau, rust-for-linux, dri-devel,
linux-kernel, dri-devel
On Sun Mar 1, 2026 at 11:03 PM JST, Alexandre Courbot wrote:
> Turing GPUs need an additional firmware file (the FWSEC generic
> bootloader) in order to initialize. Add it to `ModInfoBuilder`.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v10 00/10] gpu: nova-core: add Turing support
2026-03-01 14:03 [PATCH v10 00/10] gpu: nova-core: add Turing support Alexandre Courbot
` (9 preceding siblings ...)
2026-03-01 14:03 ` [PATCH v10 10/10] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Alexandre Courbot
@ 2026-03-06 10:53 ` Danilo Krummrich
10 siblings, 0 replies; 27+ messages in thread
From: Danilo Krummrich @ 2026-03-06 10:53 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Alice Ryhl, David Airlie, Simona Vetter, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, nouveau, rust-for-linux, dri-devel, linux-kernel
> Alexandre Courbot (8):
> gpu: nova-core: create falcon firmware DMA objects lazily
It would be nice to add a TODO that this should be followed up once we have
dma::CoherentInit (which I will most likely just name dma::CoherentBox instead).
> gpu: nova-core: falcon: add constant for memory block alignment
> gpu: nova-core: falcon: rename load parameters to reflect DMA dependency
> gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable
> gpu: nova-core: falcon: remove unwarranted safety check in dma_load
> gpu: nova-core: move brom_params and boot_addr to FalconFirmware
> gpu: nova-core: make Chipset::arch() const
> gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder
>
> Timur Tabi (2):
> gpu: nova-core: add PIO support for loading firmware images
> gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
Acked-by: Danilo Krummrich <dakr@kernel.org>
^ permalink raw reply [flat|nested] 27+ messages in thread