* [PATCH v9 1/9] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 2/9] gpu: nova-core: require DmaObject on FalconDmaLoadable, not FalconFirmware Alexandre Courbot
` (7 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 are going to fall short for PIO loading. Start by
renaming them to something that indicates that they are indeed
DMA-related, and group their declarations together as we are about to
introduce equivalent types for PIO.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 37 ++++++++++++++++----------------
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, 52 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 37bfee1d0949..85918a03b37c 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -327,17 +327,6 @@ 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).
-#[derive(Debug, Clone)]
-pub(crate) struct FalconLoadTarget {
- /// 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.
- pub(crate) dst_start: u32,
- /// Number of bytes to copy.
- pub(crate) len: u32,
-}
-
/// Parameters for the falcon boot ROM.
#[derive(Debug, Clone)]
pub(crate) struct FalconBromParams {
@@ -349,17 +338,29 @@ pub(crate) struct FalconBromParams {
pub(crate) ucode_id: u8,
}
-/// Trait for providing load parameters of falcon firmwares.
-pub(crate) trait FalconLoadParams {
+/// 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 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.
+ pub(crate) dst_start: u32,
+ /// Number of bytes to copy.
+ pub(crate) len: u32,
+}
+
+/// Trait for providing DMA load parameters of falcon firmwares.
+pub(crate) trait FalconDmaLoadable {
/// 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;
@@ -372,7 +373,7 @@ pub(crate) trait FalconLoadParams {
///
/// 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> {
+pub(crate) trait FalconFirmware: FalconDmaLoadable + Deref<Target = DmaObject> {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
@@ -420,7 +421,7 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
bar: &Bar0,
fw: &F,
target_mem: FalconMem,
- load_offsets: FalconLoadTarget,
+ load_offsets: FalconDmaLoadTarget,
) -> Result {
const DMA_LEN: u32 = 256;
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 68779540aa28..5beb27ac0f51 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -17,8 +17,8 @@
use crate::{
dma::DmaObject,
falcon::{
+ FalconDmaLoadTarget, //
FalconFirmware,
- FalconLoadTarget, //
},
gpu,
num::{
@@ -171,9 +171,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 {
@@ -205,24 +205,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,
@@ -259,21 +259,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 86556cee8e67..9e4f90dff8d0 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -22,9 +22,9 @@
sec2::Sec2,
Falcon,
FalconBromParams,
+ FalconDmaLoadTarget, //
+ FalconDmaLoadable,
FalconFirmware,
- FalconLoadParams,
- FalconLoadTarget, //
},
firmware::{
BinFirmware,
@@ -252,12 +252,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.
@@ -363,7 +363,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,
@@ -374,13 +374,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,
@@ -391,16 +391,16 @@ pub(crate) fn new(
}
}
-impl FalconLoadParams for BooterFirmware {
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
+impl FalconDmaLoadable for BooterFirmware {
+ 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 bfb7b06b13d1..b98291ec9977 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -34,9 +34,9 @@
gsp::Gsp,
Falcon,
FalconBromParams,
+ FalconDmaLoadTarget, //
+ FalconDmaLoadable,
FalconFirmware,
- FalconLoadParams,
- FalconLoadTarget, //
},
firmware::{
FalconUCodeDesc,
@@ -222,16 +222,16 @@ pub(crate) struct FwsecFirmware {
ucode: FirmwareDmaObject<Self, Signed>,
}
-impl FalconLoadParams for FwsecFirmware {
- fn imem_sec_load_params(&self) -> FalconLoadTarget {
+impl FalconDmaLoadable for FwsecFirmware {
+ 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] 14+ messages in thread* [PATCH v9 2/9] gpu: nova-core: require DmaObject on FalconDmaLoadable, not FalconFirmware
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 1/9] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 3/9] gpu: nova-core: falcon: remove generic argument from dma_wr Alexandre Courbot
` (6 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 through DMA: the PIO loading
method does not require shared memory between the CPU and GPU. Thus,
move the requirement to dereference to a DMA object from
`FalconFirmware` to the mode accurate `FalconDmaLoadable`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 85918a03b37c..d2f27847f533 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -351,7 +351,7 @@ pub(crate) struct FalconDmaLoadTarget {
}
/// Trait for providing DMA load parameters of falcon firmwares.
-pub(crate) trait FalconDmaLoadable {
+pub(crate) trait FalconDmaLoadable: Deref<Target = DmaObject> {
/// Returns the load parameters for Secure `IMEM`.
fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
@@ -373,7 +373,7 @@ pub(crate) trait FalconDmaLoadable {
///
/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
/// object.
-pub(crate) trait FalconFirmware: FalconDmaLoadable + Deref<Target = DmaObject> {
+pub(crate) trait FalconFirmware: FalconDmaLoadable {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 3/9] gpu: nova-core: falcon: remove generic argument from dma_wr
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 1/9] gpu: nova-core: falcon: rename load parameters to reflect DMA dependency Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 2/9] gpu: nova-core: require DmaObject on FalconDmaLoadable, not FalconFirmware Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 4/9] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Alexandre Courbot
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 method only needs the `DmaObject` to copy from, not the whole
firmware.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index d2f27847f533..35744f7c9cb2 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -416,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,
+ fw: &DmaObject,
target_mem: FalconMem,
load_offsets: FalconDmaLoadTarget,
) -> Result {
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 4/9] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (2 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 3/9] gpu: nova-core: falcon: remove generic argument from dma_wr Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 5/9] gpu: nova-core: move brom_params and boot_addr to FalconFirmware Alexandre Courbot
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 35744f7c9cb2..5eed48226bd5 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -373,7 +373,7 @@ pub(crate) trait FalconDmaLoadable: Deref<Target = DmaObject> {
///
/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
/// object.
-pub(crate) trait FalconFirmware: FalconDmaLoadable {
+pub(crate) trait FalconFirmware {
/// Engine on which this firmware is to be loaded.
type Target: FalconEngine;
}
@@ -516,7 +516,11 @@ 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>>(&self, bar: &Bar0, fw: &F) -> Result {
+ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
+ &self,
+ 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() {
@@ -642,7 +646,11 @@ 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> + FalconDmaLoadable>(
+ &self,
+ bar: &Bar0,
+ fw: &F,
+ ) -> Result {
match self.hal.load_method() {
LoadMethod::Dma => self.dma_load(bar, fw),
LoadMethod::Pio => Err(ENOTSUPP),
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 5/9] gpu: nova-core: move brom_params and boot_addr to FalconFirmware
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (3 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 4/9] gpu: nova-core: falcon: remove FalconFirmware's dependency on FalconDmaLoadable Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 | 24 ++++++++++++------------
drivers/gpu/nova-core/firmware/fwsec.rs | 24 ++++++++++++------------
3 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 5eed48226bd5..7f52b051206a 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -361,12 +361,6 @@ pub(crate) trait FalconDmaLoadable: Deref<Target = DmaObject> {
/// 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.
@@ -376,6 +370,12 @@ pub(crate) trait FalconDmaLoadable: Deref<Target = DmaObject> {
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 9e4f90dff8d0..54f67f4cbe87 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -403,6 +403,18 @@ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.dmem_load_target.clone()
}
+}
+
+impl Deref for BooterFirmware {
+ type Target = DmaObject;
+
+ fn deref(&self) -> &Self::Target {
+ &self.ucode.0
+ }
+}
+
+impl FalconFirmware for BooterFirmware {
+ type Target = Sec2;
fn brom_params(&self) -> FalconBromParams {
self.brom_params.clone()
@@ -416,15 +428,3 @@ 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 b98291ec9977..ebea7fed43ba 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -234,18 +234,6 @@ fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.desc.dmem_load_params()
}
-
- fn brom_params(&self) -> FalconBromParams {
- FalconBromParams {
- pkc_data_offset: self.desc.pkc_data_offset(),
- engine_id_mask: self.desc.engine_id_mask(),
- ucode_id: self.desc.ucode_id(),
- }
- }
-
- fn boot_addr(&self) -> u32 {
- 0
- }
}
impl Deref for FwsecFirmware {
@@ -258,6 +246,18 @@ fn deref(&self) -> &Self::Target {
impl FalconFirmware for FwsecFirmware {
type Target = Gsp;
+
+ fn brom_params(&self) -> FalconBromParams {
+ FalconBromParams {
+ pkc_data_offset: self.desc.pkc_data_offset(),
+ engine_id_mask: self.desc.engine_id_mask(),
+ ucode_id: self.desc.ucode_id(),
+ }
+ }
+
+ fn boot_addr(&self) -> u32 {
+ 0
+ }
}
impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (4 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 5/9] gpu: nova-core: move brom_params and boot_addr to FalconFirmware Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-13 14:47 ` Danilo Krummrich
2026-02-12 8:26 ` [PATCH v9 7/9] gpu: nova-core: make Chipset::arch() const Alexandre Courbot
` (2 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 | 230 +++++++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/falcon/hal.rs | 6 +-
drivers/gpu/nova-core/regs.rs | 30 +++++
3 files changed, 263 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 7f52b051206a..f8de36abd135 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -361,6 +361,138 @@ pub(crate) trait FalconDmaLoadable: Deref<Target = DmaObject> {
/// 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| {
+ // SAFETY: we keep a reference to `self` for as long as this slice is alive, and the
+ // device will not access this DMA object since we are using PIO.
+ let data = unsafe {
+ self.as_slice(
+ usize::from_safe_cast(params.src_start),
+ usize::from_safe_cast(params.len),
+ )?
+ };
+
+ 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();
+
+ // SAFETY: we keep a reference to `self` for as long as this slice is alive, and the
+ // device will not access this DMA object since we are using PIO.
+ let data = unsafe {
+ self.as_slice(
+ usize::from_safe_cast(params.src_start),
+ usize::from_safe_cast(params.len),
+ )?
+ };
+
+ 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.
@@ -412,6 +544,99 @@ 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(256).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())?;
+
+ // Set `BootVec` to start of non-secure code.
+ 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`.
///
@@ -645,7 +870,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,
bar: &Bar0,
@@ -653,7 +879,7 @@ pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
) -> Result {
match self.hal.load_method() {
LoadMethod::Dma => self.dma_load(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] 14+ messages in thread* Re: [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images
2026-02-12 8:26 ` [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
@ 2026-02-13 14:47 ` Danilo Krummrich
2026-02-22 10:57 ` Alexandre Courbot
0 siblings, 1 reply; 14+ messages in thread
From: Danilo Krummrich @ 2026-02-13 14:47 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
On Thu Feb 12, 2026 at 9:26 AM CET, Alexandre Courbot wrote:
> + fn try_as_pio_loadable(&self) -> Result<FalconDmaFirmwarePioAdapter<'_, Self>> {
[...]
> + let dmem = {
> + let params = self.dmem_load_params();
> +
> + // SAFETY: we keep a reference to `self` for as long as this slice is alive, and the
> + // device will not access this DMA object since we are using PIO.
How is this guaranteed by this function? I.e. how is it prevented that this
function is never called when the device acesses the DMA memory?
> + let data = unsafe {
> + self.as_slice(
> + usize::from_safe_cast(params.src_start),
> + usize::from_safe_cast(params.len),
> + )?
> + };
> +
> + 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,
> + })
> + }
> +}
<snip>
> +/// 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,
In v6 [1] I wrote:
> @@ -221,6 +286,8 @@ pub(crate) struct FwsecFirmware {
> desc: FalconUCodeDesc,
> /// GPU-accessible DMA object containing the firmware.
> ucode: FirmwareDmaObject<Self, Signed>,
> + /// Generic bootloader
> + gen_bootloader: Option<GenericBootloader>,
I'm not convinced this is a good idea. We probably want a HAL here and
have different FwsecFirmware types:
One with a DMA object and one with a system memory object when the
architecture uses PIO. In the latter case the object can have a
GenericBootloader field, i.e. this also gets us rid of the Option and
all the subsequent 'if chipset < Chipset::GA102' checks and 'match
gbl_fw' matches below.
So, I still wonder, why use an Adapter impl on top of DMA memory for PIO rather
than different base types with a common trait to avoid DMA allocations in the
PIO case altogether?
[1] https://lore.kernel.org/all/DFQBHVTTHZY8.13ASLCJ3FJP81@kernel.org/
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images
2026-02-13 14:47 ` Danilo Krummrich
@ 2026-02-22 10:57 ` Alexandre Courbot
0 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-22 10:57 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, Simona Vetter, Alistair Popple, Joel Fernandes,
Eliot Courtney, nouveau, rust-for-linux, dri-devel, linux-kernel
(sorry, took a while to come back to this)
On Fri Feb 13, 2026 at 11:47 PM JST, Danilo Krummrich wrote:
> On Thu Feb 12, 2026 at 9:26 AM CET, Alexandre Courbot wrote:
>> + fn try_as_pio_loadable(&self) -> Result<FalconDmaFirmwarePioAdapter<'_, Self>> {
>
> [...]
>
>> + let dmem = {
>> + let params = self.dmem_load_params();
>> +
>> + // SAFETY: we keep a reference to `self` for as long as this slice is alive, and the
>> + // device will not access this DMA object since we are using PIO.
>
> How is this guaranteed by this function? I.e. how is it prevented that this
> function is never called when the device acesses the DMA memory?
This relies on the fact that firmware loading and running is
synchronous, and that the firmware does not touch these objects once it
is done running. But yes, this is a pretty weak guarantee for Rust
standards.
>
>> + let data = unsafe {
>> + self.as_slice(
>> + usize::from_safe_cast(params.src_start),
>> + usize::from_safe_cast(params.len),
>> + )?
>> + };
>> +
>> + 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,
>> + })
>> + }
>> +}
>
> <snip>
>
>> +/// 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,
>
> In v6 [1] I wrote:
>
> > @@ -221,6 +286,8 @@ pub(crate) struct FwsecFirmware {
> > desc: FalconUCodeDesc,
> > /// GPU-accessible DMA object containing the firmware.
> > ucode: FirmwareDmaObject<Self, Signed>,
> > + /// Generic bootloader
> > + gen_bootloader: Option<GenericBootloader>,
>
> I'm not convinced this is a good idea. We probably want a HAL here and
> have different FwsecFirmware types:
>
> One with a DMA object and one with a system memory object when the
> architecture uses PIO. In the latter case the object can have a
> GenericBootloader field, i.e. this also gets us rid of the Option and
> all the subsequent 'if chipset < Chipset::GA102' checks and 'match
> gbl_fw' matches below.
>
> So, I still wonder, why use an Adapter impl on top of DMA memory for PIO rather
> than different base types with a common trait to avoid DMA allocations in the
> PIO case altogether?
This would require quite a lot of new (almost duplicated) code just for
handling the PIO path, and complicate things more than is worth IMHO.
All these problems (and a few others) stem from the fact that we create
the DMA object early during the loading process to avoid a copy; but
doing so also forces us to do the patching and other fun things on that
DMA object. The instance of "we cannot really guarantee that nobody else
is accessing that object" is not unique, the signature patching for
example is just as weak.
However, if we are willing to accept an extra copy of the ucode when DMA
is used, then we can change the loading process to work with a regular
KVec, and only move the ucode into a DmaObject at the last minute in
`dma_load`.
This would solve all the problems you raised while avoiding duplicating
code - it would actually *simplify* the code a bit and remove a bunch of
unsafes notably in `fwsec.rs`. WDYT? I'd say the unsafe removal alone
makes it worthwhile, and it's not like that copy would induce a
perceived slowdown anyway.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v9 7/9] gpu: nova-core: make Chipset::arch() const
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (5 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 6/9] gpu: nova-core: add PIO support for loading firmware images Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 8/9] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 9b042ef1a308..9726205d94b6 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] 14+ messages in thread* [PATCH v9 8/9] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (6 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 7/9] gpu: nova-core: make Chipset::arch() const Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-12 8:26 ` [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
8 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 5beb27ac0f51..6edcb0e0f808 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -416,11 +416,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(
@@ -430,7 +439,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 9726205d94b6..959f1f4caf42 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] 14+ messages in thread* [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-02-12 8:26 [PATCH v9 0/9] gpu: nova-core: add Turing support Alexandre Courbot
` (7 preceding siblings ...)
2026-02-12 8:26 ` [PATCH v9 8/9] gpu: nova-core: add gen_bootloader firmware to ModInfoBuilder Alexandre Courbot
@ 2026-02-12 8:26 ` Alexandre Courbot
2026-02-24 2:31 ` Eliot Courtney
8 siblings, 1 reply; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-12 8:26 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 | 276 +++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 15 +-
3 files changed, 294 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index ebea7fed43ba..f4159d7a9d0e 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,
ops::Deref, //
@@ -408,6 +410,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..1cfe72e27479
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -0,0 +1,276 @@
+// 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::*,
+ sizes,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp,
+ Falcon,
+ FalconBromParams,
+ FalconDmaLoadable,
+ FalconEngine,
+ FalconFbifMemType,
+ FalconFbifTarget,
+ FalconFirmware,
+ FalconPioDmemLoadTarget,
+ FalconPioImemLoadTarget,
+ FalconPioLoadable, //
+ },
+ firmware::{
+ fwsec::FwsecFirmware,
+ request_firmware,
+ BinHdr,
+ FIRMWARE_VERSION, //
+ },
+ gpu::Chipset,
+ num::{
+ self,
+ FromSafeCast, //
+ },
+ regs,
+};
+
+/// Descriptor used by RM to figure out the requirements of the boot loader.
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub(crate) struct BootloaderDesc {
+ /// Starting tag of bootloader.
+ pub(crate) start_tag: u32,
+ /// DMEM offset where [`BootloaderDmemDescV2`] is to be loaded.
+ pub(crate) dmem_load_off: u32,
+ /// Offset of code section in the image.
+ pub(crate) code_off: u32,
+ /// Size of code section in the image.
+ pub(crate) code_size: u32,
+ /// Offset of data section in the image.
+ pub(crate) data_off: u32,
+ /// Size of data section in the image.
+ pub(crate) 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)]
+pub(crate) struct BootloaderDmemDescV2 {
+ /// Reserved, should always be first element.
+ pub(crate) reserved: [u32; 4],
+ /// 16B signature for secure code, 0s if no secure code.
+ pub(crate) signature: [u32; 4],
+ /// DMA context used by the bootloader while loading code/data.
+ pub(crate) ctx_dma: u32,
+ /// 256B-aligned physical FB address where code is located.
+ pub(crate) code_dma_base: u64,
+ /// Offset from `code_dma_base` where the non-secure code is located (must be multiple of 256).
+ pub(crate) non_sec_code_off: u32,
+ /// Size of the non-secure code part.
+ pub(crate) non_sec_code_size: u32,
+ /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256).
+ pub(crate) sec_code_off: u32,
+ /// Size of the secure code part.
+ pub(crate) sec_code_size: u32,
+ /// Code entry point invoked by the bootloader after code is loaded.
+ pub(crate) code_entry_point: u32,
+ /// 256B-aligned physical FB address where data is located.
+ pub(crate) data_dma_base: u64,
+ /// Size of data block (should be multiple of 256B).
+ pub(crate) data_size: u32,
+ /// Arguments to be passed to the target firmware being loaded.
+ pub(crate) argc: u32,
+ /// Number of arguments to be passed to the target firmware being loaded.
+ pub(crate) 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 {
+ /// Firmware that the bootloader will load.
+ firmware: FwsecFirmware,
+ /// Descriptor to be loaded into DMEM for the bootloader to read.
+ dmem_desc: BootloaderDmemDescV2,
+ /// Code of the bootloader to be loaded into non-secure IMEM.
+ ucode: KVec<u8>,
+ /// Range-validated start offset of the firmware code in IMEM.
+ imem_dst_start: u16,
+ /// 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);
+
+ let mut ucode = KVec::new();
+ ucode.extend_from_slice(
+ fw.data()
+ .get(ucode_start..ucode_start + code_size)
+ .ok_or(EINVAL)?,
+ GFP_KERNEL,
+ )?;
+
+ ucode
+ };
+
+ 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_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_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: u32 = num::usize_into_u32::<{ sizes::SZ_64K }>();
+ let imem_dst_start = BOOTLOADER_LOAD_CEILING
+ .checked_sub(desc.code_size)
+ .ok_or(EOVERFLOW)?;
+
+ Ok(Self {
+ firmware,
+ dmem_desc,
+ ucode,
+ 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::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.firmware.brom_params()
+ }
+
+ 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 be427fe26a58..b7dbd57dd882 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] 14+ messages in thread* Re: [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-02-12 8:26 ` [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing Alexandre Courbot
@ 2026-02-24 2:31 ` Eliot Courtney
2026-02-27 4:21 ` Alexandre Courbot
0 siblings, 1 reply; 14+ messages in thread
From: Eliot Courtney @ 2026-02-24 2:31 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 Thu Feb 12, 2026 at 5:26 PM JST, Alexandre Courbot wrote:
> + 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_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_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: u32 = num::usize_into_u32::<{ sizes::SZ_64K }>();
> + let imem_dst_start = BOOTLOADER_LOAD_CEILING
> + .checked_sub(desc.code_size)
> + .ok_or(EOVERFLOW)?;
Are there any alignment requirements for `imem_dst_start`? Or maybe
`code_size` is always such that the alignment will be fine?
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v9 9/9] gpu: nova-core: use the Generic Bootloader to boot FWSEC on Turing
2026-02-24 2:31 ` Eliot Courtney
@ 2026-02-27 4:21 ` Alexandre Courbot
0 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-02-27 4:21 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 Tue Feb 24, 2026 at 11:31 AM JST, Eliot Courtney wrote:
> On Thu Feb 12, 2026 at 5:26 PM JST, Alexandre Courbot wrote:
>> + 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_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_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: u32 = num::usize_into_u32::<{ sizes::SZ_64K }>();
>> + let imem_dst_start = BOOTLOADER_LOAD_CEILING
>> + .checked_sub(desc.code_size)
>> + .ok_or(EOVERFLOW)?;
>
> Are there any alignment requirements for `imem_dst_start`? Or maybe
> `code_size` is always such that the alignment will be fine?
Looking at OpenRM, there are indeed! I will integrate them into the next
revision. Thanks for pointing this out.
^ permalink raw reply [flat|nested] 14+ messages in thread