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