[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/Kconfig b/src/kernel/linux/v4.14/drivers/char/rpmb/Kconfig
new file mode 100644
index 0000000..c5e6e90
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/Kconfig
@@ -0,0 +1,8 @@
+config RPMB
+	tristate "RPMB partition interface"
+	help
+	  Unified RPMB partition interface for eMMC and UFS.
+	  Provides interface for in kernel security controllers to
+	  access RPMB partition.
+
+	  If unsure, select N.
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/Makefile b/src/kernel/linux/v4.14/drivers/char/rpmb/Makefile
new file mode 100644
index 0000000..ffb0a22
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_RPMB) += rpmb.o
+rpmb-objs += core.o
+ifeq ($(CONFIG_TRUSTONIC_TEE_SUPPORT),y)
+ccflags-y += -I$(srctree)/drivers/char/rpmb/drrpmb_gp/public
+ccflags-y += -I$(srctree)/drivers/misc/mediatek/gud/410a/MobiCoreDriver/public
+ccflags-y += -I$(srctree)/drivers/mmc/core
+ccflags-y += -I$(srctree)/drivers/mmc/card
+rpmb-objs += rpmb-mtk.o
+endif
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/core.c b/src/kernel/linux/v4.14/drivers/char/rpmb/core.c
new file mode 100644
index 0000000..0063dd0
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/core.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/rpmb.h>
+
+static DEFINE_IDA(rpmb_ida);
+
+/**
+ * rpmb_dev_get - increase rpmb device ref counter
+ *
+ * @rdev: rpmb device
+ */
+struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
+{
+	return get_device(&rdev->dev) ? rdev : NULL;
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_get);
+
+/**
+ * rpmb_dev_put - decrease rpmb device ref counter
+ *
+ * @rdev: rpmb device
+ */
+void rpmb_dev_put(struct rpmb_dev *rdev)
+{
+	put_device(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_put);
+
+static int rpmb_request_verify(struct rpmb_dev *rdev, struct rpmb_data *rpmbd)
+{
+	u16 req_type, block_count;
+	struct rpmb_cmd *in_cmd = &rpmbd->icmd;
+	struct rpmb_cmd *out_cmd = &rpmbd->ocmd;
+
+	if (!in_cmd->frames || !in_cmd->nframes ||
+	    !out_cmd->frames || !out_cmd->nframes)
+		return -EINVAL;
+
+	req_type = be16_to_cpu(in_cmd->frames[0].req_resp);
+	block_count = be16_to_cpu(in_cmd->frames[0].block_count);
+
+	if (rpmbd->req_type != req_type) {
+		dev_err(&rdev->dev, "rpmb req type doesn't match 0x%04X = 0x%04X\n",
+			req_type, rpmbd->req_type);
+		return -EINVAL;
+	}
+
+	switch (req_type) {
+	case RPMB_PROGRAM_KEY:
+		dev_dbg(&rdev->dev, "rpmb program key = 0x%1x blk = %d\n",
+			req_type, block_count);
+		break;
+	case RPMB_GET_WRITE_COUNTER:
+		dev_dbg(&rdev->dev, "rpmb get write counter = 0x%1x blk = %d\n",
+			req_type, block_count);
+
+		break;
+	case RPMB_WRITE_DATA:
+		dev_dbg(&rdev->dev, "rpmb write data = 0x%1x blk = %d\n",
+			req_type, block_count);
+
+		if (rdev->ops->reliable_wr_cnt &&
+		    block_count > rdev->ops->reliable_wr_cnt) {
+			dev_err(&rdev->dev, "rpmb write data: block count %u > reliable wr count %u\n",
+				block_count, rdev->ops->reliable_wr_cnt);
+			return -EINVAL;
+		}
+
+		if (block_count > in_cmd->nframes) {
+			dev_err(&rdev->dev, "rpmb write data: block count %u > in frame count %u\n",
+				block_count, in_cmd->nframes);
+			return -EINVAL;
+		}
+		break;
+	case RPMB_READ_DATA:
+		dev_dbg(&rdev->dev, "rpmb read data = 0x%1x blk = %d\n",
+			req_type, block_count);
+
+		if (block_count > out_cmd->nframes) {
+			dev_err(&rdev->dev, "rpmb read data: block count %u > out frame count %u\n",
+				block_count, out_cmd->nframes);
+			return -EINVAL;
+		}
+		break;
+	case RPMB_RESULT_READ:
+		/* Internal command not supported */
+		dev_err(&rdev->dev, "NOTSUPPORTED rpmb resut read = 0x%1x blk = %d\n",
+			req_type, block_count);
+		return -EOPNOTSUPP;
+
+	default:
+		dev_err(&rdev->dev, "Error rpmb invalid command = 0x%1x blk = %d\n",
+			req_type, block_count);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * rpmb_cmd_seq - send RPMB command sequence
+ *
+ * @rdev: rpmb device
+ * @cmds: rpmb command list
+ * @ncmds: number of commands
+ *
+ * Return: 0 on success
+ *         -EINVAL on wrong parameters
+ *         -EOPNOTSUPP if device doesn't support the requested operation
+ *         < 0 if the operation fails
+ */
+int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds)
+{
+	int err;
+
+	if (!rdev || !cmds || !ncmds)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	if (rdev->ops && rdev->ops->cmd_seq)
+		err = rdev->ops->cmd_seq(rdev->dev.parent, cmds, ncmds);
+	else
+		err = -EOPNOTSUPP;
+	mutex_unlock(&rdev->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_cmd_seq);
+
+static void rpmb_cmd_set(struct rpmb_cmd *cmd, u32 flags,
+			 struct rpmb_frame *frames, u32 nframes)
+{
+	cmd->flags = flags;
+	cmd->frames = frames;
+	cmd->nframes = nframes;
+}
+
+/**
+ * rpmb_cmd_req_write - setup cmd request write sequence
+ *
+ * @cmd: cmd sequence
+ * @rpmbd: rpmb request data
+ * @cnt_in: number of input frames
+ * @cnt_out: number of output frames
+ *
+ * Return: 3 - number of commands in the sequence
+ */
+static u32 rpmb_cmd_req_write(struct rpmb_cmd *cmd, struct rpmb_data *rpmbd,
+			      u32 cnt_in, u32 cnt_out)
+{
+	struct rpmb_frame *res_frame;
+
+	rpmb_cmd_set(&cmd[0], RPMB_F_WRITE | RPMB_F_REL_WRITE,
+		     rpmbd->icmd.frames, cnt_in);
+	res_frame = rpmbd->ocmd.frames;
+	memset(res_frame, 0, sizeof(*res_frame));
+	res_frame->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+	rpmb_cmd_set(&cmd[1], RPMB_F_WRITE, res_frame, 1);
+
+	rpmb_cmd_set(&cmd[2], 0, rpmbd->ocmd.frames, cnt_out);
+
+	return  3;
+}
+
+/**
+ * rpmb_cmd_req_read - setup cmd request read sequence
+ *
+ * @cmd: cmd sequence
+ * @rpmbd: rpmb request data
+ * @cnt_in: number of input frames
+ * @cnt_out: number of output frames
+ *
+ * Return: 2 - number of commands in the sequence
+ */
+static u32 rpmb_cmd_req_read(struct rpmb_cmd *cmd, struct rpmb_data *rpmbd,
+			     u32 cnt_in, u32 cnt_out)
+{
+	rpmb_cmd_set(&cmd[0], RPMB_F_WRITE, rpmbd->icmd.frames, cnt_in);
+	rpmb_cmd_set(&cmd[1], 0, rpmbd->ocmd.frames, cnt_out);
+
+	return 2;
+}
+
+/**
+ * rpmb_cmd_req - send rpmb request command
+ *
+ * @rdev: rpmb device
+ * @rpmbd: rpmb request data
+ *
+ * Return: 0 on success
+ *         -EINVAL on wrong parameters
+ *         -EOPNOTSUPP if device doesn't support the requested operation
+ *         < 0 if the operation fails
+ */
+int rpmb_cmd_req(struct rpmb_dev *rdev, struct rpmb_data *rpmbd)
+{
+	struct rpmb_cmd cmd[3];
+	u32 cnt_in, cnt_out;
+	u32 ncmds;
+	u16 type;
+	int ret;
+
+	if (!rdev || !rpmbd)
+		return -EINVAL;
+
+	ret = rpmb_request_verify(rdev, rpmbd);
+	if (ret)
+		return ret;
+
+	if (!rdev->ops || !rdev->ops->cmd_seq)
+		return -EOPNOTSUPP;
+
+	cnt_in = rpmbd->icmd.nframes;
+	cnt_out = rpmbd->ocmd.nframes;
+	type = rpmbd->req_type;
+	switch (type) {
+	case RPMB_PROGRAM_KEY:
+		ncmds = rpmb_cmd_req_write(cmd, rpmbd, 1, 1);
+		break;
+
+	case RPMB_WRITE_DATA:
+		ncmds = rpmb_cmd_req_write(cmd, rpmbd, cnt_in, cnt_out);
+		break;
+
+	case RPMB_GET_WRITE_COUNTER:
+		ncmds = rpmb_cmd_req_read(cmd, rpmbd, 1, 1);
+		break;
+
+	case RPMB_READ_DATA:
+		ncmds = rpmb_cmd_req_read(cmd, rpmbd, cnt_in, cnt_out);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&rdev->lock);
+	ret = rdev->ops->cmd_seq(rdev->dev.parent, cmd, ncmds);
+	mutex_unlock(&rdev->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rpmb_cmd_req);
+
+static void rpmb_dev_release(struct device *dev)
+{
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+
+	ida_simple_remove(&rpmb_ida, rdev->id);
+	kfree(rdev);
+}
+
+struct class rpmb_class = {
+	.name = "rpmb",
+	.owner = THIS_MODULE,
+	.dev_release = rpmb_dev_release,
+};
+EXPORT_SYMBOL(rpmb_class);
+
+/**
+ * rpmb_dev_find_device - return first matching rpmb device
+ *
+ * @data: data for the match function
+ * @match: the matching function
+ *
+ * Return: matching rpmb device or NULL on failure
+ */
+struct rpmb_dev *rpmb_dev_find_device(void *data,
+				      int (*match)(struct device *dev,
+						   const void *data))
+{
+	struct device *dev;
+
+	dev = class_find_device(&rpmb_class, NULL, data, match);
+
+	return dev ? to_rpmb_dev(dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_find_device);
+
+static int match_by_type(struct device *dev, const void *data)
+{
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+	enum rpmb_type *type = (enum rpmb_type *)data;
+
+	return (*type == RPMB_TYPE_ANY || rdev->ops->type == *type);
+}
+
+/**
+ * rpmb_dev_get_by_type - return first registered rpmb device
+ *      with matching type.
+ *      If run with RPMB_TYPE_ANY the first an probably only
+ *      device is returned
+ *
+ * @type: rpbm underlying device type
+ *
+ * Return: matching rpmb device or NULL/ERR_PTR on failure
+ */
+struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type)
+{
+	if (type > RPMB_TYPE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	return rpmb_dev_find_device(&type, match_by_type);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_get_by_type);
+
+static int match_by_parent(struct device *dev, const void *data)
+{
+	const struct device *parent = data;
+
+	return (parent && dev->parent == parent);
+}
+
+/**
+ * rpmb_dev_find_by_device - retrieve rpmb device from the parent device
+ *
+ * @parent: parent device of the rpmb device
+ *
+ * Return: NULL if there is no rpmb device associated with the parent device
+ */
+struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent)
+{
+	if (!parent)
+		return NULL;
+
+	return rpmb_dev_find_device(parent, match_by_parent);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device);
+
+/**
+ * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem
+ *
+ * @dev: parent device of the rpmb device
+ */
+int rpmb_dev_unregister(struct device *dev)
+{
+	struct rpmb_dev *rdev;
+
+	if (!dev)
+		return -EINVAL;
+
+	rdev = rpmb_dev_find_by_device(dev);
+	if (!rdev) {
+		dev_warn(dev, "no disk found %s\n", dev_name(dev->parent));
+		return -ENODEV;
+	}
+
+	rpmb_dev_put(rdev);
+
+	mutex_lock(&rdev->lock);
+	device_del(&rdev->dev);
+	mutex_unlock(&rdev->lock);
+
+	rpmb_dev_put(rdev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
+
+/**
+ * rpmb_dev_register - register RPMB partition with the RPMB subsystem
+ *
+ * @dev: storage device of the rpmb device
+ * @ops: device specific operations
+ */
+struct rpmb_dev *rpmb_dev_register(struct device *dev,
+				   const struct rpmb_ops *ops)
+{
+	struct rpmb_dev *rdev;
+	int id;
+	int ret;
+
+	if (!dev || !ops)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->cmd_seq)
+		return ERR_PTR(-EINVAL);
+
+	if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+	if (!rdev)
+		return ERR_PTR(-ENOMEM);
+
+	id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
+	if (id < 0) {
+		ret = id;
+		goto exit;
+	}
+
+	mutex_init(&rdev->lock);
+	rdev->ops = ops;
+	rdev->id = id;
+
+	dev_set_name(&rdev->dev, "rpmb%d", id);
+	rdev->dev.class = &rpmb_class;
+	rdev->dev.parent = dev;
+	ret = device_register(&rdev->dev);
+	if (ret)
+		goto exit;
+
+	dev_dbg(&rdev->dev, "registered disk\n");
+
+	return rdev;
+
+exit:
+	if (id >= 0)
+		ida_simple_remove(&rpmb_ida, id);
+	kfree(rdev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_register);
+
+static int __init rpmb_init(void)
+{
+	ida_init(&rpmb_ida);
+	class_register(&rpmb_class);
+	return 0;
+}
+
+static void __exit rpmb_exit(void)
+{
+	class_unregister(&rpmb_class);
+	ida_destroy(&rpmb_ida);
+}
+
+subsys_initcall(rpmb_init);
+module_exit(rpmb_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("RPMB class");
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/dci.h b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/dci.h
new file mode 100644
index 0000000..5bcf5c5
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/dci.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+/**
+ * @file   dci.h
+ * @brief  Contains DCI (Driver Control
+ * Interface) definitions and data structures
+ *
+ */
+
+#ifndef __DCI_H__
+#define __DCI_H__
+
+
+typedef uint32_t dciCommandId_t;
+typedef uint32_t dciResponseId_t;
+typedef uint32_t dciReturnCode_t;
+
+/**< Responses have bit 31 set */
+#define RSP_ID_MASK (1U << 31)
+#define RSP_ID(cmdId) (((uint32_t)(cmdId)) | RSP_ID_MASK)
+#define IS_CMD(cmdId) ((((uint32_t)(cmdId)) & RSP_ID_MASK) == 0)
+#define IS_RSP(cmdId) ((((uint32_t)(cmdId)) & RSP_ID_MASK) == RSP_ID_MASK)
+
+/**
+ * Return codes of driver commands.
+ */
+#define RET_OK                    0
+#define RET_ERR_UNKNOWN_CMD       1
+#define RET_ERR_NOT_SUPPORTED     2
+#define RET_ERR_INTERNAL_ERROR    3
+/* ... add more error codes when needed */
+
+/**
+ * DCI command header.
+ */
+struct dciCommandHeader_t {
+	dciCommandId_t commandId; /**< Command ID */
+};
+
+/**
+ * DCI response header.
+ */
+struct dciResponseHeader_t {
+	dciResponseId_t     responseId; /**< Response ID (Cmd_Id|RSP_ID_MASK) */
+	dciReturnCode_t     returnCode; /**< Return code of command */
+};
+
+#endif
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_Api.h b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_Api.h
new file mode 100644
index 0000000..bc4da03
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_Api.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+/**
+ * @file   drrpmb_Api.h
+ * @brief  Contains DCI command definitions and data structures
+ *
+ */
+
+#ifndef __DRRPMBAPI_H__
+#define __DRRPMBAPI_H__
+
+#include "dci.h"
+
+
+/*
+ * Command ID's
+ */
+#define DCI_RPMB_CMD_READ_DATA      1
+#define DCI_RPMB_CMD_GET_WCNT       2
+#define DCI_RPMB_CMD_WRITE_DATA     3
+#define DCI_RPMB_CMD_PROGRAM_KEY    4
+
+
+/*... add more command ids when needed */
+
+/**
+ * command message.
+ *
+ * @param len Length of the data to process.
+ * @param data Data to be processed
+ */
+struct cmd_t {
+	struct dciCommandHeader_t header;     /**< Command header */
+	uint32_t            len;        /**< Length of data to process */
+};
+
+/**
+ * Response structure
+ */
+struct rsp_t {
+	struct dciResponseHeader_t header;     /**< Response header */
+	uint32_t            len;
+};
+
+
+/*
+ * Alternative access flow to improve performance. (this is customization)
+ */
+#define RPMB_MULTI_BLOCK_ACCESS 1
+
+#if RPMB_MULTI_BLOCK_ACCESS
+/* 16 blocks per request. */
+#define MAX_RPMB_TRANSFER_BLK (16U)
+#define MAX_RPMB_REQUEST_SIZE (512U*MAX_RPMB_TRANSFER_BLK) /* 8KB */
+#else
+/* 1 block per request. */
+#define MAX_RPMB_TRANSFER_BLK (1U)
+#define MAX_RPMB_REQUEST_SIZE (512U*MAX_RPMB_TRANSFER_BLK) /* 512byte */
+#endif
+
+struct rpmb_req_t {
+	uint8_t frame[MAX_RPMB_REQUEST_SIZE];
+	uint32_t frameLen;
+	uint16_t type;
+	uint16_t addr;
+	uint16_t blks;
+	uint16_t result;
+};
+
+/*
+ * DCI message data.
+ */
+struct dciMessage_t {
+	union {
+		struct cmd_t  command;
+		struct rsp_t  response;
+	};
+
+	struct rpmb_req_t request;
+
+};
+
+/*
+ * Driver UUID. Update accordingly after reserving UUID
+ */
+#define RPMB_UUID { { 7, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+#endif
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_gp_Api.h b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_gp_Api.h
new file mode 100644
index 0000000..3851955
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/drrpmb_gp/public/drrpmb_gp_Api.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+/**
+ * @file   drrpmb_Api.h
+ * @brief  Contains DCI command definitions and data structures
+ *
+ */
+
+#ifndef __DRRPMBGPAPI_H__
+#define __DRRPMBGPAPI_H__
+
+
+/*
+ * Driver UUID. Update accordingly after reserving UUID
+ */
+#define RPMB_GP_UUID { { 7, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+#endif
+
diff --git a/src/kernel/linux/v4.14/drivers/char/rpmb/rpmb-mtk.c b/src/kernel/linux/v4.14/drivers/char/rpmb/rpmb-mtk.c
new file mode 100644
index 0000000..659bb60
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/char/rpmb/rpmb-mtk.c
@@ -0,0 +1,1328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/random.h>
+#include <linux/memory.h>
+#include <linux/io.h>
+#include <linux/proc_fs.h>
+#include <crypto/hash.h>
+
+#include <linux/rpmb.h>
+
+#include <linux/scatterlist.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include "queue.h"
+#include <linux/of.h>
+
+/* #define __RPMB_MTK_DEBUG_MSG */
+/* #define __RPMB_MTK_DEBUG_HMAC_VERIFY */
+
+/* TEE usage */
+#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
+#include "mobicore_driver_api.h"
+#include "drrpmb_gp_Api.h"
+#include "drrpmb_Api.h"
+
+static struct mc_uuid_t rpmb_gp_uuid = RPMB_GP_UUID;
+static struct mc_session_handle rpmb_gp_session = {0};
+static u32 rpmb_gp_devid = MC_DEVICE_ID_DEFAULT;
+static struct dciMessage_t *rpmb_gp_dci;
+#endif
+
+/*
+ * Dummy definition for MAX_RPMB_TRANSFER_BLK.
+ *
+ * For UFS RPMB driver, MAX_RPMB_TRANSFER_BLK will be always
+ * used however it will NOT be defined in projects w/o Security
+ * OS. Thus we add a dummy definition here to avoid build errors.
+ *
+ * For eMMC RPMB driver, MAX_RPMB_TRANSFER_BLK will be used
+ * only if RPMB_MULTI_BLOCK_ACCESS is defined. thus
+ * build error will not happen on projects w/o Security OS.
+ *
+ * NOTE: This dummy definition shall be located after
+ *       #include "drrpmb_Api.h" and
+ *       #include "rpmb-mtk.h"
+ *       since MAX_RPMB_TRANSFER_BLK will be defined in those
+ *       header files if security OS is enabled.
+ */
+#ifndef MAX_RPMB_TRANSFER_BLK
+#define MAX_RPMB_TRANSFER_BLK (1U)
+#endif
+
+#define RPMB_NAME "rpmb"
+
+//extern struct msdc_host *mtk_msdc_host[];
+
+#define RPMB_IOCTL_PROGRAM_KEY  1
+#define RPMB_IOCTL_WRITE_DATA   3
+#define RPMB_IOCTL_READ_DATA    4
+
+struct rpmb_ioc_param {
+	unsigned char *keybytes;
+	unsigned char *databytes;
+	unsigned int  data_len;
+	unsigned short addr;
+	unsigned char *hmac;
+	unsigned int hmac_len;
+};
+
+#define RPMB_SZ_MAC   32U
+#define RPMB_SZ_DATA  256U
+#define RPMB_SZ_STUFF 196U
+#define RPMB_SZ_NONCE 16U
+
+struct s_rpmb {
+	unsigned char stuff[RPMB_SZ_STUFF];
+	unsigned char mac[RPMB_SZ_MAC];
+	unsigned char data[RPMB_SZ_DATA];
+	unsigned char nonce[RPMB_SZ_NONCE];
+	unsigned int write_counter;
+	unsigned short address;
+	unsigned short block_count;
+	unsigned short result;
+	unsigned short request;
+};
+
+enum {
+	RPMB_SUCCESS = 0,
+	RPMB_HMAC_ERROR,
+	RPMB_RESULT_ERROR,
+	RPMB_WC_ERROR,
+	RPMB_NONCE_ERROR,
+	RPMB_ALLOC_ERROR,
+	RPMB_TRANSFER_NOT_COMPLETE,
+};
+
+#define RPMB_REQ               1       /* RPMB request mark */
+#define RPMB_RESP              (1 << 1)/* RPMB response mark */
+#define RPMB_AVAILABLE_SECTORS 8       /* 4K page size */
+
+#define RPMB_TYPE_BEG          510
+#define RPMB_RES_BEG           508
+#define RPMB_BLKS_BEG          506
+#define RPMB_ADDR_BEG          504
+#define RPMB_WCOUNTER_BEG      500
+
+#define RPMB_NONCE_BEG         484
+#define RPMB_DATA_BEG          228
+#define RPMB_MAC_BEG           196
+
+struct emmc_rpmb_req {
+	__u16 type;                     /* RPMB request type */
+	__u16 *result;                  /* response or request result */
+	__u16 blk_cnt;                  /* Number of blocks(half sector 256B) */
+	__u16 addr;                     /* data address */
+	__u32 *wc;                      /* write counter */
+	__u8 *nonce;                    /* Ramdom number */
+	__u8 *data;                     /* Buffer of the user data */
+	__u8 *mac;                      /* Message Authentication Code */
+	__u8 *data_frame;
+};
+
+#define DEFAULT_HANDLES_NUM (64)
+#define MAX_OPEN_SESSIONS (0xffffffffU - 1)
+
+/* Debug message event */
+#define DBG_EVT_NONE (0) /* No event */
+#define DBG_EVT_CMD  (1U << 0)/* SEC CMD related event */
+#define DBG_EVT_FUNC (1U << 1U)/* SEC function event */
+#define DBG_EVT_INFO (1U << 2U)/* SEC information event */
+#define DBG_EVT_WRN  (1U << 30U) /* Warning event */
+#define DBG_EVT_ERR  (0x80000000U) /* Error event, 1 << 31 */
+#ifdef __RPMB_MTK_DEBUG_MSG
+#define DBG_EVT_DBG_INFO  (DBG_EVT_ERR) /* Error event */
+#else
+#define DBG_EVT_DBG_INFO  (1U << 2U) /* Information event */
+#endif
+#define DBG_EVT_ALL  (0xffffffffU)
+
+static u32 dbg_evt = DBG_EVT_ERR;
+#define DBG_EVT_MASK (dbg_evt)
+
+#define MSG(evt, fmt, args...) \
+do {\
+	if (((DBG_EVT_##evt) & DBG_EVT_MASK) != 0U) { \
+		(void)(pr_notice("[%s] "fmt, RPMB_NAME, ##args)); \
+	} \
+} while (false)
+
+static struct task_struct *open_th;
+static struct task_struct *rpmb_gp_Dci_th;
+
+
+static struct cdev rpmb_cdev;
+
+static int hmac_sha256(const char *keybytes, u32 klen, const char *str,
+			size_t len, u8 *hmac)
+{
+	struct shash_desc *shash;
+	struct crypto_shash *hmacsha256 = crypto_alloc_shash("hmac(sha256)",
+							     0, 0);
+	size_t size = 0;
+	int err = 0;
+	int nbytes = (int)len;
+
+	if (IS_ERR(hmacsha256))
+		return -1;
+
+	size = sizeof(struct shash_desc) + crypto_shash_descsize(hmacsha256);
+
+	shash = kmalloc(size, GFP_KERNEL);
+	if (shash == NULL) {
+		err = -1;
+		goto malloc_err;
+	}
+	shash->tfm = hmacsha256;
+	shash->flags = 0x0;
+
+	err = crypto_shash_setkey(hmacsha256, keybytes, klen);
+	if (err != 0) {
+		err = -1;
+		goto hash_err;
+	}
+
+	err = crypto_shash_init(shash);
+	if (err != 0) {
+		err = -1;
+		goto hash_err;
+	}
+
+	if (nbytes != crypto_shash_update(shash, str, (unsigned int)nbytes)) {
+		err = -1;
+		goto hash_err;
+	}
+	err = crypto_shash_final(shash, hmac);
+
+hash_err:
+	kfree(shash);
+malloc_err:
+	crypto_free_shash(hmacsha256);
+
+	return err;
+}
+
+#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
+unsigned char rpmb_key[32] = {
+	0x64, 0x76, 0xEE, 0xF0, 0xF1, 0x6B, 0x30, 0x47,
+	0xE9, 0x79, 0x31, 0x58, 0xF6, 0x42, 0xDA, 0x46,
+	0xF7, 0x3B, 0x53, 0xFD, 0xC5, 0xF8, 0x84, 0xCE,
+	0x03, 0x73, 0x15, 0xBC, 0x54, 0x47, 0xD4, 0x6A
+};
+
+static int rpmb_cal_hmac(struct rpmb_frame *frame, int blk_cnt,
+			u8 *key, u8 *key_mac)
+{
+	int i;
+	u8 *buf, *buf_start;
+
+	buf = buf_start = kzalloc(284 * blk_cnt, 0);
+
+	for (i = 0; i < blk_cnt; i++) {
+		memcpy(buf, frame[i].data, 284);
+		buf += 284;
+	}
+
+	if (hmac_sha256(key, 32, buf_start, 284UL * blk_cnt, key_mac) != 0)
+		MSG(ERR, "hmac_sha256() return error!\n");
+
+	kfree(buf_start);
+
+	return 0;
+}
+#endif
+
+static void rpmb_dump_frame(u8 *data_frame)
+{
+	MSG(DBG_INFO, "mac, frame[196] = 0x%x\n", data_frame[196]);
+	MSG(DBG_INFO, "mac, frame[197] = 0x%x\n", data_frame[197]);
+	MSG(DBG_INFO, "mac, frame[198] = 0x%x\n", data_frame[198]);
+	MSG(DBG_INFO, "data,frame[228] = 0x%x\n", data_frame[228]);
+	MSG(DBG_INFO, "data,frame[229] = 0x%x\n", data_frame[229]);
+	MSG(DBG_INFO, "nonce, frame[484] = 0x%x\n", data_frame[484]);
+	MSG(DBG_INFO, "nonce, frame[485] = 0x%x\n", data_frame[485]);
+	MSG(DBG_INFO, "nonce, frame[486] = 0x%x\n", data_frame[486]);
+	MSG(DBG_INFO, "nonce, frame[487] = 0x%x\n", data_frame[487]);
+	MSG(DBG_INFO, "wc, frame[500] = 0x%x\n", data_frame[500]);
+	MSG(DBG_INFO, "wc, frame[501] = 0x%x\n", data_frame[501]);
+	MSG(DBG_INFO, "wc, frame[502] = 0x%x\n", data_frame[502]);
+	MSG(DBG_INFO, "wc, frame[503] = 0x%x\n", data_frame[503]);
+	MSG(DBG_INFO, "addr, frame[504] = 0x%x\n", data_frame[504]);
+	MSG(DBG_INFO, "addr, frame[505] = 0x%x\n", data_frame[505]);
+	MSG(DBG_INFO, "blkcnt,frame[506] = 0x%x\n", data_frame[506]);
+	MSG(DBG_INFO, "blkcnt,frame[507] = 0x%x\n", data_frame[507]);
+	MSG(DBG_INFO, "result, frame[508] = 0x%x\n", data_frame[508]);
+	MSG(DBG_INFO, "result, frame[509] = 0x%x\n", data_frame[509]);
+	MSG(DBG_INFO, "type, frame[510] = 0x%x\n", data_frame[510]);
+	MSG(DBG_INFO, "type, frame[511] = 0x%x\n", data_frame[511]);
+}
+
+static struct rpmb_frame *rpmb_alloc_frames(unsigned int cnt)
+{
+	return kzalloc(sizeof(struct rpmb_frame) * cnt, 0);
+}
+
+static int rpmb_req_get_wc(u8 *keybytes, u32 *wc, u8 *frame)
+{
+	struct rpmb_data rpmbdata;
+	struct rpmb_dev *mmc_rpmb;
+	u8 nonce[RPMB_SZ_NONCE] = {0};
+	u8 hmac[RPMB_SZ_MAC];
+	int ret, i;
+
+	MSG(INFO, "%s start!!!\n", __func__);
+
+	mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
+
+	do {
+		/*
+		 * Initial frame buffers
+		 */
+
+		if (frame != NULL) {
+
+			/*
+			 * Use external frame if possible.
+			 * External frame shall have below field ready,
+			 *
+			 * nonce
+			 * req_resp
+			 */
+			rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
+			rpmbdata.ocmd.frames = (struct rpmb_frame *)frame;
+
+		} else {
+
+			rpmbdata.icmd.frames = rpmb_alloc_frames(1);
+
+			if (rpmbdata.icmd.frames == NULL)
+				return RPMB_ALLOC_ERROR;
+
+			rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
+
+			if (rpmbdata.ocmd.frames == NULL)
+				return RPMB_ALLOC_ERROR;
+		}
+
+		/*
+		 * Prepare frame contents.
+		 *
+		 * Input frame (in view of device) only needs nonce
+		 */
+
+		rpmbdata.req_type = RPMB_GET_WRITE_COUNTER;
+		rpmbdata.icmd.nframes = 1;
+
+		/* Fill-in essential field in self-prepared frame */
+
+		if (frame == NULL) {
+			get_random_bytes(nonce, (int)RPMB_SZ_NONCE);
+			rpmbdata.icmd.frames->req_resp =
+					cpu_to_be16(RPMB_GET_WRITE_COUNTER);
+			memcpy(rpmbdata.icmd.frames->nonce, nonce,
+				RPMB_SZ_NONCE);
+		}
+
+		/* Output frame (in view of device) */
+
+		rpmbdata.ocmd.nframes = 1;
+
+		ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
+
+		if (ret != 0) {
+			MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
+			    __func__, ret);
+			break;
+		}
+
+		/* Verify HMAC only if key is available */
+
+		if (keybytes != NULL) {
+			if (strlen(keybytes) != 32UL) {
+				MSG(ERR, "%s, error rpmb key len = 0x%x\n",
+				    __func__, (unsigned int)strlen(keybytes));
+				ret = RPMB_WC_ERROR;
+				break;
+			}
+
+			/*
+			 * Authenticate response write counter frame.
+			 */
+			if (hmac_sha256(keybytes, 32,
+					rpmbdata.ocmd.frames->data,
+					284UL, hmac) != 0)
+				MSG(ERR, "hmac_sha256() return error!\n");
+
+			if (memcmp(hmac, rpmbdata.ocmd.frames->key_mac,
+				   RPMB_SZ_MAC) != 0) {
+				MSG(ERR, "%s, hmac compare error!!!\n",
+				    __func__);
+				ret = RPMB_HMAC_ERROR;
+			}
+
+			/*
+			 * DEVICE ISSUE:
+			 * We found some devices will return hmac vale with
+			 * all zeros.
+			 * For this kind of device, bypass hmac comparison.
+			 */
+			if (ret == RPMB_HMAC_ERROR) {
+				for (i = 0; i < 32; i++) {
+					if (rpmbdata.ocmd.frames->key_mac[i] !=
+					    0U) {
+						MSG(ERR,
+						    "%s, dev hmac not NULL!\n",
+						    __func__);
+						break;
+					}
+				}
+
+				MSG(ERR,
+				    "%s, device hmac has all zero, bypassed!\n",
+				    __func__);
+				ret = RPMB_SUCCESS;
+			}
+		}
+
+		/*
+		 * Verify nonce and result only in self-prepared frame
+		 * External frame shall be verified by frame provider,
+		 * for example, TEE.
+		 */
+		if (frame == NULL) {
+			if (memcmp(nonce, rpmbdata.ocmd.frames->nonce,
+				   RPMB_SZ_NONCE) != 0) {
+				MSG(ERR, "%s, nonce compare error!!!\n",
+				    __func__);
+				rpmb_dump_frame((u8 *)rpmbdata.ocmd.frames);
+				ret = RPMB_NONCE_ERROR;
+				break;
+			}
+
+			if (rpmbdata.ocmd.frames->result != 0U) {
+				MSG(ERR, "%s, result error!!! (0x%x)\n",
+				    __func__,
+				    cpu_to_be16(rpmbdata.ocmd.frames->result));
+				ret = RPMB_RESULT_ERROR;
+				break;
+			}
+		}
+
+		if (wc != NULL) {
+			*wc = cpu_to_be32(rpmbdata.ocmd.frames->write_counter);
+			MSG(DBG_INFO, "%s: wc = %d (0x%x)\n",
+			    __func__, *wc, *wc);
+		}
+	} while (false);
+
+	MSG(DBG_INFO, "%s: end\n", __func__);
+
+	if (frame == NULL) {
+		kfree(rpmbdata.icmd.frames);
+		kfree(rpmbdata.ocmd.frames);
+	}
+
+	return ret;
+}
+
+static int rpmb_req_read_data(u8 *frame, u32 blk_cnt)
+{
+	struct rpmb_data rpmbdata;
+	struct rpmb_dev *mmc_rpmb;
+	int ret;
+
+	mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
+
+	MSG(DBG_INFO, "%s: blk_cnt: %d\n", __func__, blk_cnt);
+
+	rpmbdata.req_type = RPMB_READ_DATA;
+	rpmbdata.icmd.nframes = 1;
+	rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
+
+	/*
+	 * We need to fill-in block_count by ourselves for UFS case.
+	 * TEE does not fill-in this field because eMMC spec specifiy it as 0.
+	 */
+	rpmbdata.icmd.frames->block_count = cpu_to_be16((u16)blk_cnt);
+
+	rpmbdata.ocmd.nframes = blk_cnt;
+	rpmbdata.ocmd.frames = (struct rpmb_frame *)frame;
+
+	ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
+
+	if (ret != 0)
+		MSG(ERR, "%s: rpmb_cmd_req IO error, ret %d (0x%x)\n",
+		    __func__, ret, ret);
+
+	MSG(DBG_INFO, "%s: result 0x%x\n", __func__,
+	    rpmbdata.ocmd.frames->result);
+
+	MSG(DBG_INFO, "%s: ret 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+static int rpmb_req_write_data(u8 *frame, u32 blk_cnt)
+{
+	struct rpmb_data rpmbdata;
+	struct rpmb_dev *mmc_rpmb;
+	int ret;
+#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
+	u8 *key_mac;
+#endif
+
+	mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
+
+	MSG(DBG_INFO, "%s: blk_cnt: %d\n", __func__, blk_cnt);
+
+	/*
+	 * Alloc output frame to avoid overwriting input frame buffer
+	 * provided by TEE
+	 */
+	rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
+
+	if (rpmbdata.ocmd.frames == NULL)
+		return RPMB_ALLOC_ERROR;
+
+	rpmbdata.ocmd.nframes = 1;
+
+	rpmbdata.req_type = RPMB_WRITE_DATA;
+	rpmbdata.icmd.nframes = blk_cnt;
+	rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
+
+#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
+	key_mac = kzalloc(32, 0);
+
+	rpmb_cal_hmac((struct rpmb_frame *)frame, blk_cnt, rpmb_key, key_mac);
+
+	if (memcmp(key_mac, ((struct rpmb_frame *)frame)[blk_cnt - 1].key_mac,
+		   32)) {
+		MSG(ERR, "%s, Key Mac is NOT matched!\n", __func__);
+		kfree(key_mac);
+		ret = 1;
+		goto out;
+	} else
+		MSG(ERR, "%s, Key Mac check passed.\n", __func__);
+
+	kfree(key_mac);
+#endif
+
+	ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
+
+	if (ret != 0)
+		MSG(ERR, "%s: rpmb_cmd_req IO error, ret %d (0x%x)\n",
+		    __func__, ret, ret);
+
+	/*
+	 * Microtrust TEE will check write counter in the first frame,
+	 * thus we copy response frame to the first frame.
+	 */
+	memcpy(frame, rpmbdata.ocmd.frames, 512);
+
+	MSG(DBG_INFO, "%s: result 0x%x\n", __func__,
+	    rpmbdata.ocmd.frames->result);
+
+	kfree(rpmbdata.ocmd.frames);
+
+	MSG(DBG_INFO, "%s: ret 0x%x\n", __func__, ret);
+
+#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
+out:
+#endif
+
+	return ret;
+}
+
+
+static int rpmb_req_ioctl_write_data(struct rpmb_ioc_param *param)
+{
+	struct rpmb_data rpmbdata;
+	struct rpmb_dev *mmc_rpmb;
+	u32 i, tran_size, left_size = param->data_len;
+	u32 wc = 0xFFFFFFFFU;
+	u16 iCnt, total_blkcnt, tran_blkcnt, left_blkcnt;
+	u16 blkaddr;
+	u8 hmac[RPMB_SZ_MAC];
+	u8 *dataBuf, *dataBuf_start, *data_for_hmac;
+	size_t size_for_hmac;
+	int ret = 0;
+	u8 user_param_data;
+
+	MSG(DBG_INFO, "%s start!!!\n", __func__);
+
+	ret = get_user(user_param_data, param->databytes);
+	if (ret != 0)
+		return -EFAULT;
+
+	ret = get_user(user_param_data, param->keybytes);
+	if (ret != 0)
+		return -EFAULT;
+
+	mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
+
+	i = 0;
+	tran_blkcnt = 0;
+	dataBuf = NULL;
+	dataBuf_start = NULL;
+
+	total_blkcnt = (u16)(((param->data_len % RPMB_SZ_DATA) != 0U) ?
+				(param->data_len / RPMB_SZ_DATA + 1U) :
+				(param->data_len / RPMB_SZ_DATA));
+	left_blkcnt = total_blkcnt;
+
+	/*
+	 * For RPMB write data, the elements we need in the input data frame is
+	 * 1. address.
+	 * 2. write counter.
+	 * 3. data.
+	 * 4. block count.
+	 * 5. MAC
+	 */
+
+	blkaddr = param->addr;
+
+	while (left_blkcnt > 0U) {
+
+		if (left_blkcnt >= MAX_RPMB_TRANSFER_BLK)
+			tran_blkcnt = MAX_RPMB_TRANSFER_BLK;
+		else
+			tran_blkcnt = left_blkcnt;
+
+		MSG(DBG_INFO, "%s, total_blkcnt = 0x%x, tran_blkcnt = 0x%x\n",
+		    __func__, left_blkcnt, tran_blkcnt);
+
+		ret = rpmb_req_get_wc(param->keybytes, &wc, NULL);
+		if (ret != 0) {
+			MSG(ERR, "%s, rpmb_req_get_wc error!!!(0x%x)\n",
+			    __func__, ret);
+			return ret;
+		}
+
+		/*
+		 * Initial frame buffers
+		 */
+
+		rpmbdata.icmd.frames = rpmb_alloc_frames(tran_blkcnt);
+
+		if (rpmbdata.icmd.frames == NULL)
+			return RPMB_ALLOC_ERROR;
+
+		rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
+
+		if (rpmbdata.ocmd.frames == NULL)
+			return RPMB_ALLOC_ERROR;
+
+		/*
+		 * Initial data buffer for HMAC computation.
+		 * Since HAMC computation tool which we use needs
+		 * consecutive data buffer.
+		 * Pre-alloced it.
+		 */
+
+		dataBuf_start = dataBuf = kzalloc(284UL * tran_blkcnt, 0);
+
+		/*
+		 * Prepare frame contents
+		 */
+
+		rpmbdata.req_type = RPMB_WRITE_DATA;
+
+
+		/* Output frames (in view of device) */
+
+		rpmbdata.ocmd.nframes = 1;
+
+		/*
+		 * All input frames (in view of device) need below stuff,
+		 * 1. address.
+		 * 2. write counter.
+		 * 3. data.
+		 * 4. block count.
+		 * 5. MAC
+		 */
+
+		rpmbdata.icmd.nframes = tran_blkcnt;
+
+		/* size for hmac calculation: 512 - 228 = 284 */
+		size_for_hmac = sizeof(struct rpmb_frame) -
+				offsetof(struct rpmb_frame, data);
+
+		for (iCnt = 0; iCnt < tran_blkcnt; iCnt++) {
+			/*
+			 * Prepare write data frame. need addr, wc, blkcnt,
+			 * data and mac.
+			 */
+			rpmbdata.icmd.frames[iCnt].req_resp =
+						cpu_to_be16(RPMB_WRITE_DATA);
+			rpmbdata.icmd.frames[iCnt].addr = cpu_to_be16(blkaddr);
+			rpmbdata.icmd.frames[iCnt].block_count =
+						cpu_to_be16(tran_blkcnt);
+			rpmbdata.icmd.frames[iCnt].write_counter =
+						cpu_to_be32(wc);
+
+			if (left_size >= RPMB_SZ_DATA)
+				tran_size = RPMB_SZ_DATA;
+			else
+				tran_size = left_size;
+
+			memcpy(rpmbdata.icmd.frames[iCnt].data,
+			       param->databytes +
+			       i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
+			       (iCnt * RPMB_SZ_DATA),
+			       tran_size);
+			left_size -= tran_size;
+
+			data_for_hmac = rpmbdata.icmd.frames[iCnt].data;
+
+			/* copy data part */
+			memcpy(dataBuf, data_for_hmac, RPMB_SZ_DATA);
+
+			/* copy left part */
+			memcpy(dataBuf + RPMB_SZ_DATA,
+			       data_for_hmac + RPMB_SZ_DATA,
+			       size_for_hmac - RPMB_SZ_DATA);
+
+			dataBuf = dataBuf + size_for_hmac;
+
+		}
+
+		iCnt--;
+
+		if (hmac_sha256(param->keybytes, 32, dataBuf_start,
+				284UL * tran_blkcnt,
+				rpmbdata.icmd.frames[iCnt].key_mac) != 0)
+			MSG(ERR, "hmac_sha256() return error!\n");
+
+		/*
+		 * Send write data request.
+		 */
+
+		ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
+
+		if (ret != 0) {
+			MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
+			    __func__, ret);
+			break;
+		}
+
+		/*
+		 * Authenticate write result response.
+		 * 1. authenticate hmac.
+		 * 2. check result.
+		 * 3. compare write counter is increamented.
+		 */
+		if (hmac_sha256(param->keybytes, 32,
+				rpmbdata.ocmd.frames->data,
+				284UL, hmac) != 0)
+			MSG(ERR, "hmac_sha256() return error!\n");
+
+		if (memcmp(hmac, rpmbdata.ocmd.frames->key_mac,
+			   RPMB_SZ_MAC) != 0) {
+			MSG(ERR, "%s, hmac compare error!!!\n", __func__);
+			ret = RPMB_HMAC_ERROR;
+			break;
+		}
+
+		if (rpmbdata.ocmd.frames->result != 0U) {
+			MSG(ERR, "%s, result error!!! (0x%x)\n", __func__,
+			    cpu_to_be16(rpmbdata.ocmd.frames->result));
+			ret = RPMB_RESULT_ERROR;
+			break;
+		}
+
+		if (cpu_to_be32(rpmbdata.ocmd.frames->write_counter) !=
+		    wc + 1U) {
+			MSG(ERR, "%s, write counter error!!! (0x%x)\n",
+			    __func__,
+			    cpu_to_be32(rpmbdata.ocmd.frames->write_counter));
+			ret = RPMB_WC_ERROR;
+			break;
+		}
+
+		blkaddr += tran_blkcnt;
+		left_blkcnt -= tran_blkcnt;
+		i++;
+
+		kfree(rpmbdata.icmd.frames);
+		kfree(rpmbdata.ocmd.frames);
+		kfree(dataBuf_start);
+	};
+
+	if (ret != 0) {
+		kfree(rpmbdata.icmd.frames);
+		kfree(rpmbdata.ocmd.frames);
+		kfree(dataBuf_start);
+	}
+
+	if (left_blkcnt != 0U || left_size != 0U) {
+		MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
+		return RPMB_TRANSFER_NOT_COMPLETE;
+	}
+
+	MSG(DBG_INFO, "%s end!!!\n", __func__);
+
+	return ret;
+}
+
+static int rpmb_req_ioctl_read_data(struct rpmb_ioc_param *param)
+{
+	struct rpmb_data rpmbdata;
+	struct rpmb_dev *mmc_rpmb;
+	u32 i, tran_size, left_size = param->data_len;
+	u16 iCnt, total_blkcnt, tran_blkcnt, left_blkcnt;
+	u16 blkaddr;
+	u8 nonce[RPMB_SZ_NONCE] = {0};
+	u8 hmac[RPMB_SZ_MAC];
+	u8 *dataBuf, *dataBuf_start, *data_for_hmac;
+	size_t size_for_hmac;
+	int ret = 0;
+	u8 user_param_data;
+
+	MSG(DBG_INFO, "%s start!!!\n", __func__);
+
+	ret = get_user(user_param_data, param->databytes);
+	if (ret != 0)
+		return -EFAULT;
+
+	ret = get_user(user_param_data, param->keybytes);
+	if (ret != 0)
+		return -EFAULT;
+
+	mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
+
+	i = 0;
+	tran_blkcnt = 0;
+	dataBuf = NULL;
+	dataBuf_start = NULL;
+
+	total_blkcnt = (u16)(((param->data_len % RPMB_SZ_DATA) != 0U) ?
+				(param->data_len / RPMB_SZ_DATA + 1U) :
+				(param->data_len / RPMB_SZ_DATA));
+	left_blkcnt = total_blkcnt;
+
+	blkaddr = param->addr;
+
+	while (left_blkcnt > 0U) {
+
+		if (left_blkcnt >= MAX_RPMB_TRANSFER_BLK)
+			tran_blkcnt = MAX_RPMB_TRANSFER_BLK;
+		else
+			tran_blkcnt = left_blkcnt;
+
+		MSG(DBG_INFO, "%s, left_blkcnt = 0x%x, tran_blkcnt = 0x%x\n",
+		    __func__, left_blkcnt, tran_blkcnt);
+
+		/*
+		 * initial frame buffers
+		 */
+
+		rpmbdata.icmd.frames = rpmb_alloc_frames(1);
+
+		if (rpmbdata.icmd.frames == NULL)
+			return RPMB_ALLOC_ERROR;
+
+		rpmbdata.ocmd.frames = rpmb_alloc_frames(tran_blkcnt);
+
+		if (rpmbdata.ocmd.frames == NULL)
+			return RPMB_ALLOC_ERROR;
+
+		/*
+		 * Initial data buffer for HMAC computation.
+		 * Since HAMC computation tool which we use needs
+		 * consecutive data buffer.
+		 * Pre-alloced it.
+		 */
+
+		dataBuf_start = dataBuf = kzalloc(284UL * tran_blkcnt, 0);
+
+		get_random_bytes(nonce, (int)RPMB_SZ_NONCE);
+
+		/*
+		 * Prepare request read data frame.
+		 *
+		 * Input frame (in view of device) only needs addr and nonce.
+		 */
+
+		rpmbdata.req_type = RPMB_READ_DATA;
+		rpmbdata.icmd.nframes = 1;
+		rpmbdata.icmd.frames->req_resp = cpu_to_be16(RPMB_READ_DATA);
+		rpmbdata.icmd.frames->addr = cpu_to_be16(blkaddr);
+		rpmbdata.icmd.frames->block_count = cpu_to_be16(tran_blkcnt);
+		memcpy(rpmbdata.icmd.frames->nonce, nonce, RPMB_SZ_NONCE);
+
+		/* output frames (in view of device) */
+
+		rpmbdata.ocmd.nframes = tran_blkcnt;
+
+		ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
+
+		if (ret != 0) {
+			MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
+			    __func__, ret);
+			break;
+		}
+
+		/*
+		 * Retrieve every data frame one by one.
+		 */
+
+		/* size for hmac calculation: 512 - 228 = 284 */
+		size_for_hmac = sizeof(struct rpmb_frame) -
+				offsetof(struct rpmb_frame, data);
+
+		for (iCnt = 0; iCnt < tran_blkcnt; iCnt++) {
+
+			if (left_size >= RPMB_SZ_DATA)
+				tran_size = RPMB_SZ_DATA;
+			else
+				tran_size = left_size;
+
+			/*
+			 * dataBuf used for hmac calculation. we need to
+			 * aggregate each block's data till to type field.
+			 * each block has 284 bytes (size_for_hmac) need
+			 * aggregation.
+			 */
+			data_for_hmac = rpmbdata.ocmd.frames[iCnt].data;
+
+			/* copy data part */
+			memcpy(dataBuf, data_for_hmac, RPMB_SZ_DATA);
+
+			/* copy left part */
+			memcpy(dataBuf + RPMB_SZ_DATA,
+			       data_for_hmac + RPMB_SZ_DATA,
+			       size_for_hmac - RPMB_SZ_DATA);
+
+			dataBuf = dataBuf + size_for_hmac;
+
+			/*
+			 * Sorry, I shouldn't copy read data to user's
+			 * buffer now, it should be later
+			 * after checking no problem,
+			 * but for convenience...you know...
+			 */
+			memcpy(param->databytes +
+			       i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
+			       (iCnt * RPMB_SZ_DATA),
+			       rpmbdata.ocmd.frames[iCnt].data,
+			       tran_size);
+			left_size -= tran_size;
+		}
+
+		iCnt--;
+
+		/*
+		 * Authenticate response read data frame.
+		 */
+		if (hmac_sha256(param->keybytes, 32,
+			    dataBuf_start, size_for_hmac * tran_blkcnt,
+			    hmac) != 0)
+			MSG(ERR, "hmac_sha256() return error!\n");
+
+		if (memcmp(hmac, rpmbdata.ocmd.frames[iCnt].key_mac,
+			   RPMB_SZ_MAC) != 0) {
+			MSG(ERR, "%s, hmac compare error!!!\n", __func__);
+			ret = RPMB_HMAC_ERROR;
+			break;
+		}
+
+		if (memcmp(nonce, rpmbdata.ocmd.frames[iCnt].nonce,
+			   RPMB_SZ_NONCE) != 0) {
+			MSG(ERR, "%s, nonce compare error!!!\n", __func__);
+			ret = RPMB_NONCE_ERROR;
+			break;
+		}
+
+		if (rpmbdata.ocmd.frames[iCnt].result != 0U) {
+			MSG(ERR, "%s, result error!!! (0x%x)\n",
+			    __func__,
+			    cpu_to_be16p(&rpmbdata.ocmd.frames[iCnt].result));
+			ret = RPMB_RESULT_ERROR;
+			break;
+		}
+
+		blkaddr += tran_blkcnt;
+		left_blkcnt -= tran_blkcnt;
+		i++;
+
+		kfree(rpmbdata.icmd.frames);
+		kfree(rpmbdata.ocmd.frames);
+		kfree(dataBuf_start);
+	};
+
+	if (ret != 0) {
+		kfree(rpmbdata.icmd.frames);
+		kfree(rpmbdata.ocmd.frames);
+		kfree(dataBuf_start);
+	}
+
+	if (left_blkcnt != 0U || left_size != 0U) {
+		MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
+		return RPMB_TRANSFER_NOT_COMPLETE;
+	}
+
+	MSG(DBG_INFO, "%s end!!!\n", __func__);
+
+	return ret;
+}
+
+/*
+ * End of above.
+ *
+ ******************************************************************************/
+
+
+#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
+
+static enum mc_result rpmb_gp_execute(u32 cmdId)
+{
+	int ret;
+
+	switch (cmdId) {
+
+	case DCI_RPMB_CMD_READ_DATA:
+
+		MSG(DBG_INFO, "%s: DCI_RPMB_CMD_READ_DATA\n", __func__);
+
+		ret = rpmb_req_read_data(rpmb_gp_dci->request.frame,
+					 rpmb_gp_dci->request.blks);
+
+		break;
+
+	case DCI_RPMB_CMD_GET_WCNT:
+
+		MSG(DBG_INFO, "%s: DCI_RPMB_CMD_GET_WCNT\n", __func__);
+
+		ret = rpmb_req_get_wc(NULL, NULL, rpmb_gp_dci->request.frame);
+
+		break;
+
+	case DCI_RPMB_CMD_WRITE_DATA:
+
+		MSG(DBG_INFO, "%s: DCI_RPMB_CMD_WRITE_DATA\n", __func__);
+
+		ret = rpmb_req_write_data(rpmb_gp_dci->request.frame,
+					  rpmb_gp_dci->request.blks);
+
+		break;
+
+	default:
+		MSG(ERR, "%s: receive an unknown command id(%d).\n",
+		    __func__, cmdId);
+		break;
+
+	}
+
+	return MC_DRV_OK;
+}
+
+static int rpmb_gp_listenDci(void *arg)
+{
+	enum mc_result mc_ret;
+	u32 cmdId;
+
+	MSG(INFO, "%s: DCI listener.\n", __func__);
+
+	for (;;) {
+
+		MSG(INFO, "%s: Waiting for notification\n", __func__);
+
+		/* Wait for notification from SWd */
+		mc_ret = mc_wait_notification(&rpmb_gp_session,
+					      MC_INFINITE_TIMEOUT);
+		if (mc_ret != MC_DRV_OK) {
+			MSG(ERR, "%s: mcWaitNotification failed, mc_ret=%d\n",
+			    __func__, mc_ret);
+			break;
+		}
+
+		cmdId = rpmb_gp_dci->command.header.commandId;
+
+		MSG(INFO, "%s: wait notification done!! cmdId = %x\n",
+		    __func__, cmdId);
+
+		/* Received exception. */
+		mc_ret = rpmb_gp_execute(cmdId);
+
+		/* Notify the STH*/
+		mc_ret = mc_notify(&rpmb_gp_session);
+		if (mc_ret != MC_DRV_OK) {
+			MSG(ERR, "%s: mcNotify returned: %d\n",
+			    __func__, mc_ret);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static enum mc_result rpmb_gp_open_session(void)
+{
+	int cnt = 0;
+	enum mc_result mc_ret;
+
+	MSG(INFO, "%s start\n", __func__);
+
+	do {
+		msleep(6000);
+
+		/* open device */
+		mc_ret = mc_open_device(rpmb_gp_devid);
+		if (mc_ret != MC_DRV_OK) {
+			MSG(ERR, "%s, mc_open_device failed: %d\n",
+			    __func__, mc_ret);
+			cnt++;
+			continue;
+		}
+
+		MSG(INFO, "%s, mc_open_device success.\n", __func__);
+
+
+		/* allocating WSM for DCI */
+		mc_ret = mc_malloc_wsm(rpmb_gp_devid, 0,
+				       (u32)sizeof(struct dciMessage_t),
+				       (uint8_t **)&rpmb_gp_dci, 0);
+		if (mc_ret != MC_DRV_OK) {
+			MSG(ERR, "%s, mc_malloc_wsm failed: %d\n",
+			    __func__, mc_ret);
+			mc_ret = mc_close_device(rpmb_gp_devid);
+			cnt++;
+			continue;
+		}
+
+		MSG(INFO, "%s, mc_malloc_wsm success.\n", __func__);
+		MSG(INFO, "uuid[0]=%d, uuid[1]=%d, uuid[2]=%d, uuid[3]=%d\n",
+			rpmb_gp_uuid.value[0],
+			rpmb_gp_uuid.value[1],
+			rpmb_gp_uuid.value[2],
+			rpmb_gp_uuid.value[3]
+			);
+
+		rpmb_gp_session.device_id = rpmb_gp_devid;
+
+		/* open session */
+		mc_ret = mc_open_session(&rpmb_gp_session,
+					 &rpmb_gp_uuid,
+					 (uint8_t *) rpmb_gp_dci,
+					 (u32)sizeof(struct dciMessage_t));
+
+		if (mc_ret != MC_DRV_OK) {
+			MSG(ERR,
+			    "%s, mc_open_session failed, result(%d), cnt(%d)\n",
+				__func__, mc_ret, cnt);
+
+			mc_ret = mc_free_wsm(rpmb_gp_devid,
+					     (uint8_t *)rpmb_gp_dci);
+			MSG(ERR, "%s, free wsm result (%d)\n",
+			    __func__, mc_ret);
+
+			mc_ret = mc_close_device(rpmb_gp_devid);
+			MSG(ERR, "%s, try free wsm and close device\n",
+			    __func__);
+			cnt++;
+			continue;
+		}
+		MSG(INFO, "%s, mc_open_session success.\n", __func__);
+
+		/* create a thread for listening DCI signals */
+		rpmb_gp_Dci_th = kthread_run(rpmb_gp_listenDci,
+					     NULL, "rpmb_gp_Dci");
+		if (IS_ERR(rpmb_gp_Dci_th))
+			MSG(ERR, "%s, init kthread_run failed!\n", __func__);
+		else
+			break;
+
+	} while (cnt < 60);
+
+	if (cnt >= 60)
+		MSG(ERR, "%s, open session failed!!!\n", __func__);
+
+
+	MSG(ERR, "%s end, mc_ret = %x\n", __func__, mc_ret);
+
+	return mc_ret;
+}
+
+
+static int rpmb_thread(void *context)
+{
+	enum mc_result ret;
+
+	MSG(INFO, "%s start\n", __func__);
+
+	ret = rpmb_gp_open_session();
+	MSG(INFO, "%s rpmb_gp_open_session, ret = %x\n", __func__, ret);
+
+	return 0;
+}
+#endif
+
+static int rpmb_open(struct inode *pinode, struct file *pfile)
+{
+	return 0;
+}
+
+static long rpmb_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	unsigned long n;
+	struct rpmb_ioc_param param;
+
+	n = copy_from_user(&param, (void *)arg, sizeof(param));
+
+	if (n != 0UL) {
+		MSG(ERR, "%s, copy from user failed: %x\n", __func__, err);
+		return -EFAULT;
+	}
+
+	switch (cmd) {
+
+	case RPMB_IOCTL_PROGRAM_KEY:
+
+		MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_PROGRAM_KEY not support\n",
+		    __func__);
+
+		break;
+
+	case RPMB_IOCTL_READ_DATA:
+
+		MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_READ_DATA!!!!\n", __func__);
+
+		err = rpmb_req_ioctl_read_data(&param);
+
+		if (err != 0) {
+			MSG(ERR, "%s, rpmb_req_ioctl_read_data() error!(%x)\n",
+			    __func__, err);
+			break;
+		}
+
+		n = copy_to_user((void *)arg, &param, sizeof(param));
+
+		if (n != 0UL) {
+			MSG(ERR, "%s, copy to user user failed: %x\n",
+			    __func__, err);
+			err = -EFAULT;
+			break;
+		}
+
+		break;
+
+	case RPMB_IOCTL_WRITE_DATA:
+
+		MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_WRITE_DATA!!!\n", __func__);
+
+		err = rpmb_req_ioctl_write_data(&param);
+
+		if (err != 0)
+			MSG(ERR, "%s, rpmb_req_ioctl_write_data() error!(%x)\n",
+			    __func__, err);
+
+		break;
+
+	default:
+		MSG(ERR, "%s, wrong ioctl code (%d)!!!\n", __func__, cmd);
+		err = -ENOTTY;
+		break;
+	}
+
+	return err;
+}
+
+static int rpmb_close(struct inode *pinode, struct file *pfile)
+{
+	int ret = 0;
+
+	MSG(INFO, "%s, !!!!!!!!!!!!\n", __func__);
+
+	return ret;
+}
+
+static const struct file_operations rpmb_fops = {
+	.owner = THIS_MODULE,
+	.open = rpmb_open,
+	.release = rpmb_close,
+	.unlocked_ioctl = rpmb_ioctl,
+	.write = NULL,
+	.read = NULL,
+};
+
+static int __init rpmb_init(void)
+{
+	int alloc_ret;
+	int cdev_ret = -1;
+	unsigned int major;
+	dev_t dev;
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, NULL, "mediatek,rpmb-kinibi");
+	if (np == NULL)
+		goto out_err;
+
+	MSG(INFO, "%s start\n", __func__);
+
+	alloc_ret = alloc_chrdev_region(&dev, 0, 1, RPMB_NAME);
+
+	if (alloc_ret != 0) {
+		MSG(ERR, "%s, init alloc_chrdev_region failed!\n", __func__);
+		goto error;
+	}
+
+	major = MAJOR(dev);
+
+	cdev_init(&rpmb_cdev, &rpmb_fops);
+	rpmb_cdev.owner = THIS_MODULE;
+
+	cdev_ret = cdev_add(&rpmb_cdev, MKDEV(major, 0U), 1);
+	if (cdev_ret != 0) {
+		MSG(ERR, "%s, init cdev_add failed!\n", __func__);
+		goto error;
+	}
+
+#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
+	open_th = kthread_run(rpmb_thread, NULL, "rpmb_open");
+	if (IS_ERR(open_th))
+		MSG(ERR, "%s, init kthread_run failed!\n", __func__);
+#endif
+
+	MSG(INFO, "%s end!!!!\n", __func__);
+
+	return 0;
+
+error:
+
+	if (cdev_ret == 0)
+		cdev_del(&rpmb_cdev);
+
+	if (alloc_ret == 0)
+		unregister_chrdev_region(dev, 1);
+
+out_err:
+	return -1;
+}
+
+late_initcall(rpmb_init);
+