Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3use core::ops::Range;
4
5use kernel::{
6 device,
7 prelude::*,
8 ptr::{
9 Alignable,
10 Alignment, //
11 },
12 sizes::*,
13 sync::aref::ARef, //
14};
15
16use crate::{
17 dma::DmaObject,
18 driver::Bar0,
19 firmware::gsp::GspFirmware,
20 gpu::Chipset,
21 gsp,
22 num::{
23 usize_as_u64,
24 FromSafeCast, //
25 },
26 regs,
27};
28
29mod hal;
30
31/// Type holding the sysmem flush memory page, a page of memory to be written into the
32/// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
33///
34/// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware
35/// memory-barrier operation that flushes all pending GPU-side memory writes that were done through
36/// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a
37/// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure
38/// this write is visible to the host and prevent driver timeouts, the falcon must perform a
39/// sysmembar operation to flush its writes.
40///
41/// Because of this, the sysmem flush memory page must be registered as early as possible during
42/// driver initialization, and before any falcon is reset.
43///
44/// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
45/// otherwise the GPU might still use it even after it has been freed.
46pub(crate) struct SysmemFlush {
47 /// Chipset we are operating on.
48 chipset: Chipset,
49 device: ARef<device::Device>,
50 /// Keep the page alive as long as we need it.
51 page: DmaObject,
52}
53
54impl SysmemFlush {
55 /// Allocate a memory page and register it as the sysmem flush page.
56 pub(crate) fn register(
57 dev: &device::Device<device::Bound>,
58 bar: &Bar0,
59 chipset: Chipset,
60 ) -> Result<Self> {
61 let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
62
63 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
64
65 Ok(Self {
66 chipset,
67 device: dev.into(),
68 page,
69 })
70 }
71
72 /// Unregister the managed sysmem flush page.
73 ///
74 /// In order to gracefully tear down the GPU, users must make sure to call this method before
75 /// dropping the object.
76 pub(crate) fn unregister(&self, bar: &Bar0) {
77 let hal = hal::fb_hal(self.chipset);
78
79 if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
80 let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
81 dev_warn!(
82 &self.device,
83 "failed to unregister sysmem flush page: {:?}",
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/// Layout of the GPU framebuffer memory.
98///
99/// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
100#[derive(Debug)]
101pub(crate) struct FbLayout {
102 /// Range of the framebuffer. Starts at `0`.
103 pub(crate) fb: Range<u64>,
104 /// VGA workspace, small area of reserved memory at the end of the framebuffer.
105 pub(crate) vga_workspace: Range<u64>,
106 /// FRTS range.
107 pub(crate) frts: Range<u64>,
108 /// Memory area containing the GSP bootloader image.
109 pub(crate) boot: Range<u64>,
110 /// Memory area containing the GSP firmware image.
111 pub(crate) elf: Range<u64>,
112 /// WPR2 heap.
113 pub(crate) wpr2_heap: Range<u64>,
114 /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
115 pub(crate) wpr2: Range<u64>,
116 pub(crate) heap: Range<u64>,
117 pub(crate) vf_partition_count: u8,
118}
119
120impl FbLayout {
121 /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
122 pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
123 let hal = hal::fb_hal(chipset);
124
125 let fb = {
126 let fb_size = hal.vidmem_size(bar);
127
128 0..fb_size
129 };
130
131 let vga_workspace = {
132 let vga_base = {
133 const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M);
134 let base = fb.end - NV_PRAMIN_SIZE;
135
136 if hal.supports_display(bar) {
137 match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
138 Some(addr) => {
139 if addr < base {
140 const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K);
141
142 // Point workspace address to end of framebuffer.
143 fb.end - VBIOS_WORKSPACE_SIZE
144 } else {
145 addr
146 }
147 }
148 None => base,
149 }
150 } else {
151 base
152 }
153 };
154
155 vga_base..fb.end
156 };
157
158 let frts = {
159 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
160 const FRTS_SIZE: u64 = usize_as_u64(SZ_1M);
161 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE;
162
163 frts_base..frts_base + FRTS_SIZE
164 };
165
166 let boot = {
167 const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
168 let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
169 let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
170
171 bootloader_base..bootloader_base + bootloader_size
172 };
173
174 let elf = {
175 const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
176 let elf_size = u64::from_safe_cast(gsp_fw.size);
177 let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
178
179 elf_addr..elf_addr + elf_size
180 };
181
182 let wpr2_heap = {
183 const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
184 let wpr2_heap_size =
185 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
186 let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
187
188 wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)
189 };
190
191 let wpr2 = {
192 const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
193 let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
194 .align_down(WPR2_DOWN_ALIGN);
195
196 wpr2_addr..frts.end
197 };
198
199 let heap = {
200 const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
201
202 wpr2.start - HEAP_SIZE..wpr2.start
203 };
204
205 Ok(Self {
206 fb,
207 vga_workspace,
208 frts,
209 boot,
210 elf,
211 wpr2_heap,
212 wpr2,
213 heap,
214 vf_partition_count: 0,
215 })
216 }
217}