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