xref: /linux/drivers/gpu/nova-core/gsp/commands.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0
2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 use core::{
5     array,
6     convert::Infallible,
7     ffi::FromBytesUntilNulError,
8     ops::Range,
9     str::Utf8Error, //
10 };
11 
12 use kernel::{
13     device,
14     pci,
15     prelude::*,
16     transmute::{
17         AsBytes,
18         FromBytes, //
19     }, //
20 };
21 
22 use crate::{
23     gpu::Chipset,
24     gsp::{
25         cmdq::{
26             Cmdq,
27             CommandToGsp,
28             MessageFromGsp,
29             NoReply, //
30         },
31         fw::{
32             self,
33             MsgFunction, //
34         },
35     },
36     sbuffer::SBufferIter,
37     vgpu::VgpuState, //
38 };
39 
40 /// The `GspSetSystemInfo` command.
41 pub(crate) struct SetSystemInfo<'a> {
42     pdev: &'a pci::Device<device::Bound>,
43     chipset: Chipset,
44 }
45 
46 impl<'a> SetSystemInfo<'a> {
47     /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
48     pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Self {
49         Self { pdev, chipset }
50     }
51 }
52 
53 impl<'a> CommandToGsp for SetSystemInfo<'a> {
54     const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
55     type Command = fw::commands::GspSetSystemInfo;
56     type Reply = NoReply;
57     type InitError = Error;
58 
59     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
60         Self::Command::init(self.pdev, self.chipset)
61     }
62 }
63 
64 struct RegistryEntry {
65     key: &'static str,
66     value: u32,
67 }
68 
69 /// The `SetRegistry` command.
70 pub(crate) struct SetRegistry {
71     entries: KVec<RegistryEntry>,
72 }
73 
74 impl SetRegistry {
75     /// Creates a new `SetRegistry` command, using a set of hardcoded entries.
76     pub(crate) fn new(vgpu_state: VgpuState) -> Result<Self> {
77         let mut entries = KVec::new();
78 
79         // RMSecBusResetEnable - enables PCI secondary bus reset
80         entries.push(
81             RegistryEntry {
82                 key: "RMSecBusResetEnable",
83                 value: 1,
84             },
85             GFP_KERNEL,
86         )?;
87 
88         // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on
89         // any PCI reset.
90         entries.push(
91             RegistryEntry {
92                 key: "RMForcePcieConfigSave",
93                 value: 1,
94             },
95             GFP_KERNEL,
96         )?;
97 
98         // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found
99         // in the internal product name database.
100         entries.push(
101             RegistryEntry {
102                 key: "RMDevidCheckIgnore",
103                 value: 1,
104             },
105             GFP_KERNEL,
106         )?;
107 
108         if matches!(vgpu_state, VgpuState::Enabled { .. }) {
109             // RMSetSriovMode - required when vGPU is enabled.
110             entries.push(
111                 RegistryEntry {
112                     key: "RMSetSriovMode",
113                     value: 1,
114                 },
115                 GFP_KERNEL,
116             )?;
117         }
118 
119         Ok(Self { entries })
120     }
121 }
122 
123 impl CommandToGsp for SetRegistry {
124     const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
125     type Command = fw::commands::PackedRegistryTable;
126     type Reply = NoReply;
127     type InitError = Infallible;
128 
129     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
130         Self::Command::init(self.entries.len() as u32, self.size() as u32)
131     }
132 
133     fn variable_payload_len(&self) -> usize {
134         let mut key_size = 0;
135         for entry in self.entries.iter() {
136             key_size += entry.key.len() + 1; // +1 for NULL terminator
137         }
138         self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>() + key_size
139     }
140 
141     fn init_variable_payload(
142         &self,
143         dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
144     ) -> Result {
145         let string_data_start_offset = size_of::<Self::Command>()
146             + self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>();
147 
148         // Array for string data.
149         let mut string_data = KVec::new();
150 
151         for entry in self.entries.iter() {
152             dst.write_all(
153                 fw::commands::PackedRegistryEntry::new(
154                     (string_data_start_offset + string_data.len()) as u32,
155                     entry.value,
156                 )
157                 .as_bytes(),
158             )?;
159 
160             let key_bytes = entry.key.as_bytes();
161             string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
162             string_data.push(0, GFP_KERNEL)?;
163         }
164 
165         dst.write_all(string_data.as_slice())
166     }
167 }
168 
169 /// Message type for GSP initialization done notification.
170 struct GspInitDone;
171 
172 // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
173 // trivially has no uninitialized bytes.
174 unsafe impl FromBytes for GspInitDone {}
175 
176 impl MessageFromGsp for GspInitDone {
177     const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
178     type InitError = Infallible;
179     type Message = ();
180 
181     fn read(
182         _msg: &Self::Message,
183         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
184     ) -> Result<Self, Self::InitError> {
185         Ok(GspInitDone)
186     }
187 }
188 
189 /// Waits for GSP initialization to complete.
190 pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result {
191     loop {
192         match cmdq.receive_msg::<GspInitDone>(Cmdq::RECEIVE_TIMEOUT) {
193             Ok(_) => break Ok(()),
194             Err(ERANGE) => continue,
195             Err(e) => break Err(e),
196         }
197     }
198 }
199 
200 /// The `GetGspStaticInfo` command.
201 pub(crate) struct GetGspStaticInfo;
202 
203 impl CommandToGsp for GetGspStaticInfo {
204     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
205     type Command = fw::commands::GspStaticConfigInfo;
206     type Reply = GetGspStaticInfoReply;
207     type InitError = Infallible;
208 
209     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
210         Self::Command::init_zeroed()
211     }
212 }
213 
214 /// The reply from the GSP to the [`GetGspStaticInfo`] command.
215 pub(crate) struct GetGspStaticInfoReply {
216     gpu_name: [u8; 64],
217     /// Usable FB (VRAM) regions for driver memory allocation.
218     pub(crate) usable_fb_regions: KVec<Range<u64>>,
219 }
220 
221 impl MessageFromGsp for GetGspStaticInfoReply {
222     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
223     type Message = fw::commands::GspStaticConfigInfo;
224     type InitError = Error;
225 
226     fn read(
227         msg: &Self::Message,
228         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
229     ) -> Result<Self, Self::InitError> {
230         let mut usable_fb_regions = KVec::new();
231         for region in msg.usable_fb_regions() {
232             usable_fb_regions.push(region, GFP_KERNEL)?;
233         }
234 
235         Ok(GetGspStaticInfoReply {
236             gpu_name: msg.gpu_name_str(),
237             usable_fb_regions,
238         })
239     }
240 }
241 
242 /// Error type for [`GetGspStaticInfoReply::gpu_name`].
243 #[derive(Debug)]
244 pub(crate) enum GpuNameError {
245     /// The GPU name string does not contain a null terminator.
246     NoNullTerminator(FromBytesUntilNulError),
247 
248     /// The GPU name string contains invalid UTF-8.
249     #[expect(dead_code)]
250     InvalidUtf8(Utf8Error),
251 }
252 
253 impl GetGspStaticInfoReply {
254     /// Returns the name of the GPU as a string.
255     ///
256     /// Returns an error if the string given by the GSP does not contain a null terminator or
257     /// contains invalid UTF-8.
258     pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
259         CStr::from_bytes_until_nul(&self.gpu_name)
260             .map_err(GpuNameError::NoNullTerminator)?
261             .to_str()
262             .map_err(GpuNameError::InvalidUtf8)
263     }
264 }
265 
266 pub(crate) use fw::commands::PowerStateLevel;
267 
268 /// The `UnloadingGuestDriver` command, used to shut down the GSP.
269 ///
270 /// Only used within the `gsp` module.
271 pub(super) struct UnloadingGuestDriver {
272     level: PowerStateLevel,
273 }
274 
275 impl UnloadingGuestDriver {
276     /// Creates a new `UnloadingGuestDriver` command for the given [`PowerStateLevel`].
277     pub(super) fn new(level: PowerStateLevel) -> Self {
278         Self { level }
279     }
280 }
281 
282 impl CommandToGsp for UnloadingGuestDriver {
283     const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
284     type Command = fw::commands::UnloadingGuestDriver;
285     type Reply = UnloadingGuestDriverReply;
286     type InitError = Infallible;
287 
288     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
289         fw::commands::UnloadingGuestDriver::new(self.level)
290     }
291 }
292 
293 /// The reply from the GSP to the [`UnloadingGuestDriver`] command.
294 pub(super) struct UnloadingGuestDriverReply;
295 
296 impl MessageFromGsp for UnloadingGuestDriverReply {
297     const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
298     type InitError = Infallible;
299     type Message = ();
300 
301     fn read(
302         _msg: &Self::Message,
303         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
304     ) -> Result<Self, Self::InitError> {
305         Ok(UnloadingGuestDriverReply)
306     }
307 }
308