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