From 29ed14e1a427c4e308413bccdc75084c1c1ec75a Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 Feb 2023 15:09:21 +0100
Subject: [PATCH v2 5/9] blk: blkmap: Add linear device mapping support

Allow a slice of an existing block device to be mapped to a
blkmap. This means that filesystems that are not stored at exact
partition boundaries can be accessed by remapping a slice of the
existing device to a blkmap device.

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
 drivers/block/blkmap.c | 71 ++++++++++++++++++++++++++++++++++++++++++
 include/blkmap.h       | 13 ++++++++
 2 files changed, 84 insertions(+)

diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c
index 6d6eed889e..2bb0acc20f 100644
--- a/drivers/block/blkmap.c
+++ b/drivers/block/blkmap.c
@@ -130,6 +130,77 @@ static int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new)
 	return 0;
 }
 
+/**
+ * struct blkmap_linear - Linear mapping to other block device
+ *
+ * @slice: Common map data
+ * @blk: Target block device of this mapping
+ * @blknr: Start block number of the target device
+ */
+struct blkmap_linear {
+	struct blkmap_slice slice;
+
+	struct udevice *blk;
+	lbaint_t blknr;
+};
+
+static ulong blkmap_linear_read(struct blkmap *bm, struct blkmap_slice *bms,
+				lbaint_t blknr, lbaint_t blkcnt, void *buffer)
+{
+	struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice);
+
+	return blk_read(bml->blk, bml->blknr + blknr, blkcnt, buffer);
+}
+
+static ulong blkmap_linear_write(struct blkmap *bm, struct blkmap_slice *bms,
+				 lbaint_t blknr, lbaint_t blkcnt,
+				 const void *buffer)
+{
+	struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice);
+
+	return blk_write(bml->blk, bml->blknr + blknr, blkcnt, buffer);
+}
+
+int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
+		      struct udevice *lblk, lbaint_t lblknr)
+{
+	struct blkmap *bm = dev_get_plat(dev);
+	struct blkmap_linear *linear;
+	struct blk_desc *bd, *lbd;
+	int err;
+
+	bd = dev_get_uclass_plat(bm->blk);
+	lbd = dev_get_uclass_plat(lblk);
+	if (lbd->blksz != bd->blksz)
+		/* We could support block size translation, but we
+		 * don't yet.
+		 */
+		return -EINVAL;
+
+	linear = malloc(sizeof(*linear));
+	if (!linear)
+		return -ENOMEM;
+
+	*linear = (struct blkmap_linear) {
+		.slice = {
+			.blknr = blknr,
+			.blkcnt = blkcnt,
+
+			.read = blkmap_linear_read,
+			.write = blkmap_linear_write,
+		},
+
+		.blk = lblk,
+		.blknr = lblknr,
+	};
+
+	err = blkmap_slice_add(bm, &linear->slice);
+	if (err)
+		free(linear);
+
+	return err;
+}
+
 /**
  * struct blkmap_mem - Memory mapping
  *
diff --git a/include/blkmap.h b/include/blkmap.h
index 74baeb19f8..af54583c7d 100644
--- a/include/blkmap.h
+++ b/include/blkmap.h
@@ -7,6 +7,19 @@
 #ifndef _BLKMAP_H
 #define _BLKMAP_H
 
+/**
+ * blkmap_map_linear() - Map region of other block device
+ *
+ * @dev: Blkmap to create the mapping on
+ * @blknr: Start block number of the mapping
+ * @blkcnt: Number of blocks to map
+ * @lblk: The target block device of the mapping
+ * @lblknr: The start block number of the target device
+ * Returns: 0 on success, negative error code on failure
+ */
+int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
+		      struct udevice *lblk, lbaint_t lblknr);
+
 /**
  * blkmap_map_mem() - Map region of memory
  *
-- 
2.34.1

