| 1 | |
| 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 | vgpu::VgpuState, |
| 28 | };
|
| 29 |
|
| 30 | mod hal;
|
| 31 | mod regs;
|
| 32 |
|
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | pub(crate) struct SysmemFlush<'sys> {
|
| 47 | |
| 48 | chipset: Chipset,
|
| 49 | device: &'sys device::Device,
|
| 50 | bar: Bar0<'sys>,
|
| 51 | |
| 52 | page: CoherentHandle,
|
| 53 | }
|
| 54 |
|
| 55 | impl<'sys> SysmemFlush<'sys> {
|
| 56 | |
| 57 | pub(crate) fn register(
|
| 58 | dev: &'sys device::Device<device::Bound>,
|
| 59 | bar: Bar0<'sys>,
|
| 60 | chipset: Chipset,
|
| 61 | ) -> Result<Self> {
|
| 62 | let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?;
|
| 63 |
|
| 64 | hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_address())?;
|
| 65 |
|
| 66 | Ok(Self {
|
| 67 | chipset,
|
| 68 | device: dev,
|
| 69 | bar,
|
| 70 | page,
|
| 71 | })
|
| 72 | }
|
| 73 | }
|
| 74 |
|
| 75 | impl Drop for SysmemFlush<'_> {
|
| 76 | fn drop(&mut self) {
|
| 77 | let hal = hal::fb_hal(self.chipset);
|
| 78 |
|
| 79 | if hal.read_sysmem_flush_page(self.bar) == self.page.dma_address() {
|
| 80 | let _ = hal.write_sysmem_flush_page(self.bar, 0).inspect_err(|e| {
|
| 81 | dev_warn!(
|
| 82 | &self.device,
|
| 83 | "failed to unregister sysmem flush page: {:?}\n",
|
| 84 | e
|
| 85 | )
|
| 86 | });
|
| 87 | } else {
|
| 88 | |
| 89 | dev_warn!(
|
| 90 | &self.device,
|
| 91 | "attempt to unregister a sysmem flush page that is not active\n"
|
| 92 | );
|
| 93 | }
|
| 94 | }
|
| 95 | }
|
| 96 |
|
| 97 | pub(crate) struct FbRange(Range<u64>);
|
| 98 |
|
| 99 | impl FbRange {
|
| 100 | pub(crate) fn len(&self) -> u64 {
|
| 101 | self.0.end - self.0.start
|
| 102 | }
|
| 103 | }
|
| 104 |
|
| 105 | impl From<Range<u64>> for FbRange {
|
| 106 | fn from(range: Range<u64>) -> Self {
|
| 107 | Self(range)
|
| 108 | }
|
| 109 | }
|
| 110 |
|
| 111 | impl Deref for FbRange {
|
| 112 | type Target = Range<u64>;
|
| 113 |
|
| 114 | fn deref(&self) -> &Self::Target {
|
| 115 | &self.0
|
| 116 | }
|
| 117 | }
|
| 118 |
|
| 119 | impl fmt::Debug for FbRange {
|
| 120 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
| 121 | |
| 122 | if f.alternate() {
|
| 123 | let size = self.len();
|
| 124 |
|
| 125 | if size < u64::SZ_1M {
|
| 126 | let size_kib = size / u64::SZ_1K;
|
| 127 | f.write_fmt(fmt!(
|
| 128 | "{:#x}..{:#x} ({} KiB)",
|
| 129 | self.0.start,
|
| 130 | self.0.end,
|
| 131 | size_kib
|
| 132 | ))
|
| 133 | } else {
|
| 134 | let size_mib = size / u64::SZ_1M;
|
| 135 | f.write_fmt(fmt!(
|
| 136 | "{:#x}..{:#x} ({} MiB)",
|
| 137 | self.0.start,
|
| 138 | self.0.end,
|
| 139 | size_mib
|
| 140 | ))
|
| 141 | }
|
| 142 | } else {
|
| 143 | f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end))
|
| 144 | }
|
| 145 | }
|
| 146 | }
|
| 147 |
|
| 148 | |
| 149 | |
| 150 | |
| 151 | #[derive(Debug)]
|
| 152 | pub(crate) struct FbRanges {
|
| 153 | |
| 154 | pub(crate) fb: FbRange,
|
| 155 | |
| 156 | pub(crate) vga_workspace: FbRange,
|
| 157 | |
| 158 | pub(crate) frts: FbRange,
|
| 159 | |
| 160 | pub(crate) boot: FbRange,
|
| 161 | |
| 162 | pub(crate) elf: FbRange,
|
| 163 | |
| 164 | pub(crate) wpr2_heap: FbRange,
|
| 165 | |
| 166 | pub(crate) wpr2: FbRange,
|
| 167 | |
| 168 | pub(crate) non_wpr_heap: FbRange,
|
| 169 | |
| 170 | pub(crate) vf_partition_count: u8,
|
| 171 | |
| 172 | pub(crate) pmu_reserved_size: u32,
|
| 173 | }
|
| 174 |
|
| 175 | impl FbRanges {
|
| 176 | |
| 177 | pub(crate) fn new(
|
| 178 | chipset: Chipset,
|
| 179 | bar: Bar0<'_>,
|
| 180 | gsp_fw: &GspFirmware,
|
| 181 | vgpu_state: VgpuState,
|
| 182 | ) -> Result<Self> {
|
| 183 | let hal = hal::fb_hal(chipset);
|
| 184 |
|
| 185 | let fb = {
|
| 186 | let fb_size = hal.vidmem_size(bar);
|
| 187 |
|
| 188 | FbRange(0..fb_size)
|
| 189 | };
|
| 190 |
|
| 191 | let vga_workspace = {
|
| 192 | let vga_base = {
|
| 193 | const NV_PRAMIN_SIZE: u64 = u64::SZ_1M;
|
| 194 | let base = fb.end - NV_PRAMIN_SIZE;
|
| 195 |
|
| 196 | if hal.supports_display(bar) {
|
| 197 | match bar
|
| 198 | .read(regs::NV_PDISP_VGA_WORKSPACE_BASE)
|
| 199 | .vga_workspace_addr()
|
| 200 | {
|
| 201 | Some(addr) => {
|
| 202 | if addr < base {
|
| 203 | const VBIOS_WORKSPACE_SIZE: u64 = u64::SZ_128K;
|
| 204 |
|
| 205 | |
| 206 | fb.end - VBIOS_WORKSPACE_SIZE
|
| 207 | } else {
|
| 208 | addr
|
| 209 | }
|
| 210 | }
|
| 211 | None => base,
|
| 212 | }
|
| 213 | } else {
|
| 214 | base
|
| 215 | }
|
| 216 | };
|
| 217 |
|
| 218 | FbRange(vga_base..fb.end)
|
| 219 | };
|
| 220 |
|
| 221 | let frts = {
|
| 222 | const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
|
| 223 | let frts_size: u64 = hal.frts_size();
|
| 224 | let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size;
|
| 225 |
|
| 226 | FbRange(frts_base..frts_base + frts_size)
|
| 227 | };
|
| 228 |
|
| 229 | let boot = {
|
| 230 | const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
|
| 231 | let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
|
| 232 | let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
|
| 233 |
|
| 234 | FbRange(bootloader_base..bootloader_base + bootloader_size)
|
| 235 | };
|
| 236 |
|
| 237 | let elf = {
|
| 238 | const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
|
| 239 | let elf_size = u64::from_safe_cast(gsp_fw.size);
|
| 240 | let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
|
| 241 |
|
| 242 | FbRange(elf_addr..elf_addr + elf_size)
|
| 243 | };
|
| 244 |
|
| 245 | let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?;
|
| 246 |
|
| 247 | let wpr2_heap = {
|
| 248 | const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
|
| 249 | let wpr2_heap_addr = elf
|
| 250 | .start
|
| 251 | .checked_sub(wpr2_heap_size)
|
| 252 | .ok_or(EOVERFLOW)?
|
| 253 | .align_down(WPR2_HEAP_DOWN_ALIGN);
|
| 254 |
|
| 255 | FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN))
|
| 256 | };
|
| 257 |
|
| 258 | let wpr2 = {
|
| 259 | const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
|
| 260 | let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
|
| 261 | .align_down(WPR2_DOWN_ALIGN);
|
| 262 |
|
| 263 | FbRange(wpr2_addr..frts.end)
|
| 264 | };
|
| 265 |
|
| 266 | let non_wpr_heap = {
|
| 267 | let non_wpr_heap_size = hal.non_wpr_heap_size();
|
| 268 | FbRange(wpr2.start - non_wpr_heap_size..wpr2.start)
|
| 269 | };
|
| 270 |
|
| 271 | Ok(Self {
|
| 272 | fb,
|
| 273 | vga_workspace,
|
| 274 | frts,
|
| 275 | boot,
|
| 276 | elf,
|
| 277 | wpr2_heap,
|
| 278 | wpr2,
|
| 279 | non_wpr_heap,
|
| 280 | vf_partition_count,
|
| 281 | pmu_reserved_size: hal.pmu_reserved_size(),
|
| 282 | })
|
| 283 | }
|
| 284 | }
|
| 285 |
|
| 286 | |
| 287 | |
| 288 | pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> {
|
| 289 | let wpr2_hi = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI);
|
| 290 |
|
| 291 | if !wpr2_hi.is_wpr2_set() {
|
| 292 | return None;
|
| 293 | }
|
| 294 |
|
| 295 | let wpr2_lo = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO);
|
| 296 |
|
| 297 | Some(wpr2_lo.lower_bound()..wpr2_hi.higher_bound())
|
| 298 | }
|
| 299 |
|
| 300 | |
| 301 | fn wpr2_heap_params(chipset: Chipset, vgpu_state: VgpuState, fb_size: u64) -> Result<(u8, u64)> {
|
| 302 | Ok(match vgpu_state {
|
| 303 | VgpuState::Disabled => (
|
| 304 | 0,
|
| 305 | gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb_size)?,
|
| 306 | ),
|
| 307 | VgpuState::Enabled { total_vfs } => (
|
| 308 | u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?,
|
| 309 | gsp::LibosParams::vgpu_wpr_heap_size(),
|
| 310 | ),
|
| 311 | })
|
| 312 | }
|
| 313 |
|
| 314 | |
| 315 | #[derive(Debug)]
|
| 316 | pub(crate) struct FbSizes {
|
| 317 | |
| 318 | pub(crate) frts_size: u64,
|
| 319 | |
| 320 | pub(crate) wpr2_heap_size: u64,
|
| 321 | |
| 322 | pub(crate) non_wpr_heap_size: u64,
|
| 323 | |
| 324 | pub(crate) pmu_reserved_size: u32,
|
| 325 | |
| 326 | pub(crate) vf_partition_count: u8,
|
| 327 | }
|
| 328 |
|
| 329 | impl FbSizes {
|
| 330 | |
| 331 | pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, vgpu_state: VgpuState) -> Result<Self> {
|
| 332 | let hal = hal::fb_hal(chipset);
|
| 333 | let fb_size = hal.vidmem_size(bar);
|
| 334 | let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb_size)?;
|
| 335 |
|
| 336 | Ok(Self {
|
| 337 | frts_size: hal.frts_size(),
|
| 338 | wpr2_heap_size,
|
| 339 | non_wpr_heap_size: hal.non_wpr_heap_size(),
|
| 340 | pmu_reserved_size: hal.pmu_reserved_size(),
|
| 341 | vf_partition_count,
|
| 342 | })
|
| 343 | }
|
| 344 | }
|