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