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