* [PATCH 00/11] firmware: qemu_fw_cfg: implement file system
@ 2025-03-13 10:17 Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 01/11] video: ramfb: fix frame buffer screen size Ahmad Fatoum
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 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
Ahmad Fatoum (11):
video: ramfb: fix frame buffer screen size
firmware: qemu_fw_cfg: drop duplicate definitions
firmware: qemu_fw_cfg: add support for seeking
firmware: qemu_fw_cfg: rename from /dev/fw_cfg0 to /dev/fw_cfg
fs: add qemu_fw_cfg file system
firmware: qemu_fw_cfg: register at device initcall level
video: ramfb: use new qemu fw_cfg FS
libfile: give copy_file a flags parameter
libfile: pass copy_file flags through copy_recursive
libfile: add support for not clobbering files in copy_file
fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg
commands/Kconfig | 4 +-
commands/cp.c | 18 +-
commands/defaultenv.c | 2 +-
commands/tftp.c | 2 +-
common/fastboot.c | 2 +-
conftest.py | 22 ++
defaultenv/defaultenv.c | 22 ++
drivers/firmware/qemu_fw_cfg.c | 63 +++--
drivers/video/Kconfig | 2 +-
drivers/video/ramfb.c | 116 ++++------
fs/Kconfig | 7 +
fs/Makefile | 1 +
fs/qemu_fw_cfg.c | 404 +++++++++++++++++++++++++++++++++
include/envfs.h | 5 +
include/libfile.h | 8 +-
include/string.h | 5 +
lib/libfile.c | 43 +++-
17 files changed, 592 insertions(+), 134 deletions(-)
create mode 100644 fs/qemu_fw_cfg.c
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/11] video: ramfb: fix frame buffer screen size
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 02/11] firmware: qemu_fw_cfg: drop duplicate definitions Ahmad Fatoum
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
struct fb_info::screen_size is in units of bytes, but we allocated 8
times as much so far, because of bits vs bytes confusion.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/video/ramfb.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/video/ramfb.c b/drivers/video/ramfb.c
index 45b75e890c59..5b03d8a9c821 100644
--- a/drivers/video/ramfb.c
+++ b/drivers/video/ramfb.c
@@ -86,7 +86,7 @@ static int ramfb_activate_var(struct fb_info *fbi)
dma_free_coherent(DMA_DEVICE_BROKEN,
fbi->screen_base, ramfb->screen_dma, fbi->screen_size);
- fbi->screen_size = fbi->xres * fbi->yres * fbi->bits_per_pixel;
+ 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);
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 02/11] firmware: qemu_fw_cfg: drop duplicate definitions
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 01/11] video: ramfb: fix frame buffer screen size Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 03/11] firmware: qemu_fw_cfg: add support for seeking Ahmad Fatoum
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
These same defines are already in <uapi/linux/qemu_fw_cfg.h>, so let's
not duplicate them in the driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/firmware/qemu_fw_cfg.c | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 4a48ad91520b..26770da908ce 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -28,19 +28,6 @@
# define FW_CFG_DMA_OFF 0x10
#endif
-/* fw_cfg DMA commands */
-#define FW_CFG_DMA_CTL_ERROR 0x01
-#define FW_CFG_DMA_CTL_READ 0x02
-#define FW_CFG_DMA_CTL_SKIP 0x04
-#define FW_CFG_DMA_CTL_SELECT 0x08
-#define FW_CFG_DMA_CTL_WRITE 0x10
-
-struct fw_cfg_dma {
- __be32 control;
- __be32 length;
- __be64 address;
-} __packed;
-
/* fw_cfg device i/o register addresses */
struct fw_cfg {
struct resource *iores;
@@ -51,7 +38,7 @@ struct fw_cfg {
loff_t next_read_offset;
u32 sel;
bool is_mmio;
- struct fw_cfg_dma __iomem *acc_virt;
+ struct fw_cfg_dma_access __iomem *acc_virt;
dma_addr_t acc_dma;
};
@@ -172,7 +159,7 @@ static ssize_t fw_cfg_write(struct cdev *cdev, const void *buf, size_t count,
{
struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
struct device *dev = cdev->dev;
- struct fw_cfg_dma __iomem *acc = fw_cfg->acc_virt;
+ struct fw_cfg_dma_access __iomem *acc = fw_cfg->acc_virt;
void *dma_buf;
dma_addr_t mapping;
int ret = 0;
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 03/11] firmware: qemu_fw_cfg: add support for seeking
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 01/11] video: ramfb: fix frame buffer screen size Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 02/11] firmware: qemu_fw_cfg: drop duplicate definitions Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 04/11] firmware: qemu_fw_cfg: rename from /dev/fw_cfg0 to /dev/fw_cfg Ahmad Fatoum
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
Seeks were so far not possible and resulted in an -EINVAL if the accesses
weren't consecutive.
Implement it properly using FW_CFG_DMA_CTL_SKIP.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/firmware/qemu_fw_cfg.c | 44 ++++++++++++++++++++--------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 26770da908ce..33c1617a61e4 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -70,6 +70,7 @@ static int fw_cfg_ioctl(struct cdev *cdev, unsigned int request, void *buf)
switch (request) {
case FW_CFG_SELECT:
fw_cfg->sel = *(u16 *)buf;
+ fw_cfg->next_read_offset = 0;
break;
default:
ret = -ENOTTY;
@@ -87,10 +88,8 @@ static int fw_cfg_ioctl(struct cdev *cdev, unsigned int request, void *buf)
while (*remaining >= sizeof(type)) { \
val = __raw_read##type((fw_cfg)->reg_data); \
*remaining -= sizeof(type); \
- if (*address) { \
- put_unaligned(val, (type *)*address); \
- *address += sizeof(type); \
- } \
+ put_unaligned(val, (type *)*address); \
+ *address += sizeof(type); \
} \
} while(0)
@@ -123,11 +122,27 @@ static void fw_cfg_data_read(struct fw_cfg *fw_cfg, void *address, size_t remain
fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u8);
}
+static void fw_cfg_do_dma(struct fw_cfg *fw_cfg, dma_addr_t address,
+ u32 count, u32 control)
+{
+ struct fw_cfg_dma_access __iomem *acc = fw_cfg->acc_virt;
+
+ acc->address = cpu_to_be64(address);
+ acc->length = cpu_to_be32(count);
+ acc->control = cpu_to_be32(control);
+
+ iowrite64be(fw_cfg->acc_dma, fw_cfg->reg_dma);
+
+ while (ioread32be(&acc->control) & ~FW_CFG_DMA_CTL_ERROR)
+ ;
+}
+
static ssize_t fw_cfg_read(struct cdev *cdev, void *buf, size_t count,
loff_t pos, unsigned long flags)
{
struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
unsigned rdsize = FIELD_GET(O_RWSIZE_MASK, flags);
+ u32 selector = FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16;
if (!pos || pos != fw_cfg->next_read_offset) {
fw_cfg_select(fw_cfg);
@@ -145,8 +160,9 @@ static ssize_t fw_cfg_read(struct cdev *cdev, void *buf, size_t count,
rdsize = 1;
}
- while (pos-- > fw_cfg->next_read_offset)
- fw_cfg_data_read(fw_cfg, NULL, count, rdsize);
+ if (pos != fw_cfg->next_read_offset)
+ fw_cfg_do_dma(fw_cfg, DMA_ERROR_CODE, pos,
+ FW_CFG_DMA_CTL_SKIP | selector);
fw_cfg_data_read(fw_cfg, buf, count, rdsize);
@@ -159,14 +175,10 @@ static ssize_t fw_cfg_write(struct cdev *cdev, const void *buf, size_t count,
{
struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
struct device *dev = cdev->dev;
- struct fw_cfg_dma_access __iomem *acc = fw_cfg->acc_virt;
void *dma_buf;
dma_addr_t mapping;
int ret = 0;
- if (pos != 0)
- return -EINVAL;
-
dma_buf = dma_alloc(count);
if (!dma_buf)
return -ENOMEM;
@@ -181,15 +193,11 @@ static ssize_t fw_cfg_write(struct cdev *cdev, const void *buf, size_t count,
fw_cfg->next_read_offset = 0;
- acc->address = cpu_to_be64(mapping);
- acc->length = cpu_to_be32(count);
- acc->control = cpu_to_be32(FW_CFG_DMA_CTL_WRITE |
- FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16);
+ fw_cfg_do_dma(fw_cfg, DMA_ERROR_CODE, pos, FW_CFG_DMA_CTL_SKIP |
+ FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16);
- iowrite64be(fw_cfg->acc_dma, fw_cfg->reg_dma);
-
- while (ioread32be(&acc->control) & ~FW_CFG_DMA_CTL_ERROR)
- ;
+ fw_cfg_do_dma(fw_cfg, mapping, count, FW_CFG_DMA_CTL_WRITE |
+ FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16);
dma_unmap_single(dev, mapping, count, DMA_FROM_DEVICE);
free_buf:
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 04/11] firmware: qemu_fw_cfg: rename from /dev/fw_cfg0 to /dev/fw_cfg
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (2 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 03/11] firmware: qemu_fw_cfg: add support for seeking Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 05/11] fs: add qemu_fw_cfg file system Ahmad Fatoum
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
There's only a single fw_cfg device, so the 0 suffix doesn't serve any
value. Remove it.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
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 33c1617a61e4..8928cc5bf2aa 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -263,7 +263,7 @@ static int fw_cfg_probe(struct device *dev)
fw_cfg->acc_virt = dma_alloc_coherent(DMA_DEVICE_BROKEN,
sizeof(*fw_cfg->acc_virt), &fw_cfg->acc_dma);
- fw_cfg->cdev.name = basprintf("fw_cfg%d", cdev_find_free_index("fw_cfg"));
+ fw_cfg->cdev.name = "fw_cfg";
fw_cfg->cdev.flags = DEVFS_IS_CHARACTER_DEV;
fw_cfg->cdev.size = 0;
fw_cfg->cdev.ops = &fw_cfg_ops;
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 05/11] fs: add qemu_fw_cfg file system
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (3 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 04/11] firmware: qemu_fw_cfg: rename from /dev/fw_cfg0 to /dev/fw_cfg Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 06/11] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
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.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
fs/Kconfig | 7 +
fs/Makefile | 1 +
fs/qemu_fw_cfg.c | 397 +++++++++++++++++++++++++++++++++++++++++++++++
include/string.h | 5 +
4 files changed, 410 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..7f7350e67e64
--- /dev/null
+++ b/fs/qemu_fw_cfg.c
@@ -0,0 +1,397 @@
+// 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 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;
+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)
+ memcpy(buf, node->buf + f->f_pos, insize);
+ else
+ memcpy(node->buf + f->f_pos, buf, 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_fw_truncate(struct device *dev, struct file *f, loff_t size)
+{
+ return 0;
+}
+
+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,
+ .truncate = fw_cfg_fw_truncate,
+ .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 986ccd83dd73..782fb9425a95 100644
--- a/include/string.h
+++ b/include/string.h
@@ -30,6 +30,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] 12+ messages in thread
* [PATCH 06/11] firmware: qemu_fw_cfg: register at device initcall level
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (4 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 05/11] fs: add qemu_fw_cfg file system Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 07/11] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
Registration at postmmu_initcall is too early for automounts to be
created.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
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] 12+ messages in thread
* [PATCH 07/11] video: ramfb: use new qemu fw_cfg FS
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (5 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 06/11] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 08/11] libfile: give copy_file a flags parameter Ahmad Fatoum
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
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 <a.fatoum@pengutronix.de>
---
drivers/video/Kconfig | 2 +-
drivers/video/ramfb.c | 114 +++++++++++++++---------------------------
2 files changed, 42 insertions(+), 74 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0539e2d453da..437b6a3ba0f6 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..0a0888fcfbcb 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,75 @@ 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 struct driver ramfb_driver = {
+ .probe = ramfb_probe,
+ .name = "qemu-ramfb",
+};
+
static int ramfb_driver_init(void)
{
struct cdev *cdev;
- int err = 0;
+ struct device *dev;
+ const char *mntpath;
+ int dirfd, fd;
- for_each_cdev(cdev) {
- int fd, ret;
+ platform_driver_register(&ramfb_driver);
- if (!strstarts(cdev->name, "fw_cfg"))
- continue;
+ cdev = cdev_by_name("fw_cfg");
+ if (!cdev)
+ return 0;
- fd = cdev_fdopen(cdev, O_RDWR);
- if (fd < 0) {
- err = fd;
- continue;
- }
+ mntpath = cdev_mount(cdev);
+ if (IS_ERR(mntpath))
+ return PTR_ERR(mntpath);
- ret = ramfb_probe(cdev->dev, fd);
- if (ret == 0)
- continue;
- if (ret != -ENODEV && ret != -ENXIO)
- err = ret;
+ dirfd = open(mntpath, O_PATH);
+ if (dirfd < 0)
+ return dirfd;
- close(fd);
- }
+ fd = openat(dirfd, "by_name/etc/ramfb", O_WRONLY);
+ close(dirfd);
+ if (fd == -ENOENT)
+ return 0;
+ if (fd < 0)
+ return fd;
- return err;
+ 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;
}
-device_initcall(ramfb_driver_init);
+late_initcall(ramfb_driver_init);
MODULE_AUTHOR("Adrian Negreanu <adrian.negreanu@nxp.com>");
MODULE_DESCRIPTION("QEMU RamFB driver");
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 08/11] libfile: give copy_file a flags parameter
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (6 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 07/11] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 09/11] libfile: pass copy_file flags through copy_recursive Ahmad Fatoum
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
In preparation for adding an additional flag input to copy_file, let's
turn the current copy_file verbose parameter in a bitmask.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/cp.c | 8 ++++----
commands/tftp.c | 2 +-
common/fastboot.c | 2 +-
include/libfile.h | 5 ++++-
lib/libfile.c | 12 +++++++-----
5 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/commands/cp.c b/commands/cp.c
index 71448f9aff6f..e2f91123ca92 100644
--- a/commands/cp.c
+++ b/commands/cp.c
@@ -25,13 +25,13 @@ static int do_cp(int argc, char *argv[])
int last_is_dir = 0;
int i;
int opt;
- int verbose = 0, recursive = 0;
+ int flags = 0, recursive = 0;
int argc_min;
while ((opt = getopt(argc, argv, "vr")) > 0) {
switch (opt) {
case 'v':
- verbose = 1;
+ flags |= COPY_FILE_VERBOSE;
break;
case 'r':
recursive = 1;
@@ -68,9 +68,9 @@ static int do_cp(int argc, char *argv[])
if (recursive)
ret = copy_recursive(argv[i], dst);
else if (last_is_dir)
- ret = copy_file(argv[i], dst, verbose);
+ ret = copy_file(argv[i], dst, flags);
else
- ret = copy_file(argv[i], argv[argc - 1], verbose);
+ ret = copy_file(argv[i], argv[argc - 1], flags);
free(dst);
if (ret)
diff --git a/commands/tftp.c b/commands/tftp.c
index 5e8d8d17761d..f17b589ee62d 100644
--- a/commands/tftp.c
+++ b/commands/tftp.c
@@ -81,7 +81,7 @@ static int do_tftpb(int argc, char *argv[])
debug("%s: %s -> %s\n", __func__, source, dest);
- ret = copy_file(source, dest, 1);
+ ret = copy_file(source, dest, COPY_FILE_VERBOSE);
umount(TFTP_MOUNT_PATH);
diff --git a/common/fastboot.c b/common/fastboot.c
index 60bef0ec776b..9088b988cb6c 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -741,7 +741,7 @@ static void cb_flash(struct fastboot *fb, const char *cmd)
}
copy:
- ret = copy_file(fb->tempname, filename, 1);
+ ret = copy_file(fb->tempname, filename, COPY_FILE_VERBOSE);
if (ret)
fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
"write partition: %s", strerror(-ret));
diff --git a/include/libfile.h b/include/libfile.h
index bf775b022dc4..a9bef5065d08 100644
--- a/include/libfile.h
+++ b/include/libfile.h
@@ -3,6 +3,7 @@
#define __LIBFILE_H
#include <linux/types.h>
+#include <linux/bits.h>
struct resource;
@@ -26,7 +27,9 @@ int read_file_2(const char *filename, size_t *size, void **outbuf,
int write_file(const char *filename, const void *buf, size_t size);
int write_file_flash(const char *filename, const void *buf, size_t size);
-int copy_file(const char *src, const char *dst, int verbose);
+#define COPY_FILE_VERBOSE BIT(0)
+
+int copy_file(const char *src, const char *dst, unsigned flags);
int copy_recursive(const char *src, const char *dst);
diff --git a/lib/libfile.c b/lib/libfile.c
index 80d4591dd6e5..76a091878ebb 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -432,11 +432,13 @@ EXPORT_SYMBOL(write_file_flash);
* copy_file - Copy a file
* @src: The source filename
* @dst: The destination filename
- * @verbose: if true, show a progression bar
+ * @flags: A bitmask of COPY_FILE_* flags. Possible values:
+ *
+ * COPY_FILE_VERBOSE: show a progression bar
*
* Return: 0 for success or negative error code
*/
-int copy_file(const char *src, const char *dst, int verbose)
+int copy_file(const char *src, const char *dst, unsigned flags)
{
char *rw_buf = NULL;
int srcfd = 0, dstfd = 0;
@@ -488,7 +490,7 @@ int copy_file(const char *src, const char *dst, int verbose)
}
}
- if (verbose)
+ if (flags & COPY_FILE_VERBOSE)
init_progression_bar(srcstat.st_size);
while (1) {
@@ -509,7 +511,7 @@ int copy_file(const char *src, const char *dst, int verbose)
total += r;
- if (verbose) {
+ if (flags & COPY_FILE_VERBOSE) {
if (srcstat.st_size && srcstat.st_size != FILESIZE_MAX)
show_progress(total);
else
@@ -519,7 +521,7 @@ int copy_file(const char *src, const char *dst, int verbose)
ret = 0;
out:
- if (verbose)
+ if (flags & COPY_FILE_VERBOSE)
putchar('\n');
free(rw_buf);
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 09/11] libfile: pass copy_file flags through copy_recursive
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (7 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 08/11] libfile: give copy_file a flags parameter Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 10/11] libfile: add support for not clobbering files in copy_file Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 11/11] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg Ahmad Fatoum
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
copy_recursive calls copy_file internally, so it makes sense that it
should take the same flags that copy_file already takes.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/cp.c | 2 +-
commands/defaultenv.c | 2 +-
include/libfile.h | 2 +-
lib/libfile.c | 14 ++++++++++++--
4 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/commands/cp.c b/commands/cp.c
index e2f91123ca92..5b8d976c3cfe 100644
--- a/commands/cp.c
+++ b/commands/cp.c
@@ -66,7 +66,7 @@ static int do_cp(int argc, char *argv[])
dst = concat_path_file(argv[argc - 1], posix_basename(argv[i]));
if (recursive)
- ret = copy_recursive(argv[i], dst);
+ ret = copy_recursive(argv[i], dst, flags);
else if (last_is_dir)
ret = copy_file(argv[i], dst, flags);
else
diff --git a/commands/defaultenv.c b/commands/defaultenv.c
index c13d9aaac7c0..3d118f20a650 100644
--- a/commands/defaultenv.c
+++ b/commands/defaultenv.c
@@ -54,7 +54,7 @@ static int do_defaultenv(int argc, char *argv[])
if (scrub)
unlink_recursive(to, NULL);
- ret = copy_recursive(from, to);
+ ret = copy_recursive(from, to, 0);
free(from);
free(to);
diff --git a/include/libfile.h b/include/libfile.h
index a9bef5065d08..00a7f86a1477 100644
--- a/include/libfile.h
+++ b/include/libfile.h
@@ -31,7 +31,7 @@ int write_file_flash(const char *filename, const void *buf, size_t size);
int copy_file(const char *src, const char *dst, unsigned flags);
-int copy_recursive(const char *src, const char *dst);
+int copy_recursive(const char *src, const char *dst, unsigned flags);
int compare_file(const char *f1, const char *f2);
diff --git a/lib/libfile.c b/lib/libfile.c
index 76a091878ebb..aaade34a4c34 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -534,7 +534,17 @@ int copy_file(const char *src, const char *dst, unsigned flags)
}
EXPORT_SYMBOL(copy_file);
-int copy_recursive(const char *src, const char *dst)
+/**
+ * copy_recursive - Copy files recursively
+ * @src: The source filename or directory
+ * @dst: The destination filename or directory
+ * @flags: A bitmask of COPY_FILE_* flags. Possible values:
+ *
+ * COPY_FILE_VERBOSE: show a progression bar
+ *
+ * Return: 0 for success or negative error code
+ */
+int copy_recursive(const char *src, const char *dst, unsigned flags)
{
struct stat s;
DIR *dir;
@@ -563,7 +573,7 @@ int copy_recursive(const char *src, const char *dst)
from = basprintf("%s/%s", src, d->d_name);
to = basprintf("%s/%s", dst, d->d_name);
- ret = copy_recursive(from, to);
+ ret = copy_recursive(from, to, flags);
if (ret)
break;
free(from);
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 10/11] libfile: add support for not clobbering files in copy_file
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (8 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 09/11] libfile: pass copy_file flags through copy_recursive Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 11/11] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg Ahmad Fatoum
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
copy_file will always overwrite overwrite an existing file and so will
copy_recursive. This is not always the intended behavior, so add a flag
to control this.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/Kconfig | 4 +++-
commands/cp.c | 8 ++++++--
include/libfile.h | 1 +
lib/libfile.c | 17 ++++++++++++-----
4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/commands/Kconfig b/commands/Kconfig
index fe459fa862f5..0d60566ba9c6 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -921,11 +921,13 @@ config CMD_CP
help
Copy files
- Usage: cp [-v] SRC DEST
+ Usage: cp [-rnv] SRC DEST
Copy file from SRC to DEST.
Options:
+ -r recursive
+ -n do not overwrite an existing file
-v verbose
config CMD_CMP
diff --git a/commands/cp.c b/commands/cp.c
index 5b8d976c3cfe..dc38cbab889d 100644
--- a/commands/cp.c
+++ b/commands/cp.c
@@ -28,11 +28,14 @@ static int do_cp(int argc, char *argv[])
int flags = 0, recursive = 0;
int argc_min;
- while ((opt = getopt(argc, argv, "vr")) > 0) {
+ while ((opt = getopt(argc, argv, "vnr")) > 0) {
switch (opt) {
case 'v':
flags |= COPY_FILE_VERBOSE;
break;
+ case 'n':
+ flags |= COPY_FILE_NO_OVERWRITE;
+ break;
case 'r':
recursive = 1;
break;
@@ -87,13 +90,14 @@ BAREBOX_CMD_HELP_TEXT("Copy file from SRC to DEST.")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-r", "recursive")
+BAREBOX_CMD_HELP_OPT ("-n", "do not overwrite an existing file")
BAREBOX_CMD_HELP_OPT ("-v", "verbose")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(cp)
.cmd = do_cp,
BAREBOX_CMD_DESC("copy files")
- BAREBOX_CMD_OPTS("[-rv] SRC DEST")
+ BAREBOX_CMD_OPTS("[-rnv] SRC DEST")
BAREBOX_CMD_GROUP(CMD_GRP_FILE)
BAREBOX_CMD_HELP(cmd_cp_help)
BAREBOX_CMD_END
diff --git a/include/libfile.h b/include/libfile.h
index 00a7f86a1477..74333a2648f8 100644
--- a/include/libfile.h
+++ b/include/libfile.h
@@ -28,6 +28,7 @@ int write_file(const char *filename, const void *buf, size_t size);
int write_file_flash(const char *filename, const void *buf, size_t size);
#define COPY_FILE_VERBOSE BIT(0)
+#define COPY_FILE_NO_OVERWRITE BIT(1)
int copy_file(const char *src, const char *dst, unsigned flags);
diff --git a/lib/libfile.c b/lib/libfile.c
index aaade34a4c34..995531248ea4 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -435,6 +435,7 @@ EXPORT_SYMBOL(write_file_flash);
* @flags: A bitmask of COPY_FILE_* flags. Possible values:
*
* COPY_FILE_VERBOSE: show a progression bar
+ * COPY_FILE_NO_OVERWRITE: don't clobber existing files
*
* Return: 0 for success or negative error code
*/
@@ -467,8 +468,13 @@ int copy_file(const char *src, const char *dst, unsigned flags)
}
/* Set O_TRUNC only if file exists and is a regular file */
- if (!s && S_ISREG(dststat.st_mode))
+ if (!s && S_ISREG(dststat.st_mode)) {
+ if (flags & COPY_FILE_NO_OVERWRITE) {
+ ret = 0;
+ goto out;
+ }
mode |= O_TRUNC;
+ }
dstfd = open(dst, mode);
if (dstfd < 0) {
@@ -498,7 +504,7 @@ int copy_file(const char *src, const char *dst, unsigned flags)
if (r < 0) {
perror("read");
ret = r;
- goto out;
+ goto out_newline;
}
if (!r)
break;
@@ -506,7 +512,7 @@ int copy_file(const char *src, const char *dst, unsigned flags)
ret = write_full(dstfd, rw_buf, r);
if (ret < 0) {
perror("write");
- goto out;
+ goto out_newline;
}
total += r;
@@ -520,10 +526,10 @@ int copy_file(const char *src, const char *dst, unsigned flags)
}
ret = 0;
-out:
+out_newline:
if (flags & COPY_FILE_VERBOSE)
putchar('\n');
-
+out:
free(rw_buf);
if (srcfd > 0)
close(srcfd);
@@ -541,6 +547,7 @@ EXPORT_SYMBOL(copy_file);
* @flags: A bitmask of COPY_FILE_* flags. Possible values:
*
* COPY_FILE_VERBOSE: show a progression bar
+ * COPY_FILE_NO_OVERWRITE: don't clobber existing files
*
* Return: 0 for success or negative error code
*/
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 11/11] fs: qemu_fw_cfg: support populating environment via QEMU fw_cfg
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
` (9 preceding siblings ...)
2025-03-13 10:17 ` [PATCH 10/11] libfile: add support for not clobbering files in copy_file Ahmad Fatoum
@ 2025-03-13 10:17 ` Ahmad Fatoum
10 siblings, 0 replies; 12+ messages in thread
From: Ahmad Fatoum @ 2025-03-13 10:17 UTC (permalink / raw)
To: barebox; +Cc: Adrian Negreanu, Ahmad Fatoum
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 <a.fatoum@pengutronix.de>
---
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 055475eb4756..3585660fbc09 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 7f7350e67e64..ef2a1008ee5d 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>
@@ -363,7 +364,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 a6614c0a15ee..1a194c8bff2f 100644
--- a/include/envfs.h
+++ b/include/envfs.h
@@ -130,12 +130,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] 12+ messages in thread
end of thread, other threads:[~2025-03-13 10:37 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-13 10:17 [PATCH 00/11] firmware: qemu_fw_cfg: implement file system Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 01/11] video: ramfb: fix frame buffer screen size Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 02/11] firmware: qemu_fw_cfg: drop duplicate definitions Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 03/11] firmware: qemu_fw_cfg: add support for seeking Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 04/11] firmware: qemu_fw_cfg: rename from /dev/fw_cfg0 to /dev/fw_cfg Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 05/11] fs: add qemu_fw_cfg file system Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 06/11] firmware: qemu_fw_cfg: register at device initcall level Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 07/11] video: ramfb: use new qemu fw_cfg FS Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 08/11] libfile: give copy_file a flags parameter Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 09/11] libfile: pass copy_file flags through copy_recursive Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 10/11] libfile: add support for not clobbering files in copy_file Ahmad Fatoum
2025-03-13 10:17 ` [PATCH 11/11] 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