[PATCH resend] mmc: Added ioctl to let userspace apps send ACMDs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Part 3 of the SD Specification (SD Card Association; www.sdcard.org) describes
how to use the security function of an SD card using application specific
commands in conjunction with CPRM algorithms and keys licensed from the 4C
Entity (www.4centity.com).  This allows userspace applications to access this
security feature.

Tested on TI PCIxx12 (SDHCI), Sigma Designs SMP8652 SoC, TI OMAP3621 SoC, TI
OMAP3630 SoC, Samsung S5PC110 SoC, Qualcomm MSM7200A SoC.

Signed-off-by: John Calixto <john.calixto@xxxxxxxxxxxxxx>
---
(I'm resending because I forgot to cc the maintainer - sorry!)

 drivers/mmc/card/block.c  |  149 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/sd_ops.c |    3 +-
 include/linux/mmc/core.h  |    1 +
 include/linux/mmc/sd.h    |   18 ++++++
 4 files changed, 170 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index bfc8a8a..62f742d 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -39,6 +39,7 @@

 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/delay.h>

 #include "queue.h"

@@ -158,11 +159,159 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        return 0;
 }

+static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int ioc_cmd, unsigned long ioc_arg)
+{
+       struct sd_ioc_cmd sdic = {0};
+       struct mmc_blk_data *md = NULL;
+       struct mmc_host *host = NULL;
+       struct mmc_card *card = NULL;
+       struct mmc_command cmd = {0};
+       struct mmc_data data = {0};
+       int err = 0;
+       struct mmc_request mrq = {0};
+       struct scatterlist sg = {0};
+       unsigned char *blocks = NULL;
+       size_t data_bytes = 0;
+#ifdef CONFIG_MMC_DEBUG
+       char dbgbuf[64] = {0};
+#endif
+
+       /*
+        * This is primarily used for application specific commands (ACMD), so
+        * the current ioc_cmd validation is trivial.
+        */
+       if (ioc_cmd != SD_IOC_ACMD)
+               return -EINVAL;
+
+       if (copy_from_user(&sdic, (void __user *) ioc_arg, sizeof(struct sd_ioc_cmd))) {
+               printk(KERN_ERR "%s: error reading ioctl arg from userspace\n", __func__);
+               return -EFAULT;
+       }
+
+       if (sdic.struct_version != SD_IOC_CMD_STRUCT_VERSION)
+               return -EINVAL;
+
+       /* Find the mmc structures based on the bdev. */
+       md = mmc_blk_get(bdev->bd_disk);
+       if (!md)
+               return -EINVAL;
+
+       card = md->queue.card;
+#ifdef CONFIG_MMC_DEBUG
+       printk(KERN_DEBUG "%s: card = %p\n", __func__, card);
+#endif
+       if (IS_ERR(card))
+               return PTR_ERR(card);
+
+#ifdef CONFIG_MMC_DEBUG
+       printk(KERN_DEBUG "%s: host = %p\n", __func__, card->host);
+#endif
+       host = card->host;
+       BUG_ON(!host);
+
+       mmc_claim_host(host);
+
+       err = mmc_app_cmd(host, card);
+       if (err) {
+               dev_err(mmc_dev(host), "%s: CMD%d error\n", __func__, MMC_APP_CMD);
+               goto ioctl_done;
+       }
+
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+
+       cmd.opcode = sdic.opcode;
+       cmd.arg = sdic.arg;
+       cmd.flags = sdic.flags;
+
+       data.sg = &sg;
+       data.sg_len = 1;
+       data.blksz = sdic.blksz;
+       data.blocks = sdic.blocks;
+
+       data_bytes = data.blksz * data.blocks;
+       blocks = (unsigned char *) kzalloc(data_bytes, GFP_KERNEL);
+       if (!blocks) {
+               err = -ENOMEM;
+               goto ioctl_done;
+       }
+       sg_init_one(data.sg, blocks, data_bytes);
+
+
+       if (copy_from_user(blocks, sdic.data, data_bytes)) {
+               dev_err(mmc_dev(host), "%s: error reading userspace buffer\n", __func__);
+               err = -EFAULT;
+               goto ioctl_done;
+       }
+       if (sdic.write_flag) {
+               data.flags = MMC_DATA_WRITE;
+       } else {
+               data.flags = MMC_DATA_READ;
+       }
+
+       /* data.flags must already be set before doing this. */
+       mmc_set_data_timeout(&data, card);
+       /* Allow overriding the timeout_ns for empirical tuning. */
+       if (sdic.force_timeout_ns)
+               data.timeout_ns = sdic.force_timeout_ns;
+
+#ifdef CONFIG_MMC_DEBUG
+       hex_dump_to_buffer(blocks, data_bytes, 16, 1, dbgbuf, sizeof(dbgbuf), 0);
+       dev_dbg(mmc_dev(host), "%s: first bytes of pre data\n%s\n", __func__, dbgbuf);
+#endif
+
+       mmc_wait_for_req(host, &mrq);
+
+       if (cmd.error) {
+               dev_err(mmc_dev(host), "%s: cmd error %d\n", __func__, cmd.error);
+               err = cmd.error;
+               goto ioctl_done;
+       }
+       if (data.error) {
+               dev_err(mmc_dev(host), "%s: data error %d\n", __func__, data.error);
+               err = data.error;
+               goto ioctl_done;
+       }
+
+       /*
+        * According to the SD specs, some commands require a delay after
+        * issuing the command.
+        */
+       if (sdic.postsleep_us)
+               udelay(sdic.postsleep_us);
+
+       if (copy_to_user(&(((struct sd_ioc_cmd *) ioc_arg)->response), cmd.resp, sizeof(u32) * 4)) {
+               dev_err(mmc_dev(host), "%s: error copying response\n", __func__);
+               err = -EFAULT;
+               goto ioctl_done;
+       }
+
+#ifdef CONFIG_MMC_DEBUG
+       hex_dump_to_buffer(blocks, data_bytes, 16, 1, dbgbuf, sizeof(dbgbuf), 0);
+       dev_dbg(mmc_dev(host), "%s: first bytes of post data\n%s\n", __func__, dbgbuf);
+#endif
+       if (!sdic.write_flag) {
+               if (copy_to_user(sdic.data, blocks, data_bytes)) {
+                       dev_err(mmc_dev(host), "%s: error copying data\n", __func__);
+                       err = -EFAULT;
+                       goto ioctl_done;
+               }
+       }
+
+ioctl_done:
+       if (blocks)
+               kfree(blocks);
+       mmc_release_host(host);
+       mmc_blk_put(md);
+       return err;
+}
+
 static const struct block_device_operations mmc_bdops = {
        .open                   = mmc_blk_open,
        .release                = mmc_blk_release,
        .getgeo                 = mmc_blk_getgeo,
        .owner                  = THIS_MODULE,
+       .ioctl                  = mmc_blk_ioctl,
 };

 struct mmc_blk_request {
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 797cdb5..0453dcd 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -20,7 +20,7 @@
 #include "core.h"
 #include "sd_ops.h"

-static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
+int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
 {
        int err;
        struct mmc_command cmd;
@@ -48,6 +48,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)

        return 0;
 }
+EXPORT_SYMBOL(mmc_app_cmd);

 /**
  *     mmc_wait_for_app_cmd - start an application command and wait for
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 64e013f..1adda405 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -133,6 +133,7 @@ struct mmc_card;

 extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
 extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
        struct mmc_command *, int);

diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 3fd85e0..a031cba 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -12,6 +12,8 @@
 #ifndef MMC_SD_H
 #define MMC_SD_H

+#include <linux/ioctl.h>
+
 /* SD commands                           type  argument     response */
   /* class 0 */
 /* This is basically the same command as for MMC with some quirks. */
@@ -84,5 +86,21 @@
 #define SD_SWITCH_ACCESS_DEF   0
 #define SD_SWITCH_ACCESS_HS    1

+struct sd_ioc_cmd {
+    unsigned int struct_version;
+#define SD_IOC_CMD_STRUCT_VERSION 0
+    int write_flag;  /* implies direction of data.  true = write, false = read */
+    unsigned int opcode;
+    unsigned int arg;
+    unsigned int flags;
+    unsigned int postsleep_us;  /* apply usecond delay *after* issuing command */
+    unsigned int force_timeout_ns;  /* force timeout to be force_timeout_ns ns */
+    unsigned int response[4];  /* CMD response */
+    unsigned int blksz;
+    unsigned int blocks;
+    unsigned char *data;  /* DAT buffer */
+};
+#define SD_IOC_ACMD _IOWR(MMC_BLOCK_MAJOR, 0, struct sd_ioc_cmd *)
+
 #endif

--
1.7.4.1

This message contains information which is confidential and privileged.  Unless you are the addressee (or authorized to receive for the addressee), you may not use, copy or disclose to anyone the message or any information contained in the message. If you have received the message in error, please advise the sender by reply e-mail and delete the message.
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux