gpu: nova-core: consolidate and streamline GSP boot process
Subsystems (5)
Alexandre Courbot
maintainerv6 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 is inadequate for handling errors occurring in HALs.
Based on Eliot's feedback on v4, the series then removes the last remnants of the boot procedure leaking outside of the HALs, and consolidates everything within the GSP and FSP HALs.
This series applies on the latest drm-rust-next.
---
Changes in v6:
- Use ScopeGuard in GH100 HAL and do not wait for lockdown release on
the error path. (thanks Eliot!)
- Link to v5: https://patch.msgid.link/[email protected]
Changes in v5:
- Make GH100's HAL wait on both GSP lockdown release and GSP RISC-V halt
in case of error.
- Drop GspBootMethod and use HAL abstractions to cover differences in
boot procedures. (thanks Eliot!)
- Stylistic improvement in TU102 HAL.
- Convert more pdev -> dev in gpu.rs.
- Link to v4: https://patch.msgid.link/[email protected]
Changes in v4:
- Rebase on top of latest drm-rust-next.
- Add preparatory patch to separate the different lifetimes at play in GspBootContext.
- Link to v3: https://patch.msgid.link/[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: avoid repeated calls to pci::Device::as_ref
gpu: nova-core: gsp: pass GspBootContext mutably
gpu: nova-core: gsp: separate context and GPU lifetimes in GspBootContext
gpu: nova-core: store Fsp instance in Gpu
Eliot Courtney (1):
gpu: nova-core: move GSP firmware files decision to GSP HAL
drivers/gpu/nova-core/firmware.rs | 25 ++- drivers/gpu/nova-core/firmware/fwsec.rs | 5 +- drivers/gpu/nova-core/fsp.rs | 21 +- drivers/gpu/nova-core/gpu.rs | 68 ++++--- drivers/gpu/nova-core/gsp.rs | 22 ++- drivers/gpu/nova-core/gsp/boot.rs | 99 +++------- drivers/gpu/nova-core/gsp/hal.rs | 59 +++--- drivers/gpu/nova-core/gsp/hal/ga102.rs | 14 ++ drivers/gpu/nova-core/gsp/hal/gh100.rs | 53 +++-- drivers/gpu/nova-core/gsp/hal/tu102.rs | 330 ++++++++++++++++---------------- drivers/gpu/nova-core/gsp/sequencer.rs | 71 +++---- 11 files changed, 374 insertions(+), 393 deletions(-)
---
base-commit: d85845b64c0020b2812243a22fa79d57cc1c1e38
change-id: 20260619-nova-bootcontext-401d6107e8fb
Best regards,
Signature · 1 line
Alexandre Courbot <[email protected]>
Alexandre Courbot
maintainerPATCH v6 1/13 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, //
@@ -326,16 +323,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,
@@ -335,24 +336,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 <'a,Gsp >, -/// SEC2 falcon for core operations. -pub (crate ) sec2_falcon: &'aFalcon <'a,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,
@@ -363,12 +353,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.55.0
Alexandre Courbot
maintainerPATCH v6 2/13 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 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.
@@ -268,7 +266,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.
@@ -281,6 +279,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 >;
@@ -322,20 +332,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,
@@ -352,7 +348,6 @@ pub(crate) fn run( };let sequencer =GspSequencer { - seq_info, bar: ctx.bar, sec2_falcon: ctx.sec2_falcon, gsp_falcon: ctx.gsp_falcon,
@@ -363,14 +358,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.55.0
Alexandre Courbot
maintainerPATCH v6 3/13 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 | 27 ++++++++------ drivers/gpu/nova-core/gsp/hal/tu102.rs | 23 +++++++----- 4 files changed, 41 insertions(+), 89 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 <'a,Gsp >, - sec2_falcon: &'aFalcon <'a,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 <'a,Gsp >, - sec2_falcon: &'aFalcon <'a,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 (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 +16 -11
@@ -7,7 +7,8 @@ device, dma::Coherent, io::poll::read_poll_timeout, - time::Delta, // + time::Delta, + types::ScopeGuard, // };use crate ::{
@@ -23,7 +24,6 @@ Fsp, // }, gsp::{ - boot::BootUnloadGuard, hal::{GspHal ,UnloadBundle ,//
@@ -143,13 +143,13 @@ 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;
@@ -160,10 +160,6 @@ fn boot<'a>(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(
@@ -174,11 +170,20 @@ fn boot<'a>( false, )?; +// Wait for the GSP RISC-V core to halt in case of error. We create this guard after `args` +// to make sure that boot args are kept alive until halt, in case they are still being +// accessed. +let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { +let _ = unload_bundle.0 .run (dev, bar, gsp_falcon, sec2_falcon); + }); + fsp.boot_fmc (dev, fb_layout, &args)?; +// Wait for GSP-FMC to release the GSP lockdown, indicating that `args` is not accessed +// anymore. wait_for_gsp_lockdown_release (dev, gsp_falcon, args.boot_params_dma_handle ())?; -Ok (unload_guard) +Ok (Some (unload_guard.dismiss ())) } }
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 ,//
@@ -259,13 +259,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;
@@ -290,9 +290,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 () {
@@ -319,7 +322,7 @@ fn boot<'a>( )? .run(dev, 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.55.0
Alexandre Courbot
maintainerPATCH v6 4/13 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 | 17 ++++------------- drivers/gpu/nova-core/gsp/hal/tu102.rs | 23 +++++++++++------------ 5 files changed, 45 insertions(+), 57 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 (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 +4 -13
@@ -12,10 +12,8 @@ };use crate ::{ - driver::Bar0 , falcon::{ gsp::Gsp as GspEngine , - sec2::Sec2 ,Falcon ,// }, fb::FbLayout ,
@@ -117,22 +115,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 ()), + ||Ok (ctx.gsp_falcon.is_riscv_active ()), |&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" )) } }
@@ -154,7 +146,6 @@ fn boot(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>
@@ -174,7 +165,7 @@ fn boot(// to make sure that boot args are kept alive until halt, in case they are still being // accessed. let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { -let _ = unload_bundle.0 .run (dev, bar, gsp_falcon, sec2_falcon); +let _ = unload_bundle.0 .run (ctx); }); fsp.boot_fmc (dev, fb_layout, &args)?;
drivers/gpu/nova-core/gsp/hal/tu102.rs +11 -12
@@ -121,18 +121,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.
@@ -142,12 +139,14 @@ fn run(return Ok (()); } - sec2_falcon.reset ()?; - sec2_falcon.load (&self.booter_unloader)?; + ctx.sec2_falcon.reset ()?; + ctx.sec2_falcon.load (&self.booter_unloader)?;// Sentinel value to confirm that Booter Unloader has run. const MAILBOX_SENTINEL :u32 =0xff ; -let (mbox0, _) = sec2_falcon.boot (Some (MAILBOX_SENTINEL ),Some (MAILBOX_SENTINEL ))?; +let (mbox0, _) = ctx + .sec2_falcon + .boot (Some (MAILBOX_SENTINEL ),Some (MAILBOX_SENTINEL ))?;if mbox0 !=0 {dev_err! (dev,"Booter Unloader returned error 0x{:x}\n" , mbox0);return Err (EINVAL );
@@ -293,7 +292,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.55.0
Alexandre Courbot
maintainerPATCH v6 5/13 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
@@ -278,14 +278,7 @@ 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, 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.55.0
Alexandre Courbot
maintainerPATCH v6 6/13 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 | 85 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 46 deletions(-)
drivers/gpu/nova-core/gsp/hal/tu102.rs +39 -46
@@ -56,22 +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 >, - chipset:Chipset , - bios: &Vbios , - gsp_falcon: &Falcon <'_,GspEngine >, - ) ->Result <Self > { -let fwsec_sb =FwsecFirmware ::new (dev, gsp_falcon, 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,
@@ -93,33 +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 >, - chipset:Chipset , - bios: &Vbios , - gsp_falcon: &Falcon <'_,GspEngine >, - sec2_falcon: &Falcon <'_,Sec2 >, - ) ->Result <KBox <dyn UnloadBundle >> { -KBox ::new ( -Self { - fwsec_sb:FwsecUnloadFirmware ::new (dev, chipset, bios, gsp_falcon)?, - booter_unloader:BooterFirmware ::new ( - dev, -BooterKind ::Unloader , - chipset, -FIRMWARE_VERSION , - sec2_falcon, - )?, - }, -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 ();
@@ -257,6 +214,42 @@ 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 >, + 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, bios,FwsecCommand ::Sb )?; +let fwsec_sb =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, + )?, + }, +GFP_KERNEL , + ) + .map (|b|crate ::gsp::UnloadBundle (b)) + .map_err (Into ::into) + } +} +impl GspHal for Tu102 {fn boot ( &self,
@@ -277,10 +270,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, chipset, &bios, gsp_falcon, sec2_falcon) +let unload_bundle = self + .build_unload_bundle (dev, chipset, &bios, gsp_falcon, sec2_falcon) .inspect_err (|e|dev_warn! (dev,"Failed to prepare unload firmware: {:?}\n" , e)) - .ok () - .map (crate ::gsp::UnloadBundle ); + .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.55.0
Alexandre Courbot
maintainerPATCH v6 7/13 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 | 173 +++++++++++++++++---------------- 1 file changed, 87 insertions(+), 86 deletions(-)
drivers/gpu/nova-core/gsp/hal/tu102.rs +87 -86
@@ -127,94 +127,95 @@ 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, - 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)?; - } - -// 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, + 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)?; + } + +// 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,
@@ -284,7 +285,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 ()?;
Signature · 1 line
2.55.0
Alexandre Courbot
maintainerPATCH v6 8/13 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 (a chipset that boots via FSP) that the current code doesn't handle.
Turn the use of the 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
@@ -162,7 +165,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)?;
@@ -227,7 +230,7 @@ fn build_unload_bundle( ) -> Result<crate::gsp::UnloadBundle> {// Load the FWSEC SB firmware, as well as its bootloader if required. let fwsec_sb =FwsecFirmware ::new (dev, gsp_falcon, bios,FwsecCommand ::Sb )?; -let fwsec_sb =if chipset.needs_fwsec_bootloader () { +let fwsec_sb =if self.needs_fwsec_bootloader {FwsecUnloadFirmware ::WithBl (FwsecFirmwareWithBl ::new (fwsec_sb, dev, chipset)?) }else {FwsecUnloadFirmware ::WithoutBl (fwsec_sb)
@@ -323,5 +326,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.55.0
Alexandre Courbot
maintainerPATCH v6 9/13 gpu: nova-core: move GSP firmware files decision to GSP HAL
In reply to Alexandre CourbotFrom: Eliot Courtney <[email protected]>
The files to give the ModInfoBuilder depend on the chipset; this is HAL information, so move it there. Doing so lets us remove the uses_fsp and needs_fwsec_bootloader ad-hoc methods of Chipset.
--- 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 | 1 + drivers/gpu/nova-core/gsp/hal.rs | 19 +++++++++++++++++++ 5 files changed, 33 insertions(+), 33 deletions(-)
drivers/gpu/nova-core/firmware.rs +11 -14
@@ -21,6 +21,7 @@ FalconFirmware, // }, gpu, + gsp::boot_firmware_files, num::{FromSafeCast ,IntoSafeCast ,//
@@ -419,24 +420,20 @@ const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {const fn make_entry_chipset (self, chipset: gpu::Chipset ) ->Self {let name = chipset.name (); -let this = self +// GSP firmware files are always present. +let mut this = self .make_entry_file (name,"bootloader" ) .make_entry_file (name,"gsp" ); -// 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" ) - }; - -if chipset.needs_fwsec_bootloader () { - this.make_entry_file (name,"gen_bootloader" ) - }else { - this +// Add the firmware files specific to the GSP boot method of `chipset`. +let boot_files =boot_firmware_files (chipset); +let mut i =0 ; +while i < boot_files.len () { + this = this.make_entry_file (name, boot_files[i]); + i +=1 ; } + + this } pub(crate) const fn create(
drivers/gpu/nova-core/firmware/fwsec.rs +2 -3
@@ -385,9 +385,8 @@ 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. +/// This must only be called on chipsets that do not need the FWSEC bootloader. On chipsets +/// where the bootloader is required, use [`bootloader::FwsecFirmwareWithBl`] instead. pub (crate )fn run (&self, dev: &Device <device::Bound >, falcon: &Falcon <'_,Gsp >) ->Result <()> {// Reset falcon, load the firmware, and run it. falcon
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 +1 -0
@@ -30,6 +30,7 @@ GspFwWprMeta, LibosParams, // }; +pub (crate )use hal::boot_firmware_files;use crate ::{ driver::Bar0 ,
drivers/gpu/nova-core/gsp/hal.rs +19 -0
@@ -57,6 +57,25 @@ fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware } } +/// Returns the names of the firmware files required to boot the GSP of `chipset`, in addition to +/// the "bootloader" and "gsp" images required by all chipsets. +pub (crate )const fn boot_firmware_files (chipset:Chipset ) -> &'static [&'staticstr ] { +match chipset.arch () { +// Turing chipsets boot the GSP via the SEC2 Booter, and require the FWSEC bootloader. +Architecture ::Turing => &["booter_load" ,"booter_unload" ,"gen_bootloader" ], +// GA100 also requires the FWSEC bootloader. +Architecture ::Ampere if matches! (chipset,Chipset ::GA100 ) => { + &["booter_load" ,"booter_unload" ,"gen_bootloader" ] + } +// Other Ampere chipsets, as well as Ada chipsets, run FWSEC directly. +Architecture ::Ampere |Architecture ::Ada => &["booter_load" ,"booter_unload" ], +// Hopper and later chipsets boot the GSP via the FMC image loaded by FSP. +Architecture ::Hopper |Architecture ::BlackwellGB10x |Architecture ::BlackwellGB20x => { + &["fmc" ] + } + } +} +/// Returns the GSP HAL to be used for `chipset`. pub (super )fn gsp_hal (chipset:Chipset ) -> &'staticdyn GspHal {match chipset.arch () {
Signature · 1 line
2.55.0
Alexandre Courbot
maintainerPATCH v6 10/13 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 | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
drivers/gpu/nova-core/gpu.rs +13 -11
@@ -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,13 +348,13 @@ pub(crate) fn new( bar, gsp_falcon: Falcon::new( - pdev.as_ref(), + dev, spec.chipset, bar ) .inspect(|falcon| falcon.clear_swgen0_intr())?, - sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar)?, + sec2_falcon: Falcon::new(dev, spec.chipset, bar)?, gsp <- Gsp::new(pdev),
@@ -372,18 +374,18 @@ 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 () { -dev_dbg! (pdev,"Usable FB regions:\n" ); +dev_dbg! (dev,"Usable FB regions:\n" );for regionin &info.usable_fb_regions { -dev_dbg! (pdev," - {:#x?}\n" , region); +dev_dbg! (dev," - {:#x?}\n" , region); }dev_dbg! ( - pdev, + dev,"Total usable VRAM: {} MiB\n" , info.usable_fb_regions.iter ().fold (0u64 , |res, region| res .saturating_add (region.end - region.start))
Signature · 1 line
2.55.0
Alexandre Courbot
maintainerPATCH v6 11/13 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 (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
@@ -31,7 +31,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.
@@ -43,7 +43,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 >>;
@@ -52,7 +52,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
@@ -115,7 +115,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()),
@@ -138,7 +138,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;
@@ -258,7 +258,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 >> {
@@ -314,7 +314,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.55.0
Alexandre Courbot
maintainerPATCH v6 12/13 gpu: nova-core: gsp: separate context and GPU lifetimes in GspBootContext
In reply to Alexandre CourbotFalcon instances retain references tied to the lifetime of the bound GPU. GspBootContext currently uses that same lifetime for its own borrows of the Falcon instances and other references.
But these lifetimes are independent: the references captured by a Falcon remain valid for the GPU lifetime, while the context only borrows the Falcon for the duration of a boot or unload operation. This distinction is hidden for shared references by covariance, but cannot be ignored anymore if the context carries mutable references to GPU subdevices, as will happen for the Fsp and the Falcons.
Thus, give GspBootContext separate lifetimes for its subdevice borrows and the GPU resources captured by those subdevices, and update its users accordingly.
--- drivers/gpu/nova-core/gsp.rs | 19 ++++++++++++------- drivers/gpu/nova-core/gsp/boot.rs | 4 ++-- drivers/gpu/nova-core/gsp/hal.rs | 6 +++--- drivers/gpu/nova-core/gsp/hal/gh100.rs | 4 ++-- drivers/gpu/nova-core/gsp/hal/tu102.rs | 11 ++++++++--- drivers/gpu/nova-core/gsp/sequencer.rs | 2 +- 6 files changed, 28 insertions(+), 18 deletions(-)
drivers/gpu/nova-core/gsp.rs +12 -7
@@ -54,16 +54,21 @@pub (crate )const GSP_PAGE_SIZE :usize =1 <<GSP_PAGE_SHIFT ;/// Common context for the GSP boot process. -pub (crate )struct GspBootContext <'a> { -pub (crate ) pdev: &'a pci::Device <device::Bound >, -pub (crate ) bar:Bar0 <'a>, +/// +/// It carries two distinct lifetimes: +/// +/// - `'gpu` is the lifetime of the bound GPU device, as captured by the GPU subdevices. +/// - `'ctx` is a shorter lifetime during which this context borrows those subdevices. +pub (crate )struct GspBootContext <'ctx, 'gpu> { +pub (crate ) pdev: &'gpu pci::Device <device::Bound >, +pub (crate ) bar:Bar0 <'gpu>,pub (crate ) chipset:Chipset , -pub (crate ) gsp_falcon: &'aFalcon <'a,GspFalcon >, -pub (crate ) sec2_falcon: &'aFalcon <'a,Sec2Falcon >, +pub (crate ) gsp_falcon: &'ctxFalcon <'gpu,GspFalcon >, +pub (crate ) sec2_falcon: &'ctxFalcon <'gpu,Sec2Falcon >, } -impl <'a>GspBootContext <'a> { -pub (crate )fn dev (&self) -> &'a device::Device <device::Bound > { +impl <'ctx, 'gpu>GspBootContext <'ctx, 'gpu> { +pub (crate )fn dev (&self) -> &'gpu device::Device <device::Bound > { self.pdev.as_ref () } }
drivers/gpu/nova-core/gsp/boot.rs +2 -2
@@ -39,7 +39,7 @@ impl super::Gsp {/// [`Self::unload`]) returned. pub (crate )fn boot ( self:Pin <&mut Self >, -mut ctx:super ::GspBootContext <'_>, +mut ctx:super ::GspBootContext <'_, '_>, ) ->Result <Option <super ::UnloadBundle >> {let pdev = ctx.pdev;let bar = ctx.bar;
@@ -125,7 +125,7 @@ fn shutdown_gsp(/// This stops all activity on the GSP. pub (crate )fn unload ( &self, -mut ctx:super ::GspBootContext <'_>, +mut ctx:super ::GspBootContext <'_, '_>, unload_bundle:Option <super ::UnloadBundle >, ) ->Result {let dev = ctx.dev ();
drivers/gpu/nova-core/gsp/hal.rs +3 -3
@@ -31,7 +31,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: &mut GspBootContext <'_>) ->Result ; +fn run (&self, ctx: &mut GspBootContext <'_, '_>) ->Result ; }/// Trait implemented by GSP HALs.
@@ -43,7 +43,7 @@ pub(super) trait GspHal: Send {fn boot ( &self, gsp: &Gsp , - ctx: &mut GspBootContext <'_>, + ctx: &mut GspBootContext <'_, '_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >>;
@@ -55,7 +55,7 @@ fn boot(fn post_boot ( &self, _gsp: &Gsp , - _ctx: &mut GspBootContext <'_>, + _ctx: &mut GspBootContext <'_, '_>, _gsp_fw: &GspFirmware , ) ->Result {Ok (())
drivers/gpu/nova-core/gsp/hal/gh100.rs +2 -2
@@ -115,7 +115,7 @@ fn wait_for_gsp_lockdown_release(struct FspUnloadBundle ;impl UnloadBundle for FspUnloadBundle { -fn run (&self, ctx: &mut 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()),
@@ -138,7 +138,7 @@ impl GspHal for Gh100 {fn boot ( &self, gsp: &Gsp , - ctx: &mut GspBootContext <'_>, + ctx: &mut GspBootContext <'_, '_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >> {
drivers/gpu/nova-core/gsp/hal/tu102.rs +8 -3
@@ -78,7 +78,7 @@ struct Sec2UnloadBundle { }impl UnloadBundle for Sec2UnloadBundle { -fn run (&self, ctx: &mut GspBootContext <'_>) ->Result { +fn run (&self, ctx: &mut GspBootContext <'_, '_>) ->Result {let dev = ctx.dev ();let bar = ctx.bar;
@@ -258,7 +258,7 @@ impl GspHal for Tu102 {fn boot ( &self, gsp: &Gsp , - ctx: &mut GspBootContext <'_>, + ctx: &mut GspBootContext <'_, '_>, fb_layout: &FbLayout , wpr_meta: &Coherent <GspFwWprMeta >, ) ->Result <Option <crate ::gsp::UnloadBundle >> {
@@ -314,7 +314,12 @@ fn boot(Ok (unload_guard.dismiss ()) } -fn post_boot (&self, gsp: &Gsp , ctx: &mut GspBootContext <'_>, gsp_fw: &GspFirmware ) ->Result { +fn post_boot ( + &self, + gsp: &Gsp , + ctx: &mut GspBootContext <'_, '_>, + gsp_fw: &GspFirmware , + ) ->Result {GspSequencer ::run ( &gsp.cmdq, ctx,
drivers/gpu/nova-core/gsp/sequencer.rs +1 -1
@@ -335,7 +335,7 @@ fn next(&mut self) -> Option<Self::Item> {impl <'a>GspSequencer <'a> {pub (crate )fn run ( cmdq: &Cmdq , - ctx: &'aGspBootContext <'_>, + ctx: &'aGspBootContext <'_, '_>, libos_dma_handle:u64 , bootloader_app_version:u32 , ) ->Result {
Signature · 1 line
2.55.0
Alexandre Courbot
maintainerPATCH v6 13/13 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/fsp.rs | 21 +++++++++++++++++++-- drivers/gpu/nova-core/gpu.rs | 9 +++++++++ drivers/gpu/nova-core/gsp.rs | 2 ++ drivers/gpu/nova-core/gsp/hal/gh100.rs | 19 ++++++++----------- 4 files changed, 38 insertions(+), 13 deletions(-)
drivers/gpu/nova-core/fsp.rs +19 -2
@@ -231,20 +231,37 @@ pub(crate) struct Fsp<'a> { }impl <'a>Fsp <'a> { +/// Attempts to create a `Fsp` instance. +/// +/// This can involve waiting for FSP secure boot completion, but should be instantaneous in +/// practice. +/// +/// If `chipset` doesn't support FSP, `Ok(None)` is returned. +pub (crate )fn try_new ( + dev: &'a device::Device <device::Bound >, + bar:Bar0 <'a>, + chipset:Chipset , + ) ->Result <Option <Self >> { +match hal::fsp_hal (chipset) { +None =>Ok (None ), +Some (hal) =>Self ::wait_secure_boot (dev, bar, chipset, hal).map (Option ::Some ), + } + } +/// Waits for FSP secure boot completion, then returns the [`Fsp`] interface. /// /// Polls the thermal scratch register until FSP signals boot completion or the timeout /// elapses. Returning an [`Fsp`] only on success guarantees, at the API level, that the /// interface is not used before secure boot has completed. -pub (crate )fn wait_secure_boot ( +fn wait_secure_boot ( dev: &'a device::Device <device::Bound >, bar:Bar0 <'a>, chipset:Chipset , + hal: &'staticdyn hal::FspHal , ) ->Result <Fsp <'a>> {/// FSP secure boot completion timeout in milliseconds. const FSP_SECURE_BOOT_TIMEOUT_MS :i64 =5000 ; -let hal = hal::fsp_hal (chipset).ok_or (ENOTSUPP )?;let falcon =Falcon ::<FspEngine >::new (dev, chipset, bar)?;let fsp_fw =FspFirmware ::new (dev, chipset,FIRMWARE_VERSION )?;
drivers/gpu/nova-core/gpu.rs +9 -0
@@ -22,6 +22,7 @@ Falcon, // }, fb::SysmemFlush, + fsp::Fsp, gsp::{ self, commands::GetGspStaticInfoReply,
@@ -262,6 +263,10 @@ struct GspResources<'gpu> { gsp_falcon: Falcon<'gpu, GspFalcon>, /// SEC2 falcon instance, used for GSP boot up and cleanup. sec2_falcon: Falcon<'gpu, 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<'gpu>>,/// GSP runtime data. #[pin] gsp: Gsp,
@@ -305,6 +310,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, )
@@ -356,6 +362,8 @@ pub(crate) fn new( sec2_falcon: Falcon::new(dev, spec.chipset, bar)?, + fsp: Fsp::try_new(dev, bar, spec.chipset)?, + gsp <- Gsp::new(pdev),// This member must be initialized last, so the `UnloadBundle` can never be dropped
@@ -367,6 +375,7 @@ pub(crate) fn new( chipset: spec.chipset, gsp_falcon, sec2_falcon, + fsp: fsp.as_mut(), })?, }),
drivers/gpu/nova-core/gsp.rs +2 -0
@@ -39,6 +39,7 @@ sec2::Sec2 as Sec2Falcon, Falcon, // }, + fsp::Fsp, gpu::Chipset, gsp::{ cmdq::Cmdq,
@@ -65,6 +66,7 @@ pub(crate) struct GspBootContext<'ctx, 'gpu> { pub(crate) chipset: Chipset, pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>, pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>, + pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>, }impl <'ctx, 'gpu>GspBootContext <'ctx, 'gpu> {
drivers/gpu/nova-core/gsp/hal/gh100.rs +8 -11
@@ -17,10 +17,7 @@ Falcon, // }, fb::FbLayout, - fsp::{ - FmcBootArgs, - Fsp, // - }, + fsp::FmcBootArgs, gsp::{ hal::{GspHal ,
@@ -143,7 +140,6 @@ fn boot( wpr_meta: &Coherent<GspFwWprMeta>, ) -> Result<Option<crate::gsp::UnloadBundle>> {let dev = ctx.dev (); -let bar = ctx.bar;let chipset = ctx.chipset;let gsp_falcon = ctx.gsp_falcon;
@@ -151,8 +147,6 @@ fn boot(KBox ::new (FspUnloadBundle ,GFP_KERNEL )?as KBox <dyn UnloadBundle > ); -let mut fsp =Fsp ::wait_secure_boot (dev, bar, chipset)?; -let args =FmcBootArgs ::new ( dev, chipset,
@@ -164,9 +158,12 @@ fn boot(// Wait for the GSP RISC-V core to halt in case of error. We create this guard after `args` // to make sure that boot args are kept alive until halt, in case they are still being // accessed. -let unload_guard =ScopeGuard ::new_with_data (unload_bundle, |unload_bundle| { -let _ = unload_bundle.0 .run (ctx); - }); +let mut unload_guard = +ScopeGuard ::new_with_data ((unload_bundle, ctx), |(unload_bundle, ctx)| { +let _ = unload_bundle.0 .run (ctx); + }); + +let fsp = unload_guard.1 .fsp.as_mut ().ok_or (ENODEV )?; fsp.boot_fmc (dev, fb_layout, &args)?;
@@ -174,7 +171,7 @@ fn boot(// anymore. wait_for_gsp_lockdown_release (dev, gsp_falcon, args.boot_params_dma_handle ())?; -Ok (Some (unload_guard.dismiss ())) +Ok (Some (unload_guard.dismiss ().0 )) } }
Signature · 1 line
2.55.0
Quoted 19 lines · Alexandre Courbot
The recently introduced
GspBootContextis useful in at least two other places:- As a replacement for the sequencer's
GspSequencerParams,
- As the main parameter ofGsp::unload, for symmetry withGsp::boot.This symmetry can be further exploited to make
Gsp::bootandGsp::unloadgeneric 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 theFalcons that should require exclusive access to load and run firmware). Furthermore, it is only needed a single time inGsp::boot, and is inadequate for handling errors occurring in HALs.
Quoted 7 lines
Based on Eliot's feedback on v4, the series then removes the last remnants of the boot procedure leaking outside of the HALs, and consolidates everything within the GSP and FSP HALs.
This series applies on the latest
drm-rust-next.Signed-off-by Alexandre Courbot <[email protected]>
Pushed to drm-rust-next! Thanks all.