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