In some scenarios it is desirable to package U-Boot with other files into
a single blob. This patch allows to embed a memory disk into the U-Boot
binary. This memory disk can be accessed like any other block
device as 'mem 0'.

Signed-off-by: Heinrich Schuchardt <heinrich.schucha...@canonical.com>
---
 MAINTAINERS                      |   6 ++
 common/board_r.c                 |   4 +
 drivers/Kconfig                  |   2 +
 drivers/Makefile                 |   1 +
 drivers/block/blk-uclass.c       |   2 +
 drivers/memdisk/Kconfig          |  13 ++++
 drivers/memdisk/Makefile         |  11 +++
 drivers/memdisk/memdisk-uclass.c |  22 ++++++
 drivers/memdisk/memdisk.c        | 128 +++++++++++++++++++++++++++++++
 drivers/memdisk/memdisk_file.S   |  17 ++++
 include/asm-generic/sections.h   |   2 +
 include/blk.h                    |   1 +
 include/dm/uclass-id.h           |   1 +
 include/memdisk.h                |  28 +++++++
 14 files changed, 238 insertions(+)
 create mode 100644 drivers/memdisk/Kconfig
 create mode 100644 drivers/memdisk/Makefile
 create mode 100644 drivers/memdisk/memdisk-uclass.c
 create mode 100644 drivers/memdisk/memdisk.c
 create mode 100644 drivers/memdisk/memdisk_file.S
 create mode 100644 include/memdisk.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 34446127d4..be71f8d9b7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -930,6 +930,12 @@ T: git git://github.com/ARM-software/u-boot.git
 F:     drivers/video/mali_dp.c
 F:     drivers/i2c/i2c-versatile.c
 
+MEMDISK
+M:     Heinrich Schuchardt <xypron.g...@gmx.de>
+S:     Supported
+F:     drivers/memdisk/
+F:     include/memdisk.h
+
 MICROBLAZE
 M:     Michal Simek <mon...@monstr.eu>
 S:     Maintained
diff --git a/common/board_r.c b/common/board_r.c
index 8dc87ed2be..f416dbd17e 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -37,6 +37,7 @@
 #include <irq_func.h>
 #include <malloc.h>
 #include <mapmem.h>
+#include <memdisk.h>
 #include <miiphy.h>
 #include <mmc.h>
 #include <mux.h>
@@ -700,6 +701,9 @@ static init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_CMD_ONENAND
        initr_onenand,
 #endif
+#ifdef CONFIG_MEMDISK
+       initr_memdisk,
+#endif
 #ifdef CONFIG_MMC
        initr_mmc,
 #endif
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b26ca8cf70..bf475c25e7 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -56,6 +56,8 @@ source "drivers/led/Kconfig"
 
 source "drivers/mailbox/Kconfig"
 
+source "drivers/memdisk/Kconfig"
+
 source "drivers/memory/Kconfig"
 
 source "drivers/misc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 4e7cf28440..3c2906b9c5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_$(SPL_TPL_)FIRMWARE) +=firmware/
 obj-$(CONFIG_$(SPL_TPL_)I2C) += i2c/
 obj-$(CONFIG_$(SPL_TPL_)INPUT) += input/
 obj-$(CONFIG_$(SPL_TPL_)LED) += led/
+obj-$(CONFIG_$(SPL_TPL_)MEMDISK) += memdisk/
 obj-$(CONFIG_$(SPL_TPL_)MMC) += mmc/
 obj-y += mtd/
 obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux/
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index f1e4a85646..4beec38f71 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -23,6 +23,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
        [IF_TYPE_ATAPI]         = "atapi",
        [IF_TYPE_USB]           = "usb",
        [IF_TYPE_DOC]           = "doc",
+       [IF_TYPE_MEMDISK]       = "mem",
        [IF_TYPE_MMC]           = "mmc",
        [IF_TYPE_SD]            = "sd",
        [IF_TYPE_SATA]          = "sata",
@@ -40,6 +41,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
        [IF_TYPE_ATAPI]         = UCLASS_INVALID,
        [IF_TYPE_USB]           = UCLASS_MASS_STORAGE,
        [IF_TYPE_DOC]           = UCLASS_INVALID,
+       [IF_TYPE_MEMDISK]       = UCLASS_MEMDISK,
        [IF_TYPE_MMC]           = UCLASS_MMC,
        [IF_TYPE_SD]            = UCLASS_INVALID,
        [IF_TYPE_SATA]          = UCLASS_AHCI,
diff --git a/drivers/memdisk/Kconfig b/drivers/memdisk/Kconfig
new file mode 100644
index 0000000000..03f646539c
--- /dev/null
+++ b/drivers/memdisk/Kconfig
@@ -0,0 +1,13 @@
+config MEMDISK
+       bool "Support embedded memory disk"
+       select HAVE_BLOCK_DEVICE
+       help
+        This option allows to embed a memory disk.
+
+config MEMDISK_FILE
+       string "Memory disk file"
+       depends on MEMDISK
+       default "memdisk.img"
+       help
+        File to be embedded as memory disk.
+        It can be accessed as block device 'mem 0'.
diff --git a/drivers/memdisk/Makefile b/drivers/memdisk/Makefile
new file mode 100644
index 0000000000..09d6de22d2
--- /dev/null
+++ b/drivers/memdisk/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_MEMDISK),y)
+
+obj-y += \
+       memdisk.o \
+       memdisk-uclass.o \
+       memdisk_file.o
+
+MEMDISK_FILE := $(subst $\",,$(CONFIG_MEMDISK_FILE))
+$(obj)/memdisk_file.o: $(srctree)/$(MEMDISK_FILE)
+
+endif
diff --git a/drivers/memdisk/memdisk-uclass.c b/drivers/memdisk/memdisk-uclass.c
new file mode 100644
index 0000000000..b7b96f91a4
--- /dev/null
+++ b/drivers/memdisk/memdisk-uclass.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Embedded disk image
+ *
+ * Copyright (c) 2022, Heinrich Schuchardt <xypron.g...@gmx.de>
+ */
+
+#define LOG_CATEGORY UCLASS_MEMDISK
+
+#include <common.h>
+#include <dm.h>
+#include <memdisk.h>
+
+UCLASS_DRIVER(memdsk) = {
+       .id             = UCLASS_MEMDISK,
+       .name           = "memdsk",
+};
+
+U_BOOT_DRIVER(memdsk) = {
+       .name           = "memdsk",
+       .id             = UCLASS_MEMDISK,
+};
diff --git a/drivers/memdisk/memdisk.c b/drivers/memdisk/memdisk.c
new file mode 100644
index 0000000000..2c5521a3b6
--- /dev/null
+++ b/drivers/memdisk/memdisk.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Memory disk image
+ *
+ * Copyright (c) 2022, Heinrich Schuchardt <xypron.g...@gmx.de>
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <malloc.h>
+#include <memdisk.h>
+#include <asm/sections.h>
+
+#define LOG_BLK_SIZE 9
+#define BLK_SIZE (1 << LOG_BLK_SIZE)
+
+/**
+ * initr_memdisk() - Initialize embedded memory disk
+ */
+int initr_memdisk(void)
+{
+       memdisk_create(__memdisk_file_begin,
+                      __memdisk_file_end - __memdisk_file_begin);
+
+       return 0;
+}
+
+/**
+ * mem_bl_read() - read from block device
+ *
+ * @dev:       device
+ * @blknr:     first block to be read
+ * @blkcnt:    number of blocks to read
+ * @buffer:    output buffer
+ * Return:     number of blocks transferred
+ */
+static ulong mem_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
+                        void *buffer)
+{
+       struct memdisk_plat *plat = dev_get_plat(dev);
+       char *start = plat->start;
+
+       if (blknr + blkcnt > ((lbaint_t)plat->size >> LOG_BLK_SIZE))
+               return 0;
+       start += blknr << LOG_BLK_SIZE;
+       memcpy(buffer, start, blkcnt << LOG_BLK_SIZE);
+
+       return blkcnt;
+}
+
+/**
+ * memdisk_create() - create memory disk
+ *
+ * The block device will be read-only.
+ * Write support will require security checks using lmb.
+ *
+ * @start:     start address
+ * @size       size
+ * Return:     0 on success
+ */
+int memdisk_create(void *start, size_t size)
+{
+       struct udevice *dev, *parent = NULL;
+       struct memdisk_plat *plat;
+       char dev_name[20], *strblk, *strdsk;
+       int devnum;
+       int ret;
+
+       devnum = blk_next_free_devnum(IF_TYPE_MEMDISK);
+       snprintf(dev_name, sizeof(dev_name), "memdsk%d", devnum);
+       strdsk = strdup(dev_name);
+       if (!strdsk)
+               return -ENOMEM;
+       /*
+        * This dummy device is only needed due to the broken
+        * blk_get_devnum_by_typename() function which looks at the
+        * parent's uclass instead of the interface type. See
+        * 
https://lore.kernel.org/all/20211023140647.7661-1-heinrich.schucha...@canonical.com/
+        */
+       ret = device_bind_driver(gd->dm_root, "memdsk", strdsk, &parent);
+       if (ret) {
+               free(strdsk);
+               return ret;
+       }
+
+       snprintf(dev_name, sizeof(dev_name), "memblk%d", devnum);
+       strblk = strdup(dev_name);
+       if (!strblk) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       ret = blk_create_device(parent, "memblk", strblk,
+                               IF_TYPE_MEMDISK, -1, BLK_SIZE,
+                               size / BLK_SIZE, &dev);
+       if (ret)
+               goto err;
+
+       plat = dev_get_plat(dev);
+       plat->start = start;
+       plat->size = size;
+
+       ret = blk_probe_or_unbind(dev);
+       if (!ret)
+               return 0;
+
+err:
+       if (parent)
+               device_remove(parent, DM_REMOVE_NORMAL);
+
+       return ret;
+}
+
+/* Block device driver operators */
+static const struct blk_ops mem_blk_ops = {
+       .read   = mem_bl_read,
+};
+
+/* Identify as block device driver */
+U_BOOT_DRIVER(memblk) = {
+       .name           = "memblk",
+       .id             = UCLASS_BLK,
+       .ops            = &mem_blk_ops,
+       .plat_auto      = sizeof(struct memdisk_plat),
+
+};
diff --git a/drivers/memdisk/memdisk_file.S b/drivers/memdisk/memdisk_file.S
new file mode 100644
index 0000000000..b535ea313e
--- /dev/null
+++ b/drivers/memdisk/memdisk_file.S
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Embedded disk image
+ *
+ * Copyright (c) 2022, Heinrich Schuchardt <xypron.g...@gmx.de>
+ */
+
+#include <config.h>
+
+.section .rodata.memdisk.init,"a"
+.balign 16
+.global __memdisk_file_begin
+__memdisk_file_begin:
+.incbin CONFIG_MEMDISK_FILE
+.global __memdisk_file_end
+__memdisk_file_end:
+.balign 16
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index 267f1db73f..e3aa823870 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -27,6 +27,8 @@ extern char __efi_helloworld_begin[];
 extern char __efi_helloworld_end[];
 extern char __efi_var_file_begin[];
 extern char __efi_var_file_end[];
+extern char __memdisk_file_begin[];
+extern char __memdisk_file_end[];
 
 /* Private data used by of-platdata devices/uclasses */
 extern char __priv_data_start[], __priv_data_end[];
diff --git a/include/blk.h b/include/blk.h
index dbe9ae219d..c616ad2e29 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -29,6 +29,7 @@ enum if_type {
        IF_TYPE_ATAPI,
        IF_TYPE_USB,
        IF_TYPE_DOC,
+       IF_TYPE_MEMDISK,
        IF_TYPE_MMC,
        IF_TYPE_SD,
        IF_TYPE_SATA,
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 0e26e1d138..7ef49ec829 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -69,6 +69,7 @@ enum uclass_id {
        UCLASS_LED,             /* Light-emitting diode (LED) */
        UCLASS_LPC,             /* x86 'low pin count' interface */
        UCLASS_MAILBOX,         /* Mailbox controller */
+       UCLASS_MEMDISK,         /* Memory disk */
        UCLASS_MASS_STORAGE,    /* Mass storage device */
        UCLASS_MDIO,            /* MDIO bus */
        UCLASS_MDIO_MUX,        /* MDIO MUX/switch */
diff --git a/include/memdisk.h b/include/memdisk.h
new file mode 100644
index 0000000000..a36ffa77d0
--- /dev/null
+++ b/include/memdisk.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Embedded disk image
+ *
+ * Copyright (c) 2022, Heinrich Schuchardt <xypron.g...@gmx.de>
+ */
+
+/**
+ * initr_memdisk() - Initialize embedded memory disk
+ */
+int initr_memdisk(void);
+
+/**
+ * memdisk_create() - create memory disk
+ *
+ * The block device will be read-only.
+ * Write support will require security checks using lmb.
+ *
+ * @start:     start address
+ * @size       size
+ * Return:     0 on success
+ */
+int memdisk_create(void *start, size_t size);
+
+struct memdisk_plat {
+       char *start;
+       size_t size;
+};
-- 
2.34.1

Reply via email to