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