mmc: card: Fix RPMB IOCTL to handle all cards
The eMMC 4.5 spec for RPMB accesses is not very clear on whether user parition accesses can be allowed in the middle of RPMB accesses. Due to this ambiguity, it turns out this is implementation defined and certain cards support it while others do not. In order to allow this feature to function across a wide variety of cards, this patch takes the pessimistic approach and ensures that any RPMB access is completed before user partition can be accessed. Change-Id: I77959f462c874771a0a854d9a2bc48df446eff56 Signed-off-by: Krishna Konda <kkonda@codeaurora.org> Signed-off-by: Oluwafemi Adeyemi <aadeyemi@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> [merez@codeaurora: fix conflicts due to changes in 3.14] Signed-off-by: Maya Erez <merez@codeaurora.org> [subhashj@codeaurora.org: fixed merge conflicts and fixed compilation errors] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
18941a3b87
commit
26a145722e
1 changed files with 173 additions and 13 deletions
|
@ -579,15 +579,10 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|||
struct mmc_request mrq = {NULL};
|
||||
struct scatterlist sg;
|
||||
int err;
|
||||
int is_rpmb = false;
|
||||
u32 status = 0;
|
||||
|
||||
if (!card || !md || !idata)
|
||||
return -EINVAL;
|
||||
|
||||
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
|
||||
is_rpmb = true;
|
||||
|
||||
cmd.opcode = idata->ic.opcode;
|
||||
cmd.arg = idata->ic.arg;
|
||||
cmd.flags = idata->ic.flags;
|
||||
|
@ -640,13 +635,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (is_rpmb) {
|
||||
err = mmc_set_blockcount(card, data.blocks,
|
||||
idata->ic.write_flag & (1 << 31));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
|
||||
(cmd.opcode == MMC_SWITCH)) {
|
||||
err = ioctl_do_sanitize(card);
|
||||
|
@ -680,7 +668,164 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|||
|
||||
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
|
||||
|
||||
if (is_rpmb) {
|
||||
return err;
|
||||
}
|
||||
|
||||
struct mmc_blk_ioc_rpmb_data {
|
||||
struct mmc_blk_ioc_data *data[MMC_IOC_MAX_RPMB_CMD];
|
||||
};
|
||||
|
||||
static struct mmc_blk_ioc_rpmb_data *mmc_blk_ioctl_rpmb_copy_from_user(
|
||||
struct mmc_ioc_rpmb __user *user)
|
||||
{
|
||||
struct mmc_blk_ioc_rpmb_data *idata;
|
||||
int err, i;
|
||||
|
||||
idata = kzalloc(sizeof(*idata), GFP_KERNEL);
|
||||
if (!idata) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
|
||||
idata->data[i] = mmc_blk_ioctl_copy_from_user(&(user->cmds[i]));
|
||||
if (IS_ERR(idata->data[i])) {
|
||||
err = PTR_ERR(idata->data[i]);
|
||||
goto copy_err;
|
||||
}
|
||||
}
|
||||
|
||||
return idata;
|
||||
|
||||
copy_err:
|
||||
while (--i >= 0) {
|
||||
kfree(idata->data[i]->buf);
|
||||
kfree(idata->data[i]);
|
||||
}
|
||||
kfree(idata);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev,
|
||||
struct mmc_ioc_rpmb __user *ic_ptr)
|
||||
{
|
||||
struct mmc_blk_ioc_rpmb_data *idata;
|
||||
struct mmc_blk_data *md;
|
||||
struct mmc_card *card;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct scatterlist sg;
|
||||
int err = 0, i = 0;
|
||||
u32 status = 0;
|
||||
|
||||
/* The caller must have CAP_SYS_RAWIO */
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
return -EPERM;
|
||||
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
/* make sure this is a rpmb partition */
|
||||
if ((!md) || (!(md->area_type & MMC_BLK_DATA_AREA_RPMB))) {
|
||||
err = -EINVAL;
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
idata = mmc_blk_ioctl_rpmb_copy_from_user(ic_ptr);
|
||||
if (IS_ERR(idata)) {
|
||||
err = PTR_ERR(idata);
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
card = md->queue.card;
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto idata_free;
|
||||
}
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
err = mmc_blk_part_switch(card, md);
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
|
||||
for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
|
||||
struct mmc_blk_ioc_data *curr_data;
|
||||
struct mmc_ioc_cmd *curr_cmd;
|
||||
|
||||
curr_data = idata->data[i];
|
||||
curr_cmd = &curr_data->ic;
|
||||
if (!curr_cmd->opcode)
|
||||
break;
|
||||
|
||||
cmd.opcode = curr_cmd->opcode;
|
||||
cmd.arg = curr_cmd->arg;
|
||||
cmd.flags = curr_cmd->flags;
|
||||
|
||||
if (curr_data->buf_bytes) {
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
data.blksz = curr_cmd->blksz;
|
||||
data.blocks = curr_cmd->blocks;
|
||||
|
||||
sg_init_one(data.sg, curr_data->buf,
|
||||
curr_data->buf_bytes);
|
||||
|
||||
if (curr_cmd->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 (curr_cmd->data_timeout_ns)
|
||||
data.timeout_ns = curr_cmd->data_timeout_ns;
|
||||
|
||||
mrq.data = &data;
|
||||
}
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
|
||||
err = mmc_set_blockcount(card, data.blocks,
|
||||
curr_cmd->write_flag & (1 << 31));
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
if (cmd.error) {
|
||||
dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
|
||||
__func__, cmd.error);
|
||||
err = cmd.error;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
if (data.error) {
|
||||
dev_err(mmc_dev(card->host), "%s: data error %d\n",
|
||||
__func__, data.error);
|
||||
err = data.error;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
if (copy_to_user(&(ic_ptr->cmds[i].response), cmd.resp,
|
||||
sizeof(cmd.resp))) {
|
||||
err = -EFAULT;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
if (!curr_cmd->write_flag) {
|
||||
if (copy_to_user((void __user *)(unsigned long)
|
||||
curr_cmd->data_ptr,
|
||||
curr_data->buf,
|
||||
curr_data->buf_bytes)) {
|
||||
err = -EFAULT;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure RPMB command has completed by polling CMD13
|
||||
* "Send Status".
|
||||
|
@ -692,6 +837,18 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|||
__func__, status, err);
|
||||
}
|
||||
|
||||
cmd_rel_host:
|
||||
mmc_put_card(card);
|
||||
|
||||
idata_free:
|
||||
for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
|
||||
kfree(idata->data[i]->buf);
|
||||
kfree(idata->data[i]);
|
||||
}
|
||||
kfree(idata);
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -812,6 +969,9 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
|||
case MMC_IOC_CMD:
|
||||
return mmc_blk_ioctl_cmd(bdev,
|
||||
(struct mmc_ioc_cmd __user *)arg);
|
||||
case MMC_IOC_RPMB_CMD:
|
||||
return mmc_blk_ioctl_rpmb_cmd(bdev,
|
||||
(struct mmc_ioc_rpmb __user *)arg);
|
||||
case MMC_IOC_MULTI_CMD:
|
||||
return mmc_blk_ioctl_multi_cmd(bdev,
|
||||
(struct mmc_ioc_multi_cmd __user *)arg);
|
||||
|
|
Loading…
Add table
Reference in a new issue