projects/linux·blob master · refs

344 lines · 10.5 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3use core::ops::{
4 Deref,
5 Range, //
6};
7
8use kernel::{
9 device,
10 dma::CoherentHandle,
11 fmt,
12 io::Io,
13 prelude::*,
14 ptr::{
15 Alignable,
16 Alignment, //
17 },
18 sizes::*, //
19};
20
21use crate::{
22 driver::Bar0,
23 firmware::gsp::GspFirmware,
24 gpu::Chipset,
25 gsp,
26 num::FromSafeCast,
27 vgpu::VgpuState, //
28};
29
30mod hal;
31mod 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///
46pub(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
55impl<'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
75impl 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
97pub(crate) struct FbRange(Range<u64>);
98
99impl FbRange {
100 pub(crate) fn len(&self) -> u64 {
101 self.0.end - self.0.start
102 }
103}
104
105impl From<Range<u64>> for FbRange {
106 fn from(range: Range<u64>) -> Self {
107 Self(range)
108 }
109}
110
111impl Deref for FbRange {
112 type Target = Range<u64>;
113
114 fn deref(&self) -> &Self::Target {
115 &self.0
116 }
117}
118
119impl 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)]
152pub(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
175impl 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.
288pub(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.
301fn 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)]
316pub(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
329impl 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}