xref: /linux/drivers/gpu/nova-core/gsp.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 mod boot;
4 mod hal;
5 
6 use 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 
24 pub(crate) mod cmdq;
25 pub(crate) mod commands;
26 mod fw;
27 mod regs;
28 mod sequencer;
29 
30 pub(crate) use fw::{
31     GspFmcBootParams,
32     GspFwWprMeta,
33     LibosMemoryRegionInitArgument,
34     LibosParams, //
35 };
36 pub(crate) use hal::boot_firmware_files;
37 
38 use 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 
55 pub(crate) const GSP_PAGE_SHIFT: usize = 12;
56 pub(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.
64 pub(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 
74 impl<'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.
81 const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10;
82 const 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)]
87 struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
88 
89 impl<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)
118 struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>);
119 
120 impl 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 
138 struct 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]
149 pub(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 
162 impl 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`].
226 pub(crate) struct UnloadBundle(KBox<dyn hal::UnloadBundle>);
227