diff options
| -rw-r--r-- | board/freebox/nbx10g/Kconfig | 29 | ||||
| -rw-r--r-- | board/freebox/nbx10g/Makefile | 1 | ||||
| -rw-r--r-- | board/freebox/nbx10g/nbx_fbxserial.c | 286 | ||||
| -rw-r--r-- | board/freebox/nbx10g/nbx_fbxserial.h | 156 |
4 files changed, 472 insertions, 0 deletions
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 */ |
