* [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system
@ 2025-06-06 7:29 Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 1/4] fs: add qemu_fw_cfg " Ahmad Fatoum
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Ahmad Fatoum @ 2025-06-06 7:29 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu
The character device we currently have is cumbersome to use.
The Linux way to access it at /sys/firmware/qemu_fw_cfg
is much nicer to use, so this series adds support for a similar
FS to barebox.
A very nice result of having file system access to the QEMU variables
in barebox is that we get a trivial way to pass data to barebox.
To make it even more convenient to use, this series enables overriding
individual files in the environment via the new mechanism, e.g.:
pytest --env nv/boot.default=fit --env boot/fit=@boot.sh
v1 -> v2:
- drop already upstream patches
- drop truncate (Sascha)
- make writing files besides "raw" an error (Sascha)
- mount file system unconditionally, so environment can be read
from it
Ahmad Fatoum (4):
fs: add qemu_fw_cfg file system
firmware: qemu_fw_cfg: register at device initcall level
video: ramfb: use new qemu fw_cfg FS
fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg
conftest.py | 22 ++
defaultenv/defaultenv.c | 22 ++
drivers/firmware/qemu_fw_cfg.c | 2 +-
drivers/video/Kconfig | 2 +-
drivers/video/ramfb.c | 98 ++------
fs/Kconfig | 7 +
fs/Makefile | 1 +
fs/qemu_fw_cfg.c | 446 +++++++++++++++++++++++++++++++++
include/envfs.h | 5 +
include/string.h | 5 +
10 files changed, 525 insertions(+), 85 deletions(-)
create mode 100644 fs/qemu_fw_cfg.c
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/4] fs: add qemu_fw_cfg file system
2025-06-06 7:29 [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
@ 2025-06-06 7:29 ` Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 2/4] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Ahmad Fatoum @ 2025-06-06 7:29 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu
From: Ahmad Fatoum <ahmad@a3f.at>
The character device we currently have is cumbersome to use.
The Linux way to access it at /sys/firmware/qemu_fw_cfg
is much nicer to use, so add support for a similar FS to barebox.
Note that there are limitations around writing, writing a file passed
via -fw_cfg doesn't work, but writing the framebuffer does.
Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
fs/Kconfig | 7 +
fs/Makefile | 1 +
fs/qemu_fw_cfg.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++
include/string.h | 5 +
4 files changed, 416 insertions(+)
create mode 100644 fs/qemu_fw_cfg.c
diff --git a/fs/Kconfig b/fs/Kconfig
index 9118d1114daa..01ac3040438d 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -156,4 +156,11 @@ config FS_UBOOTVARFS
This filesystem driver provides access to U-Boot environment
variables.
+config FS_QEMU_FW_CFG
+ bool "QEMU FW CFG interface"
+ select QEMU_FW_CFG
+ help
+ This filesystem driver provides access to the QEMU FW CFG conduit
+ as a file system.
+
endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 6160ef4e1a0b..20a26a16c5ab 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_FS_PSTORE) += pstore/
obj-$(CONFIG_FS_SQUASHFS) += squashfs/
obj-$(CONFIG_FS_RATP) += ratpfs.o
obj-$(CONFIG_FS_UBOOTVARFS) += ubootvarfs.o
+obj-$(CONFIG_FS_QEMU_FW_CFG) += qemu_fw_cfg.o
diff --git a/fs/qemu_fw_cfg.c b/fs/qemu_fw_cfg.c
new file mode 100644
index 000000000000..a21e6fbf637b
--- /dev/null
+++ b/fs/qemu_fw_cfg.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: 2024 Ahmad Fatoum
+
+#define pr_fmt(fmt) "qemu_fw_cfg-fs: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+#include <fs.h>
+#include <string.h>
+#include <libfile.h>
+#include <errno.h>
+#include <linux/stat.h>
+#include <xfuncs.h>
+#include <fcntl.h>
+#include <linux/qemu_fw_cfg.h>
+#include <wchar.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+
+struct fw_cfg_fs_inode {
+ struct inode inode;
+ const char *name;
+ struct list_head sibling;
+ struct list_head children;
+ char *buf;
+};
+
+struct fw_cfg_fs_data {
+ int fd;
+ int next_ino;
+};
+
+static struct fw_cfg_fs_inode *inode_to_node(struct inode *inode)
+{
+ return container_of(inode, struct fw_cfg_fs_inode, inode);
+}
+
+static const char *fw_cfg_fs_get_link(struct dentry *dentry, struct inode *inode)
+{
+ return inode->i_link;
+}
+
+static int fw_cfg_fs_open(struct inode *inode, struct file *file)
+{
+ struct fw_cfg_fs_inode *node = inode_to_node(inode);
+
+ if (node->buf && (file->f_flags & O_ACCMODE) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
+static const struct inode_operations fw_cfg_fs_file_inode_operations;
+static const struct inode_operations fw_cfg_fs_dir_inode_operations;
+static const struct inode_operations fw_cfg_fs_symlink_inode_operations = {
+ .get_link = fw_cfg_fs_get_link,
+};
+static const struct file_operations fw_cfg_fs_file_operations = {
+ .open = fw_cfg_fs_open
+};
+static const struct file_operations fw_cfg_fs_dir_operations;
+
+static struct inode *fw_cfg_fs_get_inode(struct inode *iparent,
+ const char *name)
+{
+ struct fw_cfg_fs_inode *parent = inode_to_node(iparent);
+
+ while (true) {
+ struct fw_cfg_fs_inode *node;
+ char *slash;
+
+ slash = strchrnul(name, '/');
+
+ list_for_each_entry(node, &parent->children, sibling) {
+ size_t namelen = slash - name;
+ if (!strncmp_ptr(name, node->name, namelen) &&
+ !node->name[namelen]) {
+ if (*slash == '\0')
+ return &node->inode;
+ parent = node;
+ goto next;
+ }
+ }
+
+ return NULL;
+
+next:
+ name = slash + 1;
+ }
+}
+
+static struct dentry *fw_cfg_fs_lookup(struct inode *dir,
+ struct dentry *dentry,
+ unsigned int flags)
+{
+ struct inode *inode;
+
+ inode = fw_cfg_fs_get_inode(dir, dentry->name);
+ if (IS_ERR_OR_NULL(inode))
+ return ERR_CAST(inode);
+
+ d_add(dentry, inode);
+
+ return NULL;
+}
+
+static const struct inode_operations fw_cfg_fs_dir_inode_operations = {
+ .lookup = fw_cfg_fs_lookup,
+};
+
+static struct fw_cfg_fs_inode *fw_cfg_fs_node_new(struct super_block *sb,
+ struct fw_cfg_fs_inode *parent,
+ const char *name,
+ ulong select,
+ umode_t mode)
+{
+ struct fw_cfg_fs_inode *node;
+ struct inode *inode;
+
+
+ inode = new_inode(sb);
+ if (!inode)
+ return NULL;
+
+ inode->i_ino = select;
+ inode->i_mode = 0777 | mode;
+ node = inode_to_node(inode);
+ node->name = strdup(name);
+
+ switch (inode->i_mode & S_IFMT) {
+ default:
+ return ERR_PTR(-EINVAL);
+ case S_IFREG:
+ inode->i_op = &fw_cfg_fs_file_inode_operations;
+ inode->i_fop = &fw_cfg_fs_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &fw_cfg_fs_dir_inode_operations;
+ inode->i_fop = &fw_cfg_fs_dir_operations;
+ inc_nlink(inode);
+ break;
+ case S_IFLNK:
+ inode->i_op = &fw_cfg_fs_symlink_inode_operations;
+ break;
+ }
+
+ if (parent)
+ list_add_tail(&node->sibling, &parent->children);
+
+ return node;
+}
+
+#define fw_cfg_fs_node_sprintf(node, args...) \
+do { \
+ node->inode.i_size = asprintf(&node->buf, args);\
+} while (0)
+
+static int fw_cfg_fs_parse(struct super_block *sb)
+{
+ struct fw_cfg_fs_data *data = sb->s_fs_info;
+ struct fw_cfg_fs_inode *root, *by_key, *by_name;
+ __be32 count;
+ int i, ret;
+
+ ioctl(data->fd, FW_CFG_SELECT, &(u16) { FW_CFG_FILE_DIR });
+
+ lseek(data->fd, 0, SEEK_SET);
+
+ ret = read(data->fd, &count, sizeof(count));
+ if (ret < 0)
+ return ret;
+
+ root = inode_to_node(d_inode(sb->s_root));
+
+ by_key = fw_cfg_fs_node_new(sb, root, "by_key", data->next_ino++, S_IFDIR);
+ if (IS_ERR(by_key))
+ return PTR_ERR(by_key);
+
+ by_name = fw_cfg_fs_node_new(sb, root, "by_name", data->next_ino++, S_IFDIR);
+ if (IS_ERR(by_name))
+ return PTR_ERR(by_name);
+
+ for (i = 0; i < be32_to_cpu(count); i++) {
+ struct fw_cfg_fs_inode *parent, *dir, *node;
+ struct fw_cfg_file qfile;
+ char buf[sizeof("65536")];
+ char *context, *name;
+ int ndirs = 0;
+
+ ret = read(data->fd, &qfile, sizeof(qfile));
+ if (ret < 0)
+ break;
+
+ snprintf(buf, sizeof(buf), "%u", be16_to_cpu(qfile.select));
+
+ dir = fw_cfg_fs_node_new(sb, by_key, buf, data->next_ino++, S_IFDIR);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ node = fw_cfg_fs_node_new(sb, dir, "name", data->next_ino++, S_IFREG);
+ fw_cfg_fs_node_sprintf(node, "%s", qfile.name);
+
+ node = fw_cfg_fs_node_new(sb, dir, "size", data->next_ino++, S_IFREG);
+ fw_cfg_fs_node_sprintf(node, "%u", be32_to_cpu(qfile.size));
+
+ node = fw_cfg_fs_node_new(sb, dir, "key", data->next_ino++, S_IFREG);
+ fw_cfg_fs_node_sprintf(node, "%u", be16_to_cpu(qfile.select));
+
+ node = fw_cfg_fs_node_new(sb, dir, "raw", be16_to_cpu(qfile.select), S_IFREG);
+ node->inode.i_size = be32_to_cpu(qfile.size);
+
+ for (const char *s = qfile.name; *s; s++) {
+ if (*s == '/')
+ ndirs++;
+ }
+
+ context = qfile.name;
+ parent = by_name;
+
+ while ((name = strsep(&context, "/"))) {
+ struct fw_cfg_fs_inode *node;
+ mode_t mode;
+
+ list_for_each_entry(node, &parent->children, sibling) {
+ if (streq_ptr(name, node->name)) {
+ parent = node;
+ goto next;
+ }
+ }
+
+ mode = context && *context ? S_IFDIR : S_IFLNK;
+ parent = fw_cfg_fs_node_new(sb, parent, name, data->next_ino++,
+ mode);
+ if (IS_ERR(parent))
+ break;
+ if (mode == S_IFLNK) {
+ char *s = basprintf("%*sby_key/%s/raw",
+ (ndirs + 1) * 3, "", buf);
+
+ parent->inode.i_link = s;
+ while (*s == ' ')
+ s = mempcpy(s, "../", 3);
+ }
+next:
+ ;
+ }
+ }
+
+ return ret >= 0 ? 0 : ret;
+}
+
+static inline unsigned char dt_type(struct inode *inode)
+{
+ return (inode->i_mode >> 12) & 15;
+}
+
+static int fw_cfg_fs_dcache_readdir(struct file *file, struct dir_context *ctx)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode *iparent = d_inode(dentry);
+ struct fw_cfg_fs_inode *node, *parent = inode_to_node(iparent);
+
+ dir_emit_dots(file, ctx);
+
+ list_for_each_entry(node, &parent->children, sibling) {
+ dir_emit(ctx, node->name, strlen(node->name),
+ node->inode.i_ino, dt_type(&node->inode));
+ }
+
+ return 0;
+}
+
+static const struct file_operations fw_cfg_fs_dir_operations = {
+ .iterate = fw_cfg_fs_dcache_readdir,
+};
+
+static struct inode *fw_cfg_fs_alloc_inode(struct super_block *sb)
+{
+ struct fw_cfg_fs_inode *node;
+
+ node = xzalloc(sizeof(*node));
+
+ INIT_LIST_HEAD(&node->children);
+ INIT_LIST_HEAD(&node->sibling);
+
+ return &node->inode;
+}
+
+static void fw_cfg_fs_destroy_inode(struct inode *inode)
+{
+ struct fw_cfg_fs_inode *node = inode_to_node(inode);
+
+ list_del(&node->children);
+ list_del(&node->sibling);
+ free(node->buf);
+ free(node);
+}
+
+static const struct super_operations fw_cfg_fs_ops = {
+ .alloc_inode = fw_cfg_fs_alloc_inode,
+ .destroy_inode = fw_cfg_fs_destroy_inode,
+};
+
+static int fw_cfg_fs_io(struct device *dev, struct file *f, void *buf,
+ size_t insize, bool read)
+{
+ struct inode *inode = f->f_inode;
+ struct fw_cfg_fs_inode *node = inode_to_node(inode);
+ struct fw_cfg_fs_data *data = dev->priv;
+ int fd = data->fd;
+
+ if (node->buf) {
+ if (!read)
+ return -EBADF;
+
+ memcpy(buf, node->buf + f->f_pos, insize);
+ return insize;
+ }
+
+ ioctl(fd, FW_CFG_SELECT, &(u16) { inode->i_ino });
+
+ if (read)
+ return pread(fd, buf, insize, f->f_pos);
+ else
+ return pwrite(fd, buf, insize, f->f_pos);
+}
+
+static int fw_cfg_fs_read(struct device *dev, struct file *f, void *buf,
+ size_t insize)
+{
+ return fw_cfg_fs_io(dev, f, buf, insize, true);
+}
+
+static int fw_cfg_fs_write(struct device *dev, struct file *f, const void *buf,
+ size_t insize)
+{
+ return fw_cfg_fs_io(dev, f, (void *)buf, insize, false);
+}
+
+static int fw_cfg_fs_probe(struct device *dev)
+{
+ struct fw_cfg_fs_inode *node;
+ struct fw_cfg_fs_data *data = xzalloc(sizeof(*data));
+ struct fs_device *fsdev = dev_to_fs_device(dev);
+ struct super_block *sb = &fsdev->sb;
+ int ret;
+
+ dev->priv = data;
+
+ data->next_ino = U16_MAX + 1;
+ data->fd = open(fsdev->backingstore, O_RDWR);
+ if (data->fd < 0) {
+ ret = -errno;
+ goto free_data;
+ }
+
+ sb->s_op = &fw_cfg_fs_ops;
+ node = fw_cfg_fs_node_new(sb, NULL, NULL, data->next_ino++, S_IFDIR);
+ if (IS_ERR(node))
+ return PTR_ERR(node);
+ sb->s_root = d_make_root(&node->inode);
+ sb->s_fs_info = data;
+
+
+ /*
+ * We don't use cdev * directly, but this is needed for
+ * cdev_get_mount_path() to work right
+ */
+ fsdev->cdev = cdev_by_name(devpath_to_name(fsdev->backingstore));
+
+ return fw_cfg_fs_parse(sb);
+free_data:
+ free(data);
+ return ret;
+}
+
+static void fw_cfg_fs_remove(struct device *dev)
+{
+ struct fw_cfg_fs_data *data = dev->priv;
+
+ flush(data->fd);
+ close(data->fd);
+ free(data);
+}
+
+static struct fs_driver fw_cfg_fs_driver = {
+ .read = fw_cfg_fs_read,
+ .write = fw_cfg_fs_write,
+ .type = filetype_qemu_fw_cfg,
+ .drv = {
+ .probe = fw_cfg_fs_probe,
+ .remove = fw_cfg_fs_remove,
+ .name = "qemu_fw_cfg-fs",
+ }
+};
+
+static int qemu_fw_cfg_fs_init(void)
+{
+ return register_fs_driver(&fw_cfg_fs_driver);
+}
+coredevice_initcall(qemu_fw_cfg_fs_init);
diff --git a/include/string.h b/include/string.h
index 16d09fab110f..b54a8fc3f38a 100644
--- a/include/string.h
+++ b/include/string.h
@@ -32,6 +32,11 @@ static inline int strcmp_ptr(const char *a, const char *b)
return a && b ? strcmp(a, b) : compare3(a, b);
}
+static inline int strncmp_ptr(const char *a, const char *b, size_t n)
+{
+ return a && b ? strncmp(a, b, n) : compare3(a, b);
+}
+
static inline bool streq_ptr(const char *a, const char *b)
{
return strcmp_ptr(a, b) == 0;
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/4] firmware: qemu_fw_cfg: register at device initcall level
2025-06-06 7:29 [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 1/4] fs: add qemu_fw_cfg " Ahmad Fatoum
@ 2025-06-06 7:29 ` Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 3/4] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 4/4] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg Ahmad Fatoum
3 siblings, 0 replies; 5+ messages in thread
From: Ahmad Fatoum @ 2025-06-06 7:29 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu
From: Ahmad Fatoum <ahmad@a3f.at>
Registration at postmmu_initcall is too early for automounts to be
created.
Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
drivers/firmware/qemu_fw_cfg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 8928cc5bf2aa..ab34f999ceaa 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -313,4 +313,4 @@ static int qemu_fw_cfg_init(void)
return of_devices_ensure_probed_by_dev_id(qemu_fw_cfg_of_match);
}
-postmmu_initcall(qemu_fw_cfg_init);
+device_initcall(qemu_fw_cfg_init);
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 3/4] video: ramfb: use new qemu fw_cfg FS
2025-06-06 7:29 [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 1/4] fs: add qemu_fw_cfg " Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 2/4] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
@ 2025-06-06 7:29 ` Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 4/4] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg Ahmad Fatoum
3 siblings, 0 replies; 5+ messages in thread
From: Ahmad Fatoum @ 2025-06-06 7:29 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu
From: Ahmad Fatoum <ahmad@a3f.at>
The new qemu_fw_cfg file system can simplify the ramfb driver a great
deal as it's then no longer necessary to enumerate the files in the
driver.
Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
drivers/video/Kconfig | 2 +-
drivers/video/ramfb.c | 98 +++++++------------------------------------
fs/qemu_fw_cfg.c | 36 ++++++++++++++++
3 files changed, 52 insertions(+), 84 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ef19948219f3..86f780ffc417 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -106,7 +106,7 @@ config DRIVER_VIDEO_SIMPLEFB
config DRIVER_VIDEO_RAMFB
bool "QEMU RamFB support"
- select QEMU_FW_CFG
+ select FS_QEMU_FW_CFG
help
Add support for setting up a QEMU RamFB driver.
diff --git a/drivers/video/ramfb.c b/drivers/video/ramfb.c
index 5b03d8a9c821..cc490496175b 100644
--- a/drivers/video/ramfb.c
+++ b/drivers/video/ramfb.c
@@ -19,7 +19,6 @@ struct ramfb {
struct fb_info info;
dma_addr_t screen_dma;
struct fb_videomode mode;
- u16 etcfb_select;
};
struct fw_cfg_etc_ramfb {
@@ -31,37 +30,6 @@ struct fw_cfg_etc_ramfb {
u32 stride;
} __packed;
-static int fw_cfg_find_file(struct device *dev, int fd, const char *filename)
-{
- size_t filename_len = strlen(filename);
- ssize_t ret;
- __be32 count;
- int i;
-
- ioctl(fd, FW_CFG_SELECT, &(u16) { FW_CFG_FILE_DIR });
-
- lseek(fd, 0, SEEK_SET);
-
- ret = read(fd, &count, sizeof(count));
- if (ret < 0)
- return ret;
-
- for (i = 0; i < be32_to_cpu(count); i++) {
- struct fw_cfg_file qfile;
-
- read(fd, &qfile, sizeof(qfile));
-
- dev_dbg(dev, "enumerating file %s\n", qfile.name);
-
- if (memcmp(qfile.name, filename, filename_len))
- continue;
-
- return be16_to_cpu(qfile.select);
- }
-
- return -ENOENT;
-}
-
static void ramfb_populate_modes(struct ramfb *ramfb)
{
struct fb_info *info = &ramfb->info;
@@ -81,14 +49,15 @@ static void ramfb_populate_modes(struct ramfb *ramfb)
static int ramfb_activate_var(struct fb_info *fbi)
{
struct ramfb *ramfb = fbi->priv;
+ struct device *hwdev = fbi->dev.parent->parent;
if (fbi->screen_base)
- dma_free_coherent(DMA_DEVICE_BROKEN,
- fbi->screen_base, ramfb->screen_dma, fbi->screen_size);
+ dma_free_coherent(hwdev, fbi->screen_base, ramfb->screen_dma,
+ fbi->screen_size);
fbi->screen_size = fbi->xres * fbi->yres * fbi->bits_per_pixel / BITS_PER_BYTE;
- fbi->screen_base = dma_alloc_coherent(DMA_DEVICE_BROKEN,
- fbi->screen_size, &ramfb->screen_dma);
+ fbi->screen_base = dma_alloc_coherent(hwdev, fbi->screen_size,
+ &ramfb->screen_dma);
return 0;
}
@@ -107,8 +76,6 @@ static void ramfb_enable(struct fb_info *fbi)
etc_ramfb->height = cpu_to_be32(fbi->yres);
etc_ramfb->stride = cpu_to_be32(fbi->line_length);
- ioctl(ramfb->fd, FW_CFG_SELECT, &ramfb->etcfb_select);
-
pwrite(ramfb->fd, etc_ramfb, sizeof(*etc_ramfb), 0);
dma_free(etc_ramfb);
@@ -119,74 +86,39 @@ static struct fb_ops ramfb_ops = {
.fb_enable = ramfb_enable,
};
-static int ramfb_probe(struct device *parent_dev, int fd)
+static int ramfb_probe(struct device *dev)
{
int ret;
struct ramfb *ramfb;
struct fb_info *fbi;
- ret = -ENODEV;
-
ramfb = xzalloc(sizeof(*ramfb));
- ramfb->fd = fd;
-
- ret = fw_cfg_find_file(parent_dev, fd, "etc/ramfb");
- if (ret < 0) {
- dev_dbg(parent_dev, "ramfb: fw_cfg (etc/ramfb) file not found\n");
- return -ENODEV;
- }
-
- ramfb->etcfb_select = ret;
- dev_dbg(parent_dev, "etc/ramfb file at slot 0x%x\n", ramfb->etcfb_select);
+ ramfb->fd = (int)(uintptr_t)dev->platform_data;
fbi = &ramfb->info;
fbi->priv = ramfb;
fbi->fbops = &ramfb_ops;
- fbi->dev.parent = parent_dev;
+ fbi->dev.parent = dev;
ramfb_populate_modes(ramfb);
ret = register_framebuffer(fbi);
if (ret < 0) {
- dev_err(parent_dev, "Unable to register ramfb: %d\n", ret);
+ dev_err(dev, "Unable to register ramfb: %d\n", ret);
return ret;
}
- dev_info(parent_dev, "ramfb registered\n");
+ dev_info(dev, "ramfb registered\n");
return 0;
}
-static int ramfb_driver_init(void)
-{
- struct cdev *cdev;
- int err = 0;
-
- for_each_cdev(cdev) {
- int fd, ret;
-
- if (!strstarts(cdev->name, "fw_cfg"))
- continue;
-
- fd = cdev_fdopen(cdev, O_RDWR);
- if (fd < 0) {
- err = fd;
- continue;
- }
-
- ret = ramfb_probe(cdev->dev, fd);
- if (ret == 0)
- continue;
- if (ret != -ENODEV && ret != -ENXIO)
- err = ret;
-
- close(fd);
- }
-
- return err;
-}
-device_initcall(ramfb_driver_init);
+static struct driver ramfb_driver = {
+ .probe = ramfb_probe,
+ .name = "qemu-ramfb",
+};
+device_platform_driver(ramfb_driver);
MODULE_AUTHOR("Adrian Negreanu <adrian.negreanu@nxp.com>");
MODULE_DESCRIPTION("QEMU RamFB driver");
diff --git a/fs/qemu_fw_cfg.c b/fs/qemu_fw_cfg.c
index a21e6fbf637b..1aa44fb52c70 100644
--- a/fs/qemu_fw_cfg.c
+++ b/fs/qemu_fw_cfg.c
@@ -401,3 +401,39 @@ static int qemu_fw_cfg_fs_init(void)
return register_fs_driver(&fw_cfg_fs_driver);
}
coredevice_initcall(qemu_fw_cfg_fs_init);
+
+static int qemu_fw_cfg_early_mount(void)
+{
+ struct cdev *cdev;
+ struct device *dev;
+ const char *mntpath;
+ int dirfd, fd;
+
+ cdev = cdev_by_name("fw_cfg");
+ if (!cdev)
+ return 0;
+
+ /*
+ * Trigger a mount, so ramfb device can be detected and
+ * environment can be loaded
+ */
+ mntpath = cdev_mount(cdev);
+ if (IS_ERR(mntpath))
+ return PTR_ERR(mntpath);
+
+ dirfd = open(mntpath, O_PATH | O_DIRECTORY);
+ if (dirfd < 0)
+ return dirfd;
+
+ fd = openat(dirfd, "by_name/etc/ramfb", O_WRONLY);
+ close(dirfd);
+ if (fd >= 0) {
+ dev = device_alloc("qemu-ramfb", DEVICE_ID_SINGLE);
+ dev->parent = cdev->dev;
+ dev->platform_data = (void *)(uintptr_t)fd;
+ platform_device_register(dev);
+ }
+
+ return 0;
+}
+late_initcall(qemu_fw_cfg_early_mount);
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 4/4] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg
2025-06-06 7:29 [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (2 preceding siblings ...)
2025-06-06 7:29 ` [PATCH v2 3/4] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
@ 2025-06-06 7:29 ` Ahmad Fatoum
3 siblings, 0 replies; 5+ messages in thread
From: Ahmad Fatoum @ 2025-06-06 7:29 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu
QEMU fw_cfg allows passing through user defined key/values, which is
more powerful than what we currently have with sandbox as it allows
passing arbitrary files without having to mess with block devices and
their alignment.
All the added files are already available in the QEMU fw_cfg file system,
but for added convenience, let's map all files under a opt/org.barebox.env
directory to the environment of the running barebox image, thereby
allowing configuring barebox easily from the outside, e.g.
pytest --env nv/boot.default=fit --env boot/fit=@boot.sh
Will boot, after coundown, whatever is in the boot.sh script in the
working directory of QEMU.
Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
conftest.py | 22 ++++++++++++++++++++++
defaultenv/defaultenv.c | 22 ++++++++++++++++++++++
fs/qemu_fw_cfg.c | 9 ++++++++-
include/envfs.h | 5 +++++
4 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/conftest.py b/conftest.py
index 941eddcb72dd..5dfefef24638 100644
--- a/conftest.py
+++ b/conftest.py
@@ -39,6 +39,9 @@ def pytest_configure(config):
os.environ['LG_BUILDDIR'] = os.path.realpath(os.environ['LG_BUILDDIR'])
def pytest_addoption(parser):
+ def assignment(arg):
+ return arg.split('=', 1)
+
parser.addoption('--interactive', action='store_const', const='qemu_interactive',
dest='lg_initial_state',
help=('(for debugging) skip tests and just start Qemu interactively'))
@@ -55,6 +58,9 @@ def pytest_addoption(parser):
parser.addoption('--blk', action='append', dest='qemu_block',
default=[], metavar="FILE",
help=('Pass block device to emulated barebox. Can be specified more than once'))
+ parser.addoption('--env', action='append', dest='qemu_fw_cfg',
+ default=[], metavar="[envpath=]content | [envpath=]@filepath", type=assignment,
+ help=('Pass barebox environment files to barebox. Can be specified more than once'))
parser.addoption('--qemu', dest='qemu_arg', nargs=argparse.REMAINDER, default=[],
help=('Pass all remaining options to QEMU as is'))
@@ -109,6 +115,22 @@ def strategy(request, target, pytestconfig):
else:
pytest.exit("--blk unsupported for target\n", 1)
+ for i, fw_cfg in enumerate(pytestconfig.option.qemu_fw_cfg):
+ if virtio:
+ value = fw_cfg.pop()
+ envpath = fw_cfg.pop() if fw_cfg else f"data/fw_cfg{i}"
+
+ if value.startswith('@'):
+ source = f"file='{value[1:]}'"
+ else:
+ source = f"string='{value}'"
+
+ strategy.append_qemu_args(
+ '-fw_cfg', f'name=opt/org.barebox.env/{envpath},{source}'
+ )
+ else:
+ pytest.exit("--env unsupported for target\n", 1)
+
for arg in pytestconfig.option.qemu_arg:
strategy.append_qemu_args(arg)
diff --git a/defaultenv/defaultenv.c b/defaultenv/defaultenv.c
index be04ae37c17c..2567653751cb 100644
--- a/defaultenv/defaultenv.c
+++ b/defaultenv/defaultenv.c
@@ -18,6 +18,7 @@
#include <uncompress.h>
#include <malloc.h>
#include <init.h>
+#include <libfile.h>
#include <asm/unaligned.h>
#include "barebox_default_env.h"
@@ -25,6 +26,7 @@ static LIST_HEAD(defaultenv_list);
struct defaultenv {
struct list_head list;
+ const char *srcdir;
const char *name;
void *buf;
size_t size;
@@ -93,6 +95,19 @@ void defaultenv_append(void *buf, unsigned int size, const char *name)
list_add_tail(&df->list, &defaultenv_list);
}
+void defaultenv_append_runtime_directory(const char *srcdir)
+{
+ struct defaultenv *df;
+
+ defaultenv_add_base();
+
+ df = xzalloc(sizeof(*df));
+ df->srcdir = srcdir;
+ df->name = srcdir;
+
+ list_add_tail(&df->list, &defaultenv_list);
+}
+
static int defaultenv_load_one(struct defaultenv *df, const char *dir,
unsigned flags)
{
@@ -104,6 +119,13 @@ static int defaultenv_load_one(struct defaultenv *df, const char *dir,
pr_debug("loading %s\n", df->name);
+ if (df->srcdir) {
+ int cpflags = 0;
+ if (flags & ENV_FLAG_NO_OVERWRITE)
+ cpflags |= COPY_FILE_NO_OVERWRITE;
+ return copy_recursive(df->srcdir, dir, cpflags);
+ }
+
if (!IS_ENABLED(CONFIG_DEFAULT_COMPRESSION_NONE) &&
ft != filetype_barebox_env) {
size = get_unaligned_le32(df->buf + df->size - 4);
diff --git a/fs/qemu_fw_cfg.c b/fs/qemu_fw_cfg.c
index 1aa44fb52c70..caf541574649 100644
--- a/fs/qemu_fw_cfg.c
+++ b/fs/qemu_fw_cfg.c
@@ -13,6 +13,7 @@
#include <errno.h>
#include <linux/stat.h>
#include <xfuncs.h>
+#include <envfs.h>
#include <fcntl.h>
#include <linux/qemu_fw_cfg.h>
#include <wchar.h>
@@ -370,7 +371,13 @@ static int fw_cfg_fs_probe(struct device *dev)
*/
fsdev->cdev = cdev_by_name(devpath_to_name(fsdev->backingstore));
- return fw_cfg_fs_parse(sb);
+ ret = fw_cfg_fs_parse(sb);
+ if (ret)
+ goto free_data;
+
+ defaultenv_append_runtime_directory("/mnt/fw_cfg/by_name/opt/org.barebox.env");
+
+ return 0;
free_data:
free(data);
return ret;
diff --git a/include/envfs.h b/include/envfs.h
index a74f574722fa..e21f2b52368a 100644
--- a/include/envfs.h
+++ b/include/envfs.h
@@ -128,12 +128,17 @@ static inline const char *of_env_get_device_alias_by_path(const char *of_path)
#ifdef CONFIG_DEFAULT_ENVIRONMENT
void defaultenv_append(void *buf, unsigned int size, const char *name);
+void defaultenv_append_runtime_directory(const char *srcdir);
int defaultenv_load(const char *dir, unsigned flags);
#else
static inline void defaultenv_append(void *buf, unsigned int size, const char *name)
{
}
+static inline void defaultenv_append_runtime_directory(const char *srcdir)
+{
+}
+
static inline int defaultenv_load(const char *dir, unsigned flags)
{
return -ENOSYS;
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-06-06 7:35 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-06 7:29 [PATCH v2 0/4] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 1/4] fs: add qemu_fw_cfg " Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 2/4] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 3/4] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
2025-06-06 7:29 ` [PATCH v2 4/4] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg Ahmad Fatoum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox