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: &'sys Bar0, 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: &'sys Bar0, 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 } 169 170 impl FbLayout { 171 /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware. 172 pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> { 173 let hal = hal::fb_hal(chipset); 174 175 let fb = { 176 let fb_size = hal.vidmem_size(bar); 177 178 FbRange(0..fb_size) 179 }; 180 181 let vga_workspace = { 182 let vga_base = { 183 const NV_PRAMIN_SIZE: u64 = u64::SZ_1M; 184 let base = fb.end - NV_PRAMIN_SIZE; 185 186 if hal.supports_display(bar) { 187 match bar 188 .read(regs::NV_PDISP_VGA_WORKSPACE_BASE) 189 .vga_workspace_addr() 190 { 191 Some(addr) => { 192 if addr < base { 193 const VBIOS_WORKSPACE_SIZE: u64 = u64::SZ_128K; 194 195 // Point workspace address to end of framebuffer. 196 fb.end - VBIOS_WORKSPACE_SIZE 197 } else { 198 addr 199 } 200 } 201 None => base, 202 } 203 } else { 204 base 205 } 206 }; 207 208 FbRange(vga_base..fb.end) 209 }; 210 211 let frts = { 212 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 213 let frts_size: u64 = hal.frts_size(); 214 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size; 215 216 FbRange(frts_base..frts_base + frts_size) 217 }; 218 219 let boot = { 220 const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>(); 221 let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); 222 let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN); 223 224 FbRange(bootloader_base..bootloader_base + bootloader_size) 225 }; 226 227 let elf = { 228 const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>(); 229 let elf_size = u64::from_safe_cast(gsp_fw.size); 230 let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN); 231 232 FbRange(elf_addr..elf_addr + elf_size) 233 }; 234 235 let wpr2_heap = { 236 const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 237 let wpr2_heap_size = 238 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end)?; 239 let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN); 240 241 FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)) 242 }; 243 244 let wpr2 = { 245 const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 246 let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>())) 247 .align_down(WPR2_DOWN_ALIGN); 248 249 FbRange(wpr2_addr..frts.end) 250 }; 251 252 let heap = { 253 const HEAP_SIZE: u64 = u64::SZ_1M; 254 255 FbRange(wpr2.start - HEAP_SIZE..wpr2.start) 256 }; 257 258 Ok(Self { 259 fb, 260 vga_workspace, 261 frts, 262 boot, 263 elf, 264 wpr2_heap, 265 wpr2, 266 heap, 267 vf_partition_count: 0, 268 }) 269 } 270 } 271