[COMMON] scsi: add ioctl for UFS RPMB
authorhgchu <hg.chu@samsung.com>
Fri, 12 Jan 2018 07:06:23 +0000 (16:06 +0900)
committerJaeHun Jung <jh0801.jung@samsung.com>
Tue, 8 May 2018 08:20:39 +0000 (17:20 +0900)
Change-Id: I8c2159112235b80b567571d9e87add1e85fd5fbb
Signed-off-by: hgchu <hg.chu@samsung.com>
block/scsi_ioctl.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/sd.c
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h
fs/compat_ioctl.c
include/scsi/scsi_host.h
include/scsi/scsi_ioctl.h
include/scsi/scsi_proto.h

index 7440de44dd8577e1a3483b2a1c9d581329fe20e9..63da33334a0eec294c015077bf8215aa3705e51b 100644 (file)
@@ -706,6 +706,8 @@ int scsi_verify_blk_ioctl(struct block_device *bd, unsigned int cmd)
        case SG_GET_RESERVED_SIZE:
        case SG_SET_RESERVED_SIZE:
        case SG_EMULATED_HOST:
+       case SCSI_IOCTL_SECURITY_PROTOCOL_IN:
+       case SCSI_IOCTL_SECURITY_PROTOCOL_OUT:
                return 0;
        case CDROM_GET_CAPABILITY:
                /* Keep this until we remove the printk below.  udev sends it
index 0a875491f5a7621d59268217890c973ab6047083..0e6c2f15b46a3f9a0d97011e119be3ba54e460a7 100644 (file)
@@ -22,6 +22,7 @@
 #include <scsi/scsi_ioctl.h>
 #include <scsi/sg.h>
 #include <scsi/scsi_dbg.h>
+#include <linux/compat.h>
 
 #include "scsi_logging.h"
 
@@ -29,6 +30,7 @@
 #define IOCTL_NORMAL_TIMEOUT                   (10 * HZ)
 
 #define MAX_BUF PAGE_SIZE
+#define MAX_BUFFLEN    (32 * 512)
 
 /**
  * ioctl_probe  --  return host identification
@@ -138,6 +140,79 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
        return result;
 }
 
+
+static int ioctl_secu_prot_command(struct scsi_device *sdev, char *cmd,
+                                       int prot_in_out, void __user *arg,
+                                       int timeout, int retries)
+{
+       int result, dma_direction;
+       struct scsi_sense_hdr sshdr;
+       unsigned char *buf;
+       unsigned int bufflen;
+       Scsi_Ioctl_Command __user *s_ioc_arg;
+
+       SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
+
+       s_ioc_arg = (Scsi_Ioctl_Command *)kmalloc(sizeof(*s_ioc_arg), GFP_KERNEL);
+       if (!s_ioc_arg) {
+               printk(KERN_INFO "%s kmalloc faild\n", __func__);
+               return -EFAULT;
+       }
+
+
+       if (copy_from_user(s_ioc_arg, arg, sizeof(*s_ioc_arg))) {
+               printk(KERN_INFO "Argument copy faild\n");
+               result = -EFAULT;
+               goto err_pre_buf_alloc;
+       }
+       if (prot_in_out == SCSI_IOCTL_SECURITY_PROTOCOL_IN) {
+               dma_direction = DMA_FROM_DEVICE;
+               bufflen = s_ioc_arg->inlen;
+               buf = kzalloc(bufflen, GFP_KERNEL);
+       } else if (prot_in_out == SCSI_IOCTL_SECURITY_PROTOCOL_OUT) {
+               dma_direction = DMA_TO_DEVICE;
+               bufflen = s_ioc_arg->outlen;
+               buf = kzalloc(bufflen, GFP_KERNEL);
+               if (copy_from_user(buf, arg + sizeof(*s_ioc_arg), s_ioc_arg->outlen)) {
+                       printk(KERN_INFO "copy_from_user failed\n");
+                       result = -EFAULT;
+                       goto err_post_buf_alloc;
+               }
+       } else {
+               sdev_printk(KERN_INFO, sdev,
+                               "prot_in_out not set!! %d\n", prot_in_out);
+               result = -EFAULT;
+               goto err_pre_buf_alloc;
+       }
+
+       result = scsi_execute_req(sdev, cmd, dma_direction, buf, bufflen,
+                                 &sshdr, timeout, retries, NULL);
+
+       if (prot_in_out == SCSI_IOCTL_SECURITY_PROTOCOL_IN) {
+               if (copy_to_user(arg + sizeof(*s_ioc_arg), buf, s_ioc_arg->inlen)) {
+                       printk(KERN_INFO "copy_to_user failed\n");
+                       result = -EFAULT;
+                       goto err_post_buf_alloc;
+               }
+       }
+       SCSI_LOG_IOCTL(2, printk("Ioctl returned  0x%x\n", result));
+
+       if ((driver_byte(result) & DRIVER_SENSE) &&
+           (scsi_sense_valid(&sshdr))) {
+               sdev_printk(KERN_INFO, sdev,
+                           "ioctl_secu_prot_command return code = %x\n",
+                           result);
+               scsi_print_sense_hdr(sdev, NULL, &sshdr);
+       }
+
+err_post_buf_alloc:
+       kfree(buf);
+err_pre_buf_alloc:
+       kfree(s_ioc_arg);
+       SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
+       return result;
+}
+
 int scsi_set_medium_removal(struct scsi_device *sdev, char state)
 {
        char scsi_cmd[MAX_COMMAND_SIZE];
@@ -203,6 +278,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
        char scsi_cmd[MAX_COMMAND_SIZE];
        struct scsi_sense_hdr sense_hdr;
 
+       memset(scsi_cmd, 0x0, MAX_COMMAND_SIZE);
+
        /* Check for deprecated ioctls ... all the ioctls which don't
         * follow the new unique numbering scheme are deprecated */
        switch (cmd) {
@@ -261,7 +338,44 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
                scsi_cmd[4] = 0;
                return ioctl_internal_command(sdev, scsi_cmd,
                                     START_STOP_TIMEOUT, NORMAL_RETRIES);
-        case SCSI_IOCTL_GET_PCI:
+       case SCSI_IOCTL_SECURITY_PROTOCOL_IN:
+       case SCSI_IOCTL_SECURITY_PROTOCOL_OUT:
+               {
+                       unsigned short prot_spec;
+                       unsigned long t_len;
+                       int _cmd;
+                       struct scsi_ioctl_command *ic = (struct scsi_ioctl_command *) arg;
+
+                       _cmd = SCSI_UFS_REQUEST_SENSE;
+                       if(sdev->host->wlun_clr_uac)
+                               sdev->host->hostt->ioctl(sdev, _cmd, NULL);
+
+                       prot_spec = SECU_PROT_SPEC_CERT_DATA;
+                       if(cmd == SCSI_IOCTL_SECURITY_PROTOCOL_IN)
+                               t_len = ic->inlen;
+                       else
+                               t_len = ic->outlen;
+
+                       scsi_cmd[0] = (cmd == SCSI_IOCTL_SECURITY_PROTOCOL_IN) ?
+                               SECURITY_PROTOCOL_IN :
+                               SECURITY_PROTOCOL_OUT;
+                       scsi_cmd[1] = SECU_PROT_UFS;
+                       scsi_cmd[2] = ((unsigned char)(prot_spec >> 8)) & 0xff;
+                       scsi_cmd[3] = ((unsigned char)(prot_spec)) & 0xff;
+                       scsi_cmd[4] = 0;
+                       scsi_cmd[5] = 0;
+                       scsi_cmd[6] = ((unsigned char)(t_len >> 24)) & 0xff;
+                       scsi_cmd[7] = ((unsigned char)(t_len >> 16)) & 0xff;
+                       scsi_cmd[8] = ((unsigned char)(t_len >> 8)) & 0xff;
+                       scsi_cmd[9] = (unsigned char)t_len & 0xff;
+                       scsi_cmd[10] = 0;
+                       scsi_cmd[11] = 0;
+                       return ioctl_secu_prot_command(sdev, scsi_cmd,
+                                       cmd, arg,
+                                       START_STOP_TIMEOUT, NORMAL_RETRIES);
+               }
+
+       case SCSI_IOCTL_GET_PCI:
                 return scsi_ioctl_get_pci(sdev, arg);
        case SG_SCSI_RESET:
                return scsi_ioctl_reset(sdev, arg);
index 72db0f7d221a7fb4a3f98ed1a6d7d5e449fb1b92..8d352e7f1d981053ed5b0a7c60b8b2feacaaa2a5 100644 (file)
@@ -1494,6 +1494,8 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
        switch (cmd) {
                case SCSI_IOCTL_GET_IDLUN:
                case SCSI_IOCTL_GET_BUS_NUMBER:
+               case SCSI_IOCTL_SECURITY_PROTOCOL_IN:
+               case SCSI_IOCTL_SECURITY_PROTOCOL_OUT:
                        error = scsi_ioctl(sdp, cmd, p);
                        break;
                default:
index ba52714674a5265ddffb7fbd39c51274b8df1350..5c0a189c5d7af97939423d26b283db06dff45a5e 100644 (file)
@@ -2374,6 +2374,16 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
                return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID;
 }
 
+static inline unsigned int ufshcd_get_scsi_lun(struct scsi_cmnd *cmd)
+{
+       if (cmd->cmnd[0] == SECURITY_PROTOCOL_IN ||
+                       cmd->cmnd[0] == SECURITY_PROTOCOL_OUT)
+               return (SCSI_W_LUN_BASE |
+                       (UFS_UPIU_RPMB_WLUN & UFS_UPIU_MAX_UNIT_NUM_ID));
+       else
+               return cmd->device->lun;
+}
+
 /**
  * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID
  * @scsi_lun: UPIU W-LUN id
@@ -6346,7 +6356,6 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
 static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
 {
        int ret = 0;
-       struct scsi_device *sdev_rpmb;
        struct scsi_device *sdev_boot;
 
        hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
@@ -6366,13 +6375,13 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
        }
        scsi_device_put(sdev_boot);
 
-       sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
+       hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
                ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
-       if (IS_ERR(sdev_rpmb)) {
-               ret = PTR_ERR(sdev_rpmb);
+       if (IS_ERR(hba->sdev_rpmb)) {
+               ret = PTR_ERR(hba->sdev_rpmb);
                goto remove_sdev_boot;
        }
-       scsi_device_put(sdev_rpmb);
+       scsi_device_put(hba->sdev_rpmb);
        goto out;
 
 remove_sdev_boot:
@@ -6805,6 +6814,7 @@ retry:
                pm_runtime_put_sync(hba->dev);
        }
 
+       hba->host->wlun_clr_uac = true;
        if (!hba->is_init_prefetch)
                hba->is_init_prefetch = true;
 
index 9ee8315c6c013afb6fef759142792a5685afcce5..1e4431a979ff428b07070d2d5d907753bb4ee519 100644 (file)
@@ -66,6 +66,7 @@
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_eh.h>
+#include <scsi/scsi_ioctl.h>
 
 #include "ufs.h"
 #include "ufshci.h"
@@ -544,6 +545,7 @@ struct ufs_hba {
         * "UFS device" W-LU.
         */
        struct scsi_device *sdev_ufs_device;
+       struct scsi_device *sdev_rpmb;
 
        enum ufs_dev_pwr_mode curr_dev_pwr_mode;
        enum uic_link_state uic_link_state;
index bd5d91e119ca95f2bbf43110c10ec46816830ee8..4303079a457dd13b8545c2b812216dda44bb5c58 100644 (file)
@@ -940,6 +940,8 @@ COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER)
 COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND)
 COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST)
 COMPATIBLE_IOCTL(SCSI_IOCTL_GET_PCI)
+COMPATIBLE_IOCTL(SCSI_IOCTL_SECURITY_PROTOCOL_IN)
+COMPATIBLE_IOCTL(SCSI_IOCTL_SECURITY_PROTOCOL_OUT)
 #endif
 /* Big V (don't complain on serial console) */
 IGNORE_IOCTL(VT_OPENQRY)
index a8b7bf879cede4240d921a42f915a230163e5e4e..b94e887a7b2b6da3c0a27393d7faaf01fe2db1d0 100644 (file)
@@ -701,6 +701,7 @@ struct Scsi_Host {
        
 
        enum scsi_host_state shost_state;
+       bool wlun_clr_uac;
 
        /* ldm bits */
        struct device           shost_gendev, shost_dev;
index 5101e987c0eff42587200a5625a6ac097463481a..578817848e8502d3c0f52b692382637501024ae1 100644 (file)
@@ -8,6 +8,8 @@
 #define SCSI_IOCTL_SYNC 4                      /* Request synchronous parameters */
 #define SCSI_IOCTL_START_UNIT 5
 #define SCSI_IOCTL_STOP_UNIT 6
+#define SCSI_IOCTL_SECURITY_PROTOCOL_IN        7
+#define SCSI_IOCTL_SECURITY_PROTOCOL_OUT 8
 /* The door lock/unlock constants are compatible with Sun constants for
    the cdrom */
 #define SCSI_IOCTL_DOORLOCK 0x5380             /* lock the eject mechanism */
@@ -16,6 +18,8 @@
 #define        SCSI_REMOVAL_PREVENT    1
 #define        SCSI_REMOVAL_ALLOW      0
 
+#define SCSI_UFS_REQUEST_SENSE 0x6000          /* clear UAC in w-lun */
+
 #ifdef __KERNEL__
 
 struct scsi_device;
index 1c41dbcfcb35544f03e3557fbf765c81b2c770a6..68e7e3e18004c5b61520cebeeff38af55d75e0a6 100644 (file)
@@ -86,6 +86,8 @@
 #define VARIABLE_LENGTH_CMD   0x7f
 #define REPORT_LUNS           0xa0
 #define SECURITY_PROTOCOL_IN  0xa2
+#define SECU_PROT_UFS                  0xEC
+#define SECU_PROT_SPEC_CERT_DATA       0x0001
 #define MAINTENANCE_IN        0xa3
 #define MAINTENANCE_OUT       0xa4
 #define MOVE_MEDIUM           0xa5