// SPDX-License-Identifier: GPL-2.0 use core::{ array, convert::Infallible, // }; use kernel::{ device, pci, prelude::*, time::Delta, transmute::{ AsBytes, FromBytes, // }, // }; use crate::{ driver::Bar0, gsp::{ cmdq::{ Cmdq, CommandToGsp, MessageFromGsp, // }, fw::{ commands::*, MsgFunction, // }, }, sbuffer::SBufferIter, util, }; /// The `GspSetSystemInfo` command. pub(crate) struct SetSystemInfo<'a> { pdev: &'a pci::Device, } impl<'a> SetSystemInfo<'a> { /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`. pub(crate) fn new(pdev: &'a pci::Device) -> Self { Self { pdev } } } impl<'a> CommandToGsp for SetSystemInfo<'a> { const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo; type Command = GspSetSystemInfo; type InitError = Error; fn init(&self) -> impl Init { GspSetSystemInfo::init(self.pdev) } } struct RegistryEntry { key: &'static str, value: u32, } /// The `SetRegistry` command. pub(crate) struct SetRegistry { entries: [RegistryEntry; Self::NUM_ENTRIES], } impl SetRegistry { // For now we hard-code the registry entries. Future work will allow others to // be added as module parameters. const NUM_ENTRIES: usize = 3; /// Creates a new `SetRegistry` command, using a set of hardcoded entries. pub(crate) fn new() -> Self { Self { entries: [ // RMSecBusResetEnable - enables PCI secondary bus reset RegistryEntry { key: "RMSecBusResetEnable", value: 1, }, // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on // any PCI reset. RegistryEntry { key: "RMForcePcieConfigSave", value: 1, }, // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found // in the internal product name database. RegistryEntry { key: "RMDevidCheckIgnore", value: 1, }, ], } } } impl CommandToGsp for SetRegistry { const FUNCTION: MsgFunction = MsgFunction::SetRegistry; type Command = PackedRegistryTable; type InitError = Infallible; fn init(&self) -> impl Init { PackedRegistryTable::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32) } fn variable_payload_len(&self) -> usize { let mut key_size = 0; for i in 0..Self::NUM_ENTRIES { key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator } Self::NUM_ENTRIES * size_of::() + key_size } fn init_variable_payload( &self, dst: &mut SBufferIter>, ) -> Result { let string_data_start_offset = size_of::() + Self::NUM_ENTRIES * size_of::(); // Array for string data. let mut string_data = KVec::new(); for entry in self.entries.iter().take(Self::NUM_ENTRIES) { dst.write_all( PackedRegistryEntry::new( (string_data_start_offset + string_data.len()) as u32, entry.value, ) .as_bytes(), )?; let key_bytes = entry.key.as_bytes(); string_data.extend_from_slice(key_bytes, GFP_KERNEL)?; string_data.push(0, GFP_KERNEL)?; } dst.write_all(string_data.as_slice()) } } /// Message type for GSP initialization done notification. struct GspInitDone {} // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it // trivially has no uninitialized bytes. unsafe impl FromBytes for GspInitDone {} impl MessageFromGsp for GspInitDone { const FUNCTION: MsgFunction = MsgFunction::GspInitDone; type InitError = Infallible; type Message = GspInitDone; fn read( _msg: &Self::Message, _sbuffer: &mut SBufferIter>, ) -> Result { Ok(GspInitDone {}) } } /// Waits for GSP initialization to complete. pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { loop { match cmdq.receive_msg::(Delta::from_secs(10)) { Ok(_) => break Ok(()), Err(ERANGE) => continue, Err(e) => break Err(e), } } } /// The `GetGspStaticInfo` command. struct GetGspStaticInfo; impl CommandToGsp for GetGspStaticInfo { const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; type Command = GspStaticConfigInfo; type InitError = Infallible; fn init(&self) -> impl Init { GspStaticConfigInfo::init_zeroed() } } /// The reply from the GSP to the [`GetGspInfo`] command. pub(crate) struct GetGspStaticInfoReply { gpu_name: [u8; 64], } impl MessageFromGsp for GetGspStaticInfoReply { const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; type Message = GspStaticConfigInfo; type InitError = Infallible; fn read( msg: &Self::Message, _sbuffer: &mut SBufferIter>, ) -> Result { Ok(GetGspStaticInfoReply { gpu_name: msg.gpu_name_str(), }) } } impl GetGspStaticInfoReply { /// Returns the name of the GPU as a string, or `None` if the string given by the GSP was /// invalid. pub(crate) fn gpu_name(&self) -> Option<&str> { util::str_from_null_terminated(&self.gpu_name) } } /// Send the [`GetGspInfo`] command and awaits for its reply. pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result { cmdq.send_command(bar, GetGspStaticInfo)?; loop { match cmdq.receive_msg::(Delta::from_secs(5)) { Ok(info) => return Ok(info), Err(ERANGE) => continue, Err(e) => return Err(e), } } }