linux/drivers/gpu/nova-core/gsp/fw.rs

929 lines
35 KiB
Rust

// SPDX-License-Identifier: GPL-2.0
pub(crate) mod commands;
mod r570_144;
// Alias to avoid repeating the version number with every use.
use r570_144 as bindings;
use core::ops::Range;
use kernel::{
dma::CoherentAllocation,
fmt,
prelude::*,
ptr::{
Alignable,
Alignment, //
},
sizes::{
SZ_128K,
SZ_1M, //
},
transmute::{
AsBytes,
FromBytes, //
},
};
use crate::{
fb::FbLayout,
firmware::gsp::GspFirmware,
gpu::Chipset,
gsp::{
cmdq::Cmdq, //
GSP_PAGE_SIZE,
},
num::{
self,
FromSafeCast, //
},
};
/// Empty type to group methods related to heap parameters for running the GSP firmware.
enum GspFwHeapParams {}
/// Minimum required alignment for the GSP heap.
const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
impl GspFwHeapParams {
/// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
/// and including the first client subdevice allocation).
fn base_rm_size(_chipset: Chipset) -> u64 {
// TODO: this needs to be updated to return the correct value for Hopper+ once support for
// them is added:
// u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
}
/// Returns the amount of heap memory required to support a single channel allocation.
fn client_alloc_size() -> u64 {
u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
.align_up(GSP_HEAP_ALIGNMENT)
.unwrap_or(u64::MAX)
}
/// Returns the amount of memory to reserve for management purposes for a framebuffer of size
/// `fb_size`.
fn management_overhead(fb_size: u64) -> u64 {
let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G));
u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB)
.saturating_mul(fb_size_gb)
.align_up(GSP_HEAP_ALIGNMENT)
.unwrap_or(u64::MAX)
}
}
/// Heap memory requirements and constraints for a given version of the GSP LIBOS.
pub(crate) struct LibosParams {
/// The base amount of heap required by the GSP operating system, in bytes.
carveout_size: u64,
/// The minimum and maximum sizes allowed for the GSP FW heap, in bytes.
allowed_heap_size: Range<u64>,
}
impl LibosParams {
/// Version 2 of the GSP LIBOS (Turing and GA100)
const LIBOS2: LibosParams = LibosParams {
carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
* num::usize_as_u64(SZ_1M)
..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB)
* num::usize_as_u64(SZ_1M),
};
/// Version 3 of the GSP LIBOS (GA102+)
const LIBOS3: LibosParams = LibosParams {
carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
allowed_heap_size: num::u32_as_u64(
bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
) * num::usize_as_u64(SZ_1M)
..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
* num::usize_as_u64(SZ_1M),
};
/// Returns the libos parameters corresponding to `chipset`.
pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
if chipset < Chipset::GA102 {
&Self::LIBOS2
} else {
&Self::LIBOS3
}
}
/// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
/// of `fb_size` (in bytes) for `chipset`.
pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
// The WPR heap will contain the following:
// LIBOS carveout,
self.carveout_size
// RM boot working memory,
.saturating_add(GspFwHeapParams::base_rm_size(chipset))
// One RM client,
.saturating_add(GspFwHeapParams::client_alloc_size())
// Overhead for memory management.
.saturating_add(GspFwHeapParams::management_overhead(fb_size))
// Clamp to the supported heap sizes.
.clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1)
}
}
/// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
/// addresses of the GSP bootloader and firmware.
#[repr(transparent)]
pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for GspFwWprMeta {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for GspFwWprMeta {}
type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1;
impl GspFwWprMeta {
/// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the
/// `fb_layout` layout.
pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self {
Self(bindings::GspFwWprMeta {
// CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified.
magic: r570_144::GSP_FW_WPR_META_MAGIC as u64,
revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION),
sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(),
sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(),
sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset),
bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset),
__bindgen_anon_1: GspFwWprMetaBootResumeInfo {
__bindgen_anon_1: GspFwWprMetaBootInfo {
sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(),
sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()),
},
},
gspFwRsvdStart: fb_layout.heap.start,
nonWprHeapOffset: fb_layout.heap.start,
nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
gspFwWprStart: fb_layout.wpr2.start,
gspFwHeapOffset: fb_layout.wpr2_heap.start,
gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
gspFwOffset: fb_layout.elf.start,
bootBinOffset: fb_layout.boot.start,
frtsOffset: fb_layout.frts.start,
frtsSize: fb_layout.frts.end - fb_layout.frts.start,
gspFwWprEnd: fb_layout
.vga_workspace
.start
.align_down(Alignment::new::<SZ_128K>()),
gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
fbSize: fb_layout.fb.end - fb_layout.fb.start,
vgaWorkspaceOffset: fb_layout.vga_workspace.start,
vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
..Default::default()
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u32)]
pub(crate) enum MsgFunction {
// Common function codes
Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP,
SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT,
AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY,
AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA,
AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY,
BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA,
AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
Free = bindings::NV_VGPU_MSG_FUNCTION_FREE,
Log = bindings::NV_VGPU_MSG_FUNCTION_LOG,
GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU,
GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
// Event codes
GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT,
RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED,
MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD,
GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
}
impl fmt::Display for MsgFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// Common function codes
MsgFunction::Nop => write!(f, "NOP"),
MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"),
MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"),
MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"),
MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"),
MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"),
MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"),
MsgFunction::MapMemory => write!(f, "MAP_MEMORY"),
MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"),
MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"),
MsgFunction::Free => write!(f, "FREE"),
MsgFunction::Log => write!(f, "LOG"),
MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"),
MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"),
MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"),
MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"),
MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"),
MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"),
// Event codes
MsgFunction::GspInitDone => write!(f, "INIT_DONE"),
MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"),
MsgFunction::PostEvent => write!(f, "POST_EVENT"),
MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"),
MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"),
MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"),
MsgFunction::GspPostNoCat => write!(f, "NOCAT"),
MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"),
MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"),
}
}
}
impl TryFrom<u32> for MsgFunction {
type Error = kernel::error::Error;
fn try_from(value: u32) -> Result<MsgFunction> {
match value {
bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop),
bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => {
Ok(MsgFunction::SetGuestSystemInfo)
}
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot),
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice),
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory),
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma),
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma),
bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory),
bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma),
bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject),
bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free),
bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log),
bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo),
bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry),
bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo),
bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => {
Ok(MsgFunction::GspInitPostObjGpu)
}
bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl),
bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo),
bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone),
bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => {
Ok(MsgFunction::GspRunCpuSequencer)
}
bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent),
bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered),
bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued),
bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog),
bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat),
bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice),
bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint),
_ => Err(EINVAL),
}
}
}
impl From<MsgFunction> for u32 {
fn from(value: MsgFunction) -> Self {
// CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly.
value as u32
}
}
/// Sequencer buffer opcode for GSP sequencer commands.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u32)]
pub(crate) enum SeqBufOpcode {
// Core operation opcodes
CoreReset = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET,
CoreResume = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME,
CoreStart = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START,
CoreWaitForHalt = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT,
// Delay opcode
DelayUs = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US,
// Register operation opcodes
RegModify = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY,
RegPoll = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL,
RegStore = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE,
RegWrite = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE,
}
impl fmt::Display for SeqBufOpcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SeqBufOpcode::CoreReset => write!(f, "CORE_RESET"),
SeqBufOpcode::CoreResume => write!(f, "CORE_RESUME"),
SeqBufOpcode::CoreStart => write!(f, "CORE_START"),
SeqBufOpcode::CoreWaitForHalt => write!(f, "CORE_WAIT_FOR_HALT"),
SeqBufOpcode::DelayUs => write!(f, "DELAY_US"),
SeqBufOpcode::RegModify => write!(f, "REG_MODIFY"),
SeqBufOpcode::RegPoll => write!(f, "REG_POLL"),
SeqBufOpcode::RegStore => write!(f, "REG_STORE"),
SeqBufOpcode::RegWrite => write!(f, "REG_WRITE"),
}
}
}
impl TryFrom<u32> for SeqBufOpcode {
type Error = kernel::error::Error;
fn try_from(value: u32) -> Result<SeqBufOpcode> {
match value {
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET => {
Ok(SeqBufOpcode::CoreReset)
}
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME => {
Ok(SeqBufOpcode::CoreResume)
}
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START => {
Ok(SeqBufOpcode::CoreStart)
}
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT => {
Ok(SeqBufOpcode::CoreWaitForHalt)
}
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => Ok(SeqBufOpcode::DelayUs),
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => {
Ok(SeqBufOpcode::RegModify)
}
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => Ok(SeqBufOpcode::RegPoll),
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => Ok(SeqBufOpcode::RegStore),
r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => Ok(SeqBufOpcode::RegWrite),
_ => Err(EINVAL),
}
}
}
impl From<SeqBufOpcode> for u32 {
fn from(value: SeqBufOpcode) -> Self {
// CAST: `SeqBufOpcode` is `repr(u32)` and can thus be cast losslessly.
value as u32
}
}
/// Wrapper for GSP sequencer register write payload.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub(crate) struct RegWritePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_WRITE);
impl RegWritePayload {
/// Returns the register address.
pub(crate) fn addr(&self) -> u32 {
self.0.addr
}
/// Returns the value to write.
pub(crate) fn val(&self) -> u32 {
self.0.val
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for RegWritePayload {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for RegWritePayload {}
/// Wrapper for GSP sequencer register modify payload.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub(crate) struct RegModifyPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY);
impl RegModifyPayload {
/// Returns the register address.
pub(crate) fn addr(&self) -> u32 {
self.0.addr
}
/// Returns the mask to apply.
pub(crate) fn mask(&self) -> u32 {
self.0.mask
}
/// Returns the value to write.
pub(crate) fn val(&self) -> u32 {
self.0.val
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for RegModifyPayload {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for RegModifyPayload {}
/// Wrapper for GSP sequencer register poll payload.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub(crate) struct RegPollPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_POLL);
impl RegPollPayload {
/// Returns the register address.
pub(crate) fn addr(&self) -> u32 {
self.0.addr
}
/// Returns the mask to apply.
pub(crate) fn mask(&self) -> u32 {
self.0.mask
}
/// Returns the expected value.
pub(crate) fn val(&self) -> u32 {
self.0.val
}
/// Returns the timeout in microseconds.
pub(crate) fn timeout(&self) -> u32 {
self.0.timeout
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for RegPollPayload {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for RegPollPayload {}
/// Wrapper for GSP sequencer delay payload.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub(crate) struct DelayUsPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_DELAY_US);
impl DelayUsPayload {
/// Returns the delay value in microseconds.
pub(crate) fn val(&self) -> u32 {
self.0.val
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for DelayUsPayload {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for DelayUsPayload {}
/// Wrapper for GSP sequencer register store payload.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub(crate) struct RegStorePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_STORE);
impl RegStorePayload {
/// Returns the register address.
pub(crate) fn addr(&self) -> u32 {
self.0.addr
}
/// Returns the storage index.
#[allow(unused)]
pub(crate) fn index(&self) -> u32 {
self.0.index
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for RegStorePayload {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for RegStorePayload {}
/// Wrapper for GSP sequencer buffer command.
#[repr(transparent)]
pub(crate) struct SequencerBufferCmd(r570_144::GSP_SEQUENCER_BUFFER_CMD);
impl SequencerBufferCmd {
/// Returns the opcode as a `SeqBufOpcode` enum, or error if invalid.
pub(crate) fn opcode(&self) -> Result<SeqBufOpcode> {
self.0.opCode.try_into()
}
/// Returns the register write payload by value.
///
/// Returns an error if the opcode is not `SeqBufOpcode::RegWrite`.
pub(crate) fn reg_write_payload(&self) -> Result<RegWritePayload> {
if self.opcode()? != SeqBufOpcode::RegWrite {
return Err(EINVAL);
}
// SAFETY: Opcode is verified to be `RegWrite`, so union contains valid `RegWritePayload`.
let payload_bytes = unsafe {
core::slice::from_raw_parts(
core::ptr::addr_of!(self.0.payload.regWrite).cast::<u8>(),
core::mem::size_of::<RegWritePayload>(),
)
};
Ok(*RegWritePayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
}
/// Returns the register modify payload by value.
///
/// Returns an error if the opcode is not `SeqBufOpcode::RegModify`.
pub(crate) fn reg_modify_payload(&self) -> Result<RegModifyPayload> {
if self.opcode()? != SeqBufOpcode::RegModify {
return Err(EINVAL);
}
// SAFETY: Opcode is verified to be `RegModify`, so union contains valid `RegModifyPayload`.
let payload_bytes = unsafe {
core::slice::from_raw_parts(
core::ptr::addr_of!(self.0.payload.regModify).cast::<u8>(),
core::mem::size_of::<RegModifyPayload>(),
)
};
Ok(*RegModifyPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
}
/// Returns the register poll payload by value.
///
/// Returns an error if the opcode is not `SeqBufOpcode::RegPoll`.
pub(crate) fn reg_poll_payload(&self) -> Result<RegPollPayload> {
if self.opcode()? != SeqBufOpcode::RegPoll {
return Err(EINVAL);
}
// SAFETY: Opcode is verified to be `RegPoll`, so union contains valid `RegPollPayload`.
let payload_bytes = unsafe {
core::slice::from_raw_parts(
core::ptr::addr_of!(self.0.payload.regPoll).cast::<u8>(),
core::mem::size_of::<RegPollPayload>(),
)
};
Ok(*RegPollPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
}
/// Returns the delay payload by value.
///
/// Returns an error if the opcode is not `SeqBufOpcode::DelayUs`.
pub(crate) fn delay_us_payload(&self) -> Result<DelayUsPayload> {
if self.opcode()? != SeqBufOpcode::DelayUs {
return Err(EINVAL);
}
// SAFETY: Opcode is verified to be `DelayUs`, so union contains valid `DelayUsPayload`.
let payload_bytes = unsafe {
core::slice::from_raw_parts(
core::ptr::addr_of!(self.0.payload.delayUs).cast::<u8>(),
core::mem::size_of::<DelayUsPayload>(),
)
};
Ok(*DelayUsPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
}
/// Returns the register store payload by value.
///
/// Returns an error if the opcode is not `SeqBufOpcode::RegStore`.
pub(crate) fn reg_store_payload(&self) -> Result<RegStorePayload> {
if self.opcode()? != SeqBufOpcode::RegStore {
return Err(EINVAL);
}
// SAFETY: Opcode is verified to be `RegStore`, so union contains valid `RegStorePayload`.
let payload_bytes = unsafe {
core::slice::from_raw_parts(
core::ptr::addr_of!(self.0.payload.regStore).cast::<u8>(),
core::mem::size_of::<RegStorePayload>(),
)
};
Ok(*RegStorePayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for SequencerBufferCmd {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for SequencerBufferCmd {}
/// Wrapper for GSP run CPU sequencer RPC.
#[repr(transparent)]
pub(crate) struct RunCpuSequencer(r570_144::rpc_run_cpu_sequencer_v17_00);
impl RunCpuSequencer {
/// Returns the command index.
pub(crate) fn cmd_index(&self) -> u32 {
self.0.cmdIndex
}
}
// SAFETY: This struct only contains integer types for which all bit patterns are valid.
unsafe impl FromBytes for RunCpuSequencer {}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for RunCpuSequencer {}
/// Struct containing the arguments required to pass a memory buffer to the GSP
/// for use during initialisation.
///
/// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
/// configured for a larger page size (e.g. 64K pages), we need to give
/// the GSP an array of 4K pages. Since we only create physically contiguous
/// buffers the math to calculate the addresses is simple.
///
/// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently
/// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
/// buffers to be physically contiguous anyway.
///
/// The memory allocated for the arguments must remain until the GSP sends the
/// init_done RPC.
#[repr(transparent)]
pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument);
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
impl LibosMemoryRegionInitArgument {
pub(crate) fn new<A: AsBytes + FromBytes>(
name: &'static str,
obj: &CoherentAllocation<A>,
) -> Self {
/// Generates the `ID8` identifier required for some GSP objects.
fn id8(name: &str) -> u64 {
let mut bytes = [0u8; core::mem::size_of::<u64>()];
for (c, b) in name.bytes().rev().zip(&mut bytes) {
*b = c;
}
u64::from_ne_bytes(bytes)
}
Self(bindings::LibosMemoryRegionInitArgument {
id8: id8(name),
pa: obj.dma_handle(),
size: num::usize_as_u64(obj.size()),
kind: num::u32_into_u8::<
{ bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
>(),
loc: num::u32_into_u8::<
{ bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
>(),
..Default::default()
})
}
}
/// TX header for setting up a message queue with the GSP.
#[repr(transparent)]
pub(crate) struct MsgqTxHeader(bindings::msgqTxHeader);
impl MsgqTxHeader {
/// Create a new TX queue header.
///
/// # Arguments
///
/// * `msgq_size` - Total size of the message queue structure, in bytes.
/// * `rx_hdr_offset` - Offset, in bytes, of the start of the RX header in the message queue
/// structure.
/// * `msg_count` - Number of messages that can be sent, i.e. the number of memory pages
/// allocated for the message queue in the message queue structure.
pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self {
Self(bindings::msgqTxHeader {
version: 0,
size: msgq_size,
msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(),
msgCount: msg_count,
writePtr: 0,
flags: 1,
rxHdrOff: rx_hdr_offset,
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
})
}
/// Returns the value of the write pointer for this queue.
pub(crate) fn write_ptr(&self) -> u32 {
let ptr = core::ptr::from_ref(&self.0.writePtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.read_volatile() }
}
/// Sets the value of the write pointer for this queue.
pub(crate) fn set_write_ptr(&mut self, val: u32) {
let ptr = core::ptr::from_mut(&mut self.0.writePtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.write_volatile(val) }
}
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for MsgqTxHeader {}
/// RX header for setting up a message queue with the GSP.
#[repr(transparent)]
pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader);
/// Header for the message RX queue.
impl MsgqRxHeader {
/// Creates a new RX queue header.
pub(crate) fn new() -> Self {
Self(Default::default())
}
/// Returns the value of the read pointer for this queue.
pub(crate) fn read_ptr(&self) -> u32 {
let ptr = core::ptr::from_ref(&self.0.readPtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.read_volatile() }
}
/// Sets the value of the read pointer for this queue.
pub(crate) fn set_read_ptr(&mut self, val: u32) {
let ptr = core::ptr::from_mut(&mut self.0.readPtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.write_volatile(val) }
}
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for MsgqRxHeader {}
bitfield! {
struct MsgHeaderVersion(u32) {
31:24 major as u8;
23:16 minor as u8;
}
}
impl MsgHeaderVersion {
const MAJOR_TOT: u8 = 3;
const MINOR_TOT: u8 = 0;
fn new() -> Self {
Self::default()
.set_major(Self::MAJOR_TOT)
.set_minor(Self::MINOR_TOT)
}
}
impl bindings::rpc_message_header_v {
fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> {
type RpcMessageHeader = bindings::rpc_message_header_v;
try_init!(RpcMessageHeader {
header_version: MsgHeaderVersion::new().into(),
signature: bindings::NV_VGPU_MSG_SIGNATURE_VALID,
function: function.into(),
length: size_of::<Self>()
.checked_add(cmd_size)
.ok_or(EOVERFLOW)
.and_then(|v| v.try_into().map_err(|_| EINVAL))?,
rpc_result: 0xffffffff,
rpc_result_private: 0xffffffff,
..Zeroable::init_zeroed()
})
}
}
// SAFETY: We can't derive the Zeroable trait for this binding because the
// procedural macro doesn't support the syntax used by bindgen to create the
// __IncompleteArrayField types. So instead we implement it here, which is safe
// because these are explicitly padded structures only containing types for
// which any bit pattern, including all zeros, is valid.
unsafe impl Zeroable for bindings::rpc_message_header_v {}
/// GSP Message Element.
///
/// This is essentially a message header expected to be followed by the message data.
#[repr(transparent)]
pub(crate) struct GspMsgElement {
inner: bindings::GSP_MSG_QUEUE_ELEMENT,
}
impl GspMsgElement {
/// Creates a new message element.
///
/// # Arguments
///
/// * `sequence` - Sequence number of the message.
/// * `cmd_size` - Size of the command (not including the message element), in bytes.
/// * `function` - Function of the message.
#[allow(non_snake_case)]
pub(crate) fn init(
sequence: u32,
cmd_size: usize,
function: MsgFunction,
) -> impl Init<Self, Error> {
type RpcMessageHeader = bindings::rpc_message_header_v;
type InnerGspMsgElement = bindings::GSP_MSG_QUEUE_ELEMENT;
let init_inner = try_init!(InnerGspMsgElement {
seqNum: sequence,
elemCount: size_of::<Self>()
.checked_add(cmd_size)
.ok_or(EOVERFLOW)?
.div_ceil(GSP_PAGE_SIZE)
.try_into()
.map_err(|_| EOVERFLOW)?,
rpc <- RpcMessageHeader::init(cmd_size, function),
..Zeroable::init_zeroed()
});
try_init!(GspMsgElement {
inner <- init_inner,
})
}
/// Sets the checksum of this message.
///
/// Since the header is also part of the checksum, this is usually called after the whole
/// message has been written to the shared memory area.
pub(crate) fn set_checksum(&mut self, checksum: u32) {
self.inner.checkSum = checksum;
}
/// Returns the total length of the message.
pub(crate) fn length(&self) -> usize {
// `rpc.length` includes the length of the GspRpcHeader but not the message header.
size_of::<Self>() - size_of::<bindings::rpc_message_header_v>()
+ num::u32_as_usize(self.inner.rpc.length)
}
// Returns the sequence number of the message.
pub(crate) fn sequence(&self) -> u32 {
self.inner.rpc.sequence
}
// Returns the function of the message, if it is valid, or the invalid function number as an
// error.
pub(crate) fn function(&self) -> Result<MsgFunction, u32> {
self.inner
.rpc
.function
.try_into()
.map_err(|_| self.inner.rpc.function)
}
// Returns the number of elements (i.e. memory pages) used by this message.
pub(crate) fn element_count(&self) -> u32 {
self.inner.elemCount
}
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
unsafe impl AsBytes for GspMsgElement {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for GspMsgElement {}
/// Arguments for GSP startup.
#[repr(transparent)]
pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED);
impl GspArgumentsCached {
/// Creates the arguments for starting the GSP up using `cmdq` as its command queue.
pub(crate) fn new(cmdq: &Cmdq) -> Self {
Self(bindings::GSP_ARGUMENTS_CACHED {
messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0,
bDmemStack: 1,
..Default::default()
})
}
}
// SAFETY: Padding is explicit and will not contain uninitialized data.
unsafe impl AsBytes for GspArgumentsCached {}
// SAFETY: This struct only contains integer types for which all bit patterns
// are valid.
unsafe impl FromBytes for GspArgumentsCached {}
/// Init arguments for the message queue.
#[repr(transparent)]
struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS);
impl MessageQueueInitArguments {
/// Creates a new init arguments structure for `cmdq`.
fn new(cmdq: &Cmdq) -> Self {
Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS {
sharedMemPhysAddr: cmdq.dma_handle(),
pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(),
cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET),
statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET),
..Default::default()
})
}
}