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
