Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: nova-core: compute layout of the FRTS region

FWSEC-FRTS is run with the desired address of the FRTS region as
parameter, which we need to compute depending on some hardware
parameters.

Do this in a `FbLayout` structure, that will be later extended to
describe more memory regions used to boot the GSP.

Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/r/20250619-nova-frts-v6-20-ecf41ef99252@nvidia.com
[ In doc-comment of FbLayout s/bootup process/boot process/ - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Alexandre Courbot and committed by
Danilo Krummrich
80213934 47c4846e

+224 -2
+70
drivers/gpu/nova-core/fb.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 + use core::ops::Range; 4 + 3 5 use kernel::prelude::*; 6 + use kernel::sizes::*; 4 7 use kernel::types::ARef; 5 8 use kernel::{dev_warn, device}; 6 9 7 10 use crate::dma::DmaObject; 8 11 use crate::driver::Bar0; 9 12 use crate::gpu::Chipset; 13 + use crate::regs; 10 14 11 15 mod hal; 12 16 ··· 67 63 "attempt to unregister a sysmem flush page that is not active\n" 68 64 ); 69 65 } 66 + } 67 + } 68 + 69 + /// Layout of the GPU framebuffer memory. 70 + /// 71 + /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 72 + #[derive(Debug)] 73 + #[expect(dead_code)] 74 + pub(crate) struct FbLayout { 75 + pub(crate) fb: Range<u64>, 76 + pub(crate) vga_workspace: Range<u64>, 77 + pub(crate) frts: Range<u64>, 78 + } 79 + 80 + impl FbLayout { 81 + /// Computes the FB layout. 82 + pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 83 + let hal = hal::fb_hal(chipset); 84 + 85 + let fb = { 86 + let fb_size = hal.vidmem_size(bar); 87 + 88 + 0..fb_size 89 + }; 90 + 91 + let vga_workspace = { 92 + let vga_base = { 93 + const NV_PRAMIN_SIZE: u64 = SZ_1M as u64; 94 + let base = fb.end - NV_PRAMIN_SIZE; 95 + 96 + if hal.supports_display(bar) { 97 + match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 98 + Some(addr) => { 99 + if addr < base { 100 + const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64; 101 + 102 + // Point workspace address to end of framebuffer. 103 + fb.end - VBIOS_WORKSPACE_SIZE 104 + } else { 105 + addr 106 + } 107 + } 108 + None => base, 109 + } 110 + } else { 111 + base 112 + } 113 + }; 114 + 115 + vga_base..fb.end 116 + }; 117 + 118 + let frts = { 119 + const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64; 120 + const FRTS_SIZE: u64 = SZ_1M as u64; 121 + // TODO: replace with `align_down` once it lands. 122 + let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE; 123 + 124 + frts_base..frts_base + FRTS_SIZE 125 + }; 126 + 127 + Ok(Self { 128 + fb, 129 + vga_workspace, 130 + frts, 131 + }) 70 132 } 71 133 }
+10 -2
drivers/gpu/nova-core/fb/hal.rs
··· 6 6 use crate::gpu::Chipset; 7 7 8 8 mod ga100; 9 + mod ga102; 9 10 mod tu102; 10 11 11 12 pub(crate) trait FbHal { ··· 17 16 /// 18 17 /// This might fail if the address is too large for the receiving register. 19 18 fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result; 19 + 20 + /// Returns `true` is display is supported. 21 + fn supports_display(&self, bar: &Bar0) -> bool; 22 + 23 + /// Returns the VRAM size, in bytes. 24 + fn vidmem_size(&self, bar: &Bar0) -> u64; 20 25 } 21 26 22 27 /// Returns the HAL corresponding to `chipset`. ··· 31 24 32 25 match chipset { 33 26 TU102 | TU104 | TU106 | TU117 | TU116 => tu102::TU102_HAL, 34 - GA100 | GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => { 35 - ga100::GA100_HAL 27 + GA100 => ga100::GA100_HAL, 28 + GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => { 29 + ga102::GA102_HAL 36 30 } 37 31 } 38 32 }
+12
drivers/gpu/nova-core/fb/hal/ga100.rs
··· 25 25 .write(bar); 26 26 } 27 27 28 + pub(super) fn display_enabled_ga100(bar: &Bar0) -> bool { 29 + !regs::ga100::NV_FUSE_STATUS_OPT_DISPLAY::read(bar).display_disabled() 30 + } 31 + 28 32 /// Shift applied to the sysmem address before it is written into 29 33 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`, 30 34 const FLUSH_SYSMEM_ADDR_SHIFT_HI: u32 = 40; ··· 42 38 write_sysmem_flush_page_ga100(bar, addr); 43 39 44 40 Ok(()) 41 + } 42 + 43 + fn supports_display(&self, bar: &Bar0) -> bool { 44 + display_enabled_ga100(bar) 45 + } 46 + 47 + fn vidmem_size(&self, bar: &Bar0) -> u64 { 48 + super::tu102::vidmem_size_gp102(bar) 45 49 } 46 50 } 47 51
+36
drivers/gpu/nova-core/fb/hal/ga102.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::prelude::*; 4 + 5 + use crate::driver::Bar0; 6 + use crate::fb::hal::FbHal; 7 + use crate::regs; 8 + 9 + fn vidmem_size_ga102(bar: &Bar0) -> u64 { 10 + regs::NV_USABLE_FB_SIZE_IN_MB::read(bar).usable_fb_size() 11 + } 12 + 13 + struct Ga102; 14 + 15 + impl FbHal for Ga102 { 16 + fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 { 17 + super::ga100::read_sysmem_flush_page_ga100(bar) 18 + } 19 + 20 + fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result { 21 + super::ga100::write_sysmem_flush_page_ga100(bar, addr); 22 + 23 + Ok(()) 24 + } 25 + 26 + fn supports_display(&self, bar: &Bar0) -> bool { 27 + super::ga100::display_enabled_ga100(bar) 28 + } 29 + 30 + fn vidmem_size(&self, bar: &Bar0) -> u64 { 31 + vidmem_size_ga102(bar) 32 + } 33 + } 34 + 35 + const GA102: Ga102 = Ga102; 36 + pub(super) const GA102_HAL: &dyn FbHal = &GA102;
+16
drivers/gpu/nova-core/fb/hal/tu102.rs
··· 26 26 } 27 27 } 28 28 29 + pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool { 30 + !regs::gm107::NV_FUSE_STATUS_OPT_DISPLAY::read(bar).display_disabled() 31 + } 32 + 33 + pub(super) fn vidmem_size_gp102(bar: &Bar0) -> u64 { 34 + regs::NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE::read(bar).usable_fb_size() 35 + } 36 + 29 37 struct Tu102; 30 38 31 39 impl FbHal for Tu102 { ··· 43 35 44 36 fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result { 45 37 write_sysmem_flush_page_gm107(bar, addr) 38 + } 39 + 40 + fn supports_display(&self, bar: &Bar0) -> bool { 41 + display_enabled_gm107(bar) 42 + } 43 + 44 + fn vidmem_size(&self, bar: &Bar0) -> u64 { 45 + vidmem_size_gp102(bar) 46 46 } 47 47 } 48 48
+4
drivers/gpu/nova-core/gpu.rs
··· 4 4 5 5 use crate::driver::Bar0; 6 6 use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon}; 7 + use crate::fb::FbLayout; 7 8 use crate::fb::SysmemFlush; 8 9 use crate::firmware::{Firmware, FIRMWARE_VERSION}; 9 10 use crate::gfw; ··· 215 214 gsp_falcon.clear_swgen0_intr(bar); 216 215 217 216 let _sec2_falcon = Falcon::<Sec2>::new(pdev.as_ref(), spec.chipset, bar, true)?; 217 + 218 + let fb_layout = FbLayout::new(spec.chipset, bar)?; 219 + dev_dbg!(pdev.as_ref(), "{:#x?}\n", fb_layout); 218 220 219 221 // Will be used in a later patch when fwsec firmware is needed. 220 222 let _bios = Vbios::new(pdev, bar)?;
+76
drivers/gpu/nova-core/regs.rs
··· 52 52 23:0 adr_63_40 as u32; 53 53 }); 54 54 55 + register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 { 56 + 3:0 lower_scale as u8; 57 + 9:4 lower_mag as u8; 58 + 30:30 ecc_mode_enabled as bool; 59 + }); 60 + 61 + impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE { 62 + /// Returns the usable framebuffer size, in bytes. 63 + pub(crate) fn usable_fb_size(self) -> u64 { 64 + let size = ((self.lower_mag() as u64) << (self.lower_scale() as u64)) 65 + * kernel::sizes::SZ_1M as u64; 66 + 67 + if self.ecc_mode_enabled() { 68 + // Remove the amount of memory reserved for ECC (one per 16 units). 69 + size / 16 * 15 70 + } else { 71 + size 72 + } 73 + } 74 + } 75 + 55 76 /* PGC6 */ 56 77 57 78 register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128 { ··· 95 74 /// Returns `true` if GFW boot is completed. 96 75 pub(crate) fn completed(self) -> bool { 97 76 self.progress() == 0xff 77 + } 78 + } 79 + 80 + register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 { 81 + 31:0 value as u32; 82 + }); 83 + 84 + register!( 85 + NV_USABLE_FB_SIZE_IN_MB => NV_PGC6_AON_SECURE_SCRATCH_GROUP_42, 86 + "Scratch group 42 register used as framebuffer size" { 87 + 31:0 value as u32, "Usable framebuffer size, in megabytes"; 88 + } 89 + ); 90 + 91 + impl NV_USABLE_FB_SIZE_IN_MB { 92 + /// Returns the usable framebuffer size, in bytes. 93 + pub(crate) fn usable_fb_size(self) -> u64 { 94 + u64::from(self.value()) * kernel::sizes::SZ_1M as u64 95 + } 96 + } 97 + 98 + /* PDISP */ 99 + 100 + register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 { 101 + 3:3 status_valid as bool, "Set if the `addr` field is valid"; 102 + 31:8 addr as u32, "VGA workspace base address divided by 0x10000"; 103 + }); 104 + 105 + impl NV_PDISP_VGA_WORKSPACE_BASE { 106 + /// Returns the base address of the VGA workspace, or `None` if none exists. 107 + pub(crate) fn vga_workspace_addr(self) -> Option<u64> { 108 + if self.status_valid() { 109 + Some((self.addr() as u64) << 16) 110 + } else { 111 + None 112 + } 98 113 } 99 114 } 100 115 ··· 275 218 4:4 core_select as bool => PeregrineCoreSelect; 276 219 8:8 br_fetch as bool; 277 220 }); 221 + 222 + // The modules below provide registers that are not identical on all supported chips. They should 223 + // only be used in HAL modules. 224 + 225 + pub(crate) mod gm107 { 226 + /* FUSE */ 227 + 228 + register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 { 229 + 0:0 display_disabled as bool; 230 + }); 231 + } 232 + 233 + pub(crate) mod ga100 { 234 + /* FUSE */ 235 + 236 + register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 { 237 + 0:0 display_disabled as bool; 238 + }); 239 + }