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