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