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