xref: /linux/drivers/gpu/nova-core/fb.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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     vgpu::VgpuState, //
28 };
29 
30 mod hal;
31 mod regs;
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 pub(crate) struct SysmemFlush<'sys> {
47     /// Chipset we are operating on.
48     chipset: Chipset,
49     device: &'sys device::Device,
50     bar: Bar0<'sys>,
51     /// Keep the page alive as long as we need it.
52     page: CoherentHandle,
53 }
54 
55 impl<'sys> SysmemFlush<'sys> {
56     /// Allocate a memory page and register it as the sysmem flush page.
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             // Another page has been registered after us for some reason - warn as this is a bug.
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         // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range.
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 /// Layout of the GPU framebuffer memory.
149 ///
150 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
151 #[derive(Debug)]
152 pub(crate) struct FbRanges {
153     /// Range of the framebuffer. Starts at `0`.
154     pub(crate) fb: FbRange,
155     /// VGA workspace, small area of reserved memory at the end of the framebuffer.
156     pub(crate) vga_workspace: FbRange,
157     /// FRTS range.
158     pub(crate) frts: FbRange,
159     /// Memory area containing the GSP bootloader image.
160     pub(crate) boot: FbRange,
161     /// Memory area containing the GSP firmware image.
162     pub(crate) elf: FbRange,
163     /// WPR2 heap.
164     pub(crate) wpr2_heap: FbRange,
165     /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
166     pub(crate) wpr2: FbRange,
167     /// Non-WPR heap, located just below WPR2.
168     pub(crate) non_wpr_heap: FbRange,
169     /// Number of VF partitions.
170     pub(crate) vf_partition_count: u8,
171     /// PMU reserved memory size, in bytes.
172     pub(crate) pmu_reserved_size: u32,
173 }
174 
175 impl FbRanges {
176     /// Computes concrete framebuffer ranges required on non-FSP booting architectures.
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                                 // Point workspace address to end of framebuffer.
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 /// Reads the WPR2 memory region registers and returns the range if set.
287 /// Returns `None` if the WPR2 region is not set.
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 /// Computes the number of VF partitions and the WPR2 heap size from the vGPU state.
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 /// Framebuffer region sizes needed for GSP-FMC boot.
315 #[derive(Debug)]
316 pub(crate) struct FbSizes {
317     /// FRTS size, in bytes.
318     pub(crate) frts_size: u64,
319     /// WPR2 heap size, in bytes.
320     pub(crate) wpr2_heap_size: u64,
321     /// Non-WPR heap size, in bytes.
322     pub(crate) non_wpr_heap_size: u64,
323     /// PMU reserved memory size, in bytes.
324     pub(crate) pmu_reserved_size: u32,
325     /// Number of VF partitions.
326     pub(crate) vf_partition_count: u8,
327 }
328 
329 impl FbSizes {
330     /// Computes the framebuffer region sizes for GSP-FMC boot.
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 }
345