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 regs, // 28 }; 29 30 mod hal; 31 32 /// Type holding the sysmem flush memory page, a page of memory to be written into the 33 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 34 /// 35 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware 36 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through 37 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a 38 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure 39 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a 40 /// sysmembar operation to flush its writes. 41 /// 42 /// Because of this, the sysmem flush memory page must be registered as early as possible during 43 /// driver initialization, and before any falcon is reset. 44 /// 45 pub(crate) struct SysmemFlush<'sys> { 46 /// Chipset we are operating on. 47 chipset: Chipset, 48 device: &'sys device::Device, 49 bar: Bar0<'sys>, 50 /// Keep the page alive as long as we need it. 51 page: CoherentHandle, 52 } 53 54 impl<'sys> SysmemFlush<'sys> { 55 /// Allocate a memory page and register it as the sysmem flush page. 56 pub(crate) fn register( 57 dev: &'sys device::Device<device::Bound>, 58 bar: Bar0<'sys>, 59 chipset: Chipset, 60 ) -> Result<Self> { 61 let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?; 62 63 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 64 65 Ok(Self { 66 chipset, 67 device: dev, 68 bar, 69 page, 70 }) 71 } 72 } 73 74 impl Drop for SysmemFlush<'_> { 75 fn drop(&mut self) { 76 let hal = hal::fb_hal(self.chipset); 77 78 if hal.read_sysmem_flush_page(self.bar) == self.page.dma_handle() { 79 let _ = hal.write_sysmem_flush_page(self.bar, 0).inspect_err(|e| { 80 dev_warn!( 81 &self.device, 82 "failed to unregister sysmem flush page: {:?}\n", 83 e 84 ) 85 }); 86 } else { 87 // Another page has been registered after us for some reason - warn as this is a bug. 88 dev_warn!( 89 &self.device, 90 "attempt to unregister a sysmem flush page that is not active\n" 91 ); 92 } 93 } 94 } 95 96 pub(crate) struct FbRange(Range<u64>); 97 98 impl FbRange { 99 pub(crate) fn len(&self) -> u64 { 100 self.0.end - self.0.start 101 } 102 } 103 104 impl From<Range<u64>> for FbRange { 105 fn from(range: Range<u64>) -> Self { 106 Self(range) 107 } 108 } 109 110 impl Deref for FbRange { 111 type Target = Range<u64>; 112 113 fn deref(&self) -> &Self::Target { 114 &self.0 115 } 116 } 117 118 impl fmt::Debug for FbRange { 119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 120 // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range. 121 if f.alternate() { 122 let size = self.len(); 123 124 if size < u64::SZ_1M { 125 let size_kib = size / u64::SZ_1K; 126 f.write_fmt(fmt!( 127 "{:#x}..{:#x} ({} KiB)", 128 self.0.start, 129 self.0.end, 130 size_kib 131 )) 132 } else { 133 let size_mib = size / u64::SZ_1M; 134 f.write_fmt(fmt!( 135 "{:#x}..{:#x} ({} MiB)", 136 self.0.start, 137 self.0.end, 138 size_mib 139 )) 140 } 141 } else { 142 f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end)) 143 } 144 } 145 } 146 147 /// Layout of the GPU framebuffer memory. 148 /// 149 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 150 #[derive(Debug)] 151 pub(crate) struct FbLayout { 152 /// Range of the framebuffer. Starts at `0`. 153 pub(crate) fb: FbRange, 154 /// VGA workspace, small area of reserved memory at the end of the framebuffer. 155 pub(crate) vga_workspace: FbRange, 156 /// FRTS range. 157 pub(crate) frts: FbRange, 158 /// Memory area containing the GSP bootloader image. 159 pub(crate) boot: FbRange, 160 /// Memory area containing the GSP firmware image. 161 pub(crate) elf: FbRange, 162 /// WPR2 heap. 163 pub(crate) wpr2_heap: FbRange, 164 /// WPR2 region range, starting with an instance of `GspFwWprMeta`. 165 pub(crate) wpr2: FbRange, 166 pub(crate) heap: FbRange, 167 pub(crate) vf_partition_count: u8, 168 /// PMU reserved memory size, in bytes. 169 pub(crate) pmu_reserved_size: u32, 170 } 171 172 impl FbLayout { 173 /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware. 174 pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, gsp_fw: &GspFirmware) -> Result<Self> { 175 let hal = hal::fb_hal(chipset); 176 177 let fb = { 178 let fb_size = hal.vidmem_size(bar); 179 180 FbRange(0..fb_size) 181 }; 182 183 let vga_workspace = { 184 let vga_base = { 185 const NV_PRAMIN_SIZE: u64 = u64::SZ_1M; 186 let base = fb.end - NV_PRAMIN_SIZE; 187 188 if hal.supports_display(bar) { 189 match bar 190 .read(regs::NV_PDISP_VGA_WORKSPACE_BASE) 191 .vga_workspace_addr() 192 { 193 Some(addr) => { 194 if addr < base { 195 const VBIOS_WORKSPACE_SIZE: u64 = 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 let frts_size: u64 = hal.frts_size(); 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 let heap_size = u64::from(hal.non_wpr_heap_size()); 256 FbRange(wpr2.start - heap_size..wpr2.start) 257 }; 258 259 Ok(Self { 260 fb, 261 vga_workspace, 262 frts, 263 boot, 264 elf, 265 wpr2_heap, 266 wpr2, 267 heap, 268 vf_partition_count: 0, 269 pmu_reserved_size: hal.pmu_reserved_size(), 270 }) 271 } 272 } 273