1 // SPDX-License-Identifier: GPL-2.0 2 3 use core::ops::{ 4 Deref, 5 Range, // 6 }; 7 8 use kernel::{ 9 device, 10 dma::CoherentHandle, 11 fmt, 12 io::Io, 13 prelude::*, 14 ptr::{ 15 Alignable, 16 Alignment, // 17 }, 18 sizes::*, // 19 }; 20 21 use crate::{ 22 driver::Bar0, 23 firmware::gsp::GspFirmware, 24 gpu::Chipset, 25 gsp, 26 num::FromSafeCast, 27 vgpu::VgpuState, // 28 }; 29 30 mod hal; 31 mod regs; 32 33 /// Type holding the sysmem flush memory page, a page of memory to be written into the 34 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 35 /// 36 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware 37 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through 38 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a 39 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure 40 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a 41 /// sysmembar operation to flush its writes. 42 /// 43 /// Because of this, the sysmem flush memory page must be registered as early as possible during 44 /// driver initialization, and before any falcon is reset. 45 /// 46 pub(crate) struct SysmemFlush<'sys> { 47 /// Chipset we are operating on. 48 chipset: Chipset, 49 device: &'sys device::Device, 50 bar: Bar0<'sys>, 51 /// Keep the page alive as long as we need it. 52 page: CoherentHandle, 53 } 54 55 impl<'sys> SysmemFlush<'sys> { 56 /// Allocate a memory page and register it as the sysmem flush page. 57 pub(crate) fn register( 58 dev: &'sys device::Device<device::Bound>, 59 bar: Bar0<'sys>, 60 chipset: Chipset, 61 ) -> Result<Self> { 62 let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?; 63 64 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_address())?; 65 66 Ok(Self { 67 chipset, 68 device: dev, 69 bar, 70 page, 71 }) 72 } 73 } 74 75 impl Drop for SysmemFlush<'_> { 76 fn drop(&mut self) { 77 let hal = hal::fb_hal(self.chipset); 78 79 if hal.read_sysmem_flush_page(self.bar) == self.page.dma_address() { 80 let _ = hal.write_sysmem_flush_page(self.bar, 0).inspect_err(|e| { 81 dev_warn!( 82 &self.device, 83 "failed to unregister sysmem flush page: {:?}\n", 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 pub(crate) struct FbRange(Range<u64>); 98 99 impl FbRange { 100 pub(crate) fn len(&self) -> u64 { 101 self.0.end - self.0.start 102 } 103 } 104 105 impl From<Range<u64>> for FbRange { 106 fn from(range: Range<u64>) -> Self { 107 Self(range) 108 } 109 } 110 111 impl Deref for FbRange { 112 type Target = Range<u64>; 113 114 fn deref(&self) -> &Self::Target { 115 &self.0 116 } 117 } 118 119 impl fmt::Debug for FbRange { 120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 121 // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range. 122 if f.alternate() { 123 let size = self.len(); 124 125 if size < u64::SZ_1M { 126 let size_kib = size / u64::SZ_1K; 127 f.write_fmt(fmt!( 128 "{:#x}..{:#x} ({} KiB)", 129 self.0.start, 130 self.0.end, 131 size_kib 132 )) 133 } else { 134 let size_mib = size / u64::SZ_1M; 135 f.write_fmt(fmt!( 136 "{:#x}..{:#x} ({} MiB)", 137 self.0.start, 138 self.0.end, 139 size_mib 140 )) 141 } 142 } else { 143 f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end)) 144 } 145 } 146 } 147 148 /// Layout of the GPU framebuffer memory. 149 /// 150 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 151 #[derive(Debug)] 152 pub(crate) struct FbRanges { 153 /// Range of the framebuffer. Starts at `0`. 154 pub(crate) fb: FbRange, 155 /// VGA workspace, small area of reserved memory at the end of the framebuffer. 156 pub(crate) vga_workspace: FbRange, 157 /// FRTS range. 158 pub(crate) frts: FbRange, 159 /// Memory area containing the GSP bootloader image. 160 pub(crate) boot: FbRange, 161 /// Memory area containing the GSP firmware image. 162 pub(crate) elf: FbRange, 163 /// WPR2 heap. 164 pub(crate) wpr2_heap: FbRange, 165 /// WPR2 region range, starting with an instance of `GspFwWprMeta`. 166 pub(crate) wpr2: FbRange, 167 /// Non-WPR heap, located just below WPR2. 168 pub(crate) non_wpr_heap: FbRange, 169 /// Number of VF partitions. 170 pub(crate) vf_partition_count: u8, 171 /// PMU reserved memory size, in bytes. 172 pub(crate) pmu_reserved_size: u32, 173 } 174 175 impl FbRanges { 176 /// Computes concrete framebuffer ranges required on non-FSP booting architectures. 177 pub(crate) fn new( 178 chipset: Chipset, 179 bar: Bar0<'_>, 180 gsp_fw: &GspFirmware, 181 vgpu_state: VgpuState, 182 ) -> Result<Self> { 183 let hal = hal::fb_hal(chipset); 184 185 let fb = { 186 let fb_size = hal.vidmem_size(bar); 187 188 FbRange(0..fb_size) 189 }; 190 191 let vga_workspace = { 192 let vga_base = { 193 const NV_PRAMIN_SIZE: u64 = u64::SZ_1M; 194 let base = fb.end - NV_PRAMIN_SIZE; 195 196 if hal.supports_display(bar) { 197 match bar 198 .read(regs::NV_PDISP_VGA_WORKSPACE_BASE) 199 .vga_workspace_addr() 200 { 201 Some(addr) => { 202 if addr < base { 203 const VBIOS_WORKSPACE_SIZE: u64 = u64::SZ_128K; 204 205 // Point workspace address to end of framebuffer. 206 fb.end - VBIOS_WORKSPACE_SIZE 207 } else { 208 addr 209 } 210 } 211 None => base, 212 } 213 } else { 214 base 215 } 216 }; 217 218 FbRange(vga_base..fb.end) 219 }; 220 221 let frts = { 222 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 223 let frts_size: u64 = hal.frts_size(); 224 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size; 225 226 FbRange(frts_base..frts_base + frts_size) 227 }; 228 229 let boot = { 230 const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>(); 231 let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); 232 let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN); 233 234 FbRange(bootloader_base..bootloader_base + bootloader_size) 235 }; 236 237 let elf = { 238 const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>(); 239 let elf_size = u64::from_safe_cast(gsp_fw.size); 240 let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN); 241 242 FbRange(elf_addr..elf_addr + elf_size) 243 }; 244 245 let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?; 246 247 let wpr2_heap = { 248 const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 249 let wpr2_heap_addr = elf 250 .start 251 .checked_sub(wpr2_heap_size) 252 .ok_or(EOVERFLOW)? 253 .align_down(WPR2_HEAP_DOWN_ALIGN); 254 255 FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)) 256 }; 257 258 let wpr2 = { 259 const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 260 let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>())) 261 .align_down(WPR2_DOWN_ALIGN); 262 263 FbRange(wpr2_addr..frts.end) 264 }; 265 266 let non_wpr_heap = { 267 let non_wpr_heap_size = hal.non_wpr_heap_size(); 268 FbRange(wpr2.start - non_wpr_heap_size..wpr2.start) 269 }; 270 271 Ok(Self { 272 fb, 273 vga_workspace, 274 frts, 275 boot, 276 elf, 277 wpr2_heap, 278 wpr2, 279 non_wpr_heap, 280 vf_partition_count, 281 pmu_reserved_size: hal.pmu_reserved_size(), 282 }) 283 } 284 } 285 286 /// Reads the WPR2 memory region registers and returns the range if set. 287 /// Returns `None` if the WPR2 region is not set. 288 pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> { 289 let wpr2_hi = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI); 290 291 if !wpr2_hi.is_wpr2_set() { 292 return None; 293 } 294 295 let wpr2_lo = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO); 296 297 Some(wpr2_lo.lower_bound()..wpr2_hi.higher_bound()) 298 } 299 300 /// Computes the number of VF partitions and the WPR2 heap size from the vGPU state. 301 fn wpr2_heap_params(chipset: Chipset, vgpu_state: VgpuState, fb_size: u64) -> Result<(u8, u64)> { 302 Ok(match vgpu_state { 303 VgpuState::Disabled => ( 304 0, 305 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb_size)?, 306 ), 307 VgpuState::Enabled { total_vfs } => ( 308 u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?, 309 gsp::LibosParams::vgpu_wpr_heap_size(), 310 ), 311 }) 312 } 313 314 /// Framebuffer region sizes needed for GSP-FMC boot. 315 #[derive(Debug)] 316 pub(crate) struct FbSizes { 317 /// FRTS size, in bytes. 318 pub(crate) frts_size: u64, 319 /// WPR2 heap size, in bytes. 320 pub(crate) wpr2_heap_size: u64, 321 /// Non-WPR heap size, in bytes. 322 pub(crate) non_wpr_heap_size: u64, 323 /// PMU reserved memory size, in bytes. 324 pub(crate) pmu_reserved_size: u32, 325 /// Number of VF partitions. 326 pub(crate) vf_partition_count: u8, 327 } 328 329 impl FbSizes { 330 /// Computes the framebuffer region sizes for GSP-FMC boot. 331 pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, vgpu_state: VgpuState) -> Result<Self> { 332 let hal = hal::fb_hal(chipset); 333 let fb_size = hal.vidmem_size(bar); 334 let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb_size)?; 335 336 Ok(Self { 337 frts_size: hal.frts_size(), 338 wpr2_heap_size, 339 non_wpr_heap_size: hal.non_wpr_heap_size(), 340 pmu_reserved_size: hal.pmu_reserved_size(), 341 vf_partition_count, 342 }) 343 } 344 } 345