[PATCH v2 4/5] board: freebox: nbx10g: add device serial and MAC address initialization

Stefan Roese stefan.roese at mailbox.org
Fri May 8 13:11:51 CEST 2026


On 4/21/26 11:04, Vincent Jardin wrote:
> Read device identification data from a dedicated eMMC region. This
> provides:
> 
> - Unique device serial number for identification and tracking
> - Factory-programmed MAC address for network interfaces
> - Bundle information for device variant identification
> 
> The serial structure includes CRC32 validation to detect corruption.
> On read failure or invalid data, sensible defaults are used to ensure
> the system remains bootable.
> 
> The fbxserial command provides two subcommands:
> - fbxserial show: Display serial info (default)
> - fbxserial init: Initialize ethaddr from serial info
> 
> Use CONFIG_PREBOOT="fbxserial init" to automatically set MAC addresses
> during boot. This approach avoids patching shared board code.
> 
> Signed-off-by: Vincent Jardin <vjardin at free.fr>

Reviewed-by: Stefan Roese <stefan.roese at mailbox.org>

Thanks,
Stefan


> ---
>   board/freebox/nbx10g/Kconfig         |  29 +++
>   board/freebox/nbx10g/Makefile        |   1 +
>   board/freebox/nbx10g/nbx_fbxserial.c | 286 +++++++++++++++++++++++++++
>   board/freebox/nbx10g/nbx_fbxserial.h | 156 +++++++++++++++
>   4 files changed, 472 insertions(+)
>   create mode 100644 board/freebox/nbx10g/nbx_fbxserial.c
>   create mode 100644 board/freebox/nbx10g/nbx_fbxserial.h
> 
> diff --git a/board/freebox/nbx10g/Kconfig b/board/freebox/nbx10g/Kconfig
> index d21153eae75..958c8fdd4c3 100644
> --- a/board/freebox/nbx10g/Kconfig
> +++ b/board/freebox/nbx10g/Kconfig
> @@ -62,4 +62,33 @@ config NBX_MMC_PART_BANK1_SIZE
>   
>   endif
>   
> +config CMD_NBX_FBXSERIAL
> +	bool "fbxserial command"
> +	depends on MMC_SDHCI_XENON
> +	help
> +	  Enable the fbxserial command to read and display device
> +	  serial information from eMMC. This includes:
> +	  - Device serial number (type, version, manufacturer, date, number)
> +	  - MAC address (used to set ethaddr environment variables)
> +	  - Bundle information (if present)
> +
> +	  The serial info is stored at a fixed offset in the eMMC user area.
> +
> +	  Subcommands:
> +	  - fbxserial show: display serial info (default)
> +	  - fbxserial init: initialize ethaddr from serial info
> +
> +	  Use CONFIG_PREBOOT="fbxserial init" to auto-initialize at boot.
> +
> +if CMD_NBX_FBXSERIAL
> +
> +config NBX_MMC_PART_SERIAL_OFFSET
> +	hex "Serial info offset in eMMC"
> +	default 0x800000
> +	help
> +	  Byte offset in eMMC where the serial info structure is stored.
> +	  Default: 0x800000 (8MB)
> +
> +endif
> +
>   endif
> diff --git a/board/freebox/nbx10g/Makefile b/board/freebox/nbx10g/Makefile
> index a3b3d3a1fe3..4b70d94e14d 100644
> --- a/board/freebox/nbx10g/Makefile
> +++ b/board/freebox/nbx10g/Makefile
> @@ -2,3 +2,4 @@
>   
>   obj-y	:= board.o
>   obj-$(CONFIG_CMD_NBX_EMMCBOOT)	+= nbx_emmcboot.o
> +obj-$(CONFIG_CMD_NBX_FBXSERIAL)	+= nbx_fbxserial.o
> diff --git a/board/freebox/nbx10g/nbx_fbxserial.c b/board/freebox/nbx10g/nbx_fbxserial.c
> new file mode 100644
> index 00000000000..088133a9496
> --- /dev/null
> +++ b/board/freebox/nbx10g/nbx_fbxserial.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * NBX Freebox Serial Info Support
> + *
> + * Copyright (C) 2025 Free Mobile, Freebox
> + *
> + * Reads device serial number and MAC address from eMMC.
> + * The serial info is stored at a fixed offset in the eMMC user area.
> + *
> + * Serial format: TTTT-VV-M-(YY)WW-NN-NNNNN / FLAGS
> + * Where:
> + *   TTTT  = Device type (e.g., 9018)
> + *   VV    = Board version
> + *   M     = Manufacturer code (ASCII)
> + *   YY    = Year (BCD)
> + *   WW    = Week (1-53)
> + *   NNNNN = Serial number
> + *   FLAGS = Feature flags
> + */
> +
> +#include <command.h>
> +#include <dm/device.h>
> +#include <env.h>
> +#include <event.h>
> +#include <mmc.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <vsprintf.h>
> +#include <u-boot/crc.h>
> +#include <asm/byteorder.h>
> +#include <linux/ctype.h>
> +#include <linux/errno.h>
> +#include "nbx_fbxserial.h"
> +
> +/* Partition offset defined in Kconfig (CONFIG_NBX_MMC_PART_SERIAL_OFFSET) */
> +
> +/*
> + * Validate serial info structure
> + */
> +static int nbx_fbx_check_serial(struct nbx_fbx_serial *fs)
> +{
> +	unsigned int sum, len;
> +
> +	/* Check magic first */
> +	if (be32_to_cpu(fs->magic) != NBX_FBXSERIAL_MAGIC) {
> +		printf("Invalid magic for serial info (%08x != %08x)!\n",
> +		       be32_to_cpu(fs->magic), NBX_FBXSERIAL_MAGIC);
> +		return -EINVAL;
> +	}
> +
> +	/* Check struct version */
> +	if (be32_to_cpu(fs->struct_version) > NBX_FBXSERIAL_VERSION) {
> +		printf("Version too big for fbxserial info (0x%08x)!\n",
> +		       be32_to_cpu(fs->struct_version));
> +		return -EINVAL;
> +	}
> +
> +	/* Check for silly len */
> +	len = be32_to_cpu(fs->len);
> +	if (len > NBX_FBXSERIAL_MAX_SIZE) {
> +		printf("Silly len for serial info (%d)\n", len);
> +		return -EINVAL;
> +	}
> +
> +	/* Validate CRC (crc32_no_comp: no one's complement) */
> +	sum = crc32_no_comp(0, (void *)fs + 4, len - 4);
> +	if (be32_to_cpu(fs->crc32) != sum) {
> +		printf("Invalid checksum for serial info (%08x != %08x)\n",
> +		       sum, be32_to_cpu(fs->crc32));
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int nbx_fbx_read_serial(int dev_num, unsigned long offset,
> +			struct nbx_fbx_serial *fs)
> +{
> +	struct mmc *mmc;
> +	struct blk_desc *bd;
> +	uint blk_start, blk_cnt;
> +	uint n;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER(char, buf, ALIGN(sizeof(*fs), 512));
> +	mmc = find_mmc_device(dev_num);
> +	if (!mmc) {
> +		printf("No MMC device %d found\n", dev_num);
> +		nbx_fbxserial_set_default(fs);
> +		return -ENODEV;
> +	}
> +
> +	if (mmc_init(mmc)) {
> +		puts("MMC init failed\n");
> +		nbx_fbxserial_set_default(fs);
> +		return -EIO;
> +	}
> +
> +	/* Switch to partition 0 (user data area) */
> +	if (blk_select_hwpart_devnum(UCLASS_MMC, dev_num, 0)) {
> +		puts("MMC partition switch failed\n");
> +		nbx_fbxserial_set_default(fs);
> +		return -EIO;
> +	}
> +
> +	bd = mmc_get_blk_desc(mmc);
> +	if (!bd) {
> +		puts("Failed to get MMC block descriptor\n");
> +		nbx_fbxserial_set_default(fs);
> +		return -EIO;
> +	}
> +
> +	blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
> +	blk_cnt = ALIGN(sizeof(*fs), bd->blksz) / bd->blksz;
> +
> +	memset(fs, 0x42, sizeof(*fs));
> +
> +	n = blk_dread(bd, blk_start, blk_cnt, buf);
> +	if (n != blk_cnt) {
> +		printf("Failed to read serial info from MMC\n");
> +		nbx_fbxserial_set_default(fs);
> +		return -EIO;
> +	}
> +
> +	memcpy(fs, buf, sizeof(*fs));
> +
> +	if (nbx_fbx_check_serial(fs) != 0) {
> +		nbx_fbxserial_set_default(fs);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs)
> +{
> +	int i;
> +
> +	printf("Serial: %04u-%02u-%c-(%02u)%02u-%02u-%05u / %08x\n",
> +	       ntohs(fs->type),
> +	       fs->version,
> +	       isprint(fs->manufacturer) ? fs->manufacturer : '?',
> +	       ntohs(fs->year) / 100,
> +	       ntohs(fs->year) % 100,
> +	       fs->week,
> +	       ntohl(fs->number),
> +	       ntohl(fs->flags));
> +
> +	printf("Mac:    %02X:%02X:%02X:%02X:%02X:%02X\n",
> +	       fs->mac_addr_base[0],
> +	       fs->mac_addr_base[1],
> +	       fs->mac_addr_base[2],
> +	       fs->mac_addr_base[3],
> +	       fs->mac_addr_base[4],
> +	       fs->mac_addr_base[5]);
> +
> +	/* Show bundle info */
> +	for (i = 0; i < be32_to_cpu(fs->extinfo_count); i++) {
> +		struct nbx_serial_extinfo *p;
> +
> +		if (i >= NBX_EXTINFO_MAX_COUNT)
> +			break;
> +
> +		p = &fs->extinfos[i];
> +		if (be32_to_cpu(p->type) == NBX_EXTINFO_TYPE_EXTDEV &&
> +		    be32_to_cpu(p->u.extdev.type) == NBX_EXTDEV_TYPE_BUNDLE) {
> +			/* Ensure null termination */
> +			p->u.extdev.serial[sizeof(p->u.extdev.serial) - 1] = 0;
> +			printf("Bundle: %s\n", p->u.extdev.serial);
> +		}
> +	}
> +
> +	printf("\n");
> +}
> +
> +int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset)
> +{
> +	struct nbx_fbx_serial fs;
> +	char mac[32];
> +	int ret;
> +
> +	ret = nbx_fbx_read_serial(dev_num, offset, &fs);
> +
> +	/* Even on error, fs has default values set */
> +	snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
> +		 fs.mac_addr_base[0], fs.mac_addr_base[1],
> +		 fs.mac_addr_base[2], fs.mac_addr_base[3],
> +		 fs.mac_addr_base[4], fs.mac_addr_base[5]);
> +
> +	nbx_fbx_dump_serial(&fs);
> +
> +	env_set("ethaddr", mac);
> +	env_set("eth1addr", mac);
> +	env_set("eth2addr", mac);
> +
> +	return ret;
> +}
> +
> +/*
> + * fbxserial show - display serial info from eMMC
> + */
> +static int do_fbxserial_show(struct cmd_tbl *cmdtp, int flag, int argc,
> +			     char *const argv[])
> +{
> +	struct nbx_fbx_serial fs;
> +	int dev = 0;
> +	unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
> +
> +	if (argc >= 1)
> +		dev = dectoul(argv[0], NULL);
> +
> +	if (argc >= 2)
> +		offset = hextoul(argv[1], NULL);
> +
> +	if (nbx_fbx_read_serial(dev, offset, &fs) != 0)
> +		printf("Warning: Using default serial info\n");
> +
> +	nbx_fbx_dump_serial(&fs);
> +
> +	return CMD_RET_SUCCESS;
> +}
> +
> +/*
> + * fbxserial init - initialize ethaddr from serial info
> + */
> +static int do_fbxserial_init(struct cmd_tbl *cmdtp, int flag, int argc,
> +			     char *const argv[])
> +{
> +	int dev = 0;
> +	unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
> +
> +	if (argc >= 1)
> +		dev = dectoul(argv[0], NULL);
> +
> +	if (argc >= 2)
> +		offset = hextoul(argv[1], NULL);
> +
> +	return nbx_fbx_init_ethaddr(dev, offset);
> +}
> +
> +static struct cmd_tbl cmd_fbxserial_sub[] = {
> +	U_BOOT_CMD_MKENT(show, 3, 0, do_fbxserial_show, "", ""),
> +	U_BOOT_CMD_MKENT(init, 3, 0, do_fbxserial_init, "", ""),
> +};
> +
> +static int do_fbxserial(struct cmd_tbl *cmdtp, int flag, int argc,
> +			char *const argv[])
> +{
> +	struct cmd_tbl *cp;
> +
> +	/* Default to 'show' if no subcommand */
> +	if (argc < 2)
> +		return do_fbxserial_show(cmdtp, flag, 0, NULL);
> +
> +	cp = find_cmd_tbl(argv[1], cmd_fbxserial_sub,
> +			  ARRAY_SIZE(cmd_fbxserial_sub));
> +
> +	if (!cp)
> +		return CMD_RET_USAGE;
> +
> +	return cp->cmd(cmdtp, flag, argc - 2, argv + 2);
> +}
> +
> +U_BOOT_CMD(
> +	fbxserial, 5, 0, do_fbxserial,
> +	"NBX serial info and MAC address initialization",
> +	"show [dev] [offset] - display serial info from eMMC\n"
> +	"fbxserial init [dev] [offset] - initialize ethaddr from serial info\n"
> +	"    dev    - MMC device number (default 0)\n"
> +	"    offset - offset in eMMC in hex (default from Kconfig)"
> +);
> +
> +/*
> + * Early init hook: Set MAC address from eMMC serial info before
> + * network driver probes. EVT_SETTINGS_R is triggered after MMC
> + * is available but before initr_net().
> + */
> +static int nbx_fbx_settings_r(void)
> +{
> +	if (!of_machine_is_compatible("nbx,armada8040"))
> +		return 0;
> +
> +	nbx_fbx_init_ethaddr(0, CONFIG_NBX_MMC_PART_SERIAL_OFFSET);
> +	return 0;
> +}
> +
> +EVENT_SPY_SIMPLE(EVT_SETTINGS_R, nbx_fbx_settings_r);
> diff --git a/board/freebox/nbx10g/nbx_fbxserial.h b/board/freebox/nbx10g/nbx_fbxserial.h
> new file mode 100644
> index 00000000000..7bcaef09fe3
> --- /dev/null
> +++ b/board/freebox/nbx10g/nbx_fbxserial.h
> @@ -0,0 +1,156 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NBX Freebox Serial Info Support
> + *
> + * Copyright (C) 2025 Free Mobile, Freebox
> + *
> + * Reads device serial number and MAC address from eMMC.
> + * Used to identify the board and set network MAC addresses.
> + */
> +
> +#ifndef NBX_FBXSERIAL_H
> +#define NBX_FBXSERIAL_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * Extended info structure - variable data depending on type
> + */
> +#define NBX_EXTINFO_SIZE		128
> +#define NBX_EXTINFO_MAX_COUNT		16
> +
> +/* Extended info types */
> +#define NBX_EXTINFO_TYPE_EXTDEV		1
> +
> +/* Extended device types */
> +#define NBX_EXTDEV_TYPE_BUNDLE		1
> +#define NBX_EXTDEV_TYPE_MAX		2
> +
> +struct nbx_serial_extinfo {
> +	u32 type;
> +
> +	union {
> +		/* extdev */
> +		struct {
> +			u32 type;
> +			u32 model;
> +			char serial[64];
> +		} extdev;
> +
> +		/* raw access */
> +		unsigned char data[NBX_EXTINFO_SIZE];
> +	} u;
> +} __packed;
> +
> +/*
> + * Master serial structure
> + */
> +#define NBX_FBXSERIAL_VERSION		1
> +#define NBX_FBXSERIAL_MAGIC		0x2d9521ab
> +
> +#define NBX_MAC_ADDR_SIZE		6
> +#define NBX_RANDOM_DATA_SIZE		32
> +
> +/* Maximum size for CRC validation */
> +#define NBX_FBXSERIAL_MAX_SIZE		8192
> +
> +struct nbx_fbx_serial {
> +	u32 crc32;
> +	u32 magic;
> +	u32 struct_version;
> +	u32 len;
> +
> +	/* Board serial */
> +	u16 type;
> +	u8 version;
> +	u8 manufacturer;
> +	u16 year;
> +	u8 week;
> +	u32 number;
> +	u32 flags;
> +
> +	/* MAC address base */
> +	u8 mac_addr_base[NBX_MAC_ADDR_SIZE];
> +
> +	/* MAC address count */
> +	u8 mac_count;
> +
> +	/* Random data used to derive keys */
> +	u8 random_data[NBX_RANDOM_DATA_SIZE];
> +
> +	/* Last update of data (seconds since epoch) */
> +	u32 last_modified;
> +
> +	/* Count of following extinfo tags */
> +	u32 extinfo_count;
> +
> +	/* Beginning of extended info */
> +	struct nbx_serial_extinfo extinfos[NBX_EXTINFO_MAX_COUNT];
> +} __packed;
> +
> +/**
> + * nbx_fbxserial_set_default() - Initialize serial structure with defaults
> + * @serial: Pointer to serial structure to initialize
> + *
> + * Sets the serial structure to default values (Freebox OUI, type 9018).
> + * Used as fallback when serial info cannot be read from eMMC.
> + */
> +static inline void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial)
> +{
> +	static const struct nbx_fbx_serial def = {
> +		.crc32		= 0,
> +		.magic		= NBX_FBXSERIAL_MAGIC,
> +		.struct_version	= NBX_FBXSERIAL_VERSION,
> +		.len		= sizeof(struct nbx_fbx_serial),
> +		.type		= 9018,
> +		.version	= 0,
> +		.manufacturer	= '_',
> +		.year		= 0,
> +		.week		= 0,
> +		.number		= 0,
> +		.flags		= 0,
> +		.mac_addr_base	= { 0x00, 0x07, 0xCB, 0x00, 0x00, 0xFD },
> +		.mac_count	= 1,
> +		.random_data	= { 0 },
> +		.last_modified	= 0,
> +		.extinfo_count	= 0,
> +	};
> +
> +	memcpy(serial, &def, sizeof(def));
> +}
> +
> +/**
> + * nbx_fbx_read_serial() - Read serial info from eMMC
> + * @dev_num: MMC device number
> + * @offset: Byte offset in eMMC where serial info is stored
> + * @fs: Pointer to serial structure to fill
> + *
> + * Reads and validates the serial info from eMMC. On failure,
> + * the structure is filled with default values.
> + *
> + * Return: 0 on success, negative on error (defaults still set)
> + */
> +int nbx_fbx_read_serial(int dev_num, unsigned long offset,
> +			struct nbx_fbx_serial *fs);
> +
> +/**
> + * nbx_fbx_dump_serial() - Print serial info to console
> + * @fs: Pointer to serial structure to display
> + *
> + * Prints the serial number, MAC address, and bundle info (if present).
> + */
> +void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs);
> +
> +/**
> + * nbx_fbx_init_ethaddr() - Initialize Ethernet addresses from serial info
> + * @dev_num: MMC device number
> + * @offset: Byte offset in eMMC where serial info is stored
> + *
> + * Reads serial info and sets ethaddr, eth1addr, eth2addr environment
> + * variables from the MAC address in the serial structure.
> + *
> + * Return: 0 on success, negative on error
> + */
> +int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset);
> +
> +#endif /* NBX_FBXSERIAL_H */



More information about the U-Boot mailing list