On Thu, Mar 17, 2011 at 11:28:55AM -0700, John Calixto wrote:
> 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 <[email protected]>
> ---
> (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);
you shouldn't need this cast, 'void *' will go hapilly to any other type.
> + 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Ben Dooks, [email protected], http://www.fluff.org/ben/
Large Hadron Colada: A large Pina Colada that makes the universe disappear.
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html