Files
linux-stable-mirror/drivers/gpu/nova-core/gsp/commands.rs
T
John HubbardandAlexandre Courbot ee9414e605 gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror
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>
2026-06-02 22:33:15 +09:00

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)
}
}