[PATCH 4/7] gpu: nova-core: run the generic bootloader from its own type
From: Alexandre Courbot
Date: Tue Nov 03 2026 - 05:50:40 EST
From: John Hubbard <jhubbard@xxxxxxxxxx>
The generic falcon bootloader needs a descriptor in DMEM to know what to
load, so loading it into a falcon means loading the two together by PIO
and starting the falcon. The FWSEC wrapper did that through its own
implementation of the PIO loading traits. The r000 load-and-execute
bootloader event will need to do the same thing.
Give the generic bootloader a run method, which pairs it with the
descriptor that it receives, loads the pair by PIO, starts the falcon
with the given MAILBOX0 value and returns the mailboxes once the falcon
has halted. This will allow the FWSEC and r000 load-and-execute
bootloader event to share the same code.
No functional changes.
Assisted-by: LLM
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
Co-developed-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
---
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 55 ++-------------
drivers/gpu/nova-core/firmware/gen_bootloader.rs | 80 +++++++++++++++++++++-
2 files changed, 84 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index ad798ec5645a..c7bb877fcf20 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -16,22 +16,16 @@
register::Array,
Io, //
},
- prelude::*,
- transmute::AsBytes,
+ prelude::*, //
};
use crate::{
falcon::{
gsp::Gsp,
Falcon,
- FalconBromParams,
FalconDmaLoadable,
FalconFbifMemType,
- FalconFbifTarget,
- FalconFirmware,
- FalconPioDmemLoadTarget,
- FalconPioImemLoadTarget,
- FalconPioLoadable, //
+ FalconFbifTarget, //
},
firmware::{
fwsec::FwsecFirmware,
@@ -50,11 +44,10 @@
pub(crate) struct FwsecFirmwareWithBl<'a> {
/// DMA object the bootloader will copy the firmware from.
_firmware_dma: Coherent<'a, [u8]>,
+ /// The bootloader that copies the firmware.
bootloader: GenericBootloader,
/// Descriptor to be loaded into DMEM for the bootloader to read.
dmem_desc: BootloaderDmemDescV2,
- /// BROM parameters of the loaded firmware.
- brom_params: FalconBromParams,
}
impl<'a> FwsecFirmwareWithBl<'a> {
@@ -127,7 +120,6 @@ pub(crate) fn new(
_firmware_dma: firmware_dma,
bootloader,
dmem_desc,
- brom_params: firmware.brom_params(),
})
}
@@ -136,13 +128,9 @@ pub(crate) fn new(
/// 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>) -> Result<()> {
- // Reset falcon, load the firmware, and run it.
falcon
.reset()
.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
- falcon
- .pio_load(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.
falcon.pfalcon.update(
@@ -154,8 +142,10 @@ pub(crate) fn run(&self, dev: &Device<device::Bound>, falcon: &Falcon<'_, Gsp>)
},
);
- let (mbox0, _) = falcon
- .boot(Some(0), None)
+ // Run the bootloader, which loads the FWSEC firmware and runs it.
+ let (mbox0, _) = self
+ .bootloader
+ .run(falcon, &self.dmem_desc, 0)
.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);
@@ -165,34 +155,3 @@ pub(crate) fn run(&self, dev: &Device<device::Bound>, falcon: &Falcon<'_, Gsp>)
}
}
}
-
-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.
- self.bootloader.boot_addr()
- }
-}
-
-impl FalconPioLoadable for FwsecFirmwareWithBl<'_> {
- fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
- None
- }
-
- fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
- Some(self.bootloader.imem_load_params())
- }
-
- fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
- FalconPioDmemLoadTarget {
- data: self.dmem_desc.as_bytes(),
- dst_start: 0,
- }
- }
-}
diff --git a/drivers/gpu/nova-core/firmware/gen_bootloader.rs b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
index a3de545731f5..fe820ac6b49a 100644
--- a/drivers/gpu/nova-core/firmware/gen_bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/gen_bootloader.rs
@@ -17,8 +17,12 @@
self,
gsp::Gsp,
Falcon,
+ FalconBromParams,
FalconDmaLoadTarget,
- FalconPioImemLoadTarget, //
+ FalconFirmware,
+ FalconPioDmemLoadTarget,
+ FalconPioImemLoadTarget,
+ FalconPioLoadable, //
},
firmware::tlv::{
request_tlv, //
@@ -180,12 +184,12 @@ pub(crate) fn new(
/// Returns the address at which the falcon starts executing the bootloader: the IMEM virtual
/// address of its first code block.
- pub(crate) fn boot_addr(&self) -> u32 {
+ fn boot_addr(&self) -> u32 {
u32::from(self.start_tag) << 8
}
/// Returns the PIO parameters that place this bootloader in non-secure IMEM.
- pub(crate) fn imem_load_params(&self) -> FalconPioImemLoadTarget<'_> {
+ fn imem_load_params(&self) -> FalconPioImemLoadTarget<'_> {
FalconPioImemLoadTarget {
data: self.ucode.as_ref(),
dst_start: self.imem_dst_start,
@@ -193,4 +197,74 @@ pub(crate) fn imem_load_params(&self) -> FalconPioImemLoadTarget<'_> {
start_tag: self.start_tag,
}
}
+
+ /// Runs the bootloader on `falcon` to load and start the image that `dmem_desc` names.
+ ///
+ /// The bootloader and the descriptor are loaded by PIO, the bootloader is started with `mbox0`
+ /// in `MAILBOX0`, and this returns the mailbox values once the falcon has halted, that is,
+ /// once the loaded image has run to completion.
+ ///
+ /// The caller resets `falcon` beforehand, and programs the FBIF aperture of the context DMA
+ /// slot that the descriptor names with the location of the image.
+ ///
+ /// # Errors
+ ///
+ /// - `ETIMEDOUT` if the falcon has not halted within two seconds of starting.
+ ///
+ /// Errors from the PIO load are propagated as-is.
+ pub(crate) fn run(
+ &self,
+ falcon: &Falcon<'_, Gsp>,
+ dmem_desc: &BootloaderDmemDescV2,
+ mbox0: u32,
+ ) -> Result<(u32, u32)> {
+ falcon.pio_load(&GenericBootloaderLoad {
+ bootloader: self,
+ dmem_desc,
+ })?;
+
+ falcon.boot(Some(mbox0), None)
+ }
+}
+
+/// The generic bootloader together with the descriptor that it reads from DMEM offset 0, as one
+/// firmware that [`Falcon::pio_load`] accepts.
+struct GenericBootloaderLoad<'a> {
+ bootloader: &'a GenericBootloader,
+ dmem_desc: &'a BootloaderDmemDescV2,
+}
+
+impl FalconFirmware for GenericBootloaderLoad<'_> {
+ type Target = Gsp;
+
+ fn brom_params(&self) -> FalconBromParams {
+ // The bootloader is unsigned, and the falcon HALs of the chipsets that load it this way
+ // write no BROM registers, so these values never reach hardware.
+ FalconBromParams {
+ pkc_data_offset: 0,
+ engine_id_mask: 0,
+ ucode_id: 0,
+ }
+ }
+
+ fn boot_addr(&self) -> u32 {
+ self.bootloader.boot_addr()
+ }
+}
+
+impl FalconPioLoadable for GenericBootloaderLoad<'_> {
+ fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ None
+ }
+
+ fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> {
+ Some(self.bootloader.imem_load_params())
+ }
+
+ fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> {
+ FalconPioDmemLoadTarget {
+ data: self.dmem_desc.as_bytes(),
+ dst_start: 0,
+ }
+ }
}
--
2.56.0