mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
Hopper and Blackwell GPUs moved the PCI config space mirror from 0x088000 to 0x092000. Select the correct address per architecture when building the GSP system info command. Signed-off-by: John Hubbard <jhubbard@nvidia.com> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com> Link: https://patch.msgid.link/20260602032111.224790-3-jhubbard@nvidia.com Co-developed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
280 lines
8.0 KiB
Rust
280 lines
8.0 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
|
|
use core::{
|
|
array,
|
|
convert::Infallible,
|
|
ffi::FromBytesUntilNulError,
|
|
str::Utf8Error, //
|
|
};
|
|
|
|
use kernel::{
|
|
device,
|
|
pci,
|
|
prelude::*,
|
|
transmute::{
|
|
AsBytes,
|
|
FromBytes, //
|
|
}, //
|
|
};
|
|
|
|
use crate::{
|
|
gpu::Chipset,
|
|
gsp::{
|
|
cmdq::{
|
|
Cmdq,
|
|
CommandToGsp,
|
|
MessageFromGsp,
|
|
NoReply, //
|
|
},
|
|
fw::{
|
|
self,
|
|
MsgFunction, //
|
|
},
|
|
},
|
|
sbuffer::SBufferIter,
|
|
};
|
|
|
|
/// The `GspSetSystemInfo` command.
|
|
pub(crate) struct SetSystemInfo<'a> {
|
|
pdev: &'a pci::Device<device::Bound>,
|
|
chipset: Chipset,
|
|
}
|
|
|
|
impl<'a> SetSystemInfo<'a> {
|
|
/// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
|
|
pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Self {
|
|
Self { pdev, chipset }
|
|
}
|
|
}
|
|
|
|
impl<'a> CommandToGsp for SetSystemInfo<'a> {
|
|
const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
|
|
type Command = fw::commands::GspSetSystemInfo;
|
|
type Reply = NoReply;
|
|
type InitError = Error;
|
|
|
|
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
|
|
Self::Command::init(self.pdev, self.chipset)
|
|
}
|
|
}
|
|
|
|
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 = fw::commands::PackedRegistryTable;
|
|
type Reply = NoReply;
|
|
type InitError = Infallible;
|
|
|
|
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
|
|
Self::Command::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::<fw::commands::PackedRegistryEntry>() + key_size
|
|
}
|
|
|
|
fn init_variable_payload(
|
|
&self,
|
|
dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
|
|
) -> Result {
|
|
let string_data_start_offset = size_of::<Self::Command>()
|
|
+ Self::NUM_ENTRIES * size_of::<fw::commands::PackedRegistryEntry>();
|
|
|
|
// Array for string data.
|
|
let mut string_data = KVec::new();
|
|
|
|
for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
|
|
dst.write_all(
|
|
fw::commands::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 = ();
|
|
|
|
fn read(
|
|
_msg: &Self::Message,
|
|
_sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
|
|
) -> Result<Self, Self::InitError> {
|
|
Ok(GspInitDone)
|
|
}
|
|
}
|
|
|
|
/// Waits for GSP initialization to complete.
|
|
pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result {
|
|
loop {
|
|
match cmdq.receive_msg::<GspInitDone>(Cmdq::RECEIVE_TIMEOUT) {
|
|
Ok(_) => break Ok(()),
|
|
Err(ERANGE) => continue,
|
|
Err(e) => break Err(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The `GetGspStaticInfo` command.
|
|
pub(crate) struct GetGspStaticInfo;
|
|
|
|
impl CommandToGsp for GetGspStaticInfo {
|
|
const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
|
|
type Command = fw::commands::GspStaticConfigInfo;
|
|
type Reply = GetGspStaticInfoReply;
|
|
type InitError = Infallible;
|
|
|
|
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
|
|
Self::Command::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 = fw::commands::GspStaticConfigInfo;
|
|
type InitError = Infallible;
|
|
|
|
fn read(
|
|
msg: &Self::Message,
|
|
_sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
|
|
) -> Result<Self, Self::InitError> {
|
|
Ok(GetGspStaticInfoReply {
|
|
gpu_name: msg.gpu_name_str(),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Error type for [`GetGspStaticInfoReply::gpu_name`].
|
|
#[derive(Debug)]
|
|
pub(crate) enum GpuNameError {
|
|
/// The GPU name string does not contain a null terminator.
|
|
NoNullTerminator(FromBytesUntilNulError),
|
|
|
|
/// The GPU name string contains invalid UTF-8.
|
|
#[expect(dead_code)]
|
|
InvalidUtf8(Utf8Error),
|
|
}
|
|
|
|
impl GetGspStaticInfoReply {
|
|
/// Returns the name of the GPU as a string.
|
|
///
|
|
/// Returns an error if the string given by the GSP does not contain a null terminator or
|
|
/// contains invalid UTF-8.
|
|
pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> {
|
|
CStr::from_bytes_until_nul(&self.gpu_name)
|
|
.map_err(GpuNameError::NoNullTerminator)?
|
|
.to_str()
|
|
.map_err(GpuNameError::InvalidUtf8)
|
|
}
|
|
}
|
|
|
|
pub(crate) use fw::commands::PowerStateLevel;
|
|
|
|
/// The `UnloadingGuestDriver` command, used to shut down the GSP.
|
|
///
|
|
/// Only used within the `gsp` module.
|
|
pub(super) struct UnloadingGuestDriver {
|
|
level: PowerStateLevel,
|
|
}
|
|
|
|
impl UnloadingGuestDriver {
|
|
/// Creates a new `UnloadingGuestDriver` command for the given [`PowerStateLevel`].
|
|
pub(super) fn new(level: PowerStateLevel) -> Self {
|
|
Self { level }
|
|
}
|
|
}
|
|
|
|
impl CommandToGsp for UnloadingGuestDriver {
|
|
const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
|
|
type Command = fw::commands::UnloadingGuestDriver;
|
|
type Reply = UnloadingGuestDriverReply;
|
|
type InitError = Infallible;
|
|
|
|
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
|
|
fw::commands::UnloadingGuestDriver::new(self.level)
|
|
}
|
|
}
|
|
|
|
/// The reply from the GSP to the [`UnloadingGuestDriver`] command.
|
|
pub(super) struct UnloadingGuestDriverReply;
|
|
|
|
impl MessageFromGsp for UnloadingGuestDriverReply {
|
|
const FUNCTION: MsgFunction = MsgFunction::UnloadingGuestDriver;
|
|
type InitError = Infallible;
|
|
type Message = ();
|
|
|
|
fn read(
|
|
_msg: &Self::Message,
|
|
_sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
|
|
) -> Result<Self, Self::InitError> {
|
|
Ok(UnloadingGuestDriverReply)
|
|
}
|
|
}
|