[PATCH v2 2/3] mmc: block: register RPMB partition with the RPMB subsystem
From: Jens Wiklander
Date: Wed Jan 31 2024 - 12:44:47 EST
Register eMMC RPMB partition with the RPMB subsystem and provide
an implementation for the RPMB access operations abstracting
the actual multi step process.
Add callbacks for getting and putting the needed resources, that is, the
RPMB data and the RPMB disk.
Add a callback to extract the needed device information at registration
to avoid accessing the struct mmc_card at a later stage as we're not
holding a reference counter for this struct.
Signed-off-by: Tomas Winkler <tomas.winkler@xxxxxxxxx>
Signed-off-by: Alexander Usyskin <alexander.usyskin@xxxxxxxxx>
Signed-off-by: Jens Wiklander <jens.wiklander@xxxxxxxxxx>
---
drivers/mmc/core/block.c | 177 +++++++++++++++++++++++++++++++++++++++
1 file changed, 177 insertions(+)
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 32d49100dff5..5286e0b3a5a2 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -33,6 +33,7 @@
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/scatterlist.h>
+#include <linux/string.h>
#include <linux/string_helpers.h>
#include <linux/delay.h>
#include <linux/capability.h>
@@ -40,6 +41,7 @@
#include <linux/pm_runtime.h>
#include <linux/idr.h>
#include <linux/debugfs.h>
+#include <linux/rpmb.h>
#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
@@ -163,6 +165,7 @@ struct mmc_rpmb_data {
int id;
unsigned int part_index;
struct mmc_blk_data *md;
+ struct rpmb_dev *rdev;
struct list_head node;
};
@@ -2707,6 +2710,169 @@ static void mmc_blk_rpmb_device_release(struct device *dev)
kfree(rpmb);
}
+static void rpmb_op_mmc_get_resources(struct device *dev)
+{
+ struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
+
+ /*
+ * When the MMC card is removed rpmb_dev_unregister() is called
+ * from mmc_blk_remove_rpmb_part(). That removes references to the
+ * devices in struct mmc_rpmb_data and rpmb->md. Since struct
+ * rpmb_dev can still reach those structs we must hold a reference
+ * until struct rpmb_dev also is released.
+ *
+ * This is analogous to what's done in mmc_rpmb_chrdev_open() and
+ * mmc_rpmb_chrdev_release() below.
+ */
+ get_device(dev);
+ mmc_blk_get(rpmb->md->disk);
+}
+
+static void rpmb_op_mmc_put_resources(struct device *dev)
+{
+ struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
+
+ mmc_blk_put(rpmb->md);
+ put_device(dev);
+}
+
+static struct mmc_blk_ioc_data **alloc_idata(struct mmc_rpmb_data *rpmb,
+ unsigned int cmd_count)
+{
+ struct mmc_blk_ioc_data **idata;
+ unsigned int n;
+
+ idata = kcalloc(cmd_count, sizeof(*idata), GFP_KERNEL);
+ if (!idata)
+ return NULL;
+
+ for (n = 0; n < cmd_count; n++) {
+ idata[n] = kcalloc(1, sizeof(**idata), GFP_KERNEL);
+ if (!idata[n]) {
+ kfree(idata);
+ return NULL;
+ }
+ idata[n]->rpmb = rpmb;
+ }
+
+ return idata;
+}
+
+static void set_idata(struct mmc_blk_ioc_data *idata, u32 opcode,
+ int write_flag, u8 *buf, unsigned int buf_bytes)
+{
+ idata->ic.opcode = opcode;
+ idata->ic.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ idata->ic.write_flag = write_flag;
+ idata->ic.blksz = sizeof(struct rpmb_frame);
+ idata->ic.blocks = buf_bytes / idata->ic.blksz;
+ idata->buf = buf;
+ idata->buf_bytes = buf_bytes;
+}
+
+static void free_idata(struct mmc_blk_ioc_data **idata, unsigned int cmd_count)
+{
+ unsigned int n;
+
+ for (n = 0; n < cmd_count; n++)
+ kfree(idata[n]);
+ kfree(idata);
+}
+
+static int rpmb_op_mmc_route_frames(struct device *dev, bool write, u8 *req,
+ unsigned int req_len, u8 *resp,
+ unsigned int resp_len)
+{
+ struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
+ struct mmc_blk_data *md = rpmb->md;
+ struct mmc_blk_ioc_data **idata;
+ unsigned int cmd_count;
+ struct request *rq;
+ int ret;
+
+ if (write)
+ cmd_count = 3;
+ else
+ cmd_count = 2;
+
+ if (IS_ERR(md->queue.card))
+ return PTR_ERR(md->queue.card);
+
+ idata = alloc_idata(rpmb, cmd_count);
+ if (!idata)
+ return -ENOMEM;
+
+ if (write) {
+ struct rpmb_frame *frm = (struct rpmb_frame *)resp;
+
+ /* Send write request frame(s) */
+ set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK,
+ 1 | MMC_CMD23_ARG_REL_WR, req, req_len);
+
+ /* Send result request frame */
+ memset(frm, 0, sizeof(*frm));
+ frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+ set_idata(idata[1], MMC_WRITE_MULTIPLE_BLOCK, 1, resp,
+ resp_len);
+
+ /* Read response frame */
+ set_idata(idata[2], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len);
+ } else {
+ /* Send write request frame(s) */
+ set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK, 1, req, req_len);
+
+ /* Read response frame */
+ set_idata(idata[1], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len);
+ }
+
+ rq = blk_mq_alloc_request(md->queue.queue, REQ_OP_DRV_OUT, 0);
+ if (IS_ERR(rq)) {
+ ret = PTR_ERR(rq);
+ goto out;
+ }
+
+ req_to_mmc_queue_req(rq)->drv_op = MMC_DRV_OP_IOCTL_RPMB;
+ req_to_mmc_queue_req(rq)->drv_op_result = -EIO;
+ req_to_mmc_queue_req(rq)->drv_op_data = idata;
+ req_to_mmc_queue_req(rq)->ioc_count = cmd_count;
+ blk_execute_rq(rq, false);
+ ret = req_to_mmc_queue_req(rq)->drv_op_result;
+
+ blk_mq_free_request(rq);
+
+out:
+ free_idata(idata, cmd_count);
+ return ret;
+}
+
+static int rpmb_op_mmc_set_dev_info(struct device *dev, struct rpmb_dev *rdev)
+{
+ struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
+ struct mmc_card *card = rpmb->md->queue.card;
+ unsigned int n;
+ u32 cid[4];
+
+ for (n = 0; n < 4; n++)
+ cid[n] = be32_to_cpu(card->raw_cid[n]);
+
+ rdev->dev_id = kmemdup(cid, sizeof(cid), GFP_KERNEL);
+ if (!rdev->dev_id)
+ return -ENOMEM;
+ rdev->dev_id_len = sizeof(cid);
+ rdev->reliable_wr_count = card->ext_csd.raw_rpmb_size_mult;
+ rdev->capacity = card->ext_csd.rel_sectors;
+
+ return 0;
+}
+
+static struct rpmb_ops rpmb_mmc_ops = {
+ .type = RPMB_TYPE_EMMC,
+ .get_resources = rpmb_op_mmc_get_resources,
+ .put_resources = rpmb_op_mmc_put_resources,
+ .route_frames = rpmb_op_mmc_route_frames,
+ .set_dev_info = rpmb_op_mmc_set_dev_info,
+};
+
static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
struct mmc_blk_data *md,
unsigned int part_index,
@@ -2751,6 +2917,14 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
goto out_put_device;
}
+ rpmb->rdev = rpmb_dev_register(&rpmb->dev, &rpmb_mmc_ops);
+ if (IS_ERR(rpmb->rdev)) {
+ pr_err("%s: could not register RPMB device\n", rpmb_name);
+ ret = PTR_ERR(rpmb->rdev);
+ rpmb->rdev = NULL;
+ goto out_cdev_device_del;
+ }
+
list_add(&rpmb->node, &md->rpmbs);
string_get_size((u64)size, 512, STRING_UNITS_2,
@@ -2762,6 +2936,8 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
return 0;
+out_cdev_device_del:
+ cdev_device_del(&rpmb->chrdev, &rpmb->dev);
out_put_device:
put_device(&rpmb->dev);
return ret;
@@ -2770,6 +2946,7 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb)
{
+ rpmb_dev_unregister(rpmb->rdev);
cdev_device_del(&rpmb->chrdev, &rpmb->dev);
put_device(&rpmb->dev);
}
--
2.34.1