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