gpu: nova-core: consolidate and streamline GSP boot process
Subsystems (5)
Alexandre Courbot
maintainerv3 cover gpu: nova-core: consolidate and streamline GSP boot process
The recently introduced GspBootContext is useful in at least two other places:
- As a replacement for the sequencer's GspSequencerParams,
- As the main parameter of Gsp::unload, for symmetry with Gsp::boot.
This symmetry can be further exploited to make Gsp::boot and Gsp::unload generic over the exact boot context required for the current chip. Doing so requires some more preliminary work that goes beyond this series, but this groundwork is needed regardless.
Another cleanup that can be performed as a consequence is the removal of the BootUnloadGuard. This type is cumbersome as it holds extra references to the boot context, making it difficult to make some of these references mutable (as we want to do with the Falcons that should require exclusive access to load and run firmware). Furthermore, it is only needed a single time in Gsp::boot, and inadequate to handle errors occurring in HALs.
Building on this, the series also introduces a proper type for identifying the GSP boot method, and moves the Fsp instance to Gpu to make it available outside of the GSP boot process. This will be notably useful for vGPU support [1].
This series applies on drm-rust-next.
[1] https://lore.kernel.org/all/[email protected]/
---
Changes in v3:
- Drop the preparatory closure-only patch.
- Use ScopeGuard for GSP boot-error cleanup.
- Keep TU102 boot-error cleanup local to the HAL.
- Avoid spurious unload-bundle warnings on early failures.
- Keep GH100 FMC boot arguments alive through lockdown release.
- Link to v2: https://patch.msgid.link/[email protected]
Changes in v2:
- Replace nonexistent ENODATA with the more appropriate EAGAIN.
- Add patches introducing GspBootMethod.
- Add patches moving Fsp to Gpu.
- Link to v1: https://patch.msgid.link/[email protected]
---
Alexandre Courbot (12):
gpu: nova-core: gsp: sequencer: use GspBootContext
gpu: nova-core: gsp: sequencer: do not store sequence into GspSequencer
gpu: nova-core: gsp: replace BootUnloadGuard with local handlers
gpu: nova-core: gsp: pass GspBootContext to unload methods
gpu: nova-core: gsp: centralize missing unload bundle warnings
gpu: nova-core: gsp: fold TU102 unload bundle construction into HAL method
gpu: nova-core: gsp: turn FWSEC execution into HAL method
gpu: nova-core: gsp: make use of FWSEC bootloader a property of the TU102 HAL
gpu: nova-core: introduce GspBootMethod
gpu: nova-core: avoid repeated calls to pci::Device::as_ref
gpu: nova-core: gsp: pass GspBootContext mutably
gpu: nova-core: store Fsp instance in Gpu
drivers/gpu/nova-core/firmware.rs | 25 ++- drivers/gpu/nova-core/firmware/fwsec.rs | 5 +- drivers/gpu/nova-core/gpu.rs | 68 ++++--- drivers/gpu/nova-core/gsp.rs | 44 ++++- drivers/gpu/nova-core/gsp/boot.rs | 99 +++------- drivers/gpu/nova-core/gsp/hal.rs | 56 +++--- drivers/gpu/nova-core/gsp/hal/ga102.rs | 14 ++ drivers/gpu/nova-core/gsp/hal/gh100.rs | 40 ++-- drivers/gpu/nova-core/gsp/hal/tu102.rs | 335 +++++++++++++++----------------- drivers/gpu/nova-core/gsp/sequencer.rs | 71 +++---- 10 files changed, 369 insertions(+), 388 deletions(-)
---
base-commit: f1bd7119ac4c98fc2f0ddf5a6d851de66bc5f62f
change-id: 20260619-nova-bootcontext-401d6107e8fb
Best regards,
Signature · 1 line
Alexandre Courbot <[email protected]>
Alexandre Courbot
maintainerPATCH v3 1/12 gpu: nova-core: gsp: sequencer: use GspBootContext
GspBootContext contains all the resources currently carried by GspSequencerParams, so replace the latter with the former for better integration with the boot process and less code.
--- drivers/gpu/nova-core/gsp/hal/tu102.rs | 21 +++++++------------- drivers/gpu/nova-core/gsp/sequencer.rs | 36 ++++++++++++---------------------- 2 files changed, 20 insertions(+), 37 deletions(-)
drivers/gpu/nova-core/gsp/hal/tu102.rs +7 -14
@@ -37,10 +37,7 @@ GspHal, UnloadBundle, // }, - sequencer::{ - GspSequencer, - GspSequencerParams, // - }, + sequencer::GspSequencer, Gsp, GspBootContext, GspFwWprMeta, //
@@ -336,16 +333,12 @@ fn boot<'a>( }fn post_boot (&self, gsp: &Gsp , ctx: &GspBootContext <'_>, gsp_fw: &GspFirmware ) ->Result { -// Create and run the GSP sequencer. -let seq_params =GspSequencerParams { - bootloader_app_version: gsp_fw.bootloader.app_version, - libos_dma_handle: gsp.libos.dma_handle (), - gsp_falcon: ctx.gsp_falcon, - sec2_falcon: ctx.sec2_falcon, - dev: ctx.dev (), - bar: ctx.bar, - }; -GspSequencer ::run (&gsp.cmdq, seq_params)?; +GspSequencer ::run ( + &gsp.cmdq, + ctx, + gsp.libos.dma_handle (), + gsp_fw.bootloader.app_version, + )?;Ok (()) }
drivers/gpu/nova-core/gsp/sequencer.rs +13 -23
@@ -31,6 +31,7 @@ MessageFromGsp, // }, fw, + GspBootContext, // }, num::FromSafeCast, sbuffer::SBufferIter,
@@ -338,24 +339,13 @@ fn iter(&self) -> GspSeqIter<'_> { } } -/// Parameters for running the GSP sequencer. -pub (crate )struct GspSequencerParams <'a> { -/// Bootloader application version. -pub (crate ) bootloader_app_version:u32 , -/// LibOS DMA handle address. -pub (crate ) libos_dma_handle:u64 , -/// GSP falcon for core operations. -pub (crate ) gsp_falcon: &'aFalcon <Gsp >, -/// SEC2 falcon for core operations. -pub (crate ) sec2_falcon: &'aFalcon <Sec2 >, -/// Device for logging. -pub (crate ) dev: &'a device::Device , -/// BAR0 for register access. -pub (crate ) bar:Bar0 <'a>, -} -impl <'a>GspSequencer <'a> { -pub (crate )fn run (cmdq: &Cmdq , params:GspSequencerParams <'a>) ->Result { +pub (crate )fn run ( + cmdq: &Cmdq , + ctx: &'aGspBootContext <'_>, + libos_dma_handle:u64 , + bootloader_app_version:u32 , + ) ->Result {let seq_info =loop {match cmdq.receive_msg ::<GspSequence >(Cmdq ::RECEIVE_TIMEOUT ) {Ok (seq_info) =>break seq_info,
@@ -366,12 +356,12 @@ pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result {let sequencer =GspSequencer { seq_info, - bar: params.bar, - sec2_falcon: params.sec2_falcon, - gsp_falcon: params.gsp_falcon, - libos_dma_handle: params.libos_dma_handle, - bootloader_app_version: params.bootloader_app_version, - dev: params.dev, + bar: ctx.bar, + sec2_falcon: ctx.sec2_falcon, + gsp_falcon: ctx.gsp_falcon, + libos_dma_handle, + bootloader_app_version, + dev: ctx.dev (), };dev_dbg! (sequencer.dev,"Running CPU Sequencer commands\n" );
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 2/12 gpu: nova-core: gsp: sequencer: do not store sequence into GspSequencer
In reply to Alexandre CourbotThe sequence is currently stored in the GspSequencer even though its lifetime is limited to GspSequencer::run. This object-oriented design does not play well with the borrow-checker, as GspSequencer::iter borrows a reference to the GspSequencer, which makes it difficult to introduce mutable references in GspBootContext, as we want to do in order to make the Falcon references mutable.
Thus, store the sequence locally in GspSequencer::run, and move iterator creation to GspSeqIter::new so it no longer needs to borrow the whole GspSequencer.
--- drivers/gpu/nova-core/gsp/sequencer.rs | 35 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-)
drivers/gpu/nova-core/gsp/sequencer.rs +15 -20
@@ -129,8 +129,6 @@ pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {/// GSP Sequencer for executing firmware commands during boot. pub (crate )struct GspSequencer <'a> { -/// Sequencer information with command data. - seq_info:GspSequence ,/// `Bar0` for register access. bar:Bar0 <'a>,/// SEC2 falcon for core operations.
@@ -271,7 +269,7 @@ fn run(&self, seq: &GspSequencer<'_>) -> Result { }/// Iterator over GSP sequencer commands. -pub (crate )struct GspSeqIter <'a> { +struct GspSeqIter <'a> {/// Command data buffer. cmd_data: &'a [u8 ],/// Current position in the buffer.
@@ -284,6 +282,18 @@ pub(crate) struct GspSeqIter<'a> { dev: &'a device::Device, } +impl <'a>GspSeqIter <'a> { +fn new (seq: &'aGspSequence , dev: &'a device::Device ) ->Self { +Self { + cmd_data: &seq.cmd_data, + current_offset:0 , + total_cmds: seq.cmd_index, + cmds_processed:0 , + dev, + } + } +} +impl <'a>Iterator for GspSeqIter <'a> {type Item =Result <GspSeqCmd >;
@@ -325,20 +335,6 @@ fn next(&mut self) -> Option<Self::Item> { } } -impl <'a>GspSequencer <'a> { -fn iter (&self) ->GspSeqIter <'_> { -let cmd_data = &self.seq_info.cmd_data[..]; - -GspSeqIter { - cmd_data, - current_offset:0 , - total_cmds: self.seq_info.cmd_index, - cmds_processed:0 , - dev: self.dev, - } - } -} -impl <'a>GspSequencer <'a> {pub (crate )fn run ( cmdq: &Cmdq,
@@ -355,7 +351,6 @@ pub(crate) fn run( };let sequencer =GspSequencer { - seq_info, bar: ctx.bar, sec2_falcon: ctx.sec2_falcon, gsp_falcon: ctx.gsp_falcon,
@@ -366,14 +361,14 @@ pub(crate) fn run(dev_dbg! (sequencer.dev,"Running CPU Sequencer commands\n" ); -for cmd_resultin sequencer.iter () { +for cmd_resultin GspSeqIter ::new (&seq_info, sequencer.dev) {match cmd_result {Ok (cmd) => cmd.run (&sequencer)?,Err (e) => {dev_err! ( sequencer.dev,"Error running command at index {}\n" , - sequencer.seq_info.cmd_index + seq_info.cmd_index );return Err (e); }
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 3/12 gpu: nova-core: gsp: replace BootUnloadGuard with local handlers
In reply to Alexandre CourbotWhen adding the GSP unload capability, we introduced BootUnloadGuard to automatically call Gsp::unload whenever an error occurred during the boot process, in order to try to reset the GSP to a valid state.
This approach is not well-suited to the errors that may occur in HALs:
by definition, an error occurring in the HAL means that the GSP is not booted; yet the first thing that Gsp::unload does is queue a shutdown message to the GSP, which will inevitably result in a timeout when done from a HAL.
Furthermore, BootUnloadGuard is problematic because it holds additional references to the boot context, notably the Falcons. These extra references stand in the way of making some of the Falcon's methods mutable, since those methods would require exclusive access. As this behavior is only needed in one place, introducing dedicated types for it is distracting and unnecessary.
Thus, remove BootUnloadGuard and adopt a two-level error handling strategy:
- HALs are free to handle their errors as they see fit (most likely, by running their unload bundle if it is ready by the time of the error),
- Gsp::boot uses a ScopeGuard that runs Gsp::unload, since the GSP should be up and running by the time GspHal::boot has returned.
--- drivers/gpu/nova-core/gsp/boot.rs | 67 +++------------------------------- drivers/gpu/nova-core/gsp/hal.rs | 13 +++---- drivers/gpu/nova-core/gsp/hal/gh100.rs | 20 ++++------ drivers/gpu/nova-core/gsp/hal/tu102.rs | 23 +++++++----- 4 files changed, 33 insertions(+), 90 deletions(-)
drivers/gpu/nova-core/gsp/boot.rs +6 -61
@@ -30,66 +30,6 @@ }, }; -/// Arguments required to call [`Gsp::unload`](super::Gsp::unload). -/// -/// Stored as their own type to avoid repeating a long and tedious list in [`BootUnloadGuard`]. -pub (super )struct BootUnloadArgs <'a> { - gsp: &'asuper ::Gsp , - dev: &'a device::Device <device::Bound >, - bar:Bar0 <'a>, - gsp_falcon: &'aFalcon <Gsp >, - sec2_falcon: &'aFalcon <Sec2 >, - unload_bundle:Option <super ::UnloadBundle >, -} - -/// Guard that calls [`Gsp::unload`](super::Gsp::unload) with a -/// [`UnloadBundle`](super::UnloadBundle) when dropped. -/// -/// Used to ensure the `UnloadBundle` is run during failure paths. -pub (super )struct BootUnloadGuard <'a> { - guard:ScopeGuard <BootUnloadArgs <'a>,fn (BootUnloadArgs <'a>)>, -} - -impl <'a>BootUnloadGuard <'a> { -/// Wraps `unload_bundle` into a guard that executes it when dropped. -pub (super )fn new ( - gsp: &'asuper ::Gsp , - dev: &'a device::Device <device::Bound >, - bar:Bar0 <'a>, - gsp_falcon: &'aFalcon <Gsp >, - sec2_falcon: &'aFalcon <Sec2 >, - unload_bundle:Option <super ::UnloadBundle >, - ) ->Self { -Self { - guard:ScopeGuard ::new_with_data ( -BootUnloadArgs { - gsp, - dev, - bar, - gsp_falcon, - sec2_falcon, - unload_bundle, - }, - |args| { -let _ =super ::Gsp ::unload ( - args.gsp, - args.dev, - args.bar, - args.gsp_falcon, - args.sec2_falcon, - args.unload_bundle, - ); - }, - ), - } - } - -/// Disarms the guard and returns the [`UnloadBundle`](super::UnloadBundle) it contains. -pub (super )fn dismiss (self) ->Option <super ::UnloadBundle > { - self.guard.dismiss ().unload_bundle - } -} -impl super ::Gsp {/// Attempt to boot the GSP. ///
@@ -107,6 +47,7 @@ pub(crate) fn boot(let bar = ctx.bar;let chipset = ctx.chipset;let gsp_falcon = ctx.gsp_falcon; +let sec2_falcon = ctx.sec2_falcon;let dev = pdev.as_ref ();let hal =super ::hal::gsp_hal (chipset);
@@ -118,7 +59,11 @@ pub(crate) fn boot(let wpr_meta =Coherent ::init (dev,GFP_KERNEL ,GspFwWprMeta ::new (&gsp_fw, &fb_layout))?;// Perform the chipset-specific boot sequence, and retrieve the unload bundle. -let unload_guard = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?; +let unload_bundle = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?; + +let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { +let _ = self.unload (dev, bar, gsp_falcon, sec2_falcon, unload_bundle); + }); gsp_falcon.write_os_version (bar, gsp_fw.bootloader.app_version);
drivers/gpu/nova-core/gsp/hal.rs +6 -7
@@ -24,7 +24,6 @@ Chipset, // }, gsp::{ - boot::BootUnloadGuard,Gsp ,GspBootContext ,GspFwWprMeta ,//
@@ -51,15 +50,15 @@ fn run(pub (super )trait GspHal :Send {/// Performs the GSP boot process, loading and running the required firmwares as needed. /// -/// Upon success, returns a guard that runs the GSP unload sequence if GSP boot does not -/// complete. -fn boot <'a>( +/// Upon success, returns the [`crate::gsp::UnloadBundle`] to use with [`Gsp::unload`], if one +/// could be created. +fn boot ( &self, - gsp: &'aGsp , - ctx: &GspBootContext <'a>, + gsp: &Gsp , + ctx: &GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, - ) ->Result <BootUnloadGuard <'a>>; + ) ->Result <Option <crate ::gsp::UnloadBundle >>;/// Performs HAL-specific post-GSP boot tasks. ///
drivers/gpu/nova-core/gsp/hal/gh100.rs +8 -12
@@ -23,7 +23,6 @@ Fsp, // }, gsp::{ - boot::BootUnloadGuard, hal::{GspHal ,UnloadBundle ,//
@@ -145,27 +144,22 @@ impl GspHal for Gh100 {/// /// This path uses FSP to establish a chain of trust and boot GSP-FMC. FSP handles /// the GSP boot internally - no manual GSP reset/boot is needed. -fn boot <'a>( +fn boot ( &self, - gsp: &'aGsp , - ctx: &GspBootContext <'a>, + gsp: &Gsp , + ctx: &GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, - ) ->Result <BootUnloadGuard <'a>> { + ) ->Result <Option <crate ::gsp::UnloadBundle >> {let dev = ctx.dev ();let bar = ctx.bar;let chipset = ctx.chipset;let gsp_falcon = ctx.gsp_falcon; -let sec2_falcon = ctx.sec2_falcon;let unload_bundle =crate ::gsp::UnloadBundle (KBox ::new (FspUnloadBundle ,GFP_KERNEL )?as KBox <dyn UnloadBundle > ); -// Wrap the unload bundle into a drop guard so it is automatically run upon failure. -let unload_guard = -BootUnloadGuard ::new (gsp, dev, bar, gsp_falcon, sec2_falcon,Some (unload_bundle)); -let mut fsp =Fsp ::wait_secure_boot (dev, bar, chipset)?; let args = FmcBootArgs::new(
@@ -176,11 +170,13 @@ fn boot<'a>( false, )?; - fsp.boot_fmc (dev, bar, fb_layout, &args)?; +// Keep the result as we want to wait for lockdown release even in case of error, to make +// sure `args` is not accessed by the GSP anymore. +let res = fsp.boot_fmc (dev, bar, fb_layout, &args);wait_for_gsp_lockdown_release (dev, bar, gsp_falcon, args.boot_params_dma_handle ())?; -Ok (unload_guard) + res.map (|()|Some (unload_bundle)) } }
drivers/gpu/nova-core/gsp/hal/tu102.rs +13 -10
@@ -6,7 +6,8 @@use kernel::{ device, dma::Coherent , - io::Io ,// + io::Io , + types::ScopeGuard ,// };use crate ::{
@@ -32,7 +33,6 @@ }, gpu::Chipset, gsp::{ - boot::BootUnloadGuard, hal::{GspHal ,UnloadBundle ,//
@@ -264,13 +264,13 @@ fn run_fwsec_frts(struct Tu102 ;impl GspHal for Tu102 { -fn boot <'a>( +fn boot ( &self, - gsp: &'aGsp , - ctx: &GspBootContext <'a>, + gsp: &Gsp , + ctx: &GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, - ) ->Result <BootUnloadGuard <'a>> { + ) ->Result <Option <crate ::gsp::UnloadBundle >> {let dev = ctx.dev ();let bar = ctx.bar;let chipset = ctx.chipset;
@@ -296,9 +296,12 @@ fn boot<'a>( .ok() .map (crate ::gsp::UnloadBundle ); -// Wrap the unload bundle into a drop guard so it is automatically run upon failure. -let unload_guard = -BootUnloadGuard ::new (gsp, dev, bar, gsp_falcon, sec2_falcon, unload_bundle); +// Run the unload bundle to try and recover the GSP if an error occurs. +let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { +if let Some (unload_bundle) = unload_bundle { +let _ = unload_bundle.0 .run (dev, bar, gsp_falcon, sec2_falcon); + } + });// FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100). if !fb_layout.frts.is_empty () {
@@ -329,7 +332,7 @@ fn boot<'a>( )? .run(dev, bar, sec2_falcon, wpr_meta)?; -Ok (unload_guard) +Ok (unload_guard.dismiss ()) }fn post_boot (&self, gsp: &Gsp , ctx: &GspBootContext <'_>, gsp_fw: &GspFirmware ) ->Result {
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 4/12 gpu: nova-core: gsp: pass GspBootContext to unload methods
In reply to Alexandre CourbotGspBootContext contains the resources required to boot the GSP. As it turns out, this is also the context required for unloading it.
Reflect that fact by replacing the arguments of Gsp::unload and UnloadBundle::run with the GspBootContext. This symmetry between Gsp::boot and Gsp::unload will also be convenient when we want to make these methods generic over the boot context corresponding to the boot method used.
--- drivers/gpu/nova-core/gpu.rs | 19 ++++++++++++++++--- drivers/gpu/nova-core/gsp/boot.rs | 28 +++++++++++++--------------- drivers/gpu/nova-core/gsp/hal.rs | 15 +-------------- drivers/gpu/nova-core/gsp/hal/gh100.rs | 13 +++---------- drivers/gpu/nova-core/gsp/hal/tu102.rs | 22 ++++++++++------------ 5 files changed, 43 insertions(+), 54 deletions(-)
drivers/gpu/nova-core/gpu.rs +16 -3
@@ -269,7 +269,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {#[pin_data( PinnedDrop )] struct GspResources <'gpu> {/// Device owning the GPU. - device: &'gpu device::Device <device::Bound >, + device: &'gpu pci::Device <device::Bound >, +/// Details about the chipset. + spec:Spec ,/// MMIO mapping of PCI BAR 0. bar:Bar0 <'gpu>,/// GSP falcon instance, used for GSP boot up and cleanup.
@@ -312,7 +314,16 @@ fn drop(self: Pin<&mut Self>) { .gsp .as_ref () .get_ref () - .unload (device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle) + .unload ( +GspBootContext { + pdev: device, + bar, + chipset: this.spec.chipset, + gsp_falcon: &*this.gsp_falcon, + sec2_falcon: &*this.sec2_falcon, + }, + bundle, + ) .inspect_err (|e|dev_err! (device,"failed to unload GSP: {:?}\n" , e)); } }
@@ -344,7 +355,9 @@ pub(crate) fn new( sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, gsp_resources <-try_pin_init! (GspResources { - device: pdev.as_ref (), + device: pdev, + + spec: *spec, bar,
drivers/gpu/nova-core/gsp/boot.rs +13 -15
@@ -3,7 +3,6 @@use kernel::{ bits, - device, dma::Coherent , io::poll::read_poll_timeout, prelude::*,
@@ -15,7 +14,6 @@ driver::Bar0, falcon::{ gsp::Gsp, - sec2::Sec2, Falcon, // }, fb::FbLayout,
@@ -47,7 +45,6 @@ pub(crate) fn boot(let bar = ctx.bar;let chipset = ctx.chipset;let gsp_falcon = ctx.gsp_falcon; -let sec2_falcon = ctx.sec2_falcon;let dev = pdev.as_ref ();let hal =super ::hal::gsp_hal (chipset);
@@ -61,9 +58,11 @@ pub(crate) fn boot(// Perform the chipset-specific boot sequence, and retrieve the unload bundle. let unload_bundle = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?; -let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { -let _ = self.unload (dev, bar, gsp_falcon, sec2_falcon, unload_bundle); - }); +let unload_guard = +ScopeGuard ::new_with_data ((ctx, unload_bundle), |(ctx, unload_bundle)| { +let _ = self.unload (ctx, unload_bundle); + }); +let ctx = &unload_guard.0 ; gsp_falcon.write_os_version (bar, gsp_fw.bootloader.app_version);
@@ -82,12 +81,12 @@ pub(crate) fn boot( self.cmdq .send_command_no_wait (bar, commands::SetRegistry ::new ())?; - hal.post_boot (&self, &ctx, &gsp_fw)?; + hal.post_boot (&self, ctx, &gsp_fw)?;// Wait until GSP is fully initialized. commands::wait_gsp_init_done (&self.cmdq)?; -Ok (unload_guard.dismiss ()) +Ok (unload_guard.dismiss ().1 ) }/// Shut down the GSP and wait until it is offline.
@@ -116,17 +115,16 @@ fn shutdown_gsp(/// This stops all activity on the GSP. pub (crate )fn unload ( &self, - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - gsp_falcon: &Falcon <Gsp >, - sec2_falcon: &Falcon <Sec2 >, + ctx:super ::GspBootContext <'_>, unload_bundle:Option <super ::UnloadBundle >, ) ->Result { +let dev = ctx.dev (); +// Shut down the GSP. Keep going even in case of error. let mut res =Self ::shutdown_gsp ( &self.cmdq, - bar, - gsp_falcon, + ctx.bar, + ctx.gsp_falcon, commands::PowerStateLevel ::Level0 , ) .inspect_err (|e|dev_err! (dev,"GSP shutdown failed: {:?}\n" , e));
@@ -136,7 +134,7 @@ pub(crate) fn unload( res = res.and ( unload_bundle .0 - .run (dev, bar, gsp_falcon, sec2_falcon) + .run (&ctx) .inspect_err (|e|dev_err! (dev,"Unload bundle failed: {:?}\n" , e)), ); }else {
drivers/gpu/nova-core/gsp/hal.rs +1 -14
@@ -5,18 +5,11 @@mod tu102;use kernel::{ - device, dma::Coherent , prelude::*,// };use crate ::{ - driver::Bar0 , - falcon::{ - gsp::Gsp as GspEngine , - sec2::Sec2 , -Falcon ,// - }, fb::FbLayout , firmware::gsp::GspFirmware , gpu::{
@@ -37,13 +30,7 @@/// required for unloading is prepared at load time, and stored here until it needs to be run. pub (super )trait UnloadBundle :Send {/// Performs the steps required to properly reset the GSP after it has been stopped. -fn run ( - &self, - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - gsp_falcon: &Falcon <GspEngine >, - sec2_falcon: &Falcon <Sec2 >, - ) ->Result ; +fn run (&self, ctx: &GspBootContext <'_>) ->Result ; }/// Trait implemented by GSP HALs.
drivers/gpu/nova-core/gsp/hal/gh100.rs +3 -10
@@ -14,7 +14,6 @@ driver::Bar0, falcon::{ gsp::Gsp as GspEngine, - sec2::Sec2, Falcon, // }, fb::FbLayout,
@@ -118,22 +117,16 @@ fn wait_for_gsp_lockdown_release(struct FspUnloadBundle ;impl UnloadBundle for FspUnloadBundle { -fn run ( - &self, - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - gsp_falcon: &Falcon <GspEngine >, - _sec2_falcon: &Falcon <Sec2 >, - ) ->Result { +fn run (&self, ctx: &GspBootContext <'_>) ->Result {// GSP falcon does most of the work of resetting, so just wait for it to finish. read_poll_timeout ( - ||Ok (gsp_falcon.is_riscv_active (bar)), + ||Ok (ctx.gsp_falcon.is_riscv_active (ctx.bar)), |&active| !active,Delta ::from_millis (10 ),Delta ::from_secs (5 ), ) .map (|_| ()) - .inspect_err (|_|dev_err! (dev,"GSP falcon failed to halt\n" )) + .inspect_err (|_|dev_err! (ctx.dev (),"GSP falcon failed to halt\n" )) } }
drivers/gpu/nova-core/gsp/hal/tu102.rs +10 -12
@@ -124,18 +124,15 @@ fn build( }impl UnloadBundle for Sec2UnloadBundle { -fn run ( - &self, - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - gsp_falcon: &Falcon <GspEngine >, - sec2_falcon: &Falcon <Sec2 >, - ) ->Result { +fn run (&self, ctx: &GspBootContext <'_>) ->Result { +let dev = ctx.dev (); +let bar = ctx.bar; +// Run FWSEC-SB to reset the GSP falcon to its pre-libos state. // Log errors but keep going if it fails. let fwsec_sb_res = self .fwsec_sb - .run (dev, bar, gsp_falcon) + .run (dev, bar, ctx.gsp_falcon) .inspect_err (|e|dev_err! (dev,"FWSEC-SB failed to run: {:?}\n" , e));// Remove WPR2 region if set.
@@ -145,13 +142,14 @@ fn run(return Ok (()); } - sec2_falcon.reset (bar)?; - sec2_falcon.load (dev, bar, &self.booter_unloader)?; + ctx.sec2_falcon.reset (bar)?; + ctx.sec2_falcon.load (dev, bar, &self.booter_unloader)?;// Sentinel value to confirm that Booter Unloader has run. const MAILBOX_SENTINEL :u32 =0xff ;let (mbox0, _) = - sec2_falcon.boot (bar,Some (MAILBOX_SENTINEL ),Some (MAILBOX_SENTINEL ))?; + ctx.sec2_falcon + .boot (bar,Some (MAILBOX_SENTINEL ),Some (MAILBOX_SENTINEL ))?;if mbox0 !=0 {dev_err! (dev,"Booter Unloader returned error 0x{:x}\n" , mbox0);return Err (EINVAL );
@@ -299,7 +297,7 @@ fn boot(// Run the unload bundle to try and recover the GSP if an error occurs. let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| {if let Some (unload_bundle) = unload_bundle { -let _ = unload_bundle.0 .run (dev, bar, gsp_falcon, sec2_falcon); +let _ = unload_bundle.0 .run (ctx); } });
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 5/12 gpu: nova-core: gsp: centralize missing unload bundle warnings
In reply to Alexandre CourbotThe warning emitted when the unload bundle cannot be constructed is valid regardless of the boot method, but it was local to Tu102. Move it to Gsp::boot so it applies to all boot methods.
--- drivers/gpu/nova-core/gsp/boot.rs | 10 +++++++++- drivers/gpu/nova-core/gsp/hal/tu102.rs | 9 +-------- 2 files changed, 10 insertions(+), 9 deletions(-)
drivers/gpu/nova-core/gsp/boot.rs +9 -1
@@ -56,7 +56,15 @@ pub(crate) fn boot(let wpr_meta =Coherent ::init (dev,GFP_KERNEL ,GspFwWprMeta ::new (&gsp_fw, &fb_layout))?;// Perform the chipset-specific boot sequence, and retrieve the unload bundle. -let unload_bundle = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?; +let unload_bundle = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?.or_else (|| { +dev_warn! (dev,"The GSP won't be able to unload properly on unbind.\n" ); +dev_warn! ( + dev, +"The GPU will need to be reset before the driver can bind again.\n" + ); + +None + });let unload_guard = ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| {
drivers/gpu/nova-core/gsp/hal/tu102.rs +1 -8
@@ -283,14 +283,7 @@ fn boot(// be probed again. let unload_bundle =Sec2UnloadBundle ::build (dev, bar, chipset, &bios, gsp_falcon, sec2_falcon) - .inspect_err (|e| { -dev_warn! (dev,"Failed to prepare unload firmware: {:?}\n" , e); -dev_warn! (dev,"The GSP won't be able to unload properly on unbind.\n" ); -dev_warn! ( - dev, -"The GPU will need to be reset before the driver can bind again.\n" - ); - }) + .inspect_err (|e|dev_warn! (dev,"Failed to prepare unload firmware: {:?}\n" , e)) .ok () .map (crate ::gsp::UnloadBundle );
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 6/12 gpu: nova-core: gsp: fold TU102 unload bundle construction into HAL method
In reply to Alexandre CourbotThe construction of the unload bundle is currently a bit convoluted and could be done in one function instead of two.
Additionally, turn that function into a method of Tu102. A following patch will turn the "use FWSEC bootloader" property into a flag of the TU102 HAL itself, and making this a method will allow the code to access it instead of querying Chipset.
--- drivers/gpu/nova-core/gsp/hal/tu102.rs | 96 ++++++++++++++++------------------ 1 file changed, 45 insertions(+), 51 deletions(-)
drivers/gpu/nova-core/gsp/hal/tu102.rs +45 -51
@@ -56,23 +56,6 @@ enum FwsecUnloadFirmware { }impl FwsecUnloadFirmware { -/// Loads the FWSEC SB firmware, as well as its bootloader if `chipset` requires it. -fn new ( - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - chipset:Chipset , - bios: &Vbios , - gsp_falcon: &Falcon <GspEngine >, - ) ->Result <Self > { -let fwsec_sb =FwsecFirmware ::new (dev, gsp_falcon, bar, bios,FwsecCommand ::Sb )?; - -Ok (if chipset.needs_fwsec_bootloader () { -Self ::WithBl (FwsecFirmwareWithBl ::new (fwsec_sb, dev, chipset)?) - }else { -Self ::WithoutBl (fwsec_sb) - }) - } -/// Runs the FWSEC SB firmware. fn run ( &self,
@@ -94,35 +77,6 @@ struct Sec2UnloadBundle { booter_unloader: BooterFirmware, } -impl Sec2UnloadBundle { -/// Load and prepare the resources required to properly reset the GSP after it has been stopped. -fn build ( - dev: &device::Device <device::Bound >, - bar:Bar0 <'_>, - chipset:Chipset , - bios: &Vbios , - gsp_falcon: &Falcon <GspEngine >, - sec2_falcon: &Falcon <Sec2 >, - ) ->Result <KBox <dyn UnloadBundle >> { -KBox ::new ( -Self { - fwsec_sb:FwsecUnloadFirmware ::new (dev, bar, chipset, bios, gsp_falcon)?, - booter_unloader:BooterFirmware ::new ( - dev, -BooterKind ::Unloader , - chipset, -FIRMWARE_VERSION , - sec2_falcon, - bar, - )?, - }, -GFP_KERNEL , - ) - .map (|b| bas KBox <dyn UnloadBundle >) - .map_err (Into ::into) - } -} -impl UnloadBundle for Sec2UnloadBundle {fn run (&self, ctx: &GspBootContext <'_>) ->Result {let dev = ctx.dev ();
@@ -261,6 +215,47 @@ fn run_fwsec_frts(struct Tu102 ; +impl Tu102 { +/// Load and prepare the resources required to properly reset the GSP after it has been stopped. +fn build_unload_bundle ( + &self, + dev: &device::Device <device::Bound >, + bar:Bar0 <'_>, + chipset:Chipset , + bios: &Vbios , + gsp_falcon: &Falcon <GspEngine >, + sec2_falcon: &Falcon <Sec2 >, + ) ->Result <crate ::gsp::UnloadBundle > { +// Load the FWSEC SB firmware, as well as its bootloader if required. +let fwsec_sb =FwsecFirmware ::new (dev, gsp_falcon, bar, bios,FwsecCommand ::Sb ).and_then ( + |fwsec_sb| { +Ok (if chipset.needs_fwsec_bootloader () { +FwsecUnloadFirmware ::WithBl (FwsecFirmwareWithBl ::new (fwsec_sb, dev, chipset)?) + }else { +FwsecUnloadFirmware ::WithoutBl (fwsec_sb) + }) + }, + )?; + +KBox ::new ( +Sec2UnloadBundle { + fwsec_sb, + booter_unloader:BooterFirmware ::new ( + dev, +BooterKind ::Unloader , + chipset, +FIRMWARE_VERSION , + sec2_falcon, + bar, + )?, + }, +GFP_KERNEL , + ) + .map (|b|crate ::gsp::UnloadBundle (b)) + .map_err (Into ::into) + } +} +impl GspHal for Tu102 {fn boot ( &self,
@@ -281,11 +276,10 @@ fn boot(// // If the unload bundle creation fails, the GPU will need to be reset before the driver can // be probed again. -let unload_bundle = -Sec2UnloadBundle ::build (dev, bar, chipset, &bios, gsp_falcon, sec2_falcon) - .inspect_err (|e|dev_warn! (dev,"Failed to prepare unload firmware: {:?}\n" , e)) - .ok () - .map (crate ::gsp::UnloadBundle ); +let unload_bundle = self + .build_unload_bundle (dev, bar, chipset, &bios, gsp_falcon, sec2_falcon) + .inspect_err (|e|dev_warn! (dev,"Failed to prepare unload firmware: {:?}\n" , e)) + .ok ();// Run the unload bundle to try and recover the GSP if an error occurs. let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 7/12 gpu: nova-core: gsp: turn FWSEC execution into HAL method
In reply to Alexandre CourbotTurn the run_fwsec_frts function into a method of Tu102. A following patch will turn the "use FWSEC bootloader" property into a flag of the TU102 HAL itself, and making this a method will allow the code to access it instead of querying Chipset.
--- drivers/gpu/nova-core/gsp/hal/tu102.rs | 175 +++++++++++++++++---------------- 1 file changed, 88 insertions(+), 87 deletions(-)
drivers/gpu/nova-core/gsp/hal/tu102.rs +88 -87
@@ -127,95 +127,96 @@ fn run(&self, ctx: &GspBootContext<'_>) -> Result { } } -/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly -/// created the WPR2 region. -fn run_fwsec_frts ( - dev: &device::Device <device::Bound >, - chipset:Chipset , - falcon: &Falcon <GspEngine >, - bar:Bar0 <'_>, - bios: &Vbios , - fb_layout: &FbLayout , -) ->Result { -// Check that the WPR2 region does not already exist - if it does, we cannot run -// FWSEC-FRTS until the GPU is reset. -if bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI ).higher_bound () !=0 { -dev_err! ( - dev, -"WPR2 region already exists - GPU needs to be reset to proceed\n" - ); -return Err (EBUSY ); - } - -// FWSEC-FRTS will create the WPR2 region. -let fwsec_frts =FwsecFirmware ::new ( - dev, - falcon, - bar, - bios, -FwsecCommand ::Frts { - frts_addr: fb_layout.frts.start, - frts_size: fb_layout.frts.len (), - }, - )?; - -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 = bar - .read (regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR ) - .frts_err_code (); -if frts_status !=0 { -dev_err! ( - dev, -"FWSEC-FRTS returned with error code {:#x}\n" , - frts_status - ); - -return Err (EIO ); - } - -// Check that the WPR2 region has been created as we requested. -let (wpr2_lo, wpr2_hi) = ( - bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO ).lower_bound (), - bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI ).higher_bound (), - ); - -match (wpr2_lo, wpr2_hi) { - (_,0 ) => { -dev_err! (dev,"WPR2 region not created after running FWSEC-FRTS\n" ); - -Err (EIO ) - } - (wpr2_lo, _)if wpr2_lo != fb_layout.frts.start => { -dev_err! ( - dev, -"WPR2 region created at unexpected address {:#x}; expected {:#x}\n" , - wpr2_lo, - fb_layout.frts.start, - ); - -Err (EIO ) - } - (wpr2_lo, wpr2_hi) => { -dev_dbg! (dev,"WPR2: {:#x}-{:#x}\n" , wpr2_lo, wpr2_hi); -dev_dbg! (dev,"GPU instance built\n" ); - -Ok (()) - } - } -} -struct Tu102 ;impl Tu102 { +/// Helper method to load and run the FWSEC-FRTS firmware and confirm that it has properly +/// created the WPR2 region. +fn run_fwsec_frts ( + &self, + dev: &device::Device <device::Bound >, + chipset:Chipset , + falcon: &Falcon <GspEngine >, + bar:Bar0 <'_>, + bios: &Vbios , + fb_layout: &FbLayout , + ) ->Result { +// Check that the WPR2 region does not already exist - if it does, we cannot run +// FWSEC-FRTS until the GPU is reset. +if bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI ).higher_bound () !=0 { +dev_err! ( + dev, +"WPR2 region already exists - GPU needs to be reset to proceed\n" + ); +return Err (EBUSY ); + } + +// FWSEC-FRTS will create the WPR2 region. +let fwsec_frts =FwsecFirmware ::new ( + dev, + falcon, + bar, + bios, +FwsecCommand ::Frts { + frts_addr: fb_layout.frts.start, + frts_size: fb_layout.frts.len (), + }, + )?; + +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 = bar + .read (regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR ) + .frts_err_code (); +if frts_status !=0 { +dev_err! ( + dev, +"FWSEC-FRTS returned with error code {:#x}\n" , + frts_status + ); + +return Err (EIO ); + } + +// Check that the WPR2 region has been created as we requested. +let (wpr2_lo, wpr2_hi) = ( + bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO ).lower_bound (), + bar.read (regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI ).higher_bound (), + ); + +match (wpr2_lo, wpr2_hi) { + (_,0 ) => { +dev_err! (dev,"WPR2 region not created after running FWSEC-FRTS\n" ); + +Err (EIO ) + } + (wpr2_lo, _)if wpr2_lo != fb_layout.frts.start => { +dev_err! ( + dev, +"WPR2 region created at unexpected address {:#x}; expected {:#x}\n" , + wpr2_lo, + fb_layout.frts.start, + ); + +Err (EIO ) + } + (wpr2_lo, wpr2_hi) => { +dev_dbg! (dev,"WPR2: {:#x}-{:#x}\n" , wpr2_lo, wpr2_hi); +dev_dbg! (dev,"GPU instance built\n" ); + +Ok (()) + } + } + } +/// Load and prepare the resources required to properly reset the GSP after it has been stopped. fn build_unload_bundle ( &self,
@@ -290,7 +291,7 @@ fn boot(// FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100). if !fb_layout.frts.is_empty () { -run_fwsec_frts (dev, chipset, gsp_falcon, bar, &bios, fb_layout)?; + self.run_fwsec_frts (dev, chipset, gsp_falcon, bar, &bios, fb_layout)?; } gsp_falcon.reset (bar)?;
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 8/12 gpu: nova-core: gsp: make use of FWSEC bootloader a property of the TU102 HAL
In reply to Alexandre CourbotBy being in the TU102 HAL, we already know that the GSP boot method is the SEC2 Booter, so the only variable is whether the FWSEC bootloader is used or not. Since Chipset also includes the variants that boot FSP, querying it for that information introduces a potential code path where the chipset actually boots via FSP that the current code doesn't handle.
Turn the use of FWSEC bootloader into a property of the Tu102 HAL, and give GA102+ chipsets their own instance with that property set to false. This removes the invalid code path and the only use of Chipset is now to load the correct firmware files.
This also removes some uses of the Chipset::needs_fwsec_bootloader method and prepares the ground for removing it.
--- drivers/gpu/nova-core/gsp/hal.rs | 5 ++++- drivers/gpu/nova-core/gsp/hal/ga102.rs | 14 ++++++++++++++ drivers/gpu/nova-core/gsp/hal/tu102.rs | 15 +++++++++++---- 3 files changed, 29 insertions(+), 5 deletions(-)
drivers/gpu/nova-core/gsp/hal.rs +4 -1
@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +mod ga102;mod gh100;mod tu102;
@@ -59,7 +60,9 @@ fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware/// Returns the GSP HAL to be used for `chipset`. pub (super )fn gsp_hal (chipset:Chipset ) -> &'staticdyn GspHal {match chipset.arch () { -Architecture ::Turing |Architecture ::Ampere |Architecture ::Ada => tu102::TU102_HAL , +Architecture ::Turing => tu102::TU102_HAL , +Architecture ::Ampere if matches! (chipset,Chipset ::GA100 ) => tu102::TU102_HAL , +Architecture ::Ampere |Architecture ::Ada => ga102::GA102_HAL ,Architecture ::Hopper |Architecture ::BlackwellGB10x |Architecture ::BlackwellGB20x => { gh100::GH100_HAL }
drivers/gpu/nova-core/gsp/hal/ga102.rs new file +14 -0
@@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +use crate ::gsp::hal::{ + tu102::Tu102 , +GspHal ,// +}; + +/// The GA102 HAL is like the TU102 one, except it doesn't use the bootloader. +const GA102 :Tu102 =Tu102 { + needs_fwsec_bootloader:false , +}; + +pub (super )const GA102_HAL : &dyn GspHal = &GA102 ;
drivers/gpu/nova-core/gsp/hal/tu102.rs +11 -4
@@ -127,7 +127,10 @@ fn run(&self, ctx: &GspBootContext<'_>) -> Result { } } -struct Tu102 ; +pub (super )struct Tu102 { +/// If `true`, then the FWSEC-FRTS bootloader will be used to load the actual firmware. +pub (super ) needs_fwsec_bootloader:bool , +}impl Tu102 {/// Helper method to load and run the FWSEC-FRTS firmware and confirm that it has properly
@@ -163,7 +166,7 @@ fn run_fwsec_frts( }, )?; -if chipset.needs_fwsec_bootloader () { +if self.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)?;
@@ -230,7 +233,7 @@ fn build_unload_bundle(// Load the FWSEC SB firmware, as well as its bootloader if required. let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bar, bios, FwsecCommand::Sb).and_then( |fwsec_sb| { - Ok(if chipset.needs_fwsec_bootloader() { + Ok(if self.needs_fwsec_bootloader { FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?) } else { FwsecUnloadFirmware::WithoutBl(fwsec_sb)
@@ -333,5 +336,9 @@ fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) - } } -const TU102 :Tu102 =Tu102 ; +/// The TU102 HAL requires the use of the FWSEC bootloader. +const TU102 :Tu102 =Tu102 { + needs_fwsec_bootloader:true , +}; +pub (super )const TU102_HAL : &dyn GspHal = &TU102 ;
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 9/12 gpu: nova-core: introduce GspBootMethod
In reply to Alexandre CourbotThe GSP boot method is currently determined by two ad-hoc methods of Chipset: uses_fsp (a boolean telling whether to use the FSP boot path or the Sec2 Booter one) and needs_fwsec_bootloader (another boolean valid only for the Sec2 Booter method that tells whether the FWSEC bootloader must be used).
This is neither extensible nor sound: the combination uses_fsp && needs_fwsec_bootloader is invalid, but can still be expressed.
Thus, unify these two predicates into a single gsp_boot_method method that returns an enum type unambiguously describing the boot method to use. This ensures that no invalid combination can be expressed, which makes matching sounder.
--- drivers/gpu/nova-core/firmware.rs | 25 ++++++++++++-------- drivers/gpu/nova-core/firmware/fwsec.rs | 5 ++-- drivers/gpu/nova-core/gpu.rs | 16 ------------- drivers/gpu/nova-core/gsp.rs | 42 ++++++++++++++++++++++++++++++++- drivers/gpu/nova-core/gsp/hal.rs | 22 ++++++++--------- 5 files changed, 70 insertions(+), 40 deletions(-)
drivers/gpu/nova-core/firmware.rs +15 -10
@@ -21,6 +21,7 @@ FalconFirmware, // }, gpu, + gsp::GspBootMethod, num::{FromSafeCast ,IntoSafeCast ,//
@@ -429,17 +430,21 @@ const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {// FSP-based chipsets (Hopper, Blackwell and later) boot the GSP via the FMC image loaded by // FSP. Older chipsets use the SEC2 booter instead. -let this =if chipset.uses_fsp () { - this.make_entry_file (name,"fmc" ) - }else { - this.make_entry_file (name,"booter_load" ) - .make_entry_file (name,"booter_unload" ) - }; +match chipset.gsp_boot_method () { +GspBootMethod ::Sec2 { + needs_fwsec_bootloader, + } => { +let mut this = this + .make_entry_file (name,"booter_load" ) + .make_entry_file (name,"booter_unload" ); -if chipset.needs_fwsec_bootloader () { - this.make_entry_file (name,"gen_bootloader" ) - }else { - this +if needs_fwsec_bootloader { + this = this.make_entry_file (name,"gen_bootloader" ) + } + + this + } +GspBootMethod ::Fsp => this.make_entry_file (name,"fmc" ), } }
drivers/gpu/nova-core/firmware/fwsec.rs +3 -2
@@ -388,8 +388,9 @@ 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. +/// [`Chipset::gsp_boot_method()`](crate::gpu::Chipset::gsp_boot_method) returns +/// `GspBootMethod::Sec2 { needs_fwsec_bootloader: false }`). On chipsets where +/// `needs_fwsec_bootloader` is `true`, use [`bootloader::FwsecFirmwareWithBl`] instead. pub (crate )fn run ( &self, dev: &Device<device::Bound>,
drivers/gpu/nova-core/gpu.rs +0 -16
@@ -133,22 +133,6 @@ 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 ) - } - -/// Returns `true` if this chipset boots via FSP (Hopper and later), which requires the FMC -/// firmware image. -pub (crate )const fn uses_fsp (self) ->bool { -matches! ( - self.arch (), -Architecture ::Hopper |Architecture ::BlackwellGB10x |Architecture ::BlackwellGB20x - ) - } -/// Returns the address range of the PCI config mirror space. pub (crate )fn pci_config_mirror_range (self) ->Range <u32 > { hal::gpu_hal (self).pci_config_mirror_range ()
drivers/gpu/nova-core/gsp.rs +41 -1
53 lines not on this page · raw message
drivers/gpu/nova-core/gsp/hal.rs +11 -11
@@ -13,13 +13,11 @@use crate ::{ fb::FbLayout , firmware::gsp::GspFirmware , - gpu::{ -Architecture , -Chipset ,// - }, + gpu::Chipset , gsp::{Gsp ,GspBootContext , +GspBootMethod ,GspFwWprMeta ,// }, };
@@ -59,12 +57,14 @@ fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware/// Returns the GSP HAL to be used for `chipset`. pub (super )fn gsp_hal (chipset:Chipset ) -> &'staticdyn GspHal { -match chipset.arch () { -Architecture ::Turing => tu102::TU102_HAL , -Architecture ::Ampere if matches! (chipset,Chipset ::GA100 ) => tu102::TU102_HAL , -Architecture ::Ampere |Architecture ::Ada => ga102::GA102_HAL , -Architecture ::Hopper |Architecture ::BlackwellGB10x |Architecture ::BlackwellGB20x => { - gh100::GH100_HAL - } +// The GSP HAL is entirely determined by the boot method. +match chipset.gsp_boot_method () { +GspBootMethod ::Sec2 { + needs_fwsec_bootloader:true , + } => tu102::TU102_HAL , +GspBootMethod ::Sec2 { + needs_fwsec_bootloader:false , + } => ga102::GA102_HAL , +GspBootMethod ::Fsp => gh100::GH100_HAL , } }
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 10/12 gpu: nova-core: avoid repeated calls to pci::Device::as_ref
In reply to Alexandre CourbotAdd a local Device reference created from the pci::Device in the Gpu constructor to avoid repeatedly calling as_ref.
--- drivers/gpu/nova-core/gpu.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
drivers/gpu/nova-core/gpu.rs +10 -8
@@ -317,9 +317,11 @@ pub(crate) fn new( pdev: &'gpu pci::Device<device::Core<'_>>, bar: Bar0<'gpu>, ) -> impl PinInit<Self, Error> + 'gpu { +let dev = pdev.as_ref (); +try_pin_init! (Self { - spec:Spec ::new (pdev.as_ref (), bar).inspect (|spec| { -dev_info! (pdev,"NVIDIA ({})\n" , spec); + spec:Spec ::new (dev, bar).inspect (|spec| { +dev_info! (dev,"NVIDIA ({})\n" , spec); })?,// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
@@ -332,11 +334,11 @@ pub(crate) fn new(unsafe { pdev.dma_set_mask_and_coherent (dma_mask)? }; hal.wait_gfw_boot_completion (bar) - .inspect_err (|_|dev_err! (pdev,"GFW boot did not complete\n" ))?; + .inspect_err (|_|dev_err! (dev,"GFW boot did not complete\n" ))?; },// Initialize this early because `gsp_resources` depends on it. - sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, + sysmem_flush: SysmemFlush::register(dev, bar, spec.chipset)?, gsp_resources <-try_pin_init! (GspResources { device: pdev,
@@ -346,12 +348,12 @@ pub(crate) fn new( bar, gsp_falcon: Falcon::new( - pdev.as_ref(), + dev, spec.chipset, ) .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, - sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, + sec2_falcon: Falcon::new(dev, spec.chipset)?, gsp <- Gsp::new(pdev),
@@ -371,8 +373,8 @@ pub(crate) fn new(// Obtain and display basic GPU information. let info = gsp_resources.gsp.get_static_info (bar)?;match info.gpu_name () { -Ok (name) =>dev_info! (pdev,"GPU name: {}\n" , name), -Err (e) =>dev_warn! (pdev,"GPU name unavailable: {:?}\n" , e), +Ok (name) =>dev_info! (dev,"GPU name: {}\n" , name), +Err (e) =>dev_warn! (dev,"GPU name unavailable: {:?}\n" , e), }if !info.usable_fb_regions.is_empty () {
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 11/12 gpu: nova-core: gsp: pass GspBootContext mutably
In reply to Alexandre CourbotWe want to move the Fsp instance into Gpu, which will require passing it as a mutable reference in GspBootContext, since Fsp::boot_fmc is a mutable method. In order to use the mutable references it contains, GspBootContext must also be mutable.
We will also follow up by making some methods of the Falcons mutable, which also requires passing them as mutable references.
Thus, make the GspBootContext passed to Gsp::boot and Gsp::unload mutable, and pass mutable references to it to the GSP boot HAL methods.
--- drivers/gpu/nova-core/gsp/boot.rs | 28 +++++++++++++++------------- drivers/gpu/nova-core/gsp/hal.rs | 11 ++++++++--- drivers/gpu/nova-core/gsp/hal/gh100.rs | 4 ++-- drivers/gpu/nova-core/gsp/hal/tu102.rs | 6 +++--- 4 files changed, 28 insertions(+), 21 deletions(-)
drivers/gpu/nova-core/gsp/boot.rs +15 -13
@@ -39,7 +39,7 @@ impl super::Gsp {/// [`Self::unload`]) returned. pub (crate )fn boot ( self:Pin <&mut Self >, - ctx:super ::GspBootContext <'_>, +mut ctx:super ::GspBootContext <'_>, ) ->Result <Option <super ::UnloadBundle >> {let pdev = ctx.pdev;let bar = ctx.bar;
@@ -56,21 +56,23 @@ pub(crate) fn boot(let wpr_meta =Coherent ::init (dev,GFP_KERNEL ,GspFwWprMeta ::new (&gsp_fw, &fb_layout))?;// Perform the chipset-specific boot sequence, and retrieve the unload bundle. -let unload_bundle = hal.boot (&self, &ctx, &fb_layout, &wpr_meta)?.or_else (|| { -dev_warn! (dev,"The GSP won't be able to unload properly on unbind.\n" ); -dev_warn! ( - dev, -"The GPU will need to be reset before the driver can bind again.\n" - ); +let unload_bundle = hal + .boot (&self, &mut ctx, &fb_layout, &wpr_meta)? + .or_else (|| { +dev_warn! (dev,"The GSP won't be able to unload properly on unbind.\n" ); +dev_warn! ( + dev, +"The GPU will need to be reset before the driver can bind again.\n" + ); -None - }); +None + }); -let unload_guard = +let mut unload_guard =ScopeGuard ::new_with_data ((ctx, unload_bundle), |(ctx, unload_bundle)| {let _ = self.unload (ctx, unload_bundle); }); -let ctx = &unload_guard.0 ; +let ctx = &mut unload_guard.0 ; gsp_falcon.write_os_version (bar, gsp_fw.bootloader.app_version);
@@ -123,7 +125,7 @@ fn shutdown_gsp(/// This stops all activity on the GSP. pub (crate )fn unload ( &self, - ctx:super ::GspBootContext <'_>, +mut ctx:super ::GspBootContext <'_>, unload_bundle:Option <super ::UnloadBundle >, ) ->Result {let dev = ctx.dev ();
@@ -142,7 +144,7 @@ pub(crate) fn unload( res = res.and ( unload_bundle .0 - .run (&ctx) + .run (&mut ctx) .inspect_err (|e|dev_err! (dev,"Unload bundle failed: {:?}\n" , e)), ); }else {
drivers/gpu/nova-core/gsp/hal.rs +8 -3
@@ -29,7 +29,7 @@/// required for unloading is prepared at load time, and stored here until it needs to be run. pub (super )trait UnloadBundle :Send {/// Performs the steps required to properly reset the GSP after it has been stopped. -fn run (&self, ctx: &GspBootContext <'_>) ->Result ; +fn run (&self, ctx: &mut GspBootContext <'_>) ->Result ; }/// Trait implemented by GSP HALs.
@@ -41,7 +41,7 @@ pub(super) trait GspHal: Send {fn boot ( &self, gsp: &Gsp , - ctx: &GspBootContext <'_>, + ctx: &mut GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >>;
@@ -50,7 +50,12 @@ fn boot(/// /// This method is called by the GSP boot code after the GSP is confirmed to be running, and /// after the initialization commands have been pushed onto its queue. -fn post_boot (&self, _gsp: &Gsp , _ctx: &GspBootContext <'_>, _gsp_fw: &GspFirmware ) ->Result { +fn post_boot ( + &self, + _gsp: &Gsp , + _ctx: &mut GspBootContext <'_>, + _gsp_fw: &GspFirmware , + ) ->Result {Ok (()) } }
drivers/gpu/nova-core/gsp/hal/gh100.rs +2 -2
@@ -117,7 +117,7 @@ fn wait_for_gsp_lockdown_release(struct FspUnloadBundle ;impl UnloadBundle for FspUnloadBundle { -fn run (&self, ctx: &GspBootContext <'_>) ->Result { +fn run (&self, ctx: &mut GspBootContext <'_>) ->Result {// GSP falcon does most of the work of resetting, so just wait for it to finish. read_poll_timeout ( || Ok(ctx.gsp_falcon.is_riscv_active(ctx.bar)),
@@ -140,7 +140,7 @@ impl GspHal for Gh100 {fn boot ( &self, gsp: &Gsp , - ctx: &GspBootContext <'_>, + ctx: &mut GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >> {
drivers/gpu/nova-core/gsp/hal/tu102.rs +3 -3
@@ -78,7 +78,7 @@ struct Sec2UnloadBundle { }impl UnloadBundle for Sec2UnloadBundle { -fn run (&self, ctx: &GspBootContext <'_>) ->Result { +fn run (&self, ctx: &mut GspBootContext <'_>) ->Result {let dev = ctx.dev ();let bar = ctx.bar;
@@ -264,7 +264,7 @@ impl GspHal for Tu102 {fn boot ( &self, gsp: &Gsp , - ctx: &GspBootContext <'_>, + ctx: &mut GspBootContext <'_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >> {
@@ -324,7 +324,7 @@ fn boot(Ok (unload_guard.dismiss ()) } -fn post_boot (&self, gsp: &Gsp , ctx: &GspBootContext <'_>, gsp_fw: &GspFirmware ) ->Result { +fn post_boot (&self, gsp: &Gsp , ctx: &mut GspBootContext <'_>, gsp_fw: &GspFirmware ) ->Result {GspSequencer ::run ( &gsp.cmdq, ctx,
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerPATCH v3 12/12 gpu: nova-core: store Fsp instance in Gpu
In reply to Alexandre CourbotThe Fsp instance was only used in the Hopper+ boot path, and consequently built locally (and immediately dropped) in it.
This worked well as a temporary measure, but the FSP is a GPU sub-device, so its lifetime should match the GPU rather than a single boot invocation.
It will also be needed in other parts of the driver, for instance vGPU.
Thus, create the Fsp instance in the Gpu constructor and store it there, passing it to the GSP boot as a mutable reference using GspBootContext. This makes the Fsp available even after the GSP is booted.
--- drivers/gpu/nova-core/gpu.rs | 15 ++++++++++++++- drivers/gpu/nova-core/gsp.rs | 2 ++ drivers/gpu/nova-core/gsp/hal/gh100.rs | 7 ++----- 3 files changed, 18 insertions(+), 6 deletions(-)
drivers/gpu/nova-core/gpu.rs +14 -1
@@ -22,11 +22,13 @@ Falcon, // }, fb::SysmemFlush, + fsp::Fsp, gsp::{ self, commands::GetGspStaticInfoReply, Gsp, - GspBootContext, // + GspBootContext, + GspBootMethod, // }, regs, };
@@ -262,6 +264,10 @@ struct GspResources<'gpu> { gsp_falcon: Falcon<GspFalcon>, /// SEC2 falcon instance, used for GSP boot up and cleanup. sec2_falcon: Falcon<Sec2Falcon>, + /// FSP instance, if on an arch that supports it. + // TODO: use different resource types for each boot method, and make the relevant Gsp methods + // generic against them. + fsp: Option<Fsp>,/// GSP runtime data. #[pin] gsp: Gsp,
@@ -305,6 +311,7 @@ fn drop(self: Pin<&mut Self>) { chipset: this.spec.chipset, gsp_falcon: &*this.gsp_falcon, sec2_falcon: &*this.sec2_falcon, + fsp: this.fsp.as_mut(), }, bundle, )
@@ -355,6 +362,11 @@ pub(crate) fn new( sec2_falcon: Falcon::new(dev, spec.chipset)?, + fsp: match spec.chipset.gsp_boot_method() { +GspBootMethod ::Sec2 { .. } =>None , +GspBootMethod ::Fsp =>Some (Fsp ::wait_secure_boot (dev, bar, spec.chipset)?), + }, + gsp <- Gsp::new(pdev),// This member must be initialized last, so the `UnloadBundle` can never be dropped
@@ -366,6 +378,7 @@ pub(crate) fn new( chipset: spec.chipset, gsp_falcon, sec2_falcon, + fsp: fsp.as_mut(), })?, }),
drivers/gpu/nova-core/gsp.rs +2 -0
@@ -38,6 +38,7 @@ sec2::Sec2 as Sec2Falcon, Falcon, // }, + fsp::Fsp, gpu::{Architecture ,Chipset ,//
@@ -62,6 +63,7 @@ pub(crate) struct GspBootContext<'a> { pub(crate) chipset: Chipset, pub(crate) gsp_falcon: &'a Falcon<GspFalcon>, pub(crate) sec2_falcon: &'a Falcon<Sec2Falcon>, + pub(crate) fsp: Option<&'a mut Fsp>, }impl <'a>GspBootContext <'a> {
drivers/gpu/nova-core/gsp/hal/gh100.rs +2 -5
@@ -17,10 +17,7 @@ Falcon, // }, fb::FbLayout, - fsp::{ - FmcBootArgs, - Fsp, // - }, + fsp::FmcBootArgs, gsp::{ hal::{GspHal ,
@@ -153,7 +150,7 @@ fn boot(KBox ::new (FspUnloadBundle ,GFP_KERNEL )?as KBox <dyn UnloadBundle > ); -let mut fsp =Fsp ::wait_secure_boot (dev, bar, chipset)?; +let fsp = ctx.fsp.as_mut ().ok_or (ENODEV )?;let args =FmcBootArgs ::new ( dev,
Signature · 1 line
2.54.0
Alexandre Courbot
maintainerQuoted 1 line · Alexandre Courbot
This series applies on
drm-rust-next.
... is not true anymore now that [1] has been pushed. ^_^;
I will send a rebase-only v4 shortly, sorry for the noise.