From: Alistair Popple <apop...@nvidia.com>

Introduce the get_gsp_info() command. This is a basic command which is
sent to the GSP to get some information about the GPU. In particular
it gets the model number of the GPU which is printed to the kernel log.
This proves that the GSP is fully initialised and that the command queue
is functional.

It also serves to demonstrate how interactions and data from the GSP
will be handled. GSP specific data structures will not be used outside
of the GSP command parsing layer. Instead data will be converted to
Nova specific structures (in this case the GspStaticConfigInfo struct)
for consumption by the rest of the driver. This ensures the rest of the
driver is isolated from any changes that may occur to GSP interaction.

Signed-off-by: Alistair Popple <apop...@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs                  |   8 +-
 drivers/gpu/nova-core/gsp/commands.rs         |  49 ++++++
 drivers/gpu/nova-core/nvfw.rs                 |   2 +
 .../gpu/nova-core/nvfw/r570_144_bindings.rs   | 163 ++++++++++++++++++
 drivers/gpu/nova-core/util.rs                 |  15 ++
 5 files changed, 236 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 023bafc85f58..7aeecdb0dd28 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -10,7 +10,7 @@
 use crate::firmware::{Firmware, FIRMWARE_VERSION};
 use crate::gfw;
 use crate::gsp;
-use crate::gsp::commands::gsp_init_done;
+use crate::gsp::commands::{get_gsp_info, gsp_init_done};
 use crate::regs;
 use crate::util;
 use crate::vbios::Vbios;
@@ -377,6 +377,12 @@ pub(crate) fn new(
         )?;
 
         gsp_init_done(&mut libos.cmdq, Delta::from_secs(10))?;
+        let info = get_gsp_info(&mut libos.cmdq, bar)?;
+        dev_info!(
+            pdev.as_ref(),
+            "GPU name: {}\n",
+            util::str_from_null_terminated(&info.gpu_name)
+        );
 
         Ok(pin_init!(Self {
             spec,
diff --git a/drivers/gpu/nova-core/gsp/commands.rs 
b/drivers/gpu/nova-core/gsp/commands.rs
index 9f858aedf853..3491f8edef0a 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -19,6 +19,8 @@
     NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
     NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
     NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
+    NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
+    GspStaticConfigInfo_t,
     GspSystemInfo,
     PACKED_REGISTRY_TABLE,
     PACKED_REGISTRY_ENTRY,
@@ -34,6 +36,12 @@ unsafe impl AsBytes for GspSystemInfo {}
 //         that is not a problem because they are not used outside the kernel.
 unsafe impl FromBytes for GspSystemInfo {}
 
+unsafe impl FromBytes for GspStaticConfigInfo_t {}
+
+pub(crate) struct GspStaticConfigInfo {
+    pub gpu_name: [u8; 40],
+}
+
 struct GspInitDone {}
 impl GspMessageFromGsp for GspInitDone {
     const FUNCTION: u32 = NV_VGPU_MSG_EVENT_GSP_INIT_DONE;
@@ -62,6 +70,47 @@ pub(crate) fn gsp_init_done(cmdq: &mut GspCmdq, timeout: 
Delta) -> Result {
     }
 }
 
+impl GspMessageFromGsp for GspStaticConfigInfo_t {
+    const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO;
+}
+
+impl GspCommandToGsp for GspStaticConfigInfo_t {
+    const FUNCTION: u32 = NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO;
+}
+
+pub(crate) fn get_gsp_info(cmdq: &mut GspCmdq, bar: &Bar0) -> 
Result<GspStaticConfigInfo> {
+    let mut msg = 
cmdq.alloc_gsp_queue_command(size_of::<GspStaticConfigInfo_t>())?;
+    msg.try_as::<GspStaticConfigInfo_t>();
+    msg.send_to_gsp(bar)?;
+    cmdq.wait_for_msg_from_gsp(Delta::from_secs(5))?;
+    let msg = cmdq.receive_msg_from_gsp()?;
+    let info = msg.try_as::<GspStaticConfigInfo_t>().map(|(x, _)| x)?;
+
+    let gpu_name_str = info
+        .gpuNameString
+        .get(
+            0..=info
+                .gpuNameString
+                .iter()
+                .position(|&b| b == 0)
+                .unwrap_or(info.gpuNameString.len() - 1),
+        )
+        .and_then(|bytes| CStr::from_bytes_with_nul(bytes).ok())
+        .and_then(|cstr| cstr.to_str().ok())
+        .unwrap_or("invalid utf8");
+
+    let mut gpu_name = [0u8; 40];
+    let bytes = gpu_name_str.as_bytes();
+    let copy_len = core::cmp::min(bytes.len(), gpu_name.len());
+    gpu_name[..copy_len].copy_from_slice(&bytes[..copy_len]);
+    gpu_name[copy_len] = b'\0';
+
+    let config_info = GspStaticConfigInfo { gpu_name };
+
+    msg.ack()?;
+    Ok(config_info)
+}
+
 const GSP_REGISTRY_NUM_ENTRIES: usize = 2;
 struct RegistryEntry {
     key: &'static str,
diff --git a/drivers/gpu/nova-core/nvfw.rs b/drivers/gpu/nova-core/nvfw.rs
index 39e5f3d5b432..d51a697e6c27 100644
--- a/drivers/gpu/nova-core/nvfw.rs
+++ b/drivers/gpu/nova-core/nvfw.rs
@@ -43,6 +43,8 @@ pub(crate) struct LibosParams {
 
 pub(crate) use r570_144::{
     rpc_run_cpu_sequencer_v17_00,
+    GspStaticConfigInfo_t,
+
     // Core GSP structures
     GspSystemInfo,
 
diff --git a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs 
b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
index 607d99ac2221..4aaa381749ae 100644
--- a/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
+++ b/drivers/gpu/nova-core/nvfw/r570_144_bindings.rs
@@ -320,6 +320,77 @@ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> 
::core::fmt::Result {
 pub type _bindgen_ty_3 = ffi::c_uint;
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
+    pub totalVFs: u32_,
+    pub firstVfOffset: u32_,
+    pub vfFeatureMask: u32_,
+    pub FirstVFBar0Address: u64_,
+    pub FirstVFBar1Address: u64_,
+    pub FirstVFBar2Address: u64_,
+    pub bar0Size: u64_,
+    pub bar1Size: u64_,
+    pub bar2Size: u64_,
+    pub b64bitBar0: u8_,
+    pub b64bitBar1: u8_,
+    pub b64bitBar2: u8_,
+    pub bSriovEnabled: u8_,
+    pub bSriovHeavyEnabled: u8_,
+    pub bEmulateVFBar0TlbInvalidationRegister: u8_,
+    pub bClientRmAllocatedCtxBuffer: u8_,
+    pub bNonPowerOf2ChannelCountSupported: u8_,
+    pub bVfResizableBAR1Supported: u8_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
+    pub BoardID: u32_,
+    pub chipSKU: [ffi::c_char; 9usize],
+    pub chipSKUMod: [ffi::c_char; 5usize],
+    pub skuConfigVersion: u32_,
+    pub project: [ffi::c_char; 5usize],
+    pub projectSKU: [ffi::c_char; 5usize],
+    pub CDP: [ffi::c_char; 6usize],
+    pub projectSKUMod: [ffi::c_char; 2usize],
+    pub businessCycle: u32_,
+}
+pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 
17usize];
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
+    pub base: u64_,
+    pub limit: u64_,
+    pub reserved: u64_,
+    pub performance: u32_,
+    pub supportCompressed: u8_,
+    pub supportISO: u8_,
+    pub bProtected: u8_,
+    pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
+    pub numFBRegions: u32_,
+    pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+    pub index: u32_,
+    pub flags: u32_,
+    pub length: u32_,
+    pub data: [u8_; 256usize],
+}
+impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+    fn default() -> Self {
+        let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+        unsafe {
+            ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+            s.assume_init()
+        }
+    }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct DOD_METHOD_DATA {
     pub status: u32_,
     pub acpiIdListLen: u32_,
@@ -367,6 +438,19 @@ pub struct ACPI_METHOD_DATA {
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS {
+    pub headIndex: u32_,
+    pub maxHResolution: u32_,
+    pub maxVResolution: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS {
+    pub numHeads: u32_,
+    pub maxNumHeads: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct BUSINFO {
     pub deviceID: u16_,
     pub vendorID: u16_,
@@ -395,6 +479,85 @@ pub struct GSP_PCIE_CONFIG_REG {
 }
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
+pub struct EcidManufacturingInfo {
+    pub ecidLow: u32_,
+    pub ecidHigh: u32_,
+    pub ecidExtended: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct FW_WPR_LAYOUT_OFFSET {
+    pub nonWprHeapOffset: u64_,
+    pub frtsOffset: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct GspStaticConfigInfo_t {
+    pub grCapsBits: [u8_; 23usize],
+    pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS,
+    pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS,
+    pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS,
+    pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS,
+    pub sriovMaxGfid: u32_,
+    pub engineCaps: [u32_; 3usize],
+    pub poisonFuseEnabled: u8_,
+    pub fb_length: u64_,
+    pub fbio_mask: u64_,
+    pub fb_bus_width: u32_,
+    pub fb_ram_type: u32_,
+    pub fbp_mask: u64_,
+    pub l2_cache_size: u32_,
+    pub gpuNameString: [u8_; 64usize],
+    pub gpuShortNameString: [u8_; 64usize],
+    pub gpuNameString_Unicode: [u16_; 64usize],
+    pub bGpuInternalSku: u8_,
+    pub bIsQuadroGeneric: u8_,
+    pub bIsQuadroAd: u8_,
+    pub bIsNvidiaNvs: u8_,
+    pub bIsVgx: u8_,
+    pub bGeforceSmb: u8_,
+    pub bIsTitan: u8_,
+    pub bIsTesla: u8_,
+    pub bIsMobile: u8_,
+    pub bIsGc6Rtd3Allowed: u8_,
+    pub bIsGc8Rtd3Allowed: u8_,
+    pub bIsGcOffRtd3Allowed: u8_,
+    pub bIsGcoffLegacyAllowed: u8_,
+    pub bIsMigSupported: u8_,
+    pub RTD3GC6TotalBoardPower: u16_,
+    pub RTD3GC6PerstDelay: u16_,
+    pub bar1PdeBase: u64_,
+    pub bar2PdeBase: u64_,
+    pub bVbiosValid: u8_,
+    pub vbiosSubVendor: u32_,
+    pub vbiosSubDevice: u32_,
+    pub bPageRetirementSupported: u8_,
+    pub bSplitVasBetweenServerClientRm: u8_,
+    pub bClRootportNeedsNosnoopWAR: u8_,
+    pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS,
+    pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS,
+    pub displaylessMaxPixels: u64_,
+    pub hInternalClient: u32_,
+    pub hInternalDevice: u32_,
+    pub hInternalSubdevice: u32_,
+    pub bSelfHostedMode: u8_,
+    pub bAtsSupported: u8_,
+    pub bIsGpuUefi: u8_,
+    pub bIsEfiInit: u8_,
+    pub ecidInfo: [EcidManufacturingInfo; 2usize],
+    pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET,
+}
+impl Default for GspStaticConfigInfo_t {
+    fn default() -> Self {
+        let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+        unsafe {
+            ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+            s.assume_init()
+        }
+    }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct GspSystemInfo {
     pub gpuPhysAddr: u64_,
     pub gpuPhysFbAddr: u64_,
diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs
index 76cedf3710d7..1709a5d50e54 100644
--- a/drivers/gpu/nova-core/util.rs
+++ b/drivers/gpu/nova-core/util.rs
@@ -45,3 +45,18 @@ pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(timeout: 
Delta, cond: F) -> Resul
         }
     }
 }
+
+/// Converts a null-terminated byte array to a string slice.
+///
+/// Returns "invalid" if the bytes are not valid UTF-8 or not null-terminated.
+pub(crate) fn str_from_null_terminated(bytes: &[u8]) -> &str {
+    use kernel::str::CStr;
+
+    // Find the first null byte, then create a slice that includes it
+    bytes
+        .iter()
+        .position(|&b| b == 0)
+        .and_then(|null_pos| 
CStr::from_bytes_with_nul(&bytes[..=null_pos]).ok())
+        .and_then(|cstr| cstr.to_str().ok())
+        .unwrap_or("invalid")
+}
-- 
2.34.1

Reply via email to