Message ID | 1407136559-22334-1-git-send-email-yunpeng.gao@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 4 August 2014 09:15, Yunpeng Gao <yunpeng.gao@intel.com> wrote: > From: Chuanxiao Dong <chuanxiao.dong@intel.com> > > In current kernel mmc driver stack, seems it > expects all the RPMB operations handled by > user space library or applications. > In kernel space, the mmc driver only treat > the RPMB ioctl as common eMMC CMD read/write. > > This is OK. But in some use cases, maybe it is > still needed to implement most of the RPMB > operations inside kernel space. > For example, if security driver need to access > RPMB partition and requires the eMMC driver > providing an interface API to do so. Isn't your security driver capable of interacting with user space? I think that would be the proper solution. Kind regards Ulf Hansson > > This RFC patch implements eMMC RPMB partition data > read/write and write counter reading. > But not include Key programming since Key it is > one time operation and should not be done by mmc driver. > > Signed-off-by: Yunpeng Gao <yunpeng.gao@intel.com> > Signed-off-by: Chuanxiao Dong <chuanxiao.dong@intel.com> > --- > drivers/mmc/card/block.c | 156 +++++++++++++++- > drivers/mmc/card/queue.c | 2 +- > drivers/mmc/card/queue.h | 1 + > drivers/mmc/core/mmc.c | 17 ++ > drivers/mmc/core/mmc_ops.c | 396 ++++++++++++++++++++++++++++++++++++++++ > include/linux/mmc/card.h | 5 + > include/linux/mmc/core.h | 34 ++++ > include/uapi/linux/mmc/ioctl.h | 13 ++ > 8 files changed, 619 insertions(+), 5 deletions(-) > > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c > index 452782b..e185c9d 100644 > --- a/drivers/mmc/card/block.c > +++ b/drivers/mmc/card/block.c > @@ -99,6 +99,7 @@ struct mmc_blk_data { > #define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */ > #define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */ > #define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */ > +#define MMC_BLK_SUSPENDED (1 << 3) /* MMC block device suspended */ > > unsigned int usage; > unsigned int read_only; > @@ -109,6 +110,8 @@ struct mmc_blk_data { > #define MMC_BLK_WRITE BIT(1) > #define MMC_BLK_DISCARD BIT(2) > #define MMC_BLK_SECDISCARD BIT(3) > +#define MMC_BLK_RPMB BIT(4) > +#define MMC_BLK_USER BIT(5) > > /* > * Only set in main mmc_blk_data associated > @@ -149,6 +152,9 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) > packed->blocks = 0; > } > > +static int mmc_rpmb_req_process(struct mmc_blk_data *, > + struct mmc_ioc_rpmb_req *); > + > static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) > { > struct mmc_blk_data *md; > @@ -1025,6 +1031,118 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) > md->reset_done &= ~type; > } > > +static int mmc_rpmb_req_process(struct mmc_blk_data *md, > + struct mmc_ioc_rpmb_req *req) > +{ > + struct mmc_core_rpmb_req rpmb_req; > + struct mmc_card *card = NULL; > + int ret; > + > + if (!md || !req) > + return -EINVAL; > + > + if (!(md->flags & MMC_BLK_CMD23) || > + (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB)) > + return -EOPNOTSUPP; > + > + card = md->queue.card; > + if (!card || !mmc_card_mmc(card) || !card->ext_csd.rpmb_size) > + return -ENODEV; > + > + memset(&rpmb_req, 0, sizeof(struct mmc_core_rpmb_req)); > + rpmb_req.req = req; > + /* check request */ > + ret = mmc_rpmb_pre_frame(&rpmb_req, card); > + if (ret) { > + pr_err("%s: prepare frame failed\n", mmc_hostname(card->host)); > + return ret; > + } > + > + mmc_claim_host(card->host); > + > + if (md->flags & MMC_BLK_SUSPENDED) { > + pr_warn("%s: MMC block device is already suspended\n", > + mmc_hostname(card->host)); > + ret = -EPERM; > + goto out; > + } > + > + /* wait for background operation finished */ > + mmc_stop_bkops(card); > + > + /* > + * before start, let's change to RPMB partition first > + */ > + ret = mmc_blk_part_switch(card, md); > + if (ret) { > + pr_err("%s: Invalid RPMB partition switch (%d)!\n", > + mmc_hostname(card->host), ret); > + /* > + * In case partition is not in user data area, make > + * a force partition switch. > + * we need reset eMMC card at here > + */ > + ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB); > + if (!ret) > + mmc_blk_reset_success(md, MMC_BLK_RPMB); > + else > + pr_err("%s: eMMC card reset failed (%d)\n", > + mmc_hostname(card->host), ret); > + goto out; > + } > + > + ret = mmc_rpmb_partition_ops(&rpmb_req, card); > + if (ret) > + pr_err("%s: failed (%d) to handle RPMB request type (%d)!\n", > + mmc_hostname(card->host), ret, req->type); > +out: > + mmc_release_host(card->host); > + mmc_rpmb_post_frame(&rpmb_req); > + return ret; > +} > + > +int mmc_access_rpmb(struct mmc_queue *mq) > +{ > + struct mmc_blk_data *md = mq->data; > + /* > + * If this is a RPMB partition access, return ture > + */ > + if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) > + return true; > + > + return false; > +} > +EXPORT_SYMBOL_GPL(mmc_access_rpmb); > + > +int mmc_rpmb_req_handle(struct device *emmc, struct mmc_ioc_rpmb_req *req) > +{ > + int ret = 0; > + struct gendisk *disk = NULL; > + struct mmc_blk_data *md = NULL; > + > + if (!emmc || !req) > + return -EINVAL; > + > + disk = dev_to_disk(emmc); > + if (!disk) { > + pr_err("%s: NO eMMC disk found. Try it later\n", > + __func__); > + return -ENODEV; > + } > + > + md = mmc_blk_get(disk); > + if (!md) { > + pr_err("%s: NO eMMC block data. Try it later\n", > + __func__); > + return -ENODEV; > + } > + ret = mmc_rpmb_req_process(md, req); > + mmc_blk_put(md); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_req_handle); > + > static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) > { > struct mmc_blk_data *md = mq->data; > @@ -2016,11 +2134,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) > > ret = mmc_blk_part_switch(card, md); > if (ret) { > - if (req) { > - blk_end_request_all(req, -EIO); > + pr_err("%s: switch part failed. Try to reset eMMC\n", > + mmc_hostname(card->host)); > + if (mmc_blk_reset(md, card->host, MMC_BLK_USER)) { > + if (req) > + blk_end_request_all(req, -EIO); > + ret = 0; > + goto out; > } > - ret = 0; > - goto out; > + pr_info("%s: Reset eMMC success\n", mmc_hostname(card->host)); > + mmc_blk_reset_success(md, MMC_BLK_USER); > } > > mq->flags &= ~MMC_QUEUE_NEW_REQUEST; > @@ -2501,6 +2624,19 @@ static int _mmc_blk_suspend(struct mmc_card *card) > mmc_queue_suspend(&md->queue); > list_for_each_entry(part_md, &md->part, part) { > mmc_queue_suspend(&part_md->queue); > + if (part_md->part_type == > + EXT_CSD_PART_CONFIG_ACC_RPMB) { > + /* > + * RPMB partition is accessed by API directly. > + * Driver need to set a flag when suspending > + * MMC block device to notify API that the > + * accessing of RPMB partition needs to be > + * stopped > + */ > + mmc_claim_host(card->host); > + part_md->flags |= MMC_BLK_SUSPENDED; > + mmc_release_host(card->host); > + } > } > } > return 0; > @@ -2531,6 +2667,18 @@ static int mmc_blk_resume(struct mmc_card *card) > mmc_queue_resume(&md->queue); > list_for_each_entry(part_md, &md->part, part) { > mmc_queue_resume(&part_md->queue); > + if (part_md->part_type == > + EXT_CSD_PART_CONFIG_ACC_RPMB) { > + /* > + * RPMB partition is accessed by API directly. > + * Driver need to clear MMC_BLK_SUSPENDED flag > + * to make sure the next RPMB partition access > + * request won't be blocked > + */ > + mmc_claim_host(card->host); > + part_md->flags &= ~MMC_BLK_SUSPENDED; > + mmc_release_host(card->host); > + } > } > } > return 0; > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c > index 3e049c1..6ceede0 100644 > --- a/drivers/mmc/card/queue.c > +++ b/drivers/mmc/card/queue.c > @@ -38,7 +38,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) > return BLKPREP_KILL; > } > > - if (mq && mmc_card_removed(mq->card)) > + if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq))) > return BLKPREP_KILL; > > req->cmd_flags |= REQ_DONTPREP; > diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h > index 5752d50..3bbd4e6 100644 > --- a/drivers/mmc/card/queue.h > +++ b/drivers/mmc/card/queue.h > @@ -73,4 +73,5 @@ extern void mmc_queue_bounce_post(struct mmc_queue_req *); > extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *); > extern void mmc_packed_clean(struct mmc_queue *); > > +extern int mmc_access_rpmb(struct mmc_queue *); > #endif > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c > index 793c6f7..59766ec 100644 > --- a/drivers/mmc/core/mmc.c > +++ b/drivers/mmc/core/mmc.c > @@ -553,6 +553,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) > * RPMB regions are defined in multiples of 128K. > */ > card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; > + card->ext_csd.rpmb_size = 128 * > + card->ext_csd.raw_rpmb_size_mult; > + card->ext_csd.rpmb_size <<= 2; /* Unit: half sector */ > if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { > mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, > EXT_CSD_PART_CONFIG_ACC_RPMB, > @@ -604,6 +607,18 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) > card->ext_csd.data_sector_size = 512; > } > > + /* > + * If use legacy relaible write, then the blk counts must not > + * big than the relaible write sectors > + */ > + if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { > + if (card->ext_csd.rel_sectors < RPMB_AVALIABLE_SECTORS) > + card->rpmb_max_req = card->ext_csd.rel_sectors; > + else > + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; > + } else > + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; > + > out: > return err; > } > @@ -710,6 +725,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", > MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); > MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); > MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); > +MMC_DEV_ATTR(rpmb_size, "%d\n", card->ext_csd.rpmb_size); > > static struct attribute *mmc_std_attrs[] = { > &dev_attr_cid.attr, > @@ -728,6 +744,7 @@ static struct attribute *mmc_std_attrs[] = { > &dev_attr_enhanced_area_size.attr, > &dev_attr_raw_rpmb_size_mult.attr, > &dev_attr_rel_sectors.attr, > + &dev_attr_rpmb_size.attr, > NULL, > }; > ATTRIBUTE_GROUPS(mmc_std); > diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c > index f51b5ba..8d9f921 100644 > --- a/drivers/mmc/core/mmc_ops.c > +++ b/drivers/mmc/core/mmc_ops.c > @@ -655,3 +655,399 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) > > return 0; > } > + > +static int mmc_rpmb_send_command(struct mmc_card *card, u8 *buf, __u16 blks, > + __u16 type, u8 req_type) > +{ > + struct mmc_request mrq = {NULL}; > + struct mmc_command cmd = {0}; > + struct mmc_command sbc = {0}; > + struct mmc_data data = {0}; > + struct scatterlist sg; > + u8 *transfer_buf = NULL; > + > + mrq.sbc = &sbc; > + mrq.cmd = &cmd; > + mrq.data = &data; > + mrq.stop = NULL; > + transfer_buf = kzalloc(512 * blks, GFP_KERNEL); > + if (!transfer_buf) > + return -ENOMEM; > + > + /* > + * set CMD23 > + */ > + sbc.opcode = MMC_SET_BLOCK_COUNT; > + sbc.arg = blks; > + if ((req_type == RPMB_REQ) && type == RPMB_WRITE_DATA) > + sbc.arg |= 1 << 31; > + sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; > + > + /* > + * set CMD25/18 > + */ > + sg_init_one(&sg, transfer_buf, 512 * blks); > + if (req_type == RPMB_REQ) { > + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; > + sg_copy_from_buffer(&sg, 1, buf, 512 * blks); > + data.flags |= MMC_DATA_WRITE; > + } else { > + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; > + data.flags |= MMC_DATA_READ; > + } > + cmd.arg = 0; > + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; > + data.blksz = 512; > + data.blocks = blks; > + data.sg = &sg; > + data.sg_len = 1; > + > + mmc_set_data_timeout(&data, card); > + > + mmc_wait_for_req(card->host, &mrq); > + > + if (req_type != RPMB_REQ) > + sg_copy_to_buffer(&sg, 1, buf, 512 * blks); > + > + kfree(transfer_buf); > + > + if (cmd.error) > + return cmd.error; > + if (data.error) > + return data.error; > + return 0; > +} > + > +void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *rpmb_req) > +{ > + int i; > + struct mmc_ioc_rpmb_req *p_req; > + __u8 *buf_frame; > + > + if (!rpmb_req || !rpmb_req->ready) > + return; > + > + p_req = rpmb_req->req; > + buf_frame = rpmb_req->frame; > + > + if (!p_req || !buf_frame) > + return; > + /* > + * Regarding to the check rules, here is the post > + * rules > + * All will return result. > + * GET_WRITE_COUNTER: > + * must: write counter, nonce > + * optional: MAC > + * WRITE_DATA: > + * must: MAC, write counter > + * READ_DATA: > + * must: nonce, data > + * optional: MAC > + * PROGRAM_KEY: > + * must: Nothing > + * > + * Except READ_DATA, all of these operations only need to parse > + * one frame. READ_DATA needs blks frames to get DATA > + */ > + > + memcpy(p_req->result, buf_frame + RPMB_RES_BEG, 2); > + *p_req->result = be16_to_cpup(p_req->result); > + > + if (p_req->type == RPMB_PROGRAM_KEY) > + goto out; > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_WRITE_DATA) { > + memcpy(p_req->wc, buf_frame + RPMB_WCOUNTER_BEG, 4); > + *p_req->wc = be32_to_cpup(p_req->wc); > + } > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_READ_DATA) { > + /* nonce copy */ > + memcpy(p_req->nonce, buf_frame + RPMB_NONCE_BEG, 16); > + } > + /* > + * Take MAC within the last package > + */ > + if (p_req->type == RPMB_READ_DATA) { > + __u8 *data = p_req->data; > + for (i = 0; i < p_req->blk_cnt; i++) { > + memcpy(data, buf_frame + i * 512 + RPMB_DATA_BEG, 256); > + data += 256; > + } > + /* > + * MAC stored in the last package > + */ > + if (p_req->mac) { > + i--; > + memcpy(p_req->mac, buf_frame + i * 512 + RPMB_MAC_BEG, > + 32); > + } > + } else if (p_req->mac) > + memcpy(p_req->mac, buf_frame + RPMB_MAC_BEG, 32); > +out: > + kfree(buf_frame); > + rpmb_req->frame = NULL; > + return; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_post_frame); > + > +static int mmc_rpmb_request_check(struct mmc_card *card, > + struct mmc_ioc_rpmb_req *p_req) > +{ > + /* > + * Some paramter is a must for the operation. Different > + * operation expect different paramters. Below code is > + * used for checking this. > + * > + * All operations will need result. > + * GET_WRITE_COUNTER: > + * must: write counter, nonce > + * optional: MAC > + * WRITE_DATA: > + * must: MAC, data, write counter > + * READ_DATA: > + * must: nonce, data > + * optional: MAC > + * PROGRAM_KEY: > + * must: MAC > + * > + * So here, we only check the 'must' paramters > + */ > + if (!p_req->result) { > + pr_err("%s: Type %d has NULL pointer for result\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER) { > + if (!p_req->nonce || !p_req->wc) { > + pr_err("%s: Type %d has NULL pointer for nonce/wc\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + /* > + * used to allocate frame > + */ > + p_req->blk_cnt = 1; > + } else if (p_req->type == RPMB_WRITE_DATA || > + p_req->type == RPMB_READ_DATA) { > + if ((__u32)(p_req->addr + p_req->blk_cnt) > > + card->ext_csd.rpmb_size) { > + pr_err("%s Type %d: beyond the RPMB partition rang addr %d, blk_cnt %d, rpmb_size %d\n", > + mmc_hostname(card->host), > + p_req->type, > + p_req->addr, > + p_req->blk_cnt, > + card->ext_csd.rpmb_size); > + /* > + * Not return error here since we want device to handle > + * such errors > + */ > + } > + if (p_req->blk_cnt == 0) { > + pr_err("%s: Type %d has zero block count\n", > + mmc_hostname(card->host), > + p_req->blk_cnt); > + return -EINVAL; > + } else if (p_req->blk_cnt > card->rpmb_max_req) { > + pr_err("%s: Type %d has invalid block count, cannot large than %d\n", > + mmc_hostname(card->host), > + p_req->blk_cnt, > + card->rpmb_max_req); > + return -EINVAL; > + } > + if (!p_req->data) { > + pr_err("%s: Type %d has NULL pointer for data\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + if (p_req->type == RPMB_WRITE_DATA) { > + if (!p_req->wc || !p_req->mac) { > + pr_err("%s: Type %d has NULL pointer for write counter/MAC\n", > + mmc_hostname(card->host), > + p_req->type); > + return -EINVAL; > + } > + } else { > + if (!p_req->nonce) { > + pr_err("%s: Type %d has NULL pointer for nonce\n", > + mmc_hostname(card->host), > + p_req->type); > + return -EINVAL; > + } > + } > + } else > + return -EOPNOTSUPP; > + > + return 0; > +} > + > +/* > + * prepare the request of RPMB frame > + * RPMB frame is MSB first > + * convert needed bytes > + * return how many frames will be prepared > + */ > +int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *rpmb_req, > + struct mmc_card *card) > +{ > + int i, ret; > + struct mmc_ioc_rpmb_req *p_req; > + __u8 *buf_frame; > + __u16 blk_cnt, addr, type; > + __u32 w_counter; > + > + if (!rpmb_req || !card) > + return -EINVAL; > + > + p_req = rpmb_req->req; > + if (!p_req) { > + pr_err("%s: mmc_ioc_rpmb_req is NULL. Wrong parameter\n", > + mmc_hostname(card->host)); > + return -EINVAL; > + } > + > + /* > + * make sure these two items are clear > + */ > + rpmb_req->ready = 0; > + rpmb_req->frame = NULL; > + > + ret = mmc_rpmb_request_check(card, p_req); > + if (ret) > + return ret; > + > + buf_frame = kzalloc(512 * p_req->blk_cnt, GFP_KERNEL); > + if (!buf_frame) { > + pr_err("%s: cannot allocate frame for type %d\n", > + mmc_hostname(card->host), p_req->type); > + return -ENOMEM; > + } > + > + type = cpu_to_be16p(&p_req->type); > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_READ_DATA) { > + /* > + * One package prepared > + * This request needs Nonce and type > + * If is data read, then also need addr > + */ > + memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); > + if (p_req->type == RPMB_READ_DATA) { > + addr = cpu_to_be16p(&p_req->addr); > + memcpy(buf_frame + RPMB_ADDR_BEG, &addr, 2); > + } > + /* convert Nonce code */ > + memcpy(buf_frame + RPMB_NONCE_BEG, p_req->nonce, 16); > + } else if (p_req->type == RPMB_WRITE_DATA) { > + __u8 *data = p_req->data; > + /* > + * multiple package prepared > + * This request nees blk_cnt, addr, write_counter, > + * data and mac > + */ > + blk_cnt = cpu_to_be16p(&p_req->blk_cnt); > + addr = cpu_to_be16p(&p_req->addr); > + w_counter = cpu_to_be32p(p_req->wc); > + for (i = 0; i < p_req->blk_cnt; i++) { > + memcpy(buf_frame + i * 512 + RPMB_TYPE_BEG, > + &type, 2); > + memcpy(buf_frame + i * 512 + RPMB_BLKS_BEG, > + &blk_cnt, 2); > + memcpy(buf_frame + i * 512 + RPMB_ADDR_BEG, > + &addr, 2); > + memcpy(buf_frame + i * 512 + RPMB_WCOUNTER_BEG, > + &w_counter, 4); > + memcpy(buf_frame + i * 512 + RPMB_DATA_BEG, > + data, 256); > + data += 256; > + } > + /* convert MAC code */ > + memcpy(buf_frame + 512 * (i - 1) + RPMB_MAC_BEG, > + p_req->mac, 32); > + } else { > + pr_err("%s: We shouldn't be here\n", mmc_hostname(card->host)); > + kfree(buf_frame); > + return -EINVAL; > + } > + rpmb_req->ready = 1; > + rpmb_req->frame = buf_frame; > + return 0; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_pre_frame); > + > +int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *rpmb_req, > + struct mmc_card *card) > +{ > + int err = 0; > + struct mmc_ioc_rpmb_req *p_req; > + __u16 type, blks; > + __u8 *buf_frame; > + > + if (!rpmb_req || !card) > + return -EINVAL; > + > + p_req = rpmb_req->req; > + buf_frame = rpmb_req->frame; > + > + if (!p_req || !rpmb_req->ready || !buf_frame) { > + pr_err("%s: mmc_ioc_rpmb_req is not prepared\n", > + mmc_hostname(card->host)); > + return -EINVAL; > + } > + > + type = p_req->type; > + blks = p_req->blk_cnt; > + > + /* > + * STEP 1: send request to RPMB partition > + */ > + if (type == RPMB_WRITE_DATA) > + err = mmc_rpmb_send_command(card, buf_frame, blks, > + type, RPMB_REQ); > + else > + err = mmc_rpmb_send_command(card, buf_frame, 1, type, RPMB_REQ); > + > + if (err) { > + pr_err("%s: request write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + goto out; > + } > + > + memset(buf_frame, 0, 512 * blks); > + /* > + * STEP 2: check write result > + * Only for WRITE_DATA or Program key > + */ > + if (type == RPMB_WRITE_DATA) { > + buf_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ; > + err = mmc_rpmb_send_command(card, buf_frame, 1, > + RPMB_RESULT_READ, RPMB_REQ); > + if (err) { > + pr_err("%s: request write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + goto out; > + } > + } > + > + /* > + * STEP 3: get response from RPMB partition > + */ > + > + if (type == RPMB_READ_DATA) > + err = mmc_rpmb_send_command(card, buf_frame, > + blks, type, RPMB_RESP); > + else > + err = mmc_rpmb_send_command(card, buf_frame, > + 1, type, RPMB_RESP); > + if (err) { > + pr_err("%s: response write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + } > +out: > + return err; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_partition_ops); > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h > index d424b9d..971404a 100644 > --- a/include/linux/mmc/card.h > +++ b/include/linux/mmc/card.h > @@ -83,6 +83,7 @@ struct mmc_ext_csd { > unsigned int hpi_cmd; /* cmd used as HPI */ > bool bkops; /* background support bit */ > bool bkops_en; /* background enable bit */ > + unsigned int rpmb_size; /* Units: half sector */ > unsigned int data_sector_size; /* 512 bytes or 4KB */ > unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ > unsigned int boot_ro_lock; /* ro lock support */ > @@ -304,6 +305,7 @@ struct mmc_card { > struct dentry *debugfs_root; > struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ > unsigned int nr_parts; > + unsigned int rpmb_max_req; > }; > > /* > @@ -529,4 +531,7 @@ extern void mmc_unregister_driver(struct mmc_driver *); > extern void mmc_fixup_device(struct mmc_card *card, > const struct mmc_fixup *table); > > +extern int mmc_rpmb_req_handle(struct device *emmc, > + struct mmc_ioc_rpmb_req *req); > + > #endif /* LINUX_MMC_CARD_H */ > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h > index f206e29..5e07733 100644 > --- a/include/linux/mmc/core.h > +++ b/include/linux/mmc/core.h > @@ -10,6 +10,8 @@ > > #include <linux/interrupt.h> > #include <linux/completion.h> > +#include <linux/device.h> > +#include <linux/mmc/ioctl.h> > > struct request; > struct mmc_data; > @@ -137,6 +139,34 @@ struct mmc_request { > struct mmc_host *host; > }; > > +/* > + * RPMB frame structure for MMC core stack > + */ > +struct mmc_core_rpmb_req { > + struct mmc_ioc_rpmb_req *req; > + __u8 *frame; > + bool ready; > +}; > + > +#define RPMB_PROGRAM_KEY 1 /* Program RPMB Authentication Key */ > +#define RPMB_GET_WRITE_COUNTER 2 /* Read RPMB write counter */ > +#define RPMB_WRITE_DATA 3 /* Write data to RPMB partition */ > +#define RPMB_READ_DATA 4 /* Read data from RPMB partition */ > +#define RPMB_RESULT_READ 5 /* Read result request */ > +#define RPMB_REQ 1 /* RPMB request mark */ > +#define RPMB_RESP (1 << 1)/* RPMB response mark */ > +#define RPMB_AVALIABLE_SECTORS 8 /* 4K page size */ > + > +#define RPMB_TYPE_BEG 510 > +#define RPMB_RES_BEG 508 > +#define RPMB_BLKS_BEG 506 > +#define RPMB_ADDR_BEG 504 > +#define RPMB_WCOUNTER_BEG 500 > + > +#define RPMB_NONCE_BEG 484 > +#define RPMB_DATA_BEG 228 > +#define RPMB_MAC_BEG 196 > + > struct mmc_card; > struct mmc_async_req; > > @@ -155,6 +185,10 @@ extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, > bool, bool); > extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); > extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); > +extern int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *, > + struct mmc_card *); > +extern int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *, struct mmc_card *); > +extern void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *); > > #define MMC_ERASE_ARG 0x00000000 > #define MMC_SECURE_ERASE_ARG 0x80000000 > diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h > index 1f5e689..98ad956 100644 > --- a/include/uapi/linux/mmc/ioctl.h > +++ b/include/uapi/linux/mmc/ioctl.h > @@ -47,6 +47,19 @@ struct mmc_ioc_cmd { > > #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) > > +struct mmc_ioc_rpmb_req { > + __u16 type; /* RPMB request type */ > + __u16 *result; /* response or request result */ > + __u16 blk_cnt; /* Number of blocks(half sector 256B) */ > + __u16 addr; /* data address */ > + __u32 *wc; /* write counter */ > + __u8 *nonce; /* Ramdom number */ > + __u8 *data; /* Buffer of the user data */ > + __u8 *mac; /* Message Authentication Code */ > +}; > + > +#define MMC_IOC_RPMB_REQ _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_rpmb_req) > + > /* > * Since this ioctl is only meant to enhance (and not replace) normal access > * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-mmc" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks for the response. I think the security driver can interact with user space, but need some change on top of current implementation. We'll talk with security driver guys on this. Thank you. Regards, Yunpeng -----Original Message----- From: Ulf Hansson [mailto:ulf.hansson@linaro.org] Sent: Tuesday, August 12, 2014 9:28 PM To: Gao, Yunpeng Cc: linux-mmc; Dong, Chuanxiao Subject: Re: [PATCH] [RFC] Implement eMMC RPMB feature in kernel space On 4 August 2014 09:15, Yunpeng Gao <yunpeng.gao@intel.com> wrote: > From: Chuanxiao Dong <chuanxiao.dong@intel.com> > > In current kernel mmc driver stack, seems it expects all the RPMB > operations handled by user space library or applications. > In kernel space, the mmc driver only treat the RPMB ioctl as common > eMMC CMD read/write. > > This is OK. But in some use cases, maybe it is still needed to > implement most of the RPMB operations inside kernel space. > For example, if security driver need to access RPMB partition and > requires the eMMC driver providing an interface API to do so. Isn't your security driver capable of interacting with user space? I think that would be the proper solution. Kind regards Ulf Hansson > > This RFC patch implements eMMC RPMB partition data read/write and > write counter reading. > But not include Key programming since Key it is one time operation and > should not be done by mmc driver. > > Signed-off-by: Yunpeng Gao <yunpeng.gao@intel.com> > Signed-off-by: Chuanxiao Dong <chuanxiao.dong@intel.com> > --- > drivers/mmc/card/block.c | 156 +++++++++++++++- > drivers/mmc/card/queue.c | 2 +- > drivers/mmc/card/queue.h | 1 + > drivers/mmc/core/mmc.c | 17 ++ > drivers/mmc/core/mmc_ops.c | 396 ++++++++++++++++++++++++++++++++++++++++ > include/linux/mmc/card.h | 5 + > include/linux/mmc/core.h | 34 ++++ > include/uapi/linux/mmc/ioctl.h | 13 ++ > 8 files changed, 619 insertions(+), 5 deletions(-) > > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index > 452782b..e185c9d 100644 > --- a/drivers/mmc/card/block.c > +++ b/drivers/mmc/card/block.c > @@ -99,6 +99,7 @@ struct mmc_blk_data { > #define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */ > #define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */ > #define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */ > +#define MMC_BLK_SUSPENDED (1 << 3) /* MMC block device suspended */ > > unsigned int usage; > unsigned int read_only; > @@ -109,6 +110,8 @@ struct mmc_blk_data { > #define MMC_BLK_WRITE BIT(1) > #define MMC_BLK_DISCARD BIT(2) > #define MMC_BLK_SECDISCARD BIT(3) > +#define MMC_BLK_RPMB BIT(4) > +#define MMC_BLK_USER BIT(5) > > /* > * Only set in main mmc_blk_data associated @@ -149,6 +152,9 > @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) > packed->blocks = 0; > } > > +static int mmc_rpmb_req_process(struct mmc_blk_data *, > + struct mmc_ioc_rpmb_req *); > + > static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { > struct mmc_blk_data *md; > @@ -1025,6 +1031,118 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) > md->reset_done &= ~type; > } > > +static int mmc_rpmb_req_process(struct mmc_blk_data *md, > + struct mmc_ioc_rpmb_req *req) { > + struct mmc_core_rpmb_req rpmb_req; > + struct mmc_card *card = NULL; > + int ret; > + > + if (!md || !req) > + return -EINVAL; > + > + if (!(md->flags & MMC_BLK_CMD23) || > + (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB)) > + return -EOPNOTSUPP; > + > + card = md->queue.card; > + if (!card || !mmc_card_mmc(card) || !card->ext_csd.rpmb_size) > + return -ENODEV; > + > + memset(&rpmb_req, 0, sizeof(struct mmc_core_rpmb_req)); > + rpmb_req.req = req; > + /* check request */ > + ret = mmc_rpmb_pre_frame(&rpmb_req, card); > + if (ret) { > + pr_err("%s: prepare frame failed\n", mmc_hostname(card->host)); > + return ret; > + } > + > + mmc_claim_host(card->host); > + > + if (md->flags & MMC_BLK_SUSPENDED) { > + pr_warn("%s: MMC block device is already suspended\n", > + mmc_hostname(card->host)); > + ret = -EPERM; > + goto out; > + } > + > + /* wait for background operation finished */ > + mmc_stop_bkops(card); > + > + /* > + * before start, let's change to RPMB partition first > + */ > + ret = mmc_blk_part_switch(card, md); > + if (ret) { > + pr_err("%s: Invalid RPMB partition switch (%d)!\n", > + mmc_hostname(card->host), ret); > + /* > + * In case partition is not in user data area, make > + * a force partition switch. > + * we need reset eMMC card at here > + */ > + ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB); > + if (!ret) > + mmc_blk_reset_success(md, MMC_BLK_RPMB); > + else > + pr_err("%s: eMMC card reset failed (%d)\n", > + mmc_hostname(card->host), ret); > + goto out; > + } > + > + ret = mmc_rpmb_partition_ops(&rpmb_req, card); > + if (ret) > + pr_err("%s: failed (%d) to handle RPMB request type (%d)!\n", > + mmc_hostname(card->host), ret, > +req->type); > +out: > + mmc_release_host(card->host); > + mmc_rpmb_post_frame(&rpmb_req); > + return ret; > +} > + > +int mmc_access_rpmb(struct mmc_queue *mq) { > + struct mmc_blk_data *md = mq->data; > + /* > + * If this is a RPMB partition access, return ture > + */ > + if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) > + return true; > + > + return false; > +} > +EXPORT_SYMBOL_GPL(mmc_access_rpmb); > + > +int mmc_rpmb_req_handle(struct device *emmc, struct mmc_ioc_rpmb_req > +*req) { > + int ret = 0; > + struct gendisk *disk = NULL; > + struct mmc_blk_data *md = NULL; > + > + if (!emmc || !req) > + return -EINVAL; > + > + disk = dev_to_disk(emmc); > + if (!disk) { > + pr_err("%s: NO eMMC disk found. Try it later\n", > + __func__); > + return -ENODEV; > + } > + > + md = mmc_blk_get(disk); > + if (!md) { > + pr_err("%s: NO eMMC block data. Try it later\n", > + __func__); > + return -ENODEV; > + } > + ret = mmc_rpmb_req_process(md, req); > + mmc_blk_put(md); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_req_handle); > + > static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct > request *req) { > struct mmc_blk_data *md = mq->data; @@ -2016,11 +2134,16 @@ > static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) > > ret = mmc_blk_part_switch(card, md); > if (ret) { > - if (req) { > - blk_end_request_all(req, -EIO); > + pr_err("%s: switch part failed. Try to reset eMMC\n", > + mmc_hostname(card->host)); > + if (mmc_blk_reset(md, card->host, MMC_BLK_USER)) { > + if (req) > + blk_end_request_all(req, -EIO); > + ret = 0; > + goto out; > } > - ret = 0; > - goto out; > + pr_info("%s: Reset eMMC success\n", mmc_hostname(card->host)); > + mmc_blk_reset_success(md, MMC_BLK_USER); > } > > mq->flags &= ~MMC_QUEUE_NEW_REQUEST; @@ -2501,6 +2624,19 @@ > static int _mmc_blk_suspend(struct mmc_card *card) > mmc_queue_suspend(&md->queue); > list_for_each_entry(part_md, &md->part, part) { > mmc_queue_suspend(&part_md->queue); > + if (part_md->part_type == > + EXT_CSD_PART_CONFIG_ACC_RPMB) { > + /* > + * RPMB partition is accessed by API directly. > + * Driver need to set a flag when suspending > + * MMC block device to notify API that the > + * accessing of RPMB partition needs to be > + * stopped > + */ > + mmc_claim_host(card->host); > + part_md->flags |= MMC_BLK_SUSPENDED; > + mmc_release_host(card->host); > + } > } > } > return 0; > @@ -2531,6 +2667,18 @@ static int mmc_blk_resume(struct mmc_card *card) > mmc_queue_resume(&md->queue); > list_for_each_entry(part_md, &md->part, part) { > mmc_queue_resume(&part_md->queue); > + if (part_md->part_type == > + EXT_CSD_PART_CONFIG_ACC_RPMB) { > + /* > + * RPMB partition is accessed by API directly. > + * Driver need to clear MMC_BLK_SUSPENDED flag > + * to make sure the next RPMB partition access > + * request won't be blocked > + */ > + mmc_claim_host(card->host); > + part_md->flags &= ~MMC_BLK_SUSPENDED; > + mmc_release_host(card->host); > + } > } > } > return 0; > diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index > 3e049c1..6ceede0 100644 > --- a/drivers/mmc/card/queue.c > +++ b/drivers/mmc/card/queue.c > @@ -38,7 +38,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) > return BLKPREP_KILL; > } > > - if (mq && mmc_card_removed(mq->card)) > + if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq))) > return BLKPREP_KILL; > > req->cmd_flags |= REQ_DONTPREP; diff --git > a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index > 5752d50..3bbd4e6 100644 > --- a/drivers/mmc/card/queue.h > +++ b/drivers/mmc/card/queue.h > @@ -73,4 +73,5 @@ extern void mmc_queue_bounce_post(struct > mmc_queue_req *); extern int mmc_packed_init(struct mmc_queue *, > struct mmc_card *); extern void mmc_packed_clean(struct mmc_queue *); > > +extern int mmc_access_rpmb(struct mmc_queue *); > #endif > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index > 793c6f7..59766ec 100644 > --- a/drivers/mmc/core/mmc.c > +++ b/drivers/mmc/core/mmc.c > @@ -553,6 +553,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) > * RPMB regions are defined in multiples of 128K. > */ > card->ext_csd.raw_rpmb_size_mult = > ext_csd[EXT_CSD_RPMB_MULT]; > + card->ext_csd.rpmb_size = 128 * > + card->ext_csd.raw_rpmb_size_mult; > + card->ext_csd.rpmb_size <<= 2; /* Unit: half sector */ > if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { > mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, > EXT_CSD_PART_CONFIG_ACC_RPMB, @@ > -604,6 +607,18 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) > card->ext_csd.data_sector_size = 512; > } > > + /* > + * If use legacy relaible write, then the blk counts must not > + * big than the relaible write sectors > + */ > + if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { > + if (card->ext_csd.rel_sectors < RPMB_AVALIABLE_SECTORS) > + card->rpmb_max_req = card->ext_csd.rel_sectors; > + else > + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; > + } else > + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; > + > out: > return err; > } > @@ -710,6 +725,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", > MMC_DEV_ATTR(enhanced_area_size, "%u\n", > card->ext_csd.enhanced_area_size); > MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", > card->ext_csd.raw_rpmb_size_mult); > MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); > +MMC_DEV_ATTR(rpmb_size, "%d\n", card->ext_csd.rpmb_size); > > static struct attribute *mmc_std_attrs[] = { > &dev_attr_cid.attr, > @@ -728,6 +744,7 @@ static struct attribute *mmc_std_attrs[] = { > &dev_attr_enhanced_area_size.attr, > &dev_attr_raw_rpmb_size_mult.attr, > &dev_attr_rel_sectors.attr, > + &dev_attr_rpmb_size.attr, > NULL, > }; > ATTRIBUTE_GROUPS(mmc_std); > diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c > index f51b5ba..8d9f921 100644 > --- a/drivers/mmc/core/mmc_ops.c > +++ b/drivers/mmc/core/mmc_ops.c > @@ -655,3 +655,399 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 > *status) > > return 0; > } > + > +static int mmc_rpmb_send_command(struct mmc_card *card, u8 *buf, __u16 blks, > + __u16 type, u8 req_type) { > + struct mmc_request mrq = {NULL}; > + struct mmc_command cmd = {0}; > + struct mmc_command sbc = {0}; > + struct mmc_data data = {0}; > + struct scatterlist sg; > + u8 *transfer_buf = NULL; > + > + mrq.sbc = &sbc; > + mrq.cmd = &cmd; > + mrq.data = &data; > + mrq.stop = NULL; > + transfer_buf = kzalloc(512 * blks, GFP_KERNEL); > + if (!transfer_buf) > + return -ENOMEM; > + > + /* > + * set CMD23 > + */ > + sbc.opcode = MMC_SET_BLOCK_COUNT; > + sbc.arg = blks; > + if ((req_type == RPMB_REQ) && type == RPMB_WRITE_DATA) > + sbc.arg |= 1 << 31; > + sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; > + > + /* > + * set CMD25/18 > + */ > + sg_init_one(&sg, transfer_buf, 512 * blks); > + if (req_type == RPMB_REQ) { > + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; > + sg_copy_from_buffer(&sg, 1, buf, 512 * blks); > + data.flags |= MMC_DATA_WRITE; > + } else { > + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; > + data.flags |= MMC_DATA_READ; > + } > + cmd.arg = 0; > + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; > + data.blksz = 512; > + data.blocks = blks; > + data.sg = &sg; > + data.sg_len = 1; > + > + mmc_set_data_timeout(&data, card); > + > + mmc_wait_for_req(card->host, &mrq); > + > + if (req_type != RPMB_REQ) > + sg_copy_to_buffer(&sg, 1, buf, 512 * blks); > + > + kfree(transfer_buf); > + > + if (cmd.error) > + return cmd.error; > + if (data.error) > + return data.error; > + return 0; > +} > + > +void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *rpmb_req) { > + int i; > + struct mmc_ioc_rpmb_req *p_req; > + __u8 *buf_frame; > + > + if (!rpmb_req || !rpmb_req->ready) > + return; > + > + p_req = rpmb_req->req; > + buf_frame = rpmb_req->frame; > + > + if (!p_req || !buf_frame) > + return; > + /* > + * Regarding to the check rules, here is the post > + * rules > + * All will return result. > + * GET_WRITE_COUNTER: > + * must: write counter, nonce > + * optional: MAC > + * WRITE_DATA: > + * must: MAC, write counter > + * READ_DATA: > + * must: nonce, data > + * optional: MAC > + * PROGRAM_KEY: > + * must: Nothing > + * > + * Except READ_DATA, all of these operations only need to parse > + * one frame. READ_DATA needs blks frames to get DATA > + */ > + > + memcpy(p_req->result, buf_frame + RPMB_RES_BEG, 2); > + *p_req->result = be16_to_cpup(p_req->result); > + > + if (p_req->type == RPMB_PROGRAM_KEY) > + goto out; > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_WRITE_DATA) { > + memcpy(p_req->wc, buf_frame + RPMB_WCOUNTER_BEG, 4); > + *p_req->wc = be32_to_cpup(p_req->wc); > + } > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_READ_DATA) { > + /* nonce copy */ > + memcpy(p_req->nonce, buf_frame + RPMB_NONCE_BEG, 16); > + } > + /* > + * Take MAC within the last package > + */ > + if (p_req->type == RPMB_READ_DATA) { > + __u8 *data = p_req->data; > + for (i = 0; i < p_req->blk_cnt; i++) { > + memcpy(data, buf_frame + i * 512 + RPMB_DATA_BEG, 256); > + data += 256; > + } > + /* > + * MAC stored in the last package > + */ > + if (p_req->mac) { > + i--; > + memcpy(p_req->mac, buf_frame + i * 512 + RPMB_MAC_BEG, > + 32); > + } > + } else if (p_req->mac) > + memcpy(p_req->mac, buf_frame + RPMB_MAC_BEG, 32); > +out: > + kfree(buf_frame); > + rpmb_req->frame = NULL; > + return; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_post_frame); > + > +static int mmc_rpmb_request_check(struct mmc_card *card, > + struct mmc_ioc_rpmb_req *p_req) { > + /* > + * Some paramter is a must for the operation. Different > + * operation expect different paramters. Below code is > + * used for checking this. > + * > + * All operations will need result. > + * GET_WRITE_COUNTER: > + * must: write counter, nonce > + * optional: MAC > + * WRITE_DATA: > + * must: MAC, data, write counter > + * READ_DATA: > + * must: nonce, data > + * optional: MAC > + * PROGRAM_KEY: > + * must: MAC > + * > + * So here, we only check the 'must' paramters > + */ > + if (!p_req->result) { > + pr_err("%s: Type %d has NULL pointer for result\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + > + if (p_req->type == RPMB_GET_WRITE_COUNTER) { > + if (!p_req->nonce || !p_req->wc) { > + pr_err("%s: Type %d has NULL pointer for nonce/wc\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + /* > + * used to allocate frame > + */ > + p_req->blk_cnt = 1; > + } else if (p_req->type == RPMB_WRITE_DATA || > + p_req->type == RPMB_READ_DATA) { > + if ((__u32)(p_req->addr + p_req->blk_cnt) > > + card->ext_csd.rpmb_size) { > + pr_err("%s Type %d: beyond the RPMB partition rang addr %d, blk_cnt %d, rpmb_size %d\n", > + mmc_hostname(card->host), > + p_req->type, > + p_req->addr, > + p_req->blk_cnt, > + card->ext_csd.rpmb_size); > + /* > + * Not return error here since we want device to handle > + * such errors > + */ > + } > + if (p_req->blk_cnt == 0) { > + pr_err("%s: Type %d has zero block count\n", > + mmc_hostname(card->host), > + p_req->blk_cnt); > + return -EINVAL; > + } else if (p_req->blk_cnt > card->rpmb_max_req) { > + pr_err("%s: Type %d has invalid block count, cannot large than %d\n", > + mmc_hostname(card->host), > + p_req->blk_cnt, > + card->rpmb_max_req); > + return -EINVAL; > + } > + if (!p_req->data) { > + pr_err("%s: Type %d has NULL pointer for data\n", > + mmc_hostname(card->host), p_req->type); > + return -EINVAL; > + } > + if (p_req->type == RPMB_WRITE_DATA) { > + if (!p_req->wc || !p_req->mac) { > + pr_err("%s: Type %d has NULL pointer for write counter/MAC\n", > + mmc_hostname(card->host), > + p_req->type); > + return -EINVAL; > + } > + } else { > + if (!p_req->nonce) { > + pr_err("%s: Type %d has NULL pointer for nonce\n", > + mmc_hostname(card->host), > + p_req->type); > + return -EINVAL; > + } > + } > + } else > + return -EOPNOTSUPP; > + > + return 0; > +} > + > +/* > + * prepare the request of RPMB frame > + * RPMB frame is MSB first > + * convert needed bytes > + * return how many frames will be prepared */ int > +mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *rpmb_req, > + struct mmc_card *card) { > + int i, ret; > + struct mmc_ioc_rpmb_req *p_req; > + __u8 *buf_frame; > + __u16 blk_cnt, addr, type; > + __u32 w_counter; > + > + if (!rpmb_req || !card) > + return -EINVAL; > + > + p_req = rpmb_req->req; > + if (!p_req) { > + pr_err("%s: mmc_ioc_rpmb_req is NULL. Wrong parameter\n", > + mmc_hostname(card->host)); > + return -EINVAL; > + } > + > + /* > + * make sure these two items are clear > + */ > + rpmb_req->ready = 0; > + rpmb_req->frame = NULL; > + > + ret = mmc_rpmb_request_check(card, p_req); > + if (ret) > + return ret; > + > + buf_frame = kzalloc(512 * p_req->blk_cnt, GFP_KERNEL); > + if (!buf_frame) { > + pr_err("%s: cannot allocate frame for type %d\n", > + mmc_hostname(card->host), p_req->type); > + return -ENOMEM; > + } > + > + type = cpu_to_be16p(&p_req->type); > + if (p_req->type == RPMB_GET_WRITE_COUNTER || > + p_req->type == RPMB_READ_DATA) { > + /* > + * One package prepared > + * This request needs Nonce and type > + * If is data read, then also need addr > + */ > + memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); > + if (p_req->type == RPMB_READ_DATA) { > + addr = cpu_to_be16p(&p_req->addr); > + memcpy(buf_frame + RPMB_ADDR_BEG, &addr, 2); > + } > + /* convert Nonce code */ > + memcpy(buf_frame + RPMB_NONCE_BEG, p_req->nonce, 16); > + } else if (p_req->type == RPMB_WRITE_DATA) { > + __u8 *data = p_req->data; > + /* > + * multiple package prepared > + * This request nees blk_cnt, addr, write_counter, > + * data and mac > + */ > + blk_cnt = cpu_to_be16p(&p_req->blk_cnt); > + addr = cpu_to_be16p(&p_req->addr); > + w_counter = cpu_to_be32p(p_req->wc); > + for (i = 0; i < p_req->blk_cnt; i++) { > + memcpy(buf_frame + i * 512 + RPMB_TYPE_BEG, > + &type, 2); > + memcpy(buf_frame + i * 512 + RPMB_BLKS_BEG, > + &blk_cnt, 2); > + memcpy(buf_frame + i * 512 + RPMB_ADDR_BEG, > + &addr, 2); > + memcpy(buf_frame + i * 512 + RPMB_WCOUNTER_BEG, > + &w_counter, 4); > + memcpy(buf_frame + i * 512 + RPMB_DATA_BEG, > + data, 256); > + data += 256; > + } > + /* convert MAC code */ > + memcpy(buf_frame + 512 * (i - 1) + RPMB_MAC_BEG, > + p_req->mac, 32); > + } else { > + pr_err("%s: We shouldn't be here\n", mmc_hostname(card->host)); > + kfree(buf_frame); > + return -EINVAL; > + } > + rpmb_req->ready = 1; > + rpmb_req->frame = buf_frame; > + return 0; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_pre_frame); > + > +int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *rpmb_req, > + struct mmc_card *card) { > + int err = 0; > + struct mmc_ioc_rpmb_req *p_req; > + __u16 type, blks; > + __u8 *buf_frame; > + > + if (!rpmb_req || !card) > + return -EINVAL; > + > + p_req = rpmb_req->req; > + buf_frame = rpmb_req->frame; > + > + if (!p_req || !rpmb_req->ready || !buf_frame) { > + pr_err("%s: mmc_ioc_rpmb_req is not prepared\n", > + mmc_hostname(card->host)); > + return -EINVAL; > + } > + > + type = p_req->type; > + blks = p_req->blk_cnt; > + > + /* > + * STEP 1: send request to RPMB partition > + */ > + if (type == RPMB_WRITE_DATA) > + err = mmc_rpmb_send_command(card, buf_frame, blks, > + type, RPMB_REQ); > + else > + err = mmc_rpmb_send_command(card, buf_frame, 1, type, > + RPMB_REQ); > + > + if (err) { > + pr_err("%s: request write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + goto out; > + } > + > + memset(buf_frame, 0, 512 * blks); > + /* > + * STEP 2: check write result > + * Only for WRITE_DATA or Program key > + */ > + if (type == RPMB_WRITE_DATA) { > + buf_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ; > + err = mmc_rpmb_send_command(card, buf_frame, 1, > + RPMB_RESULT_READ, RPMB_REQ); > + if (err) { > + pr_err("%s: request write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + goto out; > + } > + } > + > + /* > + * STEP 3: get response from RPMB partition > + */ > + > + if (type == RPMB_READ_DATA) > + err = mmc_rpmb_send_command(card, buf_frame, > + blks, type, RPMB_RESP); > + else > + err = mmc_rpmb_send_command(card, buf_frame, > + 1, type, RPMB_RESP); > + if (err) { > + pr_err("%s: response write counter failed (%d)\n", > + mmc_hostname(card->host), err); > + } > +out: > + return err; > +} > +EXPORT_SYMBOL_GPL(mmc_rpmb_partition_ops); > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index > d424b9d..971404a 100644 > --- a/include/linux/mmc/card.h > +++ b/include/linux/mmc/card.h > @@ -83,6 +83,7 @@ struct mmc_ext_csd { > unsigned int hpi_cmd; /* cmd used as HPI */ > bool bkops; /* background support bit */ > bool bkops_en; /* background enable bit */ > + unsigned int rpmb_size; /* Units: half sector */ > unsigned int data_sector_size; /* 512 bytes or 4KB */ > unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ > unsigned int boot_ro_lock; /* ro lock support */ > @@ -304,6 +305,7 @@ struct mmc_card { > struct dentry *debugfs_root; > struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ > unsigned int nr_parts; > + unsigned int rpmb_max_req; > }; > > /* > @@ -529,4 +531,7 @@ extern void mmc_unregister_driver(struct > mmc_driver *); extern void mmc_fixup_device(struct mmc_card *card, > const struct mmc_fixup *table); > > +extern int mmc_rpmb_req_handle(struct device *emmc, > + struct mmc_ioc_rpmb_req *req); > + > #endif /* LINUX_MMC_CARD_H */ > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index > f206e29..5e07733 100644 > --- a/include/linux/mmc/core.h > +++ b/include/linux/mmc/core.h > @@ -10,6 +10,8 @@ > > #include <linux/interrupt.h> > #include <linux/completion.h> > +#include <linux/device.h> > +#include <linux/mmc/ioctl.h> > > struct request; > struct mmc_data; > @@ -137,6 +139,34 @@ struct mmc_request { > struct mmc_host *host; > }; > > +/* > + * RPMB frame structure for MMC core stack */ struct > +mmc_core_rpmb_req { > + struct mmc_ioc_rpmb_req *req; > + __u8 *frame; > + bool ready; > +}; > + > +#define RPMB_PROGRAM_KEY 1 /* Program RPMB Authentication Key */ > +#define RPMB_GET_WRITE_COUNTER 2 /* Read RPMB write counter */ > +#define RPMB_WRITE_DATA 3 /* Write data to RPMB partition */ > +#define RPMB_READ_DATA 4 /* Read data from RPMB partition */ > +#define RPMB_RESULT_READ 5 /* Read result request */ > +#define RPMB_REQ 1 /* RPMB request mark */ > +#define RPMB_RESP (1 << 1)/* RPMB response mark */ > +#define RPMB_AVALIABLE_SECTORS 8 /* 4K page size */ > + > +#define RPMB_TYPE_BEG 510 > +#define RPMB_RES_BEG 508 > +#define RPMB_BLKS_BEG 506 > +#define RPMB_ADDR_BEG 504 > +#define RPMB_WCOUNTER_BEG 500 > + > +#define RPMB_NONCE_BEG 484 > +#define RPMB_DATA_BEG 228 > +#define RPMB_MAC_BEG 196 > + > struct mmc_card; > struct mmc_async_req; > > @@ -155,6 +185,10 @@ extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, > bool, bool); > extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); > extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); > +extern int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *, > + struct mmc_card *); > +extern int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *, struct > +mmc_card *); extern void mmc_rpmb_post_frame(struct mmc_core_rpmb_req > +*); > > #define MMC_ERASE_ARG 0x00000000 > #define MMC_SECURE_ERASE_ARG 0x80000000 > diff --git a/include/uapi/linux/mmc/ioctl.h > b/include/uapi/linux/mmc/ioctl.h index 1f5e689..98ad956 100644 > --- a/include/uapi/linux/mmc/ioctl.h > +++ b/include/uapi/linux/mmc/ioctl.h > @@ -47,6 +47,19 @@ struct mmc_ioc_cmd { > > #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) > > +struct mmc_ioc_rpmb_req { > + __u16 type; /* RPMB request type */ > + __u16 *result; /* response or request result */ > + __u16 blk_cnt; /* Number of blocks(half sector 256B) */ > + __u16 addr; /* data address */ > + __u32 *wc; /* write counter */ > + __u8 *nonce; /* Ramdom number */ > + __u8 *data; /* Buffer of the user data */ > + __u8 *mac; /* Message Authentication Code */ > +}; > + > +#define MMC_IOC_RPMB_REQ _IOWR(MMC_BLOCK_MAJOR, 1, struct > +mmc_ioc_rpmb_req) > + > /* > * Since this ioctl is only meant to enhance (and not replace) normal access > * to the mmc bus device, an upper data transfer limit of > MMC_IOC_MAX_BYTES > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-mmc" > in the body of a message to majordomo@vger.kernel.org More majordomo > info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 452782b..e185c9d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -99,6 +99,7 @@ struct mmc_blk_data { #define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */ #define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */ #define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */ +#define MMC_BLK_SUSPENDED (1 << 3) /* MMC block device suspended */ unsigned int usage; unsigned int read_only; @@ -109,6 +110,8 @@ struct mmc_blk_data { #define MMC_BLK_WRITE BIT(1) #define MMC_BLK_DISCARD BIT(2) #define MMC_BLK_SECDISCARD BIT(3) +#define MMC_BLK_RPMB BIT(4) +#define MMC_BLK_USER BIT(5) /* * Only set in main mmc_blk_data associated @@ -149,6 +152,9 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) packed->blocks = 0; } +static int mmc_rpmb_req_process(struct mmc_blk_data *, + struct mmc_ioc_rpmb_req *); + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { struct mmc_blk_data *md; @@ -1025,6 +1031,118 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) md->reset_done &= ~type; } +static int mmc_rpmb_req_process(struct mmc_blk_data *md, + struct mmc_ioc_rpmb_req *req) +{ + struct mmc_core_rpmb_req rpmb_req; + struct mmc_card *card = NULL; + int ret; + + if (!md || !req) + return -EINVAL; + + if (!(md->flags & MMC_BLK_CMD23) || + (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB)) + return -EOPNOTSUPP; + + card = md->queue.card; + if (!card || !mmc_card_mmc(card) || !card->ext_csd.rpmb_size) + return -ENODEV; + + memset(&rpmb_req, 0, sizeof(struct mmc_core_rpmb_req)); + rpmb_req.req = req; + /* check request */ + ret = mmc_rpmb_pre_frame(&rpmb_req, card); + if (ret) { + pr_err("%s: prepare frame failed\n", mmc_hostname(card->host)); + return ret; + } + + mmc_claim_host(card->host); + + if (md->flags & MMC_BLK_SUSPENDED) { + pr_warn("%s: MMC block device is already suspended\n", + mmc_hostname(card->host)); + ret = -EPERM; + goto out; + } + + /* wait for background operation finished */ + mmc_stop_bkops(card); + + /* + * before start, let's change to RPMB partition first + */ + ret = mmc_blk_part_switch(card, md); + if (ret) { + pr_err("%s: Invalid RPMB partition switch (%d)!\n", + mmc_hostname(card->host), ret); + /* + * In case partition is not in user data area, make + * a force partition switch. + * we need reset eMMC card at here + */ + ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB); + if (!ret) + mmc_blk_reset_success(md, MMC_BLK_RPMB); + else + pr_err("%s: eMMC card reset failed (%d)\n", + mmc_hostname(card->host), ret); + goto out; + } + + ret = mmc_rpmb_partition_ops(&rpmb_req, card); + if (ret) + pr_err("%s: failed (%d) to handle RPMB request type (%d)!\n", + mmc_hostname(card->host), ret, req->type); +out: + mmc_release_host(card->host); + mmc_rpmb_post_frame(&rpmb_req); + return ret; +} + +int mmc_access_rpmb(struct mmc_queue *mq) +{ + struct mmc_blk_data *md = mq->data; + /* + * If this is a RPMB partition access, return ture + */ + if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(mmc_access_rpmb); + +int mmc_rpmb_req_handle(struct device *emmc, struct mmc_ioc_rpmb_req *req) +{ + int ret = 0; + struct gendisk *disk = NULL; + struct mmc_blk_data *md = NULL; + + if (!emmc || !req) + return -EINVAL; + + disk = dev_to_disk(emmc); + if (!disk) { + pr_err("%s: NO eMMC disk found. Try it later\n", + __func__); + return -ENODEV; + } + + md = mmc_blk_get(disk); + if (!md) { + pr_err("%s: NO eMMC block data. Try it later\n", + __func__); + return -ENODEV; + } + ret = mmc_rpmb_req_process(md, req); + mmc_blk_put(md); + + return ret; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_req_handle); + static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -2016,11 +2134,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_part_switch(card, md); if (ret) { - if (req) { - blk_end_request_all(req, -EIO); + pr_err("%s: switch part failed. Try to reset eMMC\n", + mmc_hostname(card->host)); + if (mmc_blk_reset(md, card->host, MMC_BLK_USER)) { + if (req) + blk_end_request_all(req, -EIO); + ret = 0; + goto out; } - ret = 0; - goto out; + pr_info("%s: Reset eMMC success\n", mmc_hostname(card->host)); + mmc_blk_reset_success(md, MMC_BLK_USER); } mq->flags &= ~MMC_QUEUE_NEW_REQUEST; @@ -2501,6 +2624,19 @@ static int _mmc_blk_suspend(struct mmc_card *card) mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); + if (part_md->part_type == + EXT_CSD_PART_CONFIG_ACC_RPMB) { + /* + * RPMB partition is accessed by API directly. + * Driver need to set a flag when suspending + * MMC block device to notify API that the + * accessing of RPMB partition needs to be + * stopped + */ + mmc_claim_host(card->host); + part_md->flags |= MMC_BLK_SUSPENDED; + mmc_release_host(card->host); + } } } return 0; @@ -2531,6 +2667,18 @@ static int mmc_blk_resume(struct mmc_card *card) mmc_queue_resume(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); + if (part_md->part_type == + EXT_CSD_PART_CONFIG_ACC_RPMB) { + /* + * RPMB partition is accessed by API directly. + * Driver need to clear MMC_BLK_SUSPENDED flag + * to make sure the next RPMB partition access + * request won't be blocked + */ + mmc_claim_host(card->host); + part_md->flags &= ~MMC_BLK_SUSPENDED; + mmc_release_host(card->host); + } } } return 0; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 3e049c1..6ceede0 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -38,7 +38,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) return BLKPREP_KILL; } - if (mq && mmc_card_removed(mq->card)) + if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq))) return BLKPREP_KILL; req->cmd_flags |= REQ_DONTPREP; diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index 5752d50..3bbd4e6 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -73,4 +73,5 @@ extern void mmc_queue_bounce_post(struct mmc_queue_req *); extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *); extern void mmc_packed_clean(struct mmc_queue *); +extern int mmc_access_rpmb(struct mmc_queue *); #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 793c6f7..59766ec 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -553,6 +553,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) * RPMB regions are defined in multiples of 128K. */ card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; + card->ext_csd.rpmb_size = 128 * + card->ext_csd.raw_rpmb_size_mult; + card->ext_csd.rpmb_size <<= 2; /* Unit: half sector */ if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, EXT_CSD_PART_CONFIG_ACC_RPMB, @@ -604,6 +607,18 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + /* + * If use legacy relaible write, then the blk counts must not + * big than the relaible write sectors + */ + if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { + if (card->ext_csd.rel_sectors < RPMB_AVALIABLE_SECTORS) + card->rpmb_max_req = card->ext_csd.rel_sectors; + else + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; + } else + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; + out: return err; } @@ -710,6 +725,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +MMC_DEV_ATTR(rpmb_size, "%d\n", card->ext_csd.rpmb_size); static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, @@ -728,6 +744,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_enhanced_area_size.attr, &dev_attr_raw_rpmb_size_mult.attr, &dev_attr_rel_sectors.attr, + &dev_attr_rpmb_size.attr, NULL, }; ATTRIBUTE_GROUPS(mmc_std); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index f51b5ba..8d9f921 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -655,3 +655,399 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) return 0; } + +static int mmc_rpmb_send_command(struct mmc_card *card, u8 *buf, __u16 blks, + __u16 type, u8 req_type) +{ + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_command sbc = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + u8 *transfer_buf = NULL; + + mrq.sbc = &sbc; + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop = NULL; + transfer_buf = kzalloc(512 * blks, GFP_KERNEL); + if (!transfer_buf) + return -ENOMEM; + + /* + * set CMD23 + */ + sbc.opcode = MMC_SET_BLOCK_COUNT; + sbc.arg = blks; + if ((req_type == RPMB_REQ) && type == RPMB_WRITE_DATA) + sbc.arg |= 1 << 31; + sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; + + /* + * set CMD25/18 + */ + sg_init_one(&sg, transfer_buf, 512 * blks); + if (req_type == RPMB_REQ) { + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; + sg_copy_from_buffer(&sg, 1, buf, 512 * blks); + data.flags |= MMC_DATA_WRITE; + } else { + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; + data.flags |= MMC_DATA_READ; + } + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + data.blksz = 512; + data.blocks = blks; + data.sg = &sg; + data.sg_len = 1; + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (req_type != RPMB_REQ) + sg_copy_to_buffer(&sg, 1, buf, 512 * blks); + + kfree(transfer_buf); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + return 0; +} + +void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *rpmb_req) +{ + int i; + struct mmc_ioc_rpmb_req *p_req; + __u8 *buf_frame; + + if (!rpmb_req || !rpmb_req->ready) + return; + + p_req = rpmb_req->req; + buf_frame = rpmb_req->frame; + + if (!p_req || !buf_frame) + return; + /* + * Regarding to the check rules, here is the post + * rules + * All will return result. + * GET_WRITE_COUNTER: + * must: write counter, nonce + * optional: MAC + * WRITE_DATA: + * must: MAC, write counter + * READ_DATA: + * must: nonce, data + * optional: MAC + * PROGRAM_KEY: + * must: Nothing + * + * Except READ_DATA, all of these operations only need to parse + * one frame. READ_DATA needs blks frames to get DATA + */ + + memcpy(p_req->result, buf_frame + RPMB_RES_BEG, 2); + *p_req->result = be16_to_cpup(p_req->result); + + if (p_req->type == RPMB_PROGRAM_KEY) + goto out; + + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_WRITE_DATA) { + memcpy(p_req->wc, buf_frame + RPMB_WCOUNTER_BEG, 4); + *p_req->wc = be32_to_cpup(p_req->wc); + } + + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_READ_DATA) { + /* nonce copy */ + memcpy(p_req->nonce, buf_frame + RPMB_NONCE_BEG, 16); + } + /* + * Take MAC within the last package + */ + if (p_req->type == RPMB_READ_DATA) { + __u8 *data = p_req->data; + for (i = 0; i < p_req->blk_cnt; i++) { + memcpy(data, buf_frame + i * 512 + RPMB_DATA_BEG, 256); + data += 256; + } + /* + * MAC stored in the last package + */ + if (p_req->mac) { + i--; + memcpy(p_req->mac, buf_frame + i * 512 + RPMB_MAC_BEG, + 32); + } + } else if (p_req->mac) + memcpy(p_req->mac, buf_frame + RPMB_MAC_BEG, 32); +out: + kfree(buf_frame); + rpmb_req->frame = NULL; + return; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_post_frame); + +static int mmc_rpmb_request_check(struct mmc_card *card, + struct mmc_ioc_rpmb_req *p_req) +{ + /* + * Some paramter is a must for the operation. Different + * operation expect different paramters. Below code is + * used for checking this. + * + * All operations will need result. + * GET_WRITE_COUNTER: + * must: write counter, nonce + * optional: MAC + * WRITE_DATA: + * must: MAC, data, write counter + * READ_DATA: + * must: nonce, data + * optional: MAC + * PROGRAM_KEY: + * must: MAC + * + * So here, we only check the 'must' paramters + */ + if (!p_req->result) { + pr_err("%s: Type %d has NULL pointer for result\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + + if (p_req->type == RPMB_GET_WRITE_COUNTER) { + if (!p_req->nonce || !p_req->wc) { + pr_err("%s: Type %d has NULL pointer for nonce/wc\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + /* + * used to allocate frame + */ + p_req->blk_cnt = 1; + } else if (p_req->type == RPMB_WRITE_DATA || + p_req->type == RPMB_READ_DATA) { + if ((__u32)(p_req->addr + p_req->blk_cnt) > + card->ext_csd.rpmb_size) { + pr_err("%s Type %d: beyond the RPMB partition rang addr %d, blk_cnt %d, rpmb_size %d\n", + mmc_hostname(card->host), + p_req->type, + p_req->addr, + p_req->blk_cnt, + card->ext_csd.rpmb_size); + /* + * Not return error here since we want device to handle + * such errors + */ + } + if (p_req->blk_cnt == 0) { + pr_err("%s: Type %d has zero block count\n", + mmc_hostname(card->host), + p_req->blk_cnt); + return -EINVAL; + } else if (p_req->blk_cnt > card->rpmb_max_req) { + pr_err("%s: Type %d has invalid block count, cannot large than %d\n", + mmc_hostname(card->host), + p_req->blk_cnt, + card->rpmb_max_req); + return -EINVAL; + } + if (!p_req->data) { + pr_err("%s: Type %d has NULL pointer for data\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + if (p_req->type == RPMB_WRITE_DATA) { + if (!p_req->wc || !p_req->mac) { + pr_err("%s: Type %d has NULL pointer for write counter/MAC\n", + mmc_hostname(card->host), + p_req->type); + return -EINVAL; + } + } else { + if (!p_req->nonce) { + pr_err("%s: Type %d has NULL pointer for nonce\n", + mmc_hostname(card->host), + p_req->type); + return -EINVAL; + } + } + } else + return -EOPNOTSUPP; + + return 0; +} + +/* + * prepare the request of RPMB frame + * RPMB frame is MSB first + * convert needed bytes + * return how many frames will be prepared + */ +int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *rpmb_req, + struct mmc_card *card) +{ + int i, ret; + struct mmc_ioc_rpmb_req *p_req; + __u8 *buf_frame; + __u16 blk_cnt, addr, type; + __u32 w_counter; + + if (!rpmb_req || !card) + return -EINVAL; + + p_req = rpmb_req->req; + if (!p_req) { + pr_err("%s: mmc_ioc_rpmb_req is NULL. Wrong parameter\n", + mmc_hostname(card->host)); + return -EINVAL; + } + + /* + * make sure these two items are clear + */ + rpmb_req->ready = 0; + rpmb_req->frame = NULL; + + ret = mmc_rpmb_request_check(card, p_req); + if (ret) + return ret; + + buf_frame = kzalloc(512 * p_req->blk_cnt, GFP_KERNEL); + if (!buf_frame) { + pr_err("%s: cannot allocate frame for type %d\n", + mmc_hostname(card->host), p_req->type); + return -ENOMEM; + } + + type = cpu_to_be16p(&p_req->type); + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_READ_DATA) { + /* + * One package prepared + * This request needs Nonce and type + * If is data read, then also need addr + */ + memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); + if (p_req->type == RPMB_READ_DATA) { + addr = cpu_to_be16p(&p_req->addr); + memcpy(buf_frame + RPMB_ADDR_BEG, &addr, 2); + } + /* convert Nonce code */ + memcpy(buf_frame + RPMB_NONCE_BEG, p_req->nonce, 16); + } else if (p_req->type == RPMB_WRITE_DATA) { + __u8 *data = p_req->data; + /* + * multiple package prepared + * This request nees blk_cnt, addr, write_counter, + * data and mac + */ + blk_cnt = cpu_to_be16p(&p_req->blk_cnt); + addr = cpu_to_be16p(&p_req->addr); + w_counter = cpu_to_be32p(p_req->wc); + for (i = 0; i < p_req->blk_cnt; i++) { + memcpy(buf_frame + i * 512 + RPMB_TYPE_BEG, + &type, 2); + memcpy(buf_frame + i * 512 + RPMB_BLKS_BEG, + &blk_cnt, 2); + memcpy(buf_frame + i * 512 + RPMB_ADDR_BEG, + &addr, 2); + memcpy(buf_frame + i * 512 + RPMB_WCOUNTER_BEG, + &w_counter, 4); + memcpy(buf_frame + i * 512 + RPMB_DATA_BEG, + data, 256); + data += 256; + } + /* convert MAC code */ + memcpy(buf_frame + 512 * (i - 1) + RPMB_MAC_BEG, + p_req->mac, 32); + } else { + pr_err("%s: We shouldn't be here\n", mmc_hostname(card->host)); + kfree(buf_frame); + return -EINVAL; + } + rpmb_req->ready = 1; + rpmb_req->frame = buf_frame; + return 0; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_pre_frame); + +int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *rpmb_req, + struct mmc_card *card) +{ + int err = 0; + struct mmc_ioc_rpmb_req *p_req; + __u16 type, blks; + __u8 *buf_frame; + + if (!rpmb_req || !card) + return -EINVAL; + + p_req = rpmb_req->req; + buf_frame = rpmb_req->frame; + + if (!p_req || !rpmb_req->ready || !buf_frame) { + pr_err("%s: mmc_ioc_rpmb_req is not prepared\n", + mmc_hostname(card->host)); + return -EINVAL; + } + + type = p_req->type; + blks = p_req->blk_cnt; + + /* + * STEP 1: send request to RPMB partition + */ + if (type == RPMB_WRITE_DATA) + err = mmc_rpmb_send_command(card, buf_frame, blks, + type, RPMB_REQ); + else + err = mmc_rpmb_send_command(card, buf_frame, 1, type, RPMB_REQ); + + if (err) { + pr_err("%s: request write counter failed (%d)\n", + mmc_hostname(card->host), err); + goto out; + } + + memset(buf_frame, 0, 512 * blks); + /* + * STEP 2: check write result + * Only for WRITE_DATA or Program key + */ + if (type == RPMB_WRITE_DATA) { + buf_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ; + err = mmc_rpmb_send_command(card, buf_frame, 1, + RPMB_RESULT_READ, RPMB_REQ); + if (err) { + pr_err("%s: request write counter failed (%d)\n", + mmc_hostname(card->host), err); + goto out; + } + } + + /* + * STEP 3: get response from RPMB partition + */ + + if (type == RPMB_READ_DATA) + err = mmc_rpmb_send_command(card, buf_frame, + blks, type, RPMB_RESP); + else + err = mmc_rpmb_send_command(card, buf_frame, + 1, type, RPMB_RESP); + if (err) { + pr_err("%s: response write counter failed (%d)\n", + mmc_hostname(card->host), err); + } +out: + return err; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_partition_ops); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d424b9d..971404a 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -83,6 +83,7 @@ struct mmc_ext_csd { unsigned int hpi_cmd; /* cmd used as HPI */ bool bkops; /* background support bit */ bool bkops_en; /* background enable bit */ + unsigned int rpmb_size; /* Units: half sector */ unsigned int data_sector_size; /* 512 bytes or 4KB */ unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ @@ -304,6 +305,7 @@ struct mmc_card { struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ unsigned int nr_parts; + unsigned int rpmb_max_req; }; /* @@ -529,4 +531,7 @@ extern void mmc_unregister_driver(struct mmc_driver *); extern void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table); +extern int mmc_rpmb_req_handle(struct device *emmc, + struct mmc_ioc_rpmb_req *req); + #endif /* LINUX_MMC_CARD_H */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index f206e29..5e07733 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -10,6 +10,8 @@ #include <linux/interrupt.h> #include <linux/completion.h> +#include <linux/device.h> +#include <linux/mmc/ioctl.h> struct request; struct mmc_data; @@ -137,6 +139,34 @@ struct mmc_request { struct mmc_host *host; }; +/* + * RPMB frame structure for MMC core stack + */ +struct mmc_core_rpmb_req { + struct mmc_ioc_rpmb_req *req; + __u8 *frame; + bool ready; +}; + +#define RPMB_PROGRAM_KEY 1 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 2 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 3 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 4 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 5 /* Read result request */ +#define RPMB_REQ 1 /* RPMB request mark */ +#define RPMB_RESP (1 << 1)/* RPMB response mark */ +#define RPMB_AVALIABLE_SECTORS 8 /* 4K page size */ + +#define RPMB_TYPE_BEG 510 +#define RPMB_RES_BEG 508 +#define RPMB_BLKS_BEG 506 +#define RPMB_ADDR_BEG 504 +#define RPMB_WCOUNTER_BEG 500 + +#define RPMB_NONCE_BEG 484 +#define RPMB_DATA_BEG 228 +#define RPMB_MAC_BEG 196 + struct mmc_card; struct mmc_async_req; @@ -155,6 +185,10 @@ extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, bool, bool); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); +extern int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *, + struct mmc_card *); +extern int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *, struct mmc_card *); +extern void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h index 1f5e689..98ad956 100644 --- a/include/uapi/linux/mmc/ioctl.h +++ b/include/uapi/linux/mmc/ioctl.h @@ -47,6 +47,19 @@ struct mmc_ioc_cmd { #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) +struct mmc_ioc_rpmb_req { + __u16 type; /* RPMB request type */ + __u16 *result; /* response or request result */ + __u16 blk_cnt; /* Number of blocks(half sector 256B) */ + __u16 addr; /* data address */ + __u32 *wc; /* write counter */ + __u8 *nonce; /* Ramdom number */ + __u8 *data; /* Buffer of the user data */ + __u8 *mac; /* Message Authentication Code */ +}; + +#define MMC_IOC_RPMB_REQ _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_rpmb_req) + /* * Since this ioctl is only meant to enhance (and not replace) normal access * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES