1 // SPDX-License-Identifier: GPL-2.0 2 3 use core::ops::Range; 4 5 use kernel::{ 6 device, 7 prelude::*, 8 ptr::{ 9 Alignable, 10 Alignment, // 11 }, 12 sizes::*, 13 sync::aref::ARef, // 14 }; 15 16 use crate::{ 17 dma::DmaObject, 18 driver::Bar0, 19 firmware::gsp::GspFirmware, 20 gpu::Chipset, 21 gsp, 22 num::{ 23 usize_as_u64, 24 FromSafeCast, // 25 }, 26 regs, 27 }; 28 29 mod hal; 30 31 /// Type holding the sysmem flush memory page, a page of memory to be written into the 32 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 33 /// 34 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware 35 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through 36 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a 37 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure 38 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a 39 /// sysmembar operation to flush its writes. 40 /// 41 /// Because of this, the sysmem flush memory page must be registered as early as possible during 42 /// driver initialization, and before any falcon is reset. 43 /// 44 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object, 45 /// otherwise the GPU might still use it even after it has been freed. 46 pub(crate) struct SysmemFlush { 47 /// Chipset we are operating on. 48 chipset: Chipset, 49 device: ARef<device::Device>, 50 /// Keep the page alive as long as we need it. 51 page: DmaObject, 52 } 53 54 impl SysmemFlush { 55 /// Allocate a memory page and register it as the sysmem flush page. 56 pub(crate) fn register( 57 dev: &device::Device<device::Bound>, 58 bar: &Bar0, 59 chipset: Chipset, 60 ) -> Result<Self> { 61 let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; 62 63 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 64 65 Ok(Self { 66 chipset, 67 device: dev.into(), 68 page, 69 }) 70 } 71 72 /// Unregister the managed sysmem flush page. 73 /// 74 /// In order to gracefully tear down the GPU, users must make sure to call this method before 75 /// dropping the object. 76 pub(crate) fn unregister(&self, bar: &Bar0) { 77 let hal = hal::fb_hal(self.chipset); 78 79 if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() { 80 let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { 81 dev_warn!( 82 &self.device, 83 "failed to unregister sysmem flush page: {:?}", 84 e 85 ) 86 }); 87 } else { 88 // Another page has been registered after us for some reason - warn as this is a bug. 89 dev_warn!( 90 &self.device, 91 "attempt to unregister a sysmem flush page that is not active\n" 92 ); 93 } 94 } 95 } 96 97 /// Layout of the GPU framebuffer memory. 98 /// 99 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 100 #[derive(Debug)] 101 #[expect(dead_code)] 102 pub(crate) struct FbLayout { 103 /// Range of the framebuffer. Starts at `0`. 104 pub(crate) fb: Range<u64>, 105 /// VGA workspace, small area of reserved memory at the end of the framebuffer. 106 pub(crate) vga_workspace: Range<u64>, 107 /// FRTS range. 108 pub(crate) frts: Range<u64>, 109 /// Memory area containing the GSP bootloader image. 110 pub(crate) boot: Range<u64>, 111 /// Memory area containing the GSP firmware image. 112 pub(crate) elf: Range<u64>, 113 /// WPR2 heap. 114 pub(crate) wpr2_heap: Range<u64>, 115 /// WPR2 region range, starting with an instance of `GspFwWprMeta`. 116 pub(crate) wpr2: Range<u64>, 117 pub(crate) heap: Range<u64>, 118 pub(crate) vf_partition_count: u8, 119 } 120 121 impl FbLayout { 122 /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware. 123 pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> { 124 let hal = hal::fb_hal(chipset); 125 126 let fb = { 127 let fb_size = hal.vidmem_size(bar); 128 129 0..fb_size 130 }; 131 132 let vga_workspace = { 133 let vga_base = { 134 const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M); 135 let base = fb.end - NV_PRAMIN_SIZE; 136 137 if hal.supports_display(bar) { 138 match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 139 Some(addr) => { 140 if addr < base { 141 const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K); 142 143 // Point workspace address to end of framebuffer. 144 fb.end - VBIOS_WORKSPACE_SIZE 145 } else { 146 addr 147 } 148 } 149 None => base, 150 } 151 } else { 152 base 153 } 154 }; 155 156 vga_base..fb.end 157 }; 158 159 let frts = { 160 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 161 const FRTS_SIZE: u64 = usize_as_u64(SZ_1M); 162 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE; 163 164 frts_base..frts_base + FRTS_SIZE 165 }; 166 167 let boot = { 168 const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>(); 169 let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); 170 let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN); 171 172 bootloader_base..bootloader_base + bootloader_size 173 }; 174 175 let elf = { 176 const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>(); 177 let elf_size = u64::from_safe_cast(gsp_fw.size); 178 let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN); 179 180 elf_addr..elf_addr + elf_size 181 }; 182 183 let wpr2_heap = { 184 const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 185 let wpr2_heap_size = 186 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end); 187 let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN); 188 189 wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN) 190 }; 191 192 let wpr2 = { 193 const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 194 let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>())) 195 .align_down(WPR2_DOWN_ALIGN); 196 197 wpr2_addr..frts.end 198 }; 199 200 let heap = { 201 const HEAP_SIZE: u64 = usize_as_u64(SZ_1M); 202 203 wpr2.start - HEAP_SIZE..wpr2.start 204 }; 205 206 Ok(Self { 207 fb, 208 vga_workspace, 209 frts, 210 boot, 211 elf, 212 wpr2_heap, 213 wpr2, 214 heap, 215 vf_partition_count: 0, 216 }) 217 } 218 } 219