[PATCH] sunxi: add raw NAND secure storage support

James Hilliard james.hilliard1 at gmail.com
Mon May 11 22:58:08 CEST 2026


Some sunxi boards boot from raw NAND using an Allwinner flash layout.
Factory data is stored in redundant secure-storage blocks after the
U-Boot proper area.

Mainline U-Boot needs to preserve this area when generating NAND
partition tables. It may also need to read factory data, such as the
Ethernet MAC address.

Add helpers to locate secure-storage marker blocks, read checksummed
store_object items through the MTD ECC path, set ethaddr from a
configurable key, and generate runtime mtdparts that place UBI after
the last secure-storage marker.

Secure-storage block discovery intentionally mirrors the existing NAND
layout: use marker-only discovery from the computed U-Boot proper end
to block 50. Item data remains checksum and CRC validated before use.

Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
 board/sunxi/Kconfig               |  39 +++
 board/sunxi/Makefile              |   3 +
 board/sunxi/board.c               | 119 +++++++
 board/sunxi/nand_secure_storage.c | 521 ++++++++++++++++++++++++++++++
 board/sunxi/nand_secure_storage.h |  42 +++
 5 files changed, 724 insertions(+)
 create mode 100644 board/sunxi/nand_secure_storage.c
 create mode 100644 board/sunxi/nand_secure_storage.h

diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
index 084a8b0c6ca..c3826f0ade9 100644
--- a/board/sunxi/Kconfig
+++ b/board/sunxi/Kconfig
@@ -22,3 +22,42 @@ config SPL_IMAGE_TYPE
 	string
 	default "sunxi_egon" if SPL_IMAGE_TYPE_SUNXI_EGON
 	default "sunxi_toc0" if SPL_IMAGE_TYPE_SUNXI_TOC0
+
+config SUNXI_NAND_SECURE_STORAGE
+	bool "Allwinner NAND secure storage support"
+	depends on NAND_SUNXI && MTD_RAW_NAND
+	help
+	  Enable support for reading the Allwinner raw NAND secure-storage
+	  area from U-Boot proper. This is used by some sunxi boards to
+	  preserve factory data, such as MAC addresses, when booting a
+	  mainline NAND layout.
+
+config SUNXI_NAND_SECURE_STORAGE_ETHADDR
+	bool "Read ethaddr from Allwinner NAND secure storage"
+	depends on SUNXI_NAND_SECURE_STORAGE && NET
+	select CRC32
+	help
+	  Read the configured secure-storage key and use it as the primary
+	  Ethernet MAC address before the normal sunxi fallback environment
+	  value is generated.
+
+config SUNXI_NAND_SECURE_STORAGE_ETHADDR_KEY
+	string "NAND secure-storage key name for ethaddr"
+	depends on SUNXI_NAND_SECURE_STORAGE_ETHADDR
+	default "mac"
+	help
+	  Name of the secure-storage item containing the primary Ethernet MAC
+	  address. The store_object payload is parsed like a U-Boot
+	  Ethernet environment variable.
+
+config SUNXI_NAND_SECURE_STORAGE_MTD_PARTS
+	bool "Generate NAND mtdparts around secure storage"
+	depends on SUNXI_NAND_SECURE_STORAGE && SYS_MTDPARTS_RUNTIME
+	help
+	  Generate the default NAND mtdparts at runtime by locating
+	  secure-storage marker blocks and placing the UBI partition immediately
+	  after the last marker. This avoids hard-coding the secure-storage
+	  offset, which may shift when bad blocks are present before or inside
+	  the secure-storage window. Only SLC raw NAND is supported; MLC/TLC
+	  parts need Allwinner's LSB-block-size mapping, which is not available
+	  here.
diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
index ee82493117a..fda2a0bfe76 100644
--- a/board/sunxi/Makefile
+++ b/board/sunxi/Makefile
@@ -8,6 +8,9 @@
 # Wolfgang Denk, DENX Software Engineering, wd at denx.de.
 obj-y	+= board.o
 obj-$(CONFIG_SUN7I_GMAC)	+= gmac.o
+ifndef CONFIG_XPL_BUILD
+obj-$(CONFIG_SUNXI_NAND_SECURE_STORAGE)	+= nand_secure_storage.o
+endif
 obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i_auto.o
 obj-$(CONFIG_MACH_SUN5I)	+= dram_sun5i_auto.o
 obj-$(CONFIG_MACH_SUN7I)	+= dram_sun5i_auto.o
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 3d1afec7c66..19a0402eb03 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -50,9 +50,126 @@
 #include <spl.h>
 #include <sy8106a.h>
 #include <asm/setup.h>
+#include <vsprintf.h>
+
+#include "nand_secure_storage.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#if IS_ENABLED(CONFIG_SYS_MTDPARTS_RUNTIME) && !defined(CONFIG_XPL_BUILD)
+#define SUNXI_NAND_MTDIDS_MAXLEN	128
+#define SUNXI_NAND_MTDPARTS_MAXLEN	512
+
+static int sunxi_mtdparts_append(char *buf, size_t size, int *len,
+				 const char *fmt, ...)
+{
+	size_t avail;
+	va_list args;
+	int ret;
+
+	if (*len < 0 || (size_t)*len >= size)
+		return -ENOSPC;
+
+	avail = size - (size_t)*len;
+	va_start(args, fmt);
+	ret = vsnprintf(buf + *len, avail, fmt, args);
+	va_end(args);
+	if (ret < 0 || (size_t)ret >= avail)
+		return -ENOSPC;
+
+	*len += ret;
+	return 0;
+}
+
+static int sunxi_mtdparts_add(char *buf, size_t size, int *len,
+			      const char *name, u64 part_size, u64 offset,
+			      bool ro)
+{
+	return sunxi_mtdparts_append(buf, size, len, "0x%llx at 0x%llx(%s)%s,",
+				    (unsigned long long)part_size,
+				    (unsigned long long)offset, name,
+				    ro ? "ro" : "");
+}
+
+void board_mtdparts_default(const char **mtdids, const char **mtdparts)
+{
+	static char ids[SUNXI_NAND_MTDIDS_MAXLEN];
+	static char parts[SUNXI_NAND_MTDPARTS_MAXLEN];
+	static bool generated;
+	struct sunxi_nand_secure_storage_info ss;
+	u32 i;
+	int len, ret;
+
+	*mtdids = NULL;
+	*mtdparts = NULL;
+
+	if (!IS_ENABLED(CONFIG_SUNXI_NAND_SECURE_STORAGE_MTD_PARTS))
+		return;
+
+	if (generated) {
+		*mtdids = ids;
+		*mtdparts = parts;
+		return;
+	}
+
+	ret = sunxi_nand_secure_storage_locate(&ss);
+	if (ret) {
+		debug("sunxi secure storage: no dynamic mtdparts: %d\n", ret);
+		return;
+	}
+
+	if (!ss.mtd_name || !ss.mtd_size || !ss.spl_copy_size ||
+	    !ss.spl_copy_count || !ss.uboot_size || !ss.secure_size) {
+		debug("sunxi secure storage: invalid dynamic mtdparts geometry\n");
+		return;
+	}
+
+	if (ss.uboot_offset + ss.uboot_size > ss.secure_offset ||
+	    ss.secure_end >= ss.mtd_size) {
+		debug("sunxi secure storage: dynamic mtdparts would overlap\n");
+		return;
+	}
+
+	len = snprintf(ids, sizeof(ids), "nand0=%s", ss.mtd_name);
+	if (len < 0 || len >= sizeof(ids))
+		return;
+
+	len = snprintf(parts, sizeof(parts), "%s:", ss.mtd_name);
+	if (len < 0 || len >= sizeof(parts))
+		goto err;
+
+	for (i = 0; i < ss.spl_copy_count; i++) {
+		char name[16];
+
+		ret = snprintf(name, sizeof(name), "spl-%u", i);
+		if (ret < 0 || (size_t)ret >= sizeof(name))
+			goto err;
+		if (sunxi_mtdparts_add(parts, sizeof(parts), &len, name,
+				       ss.spl_copy_size,
+				       ss.spl_copy_size * i, false))
+			goto err;
+	}
+
+	if (sunxi_mtdparts_add(parts, sizeof(parts), &len, "uboot",
+			       ss.uboot_size, ss.uboot_offset, false))
+		goto err;
+	if (sunxi_mtdparts_add(parts, sizeof(parts), &len, "securestorage",
+			       ss.secure_size, ss.secure_offset, true))
+		goto err;
+	if (sunxi_mtdparts_append(parts, sizeof(parts), &len, "- at 0x%llx(UBI)",
+				  (unsigned long long)ss.secure_end))
+		goto err;
+
+	generated = true;
+	*mtdids = ids;
+	*mtdparts = parts;
+	return;
+
+err:
+	ids[0] = '\0';
+}
+#endif
+
 void i2c_init_board(void)
 {
 #ifdef CONFIG_I2C0_ENABLE
@@ -792,6 +909,8 @@ static void setup_environment(const void *fdt)
 	char ethaddr[16];
 	int i;
 
+	sunxi_nand_secure_storage_setup_ethaddr();
+
 	if (!get_unique_sid(sid))
 		return;
 
diff --git a/board/sunxi/nand_secure_storage.c b/board/sunxi/nand_secure_storage.c
new file mode 100644
index 00000000000..f786a47430a
--- /dev/null
+++ b/board/sunxi/nand_secure_storage.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Allwinner raw NAND secure-storage support.
+ *
+ * The Allwinner NAND layout stores redundant secure-storage blocks
+ * immediately after the U-Boot proper area. The blocks are identified by an
+ * OOB marker and their pages are read through the normal NAND ECC/randomizer
+ * path.
+ */
+
+#ifdef CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR
+#include <env.h>
+#include <net.h>
+#include <u-boot/crc.h>
+#endif
+#include <asm/unaligned.h>
+#include <memalign.h>
+#include <nand.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+#include "nand_secure_storage.h"
+
+/*
+ * The Allwinner NAND layout scans up to block 50 and starts scanning at
+ * uboot_next_block. The U-Boot range below follows the geometry-dependent
+ * defaults used when no stored NAND partition table is present.
+ */
+#define SS_SCAN_END_BLOCK		50
+
+#define SS_SMALL_NAND_MAX_BLOCK_SIZE	SZ_1M
+#define SS_SMALL_NAND_MIN_BLOCK_SIZE	SZ_128K
+#define SS_SMALL_NAND_ALWAYS_BLOCK_SIZE	SZ_512K
+#define SS_SMALL_NAND_UBOOT_START	8
+#define SS_SMALL_NAND_UBOOT_SIZE	SZ_4M
+#define SS_MEDIUM_NAND_MAX_BLOCK_SIZE	SZ_2M
+#define SS_BIG_NAND_UBOOT_START		4
+#define SS_BIG_NAND_1M_UBOOT_BLOCKS	20
+#define SS_BIG_NAND_2M_UBOOT_BLOCKS	10
+#define SS_BIG_NAND_MIN_UBOOT_BLOCKS	8
+
+/*
+ * The H616/sun50iw9 NAND layout uses the 128-page SPL-copy geometry.
+ * Only unrelated SUN8IW18 and SUN50IW11 SoC families use the 256-page
+ * variant.
+ */
+#define SS_SPL_PAGE_CNT_PER_COPY	128
+
+#define SS_OOB_MAGIC_OFF		1
+#define SS_OOB_CHECKSUM_OFF		3
+#define SS_OOB_MAGIC			0xaa5c
+#define SS_OOB_MAGIC_SIZE		(SS_OOB_MAGIC_OFF + sizeof(u16))
+#define SS_OOB_CHECKSUM_SIZE		(SS_OOB_CHECKSUM_OFF + sizeof(u32))
+
+#ifdef CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR
+#define SS_ITEM_SIZE			SZ_4K
+#define SS_EMPTY_SUM			0x1234
+#define SS_ITEMS_PER_BLOCK		32
+#define SS_MAP_MAX_ENTRIES		(SS_ITEMS_PER_BLOCK - 1)
+#define SS_NAME_LEN			64
+
+#define SS_STORE_MAGIC			0x17253948
+#define SS_STORE_MAX_LEN		0xc00
+#endif
+
+struct ss_info {
+	struct mtd_info *mtd;
+	u32 first_block;
+	u32 last_block;
+	u32 marker_count;
+	u32 page_size;
+	u32 oob_size;
+	u32 erase_size;
+	u32 pages_per_block;
+	u32 spl_copy_blocks;
+	u32 spl_copy_count;
+	u32 uboot_start_block;
+	u32 uboot_next_block;
+};
+
+#ifdef CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR
+struct ss_object {
+	u32 magic;
+	u32 id;
+	char name[SS_NAME_LEN];
+	u32 re_encrypt;
+	u32 version;
+	u32 write_protect;
+	u32 reserved[3];
+	u32 actual_len;
+	u8 data[SS_STORE_MAX_LEN];
+	u32 crc;
+} __packed;
+
+static u32 ss_get_le32(const void *buf, size_t offset)
+{
+	return get_unaligned_le32((const u8 *)buf + offset);
+}
+#endif
+
+static bool ss_oob_has_magic(const u8 *oob)
+{
+	return get_unaligned_be16(oob + SS_OOB_MAGIC_OFF) == SS_OOB_MAGIC;
+}
+
+static int ss_read_ecc_page(struct ss_info *ss, u32 block, u32 page, void *buf,
+			    u8 *oob)
+{
+	struct mtd_oob_ops ops = {};
+	loff_t offs = (loff_t)block * ss->erase_size +
+		      (loff_t)page * ss->page_size;
+	int ret;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.len = ss->page_size;
+	ops.ooblen = ss->oob_size;
+	ops.datbuf = buf;
+	ops.oobbuf = oob;
+
+	ret = mtd_read_oob(ss->mtd, offs, &ops);
+	if (ret && !mtd_is_bitflip(ret))
+		return ret;
+
+	if (ops.retlen != ss->page_size || ops.oobretlen != ss->oob_size)
+		return -EIO;
+
+	return 0;
+}
+
+#ifdef CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR
+static u32 ss_page_sum(const void *buf, size_t len)
+{
+	const u8 *bytes = buf;
+	u32 sum = SS_EMPTY_SUM;
+	size_t i;
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		sum += get_unaligned_le32(bytes + i * sizeof(u32));
+
+	return sum;
+}
+
+static int ss_read_page(struct ss_info *ss, u32 block, u32 page, void *buf,
+			u8 *oob, u32 len)
+{
+	u32 sum;
+	int ret;
+
+	ret = ss_read_ecc_page(ss, block, page, buf, oob);
+	if (ret)
+		return ret;
+
+	if (!ss_oob_has_magic(oob))
+		return -ENOENT;
+
+	sum = ss_page_sum(buf, len);
+	if (sum != get_unaligned_be32(oob + SS_OOB_CHECKSUM_OFF))
+		return -EBADMSG;
+	if (sum == SS_EMPTY_SUM)
+		return -ENODATA;
+
+	return 0;
+}
+
+static int ss_read_replicated_page(struct ss_info *ss, u32 block, u32 page,
+				   void *buf, u8 *oob, u32 len)
+{
+	int ret = -ENOENT;
+
+	for (; page < ss->pages_per_block; page += SS_ITEMS_PER_BLOCK) {
+		ret = ss_read_page(ss, block, page, buf, oob, len);
+		if (!ret)
+			return 0;
+	}
+
+	return ret;
+}
+#endif
+
+static bool ss_is_secure_block(struct ss_info *ss, u32 block, void *pagebuf,
+			       u8 *oob)
+{
+	if (ss_read_ecc_page(ss, block, 0, pagebuf, oob))
+		return false;
+
+	return ss_oob_has_magic(oob);
+}
+
+static void ss_default_uboot_range(struct ss_info *ss)
+{
+	u32 block_size = ss->erase_size;
+	u32 uboot_blocks;
+
+	if (ss->erase_size <= SS_SMALL_NAND_ALWAYS_BLOCK_SIZE ||
+	    (ss->erase_size <= SS_SMALL_NAND_MAX_BLOCK_SIZE &&
+	     ss->pages_per_block <= 128)) {
+		if (block_size < SS_SMALL_NAND_MIN_BLOCK_SIZE)
+			block_size = SS_SMALL_NAND_MIN_BLOCK_SIZE;
+		block_size = roundup_pow_of_two(block_size);
+
+		ss->uboot_start_block = SS_SMALL_NAND_UBOOT_START;
+		uboot_blocks = SS_SMALL_NAND_UBOOT_SIZE / block_size;
+	} else if (ss->erase_size <= SS_SMALL_NAND_MAX_BLOCK_SIZE) {
+		ss->uboot_start_block = SS_BIG_NAND_UBOOT_START;
+		uboot_blocks = SS_BIG_NAND_1M_UBOOT_BLOCKS;
+	} else if (ss->erase_size <= SS_MEDIUM_NAND_MAX_BLOCK_SIZE) {
+		ss->uboot_start_block = SS_BIG_NAND_UBOOT_START;
+		uboot_blocks = SS_BIG_NAND_2M_UBOOT_BLOCKS;
+	} else {
+		ss->uboot_start_block = SS_BIG_NAND_UBOOT_START;
+		uboot_blocks = SS_BIG_NAND_MIN_UBOOT_BLOCKS;
+	}
+
+	ss->uboot_next_block = ss->uboot_start_block + uboot_blocks;
+}
+
+static void ss_default_spl_layout(struct ss_info *ss)
+{
+	ss->spl_copy_blocks =
+		DIV_ROUND_UP(SS_SPL_PAGE_CNT_PER_COPY, ss->pages_per_block);
+
+	/*
+	 * The SPL-copy writer silently stops once the next SPL copy would
+	 * overlap U-Boot proper. Keep the same floor division here.
+	 */
+	if (ss->spl_copy_blocks)
+		ss->spl_copy_count =
+			ss->uboot_start_block / ss->spl_copy_blocks;
+}
+
+static int ss_scan_blocks(struct ss_info *ss)
+{
+	struct mtd_info *mtd;
+	u64 total_blocks;
+	u32 block, end_block;
+	int ret;
+
+	nand_init();
+
+	mtd = get_nand_dev_by_index(0);
+	if (!mtd)
+		return -ENODEV;
+	if (!mtd->writesize || !mtd->erasesize || !mtd->oobsize)
+		return -EINVAL;
+	if (mtd_mod_by_ws(mtd->erasesize, mtd))
+		return -EINVAL;
+	if (mtd->oobsize < SS_OOB_MAGIC_SIZE)
+		return -EINVAL;
+	if (IS_ENABLED(CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR) &&
+	    mtd->oobsize < SS_OOB_CHECKSUM_SIZE)
+		return -EINVAL;
+	if (!nand_is_slc(mtd_to_nand(mtd)))
+		return -EOPNOTSUPP;
+
+	memset(ss, 0, sizeof(*ss));
+	ss->mtd = mtd;
+	ss->page_size = mtd->writesize;
+	ss->oob_size = mtd->oobsize;
+	ss->erase_size = mtd->erasesize;
+	ss->pages_per_block = mtd_div_by_ws(mtd->erasesize, mtd);
+	ss_default_uboot_range(ss);
+	ss_default_spl_layout(ss);
+
+	ALLOC_CACHE_ALIGN_BUFFER(u8, pagebuf, ss->page_size);
+	ALLOC_CACHE_ALIGN_BUFFER(u8, oob, ss->oob_size);
+
+	total_blocks = mtd_div_by_eb(mtd->size, mtd);
+	end_block = min_t(u64, total_blocks, SS_SCAN_END_BLOCK);
+
+	if (ss->uboot_next_block >= end_block)
+		return -ENOENT;
+
+	for (block = ss->uboot_next_block; block < end_block; block++) {
+		ret = mtd_block_isbad(mtd, (loff_t)block * ss->erase_size);
+		if (ret > 0)
+			continue;
+		if (ret < 0)
+			return ret;
+		if (!ss_is_secure_block(ss, block, pagebuf, oob))
+			continue;
+
+		if (!ss->marker_count)
+			ss->first_block = block;
+		ss->last_block = block;
+		ss->marker_count++;
+	}
+
+	if (!ss->marker_count)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ss_find_blocks(struct ss_info *ss)
+{
+	static struct ss_info cached_ss;
+	static bool cached;
+	int ret;
+
+	if (cached) {
+		*ss = cached_ss;
+		return 0;
+	}
+
+	ret = ss_scan_blocks(ss);
+	if (ret)
+		return ret;
+
+	cached_ss = *ss;
+	cached = true;
+
+	return 0;
+}
+
+int sunxi_nand_secure_storage_locate(struct sunxi_nand_secure_storage_info *info)
+{
+	struct ss_info ss;
+	int ret;
+
+	if (!info)
+		return -EINVAL;
+
+	ret = ss_find_blocks(&ss);
+	if (ret)
+		return ret;
+
+	memset(info, 0, sizeof(*info));
+	info->mtd_name = ss.mtd->name;
+	info->mtd_size = ss.mtd->size;
+	info->spl_copy_size = (u64)ss.spl_copy_blocks * ss.erase_size;
+	info->spl_copy_count = ss.spl_copy_count;
+	info->uboot_offset = (u64)ss.uboot_start_block * ss.erase_size;
+	info->uboot_size = (u64)(ss.uboot_next_block - ss.uboot_start_block) *
+			   ss.erase_size;
+	info->secure_offset = (u64)ss.first_block * ss.erase_size;
+	info->secure_size = (u64)(ss.last_block - ss.first_block + 1) *
+			    ss.erase_size;
+	info->secure_end = (u64)(ss.last_block + 1) * ss.erase_size;
+
+	return 0;
+}
+
+#ifdef CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR
+static int ss_read_item(struct ss_info *ss, u32 item, void *buf)
+{
+	u32 pages_per_item = SS_ITEM_SIZE / ss->page_size;
+	u32 page_len = pages_per_item == 1 ? SS_ITEM_SIZE : ss->page_size;
+	u32 page;
+	int ret;
+
+	if (ss->page_size > SS_ITEM_SIZE || SS_ITEM_SIZE % ss->page_size)
+		return -EINVAL;
+	if ((item + 1) * pages_per_item > ss->pages_per_block)
+		return -EINVAL;
+
+	ALLOC_CACHE_ALIGN_BUFFER(u8, oob, ss->oob_size);
+
+	for (page = 0; page < pages_per_item; page++) {
+		u8 *dst = (u8 *)buf + page * ss->page_size;
+		u32 raw_page = item * pages_per_item + page;
+
+		ret = ss_read_replicated_page(ss, ss->first_block, raw_page,
+					      dst, oob, page_len);
+		if (ret && ss->last_block != ss->first_block)
+			ret = ss_read_replicated_page(ss, ss->last_block,
+						      raw_page, dst, oob,
+						      page_len);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static bool ss_object_name_matches(const struct ss_object *object,
+				   const char *key)
+{
+	size_t key_len = strlen(key);
+	size_t name_len = strnlen(object->name, SS_NAME_LEN);
+
+	return name_len == key_len && !memcmp(object->name, key, key_len);
+}
+
+static int ss_find_item(const char *map, const char *key, u32 *item)
+{
+	size_t key_len = strlen(key);
+	u32 pos = 0;
+	u32 nr = 1;
+
+	if (key_len >= SS_NAME_LEN)
+		return -ENAMETOOLONG;
+
+	while (pos < SS_ITEM_SIZE && map[pos]) {
+		const char *entry = map + pos;
+		const char *colon;
+		size_t entry_len = strnlen(entry, SS_ITEM_SIZE - pos);
+
+		if (entry_len == SS_ITEM_SIZE - pos)
+			return -EINVAL;
+		if (nr > SS_MAP_MAX_ENTRIES)
+			return -ENOSPC;
+
+		colon = memchr(entry, ':', entry_len);
+		if (!colon || colon == entry || colon + 1 == entry + entry_len)
+			return -EINVAL;
+
+		if (colon - entry == key_len && !memcmp(entry, key, key_len)) {
+			*item = nr;
+			return 0;
+		}
+
+		pos += entry_len + 1;
+		nr++;
+	}
+
+	return -ENOENT;
+}
+
+static int ss_get_payload(const void *item, const char *key, const u8 **payload,
+			  u32 *len)
+{
+	const struct ss_object *object = item;
+	u32 actual_len, stored_crc, crc;
+
+	if (ss_get_le32(item, offsetof(struct ss_object, magic)) !=
+	    SS_STORE_MAGIC)
+		return -EINVAL;
+
+	stored_crc = ss_get_le32(item, offsetof(struct ss_object, crc));
+	crc = crc32(0, item, offsetof(struct ss_object, crc));
+	if (crc != stored_crc)
+		return -EBADMSG;
+	if (!ss_object_name_matches(object, key))
+		return -ENOENT;
+
+	actual_len = ss_get_le32(item, offsetof(struct ss_object, actual_len));
+	if (actual_len > SS_STORE_MAX_LEN)
+		return -EINVAL;
+
+	*payload = ((const struct ss_object *)item)->data;
+	*len = actual_len;
+
+	return 0;
+}
+
+static bool ss_parse_mac(const u8 *buf, u32 len, u8 *mac)
+{
+	char text[ARP_HLEN_ASCII + 1];
+	size_t text_len;
+
+	text_len = strnlen((const char *)buf, len);
+	if (!text_len || text_len > ARP_HLEN_ASCII)
+		return false;
+
+	memcpy(text, buf, text_len);
+	text[text_len] = '\0';
+	string_to_enetaddr(text, mac);
+
+	return is_valid_ethaddr(mac);
+}
+
+static int ss_read_mac(const char *key, u8 *mac)
+{
+	struct ss_info ss;
+	const u8 *payload;
+	u32 item, len;
+	int ret;
+
+	ret = ss_find_blocks(&ss);
+	if (ret)
+		return ret;
+
+	ALLOC_CACHE_ALIGN_BUFFER(u8, itembuf, SS_ITEM_SIZE);
+
+	ret = ss_read_item(&ss, 0, itembuf);
+	if (ret)
+		return ret;
+
+	ret = ss_find_item((const char *)itembuf, key, &item);
+	if (ret)
+		return ret;
+
+	ret = ss_read_item(&ss, item, itembuf);
+	if (ret)
+		return ret;
+
+	ret = ss_get_payload(itembuf, key, &payload, &len);
+	if (ret)
+		return ret;
+
+	return ss_parse_mac(payload, len, mac) ? 0 : -EINVAL;
+}
+
+void sunxi_nand_secure_storage_setup_ethaddr(void)
+{
+	const char *key = CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR_KEY;
+	static bool attempted;
+	u8 mac[ARP_HLEN];
+	int ret;
+
+	if (attempted || !key[0] || eth_env_get_enetaddr("ethaddr", mac))
+		return;
+
+	attempted = true;
+
+	ret = ss_read_mac(key, mac);
+	if (ret) {
+		printf("Secure storage: failed to read key '%s' (%d)\n",
+		       key, ret);
+		return;
+	}
+
+	eth_env_set_enetaddr("ethaddr", mac);
+	printf("Secure storage mac: %pM\n", mac);
+}
+#endif
diff --git a/board/sunxi/nand_secure_storage.h b/board/sunxi/nand_secure_storage.h
new file mode 100644
index 00000000000..45b20659c0b
--- /dev/null
+++ b/board/sunxi/nand_secure_storage.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Allwinner NAND secure storage helpers.
+ */
+
+#ifndef SUNXI_NAND_SECURE_STORAGE_H
+#define SUNXI_NAND_SECURE_STORAGE_H
+
+#include <linux/errno.h>
+#include <linux/kconfig.h>
+#include <linux/types.h>
+
+struct sunxi_nand_secure_storage_info {
+	const char *mtd_name;
+	u64 mtd_size;
+	u64 spl_copy_size;
+	u32 spl_copy_count;
+	u64 uboot_offset;
+	u64 uboot_size;
+	u64 secure_offset;
+	u64 secure_size;
+	u64 secure_end;
+};
+
+#if IS_ENABLED(CONFIG_SUNXI_NAND_SECURE_STORAGE) && !defined(CONFIG_XPL_BUILD)
+int sunxi_nand_secure_storage_locate(struct sunxi_nand_secure_storage_info *info);
+#else
+static inline int
+sunxi_nand_secure_storage_locate(struct sunxi_nand_secure_storage_info *info)
+{
+	return -ENODEV;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_SUNXI_NAND_SECURE_STORAGE_ETHADDR) && \
+	!defined(CONFIG_XPL_BUILD)
+void sunxi_nand_secure_storage_setup_ethaddr(void);
+#else
+static inline void sunxi_nand_secure_storage_setup_ethaddr(void) {}
+#endif
+
+#endif
-- 
2.53.0



More information about the U-Boot mailing list