projects/linux·blob master · refs

226 lines · 7.5 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3mod boot;
4mod hal;
5
6use kernel::{
7 debugfs,
8 device,
9 dma::{
10 Coherent,
11 CoherentBox,
12 CoherentView,
13 DmaAddress, //
14 },
15 io::{
16 io_project,
17 io_write,
18 Io, //
19 },
20 pci,
21 prelude::*, //
22};
23
24pub(crate) mod cmdq;
25pub(crate) mod commands;
26mod fw;
27mod regs;
28mod sequencer;
29
30pub(crate) use fw::{
31 GspFmcBootParams,
32 GspFwWprMeta,
33 LibosMemoryRegionInitArgument,
34 LibosParams, //
35};
36pub(crate) use hal::boot_firmware_files;
37
38use crate::{
39 driver::Bar0,
40 falcon::{
41 gsp::Gsp as GspFalcon,
42 sec2::Sec2 as Sec2Falcon,
43 Falcon, //
44 },
45 fsp::Fsp,
46 gpu::Chipset,
47 gsp::{
48 cmdq::Cmdq,
49 fw::GspArgumentsPadded, //
50 },
51 num,
52 vgpu::VgpuManager, //
53};
54
55pub(crate) const GSP_PAGE_SHIFT: usize = 12;
56pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
57
58/// Common context for the GSP boot process.
59///
60/// It carries two distinct lifetimes:
61///
62/// - `'gpu` is the lifetime of the bound GPU device, as captured by the GPU subdevices.
63/// - `'ctx` is a shorter lifetime during which this context borrows those subdevices.
64pub(crate) struct GspBootContext<'ctx, 'gpu> {
65 pub(crate) pdev: &'gpu pci::Device<device::Bound>,
66 pub(crate) bar: Bar0<'gpu>,
67 pub(crate) chipset: Chipset,
68 pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>,
69 pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>,
70 pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>,
71 pub(crate) vgpu: &'ctx VgpuManager,
72}
73
74impl<'ctx, 'gpu> GspBootContext<'ctx, 'gpu> {
75 pub(crate) fn dev(&self) -> &'gpu device::Device<device::Bound> {
76 self.pdev.as_ref()
77 }
78}
79
80/// Number of GSP pages to use in a RM log buffer.
81const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10;
82const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE;
83
84/// Array of page table entries, as understood by the GSP bootloader.
85#[repr(C)]
86#[derive(FromBytes, IntoBytes)]
87struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
88
89impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
90 /// Initialize a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
91 fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
92 for i in 0..NUM_PAGES {
93 io_write!(view, .0[build: i],
94 start
95 .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
96 .ok_or(EOVERFLOW)?
97 );
98 }
99
100 Ok(())
101 }
102}
103
104/// The logging buffers are byte queues that contain encoded printf-like
105/// messages from GSP-RM. They need to be decoded by a special application
106/// that can parse the buffers.
107///
108/// The 'loginit' buffer contains logs from early GSP-RM init and
109/// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are
110/// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE.
111///
112/// The physical address map for the log buffer is stored in the buffer
113/// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp).
114/// Initially, pp is equal to 0. If the buffer has valid logging data in it,
115/// then pp points to index into the buffer where the next logging entry will
116/// be written. Therefore, the logging data is valid if:
117/// 1 <= pp < sizeof(buffer)/sizeof(u64)
118struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>);
119
120impl LogBuffer {
121 /// Creates a new `LogBuffer` mapped on `dev`.
122 fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
123 let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?);
124
125 let start_addr = obj.0.dma_address();
126
127 let pte_view = io_project!(
128 obj.0,
129 [build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
130 )
131 .try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?;
132 PteArray::init(pte_view, start_addr)?;
133
134 Ok(obj)
135 }
136}
137
138struct LogBuffers {
139 /// Init log buffer.
140 loginit: LogBuffer,
141 /// Interrupts log buffer.
142 logintr: LogBuffer,
143 /// RM log buffer.
144 logrm: LogBuffer,
145}
146
147/// GSP runtime data.
148#[pin_data]
149pub(crate) struct Gsp {
150 /// Libos arguments.
151 pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>,
152 /// Log buffers, optionally exposed via debugfs.
153 #[pin]
154 logs: debugfs::Scope<LogBuffers>,
155 /// Command queue.
156 #[pin]
157 pub(crate) cmdq: Cmdq,
158 /// RM arguments.
159 rmargs: Coherent<GspArgumentsPadded>,
160}
161
162impl Gsp {
163 // Creates an in-place initializer for a `Gsp` manager for `pdev`.
164 pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ {
165 pin_init::pin_init_scope(move || {
166 let dev = pdev.as_ref();
167
168 let loginit = LogBuffer::new(dev)?;
169 let logintr = LogBuffer::new(dev)?;
170 let logrm = LogBuffer::new(dev)?;
171
172 // Initialise the logging structures. The OpenRM equivalents are in:
173 // _kgspInitLibosLoggingStructures (allocates memory for buffers)
174 // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
175 Ok(try_pin_init!(Self {
176 cmdq <- Cmdq::new(dev),
177 rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(&cmdq))?,
178 libos: {
179 let mut libos = CoherentBox::zeroed_slice(
180 dev,
181 GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(),
182 GFP_KERNEL,
183 )?;
184
185 libos.init_at(0, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?;
186 libos.init_at(1, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?;
187 libos.init_at(2, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
188 libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
189
190 libos.into()
191 },
192 logs <- {
193 let log_buffers = LogBuffers {
194 loginit,
195 logintr,
196 logrm,
197 };
198
199 #[allow(static_mut_refs)]
200 // SAFETY: `DEBUGFS_ROOT` is created before driver registration and cleared
201 // after driver unregistration, so no probe() can race with its modification.
202 //
203 // PANIC: `DEBUGFS_ROOT` cannot be `None` here. It is set before driver
204 // registration and cleared after driver unregistration, so it is always
205 // `Some` for the entire lifetime that probe() can be called.
206 let log_parent: &debugfs::Dir = unsafe { crate::DEBUGFS_ROOT.as_ref() }
207 .expect("DEBUGFS_ROOT not initialized");
208
209 log_parent.scope(log_buffers, dev.name(), |logs, dir| {
210 dir.read_binary_file(c"loginit", &logs.loginit.0);
211 dir.read_binary_file(c"logintr", &logs.logintr.0);
212 dir.read_binary_file(c"logrm", &logs.logrm.0);
213 })
214 },
215 }))
216 })
217 }
218
219 /// Query the GSP for the static GPU information.
220 pub(crate) fn get_static_info(&self, bar: Bar0<'_>) -> Result<commands::GetGspStaticInfoReply> {
221 self.cmdq.send_command(bar, commands::GetGspStaticInfo)
222 }
223}
224
225/// Opaque bundle required to unload the GSP. Created by [`Gsp::boot`], consumed by [`Gsp::unload`].
226pub(crate) struct UnloadBundle(KBox<dyn hal::UnloadBundle>);