xref: /linux/drivers/gpu/nova-core/gsp/commands.rs (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use core::{
4     array,
5     convert::Infallible,
6     ffi::FromBytesUntilNulError,
7     str::Utf8Error, //
8 };
9 
10 use kernel::{
11     device,
12     pci,
13     prelude::*,
14     time::Delta,
15     transmute::{
16         AsBytes,
17         FromBytes, //
18     }, //
19 };
20 
21 use crate::{
22     driver::Bar0,
23     gsp::{
24         cmdq::{
25             Cmdq,
26             CommandToGsp,
27             MessageFromGsp, //
28         },
29         fw::{
30             commands::*,
31             MsgFunction, //
32         },
33     },
34     sbuffer::SBufferIter,
35 };
36 
37 /// The `GspSetSystemInfo` command.
38 pub(crate) struct SetSystemInfo<'a> {
39     pdev: &'a pci::Device<device::Bound>,
40 }
41 
42 impl<'a> SetSystemInfo<'a> {
43     /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
44     pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self {
45         Self { pdev }
46     }
47 }
48 
49 impl<'a> CommandToGsp for SetSystemInfo<'a> {
50     const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
51     type Command = GspSetSystemInfo;
52     type InitError = Error;
53 
54     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
55         GspSetSystemInfo::init(self.pdev)
56     }
57 }
58 
59 struct RegistryEntry {
60     key: &'static str,
61     value: u32,
62 }
63 
64 /// The `SetRegistry` command.
65 pub(crate) struct SetRegistry {
66     entries: [RegistryEntry; Self::NUM_ENTRIES],
67 }
68 
69 impl SetRegistry {
70     // For now we hard-code the registry entries. Future work will allow others to
71     // be added as module parameters.
72     const NUM_ENTRIES: usize = 3;
73 
74     /// Creates a new `SetRegistry` command, using a set of hardcoded entries.
75     pub(crate) fn new() -> Self {
76         Self {
77             entries: [
78                 // RMSecBusResetEnable - enables PCI secondary bus reset
79                 RegistryEntry {
80                     key: "RMSecBusResetEnable",
81                     value: 1,
82                 },
83                 // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on
84                 // any PCI reset.
85                 RegistryEntry {
86                     key: "RMForcePcieConfigSave",
87                     value: 1,
88                 },
89                 // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found
90                 // in the internal product name database.
91                 RegistryEntry {
92                     key: "RMDevidCheckIgnore",
93                     value: 1,
94                 },
95             ],
96         }
97     }
98 }
99 
100 impl CommandToGsp for SetRegistry {
101     const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
102     type Command = PackedRegistryTable;
103     type InitError = Infallible;
104 
105     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
106         PackedRegistryTable::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32)
107     }
108 
109     fn variable_payload_len(&self) -> usize {
110         let mut key_size = 0;
111         for i in 0..Self::NUM_ENTRIES {
112             key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator
113         }
114         Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>() + key_size
115     }
116 
117     fn init_variable_payload(
118         &self,
119         dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
120     ) -> Result {
121         let string_data_start_offset =
122             size_of::<PackedRegistryTable>() + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>();
123 
124         // Array for string data.
125         let mut string_data = KVec::new();
126 
127         for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
128             dst.write_all(
129                 PackedRegistryEntry::new(
130                     (string_data_start_offset + string_data.len()) as u32,
131                     entry.value,
132                 )
133                 .as_bytes(),
134             )?;
135 
136             let key_bytes = entry.key.as_bytes();
137             string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
138             string_data.push(0, GFP_KERNEL)?;
139         }
140 
141         dst.write_all(string_data.as_slice())
142     }
143 }
144 
145 /// Message type for GSP initialization done notification.
146 struct GspInitDone;
147 
148 // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
149 // trivially has no uninitialized bytes.
150 unsafe impl FromBytes for GspInitDone {}
151 
152 impl MessageFromGsp for GspInitDone {
153     const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
154     type InitError = Infallible;
155     type Message = ();
156 
157     fn read(
158         _msg: &Self::Message,
159         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
160     ) -> Result<Self, Self::InitError> {
161         Ok(GspInitDone)
162     }
163 }
164 
165 /// Waits for GSP initialization to complete.
166 pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result {
167     loop {
168         match cmdq.receive_msg::<GspInitDone>(Delta::from_secs(10)) {
169             Ok(_) => break Ok(()),
170             Err(ERANGE) => continue,
171             Err(e) => break Err(e),
172         }
173     }
174 }
175 
176 /// The `GetGspStaticInfo` command.
177 struct GetGspStaticInfo;
178 
179 impl CommandToGsp for GetGspStaticInfo {
180     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
181     type Command = GspStaticConfigInfo;
182     type InitError = Infallible;
183 
184     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
185         GspStaticConfigInfo::init_zeroed()
186     }
187 }
188 
189 /// The reply from the GSP to the [`GetGspInfo`] command.
190 pub(crate) struct GetGspStaticInfoReply {
191     gpu_name: [u8; 64],
192 }
193 
194 impl MessageFromGsp for GetGspStaticInfoReply {
195     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
196     type Message = GspStaticConfigInfo;
197     type InitError = Infallible;
198 
199     fn read(
200         msg: &Self::Message,
201         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
202     ) -> Result<Self, Self::InitError> {
203         Ok(GetGspStaticInfoReply {
204             gpu_name: msg.gpu_name_str(),
205         })
206     }
207 }
208 
209 /// Error type for [`GetGspStaticInfoReply::gpu_name`].
210 #[derive(Debug)]
211 pub(crate) enum GpuNameError {
212     /// The GPU name string does not contain a null terminator.
213     NoNullTerminator(FromBytesUntilNulError),
214 
215     /// The GPU name string contains invalid UTF-8.
216     #[expect(dead_code)]
217     InvalidUtf8(Utf8Error),
218 }
219 
220 impl GetGspStaticInfoReply {
221     /// Returns the name of the GPU as a string.
222     ///
223     /// Returns an error if the string given by the GSP does not contain a null terminator or
224     /// contains invalid UTF-8.
225     pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
226         CStr::from_bytes_until_nul(&self.gpu_name)
227             .map_err(GpuNameError::NoNullTerminator)?
228             .to_str()
229             .map_err(GpuNameError::InvalidUtf8)
230     }
231 }
232 
233 /// Send the [`GetGspInfo`] command and awaits for its reply.
234 pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> {
235     cmdq.send_command(bar, GetGspStaticInfo)?;
236 
237     loop {
238         match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) {
239             Ok(info) => return Ok(info),
240             Err(ERANGE) => continue,
241             Err(e) => return Err(e),
242         }
243     }
244 }
245