mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
gpu: nova-core: gsp: Add SetRegistry command
Add support for sending the SetRegistry command, which is critical to GSP initialization. The RM registry is serialized into a packed format and sent via the command queue. For now only three parameters which are required to boot GSP are hardcoded. In the future a kernel module parameter will be added to enable other parameters to be added. Signed-off-by: Alistair Popple <apopple@nvidia.com> [acourbot@nvidia.com: split into its own patch.] Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Message-ID: <20251110-gsp_boot-v9-12-8ae4058e3c0e@nvidia.com>
This commit is contained in:
parent
edcb134264
commit
19b0a6e7c2
|
|
@ -158,6 +158,7 @@ pub(crate) fn boot(
|
||||||
|
|
||||||
self.cmdq
|
self.cmdq
|
||||||
.send_command(bar, commands::SetSystemInfo::new(pdev))?;
|
.send_command(bar, commands::SetSystemInfo::new(pdev))?;
|
||||||
|
self.cmdq.send_command(bar, commands::SetRegistry::new())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
use kernel::{
|
use kernel::{
|
||||||
device,
|
device,
|
||||||
pci,
|
pci,
|
||||||
prelude::*, //
|
prelude::*,
|
||||||
|
transmute::AsBytes, //
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::gsp::{
|
use crate::{
|
||||||
cmdq::CommandToGsp,
|
gsp::{
|
||||||
fw::{
|
cmdq::CommandToGsp,
|
||||||
commands::GspSetSystemInfo,
|
fw::{
|
||||||
MsgFunction, //
|
commands::*,
|
||||||
|
MsgFunction, //
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
sbuffer::SBufferIter,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The `GspSetSystemInfo` command.
|
/// The `GspSetSystemInfo` command.
|
||||||
|
|
@ -35,3 +41,89 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
|
||||||
GspSetSystemInfo::init(self.pdev)
|
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<Self::Command, Self::InitError> {
|
||||||
|
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::<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::<PackedRegistryTable>() + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>();
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,53 @@ unsafe impl AsBytes for GspSetSystemInfo {}
|
||||||
// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
|
// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
|
||||||
// that is not a problem because they are not used outside the kernel.
|
// that is not a problem because they are not used outside the kernel.
|
||||||
unsafe impl FromBytes for GspSetSystemInfo {}
|
unsafe impl FromBytes for GspSetSystemInfo {}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY);
|
||||||
|
|
||||||
|
impl PackedRegistryEntry {
|
||||||
|
pub(crate) fn new(offset: u32, value: u32) -> Self {
|
||||||
|
Self({
|
||||||
|
bindings::PACKED_REGISTRY_ENTRY {
|
||||||
|
nameOffset: offset,
|
||||||
|
|
||||||
|
// We only support DWORD types for now. Support for other types
|
||||||
|
// will come later if required.
|
||||||
|
type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8,
|
||||||
|
__bindgen_padding_0: Default::default(),
|
||||||
|
data: value,
|
||||||
|
length: 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Padding is explicit and will not contain uninitialized data.
|
||||||
|
unsafe impl AsBytes for PackedRegistryEntry {}
|
||||||
|
|
||||||
|
/// Payload of the `SetRegistry` command.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct PackedRegistryTable {
|
||||||
|
inner: bindings::PACKED_REGISTRY_TABLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackedRegistryTable {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> {
|
||||||
|
type InnerPackedRegistryTable = bindings::PACKED_REGISTRY_TABLE;
|
||||||
|
let init_inner = init!(InnerPackedRegistryTable {
|
||||||
|
numEntries: num_entries,
|
||||||
|
size,
|
||||||
|
entries: Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
init!(PackedRegistryTable { inner <- init_inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Padding is explicit and will not contain uninitialized data.
|
||||||
|
unsafe impl AsBytes for PackedRegistryTable {}
|
||||||
|
|
||||||
|
// SAFETY: This struct only contains integer types for which all bit patterns
|
||||||
|
// are valid.
|
||||||
|
unsafe impl FromBytes for PackedRegistryTable {}
|
||||||
|
|
|
||||||
|
|
@ -649,6 +649,22 @@ pub struct LibosMemoryRegionInitArgument {
|
||||||
pub __bindgen_padding_0: [u8; 6usize],
|
pub __bindgen_padding_0: [u8; 6usize],
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
|
pub struct PACKED_REGISTRY_ENTRY {
|
||||||
|
pub nameOffset: u32_,
|
||||||
|
pub type_: u8_,
|
||||||
|
pub __bindgen_padding_0: [u8; 3usize],
|
||||||
|
pub data: u32_,
|
||||||
|
pub length: u32_,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PACKED_REGISTRY_TABLE {
|
||||||
|
pub size: u32_,
|
||||||
|
pub numEntries: u32_,
|
||||||
|
pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone, Zeroable)]
|
#[derive(Debug, Default, Copy, Clone, Zeroable)]
|
||||||
pub struct msgqTxHeader {
|
pub struct msgqTxHeader {
|
||||||
pub version: u32_,
|
pub version: u32_,
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,6 @@ fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> {
|
||||||
|
|
||||||
/// Ideally we would implement [`Write`], but it is not available in `core`.
|
/// Ideally we would implement [`Write`], but it is not available in `core`.
|
||||||
/// So mimic `std::io::Write::write_all`.
|
/// So mimic `std::io::Write::write_all`.
|
||||||
#[expect(unused)]
|
|
||||||
pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result {
|
pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result {
|
||||||
while !src.is_empty() {
|
while !src.is_empty() {
|
||||||
match self.get_slice_mut(src.len()) {
|
match self.get_slice_mut(src.len()) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user