diff mbox

[v7,09/11] scsi: ufs: connect to RPMB subsystem

Message ID 1478548394-8184-10-git-send-email-tomas.winkler@intel.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Winkler, Tomas Nov. 7, 2016, 7:53 p.m. UTC
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(+)

Comments

Christoph Hellwig Nov. 7, 2016, 7:01 p.m. UTC | #1
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.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Winkler, Tomas Nov. 7, 2016, 7:27 p.m. UTC | #2
> 
> 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

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig Nov. 7, 2016, 9:37 p.m. UTC | #3
On Mon, Nov 07, 2016 at 07:27:38PM +0000, Winkler, Tomas wrote:
> 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.

And the common API must go through the SCSI midlayer.  If it can't we
won't support it, so please drop the UFS patches from the series.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Winkler, Tomas March 22, 2017, 9:25 p.m. UTC | #4
> 
> On Mon, Nov 07, 2016 at 07:27:38PM +0000, Winkler, Tomas wrote:
> > 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.

Somehow I've missed that answer, now I got back when search for comments before I'll post a new series.
 
> And the common API must go through the SCSI midlayer.  

I've actually tried to do  a midlayer, but I found it's too UFS specific, so there is no point to this abstraction.  
As not all needed data are accessible via scsi calls.  The solution I propose fits with what is done in eMMC (emmc specific  IOCTL).
There is already some code that breaks that layering in  ufshcd_set_dev_pwr_mode when talking to the special sdev_ufs.
Maybe I need more hint for the correct directions.

If it can't we won't
> support it, so please drop the UFS patches from the series.

If you have more constructive comments, I'll be glad to hear and address. 

Thanks
Tomas
diff mbox

Patch

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;