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