diff mbox series

[1/2] crypto: intel-fcs: crypto service driver for Intel SoCFPGA family

Message ID 20220914144320.605421-1-wen.ping.teh@intel.com
State New
Headers show
Series [1/2] crypto: intel-fcs: crypto service driver for Intel SoCFPGA family | expand

Commit Message

wen.ping.teh@intel.com Sept. 14, 2022, 2:43 p.m. UTC
From: Teh Wen Ping <wen.ping.teh@intel.com>

Support crypto services on Intel SoCFPGA platforms. The crypto services
include security certificate, image boot validation, security key
cancellation, get provision data, random number generation and secure
data object storage services.

Signed-off-by: Teh Wen Ping <wen.ping.teh@intel.com>
---
 drivers/crypto/Kconfig               |  11 +
 drivers/crypto/Makefile              |   1 +
 drivers/crypto/intel_fcs.c           | 726 +++++++++++++++++++++++++++
 include/uapi/linux/intel_fcs-ioctl.h | 211 ++++++++
 4 files changed, 949 insertions(+)
 create mode 100644 drivers/crypto/intel_fcs.c
 create mode 100644 include/uapi/linux/intel_fcs-ioctl.h

Comments

kernel test robot Sept. 14, 2022, 8:52 p.m. UTC | #1
Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on herbert-cryptodev-2.6/master]
[also build test ERROR on herbert-crypto-2.6/master arm64/for-next/core linus/master v6.0-rc5 next-20220914]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/wen-ping-teh-intel-com/crypto-intel-fcs-Add-crypto-service-driver-for-Intel-SoCFPGA/20220914-224700
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
config: i386-randconfig-a006 (https://download.01.org/0day-ci/archive/20220915/202209150451.PQ7ojJzq-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/4cdd20e357a4c52a9479852d5464147100813280
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review wen-ping-teh-intel-com/crypto-intel-fcs-Add-crypto-service-driver-for-Intel-SoCFPGA/20220914-224700
        git checkout 4cdd20e357a4c52a9479852d5464147100813280
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from <built-in>:1:
>> ./usr/include/linux/intel_fcs-ioctl.h:40:2: error: unknown type name 'uint32_t'
           uint32_t test_bit:1;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:41:2: error: unknown type name 'uint32_t'
           uint32_t rsvd:31;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:56:2: error: unknown type name 'uint32_t'
           uint32_t size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:66:2: error: unknown type name 'uint32_t'
           uint32_t size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:79:2: error: unknown type name 'uint32_t'
           uint32_t size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:80:2: error: unknown type name 'uint32_t'
           uint32_t c_status;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:92:2: error: unknown type name 'uint32_t'
           uint32_t src_size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:94:2: error: unknown type name 'uint32_t'
           uint32_t dst_size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:106:2: error: unknown type name 'uint32_t'
           uint32_t src_size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:108:2: error: unknown type name 'uint32_t'
           uint32_t dst_size;
           ^
   ./usr/include/linux/intel_fcs-ioctl.h:116:2: error: unknown type name 'uint32_t'
           uint32_t rndm[8];
           ^
   11 errors generated.
kernel test robot Sept. 15, 2022, 10:11 a.m. UTC | #2
Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on herbert-cryptodev-2.6/master]
[also build test ERROR on herbert-crypto-2.6/master arm64/for-next/core linus/master v6.0-rc5 next-20220914]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/wen-ping-teh-intel-com/crypto-intel-fcs-Add-crypto-service-driver-for-Intel-SoCFPGA/20220914-224700
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20220915/202209151738.j2RLe6VA-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/4cdd20e357a4c52a9479852d5464147100813280
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review wen-ping-teh-intel-com/crypto-intel-fcs-Add-crypto-service-driver-for-Intel-SoCFPGA/20220914-224700
        git checkout 4cdd20e357a4c52a9479852d5464147100813280
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from <command-line>:
>> ./usr/include/linux/intel_fcs-ioctl.h:40:9: error: unknown type name 'uint32_t'
      40 |         uint32_t test_bit:1;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:41:9: error: unknown type name 'uint32_t'
      41 |         uint32_t rsvd:31;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:56:9: error: unknown type name 'uint32_t'
      56 |         uint32_t size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:66:9: error: unknown type name 'uint32_t'
      66 |         uint32_t size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:79:9: error: unknown type name 'uint32_t'
      79 |         uint32_t size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:80:9: error: unknown type name 'uint32_t'
      80 |         uint32_t c_status;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:92:9: error: unknown type name 'uint32_t'
      92 |         uint32_t src_size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:94:9: error: unknown type name 'uint32_t'
      94 |         uint32_t dst_size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:106:9: error: unknown type name 'uint32_t'
     106 |         uint32_t src_size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:108:9: error: unknown type name 'uint32_t'
     108 |         uint32_t dst_size;
         |         ^~~~~~~~
   ./usr/include/linux/intel_fcs-ioctl.h:116:9: error: unknown type name 'uint32_t'
     116 |         uint32_t rndm[8];
         |         ^~~~~~~~
diff mbox series

Patch

diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 3e6aa319920b..f307f6f159f9 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -240,6 +240,17 @@  config CRYPTO_DEV_TALITOS2
 	  Say 'Y' here to use the Freescale Security Engine (SEC)
 	  version 2 and following as found on MPC83xx, MPC85xx, etc ...
 
+config CRYPTO_DEV_INTEL_FCS
+	tristate "Intel FPGA Crypto Service support"
+	depends on INTEL_STRATIX10_SERVICE
+	help
+	 Support crypto services on Intel SoCFPGA platforms. The crypto
+	 services include security certificate, image boot validation,
+	 security key cancellation, get provision data, random number
+	 generation and secure data object storage services.
+
+	 Say Y here if you want Intel FCS support
+
 config CRYPTO_DEV_IXP4XX
 	tristate "Driver for IXP4xx crypto hardware acceleration"
 	depends on ARCH_IXP4XX && IXP4XX_QMGR && IXP4XX_NPE
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index f81703a86b98..997f48ee4db5 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -18,6 +18,7 @@  obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_COMMON) += caam/
 obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
 obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
+obj-$(CONFIG_CRYPTO_DEV_INTEL_FCS) += intel_fcs.o
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_MARVELL) += marvell/
 obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
diff --git a/drivers/crypto/intel_fcs.c b/drivers/crypto/intel_fcs.c
new file mode 100644
index 000000000000..7b2fdcc4c604
--- /dev/null
+++ b/drivers/crypto/intel_fcs.c
@@ -0,0 +1,726 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022, Intel Corporation
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/intel_fcs-ioctl.h>
+
+#define RANDOM_NUMBER_SIZE	32
+#define FILE_NAME_SIZE		32
+#define PS_BUF_SIZE		64
+#define INVALID_STATUS		0xff
+
+#define DEC_MIN_SZ		72
+#define DEC_MAX_SZ		32712
+#define ENC_MIN_SZ		128
+#define ENC_MAX_SZ		32768
+
+
+#define FCS_REQUEST_TIMEOUT (msecs_to_jiffies(SVC_FCS_REQUEST_TIMEOUT_MS))
+#define FCS_COMPLETED_TIMEOUT (msecs_to_jiffies(SVC_COMPLETED_TIMEOUT_MS))
+
+typedef void (*fcs_callback)(struct stratix10_svc_client *client,
+			     struct stratix10_svc_cb_data *data);
+
+struct intel_fcs_priv {
+	struct stratix10_svc_chan *chan;
+	struct stratix10_svc_client client;
+	struct completion completion;
+	struct mutex lock;
+	struct miscdevice miscdev;
+	unsigned int status;
+	void *kbuf;
+	unsigned int size;
+};
+
+static void fcs_data_callback(struct stratix10_svc_client *client,
+			      struct stratix10_svc_cb_data *data)
+{
+	struct intel_fcs_priv *priv = client->priv;
+
+	if ((data->status == BIT(SVC_STATUS_OK)) ||
+	    (data->status == BIT(SVC_STATUS_COMPLETED))) {
+		priv->status = 0;
+		priv->kbuf = data->kaddr2;
+		priv->size = *((unsigned int *)data->kaddr3);
+	} else if (data->status == BIT(SVC_STATUS_ERROR)) {
+		priv->status = *((unsigned int *)data->kaddr1);
+		dev_err(client->dev, "error, mbox_error=0x%x\n", priv->status);
+		priv->kbuf = data->kaddr2;
+		priv->size = (data->kaddr3) ?
+			*((unsigned int *)data->kaddr3) : 0;
+	} else {
+		dev_warn(client->dev, "rejected, invalid param\n");
+		priv->status = -EINVAL;
+		priv->kbuf = NULL;
+		priv->size = 0;
+	}
+
+	complete(&priv->completion);
+}
+
+static void fcs_vab_callback(struct stratix10_svc_client *client,
+			     struct stratix10_svc_cb_data *data)
+{
+	struct intel_fcs_priv *priv = client->priv;
+
+	if (data->status == BIT(SVC_STATUS_ERROR)) {
+		priv->status = *((unsigned int *)data->kaddr1);
+		dev_err(client->dev, "mbox_error=0x%x\n", priv->status);
+	} else if (data->status == BIT(SVC_STATUS_BUSY)) {
+		priv->status = -ETIMEDOUT;
+		dev_err(client->dev, "timeout to get completed status\n");
+	} else if (data->status == BIT(SVC_STATUS_OK)) {
+		priv->status = 0;
+	} else {
+		priv->status = -EINVAL;
+		dev_err(client->dev, "rejected, invalid param\n");
+	}
+
+	complete(&priv->completion);
+}
+
+static int fcs_request_service(struct intel_fcs_priv *priv,
+			       void *msg, unsigned long timeout)
+{
+	struct stratix10_svc_client_msg *p_msg =
+			(struct stratix10_svc_client_msg *)msg;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	reinit_completion(&priv->completion);
+
+	ret = stratix10_svc_send(priv->chan, p_msg);
+	if (ret)
+		return -EINVAL;
+
+	ret = wait_for_completion_timeout(&priv->completion,
+							timeout);
+	if (!ret) {
+		dev_err(priv->client.dev,
+			"timeout waiting for SMC call\n");
+		ret = -ETIMEDOUT;
+	} else
+		ret = 0;
+
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static void fcs_close_services(struct intel_fcs_priv *priv,
+			       void *sbuf, void *dbuf)
+{
+	if (sbuf)
+		stratix10_svc_free_memory(priv->chan, sbuf);
+
+	if (dbuf)
+		stratix10_svc_free_memory(priv->chan, dbuf);
+
+	stratix10_svc_done(priv->chan);
+}
+
+static long fcs_ioctl(struct file *file, unsigned int cmd,
+		      unsigned long arg)
+{
+	struct intel_fcs_dev_ioctl *data;
+	struct intel_fcs_priv *priv;
+	struct device *dev;
+	struct stratix10_svc_client_msg *msg;
+	const struct firmware *fw;
+	char filename[FILE_NAME_SIZE];
+	size_t tsz, datasz;
+	void *s_buf;
+	void *d_buf;
+	void *ps_buf;
+	unsigned int buf_sz;
+	int ret = 0;
+	int i;
+
+	priv = container_of(file->private_data, struct intel_fcs_priv, miscdev);
+	dev = priv->client.dev;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	msg = devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	switch (cmd) {
+	case INTEL_FCS_DEV_VALIDATION_REQUEST:
+		if (copy_from_user(data, (void __user *)arg, sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		/* for bitstream */
+		dev_dbg(dev, "file_name=%s, status=%d\n",
+			 (char *)data->com_paras.s_request.src, data->status);
+		scnprintf(filename, FILE_NAME_SIZE, "%s",
+				(char *)data->com_paras.s_request.src);
+		ret = request_firmware(&fw, filename, priv->client.dev);
+		if (ret) {
+			dev_err(dev, "error requesting firmware %s\n",
+				(char *)data->com_paras.s_request.src);
+			return -EFAULT;
+		}
+
+		dev_dbg(dev, "FW size=%ld\n", fw->size);
+		s_buf = stratix10_svc_allocate_memory(priv->chan, fw->size);
+		if (!s_buf) {
+			dev_err(dev, "failed to allocate VAB buffer\n");
+			release_firmware(fw);
+			return -ENOMEM;
+		}
+
+		memcpy(s_buf, fw->data, fw->size);
+
+		msg->payload_length = fw->size;
+		release_firmware(fw);
+
+		msg->command = COMMAND_FCS_REQUEST_SERVICE;
+		msg->payload = s_buf;
+		priv->client.receive_cb = fcs_vab_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+		dev_dbg(dev, "fcs_request_service ret=%d\n", ret);
+		if (!ret && !priv->status) {
+			/* to query the complete status */
+			msg->command = COMMAND_POLL_SERVICE_STATUS;
+			priv->client.receive_cb = fcs_data_callback;
+			ret = fcs_request_service(priv, (void *)msg,
+						  FCS_COMPLETED_TIMEOUT);
+			dev_dbg(dev, "fcs_request_service ret=%d\n", ret);
+			if (!ret && !priv->status)
+				data->status = 0;
+			else
+				data->status = priv->status;
+		} else
+			data->status = priv->status;
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, s_buf, NULL);
+			ret = -EFAULT;
+		}
+
+		fcs_close_services(priv, s_buf, NULL);
+		break;
+
+	case INTEL_FCS_DEV_SEND_CERTIFICATE:
+		if (copy_from_user(data, (void __user *)arg, sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		dev_dbg(dev, "Test=%d, Size=%d; Address=0x%p\n",
+			data->com_paras.c_request.test.test_bit,
+			data->com_paras.c_request.size,
+			data->com_paras.c_request.addr);
+
+		/* Allocate memory for certificate + test word */
+		tsz = sizeof(struct intel_fcs_cert_test_word);
+		datasz = data->com_paras.s_request.size + tsz;
+
+		s_buf = stratix10_svc_allocate_memory(priv->chan, datasz);
+		if (!s_buf) {
+			dev_err(dev, "failed to allocate VAB buffer\n");
+			return -ENOMEM;
+		}
+
+		ps_buf = stratix10_svc_allocate_memory(priv->chan, PS_BUF_SIZE);
+		if (!ps_buf) {
+			dev_err(dev, "failed to allocate p-status buf\n");
+			stratix10_svc_free_memory(priv->chan, s_buf);
+			return -ENOMEM;
+		}
+
+		/* Copy the test word */
+		memcpy(s_buf, &data->com_paras.c_request.test, tsz);
+
+		/* Copy in the certificate data (skipping over the test word) */
+		ret = copy_from_user(s_buf + tsz,
+				     data->com_paras.c_request.addr,
+				     data->com_paras.s_request.size);
+		if (ret) {
+			dev_err(dev, "failed copy buf ret=%d\n", ret);
+			fcs_close_services(priv, s_buf, ps_buf);
+			return -EFAULT;
+		}
+
+		msg->payload_length = datasz;
+		msg->command = COMMAND_FCS_SEND_CERTIFICATE;
+		msg->payload = s_buf;
+		priv->client.receive_cb = fcs_vab_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+		dev_dbg(dev, "fcs_request_service ret=%d\n", ret);
+		if (!ret && !priv->status) {
+			/* to query the complete status */
+			msg->payload = ps_buf;
+			msg->payload_length = PS_BUF_SIZE;
+			msg->command = COMMAND_POLL_SERVICE_STATUS;
+			priv->client.receive_cb = fcs_data_callback;
+			ret = fcs_request_service(priv, (void *)msg,
+						  FCS_COMPLETED_TIMEOUT);
+			dev_dbg(dev, "request service ret=%d\n", ret);
+			if (!ret && !priv->status)
+				data->status = 0;
+			else {
+				if (priv->kbuf)
+					data->com_paras.c_request.c_status =
+						(*(u32 *)priv->kbuf);
+				else
+					data->com_paras.c_request.c_status =
+						INVALID_STATUS;
+			}
+		} else
+			data->status = priv->status;
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, s_buf, NULL);
+			ret = -EFAULT;
+		}
+
+		fcs_close_services(priv, s_buf, ps_buf);
+		break;
+
+	case INTEL_FCS_DEV_RANDOM_NUMBER_GEN:
+		if (copy_from_user(data, (void __user *)arg, sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		s_buf = stratix10_svc_allocate_memory(priv->chan,
+						      RANDOM_NUMBER_SIZE);
+		if (!s_buf) {
+			dev_err(dev, "failed to allocate RNG buffer\n");
+			return -ENOMEM;
+		}
+
+		msg->command = COMMAND_FCS_RANDOM_NUMBER_GEN;
+		msg->payload = s_buf;
+		msg->payload_length = RANDOM_NUMBER_SIZE;
+		priv->client.receive_cb = fcs_data_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+
+		if (!ret && !priv->status) {
+			if (!priv->kbuf) {
+				dev_err(dev, "failure on kbuf\n");
+				fcs_close_services(priv, s_buf, NULL);
+				return -EFAULT;
+			}
+
+			for (i = 0; i < 8; i++)
+				dev_dbg(dev, "output_data[%d]=%d\n", i,
+					 *((int *)priv->kbuf + i));
+
+			for (i = 0; i < 8; i++)
+				data->com_paras.rn_gen.rndm[i] =
+					*((int *)priv->kbuf + i);
+			data->status = priv->status;
+
+		} else {
+			/* failed to get RNG */
+			data->status = priv->status;
+		}
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, s_buf, NULL);
+			ret = -EFAULT;
+		}
+
+		fcs_close_services(priv, s_buf, NULL);
+		break;
+	case INTEL_FCS_DEV_GET_PROVISION_DATA:
+		if (copy_from_user(data, (void __user *)arg,
+				   sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		s_buf = stratix10_svc_allocate_memory(priv->chan,
+					data->com_paras.gp_data.size);
+		if (!s_buf) {
+			dev_err(dev, "failed allocate provision buffer\n");
+			return -ENOMEM;
+		}
+
+		msg->command = COMMAND_FCS_GET_PROVISION_DATA;
+		msg->payload = s_buf;
+		msg->payload_length = data->com_paras.gp_data.size;
+		priv->client.receive_cb = fcs_data_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+		if (!ret && !priv->status) {
+			if (!priv->kbuf) {
+				dev_err(dev, "failure on kbuf\n");
+				fcs_close_services(priv, s_buf, NULL);
+				return -EFAULT;
+			}
+			data->com_paras.gp_data.size = priv->size;
+			ret = copy_to_user(data->com_paras.gp_data.addr,
+					   priv->kbuf, priv->size);
+			if (ret) {
+				dev_err(dev, "failure on copy_to_user\n");
+				fcs_close_services(priv, s_buf, NULL);
+				return -EFAULT;
+			}
+			data->status = 0;
+		} else {
+			data->com_paras.gp_data.addr = NULL;
+			data->com_paras.gp_data.size = 0;
+			data->status = priv->status;
+		}
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, s_buf, NULL);
+			return -EFAULT;
+		}
+
+		fcs_close_services(priv, s_buf, NULL);
+		break;
+	case INTEL_FCS_DEV_DATA_ENCRYPTION:
+		if (copy_from_user(data, (void __user *)arg, sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		if (data->com_paras.d_encryption.src_size < DEC_MIN_SZ ||
+		    data->com_paras.d_encryption.src_size > DEC_MAX_SZ) {
+			dev_err(dev, "Invalid SDOS Buffer src size:%d\n",
+				data->com_paras.d_encryption.src_size);
+			return -EFAULT;
+		}
+
+		if (data->com_paras.d_encryption.dst_size < ENC_MIN_SZ ||
+		    data->com_paras.d_encryption.dst_size > ENC_MAX_SZ) {
+			dev_err(dev, "Invalid SDOS Buffer dst size:%d\n",
+				data->com_paras.d_encryption.dst_size);
+			return -EFAULT;
+		}
+
+		/* allocate buffer for both source and destination */
+		s_buf = stratix10_svc_allocate_memory(priv->chan,
+						      DEC_MAX_SZ);
+		if (!s_buf) {
+			dev_err(dev, "failed allocate encrypt src buf\n");
+			return -ENOMEM;
+		}
+		d_buf = stratix10_svc_allocate_memory(priv->chan,
+						      ENC_MAX_SZ);
+		if (!d_buf) {
+			dev_err(dev, "failed allocate encrypt dst buf\n");
+			stratix10_svc_free_memory(priv->chan, s_buf);
+			return -ENOMEM;
+		}
+		ps_buf = stratix10_svc_allocate_memory(priv->chan, PS_BUF_SIZE);
+		if (!ps_buf) {
+			dev_err(dev, "failed allocate p-status buffer\n");
+			fcs_close_services(priv, s_buf, d_buf);
+			return -ENOMEM;
+		}
+		ret = copy_from_user(s_buf,
+				     data->com_paras.d_encryption.src,
+				     data->com_paras.d_encryption.src_size);
+		if (ret) {
+			dev_err(dev, "failure on copy_from_user\n");
+			fcs_close_services(priv, ps_buf, NULL);
+			fcs_close_services(priv, s_buf, d_buf);
+			return -ENOMEM;
+		}
+
+		msg->command = COMMAND_FCS_DATA_ENCRYPTION;
+		msg->payload = s_buf;
+		msg->payload_length =
+			data->com_paras.d_encryption.src_size;
+		msg->payload_output = d_buf;
+		msg->payload_length_output =
+			data->com_paras.d_encryption.dst_size;
+		priv->client.receive_cb = fcs_vab_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+		if (!ret && !priv->status) {
+			msg->payload = ps_buf;
+			msg->payload_length = PS_BUF_SIZE;
+			msg->command = COMMAND_POLL_SERVICE_STATUS;
+
+			priv->client.receive_cb = fcs_data_callback;
+			ret = fcs_request_service(priv, (void *)msg,
+						  FCS_COMPLETED_TIMEOUT);
+			dev_dbg(dev, "request service ret=%d\n", ret);
+
+			if (!ret && !priv->status) {
+				if (!priv->kbuf) {
+					dev_err(dev, "failure on kbuf\n");
+					fcs_close_services(priv, ps_buf, NULL);
+					fcs_close_services(priv, s_buf, d_buf);
+					return -EFAULT;
+				}
+				buf_sz = *(unsigned int *)priv->kbuf;
+				data->com_paras.d_encryption.dst_size = buf_sz;
+				data->status = 0;
+				ret = copy_to_user(data->com_paras.d_encryption.dst,
+						   d_buf, buf_sz);
+				if (ret) {
+					dev_err(dev, "failure on copy_to_user\n");
+					fcs_close_services(priv, ps_buf, NULL);
+					fcs_close_services(priv, s_buf, d_buf);
+					return -EFAULT;
+				}
+			} else {
+				data->com_paras.d_encryption.dst = NULL;
+				data->com_paras.d_encryption.dst_size = 0;
+				data->status = priv->status;
+			}
+		} else {
+			data->com_paras.d_encryption.dst = NULL;
+			data->com_paras.d_encryption.dst_size = 0;
+			data->status = priv->status;
+		}
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, ps_buf, NULL);
+			fcs_close_services(priv, s_buf, d_buf);
+			ret = -EFAULT;
+		}
+
+		fcs_close_services(priv, ps_buf, NULL);
+		fcs_close_services(priv, s_buf, d_buf);
+		break;
+	case INTEL_FCS_DEV_DATA_DECRYPTION:
+		if (copy_from_user(data, (void __user *)arg, sizeof(*data))) {
+			dev_err(dev, "failure on copy_from_user\n");
+			return -EFAULT;
+		}
+
+		if (data->com_paras.d_encryption.src_size < ENC_MIN_SZ ||
+		    data->com_paras.d_encryption.src_size > ENC_MAX_SZ) {
+			dev_err(dev, "Invalid SDOS Buffer src size:%d\n",
+				data->com_paras.d_encryption.src_size);
+			return -EFAULT;
+		}
+
+		if (data->com_paras.d_encryption.dst_size < DEC_MIN_SZ ||
+		    data->com_paras.d_encryption.dst_size > DEC_MAX_SZ) {
+			dev_err(dev, "Invalid SDOS Buffer dst size:%d\n",
+				data->com_paras.d_encryption.dst_size);
+			return -EFAULT;
+		}
+
+		/* allocate buffer for both source and destination */
+		s_buf = stratix10_svc_allocate_memory(priv->chan,
+						      ENC_MAX_SZ);
+		if (!s_buf) {
+			dev_err(dev, "failed allocate decrypt src buf\n");
+			return -ENOMEM;
+		}
+		d_buf = stratix10_svc_allocate_memory(priv->chan,
+						      DEC_MAX_SZ);
+		if (!d_buf) {
+			dev_err(dev, "failed allocate decrypt dst buf\n");
+			stratix10_svc_free_memory(priv->chan, s_buf);
+			return -ENOMEM;
+		}
+
+		ps_buf = stratix10_svc_allocate_memory(priv->chan,
+						       PS_BUF_SIZE);
+		if (!ps_buf) {
+			dev_err(dev, "failed allocate p-status buffer\n");
+			fcs_close_services(priv, s_buf, d_buf);
+			return -ENOMEM;
+		}
+
+		ret = copy_from_user(s_buf,
+				     data->com_paras.d_decryption.src,
+				     data->com_paras.d_decryption.src_size);
+		if (ret) {
+			dev_err(dev, "failure on copy_from_user\n");
+			fcs_close_services(priv, ps_buf, NULL);
+			fcs_close_services(priv, s_buf, d_buf);
+			return -EFAULT;
+		}
+
+		msg->command = COMMAND_FCS_DATA_DECRYPTION;
+		msg->payload = s_buf;
+		msg->payload_length =
+				data->com_paras.d_decryption.src_size;
+		msg->payload_output = d_buf;
+		msg->payload_length_output =
+				data->com_paras.d_decryption.dst_size;
+		priv->client.receive_cb = fcs_vab_callback;
+
+		ret = fcs_request_service(priv, (void *)msg,
+					  FCS_REQUEST_TIMEOUT);
+		if (!ret && !priv->status) {
+			msg->command = COMMAND_POLL_SERVICE_STATUS;
+			msg->payload = ps_buf;
+			msg->payload_length = PS_BUF_SIZE;
+			priv->client.receive_cb = fcs_data_callback;
+			ret = fcs_request_service(priv, (void *)msg,
+						  FCS_COMPLETED_TIMEOUT);
+			dev_dbg(dev, "request service ret=%d\n", ret);
+			if (!ret && !priv->status) {
+				if (!priv->kbuf) {
+					dev_err(dev, "failure on kbuf\n");
+					fcs_close_services(priv, ps_buf, NULL);
+					fcs_close_services(priv, s_buf, d_buf);
+					return -EFAULT;
+				}
+				buf_sz = *((unsigned int *)priv->kbuf);
+				data->com_paras.d_decryption.dst_size = buf_sz;
+				data->status = 0;
+				ret = copy_to_user(data->com_paras.d_decryption.dst,
+						   d_buf, buf_sz);
+				if (ret) {
+					dev_err(dev, "failure on copy_to_user\n");
+					fcs_close_services(priv, ps_buf, NULL);
+					fcs_close_services(priv, s_buf, d_buf);
+					return -EFAULT;
+				}
+			} else {
+				data->com_paras.d_decryption.dst = NULL;
+				data->com_paras.d_decryption.dst_size = 0;
+				data->status = priv->status;
+			}
+		} else {
+			data->com_paras.d_decryption.dst = NULL;
+			data->com_paras.d_decryption.dst_size = 0;
+			data->status = priv->status;
+		}
+
+		if (copy_to_user((void __user *)arg, data, sizeof(*data))) {
+			dev_err(dev, "failure on copy_to_user\n");
+			fcs_close_services(priv, ps_buf, NULL);
+			fcs_close_services(priv, s_buf, d_buf);
+			ret = -EFAULT;
+		}
+
+		fcs_close_services(priv, ps_buf, NULL);
+		fcs_close_services(priv, s_buf, d_buf);
+		break;
+	default:
+		dev_warn(dev, "shouldn't be here [0x%x]\n", cmd);
+		break;
+	}
+
+	return ret;
+}
+
+static int fcs_open(struct inode *inode, struct file *file)
+{
+	pr_debug("%s\n", __func__);
+
+	return 0;
+}
+
+static int fcs_close(struct inode *inode, struct file *file)
+{
+
+	pr_debug("%s\n", __func__);
+
+	return 0;
+}
+
+static const struct file_operations fcs_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = fcs_ioctl,
+	.open = fcs_open,
+	.release = fcs_close,
+};
+
+static int fcs_driver_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct intel_fcs_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client.dev = dev;
+	priv->client.receive_cb = NULL;
+	priv->client.priv = priv;
+	priv->status = INVALID_STATUS;
+
+	mutex_init(&priv->lock);
+	priv->chan = stratix10_svc_request_channel_byname(&priv->client,
+							  SVC_CLIENT_FCS);
+	if (IS_ERR(priv->chan)) {
+		dev_err(dev, "couldn't get service channel %s\n",
+			SVC_CLIENT_FCS);
+		return PTR_ERR(priv->chan);
+	}
+
+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+	priv->miscdev.name = "fcs";
+	priv->miscdev.fops = &fcs_fops;
+
+	init_completion(&priv->completion);
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = misc_register(&priv->miscdev);
+	if (ret) {
+		dev_err(dev, "can't register on minor=%d\n",
+			MISC_DYNAMIC_MINOR);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fcs_driver_remove(struct platform_device *pdev)
+{
+	struct intel_fcs_priv *priv = platform_get_drvdata(pdev);
+
+	misc_deregister(&priv->miscdev);
+	stratix10_svc_free_channel(priv->chan);
+
+	return 0;
+}
+
+static struct platform_driver fcs_driver = {
+	.probe = fcs_driver_probe,
+	.remove = fcs_driver_remove,
+	.driver = {
+		.name = "intel-fcs",
+	},
+};
+
+module_platform_driver(fcs_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel FGPA Crypto Services Driver");
+MODULE_AUTHOR("Teh Wen Ping <wen.ping.teh@intel.com>");
+
diff --git a/include/uapi/linux/intel_fcs-ioctl.h b/include/uapi/linux/intel_fcs-ioctl.h
new file mode 100644
index 000000000000..a0483480d22a
--- /dev/null
+++ b/include/uapi/linux/intel_fcs-ioctl.h
@@ -0,0 +1,211 @@ 
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020, Intel Corporation
+ */
+
+#ifndef __INTEL_FCS_IOCTL_H
+#define __INTEL_FCS_IOCTL_H
+
+#include <linux/types.h>
+
+/* the value may need be changed when upstream */
+#define INTEL_FCS_IOCTL		0xC0
+
+/**
+ * enum fcs_vab_img_type - enumeration of image types
+ * @INTEL_FCS_IMAGE_HPS: Image to validate is HPS image
+ * @INTEL_FCS_IMAGE_BITSTREAM: Image to validate is bitstream
+ */
+enum fcs_vab_img_type {
+	INTEL_FCS_IMAGE_HPS = 0,
+	INTEL_FCS_IMAGE_BITSTREAM = 1
+};
+
+/**
+ * enum fcs_certificate_test - enumeration of certificate test
+ * @INTEL_FCS_NO_TEST: Write to eFuses
+ * @INTEL_FCS_TEST: Write to cache, do not write eFuses
+ */
+enum fcs_certificate_test {
+	INTEL_FCS_NO_TEST = 0,
+	INTEL_FCS_TEST = 1
+};
+
+/**
+ * struct intel_fcs_cert_test_word - certificate test word
+ * @test_bit: if set, do not write fuses, write to cache only.
+ * @rsvd: write as 0
+ */
+struct intel_fcs_cert_test_word {
+	uint32_t test_bit:1;
+	uint32_t rsvd:31;
+};
+
+/**
+ * struct fcs_validation_request - validate HPS or bitstream image
+ * @so_type: the type of signed object, 0 for HPS and 1 for bitstream
+ * @src: the source of signed object,
+ *       for HPS, this is the virtual address of the signed source
+ *	 for Bitstream, this is path of the signed source, the default
+ *       path is /lib/firmware
+ * @size: the size of the signed object
+ */
+struct fcs_validation_request {
+	enum fcs_vab_img_type so_type;
+	void *src;
+	uint32_t size;
+};
+
+/**
+ * struct fcs_key_manage_request - Request key management from SDM
+ * @addr: the virtual address of the signed object,
+ * @size: the size of the signed object
+ */
+struct fcs_key_manage_request {
+	void *addr;
+	uint32_t size;
+};
+
+/**
+ * struct fcs_certificate_request - Certificate request to SDM
+ * @test: test bit (1 if want to write to cache instead of fuses)
+ * @addr: the virtual address of the signed object,
+ * @size: the size of the signed object
+ * @c_status: returned certificate status
+ */
+struct fcs_certificate_request {
+	struct intel_fcs_cert_test_word test;
+	void *addr;
+	uint32_t size;
+	uint32_t c_status;
+};
+
+/**
+ * struct fcs_data_encryption - aes data encryption command layout
+ * @src: the virtual address of the input data
+ * @src_size: the size of the unencrypted source
+ * @dst: the virtual address of the output data
+ * @dst_size: the size of the encrypted result
+ */
+struct fcs_data_encryption {
+	void *src;
+	uint32_t src_size;
+	void *dst;
+	uint32_t dst_size;
+};
+
+/**
+ * struct fcs_data_decryption - aes data decryption command layout
+ * @src: the virtual address of the input data
+ * @src_size: the size of the encrypted source
+ * @dst: the virtual address of the output data
+ * @dst_size: the size of the decrypted result
+ */
+struct fcs_data_decryption {
+	void *src;
+	uint32_t src_size;
+	void *dst;
+	uint32_t dst_size;
+};
+
+/**
+ * struct fcs_random_number_gen
+ * @rndm: 8 words of random data.
+ */
+struct fcs_random_number_gen {
+	uint32_t rndm[8];
+};
+
+/**
+ * struct intel_fcs_dev_ioct: common structure passed to Linux
+ *	kernel driver for all commands.
+ * @status: Used for the return code.
+ *      -1 -- operation is not started
+ *       0 -- operation is successfully completed
+ *      non-zero -- operation failed
+ * @s_request: Validation of a bitstream.
+ * @c_request: Certificate request.
+ *      hps_vab: validation of an HPS image
+ *	counter set: burn fuses for new counter values
+ * @gp_data: view the eFuse provisioning state.
+ * @d_encryption: AES encryption (SDOS)
+ * @d_decryption: AES decryption (SDOS)
+ * @rn_gen: random number generator result
+ */
+struct intel_fcs_dev_ioctl {
+	/* used for return status code */
+	int status;
+
+	/* command parameters */
+	union {
+		struct fcs_validation_request	s_request;
+		struct fcs_certificate_request	c_request;
+		struct fcs_key_manage_request	gp_data;
+		struct fcs_data_encryption	d_encryption;
+		struct fcs_data_decryption	d_decryption;
+		struct fcs_random_number_gen	rn_gen;
+	} com_paras;
+};
+
+/**
+ * intel_fcs_command_code - support fpga crypto service commands
+ *
+ * Values are subject to change as a result of upstreaming.
+ *
+ * @INTEL_FCS_DEV_VERSION_CMD:
+ *
+ * @INTEL_FCS_DEV_CERTIFICATE_CMD:
+ *
+ * @INTEL_FCS_DEV_VALIDATE_REQUEST_CMD:
+ *
+ * @INTEL_FCS_DEV_COUNTER_SET_CMD:
+ *
+ * @INTEL_FCS_DEV_SVN_COMMIT_CMD:
+ *
+ * @INTEL_FCS_DEV_DATA_ENCRYPTION_CMD:
+ *
+ * @INTEL_FCS_DEV_DATA_DECRYPTION_CMD:
+ *
+ * @INTEL_FCS_DEV_RANDOM_NUMBER_GEN_CMD:
+ */
+enum intel_fcs_command_code {
+	INTEL_FCS_DEV_COMMAND_NONE = 0,
+	INTEL_FCS_DEV_VERSION_CMD = 1,
+	INTEL_FCS_DEV_CERTIFICATE_CMD = 0xB,
+	INTEL_FCS_DEV_VALIDATE_REQUEST_CMD = 0x78,
+	INTEL_FCS_DEV_COUNTER_SET_CMD,
+	INTEL_FCS_DEV_GET_PROVISION_DATA_CMD = 0x7B,
+	INTEL_FCS_DEV_DATA_ENCRYPTION_CMD = 0x7E,
+	INTEL_FCS_DEV_DATA_DECRYPTION_CMD,
+	INTEL_FCS_DEV_RANDOM_NUMBER_GEN_CMD
+};
+
+#define INTEL_FCS_DEV_VERSION_REQUEST \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_VERSION_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_VALIDATION_REQUEST \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_VALIDATE_REQUEST_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_SEND_CERTIFICATE \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_CERTIFICATE_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_GET_PROVISION_DATA \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_GET_PROVISION_DATA_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_DATA_ENCRYPTION \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_DATA_ENCRYPTION_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_DATA_DECRYPTION \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_DATA_DECRYPTION_CMD, struct intel_fcs_dev_ioctl)
+
+#define INTEL_FCS_DEV_RANDOM_NUMBER_GEN \
+	_IOWR(INTEL_FCS_IOCTL, \
+	      INTEL_FCS_DEV_RANDOM_NUMBER_GEN_CMD, struct intel_fcs_dev_ioctl)
+#endif
+