xref: /linux/drivers/gpu/nova-core/gsp/commands.rs (revision 6dcbb4b1320fa91fee349462a52bb69135f2e45e)
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(
131             self.entries.len() as u32,
132             self.variable_payload_len() as u32,
133         )
134     }
135 
136     fn variable_payload_len(&self) -> usize {
137         let mut key_size = 0;
138         for entry in self.entries.iter() {
139             key_size += entry.key.len() + 1; // +1 for NULL terminator
140         }
141         self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>() + key_size
142     }
143 
144     fn init_variable_payload(
145         &self,
146         dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
147     ) -> Result {
148         let string_data_start_offset = size_of::<Self::Command>()
149             + self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>();
150 
151         // Array for string data.
152         let mut string_data = KVec::new();
153 
154         for entry in self.entries.iter() {
155             dst.write_all(
156                 fw::commands::PackedRegistryEntry::new(
157                     (string_data_start_offset + string_data.len()) as u32,
158                     entry.value,
159                 )
160                 .as_bytes(),
161             )?;
162 
163             let key_bytes = entry.key.as_bytes();
164             string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
165             string_data.push(0, GFP_KERNEL)?;
166         }
167 
168         dst.write_all(string_data.as_slice())
169     }
170 }
171 
172 /// Message type for GSP initialization done notification.
173 struct GspInitDone;
174 
175 // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
176 // trivially has no uninitialized bytes.
177 unsafe impl FromBytes for GspInitDone {}
178 
179 impl MessageFromGsp for GspInitDone {
180     const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
181     type InitError = Infallible;
182     type Message = ();
183 
184     fn read(
185         _msg: &Self::Message,
186         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
187     ) -> Result<Self, Self::InitError> {
188         Ok(GspInitDone)
189     }
190 }
191 
192 /// Waits for GSP initialization to complete.
193 pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result {
194     loop {
195         match cmdq.receive_msg::<GspInitDone>(Cmdq::RECEIVE_TIMEOUT) {
196             Ok(_) => break Ok(()),
197             Err(ERANGE) => continue,
198             Err(e) => break Err(e),
199         }
200     }
201 }
202 
203 /// The `GetGspStaticInfo` command.
204 pub(crate) struct GetGspStaticInfo;
205 
206 impl CommandToGsp for GetGspStaticInfo {
207     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
208     type Command = fw::commands::GspStaticConfigInfo;
209     type Reply = GetGspStaticInfoReply;
210     type InitError = Infallible;
211 
212     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
213         Self::Command::init_zeroed()
214     }
215 }
216 
217 /// The reply from the GSP to the [`GetGspStaticInfo`] command.
218 pub(crate) struct GetGspStaticInfoReply {
219     gpu_name: [u8; 64],
220     /// Usable FB (VRAM) regions for driver memory allocation.
221     pub(crate) usable_fb_regions: KVec<Range<u64>>,
222 }
223 
224 impl MessageFromGsp for GetGspStaticInfoReply {
225     const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
226     type Message = fw::commands::GspStaticConfigInfo;
227     type InitError = Error;
228 
229     fn read(
230         msg: &Self::Message,
231         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
232     ) -> Result<Self, Self::InitError> {
233         let mut usable_fb_regions = KVec::new();
234         for region in msg.usable_fb_regions() {
235             usable_fb_regions.push(region, GFP_KERNEL)?;
236         }
237 
238         Ok(GetGspStaticInfoReply {
239             gpu_name: msg.gpu_name_str(),
240             usable_fb_regions,
241         })
242     }
243 }
244 
245 /// Error type for [`GetGspStaticInfoReply::gpu_name`].
246 #[derive(Debug)]
247 pub(crate) enum GpuNameError {
248     /// The GPU name string does not contain a null terminator.
249     NoNullTerminator(FromBytesUntilNulError),
250 
251     /// The GPU name string contains invalid UTF-8.
252     #[expect(dead_code)]
253     InvalidUtf8(Utf8Error),
254 }
255 
256 impl GetGspStaticInfoReply {
257     /// Returns the name of the GPU as a string.
258     ///
259     /// Returns an error if the string given by the GSP does not contain a null terminator or
260     /// contains invalid UTF-8.
261     pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
262         CStr::from_bytes_until_nul(&self.gpu_name)
263             .map_err(GpuNameError::NoNullTerminator)?
264             .to_str()
265             .map_err(GpuNameError::InvalidUtf8)
266     }
267 }
268 
269 pub(crate) use fw::commands::PowerStateLevel;
270 
271 /// The `UnloadingGuestDriver` command, used to shut down the GSP.
272 ///
273 /// Only used within the `gsp` module.
274 pub(super) struct UnloadingGuestDriver {
275     level: PowerStateLevel,
276 }
277 
278 impl UnloadingGuestDriver {
279     /// Creates a new `UnloadingGuestDriver` command for the given [`PowerStateLevel`].
280     pub(super) fn new(level: PowerStateLevel) -> Self {
281         Self { level }
282     }
283 }
284 
285 impl CommandToGsp for UnloadingGuestDriver {
286     const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
287     type Command = fw::commands::UnloadingGuestDriver;
288     type Reply = UnloadingGuestDriverReply;
289     type InitError = Infallible;
290 
291     fn init(&self) -> impl Init<Self::Command, Self::InitError> {
292         fw::commands::UnloadingGuestDriver::new(self.level)
293     }
294 }
295 
296 /// The reply from the GSP to the [`UnloadingGuestDriver`] command.
297 pub(super) struct UnloadingGuestDriverReply;
298 
299 impl MessageFromGsp for UnloadingGuestDriverReply {
300     const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
301     type InitError = Infallible;
302     type Message = ();
303 
304     fn read(
305         _msg: &Self::Message,
306         _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
307     ) -> Result<Self, Self::InitError> {
308         Ok(UnloadingGuestDriverReply)
309     }
310 }
311