xref: /linux/drivers/gpu/nova-core/gsp.rs (revision edcb134264f7db95295a9f0feb7a8b3acde72a08)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 mod boot;
4 
5 use kernel::{
6     device,
7     dma::{
8         CoherentAllocation,
9         DmaAddress, //
10     },
11     dma_write,
12     pci,
13     prelude::*,
14     transmute::AsBytes, //
15 };
16 
17 pub(crate) mod cmdq;
18 pub(crate) mod commands;
19 mod fw;
20 
21 pub(crate) use fw::{
22     GspFwWprMeta,
23     LibosParams, //
24 };
25 
26 use crate::{
27     gsp::cmdq::Cmdq,
28     gsp::fw::{
29         GspArgumentsCached,
30         LibosMemoryRegionInitArgument, //
31     },
32     num,
33 };
34 
35 pub(crate) const GSP_PAGE_SHIFT: usize = 12;
36 pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
37 
38 /// Number of GSP pages to use in a RM log buffer.
39 const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10;
40 
41 /// Array of page table entries, as understood by the GSP bootloader.
42 #[repr(C)]
43 struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
44 
45 /// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one.
46 unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
47 
48 impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
49     /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
50     fn new(start: DmaAddress) -> Result<Self> {
51         let mut ptes = [0u64; NUM_PAGES];
52         for (i, pte) in ptes.iter_mut().enumerate() {
53             *pte = start
54                 .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
55                 .ok_or(EOVERFLOW)?;
56         }
57 
58         Ok(Self(ptes))
59     }
60 }
61 
62 /// The logging buffers are byte queues that contain encoded printf-like
63 /// messages from GSP-RM.  They need to be decoded by a special application
64 /// that can parse the buffers.
65 ///
66 /// The 'loginit' buffer contains logs from early GSP-RM init and
67 /// exception dumps.  The 'logrm' buffer contains the subsequent logs. Both are
68 /// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE.
69 ///
70 /// The physical address map for the log buffer is stored in the buffer
71 /// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp).
72 /// Initially, pp is equal to 0. If the buffer has valid logging data in it,
73 /// then pp points to index into the buffer where the next logging entry will
74 /// be written. Therefore, the logging data is valid if:
75 ///   1 <= pp < sizeof(buffer)/sizeof(u64)
76 struct LogBuffer(CoherentAllocation<u8>);
77 
78 impl LogBuffer {
79     /// Creates a new `LogBuffer` mapped on `dev`.
80     fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
81         const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES;
82 
83         let mut obj = Self(CoherentAllocation::<u8>::alloc_coherent(
84             dev,
85             NUM_PAGES * GSP_PAGE_SIZE,
86             GFP_KERNEL | __GFP_ZERO,
87         )?);
88         let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
89 
90         // SAFETY: `obj` has just been created and we are its sole user.
91         unsafe {
92             // Copy the self-mapping PTE at the expected location.
93             obj.0
94                 .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))?
95                 .copy_from_slice(ptes.as_bytes())
96         };
97 
98         Ok(obj)
99     }
100 }
101 
102 /// GSP runtime data.
103 #[pin_data]
104 pub(crate) struct Gsp {
105     /// Libos arguments.
106     pub(crate) libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
107     /// Init log buffer.
108     loginit: LogBuffer,
109     /// Interrupts log buffer.
110     logintr: LogBuffer,
111     /// RM log buffer.
112     logrm: LogBuffer,
113     /// Command queue.
114     pub(crate) cmdq: Cmdq,
115     /// RM arguments.
116     rmargs: CoherentAllocation<GspArgumentsCached>,
117 }
118 
119 impl Gsp {
120     // Creates an in-place initializer for a `Gsp` manager for `pdev`.
121     pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> {
122         let dev = pdev.as_ref();
123         let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent(
124             dev,
125             GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(),
126             GFP_KERNEL | __GFP_ZERO,
127         )?;
128 
129         // Initialise the logging structures. The OpenRM equivalents are in:
130         // _kgspInitLibosLoggingStructures (allocates memory for buffers)
131         // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
132         let loginit = LogBuffer::new(dev)?;
133         dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?;
134 
135         let logintr = LogBuffer::new(dev)?;
136         dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?;
137 
138         let logrm = LogBuffer::new(dev)?;
139         dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
140 
141         let cmdq = Cmdq::new(dev)?;
142 
143         let rmargs = CoherentAllocation::<GspArgumentsCached>::alloc_coherent(
144             dev,
145             1,
146             GFP_KERNEL | __GFP_ZERO,
147         )?;
148         dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?;
149         dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?;
150 
151         Ok(try_pin_init!(Self {
152             libos,
153             loginit,
154             logintr,
155             logrm,
156             rmargs,
157             cmdq,
158         }))
159     }
160 }
161