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