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