From: alistair23@gmail.com
To: linux-pci@vger.kernel.org, Jonathan.Cameron@huawei.com,
djbw@kernel.org, rust-for-linux@vger.kernel.org, lukas@wunner.de,
alistair@alistair23.me, jic23@kernel.org,
linux-cxl@vger.kernel.org, bhelgaas@google.com,
akpm@linux-foundation.org, linux-kernel@vger.kernel.org
Cc: gary@garyguo.net, ojeda@kernel.org, benno.lossin@proton.me,
a.hindborg@kernel.org, wilfred.mallawa@wdc.com,
tmgross@umich.edu, alistair23@gmail.com, boqun.feng@gmail.com,
bjorn3_gh@protonmail.com, alex.gaynor@gmail.com,
aliceryhl@google.com
Subject: [PATCH v3 15/21] lib: rspdm: Support SPDM get_capabilities
Date: Tue, 1 Sep 2026 11:03:41 +1000 [thread overview]
Message-ID: <20260901010347.2614656-16-alistair.francis@wdc.com> (raw)
In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com>
From: Alistair Francis <alistair@alistair23.me>
Support the GET_CAPABILITIES SPDM command.
Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
lib/rspdm/consts.rs | 19 +++++++-
lib/rspdm/lib.rs | 4 ++
lib/rspdm/state.rs | 78 ++++++++++++++++++++++++++++-
lib/rspdm/validator.rs | 108 ++++++++++++++++++++++++++++++++++++++++-
4 files changed, 205 insertions(+), 4 deletions(-)
diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index 055671d43abd..15d69631ed8c 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -9,13 +9,12 @@
use crate::validator::GetVersionRsp;
use core::mem;
+use kernel::bits::bit_u32;
use kernel::error::{code::EINVAL, Error};
// SPDM versions supported by this implementation
pub(crate) const SPDM_VER_10: u8 = 0x10;
-#[allow(dead_code)]
pub(crate) const SPDM_VER_11: u8 = 0x11;
-#[allow(dead_code)]
pub(crate) const SPDM_VER_12: u8 = 0x12;
#[allow(dead_code)]
pub(crate) const SPDM_VER_13: u8 = 0x13;
@@ -102,3 +101,19 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
pub(crate) const SPDM_GET_VERSION: u8 = 0x84;
pub(crate) const SPDM_GET_VERSION_LEN: usize =
mem::size_of::<GetVersionRsp>() + (u8::MAX as usize) * mem::size_of::<u16>();
+
+pub(crate) const SPDM_GET_CAPABILITIES: u8 = 0xe1;
+pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 = 42;
+
+// SPDM cryptographic timeout of this implementation:
+// Assume calculations may take up to 1 sec on a busy machine, which equals
+// roughly 1 << 20. That's within the limits mandated for responders by CMA
+// (1 << 23 usec, PCIe r6.2 sec 6.31.3) and DOE (1 sec, PCIe r6.2 sec 6.30.2).
+// Used in GET_CAPABILITIES exchange.
+pub(crate) const SPDM_CTEXPONENT: u8 = 20;
+
+pub(crate) const SPDM_CERT_CAP: u32 = bit_u32(1);
+pub(crate) const SPDM_CHAL_CAP: u32 = bit_u32(2);
+
+pub(crate) const SPDM_REQ_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
+pub(crate) const SPDM_RSP_MIN_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 58d86ea06fd9..76325babdff2 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -102,6 +102,10 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut spdm_state) -> c_int {
return e.to_errno() as c_int;
}
+ if let Err(e) = state.get_capabilities() {
+ return e.to_errno() as c_int;
+ }
+
-(EPROTONOSUPPORT as i32)
}
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 9e8c65a12199..5ef14c8ed237 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -25,10 +25,17 @@
SPDM_ERROR,
SPDM_GET_VERSION_LEN,
SPDM_MAX_VER,
+ SPDM_MIN_DATA_TRANSFER_SIZE,
SPDM_MIN_VER,
- SPDM_REQ, //
+ SPDM_REQ,
+ SPDM_RSP_MIN_CAPS,
+ SPDM_VER_10,
+ SPDM_VER_11,
+ SPDM_VER_12, //
};
use crate::validator::{
+ GetCapabilitiesReq,
+ GetCapabilitiesRsp,
GetVersionReq,
GetVersionRsp,
SpdmErrorRsp,
@@ -52,6 +59,8 @@
///
/// `version`: Maximum common supported version of requester and responder.
/// Negotiated during GET_VERSION exchange.
+/// `rsp_caps`: Cached capabilities of responder.
+/// Received during GET_CAPABILITIES exchange.
#[expect(dead_code)]
pub(crate) struct SpdmState {
pub(crate) dev: *mut bindings::device,
@@ -62,6 +71,7 @@ pub(crate) struct SpdmState {
// Negotiated state
pub(crate) version: u8,
+ pub(crate) rsp_caps: u32,
}
impl SpdmState {
@@ -79,6 +89,7 @@ pub(crate) fn new(
transport_sz,
validate,
version: SPDM_MIN_VER,
+ rsp_caps: 0,
}
}
@@ -297,4 +308,69 @@ pub(crate) fn get_version(&mut self) -> Result<(), Error> {
Ok(())
}
+
+ /// Obtain the supported capabilities from an SPDM session and store the
+ /// information in the `SpdmState`.
+ pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {
+ let mut request = GetCapabilitiesReq::default();
+ request.version = self.version;
+
+ let (req_sz, rsp_sz) = match self.version {
+ SPDM_VER_10 => (
+ core::mem::size_of::<SpdmHeader>(),
+ core::mem::size_of::<SpdmHeader>() + 4 + core::mem::size_of::<u32>(),
+ ),
+ SPDM_VER_11 => {
+ let len = core::mem::size_of::<SpdmHeader>() + 4 + core::mem::size_of::<u32>();
+ (len, len)
+ }
+ _ => {
+ request.data_transfer_size = self.transport_sz.to_le();
+ request.max_spdm_msg_size = request.data_transfer_size;
+
+ (
+ core::mem::size_of::<GetCapabilitiesReq>(),
+ core::mem::size_of::<GetCapabilitiesRsp>(),
+ )
+ }
+ };
+
+ // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+ let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+ let mut response_vec: KVec<u8> = KVec::from_elem(0u8, rsp_sz, GFP_KERNEL)?;
+
+ let rc = self.spdm_exchange(request_buf, response_vec.as_mut_slice())? as usize;
+
+ // The transport must report a length within the buffer we provided.
+ if rc > response_vec.len() {
+ pr_err!("Overflowed capabilities response\n");
+ return Err(EIO);
+ }
+ response_vec.truncate(rc);
+
+ let response: &mut GetCapabilitiesRsp = Untrusted::new(&mut response_vec).validate()?;
+
+ self.rsp_caps = u32::from_le(response.flags);
+ if (self.rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS {
+ pr_err!(
+ "{:#x} capabilities are supported, which don't meet required {:#x}\n",
+ self.rsp_caps,
+ SPDM_RSP_MIN_CAPS
+ );
+ self.rsp_caps = 0;
+ return Err(EPROTONOSUPPORT);
+ }
+
+ if self.version >= SPDM_VER_12 {
+ let data_transfer_size = u32::from_le(response.data_transfer_size);
+ if data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE {
+ pr_err!("Malformed capabilities response\n");
+ return Err(EPROTO);
+ }
+ self.transport_sz = self.transport_sz.min(data_transfer_size);
+ }
+
+ Ok(())
+ }
}
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index 5990ff7ada29..42c0b28cdcaa 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -26,8 +26,13 @@
};
use crate::consts::{
+ SPDM_CTEXPONENT,
+ SPDM_GET_CAPABILITIES,
SPDM_GET_VERSION,
- SPDM_MIN_VER, //
+ SPDM_MIN_VER,
+ SPDM_REQ_CAPS,
+ SPDM_VER_10,
+ SPDM_VER_11, //
};
#[repr(C, packed)]
@@ -159,3 +164,104 @@ fn validate(unvalidated: &[u8]) -> Result<Self, Self::Err> {
Ok(rsp)
}
}
+
+#[repr(C, packed)]
+pub(crate) struct GetCapabilitiesReq {
+ pub(crate) version: u8,
+ pub(crate) code: u8,
+ pub(crate) param1: u8,
+ pub(crate) param2: u8,
+
+ reserved1: u8,
+ pub(crate) ctexponent: u8,
+ reserved2: [u8; 2],
+
+ pub(crate) flags: u32,
+
+ /* End of SPDM 1.1 structure */
+ pub(crate) data_transfer_size: u32,
+ pub(crate) max_spdm_msg_size: u32,
+}
+
+impl Default for GetCapabilitiesReq {
+ fn default() -> Self {
+ GetCapabilitiesReq {
+ version: 0,
+ code: SPDM_GET_CAPABILITIES,
+ param1: 0,
+ param2: 0,
+ reserved1: 0,
+ ctexponent: SPDM_CTEXPONENT,
+ reserved2: [0; 2],
+ flags: SPDM_REQ_CAPS.to_le(),
+ data_transfer_size: 0,
+ max_spdm_msg_size: 0,
+ }
+ }
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetCapabilitiesRsp {
+ pub(crate) version: u8,
+ pub(crate) code: u8,
+ pub(crate) param1: u8,
+ param2: u8,
+
+ reserved1: u8,
+ pub(crate) ctexponent: u8,
+ reserved2: [u8; 2],
+
+ pub(crate) flags: u32,
+
+ /* End of SPDM 1.1 structure */
+ pub(crate) data_transfer_size: u32,
+ pub(crate) max_spdm_msg_size: u32,
+
+ pub(crate) supported_algorithms: __IncompleteArrayField<__le16>,
+}
+
+impl<'a> Validate<Untrusted<&'a mut KVec<u8>>> for &'a mut GetCapabilitiesRsp {
+ type Err = Error;
+
+ fn validate(unvalidated: &mut KVec<u8>) -> Result<Self, Self::Err> {
+ let version = *(unvalidated.get(0).ok_or(EINVAL))?;
+
+ let expected_length = match version {
+ SPDM_VER_10 | SPDM_VER_11 => {
+ core::mem::size_of::<SpdmHeader>() + 4 + core::mem::size_of::<u32>()
+ }
+ _ => {
+ // Check to see if param1 is set
+ if *(unvalidated.get(2).ok_or(EINVAL))? == 0 {
+ mem::size_of::<GetCapabilitiesRsp>()
+ - mem::size_of::<__IncompleteArrayField<__le16>>()
+ } else {
+ // Not currently supported by Linux, we don't set the bit
+ // so the responder shouldn't either.
+ return Err(EINVAL);
+ }
+ }
+ };
+
+ // Make sure the response meets the SPDM spec version requirements
+ if unvalidated.len() < expected_length {
+ return Err(EINVAL);
+ }
+
+ // If the response is shorter than GetCapabilitiesRsp
+ // (which is valid for older spec versions and when param1 is
+ // set to 0) then we need to pad the vector to ensure
+ // GetCapabilitiesRsp will be initialised.
+ while unvalidated.len() < mem::size_of::<GetCapabilitiesRsp>() {
+ unvalidated.push(0, GFP_KERNEL)?;
+ }
+
+ let ptr = unvalidated.as_mut_ptr();
+ // CAST: `GetCapabilitiesRsp` only contains integers and has `repr(C)`.
+ let ptr = ptr.cast::<GetCapabilitiesRsp>();
+ // SAFETY: `ptr` came from a reference and the cast above is valid.
+ let rsp: &mut GetCapabilitiesRsp = unsafe { &mut *ptr };
+
+ Ok(rsp)
+ }
+}
--
2.55.0
next prev parent reply other threads:[~2026-09-01 1:06 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 1:03 [PATCH v3 00/21] lib: Rust implementation of SPDM alistair23
2026-09-01 1:03 ` [PATCH v3 01/21] rust: transmute: add `cast_slice[_mut]` functions alistair23
2026-09-01 1:03 ` [PATCH v3 02/21] rust: create basic untrusted data API alistair23
2026-09-01 1:03 ` [PATCH v3 03/21] rust: validate: add `Validate` trait alistair23
2026-09-01 1:03 ` [PATCH v3 04/21] X.509: Make certificate parser public alistair23
2026-09-01 1:03 ` [PATCH v3 05/21] X.509: Parse Subject Alternative Name in certificates alistair23
2026-09-01 1:03 ` [PATCH v3 06/21] X.509: Move certificate length retrieval into new helper alistair23
2026-09-01 1:03 ` [PATCH v3 07/21] rust: add bindings for hash.h alistair23
2026-09-01 1:03 ` [PATCH v3 08/21] rust: error: impl From<FromBytesWithNulError> for Kernel Error alistair23
2026-09-01 1:03 ` [PATCH v3 09/21] lib: rspdm: Initial commit of Rust SPDM alistair23
2026-09-01 1:03 ` [PATCH v3 10/21] PCI/TSM: Rename pf0 to host alistair23
2026-09-01 1:03 ` [PATCH v3 11/21] PCI/TSM: Support connecting to PCIe CMA devices alistair23
2026-09-01 1:03 ` [PATCH v3 12/21] PCI/CMA: Add a PCI TSM CMA driver using SPDM alistair23
2026-09-01 1:03 ` [PATCH v3 13/21] PCI/CMA: Validate Subject Alternative Name in certificates alistair23
2026-09-01 1:03 ` [PATCH v3 14/21] lib: rspdm: Support SPDM get_version alistair23
2026-09-01 1:03 ` alistair23 [this message]
2026-09-01 1:03 ` [PATCH v3 16/21] lib: rspdm: Support SPDM negotiate_algorithms alistair23
2026-09-04 5:01 ` Aksh Garg
2026-09-01 1:03 ` [PATCH v3 17/21] lib: rspdm: Support SPDM get_digests alistair23
2026-09-01 1:03 ` [PATCH v3 18/21] lib: rspdm: Support SPDM get_certificate alistair23
2026-09-01 1:03 ` [PATCH v3 19/21] lib: rspdm: Support SPDM certificate validation alistair23
2026-09-01 1:03 ` [PATCH v3 20/21] rust: allow extracting the buffer from a CString alistair23
2026-09-01 1:03 ` [PATCH v3 21/21] lib: rspdm: Support SPDM challenge alistair23
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260901010347.2614656-16-alistair.francis@wdc.com \
--to=alistair23@gmail.com \
--cc=Jonathan.Cameron@huawei.com \
--cc=a.hindborg@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=alistair@alistair23.me \
--cc=benno.lossin@proton.me \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=djbw@kernel.org \
--cc=gary@garyguo.net \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=wilfred.mallawa@wdc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox