Support the GET_VERSION SPDM command. Signed-off-by: Alistair Francis <alistair@xxxxxxxxxxxxx> --- 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(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 017e673ff194..e263d62fa648 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -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; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index edfd94ab56dd..670ecc02a471 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -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 } diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 5b55c4655e2e..9ace0bbaa21a 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -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(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 5004804f85c8..05f1ba155920 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -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) + } +} -- 2.47.0