mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/6] firmware: qemu_fw_cfg: improve support
@ 2026-01-15 12:06 Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable Ahmad Fatoum
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox

This series adds DMA support to QEMU FW_CFG also on the read size,
allows a completely dma-less PIO mode and adds support for parsing
the QEMU options for: -kernel, -initrd, -uuid and -append.

Ahmad Fatoum (6):
  firmware: qemu_fw_cfg: use wider PIO reads if applicable
  param: support uuid/guid parameter type
  lib: smbios: add support for setting product UUID
  common: boards: qemu: process some standard fw_cfg keys
  firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating
    modes
  ARM: configs: multi: enable QEMU FW_CFG

 arch/arm/configs/multi_v5_v6_defconfig        |   2 +
 arch/arm/configs/multi_v7_defconfig           |   2 +
 arch/arm/configs/multi_v8_defconfig           |   2 +
 common/boards/Kconfig                         |   8 +
 common/boards/Makefile                        |   1 +
 common/boards/qemu/Makefile                   |   4 +
 .../defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg   |  12 +
 common/boards/qemu/fw_cfg.c                   | 208 ++++++++++++
 common/globalvar.c                            |  40 +++
 common/misc.c                                 |  14 +
 drivers/firmware/Kconfig                      |   1 +
 drivers/firmware/qemu_fw_cfg.c                | 297 +++++++++++-------
 include/barebox-info.h                        |   4 +
 include/globalvar.h                           |  12 +
 include/linux/sprintf.h                       |   3 +
 include/param.h                               |  39 +++
 lib/parameter.c                               |  95 ++++++
 lib/smbios.c                                  |  18 +-
 lib/vsprintf.c                                |   2 +-
 19 files changed, 648 insertions(+), 116 deletions(-)
 create mode 100644 common/boards/qemu/Makefile
 create mode 100755 common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
 create mode 100644 common/boards/qemu/fw_cfg.c

-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 2/6] param: support uuid/guid parameter type Ahmad Fatoum
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We have some logic for wider reading register, but it's not used,
because we break in fw_cfg_data_read instead of returning.

The logic is broken though, because it doesn't increment correctly,
so rewrite it to actually work.

This doesn't go out of its way to use the biggest width possible
in all cases, because we are going to add a dedicated DMA mode later,
so we don't need to optimize PIO much.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 drivers/firmware/qemu_fw_cfg.c | 78 +++++++++++-----------------------
 1 file changed, 25 insertions(+), 53 deletions(-)

diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 42bc5879879c..33cc5bc54ce9 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -79,47 +79,29 @@ static int fw_cfg_ioctl(struct cdev *cdev, unsigned int request, void *buf)
 	return 0;
 }
 
-#define __raw_readu64 __raw_readq
-#define __raw_readu32 __raw_readl
-#define __raw_readu16 __raw_readw
-#define __raw_readu8 __raw_readb
-
-#define fw_cfg_data_read_sized(fw_cfg, remaining, address, type) do {	\
-	while (*remaining >= sizeof(type)) {				\
-		val = __raw_read##type((fw_cfg)->reg_data);		\
-		*remaining -= sizeof(type);				\
-		put_unaligned(val, (type *)*address);		\
-		*address += sizeof(type);			\
-	}								\
-} while(0)
-
-static void fw_cfg_data_read(struct fw_cfg *fw_cfg, void *address, size_t remaining,
-			     unsigned rdsize)
+static void reads_n(const void __iomem *src, void *dst,
+		   resource_size_t count, int rwsize)
 {
-
-	u64 val;
-
-	if (fw_cfg->is_mmio) {
-		/*
-		 * This is just a preference. If we can't honour it, we
-		 * fall back to byte-sized copy
-		 */
-		switch(rdsize) {
-		case 8:
+	switch (rwsize) {
+	case 1: readsb(src, dst, count / 1); break;
+	case 2: readsw(src, dst, count / 2); break;
+	case 4: readsl(src, dst, count / 4); break;
 #ifdef CONFIG_64BIT
-			fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u64);
-			break;
+	case 8: readsq(src, dst, count / 8); break;
 #endif
-		case 4:
-			fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u32);
-			break;
-		case 2:
-			fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u16);
-			break;
-		}
 	}
+}
 
-	fw_cfg_data_read_sized(fw_cfg, &remaining, &address, u8);
+static void ins_n(unsigned long src, void *dst,
+		  resource_size_t count, int rwsize)
+{
+	switch (rwsize) {
+	case 1: insb(src, dst, count / 1); break;
+	case 2: insw(src, dst, count / 2); break;
+	case 4: insl(src, dst, count / 4); break;
+	/* No insq, so just do 32-bit accesses */
+	case 8: insl(src, dst, count / 4); break;
+	}
 }
 
 static void fw_cfg_do_dma(struct fw_cfg *fw_cfg, dma_addr_t address,
@@ -141,30 +123,20 @@ 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;
+	unsigned rdsize = FIELD_GET(O_RWSIZE_MASK, flags) ?: 8;
 
 	if (!pos || pos != fw_cfg->next_read_offset) {
 		fw_cfg_select(fw_cfg);
 		fw_cfg->next_read_offset = 0;
 	}
 
-	if (!rdsize) {
-		if (pos % 8 == 0)
-			rdsize = 8;
-		else if (pos % 4 == 0)
-			rdsize = 4;
-		else if (pos % 2 == 0)
-			rdsize = 2;
-		else
-			rdsize = 1;
-	}
+	if (!IS_ALIGNED(pos, rdsize) || !IS_ALIGNED(count, rdsize))
+		rdsize = 1;
 
-	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);
+	if (fw_cfg->is_mmio)
+		reads_n(fw_cfg->reg_data, buf, count, rdsize);
+	else
+		ins_n((ulong)fw_cfg->reg_data, buf, count, rdsize);
 
 	fw_cfg->next_read_offset += count;
 	return count;
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/6] param: support uuid/guid parameter type
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 3/6] lib: smbios: add support for setting product UUID Ahmad Fatoum
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Make UUIDs and GUIDs first class citizens for parameters by adding
dedicated dev_add_param_guid/uuid functions as well as globalvar_
wrappers.

We add different functions for both UUID and GUID to handle the
difference in string representation.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 common/globalvar.c      | 40 +++++++++++++++++
 include/globalvar.h     | 12 ++++++
 include/linux/sprintf.h |  3 ++
 include/param.h         | 39 +++++++++++++++++
 lib/parameter.c         | 95 +++++++++++++++++++++++++++++++++++++++++
 lib/vsprintf.c          |  2 +-
 6 files changed, 190 insertions(+), 1 deletion(-)

diff --git a/common/globalvar.c b/common/globalvar.c
index fcaa15179f68..1fac891ae073 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -672,6 +672,46 @@ int globalvar_add_simple_ip(const char *name, IPaddr_t *ip)
 	return 0;
 }
 
+int globalvar_add_simple_uuid(const char *name, uuid_t *uuid)
+{
+	struct param_d *p;
+	int ret;
+
+	ret = globalvar_remove_unqualified(name);
+	if (ret)
+		return ret;
+
+	p = dev_add_param_uuid(&global_device, name,
+			       NULL, NULL, uuid, 0);
+
+	if (IS_ERR(p))
+		return PTR_ERR(p);
+
+	globalvar_nv_sync(name);
+
+	return 0;
+}
+
+int globalvar_add_simple_guid(const char *name, guid_t *guid)
+{
+	struct param_d *p;
+	int ret;
+
+	ret = globalvar_remove_unqualified(name);
+	if (ret)
+		return ret;
+
+	p = dev_add_param_guid(&global_device, name,
+			       NULL, NULL, guid, 0);
+
+	if (IS_ERR(p))
+		return PTR_ERR(p);
+
+	globalvar_nv_sync(name);
+
+	return 0;
+}
+
 static int globalvar_init(void)
 {
 	const char *endianness;
diff --git a/include/globalvar.h b/include/globalvar.h
index d0a8272588b4..e369616c632a 100644
--- a/include/globalvar.h
+++ b/include/globalvar.h
@@ -5,6 +5,7 @@
 #include <param.h>
 #include <driver.h>
 #include <linux/err.h>
+#include <linux/uuid.h>
 #include <stringlist.h>
 
 extern struct device global_device;
@@ -36,6 +37,8 @@ int globalvar_add_simple_enum(const char *name,	int *value,
 int globalvar_add_simple_bitmask(const char *name, unsigned long *value,
 				 const char * const *names, int max);
 int globalvar_add_simple_ip(const char *name, IPaddr_t *ip);
+int globalvar_add_simple_uuid(const char *name, uuid_t *uuid);
+int globalvar_add_simple_guid(const char *name, guid_t *guid);
 
 int nvvar_load(void);
 void nvvar_print(void);
@@ -101,6 +104,15 @@ static inline int globalvar_add_simple_ip(const char *name,
 	return 0;
 }
 
+static inline int globalvar_add_simple_uuid(const char *name, uuid_t *uuid)
+{
+	return 0;
+}
+static inline int globalvar_add_simple_guid(const char *name, guid_t *guid)
+{
+	return 0;
+}
+
 static inline void globalvar_remove(const char *name) {}
 
 static inline void globalvar_print(void) {}
diff --git a/include/linux/sprintf.h b/include/linux/sprintf.h
index 349773d3e16a..e4b3df959a72 100644
--- a/include/linux/sprintf.h
+++ b/include/linux/sprintf.h
@@ -43,6 +43,9 @@ static inline __printf(2, 3) int rasprintf(char **strp, const char *fmt, ...)
 
 #define basprintf xasprintf
 
+char *uuid_string(char *buf, const char *end, const u8 *addr, int field_width,
+		  int precision, int flags, const char *fmt);
 const char *size_human_readable(unsigned long long size);
 
+
 #endif	/* _LINUX_KERNEL_SPRINTF_H */
diff --git a/include/param.h b/include/param.h
index 1713a18c378e..c223114fbde1 100644
--- a/include/param.h
+++ b/include/param.h
@@ -5,6 +5,7 @@
 #include <linux/err.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/uuid.h>
 #include <bobject.h>
 #include <stdarg.h>
 
@@ -27,6 +28,8 @@ enum param_type {
 	PARAM_TYPE_IPV4,
 	PARAM_TYPE_MAC,
 	PARAM_TYPE_FILE_LIST,
+	PARAM_TYPE_UUID,
+	PARAM_TYPE_GUID,
 };
 
 struct param_d {
@@ -106,6 +109,12 @@ struct param_d *bobject_add_param_file_list(bobject_t bobj, const char *name,
 					struct file_list **file_list,
 					void *priv);
 
+struct param_d *__bobject_add_param_uuid(bobject_t _bobj, const char *name,
+					 int (*set)(struct param_d *p, void *priv),
+					 int (*get)(struct param_d *p, void *priv),
+					 void *uuid, bool is_guid,
+					 void *priv);
+
 struct param_d *vbobject_add_param_fixed(struct bobject *bobj, const char *name,
 					 const char *fmt, va_list ap);
 
@@ -233,6 +242,15 @@ static inline struct param_d *bobject_add_param_file_list(bobject_t bobj,
 	return NULL;
 }
 
+static inline struct param_d *__bobject_add_param_uuid(bobject_t _bobj, const char *name,
+						       int (*set)(struct param_d *p, void *priv),
+						       int (*get)(struct param_d *p, void *priv),
+						       void *uuid, bool is_guid,
+						       void *priv)
+{
+	return NULL;
+}
+
 static inline
 struct param_d *vbobject_add_param_fixed(struct bobject *bobj, const char *name,
 					 const char *fmt, va_list ap)
@@ -273,6 +291,25 @@ static inline const char *get_param_value(struct param_d *param)
 	return param->get(param->bobj, param);
 }
 
+static inline struct param_d *
+bobject_add_param_uuid(bobject_t _bobj, const char *name,
+		       int (*set)(struct param_d *p, void *priv),
+		       int (*get)(struct param_d *p, void *priv),
+		       uuid_t *uuid, void *priv)
+{
+	return __bobject_add_param_uuid(_bobj, name, set, get, uuid, false, priv);
+}
+
+static inline struct param_d *
+bobject_add_param_guid(bobject_t _bobj, const char *name,
+		       int (*set)(struct param_d *p, void *priv),
+		       int (*get)(struct param_d *p, void *priv),
+		       guid_t *guid, void *priv)
+{
+	return __bobject_add_param_uuid(_bobj, name, set, get, guid, true, priv);
+}
+
+
 int param_set_readonly(struct param_d *p, void *priv);
 
 /*
@@ -409,6 +446,8 @@ static inline struct param_d *bobject_add_param_bitmask_ro(bobject_t bobj,
 #define dev_add_param_ip		bobject_add_param_ip
 #define dev_add_param_mac		bobject_add_param_mac
 #define dev_add_param_file_list		bobject_add_param_file_list
+#define dev_add_param_uuid		bobject_add_param_uuid
+#define dev_add_param_guid		bobject_add_param_guid
 #define dev_param_set_generic		bobject_param_set_generic
 
 #define dev_add_param_int		bobject_add_param_int
diff --git a/lib/parameter.c b/lib/parameter.c
index 1243a12e8701..5c8b86d0dff3 100644
--- a/lib/parameter.c
+++ b/lib/parameter.c
@@ -42,6 +42,8 @@ static const char *param_type_string[] = {
 	[PARAM_TYPE_IPV4] = "ipv4",
 	[PARAM_TYPE_MAC] = "MAC",
 	[PARAM_TYPE_FILE_LIST] = "file-list",
+	[PARAM_TYPE_UUID] = "uuid",
+	[PARAM_TYPE_GUID] = "guid",
 };
 
 const char *get_param_type(struct param_d *param)
@@ -1095,6 +1097,99 @@ struct param_d *bobject_add_param_file_list(bobject_t _bobj, const char *name,
 	return &pfl->param;
 }
 
+struct param_uuid {
+	struct param_d param;
+	void *uuid;
+	char *uuid_str;
+	int (*set)(struct param_d *p, void *priv);
+	int (*get)(struct param_d *p, void *priv);
+};
+
+static inline struct param_uuid *to_param_uuid(struct param_d *p)
+{
+	return container_of(p, struct param_uuid, param);
+}
+
+static int param_uuid_set(struct bobject *bobj, struct param_d *p,
+			  const char *val)
+{
+	struct param_uuid *pui = to_param_uuid(p);
+	u8 uuid_save[UUID_SIZE];
+	int ret;
+
+	if (!val || strlen(val) == UUID_STRING_LEN)
+		return -EINVAL;
+
+	memcpy(uuid_save, pui->uuid, UUID_SIZE);
+
+	if (pui->param.type == PARAM_TYPE_GUID)
+		ret = guid_parse(val, pui->uuid);
+	else
+		ret = uuid_parse(val, pui->uuid);
+
+	if (ret)
+		return ret;
+
+	if (!pui->set)
+		return 0;
+
+	ret = pui->set(p, p->driver_priv);
+	if (ret)
+		memcpy(pui->uuid, uuid_save, UUID_SIZE);
+
+	return ret;
+}
+
+static const char *param_uuid_get(struct bobject *bobj, struct param_d *p)
+{
+	struct param_uuid *pui = to_param_uuid(p);
+	int ret;
+
+	if (pui->get) {
+		ret = pui->get(p, p->driver_priv);
+		if (ret)
+			return NULL;
+	}
+
+	p->value = xrealloc(p->value, UUID_STRING_LEN + 1);
+
+	/* We don't use xasprintf here to avoid forcing a PRINTF_UUID
+	 * dependency on all CONFIG_PARAMETER users
+	 */
+	*uuid_string(p->value, p->value + UUID_STRING_LEN + 1,
+		    pui->uuid, -1, -1, 0,
+		    pui->param.type == PARAM_TYPE_GUID ? "Ul" : "Ub") = '\0';
+
+	return p->value;
+}
+
+struct param_d *__bobject_add_param_uuid(bobject_t _bobj, const char *name,
+					 int (*set)(struct param_d *p, void *priv),
+					 int (*get)(struct param_d *p, void *priv),
+					 void *uuid, bool is_guid,
+					 void *priv)
+{
+	struct bobject *bobj = _bobj.bobj;
+	struct param_uuid *pui;
+	int ret;
+
+	pui = xzalloc(sizeof(*pui));
+	pui->uuid = uuid;
+	pui->set = set;
+	pui->get = get;
+	pui->param.driver_priv = priv;
+	pui->param.type = is_guid ? PARAM_TYPE_GUID : PARAM_TYPE_UUID;
+
+	ret = __bobject_add_param(&pui->param, bobj, name,
+			param_uuid_set, param_uuid_get, 0);
+	if (ret) {
+		free(pui);
+		return ERR_PTR(ret);
+	}
+
+	return &pui->param;
+}
+
 
 /**
  * param_remove - remove a parameter and free its memory
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 6c9dae467496..2dca20b8b0a3 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -321,7 +321,7 @@ char *error_string(char *buf, const char *end, const void *errptr, int field_wid
     return string(buf, end, strerror(-PTR_ERR(errptr)), field_width, precision, flags);
 }
 
-static noinline_for_stack
+noinline_for_stack
 char *uuid_string(char *buf, const char *end, const u8 *addr, int field_width,
 		  int precision, int flags, const char *fmt)
 {
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/6] lib: smbios: add support for setting product UUID
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 2/6] param: support uuid/guid parameter type Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys Ahmad Fatoum
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

SMBIOS describes how firmware can report a product UUID to the OS.
Currently, we derive one from the machine ID, which should ensure
uniqueness, but for easier identification, it would be good if board
code could explicitly set a UUID that's reported as-is to the OS.

Add global.product.uuid to allow for this.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 common/misc.c          | 14 ++++++++++++++
 include/barebox-info.h |  4 ++++
 lib/smbios.c           | 18 ++++++++++++------
 3 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/common/misc.c b/common/misc.c
index 0af5a9cf30cd..b99a1b4ad8e7 100644
--- a/common/misc.c
+++ b/common/misc.c
@@ -142,6 +142,7 @@ BAREBOX_MAGICVAR(global.model, "Product name of this hardware");
 
 static char *hostname;
 static char *serial_number;
+static uuid_t product_uuid;
 
 /* Note that HOST_NAME_MAX is 64 on Linux */
 #define BAREBOX_HOST_NAME_MAX	64
@@ -252,6 +253,19 @@ const char *barebox_get_serial_number(void)
 
 BAREBOX_MAGICVAR(global.serial_number, "Board serial number");
 
+void barebox_set_product_uuid(const uuid_t *__product_uuid)
+{
+	globalvar_add_simple_uuid("product.uuid", &product_uuid);
+	product_uuid = *__product_uuid;
+}
+
+const uuid_t *barebox_get_product_uuid(void)
+{
+	return &product_uuid;
+}
+
+BAREBOX_MAGICVAR(global.product.uuid, "SMBIOS-reported product UUID");
+
 #ifdef CONFIG_OFTREE
 static char *of_machine_compatible;
 
diff --git a/include/barebox-info.h b/include/barebox-info.h
index bcceb7b0e021..70a940862ce2 100644
--- a/include/barebox-info.h
+++ b/include/barebox-info.h
@@ -4,6 +4,7 @@
 #define __BAREBOX_INFO_H__
 
 #include <linux/types.h>
+#include <linux/uuid.h>
 
 extern const char version_string[];
 extern const char release_string[];
@@ -25,6 +26,9 @@ bool barebox_hostname_is_valid(const char *s);
 const char *barebox_get_serial_number(void);
 void barebox_set_serial_number(const char *);
 
+void barebox_set_product_uuid(const uuid_t *uuid);
+const uuid_t *barebox_get_product_uuid(void);
+
 #ifdef CONFIG_OFTREE
 void barebox_set_of_machine_compatible(const char *);
 const char *barebox_get_of_machine_compatible(void);
diff --git a/lib/smbios.c b/lib/smbios.c
index c50fc907b255..d5aae1651f86 100644
--- a/lib/smbios.c
+++ b/lib/smbios.c
@@ -221,6 +221,8 @@ static int smbios_write_type1(void **current, int handle,
 {
 	struct smbios_type1 *t;
 	int len = sizeof(*t);
+	const uuid_t *product_uuid = NULL;
+	uuid_t product_uuid_buf;
 	char *vendor;
 
 	t = map_sysmem(*current, len);
@@ -236,16 +238,20 @@ static int smbios_write_type1(void **current, int handle,
 	t->version = smbios_add_string(ctx, NULL);
 	t->serial_number = smbios_add_string(ctx, barebox_get_serial_number());
 
-	if (IS_ENABLED(CONFIG_MACHINE_ID_SPECIFIC)) {
-		uuid_t id;
-		int ret;
+	product_uuid = barebox_get_product_uuid();
+	if (!product_uuid && IS_ENABLED(CONFIG_MACHINE_ID_SPECIFIC)) {
+		int err;
 
-		ret = machine_id_get_app_specific(&id, ARRAY_AND_SIZE("barebox-smbios"),
+		err = machine_id_get_app_specific(&product_uuid_buf,
+						  ARRAY_AND_SIZE("barebox-smbios"),
 						  NULL);
-		if (!ret)
-			export_uuid(t->uuid, &id);
+		if (!err)
+			product_uuid = &product_uuid_buf;
 	}
 
+	if (product_uuid)
+		export_uuid(t->uuid, product_uuid);
+
 	if (reset_source_get() == RESET_WKE)
 		t->wakeup_type = SMBIOS_WAKEUP_TYPE_OTHER;
 	else
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2026-01-15 12:06 ` [PATCH 3/6] lib: smbios: add support for setting product UUID Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-19 11:13   ` Sascha Hauer
  2026-01-15 12:06 ` [PATCH 5/6] firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating modes Ahmad Fatoum
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

While we have FW_CFG support as file system, there is a number of keys
that precede the keys in the file system, which can be useful in some
scenarios.

Add support for QEMU -uuid, -kernel, -initrd, -append options as well as
a /boot/qemu_fw_cfg script that continues booting according to the latter
three options.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 common/boards/Kconfig                         |   8 +
 common/boards/Makefile                        |   1 +
 common/boards/qemu/Makefile                   |   4 +
 .../defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg   |  12 +
 common/boards/qemu/fw_cfg.c                   | 208 ++++++++++++++++++
 drivers/firmware/Kconfig                      |   1 +
 6 files changed, 234 insertions(+)
 create mode 100644 common/boards/qemu/Makefile
 create mode 100755 common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
 create mode 100644 common/boards/qemu/fw_cfg.c

diff --git a/common/boards/Kconfig b/common/boards/Kconfig
index 74947316954b..a9e1b75a7641 100644
--- a/common/boards/Kconfig
+++ b/common/boards/Kconfig
@@ -1,5 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
+config BOARD_QEMU
+	bool "Extra support for QEMU-emulated boards" if COMPILE_TEST
+
+config BOARD_QEMU_VIRT
+	bool
+	select OF_OVERLAY
+	select BOARD_QEMU
+
 config BOARD_QEMU_VIRT
 	bool
 	select OF_OVERLAY
diff --git a/common/boards/Makefile b/common/boards/Makefile
index 058733522411..86d07e04a2ad 100644
--- a/common/boards/Makefile
+++ b/common/boards/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
+obj-$(CONFIG_BOARD_QEMU)	+= qemu/
 obj-$(CONFIG_BOARD_QEMU_VIRT)	+= qemu-virt/
 obj-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec/
 obj-$(CONFIG_BOARD_TQ) += tq/
diff --git a/common/boards/qemu/Makefile b/common/boards/qemu/Makefile
new file mode 100644
index 000000000000..2ea91d194bfc
--- /dev/null
+++ b/common/boards/qemu/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_QEMU_FW_CFG) += fw_cfg.o
+bbenv-$(CONFIG_QEMU_FW_CFG) += defaultenv-qemu_fw_cfg
diff --git a/common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg b/common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
new file mode 100755
index 000000000000..a3c12df67ff5
--- /dev/null
+++ b/common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+global.bootm.image=/dev/fw_cfg.kernel
+
+if [ -s /dev/fw_cfg.initrd ]; then
+        global.bootm.initrd=/dev/fw_cfg.initrd
+fi
+
+if [ -s /dev/fw_cfg.cmdline ]; then
+        readf /dev/fw_cfg.initrd qemu_fw_cfg_cmdline
+        global linux.bootargs.dyn.qemu="${qemu_fw_cfg_cmdline}"
+fi
diff --git a/common/boards/qemu/fw_cfg.c b/common/boards/qemu/fw_cfg.c
new file mode 100644
index 000000000000..d0900b2ab7c3
--- /dev/null
+++ b/common/boards/qemu/fw_cfg.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Common QEMU fw_cfg board code */
+
+#include <fs.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <envfs.h>
+#include <machine_id.h>
+#include <barebox-info.h>
+#include <linux/uuid.h>
+#include <linux/qemu_fw_cfg.h>
+
+struct fw_cfg_cdev {
+	struct cdev cdev;
+	u16 size_selector;
+	u16 data_selector;
+	void *cache;
+	size_t cached_size;	/* How many bytes from start are cached */
+};
+
+static struct fw_cfg_cdev *to_fw_cfg_cdev(struct cdev *cdev)
+{
+	return container_of(cdev, struct fw_cfg_cdev, cdev);
+}
+
+static int qemu_fw_cfg_read(int fd, u16 selector, void *buf, size_t size)
+{
+	ssize_t ret;
+
+	ret = ioctl(fd, FW_CFG_SELECT, &selector);
+	if (ret || !buf)
+		return ret;
+
+	ret = pread(fd, buf, size, 0);
+	return ret < 0 ? ret : 0;
+}
+
+static ssize_t fw_cfg_cdev_read(struct cdev *cdev, void *buf, size_t count,
+				loff_t offset, ulong flags)
+{
+	struct fw_cfg_cdev *fcc = to_fw_cfg_cdev(cdev);
+	size_t end_pos = offset + count;
+	size_t to_read, to_copy;
+	int ret;
+
+	/* Bounds check */
+	if (offset >= cdev->size)
+		return 0;
+
+	if (end_pos > cdev->size)
+		end_pos = cdev->size;
+
+	count = end_pos - offset;
+
+	/* Allocate cache on first read */
+	if (!fcc->cache) {
+		fcc->cache = malloc(cdev->size);
+		if (!fcc->cache)
+			return -ENOMEM;
+		fcc->cached_size = 0;
+	}
+
+	/* Extend cache if needed */
+	if (end_pos > fcc->cached_size) {
+		int fd;
+
+		to_read = end_pos - fcc->cached_size;
+
+		fd = open("/dev/fw_cfg", O_RDWR);
+		if (fd < 0)
+			return fd;
+
+		ret = qemu_fw_cfg_read(fd, fcc->data_selector,
+				       fcc->cache + fcc->cached_size, to_read);
+
+		close(fd);
+
+		if (ret)
+			return ret;
+
+		fcc->cached_size = end_pos;
+	}
+
+	/* Copy from cache to user buffer */
+	to_copy = count;
+
+	if (buf)
+		memcpy(buf, fcc->cache + offset, to_copy);
+
+	return to_copy;
+}
+
+static int fw_cfg_cdev_mmap(struct cdev *cdev, void **map, int flags)
+{
+	struct fw_cfg_cdev *fcc = to_fw_cfg_cdev(cdev);
+	ssize_t ret;
+
+	if (flags & PROT_WRITE)
+		return -EACCES;
+
+	ret = fw_cfg_cdev_read(cdev, NULL, ~0, 0, 0);
+	if (ret == 0)
+		ret = -EINVAL;
+	if (ret < 0)
+		return ret;
+
+	*map = fcc->cache;
+
+	return PTR_ERR_OR_ZERO(*map);
+}
+
+static int fw_cfg_cdev_flush(struct cdev *cdev)
+{
+	struct fw_cfg_cdev *fcc = to_fw_cfg_cdev(cdev);
+
+	if (fcc->cache) {
+		free(fcc->cache);
+		fcc->cache = NULL;
+		fcc->cached_size = 0;
+	}
+
+	return 0;
+}
+
+static struct cdev_operations fw_cfg_cdev_ops = {
+	.read = fw_cfg_cdev_read,
+	.flush = fw_cfg_cdev_flush,
+	.memmap = fw_cfg_cdev_mmap,
+};
+
+static int fw_cfg_register_cdev(int fd, const char *name,
+				u16 size_selector, u16 data_selector)
+{
+	struct fw_cfg_cdev *fcc;
+	u32 size;
+	int ret;
+
+	/* Read size upfront */
+	ret = qemu_fw_cfg_read(fd, size_selector, &size, sizeof(size));
+	if (!ret && !size)
+		ret = -ENOENT;
+	if (ret < 0)
+		return ret;
+
+	fcc = xzalloc(sizeof(*fcc));
+	fcc->size_selector = size_selector;
+	fcc->data_selector = data_selector;
+	fcc->cache = xzalloc(size);
+	fcc->cached_size = size;
+
+	ret = qemu_fw_cfg_read(fd, data_selector,
+			       fcc->cache, fcc->cached_size);
+	if (ret)
+		return ret;
+
+	fcc->cdev.name = xstrdup(name);
+	fcc->cdev.size = size;
+	fcc->cdev.ops = &fw_cfg_cdev_ops;
+
+	ret = devfs_create(&fcc->cdev);
+	if (ret < 0) {
+		free(fcc->cdev.name);
+		free(fcc);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qemu_fw_cfg_boot_init(void)
+{
+	struct {
+		const char prefix[16];
+		uuid_t uuid;
+	} machine_hashable = { "qemubareboxuuid"};
+	__always_unused size_t size;
+	int fd, ret;
+
+	fd = open("/dev/fw_cfg", O_RDWR);
+	if (fd < 0)
+		return 0;
+
+	/* Specified by -uuid. Zero by default */
+	ret = qemu_fw_cfg_read(fd, FW_CFG_UUID, &machine_hashable.uuid,
+			       sizeof(machine_hashable.uuid));
+	if (!ret) {
+		barebox_set_product_uuid(&machine_hashable.uuid);
+		if (!machine_id_get_hashable(&size))
+			machine_id_set_hashable(&machine_hashable, sizeof(machine_hashable));
+	}
+
+	/* Register fw_cfg cdevs */
+	fw_cfg_register_cdev(fd, "fw_cfg.kernel", FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA);
+	fw_cfg_register_cdev(fd, "fw_cfg.initrd", FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA);
+	fw_cfg_register_cdev(fd, "fw_cfg.cmdline", FW_CFG_CMDLINE_SIZE, FW_CFG_CMDLINE_DATA);
+
+	defaultenv_append_directory(defaultenv_qemu_fw_cfg);
+
+	/* Reset cdev to beginning */
+	ret = qemu_fw_cfg_read(fd, FW_CFG_SIGNATURE, NULL, 0);
+	if (ret)
+		return ret;
+
+	close(fd);
+	return 0;
+}
+crypto_initcall(qemu_fw_cfg_boot_init);
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 649b80ac289c..d7bf1d78937a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -45,6 +45,7 @@ config FIRMWARE_ZYNQ7000_FPGA
 
 config QEMU_FW_CFG
 	bool "QEMU FW CFG interface"
+	select BOARD_QEMU
 	help
 	  This driver exposes the QEMU FW CFG conduit as a single
 	  character device.
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 5/6] firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating modes
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2026-01-15 12:06 ` [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-15 12:06 ` [PATCH 6/6] ARM: configs: multi: enable QEMU FW_CFG Ahmad Fatoum
  2026-01-19 12:11 ` [PATCH 0/6] firmware: qemu_fw_cfg: improve support Sascha Hauer
  6 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We have a strange hodgepodge of DMA and PIO right now: We write always
via DMA, but we read via PIO, except that we skip leading bytes to reach
the intended offset via the DMA API.

This doesn't make much sense, because if DMA is unavailable, all of it
is. Rework this by checking the feature flag and either using DMA all
the way if possible or PIO as fallback.

For added flexibility, it's possible to switch between DMA and PIO at
runtime using the fw_cfg.pio device parameter.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 drivers/firmware/qemu_fw_cfg.c | 231 ++++++++++++++++++++++++---------
 1 file changed, 169 insertions(+), 62 deletions(-)

diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 33cc5bc54ce9..7a699539d672 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -35,11 +35,12 @@ struct fw_cfg {
 	void __iomem *reg_data;
 	void __iomem *reg_dma;
 	struct cdev cdev;
-	loff_t next_read_offset;
 	u32 sel;
 	bool is_mmio;
 	struct fw_cfg_dma_access __iomem *acc_virt;
 	dma_addr_t acc_dma;
+	u32 rev;
+	int use_pio;
 };
 
 static struct fw_cfg *to_fw_cfg(struct cdev *cdev)
@@ -70,7 +71,6 @@ 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;
@@ -79,6 +79,105 @@ static int fw_cfg_ioctl(struct cdev *cdev, unsigned int request, void *buf)
 	return 0;
 }
 
+static inline bool fw_cfg_dma_enabled(struct fw_cfg *fw_cfg)
+{
+	return (fw_cfg->rev & FW_CFG_VERSION_DMA) && fw_cfg->reg_dma;
+}
+
+/* qemu fw_cfg device is sync today, but spec says it may become async */
+static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d)
+{
+	for (;;) {
+		u32 ctrl = be32_to_cpu(READ_ONCE(d->control));
+
+		/* do not reorder the read to d->control */
+		/* rmb(); */
+		if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0)
+			return;
+
+		cpu_relax();
+	}
+}
+
+static ssize_t fw_cfg_dma_transfer(struct fw_cfg *fw_cfg,
+				   void *address, u32 length, u32 control)
+{
+	phys_addr_t dma;
+	struct fw_cfg_dma_access *d = NULL;
+	ssize_t ret = length;
+
+	d = dma_alloc(sizeof(*d));
+	if (!d) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	/* fw_cfg device does not need IOMMU protection, so use physical addresses */
+	*d = (struct fw_cfg_dma_access) {
+		.address = cpu_to_be64(address ? virt_to_phys(address) : 0),
+		.length = cpu_to_be32(length),
+		.control = cpu_to_be32(control)
+	};
+
+	dma = virt_to_phys(d);
+
+	iowrite32be((u64)dma >> 32, fw_cfg->reg_dma);
+	/* force memory to sync before notifying device via MMIO */
+	/* wmb(); */
+	iowrite32be(dma, fw_cfg->reg_dma + 4);
+
+	fw_cfg_wait_for_control(d);
+
+	if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) {
+		ret = -EIO;
+	}
+
+end:
+	dma_free(d);
+
+	return ret;
+}
+
+static ssize_t fw_cfg_dma_transfer_at(struct fw_cfg *fw_cfg,
+				      void *buf, u32 count, loff_t pos,
+				      u32 direction)
+{
+	int ret;
+
+	if (pos == 0) {
+		ret = fw_cfg_dma_transfer(fw_cfg, buf, count,
+					  fw_cfg->sel << 16 |
+					  FW_CFG_DMA_CTL_SELECT | direction);
+	} else {
+		fw_cfg_select(fw_cfg);
+		ret = fw_cfg_dma_transfer(fw_cfg, NULL, pos, FW_CFG_DMA_CTL_SKIP);
+		if (ret >= 0)
+			ret = fw_cfg_dma_transfer(fw_cfg, buf, count, direction);
+	}
+
+	return ret;
+}
+
+static ssize_t fw_cfg_dma_read(struct cdev *cdev, void *buf,
+			       size_t count, loff_t pos, unsigned long flags)
+{
+	return fw_cfg_dma_transfer_at(to_fw_cfg(cdev), buf, count, pos,
+				      FW_CFG_DMA_CTL_READ);
+}
+
+static ssize_t fw_cfg_dma_write(struct cdev *cdev, const void *buf,
+				size_t count, loff_t pos, unsigned long flags)
+{
+	return fw_cfg_dma_transfer_at(to_fw_cfg(cdev), (void *)buf, count, pos,
+				      FW_CFG_DMA_CTL_WRITE);
+}
+
+static struct cdev_operations fw_cfg_dma_ops = {
+	.read = fw_cfg_dma_read,
+	.write = fw_cfg_dma_write,
+	.ioctl = fw_cfg_ioctl,
+};
+
 static void reads_n(const void __iomem *src, void *dst,
 		   resource_size_t count, int rwsize)
 {
@@ -104,83 +203,72 @@ static void ins_n(unsigned long src, void *dst,
 	}
 }
 
-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)
+static ssize_t fw_cfg_pio_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) ?: 8;
 
-	if (!pos || pos != fw_cfg->next_read_offset) {
-		fw_cfg_select(fw_cfg);
-		fw_cfg->next_read_offset = 0;
-	}
-
 	if (!IS_ALIGNED(pos, rdsize) || !IS_ALIGNED(count, rdsize))
 		rdsize = 1;
 
+	fw_cfg_select(fw_cfg);
+	while (pos-- > 0)
+		ioread8(fw_cfg->reg_data);
+
 	if (fw_cfg->is_mmio)
 		reads_n(fw_cfg->reg_data, buf, count, rdsize);
 	else
 		ins_n((ulong)fw_cfg->reg_data, buf, count, rdsize);
 
-	fw_cfg->next_read_offset += count;
 	return count;
 }
 
-static ssize_t fw_cfg_write(struct cdev *cdev, const void *buf, size_t count,
-			    loff_t pos, unsigned long flags)
+static void writes_n(void __iomem *dst, const void *src,
+		   resource_size_t count, int rwsize)
 {
-	struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
-	struct device *dev = cdev->dev;
-	void *dma_buf;
-	dma_addr_t mapping;
-	int ret = 0;
-
-	dma_buf = dma_alloc(count);
-	if (!dma_buf)
-		return -ENOMEM;
-
-	memcpy(dma_buf, buf, count);
-
-	mapping = dma_map_single(dev, dma_buf, count, DMA_TO_DEVICE);
-	if (dma_mapping_error(dev, mapping)) {
-		ret = -EFAULT;
-		goto free_buf;
+	switch (rwsize) {
+	case 1: writesb(dst, src, count / 1); break;
+	case 2: writesw(dst, src, count / 2); break;
+	case 4: writesl(dst, src, count / 4); break;
+#ifdef CONFIG_64BIT
+	case 8: writesq(dst, src, count / 8); break;
+#endif
 	}
-
-	fw_cfg->next_read_offset = 0;
-
-	fw_cfg_do_dma(fw_cfg, DMA_ERROR_CODE, pos, FW_CFG_DMA_CTL_SKIP |
-		      FW_CFG_DMA_CTL_SELECT | fw_cfg->sel << 16);
-
-	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:
-	dma_free(dma_buf);
-
-	return ret ?: count;
 }
 
-static struct cdev_operations fw_cfg_ops = {
-	.read = fw_cfg_read,
-	.write = fw_cfg_write,
+static void outs_n(unsigned long dst, const void *src,
+		  resource_size_t count, int rwsize)
+{
+	switch (rwsize) {
+	case 1: outsb(dst, src, count / 1); break;
+	case 2: outsw(dst, src, count / 2); break;
+	case 4: outsl(dst, src, count / 4); break;
+	/* No insq, so just do 32-bit accesses */
+	case 8: outsl(dst, src, count / 4); break;
+	}
+}
+
+static ssize_t fw_cfg_pio_write(struct cdev *cdev, const void *buf, size_t count,
+			       loff_t pos, unsigned long flags)
+{
+	struct fw_cfg *fw_cfg = to_fw_cfg(cdev);
+	unsigned wrsize = FIELD_GET(O_RWSIZE_MASK, flags) ?: 8;
+
+	if (!IS_ALIGNED(pos, wrsize) || !IS_ALIGNED(count, wrsize))
+		wrsize = 1;
+
+	if (fw_cfg->is_mmio)
+		writes_n(fw_cfg->reg_data, buf, count, wrsize);
+	else
+		outs_n((ulong)fw_cfg->reg_data, buf, count, wrsize);
+
+	return count;
+}
+
+static struct cdev_operations fw_cfg_pio_ops = {
+	.read = fw_cfg_pio_read,
+	.write = fw_cfg_pio_write,
 	.ioctl = fw_cfg_ioctl,
 };
 
@@ -191,12 +279,20 @@ static int fw_cfg_param_select(struct param_d *p, void *priv)
 	return fw_cfg->sel <= U16_MAX ? 0 : -EINVAL;
 }
 
+static int fw_cfg_param_use_pio(struct param_d *p, void *priv)
+{
+	struct fw_cfg *fw_cfg = priv;
+	fw_cfg->cdev.ops = fw_cfg->use_pio ? &fw_cfg_pio_ops : &fw_cfg_dma_ops;
+	return 0;
+}
+
 static int fw_cfg_probe(struct device *dev)
 {
 	struct device_node *np = dev_of_node(dev);
 	struct resource *parent_res, *iores;
 	char sig[FW_CFG_SIG_SIZE];
 	struct fw_cfg *fw_cfg;
+	__le32 rev;
 	int ret;
 
 	fw_cfg = xzalloc(sizeof(*fw_cfg));
@@ -225,20 +321,28 @@ static int fw_cfg_probe(struct device *dev)
 
 	/* verify fw_cfg device signature */
 	fw_cfg->sel = FW_CFG_SIGNATURE;
-	fw_cfg_read(&fw_cfg->cdev, sig, FW_CFG_SIG_SIZE, 0, 0);
+	fw_cfg_pio_read(&fw_cfg->cdev, sig, FW_CFG_SIG_SIZE, 0, 0);
 
 	if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
 		ret = np ? -EILSEQ : -ENODEV;
 		goto err;
 	}
 
+	fw_cfg->sel = FW_CFG_ID;
+	ret = fw_cfg_pio_read(&fw_cfg->cdev, &rev, sizeof(rev), 0, 0);
+	if (ret < 0)
+		goto err;
+
+	fw_cfg->rev = le32_to_cpu(rev);
+	fw_cfg->use_pio = !fw_cfg_dma_enabled(fw_cfg);
+
 	fw_cfg->acc_virt = dma_alloc_coherent(DMA_DEVICE_BROKEN,
 					      sizeof(*fw_cfg->acc_virt), &fw_cfg->acc_dma);
 
 	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;
+	fw_cfg->cdev.ops = fw_cfg->use_pio ? &fw_cfg_pio_ops : &fw_cfg_dma_ops;
 	fw_cfg->cdev.dev = dev;
 	fw_cfg->cdev.filetype = filetype_qemu_fw_cfg;
 
@@ -255,6 +359,9 @@ static int fw_cfg_probe(struct device *dev)
 	dev_add_param_uint32(dev, "selector", fw_cfg_param_select,
 			     NULL, &fw_cfg->sel, "%u", fw_cfg);
 
+	dev_add_param_bool(dev, "pio", fw_cfg_param_use_pio, NULL,
+			   &fw_cfg->use_pio, fw_cfg);
+
 	dev->priv = fw_cfg;
 
 	return 0;
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 6/6] ARM: configs: multi: enable QEMU FW_CFG
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2026-01-15 12:06 ` [PATCH 5/6] firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating modes Ahmad Fatoum
@ 2026-01-15 12:06 ` Ahmad Fatoum
  2026-01-19 12:11 ` [PATCH 0/6] firmware: qemu_fw_cfg: improve support Sascha Hauer
  6 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-15 12:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

On ARM, FW_CFG is probed if the associated device tree node is probed,
so there is no harm in enabling it in the multi defconfigs, but having
it for QEMU platforms enables us to use pytest --env.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 arch/arm/configs/multi_v5_v6_defconfig | 2 ++
 arch/arm/configs/multi_v7_defconfig    | 2 ++
 arch/arm/configs/multi_v8_defconfig    | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/arch/arm/configs/multi_v5_v6_defconfig b/arch/arm/configs/multi_v5_v6_defconfig
index 27a925f8bb26..bc6645b3ce27 100644
--- a/arch/arm/configs/multi_v5_v6_defconfig
+++ b/arch/arm/configs/multi_v5_v6_defconfig
@@ -122,3 +122,5 @@ CONFIG_FS_UBIFS=y
 CONFIG_FS_UBIFS_COMPRESSION_LZO=y
 CONFIG_DIGEST_SHA1_ARM=y
 CONFIG_DIGEST_SHA256_ARM=y
+CONFIG_QEMU_FW_CFG=y
+CONFIG_FS_QEMU_FW_CFG=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index ef4e10614801..688fd1c7c19e 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -374,3 +374,5 @@ CONFIG_FS_RATP=y
 CONFIG_PNG=y
 CONFIG_DIGEST_SHA1_ARM=y
 CONFIG_DIGEST_SHA256_ARM=y
+CONFIG_QEMU_FW_CFG=y
+CONFIG_FS_QEMU_FW_CFG=y
diff --git a/arch/arm/configs/multi_v8_defconfig b/arch/arm/configs/multi_v8_defconfig
index e63bb4618900..73a2d5feef37 100644
--- a/arch/arm/configs/multi_v8_defconfig
+++ b/arch/arm/configs/multi_v8_defconfig
@@ -300,3 +300,5 @@ CONFIG_DIGEST_SHA1_ARM64_CE=y
 CONFIG_DIGEST_SHA256_ARM64_CE=y
 CONFIG_LZO_DECOMPRESS=y
 # CONFIG_MISSING_FIRMWARE_ERROR is not set
+CONFIG_QEMU_FW_CFG=y
+CONFIG_FS_QEMU_FW_CFG=y
-- 
2.47.3




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys
  2026-01-15 12:06 ` [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys Ahmad Fatoum
@ 2026-01-19 11:13   ` Sascha Hauer
  2026-01-19 11:33     ` Ahmad Fatoum
  0 siblings, 1 reply; 10+ messages in thread
From: Sascha Hauer @ 2026-01-19 11:13 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, Jan 15, 2026 at 01:06:12PM +0100, Ahmad Fatoum wrote:
> While we have FW_CFG support as file system, there is a number of keys
> that precede the keys in the file system, which can be useful in some
> scenarios.
> 
> Add support for QEMU -uuid, -kernel, -initrd, -append options as well as
> a /boot/qemu_fw_cfg script that continues booting according to the latter
> three options.
> 
> Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
> ---
>  common/boards/Kconfig                         |   8 +
>  common/boards/Makefile                        |   1 +
>  common/boards/qemu/Makefile                   |   4 +
>  .../defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg   |  12 +
>  common/boards/qemu/fw_cfg.c                   | 208 ++++++++++++++++++
>  drivers/firmware/Kconfig                      |   1 +
>  6 files changed, 234 insertions(+)
>  create mode 100644 common/boards/qemu/Makefile
>  create mode 100755 common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
>  create mode 100644 common/boards/qemu/fw_cfg.c
> 
> diff --git a/common/boards/Kconfig b/common/boards/Kconfig
> index 74947316954b..a9e1b75a7641 100644
> --- a/common/boards/Kconfig
> +++ b/common/boards/Kconfig
> @@ -1,5 +1,13 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  
> +config BOARD_QEMU
> +	bool "Extra support for QEMU-emulated boards" if COMPILE_TEST
> +
> +config BOARD_QEMU_VIRT
> +	bool
> +	select OF_OVERLAY
> +	select BOARD_QEMU
> +
>  config BOARD_QEMU_VIRT
>  	bool
>  	select OF_OVERLAY

You likely intended to just add "select BOARD_QEMU" instead of
duplicating the option. I'll fix that while applying.

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] 10+ messages in thread

* Re: [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys
  2026-01-19 11:13   ` Sascha Hauer
@ 2026-01-19 11:33     ` Ahmad Fatoum
  0 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2026-01-19 11:33 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

Hi Sascha,

On 1/19/26 12:13 PM, Sascha Hauer wrote:
> On Thu, Jan 15, 2026 at 01:06:12PM +0100, Ahmad Fatoum wrote:
>> While we have FW_CFG support as file system, there is a number of keys
>> that precede the keys in the file system, which can be useful in some
>> scenarios.
>>
>> Add support for QEMU -uuid, -kernel, -initrd, -append options as well as
>> a /boot/qemu_fw_cfg script that continues booting according to the latter
>> three options.
>>
>> Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
>> ---
>>  common/boards/Kconfig                         |   8 +
>>  common/boards/Makefile                        |   1 +
>>  common/boards/qemu/Makefile                   |   4 +
>>  .../defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg   |  12 +
>>  common/boards/qemu/fw_cfg.c                   | 208 ++++++++++++++++++
>>  drivers/firmware/Kconfig                      |   1 +
>>  6 files changed, 234 insertions(+)
>>  create mode 100644 common/boards/qemu/Makefile
>>  create mode 100755 common/boards/qemu/defaultenv-qemu_fw_cfg/boot/qemu_fw_cfg
>>  create mode 100644 common/boards/qemu/fw_cfg.c
>>
>> diff --git a/common/boards/Kconfig b/common/boards/Kconfig
>> index 74947316954b..a9e1b75a7641 100644
>> --- a/common/boards/Kconfig
>> +++ b/common/boards/Kconfig
>> @@ -1,5 +1,13 @@
>>  # SPDX-License-Identifier: GPL-2.0-only
>>  
>> +config BOARD_QEMU
>> +	bool "Extra support for QEMU-emulated boards" if COMPILE_TEST
>> +
>> +config BOARD_QEMU_VIRT
>> +	bool
>> +	select OF_OVERLAY
>> +	select BOARD_QEMU
>> +
>>  config BOARD_QEMU_VIRT
>>  	bool
>>  	select OF_OVERLAY
> 
> You likely intended to just add "select BOARD_QEMU" instead of
> duplicating the option. I'll fix that while applying.

Indeed. Thank you.

Cheers,
Ahmad

> 
> Sascha
> 




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/6] firmware: qemu_fw_cfg: improve support
  2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2026-01-15 12:06 ` [PATCH 6/6] ARM: configs: multi: enable QEMU FW_CFG Ahmad Fatoum
@ 2026-01-19 12:11 ` Sascha Hauer
  6 siblings, 0 replies; 10+ messages in thread
From: Sascha Hauer @ 2026-01-19 12:11 UTC (permalink / raw)
  To: barebox, Ahmad Fatoum


On Thu, 15 Jan 2026 13:06:08 +0100, Ahmad Fatoum wrote:
> This series adds DMA support to QEMU FW_CFG also on the read size,
> allows a completely dma-less PIO mode and adds support for parsing
> the QEMU options for: -kernel, -initrd, -uuid and -append.
> 
> Ahmad Fatoum (6):
>   firmware: qemu_fw_cfg: use wider PIO reads if applicable
>   param: support uuid/guid parameter type
>   lib: smbios: add support for setting product UUID
>   common: boards: qemu: process some standard fw_cfg keys
>   firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating
>     modes
>   ARM: configs: multi: enable QEMU FW_CFG
> 
> [...]

Applied, thanks!

[1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable
      https://git.pengutronix.de/cgit/barebox/commit/?id=d5744b7a5b68 (link may not be stable)
[2/6] param: support uuid/guid parameter type
      https://git.pengutronix.de/cgit/barebox/commit/?id=6c1df4e8e236 (link may not be stable)
[3/6] lib: smbios: add support for setting product UUID
      https://git.pengutronix.de/cgit/barebox/commit/?id=4728bc697b9a (link may not be stable)
[4/6] common: boards: qemu: process some standard fw_cfg keys
      https://git.pengutronix.de/cgit/barebox/commit/?id=67041eb8e505 (link may not be stable)
[5/6] firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating modes
      https://git.pengutronix.de/cgit/barebox/commit/?id=3f52ee9f37d3 (link may not be stable)
[6/6] ARM: configs: multi: enable QEMU FW_CFG
      https://git.pengutronix.de/cgit/barebox/commit/?id=aa09eec6b60a (link may not be stable)

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>




^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-01-19 12:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-15 12:06 [PATCH 0/6] firmware: qemu_fw_cfg: improve support Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 1/6] firmware: qemu_fw_cfg: use wider PIO reads if applicable Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 2/6] param: support uuid/guid parameter type Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 3/6] lib: smbios: add support for setting product UUID Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 4/6] common: boards: qemu: process some standard fw_cfg keys Ahmad Fatoum
2026-01-19 11:13   ` Sascha Hauer
2026-01-19 11:33     ` Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 5/6] firmware: qemu_fw_cfg: add proper DMA and PIO bidirectional operating modes Ahmad Fatoum
2026-01-15 12:06 ` [PATCH 6/6] ARM: configs: multi: enable QEMU FW_CFG Ahmad Fatoum
2026-01-19 12:11 ` [PATCH 0/6] firmware: qemu_fw_cfg: improve support Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox