[PATCH v4 17/32] gpu: nova-core: gsp: factor out NVKV payload conversion

From: Alok Kumar

Date: Fri Nov 06 2026 - 00:49:33 EST


From: Zhi Wang <zhiw@xxxxxxxxxx>

Both GSP initialization and vGPU property queries decode their replies as
NVKV streams.

Move the byte-to-word conversion helper into the NVKV module and make
that module accessible to the vGPU firmware interface.

No functional change is intended.

Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx>
Signed-off-by: Alok Kumar <alkumar@xxxxxxxxxx>
---
drivers/gpu/nova-core/gsp.rs | 2 +-
drivers/gpu/nova-core/gsp/commands/gmc.rs | 12 ++-------
drivers/gpu/nova-core/gsp/nvkv.rs | 31 ++++++++++++++++++++++-
3 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index cac123cbeac4..8424aa3937e7 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -28,7 +28,7 @@
pub(crate) mod cmdq;
pub(crate) mod commands;
mod fw;
-mod nvkv;
+pub(crate) mod nvkv;
mod regs;

pub(crate) use fw::{
diff --git a/drivers/gpu/nova-core/gsp/commands/gmc.rs b/drivers/gpu/nova-core/gsp/commands/gmc.rs
index 6f88c99349ac..33aea614cf72 100644
--- a/drivers/gpu/nova-core/gsp/commands/gmc.rs
+++ b/drivers/gpu/nova-core/gsp/commands/gmc.rs
@@ -29,6 +29,7 @@
GmcCommandId, //
},
nvkv::{
+ nvkv_words,
Decoder,
Encodable,
Encoder,
@@ -151,16 +152,7 @@ impl MessageFromGsp for GspStaticInfo {
fn read(payload: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>) -> Result<Self> {
let bytes = payload.flush_into_kvec(GFP_KERNEL)?;

- let chunks = bytes.chunks_exact(size_of::<u64>());
- if !chunks.remainder().is_empty() {
- return Err(EINVAL);
- }
- let mut words = KVVec::with_capacity(chunks.len(), GFP_KERNEL)?;
- for chunk in chunks {
- // `chunks_exact` yields slices of exactly `size_of::<u64>()` bytes.
- let word = chunk.try_into().map_err(|_| EINVAL)?;
- words.push(u64::from_le_bytes(word), GFP_KERNEL)?;
- }
+ let words = nvkv_words(&bytes, &[])?;

let decoder = Decoder::new(&words, UnknownKeyPolicy::Ignore);
let mut schema = KBox::init(GspInitResponseSchema::init(), GFP_KERNEL)?;
diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/nvkv.rs
index f81a9e60d89c..5887a61af02a 100644
--- a/drivers/gpu/nova-core/gsp/nvkv.rs
+++ b/drivers/gpu/nova-core/gsp/nvkv.rs
@@ -20,11 +20,40 @@
use kernel::{
alloc::ArrayVec,
bitfield,
- num::Bounded, //
+ num::Bounded,
+ prelude::*, //
};

use crate::bounded_enum;

+use crate::sbuffer::SBufferIter;
+
+/// Joins the two halves of a wrapped payload into the `u64` words an NVKV stream is made of.
+///
+/// # Errors
+///
+/// - `EINVAL` if the combined length is not a whole number of words.
+/// - `ENOMEM` if the buffer cannot be allocated.
+pub(crate) fn nvkv_words(payload_0: &[u8], payload_1: &[u8]) -> Result<KVVec<u64>> {
+ const WORD_SIZE: usize = size_of::<u64>();
+
+ // Each byte slice is at most `isize::MAX` bytes, so their sum fits in `usize`.
+ let len = payload_0.len() + payload_1.len();
+ if len % WORD_SIZE != 0 {
+ return Err(EINVAL);
+ }
+
+ let mut words = KVVec::with_capacity(len / WORD_SIZE, GFP_KERNEL)?;
+ let mut bytes = SBufferIter::new_reader([payload_0, payload_1]);
+ for _ in 0..len / WORD_SIZE {
+ let mut word = [0u8; WORD_SIZE];
+ bytes.read_exact(&mut word)?;
+ words.push(u64::from_le_bytes(word), GFP_KERNEL)?;
+ }
+
+ Ok(words)
+}
+
mod encode;
pub(crate) use encode::*;

--
2.53.0