All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport
@ 2024-05-07 18:01 David E. Box
  2024-05-07 18:01 ` [PATCH V2 2/3] tools/arch/x86/intel_sdsi: Rework Makefile David E. Box
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David E. Box @ 2024-05-07 18:01 UTC (permalink / raw
  To: linux-doc, david.e.box, ilpo.jarvinen, hdegoede, linux-kernel,
	platform-driver-x86

Intel On Demand adds attestation and firmware measurement retrieval
services through use of the protocols defined the Security Protocols and
Data Measurement (SPDM) specification. SPDM messages exchanges are used to
authenticate On Demand hardware and to retrieve signed measurements of the
NVRAM state used to track feature provisioning and the NVRAM state used for
metering services. These allow software to verify the authenticity of the
On Demand hardware as well as the integrity of the reported silicon
configuration.

Add an ioctl interface for sending SPDM messages through the On Demand
mailbox. Provides commands to get a list of SPDM enabled devices, get the
message size limits for SPDM Requesters and Responders, and perform an SPDM
message exchange.

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.0.1.pdf [1]
---
V2
   - Move size < 4 check into sdsi_spdm_exchange() and add comment
     clarifying return values of that function.
   - Use SZ_4K and add helpers
   - Use devm_kasprintf()
   - Remove unnecessary parens
   - Use --attest for long option

 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 MAINTAINERS                                   |   1 +
 drivers/platform/x86/intel/sdsi.c             | 210 +++++++++++++++++-
 include/uapi/linux/intel_sdsi.h               |  81 +++++++
 4 files changed, 292 insertions(+), 1 deletion(-)
 create mode 100644 include/uapi/linux/intel_sdsi.h

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index c472423412bf..20dcc2dbcaf6 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -382,6 +382,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <mailto:mathieu.desnoyers@efficios.com>
 0xF8  all    arch/x86/include/uapi/asm/amd_hsmp.h                    AMD HSMP EPYC system management interface driver
                                                                      <mailto:nchatrad@amd.com>
+0xFC  all    linux/intel_sdsi.h
 0xFD  all    linux/dm-ioctl.h
 0xFE  all    linux/isst_if.h
 ====  =====  ======================================================= ================================================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 846187625552..060bd3358cec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11165,6 +11165,7 @@ INTEL SDSI DRIVER
 M:	David E. Box <david.e.box@linux.intel.com>
 S:	Supported
 F:	drivers/platform/x86/intel/sdsi.c
+F:	include/uapi/linux/intel_sdsi.h
 F:	tools/arch/x86/intel_sdsi/
 F:	tools/testing/selftests/drivers/sdsi/
 
diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
index 277e4f4b20ac..686dd9e4e026 100644
--- a/drivers/platform/x86/intel/sdsi.c
+++ b/drivers/platform/x86/intel/sdsi.c
@@ -11,9 +11,12 @@
 #include <linux/auxiliary_bus.h>
 #include <linux/bits.h>
 #include <linux/bitfield.h>
+#include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/iopoll.h>
+#include <linux/intel_sdsi.h>
 #include <linux/kernel.h>
+#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/overflow.h>
 #include <linux/pci.h>
@@ -42,6 +45,7 @@
 
 #define SDSI_ENABLED_FEATURES_OFFSET	16
 #define SDSI_FEATURE_SDSI		BIT(3)
+#define SDSI_FEATURE_ATTESTATION	BIT(12)
 #define SDSI_FEATURE_METERING		BIT(26)
 
 #define SDSI_SOCKET_ID_OFFSET		64
@@ -91,6 +95,7 @@ enum sdsi_command {
 	SDSI_CMD_PROVISION_CAP		= 0x0008,
 	SDSI_CMD_READ_STATE		= 0x0010,
 	SDSI_CMD_READ_METER		= 0x0014,
+	SDSI_CMD_ATTESTATION		= 0x1012,
 };
 
 struct sdsi_mbox_info {
@@ -109,12 +114,14 @@ struct disc_table {
 struct sdsi_priv {
 	struct mutex		mb_lock;	/* Mailbox access lock */
 	struct device		*dev;
+	struct miscdevice	miscdev;
 	void __iomem		*control_addr;
 	void __iomem		*mbox_addr;
 	void __iomem		*regs_addr;
 	int			control_size;
 	int			maibox_size;
 	int			registers_size;
+	int			id;
 	u32			guid;
 	u32			features;
 };
@@ -582,6 +589,97 @@ static const struct attribute_group sdsi_group = {
 };
 __ATTRIBUTE_GROUPS(sdsi);
 
+/*
+ * SPDM transport
+ * Returns size of the response message or an error code on failure.
+ */
+static int sdsi_spdm_exchange(void *private, const void *request,
+			      size_t request_sz, void *response,
+			      size_t response_sz)
+{
+	struct sdsi_priv *priv = private;
+	struct sdsi_mbox_info info = {};
+	size_t spdm_msg_size, size;
+	int ret;
+
+	/*
+	 * For the attestation command, the mailbox write size is the sum of:
+	 *     Size of the SPDM request payload, padded for qword alignment
+	 *     8 bytes for the mailbox command
+	 *     8 bytes for the actual (non-padded) size of the SPDM request
+	 */
+	if (request_sz > SDSI_SIZE_WRITE_MSG - 2 * sizeof(u64))
+		return -EOVERFLOW;
+
+	info.size = round_up(request_sz, sizeof(u64)) + 2 * sizeof(u64);
+
+	u64 *payload __free(kfree) = kzalloc(info.size, GFP_KERNEL);
+	if (!payload)
+		return -ENOMEM;
+
+	memcpy(payload, request, request_sz);
+
+	/* The non-padded SPDM payload size is the 2nd-to-last qword */
+	payload[(info.size / sizeof(u64)) - 2] = request_sz;
+
+	/* Attestation mailbox command is the last qword of payload buffer */
+	payload[(info.size / sizeof(u64)) - 1] = SDSI_CMD_ATTESTATION;
+
+	info.payload = payload;
+	info.buffer = response;
+
+	ret = mutex_lock_interruptible(&priv->mb_lock);
+	if (ret)
+		return ret;
+	ret = sdsi_mbox_write(priv, &info, &size);
+	mutex_unlock(&priv->mb_lock);
+
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The read size is the sum of:
+	 *     Size of the SPDM response payload, padded for qword alignment
+	 *     8 bytes for the actual (non-padded) size of the SPDM payload
+	 */
+
+	if (size < sizeof(u64)) {
+		dev_err(priv->dev,
+			"Attestation error: Mailbox reply size, %ld, too small\n",
+			size);
+		return -EPROTO;
+	}
+
+	if (!IS_ALIGNED(size, sizeof(u64))) {
+		dev_err(priv->dev,
+			"Attestation error: Mailbox reply size, %ld, is not aligned\n",
+			size);
+		return -EPROTO;
+	}
+
+	/*
+	 * Get the SPDM response size from the last QWORD and check it fits
+	 * with no more than 7 bytes of padding
+	 */
+	spdm_msg_size = ((u64 *)info.buffer)[(size - sizeof(u64)) / sizeof(u64)];
+	if (!in_range(size - spdm_msg_size - sizeof(u64), 0, 8)) {
+		dev_err(priv->dev,
+			"Attestation error: Invalid SPDM response size, %ld\n",
+			spdm_msg_size);
+		return -EPROTO;
+	}
+
+	if (spdm_msg_size > response_sz || spdm_msg_size < SPDM_HEADER_SIZE) {
+		dev_err(priv->dev, "Attestation error: Expected response size %ld, got %ld\n",
+			response_sz, spdm_msg_size);
+		return -EOVERFLOW;
+	}
+
+	memcpy(response, info.buffer, spdm_msg_size);
+
+	return spdm_msg_size;
+}
+
 static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table)
 {
 	switch (table->guid) {
@@ -649,6 +747,92 @@ static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *paren
 	return 0;
 }
 
+#define SDSI_SPDM_DRIVER_VERSION	1
+
+static int sdsi_spdm_get_info(struct sdsi_priv *priv,
+			      struct sdsi_spdm_info __user *argp)
+{
+	struct sdsi_spdm_info info;
+
+	info.driver_version = SDSI_SPDM_DRIVER_VERSION;
+	info.api_version = priv->guid;
+	info.dev_no = priv->id;
+	info.max_request_size = SDSI_SIZE_WRITE_MSG - 2 * sizeof(u64);
+	info.max_response_size = SDSI_SIZE_READ_MSG - sizeof(u64);
+
+	if (copy_to_user(argp, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int sdsi_spdm_do_command(struct sdsi_priv *priv,
+				struct sdsi_spdm_command __user *argp)
+{
+	u32 req_size, rsp_size;
+
+	if (get_user(req_size, &argp->size))
+		return -EFAULT;
+
+	if (req_size < 4 || req_size > sizeof(struct sdsi_spdm_message))
+		return -EINVAL;
+
+	struct sdsi_spdm_message *request __free(kfree) =
+		kmalloc(req_size, GFP_KERNEL);
+	if (!request)
+		return -ENOMEM;
+
+	struct sdsi_spdm_command *response __free(kfree) =
+		kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL);
+	if (!response)
+		return -ENOMEM;
+
+	if (copy_from_user(request, &argp->message, req_size))
+		return -EFAULT;
+
+	rsp_size = sdsi_spdm_exchange(priv, request, req_size, response,
+				      SDSI_SIZE_READ_MSG);
+	if (rsp_size < 0)
+		return rsp_size;
+
+	if (put_user(rsp_size, &argp->size))
+		return -EFAULT;
+
+	if (copy_to_user(&argp->message, response, rsp_size))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long sdsi_spdm_ioctl(struct file *file, unsigned int cmd,
+			    unsigned long arg)
+{
+	struct sdsi_priv *priv;
+	long ret = -ENOTTY;
+
+	priv = container_of(file->private_data, struct sdsi_priv, miscdev);
+
+	switch (cmd) {
+	case SDSI_IF_SPDM_INFO:
+		ret = sdsi_spdm_get_info(priv,
+				(struct sdsi_spdm_info __user *)arg);
+		break;
+	case SDSI_IF_SPDM_COMMAND:
+		ret = sdsi_spdm_do_command(priv,
+				(struct sdsi_spdm_command __user *)arg);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct file_operations sdsi_spdm_ops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = sdsi_spdm_ioctl,
+};
+
 static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
 {
 	struct intel_vsec_device *intel_cap_dev = auxdev_to_ivdev(auxdev);
@@ -663,6 +847,7 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de
 		return -ENOMEM;
 
 	priv->dev = &auxdev->dev;
+	priv->id = auxdev->id;
 	mutex_init(&priv->mb_lock);
 	auxiliary_set_drvdata(auxdev, priv);
 
@@ -686,9 +871,32 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de
 	if (ret)
 		return ret;
 
+	/* Attestation miscdevice */
+	if (priv->features & SDSI_FEATURE_ATTESTATION) {
+		priv->miscdev.name = devm_kasprintf(&auxdev->dev, GFP_KERNEL,
+						    "isdsi%d", priv->id);
+		if (!priv->miscdev.name)
+			return -ENOMEM;
+
+		priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+		priv->miscdev.fops = &sdsi_spdm_ops;
+
+		ret = misc_register(&priv->miscdev);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
+static void sdsi_remove(struct auxiliary_device *auxdev)
+{
+	struct sdsi_priv *priv = auxiliary_get_drvdata(auxdev);
+
+	if (priv->features & SDSI_FEATURE_ATTESTATION)
+		misc_deregister(&priv->miscdev);
+}
+
 static const struct auxiliary_device_id sdsi_aux_id_table[] = {
 	{ .name = "intel_vsec.sdsi" },
 	{}
@@ -701,7 +909,7 @@ static struct auxiliary_driver sdsi_aux_driver = {
 	},
 	.id_table	= sdsi_aux_id_table,
 	.probe		= sdsi_probe,
-	/* No remove. All resources are handled under devm */
+	.remove		= sdsi_remove,
 };
 module_auxiliary_driver(sdsi_aux_driver);
 
diff --git a/include/uapi/linux/intel_sdsi.h b/include/uapi/linux/intel_sdsi.h
new file mode 100644
index 000000000000..8e28764f4a98
--- /dev/null
+++ b/include/uapi/linux/intel_sdsi.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Intel On Demand (SDSi) Interface for SPDM based attestation.
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: David E. Box <david.e.box@linux.intel.com>
+ */
+
+#ifndef __SDSI_H
+#define __SDSI_H
+
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+/**
+ * struct sdsi_spdm_info - Define platform information
+ * @api_version:	Version of the firmware document, which this driver
+ *			can communicate
+ * @driver_version:	Driver version, which will help user to send right
+ *			commands. Even if the firmware is capable, driver may
+ *			not be ready
+ * @dev_no:		Returns the auxiliary device number the corresponding
+ *			sdsi instance
+ * @max_request_size:	Returns the maximum allowed size for SPDM request
+ *			messages
+ * @max_response_size:	Returns the maximum size of an SPDM response message
+ *
+ * Used to return output of IOCTL SDSI_SPDM_INFO. This
+ * information can be used by the user space, to get the driver, firmware
+ * support and also number of commands to send in a single IOCTL request.
+ */
+struct sdsi_spdm_info {
+	__u32 api_version;
+	__u16 driver_version;
+	__u16 dev_no;
+	__u16 max_request_size;
+	__u16 max_response_size;
+};
+
+#define SPDM_HEADER				\
+	struct {				\
+		__u8 spdm_version;		\
+		__u8 request_response_code;	\
+		__u8 param1;			\
+		__u8 param2;			\
+	}
+#define SPDM_HEADER_SIZE	sizeof(SPDM_HEADER)
+
+/**
+ * struct sdsi_spdm_message - The SPDM message sent and received from the device
+ * @spdm_version:		Supported SPDM version
+ * @request_response_code:	The SPDM message code for requests and responses
+ * @param1:			Parameter 1
+ * @param2:			Parameter 2
+ * @buffer:			SDPM message specific buffer
+ *
+ */
+struct sdsi_spdm_message {
+	SPDM_HEADER;
+	__u8 buffer[SZ_4K - SPDM_HEADER_SIZE];
+};
+
+#define SDSI_SPDM_BUF_SIZE	(sizeof(struct sdsi_spdm_message) - SPDM_HEADER_SIZE)
+
+/**
+ * struct sdsi_spdm_command - The SPDM command
+ * @ size:		The size of the SPDM message
+ * @ message:		The SPDM message
+ *
+ * Used to return output of IOCTL SDSI_SPDM_COMMAND.
+ */
+struct sdsi_spdm_command {
+	__u32 size;
+	struct sdsi_spdm_message message;
+};
+
+#define SDSI_IF_MAGIC		0xFC
+#define SDSI_IF_SPDM_INFO	_IOR(SDSI_IF_MAGIC, 0, struct sdsi_spdm_info *)
+#define SDSI_IF_SPDM_COMMAND	_IOWR(SDSI_IF_MAGIC, 1, struct sdsi_spdm_command *)
+#endif

base-commit: 76f09e22027fc0dbec1e9c82898d9059b4455df6
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH V2 2/3] tools/arch/x86/intel_sdsi: Rework Makefile
  2024-05-07 18:01 [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport David E. Box
@ 2024-05-07 18:01 ` David E. Box
  2024-05-07 18:01 ` [PATCH V2 3/3] tools/arch/x86/intel_sdsi: Add attestation support David E. Box
  2024-05-08  8:23 ` [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport Ilpo Järvinen
  2 siblings, 0 replies; 4+ messages in thread
From: David E. Box @ 2024-05-07 18:01 UTC (permalink / raw
  To: linux-doc, david.e.box, ilpo.jarvinen, hdegoede, linux-kernel,
	platform-driver-x86

In preparation for more source files, rework the Makefile to handle
prerequisites more generically, making it easier to isolate future changes.
Also update PHONY targets.

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---

V2 - New Patch.

 tools/arch/x86/intel_sdsi/Makefile | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/tools/arch/x86/intel_sdsi/Makefile b/tools/arch/x86/intel_sdsi/Makefile
index 5de2288cda79..47b6fd98372c 100644
--- a/tools/arch/x86/intel_sdsi/Makefile
+++ b/tools/arch/x86/intel_sdsi/Makefile
@@ -1,21 +1,25 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for Intel Software Defined Silicon provisioning tool
-
-intel_sdsi: intel_sdsi.c
-
-CFLAGS = -Wextra
-
+include ../../../scripts/Makefile.include
 BINDIR ?= /usr/sbin
 
-override CFLAGS += -O2 -Wall
+SRCS = intel_sdsi.c
 
-%: %.c
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+OBJS = $(SRCS:.c=.o)
+
+override CFLAGS += -O2 -Wall -Wextra
+
+intel_sdsi: $(OBJS)
+	$(CC) $(CFLAGS) $(OBJS) -o $@
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c $< -o $@
 
-.PHONY : clean
 clean :
-	@rm -f intel_sdsi
+	@rm -f intel_sdsi $(OBJS)
 
 install : intel_sdsi
 	install -d  $(DESTDIR)$(BINDIR)
 	install -m 755 -p intel_sdsi $(DESTDIR)$(BINDIR)/intel_sdsi
+
+.PHONY : clean install
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH V2 3/3] tools/arch/x86/intel_sdsi: Add attestation support
  2024-05-07 18:01 [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport David E. Box
  2024-05-07 18:01 ` [PATCH V2 2/3] tools/arch/x86/intel_sdsi: Rework Makefile David E. Box
@ 2024-05-07 18:01 ` David E. Box
  2024-05-08  8:23 ` [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport Ilpo Järvinen
  2 siblings, 0 replies; 4+ messages in thread
From: David E. Box @ 2024-05-07 18:01 UTC (permalink / raw
  To: linux-doc, david.e.box, ilpo.jarvinen, hdegoede, linux-kernel,
	platform-driver-x86

Add support in the intel_sdsi tool to perform SPDM GET_DIGESTS and
GET_CERTIFICATE commands. Output is sent to stdout.

Example reading the certificate chain from socket 0:

intel_sdsi -d 1 -attest get_certificate | openssl x509 -inform DER -nout -text

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---
V2
   - Remove unnecessary struct packing
   - Remove newline from perror()
   - Use new SDSI_SPDM_BUF_SIZE from uapi header
   - In spdm_get_certificate:
        - Initialize remainder length to the minimum of the actual size
          or the maximum buffer size.
        - Add old_remainder to test for progress in retrieving the
	  certificate chain
   - Add the MESSAGE options for --attest in --help output

 tools/arch/x86/intel_sdsi/Makefile     |  11 +-
 tools/arch/x86/intel_sdsi/intel_sdsi.c |  72 +++-
 tools/arch/x86/intel_sdsi/spdm.c       | 476 +++++++++++++++++++++++++
 tools/arch/x86/intel_sdsi/spdm.h       |  13 +
 4 files changed, 567 insertions(+), 5 deletions(-)
 create mode 100644 tools/arch/x86/intel_sdsi/spdm.c
 create mode 100644 tools/arch/x86/intel_sdsi/spdm.h

diff --git a/tools/arch/x86/intel_sdsi/Makefile b/tools/arch/x86/intel_sdsi/Makefile
index 47b6fd98372c..0d59a0299bc7 100644
--- a/tools/arch/x86/intel_sdsi/Makefile
+++ b/tools/arch/x86/intel_sdsi/Makefile
@@ -3,20 +3,23 @@
 include ../../../scripts/Makefile.include
 BINDIR ?= /usr/sbin
 
-SRCS = intel_sdsi.c
+SRCS = intel_sdsi.c spdm.c
 
 OBJS = $(SRCS:.c=.o)
 
-override CFLAGS += -O2 -Wall -Wextra
+override CFLAGS += -O2 -Wall -Wextra -I../../../include
 
-intel_sdsi: $(OBJS)
+intel_sdsi: intel_sdsi.h $(OBJS)
 	$(CC) $(CFLAGS) $(OBJS) -o $@
 
+intel_sdsi.h: ../../../../include/uapi/linux/intel_sdsi.h
+	ln -sf ../../../../include/uapi/linux/intel_sdsi.h $@
+
 %.o: %.c
 	$(CC) $(CFLAGS) -c $< -o $@
 
 clean :
-	@rm -f intel_sdsi $(OBJS)
+	@rm -f intel_sdsi intel_sdsi.h $(OBJS)
 
 install : intel_sdsi
 	install -d  $(DESTDIR)$(BINDIR)
diff --git a/tools/arch/x86/intel_sdsi/intel_sdsi.c b/tools/arch/x86/intel_sdsi/intel_sdsi.c
index 766a5d26f534..b3c13e4696f4 100644
--- a/tools/arch/x86/intel_sdsi/intel_sdsi.c
+++ b/tools/arch/x86/intel_sdsi/intel_sdsi.c
@@ -22,6 +22,9 @@
 
 #include <sys/types.h>
 
+#include "spdm.h"
+#include "intel_sdsi.h"
+
 #ifndef __packed
 #define __packed __attribute__((packed))
 #endif
@@ -179,6 +182,7 @@ struct sdsi_dev {
 	struct state_certificate sc;
 	char *dev_name;
 	char *dev_path;
+	int dev_no;
 	uint32_t guid;
 };
 
@@ -189,6 +193,12 @@ enum command {
 	CMD_STATE_CERT,
 	CMD_PROV_AKC,
 	CMD_PROV_CAP,
+	CMD_ATTESTATION,
+};
+
+enum spdm_message {
+	GET_DIGESTS,
+	GET_CERTIFICATE,
 };
 
 static void sdsi_list_devices(void)
@@ -647,6 +657,41 @@ static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file)
 	return sdsi_provision(s, bin_file, CMD_PROV_CAP);
 }
 
+static int sdsi_attestation(struct sdsi_dev *s, enum spdm_message message)
+{
+	struct cert_chain c;
+	uint8_t digest[TPM_ALG_SHA_384_SIZE];
+	size_t size;
+	int ret, i;
+
+	switch (message) {
+	case GET_CERTIFICATE:
+		ret = spdm_get_certificate(s->dev_no, &c);
+		if (ret)
+			return ret;
+
+		size = fwrite(c.chain, sizeof(uint8_t), c.len, stdout);
+		if (size != c.len) {
+			fprintf(stderr, "Unable to write complete certificate chain\n");
+			ret = -1;
+		}
+
+		free(c.chain);
+		break;
+	case GET_DIGESTS:
+		ret = spdm_get_digests(s->dev_no, digest);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < TPM_ALG_SHA_384_SIZE; i++)
+			printf("%02x", digest[i]);
+		printf("\n");
+		break;
+	}
+
+	return ret;
+}
+
 static int read_sysfs_data(const char *file, int *value)
 {
 	char buff[16];
@@ -728,6 +773,7 @@ static struct sdsi_dev *sdsi_create_dev(char *dev_no)
 	}
 
 	s->guid = guid;
+	s->dev_no = atoi(dev_no);
 
 	return s;
 }
@@ -742,6 +788,7 @@ static void sdsi_free_dev(struct sdsi_dev *s)
 static void usage(char *prog)
 {
 	printf("Usage: %s [-l] [-d DEVNO [-i] [-s] [-m | -C] [-a FILE] [-c FILE]\n", prog);
+	printf("       [-attest MESSAGE]\n");
 }
 
 static void show_help(void)
@@ -754,12 +801,17 @@ static void show_help(void)
 	printf("  %-18s\t%s\n", "-m, --meter",          "show meter certificate data");
 	printf("  %-18s\t%s\n", "-C, --meter_current",  "show live unattested meter data");
 	printf("  %-18s\t%s\n", "-a, --akc FILE",       "provision socket with AKC FILE");
-	printf("  %-18s\t%s\n", "-c, --cap FILE>",      "provision socket with CAP FILE");
+	printf("  %-18s\t%s\n", "-c, --cap FILE",       "provision socket with CAP FILE");
+	printf("  %-18s\t%s\n", "--attest MESSAGE",     "send attestion MESSAGE. Valid");
+	printf("  %-18s\t%s\n", "",                     "messages are:");
+	printf("  %-18s\t%s\n", "",                     "    get_digests");
+	printf("  %-18s\t%s\n", "",                     "    get_certificate");
 }
 
 int main(int argc, char *argv[])
 {
 	char bin_file[PATH_MAX], *dev_no = NULL;
+	enum spdm_message message = GET_DIGESTS;
 	bool device_selected = false;
 	char *progname;
 	enum command command = -1;
@@ -769,6 +821,7 @@ int main(int argc, char *argv[])
 
 	static struct option long_options[] = {
 		{"akc",			required_argument,	0, 'a'},
+		{"attest",		required_argument,	0, 0},
 		{"cap",			required_argument,	0, 'c'},
 		{"devno",		required_argument,	0, 'd'},
 		{"help",		no_argument,		0, 'h'},
@@ -820,6 +873,20 @@ int main(int argc, char *argv[])
 
 			command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP;
 			break;
+		case 0:
+			if (strcmp(long_options[option_index].name, "attest") == 0) {
+				command = CMD_ATTESTATION;
+
+				if (strcmp(optarg, "get_digests") == 0)
+					message = GET_DIGESTS;
+				else if (strcmp(optarg, "get_certificate") == 0)
+					message = GET_CERTIFICATE;
+				else {
+					fprintf(stderr, "Unrecognized attestation command\n");
+					return -1;
+				}
+			}
+			break;
 		case 'h':
 			usage(progname);
 			show_help();
@@ -854,6 +921,9 @@ int main(int argc, char *argv[])
 		case CMD_PROV_CAP:
 			ret = sdsi_provision_cap(s, bin_file);
 			break;
+		case CMD_ATTESTATION:
+			ret = sdsi_attestation(s, message);
+			break;
 		default:
 			fprintf(stderr, "No command specified\n");
 			return -1;
diff --git a/tools/arch/x86/intel_sdsi/spdm.c b/tools/arch/x86/intel_sdsi/spdm.c
new file mode 100644
index 000000000000..71ac1e91a6d8
--- /dev/null
+++ b/tools/arch/x86/intel_sdsi/spdm.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * spdm: Lightweight Security Protocol and Data Model (SPDM) specification
+ * support code for performing attestation commands using the Intel On
+ * Demand driver ioctl interface. Intel On Demand currently supports
+ * SPDM version 1.0
+ *
+ * See the SPDM v1.0 specification at:
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.0.1.pdf
+ *
+ * Copyright (C) 2024 Intel Corporation. All rights reserved.
+ */
+
+#include<linux/bits.h>
+
+#include<fcntl.h>
+#include<stdio.h>
+#include<stdlib.h>
+#include<stdint.h>
+#include<string.h>
+#include<unistd.h>
+#include<sys/ioctl.h>
+#include "spdm.h"
+#include "intel_sdsi.h"
+
+// SPDM constants
+#define SPDM_VERSION			0x10
+#define SPDM_REQUEST			0x80
+#define SPDM_ERROR			0x7f
+
+// SPDM request codes
+#define SPDM_GET_VERSION		0x84
+#define SPDM_GET_CAPABILITIES		0xE1
+#define SPDM_NEGOTIATE_ALGORITHMS	0xE3
+#define SPDM_GET_DIGESTS		0x81
+#define SPDM_GET_CERTIFICATE		0x82
+#define SPDM_CHALLENGE			0x83
+#define SPDM_GET_MEASUREMENTS		0xE0
+
+#define SDSI_DEV_PATH			"/dev/isdsi"
+
+#define SPDM_RSVD			0
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct spdm_header {
+	uint8_t version;
+	uint8_t code;
+	uint8_t param1;
+	uint8_t param2;
+};
+
+static int error_response(struct spdm_header *response)
+{
+	if (response->code != SPDM_ERROR)
+		fprintf(stderr, "ERROR: Unrecognized SPDM response\n");
+
+	switch (response->param1) {
+	case 0x00:
+	case 0x02:
+	case 0x06:
+	case 0x08 ... 0x40:
+	case 0x44 ... 0xfe:
+		fprintf(stderr, "SPDM RSP ERROR: Reserved.\n");
+		break;
+	case 0x01:
+		fprintf(stderr, "SPDM RSP ERROR: One or more request fields are invalid.\n");
+		break;
+	case 0x03:
+		fprintf(stderr, "SPDM RSP ERROR: The Responder received the request message\n");
+		fprintf(stderr, "and the Responder decided to ignore the request message\n");
+		fprintf(stderr, "but the Responder may be able to process the request message\n");
+		fprintf(stderr, "if the request message is sent again in the future.\n");
+		break;
+	case 0x04:
+		fprintf(stderr, "SPDM RSP ERROR: The Responder received an unexpected request\n");
+		fprintf(stderr, "message. For example, CHALLENGE before NEGOTIATE_ALGORITHMS.\n");
+		break;
+	case 0x05:
+		fprintf(stderr, "SPDM RSP ERROR: Unspecified error occurred.\n");
+		break;
+	case 0x07:
+		fprintf(stderr, "SPDM RSP ERROR: The RequestResponseCode in the request\n");
+		fprintf(stderr, "message is unsupported.\n");
+		break;
+	case 0x41:
+		fprintf(stderr, "SPDM RSP ERROR: Requested SPDM Major Version is not\n");
+		fprintf(stderr, "supported.\n");
+		break;
+	case 0x42:
+		fprintf(stderr, "SPDM RSP ERROR: See the RESPONSE_IF_READY request message.\n");
+		break;
+	case 0x43:
+		fprintf(stderr, "SPDM RSP ERROR: Responder is requesting Requester to reissue\n");
+		fprintf(stderr, "GET_VERSION to resynchronize.\n");
+		break;
+	case 0xFF:
+		fprintf(stderr, "SPDM RSP ERROR: Vendor or Other Standards defined.\n");
+		break;
+	}
+
+	return -1;
+}
+
+static int sdsi_process_ioctl(int ioctl_no, void *info, uint8_t dev_no)
+{
+	char pathname[14];
+	int fd, ret;
+
+	ret = snprintf(pathname, 14, "%s%d", SDSI_DEV_PATH, dev_no);
+	if (ret < 0)
+		return ret;
+
+	fd = open(pathname, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	ret = ioctl(fd, ioctl_no, info);
+	if (ret)
+		perror("Failed to process ioctl");
+
+	close(fd);
+
+	return ret;
+}
+
+static int
+sdsi_process_spdm(void *request, void *response, int req_size, uint32_t rsp_size,
+		  int dev_no)
+{
+	struct sdsi_spdm_command *command;
+	struct sdsi_spdm_message *message = request;
+	uint8_t request_code;
+	int ret;
+
+	command = malloc(sizeof(*command));
+	if (!command) {
+		perror("malloc");
+		return -1;
+	}
+
+	command->size = req_size;
+	command->message = *message;
+	request_code = command->message.request_response_code;
+
+	ret = sdsi_process_ioctl(SDSI_IF_SPDM_COMMAND, command, dev_no);
+	if (ret)
+		goto free_command;
+
+	if (command->size < sizeof(struct spdm_header)) {
+		fprintf(stderr, "Bad SPDM message size\n");
+		ret = -1;
+		goto free_command;
+	}
+
+	if (command->message.request_response_code != (request_code & ~SPDM_REQUEST)) {
+		ret = error_response((struct spdm_header *)&command->message);
+		goto free_command;
+	}
+
+	if (response) {
+		if (command->size > rsp_size) {
+			fprintf(stderr, "SPDM response buffer too small\n");
+			ret = -1;
+			goto free_command;
+		}
+
+		memcpy(response, &command->message, command->size);
+	}
+
+free_command:
+	free(command);
+	return ret;
+}
+
+struct version_number_entry {
+	uint8_t alpha:4;
+	uint8_t update_version_number:4;
+	union {
+		uint8_t version;
+		struct {
+			uint8_t minor:4;
+			uint8_t major:4;
+		};
+	};
+} __packed;
+
+struct get_version_response {
+	struct spdm_header header;
+	uint16_t reserved:8;
+	uint16_t version_number_entry_count:8;
+	struct version_number_entry entry[10];
+} __packed;
+
+static int spdm_get_version(int dev_no)
+{
+	struct spdm_header request = {};
+	struct get_version_response response = {};
+	uint8_t version;
+	int ret;
+
+	request.version = SPDM_VERSION;
+	request.code = SPDM_GET_VERSION;
+	request.param1 = SPDM_RSVD;
+	request.param2 = SPDM_RSVD;
+
+	ret = sdsi_process_spdm(&request, &response, sizeof(request),
+				sizeof(response), dev_no);
+	if (ret) {
+		fprintf(stderr, "Failed GET_VERSION\n");
+		return ret;
+	}
+
+	if (!response.version_number_entry_count) {
+		fprintf(stderr, "Bad GET_VERSION entry count\n");
+		return -1;
+	}
+
+	version = response.entry[0].version;
+
+	if (version != SPDM_VERSION) {
+		fprintf(stderr, "Unsupported version 0x%x\n", SPDM_VERSION);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int spdm_get_capabilities(int dev_no)
+{
+	struct spdm_header request = {};
+	int ret;
+
+	request.version = SPDM_VERSION;
+	request.code = SPDM_GET_CAPABILITIES;
+	request.param1 = SPDM_RSVD;
+	request.param2 = SPDM_RSVD;
+
+	ret = sdsi_process_spdm(&request, NULL, sizeof(request), 0, dev_no);
+	if (ret) {
+		fprintf(stderr, "Failed GET_CAPABILITIES\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+struct spdm_negotiate_alg {
+	struct spdm_header header;
+	uint32_t	length:16;
+	uint32_t	measurement_specification:8;
+	uint32_t	reserved:8;
+	uint32_t	base_asym_algo;
+	uint32_t	base_hash_algo;
+	uint32_t	reserved2[3];
+	uint32_t	ext_asym_count:8;
+	uint32_t	ext_hash_count:8;
+	uint32_t	reserved3:16;
+};
+
+#define MEASUREMENT_SPEC_DMTF			BIT(0)
+#define BASE_ASYM_ALG_ECDSA_ECC_NIST_P384	BIT(7)
+#define BASE_HASH_ALG_SHA_384			BIT(1)
+
+static int spdm_negotiate_algorithms(int dev_no)
+{
+	struct spdm_negotiate_alg request = {};
+	int ret;
+
+	request.header.version = SPDM_VERSION;
+	request.header.code = SPDM_NEGOTIATE_ALGORITHMS;
+	request.header.param1 = SPDM_RSVD;
+	request.header.param2 = SPDM_RSVD;
+
+	request.length = sizeof(request);
+	request.measurement_specification = MEASUREMENT_SPEC_DMTF;
+	request.base_asym_algo = BASE_ASYM_ALG_ECDSA_ECC_NIST_P384;
+	request.base_hash_algo = BASE_HASH_ALG_SHA_384;
+
+	ret = sdsi_process_spdm(&request, NULL, sizeof(request), 0, dev_no);
+	if (ret) {
+		fprintf(stderr, "Failed NEGOTIATE_ALGORITHMS\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spdm_negotiate(int dev_no)
+{
+	int ret;
+
+	ret = spdm_get_version(dev_no);
+	if (ret)
+		return ret;
+
+	ret = spdm_get_capabilities(dev_no);
+	if (ret)
+		return ret;
+
+	return spdm_negotiate_algorithms(dev_no);
+}
+
+struct get_digests_response {
+	struct spdm_header header;
+	uint8_t digest[TPM_ALG_SHA_384_SIZE];
+};
+
+#define SLOT_MASK(slot) BIT(slot)
+
+int spdm_get_digests(int dev_no, uint8_t digest[TPM_ALG_SHA_384_SIZE])
+{
+	struct spdm_header request = {};
+	struct get_digests_response response = {};
+	int ret;
+
+	ret = spdm_negotiate(dev_no);
+	if (ret)
+		return ret;
+
+	request.version = SPDM_VERSION;
+	request.code = SPDM_GET_DIGESTS;
+	request.param1 = SPDM_RSVD;
+	request.param2 = SPDM_RSVD;
+
+	ret = sdsi_process_spdm(&request, &response, sizeof(request),
+				sizeof(response), dev_no);
+	if (ret) {
+		fprintf(stderr, "Failed GET_DIGESTS\n");
+		return ret;
+	}
+
+	if (!(response.header.param2 & SLOT_MASK(0))) {
+		fprintf(stderr, "Error, Slot 0 not selected in GET_DIGESTS\n");
+		return -1;
+	}
+
+	if (digest)
+		memcpy(digest, response.digest, TPM_ALG_SHA_384_SIZE);
+
+	return 0;
+}
+
+#define CERT_SLOT	0
+
+struct get_cert_request {
+	struct spdm_header header;
+	uint16_t offset;
+	uint16_t length;
+};
+
+struct get_cert_response {
+	struct spdm_header header;
+	uint16_t portion_length;
+	uint16_t remainder_length;
+	uint8_t certificate_chain[SDSI_SPDM_BUF_SIZE];
+};
+
+static int get_certificate_size(int dev_no)
+{
+	struct get_cert_request request = {};
+	struct get_cert_response response = {};
+	int ret;
+
+	request.header.version = SPDM_VERSION;
+	request.header.code = SPDM_GET_CERTIFICATE;
+	request.header.param1 = CERT_SLOT;
+	request.header.param2 = SPDM_RSVD;
+	request.offset = 0;
+	request.length = SDSI_SPDM_BUF_SIZE;
+
+	ret = sdsi_process_spdm(&request, &response, sizeof(request),
+				sizeof(response), dev_no);
+	if (ret) {
+		fprintf(stderr, "Error getting size during GET_CERTIFICATE\n");
+		return ret;
+	}
+
+	return response.portion_length + response.remainder_length;
+}
+
+static int get_certificate_portion(int dev_no, uint16_t offset, uint16_t length,
+				   uint16_t *portion_length, uint16_t *remainder_length,
+				   uint8_t *cert_chain)
+{
+	struct get_cert_request request = {};
+	struct get_cert_response response = {};
+	int ret;
+
+	request.header.version = SPDM_VERSION;
+	request.header.code = SPDM_GET_CERTIFICATE;
+	request.header.param1 = CERT_SLOT;
+	request.header.param2 = SPDM_RSVD;
+	request.offset = offset;
+	request.length = length;
+
+	ret = sdsi_process_spdm(&request, &response, sizeof(request),
+				sizeof(response), dev_no);
+	if (ret) {
+		fprintf(stderr, "Failed GET_CERTIFICATE\n");
+		return ret;
+	}
+
+	*portion_length = response.portion_length;
+	*remainder_length = response.remainder_length;
+
+	memcpy(cert_chain + offset, response.certificate_chain, *portion_length);
+
+	return 0;
+}
+
+int spdm_get_certificate(int dev_no, struct cert_chain *c)
+{
+	uint16_t remainder_length;
+	uint16_t old_remainder;
+	uint16_t portion_length = 0;
+	uint16_t offset = 0;
+	uint16_t size;
+	int ret;
+
+	ret = spdm_negotiate(dev_no);
+	if (ret)
+		return ret;
+
+	ret = spdm_get_digests(dev_no, NULL);
+	if (ret)
+		return ret;
+
+	ret = get_certificate_size(dev_no);
+	if (ret < 0)
+		return ret;
+
+	size = ret;
+
+	c->chain = malloc(size);
+	if (!c->chain) {
+		perror("malloc");
+		return -1;
+	}
+
+	remainder_length = size < SDSI_SPDM_BUF_SIZE ? size : SDSI_SPDM_BUF_SIZE;
+	old_remainder = remainder_length;
+
+	while (remainder_length) {
+		uint16_t length;
+
+		length = remainder_length < SDSI_SPDM_BUF_SIZE ?
+				remainder_length : SDSI_SPDM_BUF_SIZE;
+		offset += portion_length;
+
+		ret = get_certificate_portion(dev_no, offset, length,
+					      &portion_length,
+					      &remainder_length,
+					      c->chain);
+		if (ret < 0)
+			goto free_cert_chain;
+
+		if (!(remainder_length < old_remainder)) {
+			fprintf(stderr, "Bad GET_CERTIFICATE length\n");
+			ret = -1;
+			goto free_cert_chain;
+		}
+
+		old_remainder = remainder_length;
+	}
+
+	c->len = offset + portion_length;
+	return 0;
+
+free_cert_chain:
+	free(c->chain);
+	c->chain = NULL;
+	return ret;
+}
diff --git a/tools/arch/x86/intel_sdsi/spdm.h b/tools/arch/x86/intel_sdsi/spdm.h
new file mode 100644
index 000000000000..aa7e08ffb872
--- /dev/null
+++ b/tools/arch/x86/intel_sdsi/spdm.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <stdint.h>
+
+#define TPM_ALG_SHA_384_SIZE 48
+
+struct cert_chain {
+	void *chain;
+	size_t len;
+};
+
+int spdm_get_digests(int dev_no, uint8_t digest[TPM_ALG_SHA_384_SIZE]);
+int spdm_get_certificate(int dev_no, struct cert_chain *c);
+
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport
  2024-05-07 18:01 [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport David E. Box
  2024-05-07 18:01 ` [PATCH V2 2/3] tools/arch/x86/intel_sdsi: Rework Makefile David E. Box
  2024-05-07 18:01 ` [PATCH V2 3/3] tools/arch/x86/intel_sdsi: Add attestation support David E. Box
@ 2024-05-08  8:23 ` Ilpo Järvinen
  2 siblings, 0 replies; 4+ messages in thread
From: Ilpo Järvinen @ 2024-05-08  8:23 UTC (permalink / raw
  To: David E. Box; +Cc: linux-doc, Hans de Goede, LKML, platform-driver-x86

On Tue, 7 May 2024, David E. Box wrote:

> Intel On Demand adds attestation and firmware measurement retrieval
> services through use of the protocols defined the Security Protocols and
> Data Measurement (SPDM) specification. SPDM messages exchanges are used to
> authenticate On Demand hardware and to retrieve signed measurements of the
> NVRAM state used to track feature provisioning and the NVRAM state used for
> metering services. These allow software to verify the authenticity of the
> On Demand hardware as well as the integrity of the reported silicon
> configuration.
> 
> Add an ioctl interface for sending SPDM messages through the On Demand
> mailbox. Provides commands to get a list of SPDM enabled devices, get the
> message size limits for SPDM Requesters and Responders, and perform an SPDM
> message exchange.
> 
> Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.0.1.pdf [1]
> ---
> V2
>    - Move size < 4 check into sdsi_spdm_exchange() and add comment
>      clarifying return values of that function.
>    - Use SZ_4K and add helpers
>    - Use devm_kasprintf()
>    - Remove unnecessary parens
>    - Use --attest for long option
> 
>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>  MAINTAINERS                                   |   1 +
>  drivers/platform/x86/intel/sdsi.c             | 210 +++++++++++++++++-
>  include/uapi/linux/intel_sdsi.h               |  81 +++++++
>  4 files changed, 292 insertions(+), 1 deletion(-)
>  create mode 100644 include/uapi/linux/intel_sdsi.h
> 
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index c472423412bf..20dcc2dbcaf6 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -382,6 +382,7 @@ Code  Seq#    Include File                                           Comments
>                                                                       <mailto:mathieu.desnoyers@efficios.com>
>  0xF8  all    arch/x86/include/uapi/asm/amd_hsmp.h                    AMD HSMP EPYC system management interface driver
>                                                                       <mailto:nchatrad@amd.com>
> +0xFC  all    linux/intel_sdsi.h
>  0xFD  all    linux/dm-ioctl.h
>  0xFE  all    linux/isst_if.h
>  ====  =====  ======================================================= ================================================================
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 846187625552..060bd3358cec 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11165,6 +11165,7 @@ INTEL SDSI DRIVER
>  M:	David E. Box <david.e.box@linux.intel.com>
>  S:	Supported
>  F:	drivers/platform/x86/intel/sdsi.c
> +F:	include/uapi/linux/intel_sdsi.h
>  F:	tools/arch/x86/intel_sdsi/
>  F:	tools/testing/selftests/drivers/sdsi/
>  
> diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
> index 277e4f4b20ac..686dd9e4e026 100644
> --- a/drivers/platform/x86/intel/sdsi.c
> +++ b/drivers/platform/x86/intel/sdsi.c
> @@ -11,9 +11,12 @@
>  #include <linux/auxiliary_bus.h>
>  #include <linux/bits.h>
>  #include <linux/bitfield.h>
> +#include <linux/cleanup.h>
>  #include <linux/device.h>
>  #include <linux/iopoll.h>
> +#include <linux/intel_sdsi.h>
>  #include <linux/kernel.h>
> +#include <linux/miscdevice.h>
>  #include <linux/module.h>
>  #include <linux/overflow.h>
>  #include <linux/pci.h>
> @@ -42,6 +45,7 @@
>  
>  #define SDSI_ENABLED_FEATURES_OFFSET	16
>  #define SDSI_FEATURE_SDSI		BIT(3)
> +#define SDSI_FEATURE_ATTESTATION	BIT(12)
>  #define SDSI_FEATURE_METERING		BIT(26)
>  
>  #define SDSI_SOCKET_ID_OFFSET		64
> @@ -91,6 +95,7 @@ enum sdsi_command {
>  	SDSI_CMD_PROVISION_CAP		= 0x0008,
>  	SDSI_CMD_READ_STATE		= 0x0010,
>  	SDSI_CMD_READ_METER		= 0x0014,
> +	SDSI_CMD_ATTESTATION		= 0x1012,
>  };
>  
>  struct sdsi_mbox_info {
> @@ -109,12 +114,14 @@ struct disc_table {
>  struct sdsi_priv {
>  	struct mutex		mb_lock;	/* Mailbox access lock */
>  	struct device		*dev;
> +	struct miscdevice	miscdev;
>  	void __iomem		*control_addr;
>  	void __iomem		*mbox_addr;
>  	void __iomem		*regs_addr;
>  	int			control_size;
>  	int			maibox_size;
>  	int			registers_size;
> +	int			id;
>  	u32			guid;
>  	u32			features;
>  };
> @@ -582,6 +589,97 @@ static const struct attribute_group sdsi_group = {
>  };
>  __ATTRIBUTE_GROUPS(sdsi);
>  
> +/*
> + * SPDM transport
> + * Returns size of the response message or an error code on failure.
> + */
> +static int sdsi_spdm_exchange(void *private, const void *request,
> +			      size_t request_sz, void *response,
> +			      size_t response_sz)
> +{
> +	struct sdsi_priv *priv = private;
> +	struct sdsi_mbox_info info = {};
> +	size_t spdm_msg_size, size;
> +	int ret;
> +
> +	/*
> +	 * For the attestation command, the mailbox write size is the sum of:
> +	 *     Size of the SPDM request payload, padded for qword alignment
> +	 *     8 bytes for the mailbox command
> +	 *     8 bytes for the actual (non-padded) size of the SPDM request
> +	 */
> +	if (request_sz > SDSI_SIZE_WRITE_MSG - 2 * sizeof(u64))
> +		return -EOVERFLOW;
> +
> +	info.size = round_up(request_sz, sizeof(u64)) + 2 * sizeof(u64);
> +
> +	u64 *payload __free(kfree) = kzalloc(info.size, GFP_KERNEL);
> +	if (!payload)
> +		return -ENOMEM;
> +
> +	memcpy(payload, request, request_sz);
> +
> +	/* The non-padded SPDM payload size is the 2nd-to-last qword */
> +	payload[(info.size / sizeof(u64)) - 2] = request_sz;
> +
> +	/* Attestation mailbox command is the last qword of payload buffer */
> +	payload[(info.size / sizeof(u64)) - 1] = SDSI_CMD_ATTESTATION;
> +
> +	info.payload = payload;
> +	info.buffer = response;
> +
> +	ret = mutex_lock_interruptible(&priv->mb_lock);
> +	if (ret)
> +		return ret;
> +	ret = sdsi_mbox_write(priv, &info, &size);
> +	mutex_unlock(&priv->mb_lock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * The read size is the sum of:
> +	 *     Size of the SPDM response payload, padded for qword alignment
> +	 *     8 bytes for the actual (non-padded) size of the SPDM payload
> +	 */
> +
> +	if (size < sizeof(u64)) {
> +		dev_err(priv->dev,
> +			"Attestation error: Mailbox reply size, %ld, too small\n",
> +			size);

For size_t, %zu is the correct printf format. There are more of these 
below but I won't mark them explicitly.

> +		return -EPROTO;
> +	}
> +
> +	if (!IS_ALIGNED(size, sizeof(u64))) {
> +		dev_err(priv->dev,
> +			"Attestation error: Mailbox reply size, %ld, is not aligned\n",
> +			size);
> +		return -EPROTO;
> +	}
> +
> +	/*
> +	 * Get the SPDM response size from the last QWORD and check it fits
> +	 * with no more than 7 bytes of padding
> +	 */
> +	spdm_msg_size = ((u64 *)info.buffer)[(size - sizeof(u64)) / sizeof(u64)];
> +	if (!in_range(size - spdm_msg_size - sizeof(u64), 0, 8)) {
> +		dev_err(priv->dev,
> +			"Attestation error: Invalid SPDM response size, %ld\n",
> +			spdm_msg_size);
> +		return -EPROTO;
> +	}
> +
> +	if (spdm_msg_size > response_sz || spdm_msg_size < SPDM_HEADER_SIZE) {
> +		dev_err(priv->dev, "Attestation error: Expected response size %ld, got %ld\n",
> +			response_sz, spdm_msg_size);
> +		return -EOVERFLOW;
> +	}
> +
> +	memcpy(response, info.buffer, spdm_msg_size);
> +
> +	return spdm_msg_size;
> +}
> +
>  static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table)
>  {
>  	switch (table->guid) {
> @@ -649,6 +747,92 @@ static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *paren
>  	return 0;
>  }
>  
> +#define SDSI_SPDM_DRIVER_VERSION	1
> +
> +static int sdsi_spdm_get_info(struct sdsi_priv *priv,
> +			      struct sdsi_spdm_info __user *argp)
> +{
> +	struct sdsi_spdm_info info;
> +
> +	info.driver_version = SDSI_SPDM_DRIVER_VERSION;
> +	info.api_version = priv->guid;
> +	info.dev_no = priv->id;
> +	info.max_request_size = SDSI_SIZE_WRITE_MSG - 2 * sizeof(u64);
> +	info.max_response_size = SDSI_SIZE_READ_MSG - sizeof(u64);
> +
> +	if (copy_to_user(argp, &info, sizeof(info)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int sdsi_spdm_do_command(struct sdsi_priv *priv,
> +				struct sdsi_spdm_command __user *argp)
> +{
> +	u32 req_size, rsp_size;
> +
> +	if (get_user(req_size, &argp->size))
> +		return -EFAULT;
> +
> +	if (req_size < 4 || req_size > sizeof(struct sdsi_spdm_message))
> +		return -EINVAL;
> +
> +	struct sdsi_spdm_message *request __free(kfree) =
> +		kmalloc(req_size, GFP_KERNEL);
> +	if (!request)
> +		return -ENOMEM;
> +
> +	struct sdsi_spdm_command *response __free(kfree) =
> +		kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL);
> +	if (!response)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(request, &argp->message, req_size))
> +		return -EFAULT;
> +
> +	rsp_size = sdsi_spdm_exchange(priv, request, req_size, response,
> +				      SDSI_SIZE_READ_MSG);
> +	if (rsp_size < 0)
> +		return rsp_size;
> +
> +	if (put_user(rsp_size, &argp->size))
> +		return -EFAULT;
> +
> +	if (copy_to_user(&argp->message, response, rsp_size))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static long sdsi_spdm_ioctl(struct file *file, unsigned int cmd,
> +			    unsigned long arg)
> +{
> +	struct sdsi_priv *priv;
> +	long ret = -ENOTTY;
> +
> +	priv = container_of(file->private_data, struct sdsi_priv, miscdev);
> +
> +	switch (cmd) {
> +	case SDSI_IF_SPDM_INFO:
> +		ret = sdsi_spdm_get_info(priv,
> +				(struct sdsi_spdm_info __user *)arg);
> +		break;
> +	case SDSI_IF_SPDM_COMMAND:
> +		ret = sdsi_spdm_do_command(priv,
> +				(struct sdsi_spdm_command __user *)arg);

You can return directly.

> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;

return -ENOTTY;

and remove the ret variable entirely.


-- 
 i.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-05-08  8:23 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-07 18:01 [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport David E. Box
2024-05-07 18:01 ` [PATCH V2 2/3] tools/arch/x86/intel_sdsi: Rework Makefile David E. Box
2024-05-07 18:01 ` [PATCH V2 3/3] tools/arch/x86/intel_sdsi: Add attestation support David E. Box
2024-05-08  8:23 ` [PATCH V2 1/3] platform/x86/intel/sdsi: Add ioctl SPDM transport Ilpo Järvinen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.