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