From: Zhi Wang <[email protected]>

The boot request describes a vGPU's identity, channel map, VRAM,
plugin heap and log buffers to GSP-RM.

Add the NVKV schema and encode it from a named boot description. Pass
VRAM region views so each physical address stays paired with its length
until encoding, and retain the control offset relative to the plugin
heap. Represent channel-map engine and index fields in their 16-bit wire
domains.

Describe one guest VRAM segment and the current whole-GPU,
non-SMIG resource layout. Keep separate MIG-RM heap fields zero for that
supported firmware path.

Co-developed-by: Eliot Courtney <[email protected]>
Signed-off-by: Eliot Courtney <[email protected]>
Signed-off-by: Zhi Wang <[email protected]>
Signed-off-by: Alok Kumar <[email protected]>
---
 drivers/gpu/nova-core/gsp/nvkv.rs         |  11 ++
 drivers/gpu/nova-core/gsp/nvkv/encode.rs  |  12 ++
 drivers/gpu/nova-core/vgpu/fw/commands.rs | 168 +++++++++++++++++++++-
 3 files changed, 188 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs 
b/drivers/gpu/nova-core/gsp/nvkv.rs
index 5887a61af02a..0e8e14f98059 100644
--- a/drivers/gpu/nova-core/gsp/nvkv.rs
+++ b/drivers/gpu/nova-core/gsp/nvkv.rs
@@ -131,6 +131,17 @@ pub(crate) struct Array<T, const N: usize, const KEY_ID: 
KeyId> {
     vec: ArrayVec<T, N>,
 }
 
+impl<T: Default + Copy, const N: usize, const KEY_ID: KeyId> Array<T, N, 
KEY_ID> {
+    /// Creates an array filled from `values`.
+    ///
+    /// Fails with `EINVAL` if `values` is longer than `N`.
+    pub(crate) fn new(values: &[T]) -> Result<Self> {
+        let mut vec = ArrayVec::default();
+        vec.extend_from_slice(values)?;
+        Ok(Self { vec })
+    }
+}
+
 bitfield! {
     /// The op word that starts each NVKV operation.
     struct Op(u64) {
diff --git a/drivers/gpu/nova-core/gsp/nvkv/encode.rs 
b/drivers/gpu/nova-core/gsp/nvkv/encode.rs
index c6891c0441ff..c4578e3ecef9 100644
--- a/drivers/gpu/nova-core/gsp/nvkv/encode.rs
+++ b/drivers/gpu/nova-core/gsp/nvkv/encode.rs
@@ -7,6 +7,7 @@
 use zerocopy::Immutable;
 
 use super::{
+    Array,
     Index,
     IndexedKey,
     Key,
@@ -130,6 +131,17 @@ fn encode(&self, encoder: &mut Encoder) -> Result {
     }
 }
 
+impl<T, const N: usize, const KEY_ID: KeyId> Encodable for Array<T, N, KEY_ID>
+where
+    T: Default + Copy,
+    for<'a> IndexedKey<&'a [T], KEY_ID>: Encodable,
+{
+    #[inline(always)]
+    fn encode(&self, encoder: &mut Encoder) -> Result {
+        IndexedKey::<&[T], KEY_ID>::new(Index::new::<0>(), 
self.vec.as_slice()).encode(encoder)
+    }
+}
+
 impl<T: Encodable> Encodable for Option<T> {
     fn encode(&self, encoder: &mut Encoder) -> Result {
         if let Some(value) = self {
diff --git a/drivers/gpu/nova-core/vgpu/fw/commands.rs 
b/drivers/gpu/nova-core/vgpu/fw/commands.rs
index efab30287f05..b8007c3e4a71 100644
--- a/drivers/gpu/nova-core/vgpu/fw/commands.rs
+++ b/drivers/gpu/nova-core/vgpu/fw/commands.rs
@@ -8,17 +8,23 @@
 
 use kernel::{
     alloc::ArrayVec,
-    bitfield, //
+    bitfield,
+    prelude::*, //
 };
 
 use crate::{
     gsp::nvkv::{
         nvkv_decode,
+        nvkv_encode,
         Array,
+        Encodable,
+        Encoder,
+        Index, //
         Key,
         KeyId,
-        Required, //
-    }, //
+        Required,
+    },
+    mm::vram::VramRegion, //
 };
 
 bitfield! {
@@ -30,6 +36,162 @@ pub(crate) struct Dbdf(u32) {
     }
 }
 
+#[derive(Clone, Copy)]
+struct SwizzId(u32);
+
+impl SwizzId {
+    const WHOLE_GPU: Self = Self(0xFFFF_FFFF);
+}
+
+impl From<SwizzId> for u32 {
+    fn from(value: SwizzId) -> Self {
+        value.0
+    }
+}
+
+bitfield! {
+    pub(crate) struct ChannelMapEntry(u64) {
+        15:0 engine_type;
+        31:16 index;
+        63:32 chid_offset;
+    }
+}
+
+impl ChannelMapEntry {
+    const KEY: KeyId = 0x1001;
+
+    #[expect(dead_code)]
+    pub(crate) fn new(engine_type: u16, index: u16, chid_offset: u32) -> Self {
+        Self::zeroed()
+            .with_engine_type(engine_type)
+            .with_index(index)
+            .with_chid_offset(chid_offset)
+    }
+}
+
+impl Encodable for KVVec<ChannelMapEntry> {
+    fn encode(&self, encoder: &mut Encoder) -> Result {
+        // SAFETY: `ChannelMapEntry` is a `bitfield!` over `u64`, i.e.
+        // `#[repr(transparent)]` around a `u64`, so the entries are
+        // layout-compatible with `u64` and can be viewed as a `u64` slice.
+        let slice = unsafe { 
core::slice::from_raw_parts(self.as_ptr().cast::<u64>(), self.len()) };
+        encoder.encode_array64(ChannelMapEntry::KEY, Index::new::<0>(), slice)
+    }
+}
+
+bitfield! {
+    struct VgpuBootloadOptions(u64) {
+    }
+}
+
+nvkv_encode! {
+    struct VgpuBootloadRequest {
+        dbdf: Key<Dbdf, { Self::DBDF_KEY }, u32>,
+        gfid: Key<u32, { Self::GFID_KEY }>,
+        vgpu_type: Key<u32, { Self::VGPU_TYPE_KEY }>,
+        vm_pid: Key<u32, { Self::VM_PID_KEY }>,
+        swizz_id: Key<SwizzId, { Self::SWIZZ_ID_KEY }, u32>,
+        num_channels: Key<u32, { Self::NUM_CHANNELS_KEY }>,
+        num_plugin_channels: Key<u32, { Self::NUM_PLUGIN_CHANNELS_KEY }>,
+        guest_fb_segment_count: Key<u32, { Self::GUEST_FB_SEGMENT_COUNT_KEY }>,
+        options: Key<VgpuBootloadOptions, { Self::OPTIONS_KEY }, u64>,
+        channel_mapping: KVVec<ChannelMapEntry>,
+        guest_fb_segment_phys_addr: Array<u64, 8, { 
Self::GUEST_FB_SEGMENT_PHYS_ADDR_KEY }>,
+        guest_fb_segment_length: Array<u64, 8, { 
Self::GUEST_FB_SEGMENT_LENGTH_KEY }>,
+        plugin_heap_phys_addr: Key<u64, { Self::PLUGIN_HEAP_PHYS_ADDR_KEY }>,
+        plugin_heap_length: Key<u64, { Self::PLUGIN_HEAP_LENGTH_KEY }>,
+        ctrl_buff_offset: Key<u64, { Self::CTRL_BUFF_OFFSET_KEY }>,
+        init_task_log_offset: Key<u64, { Self::INIT_TASK_LOG_OFFSET_KEY }>,
+        init_task_log_size: Key<u64, { Self::INIT_TASK_LOG_SIZE_KEY }>,
+        vgpu_task_log_offset: Key<u64, { Self::VGPU_TASK_LOG_OFFSET_KEY }>,
+        vgpu_task_log_size: Key<u64, { Self::VGPU_TASK_LOG_SIZE_KEY }>,
+        kernel_log_offset: Key<u64, { Self::KERNEL_LOG_OFFSET_KEY }>,
+        kernel_log_size: Key<u64, { Self::KERNEL_LOG_SIZE_KEY }>,
+        mig_rm_heap_phys_addr: Key<u64, { Self::MIG_RM_HEAP_PHYS_ADDR_KEY }>,
+        mig_rm_heap_length: Key<u64, { Self::MIG_RM_HEAP_LENGTH_KEY }>,
+    }
+}
+
+impl VgpuBootloadRequest {
+    const DBDF_KEY: KeyId = 0x0001;
+    const GFID_KEY: KeyId = 0x0002;
+    const VGPU_TYPE_KEY: KeyId = 0x0003;
+    const VM_PID_KEY: KeyId = 0x0004;
+    const SWIZZ_ID_KEY: KeyId = 0x0005;
+    const NUM_CHANNELS_KEY: KeyId = 0x0006;
+    const NUM_PLUGIN_CHANNELS_KEY: KeyId = 0x0007;
+    const GUEST_FB_SEGMENT_COUNT_KEY: KeyId = 0x0008;
+    const OPTIONS_KEY: KeyId = 0x1000;
+    const GUEST_FB_SEGMENT_PHYS_ADDR_KEY: KeyId = 0x1002;
+    const GUEST_FB_SEGMENT_LENGTH_KEY: KeyId = 0x1003;
+    const PLUGIN_HEAP_PHYS_ADDR_KEY: KeyId = 0x1004;
+    const PLUGIN_HEAP_LENGTH_KEY: KeyId = 0x1005;
+    const CTRL_BUFF_OFFSET_KEY: KeyId = 0x1006;
+    const INIT_TASK_LOG_OFFSET_KEY: KeyId = 0x1007;
+    const INIT_TASK_LOG_SIZE_KEY: KeyId = 0x1008;
+    const VGPU_TASK_LOG_OFFSET_KEY: KeyId = 0x1009;
+    const VGPU_TASK_LOG_SIZE_KEY: KeyId = 0x100A;
+    const KERNEL_LOG_OFFSET_KEY: KeyId = 0x100B;
+    const KERNEL_LOG_SIZE_KEY: KeyId = 0x100C;
+    const MIG_RM_HEAP_PHYS_ADDR_KEY: KeyId = 0x100D;
+    const MIG_RM_HEAP_LENGTH_KEY: KeyId = 0x100E;
+}
+
+/// Identity, channel mapping and VRAM regions passed to a GSP plugin at boot.
+///
+/// Region addresses are physical VRAM addresses, including the log fields 
whose wire
+/// names contain `offset`. The control-buffer offset is relative to the 
plugin heap.
+pub(crate) struct BootloadInfo<'a> {
+    pub(crate) dbdf: Dbdf,
+    pub(crate) gfid: u32,
+    pub(crate) vgpu_type: u32,
+    pub(crate) vm_pid: u32,
+    pub(crate) num_channels: u32,
+    pub(crate) num_plugin_channels: u32,
+    pub(crate) channel_mapping: KVVec<ChannelMapEntry>,
+    pub(crate) guest_fb: &'a VramRegion,
+    pub(crate) plugin_heap: &'a VramRegion,
+    pub(crate) ctrl_buffer_offset: u64,
+    pub(crate) init_log: &'a VramRegion,
+    pub(crate) vgpu_log: &'a VramRegion,
+    pub(crate) kernel_log: &'a VramRegion,
+}
+
+/// Encodes a `VGPU_BOOTLOAD` request using the typed NVKV schema.
+#[expect(dead_code)]
+pub(crate) fn encode_vgpu_bootload(info: BootloadInfo<'_>) -> 
Result<KVVec<u64>> {
+    let request = VgpuBootloadRequest {
+        dbdf: info.dbdf.into(),
+        gfid: info.gfid.into(),
+        vgpu_type: info.vgpu_type.into(),
+        vm_pid: info.vm_pid.into(),
+        swizz_id: SwizzId::WHOLE_GPU.into(),
+        num_channels: info.num_channels.into(),
+        num_plugin_channels: info.num_plugin_channels.into(),
+        guest_fb_segment_count: 1.into(),
+        options: VgpuBootloadOptions::zeroed().into(),
+        channel_mapping: info.channel_mapping,
+        guest_fb_segment_phys_addr: Array::new(&[info.guest_fb.address()])?,
+        guest_fb_segment_length: Array::new(&[info.guest_fb.size()])?,
+        plugin_heap_phys_addr: info.plugin_heap.address().into(),
+        plugin_heap_length: info.plugin_heap.size().into(),
+        ctrl_buff_offset: info.ctrl_buffer_offset.into(),
+        init_task_log_offset: info.init_log.address().into(),
+        init_task_log_size: info.init_log.size().into(),
+        vgpu_task_log_offset: info.vgpu_log.address().into(),
+        vgpu_task_log_size: info.vgpu_log.size().into(),
+        kernel_log_offset: info.kernel_log.address().into(),
+        kernel_log_size: info.kernel_log.size().into(),
+        // The current non-SMIG firmware path does not map a separate MIG-RM 
heap.
+        mig_rm_heap_phys_addr: 0.into(),
+        mig_rm_heap_length: 0.into(),
+    };
+
+    let mut encoder = Encoder::new();
+    request.encode(&mut encoder)?;
+    Ok(encoder.finish())
+}
+
 nvkv_decode! {
     pub(crate) struct VgpuPropertiesSchema => VgpuProperties {
         // TODO: `name`/`class` required?
-- 
2.53.0

Reply via email to