Linux MultiMedia Card development
 help / color / mirror / Atom feed
* Re: [v15, 3/7] powerpc/fsl: move mpc85xx.h to include/linux/fsl
From: Arnd Bergmann @ 2016-11-07 21:20 UTC (permalink / raw)
  To: Y.B. Lu
  Cc: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org, Scott Wood,
	Mark Rutland, Greg Kroah-Hartman, X.B. Xie, M.H. Lian,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-clk-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Qiang Zhao,
	Russell King, Bhupesh Sharma, Joerg Roedel, Claudiu Manoil,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring
In-Reply-To: <DB6PR0401MB2536B1B2E6AC36BDC8FC540DF8AE0-2mNvjAGDOPkZcyyZo0JLBI3W/0Ik+aLCnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>

On Monday, October 31, 2016 9:35:33 AM CET Y.B. Lu wrote:
> > 
> > I don't see any of the contents of this header referenced by the soc
> > driver any more. I think you can just drop this patch.
> > 
> 
> [Lu Yangbo-B47093] This header file was included by guts.c.
> The guts driver used macro SVR_MAJ/SVR_MIN for calculation.
> 
> This header file was for powerpc arch before. And this patch is to made it as
> common header file for both ARM and PPC.
> Sooner or later this is needed.

Let's discuss it once we actually need the header then, ok?

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v7 11/11] char: rpmb: Document Replay Protected Memory Block (RPMB) subsystem
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Add rpmb documentatin in sphinx format.

V7: new in the series

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 Documentation/index.rst                  |   1 +
 Documentation/rpmb/conf.py               |   5 ++
 Documentation/rpmb/index.rst             |  18 ++++++
 Documentation/rpmb/introduction.rst      | 102 +++++++++++++++++++++++++++++++
 Documentation/rpmb/rpmb-tool.rst         |  19 ++++++
 Documentation/rpmb/simulation-device.rst |  19 ++++++
 MAINTAINERS                              |   1 +
 7 files changed, 165 insertions(+)
 create mode 100644 Documentation/rpmb/conf.py
 create mode 100644 Documentation/rpmb/index.rst
 create mode 100644 Documentation/rpmb/introduction.rst
 create mode 100644 Documentation/rpmb/rpmb-tool.rst
 create mode 100644 Documentation/rpmb/simulation-device.rst

diff --git a/Documentation/index.rst b/Documentation/index.rst
index c53d089455a4..56dd46e9359c 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -18,6 +18,7 @@ Contents:
    media/index
    gpu/index
    80211/index
+   rpmb/index
 
 Indices and tables
 ==================
diff --git a/Documentation/rpmb/conf.py b/Documentation/rpmb/conf.py
new file mode 100644
index 000000000000..20c7c275ef4a
--- /dev/null
+++ b/Documentation/rpmb/conf.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8; mode: python -*-
+
+project = "Linux 802.11 Driver Developer's Guide"
+
+tags.add("subproject")
diff --git a/Documentation/rpmb/index.rst b/Documentation/rpmb/index.rst
new file mode 100644
index 000000000000..876a2603e4b5
--- /dev/null
+++ b/Documentation/rpmb/index.rst
@@ -0,0 +1,18 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+==============================================
+Replay Protected Memory Block (RPMB) subsystem
+==============================================
+
+.. toctree::
+
+   introduction
+   simulation-device.rst
+   rpmb-tool.rst
+
+.. only::  subproject
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/rpmb/introduction.rst b/Documentation/rpmb/introduction.rst
new file mode 100644
index 000000000000..f4ded7d18e55
--- /dev/null
+++ b/Documentation/rpmb/introduction.rst
@@ -0,0 +1,102 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+=============
+Introduction:
+=============
+
+Few storage technologies such is EMMC, UFS, and NVMe support RPMB
+hardware partition with common protocol and frame layout.
+The RPMB partition `cannot` be accessed via standard block layer,
+but by a set of specific commands:
+
+WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY.
+
+The commands and the data are embedded within :c:type:`rpmb_frame <rpmb_frame>`.
+
+An RPMB partition provides authenticated and replay protected access,
+hence it is suitable as a secure storage.
+
+In-kernel API
+-------------
+The RPMB layer aims to provide in-kernel API for Trusted Execution
+Environment (TEE) devices that are capable to securely compute the block
+frame signature. In case a TEE device wish to store a replay protected
+data, it creates an RPMB frame with requested data and computes HMAC of
+the frame, then it requests the storage device via RPMB layer to store
+the data.
+
+The layer provides two APIs, for :c:func:`rpmb_req_cmd()` for issuing one of RPMB
+specific commands and :c:func:`rpmb_seq_cmd()` for issuing of raw RPMB protocol
+frames, which is close to the functionality provided by emmc multi ioctl
+interface.
+
+.. c:function:: int rpmb_cmd_req(struct rpmb_dev *rdev, struct rpmb_data *data);
+
+.. c:function:: int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds);
+
+
+A TEE driver can claim the RPMB interface, for example, via
+:c:func:`class_interface_register`:
+
+.. code-block:: c
+
+        struct class_interface tee_rpmb_intf = {
+                .class      = &rpmb_class;
+                .add_dev    = rpmb_add_device;
+                .remove_dev = rpmb_remove_device;
+        }
+        class_interface_register(&tee_rpmb_intf);
+
+
+RPMB device registeration
+----------------------------
+
+A storage device registers its RPMB hardware (eMMC) partition or RPMB
+W-LUN (UFS) with the RPMB layer :c:func:`rpmb_dev_register` providing
+an implementation for :c:func:`rpmb_seq_cmd()` handler. The interface
+enables sending sequence of RPMB standard frames.
+
+.. code-block:: c
+
+        struct rpmb_ops mmc_rpmb_dev_ops = {
+                .cmd_seq = mmc_blk_rpmb_cmd_seq,
+                .type = RPMB_TYPE_EMMC,
+                ...
+        }
+        rpmb_dev_register(disk_to_dev(part_md->disk), &mmc_rpmb_dev_ops);
+
+
+User space API
+--------------
+
+A parallel user space API is provided via /dev/rpmbX character
+device with two IOCTL commands.
+Simplified one, ``RPMB_IOC_REQ_CMD``, were read result cycles is performed
+by the framework on behalf the user and second, ``RPMB_IOC_SEQ_CMD`` where
+the whole RPMB sequence, including ``RESULT_READ`` is supplied by the caller.
+The latter is intended for easier adjusting of the applications that
+use ``MMC_IOC_MULTI_CMD`` ioctl, such as
+https://android.googlesource.com/trusty/app/storage/
+
+.. code-block:: c
+
+        struct rpmb_ioc_req_cmd ireq;
+        int ret;
+
+        ireq.req_type = RPMB_WRITE_DATA;
+        rpmb_ioc_cmd_set(ireq.icmd, RPMB_F_WRITE, frames_in, cnt_in);
+        rpmb_ioc_cmd_set(ireq.ocmd, 0, frames_out, cnt_out);
+
+        ret = ioctl(fd, RPMB_IOC_REQ_CMD, &ireq);
+
+
+API
+---
+.. kernel-doc:: include/linux/rpmb.h
+
+.. kernel-doc:: drivers/char/rpmb/core.c
+
+.. kernel-doc:: include/uapi/linux/rpmb.h
+
+.. kernel-doc:: drivers/char/rpmb/cdev.c
+
diff --git a/Documentation/rpmb/rpmb-tool.rst b/Documentation/rpmb/rpmb-tool.rst
new file mode 100644
index 000000000000..148eb0df4750
--- /dev/null
+++ b/Documentation/rpmb/rpmb-tool.rst
@@ -0,0 +1,19 @@
+==========
+RPMB Tool
+==========
+
+There is a sample rpmb tool under tools/rpmb/ directory that exercises
+the RPMB devices via RPMB character devices interface (/dev/rpmbX)
+
+.. code-block:: none
+
+        rpmb [-v] [-r|-s] <command> <args>
+
+        rpmb program-key [-<RPMB_DEVICE> <KEY_FILE>
+        rpmb write-counter <RPMB_DEVICE> [KEY_FILE]
+        rpmb write-blocks <RPMB_DEVICE> <address> <block_count> <DATA_FILE> <KEY_FILE>
+        rpmb read-blocks <RPMB_DEVICE> <address> <blocks count> <OUTPUT_FILE> [KEY_FILE]
+
+        rpmb -v/--verbose:  runs in verbose mode
+        rpmb -s/--sequence: use RPMB_IOC_SEQ_CMD
+        rpmb -r/--request:  use RPMB_IOC_REQ_CMD
diff --git a/Documentation/rpmb/simulation-device.rst b/Documentation/rpmb/simulation-device.rst
new file mode 100644
index 000000000000..9192a78a71d4
--- /dev/null
+++ b/Documentation/rpmb/simulation-device.rst
@@ -0,0 +1,19 @@
+======================
+RPMB Simulation Device
+======================
+
+RPMB partition simulation device is a virtual device that
+provides simulation of the RPMB protocol and uses kernel memory
+as storage.
+
+This driver cannot promise any real security, it is suitable for testing
+of the RPMB subsystem it self and mostly it was found useful for testing of
+RPMB applications prior to RPMB key provisioning/programming as
+The RPMB key programming can be performed only once in the life time
+of the storage device.
+
+Implementation:
+---------------
+
+.. kernel-doc:: drivers/char/rpmb/rpmb_sim.c
+
diff --git a/MAINTAINERS b/MAINTAINERS
index d9bca5134c7f..fa6f8017a05c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10292,6 +10292,7 @@ F:	drivers/char/rpmb/*
 F:	include/uapi/linux/rpmb.h
 F:	include/linux/rpmb.h
 F:	Documentation/ABI/testing/sysfs-class-rpmb
+F:	Documentation/rpmb.rst
 F:	tools/rpmb/
 
 RTL2830 MEDIA DRIVER
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 10/11] scsi: ufs: retrieve rpmb rw size
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler, Alexander Usyskin
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Retrieve maximum number of RPMB frames that can be
in a single request.
Currently the limitations is provided only for writes
via reliable_wr_cnt API.

V7: New in the series

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/scsi/ufs/ufs.h    |  6 ++++++
 drivers/scsi/ufs/ufshcd.c | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 845b874e2977..84bb8d7c6b63 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -176,6 +176,12 @@ enum ufs_desc_max_size {
 	QUERY_DESC_RFU_MAX_SIZE			= 0x00,
 };
 
+enum geometry_desc_param_offset {
+	GEOMETRY_DESC_LEN		= 0x0,
+	GEOMETRY_DESC_TYPE		= 0x1,
+	GEOMETRY_DESC_RPMB_RW_SIZE	= 0x17,
+};
+
 /* Unit descriptor parameters offsets in bytes*/
 enum unit_desc_param {
 	UNIT_DESC_PARAM_LEN			= 0x0,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 95bb9dc7fbfe..69f5b03f6774 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2196,6 +2196,15 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
 				      param_offset, param_read_buf, param_size);
 }
 
+static inline int ufshcd_read_geometry_desc_param(struct ufs_hba *hba,
+				enum geometry_desc_param_offset param_offset,
+				u8 *param_read_buf, u32 param_size)
+{
+	return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0,
+				      param_offset, param_read_buf, param_size);
+}
+
+
 /**
  * ufshcd_memory_alloc - allocate memory for host memory space data structures
  * @hba: per adapter instance
@@ -4896,6 +4905,19 @@ static struct rpmb_ops ufshcd_rpmb_dev_ops = {
 static inline void ufshcd_rpmb_add(struct ufs_hba *hba)
 {
 	struct rpmb_dev *rdev;
+	u8 rw_size;
+	int ret;
+
+	ret = ufshcd_read_geometry_desc_param(hba, GEOMETRY_DESC_RPMB_RW_SIZE,
+					&rw_size, sizeof(rw_size));
+	if (ret) {
+		dev_warn(hba->dev, "%s: cannot get rpmb rw limit %d\n",
+			 dev_name(hba->dev), ret);
+		/* fallback to singel frame write */
+		rw_size = 1;
+	}
+
+	ufshcd_rpmb_dev_ops.reliable_wr_cnt = rw_size;
 
 	scsi_device_get(hba->sdev_ufs_rpmb);
 	rdev = rpmb_dev_register(hba->dev, &ufshcd_rpmb_dev_ops);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 09/11] scsi: ufs: connect to RPMB subsystem
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler, Alexander Usyskin
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Register UFS RPMB LUN with the RPMB subsystem and provide
implementation for the RPMB access operations. RPMB partition is
accessed via a sequence of security protocol in and security protocol
out commands with UFS specific parameters. This multi step process is
abstracted into 4 basic RPMB commands.

V2: resend
V3: resend
V4: Kconfig: use select RPMB to ensure valid configuration
V5: Revamp code using new sequence command.
V6: Resend
V7: Resend

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 drivers/scsi/ufs/Kconfig  |   1 +
 drivers/scsi/ufs/ufshcd.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshcd.h |   2 +
 3 files changed, 186 insertions(+)

diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index e27b4d4e6ae2..8b545a9c51dd 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -38,6 +38,7 @@ config SCSI_UFSHCD
 	select PM_DEVFREQ
 	select DEVFREQ_GOV_SIMPLE_ONDEMAND
 	select NLS
+	select RPMB
 	---help---
 	This selects the support for UFS devices in Linux, say Y and make
 	  sure that you know the name of your UFS host adapter (the card
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 05c745663c10..95bb9dc7fbfe 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -37,10 +37,13 @@
  * license terms, and distributes only under these terms.
  */
 
+#include <asm/unaligned.h>
 #include <linux/async.h>
 #include <linux/devfreq.h>
 #include <linux/nls.h>
 #include <linux/of.h>
+#include <linux/rpmb.h>
+
 #include "ufshcd.h"
 #include "ufs_quirks.h"
 #include "unipro.h"
@@ -4753,6 +4756,178 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
 
 }
 
+#define SEC_PROTOCOL_UFS  0xEC
+#define   SEC_SPECIFIC_UFS_RPMB 0x0001
+
+#define SEC_PROTOCOL_CMD_SIZE 12
+#define SEC_PROTOCOL_RETRIES 3
+#define SEC_PROTOCOL_RETRIES_ON_RESET 10
+#define SEC_PROTOCOL_TIMEOUT msecs_to_jiffies(1000)
+
+static int
+ufshcd_rpmb_security_out(struct scsi_device *sdev,
+			 struct rpmb_frame *frames, u32 cnt)
+{
+	struct scsi_sense_hdr sshdr;
+	u32 trans_len = cnt * sizeof(struct rpmb_frame);
+	int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET;
+	int ret;
+	u8 cmd[SEC_PROTOCOL_CMD_SIZE];
+
+	memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE);
+	cmd[0] = SECURITY_PROTOCOL_OUT;
+	cmd[1] = SEC_PROTOCOL_UFS;
+	put_unaligned_be16(SEC_SPECIFIC_UFS_RPMB, cmd + 2);
+	cmd[4] = 0;                              /* inc_512 bit 7 set to 0 */
+	put_unaligned_be32(trans_len, cmd + 6);  /* transfer length */
+
+retry:
+	ret = scsi_execute_req_flags(sdev, cmd, DMA_TO_DEVICE,
+				     frames, trans_len, &sshdr,
+				     SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES,
+				     NULL, 0);
+
+	if (ret && scsi_sense_valid(&sshdr) &&
+	    sshdr.sense_key == UNIT_ATTENTION &&
+	    sshdr.asc == 0x29 && sshdr.ascq == 0x00)
+		/*
+		 * Device reset might occur several times,
+		 * give it one more chance
+		 */
+		if (--reset_retries > 0)
+			goto retry;
+
+	if (ret)
+		dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n",
+			__func__, ret);
+
+	if (driver_byte(ret) & DRIVER_SENSE)
+		scsi_print_sense_hdr(sdev, "rpmb: security out", &sshdr);
+
+	return ret;
+}
+
+static int
+ufshcd_rpmb_security_in(struct scsi_device *sdev,
+			struct rpmb_frame *frames, u32 cnt)
+{
+	struct scsi_sense_hdr sshdr;
+	u32 alloc_len = cnt * sizeof(struct rpmb_frame);
+	int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET;
+	int ret;
+	u8 cmd[SEC_PROTOCOL_CMD_SIZE];
+
+	memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE);
+	cmd[0] = SECURITY_PROTOCOL_IN;
+	cmd[1] = SEC_PROTOCOL_UFS;
+	put_unaligned_be16(SEC_SPECIFIC_UFS_RPMB, cmd + 2);
+	cmd[4] = 0;                             /* inc_512 bit 7 set to 0 */
+	put_unaligned_be32(alloc_len, cmd + 6); /* allocation length */
+
+retry:
+	ret = scsi_execute_req_flags(sdev, cmd, DMA_FROM_DEVICE,
+				     frames, alloc_len, &sshdr,
+				     SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES,
+				     NULL, 0);
+
+	if (ret && scsi_sense_valid(&sshdr) &&
+	    sshdr.sense_key == UNIT_ATTENTION &&
+	    sshdr.asc == 0x29 && sshdr.ascq == 0x00)
+		/*
+		 * Device reset might occur several times,
+		 * give it one more chance
+		*/
+		if (--reset_retries > 0)
+			goto retry;
+
+	if (ret)
+		dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n",
+			__func__, ret);
+
+	if (driver_byte(ret) & DRIVER_SENSE)
+		scsi_print_sense_hdr(sdev, "rpmb: security in", &sshdr);
+
+	return ret;
+}
+
+static int ufshcd_rpmb_cmd_seq(struct device *dev,
+			       struct rpmb_cmd *cmds, u32 ncmds)
+{
+	unsigned long flags;
+	struct ufs_hba *hba = dev_get_drvdata(dev);
+	struct scsi_device *sdev;
+	struct rpmb_cmd *cmd;
+	int i;
+	int ret;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	sdev = hba->sdev_ufs_rpmb;
+	if (sdev) {
+		ret = scsi_device_get(sdev);
+		if (!ret && !scsi_device_online(sdev)) {
+			ret = -ENODEV;
+			scsi_device_put(sdev);
+		}
+	} else {
+		ret = -ENODEV;
+	}
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	if (ret)
+		return ret;
+
+	for (ret = 0, i = 0; i < ncmds && !ret; i++) {
+		cmd = &cmds[i];
+		if (cmd->flags & RPMB_F_WRITE)
+			ret = ufshcd_rpmb_security_out(sdev, cmd->frames,
+						       cmd->nframes);
+		else
+			ret = ufshcd_rpmb_security_in(sdev, cmd->frames,
+						      cmd->nframes);
+	}
+	scsi_device_put(sdev);
+	return ret;
+}
+
+static struct rpmb_ops ufshcd_rpmb_dev_ops = {
+	.cmd_seq = ufshcd_rpmb_cmd_seq,
+	.type = RPMB_TYPE_UFS,
+};
+
+static inline void ufshcd_rpmb_add(struct ufs_hba *hba)
+{
+	struct rpmb_dev *rdev;
+
+	scsi_device_get(hba->sdev_ufs_rpmb);
+	rdev = rpmb_dev_register(hba->dev, &ufshcd_rpmb_dev_ops);
+	if (IS_ERR(rdev)) {
+		dev_warn(hba->dev, "%s: cannot register to rpmb %ld\n",
+			 dev_name(hba->dev), PTR_ERR(rdev));
+		goto out_put_dev;
+	}
+
+	return;
+
+out_put_dev:
+	scsi_device_put(hba->sdev_ufs_rpmb);
+	hba->sdev_ufs_rpmb = NULL;
+}
+
+static inline void ufshcd_rpmb_remove(struct ufs_hba *hba)
+{
+	unsigned long flags;
+
+	if (!hba->sdev_ufs_rpmb)
+		return;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+
+	rpmb_dev_unregister(hba->dev);
+	scsi_device_put(hba->sdev_ufs_rpmb);
+	hba->sdev_ufs_rpmb = NULL;
+
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
+
 /**
  * ufshcd_scsi_add_wlus - Adds required W-LUs
  * @hba: per-adapter instance
@@ -4808,7 +4983,11 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
 		ret = PTR_ERR(sdev_rpmb);
 		goto remove_sdev_boot;
 	}
+	hba->sdev_ufs_rpmb = sdev_rpmb;
+
+	ufshcd_rpmb_add(hba);
 	scsi_device_put(sdev_rpmb);
+
 	goto out;
 
 remove_sdev_boot:
@@ -6177,6 +6356,8 @@ int ufshcd_shutdown(struct ufs_hba *hba)
 			goto out;
 	}
 
+	ufshcd_rpmb_remove(hba);
+
 	ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
 out:
 	if (ret)
@@ -6193,6 +6374,8 @@ EXPORT_SYMBOL(ufshcd_shutdown);
  */
 void ufshcd_remove(struct ufs_hba *hba)
 {
+	ufshcd_rpmb_remove(hba);
+
 	scsi_remove_host(hba->host);
 	/* disable interrupts */
 	ufshcd_disable_intr(hba, hba->intr_mask);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 430bef111293..a8bd7216825a 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -348,6 +348,7 @@ struct ufs_init_prefetch {
  * @utmrdl_dma_addr: UTMRDL DMA address
  * @host: Scsi_Host instance of the driver
  * @dev: device handle
+ * @sdev_ufs_rpmb: reference to RPMB device W-LU
  * @lrb: local reference block
  * @lrb_in_use: lrb in use
  * @outstanding_tasks: Bits representing outstanding task requests
@@ -410,6 +411,7 @@ struct ufs_hba {
 	 * "UFS device" W-LU.
 	 */
 	struct scsi_device *sdev_ufs_device;
+	struct scsi_device *sdev_ufs_rpmb;
 
 	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
 	enum uic_link_state uic_link_state;
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 08/11] mmc: block: register RPMB partition with the RPMB subsystem
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler, Alexander Usyskin
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Register eMMC RPMB partition with the RPMB subsystem and provide
implementation for the RPMB access operations abstracting
actual multi step process.

V2: resend
V3: commit message fix
V4: Kconfig: use select RPMB to ensure valid configuration
    Switch back to main area after RPMB access
V5: Revamp code using new sequence command
    Support for 8K packets in e.MMC v5.1
V6: Resend.
V7: Resend.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/mmc/card/Kconfig |   1 +
 drivers/mmc/card/block.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 256 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 5562308699bc..537d0bc82781 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -7,6 +7,7 @@ comment "MMC/SD/SDIO Card Drivers"
 config MMC_BLOCK
 	tristate "MMC block device driver"
 	depends on BLOCK
+	select RPMB
 	default y
 	help
 	  Say Y here to enable the MMC block device driver support.
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 709a872ed484..523b2d6afc1c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -42,6 +42,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sd.h>
+#include <linux/rpmb.h>
 
 #include <asm/uaccess.h>
 
@@ -112,6 +113,7 @@ struct mmc_blk_data {
 #define MMC_BLK_WRITE		BIT(1)
 #define MMC_BLK_DISCARD		BIT(2)
 #define MMC_BLK_SECDISCARD	BIT(3)
+#define MMC_BLK_RPMB		BIT(4)
 
 	/*
 	 * Only set in main mmc_blk_data associated
@@ -400,8 +402,8 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
 	return 0;
 }
 
-static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
-				       u32 retries_max)
+static int mmc_blk_rpmb_status_poll(struct mmc_card *card, u32 *status,
+				    u32 retries_max)
 {
 	int err;
 	u32 retry_count = 0;
@@ -574,7 +576,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
 		 * Ensure RPMB command has completed by polling CMD13
 		 * "Send Status".
 		 */
-		err = ioctl_rpmb_card_status_poll(card, &status, 5);
+		err = mmc_blk_rpmb_status_poll(card, &status, 5);
 		if (err)
 			dev_err(mmc_dev(card->host),
 					"%s: Card Status=0x%08X, error %d\n",
@@ -1164,6 +1166,252 @@ int mmc_access_rpmb(struct mmc_queue *mq)
 	return false;
 }
 
+static int mmc_rpmb_send_cmd(struct mmc_card *card,
+			     unsigned int data_type, bool do_rel_wr,
+			     void *buf, u16 blks)
+{
+	int err;
+	u32 status;
+	struct mmc_command sbc = {
+		.opcode = MMC_SET_BLOCK_COUNT,
+		.flags  = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC,
+	};
+
+	struct mmc_command cmd = {
+		.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
+	};
+
+	struct mmc_data data = {
+		.blksz = 512,
+	};
+	struct mmc_request mrq = {
+		.sbc    = &sbc,
+		.cmd    = &cmd,
+		.data   = &data,
+		.stop   = NULL,
+	};
+	struct scatterlist sg;
+
+	/*  set CMD23 */
+	sbc.arg = blks & 0x0000FFFF;
+	if (do_rel_wr)
+		sbc.arg |= MMC_CMD23_ARG_REL_WR;
+
+	/*  set CMD25/18 */
+	cmd.opcode = (data_type == MMC_DATA_WRITE) ?
+		MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+
+	sg_init_one(&sg, buf, 512 * blks);
+
+	data.blocks = blks;
+	data.sg     = &sg;
+	data.sg_len = 1;
+	data.flags  = data_type;
+	mmc_set_data_timeout(&data, card);
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	if (cmd.error) {
+		dev_err(mmc_dev(card->host), "cmd error (%d)\n", cmd.error);
+		return cmd.error;
+	}
+
+	if (data.error) {
+		dev_err(mmc_dev(card->host), "data error (%d)\n", data.error);
+		return data.error;
+	}
+
+	err = mmc_blk_rpmb_status_poll(card, &status, 5);
+	if (err)
+		dev_err(mmc_dev(card->host), "Card Status=0x%08X, error %d\n",
+			status, err);
+	return err;
+}
+
+static int mmc_blk_rpmb_sequence(struct mmc_card *card,
+				 struct rpmb_cmd *cmds, u32 ncmds)
+{
+	int err, i;
+	struct rpmb_cmd *cmd;
+	unsigned int data_type;
+	bool do_rel_wr;
+
+	for (err = 0, i = 0; i < ncmds && !err; i++) {
+		cmd = &cmds[i];
+		if (cmd->flags & RPMB_F_WRITE) {
+			data_type = MMC_DATA_WRITE;
+			do_rel_wr = !!(cmd->flags & RPMB_F_REL_WRITE);
+		} else {
+			data_type = MMC_DATA_READ;
+			do_rel_wr = false;
+		}
+
+		err =  mmc_rpmb_send_cmd(card, data_type, do_rel_wr,
+					 cmd->frames, cmd->nframes);
+	}
+
+	return err;
+}
+
+static int mmc_blk_rpmb_process(struct mmc_blk_data *md,
+				struct rpmb_cmd *cmds, u32 ncmds)
+{
+	struct mmc_card *card;
+	int ret;
+
+	if (WARN_ON(!md || !cmds || !ncmds))
+		return -EINVAL;
+
+	if (!(md->flags & MMC_BLK_CMD23) ||
+	    (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB))
+		return -EOPNOTSUPP;
+
+	card = md->queue.card;
+	if (!card || !mmc_card_mmc(card))
+		return -ENODEV;
+
+	mmc_get_card(card);
+
+	/* switch to RPMB partition */
+	ret = mmc_blk_part_switch(card, md);
+	if (ret) {
+		dev_err(mmc_dev(card->host), "Invalid RPMB partition switch (%d)!\n",
+			ret);
+		/*
+		 * In case partition is not in user data area, make
+		 * a force partition switch.
+		 * we need reset eMMC card at here
+		 */
+		ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB);
+		if (!ret)
+			mmc_blk_reset_success(md, MMC_BLK_RPMB);
+		else
+			dev_err(mmc_dev(card->host), "eMMC card reset failed (%d)\n",
+				ret);
+		goto out;
+	}
+
+	ret = mmc_blk_rpmb_sequence(card, cmds, ncmds);
+	if (ret)
+		dev_err(mmc_dev(card->host), "failed (%d) to handle RPMB request\n",
+			ret);
+
+	/* Always switch back to main area after RPMB access */
+	mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
+out:
+	mmc_put_card(card);
+	return ret;
+}
+
+static int mmc_blk_rpmb_cmd_seq(struct device *dev,
+				struct rpmb_cmd *cmds, u32 ncmds)
+{
+	struct mmc_blk_data *md;
+	int ret;
+
+	md = mmc_blk_get(dev_to_disk(dev));
+	if (!md)
+		return -ENODEV;
+
+	if (!cmds || !ncmds)
+		return -EINVAL;
+
+	ret = mmc_blk_rpmb_process(md, cmds, ncmds);
+
+	mmc_blk_put(md);
+
+	return ret;
+}
+
+static struct rpmb_ops mmc_rpmb_dev_ops = {
+	.cmd_seq = mmc_blk_rpmb_cmd_seq,
+	.type = RPMB_TYPE_EMMC,
+};
+
+static struct mmc_blk_data *mmc_blk_rpmb_part_get(struct mmc_blk_data *md)
+{
+	struct mmc_blk_data *part_md;
+
+	if (!md)
+		return NULL;
+
+	list_for_each_entry(part_md, &md->part, part) {
+		if (part_md->area_type == MMC_BLK_DATA_AREA_RPMB)
+			return part_md;
+	}
+
+	return NULL;
+}
+
+static void mmc_blk_rpmb_unset_dev_id(struct rpmb_ops *ops)
+{
+	kfree(ops->dev_id);
+	ops->dev_id = NULL;
+}
+
+static int mmc_blk_rpmb_set_dev_id(struct rpmb_ops *ops, struct mmc_card *card)
+{
+	char *id;
+
+	id = kmalloc(sizeof(card->raw_cid), GFP_KERNEL);
+	if (!id)
+		return -ENOMEM;
+
+	memcpy(id, card->raw_cid, sizeof(card->raw_cid));
+	ops->dev_id = id;
+	ops->dev_id_len = sizeof(card->raw_cid);
+
+	return 0;
+}
+
+static void mmc_blk_rpmb_set_rel_wr_cnt(struct rpmb_ops *ops,
+					struct mmc_card *card)
+{
+	u16 rel_wr_cnt;
+
+	/* RPMB blocks are written in half sectors hence '* 2' */
+	rel_wr_cnt = card->ext_csd.rel_sectors * 2;
+	/* eMMC 5.1 may support RPMB 8K (32) frames */
+	if (card->ext_csd.rev >= 8) {
+		if (card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)
+			rel_wr_cnt = 32;
+		else
+			rel_wr_cnt = 2;
+	}
+	ops->reliable_wr_cnt = rel_wr_cnt;
+}
+
+static void mmc_blk_rpmb_add(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+	struct mmc_blk_data *part_md = mmc_blk_rpmb_part_get(md);
+	struct rpmb_dev *rdev;
+
+	if (!part_md)
+		return;
+
+	mmc_blk_rpmb_set_dev_id(&mmc_rpmb_dev_ops, card);
+	mmc_blk_rpmb_set_rel_wr_cnt(&mmc_rpmb_dev_ops, card);
+
+	rdev = rpmb_dev_register(disk_to_dev(part_md->disk),
+				 &mmc_rpmb_dev_ops);
+	if (IS_ERR(rdev)) {
+		pr_warn("%s: cannot register to rpmb %ld\n",
+			part_md->disk->disk_name, PTR_ERR(rdev));
+	}
+}
+
+static void mmc_blk_rpmb_remove(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+	struct mmc_blk_data *part_md = mmc_blk_rpmb_part_get(md);
+
+	if (part_md)
+		rpmb_dev_unregister(disk_to_dev(part_md->disk));
+
+	mmc_blk_rpmb_unset_dev_id(&mmc_rpmb_dev_ops);
+}
+
 static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
 {
 	struct mmc_blk_data *md = mq->data;
@@ -2612,6 +2860,8 @@ static int mmc_blk_probe(struct mmc_card *card)
 			goto out;
 	}
 
+	mmc_blk_rpmb_add(card);
+
 	pm_runtime_set_autosuspend_delay(&card->dev, 3000);
 	pm_runtime_use_autosuspend(&card->dev);
 
@@ -2636,6 +2886,7 @@ static void mmc_blk_remove(struct mmc_card *card)
 {
 	struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
 
+	mmc_blk_rpmb_remove(card);
 	mmc_blk_remove_parts(card, md);
 	pm_runtime_get_sync(&card->dev);
 	mmc_claim_host(card->host);
@@ -2664,6 +2915,7 @@ static int _mmc_blk_suspend(struct mmc_card *card)
 
 static void mmc_blk_shutdown(struct mmc_card *card)
 {
+	mmc_blk_rpmb_remove(card);
 	_mmc_blk_suspend(card);
 }
 
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 07/11] tools rpmb: add RPBM access tool
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Add simple RPMB host testing tool. It can be used
to program key, write and read data block, and retrieve
write counter.

V2: resend
V3: fix missing objtool
V4: add verbose option
V5: 1. Adjust to the new API.
    2. Exercise both request and sequence ioctls.
V6: 1. Add includes to openssl/rand.h and endian.h
    2. Fix some signed, unsigned comparisons
    3. Check results more thoroughly
    4. use HOSTCFLAGS in compilation
    5. Allocate frames dynamically.
V7: 1. Fix rpmb_alloc_frames, it has always allocated one frame instead of
    requested number.
    2. Use an inline function instead of macro for rw blocking wrapper

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 MAINTAINERS           |    1 +
 tools/Makefile        |   14 +-
 tools/rpmb/.gitignore |    2 +
 tools/rpmb/Makefile   |   34 ++
 tools/rpmb/rpmb.c     | 1035 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1081 insertions(+), 5 deletions(-)
 create mode 100644 tools/rpmb/.gitignore
 create mode 100644 tools/rpmb/Makefile
 create mode 100644 tools/rpmb/rpmb.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 090b6d2d31fb..d9bca5134c7f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10292,6 +10292,7 @@ F:	drivers/char/rpmb/*
 F:	include/uapi/linux/rpmb.h
 F:	include/linux/rpmb.h
 F:	Documentation/ABI/testing/sysfs-class-rpmb
+F:	tools/rpmb/
 
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
diff --git a/tools/Makefile b/tools/Makefile
index daa8fb3e4363..1d481b78063f 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -20,6 +20,7 @@ help:
 	@echo '  lguest                 - a minimal 32-bit x86 hypervisor'
 	@echo '  net                    - misc networking tools'
 	@echo '  perf                   - Linux performance measurement and analysis tool'
+	@echo '  rpmb                   - Replay protected memory block access tool'
 	@echo '  selftests              - various kernel selftests'
 	@echo '  spi                    - spi tools'
 	@echo '  objtool                - an ELF object analysis tool'
@@ -56,7 +57,7 @@ acpi: FORCE
 cpupower: FORCE
 	$(call descend,power/$@)
 
-cgroup firewire hv guest spi usb virtio vm net iio gpio objtool: FORCE
+cgroup firewire hv guest rpmb spi usb virtio vm net iio gpio objtool: FORCE
 	$(call descend,$@)
 
 liblockdep: FORCE
@@ -86,7 +87,7 @@ freefall: FORCE
 	$(call descend,laptop/$@)
 
 all: acpi cgroup cpupower gpio hv firewire lguest \
-		perf selftests turbostat usb \
+		perf rpmb selftests turbostat usb \
 		virtio vm net x86_energy_perf_policy \
 		tmon freefall objtool
 
@@ -96,7 +97,7 @@ acpi_install:
 cpupower_install:
 	$(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
+cgroup_install firewire_install gpio_install hv_install lguest_install perf_install rpmb_install usb_install virtio_install vm_install net_install objtool_install:
 	$(call descend,$(@:_install=),install)
 
 selftests_install:
@@ -116,7 +117,7 @@ kvm_stat_install:
 
 install: acpi_install cgroup_install cpupower_install gpio_install \
 		hv_install firewire_install lguest_install \
-		perf_install selftests_install turbostat_install usb_install \
+		perf_install rpmb_install selftests_install turbostat_install usb_install \
 		virtio_install vm_install net_install x86_energy_perf_policy_install \
 		tmon_install freefall_install objtool_install kvm_stat_install
 
@@ -145,6 +146,9 @@ perf_clean:
 	$(Q)mkdir -p $(PERF_O) .
 	$(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= clean
 
+rpmb_clean:
+	$(call descend,$(@:_clean=),clean)
+
 selftests_clean:
 	$(call descend,testing/$(@:_clean=),clean)
 
@@ -161,7 +165,7 @@ build_clean:
 	$(call descend,build,clean)
 
 clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
-		perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
+		perf_clean rpmb_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
 		vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
 		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
 		gpio_clean objtool_clean
diff --git a/tools/rpmb/.gitignore b/tools/rpmb/.gitignore
new file mode 100644
index 000000000000..218f680548e6
--- /dev/null
+++ b/tools/rpmb/.gitignore
@@ -0,0 +1,2 @@
+*.o
+rpmb
diff --git a/tools/rpmb/Makefile b/tools/rpmb/Makefile
new file mode 100644
index 000000000000..debb5a6bc208
--- /dev/null
+++ b/tools/rpmb/Makefile
@@ -0,0 +1,34 @@
+CC = $(CROSS_COMPILE)gcc
+LD = $(CROSS_COMPILE)ld
+PKG_CONFIG = $(CROSS_COMPILE)pkg-config
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+INSTALL = install
+prefix ?= /usr/local
+bindir = $(prefix)/bin
+
+
+CFLAGS += $(HOSTCFLAGS)
+CFLAGS += -D__EXPORTED_HEADERS__ -g -static
+LDFLAGS += -static
+CFLAGS += -I$(srctree)/include/uapi -I$(srctree)/include
+LDLIBS += $(shell $(PKG_CONFIG) --libs --static libcrypto)
+
+prog := rpmb
+
+all : $(prog)
+
+$(prog): rpmb.o
+
+clean :
+	$(RM) $(prog) *.o
+
+install: $(prog)
+	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
+	$(INSTALL) $(prog) $(DESTDIR)$(bindir)
+
diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c
new file mode 100644
index 000000000000..1737ad8a258b
--- /dev/null
+++ b/tools/rpmb/rpmb.c
@@ -0,0 +1,1035 @@
+/******************************************************************************
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *	Intel Corporation.
+ *	linux-mei@linux.intel.com
+ *	http://www.intel.com
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <endian.h>
+
+#include <openssl/engine.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
+#include "linux/rpmb.h"
+
+#define RPMB_KEY_SIZE 32
+#define RPMB_MAC_SIZE 32
+#define RPMB_NONCE_SIZE 16
+
+bool verbose;
+#define rpmb_dbg(fmt, ARGS...) do {                     \
+	if (verbose)                                    \
+		fprintf(stderr, "rpmb: " fmt, ##ARGS);  \
+} while (0)
+
+#define rpmb_msg(fmt, ARGS...) \
+	fprintf(stderr, "rpmb: " fmt, ##ARGS)
+
+#define rpmb_err(fmt, ARGS...) \
+	fprintf(stderr, "rpmb: error: " fmt, ##ARGS)
+
+static const char *rpmb_op_str(uint16_t op)
+{
+#define RPMB_OP(_op) case RPMB_##_op: return #_op
+
+	switch (op) {
+	RPMB_OP(PROGRAM_KEY);
+	RPMB_OP(GET_WRITE_COUNTER);
+	RPMB_OP(WRITE_DATA);
+	RPMB_OP(READ_DATA);
+	RPMB_OP(RESULT_READ);
+	break;
+	default:
+		return "unknown";
+	}
+#undef RPMB_OP
+}
+
+static const char *rpmb_result_str(enum rpmb_op_result result)
+{
+#define str(x) #x
+#define RPMB_ERR(_res) case RPMB_ERR_##_res:         \
+	{ if (result & RPMB_ERR_COUNTER_EXPIRED)     \
+		return "COUNTER_EXPIRE:" str(_res);  \
+	else                                         \
+		return #_res;                        \
+	}
+
+	switch (result & 0x000F) {
+	RPMB_ERR(OK);
+	RPMB_ERR(GENERAL);
+	RPMB_ERR(AUTH);
+	RPMB_ERR(COUNTER);
+	RPMB_ERR(ADDRESS);
+	RPMB_ERR(WRITE);
+	RPMB_ERR(READ);
+	RPMB_ERR(NO_KEY);
+	break;
+	default:
+		return "unknown";
+	}
+#undef RPMB_ERR
+#undef str
+};
+
+static inline void __dump_buffer(const char *buf)
+{
+	fprintf(stderr, "%s\n", buf);
+}
+
+static void
+dump_hex_buffer(const char *title, const void *buf, size_t len)
+{
+	const unsigned char *_buf = (const unsigned char *)buf;
+	const size_t pbufsz = 16 * 3;
+	char pbuf[pbufsz];
+	int j = 0;
+
+	if (title)
+		fprintf(stderr, "%s\n", title);
+	while (len-- > 0) {
+		snprintf(&pbuf[j], pbufsz - j, "%02X ", *_buf++);
+		j += 3;
+		if (j == 16 * 3) {
+			__dump_buffer(pbuf);
+			j = 0;
+		}
+	}
+	if (j)
+		__dump_buffer(pbuf);
+}
+
+static void dbg_dump_frame(const char *title, const struct rpmb_frame *f)
+{
+	uint16_t result, req_resp;
+
+	if (!verbose)
+		return;
+
+	if (!f)
+		return;
+
+	if (title)
+		fprintf(stderr, "%s\n", title);
+
+	result = be16toh(f->result);
+	req_resp = be16toh(f->req_resp);
+	if (req_resp & 0xf00)
+		req_resp = RPMB_RESP2REQ(req_resp);
+
+	fprintf(stderr, "ptr: %p\n", f);
+	dump_hex_buffer("key_mac: ", f->key_mac, 32);
+	dump_hex_buffer("data: ", f->data, 256);
+	dump_hex_buffer("nonce: ", f->nonce, 16);
+	fprintf(stderr, "write_counter: %u\n", be32toh(f->write_counter));
+	fprintf(stderr, "address:  %0X\n", be16toh(f->addr));
+	fprintf(stderr, "block_count: %u\n", be16toh(f->block_count));
+	fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result);
+	fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp));
+}
+
+static int open_dev_file(const char *devfile)
+{
+	int fd;
+
+	fd = open(devfile, O_RDWR);
+	if (fd < 0)
+		rpmb_err("Cannot open: %s: %s\n", devfile, strerror(errno));
+	return fd;
+}
+
+static int open_rd_file(const char *datafile, const char *type)
+{
+	int fd;
+
+	if (!strcmp(datafile, "-"))
+		fd = STDIN_FILENO;
+	else
+		fd = open(datafile, O_RDONLY);
+
+	if (fd < 0)
+		rpmb_err("Cannot open %s: %s: %s\n",
+			 type, datafile, strerror(errno));
+
+	return fd;
+}
+
+static int open_wr_file(const char *datafile, const char *type)
+{
+	int fd;
+
+	if (!strcmp(datafile, "-"))
+		fd = STDOUT_FILENO;
+	else
+		fd = open(datafile, O_WRONLY | O_CREAT | O_APPEND,
+			  S_IRUSR | S_IWUSR);
+	if (fd < 0)
+		rpmb_err("Cannot open %s: %s: %s\n",
+			 type, datafile, strerror(errno));
+	return fd;
+}
+
+static void close_fd(int fd)
+{
+	if (fd > 0 && fd != STDIN_FILENO && fd != STDOUT_FILENO)
+		close(fd);
+}
+
+/* need to just cast out 'const' in write(2) */
+typedef ssize_t (*rwfunc_t)(int fd, void *buf, size_t count);
+/* blocking rw wrapper */
+static inline ssize_t rw(rwfunc_t func, int fd, unsigned char *buf, size_t size)
+{
+	ssize_t ntotal = 0, n;
+	char *_buf = (char *)buf;
+
+	do {
+		n = func(fd, _buf + ntotal, size - ntotal);
+		if (n == -1 && errno != EINTR) {
+			ntotal = -1;
+			break;
+		} else if (n > 0) {
+			ntotal += n;
+		}
+	} while (n != 0 && (size_t)ntotal != size);
+
+	return ntotal;
+}
+
+static ssize_t read_file(int fd, unsigned char *data, size_t size)
+{
+	ssize_t ret;
+
+	ret = rw(read, fd, data, size);
+	if (ret < 0) {
+		rpmb_err("cannot read file: %s\n", strerror(errno));
+	} else if ((size_t)ret != size) {
+		rpmb_err("read %zd but must be %zu bytes length\n", ret, size);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t write_file(int fd, unsigned char *data, size_t size)
+{
+	ssize_t ret;
+
+	ret = rw((rwfunc_t)write, fd, data, size);
+	if (ret < 0) {
+		rpmb_err("cannot read file: %s\n", strerror(errno));
+	} else if ((size_t)ret != size) {
+		rpmb_err("data is %zd but must be %zu bytes length\n",
+			 ret, size);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static struct rpmb_frame *rpmb_alloc_frames(unsigned int cnt)
+{
+	return calloc(cnt, sizeof(struct rpmb_frame));
+}
+
+static int rpmb_calc_hmac_sha256(struct rpmb_frame *frames, size_t blocks_cnt,
+				 const unsigned char key[],
+				 unsigned int key_size,
+				 unsigned char mac[],
+				 unsigned int mac_size)
+{
+	HMAC_CTX ctx;
+	int ret;
+	unsigned int i;
+
+	 /* SSL returns 1 on success 0 on failure */
+
+	HMAC_CTX_init(&ctx);
+	ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
+	if (ret == 0)
+		goto out;
+	for (i = 0; i < blocks_cnt; i++)
+		HMAC_Update(&ctx, frames[i].data, hmac_data_len);
+
+	ret = HMAC_Final(&ctx, mac, &mac_size);
+	if (ret == 0)
+		goto out;
+	if (mac_size != RPMB_MAC_SIZE)
+		ret = 0;
+
+	ret = 1;
+out:
+	HMAC_CTX_cleanup(&ctx);
+	return ret == 1 ? 0 : -1;
+}
+
+static int rpmb_check_req_resp(uint16_t req, struct rpmb_frame *frame_out)
+{
+	if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) {
+		rpmb_err("RPMB response mismatch %04X != %04X\n",
+			 RPMB_REQ2RESP(req), be16toh(frame_out->req_resp));
+		return -1;
+	}
+	return 0;
+}
+
+static int rpmb_check_mac(const unsigned char *key,
+			  struct rpmb_frame *frames_out,
+			  unsigned int cnt_out)
+{
+	unsigned char mac[RPMB_MAC_SIZE];
+
+	if (cnt_out == 0) {
+		rpmb_err("RPMB 0 output frames\n");
+		return -1;
+	}
+
+	rpmb_calc_hmac_sha256(frames_out, cnt_out,
+			      key, RPMB_KEY_SIZE,
+			      mac, RPMB_MAC_SIZE);
+
+	if (memcmp(mac, frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE)) {
+		rpmb_err("RPMB hmac mismatch\n");
+		dump_hex_buffer("Result MAC: ",
+				frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE);
+		dump_hex_buffer("Expected MAC: ", mac, RPMB_MAC_SIZE);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int (*rpmb_ioctl)(int fd, uint16_t req,
+			 const struct rpmb_frame *frames_in,
+			 unsigned int cnt_in,
+			 struct rpmb_frame *frames_out,
+			 unsigned int cnt_out);
+
+static int rpmb_ioctl_seq(int fd, uint16_t req,
+			  const struct rpmb_frame *frames_in,
+			  unsigned int cnt_in,
+			  struct rpmb_frame *frames_out,
+			  unsigned int cnt_out)
+{
+	int ret;
+	struct {
+		struct rpmb_ioc_seq_cmd h;
+		struct rpmb_ioc_cmd cmd[3];
+	} iseq = {};
+	struct rpmb_frame *frame_res = NULL;
+	int i;
+	uint32_t flags;
+
+	rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req));
+	dbg_dump_frame("In Frame: ", frames_in);
+
+	i = 0;
+	flags = RPMB_F_WRITE;
+	if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY)
+		flags |= RPMB_F_REL_WRITE;
+	rpmb_ioc_cmd_set(iseq.cmd[i], flags, frames_in, cnt_in);
+	i++;
+
+	if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) {
+		frame_res = rpmb_alloc_frames(1);
+		if (!frame_res)
+			return -ENOMEM;
+		frame_res->req_resp =  htobe16(RPMB_RESULT_READ);
+		rpmb_ioc_cmd_set(iseq.cmd[i], RPMB_F_WRITE, frame_res, 1);
+		i++;
+	}
+
+	rpmb_ioc_cmd_set(iseq.cmd[i], 0, frames_out, cnt_out);
+	i++;
+
+	iseq.h.num_of_cmds = i;
+	ret = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq);
+	if (ret < 0)
+		rpmb_err("ioctl failure %d: %s\n", ret, strerror(errno));
+
+	ret = rpmb_check_req_resp(req, frames_out);
+
+	dbg_dump_frame("Res Frame: ", frame_res);
+	dbg_dump_frame("Out Frame: ", frames_out);
+	free(frame_res);
+	return ret;
+}
+
+static int rpmb_ioctl_req(int fd, uint16_t req,
+			  const struct rpmb_frame *frames_in,
+			  unsigned int cnt_in,
+			  struct rpmb_frame *frames_out,
+			  unsigned int cnt_out)
+{
+	struct rpmb_ioc_req_cmd ireq;
+	int ret;
+
+	ireq.req_type = req;
+	rpmb_ioc_cmd_set(ireq.icmd, RPMB_F_WRITE, frames_in, cnt_in);
+	rpmb_ioc_cmd_set(ireq.ocmd, 0, frames_out, cnt_out);
+
+	rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req));
+	dbg_dump_frame("In Frame: ", frames_in);
+	ret = ioctl(fd, RPMB_IOC_REQ_CMD, &ireq);
+	if (ret < 0)
+		rpmb_err("ioctl failure %d: %s\n", ret, strerror(errno));
+
+	ret = rpmb_check_req_resp(req, frames_out);
+	dbg_dump_frame("Out Frame: ", frames_out);
+
+	return ret;
+}
+
+static int op_rpmb_program_key(int nargs, char *argv[])
+{
+	int ret;
+	int  dev_fd = -1, key_fd = -1;
+	uint16_t req = RPMB_PROGRAM_KEY;
+	struct rpmb_frame *frame_in = NULL, *frame_out = NULL;
+
+	ret = -EINVAL;
+	if (nargs != 2)
+		return ret;
+
+	dev_fd = open_dev_file(argv[0]);
+	if (dev_fd < 0)
+		goto out;
+	argv++;
+
+	key_fd = open_rd_file(argv[0], "key file");
+	if (key_fd < 0)
+		goto out;
+	argv++;
+
+	frame_in = rpmb_alloc_frames(1);
+	frame_out = rpmb_alloc_frames(1);
+	if (!frame_in || !frame_out) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	frame_in->req_resp = htobe16(req);
+
+	read_file(key_fd, frame_in->key_mac, RPMB_KEY_SIZE);
+
+	ret = rpmb_ioctl(dev_fd, req, frame_in, 1, frame_out, 1);
+	if (ret)
+		goto out;
+
+	if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) {
+		rpmb_err("RPMB response mismatch\n");
+		ret = -1;
+		goto out;
+	}
+
+	ret = be16toh(frame_out->result);
+	if (ret)
+		rpmb_err("RPMB operation %s failed, %s[0x%04x]\n",
+			 rpmb_op_str(req), rpmb_result_str(ret), ret);
+
+out:
+	free(frame_in);
+	free(frame_out);
+	close_fd(dev_fd);
+	close_fd(key_fd);
+
+	return ret;
+}
+
+static int rpmb_get_write_counter(int dev_fd, unsigned int *cnt,
+				  const unsigned char *key)
+{
+	int ret;
+	uint16_t res = 0x000F;
+	uint16_t req = RPMB_GET_WRITE_COUNTER;
+	struct rpmb_frame *frame_in = NULL;
+	struct rpmb_frame *frame_out = NULL;
+
+	frame_in = rpmb_alloc_frames(1);
+	frame_out = rpmb_alloc_frames(1);
+	if (!frame_in || !frame_out) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	frame_in->req_resp = htobe16(req);
+	RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE);
+
+	ret = rpmb_ioctl(dev_fd, req, frame_in, 1, frame_out, 1);
+	if (ret != 0)
+		return ret;
+
+	res = be16toh(frame_out->result);
+	if (res != RPMB_ERR_OK) {
+		ret = -1;
+		goto out;
+	}
+
+	if (memcmp(&frame_in->nonce, &frame_out->nonce, RPMB_NONCE_SIZE)) {
+		rpmb_err("RPMB NONCE mismatch\n");
+		dump_hex_buffer("Result NONCE:",
+				&frame_out->nonce, RPMB_NONCE_SIZE);
+		dump_hex_buffer("Expected NONCE: ",
+				&frame_in->nonce, RPMB_NONCE_SIZE);
+		ret = -1;
+		goto out;
+	}
+
+	if (key) {
+		ret = rpmb_check_mac(key, frame_out, 1);
+		if (ret)
+			goto out;
+	}
+
+	*cnt = be32toh(frame_out->write_counter);
+
+out:
+	if (ret)
+		rpmb_err("RPMB operation %s failed, %s[0x%04x]\n",
+			 rpmb_op_str(req), rpmb_result_str(res), res);
+	return ret;
+}
+
+static int op_rpmb_get_write_counter(int nargs, char **argv)
+{
+	int ret;
+	int dev_fd = -1, key_fd = -1;
+	bool has_key;
+	unsigned char key[RPMB_KEY_SIZE];
+	unsigned int cnt;
+
+	if (nargs == 2)
+		has_key = true;
+	else if (nargs == 1)
+		has_key = false;
+	else
+		return -EINVAL;
+
+	ret = -EINVAL;
+	dev_fd = open_dev_file(argv[0]);
+	if (dev_fd < 0)
+		return ret;
+	argv++;
+
+	if (has_key) {
+		key_fd = open_rd_file(argv[0], "key file");
+		if (key_fd < 0)
+			goto out;
+		argv++;
+
+		ret = read_file(key_fd, key, RPMB_KEY_SIZE);
+		if (ret < 0)
+			goto out;
+
+		ret = rpmb_get_write_counter(dev_fd, &cnt, key);
+	} else {
+		ret = rpmb_get_write_counter(dev_fd, &cnt, NULL);
+	}
+
+	if (!ret)
+		printf("Counter value: 0x%08x\n", cnt);
+
+out:
+	close_fd(dev_fd);
+	close_fd(key_fd);
+	return ret;
+}
+
+static int op_rpmb_read_blocks(int nargs, char **argv)
+{
+	int i, ret;
+	int dev_fd = -1, data_fd = -1, key_fd = -1;
+	uint16_t req = RPMB_READ_DATA;
+	uint16_t addr, blocks_cnt;
+	unsigned char key[RPMB_KEY_SIZE];
+	unsigned long numarg;
+	bool has_key;
+	struct rpmb_frame *frame_in = NULL;
+	struct rpmb_frame *frames_out = NULL;
+	struct rpmb_frame *frame_out;
+
+	ret = -EINVAL;
+	if (nargs == 4)
+		has_key = false;
+	else if (nargs == 5)
+		has_key = true;
+	else
+		return ret;
+
+	dev_fd = open_dev_file(argv[0]);
+	if (dev_fd < 0)
+		goto out;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong block address\n");
+		goto out;
+	}
+	addr = (uint16_t)numarg;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+	blocks_cnt = (uint16_t)numarg;
+	argv++;
+
+	if (blocks_cnt == 0) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+
+	data_fd = open_wr_file(argv[0], "output data");
+	if (data_fd < 0)
+		goto out;
+	argv++;
+
+	if (has_key) {
+		key_fd = open_rd_file(argv[0], "key file");
+		if (key_fd < 0)
+			goto out;
+		argv++;
+
+		ret = read_file(key_fd, key, RPMB_KEY_SIZE);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = 0;
+	frames_out = rpmb_alloc_frames(blocks_cnt);
+	frame_in = rpmb_alloc_frames(1);
+	if (!frames_out || !frame_in) {
+		rpmb_err("Cannot allocate %d RPMB frames\n", blocks_cnt);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	frame_in->req_resp = htobe16(req);
+	frame_in->addr = htobe16(addr);
+	/* eMMc spec ask for 0 here this will be translated by the rpmb layer */
+	frame_in->block_count = htobe16(blocks_cnt);
+	if (has_key)
+		RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE);
+
+	ret = rpmb_ioctl(dev_fd, req, frame_in, 1, frames_out, blocks_cnt);
+	if (ret)
+		goto out;
+
+	frame_out = &frames_out[blocks_cnt - 1];
+	ret = be16toh(frame_out->result);
+	if (ret) {
+		rpmb_err("RPMB operation %s failed, %s[0x%04x]\n",
+			 rpmb_op_str(req), rpmb_result_str(ret), ret);
+		goto out;
+	}
+
+	if (has_key) {
+		ret = rpmb_check_mac(key, frames_out, blocks_cnt);
+		if (ret)
+			goto out;
+	}
+
+	for (i = 0; i < blocks_cnt; i++) {
+		ret = write_file(data_fd, frames_out[i].data,
+				 sizeof(frames_out[i].data));
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	free(frame_in);
+	free(frames_out);
+	close_fd(dev_fd);
+	close_fd(data_fd);
+	close_fd(key_fd);
+
+	return ret;
+}
+
+static int op_rpmb_write_blocks(int nargs, char **argv)
+{
+	int ret;
+	int dev_fd = -1, key_fd = -1, data_fd = -1;
+	int i;
+	uint16_t req = RPMB_WRITE_DATA;
+	unsigned char key[RPMB_KEY_SIZE];
+	unsigned char mac[RPMB_MAC_SIZE];
+	unsigned long numarg;
+	uint16_t addr, blocks_cnt;
+	uint32_t write_counter;
+	struct rpmb_frame *frames_in = NULL;
+	struct rpmb_frame *frame_out = NULL;
+
+	ret = -EINVAL;
+	if (nargs != 5)
+		goto out;
+
+	dev_fd = open_dev_file(argv[0]);
+	if (dev_fd < 0)
+		goto out;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong block address %s\n", argv[0]);
+		goto out;
+	}
+	addr = (uint16_t)numarg;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+	blocks_cnt = (uint16_t)numarg;
+	argv++;
+
+	if (blocks_cnt == 0) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+
+	data_fd = open_rd_file(argv[0], "data file");
+	if (data_fd < 0)
+		goto out;
+	argv++;
+
+	key_fd = open_rd_file(argv[0], "key file");
+	if (key_fd < 0)
+		goto out;
+	argv++;
+
+	ret = read_file(key_fd, key, RPMB_KEY_SIZE);
+	if (ret < 0)
+		goto out;
+
+	frames_in = rpmb_alloc_frames(blocks_cnt);
+	frame_out = rpmb_alloc_frames(1);
+	if (!frames_in || !frame_out) {
+		rpmb_err("can't allocate memory for RPMB outer frames\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = rpmb_get_write_counter(dev_fd, &write_counter, key);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < blocks_cnt; i++) {
+		frames_in[i].req_resp      = htobe16(req);
+		frames_in[i].block_count   = htobe16(blocks_cnt);
+		frames_in[i].addr          = htobe16(addr);
+		frames_in[i].write_counter = htobe32(write_counter);
+	}
+
+	for (i = 0; i < blocks_cnt; i++) {
+		ret = read_file(data_fd, frames_in[i].data,
+				sizeof(frames_in[0].data));
+		if (ret < 0)
+			goto out;
+	}
+
+	rpmb_calc_hmac_sha256(frames_in, blocks_cnt,
+			      key, RPMB_KEY_SIZE,
+			      mac, RPMB_MAC_SIZE);
+	memcpy(frames_in[blocks_cnt - 1].key_mac, mac, RPMB_MAC_SIZE);
+	ret = rpmb_ioctl(dev_fd, req, frames_in, blocks_cnt, frame_out, 1);
+	if (ret != 0)
+		goto out;
+
+	ret = be16toh(frame_out->result);
+	if (ret) {
+		rpmb_err("RPMB operation %s failed, %s[0x%04x]\n",
+			 rpmb_op_str(req), rpmb_result_str(ret), ret);
+		ret = -1;
+	}
+
+	if (be16toh(frame_out->addr) != addr) {
+		rpmb_err("RPMB addr mismatchs res=%04x req=%04x\n",
+			 be16toh(frame_out->addr), addr);
+		ret = -1;
+	}
+
+	if (be32toh(frame_out->write_counter) <= write_counter) {
+		rpmb_err("RPMB write counter not incremeted res=%x req=%x\n",
+			 be32toh(frame_out->write_counter), write_counter);
+		ret = -1;
+	}
+
+	ret = rpmb_check_mac(key, frame_out, 1);
+out:
+	free(frames_in);
+	free(frame_out);
+	close_fd(dev_fd);
+	close_fd(data_fd);
+	close_fd(key_fd);
+	return ret;
+}
+
+typedef int (*rpmb_op)(int argc, char *argv[]);
+
+struct rpmb_cmd {
+	const char *op_name;
+	rpmb_op     op;
+	const char  *usage; /* usage title */
+	const char  *help;  /* help */
+};
+
+static const struct rpmb_cmd cmds[] = {
+	{
+	 "program-key",
+	 op_rpmb_program_key,
+	 "<RPMB_DEVICE> <KEY_FILE>",
+	 "    Program authentication key of 32 bytes length from the KEY_FILE\n"
+	 "    when KEY_FILE is -, read standard input.\n"
+	 "    NOTE: This is a one-time programmable irreversible change.\n",
+	},
+	{
+	 "write-counter",
+	 op_rpmb_get_write_counter,
+	 "<RPMB_DEVICE> [KEY_FILE]",
+	 "    Rertrive write counter value from the <RPMB_DEVICE> to stdout.\n"
+	 "    When KEY_FILE is present data is verified via HMAC\n"
+	 "    when KEY_FILE is -, read standard input.\n"
+	},
+	{
+	  "write-blocks",
+	  op_rpmb_write_blocks,
+	  "<RPMB_DEVICE> <address> <block_count> <DATA_FILE> <KEY_FILE>",
+	  "    <block count> of 256 bytes will be written from the DATA_FILE\n"
+	  "    to the <RPMB_DEVICE> at block offset <address>.\n"
+	  "    When DATA_FILE is -, read from standard input.\n",
+	},
+	{
+	  "read-blocks",
+	  op_rpmb_read_blocks,
+	  "<RPMB_DEVICE> <address> <blocks count> <OUTPUT_FILE> [KEY_FILE]",
+	  "    <block count> of 256 bytes will be read from <RPMB_DEVICE>\n"
+	  "    to the OUTPUT_FILE\n"
+	  "    When KEY_FILE is present data is verified via HMAC\n"
+	  "    When OUTPUT/KEY_FILE is -, read from standard input.\n"
+	  "    When OUTPUT_FILE is -, write to standard output\n",
+	},
+
+	{ NULL, NULL, NULL, NULL }
+};
+
+static void help(const char *prog, const struct rpmb_cmd *cmd)
+{
+	printf("%s %s %s\n", prog, cmd->op_name, cmd->usage);
+	printf("%s\n", cmd->help);
+}
+
+static void usage(const char *prog)
+{
+	int i;
+
+	printf("\n");
+	printf("Usage: %s [-v] [-r|-s] <command> <args>\n\n", prog);
+	for (i = 0; cmds[i].op_name; i++)
+		printf("       %s %s %s\n",
+		       prog, cmds[i].op_name, cmds[i].usage);
+
+	printf("\n");
+	printf("      %s -v/--verbose: runs in verbose mode\n", prog);
+	printf("      %s -s/ --sequence: use RPMB_IOC_SEQ_CMD\n", prog);
+	printf("      %s -r/--request: use RPMB_IOC_REQ_CMD\n", prog);
+	printf("      %s help : shows this help\n", prog);
+	printf("      %s help <command>: shows detailed help\n", prog);
+}
+
+static bool call_for_help(const char *arg)
+{
+	return !strcmp(arg, "help") ||
+	       !strcmp(arg, "-h")   ||
+	       !strcmp(arg, "--help");
+}
+
+static bool parse_verbose(const char *arg)
+{
+	return !strcmp(arg, "-v") ||
+	       !strcmp(arg, "--verbose");
+}
+
+static bool parse_req(const char *arg)
+{
+	return !strcmp(arg, "-r") ||
+	       !strcmp(arg, "--request");
+}
+
+static bool parse_seq(const char *arg)
+{
+	return !strcmp(arg, "-s") ||
+	       !strcmp(arg, "--sequence");
+}
+
+static const
+struct rpmb_cmd *parse_args(const char *prog, int *_argc, char **_argv[])
+{
+	int i;
+	int argc = *_argc;
+	char **argv =  *_argv;
+	const struct rpmb_cmd *cmd = NULL;
+	bool need_help = false;
+
+	argc--; argv++;
+
+	if (argc == 0)
+		goto out;
+
+	if (call_for_help(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		need_help = true;
+	}
+
+	if (parse_verbose(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		verbose = true;
+	}
+
+	if (parse_req(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		rpmb_ioctl = rpmb_ioctl_req;
+	}
+
+	if (parse_seq(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		rpmb_ioctl = rpmb_ioctl_seq;
+	}
+
+	if (!rpmb_ioctl)
+		rpmb_ioctl = rpmb_ioctl_req;
+
+	for (i = 0; cmds[i].op_name; i++) {
+		if (!strncmp(argv[0], cmds[i].op_name,
+			     strlen(cmds[i].op_name))) {
+			cmd = &cmds[i];
+			argc--; argv++;
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto out;
+
+	if (need_help || (argc > 0 && call_for_help(argv[0]))) {
+		help(prog, cmd);
+		argc--; argv++;
+		return NULL;
+	}
+
+out:
+	*_argc = argc;
+	*_argv = argv;
+
+	if (!cmd)
+		usage(prog);
+
+	return cmd;
+}
+
+int main(int argc, char *argv[])
+{
+	const char *prog = basename(argv[0]);
+	const struct rpmb_cmd *cmd;
+	int ret;
+
+	cmd = parse_args(prog, &argc, &argv);
+	if (!cmd)
+		exit(EXIT_SUCCESS);
+
+	ret = cmd->op(argc, argv);
+	if (ret == -EINVAL)
+		help(prog, cmd);
+
+	if (ret)
+		exit(EXIT_FAILURE);
+
+	exit(EXIT_SUCCESS);
+}
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 06/11] char: rpmb: add RPMB simulation device
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

The RPMB partition simulation device is a virtual device that
provides simulation of the RPMB protocol and use kernel memory
as storage.

Be aware it doesn't promise any real security. This driver is
suitable only for testing of the RPMB subsystem or RPMB
applications prior to RPMB key provisioning, as RPMB key
programming can be performed only once in the life time of the
storage device.

The module currently supports two configuration options via
module parameters
1. max_wr_blks: for specifying max blocks that can be written
in a single command
2. daunits:  used to set storage capacity in 128K units.

V2: remove .owner setting, it is set automatically
V3: 1. Add shutdown handler (similar to ufshcd)
    2. Commit message fix
V4: Use select RPMB in Kconfg to ensure valid configuration.
V5: Revamp the code using the sequence command.
V6: 1. Be more verbose about some errors, after all this is a testing
    module.
    2. Fix RPMB_READ_DATA:
       a. The number of blocks for eMMC request frame should be 0
       b. Fix missing return before bailing on error
       c. Copy all the frames back
    3. Fix RPMB_WRITE_DATA:
       a. Compute MAC on result packet
       b. Also address should be set in the result frame.
    4. Remove platform device
    5. Update the commit message
V7: Resend.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 drivers/char/rpmb/Kconfig    |  17 +
 drivers/char/rpmb/Makefile   |   1 +
 drivers/char/rpmb/rpmb_sim.c | 744 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 762 insertions(+)
 create mode 100644 drivers/char/rpmb/rpmb_sim.c

diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
index 6794be9fcc5e..533d813e9ed5 100644
--- a/drivers/char/rpmb/Kconfig
+++ b/drivers/char/rpmb/Kconfig
@@ -13,3 +13,20 @@ config RPMB_INTF_DEV
 	help
 	  Say yes here if you want to access RPMB from user space
 	  via character device interface /dev/rpmb%d
+
+
+config RPMB_SIM
+	tristate "RPMB partition device simulator"
+	default n
+	select RPMB
+	select CRYPTO_SHA256
+	select CRYPTO_HMAC
+	help
+	  RPMB partition simulation device is a virtual device that
+	  provides simulation of the RPMB protocol and use kernel memory
+	  as storage.
+
+	  Be aware it doesn't promise any real security. This driver is
+	  suitable only for testing of the RPMB subsystem or RPMB applications
+	  prior to RPMB key provisioning.
+          Most people should say N here.
diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
index b5dc087b1299..81f924fd9a87 100644
--- a/drivers/char/rpmb/Makefile
+++ b/drivers/char/rpmb/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_RPMB) += rpmb.o
 rpmb-objs += core.o
 rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
+obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/rpmb_sim.c b/drivers/char/rpmb/rpmb_sim.c
new file mode 100644
index 000000000000..429a08bd67d7
--- /dev/null
+++ b/drivers/char/rpmb/rpmb_sim.c
@@ -0,0 +1,744 @@
+/******************************************************************************
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *	Intel Corporation.
+ *	linux-mei@linux.intel.com
+ *	http://www.intel.com
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <crypto/hash.h>
+#include <linux/scatterlist.h>
+#include <linux/sizes.h>
+
+#include <linux/rpmb.h>
+
+static const char id[] = "RPMB:SIM";
+#define CAPACITY_UNIT SZ_128K
+#define CAPACITY_MIN  SZ_128K
+#define CAPACITY_MAX  SZ_16M
+#define BLK_UNIT      SZ_256
+
+static unsigned int max_wr_blks = 2;
+module_param(max_wr_blks, uint, 0644);
+MODULE_PARM_DESC(max_wr_blks, "max blocks that can be written in a single command (default: 2)");
+
+static unsigned int daunits = 1;
+module_param(daunits, uint, 0644);
+MODULE_PARM_DESC(daunits, "number of data area units of 128K (default: 1)");
+
+struct blk {
+	u8 data[BLK_UNIT];
+};
+
+/**
+ * struct rpmb_sim_dev
+ *
+ * @dev:  back pointer device
+ * @rdev: rpmb device
+ * @auth_key: Authentication key register which is used to authenticate
+ *            accesses when MAC is calculated;
+ * @auth_key_set: true if auth key was set
+ * @write_counter: Counter value for the total amount of successful
+ *             authenticated data write requests made by the host.
+ *             The initial value of this register after production is 00000000h.
+ *             The value will be incremented by one along with each successful
+ *             programming access. The value cannot be reset. After the counter
+ *             has reached the maximum value of FFFFFFFFh,
+ *             it will not be incremented anymore (overflow prevention)
+ * @hash_tfm:  hmac(sha256) tfm
+ *
+ * @res_frames: frame that holds the result of the last write operation
+ * @out_frames: next read operation result frames
+ * @out_frames_cnt: number of the output frames
+ *
+ * @capacity: size of the partition in bytes multiple of 128K
+ * @blkcnt:   block count
+ * @da:       data area in blocks
+ */
+struct rpmb_sim_dev {
+	struct device *dev;
+	struct rpmb_dev *rdev;
+	u8 auth_key[32];
+	bool auth_key_set;
+	u32 write_counter;
+	struct crypto_shash *hash_tfm;
+
+	struct rpmb_frame res_frames[1];
+	struct rpmb_frame *out_frames;
+	unsigned int out_frames_cnt;
+
+	size_t capacity;
+	size_t blkcnt;
+	struct blk *da;
+};
+
+static __be16 op_result(struct rpmb_sim_dev *rsdev, u16 result)
+{
+	if (!rsdev->auth_key_set)
+		return cpu_to_be16(RPMB_ERR_NO_KEY);
+
+	if (rsdev->write_counter == 0xFFFFFFFF)
+		result |=  RPMB_ERR_COUNTER_EXPIRED;
+
+	return cpu_to_be16(result);
+}
+
+static __be16 req_to_resp(u16 req)
+{
+	return cpu_to_be16(RPMB_REQ2RESP(req));
+}
+
+static int rpmb_sim_calc_hmac(struct rpmb_sim_dev *rsdev,
+			      struct rpmb_frame *frames,
+			      unsigned int blks, u8 *mac)
+{
+	SHASH_DESC_ON_STACK(desc, rsdev->hash_tfm);
+	int i;
+	int ret;
+
+	desc->tfm = rsdev->hash_tfm;
+	desc->flags = 0;
+
+	ret = crypto_shash_init(desc);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < blks; i++) {
+		ret = crypto_shash_update(desc, frames[i].data, hmac_data_len);
+		if (ret)
+			goto out;
+	}
+	ret = crypto_shash_final(desc, mac);
+out:
+	if (ret)
+		dev_err(rsdev->dev, "digest error = %d", ret);
+
+	return ret;
+}
+
+static int rpmb_op_not_programmed(struct rpmb_sim_dev *rsdev, u16 req)
+{
+	struct rpmb_frame *res_frame = rsdev->res_frames;
+
+	res_frame->req_resp = req_to_resp(req);
+	res_frame->result = op_result(rsdev, RPMB_ERR_NO_KEY);
+
+	dev_err(rsdev->dev, "not programmed\n");
+
+	return 0;
+}
+
+static int rpmb_op_program_key(struct rpmb_sim_dev *rsdev,
+			       struct rpmb_frame *in_frame, u32 cnt)
+{
+	struct rpmb_frame *res_frame = rsdev->res_frames;
+	u16 req;
+	int ret;
+	u16 err = RPMB_ERR_OK;
+
+	req = be16_to_cpu(in_frame[0].req_resp);
+
+	if (req != RPMB_PROGRAM_KEY)
+		return -EINVAL;
+
+	if (cnt != 1) {
+		dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt);
+		return -EINVAL;
+	}
+
+	if (rsdev->auth_key_set) {
+		dev_err(rsdev->dev, "key allread set\n");
+		err = RPMB_ERR_WRITE;
+		goto out;
+	}
+
+	ret = crypto_shash_setkey(rsdev->hash_tfm, in_frame[0].key_mac, 32);
+	if (ret) {
+		dev_err(rsdev->dev, "set key failed = %d\n", ret);
+		err = RPMB_ERR_GENERAL;
+		goto out;
+	}
+
+	dev_dbg(rsdev->dev, "digest size %u\n",
+		crypto_shash_digestsize(rsdev->hash_tfm));
+
+	memcpy(rsdev->auth_key, in_frame[0].key_mac, 32);
+	rsdev->auth_key_set = true;
+out:
+
+	memset(res_frame, 0, sizeof(struct rpmb_frame));
+	res_frame->req_resp = req_to_resp(req);
+	res_frame->result = op_result(rsdev, err);
+
+	return 0;
+}
+
+static int rpmb_op_get_wr_counter(struct rpmb_sim_dev *rsdev,
+				  struct rpmb_frame *in_frame, u32 cnt)
+{
+	struct rpmb_frame *frame;
+	int ret = 0;
+	u16 req;
+	u16 err;
+
+	req = be16_to_cpu(in_frame[0].req_resp);
+	if (req != RPMB_GET_WRITE_COUNTER)
+		return -EINVAL;
+
+	if (cnt != 1) {
+		dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt);
+		return -EINVAL;
+	}
+
+	frame = kcalloc(1, sizeof(struct rpmb_frame), GFP_KERNEL);
+	if (!frame) {
+		err = RPMB_ERR_READ;
+		ret = -ENOMEM;
+		rsdev->out_frames = rsdev->res_frames;
+		rsdev->out_frames_cnt = cnt;
+		goto out;
+	}
+
+	rsdev->out_frames = frame;
+	rsdev->out_frames_cnt = cnt;
+
+	frame->req_resp = req_to_resp(req);
+	frame->write_counter = cpu_to_be32(rsdev->write_counter);
+	memcpy(frame->nonce, in_frame[0].nonce, 16);
+
+	err = RPMB_ERR_OK;
+	if (rpmb_sim_calc_hmac(rsdev, frame, cnt, frame->key_mac))
+		err = RPMB_ERR_READ;
+
+out:
+	rsdev->out_frames[0].req_resp = req_to_resp(req);
+	rsdev->out_frames[0].result = op_result(rsdev, err);
+
+	return ret;
+}
+
+static int rpmb_op_write_data(struct rpmb_sim_dev *rsdev,
+			      struct rpmb_frame *in_frame, u32 cnt)
+{
+	struct rpmb_frame *res_frame = rsdev->res_frames;
+	u8 mac[32];
+	u16 req, err, addr, blks;
+	unsigned int i;
+	int ret = 0;
+
+	req = be16_to_cpu(in_frame[0].req_resp);
+	if (req != RPMB_WRITE_DATA)
+		return -EINVAL;
+
+	if (rsdev->write_counter == 0xFFFFFFFF) {
+		err = RPMB_ERR_WRITE;
+		goto out;
+	}
+
+	blks = be16_to_cpu(in_frame[0].block_count);
+	if (blks == 0 || blks > cnt) {
+		dev_err(rsdev->dev, "wrong number of frames %u > %u\n",
+			blks, cnt);
+		ret = -EINVAL;
+		err = RPMB_ERR_GENERAL;
+		goto out;
+	}
+
+	if (blks > max_wr_blks) {
+		err = RPMB_ERR_WRITE;
+		goto out;
+	}
+
+	addr = be16_to_cpu(in_frame[0].addr);
+	if (addr >= rsdev->blkcnt) {
+		err = RPMB_ERR_ADDRESS;
+		goto out;
+	}
+
+	if (rpmb_sim_calc_hmac(rsdev, in_frame, blks, mac)) {
+		err = RPMB_ERR_AUTH;
+		goto out;
+	}
+
+	/* mac is in the last frame */
+	if (memcmp(mac, in_frame[blks - 1].key_mac, sizeof(mac)) != 0) {
+		err = RPMB_ERR_AUTH;
+		goto out;
+	}
+
+	if (be32_to_cpu(in_frame[0].write_counter) != rsdev->write_counter) {
+		err = RPMB_ERR_COUNTER;
+		goto out;
+	}
+
+	if (addr + blks > rsdev->blkcnt) {
+		err = RPMB_ERR_WRITE;
+		goto out;
+	}
+
+	dev_dbg(rsdev->dev, "Writing = %u blokcs at addr = 0x%X\n", blks, addr);
+	err = RPMB_ERR_OK;
+	for (i = 0; i < blks; i++)
+		memcpy(rsdev->da[addr + i].data, in_frame[i].data, BLK_UNIT);
+
+	rsdev->write_counter++;
+
+	memset(res_frame, 0, sizeof(struct rpmb_frame));
+	res_frame->req_resp = req_to_resp(req);
+	res_frame->write_counter = cpu_to_be32(rsdev->write_counter);
+	res_frame->addr = cpu_to_be16(addr);
+	if (rpmb_sim_calc_hmac(rsdev, res_frame, 1, res_frame->key_mac))
+		err = RPMB_ERR_READ;
+
+out:
+	if (err != RPMB_ERR_OK) {
+		memset(res_frame, 0, sizeof(struct rpmb_frame));
+		res_frame->req_resp = req_to_resp(req);
+	}
+	res_frame->result = op_result(rsdev, err);
+
+	return ret;
+}
+
+static int rpmb_do_read_data(struct rpmb_sim_dev *rsdev,
+			     struct rpmb_frame *in_frame, u32 cnt)
+{
+	struct rpmb_frame *res_frame = rsdev->res_frames;
+	struct rpmb_frame *out_frames = NULL;
+	u8 mac[32];
+	u16 req, err, addr, blks;
+	unsigned int i;
+	int ret;
+
+	req = be16_to_cpu(in_frame->req_resp);
+	if (req != RPMB_READ_DATA)
+		return -EINVAL;
+
+	/* eMMc intentially set 0 here */
+	blks = be16_to_cpu(in_frame->block_count);
+	blks = blks ?: cnt;
+	if (blks > cnt) {
+		dev_err(rsdev->dev, "wrong number of frames cnt %u\n", blks);
+		ret = -EINVAL;
+		err = RPMB_ERR_GENERAL;
+		goto out;
+	}
+
+	out_frames = kcalloc(blks, sizeof(struct rpmb_frame), GFP_KERNEL);
+	if (!out_frames) {
+		ret = -ENOMEM;
+		err = RPMB_ERR_READ;
+		goto out;
+	}
+
+	ret = 0;
+	addr = be16_to_cpu(in_frame[0].addr);
+	if (addr >= rsdev->blkcnt) {
+		err = RPMB_ERR_ADDRESS;
+		goto out;
+	}
+
+	if (addr + blks > rsdev->blkcnt) {
+		err = RPMB_ERR_READ;
+		goto out;
+	}
+
+	dev_dbg(rsdev->dev, "reading = %u blokcs at addr = 0x%X\n", blks, addr);
+	for (i = 0; i < blks; i++) {
+		memcpy(out_frames[i].data, rsdev->da[addr + i].data, BLK_UNIT);
+		memcpy(out_frames[i].nonce, in_frame[0].nonce, 16);
+		out_frames[i].req_resp = req_to_resp(req);
+		out_frames[i].addr = in_frame[0].addr;
+		out_frames[i].block_count = cpu_to_be16(blks);
+	}
+
+	if (rpmb_sim_calc_hmac(rsdev, out_frames, blks, mac)) {
+		err = RPMB_ERR_AUTH;
+		goto out;
+	}
+
+	memcpy(out_frames[blks - 1].key_mac, mac, sizeof(mac));
+
+	err = RPMB_ERR_OK;
+	for (i = 0; i < blks; i++)
+		out_frames[i].result = op_result(rsdev, err);
+
+	rsdev->out_frames = out_frames;
+	rsdev->out_frames_cnt = cnt;
+
+	return 0;
+
+out:
+	memset(res_frame, 0, sizeof(struct rpmb_frame));
+	res_frame->req_resp = req_to_resp(req);
+	res_frame->result = op_result(rsdev, err);
+	kfree(out_frames);
+	rsdev->out_frames = res_frame;
+	rsdev->out_frames_cnt = 1;
+
+	return ret;
+}
+
+static int rpmb_op_read_data(struct rpmb_sim_dev *rsdev,
+			     struct rpmb_frame *in_frame, u32 cnt)
+{
+	struct rpmb_frame *res_frame = rsdev->res_frames;
+	u16 req;
+
+	req = be16_to_cpu(in_frame->req_resp);
+	if (req != RPMB_READ_DATA)
+		return -EINVAL;
+
+	memcpy(res_frame, in_frame, sizeof(*res_frame));
+
+	rsdev->out_frames = res_frame;
+	rsdev->out_frames_cnt = 1;
+
+	return 0;
+}
+
+static int rpmb_op_result_read(struct rpmb_sim_dev *rsdev,
+			       struct rpmb_frame *frames, u32 cnt)
+{
+	u16 req = be16_to_cpu(frames[0].req_resp);
+	u16 blks = be16_to_cpu(frames[0].block_count);
+
+	if (req != RPMB_RESULT_READ)
+		return -EINVAL;
+
+	if (blks != 0) {
+		dev_err(rsdev->dev, "wrong number of frames %u != 0\n",  blks);
+		return -EINVAL;
+	}
+
+	rsdev->out_frames = rsdev->res_frames;
+	rsdev->out_frames_cnt = 1;
+	return 0;
+}
+
+static int rpmb_sim_write(struct rpmb_sim_dev *rsdev,
+			  struct rpmb_frame *frames, u32 cnt)
+{
+	u16 req;
+	int ret;
+
+	if (!frames || !cnt)
+		return -EINVAL;
+
+	req = be16_to_cpu(frames[0].req_resp);
+	if (!rsdev->auth_key_set && req != RPMB_PROGRAM_KEY)
+		return rpmb_op_not_programmed(rsdev, req);
+
+	switch (req) {
+	case RPMB_PROGRAM_KEY:
+		dev_dbg(rsdev->dev, "rpmb: program key\n");
+		ret = rpmb_op_program_key(rsdev, frames, cnt);
+		break;
+	case RPMB_WRITE_DATA:
+		dev_dbg(rsdev->dev, "rpmb: write data\n");
+		ret = rpmb_op_write_data(rsdev, frames, cnt);
+		break;
+	case RPMB_GET_WRITE_COUNTER:
+		dev_dbg(rsdev->dev, "rpmb: get write counter\n");
+		ret = rpmb_op_get_wr_counter(rsdev, frames, cnt);
+		break;
+	case RPMB_READ_DATA:
+		dev_dbg(rsdev->dev, "rpmb: read data\n");
+		ret = rpmb_op_read_data(rsdev, frames, cnt);
+		break;
+	case RPMB_RESULT_READ:
+		dev_dbg(rsdev->dev, "rpmb: result read\n");
+		ret = rpmb_op_result_read(rsdev, frames, cnt);
+		break;
+	default:
+		dev_err(rsdev->dev, "unsupported command %u\n", req);
+		ret = -EINVAL;
+		break;
+	}
+
+	dev_dbg(rsdev->dev, "rpmb: ret=%d\n", ret);
+
+	return ret;
+}
+
+static int rpmb_sim_read(struct rpmb_sim_dev *rsdev,
+			 struct rpmb_frame *frames, u32 cnt)
+{
+	int i;
+
+	if (!frames || !cnt)
+		return -EINVAL;
+
+	if (!rsdev->out_frames || rsdev->out_frames_cnt == 0) {
+		dev_err(rsdev->dev, "out_frames are not set\n");
+		return -EINVAL;
+	}
+
+	if (rsdev->out_frames->req_resp == cpu_to_be16(RPMB_READ_DATA))
+		rpmb_do_read_data(rsdev, rsdev->out_frames, cnt);
+
+	for (i = 0; i < min_t(u32, rsdev->out_frames_cnt, cnt); i++)
+		memcpy(&frames[i], &rsdev->out_frames[i], sizeof(frames[i]));
+
+	if (rsdev->out_frames != rsdev->res_frames)
+		kfree(rsdev->out_frames);
+
+	rsdev->out_frames = NULL;
+	rsdev->out_frames_cnt = 0;
+	dev_dbg(rsdev->dev, "rpmb: cnt=%d\n", cnt);
+
+	return 0;
+}
+
+static int rpmb_sim_cmd_seq(struct device *dev,
+			    struct rpmb_cmd *cmds, u32 ncmds)
+{
+	struct rpmb_sim_dev *rsdev;
+	int i;
+	int ret;
+	struct rpmb_cmd *cmd;
+
+	if (!dev)
+		return -EINVAL;
+
+	dev_err(dev, "rpmb_cmd_seq\n");
+
+	rsdev = dev_get_drvdata(dev);
+
+	if (!rsdev)
+		return -EINVAL;
+
+	for (ret = 0, i = 0; i < ncmds && !ret; i++) {
+		cmd = &cmds[i];
+		if (cmd->flags & RPMB_F_WRITE)
+			ret = rpmb_sim_write(rsdev, cmd->frames, cmd->nframes);
+		else
+			ret = rpmb_sim_read(rsdev, cmd->frames, cmd->nframes);
+	}
+	return ret;
+}
+
+static struct rpmb_ops rpmb_sim_ops = {
+	.cmd_seq = rpmb_sim_cmd_seq,
+	.type = RPMB_TYPE_EMMC,
+};
+
+static int rpmb_sim_hmac_256_alloc(struct rpmb_sim_dev *rsdev)
+{
+	struct crypto_shash *hash_tfm;
+
+	hash_tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+	if (IS_ERR(hash_tfm))
+		return PTR_ERR(hash_tfm);
+
+	rsdev->hash_tfm = hash_tfm;
+
+	dev_dbg(rsdev->dev, "hamac(sha256) registered\n");
+	return 0;
+}
+
+static void rpmb_sim_hmac_256_free(struct rpmb_sim_dev *rsdev)
+{
+	if (rsdev->hash_tfm)
+		crypto_free_shash(rsdev->hash_tfm);
+
+	rsdev->hash_tfm = NULL;
+}
+
+static int rpmb_sim_probe(struct device *dev)
+{
+	struct rpmb_sim_dev *rsdev;
+	int ret;
+
+	rsdev = kzalloc(sizeof(*rsdev), GFP_KERNEL);
+	if (!rsdev)
+		return -ENOMEM;
+
+	rsdev->dev = dev;
+
+	ret = rpmb_sim_hmac_256_alloc(rsdev);
+	if (ret)
+		goto err;
+
+	rsdev->capacity = CAPACITY_UNIT * daunits;
+	rsdev->blkcnt  = rsdev->capacity / BLK_UNIT;
+	rsdev->da = kzalloc(rsdev->capacity, GFP_KERNEL);
+	if (!rsdev->da) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	rpmb_sim_ops.dev_id_len = strlen(id);
+	rpmb_sim_ops.dev_id = id;
+	rpmb_sim_ops.reliable_wr_cnt = max_wr_blks;
+
+	rsdev->rdev = rpmb_dev_register(rsdev->dev, &rpmb_sim_ops);
+	if (IS_ERR(rsdev->rdev)) {
+		ret = PTR_ERR(rsdev->rdev);
+		goto err;
+	}
+
+	dev_info(dev, "registered RPMB capacity = %zu of %zu blocks\n",
+		 rsdev->capacity, rsdev->blkcnt);
+
+	dev_set_drvdata(dev, rsdev);
+
+	return 0;
+err:
+	rpmb_sim_hmac_256_free(rsdev);
+	if (rsdev)
+		kfree(rsdev->da);
+	kfree(rsdev);
+	return ret;
+}
+
+static int rpmb_sim_remove(struct device *dev)
+{
+	struct rpmb_sim_dev *rsdev;
+
+	rsdev = dev_get_drvdata(dev);
+
+	rpmb_dev_unregister(rsdev->dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	rpmb_sim_hmac_256_free(rsdev);
+
+	kfree(rsdev->da);
+	kfree(rsdev);
+	return 0;
+}
+
+static void rpmb_sim_shutdown(struct device *dev)
+{
+	rpmb_sim_remove(dev);
+}
+
+static int rpmb_sim_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static struct bus_type rpmb_sim_bus = {
+	.name = "rpmb_sim",
+	.match = rpmb_sim_match,
+};
+
+static struct device_driver rpmb_sim_drv = {
+	.name  = "rpmb_sim",
+	.probe = rpmb_sim_probe,
+	.remove = rpmb_sim_remove,
+	.shutdown = rpmb_sim_shutdown,
+};
+
+static void rpmb_sim_dev_release(struct device *dev)
+{
+}
+
+static struct device rpmb_sim_dev;
+
+static int __init rpmb_sim_init(void)
+{
+	int ret;
+	struct device *dev = &rpmb_sim_dev;
+	struct device_driver *drv = &rpmb_sim_drv;
+
+	ret = bus_register(&rpmb_sim_bus);
+	if (ret)
+		return ret;
+
+	dev->bus = &rpmb_sim_bus;
+	dev->release = rpmb_sim_dev_release;
+	dev_set_name(dev, "%s", "rpmb_sim");
+	ret = device_register(dev);
+	if (ret) {
+		pr_err("device register failed %d\n", ret);
+		goto err_device;
+	}
+
+	drv->bus = &rpmb_sim_bus;
+	ret = driver_register(drv);
+	if (ret) {
+		pr_err("driver register failed %d\n", ret);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	device_unregister(dev);
+err_device:
+	bus_unregister(&rpmb_sim_bus);
+	return ret;
+}
+
+static void __exit rpmb_sim_exit(void)
+{
+	struct device *dev = &rpmb_sim_dev;
+	struct device_driver *drv = &rpmb_sim_drv;
+
+	device_unregister(dev);
+	driver_unregister(drv);
+	bus_unregister(&rpmb_sim_bus);
+}
+
+module_init(rpmb_sim_init);
+module_exit(rpmb_sim_exit);
+
+MODULE_AUTHOR("Tomas Winkler <tomas.winkler@intel.com");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("rpmb_sim:rpmb_sim");
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 05/11] char: rpmb: provide a user space interface
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

The user space API is achieved via two synchronous IOCTLs.
Simplified one, RPMB_IOC_REQ_CMD, were read result cycles is performed
by the framework on behalf the user and second, RPMB_IOC_SEQ_CMD where
the whole RPMB sequence including RESULT_READ is supplied by the caller.
The latter is intended for easier adjusting of the applications that
use MMC_IOC_MULTI_CMD ioctl.

V2: use memdup_user
V3: commit message fix
V4: resend
V5: 1. Add RPMB_IOC_SEQ_CMD API.
    2. Export uapi rpmb.h header
V6: 1. Remove #include <linux/module.h>.
    2. Add ioctl documentation.
V7: 1. copy_from_user the value of the frame pointer.
    2. Fix possible macro side-effect due to macro argument reuse.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 Documentation/ioctl/ioctl-number.txt |   1 +
 MAINTAINERS                          |   1 +
 drivers/char/rpmb/Kconfig            |   7 +
 drivers/char/rpmb/Makefile           |   1 +
 drivers/char/rpmb/cdev.c             | 331 +++++++++++++++++++++++++++++++++++
 drivers/char/rpmb/core.c             |   9 +-
 drivers/char/rpmb/rpmb-cdev.h        |  25 +++
 include/linux/rpmb.h                 |  81 +--------
 include/uapi/linux/Kbuild            |   1 +
 include/uapi/linux/rpmb.h            | 153 ++++++++++++++++
 10 files changed, 536 insertions(+), 74 deletions(-)
 create mode 100644 drivers/char/rpmb/cdev.c
 create mode 100644 drivers/char/rpmb/rpmb-cdev.h
 create mode 100644 include/uapi/linux/rpmb.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 81c7f2bb7daf..f645543866c7 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,7 @@ Code  Seq#(hex)	Include File		Comments
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
 0xB3	00	linux/mmc/ioctl.h
 0xB4	00-0F	linux/gpio.h		<mailto:linux-gpio@vger.kernel.org>
+0xB5	00-01	linux/uapi/linux/rpmb.h <mailto:linux-mei@linux.intel.com>
 0xC0	00-0F	linux/usb/iowarrior.h
 0xCA	00-0F	uapi/misc/cxl.h
 0xCA	80-8F	uapi/scsi/cxlflash_ioctl.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 4c8ab6c09f3a..090b6d2d31fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10289,6 +10289,7 @@ M:	Tomas Winkler <tomas.winkler@intel.com>
 L:	linux-kernel@vger.kernel.org
 S:	Supported
 F:	drivers/char/rpmb/*
+F:	include/uapi/linux/rpmb.h
 F:	include/linux/rpmb.h
 F:	Documentation/ABI/testing/sysfs-class-rpmb
 
diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
index c5e6e909efce..6794be9fcc5e 100644
--- a/drivers/char/rpmb/Kconfig
+++ b/drivers/char/rpmb/Kconfig
@@ -6,3 +6,10 @@ config RPMB
 	  access RPMB partition.
 
 	  If unsure, select N.
+
+config RPMB_INTF_DEV
+	bool "RPMB character device interface /dev/rpmbN"
+	depends on RPMB
+	help
+	  Say yes here if you want to access RPMB from user space
+	  via character device interface /dev/rpmb%d
diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
index 812b3ed264c0..b5dc087b1299 100644
--- a/drivers/char/rpmb/Makefile
+++ b/drivers/char/rpmb/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_RPMB) += rpmb.o
 rpmb-objs += core.o
+rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c
new file mode 100644
index 000000000000..9e3ae753a9a1
--- /dev/null
+++ b/drivers/char/rpmb/cdev.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2015-2016 Intel Corp. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+
+#include <linux/rpmb.h>
+
+#include "rpmb-cdev.h"
+
+static dev_t rpmb_devt;
+#define RPMB_MAX_DEVS  MINORMASK
+
+#define RPMB_DEV_OPEN    0  /** single open bit (position) */
+/* from MMC_IOC_MAX_CMDS */
+#define RPMB_MAX_FRAMES 255
+
+/**
+ * rpmb_open - the open function
+ *
+ * @inode: pointer to inode structure
+ * @fp: pointer to file structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int rpmb_open(struct inode *inode, struct file *fp)
+{
+	struct rpmb_dev *rdev;
+
+	rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev);
+	if (!rdev)
+		return -ENODEV;
+
+	/* the rpmb is single open! */
+	if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
+		return -EBUSY;
+
+	mutex_lock(&rdev->lock);
+
+	fp->private_data = rdev;
+
+	mutex_unlock(&rdev->lock);
+
+	return nonseekable_open(inode, fp);
+}
+
+/**
+ * rpmb_open - the open function
+ *
+ * @inode: pointer to inode structure
+ * @fp: pointer to file structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int rpmb_release(struct inode *inode, struct file *fp)
+{
+	struct rpmb_dev *rdev = fp->private_data;
+
+	clear_bit(RPMB_DEV_OPEN, &rdev->status);
+
+	return 0;
+}
+
+/**
+ * rpmb_cmd_copy_from_user -  copy rpmb command from the user space
+ *
+ * @cmd:  internal cmd structure
+ * @ucmd: user space cmd structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd,
+				   struct rpmb_ioc_cmd __user *ucmd)
+{
+	size_t sz;
+	struct rpmb_frame *frames;
+	u64 frames_ptr;
+
+	if (get_user(cmd->flags, &ucmd->flags))
+		return -EFAULT;
+
+	if (get_user(cmd->nframes, &ucmd->nframes))
+		return -EFAULT;
+
+	if (cmd->nframes > RPMB_MAX_FRAMES)
+		return -EOVERFLOW;
+
+	if (!cmd->nframes)
+		return -EINVAL;
+
+	/* some archs have issues with 64bit get_user */
+	if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr)))
+		return -EFAULT;
+
+	sz = cmd->nframes * sizeof(struct rpmb_frame);
+	frames = memdup_user(u64_to_user_ptr(frames_ptr), sz);
+	if (IS_ERR(frames))
+		return PTR_ERR(frames);
+
+	cmd->frames = frames;
+	return 0;
+}
+
+/**
+ * rpmb_cmd_copy_to_user -  copy rpmb command the the user space
+ *
+ * @ucmd: user space cmd structure
+ * @cmd:  internal cmd structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd,
+				 struct rpmb_cmd *cmd)
+{
+	size_t sz;
+	u64 frames_ptr;
+
+	sz = cmd->nframes * sizeof(struct rpmb_frame);
+
+	if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr)))
+		return -EFAULT;
+
+	/* some archs have issues with 64bit get_user */
+	if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, sz))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * rpmb_ioctl_seq_cmd: issue rpmb sequence
+ *
+ * @rdev: rpmb device
+ * @ptr:  rpmb cmd sequence
+ *
+ * RPMB_IOC_SEQ_CMD handler
+ *
+ * Return: 0 on success, <0 on error
+ */
+static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev,
+			       struct rpmb_ioc_seq_cmd __user *ptr)
+{
+	__u64 ncmds;
+	struct rpmb_cmd *cmds;
+	struct rpmb_ioc_cmd __user *ucmds;
+
+	int i;
+	int ret;
+
+	/* The caller must have CAP_SYS_RAWIO, like mmc ioctl */
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	/* some archs have issues with 64bit get_user */
+	if (copy_from_user(&ncmds, &ptr->num_of_cmds, sizeof(ncmds)))
+		return -EFAULT;
+
+	if (ncmds > 3) {
+		dev_err(&rdev->dev, "supporting up to 3 packets (%llu)\n",
+			ncmds);
+		return -EINVAL;
+	}
+
+	cmds = kcalloc(ncmds, sizeof(*cmds), GFP_KERNEL);
+	if (!cmds)
+		return -ENOMEM;
+
+	ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds;
+	for (i = 0; i < ncmds; i++) {
+		ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]);
+		if (ret)
+			goto out;
+	}
+
+	ret = rpmb_cmd_seq(rdev, cmds, ncmds);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < ncmds; i++) {
+		ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]);
+		if (ret)
+			goto out;
+	}
+out:
+	for (i = 0; i < ncmds; i++)
+		kfree(cmds[i].frames);
+	kfree(cmds);
+	return ret;
+}
+
+/**
+ * rpmb_ioctl_req_cmd: issue rpmb request command
+ *
+ * @rdev: rpmb device
+ * @ptr: rpmb request command
+ *
+ * RPMB_IOC_REQ_CMD handler
+ *
+ * Return: 0 on success; < 0 on error
+ */
+static long rpmb_ioctl_req_cmd(struct rpmb_dev *rdev,
+			       struct rpmb_ioc_req_cmd __user *ptr)
+{
+	struct rpmb_data rpmbd;
+	u64 req_type;
+	int ret;
+
+	/* some archs have issues with 64bit get_user */
+	if (copy_from_user(&req_type, &ptr->req_type, sizeof(req_type)))
+		return -EFAULT;
+
+	if (req_type >= U16_MAX)
+		return -EINVAL;
+
+	memset(&rpmbd, 0, sizeof(rpmbd));
+
+	rpmbd.req_type = req_type & 0xFFFF;
+
+	ret = rpmb_cmd_copy_from_user(&rpmbd.icmd, &ptr->icmd);
+	if (ret)
+		goto out;
+
+	ret = rpmb_cmd_copy_from_user(&rpmbd.ocmd, &ptr->ocmd);
+	if (ret)
+		goto out;
+
+	ret = rpmb_cmd_req(rdev, &rpmbd);
+	if (ret)
+		goto out;
+
+	ret = rpmb_cmd_copy_to_user(&ptr->ocmd, &rpmbd.ocmd);
+
+out:
+	kfree(rpmbd.icmd.frames);
+	kfree(rpmbd.ocmd.frames);
+	return ret;
+}
+
+/**
+ * rpmb_ioctl - rpmb ioctl dispatcher
+ *
+ * @fp: a file pointer
+ * @cmd: ioctl command RPMB_IOC_REQ_CMD or RPMB_IOC_SEQ_CMD
+ * @arg: ioctl data: rpmb_ioc_req_cmd or rpmb_ioc_seq_cmd
+ *
+ * Return: 0 on success; < 0 on error
+ */
+static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	struct rpmb_dev *rdev = fp->private_data;
+	void __user *ptr = (void __user *)arg;
+
+	switch (cmd) {
+	case RPMB_IOC_REQ_CMD:
+		return rpmb_ioctl_req_cmd(rdev, ptr);
+	case RPMB_IOC_SEQ_CMD:
+		return rpmb_ioctl_seq_cmd(rdev, ptr);
+	default:
+		dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd);
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static long rpmb_compat_ioctl(struct file *fp, unsigned int cmd,
+			      unsigned long arg)
+{
+	return rpmb_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif /* CONFIG_COMPAT */
+
+static const struct file_operations rpmb_fops = {
+	.open           = rpmb_open,
+	.release        = rpmb_release,
+	.unlocked_ioctl = rpmb_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = rpmb_compat_ioctl,
+#endif
+	.owner          = THIS_MODULE,
+	.llseek         = noop_llseek,
+};
+
+void rpmb_cdev_prepare(struct rpmb_dev *rdev)
+{
+	rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id);
+	rdev->cdev.owner = THIS_MODULE;
+	cdev_init(&rdev->cdev, &rpmb_fops);
+}
+
+void rpmb_cdev_add(struct rpmb_dev *rdev)
+{
+	cdev_add(&rdev->cdev, rdev->dev.devt, 1);
+}
+
+void rpmb_cdev_del(struct rpmb_dev *rdev)
+{
+	if (rdev->dev.devt)
+		cdev_del(&rdev->cdev);
+}
+
+int __init rpmb_cdev_init(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS, "rpmb");
+	if (ret < 0)
+		pr_err("unable to allocate char dev region\n");
+
+	return ret;
+}
+
+void __exit rpmb_cdev_exit(void)
+{
+	if (rpmb_devt)
+		unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS);
+}
diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
index f6b4cabb0982..7707ccb37b23 100644
--- a/drivers/char/rpmb/core.c
+++ b/drivers/char/rpmb/core.c
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 
 #include <linux/rpmb.h>
+#include "rpmb-cdev.h"
 
 static DEFINE_IDA(rpmb_ida);
 
@@ -429,6 +430,7 @@ int rpmb_dev_unregister(struct device *dev)
 	rpmb_dev_put(rdev);
 
 	mutex_lock(&rdev->lock);
+	rpmb_cdev_del(rdev);
 	device_del(&rdev->dev);
 	mutex_unlock(&rdev->lock);
 
@@ -479,10 +481,14 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev,
 	rdev->dev.parent = dev;
 	rdev->dev.groups = rpmb_attr_groups;
 
+	rpmb_cdev_prepare(rdev);
+
 	ret = device_register(&rdev->dev);
 	if (ret)
 		goto exit;
 
+	rpmb_cdev_add(rdev);
+
 	dev_dbg(&rdev->dev, "registered disk\n");
 
 	return rdev;
@@ -499,11 +505,12 @@ static int __init rpmb_init(void)
 {
 	ida_init(&rpmb_ida);
 	class_register(&rpmb_class);
-	return 0;
+	return rpmb_cdev_init();
 }
 
 static void __exit rpmb_exit(void)
 {
+	rpmb_cdev_exit();
 	class_unregister(&rpmb_class);
 	ida_destroy(&rpmb_ida);
 }
diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-cdev.h
new file mode 100644
index 000000000000..5fb41e586ba9
--- /dev/null
+++ b/drivers/char/rpmb/rpmb-cdev.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015-2016 Intel Corp. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#ifdef CONFIG_RPMB_INTF_DEV
+int __init rpmb_cdev_init(void);
+void __exit rpmb_cdev_exit(void);
+void rpmb_cdev_prepare(struct rpmb_dev *rdev);
+void rpmb_cdev_add(struct rpmb_dev *rdev);
+void rpmb_cdev_del(struct rpmb_dev *rdev);
+#else
+static inline int __init rpmb_cdev_init(void) { return 0; }
+static inline void __exit rpmb_cdev_exit(void) {}
+static inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {}
+static inline void rpmb_cdev_add(struct rpmb_dev *rdev) {}
+static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {}
+#endif /* CONFIG_RPMB_INTF_DEV */
diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h
index ed4e372d5b13..1f3fa3fc5d67 100644
--- a/include/linux/rpmb.h
+++ b/include/linux/rpmb.h
@@ -15,79 +15,11 @@
 
 #include <linux/types.h>
 #include <linux/device.h>
+#include <linux/cdev.h>
+#include <uapi/linux/rpmb.h>
 #include <linux/kref.h>
 
 /**
- * struct rpmb_frame - rpmb frame as defined by specs
- *
- * @stuff        : stuff bytes
- * @key_mac      : The authentication key or the message authentication
- *                 code (MAC) depending on the request/response type.
- *                 The MAC will be delivered in the last (or the only)
- *                 block of data.
- * @data         : Data to be written or read by signed access.
- * @nonce        : Random number generated by the host for the requests
- *                 and copied to the response by the RPMB engine.
- * @write_counter: Counter value for the total amount of the successful
- *                 authenticated data write requests made by the host.
- * @addr         : Address of the data to be programmed to or read
- *                 from the RPMB. Address is the serial number of
- *                 the accessed block (half sector 256B).
- * @block_count  : Number of blocks (half sectors, 256B) requested to be
- *                 read/programmed.
- * @result       : Includes information about the status of the write counter
- *                 (valid, expired) and result of the access made to the RPMB.
- * @req_resp     : Defines the type of request and response to/from the memory.
- */
-struct rpmb_frame {
-	u8     stuff[196];
-	u8     key_mac[32];
-	u8     data[256];
-	u8     nonce[16];
-	__be32 write_counter;
-	__be16 addr;
-	__be16 block_count;
-	__be16 result;
-	__be16 req_resp;
-} __packed;
-
-#define RPMB_PROGRAM_KEY       0x1    /* Program RPMB Authentication Key */
-#define RPMB_GET_WRITE_COUNTER 0x2    /* Read RPMB write counter */
-#define RPMB_WRITE_DATA        0x3    /* Write data to RPMB partition */
-#define RPMB_READ_DATA         0x4    /* Read data from RPMB partition */
-#define RPMB_RESULT_READ       0x5    /* Read result request  (Internal) */
-
-#define RPMB_REQ2RESP(_OP) ((_OP) << 8)
-#define RPMB_RESP2REQ(_OP) ((_OP) >> 8)
-
-/**
- * enum rpmb_op_result - rpmb operation results
- *
- * @RPMB_ERR_OK      : operation successful
- * @RPMB_ERR_GENERAL : general failure
- * @RPMB_ERR_AUTH    : mac doesn't match or ac calculation failure
- * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure
- * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment
- * @RPMB_ERR_WRITE   : data, counter, or result write failure
- * @RPMB_ERR_READ    : data, counter, or result read failure
- * @RPMB_ERR_NO_KEY  : authentication key not yet programmed
- *
- * @RPMB_ERR_COUNTER_EXPIRED:  counter expired
- */
-enum rpmb_op_result {
-	RPMB_ERR_OK      = 0x0000,
-	RPMB_ERR_GENERAL = 0x0001,
-	RPMB_ERR_AUTH    = 0x0002,
-	RPMB_ERR_COUNTER = 0x0003,
-	RPMB_ERR_ADDRESS = 0x0004,
-	RPMB_ERR_WRITE   = 0x0005,
-	RPMB_ERR_READ    = 0x0006,
-	RPMB_ERR_NO_KEY  = 0x0007,
-
-	RPMB_ERR_COUNTER_EXPIRED = 0x0080
-};
-
-/**
  * enum rpmb_type - type of underlaying storage technology
  *
  * @RPMB_TYPE_ANY   : any type used for search only
@@ -106,9 +38,6 @@ enum rpmb_type {
 
 extern struct class rpmb_class;
 
-#define RPMB_F_WRITE     BIT(0)
-#define RPMB_F_REL_WRITE BIT(1)
-
 /**
  * struct rpmb_cmd: rpmb access command
  *
@@ -162,12 +91,18 @@ struct rpmb_ops {
  * @lock       : the device lock
  * @dev        : device
  * @id         : device id
+ * @cdev       : character dev
+ * @status     : device status
  * @ops        : operation exported by block layer
  */
 struct rpmb_dev {
 	struct mutex lock; /* device serialization lock */
 	struct device dev;
 	int    id;
+#ifdef CONFIG_RPMB_INTF_DEV
+	struct cdev cdev;
+	unsigned long status;
+#endif /* CONFIG_RPMB_INTF_DEV */
 	const struct rpmb_ops *ops;
 };
 
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index cd2be1c8e9fb..f9a67b0bfde9 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -366,6 +366,7 @@ header-y += rio_mport_cdev.h
 header-y += romfs_fs.h
 header-y += rose.h
 header-y += route.h
+header-y += rpmb.h
 header-y += rtc.h
 header-y += rtnetlink.h
 header-y += scc.h
diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h
new file mode 100644
index 000000000000..ca2d5d5e366c
--- /dev/null
+++ b/include/uapi/linux/rpmb.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015-2016, Intel Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_LINUX_RPMB_H_
+#define _UAPI_LINUX_RPMB_H_
+
+#include <linux/types.h>
+
+/**
+ * struct rpmb_frame - rpmb frame as defined by specs
+ *
+ * @stuff        : stuff bytes
+ * @key_mac      : The authentication key or the message authentication
+ *                 code (MAC) depending on the request/response type.
+ *                 The MAC will be delivered in the last (or the only)
+ *                 block of data.
+ * @data         : Data to be written or read by signed access.
+ * @nonce        : Random number generated by the host for the requests
+ *                 and copied to the response by the RPMB engine.
+ * @write_counter: Counter value for the total amount of the successful
+ *                 authenticated data write requests made by the host.
+ * @addr         : Address of the data to be programmed to or read
+ *                 from the RPMB. Address is the serial number of
+ *                 the accessed block (half sector 256B).
+ * @block_count  : Number of blocks (half sectors, 256B) requested to be
+ *                 read/programmed.
+ * @result       : Includes information about the status of the write counter
+ *                 (valid, expired) and result of the access made to the RPMB.
+ * @req_resp     : Defines the type of request and response to/from the memory.
+ */
+struct rpmb_frame {
+	__u8   stuff[196];
+	__u8   key_mac[32];
+	__u8   data[256];
+	__u8   nonce[16];
+	__be32 write_counter;
+	__be16 addr;
+	__be16 block_count;
+	__be16 result;
+	__be16 req_resp;
+} __attribute__((packed));
+
+#define RPMB_PROGRAM_KEY       0x1    /* Program RPMB Authentication Key */
+#define RPMB_GET_WRITE_COUNTER 0x2    /* Read RPMB write counter */
+#define RPMB_WRITE_DATA        0x3    /* Write data to RPMB partition */
+#define RPMB_READ_DATA         0x4    /* Read data from RPMB partition */
+#define RPMB_RESULT_READ       0x5    /* Read result request  (Internal) */
+
+#define RPMB_REQ2RESP(_OP) ((_OP) << 8)
+#define RPMB_RESP2REQ(_OP) ((_OP) >> 8)
+
+/* length of the part of the frame used for HMAC computation */
+#define hmac_data_len \
+	(sizeof(struct rpmb_frame) - offsetof(struct rpmb_frame, data))
+
+/**
+ * enum rpmb_op_result - rpmb operation results
+ *
+ * @RPMB_ERR_OK:       operation successful
+ * @RPMB_ERR_GENERAL:  general failure
+ * @RPMB_ERR_AUTH:     mac doesn't match or ac calculation failure
+ * @RPMB_ERR_COUNTER:  counter doesn't match or counter increment failure
+ * @RPMB_ERR_ADDRESS:  address out of range or wrong address alignment
+ * @RPMB_ERR_WRITE:    data, counter, or result write failure
+ * @RPMB_ERR_READ:     data, counter, or result read failure
+ * @RPMB_ERR_NO_KEY:   authentication key not yet programmed
+ *
+ * @RPMB_ERR_COUNTER_EXPIRED:  counter expired
+ */
+enum rpmb_op_result {
+	RPMB_ERR_OK      = 0x0000,
+	RPMB_ERR_GENERAL = 0x0001,
+	RPMB_ERR_AUTH    = 0x0002,
+	RPMB_ERR_COUNTER = 0x0003,
+	RPMB_ERR_ADDRESS = 0x0004,
+	RPMB_ERR_WRITE   = 0x0005,
+	RPMB_ERR_READ    = 0x0006,
+	RPMB_ERR_NO_KEY  = 0x0007,
+
+	RPMB_ERR_COUNTER_EXPIRED = 0x0080
+};
+
+#define RPMB_F_WRITE     (1UL << 0)
+#define RPMB_F_REL_WRITE (1UL << 1)
+
+/**
+ * struct rpmb_cmd - rpmb access command
+ *
+ * @flags: command flags
+ *      0 - read command
+ *      1 - write commnad RPMB_F_WRITE
+ *      2 - reliable write RPMB_F_REL_WRITE
+ * @nframes: number of rpmb frames in the command
+ * @frames_ptr:  a pointer to the list of rpmb frames
+ */
+struct rpmb_ioc_cmd {
+	__u32 flags;
+	__u32 nframes;
+	__aligned_u64 frames_ptr;
+};
+
+#define rpmb_ioc_cmd_set_frames(_cmd, _ptr) \
+	(_cmd).frames_ptr = (__aligned_u64)(intptr_t)(_ptr)
+
+#define rpmb_ioc_cmd_set(_cmd, _flags, _ptr, _n) do {        \
+	struct rpmb_ioc_cmd *icmd = &(_cmd);                 \
+	icmd->flags = (_flags);                              \
+	icmd->nframes = (_n);                                \
+	icmd->frames_ptr = (__aligned_u64)(intptr_t)(_ptr);  \
+} while (0)
+
+/**
+ * struct rpmb_ioc_req_cmd - rpmb operation request command
+ *
+ * @req_type: request type:  must match the in frame req_resp
+ *            program key
+ *            get write counter
+ *            write data
+ *            read data
+ * @icmd: input command
+ * @ocmd: output/result command
+ */
+struct rpmb_ioc_req_cmd {
+	__u64 req_type;
+	struct rpmb_ioc_cmd icmd;
+	struct rpmb_ioc_cmd ocmd;
+};
+
+/**
+ * struct rpmb_ioc_seq_cmd - rpmb command sequence
+ *
+ * @num_of_cmds: number of commands
+ * @cmds: list of rpmb commands
+ */
+struct rpmb_ioc_seq_cmd {
+	__u64 num_of_cmds;
+	struct rpmb_ioc_cmd cmds[0];
+};
+
+#define RPMB_IOC_REQ_CMD _IOWR(0xB5, 0, struct rpmb_ioc_req_cmd)
+#define RPMB_IOC_SEQ_CMD _IOWR(0xB5, 1, struct rpmb_ioc_seq_cmd)
+
+#endif /* _UAPI_LINUX_RPMB_H_ */
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 03/11] rpmb: add sysfs-class ABI documentation
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

V2: resend
V3: add more verbose description
V4: resend
V5: adjust date and kernel version
V6: adjust date and kernel version
V7: adjust date and kernel version

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 Documentation/ABI/testing/sysfs-class-rpmb | 20 ++++++++++++++++++++
 MAINTAINERS                                |  1 +
 2 files changed, 21 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-rpmb

diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb
new file mode 100644
index 000000000000..2ea6dbb91fac
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-rpmb
@@ -0,0 +1,20 @@
+What:		/sys/class/rpmb/
+Date:		Nov 2016
+KernelVersion:  4.10
+Contact:	Tomas Winkler <tomas.winkler@intel.com>
+Description:
+		The rpmb/ class sub-directory belongs to RPMB device class.
+
+		Few storage technologies such is EMMC, UFS, and NVMe support
+		Replay Protected Memory Block (RPMB) hardware partition with
+		common protocol and frame layout.
+		Such a partition provides authenticated and replay protected access,
+		hence suitable as a secure storage.
+
+What:		/sys/class/rpmb/rpmbN/
+Date:		Nov 2016
+KernelVersion:	4.10
+Contact:	Tomas Winkler <tomas.winkler@intel.com>
+Description:
+		The /sys/class/rpmb/rpmbN directory is created for
+		each RPMB registered device.
diff --git a/MAINTAINERS b/MAINTAINERS
index 7bae7c014af0..4c8ab6c09f3a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10290,6 +10290,7 @@ L:	linux-kernel@vger.kernel.org
 S:	Supported
 F:	drivers/char/rpmb/*
 F:	include/linux/rpmb.h
+F:	Documentation/ABI/testing/sysfs-class-rpmb
 
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 02/11] rpmb: enable emmc specific read data fixup
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

For eMMC the block count of the RPMB read operation is not indicated in
the original RPMB Data Read Request packet.
This might be different then the implementation of other protocol
standards.
This patch implements a fixup for this behavior.

V6: new in the series
V7: resend

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 drivers/char/rpmb/core.c | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
index ff10cbb7b644..8cfbbb721538 100644
--- a/drivers/char/rpmb/core.c
+++ b/drivers/char/rpmb/core.c
@@ -117,6 +117,35 @@ static int rpmb_request_verify(struct rpmb_dev *rdev, struct rpmb_data *rpmbd)
 }
 
 /**
+ * rpmb_cmd_fixup - fixup rpmb command
+ *
+ * @rdev: rpmb device
+ * @cmds: rpmb command list
+ * @ncmds: number of commands
+ *
+ */
+static void rpmb_cmd_fixup(struct rpmb_dev *rdev,
+			   struct rpmb_cmd *cmds, u32 ncmds)
+{
+	int i;
+
+	if (rdev->ops->type != RPMB_TYPE_EMMC)
+		return;
+
+	/* Fixup RPMB_READ_DATA specific to eMMC
+	 * The block count of the RPMB read operation is not indicated
+	 * in the original RPMB Data Read Request packet.
+	 * This is different then implementation for other protocol
+	 * standards.
+	 */
+	for (i = 0; i < ncmds; i++)
+		if (cmds->frames->req_resp == cpu_to_be16(RPMB_READ_DATA)) {
+			dev_dbg(&rdev->dev, "Fixing up READ_DATA frame to block_count=0\n");
+			cmds->frames->block_count = 0;
+		}
+}
+
+/**
  * rpmb_cmd_seq - send RPMB command sequence
  *
  * @rdev: rpmb device
@@ -136,10 +165,11 @@ int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds)
 		return -EINVAL;
 
 	mutex_lock(&rdev->lock);
-	if (rdev->ops && rdev->ops->cmd_seq)
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->cmd_seq) {
+		rpmb_cmd_fixup(rdev, cmds, ncmds);
 		err = rdev->ops->cmd_seq(rdev->dev.parent, cmds, ncmds);
-	else
-		err = -EOPNOTSUPP;
+	}
 	mutex_unlock(&rdev->lock);
 	return err;
 }
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 01/11] rpmb: add Replay Protected Memory Block (RPMB) subsystem
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler, Alexander Usyskin
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Few storage technologies such is EMMC, UFS, and NVMe support RPMB
hardware partition with common protocol and frame layout.
The RPMB partition cannot be accessed via standard block layer,
but by a set of specific commands: WRITE, READ, GET_WRITE_COUNTER,
and PROGRAM_KEY.
Such a partition provides authenticated and replay protected access,
hence suitable as a secure storage.

The RPMB layer aims to provide in-kernel API for Trusted Execution
Environment (TEE) devices that are capable to securely compute block
frame signature. In case a TEE device wish to store a replay protected
data, it creates an RPMB frame with requested data and computes HMAC of
the frame, then it requests the storage device via RPMB layer to store
the data.
A TEE device driver can claim the RPMB interface, for example, via
class_interface_register ().
The layer provides two APIs rpmb_cmd_req() for issuing one of RPMB
specific commands and rpmb_cmd_seq() for issuing of raw RPMB protocol
frames sequence.
The major difference between the APIs is that for rpmb_cmd_req() the
framework performs RPMB_RESULT_READ operation on behalf of a user.
The RPMB_RESULT_READ is used for retrieving result of the commands
that carry data in the write cycle and need one more step to retrieve
the result.

A storage device registers its RPMB (eMMC) partition or RPMB
W-LUN (UFS) with the RPMB layer providing an implementation for
rpmb_cmd_seq() handler. The interface enables sending sequence of RPMB
standard frames.

V2: added short workflow description in the commit message
V3: commit message fix
V4: resend
V5: add rpmb sequence interface.
V6: 1. More info in the commit message
    2. Define simulation device type
V7: resend

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 MAINTAINERS                |   7 +
 drivers/char/Kconfig       |   2 +
 drivers/char/Makefile      |   1 +
 drivers/char/rpmb/Kconfig  |   8 +
 drivers/char/rpmb/Makefile |   4 +
 drivers/char/rpmb/core.c   | 414 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/rpmb.h       | 232 +++++++++++++++++++++++++
 7 files changed, 668 insertions(+)
 create mode 100644 drivers/char/rpmb/Kconfig
 create mode 100644 drivers/char/rpmb/Makefile
 create mode 100644 drivers/char/rpmb/core.c
 create mode 100644 include/linux/rpmb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 3d838cf49f81..7bae7c014af0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10284,6 +10284,13 @@ F:	include/net/rose.h
 F:	include/uapi/linux/rose.h
 F:	net/rose/
 
+RPMB SUBSYSTEM
+M:	Tomas Winkler <tomas.winkler@intel.com>
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+F:	drivers/char/rpmb/*
+F:	include/linux/rpmb.h
+
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index dc4f35ad19d0..220c86663b25 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -589,5 +589,7 @@ config TILE_SROM
 
 source "drivers/char/xillybus/Kconfig"
 
+source "drivers/char/rpmb/Kconfig"
+
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 6e6c244a66a0..3a0cac3ff2dc 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -60,3 +60,4 @@ js-rtc-y = rtc.o
 obj-$(CONFIG_TILE_SROM)		+= tile-srom.o
 obj-$(CONFIG_XILLYBUS)		+= xillybus/
 obj-$(CONFIG_POWERNV_OP_PANEL)	+= powernv-op-panel.o
+obj-$(CONFIG_RPMB)		+= rpmb/
diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
new file mode 100644
index 000000000000..c5e6e909efce
--- /dev/null
+++ b/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/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
new file mode 100644
index 000000000000..812b3ed264c0
--- /dev/null
+++ b/drivers/char/rpmb/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_RPMB) += rpmb.o
+rpmb-objs += core.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
new file mode 100644
index 000000000000..ff10cbb7b644
--- /dev/null
+++ b/drivers/char/rpmb/core.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2015-2016 Intel Corp. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#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 - 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];
+	struct rpmb_frame *res_frame;
+	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:
+		cnt_in = 1;
+		cnt_out = 1;
+		/* fall through */
+	case RPMB_WRITE_DATA:
+		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);
+		ncmds = 3;
+		break;
+	case RPMB_GET_WRITE_COUNTER:
+		cnt_in = 1;
+		cnt_out = 1;
+		/* fall through */
+	case RPMB_READ_DATA:
+		rpmb_cmd_set(&cmd[0], RPMB_F_WRITE, rpmbd->icmd.frames, cnt_in);
+		rpmb_cmd_set(&cmd[1], 0, rpmbd->ocmd.frames, cnt_out);
+		ncmds = 2;
+		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/include/linux/rpmb.h b/include/linux/rpmb.h
new file mode 100644
index 000000000000..ed4e372d5b13
--- /dev/null
+++ b/include/linux/rpmb.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2015-2016 Intel Corp. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#ifndef __RPMB_H__
+#define __RPMB_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+
+/**
+ * struct rpmb_frame - rpmb frame as defined by specs
+ *
+ * @stuff        : stuff bytes
+ * @key_mac      : The authentication key or the message authentication
+ *                 code (MAC) depending on the request/response type.
+ *                 The MAC will be delivered in the last (or the only)
+ *                 block of data.
+ * @data         : Data to be written or read by signed access.
+ * @nonce        : Random number generated by the host for the requests
+ *                 and copied to the response by the RPMB engine.
+ * @write_counter: Counter value for the total amount of the successful
+ *                 authenticated data write requests made by the host.
+ * @addr         : Address of the data to be programmed to or read
+ *                 from the RPMB. Address is the serial number of
+ *                 the accessed block (half sector 256B).
+ * @block_count  : Number of blocks (half sectors, 256B) requested to be
+ *                 read/programmed.
+ * @result       : Includes information about the status of the write counter
+ *                 (valid, expired) and result of the access made to the RPMB.
+ * @req_resp     : Defines the type of request and response to/from the memory.
+ */
+struct rpmb_frame {
+	u8     stuff[196];
+	u8     key_mac[32];
+	u8     data[256];
+	u8     nonce[16];
+	__be32 write_counter;
+	__be16 addr;
+	__be16 block_count;
+	__be16 result;
+	__be16 req_resp;
+} __packed;
+
+#define RPMB_PROGRAM_KEY       0x1    /* Program RPMB Authentication Key */
+#define RPMB_GET_WRITE_COUNTER 0x2    /* Read RPMB write counter */
+#define RPMB_WRITE_DATA        0x3    /* Write data to RPMB partition */
+#define RPMB_READ_DATA         0x4    /* Read data from RPMB partition */
+#define RPMB_RESULT_READ       0x5    /* Read result request  (Internal) */
+
+#define RPMB_REQ2RESP(_OP) ((_OP) << 8)
+#define RPMB_RESP2REQ(_OP) ((_OP) >> 8)
+
+/**
+ * enum rpmb_op_result - rpmb operation results
+ *
+ * @RPMB_ERR_OK      : operation successful
+ * @RPMB_ERR_GENERAL : general failure
+ * @RPMB_ERR_AUTH    : mac doesn't match or ac calculation failure
+ * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure
+ * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment
+ * @RPMB_ERR_WRITE   : data, counter, or result write failure
+ * @RPMB_ERR_READ    : data, counter, or result read failure
+ * @RPMB_ERR_NO_KEY  : authentication key not yet programmed
+ *
+ * @RPMB_ERR_COUNTER_EXPIRED:  counter expired
+ */
+enum rpmb_op_result {
+	RPMB_ERR_OK      = 0x0000,
+	RPMB_ERR_GENERAL = 0x0001,
+	RPMB_ERR_AUTH    = 0x0002,
+	RPMB_ERR_COUNTER = 0x0003,
+	RPMB_ERR_ADDRESS = 0x0004,
+	RPMB_ERR_WRITE   = 0x0005,
+	RPMB_ERR_READ    = 0x0006,
+	RPMB_ERR_NO_KEY  = 0x0007,
+
+	RPMB_ERR_COUNTER_EXPIRED = 0x0080
+};
+
+/**
+ * enum rpmb_type - type of underlaying storage technology
+ *
+ * @RPMB_TYPE_ANY   : any type used for search only
+ * @RPMB_TYPE_EMMC  : emmc (JESD84-B50.1)
+ * @RPMB_TYPE_UFS   : UFS (JESD220)
+ * @RPMB_TYPE_SIM   : Simulation Device type
+ * @RPMB_TYPE_MAX   : upper sentinel
+ */
+enum rpmb_type {
+	RPMB_TYPE_ANY = 0,
+	RPMB_TYPE_EMMC,
+	RPMB_TYPE_UFS,
+	RPMB_TYPE_SIM,
+	RPMB_TYPE_MAX = RPMB_TYPE_SIM
+};
+
+extern struct class rpmb_class;
+
+#define RPMB_F_WRITE     BIT(0)
+#define RPMB_F_REL_WRITE BIT(1)
+
+/**
+ * struct rpmb_cmd: rpmb access command
+ *
+ * @flags:   command flags
+ *      0 - read command
+ *      1 - write commnad RPMB_F_WRITE
+ *      2 - reliable write RPMB_F_REL_WRITE
+ * @nframes: number of rpmb frames in the command
+ * @frames:  list of rpmb frames
+ */
+struct rpmb_cmd {
+	u32 flags;
+	u32 nframes;
+	struct rpmb_frame *frames __aligned(8);
+};
+
+/**
+ * struct rpmb_data - rpmb data be transmitted in RPMB request
+ *
+ * @req_type: request type (program key, read, write, write counter)
+ * @icmd:     list of input frames
+ * @ocmd:     list of result frames
+ */
+struct rpmb_data {
+	u16 req_type;
+	struct rpmb_cmd icmd;
+	struct rpmb_cmd ocmd;
+};
+
+/**
+ * struct rpmb_ops - RPMB ops to be implemented by underlaying block device
+ *
+ * @cmd_seq        : send RPMB command sequence to the RPBM partition
+ *                   backed by the disk
+ * @type           : block device type
+ * @dev_id         : unique device identifier
+ * @dev_id_len     : unique device identifier length
+ * @reliable_wr_cnt: number of sectors that can be written in one access
+ */
+struct rpmb_ops {
+	int (*cmd_seq)(struct device *dev, struct rpmb_cmd *cmds, u32 ncmds);
+	enum rpmb_type type;
+	const u8 *dev_id;
+	size_t dev_id_len;
+	u16 reliable_wr_cnt;
+};
+
+/**
+ * struct rpmb_dev - device which can support RPMB partition
+ *
+ * @lock       : the device lock
+ * @dev        : device
+ * @id         : device id
+ * @ops        : operation exported by block layer
+ */
+struct rpmb_dev {
+	struct mutex lock; /* device serialization lock */
+	struct device dev;
+	int    id;
+	const struct rpmb_ops *ops;
+};
+
+#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
+
+#if IS_ENABLED(CONFIG_RPMB)
+struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
+void rpmb_dev_put(struct rpmb_dev *rdev);
+struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent);
+struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type);
+struct rpmb_dev *rpmb_dev_register(struct device *dev,
+				   const struct rpmb_ops *ops);
+struct rpmb_dev *rpmb_dev_find_device(void *data,
+			int (*match)(struct device *dev, const void *data));
+int rpmb_dev_unregister(struct device *dev);
+int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds);
+int rpmb_cmd_req(struct rpmb_dev *rdev, struct rpmb_data *data);
+
+#else
+static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
+{
+	return NULL;
+}
+
+static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
+
+static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent)
+{
+	return NULL;
+}
+
+static inline
+struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type)
+{
+	return NULL;
+}
+
+static inline struct rpmb_dev *
+rpmb_dev_register(struct device *dev, const struct rpmb_ops *ops)
+{
+	return NULL;
+}
+
+static inline int rpmb_dev_unregister(struct device *dev)
+{
+	return 0;
+}
+
+static inline int rpmb_cmd_seq(struct rpmb_dev *rdev,
+			       struct rpmb_cmd *cmds, u32 ncmds)
+{
+	return 0;
+}
+
+static inline int rpmb_cmd_req(struct rpmb_dev *rdev, struct rpmb_data *data)
+{
+	return 0;
+}
+
+#endif /* CONFIG_RPMB */
+
+#endif /* __RPMB_H__ */
-- 
2.7.4


^ permalink raw reply related

* [PATCH v7 00/11] char:rpmb: Replay Protected Memory Block (RPMB) subsystem
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler

Few storage technologies such is EMMC, UFS, and NVMe support RPMB
hardware partition with common protocol and frame layout.
The RPMB partition cannot be accessed via standard block layer, but by a
set of specific commands: WRITE, READ, GET_WRITE_COUNTER, and
PROGRAM_KEY.
Such a partition provides authenticated and replay protected access,
hence suitable as a secure storage.

The RPMB layer aims to provide in-kernel API for Trusted Execution
Environment (TEE) devices that are capable to securely compute block
frame signature. In case a TEE device wish to store a replay protected
data, it creates an RPMB frame with requested data and computes HMAC of
the frame, then it requests the storage device via RPMB layer to store
the data.

The layer provides two APIs, for rpmb_req_cmd() for issuing one of RPMB
specific commands and rpmb_seq_cmd() for issuing of raw RPMB protocol
frames,  which is close to the functionality provided by emmc multi ioctl
interface.

A TEE driver can claim the RPMB interface, for example, via
class_interface_register ().

A storage device registers its RPMB hardware (eMMC) partition or RPMB
W-LUN (UFS) with the RPMB layer providing an implementation for
rpmb_seq_cmd() handler. The interface enables sending sequence of RPMB
standard frames.

A parallel user space API is provided via /dev/rpmbX character
device with two IOCTL commands.
Simplified one, RPMB_IOC_REQ_CMD, were read result cycles is performed
by the framework on behalf the user and second, RPMB_IOC_SEQ_CMD where
the whole RPMB sequence, including RESULT_READ is supplied by the caller.
The latter is intended for easier adjusting of the applications that
use MMC_IOC_MULTI_CMD ioctl, such as
https://android.googlesource.com/trusty/app/storage/

There is a also sample tool under tools/rpmb/ directory that exercises
these interfaces and a simulation device that implements the device part.

The code is also available from:

https://github.com/tomasbw/linux-mei.git rpmb

V7  1. Special thanks to Avri Altman <avri.altman@sandisk.com> for testing emmc and ufs.
    2. Fix char device interface.
    3. Add Documentation
    4. Addional small fixes described in individual patches.

Tomas Winkler (11):
  rpmb: add Replay Protected Memory Block (RPMB) subsystem
  rpmb: enable emmc specific read data fixup
  rpmb: add sysfs-class ABI documentation
  char: rpmb: add device attributes
  char: rpmb: provide a user space interface
  char: rpmb: add RPMB simulation device
  tools rpmb: add RPBM access tool
  mmc: block: register RPMB partition with the RPMB subsystem
  scsi: ufs: connect to RPMB subsystem
  scsi: ufs: retrieve rpmb rw size
  char: rpmb: Document Replay Protected Memory Block (RPMB) subsystem

 Documentation/ABI/testing/sysfs-class-rpmb |   47 ++
 Documentation/index.rst                    |    1 +
 Documentation/ioctl/ioctl-number.txt       |    1 +
 Documentation/rpmb/conf.py                 |    5 +
 Documentation/rpmb/index.rst               |   18 +
 Documentation/rpmb/introduction.rst        |  102 +++
 Documentation/rpmb/rpmb-tool.rst           |   19 +
 Documentation/rpmb/simulation-device.rst   |   19 +
 MAINTAINERS                                |   11 +
 drivers/char/Kconfig                       |    2 +
 drivers/char/Makefile                      |    1 +
 drivers/char/rpmb/Kconfig                  |   32 +
 drivers/char/rpmb/Makefile                 |    6 +
 drivers/char/rpmb/cdev.c                   |  331 +++++++++
 drivers/char/rpmb/core.c                   |  523 ++++++++++++++
 drivers/char/rpmb/rpmb-cdev.h              |   25 +
 drivers/char/rpmb/rpmb_sim.c               |  744 ++++++++++++++++++++
 drivers/mmc/card/Kconfig                   |    1 +
 drivers/mmc/card/block.c                   |  258 ++++++-
 drivers/scsi/ufs/Kconfig                   |    1 +
 drivers/scsi/ufs/ufs.h                     |    6 +
 drivers/scsi/ufs/ufshcd.c                  |  205 ++++++
 drivers/scsi/ufs/ufshcd.h                  |    2 +
 include/linux/rpmb.h                       |  167 +++++
 include/uapi/linux/Kbuild                  |    1 +
 include/uapi/linux/rpmb.h                  |  153 ++++
 tools/Makefile                             |   14 +-
 tools/rpmb/.gitignore                      |    2 +
 tools/rpmb/Makefile                        |   34 +
 tools/rpmb/rpmb.c                          | 1035 ++++++++++++++++++++++++++++
 30 files changed, 3758 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-rpmb
 create mode 100644 Documentation/rpmb/conf.py
 create mode 100644 Documentation/rpmb/index.rst
 create mode 100644 Documentation/rpmb/introduction.rst
 create mode 100644 Documentation/rpmb/rpmb-tool.rst
 create mode 100644 Documentation/rpmb/simulation-device.rst
 create mode 100644 drivers/char/rpmb/Kconfig
 create mode 100644 drivers/char/rpmb/Makefile
 create mode 100644 drivers/char/rpmb/cdev.c
 create mode 100644 drivers/char/rpmb/core.c
 create mode 100644 drivers/char/rpmb/rpmb-cdev.h
 create mode 100644 drivers/char/rpmb/rpmb_sim.c
 create mode 100644 include/linux/rpmb.h
 create mode 100644 include/uapi/linux/rpmb.h
 create mode 100644 tools/rpmb/.gitignore
 create mode 100644 tools/rpmb/Makefile
 create mode 100644 tools/rpmb/rpmb.c

-- 
2.7.4


^ permalink raw reply

* RE: [PATCH v7 09/11] scsi: ufs: connect to RPMB subsystem
From: Winkler, Tomas @ 2016-11-07 19:27 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Greg Kroah-Hartman, Ulf Hansson, Hunter, Adrian, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hj?nnev?g, Michael Ryleev, Joao Pinto, Yaniv Gardi,
	Avri Altman, linux-kernel@vger.kernel.org,
	linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org,
	linux-doc@vger.kernel.org,
	"Usyskin, Alexander" <alexan>
In-Reply-To: <20161107190109.GA5110@lst.de>

> 
> On Mon, Nov 07, 2016 at 09:53:12PM +0200, Tomas Winkler wrote:
> > Register UFS RPMB LUN with the RPMB subsystem and provide
> > implementation for the RPMB access operations. RPMB partition is
> > accessed via a sequence of security protocol in and security protocol
> > out commands with UFS specific parameters. This multi step process is
> > abstracted into 4 basic RPMB commands.
> 
> This is a giant layering violation - the security protocol is not something up to
> the LLDD but the core code.
> 
> And honestly the idea of defintining a security protocol in the UFS spec is just
> as braindead.  If you care about this please take it up with T10 to get RPMB
> support into one of the core SCSI specs instead of a transport.

I  value your opinion but I'm not responsible for inventing RPMB 
and/or its  implementation storage devices (eMMC, UFC, NVMe), it's pretty much done deal out there in the wild. 
I'm just trying to provide common API above it.

Thanks
Tomas


^ permalink raw reply

* Re: [PATCH v7 09/11] scsi: ufs: connect to RPMB subsystem
From: Christoph Hellwig @ 2016-11-07 19:01 UTC (permalink / raw)
  To: Tomas Winkler
  Cc: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi, Avri Altman, linux-kernel,
	linux-mmc, linux-scsi, linux-doc, Alexander Usyskin
In-Reply-To: <1478548394-8184-10-git-send-email-tomas.winkler@intel.com>

On Mon, Nov 07, 2016 at 09:53:12PM +0200, Tomas Winkler wrote:
> Register UFS RPMB LUN with the RPMB subsystem and provide
> implementation for the RPMB access operations. RPMB partition is
> accessed via a sequence of security protocol in and security protocol
> out commands with UFS specific parameters. This multi step process is
> abstracted into 4 basic RPMB commands.

This is a giant layering violation - the security protocol is not something
up to the LLDD but the core code.

And honestly the idea of defintining a security protocol in the UFS
spec is just as braindead.  If you care about this please take it up
with T10 to get RPMB support into one of the core SCSI specs instead
of a transport.

^ permalink raw reply

* [PATCH v7 04/11] char: rpmb: add device attributes
From: Tomas Winkler @ 2016-11-07 19:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Ulf Hansson, Adrian Hunter, James Bottomley,
	Martin K . Petersen, Vinayak Holikatti, Andy Lutomirski,
	Arve Hjønnevåg, Michael Ryleev, Joao Pinto,
	Christoph Hellwig, Yaniv Gardi
  Cc: Avri Altman, linux-kernel, linux-mmc, linux-scsi, linux-doc,
	Tomas Winkler
In-Reply-To: <1478548394-8184-1-git-send-email-tomas.winkler@intel.com>

Add attribute type that displays underlay storage type technology
EMMC, UFS, and attribute id, that displays underlay storage device id.
For EMMC this would be content of CID and for UFS serial number from
the device descriptor.

V2: resend
V3: set kernel version to 4.7
V4: update target date to Maj
V5: update date and kernel version
V6: 1. Add simulation device type
    2. Update date and kernel version
    3. Use binary attribute for id
    4. use simple sprintf instead of scnprintf
    5. Add more verbose documenation
V7: resend

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
 Documentation/ABI/testing/sysfs-class-rpmb | 27 +++++++++++
 drivers/char/rpmb/core.c                   | 72 ++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb
index 2ea6dbb91fac..3c01925e29dc 100644
--- a/Documentation/ABI/testing/sysfs-class-rpmb
+++ b/Documentation/ABI/testing/sysfs-class-rpmb
@@ -18,3 +18,30 @@ Contact:	Tomas Winkler <tomas.winkler@intel.com>
 Description:
 		The /sys/class/rpmb/rpmbN directory is created for
 		each RPMB registered device.
+
+What:		/sys/class/rpmb/rpmbN/type
+Date:		Nov 2016
+KernelVersion:	4.10
+Contact:	Tomas Winkler <tomas.winkler@intel.com>
+Description:
+		The /sys/class/rpmb/rpmbN/type file contains device
+		underlaying storage type technology: EMMC, UFS, SIM.
+
+What:		/sys/class/rpmb/rpmbN/id
+Date:		Nov 2016
+KernelVersion:	4.10
+Contact:	Tomas Winkler <tomas.winkler@intel.com>
+Description:
+		The /sys/class/rpmb/rpmbN/id file contains unique device id
+		in a binary form as defined by underlying storage device.
+		In case of multiple RPMB devices a user can determine correct
+		device.
+		The content can be parsed according the storage device type.
+
+What:		/sys/class/rpmb/rpmbN/reliable_wr_cnt
+Date:		Nov 2016
+KernelVersion:	4.10
+Contact:	Tomas Winkler <tomas.winkler@intel.com>
+Description:
+		The /sys/class/rpmb/rpmbN/reliable_wr_cnt file contains
+		number of blocks that can be reliable written in a single request.
diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
index 8cfbbb721538..f6b4cabb0982 100644
--- a/drivers/char/rpmb/core.c
+++ b/drivers/char/rpmb/core.c
@@ -338,6 +338,76 @@ struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent)
 }
 EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device);
 
+static ssize_t type_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+	ssize_t ret;
+
+	switch (rdev->ops->type) {
+	case RPMB_TYPE_EMMC:
+		ret = sprintf(buf, "EMMC\n");
+		break;
+	case RPMB_TYPE_UFS:
+		ret = sprintf(buf, "UFS\n");
+		break;
+	case RPMB_TYPE_SIM:
+		ret = sprintf(buf, "SIM\n");
+		break;
+	default:
+		ret = sprintf(buf, "UNKNOWN\n");
+		break;
+	}
+
+	return ret;
+}
+static DEVICE_ATTR_RO(type);
+
+static ssize_t id_read(struct file *file, struct kobject *kobj,
+		       struct bin_attribute *attr, char *buf,
+		       loff_t off, size_t count)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+	size_t sz = min_t(size_t, rdev->ops->dev_id_len, PAGE_SIZE);
+
+	if (!rdev->ops->dev_id)
+		return 0;
+
+	return memory_read_from_buffer(buf, count, &off, rdev->ops->dev_id, sz);
+}
+static BIN_ATTR_RO(id, 0);
+
+static ssize_t reliable_wr_cnt_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+
+	return sprintf(buf, "%u\n", rdev->ops->reliable_wr_cnt);
+}
+static DEVICE_ATTR_RO(reliable_wr_cnt);
+
+static struct attribute *rpmb_attrs[] = {
+	&dev_attr_type.attr,
+	&dev_attr_reliable_wr_cnt.attr,
+	NULL,
+};
+
+static struct bin_attribute *rpmb_bin_attributes[] = {
+	&bin_attr_id,
+	NULL,
+};
+
+static struct attribute_group rpmb_attr_group = {
+	.attrs = rpmb_attrs,
+	.bin_attrs = rpmb_bin_attributes,
+};
+
+static const struct attribute_group *rpmb_attr_groups[] = {
+	&rpmb_attr_group,
+	NULL
+};
+
 /**
  * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem
  *
@@ -407,6 +477,8 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev,
 	dev_set_name(&rdev->dev, "rpmb%d", id);
 	rdev->dev.class = &rpmb_class;
 	rdev->dev.parent = dev;
+	rdev->dev.groups = rpmb_attr_groups;
+
 	ret = device_register(&rdev->dev);
 	if (ret)
 		goto exit;
-- 
2.7.4


^ permalink raw reply related

* Re: [PATCH v3 0/3] mmc: sh_mobile_sdhi: Add r7s72100 support
From: Ulf Hansson @ 2016-11-07 18:38 UTC (permalink / raw)
  To: Chris Brandt
  Cc: Wolfram Sang, Sergei Shtylyov, Geert Uytterhoeven, Simon Horman,
	linux-mmc, Linux-Renesas
In-Reply-To: <20160912141507.6837-1-chris.brandt@renesas.com>

On 12 September 2016 at 16:15, Chris Brandt <chris.brandt@renesas.com> wrote:
> For the most part, the SDHI controller in the RZ/A1 (r7s72100)
> is the same as other Renesas SoC...except for the fact that the
> data port register was widened to 32-bits and the 16-bit accesses
> in the current tmio driver aren't going to cut it.
>
> Also, the tmio driver allows you to pass in what voltages you
> support via the ocr_mask. For the RZ/A1, it's just simple 3.3v.
> So, I added the ability to pass that when using DT.
>
> Chris Brandt (3):
>   mmc: sh_mobile_sdhi: add ocr_mask option
>   mmc: tmio-mmc: add support for 32bit data port
>   mmc: sh_mobile_sdhi: Add r7s72100 support
>
>  Documentation/devicetree/bindings/mmc/tmio_mmc.txt |  1 +
>  drivers/mmc/host/sh_mobile_sdhi.c                  |  9 +++++++
>  drivers/mmc/host/tmio_mmc.h                        | 12 +++++++++
>  drivers/mmc/host/tmio_mmc_pio.c                    | 30 ++++++++++++++++++++++
>  include/linux/mfd/tmio.h                           |  5 ++++
>  5 files changed, 57 insertions(+)
>
> --
> 2.9.2
>
>

Thanks, applied for next!

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH] mmc: mmc_test: Fix "Commands during non-blocking write" tests
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: linux-mmc, Harjani Ritesh
In-Reply-To: <1478259665-13406-1-git-send-email-adrian.hunter@intel.com>

On 4 November 2016 at 12:41, Adrian Hunter <adrian.hunter@intel.com> wrote:
> mmc_test_check_result_async() requires that struct mmc_async_req is
> contained within struct mmc_test_async_req. Fix the "Commands during
> non-blocking write" tests so that is the case.
>
> Fixes: 4bbb9aac9a9a ("mmc: mmc_test: Add tests for sending commands during transfer")
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>

Thanks, applied for fixes!

Kind regards
Uffe

> ---
>  drivers/mmc/card/mmc_test.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
> index 5a8dc5a76e0d..3678220964fe 100644
> --- a/drivers/mmc/card/mmc_test.c
> +++ b/drivers/mmc/card/mmc_test.c
> @@ -2347,7 +2347,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
>         struct mmc_test_req *rq = mmc_test_req_alloc();
>         struct mmc_host *host = test->card->host;
>         struct mmc_test_area *t = &test->area;
> -       struct mmc_async_req areq;
> +       struct mmc_test_async_req test_areq = { .test = test };
>         struct mmc_request *mrq;
>         unsigned long timeout;
>         bool expired = false;
> @@ -2363,8 +2363,8 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
>                 mrq->sbc = &rq->sbc;
>         mrq->cap_cmd_during_tfr = true;
>
> -       areq.mrq = mrq;
> -       areq.err_check = mmc_test_check_result_async;
> +       test_areq.areq.mrq = mrq;
> +       test_areq.areq.err_check = mmc_test_check_result_async;
>
>         mmc_test_prepare_mrq(test, mrq, t->sg, t->sg_len, dev_addr, t->blocks,
>                              512, write);
> @@ -2378,7 +2378,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
>
>         /* Start ongoing data request */
>         if (use_areq) {
> -               mmc_start_req(host, &areq, &ret);
> +               mmc_start_req(host, &test_areq.areq, &ret);
>                 if (ret)
>                         goto out_free;
>         } else {
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH] clk: qcom: fix semicolon.cocci warnings
From: kbuild test robot @ 2016-11-07 13:51 UTC (permalink / raw)
  To: Ritesh Harjani
  Cc: kbuild-all, ulf.hansson, linux-mmc, adrian.hunter, shawn.lin,
	sboyd, devicetree, linux-clk, david.brown, andy.gross,
	linux-arm-msm, georgi.djakov, alex.lemberg, mateusz.nowak,
	Yuliy.Izrailov, asutoshd, kdorfman, david.griego, stummala,
	venkatg, rnayak, pramod.gurav
In-Reply-To: <1478517877-23733-3-git-send-email-riteshh@codeaurora.org>

drivers/clk/qcom/clk-rcg2.c:306:2-3: Unneeded semicolon
drivers/clk/qcom/clk-rcg2.c:201:2-3: Unneeded semicolon


 Remove unneeded semicolon.

Generated by: scripts/coccinelle/misc/semicolon.cocci

CC: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---

 clk-rcg2.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -198,7 +198,7 @@ static int _freq_tbl_determine_rate(stru
 		break;
 	default:
 		return -EINVAL;
-	};
+	}
 
 	if (!f)
 		return -EINVAL;
@@ -303,7 +303,7 @@ static int __clk_rcg2_set_rate(struct cl
 		break;
 	default:
 		return -EINVAL;
-	};
+	}
 
 	if (!f)
 		return -EINVAL;

^ permalink raw reply

* Re: [PATCH v6 02/14] clk: qcom: Add rcg ops to return floor value closest to the requested rate
From: kbuild test robot @ 2016-11-07 13:51 UTC (permalink / raw)
  To: Ritesh Harjani
  Cc: kbuild-all-JC7UmRfGjtg, ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	adrian.hunter-ral2JQCrhuEAvxtiuMwx3w,
	shawn.lin-TNX95d0MmH7DzftRWevZcw, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	georgi.djakov-QSEj5FYQhm4dnm+yROfE0A,
	alex.lemberg-XdAiOPVOjttBDgjK7y7TUQ,
	mateusz.nowak-ral2JQCrhuEAvxtiuMwx3w,
	Yuliy.Izrailov-XdAiOPVOjttBDgjK7y7TUQ,
	asutoshd-sgV2jX0FEOL9JmXXK+q4OQ, kdorfman-sgV2jX0FEOL9JmXXK+q4OQ,
	david.griego-QSEj5FYQhm4dnm+yROfE0A,
	stummala-sgV2jX0FEOL9JmXXK+q4OQ, venkatg-sgV2jX0FEOL9JmXXK+q4OQ,
	rnayak-sgV2jX0FEOL9JmXXK+q4OQ,
	pramod.gurav-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1478517877-23733-3-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 4963 bytes --]

Hi Rajendra,

[auto build test WARNING on ulf.hansson-mmc/next]
[also build test WARNING on v4.9-rc4 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Ritesh-Harjani/mmc-sdhci-msm-Add-clk-rates-DDR-HS400-support/20161107-203031
base:   https://git.linaro.org/people/ulf.hansson/mmc next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   drivers/clk/qcom/clk-rcg2.c: In function '_freq_tbl_determine_rate':
>> drivers/clk/qcom/clk-rcg2.c:192:2: warning: switch condition has boolean value [-Wswitch-bool]
     switch (match) {
     ^~~~~~
   drivers/clk/qcom/clk-rcg2.c: In function '__clk_rcg2_set_rate':
   drivers/clk/qcom/clk-rcg2.c:297:2: warning: switch condition has boolean value [-Wswitch-bool]
     switch (match) {
     ^~~~~~

coccinelle warnings: (new ones prefixed by >>)

>> drivers/clk/qcom/clk-rcg2.c:306:2-3: Unneeded semicolon
   drivers/clk/qcom/clk-rcg2.c:201:2-3: Unneeded semicolon

Please review and possibly fold the followup patch.

vim +192 drivers/clk/qcom/clk-rcg2.c

   186	{
   187		unsigned long clk_flags, rate = req->rate;
   188		struct clk_hw *p;
   189		struct clk_rcg2 *rcg = to_clk_rcg2(hw);
   190		int index;
   191	
 > 192		switch (match) {
   193		case FLOOR:
   194			f = qcom_find_freq_floor(f, rate);
   195			break;
   196		case CEIL:
   197			f = qcom_find_freq(f, rate);
   198			break;
   199		default:
   200			return -EINVAL;
   201		};
   202	
   203		if (!f)
   204			return -EINVAL;
   205	
   206		index = qcom_find_src_index(hw, rcg->parent_map, f->src);
   207		if (index < 0)
   208			return index;
   209	
   210		clk_flags = clk_hw_get_flags(hw);
   211		p = clk_hw_get_parent_by_index(hw, index);
   212		if (clk_flags & CLK_SET_RATE_PARENT) {
   213			if (f->pre_div) {
   214				rate /= 2;
   215				rate *= f->pre_div + 1;
   216			}
   217	
   218			if (f->n) {
   219				u64 tmp = rate;
   220				tmp = tmp * f->n;
   221				do_div(tmp, f->m);
   222				rate = tmp;
   223			}
   224		} else {
   225			rate =  clk_hw_get_rate(p);
   226		}
   227		req->best_parent_hw = p;
   228		req->best_parent_rate = rate;
   229		req->rate = f->freq;
   230	
   231		return 0;
   232	}
   233	
   234	static int clk_rcg2_determine_rate(struct clk_hw *hw,
   235					   struct clk_rate_request *req)
   236	{
   237		struct clk_rcg2 *rcg = to_clk_rcg2(hw);
   238	
   239		return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
   240	}
   241	
   242	static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
   243						 struct clk_rate_request *req)
   244	{
   245		struct clk_rcg2 *rcg = to_clk_rcg2(hw);
   246	
   247		return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
   248	}
   249	
   250	static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
   251	{
   252		u32 cfg, mask;
   253		struct clk_hw *hw = &rcg->clkr.hw;
   254		int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
   255	
   256		if (index < 0)
   257			return index;
   258	
   259		if (rcg->mnd_width && f->n) {
   260			mask = BIT(rcg->mnd_width) - 1;
   261			ret = regmap_update_bits(rcg->clkr.regmap,
   262					rcg->cmd_rcgr + M_REG, mask, f->m);
   263			if (ret)
   264				return ret;
   265	
   266			ret = regmap_update_bits(rcg->clkr.regmap,
   267					rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
   268			if (ret)
   269				return ret;
   270	
   271			ret = regmap_update_bits(rcg->clkr.regmap,
   272					rcg->cmd_rcgr + D_REG, mask, ~f->n);
   273			if (ret)
   274				return ret;
   275		}
   276	
   277		mask = BIT(rcg->hid_width) - 1;
   278		mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
   279		cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
   280		cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
   281		if (rcg->mnd_width && f->n && (f->m != f->n))
   282			cfg |= CFG_MODE_DUAL_EDGE;
   283		ret = regmap_update_bits(rcg->clkr.regmap,
   284				rcg->cmd_rcgr + CFG_REG, mask, cfg);
   285		if (ret)
   286			return ret;
   287	
   288		return update_config(rcg);
   289	}
   290	
   291	static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
   292				       bool match)
   293	{
   294		struct clk_rcg2 *rcg = to_clk_rcg2(hw);
   295		const struct freq_tbl *f;
   296	
   297		switch (match) {
   298		case FLOOR:
   299			f = qcom_find_freq_floor(rcg->freq_tbl, rate);
   300			break;
   301		case CEIL:
   302			f = qcom_find_freq(rcg->freq_tbl, rate);
   303			break;
   304		default:
   305			return -EINVAL;
 > 306		};
   307	
   308		if (!f)
   309			return -EINVAL;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56829 bytes --]

^ permalink raw reply

* [PATCH v4 0/5] Add intial support to DW MMC host on ZTE SoC
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie

Add intial support to DW MMC host on ZTE SoC. It include platform
specific wrapper driver and workarounds for fifo quirk.

Patches are prepared based on latest dw mmc runtime change:
   https://github.com/jh80chung/dw-mmc.git for-ulf

Changes vs version 3:
  - Fix brace error in document.

Changes vs version 2:
  - Change dt property fifo-addr to data-addr and fifo-watermark-quirk to
    fifo-watermark-aligned.
  - Polish ZX MMC driver on minor coding style issues.

Changes vs version 1:
  - Change fifo-addr-override to fifo-addr and remove its workaround tag in comments.
  - Remove ZX DW MMC driver reset cap in driver, which can be added in dt nodes.

Jun Nie (5):
  mmc: dt-bindings: add ZTE ZX296718 MMC bindings
  mmc: zx: Initial support for ZX mmc controller
  Documentation: synopsys-dw-mshc: add binding for fifo quirks
  mmc: dw: Add fifo address property
  mmc: dw: Add fifo watermark alignment property

 .../devicetree/bindings/mmc/synopsys-dw-mshc.txt   |  13 ++
 .../devicetree/bindings/mmc/zx-dw-mshc.txt         |  34 +++
 drivers/mmc/host/Kconfig                           |   9 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/dw_mmc-zx.c                       | 242 +++++++++++++++++++++
 drivers/mmc/host/dw_mmc-zx.h                       |  31 +++
 drivers/mmc/host/dw_mmc.c                          |  17 +-
 include/linux/mmc/dw_mmc.h                         |   5 +
 8 files changed, 349 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-zx.c
 create mode 100644 drivers/mmc/host/dw_mmc-zx.h

-- 
1.9.1


^ permalink raw reply

* [PATCH v4 1/5] mmc: dt-bindings: add ZTE ZX296718 MMC bindings
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

Document the device-tree binding of ZTE MMC host on
ZX296718 SoC.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 .../devicetree/bindings/mmc/zx-dw-mshc.txt         | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt

diff --git a/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
new file mode 100644
index 0000000..c175c4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
@@ -0,0 +1,35 @@
+* ZTE specific extensions to the Synopsys Designware Mobile Storage
+  Host Controller
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the ZTE specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+	- "zte,zx296718-dw-mshc": for ZX SoCs
+
+Example:
+
+	mmc1: mmc@1110000 {
+		compatible = "zte,zx296718-dw-mshc";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x01110000 0x1000>;
+		interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+		fifo-depth = <32>;
+		data-addr = <0x200>;
+		fifo-watermark-aligned;
+		bus-width = <4>;
+		clock-frequency = <50000000>;
+		clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
+		clock-names = "biu", "ciu";
+		num-slots = <1>;
+		max-frequency = <50000000>;
+		cap-sdio-irq;
+		cap-sd-highspeed;
+		status = "disabled";
+	};
-- 
1.9.1


^ permalink raw reply related

* [PATCH v4 5/5] mmc: dw: Add fifo watermark alignment property
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

Data done irq is expected if data length is less than
watermark in PIO mode. But fifo watermark is requested
to be aligned with data length in some SoC so that TX/RX
irq can be generated with data done irq. Add the
watermark alignment to mark this requirement and force
fifo watermark setting accordingly.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 drivers/mmc/host/dw_mmc.c  | 11 +++++++++--
 include/linux/mmc/dw_mmc.h |  3 +++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 696b5e6..6d85ca6 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1111,11 +1111,15 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
 		mci_writel(host, CTRL, temp);
 
 		/*
-		 * Use the initial fifoth_val for PIO mode.
+		 * Use the initial fifoth_val for PIO mode. If wm_algined
+		 * is set, we set watermark same as data size.
 		 * If next issued data may be transfered by DMA mode,
 		 * prev_blksz should be invalidated.
 		 */
-		mci_writel(host, FIFOTH, host->fifoth_val);
+		if (host->wm_aligned)
+			dw_mci_adjust_fifoth(host, data);
+		else
+			mci_writel(host, FIFOTH, host->fifoth_val);
 		host->prev_blksz = 0;
 	} else {
 		/*
@@ -2957,6 +2961,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
 	of_property_read_u32(np, "data-addr", &host->data_addr_override);
 
+	if (of_get_property(np, "fifo-watermark-aligned", NULL))
+		host->wm_aligned = true;
+
 	if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
 		pdata->bus_hz = clock_frequency;
 
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 17cb95a..ee4bb30 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -108,6 +108,8 @@ struct dw_mci_dma_slave {
  * @slot: Slots sharing this MMC controller.
  * @fifo_depth: depth of FIFO.
  * @data_addr_override: override fifo reg offset with this value.
+ * @wm_aligned: force fifo watermark equal with data length in PIO mode.
+ *	Set as true if alignment is needed.
  * @data_shift: log2 of FIFO item size.
  * @part_buf_start: Start index in part_buf.
  * @part_buf_count: Bytes of partial data in part_buf.
@@ -156,6 +158,7 @@ struct dw_mci {
 	void __iomem		*regs;
 	void __iomem		*fifo_reg;
 	u32			data_addr_override;
+	bool			wm_aligned;
 
 	struct scatterlist	*sg;
 	struct sg_mapping_iter	sg_miter;
-- 
1.9.1


^ permalink raw reply related

* [PATCH v4 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

Add fifo-addr property and fifo-watermark-quirk property to
synopsys-dw-mshc bindings. It is intended to provide more
dt interface to support SoCs specific configuration.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 4e00e85..8bf2e41 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -76,6 +76,17 @@ Optional properties:
 
 * broken-cd: as documented in mmc core bindings.
 
+* data-addr: Override fifo address with value provided by DT. The default FIFO reg
+  offset is assumed as 0x100 (version < 0x240A) and 0x200(version >= 0x240A) by
+  driver. If the controller does not follow this rule, please use this property
+  to set fifo address in device tree.
+
+* fifo-watermark-aligned: Data done irq is expected if data length is less than
+  watermark in PIO mode. But fifo watermark is requested to be aligned with data
+  length in some SoC so that TX/RX irq can be generated with data done irq. Add this
+  watermark quirk to mark this requirement and force fifo watermark setting
+  accordingly.
+
 * vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
   specified we'll defer probe until we can find this regulator.
 
@@ -103,6 +114,8 @@ board specific portions as listed below.
 		interrupts = <0 75 0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
+		data-addr = <0x200>;
+		fifo-watermark-aligned;
 	};
 
 [board specific internal DMA resources]
-- 
1.9.1


^ permalink raw reply related

* [PATCH v4 4/5] mmc: dw: Add fifo address property
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

The FIFO address may break default address assumption of 0x100
(version < 0x240A) and 0x200(version >= 0x240A) in current driver.
The new property is introduced to override fifo address via DT
node information.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 drivers/mmc/host/dw_mmc.c  | 6 +++++-
 include/linux/mmc/dw_mmc.h | 2 ++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1c9ee36..696b5e6 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2955,6 +2955,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
 	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
 
+	of_property_read_u32(np, "data-addr", &host->data_addr_override);
+
 	if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
 		pdata->bus_hz = clock_frequency;
 
@@ -3158,7 +3160,9 @@ int dw_mci_probe(struct dw_mci *host)
 	host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
 	dev_info(host->dev, "Version ID is %04x\n", host->verid);
 
-	if (host->verid < DW_MMC_240A)
+	if (host->data_addr_override)
+		host->fifo_reg = host->regs + host->data_addr_override;
+	else if (host->verid < DW_MMC_240A)
 		host->fifo_reg = host->regs + DATA_OFFSET;
 	else
 		host->fifo_reg = host->regs + DATA_240A_OFFSET;
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index f5af2bd..17cb95a 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -107,6 +107,7 @@ struct dw_mci_dma_slave {
  * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
  * @fifo_depth: depth of FIFO.
+ * @data_addr_override: override fifo reg offset with this value.
  * @data_shift: log2 of FIFO item size.
  * @part_buf_start: Start index in part_buf.
  * @part_buf_count: Bytes of partial data in part_buf.
@@ -154,6 +155,7 @@ struct dw_mci {
 	spinlock_t		irq_lock;
 	void __iomem		*regs;
 	void __iomem		*fifo_reg;
+	u32			data_addr_override;
 
 	struct scatterlist	*sg;
 	struct sg_mapping_iter	sg_miter;
-- 
1.9.1


^ permalink raw reply related

* [PATCH v4 2/5] mmc: zx: Initial support for ZX mmc controller
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

This platform driver adds initial support for the DW host controller
found on ZTE SoCs.

It has been tested on ZX296718 EVB board currently. More support on
timing tuning will be added when hardware is available.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 drivers/mmc/host/Kconfig     |   9 ++
 drivers/mmc/host/Makefile    |   1 +
 drivers/mmc/host/dw_mmc-zx.c | 242 +++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc-zx.h |  31 ++++++
 4 files changed, 283 insertions(+)
 create mode 100644 drivers/mmc/host/dw_mmc-zx.c
 create mode 100644 drivers/mmc/host/dw_mmc-zx.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f50..4dafbc2 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -662,6 +662,15 @@ config MMC_DW_ROCKCHIP
 	  Synopsys DesignWare Memory Card Interface driver. Select this option
 	  for platforms based on RK3066, RK3188 and RK3288 SoC's.
 
+config MMC_DW_ZX
+	tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW && ARCH_ZX
+	select MMC_DW_PLTFM
+	help
+	  This selects support for ZTE SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on ZX296718 SoC's.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf..9766143 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_DW_ROCKCHIP)	+= dw_mmc-rockchip.o
+obj-$(CONFIG_MMC_DW_ZX)		+= dw_mmc-zx.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
new file mode 100644
index 0000000..de77408
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.c
@@ -0,0 +1,242 @@
+/*
+ * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Copyright (C) 2016, ZTE Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+#include "dw_mmc-zx.h"
+
+struct dw_mci_zx_priv_data {
+	struct regmap	*sysc_base;
+};
+
+enum delay_type {
+	DELAY_TYPE_READ,	/* read dqs delay */
+	DELAY_TYPE_CLK,		/* clk sample delay */
+};
+
+static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
+				    enum delay_type dflag)
+{
+	struct dw_mci_zx_priv_data *priv = host->priv;
+	struct regmap *sysc_base = priv->sysc_base;
+	unsigned int clksel;
+	unsigned int loop = 1000;
+	int ret;
+
+	if (!sysc_base)
+		return -EINVAL;
+
+	ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
+				 PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
+				 PARA_PHASE_DET_SEL_MASK |
+				 PARA_DLL_LOCK_NUM_MASK |
+				 DLL_REG_SET | PARA_DLL_START_MASK,
+				 PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
+	if (ret)
+		return ret;
+
+	ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
+	if (ret)
+		return ret;
+
+	if (dflag == DELAY_TYPE_CLK) {
+		clksel &= ~CLK_SAMP_DELAY_MASK;
+		clksel |= CLK_SAMP_DELAY(delay);
+	} else {
+		clksel &= ~READ_DQS_DELAY_MASK;
+		clksel |= READ_DQS_DELAY(delay);
+	}
+
+	regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
+	regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
+			   PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
+			   DLL_REG_SET,
+			   PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
+			   DLL_REG_SET);
+
+	do {
+		ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
+		if (ret)
+			return ret;
+
+	} while (--loop && !(clksel & ZX_DLL_LOCKED));
+
+	if (!loop) {
+		dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+	struct dw_mci *host = slot->host;
+	struct mmc_host *mmc = slot->mmc;
+	int len, start = 0, end = 0, delay, best = 0;
+
+	for (delay = 1 ; delay < 128; delay++) {
+		dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
+		if (mmc_send_tuning(mmc, opcode, NULL)) {
+			if (start >= 0) {
+				end = delay - 1;
+				/* check and update longest good range */
+				if ((end - start) > len) {
+					best = (start + end) >> 1;
+					len = end - start;
+				}
+			}
+			start = -1;
+			end = 0;
+			continue;
+		}
+		if (start < 0)
+			start = delay;
+	}
+
+	if (start >= 0) {
+		end = delay - 1;
+		if ((end - start) > len) {
+			best = (start + end) >> 1;
+			len = end - start;
+		}
+	}
+	if (best < 0)
+		return -EIO;
+
+	dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
+		 start, end);
+	dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
+	return 0;
+}
+
+static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
+					  struct mmc_ios *ios)
+{
+	int ret;
+
+	/* config phase shift as 90 degree */
+	ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
+	if (ret < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+	struct dw_mci *host = slot->host;
+
+	if (host->verid == 0x290a) /* only for emmc */
+		return dw_mci_zx_emmc_execute_tuning(slot, opcode);
+	/* TODO: Add 0x210a dedicated tuning for sd/sdio */
+
+	return 0;
+}
+
+static int dw_mci_zx_parse_dt(struct dw_mci *host)
+{
+	struct device_node *np = host->dev->of_node;
+	struct device_node *node;
+	struct dw_mci_zx_priv_data *priv;
+	struct regmap *sysc_base;
+	int ret;
+
+	/* syscon is needed only by emmc */
+	node = of_parse_phandle(np, "zte,aon-syscon", 0);
+	if (node) {
+		sysc_base = syscon_node_to_regmap(node);
+		of_node_put(node);
+
+		if (IS_ERR(sysc_base)) {
+			ret = PTR_ERR(sysc_base);
+			if (ret != -EPROBE_DEFER)
+				dev_err(host->dev, "Can't get syscon: %d\n",
+					ret);
+			return ret;
+		}
+	} else {
+		return 0;
+	}
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->sysc_base = sysc_base;
+	host->priv = priv;
+
+	return 0;
+}
+
+static unsigned long zx_dwmmc_caps[3] = {
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+};
+
+static const struct dw_mci_drv_data zx_drv_data = {
+	.caps			= zx_dwmmc_caps,
+	.execute_tuning		= dw_mci_zx_execute_tuning,
+	.prepare_hs400_tuning	= dw_mci_zx_prepare_hs400_tuning,
+	.parse_dt               = dw_mci_zx_parse_dt,
+};
+
+static const struct of_device_id dw_mci_zx_match[] = {
+	{ .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
+
+static int dw_mci_zx_probe(struct platform_device *pdev)
+{
+	const struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
+	drv_data = match->data;
+
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+			   dw_mci_runtime_resume,
+			   NULL)
+};
+
+static struct platform_driver dw_mci_zx_pltfm_driver = {
+	.probe		= dw_mci_zx_probe,
+	.remove		= dw_mci_pltfm_remove,
+	.driver		= {
+		.name		= "dwmmc_zx",
+		.of_match_table	= dw_mci_zx_match,
+		.pm		= &dw_mci_zx_dev_pm_ops,
+	},
+};
+
+module_platform_driver(dw_mci_zx_pltfm_driver);
+
+MODULE_DESCRIPTION("ZTE emmc/sd driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
new file mode 100644
index 0000000..f369997
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.h
@@ -0,0 +1,31 @@
+#ifndef _DW_MMC_ZX_H_
+#define _DW_MMC_ZX_H_
+
+/* ZX296718 SoC specific DLL register offset. */
+#define LB_AON_EMMC_CFG_REG0  0x1B0
+#define LB_AON_EMMC_CFG_REG1  0x1B4
+#define LB_AON_EMMC_CFG_REG2  0x1B8
+
+/* LB_AON_EMMC_CFG_REG0 register defines */
+#define PARA_DLL_START(x)	((x) & 0xFF)
+#define PARA_DLL_START_MASK	0xFF
+#define DLL_REG_SET		BIT(8)
+#define PARA_DLL_LOCK_NUM(x)	(((x) & 7) << 16)
+#define PARA_DLL_LOCK_NUM_MASK  (7 << 16)
+#define PARA_PHASE_DET_SEL(x)	(((x) & 7) << 20)
+#define PARA_PHASE_DET_SEL_MASK	(7 << 20)
+#define PARA_DLL_BYPASS_MODE	BIT(23)
+#define PARA_HALF_CLK_MODE	BIT(24)
+
+/* LB_AON_EMMC_CFG_REG1 register defines */
+#define READ_DQS_DELAY(x)	((x) & 0x7F)
+#define READ_DQS_DELAY_MASK	(0x7F)
+#define READ_DQS_BYPASS_MODE	BIT(7)
+#define CLK_SAMP_DELAY(x)	(((x) & 0x7F) << 8)
+#define CLK_SAMP_DELAY_MASK	(0x7F << 8)
+#define CLK_SAMP_BYPASS_MODE	BIT(15)
+
+/* LB_AON_EMMC_CFG_REG2 register defines */
+#define ZX_DLL_LOCKED		BIT(2)
+
+#endif /* _DW_MMC_ZX_H_ */
-- 
1.9.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox