* [PATCH 0/5] dm: Initial work on a device mapper
@ 2025-08-28 15:05 Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 1/5] string: add strtok/strtokv Tobias Waldekranz
` (5 more replies)
0 siblings, 6 replies; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
Start work on adding a device mapper that is compatible with the
corresponding subsystem in Linux.
This is the foundation of several higher level abstractions, for
example:
- LVM: Linux Volume manager. Dynamically allocates logical volumes
from one or more storage devices, manages RAID arrays, etc.
- LUKS: Linux Unified Key Setup. Transparent disk
encryption/decryption.
- dm-verity: Transparent integrity checking of block devices.
This is part of an exploratory project for investigating how we could
boot Infix[1] in a more platform-independent way. I.e., my intention
is to eventually add support for some of the features mentioned above,
assuming we don't hit any major road blocks. The rest of this letter
just gives context for how we got here and where we would like to take
Barebox. If that is not interesting, feel free to stop reading here :)
Our idea is to relegate U-Boot to serve only as a UEFI firmware on the
platforms where we can't escape it, and then do most of our boot logic
in Barebox instead. Primarily we want to do this for two reasons:
1. Being able to ship barebox as an EFI app means we can use the same
boot logic on x86 machines as we to on everything else.
2. Barebox is a much higher quality code base to work in than
U-Boot. I'm sorry, but it just is.
Barebox would thus take the place occupied by systemd-boot in many
distro setups. So why not go with systemd-boot?
1. Infix does not run systemd as PID 1, so reusing their bootloader is
awkward.
2. Infix ships as a single immutable filesystem image, including
kernel, DTBs, etc. So we want to extract these files from the
filesystem before booting the kernel. This is not supported by
systemd-boot, AFAIK - all boot files must live on the ESP.
3. We would like to manage our devices' non-volatile storage with LVM,
and not be bound to a fixed partition table. This will give us more
flexibility in growing our image, efficiently having images of
varying sizes installed, etc.
Therefore, our plan is (roughly):
1. Add dm-verity support
2. Add dm-verity root-hash-signature verification support
With that, we can securely extract kernel+DTB from our filesystem
without having to sign them individually.
3. Add basic LVM support, no RAID or anything, just basic (linear)
logical volumes.
This will allow us to install multiple versions of Infix on individual
logical volumes, which Barebox can then find and boot from.
4. Add high-level helpers for working with DPS disks and DDI images.
I really like the Linux Userspace API Group's thinking around
Discoverable Partitions Specification (DPS) and Discoverable Disk
Images (DDI). I think it would be great if Barebox had knowledge about
these patterns, and could automatically set up the dm-verity
configuration for a partition when available, for example.
My hope is that this plan sparks some ideas and reflections. If so, I
would love to hear them. If not, sorry for the wall of text :)
[1]: https://github.com/kernelkit/infix/
Tobias Waldekranz (5):
string: add strtok/strtokv
dm: Add initial device mapper infrastructure
dm: linear: Add linear target
test: self: dm: Add test of linear target
commands: dmsetup: Basic command set for dm device management
commands/Kconfig | 14 ++
commands/Makefile | 1 +
commands/dmsetup.c | 145 +++++++++++++
drivers/block/Kconfig | 2 +
drivers/block/Makefile | 1 +
drivers/block/dm/Kconfig | 14 ++
drivers/block/dm/Makefile | 3 +
drivers/block/dm/dm-core.c | 393 +++++++++++++++++++++++++++++++++++
drivers/block/dm/dm-linear.c | 123 +++++++++++
drivers/block/dm/dm-target.h | 39 ++++
include/dm.h | 16 ++
include/string.h | 2 +
lib/string.c | 66 ++++++
test/self/Kconfig | 7 +
test/self/Makefile | 1 +
test/self/dm.c | 159 ++++++++++++++
16 files changed, 986 insertions(+)
create mode 100644 commands/dmsetup.c
create mode 100644 drivers/block/dm/Kconfig
create mode 100644 drivers/block/dm/Makefile
create mode 100644 drivers/block/dm/dm-core.c
create mode 100644 drivers/block/dm/dm-linear.c
create mode 100644 drivers/block/dm/dm-target.h
create mode 100644 include/dm.h
create mode 100644 test/self/dm.c
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/5] string: add strtok/strtokv
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
@ 2025-08-28 15:05 ` Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 2/5] dm: Add initial device mapper infrastructure Tobias Waldekranz
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
Add an implementation of libc's standard strtok(3), which is useful
for tokenizing strings.
Also, add a version that will collect all tokens from a string into an
array, which is useful in situations where you need to know how many
tokens there are, and when a token's relative position in the order is
significant.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
include/string.h | 2 ++
lib/string.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/include/string.h b/include/string.h
index 71affe48b6..c8df8540d8 100644
--- a/include/string.h
+++ b/include/string.h
@@ -8,6 +8,8 @@
void *mempcpy(void *dest, const void *src, size_t count);
int strtobool(const char *str, int *val);
char *strsep_unescaped(char **, const char *, char *);
+char *strtok(char *str, const char *delim);
+int strtokv(char *str, const char *delim, char ***vecp);
char *stpcpy(char *dest, const char *src);
bool strends(const char *str, const char *postfix);
diff --git a/lib/string.c b/lib/string.c
index 73637cd971..be7e65eb45 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -593,6 +593,72 @@ char *strsep_unescaped(char **s, const char *ct, char *delim)
return sbegin;
}
+/**
+ * strtok - extract tokens from string
+ * @str: string to split
+ * @delim: set of delimiter characters
+ *
+ * The strtok() function breaks up a string into zero or more nonempty
+ * tokens. On the first call, the string to be parsed should be
+ * specified in @str. In each subsequent call that should parse the
+ * same string, @str must be NULL.
+ *
+ * @delim specifies a set of bytes that delimit the tokens in the
+ * string.
+ *
+ * Each call to strtok() returns a pointer to a string containing the
+ * next token. This is done by replacing the first delimiter with a
+ * NUL character, the operation is thus destructive to the string. If
+ * no more tokens are found, strtok() returns NULL.
+ */
+char *strtok(char *str, const char *delim)
+{
+ static char *cursor;
+
+ if (str)
+ cursor = str;
+
+ if (!cursor)
+ return NULL;
+
+ cursor += strspn(cursor, delim);
+ if (*cursor == '\0') {
+ cursor = NULL;
+ return NULL;
+ }
+
+ return strsep(&cursor, delim);
+}
+EXPORT_SYMBOL(strtok);
+
+/**
+ * strtokv - split string into array of tokens based on a delimiter set
+ * @str: string to split
+ * @delim: set of delimiter characters
+ * @vecp: array of tokens
+ *
+ * Split @str into tokens delimited by @delim, using strtok(), and
+ * store the allocated token array in @vecp, which the caller is
+ * responsible for freeing.
+ *
+ * Return: The number of tokens in the array.
+ */
+int strtokv(char *str, const char *delim, char ***vecp)
+{
+ char *tok, **vec = NULL;
+ int cnt = 0;
+
+
+ for (tok = strtok(str, delim); tok; tok = strtok(NULL, delim)) {
+ vec = xrealloc(vec, (cnt + 1) * sizeof(*vec));
+ vec[cnt++] = tok;
+ }
+
+ *vecp = vec;
+ return cnt;
+}
+EXPORT_SYMBOL(strtokv);
+
#ifndef __HAVE_ARCH_STRSWAB
/**
* strswab - swap adjacent even and odd bytes in %NUL-terminated string
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/5] dm: Add initial device mapper infrastructure
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 1/5] string: add strtok/strtokv Tobias Waldekranz
@ 2025-08-28 15:05 ` Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 3/5] dm: linear: Add linear target Tobias Waldekranz
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
Add initial scaffolding for a block device mapper which is intended to
be compatible with the corresponding subsystem in Linux.
This is the foundation of several higher level abstractions, for
example:
- LVM: Linux Volume manager. Dynamically allocates logical volumes
from one or more storage devices, manages RAID arrays, etc.
- LUKS: Linux Unified Key Setup. Transparent disk
encryption/decryption.
- dm-verity: Transparent integrity checking of block devices.
Being able to configure dm devices in barebox will allow us to access
data from LVM volumes, access filesystems with block layer integrity
validation, etc.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/block/Kconfig | 2 +
drivers/block/Makefile | 1 +
drivers/block/dm/Kconfig | 7 +
drivers/block/dm/Makefile | 2 +
drivers/block/dm/dm-core.c | 393 +++++++++++++++++++++++++++++++++++
drivers/block/dm/dm-target.h | 39 ++++
include/dm.h | 16 ++
7 files changed, 460 insertions(+)
create mode 100644 drivers/block/dm/Kconfig
create mode 100644 drivers/block/dm/Makefile
create mode 100644 drivers/block/dm/dm-core.c
create mode 100644 drivers/block/dm/dm-target.h
create mode 100644 include/dm.h
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 5b1b778917..dace861670 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -32,3 +32,5 @@ config RAMDISK_BLK
help
This symbol is selected by testing code that requires lightweight
creation of anonymous block devices backed fully by memory buffers.
+
+source "drivers/block/dm/Kconfig"
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 6066b35c31..8f913bd0ad 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_EFI_BLK) += efi-block-io.o
obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
obj-$(CONFIG_RAMDISK_BLK) += ramdisk.o
+obj-y += dm/
diff --git a/drivers/block/dm/Kconfig b/drivers/block/dm/Kconfig
new file mode 100644
index 0000000000..f64c69e03d
--- /dev/null
+++ b/drivers/block/dm/Kconfig
@@ -0,0 +1,7 @@
+menuconfig DM_BLK
+ bool "Device mapper"
+ help
+ Composable virtual block devices made up of mappings to
+ other data sources. Modeled after, and intended to be
+ compatible with, the Linux kernel's device mapper subsystem.
+
diff --git a/drivers/block/dm/Makefile b/drivers/block/dm/Makefile
new file mode 100644
index 0000000000..9c045087c0
--- /dev/null
+++ b/drivers/block/dm/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_DM_BLK) += dm-core.o
diff --git a/drivers/block/dm/dm-core.c b/drivers/block/dm/dm-core.c
new file mode 100644
index 0000000000..cf92dac94c
--- /dev/null
+++ b/drivers/block/dm/dm-core.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2025 Tobias Waldekranz <tobias@waldekranz.com>, Wires
+
+#include <block.h>
+#include <common.h>
+#include <disks.h>
+#include <dm.h>
+#include <string.h>
+
+#include "dm-target.h"
+
+static LIST_HEAD(dm_target_ops_list);
+
+static struct dm_target_ops *dm_target_ops_find(const char *name)
+{
+ struct dm_target_ops *ops;
+
+ list_for_each_entry(ops, &dm_target_ops_list, list) {
+ if (!strcmp(ops->name, name))
+ return ops;
+ }
+ return NULL;
+}
+
+int dm_target_register(struct dm_target_ops *ops)
+{
+ list_add(&ops->list, &dm_target_ops_list);
+ return 0;
+}
+
+void dm_target_unregister(struct dm_target_ops *ops)
+{
+ list_del(&ops->list);
+}
+
+struct dm_device {
+ struct device dev;
+ struct block_device blk;
+ struct list_head list;
+ struct list_head targets;
+};
+
+static LIST_HEAD(dm_device_list);
+
+struct dm_device *dm_find_by_name(const char *name)
+{
+ struct dm_device *dm;
+
+ list_for_each_entry(dm, &dm_device_list, list) {
+ if (!strcmp(dev_name(&dm->dev), name))
+ return dm;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(dm_find_by_name);
+
+int dm_foreach(int (*cb)(struct dm_device *dm, void *ctx), void *ctx)
+{
+ struct dm_device *dm;
+ int err;
+
+ list_for_each_entry(dm, &dm_device_list, list) {
+ err = cb(dm, ctx);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dm_foreach);
+
+void dm_target_err(struct dm_target *ti, const char *fmt, ...)
+{
+ struct dm_target *iter;
+ va_list ap;
+ char *msg;
+ int i = 0;
+
+ list_for_each_entry(iter, &ti->dm->targets, list) {
+ if (iter == ti)
+ break;
+ i++;
+ }
+
+ va_start(ap, fmt);
+ msg = xvasprintf(fmt, ap);
+ va_end(ap);
+
+ dev_err(&ti->dm->dev, "%d(%s): %s", i, ti->ops->name, msg);
+ free(msg);
+}
+EXPORT_SYMBOL(dm_target_err);
+
+static int dm_blk_read(struct block_device *blk, void *buf,
+ sector_t block, blkcnt_t num_blocks)
+{
+ struct dm_device *dm = container_of(blk, struct dm_device, blk);
+ struct dm_target *ti;
+ blkcnt_t tnblks, todo;
+ sector_t tblk;
+ int err;
+
+ todo = num_blocks;
+
+ list_for_each_entry(ti, &dm->targets, list) {
+ if (block < ti->base || block >= ti->base + ti->size)
+ continue;
+
+ if (!ti->ops->read)
+ return -EIO;
+
+ tblk = block - ti->base;
+ tnblks = min(todo, ti->size - tblk);
+ err = ti->ops->read(ti, buf, tblk, tnblks);
+ if (err)
+ return err;
+
+ block += tnblks;
+ todo -= tnblks;
+ buf += tnblks << SECTOR_SHIFT;
+ if (!todo)
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int dm_blk_write(struct block_device *blk, const void *buf,
+ sector_t block, blkcnt_t num_blocks)
+{
+ struct dm_device *dm = container_of(blk, struct dm_device, blk);
+ struct dm_target *ti;
+ blkcnt_t tnblks, todo;
+ sector_t tblk;
+ int err;
+
+ todo = num_blocks;
+
+ list_for_each_entry(ti, &dm->targets, list) {
+ if (block < ti->base || block >= ti->base + ti->size)
+ continue;
+
+ if (!ti->ops->write)
+ return -EIO;
+
+ tblk = block - ti->base;
+ tnblks = min(todo, ti->size - tblk);
+ err = ti->ops->write(ti, buf, tblk, tnblks);
+ if (err)
+ return err;
+
+ block += tnblks;
+ todo -= tnblks;
+ buf += tnblks << SECTOR_SHIFT;
+ if (!todo)
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static struct block_device_ops dm_blk_ops = {
+ .read = dm_blk_read,
+ .write = dm_blk_write,
+};
+
+static blkcnt_t dm_size(struct dm_device *dm)
+{
+ struct dm_target *last;
+
+ if (list_empty(&dm->targets))
+ return 0;
+
+ last = list_last_entry(&dm->targets, struct dm_target, list);
+ return last->base + last->size;
+}
+
+static int dm_n_targets(struct dm_device *dm)
+{
+ struct dm_target *ti;
+ int n = 0;
+
+ list_for_each_entry(ti, &dm->targets, list) {
+ n++;
+ }
+
+ return n;
+}
+
+char *dm_asprint(struct dm_device *dm)
+{
+ char **strv, *str, *tistr;
+ struct dm_target *ti;
+ int n = 0, strc;
+
+ strc = 1 + dm_n_targets(dm);
+ strv = xmalloc(strc * sizeof(*strv));
+
+ strv[n++] = xasprintf(
+ "Device: %s\n"
+ "Size: %llu\n"
+ "Table:\n"
+ " # Start End Size Target\n",
+ dev_name(&dm->dev), dm_size(dm));
+
+ list_for_each_entry(ti, &dm->targets, list) {
+ tistr = ti->ops->asprint ? ti->ops->asprint(ti) : NULL;
+
+ strv[n] = xasprintf("%2d %10llu %10llu %10llu %s %s\n",
+ n - 1, ti->base, ti->base + ti->size - 1,
+ ti->size, ti->ops->name, tistr ? : "");
+ n++;
+
+ if (tistr)
+ free(tistr);
+ }
+
+ str = strjoin("", strv, strc);
+
+ for (n = 0; n < strc; n++)
+ free(strv[n]);
+
+ free(strv);
+
+ return str;
+}
+EXPORT_SYMBOL(dm_asprint);
+
+static struct dm_target *dm_parse_row(struct dm_device *dm, const char *crow)
+{
+ struct dm_target *ti;
+ char *row, **argv;
+ int argc;
+
+ row = xstrdup(crow);
+ argc = strtokv(row, " \t", &argv);
+
+ if (argc < 3) {
+ dev_err(&dm->dev, "Invalid row: \"%s\"\n", crow);
+ goto err;
+ }
+
+ ti = xzalloc(sizeof(*ti));
+ ti->dm = dm;
+
+ ti->ops = dm_target_ops_find(argv[2]);
+ if (!ti->ops) {
+ dev_err(&dm->dev, "Unknown target: \"%s\"\n", argv[2]);
+ goto err_free;
+ }
+
+ if (kstrtoull(argv[0], 0, &ti->base)) {
+ dm_target_err(ti, "Invalid start: \"%s\"\n", argv[0]);
+ goto err_free;
+ }
+
+ if (ti->base != dm_size(dm)) {
+ /* Could we just skip the start argument, then? Seems
+ * like it, but let's keep things compatible with the
+ * table format in Linux.
+ */
+ dm_target_err(ti, "Non-contiguous start: %llu, expected %llu\n",
+ ti->base, dm_size(dm));
+ goto err_free;
+ }
+
+ if (kstrtoull(argv[1], 0, &ti->size) || !ti->size) {
+ dm_target_err(ti, "Invalid length: \"%s\"\n", argv[1]);
+ goto err_free;
+ }
+
+ argc -= 3;
+
+ if (ti->ops->create(ti, argc, argc ? &argv[3] : NULL))
+ goto err_free;
+
+ free(argv);
+ free(row);
+ return ti;
+
+err_free:
+ free(ti);
+err:
+ free(argv);
+ free(row);
+ return NULL;
+}
+
+static int dm_parse_table(struct dm_device *dm, const char *ctable)
+{
+ struct dm_target *ti, *tmp;
+ char *table, **rowv;
+ int i, rowc;
+
+ table = xstrdup(ctable);
+ rowc = strtokv(table, "\n", &rowv);
+
+ for (i = 0; i < rowc; i++) {
+ ti = dm_parse_row(dm, rowv[i]);
+ if (!ti)
+ goto err_destroy;
+
+ list_add_tail(&ti->list, &dm->targets);
+ }
+
+ free(rowv);
+ free(table);
+ return 0;
+
+err_destroy:
+ list_for_each_entry_safe_reverse(ti, tmp, &dm->targets, list) {
+ ti->ops->destroy(ti);
+ list_del(&ti->list);
+ }
+
+ free(rowv);
+ free(table);
+
+ dev_err(&dm->dev, "Failed to parse table\n");
+ return -EINVAL;
+}
+
+void dm_destroy(struct dm_device *dm)
+{
+ struct dm_target *ti;
+
+ list_del(&dm->list);
+
+ blockdevice_unregister(&dm->blk);
+
+ list_for_each_entry_reverse(ti, &dm->targets, list) {
+ ti->ops->destroy(ti);
+ }
+
+ unregister_device(&dm->dev);
+
+ free(dm);
+}
+EXPORT_SYMBOL(dm_destroy);
+
+struct dm_device *dm_create(const char *name, const char *table)
+{
+ struct dm_target *ti;
+ struct dm_device *dm;
+ int err;
+
+ dm = xzalloc(sizeof(*dm));
+
+ dev_set_name(&dm->dev, "%s", name);
+ dm->dev.id = DEVICE_ID_SINGLE;
+ err = register_device(&dm->dev);
+ if (err)
+ goto err_free;
+
+ INIT_LIST_HEAD(&dm->targets);
+ err = dm_parse_table(dm, table);
+ if (err)
+ goto err_unregister;
+
+ dm->blk = (struct block_device) {
+ .dev = &dm->dev,
+ .cdev = {
+ .name = xstrdup(name),
+ },
+
+ .type = BLK_TYPE_VIRTUAL,
+ .ops = &dm_blk_ops,
+
+ .num_blocks = dm_size(dm),
+ .blockbits = SECTOR_SHIFT,
+ };
+
+ err = blockdevice_register(&dm->blk);
+ if (err)
+ goto err_destroy;
+
+ list_add_tail(&dm->list, &dm_device_list);
+ return dm;
+
+err_destroy:
+ list_for_each_entry_reverse(ti, &dm->targets, list) {
+ ti->ops->destroy(ti);
+ }
+
+err_unregister:
+ unregister_device(&dm->dev);
+
+err_free:
+ free(dm);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(dm_create);
diff --git a/drivers/block/dm/dm-target.h b/drivers/block/dm/dm-target.h
new file mode 100644
index 0000000000..506e808b79
--- /dev/null
+++ b/drivers/block/dm/dm-target.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: © 2025 Tobias Waldekranz <tobias@waldekranz.com>, Wires */
+
+#ifndef __DM_TARGET_H
+#define __DM_TARGET_H
+
+struct dm_device;
+struct dm_target_ops;
+
+struct dm_target {
+ struct dm_device *dm;
+ struct list_head list;
+
+ sector_t base;
+ blkcnt_t size;
+
+ const struct dm_target_ops *ops;
+ void *private;
+};
+
+void dm_target_err(struct dm_target *ti, const char *fmt, ...);
+
+struct dm_target_ops {
+ struct list_head list;
+ const char *name;
+
+ char *(*asprint)(struct dm_target *ti);
+ int (*create)(struct dm_target *ti, unsigned int argc, char **argv);
+ int (*destroy)(struct dm_target *ti);
+ int (*read)(struct dm_target *ti, void *buf,
+ sector_t block, blkcnt_t num_blocks);
+ int (*write)(struct dm_target *ti, const void *buf,
+ sector_t block, blkcnt_t num_blocks);
+};
+
+int dm_target_register(struct dm_target_ops *ops);
+void dm_target_unregister(struct dm_target_ops *ops);
+
+#endif /* __DM_TARGET_H */
diff --git a/include/dm.h b/include/dm.h
new file mode 100644
index 0000000000..255796ca2f
--- /dev/null
+++ b/include/dm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DM_H
+#define __DM_H
+
+struct dm_device;
+
+struct dm_device *dm_find_by_name(const char *name);
+int dm_foreach(int (*cb)(struct dm_device *dm, void *ctx), void *ctx);
+
+char *dm_asprint(struct dm_device *dm);
+
+void dm_destroy(struct dm_device *dm);
+struct dm_device *dm_create(const char *name, const char *ctable);
+
+#endif /* __DM_H */
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/5] dm: linear: Add linear target
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 1/5] string: add strtok/strtokv Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 2/5] dm: Add initial device mapper infrastructure Tobias Waldekranz
@ 2025-08-28 15:05 ` Tobias Waldekranz
2025-08-29 5:56 ` Ahmad Fatoum
2025-08-28 15:05 ` [PATCH 4/5] test: self: dm: Add test of " Tobias Waldekranz
` (2 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
This target allows you to map in a region of another device as a
contiguous range of blocks of a dm device.
This is the basic building block used by LVM to stitch together
logical volumes from one or more ranges of blocks from one or more
physical disks.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/block/dm/Kconfig | 7 ++
drivers/block/dm/Makefile | 1 +
drivers/block/dm/dm-linear.c | 123 +++++++++++++++++++++++++++++++++++
3 files changed, 131 insertions(+)
create mode 100644 drivers/block/dm/dm-linear.c
diff --git a/drivers/block/dm/Kconfig b/drivers/block/dm/Kconfig
index f64c69e03d..e763e530d3 100644
--- a/drivers/block/dm/Kconfig
+++ b/drivers/block/dm/Kconfig
@@ -5,3 +5,10 @@ menuconfig DM_BLK
other data sources. Modeled after, and intended to be
compatible with, the Linux kernel's device mapper subsystem.
+config DM_BLK_LINEAR
+ bool "Linear target"
+ depends on DM_BLK
+ help
+ Maps a contiguous region of an existing device into a dm
+ device.
+
diff --git a/drivers/block/dm/Makefile b/drivers/block/dm/Makefile
index 9c045087c0..f52d19f9c4 100644
--- a/drivers/block/dm/Makefile
+++ b/drivers/block/dm/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DM_BLK) += dm-core.o
+obj-$(CONFIG_DM_BLK_LINEAR) += dm-linear.o
diff --git a/drivers/block/dm/dm-linear.c b/drivers/block/dm/dm-linear.c
new file mode 100644
index 0000000000..d3415814cb
--- /dev/null
+++ b/drivers/block/dm/dm-linear.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2025 Tobias Waldekranz <tobias@waldekranz.com>, Wires
+
+#include <block.h>
+#include <common.h>
+#include <disks.h>
+#include <fcntl.h>
+
+#include "dm-target.h"
+
+struct dm_linear {
+ struct cdev *cdev;
+ loff_t offset;
+};
+
+static int dm_linear_read(struct dm_target *ti, void *buf,
+ sector_t block, blkcnt_t num_blocks)
+{
+ struct dm_linear *l = ti->private;
+
+ block <<= SECTOR_SHIFT;
+ num_blocks <<= SECTOR_SHIFT;
+
+ if (cdev_read(l->cdev, buf, num_blocks, l->offset + block, 0) < num_blocks)
+ return -EIO;
+
+ return 0;
+}
+
+static int dm_linear_write(struct dm_target *ti, const void *buf,
+ sector_t block, blkcnt_t num_blocks)
+{
+ struct dm_linear *l = ti->private;
+
+ block <<= SECTOR_SHIFT;
+ num_blocks <<= SECTOR_SHIFT;
+
+ if (cdev_write(l->cdev, buf, num_blocks, l->offset + block, 0) < num_blocks)
+ return -EIO;
+
+ return 0;
+}
+
+static int dm_linear_create(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ struct dm_linear *l;
+ int err;
+
+ if (argc != 2) {
+ dm_target_err(ti, "Expected 2 arguments (\"<dev> <offset>\"), got %u\n",
+ argc);
+ err = -EINVAL;
+ goto err;
+ }
+
+ l = xzalloc(sizeof(*l));
+ ti->private = l;
+
+ if (kstrtoull(argv[1], 0, &l->offset)) {
+ dm_target_err(ti, "Invalid offset: \"%s\"\n", argv[1]);
+ err = -EINVAL;
+ goto err_free;
+ }
+ l->offset <<= SECTOR_SHIFT;
+
+ l->cdev = cdev_open_by_path_name(argv[0], O_RDWR);
+ if (!l->cdev) {
+ dm_target_err(ti, "Cannot open device %s: %m\n", argv[0]);
+ err = -ENODEV;
+ goto err_free;
+ }
+
+ l->cdev = cdev_readlink(l->cdev);
+
+ if ((ti->size << SECTOR_SHIFT) > (l->cdev->size - l->offset)) {
+ dm_target_err(ti, "%s is too small to map %llu blocks at %llu, %llu available\n",
+ argv[0], ti->size, l->offset >> SECTOR_SHIFT,
+ (l->cdev->size - l->offset) >> SECTOR_SHIFT);
+ err = -ENOSPC;
+ goto err_close;
+ }
+
+ return 0;
+
+err_close:
+ cdev_close(l->cdev);
+err_free:
+ free(l);
+err:
+ return err;
+}
+
+static int dm_linear_destroy(struct dm_target *ti)
+{
+ struct dm_linear *l = ti->private;
+
+ cdev_close(l->cdev);
+ free(l);
+ return 0;
+}
+
+static char *dm_linear_asprint(struct dm_target *ti)
+{
+ struct dm_linear *l = ti->private;
+
+ return xasprintf("dev:%s offset:%llu",
+ cdev_name(l->cdev), l->offset >> SECTOR_SHIFT);
+}
+
+static struct dm_target_ops dm_linear_ops = {
+ .name = "linear",
+ .asprint = dm_linear_asprint,
+ .create = dm_linear_create,
+ .destroy = dm_linear_destroy,
+ .read = dm_linear_read,
+ .write = dm_linear_write,
+};
+
+static int __init dm_linear_init(void)
+{
+ return dm_target_register(&dm_linear_ops);
+}
+device_initcall(dm_linear_init);
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/5] test: self: dm: Add test of linear target
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
` (2 preceding siblings ...)
2025-08-28 15:05 ` [PATCH 3/5] dm: linear: Add linear target Tobias Waldekranz
@ 2025-08-28 15:05 ` Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 5/5] commands: dmsetup: Basic command set for dm device management Tobias Waldekranz
2025-08-29 8:29 ` [PATCH 0/5] dm: Initial work on a device mapper Sascha Hauer
5 siblings, 0 replies; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
Verify that the 'linear' target works as expected.
Do this by (1) creating a dm device with a couple of linear mappings
to two underlying ramdisks, and then (2) verify that data is returned
in the expected order when reading it through the dm device.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
test/self/Kconfig | 7 ++
test/self/Makefile | 1 +
test/self/dm.c | 159 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 167 insertions(+)
create mode 100644 test/self/dm.c
diff --git a/test/self/Kconfig b/test/self/Kconfig
index 4c43dfe394..a6dfd5f9ae 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -125,4 +125,11 @@ config SELFTEST_TLV
select BASE64
select BOARD_LXA
+config SELFTEST_DM
+ bool "Device mapper selftest"
+ depends on DM_BLK
+ select RAMDISK_BLK
+ help
+ Tests the available device mapper targets
+
endif
diff --git a/test/self/Makefile b/test/self/Makefile
index 9aa8aab78b..3d74bf9e98 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o
obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
obj-$(CONFIG_SELFTEST_IDR) += idr.o
obj-$(CONFIG_SELFTEST_TLV) += tlv.o tlv.dtb.o
+obj-$(CONFIG_SELFTEST_DM) += dm.o
ifdef REGENERATE_KEYTOC
diff --git a/test/self/dm.c b/test/self/dm.c
new file mode 100644
index 0000000000..e2add03a48
--- /dev/null
+++ b/test/self/dm.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <disks.h>
+#include <dirent.h>
+#include <libfile.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <bselftest.h>
+#include <linux/sizes.h>
+#include <ramdisk.h>
+#include <dm.h>
+#include <driver.h>
+#include <block.h>
+
+BSELFTEST_GLOBALS();
+
+struct rdctx {
+ char mem[16][SECTOR_SIZE];
+ struct ramdisk *rd;
+ const char *name;
+};
+
+static struct rdctx rdctx[2];
+
+static int rd_create(void)
+{
+ struct block_device *blk;
+ struct rdctx *ctx;
+ char base;
+ int i, s;
+
+
+ for (i = 0, ctx = rdctx; i < 2; i++, ctx++) {
+ /* In case tests are run multiple times */
+ memset(ctx->mem, '\0', sizeof(ctx->mem));
+
+ /* Add an identifying mark ('a'-'p' and 'A'-'P') at
+ * the start and end of every sector in both disks, so
+ * that we have something to compare against when we
+ * read them back through the DM device.
+ */
+ base = i ? 'A' : 'a';
+ for (s = 0; s < 16; s++) {
+ ctx->mem[s][0] = base + s;
+ ctx->mem[s][SECTOR_SIZE - 1] = base + s;
+ }
+
+ ctx->rd = ramdisk_init(SECTOR_SIZE);
+ if (!ctx->rd) {
+ failed_tests++;
+ pr_err("Could not create ramdisk\n");
+ return 1;
+ }
+
+ ramdisk_setup_rw(ctx->rd, ctx->mem, sizeof(ctx->mem));
+ blk = ramdisk_get_block_device(ctx->rd);
+ ctx->name = cdev_name(&blk->cdev);
+ }
+
+ return 0;
+}
+
+static void rd_destroy(void)
+{
+ ramdisk_free(rdctx[0].rd);
+ ramdisk_free(rdctx[1].rd);
+}
+
+static void verify_read(const char *pattern, const char *buf)
+{
+ off_t first, last;
+ int s, len;
+
+ for (s = 0, len = strlen(pattern); s < len; s++) {
+ first = s << SECTOR_SHIFT;
+ last = first + SECTOR_SIZE - 1;
+
+ if (buf[first] != pattern[s]) {
+ failed_tests++;
+ pr_err("Expected '%c' at beginning of sector %d, read '%c'\n",
+ pattern[s], s, buf[first]);
+ return;
+ }
+
+ if (buf[last] != pattern[s]) {
+ failed_tests++;
+ pr_err("Expected '%c' at end of sector %d, read '%c'\n",
+ pattern[s], s, buf[last]);
+ return;
+ }
+ }
+}
+
+static void test_dm_linear(void)
+{
+ static const char pattern[] = "DEFaghijklmnopNOP";
+ const size_t dmsize = (sizeof(pattern) - 1) * SECTOR_SIZE;
+ struct dm_device *dm;
+ struct cdev *cdev;
+ char *buf, *table;
+
+ total_tests++;
+
+ if (!IS_ENABLED(CONFIG_DM_BLK_LINEAR)) {
+ pr_info("skipping dm-linear test: disabled in config\n");
+ skipped_tests++;
+ return;
+ }
+
+ if (rd_create())
+ return;
+
+ table = xasprintf(" 0 3 linear /dev/%s 3\n" /* "DEF" */
+ " 3 1 linear /dev/%s 0\n" /* "a" */
+ " 4 10 linear /dev/%s 6\n" /* "ghijklmnop" */
+ "14 3 linear /dev/%s 13\n" /* "NOP" */,
+ rdctx[1].name,
+ rdctx[0].name,
+ rdctx[0].name,
+ rdctx[1].name);
+
+ dm = dm_create("dmtest", table);
+ free(table);
+
+ if (IS_ERR_OR_NULL(dm)) {
+ failed_tests++;
+ pr_err("Could not create dm device\n");
+ goto out_destroy;
+ }
+
+ cdev = cdev_by_name("dmtest");
+ if (!cdev) {
+ failed_tests++;
+ pr_err("Could not find dm device\n");
+ goto out_destroy;
+ }
+
+ buf = xmalloc(dmsize);
+
+ if (cdev_read(cdev, buf, dmsize, 0, 0) < dmsize) {
+ failed_tests++;
+ pr_err("Could not read dm device\n");
+ goto out_free_buf;
+ }
+
+ verify_read(pattern, buf);
+
+out_free_buf:
+ free(buf);
+out_destroy:
+ dm_destroy(dm);
+ rd_destroy();
+}
+bselftest(core, test_dm_linear);
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 5/5] commands: dmsetup: Basic command set for dm device management
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
` (3 preceding siblings ...)
2025-08-28 15:05 ` [PATCH 4/5] test: self: dm: Add test of " Tobias Waldekranz
@ 2025-08-28 15:05 ` Tobias Waldekranz
2025-08-29 8:29 ` [PATCH 0/5] dm: Initial work on a device mapper Sascha Hauer
5 siblings, 0 replies; 8+ messages in thread
From: Tobias Waldekranz @ 2025-08-28 15:05 UTC (permalink / raw)
To: barebox
Modeled on dmsetup(8) from LVM2.
This lets the user create/remove arbitrary dm devices using the same
table file format used in Linux, and dump information about currently
configured devices.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
commands/Kconfig | 14 +++++
commands/Makefile | 1 +
commands/dmsetup.c | 145 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+)
create mode 100644 commands/dmsetup.c
diff --git a/commands/Kconfig b/commands/Kconfig
index 1626912c30..219f626c3e 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -732,6 +732,20 @@ config CMD_PARTED
unit <unit> change display/input units
refresh refresh a partition table
+config CMD_DMSETUP
+ tristate
+ depends on DM_BLK
+ prompt "dmsetup"
+ help
+ dmsetup - low level interface to the device mapper
+
+ Compose virtual block devices from a table of mappings from
+ logical block addresses to various data sources, such as
+ linear ranges from other existing devices.
+
+ commands:
+ create <name> <table-file>
+
config CMD_UBI
tristate
default y if MTD_UBI
diff --git a/commands/Makefile b/commands/Makefile
index d9403b18d5..6b010fe30c 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -166,4 +166,5 @@ obj-$(CONFIG_CMD_STACKSMASH) += stacksmash.o
obj-$(CONFIG_CMD_PARTED) += parted.o
obj-$(CONFIG_CMD_EFI_HANDLE_DUMP) += efi_handle_dump.o
obj-$(CONFIG_CMD_HOST) += host.o
+obj-$(CONFIG_CMD_DMSETUP) += dmsetup.o
UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/dmsetup.c b/commands/dmsetup.c
new file mode 100644
index 0000000000..722aa6cbf0
--- /dev/null
+++ b/commands/dmsetup.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2025 Tobias Waldekranz <tobias@waldekranz.com>, Wires
+
+#include <command.h>
+#include <common.h>
+#include <dm.h>
+#include <libfile.h>
+
+static struct dm_device *dmsetup_find(const char *name)
+{
+ struct dm_device *dm;
+
+ dm = dm_find_by_name(name);
+ if (IS_ERR_OR_NULL(dm)) {
+ pr_err("Found no device named \"%s\"\n", name);
+ return NULL;
+ }
+
+ return dm;
+}
+
+static int dmsetup_remove(int argc, char *argv[])
+{
+ struct dm_device *dm;
+
+ if (argc != 1)
+ return COMMAND_ERROR_USAGE;
+
+ dm = dmsetup_find(argv[0]);
+ if (!dm)
+ return COMMAND_ERROR;
+
+ dm_destroy(dm);
+
+ pr_info("Removed %s\n", argv[0]);
+ return COMMAND_SUCCESS;
+}
+
+static int dmsetup_info_one(struct dm_device *dm, void *_n)
+{
+ int *n = _n;
+ char *str;
+
+ if (*n)
+ pr_info("\n");
+
+ str = dm_asprint(dm);
+ pr_info("%s", str);
+ free(str);
+ (*n)++;
+ return 0;
+}
+
+static int dmsetup_info(int argc, char *argv[])
+{
+ struct dm_device *dm;
+ int n = 0;
+
+ if (argc == 0) {
+ dm_foreach(dmsetup_info_one, &n);
+ if (n == 0)
+ pr_info("No devices found\n");
+ return COMMAND_SUCCESS;
+ }
+
+ if (argc != 1)
+ return COMMAND_ERROR_USAGE;
+
+ dm = dmsetup_find(argv[0]);
+ if (!dm)
+ return COMMAND_ERROR;
+
+ dmsetup_info_one(dm, &n);
+ return COMMAND_SUCCESS;
+}
+
+
+static int dmsetup_create(int argc, char *argv[])
+{
+ struct dm_device *dm;
+ char *table;
+
+ if (argc != 2)
+ return COMMAND_ERROR_USAGE;
+
+ table = read_file(argv[1], NULL);
+ if (!table) {
+ pr_err("Failed to read table from %s: %m\n", argv[1]);
+ return COMMAND_ERROR;
+ }
+
+ dm = dm_create(argv[0], table);
+ free(table);
+ if (IS_ERR_OR_NULL(dm)) {
+ pr_err("Failed to create %s: %s\n",
+ argv[0], strerror(-PTR_ERR(dm)));
+ return COMMAND_ERROR;
+ }
+
+ pr_info("Created %s\n", argv[0]);
+ return COMMAND_SUCCESS;
+}
+
+static int do_dmsetup(int argc, char *argv[])
+{
+ const char *cmd;
+
+ if (argc < 2)
+ return COMMAND_ERROR_USAGE;
+
+ cmd = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ if (!strcmp(cmd, "create"))
+ return dmsetup_create(argc, argv);
+ else if (!strcmp(cmd, "info"))
+ return dmsetup_info(argc, argv);
+ else if (!strcmp(cmd, "remove"))
+ return dmsetup_remove(argc, argv);
+
+ printf("Unknown command: %s\n", cmd);
+ return -EINVAL;
+}
+
+BAREBOX_CMD_HELP_START(dmsetup)
+BAREBOX_CMD_HELP_TEXT("dmsetup - low level interface to the device mapper")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Compose virtual block devices from a table of mappings from")
+BAREBOX_CMD_HELP_TEXT("logical block addresses to various data sources, such as")
+BAREBOX_CMD_HELP_TEXT("linear ranges from other existing devices.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("commands:")
+BAREBOX_CMD_HELP_OPT("create <name> <table-file>", "Create new device")
+BAREBOX_CMD_HELP_OPT("info [<name>]", "Show device information")
+BAREBOX_CMD_HELP_OPT("remove <name>", "Remove device")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(dmsetup)
+ .cmd = do_dmsetup,
+ BAREBOX_CMD_DESC("low level interface to the device mapper")
+ BAREBOX_CMD_OPTS("<command> [args...]")
+ BAREBOX_CMD_GROUP(CMD_GRP_PART)
+ BAREBOX_CMD_HELP(cmd_dmsetup_help)
+BAREBOX_CMD_END
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/5] dm: linear: Add linear target
2025-08-28 15:05 ` [PATCH 3/5] dm: linear: Add linear target Tobias Waldekranz
@ 2025-08-29 5:56 ` Ahmad Fatoum
0 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-08-29 5:56 UTC (permalink / raw)
To: Tobias Waldekranz, barebox
Hi,
On 28.08.25 17:05, Tobias Waldekranz wrote:
> This target allows you to map in a region of another device as a
> contiguous range of blocks of a dm device.
>
> This is the basic building block used by LVM to stitch together
> logical volumes from one or more ranges of blocks from one or more
> physical disks.
I have yet to read through the patch series, but a thought coming to mind:
We have storage-by-alias driver, which creates new cdevs that 1:1 map
an existing cdev, but given that they are separate, they allow a different
fixed partitioning. This was originally useful when running under EFI to
make partitions known to barebox that are outside of a MBR and are not
described in the device tree. More recently, they provide the
barebox,bootsource driver which allows referencing in the DT the bootsource
in a generic manner.
I am wondering if this at some future point of time could be addressed
using the DM linear API. What comes to mind is that instead of only dmsetup,
DT nodes would also be able to create at least a linear dm target and
then we can drop most of code in the storage-by-alias driver.
Exciting times ahead!
Cheers,
Ahmad
>
> Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
> ---
> drivers/block/dm/Kconfig | 7 ++
> drivers/block/dm/Makefile | 1 +
> drivers/block/dm/dm-linear.c | 123 +++++++++++++++++++++++++++++++++++
> 3 files changed, 131 insertions(+)
> create mode 100644 drivers/block/dm/dm-linear.c
>
> diff --git a/drivers/block/dm/Kconfig b/drivers/block/dm/Kconfig
> index f64c69e03d..e763e530d3 100644
> --- a/drivers/block/dm/Kconfig
> +++ b/drivers/block/dm/Kconfig
> @@ -5,3 +5,10 @@ menuconfig DM_BLK
> other data sources. Modeled after, and intended to be
> compatible with, the Linux kernel's device mapper subsystem.
>
> +config DM_BLK_LINEAR
> + bool "Linear target"
> + depends on DM_BLK
> + help
> + Maps a contiguous region of an existing device into a dm
> + device.
> +
> diff --git a/drivers/block/dm/Makefile b/drivers/block/dm/Makefile
> index 9c045087c0..f52d19f9c4 100644
> --- a/drivers/block/dm/Makefile
> +++ b/drivers/block/dm/Makefile
> @@ -1,2 +1,3 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-$(CONFIG_DM_BLK) += dm-core.o
> +obj-$(CONFIG_DM_BLK_LINEAR) += dm-linear.o
> diff --git a/drivers/block/dm/dm-linear.c b/drivers/block/dm/dm-linear.c
> new file mode 100644
> index 0000000000..d3415814cb
> --- /dev/null
> +++ b/drivers/block/dm/dm-linear.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// SPDX-FileCopyrightText: © 2025 Tobias Waldekranz <tobias@waldekranz.com>, Wires
> +
> +#include <block.h>
> +#include <common.h>
> +#include <disks.h>
> +#include <fcntl.h>
> +
> +#include "dm-target.h"
> +
> +struct dm_linear {
> + struct cdev *cdev;
> + loff_t offset;
> +};
> +
> +static int dm_linear_read(struct dm_target *ti, void *buf,
> + sector_t block, blkcnt_t num_blocks)
> +{
> + struct dm_linear *l = ti->private;
> +
> + block <<= SECTOR_SHIFT;
> + num_blocks <<= SECTOR_SHIFT;
> +
> + if (cdev_read(l->cdev, buf, num_blocks, l->offset + block, 0) < num_blocks)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int dm_linear_write(struct dm_target *ti, const void *buf,
> + sector_t block, blkcnt_t num_blocks)
> +{
> + struct dm_linear *l = ti->private;
> +
> + block <<= SECTOR_SHIFT;
> + num_blocks <<= SECTOR_SHIFT;
> +
> + if (cdev_write(l->cdev, buf, num_blocks, l->offset + block, 0) < num_blocks)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int dm_linear_create(struct dm_target *ti, unsigned int argc, char **argv)
> +{
> + struct dm_linear *l;
> + int err;
> +
> + if (argc != 2) {
> + dm_target_err(ti, "Expected 2 arguments (\"<dev> <offset>\"), got %u\n",
> + argc);
> + err = -EINVAL;
> + goto err;
> + }
> +
> + l = xzalloc(sizeof(*l));
> + ti->private = l;
> +
> + if (kstrtoull(argv[1], 0, &l->offset)) {
> + dm_target_err(ti, "Invalid offset: \"%s\"\n", argv[1]);
> + err = -EINVAL;
> + goto err_free;
> + }
> + l->offset <<= SECTOR_SHIFT;
> +
> + l->cdev = cdev_open_by_path_name(argv[0], O_RDWR);
> + if (!l->cdev) {
> + dm_target_err(ti, "Cannot open device %s: %m\n", argv[0]);
> + err = -ENODEV;
> + goto err_free;
> + }
> +
> + l->cdev = cdev_readlink(l->cdev);
> +
> + if ((ti->size << SECTOR_SHIFT) > (l->cdev->size - l->offset)) {
> + dm_target_err(ti, "%s is too small to map %llu blocks at %llu, %llu available\n",
> + argv[0], ti->size, l->offset >> SECTOR_SHIFT,
> + (l->cdev->size - l->offset) >> SECTOR_SHIFT);
> + err = -ENOSPC;
> + goto err_close;
> + }
> +
> + return 0;
> +
> +err_close:
> + cdev_close(l->cdev);
> +err_free:
> + free(l);
> +err:
> + return err;
> +}
> +
> +static int dm_linear_destroy(struct dm_target *ti)
> +{
> + struct dm_linear *l = ti->private;
> +
> + cdev_close(l->cdev);
> + free(l);
> + return 0;
> +}
> +
> +static char *dm_linear_asprint(struct dm_target *ti)
> +{
> + struct dm_linear *l = ti->private;
> +
> + return xasprintf("dev:%s offset:%llu",
> + cdev_name(l->cdev), l->offset >> SECTOR_SHIFT);
> +}
> +
> +static struct dm_target_ops dm_linear_ops = {
> + .name = "linear",
> + .asprint = dm_linear_asprint,
> + .create = dm_linear_create,
> + .destroy = dm_linear_destroy,
> + .read = dm_linear_read,
> + .write = dm_linear_write,
> +};
> +
> +static int __init dm_linear_init(void)
> +{
> + return dm_target_register(&dm_linear_ops);
> +}
> +device_initcall(dm_linear_init);
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/5] dm: Initial work on a device mapper
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
` (4 preceding siblings ...)
2025-08-28 15:05 ` [PATCH 5/5] commands: dmsetup: Basic command set for dm device management Tobias Waldekranz
@ 2025-08-29 8:29 ` Sascha Hauer
5 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2025-08-29 8:29 UTC (permalink / raw)
To: Tobias Waldekranz; +Cc: barebox
Hi Tobias,
On Thu, Aug 28, 2025 at 05:05:25PM +0200, Tobias Waldekranz wrote:
> Start work on adding a device mapper that is compatible with the
> corresponding subsystem in Linux.
>
> This is the foundation of several higher level abstractions, for
> example:
>
> - LVM: Linux Volume manager. Dynamically allocates logical volumes
> from one or more storage devices, manages RAID arrays, etc.
>
> - LUKS: Linux Unified Key Setup. Transparent disk
> encryption/decryption.
>
> - dm-verity: Transparent integrity checking of block devices.
That's great stuff. We are also interested especially in dm-verity as
that would allow us to get rid of separate partitions for the Kernel
image and maybe even the FIT image format we currently use whenever we
need signed Kernel images.
>
> This is part of an exploratory project for investigating how we could
> boot Infix[1] in a more platform-independent way. I.e., my intention
> is to eventually add support for some of the features mentioned above,
> assuming we don't hit any major road blocks. The rest of this letter
> just gives context for how we got here and where we would like to take
> Barebox. If that is not interesting, feel free to stop reading here :)
>
> Our idea is to relegate U-Boot to serve only as a UEFI firmware on the
> platforms where we can't escape it, and then do most of our boot logic
> in Barebox instead. Primarily we want to do this for two reasons:
>
> 1. Being able to ship barebox as an EFI app means we can use the same
> boot logic on x86 machines as we to on everything else.
>
> 2. Barebox is a much higher quality code base to work in than
> U-Boot. I'm sorry, but it just is.
>
> Barebox would thus take the place occupied by systemd-boot in many
> distro setups. So why not go with systemd-boot?
>
> 1. Infix does not run systemd as PID 1, so reusing their bootloader is
> awkward.
>
> 2. Infix ships as a single immutable filesystem image, including
> kernel, DTBs, etc. So we want to extract these files from the
> filesystem before booting the kernel. This is not supported by
> systemd-boot, AFAIK - all boot files must live on the ESP.
>
> 3. We would like to manage our devices' non-volatile storage with LVM,
> and not be bound to a fixed partition table. This will give us more
> flexibility in growing our image, efficiently having images of
> varying sizes installed, etc.
>
> Therefore, our plan is (roughly):
>
> 1. Add dm-verity support
> 2. Add dm-verity root-hash-signature verification support
>
> With that, we can securely extract kernel+DTB from our filesystem
> without having to sign them individually.
Ok, it seems we have the same goals ;)
>
> 3. Add basic LVM support, no RAID or anything, just basic (linear)
> logical volumes.
>
> This will allow us to install multiple versions of Infix on individual
> logical volumes, which Barebox can then find and boot from.
>
> 4. Add high-level helpers for working with DPS disks and DDI images.
>
> I really like the Linux Userspace API Group's thinking around
> Discoverable Partitions Specification (DPS) and Discoverable Disk
> Images (DDI). I think it would be great if Barebox had knowledge about
> these patterns, and could automatically set up the dm-verity
> configuration for a partition when available, for example.
I haven't looked into this yet, but it sounds good. It's a good thing
when barebox can just do the right thing without much configuration.
>
> My hope is that this plan sparks some ideas and reflections. If so, I
> would love to hear them. If not, sorry for the wall of text :)
Well I am very open for adding DM and dm-verity support to barebox. We
would likely have done it anyway at some point, but that could have
taken some time.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-08-29 9:07 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-28 15:05 [PATCH 0/5] dm: Initial work on a device mapper Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 1/5] string: add strtok/strtokv Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 2/5] dm: Add initial device mapper infrastructure Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 3/5] dm: linear: Add linear target Tobias Waldekranz
2025-08-29 5:56 ` Ahmad Fatoum
2025-08-28 15:05 ` [PATCH 4/5] test: self: dm: Add test of " Tobias Waldekranz
2025-08-28 15:05 ` [PATCH 5/5] commands: dmsetup: Basic command set for dm device management Tobias Waldekranz
2025-08-29 8:29 ` [PATCH 0/5] dm: Initial work on a device mapper Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox