* [PATCH 00/10] Add barebox TLV infrastructure
@ 2025-04-11 7:40 Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 01/10] net: factor out eth_of_get_fixup_node Ahmad Fatoum
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox
barebox TLV is a system to store and retrieve a device's (read-only)
meta-data from non-volatile memory.
It is intended to handle information that are usually only set in
the factory - like serial number, MAC-addresses, analog calibration
data, etc.
Data is stored in a tag-length-value format (hence the name) and read
from non-volatile memory during startup.
Unpacked values are fixed up into the devicetree ``chosen``-node
for consumption by the operating system.
This system has already been deployed in the field for a couple of years
now on the LXA TAC. The feature is given a generic name though, because
it's hoped it will be useful to a more general audience.
Nowadays, this could be implemented as a NVMEM layout in Linux.
The benefit of doing it in barebox is that, when signing is added
in the future, we preclude an attacker from showing barebox
and Linux different content.
Nevertheless, NVMEM layout infrastructure in barebox could be
useful, but our NVMEM support needs an overhaul to support layouts,
so fixed-partitions is the only supported binding for now.
Ahmad Fatoum (8):
net: factor out eth_of_get_fixup_node
net: export list of registered ethernet addresses
common: add optional systemd.hostname generation
common: add barebox TLV support
commands: add TLV debugging command
scripts: add bareboxtlv host/target tool
boards: add decoder for LXA TLV v1 format
ARM: stm32mp: lxa: enable TLV support for TAC & FairyTux2
Chris Fiege (2):
scripts: Add Barebox TLV Generator Tooling
doc: Add User-Documentation for Barebox TLV
.../bindings/nvmem/barebox,tlv.yaml | 60 ++++
Documentation/user/barebox-tlv.rst | 93 +++++
Documentation/user/user-manual.rst | 1 +
arch/arm/boards/lxa-fairytux2/board.c | 8 +
arch/arm/boards/lxa-tac/board.c | 8 +
.../dts/stm32mp153c-lxa-fairytux2-gen2.dts | 1 +
arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi | 1 +
arch/arm/dts/stm32mp15xc-lxa-tac.dtsi | 2 +
arch/arm/mach-stm32mp/Kconfig | 2 +
commands/Kconfig | 12 +
commands/Makefile | 1 +
commands/tlv.c | 55 +++
common/Kconfig | 34 ++
common/Makefile | 1 +
common/boards/Kconfig | 5 +
common/boards/Makefile | 1 +
common/boards/lxa/Makefile | 2 +
common/boards/lxa/factory-data.c | 132 +++++++
common/bootm.c | 9 +-
common/tlv/Makefile | 4 +
common/tlv/barebox.c | 183 ++++++++++
common/tlv/bus.c | 133 ++++++++
common/tlv/drv.c | 49 +++
common/tlv/parser.c | 211 ++++++++++++
common/tlv/register.c | 94 +++++
include/net.h | 21 ++
include/string.h | 7 +
include/tlv/format.h | 72 ++++
include/tlv/tlv.h | 99 ++++++
lib/string.c | 21 ++
net/eth.c | 40 +--
scripts/.gitignore | 2 +
scripts/Makefile | 2 +
.../bareboxtlv-generator.py | 321 ++++++++++++++++++
.../bareboxtlv-generator/data-example.yaml | 12 +
scripts/bareboxtlv-generator/requirements.txt | 2 +
.../bareboxtlv-generator/schema-example.yaml | 48 +++
scripts/bareboxtlv-target.c | 1 +
scripts/bareboxtlv.c | 183 ++++++++++
scripts/include/asm/unaligned.h | 21 ++
scripts/include/linux/build_bug.h | 8 +
scripts/include/linux/stringify.h | 14 +
test/self/Kconfig | 7 +
test/self/Makefile | 1 +
test/self/tlv.c | 89 +++++
test/self/tlv.dts | 27 ++
46 files changed, 2080 insertions(+), 20 deletions(-)
create mode 100644 Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
create mode 100644 Documentation/user/barebox-tlv.rst
create mode 100644 commands/tlv.c
create mode 100644 common/boards/lxa/Makefile
create mode 100644 common/boards/lxa/factory-data.c
create mode 100644 common/tlv/Makefile
create mode 100644 common/tlv/barebox.c
create mode 100644 common/tlv/bus.c
create mode 100644 common/tlv/drv.c
create mode 100644 common/tlv/parser.c
create mode 100644 common/tlv/register.c
create mode 100644 include/tlv/format.h
create mode 100644 include/tlv/tlv.h
create mode 100755 scripts/bareboxtlv-generator/bareboxtlv-generator.py
create mode 100644 scripts/bareboxtlv-generator/data-example.yaml
create mode 100644 scripts/bareboxtlv-generator/requirements.txt
create mode 100644 scripts/bareboxtlv-generator/schema-example.yaml
create mode 100644 scripts/bareboxtlv-target.c
create mode 100644 scripts/bareboxtlv.c
create mode 100644 scripts/include/asm/unaligned.h
create mode 100644 scripts/include/linux/build_bug.h
create mode 100644 scripts/include/linux/stringify.h
create mode 100644 test/self/tlv.c
create mode 100644 test/self/tlv.dts
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 01/10] net: factor out eth_of_get_fixup_node
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 02/10] net: export list of registered ethernet addresses Ahmad Fatoum
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We only know at fixup time, which MAC addresses were actually assigned
to interfaces. Factor out eth_of_get_fixup_node from the MAC address
assignment code, so board code can use this to retrace which MAC
addresses would be fixed up.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/net.h | 12 ++++++++++++
net/eth.c | 29 +++++++++++++++++++----------
2 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/include/net.h b/include/net.h
index a3c466784630..b742e9499434 100644
--- a/include/net.h
+++ b/include/net.h
@@ -128,6 +128,18 @@ static inline void of_eth_register_ethaddr(struct device_node *node,
void eth_register_ethaddr(int ethid, const char *ethaddr);
void of_eth_register_ethaddr(struct device_node *node, const char *ethaddr);
#endif
+
+#ifdef CONFIG_OFTREE
+struct device_node *eth_of_get_fixup_node(struct device_node *root,
+ const char *node_path, int ethid);
+#else
+static inline struct device_node *eth_of_get_fixup_node(struct device_node *root,
+ const char *node_path, int ethid)
+{
+ return NULL;
+}
+#endif
+
/*
* Ethernet header
*/
diff --git a/net/eth.c b/net/eth.c
index bc2527de3670..16857ac838dd 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -319,19 +319,11 @@ static int eth_param_set_ethaddr(struct param_d *param, void *priv)
}
#ifdef CONFIG_OFTREE
-static void eth_of_fixup_node(struct device_node *root,
- const char *node_path, int ethid,
- const u8 ethaddr[ETH_ALEN])
+struct device_node *eth_of_get_fixup_node(struct device_node *root,
+ const char *node_path, int ethid)
{
struct device_node *bb_node, *fixup_node;
char *name;
- int ret;
-
- if (!is_valid_ether_addr(ethaddr)) {
- pr_debug("%s: no valid mac address, cannot fixup\n",
- __func__);
- return;
- }
if (node_path) {
bb_node = of_find_node_by_path_from(0, node_path);
@@ -344,6 +336,23 @@ static void eth_of_fixup_node(struct device_node *root,
fixup_node = of_find_node_by_alias(root, eth);
}
+ return fixup_node;
+}
+
+static void eth_of_fixup_node(struct device_node *root,
+ const char *node_path, int ethid,
+ const u8 ethaddr[ETH_ALEN])
+{
+ struct device_node *fixup_node;
+ int ret;
+
+ if (!is_valid_ether_addr(ethaddr)) {
+ pr_debug("%s: no valid mac address, cannot fixup\n",
+ __func__);
+ return;
+ }
+
+ fixup_node = eth_of_get_fixup_node(root, node_path, ethid);
if (!fixup_node) {
pr_debug("%s: no node to fixup\n", __func__);
return;
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 02/10] net: export list of registered ethernet addresses
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 01/10] net: factor out eth_of_get_fixup_node Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 03/10] common: add optional systemd.hostname generation Ahmad Fatoum
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Network stack keeps an ethaddr_list of Ethernet addresses that were
registered by drivers and board code.
The list currently lacks ethernet devices for which a net_device already
existed. If a net_device is added later on, the list is consulted, but
the element is not removed.
This means ethaddr_list is "the list of registered Ethernet addresses
that were added before the relevant device has been probed". Make it more
useful by genericising it to be "the list of registered Ethernet addresses".
This introduces no functional change (only consumer is fixup code that
handles both cases already), but it allows future board code to consult
the list to check which Ethernet addresses were actually assigned.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/net.h | 9 +++++++++
net/eth.c | 11 ++---------
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/include/net.h b/include/net.h
index b742e9499434..bc0c1edce0ce 100644
--- a/include/net.h
+++ b/include/net.h
@@ -613,4 +613,13 @@ void ifdown_all(void);
extern struct class eth_class;
+struct eth_ethaddr {
+ struct list_head list;
+ u8 ethaddr[ETH_ALEN];
+ int ethid;
+ struct device_node *node;
+};
+
+extern struct list_head ethaddr_list;
+
#endif /* __NET_H__ */
diff --git a/net/eth.c b/net/eth.c
index 16857ac838dd..2489ed152634 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -23,14 +23,7 @@
DEFINE_DEV_CLASS(eth_class, "eth");
-struct eth_ethaddr {
- struct list_head list;
- u8 ethaddr[ETH_ALEN];
- int ethid;
- struct device_node *node;
-};
-
-static LIST_HEAD(ethaddr_list);
+LIST_HEAD(ethaddr_list);
int eth_set_promisc(struct eth_device *edev, bool enable)
{
@@ -102,7 +95,7 @@ void eth_register_ethaddr(int ethid, const char *ethaddr)
for_each_netdev(edev) {
if (edev->dev.id == ethid) {
register_preset_mac_address(edev, ethaddr);
- return;
+ break;
}
}
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 03/10] common: add optional systemd.hostname generation
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 01/10] net: factor out eth_of_get_fixup_node Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 02/10] net: export list of registered ethernet addresses Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 04/10] common: add barebox TLV support Ahmad Fatoum
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To maintain unique hostnames, having barebox fix up its own hostname
appended by "-${global.serial_number} is a sane default. Add a Kconfig
option to enable this.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/Kconfig | 10 ++++++++++
common/bootm.c | 9 ++++++++-
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/common/Kconfig b/common/Kconfig
index 1e04d0403ad0..2d2be0f7c4f6 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1254,6 +1254,16 @@ config MACHINE_ID
Note: if no hashable information is available no machine id will be passed
to the kernel.
+config SERIAL_NUMBER_FIXUP_SYSTEMD_HOSTNAME
+ bool "append board serial number to systemd.hostname= fixup"
+ depends on FLEXIBLE_BOOTARGS
+ help
+ When a systemd.hostname= kernel command line argument is
+ fixed up, append a `-${global.serial_number}' suffix.
+
+ This option without effect if global.bootm.provide_hostname
+ is unset.
+
config SYSTEMD_OF_WATCHDOG
bool "inform devicetree-enabled kernel of used watchdog"
depends on WATCHDOG && OFTREE && FLEXIBLE_BOOTARGS
diff --git a/common/bootm.c b/common/bootm.c
index 5370e1485bc6..a26ea82684a6 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -864,6 +864,7 @@ int bootm_boot(struct bootm_data *bootm_data)
if (bootm_data->provide_hostname) {
const char *hostname = getenv_nonempty("global.hostname");
+ const char *suffix = NULL;
char *hostname_bootarg;
if (!hostname) {
@@ -878,7 +879,13 @@ int bootm_boot(struct bootm_data *bootm_data)
goto err_out;
}
- hostname_bootarg = basprintf("systemd.hostname=%s", hostname);
+ if (IS_ENABLED(CONFIG_SERIAL_NUMBER_FIXUP_SYSTEMD_HOSTNAME))
+ suffix = barebox_get_serial_number();
+
+ hostname_bootarg = basprintf("systemd.hostname=%s%s%s",
+ hostname, suffix ? "-" : "",
+ suffix ?: "");
+
globalvar_add_simple("linux.bootargs.hostname", hostname_bootarg);
free(hostname_bootarg);
}
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 04/10] common: add barebox TLV support
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (2 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 03/10] common: add optional systemd.hostname generation Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 05/10] commands: add TLV debugging command Ahmad Fatoum
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
barebox TLV is a scheme for storing factory data on non-volatile
storage. Unlike state, it's meant to be read-only and if content
is limited to already specified tags, it can be extended later on,
without modifying the bootloader binary.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
.../bindings/nvmem/barebox,tlv.yaml | 58 +++++
common/Kconfig | 24 ++
common/Makefile | 1 +
common/tlv/Makefile | 4 +
common/tlv/barebox.c | 183 +++++++++++++++
common/tlv/bus.c | 133 +++++++++++
common/tlv/drv.c | 49 ++++
common/tlv/parser.c | 211 ++++++++++++++++++
common/tlv/register.c | 94 ++++++++
include/string.h | 5 +
include/tlv/format.h | 69 ++++++
include/tlv/tlv.h | 99 ++++++++
test/self/Kconfig | 7 +
test/self/Makefile | 1 +
test/self/tlv.c | 89 ++++++++
test/self/tlv.dts | 27 +++
16 files changed, 1054 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
create mode 100644 common/tlv/Makefile
create mode 100644 common/tlv/barebox.c
create mode 100644 common/tlv/bus.c
create mode 100644 common/tlv/drv.c
create mode 100644 common/tlv/parser.c
create mode 100644 common/tlv/register.c
create mode 100644 include/tlv/format.h
create mode 100644 include/tlv/tlv.h
create mode 100644 test/self/tlv.c
create mode 100644 test/self/tlv.dts
diff --git a/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml b/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
new file mode 100644
index 000000000000..b54ab600e309
--- /dev/null
+++ b/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://barebox.org/schemas/nvmem/barebox,tlv.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: barebox TLV factory data
+
+description: |
+ barebox TLV is a scheme for storing factory data on non-volatile
+ storage. Unlike state, it's meant to be read-only and if content
+ is limited to already specified tags, it can be extended later on,
+ without modifying the bootloader binary.
+
+ Variables can not yet be defined as NVMEM device subnodes.
+
+maintainers:
+ - Ahmad Fatoum <a.fatoum@pengutronix.de>
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - barebox,tlv-v1 # magic: 0x61bb95f2
+ - const: barebox,tlv
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - label
+ - reg
+
+allOf:
+ - $ref: partition.yaml#
+
+additionalProperties: false
+
+examples:
+ - |
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ reg = <0x0 0x100000>;
+ label = "barebox";
+ read-only;
+ };
+
+ partition@100000 {
+ compatible = "barebox,tlv";
+ label = "tlv";
+ reg = <0x100000 0x10000>;
+ };
+ };
diff --git a/common/Kconfig b/common/Kconfig
index 2d2be0f7c4f6..ee2737c93eb9 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1151,6 +1151,30 @@ config BTHREAD
scheduled within delay loops and the console idle to asynchronously
execute actions, like checking for link up or feeding a watchdog.
+config TLV
+ bool "barebox TLV support"
+ depends on OFDEVICE
+ help
+ barebox TLV is a scheme for storing factory data on non-volatile
+ storage. Unlike state, it's meant to be read-only.
+
+config TLV_DRV
+ bool "barebox TLV generic driver"
+ depends on TLV
+ default y
+ help
+ barebox,tlv devices in the device tree will be matched against
+ a compatible decoder via the 4-byte magic header.
+
+config TLV_BAREBOX
+ bool "barebox TLV common format"
+ depends on TLV
+ depends on PARAMETER
+ select PRINTF_HEXSTR
+ default y
+ help
+ Decoder support for the common barebox TLV format.
+
config STATE
bool "generic state infrastructure"
select CRC32
diff --git a/common/Makefile b/common/Makefile
index 9b67187561bf..3ff3d9ec98ba 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_RESET_SOURCE) += reset_source.o
obj-$(CONFIG_SHELL_HUSH) += hush.o
obj-$(CONFIG_SHELL_SIMPLE) += parser.o
obj-$(CONFIG_STATE) += state/
+obj-$(CONFIG_TLV) += tlv/
obj-$(CONFIG_RATP) += ratp/
obj-$(CONFIG_BOOTCHOOSER) += bootchooser.o
obj-$(CONFIG_UIMAGE) += uimage_types.o uimage.o
diff --git a/common/tlv/Makefile b/common/tlv/Makefile
new file mode 100644
index 000000000000..ea982f229a08
--- /dev/null
+++ b/common/tlv/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += parser.o bus.o register.o drv.o
+obj-$(CONFIG_TLV_DRV) += drv.o barebox.o
+obj-$(CONFIG_TLV_BAREBOX) += barebox.o
diff --git a/common/tlv/barebox.c b/common/tlv/barebox.c
new file mode 100644
index 000000000000..a5febc3b12c3
--- /dev/null
+++ b/common/tlv/barebox.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <net.h>
+#include <tlv/tlv.h>
+
+int tlv_handle_serial(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ const char *str;
+
+ str = __tlv_format_str(dev, map, len, val);
+ if (!str)
+ return -ENOMEM;
+
+ barebox_set_serial_number(str);
+ return 0;
+}
+
+int tlv_handle_eth_address(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ int i;
+
+ if (len % ETH_ALEN != 0)
+ return -EINVAL;
+
+ for (i = 0; i < len / ETH_ALEN; i++)
+ eth_register_ethaddr(i, val + i * ETH_ALEN);
+
+ return tlv_format_mac(dev, map, len, val);
+}
+
+int tlv_handle_eth_address_seq(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ u8 eth_addr[ETH_ALEN];
+ int eth_count;
+
+ eth_count = *val;
+
+ if (len != 1 + ETH_ALEN)
+ return -EINVAL;
+
+ memcpy(eth_addr, val + 1, ETH_ALEN);
+
+ for (int i = 0; i < eth_count; i++, eth_addr_inc(eth_addr)) {
+ eth_register_ethaddr(i, eth_addr);
+ tlv_format_mac(dev, map, ETH_ALEN, eth_addr);
+ }
+
+ return 0;
+}
+
+static const char *__tlv_format(struct tlv_device *dev, struct tlv_mapping *map, char *buf)
+{
+ struct param_d *param;
+ int ret;
+
+ if (!buf)
+ return NULL;
+
+ param = dev_add_param_fixed(&dev->dev, map->prop, NULL);
+ if (!IS_ERR(param))
+ param->value = buf; /* pass ownership */
+
+ ret = of_append_property(tlv_of_node(dev), map->prop, buf, strlen(buf) + 1);
+ if (ret)
+ return NULL;
+
+ return buf;
+}
+
+#define tlv_format(tlvdev, map, ...) ({ __tlv_format(tlvdev, map, basprintf(__VA_ARGS__)) ? 0 : -ENOMEM; })
+
+const char * __tlv_format_str(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ return __tlv_format(dev, map, basprintf("%.*s", len, val));
+}
+
+int tlv_format_str(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ return __tlv_format_str(dev, map, len, val) ? 0 : -ENOMEM;
+}
+
+int tlv_format_hex(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ return tlv_format(dev, map, "%*ph", len, val);
+}
+
+int tlv_format_blob(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ struct param_d *param;
+
+ param = dev_add_param_fixed(&dev->dev, map->prop, NULL);
+ if (!IS_ERR(param))
+ param->value = basprintf("%*phN", len, val);
+
+ return of_append_property(tlv_of_node(dev), map->prop, val, len);
+}
+
+static struct device_node *of_append_node(struct device_node *root, const char *name)
+{
+ struct device_node *np;
+
+ np = of_get_child_by_name(root, name);
+ if (np)
+ return np;
+
+ return of_new_node(root, name);
+}
+
+int tlv_format_mac(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ struct device_node *np = tlv_of_node(dev);
+ struct property *pp;
+ char propname[sizeof("address-4294967295")];
+ int base = 0, i, ret;
+
+ if (len % 6 != 0)
+ return -EINVAL;
+
+ np = of_append_node(np, map->prop);
+ if (!np)
+ return -ENOMEM;
+
+ for_each_property_of_node(np, pp)
+ base++;
+
+ for (i = base; i < base + len / 6; i++) {
+ snprintf(propname, sizeof(propname), "address-%u", i);
+ ret = of_property_sprintf(np, propname, "%*phC", 6, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int tlv_format_dec(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val)
+{
+ switch (len) {
+ case 1:
+ return tlv_format(dev, map, "%u", *(u8 *)val);
+ case 2:
+ return tlv_format(dev, map, "%u", get_unaligned_be16(val));
+ case 4:
+ return tlv_format(dev, map, "%u", get_unaligned_be32(val));
+ case 8:
+ return tlv_format(dev, map, "%llu", get_unaligned_be64(val));
+ default:
+ return tlv_format_hex(dev, map, len, val);
+ }
+}
+
+struct tlv_mapping barebox_tlv_v1_mappings[] = {
+ { 0x0002, tlv_format_str, "device-hardware-release" },
+ { 0x0003, tlv_format_dec, "factory-timestamp" },
+ { 0x0004, tlv_handle_serial, "device-serial-number"},
+ { 0x0005, tlv_format_dec, "modification" },
+ { 0x0006, tlv_format_str, "featureset" },
+ { 0x0007, tlv_format_str, "pcba-serial-number"},
+ { 0x0008, tlv_format_str, "pcba-hardware-release"},
+ { 0x0011, tlv_handle_eth_address, "ethernet-address" },
+ { 0x0012, tlv_handle_eth_address_seq, "ethernet-address" },
+ { /* sentintel */ },
+};
+
+static struct tlv_mapping *mappings[] = { barebox_tlv_v1_mappings, NULL };
+static struct of_device_id of_matches[] = {
+ { .compatible = "barebox,tlv-v1" },
+ { /* sentinel */}
+};
+
+static struct tlv_decoder barebox_tlv_v1 = {
+ .magic = TLV_MAGIC_BAREBOX_V1,
+ .driver.name = "barebox-tlv-v1",
+ .driver.of_compatible = of_matches,
+ .mappings = mappings,
+};
+
+static int tlv_register_default(void)
+{
+ return tlv_register_decoder(&barebox_tlv_v1);
+}
+device_initcall(tlv_register_default);
diff --git a/common/tlv/bus.c b/common/tlv/bus.c
new file mode 100644
index 000000000000..366f67120ec2
--- /dev/null
+++ b/common/tlv/bus.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <tlv/tlv.h>
+#include <linux/err.h>
+#include <of.h>
+
+static void tlv_devinfo(struct device *dev)
+{
+ struct tlv_device *tlvdev = to_tlv_device(dev);
+
+ printf("Magic: %08x\n", tlvdev->magic);
+}
+
+struct tlv_device *tlv_register_device(struct tlv_header *header,
+ struct device *parent)
+{
+ struct tlv_device *tlvdev;
+ const char *name = NULL;
+ char *buf = NULL;
+ struct device *dev;
+ static int id = 0;
+
+ tlvdev = xzalloc(sizeof(*tlvdev));
+
+ dev = &tlvdev->dev;
+
+ dev->bus = &tlv_bus;
+ devinfo_add(dev, tlv_devinfo);
+ dev->platform_data = header;
+ tlvdev->magic = be32_to_cpu(header->magic);
+ dev->parent = parent ?: tlv_bus.dev;
+ dev->id = DEVICE_ID_SINGLE;
+
+ if (parent)
+ name = of_alias_get(parent->device_node);
+ if (!name)
+ name = buf = basprintf("tlv%u", id++);
+
+ dev->device_node = of_new_node(of_new_node(NULL, NULL), name);
+ dev->device_node->dev = dev;
+ dev_set_name(dev, name);
+ register_device(dev);
+
+ free(buf);
+ return tlvdev;
+}
+
+void tlv_free_device(struct tlv_device *tlvdev)
+{
+ tlv_of_unregister_fixup(tlvdev);
+
+ devinfo_del(&tlvdev->dev, tlv_devinfo);
+ unregister_device(&tlvdev->dev);
+
+ free(tlvdev->dev.platform_data);
+ of_delete_node(tlvdev->dev.device_node);
+
+ free(tlvdev);
+}
+
+static int tlv_bus_match(struct device *dev, struct driver *drv)
+{
+ struct tlv_decoder *decoder = to_tlv_decoder(drv);
+ struct tlv_device *tlvdev = to_tlv_device(dev);
+
+ return decoder->magic == tlvdev->magic ? 0 : -1;
+}
+
+static int tlv_bus_probe(struct device *dev)
+{
+ return dev->driver->probe(dev);
+}
+
+static void tlv_bus_remove(struct device *dev)
+{
+ if (dev->driver->remove)
+ dev->driver->remove(dev);
+}
+
+struct bus_type tlv_bus = {
+ .name = "barebox-tlv",
+ .match = tlv_bus_match,
+ .probe = tlv_bus_probe,
+ .remove = tlv_bus_remove,
+};
+
+struct tlv_device *tlv_ensure_probed_by_alias(const char *alias)
+{
+ struct device_node *np;
+ struct device *dev;
+
+ np = of_find_node_by_alias(NULL, alias);
+ if (!np)
+ return ERR_PTR(-EINVAL);
+
+ of_partition_ensure_probed(np);
+
+ bus_for_each_device(&tlv_bus, dev) {
+ if (dev->parent && dev->parent->device_node == np)
+ return to_tlv_device(dev);
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static void tlv_bus_info(struct device *dev)
+{
+ struct driver *drv;
+
+ puts("Registered Magic:\n");
+ bus_for_each_driver(&tlv_bus, drv) {
+ struct tlv_decoder *tlvdrv = to_tlv_decoder(drv);
+
+ printf(" %08x (%s)\n", tlvdrv->magic, tlvdrv->driver.name);
+ }
+}
+
+static int tlv_bus_register(void)
+{
+ int ret;
+
+ ret = bus_register(&tlv_bus);
+ if (ret)
+ return ret;
+
+ devinfo_add(tlv_bus.dev, tlv_bus_info);
+
+ return 0;
+}
+postcore_initcall(tlv_bus_register);
diff --git a/common/tlv/drv.c b/common/tlv/drv.c
new file mode 100644
index 000000000000..baba30808683
--- /dev/null
+++ b/common/tlv/drv.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <driver.h>
+#include <tlv/tlv.h>
+#include <init.h>
+#include <of.h>
+#include <of_device.h>
+#include <stdio.h>
+
+static int barebox_tlv_probe(struct device *dev)
+{
+ const struct of_device_id *match;
+ struct tlv_device *tlvdev;
+ struct cdev *cdev;
+ char *backend_path;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ /*
+ * We only match with the device if this driver is the most specific match
+ * because we don't want to incorrectly bind to a device that has a more
+ * specific driver.
+ */
+ if (match && of_property_match_string(dev->of_node, "compatible",
+ match->compatible) != 0)
+ return -ENODEV;
+
+ cdev = cdev_by_device_node(dev->of_node);
+ if (!cdev)
+ return -EINVAL;
+
+ backend_path = basprintf("/dev/%s", cdev_name(cdev));
+
+ tlvdev = tlv_register_device_by_path(backend_path, dev);
+ free(backend_path);
+
+ return PTR_ERR_OR_ZERO(tlvdev);
+}
+
+static const __maybe_unused struct of_device_id tlv_ids[] = {
+ { .compatible = "barebox,tlv" },
+ { /* sentinel */ }
+};
+
+static struct driver barebox_tlv_driver = {
+ .name = "tlv",
+ .probe = barebox_tlv_probe,
+ .of_compatible = tlv_ids,
+};
+core_platform_driver(barebox_tlv_driver);
diff --git a/common/tlv/parser.c b/common/tlv/parser.c
new file mode 100644
index 000000000000..468eeafceda1
--- /dev/null
+++ b/common/tlv/parser.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "barebox-tlv: " fmt
+
+#include <common.h>
+#include <tlv/tlv.h>
+#include <fcntl.h>
+#include <libfile.h>
+#include <linux/stat.h>
+#include <crc.h>
+#include <net.h>
+
+int tlv_parse(struct tlv_device *tlvdev,
+ const struct tlv_decoder *decoder)
+{
+ const struct tlv *tlv = NULL;
+ struct tlv_mapping *map = NULL;
+ struct tlv_header *header = tlv_device_header(tlvdev);
+ u32 magic, size;
+ int ret = 0;
+ u32 crc = ~0;
+
+
+ magic = be32_to_cpu(header->magic);
+
+ size = tlv_total_len(header);
+
+ crc = crc32_be(crc, header, size - 4);
+ if (crc != tlv_crc(header)) {
+ pr_warn("Invalid CRC32. Should be %08x\n", crc);
+ return -EILSEQ;
+ }
+
+ for_each_tlv(header, tlv) {
+ struct tlv_mapping **mappings;
+ u16 tag = TLV_TAG(tlv);
+ int len = TLV_LEN(tlv);
+ const void *val = TLV_VAL(tlv);
+
+ pr_debug("[%04x] %*ph\n", tag, len, val);
+
+ for (mappings = decoder->mappings; *mappings; mappings++) {
+ for (map = *mappings; map->tag; map++) {
+ if (map->tag == tag)
+ goto done;
+ }
+ }
+
+done:
+ if (!map || !map->tag) {
+ if (tag)
+ pr_warn("skipping unknown tag: %04x\n", tag);
+ continue;
+ }
+
+ ret = map->handle(tlvdev, map, len, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return PTR_ERR_OR_ZERO(tlv);
+}
+
+struct tlv_device *tlv_register_device_by_path(const char *path, struct device *parent)
+{
+ struct tlv_header *header;
+ struct tlv_device *tlvdev;
+ size_t size;
+
+ header = tlv_read(path, &size);
+ if (IS_ERR(header))
+ return ERR_CAST(header);
+
+ tlvdev = tlv_register_device(header, parent);
+ if (IS_ERR(tlvdev))
+ free(header);
+
+ return tlvdev;
+}
+
+int of_tlv_fixup(struct device_node *root, void *ctx)
+{
+ struct device_node *chosen, *conf, *ethaddrs;
+ struct eth_ethaddr *addr;
+
+ chosen = of_create_node(root, "/chosen");
+ if (!chosen)
+ return -ENOMEM;
+
+ conf = of_copy_node(chosen, ctx);
+
+ ethaddrs = of_get_child_by_name(conf, "ethernet-address");
+ if (!ethaddrs)
+ return 0;
+
+ list_for_each_entry(addr, ðaddr_list, list) {
+ char propname[sizeof("address-4294967295")];
+ const u8 *enetaddr_a;
+ u8 enetaddr_b[ETH_ALEN];
+ struct property *pp;
+
+ if (!eth_of_get_fixup_node(root, NULL, addr->ethid))
+ continue;
+
+ snprintf(propname, sizeof(propname), "address-%u", addr->ethid);
+ pp = of_find_property(ethaddrs, propname, NULL);
+ if (!pp)
+ continue;
+
+ enetaddr_a = of_property_get_value(pp);
+ if (string_to_ethaddr(addr->ethaddr, enetaddr_b))
+ continue;
+
+ if (memcmp(enetaddr_a, enetaddr_b, ETH_ALEN))
+ continue;
+
+ of_delete_property(pp);
+ }
+
+ return 0;
+}
+
+int tlv_of_register_fixup(struct tlv_device *tlvdev)
+{
+ return of_register_fixup(of_tlv_fixup, tlv_of_node(tlvdev));
+}
+
+void tlv_of_unregister_fixup(struct tlv_device *tlvdev)
+{
+ of_unregister_fixup(of_tlv_fixup, tlv_of_node(tlvdev));
+}
+
+struct tlv_header *tlv_read(const char *filename, size_t *nread)
+{
+ struct tlv_header *header = NULL, *tmpheader;
+ int size, fd, ret;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return ERR_PTR(fd);
+
+ header = malloc(128);
+ if (!header) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = read_full(fd, header, sizeof(*header));
+ if (ret >= 0 && ret != sizeof(*header))
+ ret = -ENODATA;
+ if (ret < 0)
+ goto err;
+
+ size = tlv_total_len(header);
+
+ tmpheader = realloc(header, size);
+ if (!tmpheader) {
+ struct stat st;
+
+ ret = fstat(fd, &st);
+ if (ret)
+ ret = -EIO;
+ else if (size > st.st_size)
+ ret = -ENODATA;
+ else
+ ret = -ENOMEM;
+ goto err;
+ }
+ header = tmpheader;
+
+ ret = read_full(fd, header->tlvs, size - sizeof(*header));
+ if (ret < 0)
+ goto err;
+
+ /* file might have been truncated, but this will be handled
+ * in tlv_parse
+ */
+
+ if (nread)
+ *nread = sizeof(*header) + ret;
+
+ close(fd);
+ return header;
+err:
+ free(header);
+ close(fd);
+ return ERR_PTR(ret);
+}
+
+static struct tlv *__tlv_next(const struct tlv *tlv)
+{
+ return (void *)tlv + 4 + TLV_LEN(tlv);
+}
+
+struct tlv *tlv_next(const struct tlv_header *header,
+ const struct tlv *tlv)
+{
+ void *tlvs_start = (void *)&header->tlvs[0], *tlvs_end, *next_tlv;
+
+ tlv = tlv ? __tlv_next(tlv) : tlvs_start;
+
+ tlvs_end = tlvs_start + get_unaligned_be32(&header->length_tlv);
+ if (tlv == tlvs_end)
+ return NULL;
+
+ next_tlv = __tlv_next(tlv);
+ if (next_tlv > tlvs_end)
+ return ERR_PTR(-ENODATA);
+
+ return (void *)tlv;
+}
diff --git a/common/tlv/register.c b/common/tlv/register.c
new file mode 100644
index 000000000000..a6d95fb8e091
--- /dev/null
+++ b/common/tlv/register.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <linux/nvmem-consumer.h>
+#include <of.h>
+#include <tlv/tlv.h>
+#include <libfile.h>
+#include <fcntl.h>
+
+#include <linux/err.h>
+
+static int tlv_probe_from_magic(struct device *dev)
+{
+ struct tlv_device *tlvdev = to_tlv_device(dev);
+ int ret;
+
+ ret = tlv_parse(tlvdev, to_tlv_decoder(dev->driver));
+ if (ret)
+ return ret;
+
+ return tlv_of_register_fixup(tlvdev);
+}
+
+static int tlv_probe_from_compatible(struct device *dev)
+{
+ struct tlv_decoder *decoder = to_tlv_decoder(dev->driver);
+ struct tlv_header *header;
+ struct tlv_device *tlvdev;
+ struct cdev *cdev;
+ char *backend_path;
+ size_t size;
+ u32 magic;
+ int ret;
+
+ cdev = cdev_by_device_node(dev->of_node);
+ if (!cdev)
+ return -EINVAL;
+
+ backend_path = basprintf("/dev/%s", cdev_name(cdev));
+
+ header = tlv_read(backend_path, &size);
+ free(backend_path);
+
+ if (IS_ERR(header))
+ return PTR_ERR(header);
+
+ magic = be32_to_cpu(header->magic);
+ if (magic != decoder->magic) {
+ dev_err(dev, "got magic %08x, but %08x expected\n",
+ magic, decoder->magic);
+ ret = -EILSEQ;
+ goto err;
+ }
+
+ tlvdev = tlv_register_device(header, dev);
+ if (IS_ERR(tlvdev)) {
+ ret = PTR_ERR(tlvdev);
+ goto err;
+ }
+
+ return 0;
+err:
+ free(header);
+ return ret;
+}
+
+int tlv_register_decoder(struct tlv_decoder *decoder)
+{
+ int ret;
+
+ if (decoder->driver.bus)
+ return -EBUSY;
+
+ if (!decoder->driver.probe)
+ decoder->driver.probe = tlv_probe_from_magic;
+ decoder->driver.bus = &tlv_bus;
+
+ ret = register_driver(&decoder->driver);
+ if (ret)
+ return ret;
+
+ if (!decoder->driver.of_compatible)
+ return 0;
+
+ decoder->_platform_driver.name = basprintf("%s-pltfm", decoder->driver.name);
+ decoder->_platform_driver.of_compatible = decoder->driver.of_compatible;
+ decoder->_platform_driver.probe = tlv_probe_from_compatible;
+
+ return platform_driver_register(&decoder->_platform_driver);
+}
diff --git a/include/string.h b/include/string.h
index 986ccd83dd73..c16529c21273 100644
--- a/include/string.h
+++ b/include/string.h
@@ -45,4 +45,9 @@ static inline const char *nonempty(const char *s)
return isempty(s) ? NULL : s;
}
+static inline bool is_nul_terminated(const char *val, size_t len)
+{
+ return strnlen(val, len) != len;
+}
+
#endif /* __STRING_H */
diff --git a/include/tlv/format.h b/include/tlv/format.h
new file mode 100644
index 000000000000..a32ec917a434
--- /dev/null
+++ b/include/tlv/format.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+ *
+ * barebox TLVs are preceded by a 12 byte header: 4 bytes for magic,
+ * 4 bytes for TLV sequence length (in bytes) and 4 bytes for
+ * the length of the signature. Each tag consists of at least four
+ * bytes: 2 bytes for the tag and two bytes for the length (in bytes)
+ * and as many bytes as the length. The TLV sequence must be equal
+ * to the TLV sequence length in the header. It can be followed by a
+ * signature of the length described in the header. At the end
+ * is a (big-endian) CRC-32/MPEG-2 of the whole structure. All
+ * integers are in big-endian. Tags and magic have their MSB set
+ * if they are vendor-specific. The second MSB is 0 for tag
+ * and 1 for magic.
+ */
+
+#ifndef __TLV_FORMAT_H_
+#define __TLV_FORMAT_H_
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+#include <linux/build_bug.h>
+
+#define TLV_MAGIC_BAREBOX_V1 0x61bb95f2
+
+#define TLV_IS_VENDOR_SPECIFIC(val) ((*(u8 *)&(val) & 0x80) == 0x80)
+#define TLV_IS_GENERIC(val) ((*(u8 *)&(val) & 0x80) != 0x80)
+
+struct tlv {
+ /*
+ * _tag:15 (MSB): product specific
+ */
+ __be16 _tag;
+ __be16 _len; /* in bytes */
+ u8 _payload[];
+} __packed;
+
+
+#define TLV_TAG(tlv) get_unaligned_be16(&(tlv)->_tag)
+#define TLV_LEN(tlv) get_unaligned_be16(&(tlv)->_len)
+#define TLV_VAL(tlv) ((tlv)->_payload)
+
+struct tlv_header {
+ __be32 magic;
+ __be32 length_tlv; /* in bytes */
+ __be32 length_sig; /* in bytes */
+ struct tlv tlvs[];
+};
+static_assert(sizeof(struct tlv_header) == 3 * 4);
+
+#define for_each_tlv(tlv_head, tlv) \
+ for (tlv = tlv_next(tlv_head, NULL); !IS_ERR_OR_NULL(tlv); tlv = tlv_next(tlv_head, tlv))
+
+static inline size_t tlv_total_len(const struct tlv_header *header)
+{
+ return sizeof(struct tlv_header) + get_unaligned_be32(&header->length_tlv)
+ + get_unaligned_be32(&header->length_sig) + 4;
+}
+
+/*
+ * Retrieves the CRC-32/MPEG-2 CRC32 at the end of a TLV blob. Parameters:
+ * Poly=0x04C11DB7, Init=0xFFFFFFFF, RefIn=RefOut=false, XorOut=0x00000000
+ */
+static inline u32 tlv_crc(const struct tlv_header *header)
+{
+ return get_unaligned_be32((void *)header + tlv_total_len(header) - sizeof(__be32));
+}
+
+#endif
diff --git a/include/tlv/tlv.h b/include/tlv/tlv.h
new file mode 100644
index 000000000000..b1635fdbf500
--- /dev/null
+++ b/include/tlv/tlv.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __TLV_H_
+#define __TLV_H_
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <asm/unaligned.h>
+#include <unistd.h>
+#include <driver.h>
+
+#include <tlv/format.h>
+
+struct tlv *tlv_next(const struct tlv_header *header, const struct tlv *tlv);
+
+struct tlv_header *tlv_read(const char *filename, size_t *nread);
+
+struct device_node;
+struct tlv_device;
+struct tlv_mapping;
+
+struct tlv_mapping {
+ u16 tag;
+ int (*handle)(struct tlv_device *dev, struct tlv_mapping *map,
+ u16 len, const u8 *val);
+ const char *prop;
+};
+
+extern struct tlv_mapping barebox_tlv_v1_mappings[];
+
+extern const char *__tlv_format_str(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_format_str(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_format_dec(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_format_hex(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_format_mac(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_format_blob(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_handle_serial(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_handle_eth_address(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+extern int tlv_handle_eth_address_seq(struct tlv_device *dev, struct tlv_mapping *map, u16 len, const u8 *val);
+
+struct tlv_decoder {
+ u32 magic;
+ void *driverata;
+ struct tlv_mapping **mappings;
+ struct driver driver;
+ /* private members */
+ struct driver _platform_driver;
+ struct list_head list;
+};
+
+struct tlv_device {
+ struct device dev;
+ u32 magic;
+};
+
+static inline struct device_node *tlv_of_node(struct tlv_device *tlvdev)
+{
+ return tlvdev->dev.device_node;
+}
+
+struct tlv_device *tlv_register_device(struct tlv_header *header, struct device *parent);
+static inline struct tlv_header *tlv_device_header(struct tlv_device *tlvdev)
+{
+ return tlvdev->dev.platform_data;
+}
+
+void tlv_free_device(struct tlv_device *tlvdev);
+
+int tlv_register_decoder(struct tlv_decoder *decoder);
+
+int tlv_parse(struct tlv_device *tlvdev,
+ const struct tlv_decoder *decoder);
+
+int of_tlv_fixup(struct device_node *root, void *ctx);
+
+int tlv_of_register_fixup(struct tlv_device *tlvdev);
+void tlv_of_unregister_fixup(struct tlv_device *tlvdev);
+
+extern struct bus_type tlv_bus;
+
+static inline struct tlv_decoder *to_tlv_decoder(struct driver *drv)
+{
+ if (drv->bus == &tlv_bus)
+ return container_of(drv, struct tlv_decoder, driver);
+ if (drv->bus == &platform_bus)
+ return container_of(drv, struct tlv_decoder, _platform_driver);
+ return NULL;
+}
+
+static inline struct tlv_device *to_tlv_device(struct device *dev)
+{
+ return container_of(dev, struct tlv_device, dev);
+}
+
+struct tlv_device *tlv_register_device_by_path(const char *path, struct device *parent);
+struct tlv_device *tlv_ensure_probed_by_alias(const char *alias);
+
+#endif
diff --git a/test/self/Kconfig b/test/self/Kconfig
index 33e478aee882..33d05e4cf205 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -45,6 +45,7 @@ config SELFTEST_ENABLE_ALL
select SELFTEST_REGULATOR if REGULATOR_FIXED
select SELFTEST_TEST_COMMAND if CMD_TEST
select SELFTEST_IDR
+ select SELFTEST_TLV
help
Selects all self-tests compatible with current configuration
@@ -117,4 +118,10 @@ config SELFTEST_IDR
bool "idr selftest"
select IDR
+config SELFTEST_TLV
+ bool "TLV selftest"
+ select TLV
+ select BASE64
+ select BOARD_LXA
+
endif
diff --git a/test/self/Makefile b/test/self/Makefile
index 666a8f9ee7e2..8cd2a4c526c8 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SELFTEST_SETJMP) += setjmp.o
obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o
obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
obj-$(CONFIG_SELFTEST_IDR) += idr.o
+obj-$(CONFIG_SELFTEST_TLV) += tlv.o tlv.dtb.o
ifdef REGENERATE_KEYTOC
diff --git a/test/self/tlv.c b/test/self/tlv.c
new file mode 100644
index 000000000000..c22335f264c5
--- /dev/null
+++ b/test/self/tlv.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <crypto/rsa.h>
+#include <bselftest.h>
+#include <base64.h>
+#include <console.h>
+#include <tlv/tlv.h>
+
+BSELFTEST_GLOBALS();
+
+static const char cpu_tlv_encoded[] =
+"vCiN/gAAAH4AAAAAAAIAGGd1dGVmZWUyLUQwMS1SMDEtVjAxLUM/PwADAAgAAAAAZ+"
+"Z4XgAEAAswMDA0MC4wMDAxMAAFAAEAAAYACGJhc2UsUG9FAAcACzAwMDM2LjAwMDEwA"
+"AgAGGd1dGVmZWUyLVMwMS1SMDEtVjAxLUM/PwASAAcEGHTioAPA6zhwRw==";
+
+static const char io_tlv_encoded[] =
+"3KWocAAAAEQAAAAAAAMACAAAAABn5nheAAUAAQAABgAEYmFzZQAHAAswMDAzNy4wMDA"
+"xMAAIABhndXRlZmVlMi1TMDItUjAxLVYwMS1DPz+xJ7bM";
+
+static u8 *base64_decode_alloc(const char *encoded, size_t *len)
+{
+ size_t enclen = strlen(encoded);
+ u8 *buf = xmalloc(enclen);
+ *len = decode_base64(buf, enclen, encoded);
+ return buf;
+}
+
+static void assert_equal(struct device_node *a, struct device_node *b)
+{
+ int ret;
+
+ ret = of_diff(a, b, -1);
+ if (ret == 0)
+ return;
+
+ pr_warn("comparison of %s and %s failed: no differences expected, %u found.\n",
+ a->full_name, b->full_name, ret);
+ of_diff(a, b, 1);
+ failed_tests++;
+}
+
+static void test_lxa_tlv(void)
+{
+ extern char __dtb_tlv_start[], __dtb_tlv_end[];
+ struct tlv_device *cpu_tlvdev, *io_tlvdev;
+ size_t cpu_bloblen, io_bloblen;
+ void *cpu_blob = base64_decode_alloc(cpu_tlv_encoded, &cpu_bloblen);
+ void *io_blob = base64_decode_alloc(io_tlv_encoded, &io_bloblen);
+ struct device_node *expected, *np;
+
+ total_tests = 4;
+
+ expected = of_unflatten_dtb(__dtb_tlv_start,
+ __dtb_tlv_end - __dtb_tlv_start);
+ if (WARN_ON(IS_ERR(expected))) {
+ skipped_tests = total_tests;
+ return;
+ }
+
+ cpu_tlvdev = tlv_register_device(cpu_blob, NULL);
+ if (IS_ERR(cpu_tlvdev)) {
+ free(cpu_blob);
+ failed_tests++;
+ skipped_tests++;
+ }
+
+ io_tlvdev = tlv_register_device(io_blob, NULL);
+ if (IS_ERR(io_tlvdev)) {
+ free(io_blob);
+ failed_tests++;
+ skipped_tests++;
+ }
+
+ for_each_child_of_node(expected, np) {
+ if (!IS_ERR(cpu_tlvdev) && !strcmp(np->full_name, "/tlv0"))
+ assert_equal(tlv_of_node(cpu_tlvdev), np);
+ if (!IS_ERR(io_tlvdev) && !strcmp(np->full_name, "/tlv1"))
+ assert_equal(tlv_of_node(io_tlvdev), np);
+ }
+
+ if (!IS_ERR(cpu_tlvdev))
+ tlv_free_device(cpu_tlvdev);
+ if (!IS_ERR(io_tlvdev))
+ tlv_free_device(io_tlvdev);
+}
+bselftest(parser, test_lxa_tlv);
diff --git a/test/self/tlv.dts b/test/self/tlv.dts
new file mode 100644
index 000000000000..40906279bafd
--- /dev/null
+++ b/test/self/tlv.dts
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/dts-v1/;
+/ {
+ tlv0 {
+ device-hardware-release = "gutefee2-D01-R01-V01-C??";
+ factory-timestamp = "1743157342";
+ serial-number = "00040.00010";
+ modification = "0";
+ featureset = "base,PoE";
+ pcba-serial-number = "00036.00010";
+ pcba-hardware-release = "gutefee2-S01-R01-V01-C??";
+ ethernet-address {
+ address-0 = "18:74:e2:a0:03:c0";
+ address-1 = "18:74:e2:a0:03:c1";
+ address-2 = "18:74:e2:a0:03:c2";
+ address-3 = "18:74:e2:a0:03:c3";
+ };
+ };
+
+ tlv1 {
+ factory-timestamp = "1743157342";
+ modification = "0";
+ featureset = "base";
+ pcba-serial-number = "00037.00010";
+ pcba-hardware-release = "gutefee2-S02-R01-V01-C??";
+ };
+};
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 05/10] commands: add TLV debugging command
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (3 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 04/10] common: add barebox TLV support Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 06/10] scripts: add bareboxtlv host/target tool Ahmad Fatoum
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The TLV command does the equivalent of having a "barebox,tlv"
compatible node point at the file. It's meant as a debugging aid.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/Kconfig | 12 +++++++++++
commands/Makefile | 1 +
commands/tlv.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+)
create mode 100644 commands/tlv.c
diff --git a/commands/Kconfig b/commands/Kconfig
index ca3f13d7e0cc..229d0a634a8c 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2573,6 +2573,18 @@ config CMD_STATE
depends on STATE
prompt "state"
+config CMD_TLV
+ tristate
+ depends on TLV
+ prompt "tlv"
+ help
+ tlv - handle barebox TLV
+
+ Usage: tlv [-f] FILE
+
+ The TLV command does the equivalent of having a "barebox,tlv"
+ compatible node point at the file. It's meant as a debugging aid.
+
config CMD_DHRYSTONE
bool
prompt "dhrystone"
diff --git a/commands/Makefile b/commands/Makefile
index 8c2749b5ebdd..53337dfb5c2d 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_CMD_CMP) += cmp.o
obj-$(CONFIG_CMD_NV) += nv.o
obj-$(CONFIG_CMD_DEFAULTENV) += defaultenv.o
obj-$(CONFIG_CMD_STATE) += state.o
+obj-$(CONFIG_CMD_TLV) += tlv.o
obj-$(CONFIG_CMD_DHCP) += dhcp.o
obj-$(CONFIG_CMD_BOOTCHOOSER) += bootchooser.o
obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o
diff --git a/commands/tlv.c b/commands/tlv.c
new file mode 100644
index 000000000000..53fbc2a29186
--- /dev/null
+++ b/commands/tlv.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2022 Ahmad Fatoum, Pengutronix
+
+#include <common.h>
+#include <of.h>
+#include <command.h>
+#include <getopt.h>
+#include <tlv/tlv.h>
+
+static int do_tlv(int argc, char *argv[])
+{
+ const char *filename;
+ bool fixup = false;
+ struct tlv_device *tlvdev;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "f")) > 0) {
+ switch (opt) {
+ case 'f':
+ fixup = true;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ filename = argv[optind];
+ if (!filename)
+ return COMMAND_ERROR_USAGE;
+
+ tlvdev = tlv_register_device_by_path(argv[optind], NULL);
+ if (IS_ERR(tlvdev))
+ return PTR_ERR(tlvdev);
+
+ if (fixup)
+ return tlv_of_register_fixup(tlvdev);
+
+ of_print_nodes(tlv_of_node(tlvdev), 0, ~0);
+
+ tlv_free_device(tlvdev);
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(tlv)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-f", "Register device tree fixup for TLV")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(tlv)
+ .cmd = do_tlv,
+ BAREBOX_CMD_DESC("handle barebox TLV")
+ BAREBOX_CMD_OPTS("[-f] FILE")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_tlv_help)
+BAREBOX_CMD_END
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 06/10] scripts: add bareboxtlv host/target tool
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (4 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 05/10] commands: add TLV debugging command Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 07/10] boards: add decoder for LXA TLV v1 format Ahmad Fatoum
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In its simplest form, a barebox TLV has a 12-byte header, a sequence of
TLV entries, where each of tags and length are 2 bytes each (in big
endian), followed by a CRC.
The TLVs are easily generated. Add a tool to allow adding, removing and
verifying the header. A full fledged python-based tool is added in the
follow up commit.
Example uses:
hex2bin tlvs | ./scripts/bareboxtlv -m 0xcfe01001 >tlv.blob
./scripts/bareboxtlv -m 0xcfe01001 -v tlv.blob && echo CRC correct
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
scripts/.gitignore | 2 +
scripts/Makefile | 2 +
scripts/bareboxtlv-target.c | 1 +
scripts/bareboxtlv.c | 183 ++++++++++++++++++++++++++++++
scripts/include/asm/unaligned.h | 21 ++++
scripts/include/linux/build_bug.h | 8 ++
scripts/include/linux/stringify.h | 14 +++
7 files changed, 231 insertions(+)
create mode 100644 scripts/bareboxtlv-target.c
create mode 100644 scripts/bareboxtlv.c
create mode 100644 scripts/include/asm/unaligned.h
create mode 100644 scripts/include/linux/build_bug.h
create mode 100644 scripts/include/linux/stringify.h
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 1b760309583b..8c74d7c9f001 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -20,6 +20,8 @@ bareboxenv-target
kernel-install-target
bareboxcrc32-target
bareboximd-target
+bareboxtlv
+bareboxtlv-target
mk-am35xx-spi-image
mkimage
mxsimage
diff --git a/scripts/Makefile b/scripts/Makefile
index eb3884471bc9..85e081544553 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -10,6 +10,7 @@ hostprogs-always-y += bareboxenv
hostprogs-always-y += bareboxcrc32
hostprogs-always-y += kernel-install
hostprogs-always-$(CONFIG_QOICONV) += qoiconv
+hostprogs-always-$(CONFIG_TLV) += bareboxtlv
hostprogs-always-$(CONFIG_KEYTOC) += keytoc
HOSTCFLAGS_keytoc.o = `$(PKG_CONFIG) --cflags openssl`
HOSTLDLIBS_keytoc = `$(PKG_CONFIG) --libs openssl`
@@ -42,6 +43,7 @@ HOSTLDLIBS_rk-usb-loader = `$(PKG_CONFIG) --libs libusb-1.0`
hostprogs-always-$(CONFIG_RK_USB_LOADER) += rk-usb-loader
userprogs-always-$(CONFIG_BAREBOXENV_TARGET) += bareboxenv-target
+userprogs-always-$(CONFIG_BAREBOXTLV_TARGET) += bareboxtlv-target
userprogs-always-$(CONFIG_KERNEL_INSTALL_TARGET) += kernel-install-target
userprogs-always-$(CONFIG_BAREBOXCRC32_TARGET) += bareboxcrc32-target
userprogs-always-$(CONFIG_IMD_TARGET) += bareboximd-target
diff --git a/scripts/bareboxtlv-target.c b/scripts/bareboxtlv-target.c
new file mode 100644
index 000000000000..52437edef634
--- /dev/null
+++ b/scripts/bareboxtlv-target.c
@@ -0,0 +1 @@
+#include "bareboxtlv.c"
diff --git a/scripts/bareboxtlv.c b/scripts/bareboxtlv.c
new file mode 100644
index 000000000000..7b8dbfc1e845
--- /dev/null
+++ b/scripts/bareboxtlv.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2021 Ahmad Fatoum, Pengutronix
+
+/* bareboxtlv.c - generate a barebox TLV header */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include "compiler.h"
+#include <linux/stringify.h>
+
+#define debug(...)
+#define printk_once(...)
+
+#include "../include/tlv/format.h"
+#include "../crypto/crc32.c"
+
+#include "common.h"
+#include "common.c"
+
+static void usage(char *prgname)
+{
+ fprintf(stderr,
+ "USAGE: %s [FILE]\n"
+ "Manipulate barebox TLV blobs\n"
+ "\n"
+ "options:\n"
+ " -m TLV magic to use (default: " __stringify(TLV_MAGIC_BAREBOX_V1) " or input magic for -s)\n"
+ " -s strip input TLV header from output\n"
+ " (default is adding header to headerless input)\n"
+ " -@ offset to start reading at\n"
+ " -v verify CRC\n",
+ prgname);
+}
+
+static inline ssize_t write_full_verbose(int fd, const void *buf, size_t len)
+{
+ ssize_t ret;
+
+ ret = write_full(fd, buf, len);
+ if (ret < 0)
+ perror("write");
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ u32 magic = 0;
+ struct tlv_header *inhdr = NULL;
+ struct tlv_header hdr;
+ size_t offset = 0, size = 0;
+ ssize_t ret;
+ bool strip_header = false, verify = false;
+ int opt, ecode = EXIT_FAILURE;
+ int infd = STDIN_FILENO;
+ const u8 *p;
+ char *endptr;
+ void *buf;
+ u32 crc = ~0;
+
+ while((opt = getopt(argc, argv, "svm:@:h")) != -1) {
+ switch (opt) {
+ case 'v':
+ verify = true;
+ /* fallthrough; */
+ case 's':
+ strip_header = true;
+ break;
+ case 'm':
+ magic = strtoul(optarg, NULL, 16);
+ if (!magic) {
+ fprintf(stderr, "invalid magic: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case '@':
+ offset = strtoul(optarg, &endptr, 0);
+ if (endptr == optarg) {
+ fprintf(stderr, "invalid offset: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ }
+ }
+
+ if (argc - optind > 1) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (argv[optind]) {
+ infd = open(argv[optind], O_RDONLY);
+ if (infd < 0) {
+ perror("open");
+ return EXIT_FAILURE;
+ }
+ lseek(infd, offset, SEEK_SET);
+ } else if (offset) {
+ fprintf(stderr, "can't combine -@ with stdin\n");
+ return EXIT_FAILURE;
+ }
+
+ p = buf = read_fd(infd, &size);
+ if (!buf) {
+ perror("read_fd");
+ goto out;
+ }
+
+ /* validate, but skip, if header given */
+ if (strip_header) {
+ inhdr = buf;
+
+ if (size < tlv_total_len(inhdr)) {
+ fprintf(stderr, "short input: got %zu bytes, but header claims %zu\n",
+ size, tlv_total_len(inhdr));
+ goto out;
+ }
+
+ if (!magic)
+ magic = get_unaligned_be32(p);
+
+ p += sizeof(struct tlv_header);
+ size = cpu_to_be32(inhdr->length_tlv);
+
+ if (cpu_to_be32(inhdr->length_sig) != 0) {
+ fprintf(stderr, "Signatures not yet supported\n");
+ goto out;
+ }
+
+ }
+
+ if (!magic)
+ magic = TLV_MAGIC_BAREBOX_V1;
+
+ hdr.magic = cpu_to_be32(magic);
+ hdr.length_tlv = cpu_to_be32(size);
+ hdr.length_sig = cpu_to_be32(0);
+
+ if (!verify) {
+ if (!strip_header) {
+ ret = write_full_verbose(1, &hdr, sizeof(hdr));
+ if (ret < 0)
+ goto out;
+ }
+ ret = write_full_verbose(1, p, size);
+ if (ret < 0)
+ goto out;
+ }
+
+ crc = crc32_be(crc, &hdr, sizeof(hdr));
+ crc = crc32_be(crc, p, size);
+
+ if (verify) {
+ u32 oldcrc;
+
+ oldcrc = get_unaligned_be32(p + size);
+ if (oldcrc != crc) {
+ fprintf(stderr, "CRC mismatch: expected %08x, but calculated %08x\n",
+ oldcrc, crc);
+ goto out;
+ }
+ }
+
+ if (!strip_header) {
+ crc = cpu_to_be32(crc);
+
+ ret = write_full_verbose(1, &crc, sizeof(crc));
+ if (ret < 0)
+ goto out;
+ }
+
+ ecode = 0;
+out:
+ free(buf);
+ if (argv[optind])
+ close(infd);
+ return ecode;
+}
diff --git a/scripts/include/asm/unaligned.h b/scripts/include/asm/unaligned.h
new file mode 100644
index 000000000000..af6ccbc1bcf5
--- /dev/null
+++ b/scripts/include/asm/unaligned.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ASM_ARM_UNALIGNED_H
+#define _ASM_ARM_UNALIGNED_H
+
+#include "../../../include/linux/unaligned/le_byteshift.h"
+#include "../../../include/linux/unaligned/be_byteshift.h"
+#include "../../../include/linux/unaligned/generic.h"
+
+/*
+ * Select endianness
+ */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define get_unaligned __get_unaligned_be
+#define put_unaligned __put_unaligned_be
+#else
+#define get_unaligned __get_unaligned_le
+#define put_unaligned __put_unaligned_le
+#endif
+
+#endif /* _ASM_ARM_UNALIGNED_H */
diff --git a/scripts/include/linux/build_bug.h b/scripts/include/linux/build_bug.h
new file mode 100644
index 000000000000..e9bc857b15e8
--- /dev/null
+++ b/scripts/include/linux/build_bug.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BUILD_BUG_H
+#define _LINUX_BUILD_BUG_H
+
+#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
+#define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
+
+#endif /* _LINUX_BUILD_BUG_H */
diff --git a/scripts/include/linux/stringify.h b/scripts/include/linux/stringify.h
new file mode 100644
index 000000000000..55f6d04d48b1
--- /dev/null
+++ b/scripts/include/linux/stringify.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __LINUX_STRINGIFY_H
+#define __LINUX_STRINGIFY_H
+
+/* Indirect stringification. Doing two levels allows the parameter to be a
+ * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+#endif /* !__LINUX_STRINGIFY_H */
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 07/10] boards: add decoder for LXA TLV v1 format
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (5 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 06/10] scripts: add bareboxtlv host/target tool Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 08/10] scripts: Add Barebox TLV Generator Tooling Ahmad Fatoum
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The LXA TLV format is stored onto the EEPROM of either the Baseboard or
Powerboard. Its contents are mainly for use in Linux userspace.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
---
.../bindings/nvmem/barebox,tlv.yaml | 2 +
common/boards/Kconfig | 5 +
common/boards/Makefile | 1 +
common/boards/lxa/Makefile | 2 +
common/boards/lxa/factory-data.c | 132 ++++++++++++++++++
include/string.h | 2 +
include/tlv/format.h | 3 +
lib/string.c | 21 +++
8 files changed, 168 insertions(+)
create mode 100644 common/boards/lxa/Makefile
create mode 100644 common/boards/lxa/factory-data.c
diff --git a/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml b/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
index b54ab600e309..7a77b9a0b44b 100644
--- a/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
+++ b/Documentation/devicetree/bindings/nvmem/barebox,tlv.yaml
@@ -22,6 +22,8 @@ properties:
items:
- enum:
- barebox,tlv-v1 # magic: 0x61bb95f2
+ - lxa,tlv-baseboard-v1 # magic: 0xbc288dfe
+ - lxa,tlv-powerboard-v1 # magic: 0xdca5a870
- const: barebox,tlv
reg:
diff --git a/common/boards/Kconfig b/common/boards/Kconfig
index 586a54d7ca13..74947316954b 100644
--- a/common/boards/Kconfig
+++ b/common/boards/Kconfig
@@ -19,3 +19,8 @@ config BOARD_WOLFVISION
bool
select AIODEV
select ROCKCHIP_SARADC
+
+config BOARD_LXA
+ bool "LXA common board support" if COMPILE_TEST
+ select TLV
+ select TLV_BAREBOX
diff --git a/common/boards/Makefile b/common/boards/Makefile
index 3f8ac57b2f82..058733522411 100644
--- a/common/boards/Makefile
+++ b/common/boards/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_BOARD_QEMU_VIRT) += qemu-virt/
obj-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec/
obj-$(CONFIG_BOARD_TQ) += tq/
obj-$(CONFIG_BOARD_WOLFVISION) += wolfvision/
+obj-$(CONFIG_BOARD_LXA) += lxa/
diff --git a/common/boards/lxa/Makefile b/common/boards/lxa/Makefile
new file mode 100644
index 000000000000..29acfec3cb92
--- /dev/null
+++ b/common/boards/lxa/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TLV) += factory-data.o
diff --git a/common/boards/lxa/factory-data.c b/common/boards/lxa/factory-data.c
new file mode 100644
index 000000000000..cca7b624dbf1
--- /dev/null
+++ b/common/boards/lxa/factory-data.c
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <string.h>
+#include <tlv/tlv.h>
+#include <common.h>
+
+static int lxa_tlv_handle_serial(struct tlv_device *dev, struct tlv_mapping *map,
+ u16 len, const u8 *val)
+{
+ const char *buf;
+ char *period;
+
+ buf = __tlv_format_str(dev, map, len, val);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Strip /\d+\./ prefix to arrive at hostname without period */
+ period = memrchr(buf, '.', len);
+ barebox_set_serial_number(period ? period + 1 : buf);
+
+ return 0;
+}
+
+static int lxa_tlv_format_calib(struct tlv_device *dev, struct tlv_mapping *map,
+ u16 len, const u8 *val)
+{
+ return tlv_format_blob(dev, map, len, val);
+}
+
+static struct tlv_mapping lxa_tlv_baseboard_v1_mappings[] = {
+ /* vendor-specific override */
+ { 0x0004, lxa_tlv_handle_serial, "serial-number"},
+ /* vendor-specific additions */
+ { 0x8000, lxa_tlv_format_calib, "usb-host-curr" },
+ { 0x8001, lxa_tlv_format_calib, "usb-host1-curr" },
+ { 0x8002, lxa_tlv_format_calib, "usb-host2-curr" },
+ { 0x8003, lxa_tlv_format_calib, "usb-host3-curr" },
+ { 0x8010, lxa_tlv_format_calib, "out0-volt" },
+ { 0x8011, lxa_tlv_format_calib, "out1-volt" },
+ { 0x8020, lxa_tlv_format_calib, "iobus-curr" },
+ { 0x8021, lxa_tlv_format_calib, "iobus-volt" },
+ { /* sentintel */ },
+};
+
+static struct tlv_mapping *baseboard_mappings[] = {
+ lxa_tlv_baseboard_v1_mappings, barebox_tlv_v1_mappings, NULL
+};
+
+static struct of_device_id baseboard_matches[] = {
+ { .compatible = "lxa,tlv-baseboard-v1" },
+ { /* sentinel */ }
+};
+
+static struct tlv_decoder lxa_tlv_baseboard_v1 = {
+ .magic = TLV_MAGIC_LXA_BASEBOARD_V1,
+ .driver.name = "lxa-tlv-baseboard-v1",
+ .driver.of_compatible = baseboard_matches,
+ .mappings = baseboard_mappings,
+};
+
+static struct tlv_mapping lxa_tlv_powerboard_v1_mappings[] = {
+ { 0x0003, tlv_format_dec, "factory-timestamp" },
+ { 0x0005, tlv_format_dec, "modification" },
+ { 0x0006, tlv_format_str, "featureset" },
+ { 0x0007, tlv_format_str, "pcba-serial-number"},
+ { 0x0008, tlv_format_str, "pcba-hardware-release"},
+ { 0x9000, lxa_tlv_format_calib, "pwr-volt" },
+ { 0x9001, lxa_tlv_format_calib, "pwr-curr" },
+ { /* sentintel */ },
+};
+
+static struct tlv_mapping *powerboard_mappings[] = {
+ lxa_tlv_powerboard_v1_mappings, NULL
+};
+
+static struct of_device_id powerboard_matches[] = {
+ { .compatible = "lxa,tlv-powerboard-v1" },
+ { /* sentinel */ }
+};
+
+static struct tlv_decoder lxa_tlv_powerboard_v1 = {
+ .magic = TLV_MAGIC_LXA_POWERBOARD_V1,
+ .driver.name = "lxa-tlv-powerboard-v1",
+ .driver.of_compatible = powerboard_matches,
+ .mappings = powerboard_mappings,
+};
+
+
+static struct tlv_mapping lxa_tlv_ioboard_v1_mappings[] = {
+ { 0x0003, tlv_format_dec, "factory-timestamp" },
+ { 0x0005, tlv_format_dec, "modification" },
+ { 0x0006, tlv_format_str, "featureset" },
+ { 0x0007, tlv_format_str, "pcba-serial-number"},
+ { 0x0008, tlv_format_str, "pcba-hardware-release"},
+ { /* sentintel */ },
+};
+
+static struct tlv_mapping *ioboard_mappings[] = {
+ lxa_tlv_ioboard_v1_mappings, NULL
+};
+
+static struct of_device_id ioboard_matches[] = {
+ { .compatible = "lxa,tlv-ioboard-v1" },
+ { /* sentinel */ }
+};
+
+static struct tlv_decoder lxa_tlv_ioboard_v1 = {
+ .magic = TLV_MAGIC_LXA_IOBOARD_V1,
+ .driver.name = "lxa-tlv-ioboard-v1",
+ .driver.of_compatible = ioboard_matches,
+ .mappings = ioboard_mappings,
+};
+
+static int lxa_tlv_v1_register(void)
+{
+ struct tlv_decoder *const decoders[] = {
+ &lxa_tlv_baseboard_v1,
+ &lxa_tlv_powerboard_v1,
+ &lxa_tlv_ioboard_v1,
+ NULL
+ };
+ int err = 0;
+
+ for (struct tlv_decoder *const *d = decoders; *d; d++) {
+ int ret = tlv_register_decoder(*d);
+ if (ret)
+ err = ret;
+ }
+
+ return err;
+}
+postmmu_initcall(lxa_tlv_v1_register);
diff --git a/include/string.h b/include/string.h
index c16529c21273..004a9daece27 100644
--- a/include/string.h
+++ b/include/string.h
@@ -11,6 +11,8 @@ char *strsep_unescaped(char **, const char *);
char *stpcpy(char *dest, const char *src);
bool strends(const char *str, const char *postfix);
+void *memrchr(const void *s, int c, size_t n);
+
void *__default_memset(void *, int, __kernel_size_t);
void *__nokasan_default_memset(void *, int, __kernel_size_t);
diff --git a/include/tlv/format.h b/include/tlv/format.h
index a32ec917a434..29c8a6d42da2 100644
--- a/include/tlv/format.h
+++ b/include/tlv/format.h
@@ -22,6 +22,9 @@
#include <linux/build_bug.h>
#define TLV_MAGIC_BAREBOX_V1 0x61bb95f2
+#define TLV_MAGIC_LXA_BASEBOARD_V1 0xbc288dfe
+#define TLV_MAGIC_LXA_POWERBOARD_V1 0xc6895c21
+#define TLV_MAGIC_LXA_IOBOARD_V1 0xdca5a870
#define TLV_IS_VENDOR_SPECIFIC(val) ((*(u8 *)&(val) & 0x80) == 0x80)
#define TLV_IS_GENERIC(val) ((*(u8 *)&(val) & 0x80) != 0x80)
diff --git a/lib/string.c b/lib/string.c
index f2272be37e76..ff3408d3249e 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -818,6 +818,27 @@ void *memchr(const void *s, int c, size_t n)
#endif
EXPORT_SYMBOL(memchr);
+/**
+ * memrchr - Find last occurrence of character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the last occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void *memrchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n-- > 0) {
+ if ((unsigned char)c == p[n]) {
+ return (void *)(p+n);
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(memrchr);
+
/**
* skip_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 08/10] scripts: Add Barebox TLV Generator Tooling
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (6 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 07/10] boards: add decoder for LXA TLV v1 format Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 09/10] doc: Add User-Documentation for Barebox TLV Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 10/10] ARM: stm32mp: lxa: enable TLV support for TAC & FairyTux2 Ahmad Fatoum
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
From: Chris Fiege <cfi@pengutronix.de>
This host-tooling creates binaries in the Barebox TLV format.
It is intended to be used in the factory (either as script or library)
and creates a tlv binary from a given schema and data.
For convenience an example schema and dataset are provided.
Signed-off-by: Chris Fiege <cfi@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
.../bareboxtlv-generator.py | 321 ++++++++++++++++++
.../bareboxtlv-generator/data-example.yaml | 12 +
scripts/bareboxtlv-generator/requirements.txt | 2 +
.../bareboxtlv-generator/schema-example.yaml | 48 +++
4 files changed, 383 insertions(+)
create mode 100755 scripts/bareboxtlv-generator/bareboxtlv-generator.py
create mode 100644 scripts/bareboxtlv-generator/data-example.yaml
create mode 100644 scripts/bareboxtlv-generator/requirements.txt
create mode 100644 scripts/bareboxtlv-generator/schema-example.yaml
diff --git a/scripts/bareboxtlv-generator/bareboxtlv-generator.py b/scripts/bareboxtlv-generator/bareboxtlv-generator.py
new file mode 100755
index 000000000000..5f9285a80630
--- /dev/null
+++ b/scripts/bareboxtlv-generator/bareboxtlv-generator.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3
+
+import argparse
+import struct
+
+import yaml
+from crcmod.predefined import mkPredefinedCrcFun
+
+_crc32_mpeg = mkPredefinedCrcFun("crc-32-mpeg")
+
+
+class MaxSizeReachedException(Exception):
+ pass
+
+
+class FactoryDataset:
+ """
+ Generates TLV-encoded datasets that can be used with Barebox's
+ TLV support.
+
+ The generated binary structure looks like this:
+ #############################################################
+ # struct tlv { #
+ # be16 tag; /* 2 bytes */ #
+ # be16 len; /* 2 bytes */ #
+ # u8 payload[]; #
+ # }; #
+ # #
+ # struct binfile { #
+ # be32 magic_version; #
+ # be32 length_tlv; /* 4 bytes */ #
+ # be32 length_sig; /* 4 bytes */ #
+ # struct tlv tlvs[]; #
+ # be32 crc32; #
+ # }; #
+ #############################################################
+
+ Limitations:
+ * Signing is currently not supported and length_sig is always 0x0.
+ """
+
+ def __init__(self, schema):
+ """
+ Create a new Factory Dataset Object.
+
+ A ``schema`` is the python-world representation of the Barebox
+ ``struct tlv_mapping`` lists.
+ It describes the tags that can be used and define which
+ formatter must be used when (de-) serializing data.
+
+ Example schema:
+ | magic: 0x0fe01fca
+ | max_size: 0x100
+ |
+ | tags:
+ | factory-timestamp:
+ | tag: 0x0003
+ | format: "decimal"
+ | length: 8
+ | (...)
+
+ With:
+ * magic: The magic header for the TLV-encoded data.
+ * max_size: Maximum length of the TLV-encoded data in bytes.
+ If set the generated binary is tested to be less or equal this size after generation.
+ * tags: dict of tags that can be used for TLV-encoding.
+ * <key>: The human-readable name of this field.
+ * tag: (Mandatory) Tag-ID for use during TLV-encoding
+ * format: (Mandatory) Specifies the formatter to use
+ * length: (Mandatory for some formats) length of the field
+
+ Some remarks:
+ * Additional keys in a tag will be ignored. This can be used to add examples or annotations
+ to an entry.
+ * Tag-IDs can be used in multiple entries inside a schema with different human-readable names.
+ This only works reasonable if the Barebox parser knows how to handle it.
+ This will work well during encoding. But: It will need the same special knowledge for decoding.
+ Otherwise, only the last occurrence will be kept.
+ * The EEPROM will be written in the order of entries in the input data.
+
+ Supported Barebox TLV data formats:
+ * string:
+ - Arbitrary length string (up to 2^16-1 bytes)
+ - UTF-8
+ - Schema example:
+ | device-hardware-release:
+ | tag: 0x0002
+ | format: "string"
+ * decimal:
+ - unsigned int
+ - length is required and must be in [1, 2, 4, 8]
+ - Schema example:
+ | modification:
+ | tag: 0x0005
+ | format: decimal
+ | length: 1
+ * mac-sequence:
+ - Describes a consecutive number of MAC addresses
+ - Contains a starting address and a count
+ - Input data must be an iterable in the following format: (first_mac: int, count: int)
+ - MAC-addresses are represented as python ints
+ - Schema example:
+ | ethernet-address:
+ | tag: 0x0012
+ | format: "mac-sequence"
+ * mac-list:
+ - A list of MAC addresses
+ - Input data must be an iterable of MAC addresses: (first_mac: int, second_mac: int, ...)
+ - MAC-addresses are represented as python ints
+ - Schema example:
+ | ethernet-address:
+ | tag: 0x0012
+ | format: "mac-list"
+ * linear-calibration
+ - Linear calibration data for analog channels
+ - Input data must be an iterable of floats: (c1: float, c2: float, ...)
+ - Values are stored as a 4-byte float
+ - Length-field is mandatory.
+ - Schema example:
+ | usb-host1-curr:
+ | tag: 0x8001
+ | format: linear-calibration
+ | length: 2
+ """
+ self.schema = schema
+
+ def encode(self, data):
+ """
+ Generate an EEPROM image for the given data.
+
+ Data must be a dict using fields described in the schema.
+ """
+ # build payload of TLVs
+ tlvs = b""
+
+ for name, value in data.items():
+ if name not in self.schema["tags"]:
+ raise KeyError(f"No Schema defined for data with name '{name}'")
+
+ tag = self.schema["tags"][name]
+ tag_format = tag["format"]
+
+ if tag_format == "string":
+ bin = value.encode("UTF-8")
+ fmt = f"{len(bin)}s"
+ if len(bin) > 2**16 - 1:
+ raise ValueError(f"String {name} is too long!")
+
+ elif tag_format == "decimal":
+ fmtl = tag["length"]
+ if fmtl == 1:
+ fmt = "B"
+ elif fmtl == 2:
+ fmt = "H"
+ elif fmtl == 4:
+ fmt = "I"
+ elif fmtl == 8:
+ fmt = "Q"
+ else:
+ raise ValueError(f"Decimal {name} has invalid len {fmtl}. Must be in [1, 2, 4, 8]!")
+ bin = abs(int(value))
+
+ elif tag_format == "mac-sequence":
+ if len(value) != 2:
+ raise ValueError(f"mac-sequence {name} must be in format (base_mac: int, count: int)")
+ fmt = "7s"
+ mac = struct.pack(">Q", value[0])[2:]
+ bin = struct.pack(">B6s", value[1], mac)
+
+ elif tag_format == "mac-list":
+ bin = b""
+ for mac in value:
+ bin += struct.pack(">Q", mac)[2:]
+ fmt = f"{len(value) * 6}s"
+
+ elif tag_format == "calibration":
+ bin = b""
+ if len(value) != tag["length"]:
+ raise ValueError(f'linear-calibration {name} must have {tag["length"]} elements.')
+ for coeff in value:
+ bin += struct.pack(">f", coeff)
+ fmt = f"{len(value) * 4}s"
+
+ else:
+ raise Exception(f"{name}: Unknown format {tag_format}")
+
+ tlvs += struct.pack(f">HH{fmt}", tag["tag"], struct.calcsize(fmt), bin)
+
+ # Convert the framing data.
+ len_sig = 0x0 # Not implemented.
+ header = struct.pack(">3I", self.schema["magic"], len(tlvs), len_sig)
+ crc_raw = _crc32_mpeg(header + tlvs)
+ crc = struct.pack(">I", crc_raw)
+ bin = header + tlvs + crc
+
+ # Check length
+ if "max_size" in self.schema and len(bin) > self.schema["max_size"]:
+ raise MaxSizeReachedException(
+ f'Generated binary too large. Is {len(bin)} bytes but max_size is {self.schema["max_size"]}.'
+ )
+ return bin
+
+ def decode(self, bin):
+ """
+ Decode a TLV-encoded binary image.
+
+ Returns a dict with entries decoded from the binary.
+ """
+ # check minimal length
+ values = {}
+
+ if len(bin) < 16:
+ raise ValueError("Supplied binary is too small to be TLV-encoded data.")
+
+ bin_magic, bin_tlv_len, bin_sig_len = struct.unpack(">3I", bin[:12])
+ # check magic
+ if bin_magic != self.schema["magic"]:
+ raise ValueError(f'Magic missmatch. Is {hex(bin_magic)} but expected {hex(self.schema["magic"])}')
+
+ # check signature length
+ if bin_sig_len != 0:
+ raise ValueError("Signature check is not supported!")
+
+ # check crc
+ crc_offset = 12 + bin_tlv_len + bin_sig_len
+ if crc_offset > len(bin) - 4:
+ raise ValueError("crc location calculated behind binary.")
+ bin_crc = struct.unpack(">I", bin[crc_offset : crc_offset + 4])[0] # noqa E203
+ calc_crc = _crc32_mpeg(bin[:crc_offset])
+ if bin_crc != calc_crc:
+ raise ValueError(f"CRC missmatch. Is {hex(bin_crc)} but expected {hex(calc_crc)}.")
+
+ ptr = 12
+ while ptr < crc_offset:
+ tag_id, tag_len = struct.unpack_from(">HH", bin, ptr)
+ data_ptr = ptr + 4
+ ptr += tag_len + 4
+
+ name, tag_schema = self._get_tag_by_id(tag_id)
+ if not name:
+ print(f"Skipping unknown tag-id {hex(tag_id)}.")
+ continue
+
+ if tag_schema["format"] == "decimal":
+ if tag_len == 1:
+ fmt = ">B"
+ elif tag_len == 2:
+ fmt = ">H"
+ elif tag_len == 4:
+ fmt = ">I"
+ elif tag_len == 8:
+ fmt = ">Q"
+ else:
+ raise ValueError(f"Decimal {name} has invalid len {tag_len}. Must be in [1, 2, 4, 8]!")
+ value = struct.unpack_from(fmt, bin, data_ptr)[0]
+ elif tag_schema["format"] == "string":
+ value = bin[data_ptr : data_ptr + tag_len].decode("UTF-8") # noqa E203
+ elif tag_schema["format"] == "mac-sequence":
+ if tag_len != 7:
+ raise ValueError(f"Tag {name} has wrong length {hex(tag_len)} but expected 0x7.")
+ count, base_mac = struct.unpack_from(">B6s", bin, data_ptr)
+ base_mac = struct.unpack(">Q", b"\x00\x00" + base_mac)[0]
+ value = [base_mac, count]
+ elif tag_schema["format"] == "mac-list":
+ if tag_len % 6 != 0:
+ raise ValueError(f"Tag {name} has wrong length {hex(tag_id)}. Must be multiple of 0x6.")
+ value = []
+ for i in range(int(tag_len / 6)):
+ mac = struct.unpack_from(">6s", bin, data_ptr + int(i * 6))[0]
+ mac = struct.unpack(">Q", b"\x00\x00" + mac)[0]
+ value.append(mac)
+ elif tag_schema["format"] == "calibration":
+ if tag_len % 4 != 0:
+ raise ValueError(f"Tag {name} has wrong length {hex(tag_id)}. Must be multiple of 0x4.")
+ value = []
+ for i in range(int(tag_len / 4)):
+ coeff = struct.unpack_from(">f", bin, data_ptr + int(i * 4))[0]
+ value.append(coeff)
+ else:
+ raise Exception(f'{name}: Unknown format {tag_schema["format"]}')
+ values[name] = value
+
+ return values
+
+ def _get_tag_by_id(self, tag_id):
+ for name, tag_schema in self.schema["tags"].items():
+ if tag_schema["tag"] == tag_id:
+ return name, tag_schema
+ return None, None
+
+
+def _main():
+ parser = argparse.ArgumentParser(description="Generate a TLV dataset for the Barebox TLV parser")
+ parser.add_argument("schema", help="YAML file describing the data.")
+ parser.add_argument("--input-data", help="YAML file containing data to write to the binary.")
+ parser.add_argument("--output-data", help="YAML file where the contents of the binary will be written to.")
+ parser.add_argument("binary", help="Path to where export data to be copied into DUT's EEPROM.")
+ args = parser.parse_args()
+
+ # load schema
+ with open(args.schema) as schema_fh:
+ schema = yaml.load(schema_fh, Loader=yaml.SafeLoader)
+ eeprom = FactoryDataset(schema)
+
+ if args.input_data:
+ with open(args.input_data) as d_fh:
+ data = yaml.load(d_fh, Loader=yaml.SafeLoader)
+ bin = eeprom.encode(data)
+ with open(args.binary, "wb+") as out_fh:
+ out_fh.write(bin)
+
+ if args.output_data:
+ with open(args.binary, "rb") as in_fh:
+ bin = in_fh.read()
+ data = eeprom.decode(bin)
+ with open(args.output_data, "w+") as out_fh:
+ yaml.dump(data, out_fh)
+
+
+if __name__ == "__main__":
+ _main()
diff --git a/scripts/bareboxtlv-generator/data-example.yaml b/scripts/bareboxtlv-generator/data-example.yaml
new file mode 100644
index 000000000000..17c787546204
--- /dev/null
+++ b/scripts/bareboxtlv-generator/data-example.yaml
@@ -0,0 +1,12 @@
+factory-timestamp: 1636451762
+modification: 0
+featureset: "base"
+pcba-serial-number: "12345.67890"
+pcba-hardware-release: "device-S01-R02-V03-C04"
+pwr-volt:
+- 1.0
+- 0.0
+pwr-curr:
+- 1.0
+- 0.0
+
diff --git a/scripts/bareboxtlv-generator/requirements.txt b/scripts/bareboxtlv-generator/requirements.txt
new file mode 100644
index 000000000000..a1f7e3b3f2a3
--- /dev/null
+++ b/scripts/bareboxtlv-generator/requirements.txt
@@ -0,0 +1,2 @@
+crcmod
+pyaml
diff --git a/scripts/bareboxtlv-generator/schema-example.yaml b/scripts/bareboxtlv-generator/schema-example.yaml
new file mode 100644
index 000000000000..4927b869513d
--- /dev/null
+++ b/scripts/bareboxtlv-generator/schema-example.yaml
@@ -0,0 +1,48 @@
+magic: 0xc6895c21
+max_size: 0x100
+
+tags:
+ factory-timestamp:
+ tag: 0x0003
+ format: "decimal"
+ length: 8
+ example: 1636451762
+
+ modification:
+ tag: 0x0005
+ format: decimal
+ length: 1
+ example: 42
+ purpose: "0: Device unmodified; 1: Device has undocumented modifications"
+
+ featureset:
+ tag: 0x0006
+ format: "string"
+ example: "base"
+ purpose: For later use. May encode a set of features of this device.
+
+ pcba-serial-number:
+ tag: 0x0007
+ format: "string"
+ example: "12345.67890"
+ purpose: Serial number of device
+
+ pcba-hardware-release:
+ tag: 0x0008
+ format: "string"
+ example: "device-S01-R02-V03-C04"
+ purpose: Detailed release information for device
+
+ pwr-volt:
+ tag: 0x9000
+ format: calibration
+ length: 2
+ example: "(485.0032, -42.23)"
+ purpose: (Gain, Offset) tuple of floats for analog calibration of a measurement
+
+ pwr-curr:
+ tag: 0x9001
+ format: calibration
+ length: 2
+ example: "(485.0032, -42.23)"
+ purpose: (Gain, Offset) tuple of floats for analog calibration of a measurement
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 09/10] doc: Add User-Documentation for Barebox TLV
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (7 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 08/10] scripts: Add Barebox TLV Generator Tooling Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 10/10] ARM: stm32mp: lxa: enable TLV support for TAC & FairyTux2 Ahmad Fatoum
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
From: Chris Fiege <cfi@pengutronix.de>
With this stub users can hopefully discover the Barebox TLV
functionality.
Additionally it discusses some key concepts of the implementation, that
are not obvious from the actual implementations.
Signed-off-by: Chris Fiege <cfi@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
Documentation/user/barebox-tlv.rst | 93 ++++++++++++++++++++++++++++++
Documentation/user/user-manual.rst | 1 +
2 files changed, 94 insertions(+)
create mode 100644 Documentation/user/barebox-tlv.rst
diff --git a/Documentation/user/barebox-tlv.rst b/Documentation/user/barebox-tlv.rst
new file mode 100644
index 000000000000..3db13ec88b22
--- /dev/null
+++ b/Documentation/user/barebox-tlv.rst
@@ -0,0 +1,93 @@
+barebox TLV - Non-Volatile Factory Data Storage
+===============================================
+
+barebox TLV is a system to store and retrieve a device's (read-only)
+meta-data from non-volatile memory.
+It is intended to handle information that are usually only set in
+the factory - like serial number, MAC-addresses, analog calibration
+data, etc.
+Data is stored in a tag-length-value format (hence the name) and read
+from non-volatile memory during startup.
+Unpacked values are stored in the devicetree ``chosen``-node.
+
+barebox TLV consists of two components:
+
+* The parser inside barebox (``common/tlv``).
+ This code parses the non-volatile memory during startup.
+* A Python-Tool (``scripts/bareboxtlv-generator``).
+ This script is intended to generate the TLV binaries in the factory.
+
+Data Format
+-----------
+
+The TLV binary has the following format:
+
+.. code-block:: C
+
+ struct tlv {
+ be16 tag; /* 2 bytes */
+ be16 len; /* 2 bytes */
+ u8 payload[];
+ };
+
+ struct binfile {
+ be32 magic_version;
+ be32 length_tlv; /* 4 bytes */
+ be32 length_sig; /* 4 bytes */
+ struct tlv tlvs[];
+ be32 crc32;
+ };
+
+.. note::
+ Even though the header has a ``length_sig`` field,
+ there is currently no support for cryptographic signatures.
+
+Tags
+----
+
+Tags are defined as 32-bit integers.
+A tag defines the following attributes:
+
+* **Data format:**
+ The data format can be something like ``string``, ``decimal`` or
+ ``serial-number``.
+* **Name of the entry:**
+ This is the name under which the value is stored in ``chosen``-node.
+
+The tag range ``0x0000`` to ``0x7FFF`` is intended for common tags,
+that can be used in every schema.
+These common tags are defined in ``common/tlv/barebox.c``.
+
+The tag range ``0x8000`` to ``0xFFFF`` is intended for custom extensions.
+Parsing must be handled by board-specific extensions.
+
+Data Types
+----------
+
+barebox defines a number of common data types.
+These are defined in ``common/tlv/barebox.c``.
+
+Board-specific extensions can add additional data types as needed.
+
+TLV Binary Generation
+---------------------
+
+To generate a TLV binary the schema for the specific TLV must be defined.
+Schemas are yaml-files that represent what the actual parser in barebox expects.
+
+An example schema can be found in ``srcipts/bareboxtlv-generator/schema-example.yaml``.
+This schema defines some well-known tags and two board-specific tags.
+
+Afterwards another yaml-file with the data for the TLV binary is needed.
+An example can be found in ``srcipts/bareboxtlv-generator/data-example.yaml``.
+
+With these information in place a TLV binary can be created:
+
+.. code-block:: shell
+
+ ./bareboxtlv-generator.py --input-data data-example.yaml \
+ schema-example.yaml tlv.bin
+
+.. note::
+ The ``FactoryDataset`` class in ``bareboxtlv-generator.py``
+ is intended to be used as a library.
diff --git a/Documentation/user/user-manual.rst b/Documentation/user/user-manual.rst
index 83ba9e4c3505..53a94f4e2933 100644
--- a/Documentation/user/user-manual.rst
+++ b/Documentation/user/user-manual.rst
@@ -39,6 +39,7 @@ Contents:
watchdog
reboot-mode
virtio
+ barebox-tlv
* :ref:`search`
* :ref:`genindex`
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 10/10] ARM: stm32mp: lxa: enable TLV support for TAC & FairyTux2
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
` (8 preceding siblings ...)
2025-04-11 7:40 ` [PATCH 09/10] doc: Add User-Documentation for Barebox TLV Ahmad Fatoum
@ 2025-04-11 7:40 ` Ahmad Fatoum
9 siblings, 0 replies; 11+ messages in thread
From: Ahmad Fatoum @ 2025-04-11 7:40 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Now that TLV support is in place and the LXA-specific parsers have been
added, enable them in the respective board code.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/boards/lxa-fairytux2/board.c | 8 ++++++++
arch/arm/boards/lxa-tac/board.c | 8 ++++++++
arch/arm/dts/stm32mp153c-lxa-fairytux2-gen2.dts | 1 +
arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi | 1 +
arch/arm/dts/stm32mp15xc-lxa-tac.dtsi | 2 ++
arch/arm/mach-stm32mp/Kconfig | 2 ++
6 files changed, 22 insertions(+)
diff --git a/arch/arm/boards/lxa-fairytux2/board.c b/arch/arm/boards/lxa-fairytux2/board.c
index 2dc625c40408..5c5a76353d32 100644
--- a/arch/arm/boards/lxa-fairytux2/board.c
+++ b/arch/arm/boards/lxa-fairytux2/board.c
@@ -7,13 +7,21 @@
#include <bootsource.h>
#include <deep-probe.h>
#include <of.h>
+#include <tlv/tlv.h>
static int fairytux2_probe(struct device *dev)
{
+ struct tlv_device *tlvcpu;
+
barebox_set_hostname("fairytux2");
stm32mp_bbu_mmc_fip_register("mmc", "/dev/mmc1", BBU_HANDLER_FLAG_DEFAULT);
+ tlvcpu = tlv_ensure_probed_by_alias("baseboard-factory-data");
+ if (!IS_ERR(tlvcpu))
+ dev_info(dev, "HW release: %s\n",
+ dev_get_param(&tlvcpu->dev, "device-hardware-release"));
+
return 0;
}
diff --git a/arch/arm/boards/lxa-tac/board.c b/arch/arm/boards/lxa-tac/board.c
index 96ffc46500bf..e513175c1b04 100644
--- a/arch/arm/boards/lxa-tac/board.c
+++ b/arch/arm/boards/lxa-tac/board.c
@@ -7,13 +7,21 @@
#include <bootsource.h>
#include <deep-probe.h>
#include <of.h>
+#include <tlv/tlv.h>
static int tac_probe(struct device *dev)
{
+ struct tlv_device *tlvcpu;
+
barebox_set_hostname("lxatac");
stm32mp_bbu_mmc_fip_register("mmc", "/dev/mmc1", BBU_HANDLER_FLAG_DEFAULT);
+ tlvcpu = tlv_ensure_probed_by_alias("baseboard-factory-data");
+ if (!IS_ERR(tlvcpu))
+ dev_info(dev, "HW release: %s\n",
+ dev_get_param(&tlvcpu->dev, "device-hardware-release"));
+
return 0;
}
diff --git a/arch/arm/dts/stm32mp153c-lxa-fairytux2-gen2.dts b/arch/arm/dts/stm32mp153c-lxa-fairytux2-gen2.dts
index 2040959965f3..fe549db3f30a 100644
--- a/arch/arm/dts/stm32mp153c-lxa-fairytux2-gen2.dts
+++ b/arch/arm/dts/stm32mp153c-lxa-fairytux2-gen2.dts
@@ -19,6 +19,7 @@ partitions {
#size-cells = <1>;
io_board_factory_data: factory-data@0 {
+ compatible = "lxa,tlv-ioboard-v1";
reg = <0 0x100>;
label = "tlv";
};
diff --git a/arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi b/arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi
index 12a7c290dd0b..a5a5f6296558 100644
--- a/arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi
+++ b/arch/arm/dts/stm32mp153c-lxa-fairytux2.dtsi
@@ -96,6 +96,7 @@ partitions {
#size-cells = <1>;
baseboard_factory_data: factory-data@0 {
+ compatible = "lxa,tlv-baseboard-v1";
reg = <0 0x400>;
label = "tlv";
};
diff --git a/arch/arm/dts/stm32mp15xc-lxa-tac.dtsi b/arch/arm/dts/stm32mp15xc-lxa-tac.dtsi
index 32f42e4b04d8..3437e81cc1af 100644
--- a/arch/arm/dts/stm32mp15xc-lxa-tac.dtsi
+++ b/arch/arm/dts/stm32mp15xc-lxa-tac.dtsi
@@ -88,6 +88,7 @@ partitions {
#size-cells = <1>;
baseboard_factory_data: factory-data@0 {
+ compatible = "lxa,tlv-baseboard-v1";
reg = <0 0x400>;
label = "tlv";
};
@@ -101,6 +102,7 @@ partitions {
#size-cells = <1>;
powerboard_factory_data: factory-data@0 {
+ compatible = "lxa,tlv-powerboard-v1";
reg = <0 0x100>;
label = "tlv";
};
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
index ad6c4e202fc1..5ea0c4004f2c 100644
--- a/arch/arm/mach-stm32mp/Kconfig
+++ b/arch/arm/mach-stm32mp/Kconfig
@@ -32,10 +32,12 @@ config MACH_LXA_MC1
config MACH_LXA_TAC
select ARCH_STM32MP157
bool "Linux Automation TAC board"
+ select BOARD_LXA
config MACH_LXA_FAIRYTUX2
select ARCH_STM32MP157
bool "Linux Automation FairyTux 2"
+ select BOARD_LXA
config MACH_SEEED_ODYSSEY
select ARCH_STM32MP157
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-04-11 9:13 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-04-11 7:40 [PATCH 00/10] Add barebox TLV infrastructure Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 01/10] net: factor out eth_of_get_fixup_node Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 02/10] net: export list of registered ethernet addresses Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 03/10] common: add optional systemd.hostname generation Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 04/10] common: add barebox TLV support Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 05/10] commands: add TLV debugging command Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 06/10] scripts: add bareboxtlv host/target tool Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 07/10] boards: add decoder for LXA TLV v1 format Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 08/10] scripts: Add Barebox TLV Generator Tooling Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 09/10] doc: Add User-Documentation for Barebox TLV Ahmad Fatoum
2025-04-11 7:40 ` [PATCH 10/10] ARM: stm32mp: lxa: enable TLV support for TAC & FairyTux2 Ahmad Fatoum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox