[PATCH] [RFC] Implement eMMC RPMB feature in kernel space

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

 



From: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx>

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.

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@xxxxxxxxx>
Signed-off-by: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx>
---
 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@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