From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B9962DB795 for ; Tue, 1 Sep 2026 01:04:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224650; cv=none; b=cqW8A9d+wBA2jWNAWpys1eUianVZ7F/U3kHwm3p23XME8bQEr6o69OAxnY1JzZ8uW2Cc+c+UNYdA/jpyDLQPnhvd51I8ZalifVOMo5Cm9nXcC4rzuN0QlT+0mPM/V4KnT9TzlBdPJbiW1isZqiB3oIF8nrnKh97b7WMdXgT8+Tc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224650; c=relaxed/simple; bh=CoPPRCtaESHUG9OwZb9CyMVn08i9IGh5mH8Q+LlV8pU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VFdmacg9Xk5g6E/OgRRX7WY1BstfUiV/xYgz3BnPjrNL/v4jfnjFtFBkjk64SaCkqPJcucHESV7FY5uL66ozGaNM4bblOY7nIAwKKB/o4RpwY3OPVxS+KR/RtdzXNAj0g67AiLYA5ahe5B+LALAvIAHgnRSIy4WEAryZ4cTWVVM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Gok1GWMs; arc=none smtp.client-ip=209.85.216.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Gok1GWMs" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-3965d3d9ab8so3240646a91.3 for ; Mon, 31 Aug 2026 18:04:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224649; x=1788829449; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=AYXVDMNHLbdJmU4DKZR7fcyBrcg7wKS5d7QhF+qczOE=; b=Gok1GWMsICvbvt7A9OQY99NKTu1NKdA11Iggvri7Wp12kyZsKtrRG/YU67JygJvdnM 6SzGpPLzAqA+Qt+9z7NYIzLIY/gb/vVdpVaJoUObtHEWD066WaD0j/IaF4c305ZYcZ0H MypzaSHpNbgE6s1xrU709N1/zgv1d1KxXT3heawYNmL2Mw2KLw6LJwv3yLFaL+cYHtzl y2VWmN2ogcOFoDIoUajqnI0VFS0k48Y7V99CG74fY8GdCMqtaMvGothbkd8UesQ0/vmV C9VtyQri6Pc1zg3CoV9ft8Rdw2pxcKZdhN9Yjr9qIhxQVrmXYzvQihsisRC9TppkHWZ/ tMEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224649; x=1788829449; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=AYXVDMNHLbdJmU4DKZR7fcyBrcg7wKS5d7QhF+qczOE=; b=IApbU3t3OfXiQe3is8ceagPHlxZmpLjU9uITkI4J8ixSfRGTgwf1NQSYzoDxn/Qdhl zrOomFWDgDimLd0NIohSfSxv9vloYeWnGGheb3p+j01OPCWvQ1HqtmUEmxOQoPc8DPok nZUGwRP1a6qud1mWSOE9D0Y2enamzr5iSYiR/4xCJ10HrBhkljdEnW7oKZT18HngF7qh wvn2JXFezXNHlJ0lOBX3jDCQV7Ag+j5DTehnNfCC0A+qLWrinItZx9cC6BFc9pI4/TgB 8O2roXLOp1NT6tkBtmuVfqvMTGhjy3Q/6uPthKoKCB8RrWBUZnDbcxstGO/mq+IpkrnH Dr3Q== X-Forwarded-Encrypted: i=1; AKwUvBze5lj/1u96iWjeUsOEa0llHE4+KoDyQEvhxGLJP6/MqbwUoVCbQyH51Bfwbkfeje0qP31CnRo5OqoW7w8=@vger.kernel.org X-Gm-Message-State: AFuF++m0PyaXRZVsi/tQi58ijRX1NgnECygJHsgN7ID7Acg6Jnev2f9A UFbWGvx3Im389BmWqRwViREETFPY4s6IpzCBr0IcjK3R3X19xIQpkem9 X-Gm-Gg: AYBFou2TdY5/zH4T8L5tBTG9NppaFP9vwQgzkSLoK2jwKa8/GTzsZ/374tHZu5wSnzz XZCDguBzwxIVFLKqK5mCFEoaPIxLVVgQXjyoJ5jOsesM5w9UvviGN62q6S2l7pnin3AkscUSMPN lERBm7Av60C2N2cyylCHfoQM2AkynV5BPFVjRDO+KmiFYWzq2j7r5DzhevqTj2GidHuYVvyuqF3 7pESCTaI1euKFUYyJ+VUNHUzSIuLv49NpPJMWVsIuakNudkwWORIcWdIfLniBfVwnSoq2ZIiAEc 1IlYGIL/TkyMx6OEeppdjFyyBhyhP8KdM4vv/mgiDoKFyaQbfgNg+svyxvEnqa+hZ3QYPxu+Npg xAZnLviWjmJqVTcgifsKalqbrfQFkjnWfStC4cLT4ODfjl1RDROQuUXSJU8rzMzE5CFgXfrfY2S Xy1NUVxVHOUAYsff+f6x9sO5Z2aZe29JpyZU9mu3GTGMEgmc0HgowZelJd9ODMV5h/LHHcMA/2K 7lhng== X-Received: by 2002:a17:90b:1a8e:b0:396:4c63:7193 with SMTP id 98e67ed59e1d1-396d0fe45damr45825286a91.11.1788224648978; Mon, 31 Aug 2026 18:04:08 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:08 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 01/21] rust: transmute: add `cast_slice[_mut]` functions Date: Tue, 1 Sep 2026 11:03:27 +1000 Message-ID: <20260901010347.2614656-2-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Benno Lossin Add functions to make casting slices only one `unsafe` block. Signed-off-by: Benno Lossin Message-ID: <20250814124424.516191-2-lossin@kernel.org> --- rust/kernel/transmute.rs | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs index 654b5ede2fe2..c1282fcc9e65 100644 --- a/rust/kernel/transmute.rs +++ b/rust/kernel/transmute.rs @@ -3,6 +3,7 @@ //! Traits for transmuting types. =20 use core::mem::size_of; +use core::slice; =20 /// Types for which any bit pattern is valid. /// @@ -227,3 +228,61 @@ macro_rules! impl_asbytes { {} [T], {} [T; N], } + +/// Casts the type of a slice to another. +/// +/// Also see [`cast_slice_mut`]. +/// +/// # Examples +/// +/// ```rust +/// # use kernel::transmute::cast_slice; +/// #[repr(transparent)] +/// #[derive(Debug)] +/// struct Container(T); +/// +/// let array =3D [0u32; 42]; +/// let slice =3D &array; +/// // SAFETY: `Container` transparently wraps a `u32`. +/// let container_slice =3D unsafe { cast_slice::>(sli= ce) }; +/// pr_info!("{container_slice:?}"); +/// ``` +/// +/// # Safety +/// +/// - `T` and `U` must have the same layout. +pub unsafe fn cast_slice(slice: &[T]) -> &[U] { + // CAST: by the safety requirements, `T` and `U` have the same layout. + let ptr =3D slice.as_ptr().cast::(); + // SAFETY: `ptr` and `len` come from the same slice reference. + unsafe { slice::from_raw_parts(ptr, slice.len()) } +} + +/// Casts the type of a slice to another. +/// +/// Also see [`cast_slice`]. +/// +/// # Examples +/// +/// ```rust +/// # use kernel::transmute::cast_slice_mut; +/// #[repr(transparent)] +/// #[derive(Debug)] +/// struct Container(T); +/// +/// let mut array =3D [0u32; 42]; +/// let slice =3D &mut array; +/// // SAFETY: `Container` transparently wraps a `u32`. +/// let container_slice =3D unsafe { cast_slice_mut::>= (slice) }; +/// pr_info!("{container_slice:?}"); +/// ``` +/// +/// # Safety +/// +/// - `T` and `U` must have the same layout. +pub unsafe fn cast_slice_mut(slice: &mut [T]) -> &mut [U] { + // CAST: by the safety requirements, `T` and `U` have the same layout. + let ptr =3D slice.as_mut_ptr().cast::(); + // SAFETY: `ptr` and `len` come from the same slice reference. + unsafe { slice::from_raw_parts_mut(ptr, slice.len()) } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pg1-f174.google.com (mail-pg1-f174.google.com [209.85.215.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 208B436B92C for ; Tue, 1 Sep 2026 01:04:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224660; cv=none; b=YdHaxzN1rJSGp2XRWUPn2l+SG4rKneMuub7YwUVIHLDzolKyb04BjBu1WFLFC+pxaE/e+bkFc3y8ERA9qBqgFUvHgrDZej4kzclGf5mIt9DqeT1qHHUOv7QYYN583UZL8TUiYI9I6I0JfSy9nd54v6pnHg5QTbOFoUgPyojE2Eo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224660; c=relaxed/simple; bh=/K808TfOsG2VgzhfRJeQIUFlgb9kInuGTOzKUhgrVD4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GWyfkm894hQlXa14os6mFiEAt8HnKWK1uS2/K/BsaSXqW1R68jqJJkFZe+oOKMB73i2H1LZ9DE1p44LuyoFS5i+TRxaRgAdtBYm2ASiiCi6qt7bpKQIATDrW9LRPpGkBIM5ac/bQIvvg72InZ2sdMs+cOrJ7tTEOM87xArA0Bng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MFZmBkn9; arc=none smtp.client-ip=209.85.215.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MFZmBkn9" Received: by mail-pg1-f174.google.com with SMTP id 41be03b00d2f7-cc1e1ff659bso404027a12.0 for ; Mon, 31 Aug 2026 18:04:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224658; x=1788829458; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=65bCRT2r3FA+N3VTYVba2Ft1meiSRlWorEkESgZc4wU=; b=MFZmBkn9ITqAj+D8M4z1VAm/gGyM7yal2PcmNAlFIl0DgcnwBVK8Ua1xLXcAgdAttR /A9/KqjICeKXH4metmRh7+1aNKNZl47nH/Jg7ZJuXsVsUPeyHHZZV4x6e++fy/VVFy7f jdUNUi98GUdDtKFsa7DOYYba5XgoEhRWT/FiL2MANQK5QelH0ye1AA1CER1KvpUhIgKY RwLtpOrfevs0vyTayos3DsYCV+zj3eoD3UFSt64gmn4Ywrdark6gHviCXiz4En1wysp+ dZZZ7fvMny86iHlAKTBEZS4BWgV7OlCZ4xNf2okgCWT7oP1WsMHxw6VpwMZP9ZYbSqAu Hn/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224658; x=1788829458; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=65bCRT2r3FA+N3VTYVba2Ft1meiSRlWorEkESgZc4wU=; b=aFUhOIGa4uZeiG9/sOkciMe0Im+fIA1QUdPVjraqdrFoxhSwnXeJzuKAd4r1QrCW3z QRKFW/CZ9Dd4mrNLAoOrREylwiZl2SG//RoBIZmp9w5CAUr3U8pgfC4IBWJAL5I/zdxo cC2an30bl9uPWobe5Va//dwR3gkocGHAulogCJ+b14Py7PUMf419YghZZRoAQglc1CwL Dj66UaziTakfs8eYDw8+AKgBzkTPVOuYyCMuuqYyWr+J04vNvQ4N/cGFiWCKoilzeJkb G0CHV//1G56Z5E+Mn8/nQhkavR36dLSY+9Ah7G9phrJNmrn9wJOLR9Tfu+uGsqSSnm5C 7+7Q== X-Forwarded-Encrypted: i=1; AKwUvBydWPwtSf/lOr3pGy5GtagJVIj5m3FmoidwAqWV1wNX8V0KlZEYeAQ7sdk9J3Pz5hbLGhToGnQizrYKrLQ=@vger.kernel.org X-Gm-Message-State: AFuF++kxk2DIz19IW0hR4S8sCzyHH1JCeewYhMIPEx3C38hyecVsSo9R XZrFbCwPrbFwaGUEHr8DwuBRrK6LuwyW4BqqFmUaudC3CJypp+R7E28M X-Gm-Gg: AYBFou35UvYd01B/d2UtC8Xnb1NwWjnFAAZvd0gslfUL+e3ituy7VhNX7kEhc5+yfW6 6HWUScgPdYcxLoI+xUlsnH4PRqPrjBwdT2xdAQlrjIhCjXDbkmglNvxAZDfcsYVZQKl/0nTz1FE qX9H5Tk7262S2yxG93cZzzlahnmWBtGf3y/aKRm5ybOUdKkVm1sqHjP+qOe6kyLqEdbrboMYu2E im954/nP4HL5C+vYsYjCP/a2zBN12JIQhAvhmiG6rNnSZTeLrSNkMIYMIjyzRuswBG1YiWl3989 qex5O/+ZhmpaZ15RdALRgWUZLRLxVCsjGfIY7kEDu1PaArj12hteq+pMRlKGCyGtLBNJOrfqRMY hD/3lADpggX/Kc8PPz0Wn/o3BVylWPP8OCHsou2iEacQE0Y3l0R/27D1L6iC6+ILo764eZiPqn+ ps8D+CH/5m30C4htOrsSw4u/bLSa86PGGDAhqv5fTVa640W3n/o6WFk3eMN5zkyEuoLO/vjHSRd u08ksvJCCmQ1b6C X-Received: by 2002:a17:90a:dfcc:b0:380:f389:447b with SMTP id 98e67ed59e1d1-396d0fd3a24mr48514295a91.11.1788224657525; Mon, 31 Aug 2026 18:04:17 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:16 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 02/21] rust: create basic untrusted data API Date: Tue, 1 Sep 2026 11:03:28 +1000 Message-ID: <20260901010347.2614656-3-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Benno Lossin When the kernel receives external data (e.g. from userspace), it usually is a very bad idea to directly use the data for logic decision in the kernel. For this reason, such data should be explicitly marked and validated before making decision based on its value. The `Untrusted` wrapper type marks a value of type `T` as untrusted. The particular meaning of "untrusted" highly depends on the type `T`. For example `T =3D u8` ensures that the value of the byte cannot be retrieved. However, `T =3D [u8]` still allows to access the length of the slice. Similarly, `T =3D KVec` allows modifications. Signed-off-by: Benno Lossin Message-ID: <20250814124424.516191-3-lossin@kernel.org> --- rust/kernel/lib.rs | 1 + rust/kernel/validate.rs | 148 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 rust/kernel/validate.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 4d5c96ddc49c..239f8325b40a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -143,6 +143,7 @@ pub mod uaccess; #[cfg(CONFIG_USB =3D "y")] pub mod usb; +pub mod validate; pub mod workqueue; pub mod xarray; =20 diff --git a/rust/kernel/validate.rs b/rust/kernel/validate.rs new file mode 100644 index 000000000000..2b28625c25ef --- /dev/null +++ b/rust/kernel/validate.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Untrusted data API. +//! +//! # Overview +//! +//! Untrusted data is marked using the [`Untrusted`] type. See [Rationa= le](#rationale) for the +//! reasons to mark untrusted data throughout the kernel. It is a totally = opaque wrapper, it is not +//! possible to read the data inside. +//! +//! APIs that write back into userspace usually allow writing untrusted by= tes directly, allowing +//! direct copying of untrusted user data back into userspace without vali= dation. +//! +//! # Rationale +//! +//! When reading data from an untrusted source, it must be validated befor= e it can be used for +//! **logic**. For example, this is a very bad idea: +//! +//! ``` +//! # fn read_bytes_from_network() -> KBox<[u8]> { +//! # Box::new([1, 0], kernel::alloc::flags::GFP_KERNEL).unwrap() +//! # } +//! let bytes: KBox<[u8]> =3D read_bytes_from_network(); +//! let data_index =3D bytes[0]; +//! let data =3D bytes[usize::from(data_index)]; +//! ``` +//! +//! While this will not lead to a memory violation (because the array inde= x checks the bounds), it +//! might result in a kernel panic. For this reason, all untrusted data mu= st be wrapped in +//! [`Untrusted`]. This type only allows validating the data or passing= it along, since copying +//! data from userspace back into userspace is allowed for untrusted data. + +use core::ops::{Deref, DerefMut}; + +use crate::{ + alloc::{ + Allocator, + Vec, // + }, + transmute::{ + cast_slice, + cast_slice_mut, // + }, +}; + +/// Untrusted data of type `T`. +/// +/// Data coming from userspace is considered untrusted and should be marke= d by this type. +/// +/// The particular meaning of [`Untrusted`] depends heavily on the type= `T`. For example, +/// `&Untrusted<[u8]>` is a reference to an untrusted slice. But the lengt= h is not considered +/// untrusted, as it would otherwise violate normal Rust rules. For this r= eason, one can easily +/// convert that reference to `&[Untrusted]`. Another such example is = `Untrusted>`, it +/// derefs to `KVec>`. Raw bytes however do not behave in thi= s way, `Untrusted` is +/// totally opaque. +/// +/// # Usage in API Design +/// +/// The exact location where to put [`Untrusted`] depends on the kind of A= PI. When asking for an +/// untrusted input value, or buffer to write to, always move the [`Untrus= ted`] wrapper as far +/// inwards as possible: +/// +/// ```ignore +/// // use this +/// pub fn read_from_userspace(buf: &mut [Untrusted]) { todo!() } +/// +/// // and not this +/// pub fn read_from_userspace(buf: &mut Untrusted<[u8]>) { todo!() } +/// ``` +/// +/// The reason for this is that `&mut Untrusted<[u8]>` can beconverted int= o `&mut [Untrusted]` +/// very easily, but the converse is not possible. +/// +/// For the same reason, when returning untrusted data by-value, one shoul= d move the [`Untrusted`] +/// wrapper as far outward as possible: +/// +/// ```ignore +/// // use this +/// pub fn read_all_from_userspace() -> Untrusted> { todo!() } +/// +/// // and not this +/// pub fn read_all_from_userspace() -> KVec> { todo!() } +/// ``` +/// +/// Here too the reason is that `KVec>` is more restrictive = compared to +/// `Untrusted>`. +#[repr(transparent)] +pub struct Untrusted(T); + +impl Untrusted { + /// Marks the given value as untrusted. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// # mod bindings { pub(crate) unsafe fn read_foo_info() -> [u8; 4] {= todo!() } }; + /// fn read_foo_info() -> Untrusted<[u8; 4]> { + /// // SAFETY: just an FFI call without preconditions. + /// Untrusted::new(unsafe { bindings::read_foo_info() }) + /// } + /// ``` + pub fn new(value: T) -> Self + where + T: Sized, + { + Self(value) + } +} + +impl Deref for Untrusted<[T]> { + type Target =3D [Untrusted]; + + fn deref(&self) -> &Self::Target { + // SAFETY: `Untrusted` transparently wraps `T`. + unsafe { cast_slice(&self.0) } + } +} + +impl DerefMut for Untrusted<[T]> { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `Untrusted` transparently wraps `T`. + unsafe { cast_slice_mut(&mut self.0) } + } +} + +impl Deref for Untrusted> { + type Target =3D Vec, A>; + + fn deref(&self) -> &Self::Target { + let ptr: *const Untrusted> =3D self; + // CAST: `Untrusted` transparently wraps `T`. + let ptr: *const Vec, A> =3D ptr.cast(); + // SAFETY: `ptr` is derived from the reference `self`. + unsafe { &*ptr } + } +} + +impl DerefMut for Untrusted> { + fn deref_mut(&mut self) -> &mut Self::Target { + let ptr: *mut Untrusted> =3D self; + // CAST: `Untrusted` transparently wraps `T`. + let ptr: *mut Vec, A> =3D ptr.cast(); + // SAFETY: `ptr` is derived from the reference `self`. + unsafe { &mut *ptr } + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f48.google.com (mail-pj1-f48.google.com [209.85.216.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2302538E12D for ; Tue, 1 Sep 2026 01:04:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224668; cv=none; b=EBI13c5OscFKVUHbLhhs6uGmUhptdVFiaPKMY2Xy9TVANRidRfhodUQQKDmMnu3nk+fBxCnsvLu14e649xQLDOxeyijxX7fAKOqnRbwdRF1bA0S5ilA4TGUac/173zStnDcrPg5TFbxAqPDsWB1DPlE4XKHNpnOvR4g1m6nuZik= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224668; c=relaxed/simple; bh=BiaV76NHkxz06E8kYtTux48D14DxjulaUaglF1xarVA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PFBTXWWsKh1gBXLbncBCXWAZFjWnE45sEnoxCDRg1zfjkXo1ZxhPVL51G0QQKZsrOLC+FRdY520LxJuY3gJsH+awGFMOuMVENIZ+OUKrM25sPGoBoci6tf2Wu+zzp/aY9GoGNd2UiFoD1F79JGew0yoKbRR7vK5uIn5xkmUJi0s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FQCg7ZlO; arc=none smtp.client-ip=209.85.216.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FQCg7ZlO" Received: by mail-pj1-f48.google.com with SMTP id 98e67ed59e1d1-3966791a6eeso5058303a91.3 for ; Mon, 31 Aug 2026 18:04:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224666; x=1788829466; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=1Y85wCLF7sPuzEoPMS3iNY9FbVM2HqAZF/UVKCQfhZk=; b=FQCg7ZlO5dP6o1Jmz76SfkFYqPGzEpZizZfVOWQ9mU0SfidARzmD776FAT7MTh5m2M 9tCZY/tJx22nt3GeC8eznD5oKRkxbaXyMJ6RmtlIW71yX9ZCt/YNz70Dd1M1zWw5pyHp FKBkXsMooV0n+9HNYBoHrATgB/4LKH2LcpYK06yMJWA2w6a0Ah/PFPDM9jfCZSSk+FZh wRwv+m+9QG7j6Vp7bmEIDAd0ZJIO47kWZLLHPJCsddGdxLYwJngLB0gi7lYMMFMii258 YKH2XdZoeRXeHXWP0woeAenbo58lPuqsjCYKMWaTiH5N/0D1RHdcdjb24Dj8VwNmgDlW IQhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224666; x=1788829466; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=1Y85wCLF7sPuzEoPMS3iNY9FbVM2HqAZF/UVKCQfhZk=; b=dIqRm7WeLGFGBD7gL5ELNpNRc0lBjuW3tptoe1CrYoExaEq4eia0h4SBDUKn7boTMQ BC1M9nZQ43QlzK5bdnyosYwJaQAwUxEdnB6+wxiRXAeUmKySXEKcXux1r5YPxdBEjrkb ybVWa1E36IesGkWalnuWnXwq229opzffZV1hdNuBFmVqiXjWuEKFyz3EWtltZv3UAhS8 hap7eXhBFe9F1/GFLGgU3pye+spflgybmBuJBRNZX1x0I4B1sV8La9tA65Sh6uOl+1hd zP0KkgryJz2nUO69Gcvj/XfTs6lgEzP+GYsauTx1PIQAoqQ/MYK/k2DId3lM6nfoaUAx Apjw== X-Forwarded-Encrypted: i=1; AKwUvBxi9ec4qHrTkzT+K7385zfJUNEO5A+lZ5dISpcXiMjnI2ZcZaHmU+HE1QkG9AKGVPvWhYY93yr1ZYIdiow=@vger.kernel.org X-Gm-Message-State: AFuF++mZ6upTSdL8Ayw9Ayl96yvZmgafyV2AvjXlqahmFCfOfVLJZaXu hISt6eZINEHc8THof8vxcMPUU3kiqZhU/u/1pzLR7a4nXGiHrSN/4G+U X-Gm-Gg: AYBFou3tN0J9Lee5K0cNcyVlcoBgpmeiGqrbAkzI7XMSWmLIFc7Pxrar8vfiiNywc1z Lj/MN6GyuqHDimsDr9tl98Yt78M9q5YsLr5mSIrf5b5TV/0o5KQJkWK8fvmyMe4YWUqqojLMayV Q+9G9tMiS1JIMtqnlyAyyHc01+WlBsjLiYeLHzRStB3sI2tsFl4F+K+p0SlPqdAEe4LphvQCiag kJ1FNOKL+zkuuYgCkoBgaM+VIUZfLSXgfYKcFUvzLwKhesg5SaR0pGiKbyevk3S3+SgdJOCkO6h kK2ZRwVDit09DDOqiA7y8i3ZQXl1CTbJC3VzxjAx+NXTHwztnQkmqFCTSG1sZf8MA5yXfQcKUfM H2NWhuLntGrifItw/Lh4s9b7Jw5GOmYd53LlkYAE50DAuYhKSrCKkc5DxEovtfDdKKLi1nosSm7 4RBnt9q5xFbks8+D/gXG3WZFDs1mYuyE3+TavX6rXh2wm0wYFzcXP7SNKW7+UwWA3Fe3WyMI4q1 3jsDg== X-Received: by 2002:a17:90a:e7d2:b0:398:9bd4:d15 with SMTP id 98e67ed59e1d1-39907e73facmr5625095a91.20.1788224666148; Mon, 31 Aug 2026 18:04:26 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:25 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 03/21] rust: validate: add `Validate` trait Date: Tue, 1 Sep 2026 11:03:29 +1000 Message-ID: <20260901010347.2614656-4-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Benno Lossin Introduce the `Validate` trait and functions to validate `Untrusted` using said trait. This allows one to access the inner value of `Untrusted` via `validate{,_ref,_mut}` functions which subsequently delegate the validation to user-implemented `Validate` trait. The `Validate` trait is the only entry point for validation code, making it easy to spot where data is being validated. The reason for restricting the types that can be inputs to `Validate::validate` is to be able to have the `validate...` functions on `Untrusted`. This is also the reason for the suggestions in the `Usage in API Design` section in the commit that introduced `Untrusted`. Signed-off-by: Benno Lossin Message-ID: <20250814124424.516191-4-lossin@kernel.org> --- rust/kernel/validate.rs | 70 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/rust/kernel/validate.rs b/rust/kernel/validate.rs index 2b28625c25ef..663681b633c8 100644 --- a/rust/kernel/validate.rs +++ b/rust/kernel/validate.rs @@ -11,6 +11,9 @@ //! APIs that write back into userspace usually allow writing untrusted by= tes directly, allowing //! direct copying of untrusted user data back into userspace without vali= dation. //! +//! The only way to access untrusted data is to [`Validate::validate`] it.= This is facilitated by +//! the [`Validate`] trait. +//! //! # Rationale //! //! When reading data from an untrusted source, it must be validated befor= e it can be used for @@ -52,7 +55,7 @@ /// untrusted, as it would otherwise violate normal Rust rules. For this r= eason, one can easily /// convert that reference to `&[Untrusted]`. Another such example is = `Untrusted>`, it /// derefs to `KVec>`. Raw bytes however do not behave in thi= s way, `Untrusted` is -/// totally opaque. +/// totally opaque and one can only access its value by calling [`Untruste= d::validate()`]. /// /// # Usage in API Design /// @@ -107,6 +110,30 @@ pub fn new(value: T) -> Self { Self(value) } + + /// Validate the underlying untrusted data. + /// + /// See the [`Validate`] trait for more information. + pub fn validate>(self) -> Result + where + T: Sized, + { + V::validate(self.0) + } + + /// Validate the underlying untrusted data. + /// + /// See the [`Validate`] trait for more information. + pub fn validate_ref<'a, V: Validate<&'a Self>>(&'a self) -> Result { + V::validate(&self.0) + } + + /// Validate the underlying untrusted data. + /// + /// See the [`Validate`] trait for more information. + pub fn validate_mut<'a, V: Validate<&'a mut Self>>(&'a mut self) -> Re= sult { + V::validate(&mut self.0) + } } =20 impl Deref for Untrusted<[T]> { @@ -146,3 +173,44 @@ fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *ptr } } } + +/// Marks valid input for the [`Validate`] trait. +pub trait ValidateInput: private::Sealed { + /// Type of the inner data. + type Inner: ?Sized; +} + +impl ValidateInput for Untrusted { + type Inner =3D T; +} + +impl<'a, T: ?Sized> ValidateInput for &'a Untrusted { + type Inner =3D &'a T; +} + +impl<'a, T: ?Sized> ValidateInput for &'a mut Untrusted { + type Inner =3D &'a mut T; +} + +mod private { + use super::Untrusted; + + pub trait Sealed {} + + impl Sealed for Untrusted {} + impl<'a, T: ?Sized> Sealed for &'a Untrusted {} + impl<'a, T: ?Sized> Sealed for &'a mut Untrusted {} +} + +/// Validate [`Untrusted`] data. +/// +/// Care must be taken when implementing this trait, as unprotected access= to unvalidated data is +/// given to the [`Validate::validate`] function. The implementer must ens= ure that the data is only +/// used for logic after successful validation. +pub trait Validate: Sized { + /// Validation error. + type Err; + + /// Validate the raw input. + fn validate(raw: Input::Inner) -> Result; +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f42.google.com (mail-pj1-f42.google.com [209.85.216.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29E8C38A72B for ; Tue, 1 Sep 2026 01:04:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224677; cv=none; b=nhr6lDRTETrkA9CRyFvdYowOFpGUg88cZncf56FLFQQvSJ6RLUEgjVKGVwVfx3sO9oPS9tA1YchoxxaYrAKE6AMdtmGYFfWIQ3R7r4YmypYf4sYnueaSFI4F/J+jitNTlwLZHEQ1W+CniUq8mmhtk2D3k6XiHIpgfQcxsPlH2mM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224677; c=relaxed/simple; bh=CQFEUDdrBOr25kMcKMXOfOQ/OinpeF576SKItpXVsJg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dJIHEjoPFGWxlkf4CRQl98EKHGFJqNSTpx5p4z4LHkUJYqT4/AHEhUd+sBi/xfQjsDRMVNIlp5oheYs5UCn3gVygBDK3eg2QF4M+9pr5DRb28s/Ed8iQ8T317vLdD2ILe9Sinf2k1Xj9sUhfufQq+U90+HVTbOXyJKG5c95I+ek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ZsBDaC0/; arc=none smtp.client-ip=209.85.216.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZsBDaC0/" Received: by mail-pj1-f42.google.com with SMTP id 98e67ed59e1d1-38e58034d05so3507366a91.2 for ; Mon, 31 Aug 2026 18:04:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224675; x=1788829475; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=UyJ6OADjMeEJ9CwYlU3XCJzTi4L5CBgkg5gwk3kMxf4=; b=ZsBDaC0/+fQmyD8tSNma39eqdyThM8INc0UOACy2Uaxa6xkRXPoltbKVX9msgUyY4j HDRReL3Hu9mqFn0Lj7970n4SznkLZri3iVOHP9GPmteNf73PClvaBg61H2fabq4dyoOh cYPi10HRGV+j6vTQ7rWmH2UHTqmloTehgFJKpe6PB6tv8FJMQdvC77/IK/BCRxL2Sl2t 3A/puScoxik8/IcLJKrXhZ9TikflCIgYwSe6qu73WcYbLMCspK75Ai4l2aiQkQz26Fvs 6PDEbZjmMUOTG5oSZnWWwvYkyA535ABSgC0DEdMnaTdtOiMJ69mCj2T3m0CItNZdQnR5 LT5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224675; x=1788829475; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=UyJ6OADjMeEJ9CwYlU3XCJzTi4L5CBgkg5gwk3kMxf4=; b=cPwQD3ab2zKNyykxzn0MCUtdQsUC7VEXWEakDPxHLV9vs1Qg40cUpMaDh09GdPiJu1 TU7IhXWwQP6ukp0kP6DH3XgA5Vm5Rk3WrysCeYoc4lMSH/YMP0a2bHI8Z+FCOrZAbN1W VOfgPbHZuc+YoaqWZblFq3d7Fl9oFqDE7a9yALUhRwxtrIEZbvJJ2jGT6TL/4gexiLU5 M9+rjPphNS6D7fbTdZfLisyaL8zHQAnMw1JoRy5c7FO1Uf3qjpMXwiGvkgJFrGS/Xpsk E6Ji8lcanoF/U1NzQYPz7V2IqWVzgCLP8eBgUGTZV+5aXp/MbVBWGS2ORS8WfKsA62Ur ZADw== X-Forwarded-Encrypted: i=1; AKwUvBzlKrr584xFPh4f1LooymDwUdI2hPsVWvVgt76FLVm3m5uEcIbJJMbxD1JHyu8oqW7ao6HwPena+BkD5Yc=@vger.kernel.org X-Gm-Message-State: AFuF++mnFUN3MDK75MQRwJ8G6/sT9C5e0UUCTp1ubR1/Tgy+ijz4dhoG YmvbtmscwI5omsk6AL1v+Hl3CKSQ8GCd0PRzFIM78KqldsxJM8Rs+wiz X-Gm-Gg: AYBFou0ZHUeIQeFXtLTM0FJjyTh2/9ETLSOhvWCbepSqodCP5YVYh7FJzBtoa82dBeX AbAKJDhztVxBtcsAB1Ld64jP6H1fzR2/HfkxFDP9tpsMANfY4zqNOLSkj2HceMG744Kz92g3TOu ECps1zelPSDIVf2+fP8O5NCXNH6wF6acGAZa12NZGZFaB2xa5sBQE/Au/ocZ7fisGjL/iM3aU6E 3rYzJcz2l+O9PyQyEoSIxpa1KEuib/mKUQC8A+ozjVTvz1uJS9/18p1ycuru6QURso6FzOuTZn/ jIccAyMJGJ1DgkNkjWi2O/Dld3bKmIohD5YB5C4rYDB2+5j23/lS+opZLTvh+d12/+4YNHdHeGd Myy164KrdDU2aJahAXEHlWjBmWhcWqwFOIXJV+DckQ2zsnmO5AGvhXsqae1CIyqb70OpRDZ/pZ2 ih2WhD4lOTg0+xMkFGIfib1Q5+xmRp82Vtpai8xXgwm16K1gGzWRbxaH1NG/c68UGKEws+eEPjg 4k/OQ== X-Received: by 2002:a17:90b:54d0:b0:398:d93a:b343 with SMTP id 98e67ed59e1d1-39907ab0ec5mr6643406a91.3.1788224675395; Mon, 31 Aug 2026 18:04:35 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:34 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Dan Williams , Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH v3 04/21] X.509: Make certificate parser public Date: Tue, 1 Sep 2026 11:03:30 +1000 Message-ID: <20260901010347.2614656-5-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates. High-level functions for X.509 parsing such as key_create_or_update() throw away the internal, low-level struct x509_certificate after extracting the struct public_key and public_key_signature from it. The Subject Alternative Name is thus inaccessible when using those functions. Afford CMA-SPDM access to the Subject Alternative Name by making struct x509_certificate public, together with the functions for parsing an X.509 certificate into such a struct and freeing such a struct. No functional change intended. Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Ilpo J=C3=A4rvinen Reviewed-by: Jonathan Cameron --- crypto/asymmetric_keys/x509_parser.h | 42 +-------------------- include/keys/x509-parser.h | 55 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 41 deletions(-) create mode 100644 include/keys/x509-parser.h diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/= x509_parser.h index b7aeebdddb36..39f1521b773d 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -5,51 +5,11 @@ * Written by David Howells (dhowells@redhat.com) */ =20 -#include -#include -#include -#include -#include - -struct x509_certificate { - struct x509_certificate *next; - struct x509_certificate *signer; /* Certificate that signed this one */ - struct public_key *pub; /* Public key details */ - struct public_key_signature *sig; /* Signature parameters */ - u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */ - char *issuer; /* Name of certificate issuer */ - char *subject; /* Name of certificate subject */ - struct asymmetric_key_id *id; /* Issuer + Serial number */ - struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - time64_t valid_from; - time64_t valid_to; - const void *tbs; /* Signed data */ - unsigned tbs_size; /* Size of signed data */ - unsigned raw_sig_size; /* Size of signature */ - const void *raw_sig; /* Signature data */ - const void *raw_serial; /* Raw serial number in ASN.1 */ - unsigned raw_serial_size; - unsigned raw_issuer_size; - const void *raw_issuer; /* Raw issuer name in ASN.1 */ - const void *raw_subject; /* Raw subject name in ASN.1 */ - unsigned raw_subject_size; - unsigned raw_skid_size; - const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ - unsigned index; - bool seen; /* Infinite recursion prevention */ - bool verified; - bool self_signed; /* T if self-signed (check unsupported_sig too) */ - bool unsupported_sig; /* T if signature uses unsupported crypto */ - bool blacklisted; -}; +#include =20 /* * x509_cert_parser.c */ -extern void x509_free_certificate(struct x509_certificate *cert); -DEFINE_FREE(x509_free_certificate, struct x509_certificate *, - if (!IS_ERR(_T)) x509_free_certificate(_T)) -extern struct x509_certificate *x509_cert_parse(const void *data, size_t d= atalen); extern int x509_decode_time(time64_t *_t, size_t hdrlen, unsigned char tag, const unsigned char *value, size_t vlen); diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h new file mode 100644 index 000000000000..8b68e720693a --- /dev/null +++ b/include/keys/x509-parser.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* X.509 certificate parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _KEYS_X509_PARSER_H +#define _KEYS_X509_PARSER_H + +#include +#include +#include +#include +#include + +struct x509_certificate { + struct x509_certificate *next; + struct x509_certificate *signer; /* Certificate that signed this one */ + struct public_key *pub; /* Public key details */ + struct public_key_signature *sig; /* Signature parameters */ + u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */ + char *issuer; /* Name of certificate issuer */ + char *subject; /* Name of certificate subject */ + struct asymmetric_key_id *id; /* Issuer + Serial number */ + struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ + time64_t valid_from; + time64_t valid_to; + const void *tbs; /* Signed data */ + unsigned tbs_size; /* Size of signed data */ + unsigned raw_sig_size; /* Size of signature */ + const void *raw_sig; /* Signature data */ + const void *raw_serial; /* Raw serial number in ASN.1 */ + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; /* Raw issuer name in ASN.1 */ + const void *raw_subject; /* Raw subject name in ASN.1 */ + unsigned raw_subject_size; + unsigned raw_skid_size; + const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ + unsigned index; + bool seen; /* Infinite recursion prevention */ + bool verified; + bool self_signed; /* T if self-signed (check unsupported_sig too) */ + bool unsupported_sig; /* T if signature uses unsupported crypto */ + bool blacklisted; +}; + +struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); +void x509_free_certificate(struct x509_certificate *cert); + +DEFINE_FREE(x509_free_certificate, struct x509_certificate *, + if (!IS_ERR(_T)) x509_free_certificate(_T)) + +#endif /* _KEYS_X509_PARSER_H */ --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53CDC38A72B for ; Tue, 1 Sep 2026 01:04:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224686; cv=none; b=O2/jOltqFcMMX/Hth5Fo/7u4tzrt+ZrqMqMAOydc9Iy+MGBljiSgVucjBLtOQ08iXZ/vr3/LaCuR7pHlj9e7es5ymmkfofAbUlMvg6bjPuU08Q8Xz2roOMszhozIATUYXxDaT7ZVQwpcIqBEtPwCH9180O6bJPYsh1m/p8qQvdo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224686; c=relaxed/simple; bh=0JGUpE4k1PmLAxdyW1CDm+kOaSuNwlxy5EWP/0iy7YA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KxEe1p7UVqn0q8PtldHSRh7qv3gYAoknWofuf6XMGJ2fs4lc3NCfy9X0OcReSxG2fCbbLc5cG1dUCgVsVfIiUo3v8N0WTf6vqXFKIuhI+OlCyerj/TBhO4mQwlwuPG2LSjmBUw/ifzg9fwxlIzFA7oEMZy27FNUeNpv4LPAjLCw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ZH/KDUO1; arc=none smtp.client-ip=209.85.215.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZH/KDUO1" Received: by mail-pg1-f171.google.com with SMTP id 41be03b00d2f7-c9e607d81fcso271905a12.2 for ; Mon, 31 Aug 2026 18:04:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224685; x=1788829485; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=jIcsSTZcRttPSVFsvlIP8uLVExwGwURdPEd6Mb7+UkA=; b=ZH/KDUO1OJqNyRRHjzC3y73qmUoR1g2KaffL5ytGdfTG1zYhN6QbNTJaivUMq/fIPM dV3KeYx2AzQ8DSmt+WOy0VzUMTv4VLQz6PqN9S74fROy6FYg0oIOyLA+YzYVASRKWjAZ LHuFbnYDNVf0tHJfIwTp/Ro0c8LQMXo6p2wojDvEx0nniofuD3TXYO48CgwHbhR62VJq QJoKoDckeLshKSmuD9hXLcKfCh3fguWu/h6v+vU2qSAVHxRYC1x7JP7z0i0vipxcWCf5 Wfp0Df0fnEekfKgCIGZN8VNGMW3pwLXfNnPMvllz/NlGymilnXXl2QCyJPvC9IVpzSCb HMQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224685; x=1788829485; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=jIcsSTZcRttPSVFsvlIP8uLVExwGwURdPEd6Mb7+UkA=; b=Veapv1S2rAnaeVIXQ0+51nVim6+Twx0oekYf0uNSsGiurj28uFAo3u36FiNGBNnSMC HWw0QShVxi9t9DDJsRT6Bl2AcPjsKjng6UlIZvm8+ZRW5UAKz4DRX16OUKj83A8B6BNK 5Zv6S9VAGCvciKrqD2DzNQQYHe85NalDvM7pvKg3rj3VYuBCNfzBa1QW534CLhRKlC/r hkmu/MwGQWN6Hn4p/kHjDHc9rRf8ZgxueYZ3rSC1TUj5XW1ICfT7icK2JdQ7jTDeuNJI ughdINqtAW9LJaGfQ6EFj/fFHICoSVAOLHSY1mg3cIe4CCv9WnnpKgDQLL+V/8IyjKfR bZWA== X-Forwarded-Encrypted: i=1; AKwUvBwl7wmVI3eXg8dEVkU7ZzDL+oOAz+9yHlnLdsrVOJ+5grwQHcWRwztkmeQfrlbuN46SfSZgI5Ux3+t5PFs=@vger.kernel.org X-Gm-Message-State: AFuF++mYpp6wTEgO03UGBGvVAgY9+J19cDH3Sr5glB5yMuOhXSh7EZLQ U4zTVerS1DJLU1RGbTZRoEqHERk4sF5Kzt2k1TYxwea2anq/frUFdA+h X-Gm-Gg: AYBFou36vecLagGaDat8V3PqPeOODuv3JkJ9xDjcYnRNZzpHTP9E6TumeKSwQ96UdX2 IH8QB4QRzzUkKCg3igWcPvTTNxOTvts/G+jf/JTGNjHjaB7rEGpfzg6HUd/pHKcfHvph4bKMGlJ H5l2k7/urGLV3b7cxoTNfNw+zNnluoEkI/jX0d2MuiMwS2RCMW64xMk+Wd35Fm3/6K7AJNItpQw nXy/fmI6WrOBEo+2YhMXYYmHMDhf2GyA6HZeTUJGQWYJLJZ50Mie5rOIRAxCtzoj+1Z6od6yZVy fbwpm3ibFPJcBHWVTMO2PqZUBnvxrl3FSFCDGyoxi1HpUhQmz8MF2k1yKKUK1j1B+2YopkbYqa+ 9I1Wgen82X6vKz3eiI/P0ph/haTXFRfXiAx+Q93AcsbPbxtt6Rt9ZeNuQSCuxDSITaIH4hL3kdR //rexOxvtVo9wVLkOQ2K+aGdtxDmwrVHoB1bcLT49LeuDLGTVeIjw0rh2IwlBgOP4I1tfc6u/ZB 04zeg== X-Received: by 2002:a17:90b:4a81:b0:399:1f8b:d255 with SMTP id 98e67ed59e1d1-3991f8bd96bmr528414a91.5.1788224684691; Mon, 31 Aug 2026 18:04:44 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:44 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Dan Williams Subject: [PATCH v3 05/21] X.509: Parse Subject Alternative Name in certificates Date: Tue, 1 Sep 2026 11:03:31 +1000 Message-ID: <20260901010347.2614656-6-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates. Store a pointer to the Subject Alternative Name upon parsing for consumption by CMA-SPDM. Signed-off-by: Lukas Wunner Reviewed-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Ilpo J=C3=A4rvinen Reviewed-by: Jonathan Cameron Acked-by: Dan Williams --- crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++ include/keys/x509-parser.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_= keys/x509_cert_parser.c index bfd10f0195e0..c3ec2846695a 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -596,6 +596,15 @@ int x509_process_extension(void *context, size_t hdrle= n, return 0; } =20 + if (ctx->last_oid =3D=3D OID_subjectAltName) { + if (ctx->cert->raw_san) + return -EBADMSG; + + ctx->cert->raw_san =3D v; + ctx->cert->raw_san_size =3D vlen; + return 0; + } + if (ctx->last_oid =3D=3D OID_keyUsage) { /* * Get hold of the keyUsage bit string diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h index 8b68e720693a..4e6a05a8c7a6 100644 --- a/include/keys/x509-parser.h +++ b/include/keys/x509-parser.h @@ -38,6 +38,8 @@ struct x509_certificate { unsigned raw_subject_size; unsigned raw_skid_size; const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ + const void *raw_san; /* Raw subjectAltName in ASN.1 */ + unsigned raw_san_size; unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 949E638D3E6 for ; Tue, 1 Sep 2026 01:04:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224696; cv=none; b=AJyVFmeYA3UV125oWVPz67zw2lhGXguKbF7pDcKCmrQPvT2fbwOcUn4TWeBePnAEL6A19/FftzIlAz7adAgVKAnyWy+JF0bVS4kSdTpUSXkdMVY0S2lMT9ah/l0LQacq3F7EojbxrB+88hcp0s17Mt87gGJaXxIBJXVUk64+duY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224696; c=relaxed/simple; bh=ka5L0T+crJoJEiz3RZq9V1G0Q0j+3Njce27Rj8beaoQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dUDgSu4Ypgy1av/Wg3do4/8LqCOUGD8i0yH2sR6lSI2BmUnpjVX/tXmQACsuj2nqUURMDR0nISLDexPPSpZECI5cfB3yNqdctV0dzcamXIO5JUTuA/XejosZtOmqDtJ9qWguJqEsYC7E8l+JR/4gWFLlcTTCdqupkH7A0zj4vB4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VSDXPpIb; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VSDXPpIb" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2cc891373e0so37542615ad.2 for ; Mon, 31 Aug 2026 18:04:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224694; x=1788829494; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xGgjKe37C8O7WXzR8vYD3Gt2KMUFwTc1B/YEqcfdSWU=; b=VSDXPpIba2vhR58Bk/6sSHrhsDqP1auIwvd+H2yJtUgp+d9Vcj+K8JO/wH6S6+NGEr 7tQA4rLyIaJq/SczBG2IRifn/ObFimuicaKwhRCvhhkX1grDqR1G+qNMNro5pJ/3MTww Hr9i7na9ZEVcqzxi0AaHSUkPmljYC6FfjC6ONmq3lxrOfvQlwQa9/f514ja3qP4/Rryv /wFuOOuLTI2RJXZrIFUeEdvOS38vE/NLGtgQqoHNEdBNkgAA4TUDWvkubPaus7TPWseg Foaub8i9ed+WjE7iP4l96Sv3FICwxpfkosDiGEoiObRUxl1n4DVQ0l9LjKMAP1Ay+CND 8vhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224694; x=1788829494; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xGgjKe37C8O7WXzR8vYD3Gt2KMUFwTc1B/YEqcfdSWU=; b=kuOwVuxq51vy3tYLkDaqOzRUoqfWPhs9xIPrnKV93dkp5vjJaOSCGbO7Vyf2hgCIQC bvoIO2BPChlyQvedSiWf4flJ4iFYwlB/DCsLjt9i9uRIizDntJ5azXuRygfWINk6jsbf z7C/nuknwk0q/GDN2hUob5GkX6VtYsXTD4xGpWGrSXLTmFgDP+Lpkeffbr1lAMSa2fp4 b3VX/U2Tyvqxj4GP2i4d2PQew/VvnnUuuNXbR+ze0ygzmCRrVPvnhkjUl3x8rf76EJyK NJpmtXjUQT0Moql+hcsrnnpdykbzThRmLBlf6CyW8Ub9zDuXRwj70yWp/eLo7ljrnKeu EIyQ== X-Forwarded-Encrypted: i=1; AKwUvBzJks1G8MSTqed2fUQZQ1tZsxzTUROuJ/TRua2RqI3CqR+PKEZ0KyhCmOwbsSbV9rrsksWDR790yb5yVVE=@vger.kernel.org X-Gm-Message-State: AFuF++kkrcdwW9rjGiiwU4YzdAc0CF6NqBHmPDTE9HnxmD6CSmfKKj/I ybY8l0HVra5+FdIpiYBXpK10hi43cKQhvcKirl18mJNK7fqhwO8shZdX X-Gm-Gg: AYBFou1/oa75OqX3vcuANQcMBJnXVa5OfKAfl9kjihJQ0VQ/E73DxOATPPIAAdH6Kul BFnM3RGWLIrQ/jsJaVVS6xaQGDsJ/5N/ngeUNtlc3reaNRE7K/0Yw9X8mTZ2LRn2Xi40peXH9xD VTwv0yRa74nTIuPZLJbusmIY66LdC7hOqBBotbJ8wNGNxvHWy0cOXujheqMu+EU98Ze0MlVszCr Ut5C1MJSVnfY+upfdlm9IiBYYTJkkeME/IcoNfcKa3O65wYOamNqWE7fmfQsOjlKdPSREmnBnse h8tTuZzbrnCZtpUNQGbeETuYRIQLBetvD58VdLz4iz8DA6lxrYMH7UTw93lk/TL45r7N8ISf5OI bW6k743Km7iwXERdlUEmbScyJYORdXECbx5cmdPPAPDhkC55zzW7pqIgyCdHOavR/gk3fStwP+H vt1619lgvePwA9ZLJVAu0mh24bl//sJU0hB/7GbBPYz9Cs4xgNQiX6JZeP/k5CE967alRvyQhr+ st0v7oreE8R0+jm X-Received: by 2002:a17:90b:538e:b0:398:e436:384 with SMTP id 98e67ed59e1d1-398e436066bmr15200914a91.1.1788224693739; Mon, 31 Aug 2026 18:04:53 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:53 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Dan Williams , Alistair Francis Subject: [PATCH v3 06/21] X.509: Move certificate length retrieval into new helper Date: Tue, 1 Sep 2026 11:03:32 +1000 Message-ID: <20260901010347.2614656-7-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Lukas Wunner The upcoming in-kernel SPDM library (Security Protocol and Data Model, https://www.dmtf.org/dsp/DSP0274) needs to retrieve the length from ASN.1 DER-encoded X.509 certificates. Such code already exists in x509_load_certificate_list(), so move it into a new helper for reuse by SPDM. Export the helper so that SPDM can be tristate. (Some upcoming users of the SPDM libray may be modular, such as SCSI and ATA.) No functional change intended. Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Jonathan Cameron --- crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++--------- include/keys/asymmetric-type.h | 2 ++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/= x509_loader.c index 0d516c77cc26..174451b93eda 100644 --- a/crypto/asymmetric_keys/x509_loader.c +++ b/crypto/asymmetric_keys/x509_loader.c @@ -4,28 +4,42 @@ #include #include =20 +ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen) +{ + ssize_t plen; + + /* Each cert begins with an ASN.1 SEQUENCE tag and must be more + * than 256 bytes in size. + */ + if (buflen < 4) + return -EINVAL; + + if (p[0] !=3D 0x30 || + p[1] !=3D 0x82) + return -EINVAL; + + plen =3D (p[2] << 8) | p[3]; + plen +=3D 4; + if (plen > buflen) + return -EINVAL; + + return plen; +} +EXPORT_SYMBOL_GPL(x509_get_certificate_length); + int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size, const struct key *keyring) { key_ref_t key; const u8 *p, *end; - size_t plen; + ssize_t plen; =20 p =3D cert_list; end =3D p + list_size; while (p < end) { - /* Each cert begins with an ASN.1 SEQUENCE tag and must be more - * than 256 bytes in size. - */ - if (end - p < 4) - goto dodgy_cert; - if (p[0] !=3D 0x30 || - p[1] !=3D 0x82) - goto dodgy_cert; - plen =3D (p[2] << 8) | p[3]; - plen +=3D 4; - if (plen > end - p) + plen =3D x509_get_certificate_length(p, end - p); + if (plen < 0) goto dodgy_cert; =20 key =3D key_create_or_update(make_key_ref(keyring, 1), diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 1b91c8f98688..301efa952e26 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -84,6 +84,8 @@ extern struct key *find_asymmetric_key(struct key *keyrin= g, const struct asymmetric_key_id *id_2, bool partial); =20 +ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen); + int x509_load_certificate_list(const u8 cert_list[], const unsigned long l= ist_size, const struct key *keyring); =20 --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 22ED13988F9 for ; Tue, 1 Sep 2026 01:05:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224704; cv=none; b=PkxYhfxT02s/8fATpGJVPy+wmMIqwQnBs02Ds1Rd2dl2h5vIQbOr1HO4LOtaQI0KocwvfxS8pYKfHgEuP374sGNIf4IP8hcsb4B3M75Dw5hiPTFvW2c8pj+6JSXl4CxdyyYCYkngL+iEeLC5M8wcHZbukiDIO6XOi/maiuL2JZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224704; c=relaxed/simple; bh=7H9GI30AsWmAlTdg30uohie4gLRcANCA6UmHmEupp3o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZuKOD50K3h0RPXxCuOURQmwb48w4lNo/ffGQj8xnryETEaRSB4JwIY0pKxtmPaHa7U4Lkyk16FsRrCNmU9DPYNcEcBdgCPFxdlnntyMsktAbZFm5p8alCGqFNLLP7J8cu6rHUuav57PejxX9+dSVr+UB8dkKYmLSM9v7v8QXhsw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d+YQ2TYC; arc=none smtp.client-ip=209.85.215.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d+YQ2TYC" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-cc1c73645a1so331403a12.1 for ; Mon, 31 Aug 2026 18:05:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224702; x=1788829502; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jWbFM6ywa1yjj692OE0MrLQnEnm7JyCgqunSYul2orw=; b=d+YQ2TYCuKChwoPQfL5IsXw8KZDWudgQ5nl55KRLhDWcU/gJC7uZKUR3Rsa/hvrFIu 1zjw7xN2aiWcDdflQlzlDhDMg77O2eY3YssOVRaf2XiZtm5rediRfOrjYTmzSJdEpWTP Etuy2TgNIKbTKKGOWNpne0L+sq8ENRQYtdnHtwvhCBmxZg6KB9F4g4A7X1n4M+PJpz9Y ILDCryRfLkRlNnRi8i7BYNjxR/d4/TkjfRKcXgwPLCXMbRaU/8yJHZzS4/uOnjuup5va Ww4MNUHPwhYNpt1U3+/xZ5Ou4r0FnLhO2m7lDhhLye1J9nQ4GWB69Gk2gc+HtyXqHFD8 uxhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224702; x=1788829502; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jWbFM6ywa1yjj692OE0MrLQnEnm7JyCgqunSYul2orw=; b=KxQggUaP2gJeoB4ETGgpFHXH4wVKtNRRnPD2t6vOutcmtbme1+a6ymZtskt+Su+YwO 7n/ssu/SLayFvrWPlh1ImQ4LgX7iC/xtpt2cWI/JTOvqO5PsO0YGKOD8CtQ2mGX+kI7m ty7hGa+ae20Cz34Vuos81InyDmpsXJ0fAsS0u0gmfUL37jyDkwWwaKqe+eA3ff3F1PkK tawAnOq23j54+EJUJhhuTkWDTWX4kgRuciqSAXLjEmZQ1kzBvXzrxbtD3SFuJmYy7wwe 9IuWVBkb3nnRgx+n1GYVOarlXxDVo7Kqi+1dQNeIAPhvYwJ4Q0rV64tUoCDshuGsnCe7 Fn1w== X-Forwarded-Encrypted: i=1; AKwUvBy+k4RCodomCScLhAJbcher+a6Q5aPky6E/W07IKeE+zrvdHf7pzn2aNhdHzK/rPmnFy0c3yFe0d/y8cLk=@vger.kernel.org X-Gm-Message-State: AFuF++lLY10IJHZeTTPFXAqOdCyMtfepXl/fqxnFEnxmIYoCF5Rz9Hdg rIQdC+O+GbKwtGMUUt7fAj8qsKGgDZ6WnpYQCjbv1nknFtPI/ikAJP18 X-Gm-Gg: AYBFou3BgjmhvOFo34owcuYZxTCM5knXcKBnpv9JQDJtp8Y3rSipQ1jwQGDzAm2AU1c 18ws6DcfxXPWL5hjRk4lAW6OIG1W8nralkEcyU61uYQxJfu6+mNSj5uxbgbu7egSvd2mCl139AD 6vyK97pMPNbbJv0oM0ommKEmawviw2Hmi4orpjeMfBA7YD18DU3oczxm+WmLEbzmoNJy+v7/5nT W3X/sXuccUFMC6w6aedgWQpsd0l7OovXfaSc/1T5yRCwcpE8xdniS7GFcaXaQPvqke6SuMYGExX O709dP6wyzXG5CHovcmc0Y+mPpphcErs8KpFmZdI029x+SY/LhTQ716BNpo3YVDS9GHzcBkCJbo h6uD2cf1MnBl/3kDkFM8n0meOfAoQ8Op4bYEP5rbC5ho1swDttfW886s9mD+cSO4MbvctXB9hCd gUUaHGwSJ5O4k1nDHfuFbkHu/VMNQf/n/M5nqjzhC2ZXpGVjV7ruHUkv78UgeuiTeNy6cbWrnz1 oGG+A== X-Received: by 2002:a17:90b:164b:b0:38e:6a44:671b with SMTP id 98e67ed59e1d1-396d0c79e8amr44052502a91.0.1788224702403; Mon, 31 Aug 2026 18:05:02 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:01 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 07/21] rust: add bindings for hash.h Date: Tue, 1 Sep 2026 11:03:33 +1000 Message-ID: <20260901010347.2614656-8-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Make the functions crypto_shash_descsize(), crypto_shash_digestsize() and crypto_free_shash() available to Rust. Signed-off-by: Alistair Francis --- rust/bindings/bindings_helper.h | 1 + rust/helpers/hash.c | 18 ++++++++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 20 insertions(+) create mode 100644 rust/helpers/hash.c diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 4b31aa7f432f..fd223b6c5aaf 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ =20 #include #include +#include #include #include #include diff --git a/rust/helpers/hash.c b/rust/helpers/hash.c new file mode 100644 index 000000000000..23a63618a370 --- /dev/null +++ b/rust/helpers/hash.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__rust_helper unsigned int rust_helper_crypto_shash_descsize(struct crypto= _shash *tfm) +{ + return crypto_shash_descsize(tfm); +} + +__rust_helper unsigned int rust_helper_crypto_shash_digestsize(struct cryp= to_shash *tfm) +{ + return crypto_shash_digestsize(tfm); +} + +__rust_helper void rust_helper_crypto_free_shash(struct crypto_shash *tfm) +{ + crypto_free_shash(tfm); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 440fb7638e3c..09ec69f736b1 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -66,6 +66,7 @@ #include "fs.c" #include "fwctl.c" #include "gpu.c" +#include "hash.c" #include "interrupt.c" #include "io.c" #include "irq.c" --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 771A238E8CC for ; Tue, 1 Sep 2026 01:05:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224718; cv=none; b=H4/FQmSsGny90CZk1t/7eMb7lY2mUROC2J4Ex8LxjNNUiH96T92vtQYFgLf47uzJ7uJpZtmWliMPLBxcDOIq8rs8YgPyF94YZFnwWSzEUQoql4zmbOcqBHs3U/OS693VgQvVXSEsLZsJ83jvFhSU10gqsDPlefWzsOBVC+NN9kI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224718; c=relaxed/simple; bh=S/AOyWuaiA3NHvzmTE2CNReBuvvkbVK6T+Yewj+2g5Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KBu8Oq4razdqHk4rOM+cBr2IhGYWJeYoznQL+tiwAsDXVIeBqh5kyxfo6MtOboM2zaqki1+GId47L19x9KINIZTT89K7Sk1E24lHNnYHtbYJK6YuBJ1AEH/HeX4Anw2GxRLdhEoabKvQ71ZnGcpf0z3dVYCLsE67SGRVHiH4SGQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fd+bz32y; arc=none smtp.client-ip=209.85.216.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fd+bz32y" Received: by mail-pj1-f44.google.com with SMTP id 98e67ed59e1d1-38fdeaed181so6244171a91.1 for ; Mon, 31 Aug 2026 18:05:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224711; x=1788829511; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ZxuwZq03lYI2TGgQtjkc9QJfZ6xOq7ziiudQrg9Xx9o=; b=fd+bz32yCWvfXhKEUaxlixgV3kiy6TsUmNr5WbxryVjQpBcFI46cP5ABP7pCz5XlBU 3s0W9Rv+ct6iQSD08ircYVhfzpDJmh6EwZwD/GHWzMGQ8h7D8KgjvTR29luloQFpgtNt mt5c8n7k9kDfShQU06bWnRXsxv/KOVQwoZGRslhvCjYo4lf9h2Qav4vK0Gm5B1e4SnhF St+MQG0l1tM5umdMm4zGBSlgnYTA7RZuS8/TM7yp2GR1qy/6G3m5q2Cl9B+6LhnVuHRO Lq0WTaNI9lk5BNmNIujFUeTBROi8DFqEneJIhS+RM/icgq+oCc5txVxH1AJ6PGSH0FXA muAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224711; x=1788829511; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ZxuwZq03lYI2TGgQtjkc9QJfZ6xOq7ziiudQrg9Xx9o=; b=JRsWGsepE0mKKtMBWAWiHV6WOG9hmPJnIuu8ynvSPeCSJ+Fw0L/okA2vgcDuveMdnm 0wb2P2fpl22e6DVsJFOd9QcmDIvbiK6O3k9wT0gnu9I0qMiJbhw+/TIEZX+BUq5ohQmh QtsTKGkpxDbPhlIzWbk8sNwp8eXMTf8daKqgRnzqQvNuPSSKsNGaXXXbaz43IbzIZgGF zpv9Mgx/QiicXrp+7cW3LrKtvLR/08KpA0rmcYIgcntb1T+A2maG6k3uuQXnE6CvhGGg CcwIlgQ8uQmHbUfy6GQRShljxNxktR6GWlmNd4SNT4+Cru3I3dP5wWVv7oGe5miNv5Lo iHAg== X-Forwarded-Encrypted: i=1; AKwUvBz1I4IShtEtqJC3x7ICjsQIsYE8wpAP0we5Gke+kJmQqZKuzvRg+rwpWbrJ/FN1hcRpRrm3Ssryk/BXg1k=@vger.kernel.org X-Gm-Message-State: AFuF++mWtPhg3a3dRi+WJWM4jTssH0TANjQ03AzELBlcCQHTa1b6pQCA /wovkIoZYL0eIBtj+NXphueW/ZCIbNK+5P29huzoDEoCCGxfEtWVLywi X-Gm-Gg: AYBFou1qF/58snxKmpmsIRnXFOiFog0vugg9zeJsy2W1uUlaPYXrbKdGzpJVURshQiI cLyonVsRR0IxsT7jWe/MQNydtkorISEnr+WSLQ2X+yUyQHgq7KqNecWekjnvuoGugfcjlYMvU+F eEjZxRkV+oouaXwErpgEXq3CLf2fmzQwJfMP5m1Ue3yOFD36+j/Gytkc/9XKC91BpzlUdJuTqvR OCKIg8MHTOpCP+9h1o7docgG57TsiLo0xsK33w6Ka4TDxs28PhBjSf8fuNZnsJ/kGSJ0vFMGq0h PTKx2cKgdEBarxHvTSxxzutMfkeytngbPnB9vfDZH9Fn5xJxd6sE2zvkv01FuQq6ZSZig7HVoeK HctYydaaoV9sYej3Ggh88MKHIVkQTjm9dsEHwId3ZAUQhFNgY4CQOvVUGj7BTw2ssHj8+RSjRlX v79RCFgajvaFm2hyrG8JMSN0yhJXu+ygqPMyUIPLWp5wM+qVwDvgauRRiIq8LZt0XDGQmINIJAy F6YSfVr6uK0Mk3M X-Received: by 2002:a17:90b:5281:b0:38e:6aa7:68ad with SMTP id 98e67ed59e1d1-39907aff83dmr6183862a91.5.1788224711002; Mon, 31 Aug 2026 18:05:11 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:10 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 08/21] rust: error: impl From for Kernel Error Date: Tue, 1 Sep 2026 11:03:34 +1000 Message-ID: <20260901010347.2614656-9-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Implement From for the Kernel Error type as we will use it in a future patch. As this is now generally available we can remove it from the test case as well. Signed-off-by: Alistair Francis --- rust/kernel/error.rs | 18 +++++++++++++++--- rust/kernel/str.rs | 7 ------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e52793f77196..84e6466f54c2 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -12,9 +12,14 @@ str::CStr, }; =20 -use core::num::NonZeroI32; -use core::num::TryFromIntError; -use core::str::Utf8Error; +use core::{ + ffi::FromBytesWithNulError, + num::{ + NonZeroI32, + TryFromIntError, // + }, + str::Utf8Error, // +}; =20 /// Contains the C-compatible error codes. #[rustfmt::skip] @@ -358,6 +363,13 @@ fn from(e: core::convert::Infallible) -> Error { } } =20 +impl From for Error { + #[inline] + fn from(_: FromBytesWithNulError) -> Error { + code::EINVAL + } +} + /// A [`Result`] with an [`Error`] error type. /// /// To be used as the return type for functions that may fail. diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index b3caa9a1c898..a556788bcc5e 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -433,13 +433,6 @@ macro_rules! c_str { mod tests { use super::*; =20 - impl From for Error { - #[inline] - fn from(_: core::ffi::FromBytesWithNulError) -> Error { - EINVAL - } - } - macro_rules! format { ($($f:tt)*) =3D> ({ CString::try_from_fmt(fmt!($($f)*))?.to_str()? --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f42.google.com (mail-pj1-f42.google.com [209.85.216.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 663293905EA for ; Tue, 1 Sep 2026 01:05:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224723; cv=none; b=a969zqe+6kFJuKKZ3b26eonVPs+LngdnJ7aJiMQCRdhWGMq99rhpDR7T2Me+4TVqFg0/dIijb9U+6kD6a1zqeSw1XvMiripDosKtLaJrHJf1+0OLxGaMZelVH5GMGxPjL8wjSS3aGBHMq4Z+aGf3LwaBQi0eWkt89/vUUC1KV4U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224723; c=relaxed/simple; bh=Uw2VLwsoDp91lblHkHXCK6pxUVZWG1IlcXaL9x6cObI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MtA/JKixs+UBVuLryizqcWVXJk1RoLyfZ88dIIGX/LesmKmaieS033sqoi4wnusus258Fhi7q/jjvs3dqXDMBras7sZFMjA7pjpMapedsf+TW0dkle2ZSl1BIgHH2FpEoEf18H8RWRuuoYgrnqhGyFFxx0ZXwpiABgFz6VV2epk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hb3RvV1S; arc=none smtp.client-ip=209.85.216.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hb3RvV1S" Received: by mail-pj1-f42.google.com with SMTP id 98e67ed59e1d1-38dc4553f62so5782441a91.0 for ; Mon, 31 Aug 2026 18:05:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224720; x=1788829520; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6liPr/5nQxFlbLIR9zEJaaR5hSXlZrWz5NGh2sBJmek=; b=hb3RvV1SDX8OCyQWPbjkZ6ocprc97teInRzdYIspQgEQr/h7LQwm6Ty0O/M2XTn5o5 SLT9BE7afCo+F/5pasOultme4pvu3Jmw+4iIsV3/cHlNPqoTFzbgIArHAeCrxbuOj0AW dtUBuxNsU97JWIcEg5p0YkgFrPjxri3Xs427ADD7zZwCkmV7Me4l7W7t9Vcsh1C0OVNk Rfc/hJLKvYCAdfkga+g4zHLZyNKQjdl5bixmfEHFgxFZDScR7pOFrDdwkBBiEoXmcpqc UcvCGgygrNKyLumEf5nSuLnL8MN8AR1H/DTDtsv6Rpssfd1QwMkyzGbA41f4YNa/4tyv SbQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224720; x=1788829520; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=6liPr/5nQxFlbLIR9zEJaaR5hSXlZrWz5NGh2sBJmek=; b=RaQ3Dpw5pH4MWZEn1LLU7MBMpihibOFGZJMoswgwABDbR4+gQsTAFzBOK0p3QU/BWI wamo6akRyMQrT7nQ55qjpuJ06BfU0AYqjTsovb4lEGQZtXHQvY0qWlphMLg5884fd+wR zO8MBzgh40A5jk9U2ZomTGGLXmCfzGfPEmVdVdNBtiuWntpwsLpRugyydu98K+xtsyZT lOqimO2tVeq2TyPxceLNXWE/uN+CmdSS0hqa6V01AYXnaRYjnC+Li04k71JLgRX3zlY9 JvcQ3p+se5wWSK3Wzz/aFixFaKFGPOGIBWYrjn7XFVNa0N2RI/vUa/tfjLuv2Ty5hlMg 2LAA== X-Forwarded-Encrypted: i=1; AKwUvBzlGPX+yTUcmtIdd5q1H1dYoVvJFPIEom2bBKIz3LPOh335IEqdZnkjNPq0huEkUPDjSn0GHdv9R6vO770=@vger.kernel.org X-Gm-Message-State: AFuF++k8kMI1gtoI/r0yH2P6QNAUfg+zx5LaNxXNhTCuUDnVzA1GZmfY oFUCDpsHZxYxfsVCqy0Wy0CcnBCIjxst1ZZghAiO+gIwAVPuqze64DSU X-Gm-Gg: AYBFou1Jl2mZAuANy3HBUhO6Ei8tgHBGLBvYl533jg9loGfT/D/+85aWYRPmgqsRCcZ ApUCpquQoEoRfQLCmamQPxRU4XGj56xq65tDMb6ZgsiGdAOmC5HvF8WF8BLN+C28IN/WwXAeOTI W0QOYPiyYrtG6tIOP6Gu6znx/uRg/xidRIrN3T7kn4a+AuBh3Lnh//4FkUnNGzqSpocotsrdABR d6YfeGc7EhZP5ex2Em38sUjtBjSCCKOYk/chsYoJOApjB3Yw7i2r04PQB39x27ws2if+PGwqyav wdVvx37C22B+iWbxRhlae4J0ynGyWLDFv8a05i7+273e3BiKQ6exYwWXzwVOGrAVVyibLGb/bEI LY2bRGYc4zI36M7xZCUrVbP5P1Y9/rNECZgumJHhzflUbStnPrLZcCNCtuYBMW3pwW+TyHQoGRU Y/Nzggl7TehdJ3PlZlxNEInTIUAvLDcwE9B1kR4y9Yvse3RTIEB8FGN4q9dxYS+LfFMINThnR9C QcT7A== X-Received: by 2002:a17:90b:2686:b0:396:635a:9b10 with SMTP id 98e67ed59e1d1-39907cd5327mr6599078a91.11.1788224719415; Mon, 31 Aug 2026 18:05:19 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:18 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 09/21] lib: rspdm: Initial commit of Rust SPDM Date: Tue, 1 Sep 2026 11:03:35 +1000 Message-ID: <20260901010347.2614656-10-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis This is the initial commit of the Rust SPDM library. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- MAINTAINERS | 12 ++ include/linux/spdm.h | 37 +++++ lib/Kconfig | 15 ++ lib/Makefile | 2 + lib/rspdm/Makefile | 10 ++ lib/rspdm/consts.rs | 92 +++++++++++++ lib/rspdm/lib.rs | 89 ++++++++++++ lib/rspdm/state.rs | 237 ++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 92 +++++++++++++ rust/bindings/bindings_helper.h | 1 + 10 files changed, 587 insertions(+) create mode 100644 include/linux/spdm.h create mode 100644 lib/rspdm/Makefile create mode 100644 lib/rspdm/consts.rs create mode 100644 lib/rspdm/lib.rs create mode 100644 lib/rspdm/state.rs create mode 100644 lib/rspdm/validator.rs diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..36f84a02894b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24753,6 +24753,18 @@ M: Security Officers S: Supported F: Documentation/process/security-bugs.rst =20 +SECURITY PROTOCOL AND DATA MODEL (SPDM) +M: Jonathan Cameron +M: Lukas Wunner +M: Alistair Francis +L: linux-coco@lists.linux.dev +L: linux-cxl@vger.kernel.org +L: linux-pci@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git +F: include/linux/spdm.h +F: lib/rspdm/ + SECURITY SUBSYSTEM M: Paul Moore M: James Morris diff --git a/include/linux/spdm.h b/include/linux/spdm.h new file mode 100644 index 000000000000..1f7207b584a8 --- /dev/null +++ b/include/linux/spdm.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#ifndef _SPDM_H_ +#define _SPDM_H_ + +#include + +struct key; +struct device; +struct spdm_state; +struct x509_certificate; + +typedef int (spdm_transport)(void *priv, struct device *dev, + const void *request, size_t request_sz, + void *response, size_t response_sz); + +typedef int (spdm_validate)(struct device *dev, u8 slot, + struct x509_certificate *leaf_cert); + +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transpo= rt, + void *transport_priv, u32 transport_sz, + spdm_validate *validate); + +int spdm_authenticate(struct spdm_state *spdm_state); + +void spdm_destroy(struct spdm_state *spdm_state); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 4e6b34c3346d..b1792db6ddf0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -588,6 +588,21 @@ config LWQ_TEST help Run boot-time test of light-weight queuing. =20 +config RSPDM + bool "Rust SPDM" + depends on RUST + select ASYMMETRIC_KEY_TYPE + select CRYPTO + select X509_CERTIFICATE_PARSER + help + The Rust implementation of the Security Protocol and Data Model (SPDM) + allows for device authentication, measurement, key exchange and + encrypted sessions. + + Crypto algorithms negotiated with SPDM are limited to those enabled + in .config. Users of SPDM therefore need to also select + any algorithms they deem mandatory. + endmenu =20 config GENERIC_IOREMAP diff --git a/lib/Makefile b/lib/Makefile index dfab958327c5..22776b7e9416 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -297,6 +297,8 @@ obj-$(CONFIG_PERCPU_TEST) +=3D percpu_test.o obj-$(CONFIG_ASN1) +=3D asn1_decoder.o obj-$(CONFIG_ASN1_ENCODER) +=3D asn1_encoder.o =20 +obj-$(CONFIG_RSPDM) +=3D rspdm/ + obj-$(CONFIG_FONT_SUPPORT) +=3D fonts/ =20 # diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile new file mode 100644 index 000000000000..1f62ee2a882d --- /dev/null +++ b/lib/rspdm/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +# https://www.dmtf.org/dsp/DSP0274 +# +# Copyright (C) 2024 Western Digital + +obj-$(CONFIG_RSPDM) +=3D spdm.o + +spdm-y :=3D lib.o diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs new file mode 100644 index 000000000000..01f008958a1f --- /dev/null +++ b/lib/rspdm/consts.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! Constants used by the library +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! + +use kernel::error::{code::EINVAL, Error}; + +// SPDM versions supported by this implementation +pub(crate) const SPDM_VER_10: u8 =3D 0x10; + +pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; + +#[allow(dead_code)] +pub(crate) const SPDM_REQ: u8 =3D 0x80; +#[allow(dead_code)] +pub(crate) const SPDM_ERROR: u8 =3D 0x7f; + +#[derive(Clone, Copy)] +#[repr(u8)] +pub(crate) enum SpdmErrorCode { + InvalidRequest =3D 0x01, + /// This was removed in version 1.2.0 and is now reserved + InvalidSession =3D 0x02, + Busy =3D 0x03, + UnexpectedRequest =3D 0x04, + Unspecified =3D 0x05, + DecryptError =3D 0x06, + UnsupportedRequest =3D 0x07, + RequestInFlight =3D 0x08, + InvalidResponseCode =3D 0x09, + SessionLimitExceeded =3D 0x0a, + SessionRequired =3D 0x0b, + ResetRequired =3D 0x0c, + ResponseTooLarge =3D 0x0d, + RequestTooLarge =3D 0x0e, + LargeResponse =3D 0x0f, + MessageLost =3D 0x10, + InvalidPolicy =3D 0x11, + VersionMismatch =3D 0x41, + ResponseNotReady =3D 0x42, + RequestResynch =3D 0x43, + OperationFailed =3D 0x44, + NoPendingRequests =3D 0x45, + RequestSessionTerminated =3D 0x46, + InvalidState =3D 0x47, + VendorDefinedError =3D 0xff, +} + +impl TryFrom for SpdmErrorCode { + type Error =3D Error; + + fn try_from(value: u8) -> Result { + Ok(match value { + 0x01 =3D> Self::InvalidRequest, + 0x02 =3D> Self::InvalidSession, + 0x03 =3D> Self::Busy, + 0x04 =3D> Self::UnexpectedRequest, + 0x05 =3D> Self::Unspecified, + 0x06 =3D> Self::DecryptError, + 0x07 =3D> Self::UnsupportedRequest, + 0x08 =3D> Self::RequestInFlight, + 0x09 =3D> Self::InvalidResponseCode, + 0x0a =3D> Self::SessionLimitExceeded, + 0x0b =3D> Self::SessionRequired, + 0x0c =3D> Self::ResetRequired, + 0x0d =3D> Self::ResponseTooLarge, + 0x0e =3D> Self::RequestTooLarge, + 0x0f =3D> Self::LargeResponse, + 0x10 =3D> Self::MessageLost, + 0x11 =3D> Self::InvalidPolicy, + 0x41 =3D> Self::VersionMismatch, + 0x42 =3D> Self::ResponseNotReady, + 0x43 =3D> Self::RequestResynch, + 0x44 =3D> Self::OperationFailed, + 0x45 =3D> Self::NoPendingRequests, + 0x46 =3D> Self::RequestSessionTerminated, + 0x47 =3D> Self::InvalidState, + 0xff =3D> Self::VendorDefinedError, + _ =3D> return Err(EINVAL), + }) + } +} + +impl core::fmt::LowerHex for SpdmErrorCode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#x}", *self as u8) + } +} diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs new file mode 100644 index 000000000000..1883579b817a --- /dev/null +++ b/lib/rspdm/lib.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! Top level library for SPDM +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! +//! +//! Top level library, including C compatible public functions to be called +//! from other subsytems. + +use crate::bindings::{ + spdm_state, + EPROTONOSUPPORT, // +}; +use core::ffi::{ + c_int, + c_void, // +}; +use core::ptr; +use kernel::prelude::*; +use kernel::{ + alloc::flags, + bindings, // +}; + +use crate::state::SpdmState; + +const __LOG_PREFIX: &[u8] =3D b"spdm\0"; + +mod consts; +mod state; +mod validator; + +/// spdm_create() - Allocate SPDM session +/// +/// `dev`: Responder device +/// `transport`: Transport function to perform one message exchange +/// `transport_priv`: Transport private data +/// `transport_sz`: Maximum message size the transport is capable of (in b= ytes) +/// `validate`: Function to validate additional leaf certificate requireme= nts +/// (optional, may be %NULL) +/// +/// Return a pointer to the allocated SPDM session state or NULL on error. +#[export] +pub extern "C" fn spdm_create( + dev: *mut bindings::device, + transport: bindings::spdm_transport, + transport_priv: *mut c_void, + transport_sz: u32, + validate: bindings::spdm_validate, +) -> *mut spdm_state { + match KBox::new( + SpdmState::new(dev, transport, transport_priv, transport_sz, valid= ate), + flags::GFP_KERNEL, + ) { + Ok(ret) =3D> KBox::into_raw(ret) as *mut spdm_state, + Err(_) =3D> ptr::null_mut(), + } +} + +/// spdm_authenticate() - Authenticate device +/// +/// @spdm_state: SPDM session state +/// +/// Authenticate a device through a sequence of GET_VERSION, GET_CAPABILIT= IES, +/// NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE and CHALLENGE excha= nges. +/// +/// Return 0 on success or a negative errno. In particular, -EPROTONOSUPP= ORT +/// indicates authentication is not supported by the device. +#[export] +pub extern "C" fn spdm_authenticate(_state_ptr: *mut spdm_state) -> c_int { + -(EPROTONOSUPPORT as i32) +} + +/// spdm_destroy() - Destroy SPDM session +/// +/// @spdm_state: SPDM session state +#[export] +pub extern "C" fn spdm_destroy(state_ptr: *mut spdm_state) { + if state_ptr.is_null() { + return; + } + // SAFETY: `state_ptr` was returned from `spdm_create` (which uses + // `KBox::into_raw`) and the caller guarantees the state is no longer + // in use. Reconstructing the `KBox` and dropping it frees the state. + drop(unsafe { KBox::from_raw(state_ptr as *mut SpdmState) }); +} diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs new file mode 100644 index 000000000000..e1f74d19ac4b --- /dev/null +++ b/lib/rspdm/state.rs @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! The `SpdmState` struct and implementation. +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! + +use core::ffi::c_void; +use kernel::prelude::*; +use kernel::{ + bindings, + error::{ + code::EINVAL, + to_result, + Error, // + }, + validate::Untrusted, +}; + +use crate::consts::{ + SpdmErrorCode, + SPDM_ERROR, + SPDM_MIN_VER, + SPDM_REQ, // +}; +use crate::validator::{ + SpdmErrorRsp, + SpdmHeader, // +}; + +/// The current SPDM session state for a device. Based on the +/// C `struct spdm_state`. +/// +/// `dev`: Responder device. Used for error reporting and passed to @tran= sport. +/// `transport`: Transport function to perform one message exchange. +/// `transport_priv`: Transport private data. +/// `transport_sz`: Maximum message size the transport is capable of (in b= ytes). +/// Used as DataTransferSize in GET_CAPABILITIES exchange. +/// `validate`: Function to validate additional leaf certificate requireme= nts. +/// +/// `version`: Maximum common supported version of requester and responder. +/// Negotiated during GET_VERSION exchange. +#[expect(dead_code)] +pub(crate) struct SpdmState { + pub(crate) dev: *mut bindings::device, + pub(crate) transport: bindings::spdm_transport, + pub(crate) transport_priv: *mut c_void, + pub(crate) transport_sz: u32, + pub(crate) validate: bindings::spdm_validate, + + // Negotiated state + pub(crate) version: u8, +} + +impl SpdmState { + pub(crate) fn new( + dev: *mut bindings::device, + transport: bindings::spdm_transport, + transport_priv: *mut c_void, + transport_sz: u32, + validate: bindings::spdm_validate, + ) -> Self { + SpdmState { + dev, + transport, + transport_priv, + transport_sz, + validate, + version: SPDM_MIN_VER, + } + } + + #[allow(dead_code)] + fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> { + match rsp.error_code { + SpdmErrorCode::InvalidRequest =3D> { + pr_err!("Invalid request\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidSession =3D> { + if rsp.version =3D=3D 0x11 { + pr_err!("Invalid session {:#x}\n", rsp.error_data); + Err(EINVAL) + } else { + pr_err!("Undefined error {:#x}\n", rsp.error_code); + Err(EINVAL) + } + } + SpdmErrorCode::Busy =3D> { + pr_err!("Busy\n"); + Err(EBUSY) + } + SpdmErrorCode::UnexpectedRequest =3D> { + pr_err!("Unexpected request\n"); + Err(EINVAL) + } + SpdmErrorCode::Unspecified =3D> { + pr_err!("Unspecified error\n"); + Err(EINVAL) + } + SpdmErrorCode::DecryptError =3D> { + pr_err!("Decrypt error\n"); + Err(EIO) + } + SpdmErrorCode::UnsupportedRequest =3D> { + pr_err!("Unsupported request {:#x}\n", rsp.error_data); + Err(EINVAL) + } + SpdmErrorCode::RequestInFlight =3D> { + pr_err!("Request in flight\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidResponseCode =3D> { + pr_err!("Invalid response code\n"); + Err(EINVAL) + } + SpdmErrorCode::SessionLimitExceeded =3D> { + pr_err!("Session limit exceeded\n"); + Err(EBUSY) + } + SpdmErrorCode::SessionRequired =3D> { + pr_err!("Session required\n"); + Err(EINVAL) + } + SpdmErrorCode::ResetRequired =3D> { + pr_err!("Reset required\n"); + Err(ECONNRESET) + } + SpdmErrorCode::ResponseTooLarge =3D> { + pr_err!("Response too large\n"); + Err(EINVAL) + } + SpdmErrorCode::RequestTooLarge =3D> { + pr_err!("Request too large\n"); + Err(EINVAL) + } + SpdmErrorCode::LargeResponse =3D> { + pr_err!("Large response\n"); + Err(EMSGSIZE) + } + SpdmErrorCode::MessageLost =3D> { + pr_err!("Message lost\n"); + Err(EIO) + } + SpdmErrorCode::InvalidPolicy =3D> { + pr_err!("Invalid policy\n"); + Err(EINVAL) + } + SpdmErrorCode::VersionMismatch =3D> { + pr_err!("Version mismatch\n"); + Err(EINVAL) + } + SpdmErrorCode::ResponseNotReady =3D> { + pr_err!("Response not ready\n"); + Err(EINPROGRESS) + } + SpdmErrorCode::RequestResynch =3D> { + pr_err!("Request resynchronization\n"); + Err(ECONNRESET) + } + SpdmErrorCode::OperationFailed =3D> { + pr_err!("Operation failed\n"); + Err(EINVAL) + } + SpdmErrorCode::NoPendingRequests =3D> Err(ENOENT), + SpdmErrorCode::VendorDefinedError =3D> { + pr_err!("Vendor defined error\n"); + Err(EINVAL) + } + SpdmErrorCode::RequestSessionTerminated =3D> { + pr_err!("Request session terminated\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidState =3D> { + pr_err!("Invalid State\n"); + Err(EINVAL) + } + } + } + + /// Start a SPDM exchange + /// + /// The data in `request_buf` is sent to the device and the response is + /// stored in `response_buf`. + #[allow(dead_code)] + pub(crate) fn spdm_exchange( + &self, + request_buf: &mut [u8], + response_buf: &mut [u8], + ) -> Result { + let header_size =3D core::mem::size_of::(); + let request: &SpdmHeader =3D Untrusted::new(&request_buf[..]).vali= date()?; + + let transport_function =3D self.transport.ok_or(EINVAL)?; + // SAFETY: `transport_function` is provided by the new(), we are + // calling the function. + // We have a immutable reference to request_buf above, and pass + // another reference here. + // We don't have any references to the mutable response_buf + let length =3D unsafe { + transport_function( + self.transport_priv, + self.dev, + request_buf.as_ptr() as *const c_void, + request_buf.len(), + response_buf.as_mut_ptr() as *mut c_void, + response_buf.len(), + ) as i32 + }; + to_result(length)?; + + if (length as usize) < header_size { + return Ok(length); // Truncated response is handled by callers + } + + let response: &SpdmHeader =3D Untrusted::new(&response_buf[..]).va= lidate()?; + + if response.code =3D=3D SPDM_ERROR { + let error_rsp: &SpdmErrorRsp =3D + Untrusted::new(&response_buf[..header_size as usize]).vali= date()?; + self.spdm_err(error_rsp)?; + } + + if response.code !=3D request.code & !SPDM_REQ { + pr_err!( + "Response code {:#x} does not match request code {:#x}\n", + response.code, + request.code + ); + return Err(EPROTO); + } + + Ok(length) + } +} diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs new file mode 100644 index 000000000000..323242e84580 --- /dev/null +++ b/lib/rspdm/validator.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! Related structs and their Validate implementations. +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! + +use crate::consts::SpdmErrorCode; +use core::mem; +use kernel::prelude::*; +use kernel::{ + error::{ + code::EINVAL, + Error, // + }, + validate::{ + Untrusted, + Validate, // + }, +}; + +#[repr(C, packed)] +pub(crate) struct SpdmHeader { + pub(crate) version: u8, + pub(crate) code: u8, /* RequestResponseCode */ + pub(crate) param1: u8, + pub(crate) param2: u8, +} + +impl Validate> for &SpdmHeader { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `SpdmHeader` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + Ok(unsafe { &*ptr }) + } +} + +impl Validate> for &mut SpdmHeader { + type Err =3D Error; + + fn validate(unvalidated: &mut [u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_mut_ptr(); + // CAST: `SpdmHeader` only contains integers and has `repr(C, pack= ed)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + Ok(unsafe { &mut *ptr }) + } +} + +#[repr(C, packed)] +pub(crate) struct SpdmErrorRsp { + pub(crate) version: u8, + /// This will always be SPDM_ERROR (0x7F) + pub(crate) code: u8, + pub(crate) error_code: SpdmErrorCode, + pub(crate) error_data: u8, +} + +impl<'a> Validate> for &'a SpdmErrorRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + // Reject responses whose `error_code` byte is not a known + // `SpdmErrorCode` discriminant before exposing the struct to call= ers. + SpdmErrorCode::try_from(unvalidated[mem::offset_of!(SpdmErrorRsp, = error_code)])?; + + let ptr =3D unvalidated.as_ptr(); + // CAST: `SpdmErrorRsp` only contains `u8` fields and `SpdmErrorCo= de` which + // we have already checked and has `repr(C, packed)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + Ok(unsafe { &*ptr }) + } +} diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index fd223b6c5aaf..d2781c27794b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -89,6 +89,7 @@ #include #include #include +#include #include #include #include --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1A68438D40F for ; Tue, 1 Sep 2026 01:05:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224733; cv=none; b=QlOdbIbNnAqvw5BsM4HKtTonfVHt0jTFHTDA7UEMmDGwL/JgklhcfKkL3M/G1aMTSr+lrHDMMB3FAKXkFLLq4BEQYvswbt4hmJOcE3Spc9zr2O2sAcJlgPWwchzEzRdApSpL5nKE/spHwoHJhTnD9QTDtOYZNOHvBXlYlAH+msY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224733; c=relaxed/simple; bh=AFZVSiROnXQsP5Ur1LhhZLtsCD7jPaFWVWhrWRXhmE0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kAgCXM9XwyFTAZUBS2F1EzG2uYauR+L3O9A40kLBslZme9mBMqcDUxtiZX/g1hTmOrahBKjzR3ho6oveS7dMUznhZyD78Dkevom0Q5uRG9THeoGnKs7uy0Sg07C4eBhT+eDbj/aJvog1W44uquFe1OLdMuAhJYxEe803s/sZ+Ew= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GpI5bSNN; arc=none smtp.client-ip=209.85.216.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GpI5bSNN" Received: by mail-pj1-f49.google.com with SMTP id 98e67ed59e1d1-3966791a6eeso5059367a91.3 for ; Mon, 31 Aug 2026 18:05:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224728; x=1788829528; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=eISXSD8r6vmZx+yib4Boijj6gpPPSZicamRzRPecgOk=; b=GpI5bSNNj3sI7UbLT3dZwI56PhUnXoyq9m8a3wdcba8uD5mh7yzXbs3E0RaDyao82e 8Nsf/F1dKLdb9WmCoIyyEiciGU3yw0fzbqHrObo2goNiJAy20Ff4qmoclZmXEmFiIGkn yWTRhgUlLBCD9/jKORY+nsE8k7Zn4VZvRDdyrnP+0H1Fhx/vLl0aZDBytm8B+Lkw2vjO 5ZFFvXwhXIs3OA5Su/pPdc1z0y5HOJRr2eo6VhezXAwphWGf/KZnCgpkQnKqyZy1+kEI 8/c0+EBTjDuhOIEzMf/WeEA/qAJzOd/dsu6ciV778SAijqIPFYIAfmrpigGP570Kfzch a5nQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224728; x=1788829528; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=eISXSD8r6vmZx+yib4Boijj6gpPPSZicamRzRPecgOk=; b=rwa6dCo+VwTriFQ4HWnMgHU42a3fR3uOAAAzny7bqIw/2EhIEC9e0UI6msZvvWKzeL xJpEPtqvmRzILt64MvXIY1vmOMaLGT9r4JF2pnY+LfcuqEN0LH9fBIkDc6kxnwmuXTxC 87/5IG6n3k27ZcgzD1Qetg0oxC6MvLceHFmLEtXu7b+ITQ8Mvkntbv0dAA+b4tHorO/K uofAlDweQgx6/N15Ug57cUwLy8Bya2a+YrUnfcZfT95DzfneY/4uS1hH/9GnJxo0NCrq di8/eDuO/yLgzg74QNR2vVS5tPklXGXnCrEAAwHoGza5wtaf6dS+OiPG9ui1ddcIYL25 MuHQ== X-Forwarded-Encrypted: i=1; AKwUvBxa3gxipxMqqR3h96si3v+XfK5QbAUSxHzrRE4caEjPROERU8e2EBjVRsuA4GxEujBcY6YHxXY3/DTU2zc=@vger.kernel.org X-Gm-Message-State: AFuF++mwZJUGlMJ/jCjzlYTXhPH+9QP+jY4sgNrLQMgKpxdy/Qr55CRb 3E/2hBH92xhq+uFzgJca7L19oQoGtRKbosXXFivWmtTs7UuPfybbc88t X-Gm-Gg: AYBFou1bK55uF28A4nfuNc3YSuBl3BxOrgLoCBY/Q0e9aoIWP1LzdJq5pXZmTuyDiGf H9tzfQTWRC51/NXV6Ib+EG2JBhmXqfYrsyFAuJbZFs/5sTPxOiv3GT1w8lEwl3/3d2eFcV5o7hF kuINhwVsSy50mKHLD6wfIZJqxAGUNAm2aDLy2dE3MiqAp2nD+ixmn8YlDoLM+6EItk/RsjXi1Ig wF/CokW/mHG5nqUKQul1t0CObOo7jRKHKSBvB5qG3ILoMrmRVylwKom7F6fqQCCdXh1SCe991Cm kH9MvZ4cnmjzLbnHJVJdZ0mjyfTgJswLjdBzMFCXFXPmZJHV4txIOhtswUYwjQQFEUeoL4ggTvc 8aFKKM6oNQ1MXUcPtLwftYVPaDi3ERYxlPTjeAISe3uAiG8fQo3HfDqgjywdFaxmCgK6F9NoLrX B1MmhOu93KR/8VTLWQ71Aeuy/55uixjZTI5C7dSzNfJv8RpEmLXdFgiDLgogd7Wvdh7Rn8Wig3J c4PTQ== X-Received: by 2002:a17:90b:394c:b0:398:9bd4:d12 with SMTP id 98e67ed59e1d1-39907e4ada0mr6637914a91.17.1788224728281; Mon, 31 Aug 2026 18:05:28 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:27 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 10/21] PCI/TSM: Rename pf0 to host Date: Tue, 1 Sep 2026 11:03:36 +1000 Message-ID: <20260901010347.2614656-11-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Rename pci_tsm_pf0 to pci_tsm_host (and rename variables and function names from pf0 to host) as part of converting pci_tsm_host to be any device that knows how to speak any of CMA, IDE, or TDISP. This commit just renames the functions and provides no functional change. That will happen in the next commit. Signed-off-by: Alistair Francis --- drivers/crypto/ccp/sev-dev-tio.h | 4 +- drivers/crypto/ccp/sev-dev-tsm.c | 12 ++--- drivers/pci/tsm.c | 84 ++++++++++++++++---------------- include/linux/pci-tsm.h | 18 ++++--- 4 files changed, 61 insertions(+), 57 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev-tio.h b/drivers/crypto/ccp/sev-dev-= tio.h index 67512b3dbc53..78d598686487 100644 --- a/drivers/crypto/ccp/sev-dev-tio.h +++ b/drivers/crypto/ccp/sev-dev-tio.h @@ -53,9 +53,9 @@ struct tsm_dsm_tio { struct pci_ide *ide[TIO_IDE_MAX_TC]; }; =20 -/* Describes TSM structure for PF0 pointed by pci_dev->tsm */ +/* Describes TSM structure for the link host pointed by pci_dev->tsm */ struct tio_dsm { - struct pci_tsm_pf0 tsm; + struct pci_tsm_host tsm; struct tsm_dsm_tio data; struct sev_device *sev; }; diff --git a/drivers/crypto/ccp/sev-dev-tsm.c b/drivers/crypto/ccp/sev-dev-= tsm.c index 46f2539d2d5a..a900fd22eac9 100644 --- a/drivers/crypto/ccp/sev-dev-tsm.c +++ b/drivers/crypto/ccp/sev-dev-tsm.c @@ -205,7 +205,7 @@ static int stream_alloc(struct pci_dev *pdev, struct pc= i_ide **ide, return 0; } =20 -static struct pci_tsm *tio_pf0_probe(struct pci_dev *pdev, struct sev_devi= ce *sev) +static struct pci_tsm *tio_host_probe(struct pci_dev *pdev, struct sev_dev= ice *sev) { struct tio_dsm *dsm __free(kfree) =3D kzalloc_obj(*dsm); int rc; @@ -213,7 +213,7 @@ static struct pci_tsm *tio_pf0_probe(struct pci_dev *pd= ev, struct sev_device *se if (!dsm) return NULL; =20 - rc =3D pci_tsm_pf0_constructor(pdev, &dsm->tsm, sev->tsmdev); + rc =3D pci_tsm_host_constructor(pdev, &dsm->tsm, sev->tsmdev); if (rc) return NULL; =20 @@ -226,8 +226,8 @@ static struct pci_tsm *dsm_probe(struct tsm_dev *tsmdev= , struct pci_dev *pdev) { struct sev_device *sev =3D tsm_dev_to_sev(tsmdev); =20 - if (is_pci_tsm_pf0(pdev)) - return tio_pf0_probe(pdev, sev); + if (is_pci_tsm_host(pdev)) + return tio_host_probe(pdev, sev); return NULL; } =20 @@ -237,10 +237,10 @@ static void dsm_remove(struct pci_tsm *tsm) =20 pci_dbg(pdev, "TSM disabled\n"); =20 - if (is_pci_tsm_pf0(pdev)) { + if (is_pci_tsm_host(pdev)) { struct tio_dsm *dsm =3D container_of(tsm, struct tio_dsm, tsm.base_tsm); =20 - pci_tsm_pf0_destructor(&dsm->tsm); + pci_tsm_host_destructor(&dsm->tsm); kfree(dsm); } } diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c index 5fdcd7f2e820..10c9c6696624 100644 --- a/drivers/pci/tsm.c +++ b/drivers/pci/tsm.c @@ -45,22 +45,22 @@ static inline bool has_tee(struct pci_dev *pdev) return pdev->devcap & PCI_EXP_DEVCAP_TEE; } =20 -/* 'struct pci_tsm_pf0' wraps 'struct pci_tsm' when ->dsm_dev =3D=3D ->pde= v (self) */ -static struct pci_tsm_pf0 *to_pci_tsm_pf0(struct pci_tsm *tsm) +/* 'struct pci_tsm_host' wraps 'struct pci_tsm' when ->dsm_dev =3D=3D ->pd= ev (self) */ +static struct pci_tsm_host *to_pci_tsm_host(struct pci_tsm *tsm) { /* * All "link" TSM contexts reference the device that hosts the DSM * interface for a set of devices. Walk to the DSM device and cast its - * ->tsm context to a 'struct pci_tsm_pf0 *'. + * ->tsm context to a 'struct pci_tsm_host *'. */ - struct pci_dev *pf0 =3D tsm->dsm_dev; + struct pci_dev *host =3D tsm->dsm_dev; =20 - if (!is_pci_tsm_pf0(pf0) || !is_dsm(pf0)) { + if (!is_pci_tsm_host(host) || !is_dsm(host)) { pci_WARN_ONCE(tsm->pdev, 1, "invalid context object\n"); return NULL; } =20 - return container_of(pf0->tsm, struct pci_tsm_pf0, base_tsm); + return container_of(host->tsm, struct pci_tsm_host, base_tsm); } =20 static void tsm_remove(struct pci_tsm *tsm) @@ -186,7 +186,7 @@ static int probe_fn(struct pci_dev *pdev, void *dsm) static int pci_tsm_connect(struct pci_dev *pdev, struct tsm_dev *tsm_dev) { int rc; - struct pci_tsm_pf0 *tsm_pf0; + struct pci_tsm_host *tsm_host; const struct pci_tsm_ops *ops =3D tsm_dev->pci_ops; struct pci_tsm *pci_tsm __free(tsm_remove) =3D ops->probe(tsm_dev, pdev); =20 @@ -197,10 +197,10 @@ static int pci_tsm_connect(struct pci_dev *pdev, stru= ct tsm_dev *tsm_dev) return -ENXIO; =20 pdev->tsm =3D pci_tsm; - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); + tsm_host =3D to_pci_tsm_host(pdev->tsm); =20 /* mutex_intr assumes connect() is always sysfs/user driven */ - ACQUIRE(mutex_intr, lock)(&tsm_pf0->lock); + ACQUIRE(mutex_intr, lock)(&tsm_host->lock); if ((rc =3D ACQUIRE_ERR(mutex_intr, &lock))) return rc; =20 @@ -300,15 +300,15 @@ static int remove_fn(struct pci_dev *pdev, void *data) static int __pci_tsm_unbind(struct pci_dev *pdev, void *data) { struct pci_tdi *tdi; - struct pci_tsm_pf0 *tsm_pf0; + struct pci_tsm_host *tsm_host; =20 lockdep_assert_held(&pci_tsm_rwsem); =20 if (!pdev->tsm) return 0; =20 - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); - guard(mutex)(&tsm_pf0->lock); + tsm_host =3D to_pci_tsm_host(pdev->tsm); + guard(mutex)(&tsm_host->lock); =20 tdi =3D pdev->tsm->tdi; if (!tdi) @@ -341,7 +341,7 @@ EXPORT_SYMBOL_GPL(pci_tsm_unbind); */ int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id) { - struct pci_tsm_pf0 *tsm_pf0; + struct pci_tsm_host *tsm_host; struct pci_tdi *tdi; =20 if (!kvm) @@ -355,8 +355,8 @@ int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm,= u32 tdi_id) if (!is_link_tsm(pdev->tsm->tsm_dev)) return -ENXIO; =20 - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); - guard(mutex)(&tsm_pf0->lock); + tsm_host =3D to_pci_tsm_host(pdev->tsm); + guard(mutex)(&tsm_host->lock); =20 /* Resolve races to bind a TDI */ if (pdev->tsm->tdi) { @@ -404,7 +404,7 @@ ssize_t pci_tsm_guest_req(struct pci_dev *pdev, enum pc= i_tsm_req_scope scope, sockptr_t req_in, size_t in_len, sockptr_t req_out, size_t out_len, u64 *tsm_code) { - struct pci_tsm_pf0 *tsm_pf0; + struct pci_tsm_host *tsm_host; struct pci_tdi *tdi; int rc; =20 @@ -422,8 +422,8 @@ ssize_t pci_tsm_guest_req(struct pci_dev *pdev, enum pc= i_tsm_req_scope scope, if (!is_link_tsm(pdev->tsm->tsm_dev)) return -ENXIO; =20 - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); - ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock); + tsm_host =3D to_pci_tsm_host(pdev->tsm); + ACQUIRE(mutex_intr, ops_lock)(&tsm_host->lock); if ((rc =3D ACQUIRE_ERR(mutex_intr, &ops_lock))) return rc; =20 @@ -443,7 +443,7 @@ static void pci_tsm_unbind_all(struct pci_dev *pdev) =20 static void __pci_tsm_disconnect(struct pci_dev *pdev) { - struct pci_tsm_pf0 *tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); + struct pci_tsm_host *tsm_host =3D to_pci_tsm_host(pdev->tsm); const struct pci_tsm_ops *ops =3D to_pci_tsm_ops(pdev->tsm); =20 /* disconnect() mutually exclusive with subfunction pci_tsm_init() */ @@ -455,7 +455,7 @@ static void __pci_tsm_disconnect(struct pci_dev *pdev) * disconnect() is uninterruptible as it may be called for device * teardown */ - guard(mutex)(&tsm_pf0->lock); + guard(mutex)(&tsm_host->lock); pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL); ops->disconnect(pdev); } @@ -494,7 +494,7 @@ static ssize_t bound_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev =3D to_pci_dev(dev); - struct pci_tsm_pf0 *tsm_pf0; + struct pci_tsm_host *tsm_host; struct pci_tsm *tsm; int rc; =20 @@ -505,9 +505,9 @@ static ssize_t bound_show(struct device *dev, tsm =3D pdev->tsm; if (!tsm) return sysfs_emit(buf, "\n"); - tsm_pf0 =3D to_pci_tsm_pf0(tsm); + tsm_host =3D to_pci_tsm_host(tsm); =20 - ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock); + ACQUIRE(mutex_intr, ops_lock)(&tsm_host->lock); if ((rc =3D ACQUIRE_ERR(mutex_intr, &ops_lock))) return rc; =20 @@ -547,7 +547,7 @@ static bool pci_tsm_link_group_visible(struct kobject *= kobj) if (!pci_is_pcie(pdev)) return false; =20 - if (is_pci_tsm_pf0(pdev)) + if (is_pci_tsm_host(pdev)) return true; =20 /* @@ -572,14 +572,14 @@ static umode_t pci_tsm_attr_visible(struct kobject *k= obj, struct pci_dev *pdev =3D to_pci_dev(kobj_to_dev(kobj)); =20 if (attr =3D=3D &dev_attr_bound.attr) { - if (is_pci_tsm_pf0(pdev) && has_tee(pdev)) + if (is_pci_tsm_host(pdev) && has_tee(pdev)) return attr->mode; if (pdev->tsm && has_tee(pdev->tsm->dsm_dev)) return attr->mode; } =20 if (attr =3D=3D &dev_attr_dsm.attr) { - if (is_pci_tsm_pf0(pdev)) + if (is_pci_tsm_host(pdev)) return attr->mode; if (pdev->tsm && has_tee(pdev->tsm->dsm_dev)) return attr->mode; @@ -587,7 +587,7 @@ static umode_t pci_tsm_attr_visible(struct kobject *kob= j, =20 if (attr =3D=3D &dev_attr_connect.attr || attr =3D=3D &dev_attr_disconnect.attr) { - if (is_pci_tsm_pf0(pdev)) + if (is_pci_tsm_host(pdev)) return attr->mode; } } @@ -662,7 +662,7 @@ static struct pci_dev *find_dsm_dev(struct pci_dev *pde= v) struct device *grandparent; struct pci_dev *uport; =20 - if (is_pci_tsm_pf0(pdev)) + if (is_pci_tsm_host(pdev)) return pdev; =20 struct pci_dev *pf0 __free(pci_dev_put) =3D pf0_dev_get(pdev); @@ -735,13 +735,13 @@ int pci_tsm_link_constructor(struct pci_dev *pdev, st= ruct pci_tsm *tsm, EXPORT_SYMBOL_GPL(pci_tsm_link_constructor); =20 /** - * pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' (DSM) initializ= ation - * @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0()) + * pci_tsm_host_constructor() - common 'struct pci_tsm_host' (DSM) initial= ization + * @pdev: TSM host PCI device (as indicated by is_pci_tsm_host()) * @tsm: context to initialize * @tsm_dev: Platform TEE Security Manager, initiator of security operatio= ns */ -int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, - struct tsm_dev *tsm_dev) +int pci_tsm_host_constructor(struct pci_dev *pdev, struct pci_tsm_host *ts= m, + struct tsm_dev *tsm_dev) { mutex_init(&tsm->lock); tsm->doe_mb =3D pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, @@ -753,13 +753,13 @@ int pci_tsm_pf0_constructor(struct pci_dev *pdev, str= uct pci_tsm_pf0 *tsm, =20 return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev); } -EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor); +EXPORT_SYMBOL_GPL(pci_tsm_host_constructor); =20 -void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm) +void pci_tsm_host_destructor(struct pci_tsm_host *host_tsm) { - mutex_destroy(&pf0_tsm->lock); + mutex_destroy(&host_tsm->lock); } -EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor); +EXPORT_SYMBOL_GPL(pci_tsm_host_destructor); =20 int pci_tsm_register(struct tsm_dev *tsm_dev) { @@ -780,7 +780,7 @@ int pci_tsm_register(struct tsm_dev *tsm_dev) /* On first enable, update sysfs groups */ if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ =3D=3D 0) { for_each_pci_dev(pdev) - if (is_pci_tsm_pf0(pdev)) + if (is_pci_tsm_host(pdev)) link_sysfs_enable(pdev); } else if (is_devsec_tsm(tsm_dev)) { pci_tsm_devsec_count++; @@ -815,7 +815,7 @@ static void __pci_tsm_destroy(struct pci_dev *pdev, str= uct tsm_dev *tsm_dev) * skipped if the device itself is being removed since sysfs goes away * naturally at that point */ - if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count) + if (is_link_tsm(tsm_dev) && is_pci_tsm_host(pdev) && !pci_tsm_link_count) link_sysfs_disable(pdev); =20 /* Nothing else to do if this device never attached to the departing TSM = */ @@ -828,7 +828,7 @@ static void __pci_tsm_destroy(struct pci_dev *pdev, str= uct tsm_dev *tsm_dev) else if (tsm_dev !=3D tsm->tsm_dev) return; =20 - if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev)) + if (is_link_tsm(tsm_dev) && is_pci_tsm_host(pdev)) pci_tsm_disconnect(pdev); else pci_tsm_fn_exit(pdev); @@ -885,12 +885,12 @@ void pci_tsm_unregister(struct tsm_dev *tsm_dev) int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, size_t req_sz, void *resp, size_t resp_sz) { - struct pci_tsm_pf0 *tsm; + struct pci_tsm_host *tsm; =20 - if (!pdev->tsm || !is_pci_tsm_pf0(pdev)) + if (!pdev->tsm || !is_pci_tsm_host(pdev)) return -ENXIO; =20 - tsm =3D to_pci_tsm_pf0(pdev->tsm); + tsm =3D to_pci_tsm_host(pdev->tsm); if (!tsm->doe_mb) return -ENXIO; =20 diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h index a6435aba03f9..950e2c36a4ca 100644 --- a/include/linux/pci-tsm.h +++ b/include/linux/pci-tsm.h @@ -40,7 +40,7 @@ struct pci_tsm_ops { * pci_tsm_rwsem held for write to sync with TSM unregistration and * mutual exclusion of @connect and @disconnect. @connect and * @disconnect additionally run under the DSM lock (struct - * pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions. + * pci_tsm_host::lock) as well as @probe and @remove of the subfunctions. * @bind, @unbind, and @guest_req run under pci_tsm_rwsem held for read * and the DSM lock. */ @@ -115,19 +115,23 @@ struct pci_tsm { }; =20 /** - * struct pci_tsm_pf0 - Physical Function 0 TDISP link context + * struct pci_tsm_host - TSM host link context (CMA, IDE, or TDISP) * @base_tsm: generic core "tsm" context * @lock: mutual exclustion for pci_tsm_ops invocation * @doe_mb: PCIe Data Object Exchange mailbox + * + * The host of a TSM link is the device that knows how to speak + * CMA, IDE, or TDISP. For TDISP / IDE that is a Physical Function 0. + * For CMA-only it is a CMA DOE mailbox. */ -struct pci_tsm_pf0 { +struct pci_tsm_host { struct pci_tsm base_tsm; struct mutex lock; struct pci_doe_mb *doe_mb; }; =20 /* physical function0 and capable of 'connect' */ -static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) +static inline bool is_pci_tsm_host(struct pci_dev *pdev) { if (!pdev) return false; @@ -204,9 +208,9 @@ int pci_tsm_register(struct tsm_dev *tsm_dev); void pci_tsm_unregister(struct tsm_dev *tsm_dev); int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm, struct tsm_dev *tsm_dev); -int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, - struct tsm_dev *tsm_dev); -void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *tsm); +int pci_tsm_host_constructor(struct pci_dev *pdev, struct pci_tsm_host *ts= m, + struct tsm_dev *tsm_dev); +void pci_tsm_host_destructor(struct pci_tsm_host *tsm); int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, size_t req_sz, void *resp, size_t resp_sz); int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id); --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DCA8039A812 for ; Tue, 1 Sep 2026 01:05:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224740; cv=none; b=JjIvwngziOUEOTtwTwpvnZlKmZGbwQu9ffvTjGsXj1vOvUHtLV9YPoJh9YVy0JC1jCx63lJZnHYeBrKRy7/VQVi3XrjwzGNre2LS1l+EbY3TplIZGLPgwnvr9zw/c4adQgZQ9zR3RfcB6uBtmC6apsTkK2qBESC4I7iJWHEaFOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224740; c=relaxed/simple; bh=rgSGcz893ueiGfuyqqAZO9aqjoYjmJCcUYsDt8Smdf0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cqrCoMg/yZUmQepM/bQ2LvspxxfF7WlFvz0TXoY/IHgkTwPKQOmp51Erip0gW7jNC7NdI9heTXtsC/c9/bqQXLxaNtZKRZ6tHjuU3Uu/NC6VF5czfuWRF7OLR8lmcVvc8NWHjAY+YsPh599icGE5Oj31xIHYKxM7nuNtTtR+GJk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=L8glTBsD; arc=none smtp.client-ip=209.85.216.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="L8glTBsD" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-398b3d66515so2914479a91.0 for ; Mon, 31 Aug 2026 18:05:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224738; x=1788829538; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=2ff32pYrLV2ye5SVxDkfabWfzjV5v7+4FFXbfOmJ02c=; b=L8glTBsD2eZPsa9Bsqm/7U6mmZLOULwyxhxwu27MzKsFaw4L11SYnTN8z1lkANN297 n9X00+e56uyVYOA8e/kuwOA8xuyddIFmiBaUF4nbYBHYQQETG09i2QxveAPAABrFPLIX IqcClt9/tCP8Wg7MvYJGq2VKC/ilt2i6TWV0z3qX/Aq/+GGCQog8MR61sjGChm0x6oMD AbcH5lrUyLxFUmm8f1/H/QSXltH7x4NG3Y5nPIajoIztg8NcDzTNxA3d7eYTUfYU+rLU URLGe+EZSBX1WTJcbpIE6+mnisB6fvaMJW4QvrlJjoINEwknM3RDswdIUwsB/sfgsgvm zO1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224738; x=1788829538; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=2ff32pYrLV2ye5SVxDkfabWfzjV5v7+4FFXbfOmJ02c=; b=hHjjEbZHcvWzTiewWAHw2yTtOOCNZVCsiEmjMY7R6kjFBzd1vscHm6n+L/Fzgyo7yc 8eHaQx+resUkUAk6NTPu86F1RdQbgXSLyuGKNaNf/kOgwIdF4OPURA78kP6LoW344G2L YzHkcVRK71hVtrgAd6Uz7uIfebId58Vfh60CLJtpTpU703WLfJ2oMBXBzhZ3suZEDMa+ NWNTHt5Kd5f6G8p+LK69T7+oxjhmAqQZnlA5ChJYQr9EAoN4tr0mQsCLxddXK05kkZH/ ibln7RPKt3QmWVF2y+oS6ynPhWUibmv54tJfLyoj6uz1fSSTy6/25fauUyAnWCjC39Ll YytA== X-Forwarded-Encrypted: i=1; AKwUvBxl3pzx3hruB9mv4uS4W96GrQ1959bmxMpOP24odUaNK5wqwphyVBtAU9D07ea36stLwgg9yHyCBRUSphQ=@vger.kernel.org X-Gm-Message-State: AFuF++kisVJMSC26Ka8yq0DLhYkYl+u4HyhfekDsqKhCLviFFIR+3ahy yCo0zUns9UlCFd0kRQLM5mT/OSmluOv3kiA/yaD7mhI6w5tpvDsSG1zv X-Gm-Gg: AYBFou2f34PG0mcTI5Y39UGtVaaUblhCbKTUcdWGyUxier2Ctbjp0qtx1/Z1oTssOpE GZaSrcHqm6FhTggi8ZTYNJEJXHJpKDKsDkxQY1F0GpAWxfz7T8uqmGL77Tg+bHtNsCqbpJ0WPaz 5m/pEHRa9wUAA/PX7mIHoLXUDNmTuS8ITf1HwRC7+kUpLQjQI/lSCIfP3DxxWjwwPq70815VBSS eDrGkkQ7zcsx4Dyd+xBeAQVMfrcrHhbkeeFnRM1wqsnS/GGW3++flHZRaGl55HFYnl4KN/7wox6 3U7kbyCpihZzLrXiB7dsJ9TVQnRQoUAFZaY9B++0fyaGV0zthw4kb5XrPH2I8cmGMwqFW9vFXCa pWrm3hUaJUC1xmVA22DlB5/DtJntI6Xfi0vcwfT08Yhz4UcdMPWyDF8VB4tz3uCm5H+ULHgrvr8 9T+bJrmDoFtH5e4pdAWEWsIK18q5WLap7g2Sgs79DfgOqdOfx2mDc39YuTxhYHQQG3AceyyX+hl r7chCwfm7Q/zjri X-Received: by 2002:a17:90a:e7cf:b0:38f:dec8:f7e9 with SMTP id 98e67ed59e1d1-396d0fccec0mr43056859a91.12.1788224738038; Mon, 31 Aug 2026 18:05:38 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:36 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 11/21] PCI/TSM: Support connecting to PCIe CMA devices Date: Tue, 1 Sep 2026 11:03:37 +1000 Message-ID: <20260901010347.2614656-12-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis In the next patch we are going to add a PCIe CMA TSM driver, as such we need to ensure that is_pci_tsm_host() will allow us to connect to CMA capable devices. These devices don't necessarily has DEVCAP_TEE or IDE support. To avoid calling pci_find_doe_mailbox() everytime is_pci_tsm_host() is called we can cache the CMA support in struct pci_dev and just check against that. Signed-off-by: Alistair Francis --- drivers/pci/doe.c | 3 +++ drivers/pci/tsm.c | 31 +++++++++++++++++++++++++------ include/linux/pci-tsm.h | 12 +++++++++++- include/linux/pci.h | 1 + 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index ac95b1d2d999..6a59969bd52f 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -870,6 +870,9 @@ void pci_doe_init(struct pci_dev *pdev) pci_doe_destroy_mb(doe_mb); } } + + pdev->doe_cma =3D pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_FEATURE_CMA); } =20 void pci_doe_destroy(struct pci_dev *pdev) diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c index 10c9c6696624..440f818ac569 100644 --- a/drivers/pci/tsm.c +++ b/drivers/pci/tsm.c @@ -88,8 +88,8 @@ static void pci_tsm_walk_fns(struct pci_dev *pdev, if (!pf) continue; =20 - /* on entry function 0 has already run @cb */ - if (i > 0) + /* the caller is responsible for running @cb on the host itself */ + if (pf !=3D pdev) cb(pf, data); =20 /* walk virtual functions of each pf */ @@ -145,8 +145,8 @@ static void pci_tsm_walk_fns_reverse(struct pci_dev *pd= ev, cb(vf, data); } =20 - /* on exit, caller will run @cb on function 0 */ - if (i > 0) + /* the caller is responsible for running @cb on the host itself */ + if (pf !=3D pdev) cb(pf, data); } } @@ -287,6 +287,16 @@ static DEVICE_ATTR_RW(connect); =20 static int remove_fn(struct pci_dev *pdev, void *data) { + struct pci_dev *host =3D data; + + /* + * Only teardown the security context of functions that belong to the + * DSM being disconnected, leaving any sibling DSM in the same slot + * untouched. + */ + if (!pdev->tsm || pdev->tsm->dsm_dev !=3D host) + return 0; + tsm_remove(pdev->tsm); link_sysfs_disable(pdev); return 0; @@ -299,6 +309,7 @@ static int remove_fn(struct pci_dev *pdev, void *data) */ static int __pci_tsm_unbind(struct pci_dev *pdev, void *data) { + struct pci_dev *host =3D data; struct pci_tdi *tdi; struct pci_tsm_host *tsm_host; =20 @@ -307,6 +318,14 @@ static int __pci_tsm_unbind(struct pci_dev *pdev, void= *data) if (!pdev->tsm) return 0; =20 + /* + * When walking a DSM's dependent functions skip any that belong to a + * different DSM in the same slot. A NULL @host is a direct unbind of + * @pdev itself. + */ + if (host && pdev->tsm->dsm_dev !=3D host) + return 0; + tsm_host =3D to_pci_tsm_host(pdev->tsm); guard(mutex)(&tsm_host->lock); =20 @@ -437,7 +456,7 @@ EXPORT_SYMBOL_GPL(pci_tsm_guest_req); =20 static void pci_tsm_unbind_all(struct pci_dev *pdev) { - pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, NULL); + pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, pdev); __pci_tsm_unbind(pdev, NULL); } =20 @@ -456,7 +475,7 @@ static void __pci_tsm_disconnect(struct pci_dev *pdev) * teardown */ guard(mutex)(&tsm_host->lock); - pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL); + pci_tsm_walk_fns_reverse(pdev, remove_fn, pdev); ops->disconnect(pdev); } =20 diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h index 950e2c36a4ca..f504fa680315 100644 --- a/include/linux/pci-tsm.h +++ b/include/linux/pci-tsm.h @@ -3,6 +3,7 @@ #define __PCI_TSM_H #include #include +#include #include =20 struct pci_tsm; @@ -130,7 +131,7 @@ struct pci_tsm_host { struct pci_doe_mb *doe_mb; }; =20 -/* physical function0 and capable of 'connect' */ +/* device is a TSM host and capable of 'connect' */ static inline bool is_pci_tsm_host(struct pci_dev *pdev) { if (!pdev) @@ -142,6 +143,15 @@ static inline bool is_pci_tsm_host(struct pci_dev *pde= v) if (pdev->is_virtfn) return false; =20 + /* + * Report capable if CMA is supported, which can be supported on any PCIe + * device. + */ +#ifdef CONFIG_PCI_DOE + if (pdev->doe_cma) + return true; +#endif + /* * Allow for a Device Security Manager (DSM) associated with function0 * of an Endpoint to coordinate TDISP requests for other functions diff --git a/include/linux/pci.h b/include/linux/pci.h index d31a8d107b1e..cb13b40952b4 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -565,6 +565,7 @@ struct pci_dev { #endif #ifdef CONFIG_PCI_DOE struct xarray doe_mbs; /* Data Object Exchange mailboxes */ + bool doe_cma; /* A CMA/SPDM DOE mailbox is present */ #endif #ifdef CONFIG_PCI_NPEM struct npem *npem; /* Native PCIe Enclosure Management */ --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f53.google.com (mail-pj1-f53.google.com [209.85.216.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 085AC35C1B7 for ; Tue, 1 Sep 2026 01:05:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224752; cv=none; b=ess38H/5kOb6CTdzTf/KxGTnXTvDzYndDfZJy7iMoLplqyuUytsyiwakDUiuKEXuUdZuF8P5fa/QIrKQ4PogaWfHmPMI/Kf4bTLprR/Up+ugCcb/XzcC2R59yB31wqy4ypWlS50Yw3OhJMNq7WroA5L66WLiyD54XrgAQ2ZZd0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224752; c=relaxed/simple; bh=SuXO6Rw2OD8RENWA3cD1nqy9B4wpX6nA3k4+CdFkYg0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=f8i52+jSng5e4MJLolAwQmeIot2VAjW92YTKmhd/KWGa8j+fC3Of9czNQsVIlTXEJL4FZsKfNg2ctEID1q7zjxVwdgBJH7xuijeII0PXDvGAn4u8FWc/BsULW+VCNCzQu/9h04WzkPpZ5bGk1iJ2yEvvoJ2FQxuB19C2i4q/e3A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=C8nO7uFV; arc=none smtp.client-ip=209.85.216.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="C8nO7uFV" Received: by mail-pj1-f53.google.com with SMTP id 98e67ed59e1d1-3966791a6eeso5059657a91.3 for ; Mon, 31 Aug 2026 18:05:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224747; x=1788829547; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xqWIp2bpYsEXRmOJBqoBVIcXDWfpHGacUwf5CoaucBQ=; b=C8nO7uFV2DmitACXWTgPwbWzm6x831cPM+tHB6/7BzxJGLyMaP3CowGGd93Uf5tvi/ Thxr6lnAjEghva7p91QH8+LT+jAdpDyGEj2kCAKFLZaU1Oy8bPc3nMgUwqqd4b1R0yKf wJ5d0fexDj83aPjvJlV/HVl031IWqB2GAgA/4W6cJad/rFCGUCXkXmZaWZ8LeE06wRvn 5q5DIkwOL1uaYH6mzQwwKIx/+YSwXM2C3oSFI4w432LR2gS8x1Wq7Av/oF4IHr+qC4W4 Pt9/YMELOZnhVAlVR7bd6kf+PDDW8Ib9ErTKtw1RU6oZ7tvSg0xjG0AzhZbR6D2caiFR hRHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224747; x=1788829547; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xqWIp2bpYsEXRmOJBqoBVIcXDWfpHGacUwf5CoaucBQ=; b=hm1OKwf93oO4NkeKeGuC8gp8JjcIXZj7db84noJsSv4eQkG+hSbkNXTFwGcHXPL7r4 S3fWeM89pIQksWKY0s2El22TKW5K/AvdjdhE01PPuyIHLAya7MpHF0aQuwQDl9cKl8kB 1PCaFVdHpsnDdPTAqUOxh7lOfssKq04S5hXSGBifdK+NKKby/k6kQTPuQv15F9B7LtEi +lLLmytAmaaN9tm9tzJMcvvRr11KVxRUd/xrMgs/9CMNz5YO1EFnbjD4kDyYqUSRCfWA ixvRqB+oIF4ihnUqPjm+sZjrjkk5tDLuNnmhC5/7uRiclkubBhOdZstG7ev8Q73cpP+q mAyQ== X-Forwarded-Encrypted: i=1; AKwUvBygLHvSIwzaeTBfKi8AMgr0xKm0QRZG43Iqqv8HLUtfxWtXTCdxhlWnuccXA2zOQFU1ltdCgR/pCvNOseo=@vger.kernel.org X-Gm-Message-State: AFuF++liLrh71uf46s+Jv4Ax69voECNUTlsf+TD81tnTf2Xr4egvrHdY cdtkOKe+0+dgoSI6D6XWXp8B0KVna9yx0HGIQRErtGMfqjEfy1vE/ted X-Gm-Gg: AYBFou24RuPyboybdN6d/6ntf2O4WfiSo094BJ/69GLDRJXj5oHwK7f+3/jHUlfbr1u FFlxWREEzEmRG8TzHpdvjL11lw84LTPjhISgk1vY93T/0PBCTUhSRN5w3kF/LhJB9CgVl5IRPa9 FIopRDEQaxFsySxACe6fyKz67UDNnk3QJv15p/ENV6veJ6ObHJiKrcIc/BTdUbBI4yTtUPTudo3 Ve2RrfEhfyoZw+NRvoYa6eXaaz1wsMVj+01XNPeXYTkYDBgg68SIVLKid+nbC5Pl68kQj0QRFGA RPjQo6t2uyImwYGCBQD9Z6Z1b7/ZB4V8Na+zLaWBQiJxiHBJNzcKOebA8aVKTzTLvIBl2Jc6SB1 +fxRBQbmeJjMQX4cf3mAaz2mfybfg2yRTEhp2kGzY8KUNS/wg0j/yNAfheqjfHGfd5jStMhHpWx 7YPzfh8iA0Ptv6lstZBCj6UFrb+OZSFso/ZaJZX2yRRDfneKqXP1Geq0QbNTQaKO9GtcHJXmneA a0wdQ== X-Received: by 2002:a17:90b:1811:b0:37f:ed7e:7e42 with SMTP id 98e67ed59e1d1-39907df0168mr5725338a91.14.1788224746603; Mon, 31 Aug 2026 18:05:46 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:46 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 12/21] PCI/CMA: Add a PCI TSM CMA driver using SPDM Date: Tue, 1 Sep 2026 11:03:38 +1000 Message-ID: <20260901010347.2614656-13-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Component Measurement and Authentication (CMA, PCIe r6.2 sec 6.31) allows for measurement and authentication of PCIe devices. It is based on the Security Protocol and Data Model specification (SPDM, https://www.dmtf.org/dsp/DSP0274). CMA-SPDM in turn forms the basis for Integrity and Data Encryption (IDE, PCIe r6.2 sec 6.33) because the key material used by IDE is transmitted over a CMA-SPDM session. As a first step, add support for authentication via a CMA TSM driver. This was previously discusd here: http://lore.kernel.org/69976d7d39c60_2f4a1009@dwillia2-mobl4.notmuch By utilising a TSM driver we get a lot of the TSM driver probe policies "for free". Currently there is no mechanism to provide evidence to userspace, as the TSM system doesn't support that at the moment. That can be added later when support by TSM. Credits: Jonathan wrote the original proof-of-concept for a CMA implementat= ion. Lukas reworked that for upstream. Wilfred contributed fixes for issues discovered during testing. Alistair reworked it as a TSM driver. Signed-off-by: Jonathan Cameron Co-developed-by: Wilfred Mallawa Signed-off-by: Wilfred Mallawa Co-developed-by: Lukas Wunner Signed-off-by: Lukas Wunner Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + drivers/pci/Kconfig | 14 ++++ drivers/pci/Makefile | 2 + drivers/pci/cma.c | 154 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/doe.c | 3 - include/linux/pci-doe.h | 4 ++ 6 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 drivers/pci/cma.c diff --git a/MAINTAINERS b/MAINTAINERS index 36f84a02894b..89cd64f88ebb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24762,6 +24762,7 @@ L: linux-cxl@vger.kernel.org L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git +F: drivers/pci/cma.c F: include/linux/spdm.h F: lib/rspdm/ =20 diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0c7408509ba2..0862ef69aa3f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -124,6 +124,20 @@ config PCI_ATS config PCI_IDE bool =20 +config PCI_CMA + bool "Component Measurement and Authentication (CMA-SPDM)" + depends on RSPDM + select CRYPTO_ECDSA + select CRYPTO_RSA + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select PCI_DOE + select PCI_TSM + help + Authenticate devices on enumeration per PCIe r6.2 sec 6.31. + A PCI DOE mailbox is used as transport for DMTF SPDM based + authentication, measurement and secure channel establishment. + config PCI_TSM bool "PCI TSM: Device security protocol support" select PCI_IDE diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 41ebc3b9a518..16abfd0e17e1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -41,6 +41,8 @@ obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o obj-$(CONFIG_CARDBUS) +=3D setup-cardbus.o =20 +obj-$(CONFIG_PCI_CMA) +=3D cma.o + # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) +=3D endpoint/ =20 diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c new file mode 100644 index 000000000000..9f2cc0b2ec8a --- /dev/null +++ b/drivers/pci/cma.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Component Measurement and Authentication (CMA-SPDM, PCIe r6.2 sec 6.31) + * + * Copyright (C) 2021 Huawei + * Jonathan Cameron + * Copyright (C) 2022-24 Intel Corporation + * Copyright (C) 2026 Western Digital + * Alistair Francis + */ + +#define dev_fmt(fmt) "CMA: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +static int pci_doe_transport(void *priv, struct device *dev, + const void *request, size_t request_sz, + void *response, size_t response_sz) +{ + struct pci_doe_mb *doe =3D priv; + + return pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA, + request, request_sz, response, response_sz); +} + +struct pci_cma_tsm { + struct pci_tsm_host host; + struct spdm_state *spdm; +}; + +static struct pci_cma_tsm *cma_tsm_from_tsm(struct pci_tsm *tsm) +{ + struct pci_tsm_host *host =3D container_of(tsm, struct pci_tsm_host, base= _tsm); + + return container_of(host, struct pci_cma_tsm, host); +} + +static struct pci_tsm *pci_cma_tsm_probe(struct tsm_dev *tsm_dev, + struct pci_dev *pdev) +{ + struct pci_cma_tsm *cma; + int rc; + + cma =3D kzalloc(sizeof(*cma), GFP_KERNEL); + if (!cma) + return NULL; + + rc =3D pci_tsm_host_constructor(pdev, &cma->host, tsm_dev); + if (rc) { + kfree(cma); + return NULL; + } + + cma->spdm =3D spdm_create(&pdev->dev, pci_doe_transport, cma->host.doe_mb, + PCI_DOE_MAX_PAYLOAD, NULL); + if (!cma->spdm) { + pci_tsm_host_destructor(&cma->host); + kfree(cma); + return NULL; + } + + return &cma->host.base_tsm; +} + +static void pci_cma_tsm_remove(struct pci_tsm *tsm) +{ + struct pci_cma_tsm *cma =3D cma_tsm_from_tsm(tsm); + + spdm_destroy(cma->spdm); + pci_tsm_host_destructor(&cma->host); + kfree(cma); +} + +static int pci_cma_tsm_connect(struct pci_dev *pdev) +{ + struct pci_cma_tsm *cma =3D cma_tsm_from_tsm(pdev->tsm); + int rc; + + /* + * The DOE mailbox lives in the device's config space, so the + * device must be runtime-resumed for the duration of the SPDM + * exchange. + */ + rc =3D pm_runtime_get_sync(&pdev->dev); + if (rc < 0) { + pm_runtime_put_noidle(&pdev->dev); + return rc; + } + + rc =3D spdm_authenticate(cma->spdm); + + pm_runtime_put_sync(&pdev->dev); + return rc; +} + +static void pci_cma_tsm_disconnect(struct pci_dev *pdev) +{ + /* SPDM state is freed in pci_cma_tsm_remove() */ +} + +static struct pci_tdi *pci_cma_tsm_bind(struct pci_dev *pdev, + struct kvm *kvm, u32 tdi_id) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static void pci_cma_tsm_unbind(struct pci_tdi *tdi) +{ +} + +static ssize_t pci_cma_tsm_guest_req(struct pci_tdi *tdi, + enum pci_tsm_req_scope scope, + sockptr_t req_in, size_t in_len, + sockptr_t req_out, size_t out_len, + u64 *tsm_code) +{ + return -EOPNOTSUPP; +} + +static const struct pci_tsm_ops pci_cma_tsm_ops =3D { + .link_ops =3D { + .probe =3D pci_cma_tsm_probe, + .remove =3D pci_cma_tsm_remove, + .connect =3D pci_cma_tsm_connect, + .disconnect =3D pci_cma_tsm_disconnect, + .bind =3D pci_cma_tsm_bind, + .unbind =3D pci_cma_tsm_unbind, + .guest_req =3D pci_cma_tsm_guest_req, + }, +}; + +static struct tsm_dev *pci_cma_tsm_dev; + +static int __init pci_cma_tsm_init(void) +{ + struct tsm_dev *tsm_dev; + + tsm_dev =3D tsm_register(NULL, (struct pci_tsm_ops *)&pci_cma_tsm_ops); + if (IS_ERR(tsm_dev)) + return PTR_ERR(tsm_dev); + + pci_cma_tsm_dev =3D tsm_dev; + return 0; +} +late_initcall(pci_cma_tsm_init); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 6a59969bd52f..1b3d6e74fbe8 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -31,9 +31,6 @@ #define PCI_DOE_FLAG_CANCEL 0 #define PCI_DOE_FLAG_DEAD 1 =20 -/* Max data object length is 2^18 dwords */ -#define PCI_DOE_MAX_LENGTH (1 << 18) - /** * struct pci_doe_mb - State for a single DOE mailbox * diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h index bd4346a7c4e7..7540396336de 100644 --- a/include/linux/pci-doe.h +++ b/include/linux/pci-doe.h @@ -19,6 +19,10 @@ struct pci_doe_mb; #define PCI_DOE_FEATURE_CMA 1 #define PCI_DOE_FEATURE_SSESSION 2 =20 +/* Max data object length is 2^18 dwords (including 2 dwords for header) */ +#define PCI_DOE_MAX_LENGTH (1 << 18) +#define PCI_DOE_MAX_PAYLOAD ((PCI_DOE_MAX_LENGTH - 2) * sizeof(u32)) + struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor, u8 type); =20 --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AADD39446B for ; Tue, 1 Sep 2026 01:05:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224759; cv=none; b=X5FdZUY9Vxxl5SwGbFTCMIMi2VdflgFzLhfjHYDUPTl6OglHvtYAsTVVrVoppXlpaWAxG0MZI8nLQiJgiYXGGOsepGoBw7KplbX6zSYWyTpGSDrsOvowixsgvNLLZu4cGCdfii/pPDZo+1WHLXIEMGK0Y9mQktuWndGvZJoOIpo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224759; c=relaxed/simple; bh=DSFWTWgpNiYmU/ZpbWeL5bujHqeCJC8+5WPf6y3bNSI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IyZ4pzwlKpnPXAA4L2iKLYmEo324C0Z8HwPP/zzUoQ4dyVR+8XCcTUlxWd7IhpHm9sMAyj1t6wvlCy2TeV6Yk5VVUm8Hsti2sI8/eL0MU82dIpowFHpkF2Y469zjusZlhkBWjQ5iZoH2yDWbWM2jiXyhfg9fFS3TP6mudKJMwIQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AMOcMzIu; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AMOcMzIu" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2d5335cf904so39941085ad.2 for ; Mon, 31 Aug 2026 18:05:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224755; x=1788829555; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=CTtzhZuEoyeqFzIIBBTh7vj6FIKdEA1vbMQrt+kzpuc=; b=AMOcMzIunIUNsdWqlULKWMM8ZvGujNoF/WneiHdMnX8gp2/QyKvp1GyNTltrKzPe7f haTYQ2F3C8/SxR5eaWPVmy5TKYkOQF/Bz8Ucy1WI8HUDteqYeN0Rbhm6aBsb7ZKCsBz7 8+GOrKPaBDC/bfn8f1yZ82bblPhNILbYzFy03knN3OyFS5kjoPQiReMTf3eEFaiMIb2M WWSxvrxP6NnZSTMNvY8+O2e4ysmweZwpYIi7zDPe4ht/r4XiFyVD58uF3f44zbtgz4Y2 b8mKFxtxBS0hq7bIq3axQW0uWYMuL1AXruWDUaSsnrUJ3Rz7pZfs20/M9Qn6W2/pEthO dnaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224755; x=1788829555; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=CTtzhZuEoyeqFzIIBBTh7vj6FIKdEA1vbMQrt+kzpuc=; b=LjhHhteu2RAFRbv6p+q+rByKUCCr/QbJvzBsuNn0rv4jBY+QY+lUAW2GnKvigmTVLR CtmXTx2NCheAXiihUTJHTNqC/Xo27HOuvDEgzkqYmI+De8BshN82KWaW4k5SbTDOFJcV tR44nUXrZ1oix0Dnf17TBRHcQCybN1ctjtMIRJXJ9XLnVTMXoDwsi9vX8+ZV7yheRCIo SnhYIKfZpTyAAh9Nm+fskDGNTgJiDzIdDTIkqAQvl/mw4n+yzGCH/sSO4uCy9DU6jkd5 afFU43cygXaAQSSgFSdCGCEkwPdYHqBfUeiE93w5DNTLHLCsaeI8Gr9yYG8zlrZ5jofF d9LA== X-Forwarded-Encrypted: i=1; AKwUvBzItcYOMNR3OgMUlcIsu8Lw5X5xZeJSYFAV2fRo3g35AYWtioz7352Ip3AsKyb9FDlrLh4uOUWyUKqhu0E=@vger.kernel.org X-Gm-Message-State: AFuF++m/pyBvOHMQhZ8vANTAuk2a/0FtbsabMMwQLCpoQLlaQfxlOwu1 5+RgbsuKEhgGczSKl+onH/zViR+u1btstHT41EEcJhFZe4nwMShQeRf0 X-Gm-Gg: AYBFou0mBxydv7n/EWXTE0D/LKZxQL/FMobUWHVQm6+/1NX6fbKPK7fl+Ojzo/0HKSW F91PYWYB01G+XAUQhvklWX3RoC7ZiQbgo/rsA6TRNBDmVAnrsavBPre4Kq+v8UgoPsAp5KsXGAv nTUsaWYTt6s0h01QXu3OUN29afmyvubA5WN06iOh2giX2VkuuedNymdxgjKBhEOVpZkb607Ofge OLcUUgy9ZhQqXW6kfqnrDicnjT6IrtR1XSBrcw6bleqtsJofzX6BDeoV4NSXAn0NnfBkJARYkGQ WsnH9AlVOr5KVqWxbZrc5htfNtWTdjjljo7L4zkgI6GbfGAl50GoOAIBrhfotF9P7P/nBElMpL2 BlcuLtIvI7JxZEUS6Q6nb1ldOt5Ov0WWlfLkfyV7Sv41V0hOV++B8mLiGb+0QOFOiXlDDZKyo8C el1TDNX7qznnPoSFncHVPqUMYDarriAu5KKJjUyhMOS4qOIiDREKyeOQ7PZd66DTJBkNb/DMuwj SZdvUs= X-Received: by 2002:a17:90b:528f:b0:38e:9eb2:9d43 with SMTP id 98e67ed59e1d1-39907e113f5mr6440117a91.16.1788224755228; Mon, 31 Aug 2026 18:05:55 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:05:54 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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, Alistair Francis Subject: [PATCH v3 13/21] PCI/CMA: Validate Subject Alternative Name in certificates Date: Tue, 1 Sep 2026 11:03:39 +1000 Message-ID: <20260901010347.2614656-14-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Lukas Wunner PCIe r6.1 sec 6.31.3 stipulates requirements for Leaf Certificates presented by devices, in particular the presence of a Subject Alternative Name which encodes the Vendor ID, Device ID, Device Serial Number, etc. This prevents a mismatch between the device identity in Config Space and the certificate. A device cannot misappropriate a certificate from a different device without also spoofing Config Space. As a corollary, it cannot dupe an arbitrary driver into binding to it. Only drivers which bind to the device identity in the Subject Alternative Name work (PCIe r6.1 sec 6.31 "Implementation Note: Overview of Threat Model"). The Subject Alternative Name is signed, hence constitutes a signed copy of a Config Space portion. It's the same concept as web certificates which contain a set of domain names in the Subject Alternative Name for identity verification. Parse the Subject Alternative Name using a small ASN.1 module and validate its contents. The theory of operation is explained in a comment at the top of the newly inserted code. This functionality is introduced in a separate commit on top of basic CMA-SPDM support to split the code into digestible, reviewable chunks. The CMA OID added here is taken from the official OID Repository (it's not documented in the PCIe Base Spec): https://oid-rep.orange-labs.fr/get/2.23.147 Side notes: * PCIe r6.2 removes the spec language on the Subject Alternative Name. It still "requires the leaf certificate to include the information typically used by system software for device driver binding", but no longer specifies how that information is encoded into the certificate. According to the editor of the PCIe Base Spec and the author of the CMA 1.1 ECN (which caused this change), FPGA cards which mutate their device identity at runtime (due to a firmware update) were thought as unable to satisfy the previous spec language. The Protocol Working Group could not agree on a better solution and therefore dropped the spec language entirely. They acknowledge that the requirement is now under-spec'd. Because products already exist which adhere to the Subject Alternative Name requirement per PCIe r6.1 sec 6.31.3, they recommended to "push through" and use it as the de facto standard. The FPGA concerns are easily overcome by reauthenticating the device after a firmware update, either via sysfs or pci_cma_reauthenticate() (added by a subsequent commit). * PCIe r6.1 sec 6.31.3 strongly recommends to verify that "the information provided in the Subject Alternative Name entry is signed by the vendor indicated by the Vendor ID." In other words, the root certificate on pci_cma_keyring which signs the device's certificate chain must have been created for a particular Vendor ID. Unfortunately the spec neglects to define how the Vendor ID shall be encoded into the root certificate. So the recommendation cannot be implemented at this point and it is thus possible that a vendor signs device certificates of a different vendor. * Instead of a Subject Alternative Name, Leaf Certificates may include "a Reference Integrity Manifest, e.g., see Trusted Computing Group" or "a pointer to a location where such a Reference Integrity Manifest can be obtained" (PCIe r6.1 sec 6.31.3). A Reference Integrity Manifest contains "golden" measurements which can be compared to actual measurements retrieved from a device. It serves a different purpose than the Subject Alternative Name, hence it is unclear why the spec says only either of them is necessary. It is also unclear how a Reference Integrity Manifest shall be encoded into a certificate. Hence ignore the Reference Integrity Manifest requirement. Signed-off-by: Lukas Wunner Reviewed-by: Jonathan Cameron # except ASN.1 [ Changed by AF: - Fixup a few issues caught by Sashiko ] Signed-off-by: Alistair Francis --- drivers/pci/Makefile | 5 +- drivers/pci/cma.asn1 | 41 ++++++++++++ drivers/pci/cma.c | 123 ++++++++++++++++++++++++++++++++++- include/linux/oid_registry.h | 3 + 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 drivers/pci/cma.asn1 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 16abfd0e17e1..882cbb108364 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -41,7 +41,10 @@ obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o obj-$(CONFIG_CARDBUS) +=3D setup-cardbus.o =20 -obj-$(CONFIG_PCI_CMA) +=3D cma.o +obj-$(CONFIG_PCI_CMA) +=3D pci-cma.o +pci-cma-y :=3D cma.o cma.asn1.o +$(obj)/cma.o: $(obj)/cma.asn1.h +$(obj)/cma.asn1.o: $(obj)/cma.asn1.c $(obj)/cma.asn1.h =20 # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) +=3D endpoint/ diff --git a/drivers/pci/cma.asn1 b/drivers/pci/cma.asn1 new file mode 100644 index 000000000000..da41421d4085 --- /dev/null +++ b/drivers/pci/cma.asn1 @@ -0,0 +1,41 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31.= 3) +-- X.509 Subject Alternative Name (RFC 5280 sec 4.2.1.6) +-- +-- Copyright (C) 2008 IETF Trust and the persons identified as authors +-- of the code +-- +-- https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6 +-- +-- The ASN.1 module in RFC 5280 appendix A.1 uses EXPLICIT TAGS whereas th= e one +-- in appendix A.2 uses IMPLICIT TAGS. The kernel's simplified asn1_compi= ler.c +-- always uses EXPLICIT TAGS, hence this ASN.1 module differs from RFC 528= 0 in +-- that it adds IMPLICIT to definitions from appendix A.2 (such as General= Name) +-- and omits EXPLICIT in those definitions. + +SubjectAltName ::=3D GeneralNames + +GeneralNames ::=3D SEQUENCE OF GeneralName + +GeneralName ::=3D CHOICE { + otherName [0] IMPLICIT OtherName, + rfc822Name [1] IMPLICIT IA5String, + dNSName [2] IMPLICIT IA5String, + x400Address [3] ANY, + directoryName [4] ANY, + ediPartyName [5] IMPLICIT EDIPartyName, + uniformResourceIdentifier [6] IMPLICIT IA5String, + iPAddress [7] IMPLICIT OCTET STRING, + registeredID [8] IMPLICIT OBJECT IDENTIFIER + } + +OtherName ::=3D SEQUENCE { + type-id OBJECT IDENTIFIER ({ pci_cma_note_oid }), + value [0] ANY ({ pci_cma_note_san }) + } + +EDIPartyName ::=3D SEQUENCE { + nameAssigner [0] ANY OPTIONAL, + partyName [1] ANY + } diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 9f2cc0b2ec8a..39a858436b28 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -12,6 +12,9 @@ #define dev_fmt(fmt) "CMA: " fmt =20 #include +#include +#include +#include #include #include #include @@ -20,8 +23,126 @@ #include #include =20 +#include "cma.asn1.h" #include "pci.h" =20 +/* + * The spdm_requester.c library calls pci_cma_validate() to check requirem= ents + * for Leaf Certificates per PCIe r6.1 sec 6.31.3. + * + * pci_cma_validate() parses the Subject Alternative Name using the ASN.1 + * module cma.asn1, which calls pci_cma_note_oid() and pci_cma_note_san() + * to compare an OtherName against the expected name. + * + * The expected name is constructed beforehand by pci_cma_construct_san(). + * + * PCIe r6.2 drops the Subject Alternative Name spec language, even though + * it continues to require "the leaf certificate to include the information + * typically used by system software for device driver binding". Use the + * Subject Alternative Name per PCIe r6.1 for lack of a replacement and + * because it is the de facto standard among existing products. + */ +#define CMA_NAME_MAX sizeof("Vendor=3D1234:Device=3D1234:CC=3D123456:" \ + "REV=3D12:SSVID=3D1234:SSID=3D1234:1234567890123456") + +struct pci_cma_x509_context { + struct pci_dev *pdev; + u8 slot; + enum OID last_oid; + char expected_name[CMA_NAME_MAX]; + unsigned int expected_len; + unsigned int found:1; +}; + +int pci_cma_note_oid(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct pci_cma_x509_context *ctx =3D context; + + ctx->last_oid =3D look_up_OID(value, vlen); + + return 0; +} + +int pci_cma_note_san(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct pci_cma_x509_context *ctx =3D context; + + /* These aren't the drOIDs we're looking for. */ + if (ctx->last_oid !=3D OID_CMA) + return 0; + + if (tag !=3D ASN1_UTF8STR || + vlen !=3D ctx->expected_len || + memcmp(value, ctx->expected_name, vlen) !=3D 0) { + pci_err(ctx->pdev, "Leaf certificate of slot %u " + "has invalid Subject Alternative Name\n", ctx->slot); + return -EINVAL; + } + + ctx->found =3D true; + + return 0; +} + +static unsigned int pci_cma_construct_san(struct pci_dev *pdev, char *name) +{ + unsigned int len; + u64 serial; + + len =3D scnprintf(name, CMA_NAME_MAX, + "Vendor=3D%04hx:Device=3D%04hx:CC=3D%06x:REV=3D%02hhx", + pdev->vendor, pdev->device, pdev->class, pdev->revision); + + if (pdev->hdr_type =3D=3D PCI_HEADER_TYPE_NORMAL) + len +=3D scnprintf(name + len, CMA_NAME_MAX - len, + ":SSVID=3D%04hx:SSID=3D%04hx", + pdev->subsystem_vendor, pdev->subsystem_device); + + serial =3D pci_get_dsn(pdev); + if (serial) + len +=3D scnprintf(name + len, CMA_NAME_MAX - len, + ":%016llx", serial); + + return len; +} + +static int pci_cma_validate(struct device *dev, u8 slot, + struct x509_certificate *leaf_cert) +{ + struct pci_dev *pdev =3D to_pci_dev(dev); + struct pci_cma_x509_context ctx; + int ret; + + if (!leaf_cert->raw_san) { + pci_err(pdev, "Leaf certificate of slot %u " + "has no Subject Alternative Name\n", slot); + return -EINVAL; + } + + ctx.pdev =3D pdev; + ctx.slot =3D slot; + ctx.found =3D false; + ctx.expected_len =3D pci_cma_construct_san(pdev, ctx.expected_name); + + ret =3D asn1_ber_decoder(&cma_decoder, &ctx, leaf_cert->raw_san, + leaf_cert->raw_san_size); + if (ret =3D=3D -EBADMSG || ret =3D=3D -EMSGSIZE) + pci_err(pdev, "Leaf certificate of slot %u " + "has malformed Subject Alternative Name\n", slot); + if (ret < 0) + return ret; + + if (!ctx.found) { + pci_err(pdev, "Leaf certificate of slot %u " + "has no OtherName with CMA OID\n", slot); + return -EINVAL; + } + + return 0; +} + static int pci_doe_transport(void *priv, struct device *dev, const void *request, size_t request_sz, void *response, size_t response_sz) @@ -61,7 +182,7 @@ static struct pci_tsm *pci_cma_tsm_probe(struct tsm_dev = *tsm_dev, } =20 cma->spdm =3D spdm_create(&pdev->dev, pci_doe_transport, cma->host.doe_mb, - PCI_DOE_MAX_PAYLOAD, NULL); + PCI_DOE_MAX_PAYLOAD, pci_cma_validate); if (!cma->spdm) { pci_tsm_host_destructor(&cma->host); kfree(cma); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index ebce402854de..113f4e802ec4 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -150,6 +150,9 @@ enum OID { OID_id_ml_dsa_65, /* 2.16.840.1.101.3.4.3.18 */ OID_id_ml_dsa_87, /* 2.16.840.1.101.3.4.3.19 */ =20 + /* PCI */ + OID_CMA, /* 2.23.147 */ + OID__NR }; =20 --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A67623A6F19 for ; Tue, 1 Sep 2026 01:06:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224766; cv=none; b=RSZFHioWI8k20k1YvUhuwLd7+/b6Ge7mdC48wbiVTWcImTz/pdyI+KJjK+enTsJB50rwcoj8q9oK/JAfjbenpjtbq3O2ojFf1Ptl/Fkb9cdV0OZLlzoDObWGsPfkdBqf20jfyobXbdNcKxHiCV9y3lXY4xKmnJZU/ETsMyk0T1M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224766; c=relaxed/simple; bh=HthUjnldegi+9zjWtI3LcXV1ZM2WtC6ROPpK/XuRS6Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XuNbp4Su53b3NP0Q3gE9ARVJ7Ez5JWu+b+EvPPatIFdQx71X92AB180pJ6PEAlLl9TR+PzxMuwLiEtSJ2/W8dJWmi4oOh2tbUZQWaHNZMl5TdKlTbZfH0VCQ+7KIzjCrQzrG5QQ+15wqexJfNRC/eX4PIYCzOS5gXDFQ05N846w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iAPjRNmi; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iAPjRNmi" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-38e041ea211so3989816a91.0 for ; Mon, 31 Aug 2026 18:06:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224764; x=1788829564; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=YcvUkNy7YSfO/SiCL7bc9lxpci0543rqGrGQfdfumlI=; b=iAPjRNmi1u4QLYVZi0Xyq5YwCpXdQLJi/T5VEocWqpJM0szM3SZgxu9ZtwgjABceqR OXjdxlR642Z7rZH3DZW/sb4W1m/gbasuj7L+k2+Hsat45NBnt47j++Z8GVBu4VF1o9GN dRqABvS8k20nnoSu3Z/Net1V40YuVn7YRwifhOKX6VAPbQYau9uTm/13bzZW+sD5kJsO H4M907dp5U7lA91pObOZfLlPqhKiAha93L5VJ4GD+/3bR2cvjSA2lUsBNzskufqDl/yg oepWiTMtYodw808JJlgWyfFZEjcNTIBcAz6DXNCXzInPOHmjroXcIGoxRjof2lMyiCrw hJFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224764; x=1788829564; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=YcvUkNy7YSfO/SiCL7bc9lxpci0543rqGrGQfdfumlI=; b=Xm0lC0FGUYIC0gWCdtTLh568o2WEiVk6ZZcxQaUW1LZCm/SaMfgeKbSIcgN1dbfRFC EpHpbRBj+c6ZeHPfZBIgxxpFE26TWxVOBW8vhHHVK1pT36hS1CjSiLbzwxsGo/UMp+uQ JCBZGcmx0ehxXcuC8l58ilHK393MXCkgSN5ifmkyY85iscYB6yjZggT336SD6X4lFGBA dM3PiFaqwzBJotdcpjD5JSW3F+TXSsRvqoouPxdu6NcrB9zuFsenxqYxE5thUBeoMti0 /yYeenXFJe334ifDkgohQ8u2XzmS832Bh9xFieHrO7lhyFd6LiZKSarR9dFB3wXlf3yP mLWQ== X-Forwarded-Encrypted: i=1; AKwUvBy3lOUba9fenFrYMl5INbPpmjna8+Ms/mvkOWsPBVvOSTPz8SZ8jEXnsUcx75jpBZvVnt1eJI4+SgWzA0o=@vger.kernel.org X-Gm-Message-State: AFuF++nxqxL6WM1IUsQSOz9NgaR2iWcxlbRZHwUD+Jqe4/FSULGuTZzI +RTOx1uqQbFa/iTCxm1deRnx6NNiPe2Y/ti4+RfmoPcc6QglI+D46bSL X-Gm-Gg: AYBFou0oKLOnLGCpAUjxH8+cOo8mDL8cZ6gH31O1KOKvV/TZj6bbxykx0C9XaZycsum h4xYQ9WzaAchcXhFKDFbzGSPjVtMrBmgsBACHsgDCqEAdzmq3Nv/WWxqRMZmqBzX9So6GwFD73C JbZvmF7gFmryX0zK4smW82OfjTosPT/2/dzRskACQ3sSdPkgbL08N5CY5AYo+jvAFuEkmSudKAK onhWgUwkBZOLc37dIm4d8JGvcntR09KsN6KB/mUA5pEi0P/2kAij6WLfryZVN0e2lzSOIWAwv2Y MEWxsuAwu1Gx+WyXjoKw3qkS+qT0WctrGgoimfIKsSs30uckgYqtzzUHnKxE+TNE8Q6lXvieAB7 GEKnm96b1f8+TSdiMpsdmG3YXHYrQbEVJnRVnjNsvQctcUSloRmoa8c9G7wGUfxG4c+ya9aW+KO GWSp3io1Fx4LQyjteuoeYh6zdvK2KRdKhiILJt5ZpgbhSILrXfAg8AXto/ueYHpHALnDRDwmNkV c9jBA== X-Received: by 2002:a17:90b:48d1:b0:393:194d:5366 with SMTP id 98e67ed59e1d1-39907b5fefamr6466281a91.10.1788224763531; Mon, 31 Aug 2026 18:06:03 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.05.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:03 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 14/21] lib: rspdm: Support SPDM get_version Date: Tue, 1 Sep 2026 11:03:40 +1000 Message-ID: <20260901010347.2614656-15-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the GET_VERSION SPDM command. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- lib/rspdm/consts.rs | 16 ++++++++-- lib/rspdm/lib.rs | 54 +++++++++++++++++++++++++++------ lib/rspdm/state.rs | 67 ++++++++++++++++++++++++++++++++++++++-- lib/rspdm/validator.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 14 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 01f008958a1f..055671d43abd 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -7,16 +7,24 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 +use crate::validator::GetVersionRsp; +use core::mem; use kernel::error::{code::EINVAL, Error}; =20 // SPDM versions supported by this implementation pub(crate) const SPDM_VER_10: u8 =3D 0x10; +#[allow(dead_code)] +pub(crate) const SPDM_VER_11: u8 =3D 0x11; +#[allow(dead_code)] +pub(crate) const SPDM_VER_12: u8 =3D 0x12; +#[allow(dead_code)] +pub(crate) const SPDM_VER_13: u8 =3D 0x13; +pub(crate) const SPDM_VER_14: u8 =3D 0x14; =20 pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; +pub(crate) const SPDM_MAX_VER: u8 =3D SPDM_VER_14; =20 -#[allow(dead_code)] pub(crate) const SPDM_REQ: u8 =3D 0x80; -#[allow(dead_code)] pub(crate) const SPDM_ERROR: u8 =3D 0x7f; =20 #[derive(Clone, Copy)] @@ -90,3 +98,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::= fmt::Result { write!(f, "{:#x}", *self as u8) } } + +pub(crate) const SPDM_GET_VERSION: u8 =3D 0x84; +pub(crate) const SPDM_GET_VERSION_LEN: usize =3D + mem::size_of::() + (u8::MAX as usize) * mem::size_of::<= u16>(); diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 1883579b817a..58d86ea06fd9 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -18,8 +18,10 @@ c_int, c_void, // }; +use core::pin::Pin; use core::ptr; use kernel::prelude::*; +use kernel::sync::{new_mutex, Mutex}; use kernel::{ alloc::flags, bindings, // @@ -51,11 +53,22 @@ pub extern "C" fn spdm_create( transport_sz: u32, validate: bindings::spdm_validate, ) -> *mut spdm_state { - match KBox::new( - SpdmState::new(dev, transport, transport_priv, transport_sz, valid= ate), - flags::GFP_KERNEL, - ) { - Ok(ret) =3D> KBox::into_raw(ret) as *mut spdm_state, + // Wrap the `SpdmState` in a `Mutex` so that concurrent FFI callers (f= or + // example, two threads racing on `spdm_authenticate()` for the same + // device) serialize on the lock and never form aliased `&mut SpdmStat= e` + // references. + let state =3D SpdmState::new(dev, transport, transport_priv, transport= _sz, validate); + match KBox::pin_init(new_mutex!(state), flags::GFP_KERNEL) { + Ok(b) =3D> { + // `Mutex` is `!Unpin` and must remain pinned in + // memory. The C side stores the raw pointer; `spdm_destroy()` + // re-pins via `Pin::new_unchecked` before dropping, preserving + // the pin invariant. + // SAFETY: The contents are not moved between here and the + // matching `KBox::from_raw` in `spdm_destroy()`. + let raw =3D KBox::into_raw(unsafe { Pin::into_inner_unchecked(= b) }); + raw as *mut spdm_state + } Err(_) =3D> ptr::null_mut(), } } @@ -70,7 +83,25 @@ pub extern "C" fn spdm_create( /// Return 0 on success or a negative errno. In particular, -EPROTONOSUPP= ORT /// indicates authentication is not supported by the device. #[export] -pub extern "C" fn spdm_authenticate(_state_ptr: *mut spdm_state) -> c_int { +pub extern "C" fn spdm_authenticate(state_ptr: *mut spdm_state) -> c_int { + if state_ptr.is_null() { + return -(bindings::EINVAL as c_int); + } + + // SAFETY: `state_ptr` was returned from `spdm_create()` (which leaks a + // `Pin>>`) and has not yet been passed to + // `spdm_destroy()`. We only form a shared reference to the mutex; the + // exclusive `&mut SpdmState` lives entirely inside the lock guard, so + // concurrent FFI callers serialize on the mutex and can never form + // aliased `&mut SpdmState` references. + let mutex: &Mutex =3D unsafe { &*(state_ptr as *const Mutex= ) }; + + let mut state =3D mutex.lock(); + + if let Err(e) =3D state.get_version() { + return e.to_errno() as c_int; + } + -(EPROTONOSUPPORT as i32) } =20 @@ -82,8 +113,11 @@ pub extern "C" fn spdm_destroy(state_ptr: *mut spdm_sta= te) { if state_ptr.is_null() { return; } - // SAFETY: `state_ptr` was returned from `spdm_create` (which uses - // `KBox::into_raw`) and the caller guarantees the state is no longer - // in use. Reconstructing the `KBox` and dropping it frees the state. - drop(unsafe { KBox::from_raw(state_ptr as *mut SpdmState) }); + // SAFETY: `state_ptr` was returned from `spdm_create()`, which leaked= a + // `Pin>>` via `KBox::into_raw`. The caller + // guarantees the state is no longer in use. Reconstructing the pinned + // box and dropping it runs `Drop` for the `Mutex` and `SpdmState` and + // frees the allocation. + let b =3D unsafe { KBox::from_raw(state_ptr as *mut Mutex) = }; + drop(unsafe { Pin::new_unchecked(b) }); } diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index e1f74d19ac4b..9e8c65a12199 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -8,6 +8,7 @@ //! =20 use core::ffi::c_void; +use core::slice::from_raw_parts_mut; use kernel::prelude::*; use kernel::{ bindings, @@ -22,10 +23,14 @@ use crate::consts::{ SpdmErrorCode, SPDM_ERROR, + SPDM_GET_VERSION_LEN, + SPDM_MAX_VER, SPDM_MIN_VER, SPDM_REQ, // }; use crate::validator::{ + GetVersionReq, + GetVersionRsp, SpdmErrorRsp, SpdmHeader, // }; @@ -33,6 +38,11 @@ /// The current SPDM session state for a device. Based on the /// C `struct spdm_state`. /// +/// Concurrent access is serialized by wrapping the whole struct in a +/// `Mutex` at the FFI boundary, so `spdm_authenticate()` calle= rs +/// run one at a time and the locked `&mut SpdmState` is the only way to +/// reach the inner fields. +/// /// `dev`: Responder device. Used for error reporting and passed to @tran= sport. /// `transport`: Transport function to perform one message exchange. /// `transport_priv`: Transport private data. @@ -72,7 +82,6 @@ pub(crate) fn new( } } =20 - #[allow(dead_code)] fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> { match rsp.error_code { SpdmErrorCode::InvalidRequest =3D> { @@ -184,7 +193,6 @@ fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Er= ror> { /// /// The data in `request_buf` is sent to the device and the response is /// stored in `response_buf`. - #[allow(dead_code)] pub(crate) fn spdm_exchange( &self, request_buf: &mut [u8], @@ -234,4 +242,59 @@ pub(crate) fn spdm_exchange( =20 Ok(length) } + + /// Negotiate a supported SPDM version and store the information + /// in the `SpdmState`. + pub(crate) fn get_version(&mut self) -> Result<(), Error> { + let mut request =3D GetVersionReq::default(); + request.version =3D SPDM_MIN_VER; + self.version =3D SPDM_MIN_VER; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { + from_raw_parts_mut( + &mut request as *mut _ as *mut u8, + core::mem::size_of::(), + ) + }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, SPDM_GET_V= ERSION_LEN, GFP_KERNEL)?; + + let rc =3D self.spdm_exchange(request_buf, response_vec.as_mut_sli= ce())? as usize; + + // The transport must report a length within the buffer we provide= d. + if rc > response_vec.len() { + return Err(EINVAL); + } + response_vec.truncate(rc); + + let response: &GetVersionRsp =3D Untrusted::new(response_vec.as_sl= ice()).validate()?; + + let mut foundver =3D false; + let entry_count =3D response.version_number_entry_count; + let entries_offset =3D core::mem::offset_of!(GetVersionRsp, versio= n_number_entries); + + for i in 0..entry_count as usize { + let off =3D entries_offset + i * core::mem::size_of::(); + let entry =3D u16::from_le_bytes([response_vec[off], response_= vec[off + 1]]); + let alpha_version =3D (entry & 0xF) as u8; + let version =3D (entry >> 8) as u8; + + if alpha_version > 0 { + pr_warn!("Alpha version {alpha_version} is not specificall= y supported\n"); + } + + if version >=3D self.version && version <=3D SPDM_MAX_VER { + self.version =3D version; + foundver =3D true; + } + } + + if !foundver { + pr_err!("No common supported version\n"); + return Err(EPROTO); + } + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 323242e84580..5990ff7ada29 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -7,6 +7,10 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 +use crate::bindings::{ + __IncompleteArrayField, + __le16, // +}; use crate::consts::SpdmErrorCode; use core::mem; use kernel::prelude::*; @@ -21,6 +25,11 @@ }, }; =20 +use crate::consts::{ + SPDM_GET_VERSION, + SPDM_MIN_VER, // +}; + #[repr(C, packed)] pub(crate) struct SpdmHeader { pub(crate) version: u8, @@ -90,3 +99,63 @@ fn validate(unvalidated: &[u8]) -> Result { Ok(unsafe { &*ptr }) } } + +#[repr(C, packed)] +pub(crate) struct GetVersionReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, +} + +impl Default for GetVersionReq { + fn default() -> Self { + GetVersionReq { + version: 0, + code: SPDM_GET_VERSION, + param1: 0, + param2: 0, + } + } +} + +#[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<'a> Validate> for &'a GetVersionRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let version =3D *(unvalidated.get(0).ok_or(ENOMEM))? as usize; + if version !=3D SPDM_MIN_VER.into() { + return Err(EINVAL); + } + + let version_number_entries =3D *(unvalidated.get(5).ok_or(ENOMEM))= ? as usize; + let total_expected_size =3D + version_number_entries * mem::size_of::<__le16>() + mem::size_= of::(); + if unvalidated.len() < total_expected_size { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `GetVersionRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &GetVersionRsp =3D unsafe { &*ptr }; + + Ok(rsp) + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B39E34D382 for ; Tue, 1 Sep 2026 01:06:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224775; cv=none; b=sZYdSeDrZstnv09HIsxFjhRDmKvg6WFoKkSNGjTFpo7oF9SW6Chugh+7moHxZg1M/8Kx9MQNvdY/pD7SlYXM5UOPC4aTXKB82UYgefn4n2mJo5k+7Nv/dSPcos0xMuRab157NXTzNnBDc55aAZQHX2KhhKrj5K2T7vs9l+asWz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224775; c=relaxed/simple; bh=SsyAyvYVRxyaGbCofbAxsc2u7fXyh+1qL/mHq3d5ifQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ko+zLxaG5AsdT11fsB5NCenaPYrFDVYK5+iEZ7fXL4cT4QMuNIgJqy6hJSHWuswW3kjRIaTF9XBrZO3VmWgJbwsbrvrE3XQYXLPt/n5ItXJHaB7X+8AQS8scGsKQDht4gSXvFSdotNp8aEno4nuhD5Xl185XAfC1So+sosZwZYg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U5dbj5i+; arc=none smtp.client-ip=209.85.216.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U5dbj5i+" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-38dc4553f62so5783296a91.0 for ; Mon, 31 Aug 2026 18:06:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224772; x=1788829572; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Psf3PZaEFVRsSqIrSGCbE3xPeNWSAB3Ycaexf1yJAk4=; b=U5dbj5i+CKxFndzGVej85yBIZWL3ROJafEhF1Yj61LDKLX1qp54mnCq8/6wA2f/L1g N8+12hK9N9PSdQKMIbxiMgrxUgJ+OWhkqGfnjI/WkYvJG7kbHkY3DtL7ymiIM4i2KrPO 7X+M1dRLqpbWT+hNfWBYbThiOMHsc6qkuoEznUY2yyM99OdmWNMIQBaEkZcYXkUjd49W 062OShkZyQiITN/6lynWJkd4JUMw+7qLVA/+YOAZNt/dBLJ3iyqW0lGmvzToiCcrkGce qqQBAVpjjur7cYZQAi2kzlX/mHtHkF8bxQc2Gwje+cr0Fw71/kgF7kHkhJ3uezzwh2vN sCZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224772; x=1788829572; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Psf3PZaEFVRsSqIrSGCbE3xPeNWSAB3Ycaexf1yJAk4=; b=Hxa8veLEhmqyFiWWSPV95KsdL1PKbErGC0eeGMgZG4bi+qIkPH/DSIeVXMn+vH/aAN PY3BsS7f77mPbhWPQfcGkrZm6eB0I/lIA7IxBFwHJDjA/1NiRd3aZXEj218ow3WbNq9A epdYC74j/xWfBMsH58bwSpbgs6BTqn8MahIR8o/+d4eaXFLxwDpqYvWQvJOx56loUyZ8 HkPjTmgWgLUlyjgDhp5aDFk2XfCKnDgIJ+tsliyDCOLPGjVHv8nERvAtqD8ANLIrewnF fxqZRikWAOvZqDb+76nng8pWWbTix+oChrjGy7b6oyTv2ZXp1w0w3hqGt3PLUQqXNO6N WLEA== X-Forwarded-Encrypted: i=1; AKwUvBwv0aAizNhJ6rloVQdLlzkvMz5m3X978vrCaMP8vLOBYDAXkdmMP/WJY+oI9X3G/H9ZYto+p+o2W8yM6xk=@vger.kernel.org X-Gm-Message-State: AFuF++lSgTKN8FlYT2BByCVhCuYOse4x1XhWdEN9mVQF6v7iK62Wc5PT 67SKdByXsmwYAyYCO0SSdiB7J9HqoDyAVszdbjclfYRysbpzNPUmZ4Ar X-Gm-Gg: AYBFou02JHqcYJmajMgSLoaL2H/LjWSzPfbNUah+H77aRPl+90p1s61RSmN6ZM+/WaW 64KKNGmEeX7vcD2MeCkzKvhsIwwTRnboi7Rj59dEnAJ8PkP0Beo20zJDfY9ii6bcd+Q1tcvve4l YprcKz3ev70JfBiY/XbVuy2Cio7DoKyz5dNFaWDuu+4yHq7Apey8ciez0oLiO0sDGN8AerlD3f4 1/hdsWfqPQ+ZOE1sBv+3srFpFRrQETDhBEtHRkt7qHOZmLsZN4ExhsgVzjT+rSYCqcMqrgvGT8c knkXP+Zjy7pjgMucl/QVodzByMD2qNOv2/FHYFw//hBe7Wblbt6KsFwYhoqix7yNd2saMnV0LKQ gj2xgQewGLZGoIKUldyYwqeq7KIH5oIsxD9pcEtEyx6x+9QCBP5BzktPaZUlb6r58EUYPNHgTwI WJNRxFTQ4bJEcn4P+tfqO001w09VeWdrQvRzbQfkAgn2t7I+yiYzYMUm1VxU1b/Ku7rwSv6/670 j44ag== X-Received: by 2002:a17:90b:3903:b0:398:ba96:1afd with SMTP id 98e67ed59e1d1-39907bbe018mr5613741a91.8.1788224771878; Mon, 31 Aug 2026 18:06:11 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:11 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 Message-ID: <20260901010347.2614656-16-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the GET_CAPABILITIES SPDM command. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- 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 @@ =20 use crate::validator::GetVersionRsp; use core::mem; +use kernel::bits::bit_u32; use kernel::error::{code::EINVAL, Error}; =20 // SPDM versions supported by this implementation pub(crate) const SPDM_VER_10: u8 =3D 0x10; -#[allow(dead_code)] pub(crate) const SPDM_VER_11: u8 =3D 0x11; -#[allow(dead_code)] pub(crate) const SPDM_VER_12: u8 =3D 0x12; #[allow(dead_code)] pub(crate) const SPDM_VER_13: u8 =3D 0x13; @@ -102,3 +101,19 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { pub(crate) const SPDM_GET_VERSION: u8 =3D 0x84; pub(crate) const SPDM_GET_VERSION_LEN: usize =3D mem::size_of::() + (u8::MAX as usize) * mem::size_of::<= u16>(); + +pub(crate) const SPDM_GET_CAPABILITIES: u8 =3D 0xe1; +pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 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 C= MA +// (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 =3D 20; + +pub(crate) const SPDM_CERT_CAP: u32 =3D bit_u32(1); +pub(crate) const SPDM_CHAL_CAP: u32 =3D bit_u32(2); + +pub(crate) const SPDM_REQ_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; +pub(crate) const SPDM_RSP_MIN_CAPS: u32 =3D 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 sp= dm_state) -> c_int { return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.get_capabilities() { + return e.to_errno() as c_int; + } + -(EPROTONOSUPPORT as i32) } =20 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 { =20 // Negotiated state pub(crate) version: u8, + pub(crate) rsp_caps: u32, } =20 impl SpdmState { @@ -79,6 +89,7 @@ pub(crate) fn new( transport_sz, validate, version: SPDM_MIN_VER, + rsp_caps: 0, } } =20 @@ -297,4 +308,69 @@ pub(crate) fn get_version(&mut self) -> Result<(), Err= or> { =20 Ok(()) } + + /// Obtain the supported capabilities from an SPDM session and store t= he + /// information in the `SpdmState`. + pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> { + let mut request =3D GetCapabilitiesReq::default(); + request.version =3D self.version; + + let (req_sz, rsp_sz) =3D match self.version { + SPDM_VER_10 =3D> ( + core::mem::size_of::(), + core::mem::size_of::() + 4 + core::mem::size_o= f::(), + ), + SPDM_VER_11 =3D> { + let len =3D core::mem::size_of::() + 4 + core:= :mem::size_of::(); + (len, len) + } + _ =3D> { + request.data_transfer_size =3D self.transport_sz.to_le(); + request.max_spdm_msg_size =3D request.data_transfer_size; + + ( + core::mem::size_of::(), + core::mem::size_of::(), + ) + } + }; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, rsp_sz, GF= P_KERNEL)?; + + let rc =3D self.spdm_exchange(request_buf, response_vec.as_mut_sli= ce())? as usize; + + // The transport must report a length within the buffer we provide= d. + if rc > response_vec.len() { + pr_err!("Overflowed capabilities response\n"); + return Err(EIO); + } + response_vec.truncate(rc); + + let response: &mut GetCapabilitiesRsp =3D Untrusted::new(&mut resp= onse_vec).validate()?; + + self.rsp_caps =3D u32::from_le(response.flags); + if (self.rsp_caps & SPDM_RSP_MIN_CAPS) !=3D SPDM_RSP_MIN_CAPS { + pr_err!( + "{:#x} capabilities are supported, which don't meet requir= ed {:#x}\n", + self.rsp_caps, + SPDM_RSP_MIN_CAPS + ); + self.rsp_caps =3D 0; + return Err(EPROTONOSUPPORT); + } + + if self.version >=3D SPDM_VER_12 { + let data_transfer_size =3D 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 =3D 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 @@ }; =20 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, // }; =20 #[repr(C, packed)] @@ -159,3 +164,104 @@ fn validate(unvalidated: &[u8]) -> Result { 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>> for &'a mut GetCapabilities= Rsp { + type Err =3D Error; + + fn validate(unvalidated: &mut KVec) -> Result { + let version =3D *(unvalidated.get(0).ok_or(EINVAL))?; + + let expected_length =3D match version { + SPDM_VER_10 | SPDM_VER_11 =3D> { + core::mem::size_of::() + 4 + core::mem::size_o= f::() + } + _ =3D> { + // Check to see if param1 is set + if *(unvalidated.get(2).ok_or(EINVAL))? =3D=3D 0 { + mem::size_of::() + - 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::() { + unvalidated.push(0, GFP_KERNEL)?; + } + + let ptr =3D unvalidated.as_mut_ptr(); + // CAST: `GetCapabilitiesRsp` only contains integers and has `repr= (C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut GetCapabilitiesRsp =3D unsafe { &mut *ptr }; + + Ok(rsp) + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A1B23AAF45 for ; Tue, 1 Sep 2026 01:06:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224785; cv=none; b=a2AjQwD9F8DHho+ygdzlpXXSh/3IibKGLzsZopvvF2jXPusln8x7A82N90VnwiVq6ED8QSnzvc1TrjR3TNpzwxHVCRSPNr7A7WvxKPtrw7tygw0ZchYFy0RHLPfDLiUx85SYYmXAd5pqS3q8OXgNdCCKfFnf2tx57x2EwItgU/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224785; c=relaxed/simple; bh=kTHJ/QPkYNd//c0TESdkc13fbM/gd48KpcZp89S1ub0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WwFD5lYywXxTRvdvDSUxTWqimPeJLTgAGCpPWEqNzR/8vt+K9sHMQf5YMJlOcOBH2RIRY3R5PUB2vf5Dcm2bRDounlo9oz+E4LAbzq/qaInzyhOESmj0J6MCuU6eySzkl3IKnDlhgYBiWLj2qW3lXkcCy3k6t5UjiT02o5djVNg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hhKsfis2; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hhKsfis2" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2d53197d8b5so35077765ad.3 for ; Mon, 31 Aug 2026 18:06:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224783; x=1788829583; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=j3Gr1v/xD1HJHe6jn4Cyq8fVkwF8e8ZZjnc+itEfv9M=; b=hhKsfis2HC/E3UpOnlEwW4jchdSZ8+Z31i59vN9ZDx+jaTpU5DLeJakoctBSqXKbyI aulYH/Nc/ka+nl2F/rMpruLr/VCP337RdGeD0KPpxuyAiPPm1bV0PGchTJEe9f0450E+ 9hiASew2FTUAa2Fflro3P75CZRTJk+6d2DCH9rVeH80G0JkPJUNYsRR+hXamjA7VmqDQ 3uRCxrjX10Ld6vuKQzWmv31k7ua6Ra9FAPfpbBZ+uk7+DBvdrW7EYnfg2klRmVbMNJqA s6uvJnW/y/AXsZpyWcTo9ZWiy3+UqinyU+b77FxtMPKw6D12akXe4lCX/qPU9nWMXr5v iTng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224783; x=1788829583; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=j3Gr1v/xD1HJHe6jn4Cyq8fVkwF8e8ZZjnc+itEfv9M=; b=BPVE+z3mNCn/VdjJWOan79FaluPpmBdNcK/g99uuSpLqV8ENWrbq5Rgt6e0QLHlZPz 1zLRvD+u3avDexAX8cxbxJmDDH8KI1HzNo4KqnvvyhctU8833+wvE5fHZOKwq9hPOVUq BTh6UEbsflvLqMAcrxlym4N9zFwyAbys83FydRRQ7U7A49pVWt9kh6OyQa1XHysveXIX w/CBWLJctb8Em4nLRw62764U7nPrU20XX72V/g6+E1BY7we3PXXE2tMZkweUwjwDjWSi npepD9cPstJOZp2PqoZktJo7fZcvgKRll5huXUXaI9ry6VUhxKwEGdtvROsdl7R6Klw4 LYEQ== X-Forwarded-Encrypted: i=1; AKwUvBzFXACUCXKTKG75e8Gu1yQfTtXn/rG/mAJAL/dagmjtaFUy0MuK6BuGgokdnddJK/qDaT1M8uatZFsoN0w=@vger.kernel.org X-Gm-Message-State: AFuF++lEk7QtghiXQVm9jzsz3d/ElXwA0IgSGTYhNAdEk2yAB3/eeJh6 at+wk8o1CcbiB65PTKj2FcR+TFpaH5OD8QzJNWm7LDjGrHNQXGtzQEbi X-Gm-Gg: AYBFou0HlhkSABHu/s1T35wdo4g3mCxqZj9FbzCcRlxEV2AFG918KUvzH8Ixj/2UAFV yvi/jzAkeLjThoN1BWxLCklROx/CjQh8g6QwO7LV6y8etBnHDTG7mkh6ckyE3QnTOHtkiKjaxPE 32kmWbx+FP6u0TEPA/NWafWiSWszMD1AHHRVtYBhgI8YT/ig/EmdbqWpYlfFX8ipLxui8EkVVwG iyh9I1CrHFyqGXFL/iZrLUnPkHDOKWtABoFlHVgaRUzgcqvFJ9XF92P8DoKGtCEQ4OBZxYDW7yI 7EllTAqtOGVdcvzW4mcyQORU1bdjRWIIgesFiD/IIquRiMeuZ1FmeyEN+S2gpAkblHN2GGTMoC+ lFzNNL02nkTWYx2oWESM6vZBgojL1Hv3EeqUjMwrvekgBIWvjkVkekqx0kW75tyGb5XUX4n1LLs BY6Pepn4JGJjZ1szV2TPU2suRd1gJAeu2e17+dAzmmf9Zs4kRD8pjtZTLLyzXZIgeK7oBB9Ap1o o0DYyo= X-Received: by 2002:a17:90b:5281:b0:38e:6aa7:68ad with SMTP id 98e67ed59e1d1-39907aff83dmr6193773a91.5.1788224780251; Mon, 31 Aug 2026 18:06:20 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:19 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 16/21] lib: rspdm: Support SPDM negotiate_algorithms Date: Tue, 1 Sep 2026 11:03:42 +1000 Message-ID: <20260901010347.2614656-17-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the NEGOTIATE_ALGORITHMS SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 56 +++++++++- lib/rspdm/lib.rs | 9 +- lib/rspdm/state.rs | 243 ++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 110 ++++++++++++++++++- 4 files changed, 412 insertions(+), 6 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 15d69631ed8c..e222821bad5d 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -9,7 +9,10 @@ =20 use crate::validator::GetVersionRsp; use core::mem; -use kernel::bits::bit_u32; +use kernel::bits::{ + bit_u32, + bit_u8, // +}; use kernel::error::{code::EINVAL, Error}; =20 // SPDM versions supported by this implementation @@ -114,6 +117,57 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { =20 pub(crate) const SPDM_CERT_CAP: u32 =3D bit_u32(1); pub(crate) const SPDM_CHAL_CAP: u32 =3D bit_u32(2); +pub(crate) const SPDM_KEY_EX_CAP: u32 =3D bit_u32(9); =20 pub(crate) const SPDM_REQ_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; pub(crate) const SPDM_RSP_MIN_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; + +pub(crate) const SPDM_NEGOTIATE_ALGS: u8 =3D 0xe3; + +pub(crate) const SPDM_MEAS_SPEC_DMTF: u8 =3D bit_u8(0); + +pub(crate) const SPDM_ASYM_RSASSA_2048: u32 =3D bit_u32(0); +pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 =3D bit_u32(1); +pub(crate) const SPDM_ASYM_RSASSA_3072: u32 =3D bit_u32(2); +pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 =3D bit_u32(3); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 =3D bit_u32(4); +pub(crate) const SPDM_ASYM_RSASSA_4096: u32 =3D bit_u32(5); +pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 =3D bit_u32(6); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 =3D bit_u32(7); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 =3D bit_u32(8); +pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 =3D bit_u32(9); +pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 =3D bit_u32(10); +pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 =3D bit_u32(11); + +pub(crate) const SPDM_HASH_SHA_256: u32 =3D bit_u32(0); +pub(crate) const SPDM_HASH_SHA_384: u32 =3D bit_u32(1); +pub(crate) const SPDM_HASH_SHA_512: u32 =3D bit_u32(2); + +// If the crypto support isn't enabled don't offer the algorithms +// to the responder +#[cfg(CONFIG_CRYPTO_RSA)] +pub(crate) const SPDM_ASYM_RSA: u32 =3D + SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; +#[cfg(not(CONFIG_CRYPTO_RSA))] +pub(crate) const SPDM_ASYM_RSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_ECDSA)] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D + SPDM_ASYM_ECDSA_ECC_NIST_P256 | SPDM_ASYM_ECDSA_ECC_NIST_P384 | SPDM_A= SYM_ECDSA_ECC_NIST_P521; +#[cfg(not(CONFIG_CRYPTO_ECDSA))] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA256)] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D SPDM_HASH_SHA_256; +#[cfg(not(CONFIG_CRYPTO_SHA256))] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA512)] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D SPDM_HASH_SHA_384 | SPDM_= HASH_SHA_512; +#[cfg(not(CONFIG_CRYPTO_SHA512))] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D 0; + +pub(crate) const SPDM_ASYM_ALGOS: u32 =3D SPDM_ASYM_RSA | SPDM_ASYM_ECDSA; +pub(crate) const SPDM_HASH_ALGOS: u32 =3D SPDM_HASH_SHA2_256 | SPDM_HASH_S= HA2_384_512; + +pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 =3D bit_u8(1); diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 76325babdff2..d418d15e4c70 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -94,7 +94,7 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut spdm_= state) -> c_int { // exclusive `&mut SpdmState` lives entirely inside the lock guard, so // concurrent FFI callers serialize on the mutex and can never form // aliased `&mut SpdmState` references. - let mutex: &Mutex =3D unsafe { &*(state_ptr as *const Mutex= ) }; + let mutex: &Mutex> =3D unsafe { &*(state_ptr as *const M= utex>) }; =20 let mut state =3D mutex.lock(); =20 @@ -106,6 +106,10 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut sp= dm_state) -> c_int { return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.negotiate_algs() { + return e.to_errno() as c_int; + } + -(EPROTONOSUPPORT as i32) } =20 @@ -117,11 +121,12 @@ pub extern "C" fn spdm_destroy(state_ptr: *mut spdm_s= tate) { if state_ptr.is_null() { return; } + // SAFETY: `state_ptr` was returned from `spdm_create()`, which leaked= a // `Pin>>` via `KBox::into_raw`. The caller // guarantees the state is no longer in use. Reconstructing the pinned // box and dropping it runs `Drop` for the `Mutex` and `SpdmState` and // frees the allocation. - let b =3D unsafe { KBox::from_raw(state_ptr as *mut Mutex) = }; + let b =3D unsafe { KBox::from_raw(state_ptr as *mut Mutex>) }; drop(unsafe { Pin::new_unchecked(b) }); } diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 5ef14c8ed237..b78086c75370 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -14,19 +14,34 @@ bindings, error::{ code::EINVAL, + from_err_ptr, to_result, Error, // }, + str::CStr, validate::Untrusted, }; =20 use crate::consts::{ SpdmErrorCode, + SPDM_ASYM_ALGOS, + SPDM_ASYM_ECDSA_ECC_NIST_P256, + SPDM_ASYM_ECDSA_ECC_NIST_P384, + SPDM_ASYM_ECDSA_ECC_NIST_P521, + SPDM_ASYM_RSASSA_2048, + SPDM_ASYM_RSASSA_3072, + SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, + SPDM_HASH_ALGOS, + SPDM_HASH_SHA_256, + SPDM_HASH_SHA_384, + SPDM_HASH_SHA_512, + SPDM_KEY_EX_CAP, SPDM_MAX_VER, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, + SPDM_OPAQUE_DATA_FMT_GENERAL, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, @@ -38,6 +53,8 @@ GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, + NegotiateAlgsReq, + NegotiateAlgsRsp, SpdmErrorRsp, SpdmHeader, // }; @@ -61,8 +78,27 @@ /// Negotiated during GET_VERSION exchange. /// `rsp_caps`: Cached capabilities of responder. /// Received during GET_CAPABILITIES exchange. +/// @base_asym_alg: Asymmetric key algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_hash_alg: Hash algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @meas_hash_alg: Hash algorithm for measurement blocks. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_asym_enc: Human-readable name of @base_asym_alg's signature enco= ding. +/// Passed to crypto subsystem when calling verify_signature(). +/// @sig_len: Signature length of @base_asym_alg (in bytes). +/// S or SigLen in SPDM specification. +/// @base_hash_alg_name: Human-readable name of @base_hash_alg. +/// Passed to crypto subsystem when calling crypto_alloc_shash() and +/// verify_signature(). +/// @shash: Synchronous hash handle for @base_hash_alg computation. +/// @desc: Synchronous hash context for @base_hash_alg computation. +/// @hash_len: Hash length of @base_hash_alg (in bytes). +/// H in SPDM specification. #[expect(dead_code)] -pub(crate) struct SpdmState { +pub(crate) struct SpdmState<'a> { pub(crate) dev: *mut bindings::device, pub(crate) transport: bindings::spdm_transport, pub(crate) transport_priv: *mut c_void, @@ -72,9 +108,43 @@ pub(crate) struct SpdmState { // Negotiated state pub(crate) version: u8, pub(crate) rsp_caps: u32, + pub(crate) base_asym_alg: u32, + pub(crate) base_hash_alg: u32, + pub(crate) meas_hash_alg: u32, + + /* Signature algorithm */ + base_asym_enc: &'a CStr, + sig_len: usize, + + /* Hash algorithm */ + base_hash_alg_name: &'a CStr, + pub(crate) shash: *mut bindings::crypto_shash, + pub(crate) desc: Option<&'a mut bindings::shash_desc>, + pub(crate) hash_len: usize, } =20 -impl SpdmState { +impl Drop for SpdmState<'_> { + fn drop(&mut self) { + if let Some(desc) =3D self.desc.take() { + // SAFETY: `self.shash` is a valid handle + let desc_len =3D core::mem::size_of::() + + unsafe { bindings::crypto_shash_descsize(self.shash) } a= s usize; + + // SAFETY: `desc` was allocated as a KVec with a length of= `desc_len` + // and then transmuted to a raw pointer with into_raw_parts() + let desc_ptr =3D + unsafe { core::mem::transmute::<&mut bindings::shash_desc,= *mut u8>(desc) }; + let desc_vec =3D unsafe { KVec::::from_raw_parts(desc_ptr,= desc_len, desc_len) }; + drop(desc_vec); + } + + unsafe { + bindings::crypto_free_shash(self.shash); + } + } +} + +impl SpdmState<'_> { pub(crate) fn new( dev: *mut bindings::device, transport: bindings::spdm_transport, @@ -90,6 +160,15 @@ pub(crate) fn new( validate, version: SPDM_MIN_VER, rsp_caps: 0, + base_asym_alg: 0, + base_hash_alg: 0, + meas_hash_alg: 0, + base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"= \0") }, + sig_len: 0, + base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_uncheck= ed(b"\0") }, + shash: core::ptr::null_mut(), + desc: None, + hash_len: 0, } } =20 @@ -373,4 +452,164 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(= ), Error> { =20 Ok(()) } + + fn update_response_algs(&mut self) -> Result<(), Error> { + match self.base_asym_alg { + SPDM_ASYM_RSASSA_2048 =3D> { + self.sig_len =3D 256; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_3072 =3D> { + self.sig_len =3D 384; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_4096 =3D> { + self.sig_len =3D 512; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P256 =3D> { + self.sig_len =3D 64; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P384 =3D> { + self.sig_len =3D 96; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P521 =3D> { + self.sig_len =3D 132; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + _ =3D> { + pr_err!("Unknown asym algorithm\n"); + return Err(EINVAL); + } + } + + match self.base_hash_alg { + SPDM_HASH_SHA_256 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a256\0")?; + } + SPDM_HASH_SHA_384 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a384\0")?; + } + SPDM_HASH_SHA_512 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a512\0")?; + } + _ =3D> { + pr_err!("Unknown hash algorithm\n"); + return Err(EINVAL); + } + } + + // This is freed in when `SpdmState` is dropped, but this call + // can happen multiple times. + if self.shash !=3D core::ptr::null_mut() { + if let Some(desc) =3D self.desc.take() { + // SAFETY: `self.shash` is a valid handle + let desc_len =3D core::mem::size_of::() + + unsafe { bindings::crypto_shash_descsize(self.shash)= } as usize; + + // SAFETY: `desc` was allocated as a KVec with a lengt= h of `desc_len` + // and then transmuted to a raw pointer with into_raw_part= s() + let desc_ptr =3D + unsafe { core::mem::transmute::<&mut bindings::shash_d= esc, *mut u8>(desc) }; + let desc_vec =3D unsafe { KVec::::from_raw_parts(desc_= ptr, desc_len, desc_len) }; + drop(desc_vec); + } + + unsafe { + bindings::crypto_free_shash(self.shash); + } + } + + self.shash =3D + unsafe { bindings::crypto_alloc_shash(self.base_hash_alg_name.= as_char_ptr(), 0, 0) }; + if let Err(e) =3D from_err_ptr(self.shash) { + self.shash =3D core::ptr::null_mut(); + return Err(e); + } + + // SAFETY: `self.shash` is a valid handle (verified above). + let desc_len =3D core::mem::size_of::() + + unsafe { bindings::crypto_shash_descsize(self.shash) } as us= ize; + + let desc_vec: KVec =3D KVec::from_elem(0u8, desc_len, GFP_KERN= EL)?; + // Consume the desc_vec to make sure it isn't dropped, untill we + // manually drop it later + let (desc_buf, _length, _capacity) =3D desc_vec.into_raw_parts(); + + // SAFETY: We are casting the allocation to be a shash_desc + let desc =3D unsafe { + core::mem::transmute::<*mut c_void, &mut bindings::shash_desc>= (desc_buf as *mut c_void) + }; + desc.tfm =3D self.shash; + + self.desc =3D Some(desc); + + /* Used frequently to compute offsets, so cache H */ + self.hash_len =3D unsafe { bindings::crypto_shash_digestsize(self.= shash) as usize }; + + if let Some(desc) =3D &mut self.desc { + // SAFETY: `self.desc` is a valid and initalised `shash_desc` = sized buffer + unsafe { to_result(bindings::crypto_shash_init(*desc)) } + } else { + Err(ENOMEM) + } + } + + pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> { + let mut request =3D NegotiateAlgsReq::default(); + request.version =3D self.version; + + if self.version >=3D SPDM_VER_12 && (self.rsp_caps & SPDM_KEY_EX_C= AP) =3D=3D SPDM_KEY_EX_CAP { + request.other_params_support =3D SPDM_OPAQUE_DATA_FMT_GENERAL; + } + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D core::mem::size_of::(); + + request.length =3D (req_sz as u16).to_le(); + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, rsp_sz, GF= P_KERNEL)?; + + let rc =3D self.spdm_exchange(request_buf, response_vec.as_mut_sli= ce())? as usize; + + // The transport must report a length within the buffer we provide= d. + if rc > response_vec.len() { + pr_err!("Overflowed capabilities response\n"); + return Err(EIO); + } + response_vec.truncate(rc); + + let response: &NegotiateAlgsRsp =3D Untrusted::new(response_vec.as= _slice()).validate()?; + + self.base_asym_alg =3D u32::from_le(response.base_asym_sel); + self.base_hash_alg =3D u32::from_le(response.base_hash_sel); + self.meas_hash_alg =3D u32::from_le(response.measurement_hash_algo= ); + + if self.base_asym_alg & SPDM_ASYM_ALGOS =3D=3D 0 || self.base_hash= _alg & SPDM_HASH_ALGOS =3D=3D 0 { + pr_err!("No common supported algorithms\n"); + return Err(EPROTO); + } + + // /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */ + if self.base_asym_alg.count_ones() !=3D 1 + || self.base_hash_alg.count_ones() !=3D 1 + || self.meas_hash_alg.count_ones() !=3D 1 + || response.ext_asym_sel_count !=3D 0 + || response.ext_hash_sel_count !=3D 0 + || response.param1 > request.param1 + || response.other_params_sel !=3D request.other_params_support + { + pr_err!("Malformed algorithms response\n"); + return Err(EPROTO); + } + + self.update_response_algs()?; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 42c0b28cdcaa..4f7a82d4b210 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -9,7 +9,8 @@ =20 use crate::bindings::{ __IncompleteArrayField, - __le16, // + __le16, + __le32, // }; use crate::consts::SpdmErrorCode; use core::mem; @@ -26,10 +27,14 @@ }; =20 use crate::consts::{ + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, + SPDM_HASH_ALGOS, + SPDM_MEAS_SPEC_DMTF, SPDM_MIN_VER, + SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, SPDM_VER_10, SPDM_VER_11, // @@ -265,3 +270,106 @@ fn validate(unvalidated: &mut KVec) -> Result { Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct RegAlg { + pub(crate) alg_type: u8, + pub(crate) alg_count: u8, + pub(crate) alg_supported: u16, + pub(crate) alg_external: __IncompleteArrayField<__le32>, +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, // size of resp_alg_struct + param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification: u8, + pub(crate) other_params_support: u8, + + pub(crate) base_asym_algo: u32, + pub(crate) base_hash_algo: u32, + + reserved1: [u8; 12], + + pub(crate) ext_asym_count: u8, + pub(crate) ext_hash_count: u8, + reserved2: u8, + pub(crate) mel_specification: u8, + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) resp_alg_struct: __IncompleteArrayField, +} + +impl Default for NegotiateAlgsReq { + fn default() -> Self { + NegotiateAlgsReq { + version: 0, + code: SPDM_NEGOTIATE_ALGS, + param1: 0, // Size of resp_alg_struct + param2: 0, + length: 32, + measurement_specification: SPDM_MEAS_SPEC_DMTF, + other_params_support: 0, + base_asym_algo: SPDM_ASYM_ALGOS.to_le(), + base_hash_algo: SPDM_HASH_ALGOS.to_le(), + reserved1: [0u8; 12], + ext_asym_count: 0, + ext_hash_count: 0, + reserved2: 0, + mel_specification: 0, + ext_asym: __IncompleteArrayField::new(), + ext_hash: __IncompleteArrayField::new(), + resp_alg_struct: __IncompleteArrayField::new(), + } + } +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification_sel: u8, + pub(crate) other_params_sel: u8, + + pub(crate) measurement_hash_algo: u32, + pub(crate) base_asym_sel: u32, + pub(crate) base_hash_sel: u32, + + reserved1: [u8; 11], + + pub(crate) mel_specification_sel: u8, + pub(crate) ext_asym_sel_count: u8, + pub(crate) ext_hash_sel_count: u8, + reserved2: [u8; 2], + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) resp_alg_struct: __IncompleteArrayField, +} + +impl<'a> Validate> for &'a NegotiateAlgsRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `NegotiateAlgsRsp` only contains integers and has `repr(C= )`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &NegotiateAlgsRsp =3D unsafe { &*ptr }; + + Ok(rsp) + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pg1-f181.google.com (mail-pg1-f181.google.com [209.85.215.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E635F3ACA5B for ; Tue, 1 Sep 2026 01:06:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224792; cv=none; b=k6C1J4sS+OHm2aeUnRfnnSCy8vkJfTIDlCOMsSduM897K95izaioyLP5NH9Iz8eKQqwBiADOjSHkqRHCaWJ6i4mOEhJwo/GVrUP14u68GVBtOh9Q+bcSAdn7559yzZcaDUbx3L1B8HPKrsgTDUECLXoYQEFGR6FrqBDBvrqQfSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224792; c=relaxed/simple; bh=cZDs5FSiErKhdb59fvbIO2wHQUXcvwBVv5IZLUaKgNU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d8qmaPHG4yOFQyYB/IuxOdsTxoOlb4wQBNSXp6hh+19VrKPUnGWazJPNud6gzk3kwwTqBQKctHpvwgt5saXTgRNh6VY5tu7GeWrXjr6pp44+BRNFAY8rVReCqTdZLA3goblln2LEKvD3pmfoVhKBZN6PgQ6FIOD3+6AN1x56OGM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U2zPAKrk; arc=none smtp.client-ip=209.85.215.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U2zPAKrk" Received: by mail-pg1-f181.google.com with SMTP id 41be03b00d2f7-ca80d708489so384719a12.1 for ; Mon, 31 Aug 2026 18:06:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224789; x=1788829589; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=zc/YFp8CPqmIoAvXYTRy9XGxn2OzKW6NDo5dSVttpB0=; b=U2zPAKrkYs9/Qd3uQgfs6Z8/63NsBzbHH+3HqV/uHOjPB2ZQIDuOfA3t+pA5AgxYeZ pSSc6/2WrUBC1Z0eBM2rZs/qBL5j1pyLd/8DjjMYC5cwdxQwe+jbGJTIAGyL2UsRMcg7 q+fsvmkVJTeK4doZQyPk3dJPcQXSZcp9CfQffCgSsL8UoLmcslU4NwiTIKYdUMHGIPwX ++pLY2EJyJKFGKLmFIK8hfxuBpJSzu0EsRz9/7DYKUGK9ZPqwzLmSzZ0IaOZQ79Sq4at +4yXHxNeRwuDbzl1w2kZ3H2so5aFOh3AY//xswmt4TNyObPlHMuA+Z+iER/LyFnV9pKN XicA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224789; x=1788829589; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=zc/YFp8CPqmIoAvXYTRy9XGxn2OzKW6NDo5dSVttpB0=; b=VDxN98RuoaFyoSHrCtC+8ApFrmVNl87CMuRAnofb1n5r/vuurGEwEAXaIaoB4OyPzX ms7+4T4RMpnJKNJ1/8qzzxP3vQgiY2uT/cSublelhLp+SIn/6+8bLhMuya+haKhWCY8B pxdrtizgBA8yV0WUwpoiB0uBHzD9yZka9GnmNeO+ZCfCl5JdstVQ17i7KMEb5AkfyJ4z 0bMjk933OykaRBz5hV6qNw7wT3Q010trWpFK3iFPMYZ7G+FqOyjkIPqyBpXWVYbPqjrp rjfjyXhK4wFXZK9P6IZ9CyEte/8/IYNgSP8z7x3ZuBcYYzn/MBTVUXKV0VHbMVJKf9jm 3Sag== X-Forwarded-Encrypted: i=1; AKwUvBxrCXf4yfK8PtKV88oh0qimBd/0ALDRUoqxevMME9uJu3jFgCRVBF4hwNqpjfUqmGdV4A2ea5BNiYmlwT0=@vger.kernel.org X-Gm-Message-State: AFuF++mHgLdZBy+rKoH79hU5jRE41Idc/Zhb1u9zNKM7J/ak+XszAkTt 67sDZ5knYQt5LUbFX/iv2dvLlBirXwBkt2sQ/ffiSWoWOOxL+XxnpvUx X-Gm-Gg: AYBFou34EU97RAgLHSCy49siEK1e49qY69LDN13FJEG8cFvU8rCecP3yBLGm3E8t4YA snOjxHifg9tXG152x19H7RRcYH1OdDJ8vcgVqFUeETZiM5t4JTaqe1lf8JW0nUtlrpj5lw8DtHf T9WakibVw6iuIZ6pYi1WjR/u1azARbTbDWIWiVDe0BQoJnn6omC9bGrP1vAVSHzWfchCcCA0Ys+ HBQB0Lo0jy2daj0perISAPYK9QKYe1PLSR9D1iiQblMd2IsJabtrp1q9awS6gfBfgoEMOLA2qnK s5f2R0K2WjjJHE6avjhFW+mOWB7vpY0jAjITDJr078fjqcxup6ni11lkspFPA3M1W6laStFRYHY GPJ6c6xNY50JcJDRkfRWtdeUjQGuRjtPqTvxW8EFw06+72xe8fFoiU9ZyHL0mudzGX5oOdGVYa6 MsefO8IDQKRKQC58sSG7I2ifrz0bs6u1KZ5g9t3b3DoxWU6pWj5fTmq576QKkmzD7AA73lR8qVi HInRW4+3LuK4NO8 X-Received: by 2002:a17:90b:5828:b0:390:84db:888e with SMTP id 98e67ed59e1d1-3990f7cc65fmr1561750a91.7.1788224788686; Mon, 31 Aug 2026 18:06:28 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:28 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 17/21] lib: rspdm: Support SPDM get_digests Date: Tue, 1 Sep 2026 11:03:43 +1000 Message-ID: <20260901010347.2614656-18-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the GET_DIGESTS SPDM command. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- lib/rspdm/consts.rs | 7 ++-- lib/rspdm/lib.rs | 4 +++ lib/rspdm/state.rs | 80 +++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 53 ++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 3 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index e222821bad5d..c4d9521866af 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -19,10 +19,11 @@ pub(crate) const SPDM_VER_10: u8 =3D 0x10; pub(crate) const SPDM_VER_11: u8 =3D 0x11; pub(crate) const SPDM_VER_12: u8 =3D 0x12; -#[allow(dead_code)] pub(crate) const SPDM_VER_13: u8 =3D 0x13; pub(crate) const SPDM_VER_14: u8 =3D 0x14; =20 +pub(crate) const SPDM_SLOTS: usize =3D 8; + pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; pub(crate) const SPDM_MAX_VER: u8 =3D SPDM_VER_14; =20 @@ -106,7 +107,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { mem::size_of::() + (u8::MAX as usize) * mem::size_of::<= u16>(); =20 pub(crate) const SPDM_GET_CAPABILITIES: u8 =3D 0xe1; -pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 42; +pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 42; // SPDM 1.2.0 ma= rgin no 226 =20 // SPDM cryptographic timeout of this implementation: // Assume calculations may take up to 1 sec on a busy machine, which equals @@ -143,6 +144,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { pub(crate) const SPDM_HASH_SHA_384: u32 =3D bit_u32(1); pub(crate) const SPDM_HASH_SHA_512: u32 =3D bit_u32(2); =20 +pub(crate) const SPDM_GET_DIGESTS: u8 =3D 0x81; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index d418d15e4c70..bda5f91ca13c 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -110,6 +110,10 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut sp= dm_state) -> c_int { return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.get_digests() { + return e.to_errno() as c_int; + } + -(EPROTONOSUPPORT as i32) } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index b78086c75370..131c22319b26 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -44,13 +44,17 @@ SPDM_OPAQUE_DATA_FMT_GENERAL, SPDM_REQ, SPDM_RSP_MIN_CAPS, + SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, - SPDM_VER_12, // + SPDM_VER_12, + SPDM_VER_13, // }; use crate::validator::{ GetCapabilitiesReq, GetCapabilitiesRsp, + GetDigestsReq, + GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, @@ -86,6 +90,10 @@ /// Selected by responder during NEGOTIATE_ALGORITHMS exchange. /// @meas_hash_alg: Hash algorithm for measurement blocks. /// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @supported_slots: Bitmask of responder's supported certificate slots. +/// Received during GET_DIGESTS exchange (from SPDM 1.3). +/// @provisioned_slots: Bitmask of responder's provisioned certificate slo= ts. +/// Received during GET_DIGESTS exchange. /// @base_asym_enc: Human-readable name of @base_asym_alg's signature enco= ding. /// Passed to crypto subsystem when calling verify_signature(). /// @sig_len: Signature length of @base_asym_alg (in bytes). @@ -97,6 +105,8 @@ /// @desc: Synchronous hash context for @base_hash_alg computation. /// @hash_len: Hash length of @base_hash_alg (in bytes). /// H in SPDM specification. +/// @certs: Certificate chain in each of the 8 slots. Empty KVec if a slot= is +/// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. #[expect(dead_code)] pub(crate) struct SpdmState<'a> { pub(crate) dev: *mut bindings::device, @@ -111,6 +121,8 @@ pub(crate) struct SpdmState<'a> { pub(crate) base_asym_alg: u32, pub(crate) base_hash_alg: u32, pub(crate) meas_hash_alg: u32, + pub(crate) supported_slots: u8, + pub(crate) provisioned_slots: u8, =20 /* Signature algorithm */ base_asym_enc: &'a CStr, @@ -121,6 +133,9 @@ pub(crate) struct SpdmState<'a> { pub(crate) shash: *mut bindings::crypto_shash, pub(crate) desc: Option<&'a mut bindings::shash_desc>, pub(crate) hash_len: usize, + + // Certificates + pub(crate) certs: [KVec; SPDM_SLOTS], } =20 impl Drop for SpdmState<'_> { @@ -163,12 +178,15 @@ pub(crate) fn new( base_asym_alg: 0, base_hash_alg: 0, meas_hash_alg: 0, + supported_slots: 0, + provisioned_slots: 0, base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"= \0") }, sig_len: 0, base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_uncheck= ed(b"\0") }, shash: core::ptr::null_mut(), desc: None, hash_len: 0, + certs: [const { KVec::new() }; SPDM_SLOTS], } } =20 @@ -612,4 +630,64 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), = Error> { =20 Ok(()) } + + pub(crate) fn get_digests(&mut self) -> Result<(), Error> { + let mut request =3D GetDigestsReq::default(); + request.version =3D self.version; + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D core::mem::size_of::() + SPDM_SLOTS = * self.hash_len; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, rsp_sz, GF= P_KERNEL)?; + + let len =3D self.spdm_exchange(request_buf, response_vec.as_mut_sl= ice())? as usize; + + // The transport must report a length within the buffer we provide= d. + if len > response_vec.len() { + pr_err!("Overflowed digests response\n"); + return Err(EIO); + } + response_vec.truncate(len); + + let response: &GetDigestsRsp =3D Untrusted::new(response_vec.as_sl= ice()).validate()?; + + if len + < core::mem::size_of::() + + response.param2.count_ones() as usize * self.hash_len + { + pr_err!("Overflowed digests response\n"); + return Err(EIO); + } + + let mut deprovisioned_slots =3D self.provisioned_slots & !response= .param2; + while (deprovisioned_slots.trailing_zeros() as usize) < SPDM_SLOTS= { + let slot =3D deprovisioned_slots.trailing_zeros() as usize; + self.certs[slot].clear(); + deprovisioned_slots &=3D !(1 << slot); + } + + if self.version >=3D SPDM_VER_13 && (response.param2 & !response.p= aram1 !=3D 0) { + pr_err!("Malformed digests response\n"); + return Err(EPROTO); + } + + self.provisioned_slots =3D response.param2; + if self.provisioned_slots =3D=3D 0 { + pr_err!("No certificates provisioned\n"); + return Err(EPROTO); + } + + let supported_slots =3D if self.version >=3D SPDM_VER_13 { + response.param1 + } else { + 0xFF + }; + + self.supported_slots =3D supported_slots; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 4f7a82d4b210..ddbf5c448bd7 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -30,6 +30,7 @@ SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, + SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, @@ -373,3 +374,55 @@ fn validate(unvalidated: &[u8]) -> Result { Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct GetDigestsReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, +} + +impl Default for GetDigestsReq { + fn default() -> Self { + GetDigestsReq { + version: 0, + code: SPDM_GET_DIGESTS, + param1: 0, + param2: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetDigestsRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) digests: __IncompleteArrayField, + // KeyPairIDs, added in 1.3 + + // CertificateInfo, added in 1.3 + + // KeyUsageMask, added in 1.3 +} + +impl<'a> Validate> for &'a GetDigestsRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `GetDigestsRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &GetDigestsRsp =3D unsafe { &*ptr }; + + Ok(rsp) + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE3B13B0AD4 for ; Tue, 1 Sep 2026 01:06:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224803; cv=none; b=Eh6pnJupR023mf+LDdECVlVaO+8s1xoR1eXCCLLMzf/zQhs6IqLoGJlkBii4kd0UjWSsVYH2FBpQ8Hx5x4iI0OntwA2GGLZsTfTdsLOTvcGwZrV9Ofc6Nc2PLeUOwfw/Q2vJDJx16J/Zak+sGAekcSeLnP20UnMCM6XrQ+BEVEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224803; c=relaxed/simple; bh=IWnqH1KNMEWGoDHsmLCK8gxjTirLjJDTUkD7YT6d8Mo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XTeuLpKAE4eqw+3qqkolIY9LP8P8h8B5ig9l79tVrihj2qpI9MeNcV0hmng63YUVFSqldrPgK8AqpuHkkwyYMlgiiy1BpisxH6c+ihsA2vF3l6JRLKlbQGnvbRuBh37ZnsQFb09HoBhqz/RWnof5riC1AENGEwxF1i4bc8ab8y8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XRkcKlL4; arc=none smtp.client-ip=209.85.216.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XRkcKlL4" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-38a0c7e841fso610719a91.2 for ; Mon, 31 Aug 2026 18:06:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224797; x=1788829597; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=OcjtJK01U5ABqRyyI3Y6HZHJVX25NC98kRl2mqG1tN8=; b=XRkcKlL4ni86UP4S+ALp/zMjhnu2mZkviTAZJkRjJpRCTmE7uJyxcrrG+WauGdj+F3 0PyfJFd/bnWavJV6lGxSrgOEtptEAGqFCIAlwoQpjnNAMQfPPKHUvDcITEMRqG42LTe/ PgnR+TBnvpQpgbPbP73VbWA1qulPz55oUZ3XSfww9bmpaR8d08Kz5T2bRjt98VSJJwGl HTka5VS1l5InjDvIf5I42qV4tvQEhA6NserWOZADuLokh4jaRb7awVLGahOhgqlFftBC 9MJYzri1c2ihr/OmDFCUzwvZxvW9EV9UgWxs0UkPxvFLyt0gh0dg9OP+29gIfHT/KFy4 EcNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224797; x=1788829597; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=OcjtJK01U5ABqRyyI3Y6HZHJVX25NC98kRl2mqG1tN8=; b=o8+2H69nsL5xitDjJZifZRd2uxJllPvGe5CkU6+gGvVZRRCa8FU0QtUhcPv7Bel1BS N2uLGr4cq1z2gFzIvdnFwzlHdlUj+4Z0sySm0NB9a7S4z9ddVa30pmjvEHMewbdpX4Go dy2aZQ/iHaL9j4gZ3pvuPTF5DQzklyMmzgqCkHDCUK+CDV4smintHzqBiLkXhp8OO76M UB49/vvY6170KYH6ww+rxAH9Wv3JgaplmbclhNjTB9h2886bZybJK9lvHkQWMhhZHz1O sG9gRpi9VXheZ3W1PAepDoPbq0d76xwRDA91L+YpmpcfxYKlHAhDj8sK1N7GNKlhoiU8 lXWw== X-Forwarded-Encrypted: i=1; AKwUvBzBQ3+qnoGWn7MHv6FFB6KDr87d5A2ac2fWj5dTK9CwU3YwNwmpCPcsa/zQbYR78l9UwBVr1x0JLHXrHXw=@vger.kernel.org X-Gm-Message-State: AFuF++l1ujvImjixVWSjjSmZhRj95PPDcxFV9OPVYSX9QxTj7zpEEXG1 lbcA1GFYApkcG/jL05VZpK6HaSTtqOG+Mio6Yw4aFgfbB+L+WOsXh+Q+ X-Gm-Gg: AYBFou0dE/h/Wk9NQozzqnKv31PhuTjsrAEtuAxtpL6/He+2+1PsgCG6jeAw/E4XqFv /mGaYrX0remfzSjx/BitJaq4XOGT/JyW/y+XJX1MCuuytBRBufnWtln0/Bzhv+CJJ4hjPRApAkP T7q9UGjtD1TQlkD/m3DdbDRHhH8df33qaNR2PiWMaCK1yGnP3aJFOKxDvqVvflvkQuMYRx2bq1F ycraF9J3WSnITwwVjuPc8B8v3m/G8bmiiL1enfRgx4E3uDehP81n6EoPzEo7gskU0LxUWykvOKU xBc80OnCh1vSkWSFelIptjsALKFlYN/+ag0iqaySg5Ie/ZCGGF9zsaabRp01bwritn25AGGJ0VJ RYyc93ZPPivWDaGR1XGcJpZuTFjFHVrBH+hjHEy1emIPsm8XDS5AxKQLngxPXaeK9BGHbDthYTa E3ptlCyQwxhwdz9ItaeFnVKtLAeY4WzMb8uWLgLNH1ImRWwSP0OIk382V2DKxog1oEASBcnYW9T mYi0NVSsFGRuPPR X-Received: by 2002:a17:90b:54c6:b0:36d:b424:4f17 with SMTP id 98e67ed59e1d1-396d0e24bdamr49357609a91.1.1788224796951; Mon, 31 Aug 2026 18:06:36 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:36 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 18/21] lib: rspdm: Support SPDM get_certificate Date: Tue, 1 Sep 2026 11:03:44 +1000 Message-ID: <20260901010347.2614656-19-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the GET_CERTIFICATE SPDM command. The kernel will send a GET_CERTIFICATE request to the the responder and then iterate over all of the certificates returned. Certificate validation happens in the next commit. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 2 + lib/rspdm/lib.rs | 15 ++++ lib/rspdm/state.rs | 156 +++++++++++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 65 +++++++++++++++++ 4 files changed, 238 insertions(+) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index c4d9521866af..2fbc4ab41869 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -146,6 +146,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { =20 pub(crate) const SPDM_GET_DIGESTS: u8 =3D 0x81; =20 +pub(crate) const SPDM_GET_CERTIFICATE: u8 =3D 0x82; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index bda5f91ca13c..488203be821d 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -114,6 +114,21 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut sp= dm_state) -> c_int { return e.to_errno() as c_int; } =20 + if state.provisioned_slots =3D=3D 0 { + return -(bindings::EIO as c_int); + } + + let mut provisioned_slots =3D state.provisioned_slots; + while (provisioned_slots as usize) > 0 { + let slot =3D provisioned_slots.trailing_zeros() as u8; + + if let Err(e) =3D state.get_certificate(slot) { + return e.to_errno() as c_int; + } + + provisioned_slots &=3D !(1 << slot); + } + -(EPROTONOSUPPORT as i32) } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 131c22319b26..1e8a4402e634 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -53,6 +53,8 @@ use crate::validator::{ GetCapabilitiesReq, GetCapabilitiesRsp, + GetCertificateReq, + GetCertificateRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq, @@ -159,6 +161,17 @@ fn drop(&mut self) { } } =20 +#[repr(C, packed)] +pub(crate) struct SpdmCertChain { + // `length` is a u16 (with 2 bytes reserved) for SPDM versions 1.3 + // and lower and u32 for 1.4. We don't currently support `LargeOffset` + // and `LargeLength`, so let's pretend this is always a u16 + length: u16, + _reserved: [u8; 2], + root_hash: bindings::__IncompleteArrayField, + certificates: bindings::__IncompleteArrayField, +} + impl SpdmState<'_> { pub(crate) fn new( dev: *mut bindings::device, @@ -690,4 +703,147 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Er= ror> { =20 Ok(()) } + + fn get_cert_exchange<'a>( + &mut self, + request_buf: &mut [u8], + response_vec: &'a mut KVec, + ) -> Result<&'a GetCertificateRsp, Error> { + let len =3D self.spdm_exchange(request_buf, response_vec.as_mut_sl= ice())? as usize; + + // The transport must report a length within the buffer we provide= d. + if len < core::mem::size_of::() { + pr_err!("Truncated certificate response\n"); + return Err(EIO); + } + if len > response_vec.len() { + pr_err!("Overflowed get certificate response\n"); + return Err(EIO); + } + response_vec.truncate(len); + + let response: &GetCertificateRsp =3D Untrusted::new(response_vec.a= s_slice()).validate()?; + + if len + < core::mem::size_of::() + + u16::from_le(response.portion_length) as usize + { + pr_err!("Truncated certificate response\n"); + return Err(EIO); + } + + Ok(response) + } + + pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error= > { + let mut request =3D GetCertificateReq::default(); + request.version =3D self.version; + request.param1 =3D slot; + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D (core::mem::size_of::() as u32 += u16::MAX as u32) + .min(self.transport_sz) as usize; + + request.offset =3D 0; + request.length =3D ((rsp_sz - core::mem::size_of::()) as u16).to_le(); + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, rsp_sz, GF= P_KERNEL)?; + + let response =3D self.get_cert_exchange(request_buf, &mut response= _vec)?; + + if response.param1 !=3D slot { + pr_err!("Invalid slot response\n"); + return Err(EPROTO); + } + + let portion_length =3D response.portion_length; + let rem_length =3D response.remainder_length; + + let total_cert_len =3D u16::from_le(portion_length) as usize + + u16::from_le(response.remainder_length) as usize; + + let mut certs_buf: KVec =3D KVec::new(); + + certs_buf.extend_from_slice( + &response_vec[8..(8 + u16::from_le(portion_length) as usize)], + GFP_KERNEL, + )?; + + let mut offset: u16 =3D u16::from_le(portion_length); + let mut remainder_length =3D u16::from_le(rem_length) as usize; + + while remainder_length > 0 { + request.offset =3D offset.to_le(); + request.length =3D + ((remainder_length.min(rsp_sz - core::mem::size_of::())) as u16) + .to_le(); + + let request_buf =3D + unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut= u8, req_sz) }; + + response_vec.resize( + request.length as usize + core::mem::size_of::(), + 0, + GFP_KERNEL, + )?; + + let response =3D self.get_cert_exchange(request_buf, &mut resp= onse_vec)?; + + let portion_length =3D response.portion_length; + let rem_length =3D response.remainder_length; + + if u16::from_le(portion_length) =3D=3D 0 + || (response.param1 & 0xF) !=3D slot + || offset as usize + + u16::from_le(portion_length) as usize + + u16::from_le(rem_length) as usize + !=3D total_cert_len + { + pr_err!("Malformed certificate response\n"); + return Err(EPROTO); + } + + certs_buf.extend_from_slice( + &response_vec[8..(8 + u16::from_le(portion_length) as usiz= e)], + GFP_KERNEL, + )?; + let (val, overflow) =3D offset.overflowing_add(u16::from_le(po= rtion_length)); + if overflow { + pr_err!("portion_length response overflowed\n"); + return Err(EPROTO); + } + offset =3D val; + remainder_length =3D u16::from_le(rem_length) as usize; + } + + let header_length =3D core::mem::size_of::() + self= .hash_len; + + if total_cert_len < header_length as usize || total_cert_len !=3D = certs_buf.len() { + pr_err!("Malformed certificate chain in slot {slot}\n"); + return Err(EPROTO); + } + + let cert_chain_length =3D { + let ptr =3D certs_buf.as_ptr(); + // SAFETY: `SpdmCertChain` is repr(C) and packed. We just + // checked the length above so we can convert it from a slice + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is v= alid. + let certs: &SpdmCertChain =3D unsafe { &*ptr }; + u16::from_le(certs.length) as usize + }; + + if total_cert_len !=3D cert_chain_length { + pr_err!("Malformed certificate chain in slot {slot}\n"); + return Err(EPROTO); + } + + self.certs[slot as usize].clear(); + self.certs[slot as usize].extend_from_slice(&certs_buf, GFP_KERNEL= )?; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index ddbf5c448bd7..2584bed73979 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -30,6 +30,7 @@ SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, + SPDM_GET_CERTIFICATE, SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, @@ -426,3 +427,67 @@ fn validate(unvalidated: &[u8]) -> Result { Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct GetCertificateReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) offset: u16, + pub(crate) length: u16, +} + +impl Default for GetCertificateReq { + fn default() -> Self { + GetCertificateReq { + version: 0, + code: SPDM_GET_CERTIFICATE, + param1: 0, + param2: 0, + offset: 0, + length: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetCertificateRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) portion_length: u16, + pub(crate) remainder_length: u16, + + pub(crate) cert_chain: __IncompleteArrayField, +} + +impl<'a> Validate> for &'a GetCertificateRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let rsp_sz =3D core::mem::size_of::() + + 4 + + (*(unvalidated.get(4).ok_or(EINVAL))? as usize) + + ((*(unvalidated.get(5).ok_or(EINVAL))? as usize) << 8); + + if unvalidated.len() < rsp_sz { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `GetCertificateRsp` only contains integers and has `repr(= C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &GetCertificateRsp =3D unsafe { &*ptr }; + + Ok(rsp) + } +} --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49B2C3AD51A for ; Tue, 1 Sep 2026 01:06:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224808; cv=none; b=LyhuvXVGDZ0ctC0iEMkHluygNg48webWhrXeFaMJjMaRyGSnPdWwxGQw981IgRCHjuEwZps1grVESiWAQysiEVj5vzjNk9wQKlCloAd3l6Rt2gw6+LTR29cyPIhdQtxu0HCHtXM/+WuMCpbquFIcR/R8FTGkU6TuDT7CwMNHiI8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224808; c=relaxed/simple; bh=J2T7JILoVByS1MCnyW03kxxqDPc+zD1Y62mEstcjhGs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Y1p6bVmn900q4Re/K80+OCjvz/ES40GXplIF+8mez0CUoq3uIVWc4BspalGrqHZ83apzjtiww9OWm6bXnlUi45IYUvyRC4bxjioXIvieFwbdtqyHji6ULuezwB0cUhGgVrr08ROw9OQcknlViQ8EjL487Ag0k1/HdZnb6faf278= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AKD2k6AN; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AKD2k6AN" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-398c1101c1bso2416667a91.1 for ; Mon, 31 Aug 2026 18:06:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224805; x=1788829605; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6+g1amS8TRdiwzXTKVJFNhTGQRsJ1L567PEixpjliHU=; b=AKD2k6ANVeCaqQk72UsRW/MXsWjkC9sv3DG5oxLDONtGMacee/UJHIQ7666bdKDVB/ yot9DhVmSutCQlxYl/cq3hmQ+gZ93xfjsC7dnejV2Vu1cE1s2uRFkuyni6Obum5BGfRt M4yNCzwvkUCAqnggj2VGgd+4+HcbTCRPRw9uYvFIXQwoZFOf0pKvoCU480RAV8n599FK 5CCy/TrNO5g6OKK6haz6RfKgjGzsoW0BCmx/FAB0wbGZL5iSxaRwDxXHFwaXuvnWNBU4 cOLs2rwmOenWUyMxlZNkdC2CKXHurNZw05s2ENmGPvZJZahqsSOTzSpcxftRZSph/PG3 /0PQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224805; x=1788829605; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=6+g1amS8TRdiwzXTKVJFNhTGQRsJ1L567PEixpjliHU=; b=as5oJDAAe2Y3cKbALZJVN9pNy/aOZnvMPcXxi9zqGctNLx5vTStVmOlkOVAOO5uhog iV/3+gBI+gfoOxCmIIfn5vWvlsapO0ennxaweKitFV7cnYrrEAkUIjJN8lfVzdh21bnm QPm23v7EAjuBWUYKqRYJAJsmrMx/WE8+jJDg3vkgmLhui9iRRMIYgDc7zsyuVqjEhIAw xMw+7Do8k+IEg7V8YC8l9hlCQDgK6tclEv6fkCGSUFY9UQ3ZkFylSF3attsjvxShPn3+ N5zPYS6PvieIPQ0cyUjIV3rMXCvco6wp7KsjCGQmIJNrMHeNlRx2gqLo+bTLHC6sVnPQ nLRQ== X-Forwarded-Encrypted: i=1; AKwUvBxIu0ahDStRkoT6nMk+E5s3lsepBUlgKNl58mdnEUAHR39Cyuxf1zVOGYhuBE1BVyWu+4sbCs6XwAeqGmE=@vger.kernel.org X-Gm-Message-State: AFuF++myt3G+jcMevotiLrTS59jPfswruR3tNync0LHtHBWBSfkl7WZ2 7PqeeLqDCUfDC4gOT3Cel2PgNLWHVnnT9L1VIApKST8ZBbIUjCNWbb9i X-Gm-Gg: AYBFou2454LFExJzZX7f0bCat+SFVcWEZVjTGjFPgKonkfPxMvTPZwgyPBprGbb80Uf tGjL/fAMhDPztcRrH70+hqKpewo1PH2LAoDd95tgkBJhpXz4NJdOj1/DrhZ6qd4o/8MN8Xmp27l 6XA7PxJgPM3J7r14dghMUiqVaz4sYybMbgCrcegDXevyDjppcNpQrRsgt8vBfOzFubFzNv4/ERm sfp7O5j3lyeTybexaoZrGnbaUA1/ZKTygDDWS9iEK2G0mowdfFZMUu+2f2StT2vkixTvTtp1wsO OOgkwrakPTb0MeOKgFeUvmaNm4O5UjUn05L9m9ZchL1MLVlYGFjmRoB9webntrrzdO8AZ273Sjf //3S3F9NIMHpjg0zP8ieXIQBOT7ghBExNJwRGHrIve0LIKJIkYSFQXAnawXz0TnEfVChlCR58rv j91N4YfrUkAdOd3eFiFlvsmHOpDijKCExFD8/KsoAWHPwoLsRB6dlE5FM8stGZiTtPGsnF2U90X bdHJQ== X-Received: by 2002:a17:90b:5544:b0:398:bd37:6a49 with SMTP id 98e67ed59e1d1-398bd376c65mr24254913a91.12.1788224805187; Mon, 31 Aug 2026 18:06:45 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:44 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 19/21] lib: rspdm: Support SPDM certificate validation Date: Tue, 1 Sep 2026 11:03:45 +1000 Message-ID: <20260901010347.2614656-20-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support validating the SPDM certificate chain. This only performs basic sanity checks on the chain before we continue on. This does not ensure that the root CA is trusted, we leave that for userspace to check and enforce. Instead we just make sure that the chain is correct, uses supported signatures and that it isn't blacklisted in the kernel. We then store the first leaf certificate for use later. Signed-off-by: Alistair Francis --- lib/rspdm/lib.rs | 12 +++ lib/rspdm/state.rs | 146 +++++++++++++++++++++++++++++++- rust/bindings/bindings_helper.h | 2 + 3 files changed, 159 insertions(+), 1 deletion(-) diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 488203be821d..fa5513e8bd4e 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -129,6 +129,18 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut sp= dm_state) -> c_int { provisioned_slots &=3D !(1 << slot); } =20 + let mut provisioned_slots =3D state.provisioned_slots; + while (provisioned_slots as usize) > 0 { + let slot =3D provisioned_slots.trailing_zeros() as u8; + + if let Err(e) =3D state.validate_cert_chain(slot) { + pr_err!("Certificate in slot {slot} failed to verify: {e:?}\n"= ); + return e.to_errno() as c_int; + } + + provisioned_slots &=3D !(1 << slot); + } + -(EPROTONOSUPPORT as i32) } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 1e8a4402e634..fc7df9dd7b97 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -109,7 +109,8 @@ /// H in SPDM specification. /// @certs: Certificate chain in each of the 8 slots. Empty KVec if a slot= is /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. -#[expect(dead_code)] +/// @leaf_key: Public key portion of leaf certificate against which to che= ck +/// responder's signatures. pub(crate) struct SpdmState<'a> { pub(crate) dev: *mut bindings::device, pub(crate) transport: bindings::spdm_transport, @@ -138,10 +139,20 @@ pub(crate) struct SpdmState<'a> { =20 // Certificates pub(crate) certs: [KVec; SPDM_SLOTS], + pub(crate) leaf_key: Option<*mut bindings::public_key>, } =20 impl Drop for SpdmState<'_> { fn drop(&mut self) { + if let Some(leaf_key) =3D self.leaf_key.take() { + // SAFETY: `leaf_key` was extracted from a x509 certificate + // in `validate_cert_chain()` so it is valid to pass to + // `public_key_free()`. + unsafe { + bindings::public_key_free(leaf_key); + } + } + if let Some(desc) =3D self.desc.take() { // SAFETY: `self.shash` is a valid handle let desc_len =3D core::mem::size_of::() @@ -200,6 +211,7 @@ pub(crate) fn new( desc: None, hash_len: 0, certs: [const { KVec::new() }; SPDM_SLOTS], + leaf_key: None, } } =20 @@ -846,4 +858,136 @@ pub(crate) fn get_certificate(&mut self, slot: u8) ->= Result<(), Error> { =20 Ok(()) } + + pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), E= rror> { + let cert_chain_buf =3D &self.certs[slot as usize]; + let cert_chain_len =3D cert_chain_buf.len(); + // We skip over the RootHash + let header_len =3D 4 + self.hash_len; + + let mut offset =3D header_len; + let mut prev_cert: Option<*mut bindings::x509_certificate> =3D Non= e; + + if offset >=3D cert_chain_len { + return Err(EPROTO); + } + + while offset < cert_chain_len { + // SAFETY: `cert_chain_buf[offset..]` is a non-empty slice of + // bytes valid for at least `cert_chain_len` bytes. + let cert_len =3D unsafe { + bindings::x509_get_certificate_length( + &cert_chain_buf[offset..] as *const _ as *const u8, + cert_chain_len - offset, + ) + }; + + if cert_len < 0 { + pr_err!("Invalid certificate length\n"); + + if let Some(prev) =3D prev_cert { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + } + + to_result(cert_len as i32)?; + } + + // SAFETY: `cert_chain_buf[offset..]` is a non-empty slice of + // bytes valid for at least `cert_len` bytes. + let cert_ptr =3D unsafe { + match from_err_ptr(bindings::x509_cert_parse( + &cert_chain_buf[offset..] as *const _ as *const c_void, + cert_len as usize, + )) { + Err(e) =3D> { + if let Some(prev) =3D prev_cert { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + bindings::x509_free_certificate(prev); + } + return Err(e); + } + Ok(c) =3D> c, + } + }; + // SAFETY: Cast the `struct x509_certificate` to a Rust binding + let cert =3D unsafe { *cert_ptr }; + + if cert.unsupported_sig || cert.blacklisted { + pr_err!("Certificate was rejected\n"); + + if let Some(prev) =3D prev_cert { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + } + // SAFETY: `cert_ptr` was just returned by + // `x509_cert_parse()`. + unsafe { bindings::x509_free_certificate(cert_ptr) }; + + return Err(EKEYREJECTED); + } + + if let Some(prev) =3D prev_cert { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + let rc =3D unsafe { bindings::public_key_verify_signature(= (*prev).pub_, cert.sig) }; + + if rc < 0 { + pr_err!("Signature validation error\n"); + + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + + // SAFETY: `cert_ptr` was just returned by + // `x509_cert_parse()`. + unsafe { bindings::x509_free_certificate(cert_ptr) }; + + to_result(rc)?; + } + } + + if let Some(prev) =3D prev_cert { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + } + + prev_cert =3D Some(cert_ptr); + offset +=3D cert_len as usize; + } + + if let Some(prev) =3D prev_cert { + if let Some(validate) =3D self.validate { + // SAFETY: Call the `validate` function provided. + let rc =3D unsafe { validate(self.dev, slot, prev) }; + if let Err(e) =3D to_result(rc) { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + return Err(e); + } + } + + // The leaf key is the same for all slots, so just store the f= irst one. + if self.leaf_key.is_none() { + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + self.leaf_key =3D unsafe { Some((*prev).pub_) }; + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. We are setting + // the `pub` key to null so it isn't freed below + unsafe { (*prev).pub_ =3D core::ptr::null_mut() }; + } + + // SAFETY: `prev_cert` is the previously parsed + // certificate from a prior loop iteration. + unsafe { bindings::x509_free_certificate(prev) }; + } + + Ok(()) + } } diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index d2781c27794b..5cc71552b524 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com [209.85.215.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E343837D131 for ; Tue, 1 Sep 2026 01:06:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224817; cv=none; b=LyJTQH+OmlqXSq3PHzfqh3qdFo1gCYvAKdl5PxCkgeEwhDJq+6JEOjTfijKt9f/lFs4ziZS0uiTQLxXjWhVh5Kinuuh+q3Vya/VU+dyXAEs0QbEBB2EiHYPei5QJWUbMZN6hlz3N7iFzB50xjRSYpDBhQoTtbJ43JTL9oRyHpcI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224817; c=relaxed/simple; bh=7Yw8TRdsC8iJj4zKIDBj3vysMEmn/ITvAglewvj0fLo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GSTOBDkLpD4gZBBCe0ENKYJnp/3M+Cr0u2V18VYHaLFRvteCHYkiO5OdBKZlNWxpdySGg72+i+TBtZJRmFlOKaHCi9jF4xT43AUsXoyZMHW3jyhjRyAk5/jiCwT5p3OehJYBOvHw1xpUFPfo/SihysIW4BIwoWAmODaNHCbe530= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=B6xOKA11; arc=none smtp.client-ip=209.85.215.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="B6xOKA11" Received: by mail-pg1-f176.google.com with SMTP id 41be03b00d2f7-c9e7391839cso447349a12.0 for ; Mon, 31 Aug 2026 18:06:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224814; x=1788829614; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Uiz6c8lHl0mIc3Nv6bMi5cOROWKCJXWfj0lE81TYGfE=; b=B6xOKA11WJKxu65gr0+Eo+Iyjosqjzcw6ONOIKWZ+sS4pk+1h9yL3lGup3YQISDg/z irW410TFAsrUzP38hX+sez7x2uJ04GH/cOVK8h0yV7ZUqhB3fF/ztuUudpnhdNK1atW1 CTxBMvDvvuHhLHu8+OYW+GNCcY6Ij2BdE9ywcFcOrRVOC72W1xY5NTJ9S2+jC6lhO7kb k5TT9p+jcnub7Ief5RE6rXcOOA1suNtQs707UmAvqvysAL/lR2OsjTXh2R3OmvsDs81J 2x7JUKaDn3VQRiUh1wgXsUzZO1yo5Sn6hg/PglZuJV33v03aRji/whjoapnJB3mlWLYU gUHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224814; x=1788829614; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Uiz6c8lHl0mIc3Nv6bMi5cOROWKCJXWfj0lE81TYGfE=; b=HLFHtCkKPSTwctni02/xiTk5A6MvDsLXaUatSnVM5ud5Np2f+0H+WKWzTveQNbtk/Y oLbEMLpksleRX1mu2mzRxmnRwC4hdM+mcKZFnKB1yuaQ2CTaoCsoLezhU2YpAvJEgR/B mRUGa2EzTT/UuWAONmQeT6/1S9gEtgBWi2vqjjQ66SRCIRhfI760HrS6XjkV5i5ecQ5k huBusk4gSvqfu8BKU71FulPBV+OM9yfBt9elpInhgbt39qgxbHNij+iZVMK+YceQiA0w qi3xCPu+yKZBboyMVej0J/SBnnZaw8maRDXjbT3WmwztEn6M3XkDoEIap/vMwxRhlL3K xjTQ== X-Forwarded-Encrypted: i=1; AKwUvBwn7byTSj7yPwUqaPTM6YUPX1WsqGDflA2L85SjKgQr8dMlys+3lTgxIRwmLIZ0GF+eDJ1ivnQZizNewpA=@vger.kernel.org X-Gm-Message-State: AFuF++nLeUxjnMYRXWHXOQxLbT9NX4++u/x9jcJ9dcYBpOQHkzLbmQgA ROpVY0yf+Ody8yWmviY8ItaApgNPuVKJuf8rYKevX8AGZjjIzgHsvOa9 X-Gm-Gg: AYBFou2Uby4vbT26h9SdNjmphHQeIXu5Ksd/bSHGbLCD8rGBkRw1wB5lZU9Gd94YKop Qrv80hh+K6MtzHJ7Oold/sBcJWGV7+aELec5YAiL0jKQ0YlXOh2FFq/wlQMgl0wbAZ+BxxL2BPA /RsF02+Ui9xfsIK39Ykw/ZpJQJ1zRvh+kpRA9OQgMuaLJ5E/q7Cx9FmH3v6sn0/yfyDmfbf/7IJ CrD31s7u1FSqjk2Bj2Xw4Fw+jV/MR4yIVZ+Uprrxt8Pi1UZlcePRhXNT04GKO6NMwAd/wUXKHl+ ND6sOjuCZqdql7h6vdbvVUaJuMRYTBD5+d1tKtrID4LU14mEH5h2KRlNHvzLJC/Cb6wjgy0e3rg SxZlE4OIW7SY9IUGHADF065lh0SO9HiMqmY7+9XH0dupB8uMoKtQAx78JnMdcZ+NUvY3nWEzWcr OtggdsiDX+V0zxFGgKwQpm/gd5n/EO44xUjjMebpQ9X81pvyXMo5dfJbP9HHfVb94ENB4K0M1MV GOahA== X-Received: by 2002:a17:90b:1f86:b0:396:4cbf:45a2 with SMTP id 98e67ed59e1d1-396d0ff5bb9mr49611662a91.14.1788224814428; Mon, 31 Aug 2026 18:06:54 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:06:53 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 20/21] rust: allow extracting the buffer from a CString Date: Tue, 1 Sep 2026 11:03:46 +1000 Message-ID: <20260901010347.2614656-21-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis The kernel CString is a wrapper around a KVec. This patch allows retrieving the underlying buffer and consuming the CString. This allows users to create a CString from a string and then retrieve the underlying buffer. Signed-off-by: Alistair Francis Reviewed-by: Gary Guo --- rust/kernel/str.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index a556788bcc5e..3fd8a218547f 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -870,6 +870,12 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Resul= t { // exist in the buffer. Ok(Self { buf }) } + + /// Return the internal buffer while consuming the original [`CString`] + #[inline] + pub fn into_vec(self) -> KVec { + self.buf + } } =20 impl Deref for CString { --=20 2.55.0 From nobody Sat Sep 26 13:48:09 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 98A73399892 for ; Tue, 1 Sep 2026 01:07:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224826; cv=none; b=kddkF2MqheIQ7yJgWpxxCPH+UA3hD6dIJ6YISjbnTtDuPc6FhXrYoY3zLI4KfdjJYZfSdkHVjhdnp8r5vYEq0fDsHsnpHTy87VNGKaPAlyZHL1uIvHHMvgjBHQfZhhw9DDrzzojvwCU/FajgUAe5PwLXfeKrpWFfzDGb0LpqPXE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224826; c=relaxed/simple; bh=4PPZUjy5GMkvTRr2oGK0IblPeYeT8JMRqXmbwdI9pqQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h+BbKog2CIoAqIbS+3Eo0Y4GgYMEJvf58oMwzYb7S5Bek/afp66Wjs2hqUMYIbrDw8VEJx0qDty+RsnSDxrxme2j7Iupjm1qyKZmxh3BjCMAcW7gR7KnUYOn+hR6OLd/m9WDnzIVsj8GHJrHJLkL+tOOLLtplDPipK68Idhp5/c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=OP+1scjO; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="OP+1scjO" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2cc891373e0so37560545ad.2 for ; Mon, 31 Aug 2026 18:07:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224823; x=1788829623; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5JB8c4S0Th+kTTgtPCyCVSVH7o5Gb2vhrXhBM7pAc4g=; b=OP+1scjOGF7sqxw1AcySr1fGcT5kJXVj6kUcHpa5RN5oxxqehgX8Sj/NH12G9CH8Ac jdoct4AhKFO4qJGmY4b3XoIXCm2+TL7Jy9BQ3cPIeN+mgXa1jvHFycJ+7MZMh4v6AQ+H s13VGpdC9Cb7DwuGNmLGQ0qglei9D5JA/mic+dIvy5uSOZhCYrTkeVEecpEcYd0tBxOw 7LrcGCgboNhrH4E+3BaHobopg/LI1eb1oQwTO/J+HQ++IBuPqbwAWT8eF75LjYsgoLI4 /3Y5c8ZoPztfsJ0rmg4wsad7Zudp27802HM+4loev2HX4GkTYgkKxREjBusR7KMTHRcJ 2khA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224823; x=1788829623; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=5JB8c4S0Th+kTTgtPCyCVSVH7o5Gb2vhrXhBM7pAc4g=; b=J7ZDkkfZ8mVH1KCUSeSV1wmQATzgJ+Euh2ei+MIZaVdbEIP5TbdZvqSMzNR0Ji5hgm zWWLhn7TZx7Ewnj352Ee74B5DNn1zGxoNV68eL/Xw1h+lYeirUiM2Xgc50YstQt7ASn5 pDPFtS66ptKq7J8E/trAsG4gLIXuaHRa2ieCUTXJa6ZuCl7H8CGU7+bzqtL6L+CGQqNx SY6nh0492W/bvuRtpKKSw90pky7DYBTyJOVg4YmDc/g0U4Iluf96jhU1CF8IEdHG5tLr WTuJOL+7Ni7kHrGY2teUw0K8ZABO0MAZRw6B39yoQxsrVzy31H8dLUn0Y0AI/eATMBSv L5PA== X-Forwarded-Encrypted: i=1; AKwUvBzH3BWK0Rb8zgB59v2GtLfGbILx19eQqoFkr6WuJAQdIKs6KO5wT/EZNJqwXM0IPL7PSx/FjxaTrEl5oEU=@vger.kernel.org X-Gm-Message-State: AFuF++m+Af/xqWBDAFhDU4zK7GJLuZSd3PRSDu9sHuWaDJIz4KH62bVB L+8HMpEivm/eQmyOws6RlgBabCn9SW4SAq/zAU4LJ3hvcVXhXU2TXGB9 X-Gm-Gg: AYBFou1cTbnQqd3/P31JrC+enCwkEPsnKyo8yC5hCIegXa/adMZDOBgn65iJwQLQgPn gfMK9Nsrxp/pFdzFITH6EKVvf4Gg4eLyU3B2xMxBLAqfpsrpqkEHAPBWBgXGV4lUUnWtMH3Vdq1 ZrAQwwnPrcpTL9AOFTkYmBa0KFkmujbX/9VyGEmvgnctMeAKvoYGcHishkUuLU+ACTe3LIMZfmx 4Q1pcOpb+gTC2Z+H56/YbTgqyERy1Ywdqo0yW2NW9xUuwBZVWSpC3SDJL20UYbJcw7k3SOcPsQO QIZtBbCZAG9JuHAEPA2Szi6jl3Z2/AkNwtjbnwFCHbk7Xv9SS7mbiJw6YXooFMWH7zHA+HvtzOS RjcOsFNd7Lm5d1UafY+dIsI70NXas3kPTnN9gthql5gizPzLzHTEj/HViOxCn01VmDJhalHC4x8 3F7PYxWqrAVkB4/6kdXWkIJtBqtAtCQXffcecj+TdaO7GbU9+Y5rRTl8tTVghcQB3BylD9vyLQ6 sx4fQ== X-Received: by 2002:a17:90b:57cf:b0:398:d132:ba76 with SMTP id 98e67ed59e1d1-398d132c2e0mr19298028a91.21.1788224822695; Mon, 31 Aug 2026 18:07:02 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.06.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:07:02 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.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 21/21] lib: rspdm: Support SPDM challenge Date: Tue, 1 Sep 2026 11:03:47 +1000 Message-ID: <20260901010347.2614656-22-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the CHALLENGE SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 6 + lib/rspdm/lib.rs | 10 +- lib/rspdm/state.rs | 239 +++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 61 ++++++++ rust/bindings/bindings_helper.h | 1 + 5 files changed, 313 insertions(+), 4 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 2fbc4ab41869..e67be8e6b057 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -148,6 +148,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { =20 pub(crate) const SPDM_GET_CERTIFICATE: u8 =3D 0x82; =20 +pub(crate) const SPDM_CHALLENGE: u8 =3D 0x83; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] @@ -176,3 +178,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { pub(crate) const SPDM_HASH_ALGOS: u32 =3D SPDM_HASH_SHA2_256 | SPDM_HASH_S= HA2_384_512; =20 pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 =3D bit_u8(1); + +pub(crate) const SPDM_PREFIX_SZ: usize =3D 64; +pub(crate) const SPDM_COMBINED_PREFIX_SZ: usize =3D 100; +pub(crate) const SPDM_MAX_OPAQUE_DATA: usize =3D 1024; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index fa5513e8bd4e..33c16f7ffb46 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -11,8 +11,7 @@ //! from other subsytems. =20 use crate::bindings::{ - spdm_state, - EPROTONOSUPPORT, // + spdm_state, // }; use core::ffi::{ c_int, @@ -141,7 +140,12 @@ pub extern "C" fn spdm_authenticate(state_ptr: *mut sp= dm_state) -> c_int { provisioned_slots &=3D !(1 << slot); } =20 - -(EPROTONOSUPPORT as i32) + let provisioned_slots =3D state.provisioned_slots.trailing_zeros(); + if let Err(e) =3D state.challenge(provisioned_slots as u8) { + return e.to_errno() as c_int; + } + + 0 } =20 /// spdm_destroy() - Destroy SPDM session diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index fc7df9dd7b97..1fd691037fa5 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -8,6 +8,7 @@ //! =20 use core::ffi::c_void; +use core::mem::offset_of; use core::slice::from_raw_parts_mut; use kernel::prelude::*; use kernel::{ @@ -19,6 +20,7 @@ Error, // }, str::CStr, + str::CString, validate::Untrusted, }; =20 @@ -31,6 +33,7 @@ SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA_3072, SPDM_ASYM_RSASSA_4096, + SPDM_COMBINED_PREFIX_SZ, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALGOS, @@ -38,10 +41,12 @@ SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, + SPDM_MAX_OPAQUE_DATA, SPDM_MAX_VER, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERAL, + SPDM_PREFIX_SZ, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, @@ -51,6 +56,8 @@ SPDM_VER_13, // }; use crate::validator::{ + ChallengeReq, + ChallengeRsp, GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, @@ -65,6 +72,8 @@ SpdmHeader, // }; =20 +const SPDM_CONTEXT: &str =3D "responder-challenge_auth signing"; + /// The current SPDM session state for a device. Based on the /// C `struct spdm_state`. /// @@ -111,6 +120,12 @@ /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. /// @leaf_key: Public key portion of leaf certificate against which to che= ck /// responder's signatures. +/// @transcript: Concatenation of all SPDM messages exchanged during an +/// authentication or measurement sequence. Used to verify the signature, +/// as it is computed over the hashed transcript. +/// @next_nonce: Requester nonce to be used for the next authentication +/// sequence. Populated from user space through sysfs. +/// If user space does not provide a nonce, the kernel uses a random one. pub(crate) struct SpdmState<'a> { pub(crate) dev: *mut bindings::device, pub(crate) transport: bindings::spdm_transport, @@ -140,6 +155,10 @@ pub(crate) struct SpdmState<'a> { // Certificates pub(crate) certs: [KVec; SPDM_SLOTS], pub(crate) leaf_key: Option<*mut bindings::public_key>, + + transcript: VVec, + + pub(crate) next_nonce: KVec, } =20 impl Drop for SpdmState<'_> { @@ -212,6 +231,8 @@ pub(crate) fn new( hash_len: 0, certs: [const { KVec::new() }; SPDM_SLOTS], leaf_key: None, + transcript: VVec::new(), + next_nonce: KVec::new(), } } =20 @@ -327,13 +348,15 @@ fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), = Error> { /// The data in `request_buf` is sent to the device and the response is /// stored in `response_buf`. pub(crate) fn spdm_exchange( - &self, + &mut self, request_buf: &mut [u8], response_buf: &mut [u8], ) -> Result { let header_size =3D core::mem::size_of::(); let request: &SpdmHeader =3D Untrusted::new(&request_buf[..]).vali= date()?; =20 + self.transcript.extend_from_slice(request_buf, GFP_KERNEL)?; + let transport_function =3D self.transport.ok_or(EINVAL)?; // SAFETY: `transport_function` is provided by the new(), we are // calling the function. @@ -383,6 +406,8 @@ pub(crate) fn get_version(&mut self) -> Result<(), Erro= r> { request.version =3D SPDM_MIN_VER; self.version =3D SPDM_MIN_VER; =20 + self.transcript.clear(); + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice let request_buf =3D unsafe { from_raw_parts_mut( @@ -402,6 +427,16 @@ pub(crate) fn get_version(&mut self) -> Result<(), Err= or> { response_vec.truncate(rc); =20 let response: &GetVersionRsp =3D Untrusted::new(response_vec.as_sl= ice()).validate()?; + let rsp_sz =3D core::mem::size_of::() + + 2 + + response.version_number_entry_count as usize * 2; + + if rsp_sz > response_vec.len() { + return Err(EIO); + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 let mut foundver =3D false; let entry_count =3D response.version_number_entry_count; @@ -471,6 +506,13 @@ pub(crate) fn get_capabilities(&mut self) -> Result<()= , Error> { } response_vec.truncate(rc); =20 + if rsp_sz > response_vec.len() { + return Err(EIO); + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; + let response: &mut GetCapabilitiesRsp =3D Untrusted::new(&mut resp= onse_vec).validate()?; =20 self.rsp_caps =3D u32::from_le(response.flags); @@ -629,6 +671,9 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), E= rror> { =20 let response: &NegotiateAlgsRsp =3D Untrusted::new(response_vec.as= _slice()).validate()?; =20 + self.transcript + .extend_from_slice(&response_vec, GFP_KERNEL)?; + self.base_asym_alg =3D u32::from_le(response.base_asym_sel); self.base_hash_alg =3D u32::from_le(response.base_hash_sel); self.meas_hash_alg =3D u32::from_le(response.measurement_hash_algo= ); @@ -678,6 +723,14 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Err= or> { response_vec.truncate(len); =20 let response: &GetDigestsRsp =3D Untrusted::new(response_vec.as_sl= ice()).validate()?; + let rsp_sz =3D core::mem::size_of::() + response.param= 2 as usize * self.hash_len; + + if rsp_sz > response_vec.len() { + return Err(EIO); + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 if len < core::mem::size_of::() @@ -735,6 +788,14 @@ fn get_cert_exchange<'a>( response_vec.truncate(len); =20 let response: &GetCertificateRsp =3D Untrusted::new(response_vec.a= s_slice()).validate()?; + let rsp_sz =3D core::mem::size_of::() + 4 + response.p= ortion_length as usize; + + if rsp_sz > response_vec.len() { + return Err(EIO); + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 if len < core::mem::size_of::() @@ -990,4 +1051,180 @@ pub(crate) fn validate_cert_chain(&mut self, slot: u= 8) -> Result<(), Error> { =20 Ok(()) } + + pub(crate) fn challenge_rsp_len(&mut self, nonce_len: usize, opaque_le= n: usize) -> usize { + // No measurement summary hash requested (MSHLength =3D=3D 0) + let mut length =3D + core::mem::size_of::() + self.hash_len + nonce_len= + opaque_len + 2; + + if self.version >=3D SPDM_VER_13 { + length +=3D 8; + } + + length + self.sig_len + } + + fn verify_signature(&mut self, signature: &mut [u8]) -> Result<(), Err= or> { + let mut sig =3D bindings::public_key_signature::default(); + let mut mhash: KVec =3D KVec::new(); + + sig.s =3D signature as *mut _ as *mut u8; + sig.s_size =3D self.sig_len as u32; + sig.encoding =3D self.base_asym_enc.as_ptr() as *const u8; + sig.hash_algo =3D self.base_hash_alg_name.as_ptr() as *const u8; + + let mut m: KVec =3D KVec::new(); + m.extend_with(SPDM_COMBINED_PREFIX_SZ + self.hash_len, 0, GFP_KERN= EL)?; + + if let Some(desc) =3D &mut self.desc { + desc.tfm =3D self.shash; + + unsafe { + to_result(bindings::crypto_shash_digest( + *desc, + self.transcript.as_ptr(), + (self.transcript.len() - self.sig_len) as u32, + m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(), + ))?; + }; + } else { + return Err(EPROTO); + } + + if self.version <=3D SPDM_VER_11 { + sig.m =3D m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(); + } else { + let major =3D self.version >> 4; + let minor =3D self.version & 0xF; + + let prefix =3D CString::try_from_fmt(fmt!("dmtf-spdm-v{major:x= }.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}= .*dmtf-spdm-v{major:x}.{minor:x}.*"))?; + let mut buf =3D prefix.into_vec(); + let zero_pad_len =3D SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ = - SPDM_CONTEXT.len() - 1; + + buf.extend_with(zero_pad_len, 0, GFP_KERNEL)?; + buf.extend_from_slice(SPDM_CONTEXT.as_bytes(), GFP_KERNEL)?; + + if buf.len() !=3D SPDM_COMBINED_PREFIX_SZ { + pr_err!("combined_spdm_prefix calculation is incorrect"); + return Err(EPROTO); + } + + m[..SPDM_COMBINED_PREFIX_SZ].copy_from_slice(&buf); + + mhash.extend_with(self.hash_len, 0, GFP_KERNEL)?; + + if let Some(desc) =3D &mut self.desc { + desc.tfm =3D self.shash; + + unsafe { + to_result(bindings::crypto_shash_digest( + *desc, + m.as_ptr(), + m.len() as u32, + mhash.as_mut_ptr(), + ))?; + }; + } else { + return Err(EPROTO); + } + + sig.m =3D mhash.as_mut_ptr(); + } + + sig.m_size =3D self.hash_len as u32; + + if let Some(leaf_key) =3D self.leaf_key { + unsafe { to_result(bindings::public_key_verify_signature(leaf_= key, &sig)) } + } else { + return Err(EPROTO); + } + } + + pub(crate) fn challenge(&mut self, slot: u8) -> Result<(), Error> { + let mut request =3D ChallengeReq::default(); + request.version =3D self.version; + request.param1 =3D slot; + + let nonce_len =3D request.nonce.len(); + + if self.next_nonce.len() > 0 { + let request_nonce_len =3D request.nonce.len(); + + if self.next_nonce.len() =3D=3D request_nonce_len { + request + .nonce + .copy_from_slice(&self.next_nonce[..request_nonce_len]= ); + } else { + return Err(EINVAL); + } + + self.next_nonce.clear(); + } else { + unsafe { + bindings::get_random_bytes(&mut request.nonce as *mut _ as= *mut c_void, nonce_len) + }; + } + + let req_sz =3D if self.version <=3D SPDM_VER_12 { + offset_of!(ChallengeReq, context) + } else { + core::mem::size_of::() + }; + + let rsp_sz =3D self.challenge_rsp_len(nonce_len, SPDM_MAX_OPAQUE_D= ATA); + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::from_elem(0u8, rsp_sz, GF= P_KERNEL)?; + + let rc =3D self.spdm_exchange(request_buf, response_vec.as_mut_sli= ce())? as usize; + + // The transport must report a length within the buffer we provide= d. + if rc < core::mem::size_of::() { + pr_err!("Truncated challenge response\n"); + return Err(EIO); + } + response_vec.truncate(rc); + + let _response: &ChallengeRsp =3D Untrusted::new(response_vec.as_sl= ice()).validate()?; + + // MSHLength is 0 as no measurement summary hash requested + let opaque_len_offset =3D core::mem::size_of::() + sel= f.hash_len + nonce_len; + + if opaque_len_offset + 2 > response_vec.len() { + return Err(EIO); + } + + let opaque_len =3D u16::from_le_bytes( + response_vec[opaque_len_offset..(opaque_len_offset + 2)] + .try_into() + .unwrap_or([0, 0]), + ); + + let rsp_sz =3D self.challenge_rsp_len(nonce_len, opaque_len as usi= ze); + + if rsp_sz > response_vec.len() { + pr_err!("Truncated challenge response\n"); + return Err(EIO); + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; + + /* Verify signature at end of transcript against leaf key */ + let sig_start =3D rsp_sz - self.sig_len; + let signature =3D &mut response_vec[sig_start..rsp_sz]; + + match self.verify_signature(signature) { + Ok(()) =3D> { + pr_info!("Authenticated with certificate slot {slot}\n"); + Ok(()) + } + Err(e) =3D> { + pr_err!("Cannot verify challenge_auth signature: {e:?}\n"); + Err(EPROTO) + } + } + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 2584bed73979..f623ac1d2860 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -28,6 +28,7 @@ =20 use crate::consts::{ SPDM_ASYM_ALGOS, + SPDM_CHALLENGE, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERTIFICATE, @@ -491,3 +492,63 @@ fn validate(unvalidated: &[u8]) -> Result { Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct ChallengeReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) nonce: [u8; 32], + pub(crate) context: [u8; 8], +} + +impl Default for ChallengeReq { + fn default() -> Self { + ChallengeReq { + version: 0, + code: SPDM_CHALLENGE, + param1: 0, + param2: 0, + nonce: [0; 32], + context: [0; 8], + } + } +} + +#[repr(C, packed)] +pub(crate) struct ChallengeRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) cert_chain_hash: __IncompleteArrayField, + pub(crate) nonce: [u8; 32], + pub(crate) message_summary_hash: __IncompleteArrayField, + + pub(crate) opaque_data_len: u16, + pub(crate) opaque_data: __IncompleteArrayField, + + pub(crate) context: [u8; 8], + pub(crate) signature: __IncompleteArrayField, +} + +impl<'a> Validate> for &'a ChallengeRsp { + type Err =3D Error; + + fn validate(unvalidated: &[u8]) -> Result { + if unvalidated.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D unvalidated.as_ptr(); + // CAST: `ChallengeRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &ChallengeRsp =3D unsafe { &*ptr }; + + Ok(rsp) + } +} diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 5cc71552b524..f3d3f6e9f60a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include --=20 2.55.0