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