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