From 682d0b982bc1f2c329dd2dcd810bddaff9febee7 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 22 Oct 2026 07:21:12 -0700 Subject: [PATCH] PolarFire SoC: make the Athena hash offload selectable with MPFS_ATHENA_SHA --- docs/PolarFire.md | 2 +- hal/mpfs250.c | 28 +++++++++++++++++++++++++--- options.mk | 22 +++++++++++++++++++--- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/docs/PolarFire.md b/docs/PolarFire.md index 2ee91a7e..5ccbd946 100644 --- a/docs/PolarFire.md +++ b/docs/PolarFire.md @@ -1203,7 +1203,7 @@ On an encrypted image the decrypt dominates everything else, and the Athena F520 | Integrity SHA-384 | 4.29 s | 5.70 s | | Verify ECC384 | 0.67 s | 0.67 s | -The AES offload is worth about 4.2x on the decrypt. **The hash offload is about 1.3x slower than software** over an image this size: the engine's per-block setup dominates once the data is in DDR rather than a small buffer in L2 scratch, so enabling it costs boot time rather than saving it. `MPFS_ATHENA=1` turns on the hash offload and `ENCRYPT_WITH_AES256=1` adds the AES offload to it, so an encrypted build that wants only the AES win cannot currently express that. +The AES offload is worth about 4.2x on the decrypt. **The hash offload is about 1.3x slower than software** over an image this size: the engine's per-block setup dominates once the data is in DDR rather than a small buffer in L2 scratch, so enabling it costs boot time rather than saving it. `MPFS_ATHENA_SHA=0` keeps the AES offload and returns hashing to software, which is the fastest arrangement for an encrypted image at 9.5 s; `MPFS_ATHENA` with neither offload selected is refused. Note that hashing in software under `MPFS_ATHENA_SHA=0` is still about 9% slower than a build with no crypto callback at all, because every block enters the callback and is turned away. Athena also does not fit eNVM alongside the full feature set: with the manifest linked it needs both `GZIP` and `MPFS_SBI_SC_FLASH` dropped. Dropping `GZIP` costs nothing when the FIT's kernel is stored uncompressed. diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 117a2ed2..88039770 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -503,6 +503,7 @@ static int mpfs_athena_aes256_ctr(const void *key, void *iv, const void *in, /* SHA-384 + AES-256-CTR via the plain-C mpfs_athena_* API (CAL headers cannot * share a TU with wolfSSL's). Undocumented CAL contract: a non-final update * must be whole 128-byte blocks, so short tails need the buffer below. */ +#ifdef MPFS_ATHENA_SHA #define ATHENA_SHA384_BLOCK 128 #define ATHENA_HASH_SLOTS 2 @@ -516,6 +517,7 @@ struct athena_hash_slot { }; static struct athena_hash_slot athena_hash_slots[ATHENA_HASH_SLOTS]; +#endif /* MPFS_ATHENA_SHA */ /* Set once the self-check has passed: from then on a SHA-384 request the * engine cannot take is a hardware error, never a silent software fallback. */ @@ -525,6 +527,7 @@ static int athena_ready; * digest cannot be mistaken for hardware use when the callback never ran. */ static uint32_t athena_cb_calls; +#ifdef MPFS_ATHENA_SHA /* Keyed by wc_Sha384 address; the cryptocb has no free event, so a context * freed without Final strands its slot and a later one at that address would * resume stale CAL state (wrong digest). Only reachable via ONESHOT today. */ @@ -581,6 +584,7 @@ static int athena_hash_feed(struct athena_hash_slot *slot, const uint8_t *in, } return 0; } +#endif /* MPFS_ATHENA_SHA */ #if defined(MPFS_ATHENA_AES) && defined(WOLFSSL_AES_COUNTER) && !defined(NO_AES) /* AES-256-CTR, one path for both directions. Key from aes->devKey, not the @@ -652,7 +656,9 @@ static int athena_aesctr(wc_CryptoInfo *info) static int mpfs_athena_cryptocb(int devIdArg, wc_CryptoInfo *info, void *ctx) { +#ifdef MPFS_ATHENA_SHA struct athena_hash_slot *slot; +#endif (void)devIdArg; (void)ctx; @@ -667,6 +673,9 @@ static int mpfs_athena_cryptocb(int devIdArg, wc_CryptoInfo *info, void *ctx) } #endif +#ifndef MPFS_ATHENA_SHA + return CRYPTOCB_UNAVAILABLE; +#else if (info->algo_type != WC_ALGO_TYPE_HASH || info->hash.type != WC_HASH_TYPE_SHA384) { return CRYPTOCB_UNAVAILABLE; @@ -724,8 +733,10 @@ static int mpfs_athena_cryptocb(int devIdArg, wc_CryptoInfo *info, void *ctx) } return 0; +#endif /* MPFS_ATHENA_SHA */ } +#ifdef MPFS_ATHENA_SHA /* Known answers for the self-check below. SHA-384 of the bytes 0x00..0xFF: * a multi-block vector, hashed in chunks that straddle the 128-byte block so * the intermediate state has to be carried across calls. */ @@ -737,6 +748,7 @@ static const uint8_t athena_kat_sha384_256b[48] = { 0x28, 0x92, 0xe9, 0x17, 0x9b, 0x34, 0x63, 0x23, 0x31, 0xa5, 0x95, 0x92, 0x73, 0x7d, 0xb5, 0xc5 }; +#endif /* MPFS_ATHENA_SHA */ #ifdef MPFS_ATHENA_AES /* AES-256-CTR of the bytes 0x00..0x1F under key 0x00..0x1F, counter 0x00..0x0F. */ static const uint8_t athena_kat_aesctr[32] = { @@ -781,9 +793,11 @@ static int athena_ctr_run(const uint8_t *key, const uint8_t *ctr, static int mpfs_athena_selfcheck(void) { +#ifdef MPFS_ATHENA_SHA wc_Sha384 sha; - uint8_t buf[256]; uint8_t digest[48]; +#endif + uint8_t buf[256]; #ifdef MPFS_ATHENA_AES uint8_t key[32], ctr[16], out[32], out2[32]; #endif @@ -797,6 +811,7 @@ static int mpfs_athena_selfcheck(void) for (i = 0; i < (int)sizeof(buf); i++) { buf[i] = (uint8_t)i; } +#ifdef MPFS_ATHENA_SHA athena_cb_calls = 0; ret = wc_InitSha384_ex(&sha, NULL, WOLFBOOT_DEVID_HASH); if (ret != 0) { @@ -833,6 +848,9 @@ static int mpfs_athena_selfcheck(void) athena_fail_what = "SHA-384 digest mismatch"; return -1; } +#else + ret = 0; +#endif /* MPFS_ATHENA_SHA */ #ifdef MPFS_ATHENA_AES for (i = 0; i < 32; i++) { @@ -886,12 +904,14 @@ static int mpfs_athena_selfcheck(void) * crypto unnoticed: every failure here halts. */ static void mpfs_athena_init(void) { +#ifdef MPFS_ATHENA_SHA int i; for (i = 0; i < ATHENA_HASH_SLOTS; i++) { athena_hash_slots[i].owner = NULL; athena_hash_slots[i].part_len = 0; } +#endif /* Free slots hold INVALID_DEVID (-2), not 0, so registration returns * BUFFER_E until the zero-initialised device table is initialised. */ @@ -920,10 +940,12 @@ static void mpfs_athena_init(void) wolfBoot_panic(); } athena_ready = 1; -#ifdef MPFS_ATHENA_AES +#if defined(MPFS_ATHENA_SHA) && defined(MPFS_ATHENA_AES) wolfBoot_printf("Athena: SHA-384 + AES-256-CTR offload active\n"); -#else +#elif defined(MPFS_ATHENA_SHA) wolfBoot_printf("Athena: SHA-384 offload active\n"); +#else + wolfBoot_printf("Athena: AES-256-CTR offload active\n"); #endif } #endif /* MPFS_ATHENA */ diff --git a/options.mk b/options.mk index 1971b24c..e8b9123b 100644 --- a/options.mk +++ b/options.mk @@ -1991,10 +1991,18 @@ ifeq ($(MPFS_ATHENA),1) CFLAGS += -DWOLFBOOT_DEVID_HASH=0xA7 CFLAGS += -DWOLFBOOT_DEVID_CRYPT=0xA7 WOLFCRYPT_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o - # The offload and its self-check are SHA-384; an image hashed with + # SHA-384 offload. Over a whole image in DDR it is slower than the software + # hash, so an encrypted build wanting only the AES win sets MPFS_ATHENA_SHA=0. + MPFS_ATHENA_SHA ?= 1 + ifeq ($(MPFS_ATHENA_SHA),1) + CFLAGS += -DMPFS_ATHENA_SHA + endif + # The hash offload and its self-check are SHA-384; an image hashed with # something else keeps that in software and still gets the AES offload. - ifneq ($(HASH),SHA384) - AUX_HASH_ALGOS+=sha384 + ifeq ($(MPFS_ATHENA_SHA),1) + ifneq ($(HASH),SHA384) + AUX_HASH_ALGOS+=sha384 + endif endif # Archive name differs between Microchip's two published builds. MPFS_ATHENA_CAL_LIB ?= mpfs-rv64imac-user-crypto-lib.a @@ -2007,6 +2015,14 @@ ifeq ($(MPFS_ATHENA),1) CFLAGS += -DMPFS_ATHENA_AES endif endif + # With neither offload selected the engine is brought up and self-checked for + # nothing, so say so rather than linking CAL into a build that cannot use it. + ifneq ($(MPFS_ATHENA_SHA),1) + ifneq ($(MPFS_ATHENA_AES),1) + $(error MPFS_ATHENA=1 with MPFS_ATHENA_SHA=0 and no AES offload leaves \ + nothing offloaded: drop MPFS_ATHENA, or set ENCRYPT_WITH_AES256=1) + endif + endif endif # Size of the wolfHSM comm data payload, shared by the client and server blocks