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