@@ -6,6 +6,20 @@
//!
//! Copyright (C) 2024 Western Digital
+use crate::validator::SpdmHeader;
+use core::mem;
+
+/* 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;
+pub(crate) const SPDM_VER_13: u8 = 0x13;
+
+pub(crate) const SPDM_MIN_VER: u8 = SPDM_VER_10;
+pub(crate) const SPDM_MAX_VER: u8 = SPDM_VER_13;
+
pub(crate) const SPDM_REQ: u8 = 0x80;
pub(crate) const SPDM_ERROR: u8 = 0x7f;
@@ -37,3 +51,6 @@ pub(crate) enum SpdmErrorCode {
NoPendingRequests = 0x45,
VendorDefinedError = 0xff,
}
+
+pub(crate) const SPDM_GET_VERSION: u8 = 0x84;
+pub(crate) const SPDM_GET_VERSION_LEN: usize = mem::size_of::<SpdmHeader>() + 255;
@@ -106,7 +106,11 @@
/// Return 0 on success or a negative errno. In particular, -EPROTONOSUPPORT
/// indicates authentication is not supported by the device.
#[no_mangle]
-pub unsafe extern "C" fn spdm_authenticate(_state: &'static mut SpdmState) -> c_int {
+pub unsafe extern "C" fn spdm_authenticate(state: &'static mut SpdmState) -> c_int {
+ if let Err(e) = state.get_version() {
+ return e.to_errno() as c_int;
+ }
+
0
}
@@ -7,6 +7,7 @@
//! Copyright (C) 2024 Western Digital
use core::ffi::c_void;
+use core::slice::from_raw_parts_mut;
use kernel::prelude::*;
use kernel::{
bindings,
@@ -14,8 +15,11 @@
validate::Untrusted,
};
-use crate::consts::{SpdmErrorCode, SPDM_ERROR, SPDM_REQ};
-use crate::validator::{SpdmErrorRsp, SpdmHeader};
+use crate::consts::{
+ SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MIN_VER,
+ SPDM_REQ,
+};
+use crate::validator::{GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHeader};
/// The current SPDM session state for a device. Based on the
/// C `struct spdm_state`.
@@ -64,7 +68,7 @@ pub(crate) fn new(
transport_sz,
keyring,
validate,
- version: 0x10,
+ version: SPDM_MIN_VER,
authenticated: false,
}
}
@@ -214,4 +218,55 @@ pub(crate) fn spdm_exchange(
Ok(length)
}
+
+ /// Negoiate a supported SPDM version and store the information
+ /// in the `SpdmState`.
+ pub(crate) fn get_version(&mut self) -> Result<(), Error> {
+ let mut request = GetVersionReq {
+ version: self.version,
+ code: SPDM_GET_VERSION,
+ param1: 0,
+ param2: 0,
+ };
+ // 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,
+ core::mem::size_of::<GetVersionReq>(),
+ )
+ };
+
+ let mut response_vec: KVec<u8> = KVec::with_capacity(SPDM_GET_VERSION_LEN, GFP_KERNEL)?;
+ // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+ let response_buf =
+ unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), SPDM_GET_VERSION_LEN) };
+
+ let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+ // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+ unsafe { response_vec.set_len(rc as usize) };
+
+ let response: &mut GetVersionRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+ let mut foundver = false;
+ for i in 0..response.version_number_entry_count {
+ // Creating a reference on a packed struct will result in
+ // undefined behaviour, so we operate on the raw data directly
+ let unaligned = core::ptr::addr_of_mut!(response.version_number_entries) as *mut u16;
+ let addr = unaligned.wrapping_add(i as usize);
+ let version = (unsafe { core::ptr::read_unaligned::<u16>(addr) } >> 8) as u8;
+
+ if version >= self.version && version <= SPDM_MAX_VER {
+ self.version = version;
+ foundver = true;
+ }
+ }
+
+ if !foundver {
+ pr_err!("No common supported version\n");
+ to_result(-(bindings::EPROTO as i32))?;
+ }
+
+ Ok(())
+ }
}
@@ -6,6 +6,7 @@
//!
//! Copyright (C) 2024 Western Digital
+use crate::bindings::{__IncompleteArrayField, __le16};
use crate::consts::SpdmErrorCode;
use core::mem;
use kernel::prelude::*;
@@ -63,3 +64,56 @@ pub(crate) struct SpdmErrorRsp {
pub(crate) error_code: SpdmErrorCode,
pub(crate) error_data: u8,
}
+
+#[repr(C, packed)]
+pub(crate) struct GetVersionReq {
+ pub(crate) version: u8,
+ pub(crate) code: u8,
+ pub(crate) param1: u8,
+ pub(crate) param2: u8,
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetVersionRsp {
+ pub(crate) version: u8,
+ pub(crate) code: u8,
+ param1: u8,
+ param2: u8,
+ reserved: u8,
+ pub(crate) version_number_entry_count: u8,
+ pub(crate) version_number_entries: __IncompleteArrayField<__le16>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetVersionRsp {
+ type Err = Error;
+
+ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+ let raw = unvalidated.raw_mut();
+ if raw.len() < mem::size_of::<GetVersionRsp>() {
+ return Err(EINVAL);
+ }
+
+ let version_number_entries = *(raw.get(5).ok_or(ENOMEM))? as usize;
+ let total_expected_size = version_number_entries * 2 + 6;
+ if raw.len() < total_expected_size {
+ return Err(EINVAL);
+ }
+
+ let ptr = raw.as_mut_ptr();
+ // CAST: `GetVersionRsp` only contains integers and has `repr(C)`.
+ let ptr = ptr.cast::<GetVersionRsp>();
+ // SAFETY: `ptr` came from a reference and the cast above is valid.
+ let rsp: &mut GetVersionRsp = unsafe { &mut *ptr };
+
+ // Creating a reference on a packed struct will result in
+ // undefined behaviour, so we operate on the raw data directly
+ let unaligned = core::ptr::addr_of_mut!(rsp.version_number_entries) as *mut u16;
+ for version_offset in 0..version_number_entries {
+ let addr = unaligned.wrapping_add(version_offset);
+ let version = unsafe { core::ptr::read_unaligned::<u16>(addr) };
+ unsafe { core::ptr::write_unaligned::<u16>(addr, version.to_le()) }
+ }
+
+ Ok(rsp)
+ }
+}
Support the GET_VERSION SPDM command. Signed-off-by: Alistair Francis <alistair@alistair23.me> --- lib/rspdm/consts.rs | 17 ++++++++++++ lib/rspdm/lib.rs | 6 ++++- lib/rspdm/state.rs | 61 +++++++++++++++++++++++++++++++++++++++--- lib/rspdm/validator.rs | 54 +++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 4 deletions(-)