[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