[PATCH v4 4/5] gpu: nova-core: split FbLayout into FSP and non-FSP versions
Eliot Courtney <[email protected]> Tue, 04 Aug 2026 14:41:15 +0900
| Newsgroups | org.kernel.vger.rust-for-linux,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
`FbLayout` is currently used for both pre and post FSP architectures. It contains ranges for each region of framebuffer, but on post FSP architectures, only the size is actually used. The region locations are decided by ACR, which runs as part of the GSP-FMC, not by the driver. The driver only provides the sizes. So, for post FSP architectures `FbLayout` contains essentially guesses for the offsets. Instead, make separate types so that we only store the information that's actually needed, rather than keeping around offsets that may not be correct. Signed-off-by: Eliot Courtney <[email protected]> --- drivers/gpu/nova-core/fb.rs | 65 ++++++++++++++++++++------ drivers/gpu/nova-core/fsp.rs | 21 ++++----- drivers/gpu/nova-core/gsp/boot.rs | 28 ++++------- drivers/gpu/nova-core/gsp/fw.rs | 85 ++++++++++++++++++++++++++-------- drivers/gpu/nova-core/gsp/hal.rs | 12 ++--- drivers/gpu/nova-core/gsp/hal/gh100.rs | 17 ++++--- drivers/gpu/nova-core/gsp/hal/tu102.rs | 32 ++++++++----- 7 files changed, 170 insertions(+), 90 deletions(-) diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 86d2bdaab7f9..77447a6567a4 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -149,7 +149,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. #[derive(Debug)] -pub(crate) struct FbLayout { +pub(crate) struct FbRanges { /// Range of the framebuffer. Starts at `0`. pub(crate) fb: FbRange, /// VGA workspace, small area of reserved memory at the end of the framebuffer. @@ -164,14 +164,16 @@ pub(crate) struct FbLayout { pub(crate) wpr2_heap: FbRange, /// WPR2 region range, starting with an instance of `GspFwWprMeta`. pub(crate) wpr2: FbRange, + /// Non-WPR heap, located just below WPR2. pub(crate) non_wpr_heap: FbRange, + /// Number of VF partitions. pub(crate) vf_partition_count: u8, /// PMU reserved memory size, in bytes. pub(crate) pmu_reserved_size: u32, } -impl FbLayout { - /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware. +impl FbRanges { + /// Computes concrete framebuffer ranges required on non-FSP booting architectures. pub(crate) fn new( chipset: Chipset, bar: Bar0<'_>, @@ -240,16 +242,7 @@ pub(crate) fn new( FbRange(elf_addr..elf_addr + elf_size) }; - let (vf_partition_count, wpr2_heap_size) = match vgpu_state { - VgpuState::Disabled => ( - 0, - gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end)?, - ), - VgpuState::Enabled { total_vfs } => ( - u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?, - gsp::LibosParams::vgpu_wpr_heap_size(), - ), - }; + let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?; let wpr2_heap = { const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); @@ -303,3 +296,49 @@ pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> { Some(wpr2_lo.lower_bound()..wpr2_hi.higher_bound()) } + +/// Computes the number of VF partitions and the WPR2 heap size from the vGPU state. +fn wpr2_heap_params(chipset: Chipset, vgpu_state: VgpuState, fb_size: u64) -> Result<(u8, u64)> { + Ok(match vgpu_state { + VgpuState::Disabled => ( + 0, + gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb_size)?, + ), + VgpuState::Enabled { total_vfs } => ( + u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?, + gsp::LibosParams::vgpu_wpr_heap_size(), + ), + }) +} + +/// Framebuffer region sizes needed for GSP-FMC boot. +#[derive(Debug)] +pub(crate) struct FbSizes { + /// FRTS size, in bytes. + pub(crate) frts_size: u64, + /// WPR2 heap size, in bytes. + pub(crate) wpr2_heap_size: u64, + /// Non-WPR heap size, in bytes. + pub(crate) non_wpr_heap_size: u64, + /// PMU reserved memory size, in bytes. + pub(crate) pmu_reserved_size: u32, + /// Number of VF partitions. + pub(crate) vf_partition_count: u8, +} + +impl FbSizes { + /// Computes the framebuffer region sizes for GSP-FMC boot. + pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, vgpu_state: VgpuState) -> Result<Self> { + let hal = hal::fb_hal(chipset); + let fb_size = hal.vidmem_size(bar); + let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb_size)?; + + Ok(Self { + frts_size: hal.frts_size(), + wpr2_heap_size, + non_wpr_heap_size: hal.non_wpr_heap_size(), + pmu_reserved_size: hal.pmu_reserved_size(), + vf_partition_count, + }) + } +} diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index a81b51028282..9add62736892 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -31,7 +31,7 @@ fsp::Fsp as FspEngine, Falcon, // }, - fb::FbLayout, + fb::FbSizes, firmware::{ fsp::{ FmcSignatures, @@ -253,11 +253,11 @@ struct FspCotMessage { impl FspCotMessage { /// Computes the FRTS vidmem offset for the Chain-of-Trust message. It is measured backwards /// from the end of the framebuffer. - fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_layout: &FbLayout) -> Result<u64> { + fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_info: &FbSizes) -> Result<u64> { let mut offset = u64::from(hal.fb_end_reserved_size()); - if fb_layout.pmu_reserved_size != 0 { - offset = (offset + u64::from(fb_layout.pmu_reserved_size)) + if fb_info.pmu_reserved_size != 0 { + offset = (offset + u64::from(fb_info.pmu_reserved_size)) // The 2 MiB alignment is r570-specific. .align_up(Alignment::new::<SZ_2M>()) .ok_or(EINVAL)?; @@ -268,20 +268,20 @@ fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_layout: &FbLayout) -> Result<u64 /// Returns an in-place initializer for [`FspCotMessage`]. fn new<'a>( - fb_layout: &FbLayout, + fb_info: &FbSizes, fsp_fw: &'a FspFirmware, args: &'a FmcBootArgs<'_>, ) -> Result<impl Init<Self> + 'a> { let hal = hal::fsp_hal(args.chipset).ok_or(ENOTSUPP)?; let frts_vidmem_offset = if !args.resume { - Self::frts_vidmem_offset(hal, fb_layout)? + Self::frts_vidmem_offset(hal, fb_info)? } else { 0 }; let frts_size: u32 = if !args.resume { - fb_layout.frts.len().try_into()? + fb_info.frts_size.try_into()? } else { 0 }; @@ -537,15 +537,12 @@ pub(crate) fn read_vgpu_mode( pub(crate) fn boot_fmc( &mut self, dev: &device::Device<device::Bound>, - fb_layout: &FbLayout, + fb_info: &FbSizes, args: &FmcBootArgs<'_>, ) -> Result { dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset); - let msg = KBox::init( - FspCotMessage::new(fb_layout, &self.fsp_fw, args)?, - GFP_KERNEL, - )?; + let msg = KBox::init(FspCotMessage::new(fb_info, &self.fsp_fw, args)?, GFP_KERNEL)?; let _response_buf = self.send_sync_fsp(dev, &*msg)?; diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 727b8ae4bcb7..97f6e7ef4ead 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -3,7 +3,6 @@ use kernel::{ bits, - dma::Coherent, io::poll::read_poll_timeout, prelude::*, time::Delta, @@ -16,15 +15,13 @@ gsp::Gsp, Falcon, // }, - fb::FbLayout, firmware::{ gsp::GspFirmware, FIRMWARE_VERSION, // }, gsp::{ cmdq::Cmdq, - commands, - GspFwWprMeta, // + commands, // }, }; @@ -50,23 +47,16 @@ pub(crate) fn boot( let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?; - let fb_layout = FbLayout::new(chipset, bar, &gsp_fw, ctx.vgpu.state())?; - dev_dbg!(dev, "{:#x?}\n", fb_layout); - - 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, &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" - ); + let unload_bundle = hal.boot(&self, &mut ctx, &gsp_fw)?.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 mut unload_guard = ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| { diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index a7637bf3f3df..998293656794 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -36,7 +36,10 @@ }; use crate::{ - fb::FbLayout, + fb::{ + FbRanges, + FbSizes, // + }, firmware::gsp::GspFirmware, gpu::{ Architecture, @@ -174,10 +177,10 @@ unsafe impl FromBytes for GspFwWprMeta {} impl GspFwWprMeta { /// Returns an initializer for a `GspFwWprMeta` suitable for booting `gsp_firmware` using the - /// `fb_layout` layout. - pub(crate) fn new<'a>( + /// framebuffer ranges `ranges`. + pub(crate) fn from_ranges<'a>( gsp_firmware: &'a GspFirmware, - fb_layout: &'a FbLayout, + ranges: &'a FbRanges, ) -> impl Init<Self> + 'a { let init_inner = init!(bindings::GspFwWprMeta { // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. @@ -196,25 +199,67 @@ pub(crate) fn new<'a>( sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()), }, }, - gspFwRsvdStart: fb_layout.non_wpr_heap.start, - nonWprHeapOffset: fb_layout.non_wpr_heap.start, - nonWprHeapSize: fb_layout.non_wpr_heap.end - fb_layout.non_wpr_heap.start, - gspFwWprStart: fb_layout.wpr2.start, - gspFwHeapOffset: fb_layout.wpr2_heap.start, - gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start, - gspFwOffset: fb_layout.elf.start, - bootBinOffset: fb_layout.boot.start, - frtsOffset: fb_layout.frts.start, - frtsSize: fb_layout.frts.end - fb_layout.frts.start, - gspFwWprEnd: fb_layout + gspFwRsvdStart: ranges.non_wpr_heap.start, + nonWprHeapOffset: ranges.non_wpr_heap.start, + nonWprHeapSize: ranges.non_wpr_heap.len(), + gspFwWprStart: ranges.wpr2.start, + gspFwHeapOffset: ranges.wpr2_heap.start, + gspFwHeapSize: ranges.wpr2_heap.len(), + gspFwOffset: ranges.elf.start, + bootBinOffset: ranges.boot.start, + frtsOffset: ranges.frts.start, + frtsSize: ranges.frts.len(), + gspFwWprEnd: ranges .vga_workspace .start .align_down(Alignment::new::<SZ_128K>()), - gspFwHeapVfPartitionCount: fb_layout.vf_partition_count, - fbSize: fb_layout.fb.end - fb_layout.fb.start, - vgaWorkspaceOffset: fb_layout.vga_workspace.start, - vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start, - pmuReservedSize: fb_layout.pmu_reserved_size, + gspFwHeapVfPartitionCount: ranges.vf_partition_count, + fbSize: ranges.fb.len(), + vgaWorkspaceOffset: ranges.vga_workspace.start, + vgaWorkspaceSize: ranges.vga_workspace.len(), + pmuReservedSize: ranges.pmu_reserved_size, + ..Zeroable::init_zeroed() + }); + + init!(GspFwWprMeta { + inner <- init_inner, + }) + } + + /// Returns an initializer for a `GspFwWprMeta` suitable for booting `gsp_firmware` using the + /// framebuffer region sizes `sizes`. + /// + /// The region offsets are left at zero: the ACR ucode computes them when it sets up WPR2. + pub(crate) fn from_sizes<'a>( + gsp_firmware: &'a GspFirmware, + sizes: &'a FbSizes, + ) -> impl Init<Self> + 'a { + /// VGA workspace size to reserve at the end of the framebuffer, in bytes. + const VGA_WORKSPACE_SIZE: u64 = u64::SZ_128K; + + let init_inner = init!(bindings::GspFwWprMeta { + // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. + magic: bindings::GSP_FW_WPR_META_MAGIC as u64, + revision: u64::from(bindings::GSP_FW_WPR_META_REVISION), + sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(), + sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size), + sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(), + sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()), + bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset), + bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset), + bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset), + __bindgen_anon_1: GspFwWprMetaBootResumeInfo { + __bindgen_anon_1: GspFwWprMetaBootInfo { + sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(), + sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()), + }, + }, + nonWprHeapSize: sizes.non_wpr_heap_size, + gspFwHeapSize: sizes.wpr2_heap_size, + frtsSize: sizes.frts_size, + gspFwHeapVfPartitionCount: sizes.vf_partition_count, + vgaWorkspaceSize: VGA_WORKSPACE_SIZE, + pmuReservedSize: sizes.pmu_reserved_size, ..Zeroable::init_zeroed() }); diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs index 34b4bb82a999..11e436651a69 100644 --- a/drivers/gpu/nova-core/gsp/hal.rs +++ b/drivers/gpu/nova-core/gsp/hal.rs @@ -5,13 +5,9 @@ mod gh100; mod tu102; -use kernel::{ - dma::Coherent, - prelude::*, // -}; +use kernel::prelude::*; use crate::{ - fb::FbLayout, firmware::gsp::GspFirmware, gpu::{ Architecture, @@ -19,8 +15,7 @@ }, gsp::{ Gsp, - GspBootContext, - GspFwWprMeta, // + GspBootContext, // }, }; @@ -44,8 +39,7 @@ fn boot( &self, gsp: &Gsp, ctx: &mut GspBootContext<'_, '_>, - fb_layout: &FbLayout, - wpr_meta: &Coherent<GspFwWprMeta>, + gsp_fw: &GspFirmware, ) -> Result<Option<crate::gsp::UnloadBundle>>; /// Performs HAL-specific post-GSP boot tasks. diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs index 22b60f9233de..be10d278f567 100644 --- a/drivers/gpu/nova-core/gsp/hal/gh100.rs +++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs @@ -16,7 +16,8 @@ gsp::Gsp as GspEngine, Falcon, // }, - fb::FbLayout, + fb::FbSizes, + firmware::gsp::GspFirmware, fsp::FmcBootArgs, gsp::{ hal::{ @@ -144,19 +145,23 @@ fn boot( &self, gsp: &Gsp, ctx: &mut GspBootContext<'_, '_>, - fb_layout: &FbLayout, - wpr_meta: &Coherent<GspFwWprMeta>, + gsp_fw: &GspFirmware, ) -> Result<Option<crate::gsp::UnloadBundle>> { let dev = ctx.dev(); let chipset = ctx.chipset; let gsp_falcon = ctx.gsp_falcon; + let fb_sizes = FbSizes::new(chipset, ctx.bar, ctx.vgpu.state())?; + dev_dbg!(dev, "{:#x?}\n", fb_sizes); + + let wpr_meta = + Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::from_sizes(gsp_fw, &fb_sizes))?; + let args = FmcBootArgs::new(dev, chipset, &wpr_meta, &gsp.libos, false)?; + let unload_bundle = crate::gsp::UnloadBundle( KBox::new(FspUnloadBundle, GFP_KERNEL)? as KBox<dyn UnloadBundle> ); - let args = FmcBootArgs::new(dev, chipset, wpr_meta, &gsp.libos, 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. @@ -167,7 +172,7 @@ fn boot( let fsp = unload_guard.1.fsp.as_mut().ok_or(ENODEV)?; - fsp.boot_fmc(dev, fb_layout, &args)?; + fsp.boot_fmc(dev, &fb_sizes, &args)?; // Wait for GSP-FMC to release the GSP lockdown, indicating that `args` is not accessed // anymore. diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs index 03133f723faf..e3c365cf4a68 100644 --- a/drivers/gpu/nova-core/gsp/hal/tu102.rs +++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs @@ -19,7 +19,7 @@ }, fb::{ wpr2_range, - FbLayout, // + FbRanges, // }, firmware::{ booter::{ @@ -143,7 +143,7 @@ fn run_fwsec_frts( falcon: &Falcon<'_, GspEngine>, bar: Bar0<'_>, bios: &Vbios, - fb_layout: &FbLayout, + fb_ranges: &FbRanges, ) -> Result { // Check that the WPR2 region does not already exist - if it does, we cannot run // FWSEC-FRTS until the GPU is reset. @@ -161,8 +161,8 @@ fn run_fwsec_frts( falcon, bios, FwsecCommand::Frts { - frts_addr: fb_layout.frts.start, - frts_size: fb_layout.frts.len(), + frts_addr: fb_ranges.frts.start, + frts_size: fb_ranges.frts.len(), }, )?; @@ -196,12 +196,12 @@ fn run_fwsec_frts( return Err(EIO); }; - if wpr2_range.start != fb_layout.frts.start { + if wpr2_range.start != fb_ranges.frts.start { dev_err!( dev, "WPR2 region created at unexpected address {:#x}; expected {:#x}\n", wpr2_range.start, - fb_layout.frts.start, + fb_ranges.frts.start, ); return Err(EIO); @@ -253,8 +253,7 @@ fn boot( &self, gsp: &Gsp, ctx: &mut GspBootContext<'_, '_>, - fb_layout: &FbLayout, - wpr_meta: &Coherent<GspFwWprMeta>, + gsp_fw: &GspFirmware, ) -> Result<Option<crate::gsp::UnloadBundle>> { let dev = ctx.dev(); let bar = ctx.bar; @@ -262,6 +261,17 @@ fn boot( let gsp_falcon = ctx.gsp_falcon; let sec2_falcon = ctx.sec2_falcon; + let fb_ranges = FbRanges::new(chipset, bar, gsp_fw, ctx.vgpu.state())?; + dev_dbg!(dev, "{:#x?}\n", fb_ranges); + + // Declared before the unload guard so that if Booter fails while running, SEC2 is reset + // by the guard before this allocation is freed. + let wpr_meta = Coherent::init( + dev, + GFP_KERNEL, + GspFwWprMeta::from_ranges(gsp_fw, &fb_ranges), + )?; + let bios = Vbios::new(dev, bar)?; // Try and prepare the unload bundle. @@ -281,8 +291,8 @@ 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() { - self.run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?; + if !fb_ranges.frts.is_empty() { + self.run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_ranges)?; } gsp_falcon.reset()?; @@ -303,7 +313,7 @@ fn boot( FIRMWARE_VERSION, sec2_falcon, )? - .run(dev, sec2_falcon, wpr_meta)?; + .run(dev, sec2_falcon, &wpr_meta)?; Ok(unload_guard.dismiss()) } -- 2.55.0