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