diff --git a/hal/stm32f4.c b/hal/stm32f4.c index 96082ffb..6bd2bd1c 100644 --- a/hal/stm32f4.c +++ b/hal/stm32f4.c @@ -196,26 +196,28 @@ void RAMFUNCTION hal_flash_unlock(void) FLASH_KEYR = FLASH_KEY2; } +#if ((FLASH_ACR_ENABLE_INST_CACHE << 2) != FLASH_ACR_RESET_INST_CACHE) || \ + ((FLASH_ACR_ENABLE_DATA_CACHE << 2) != FLASH_ACR_RESET_DATA_CACHE) +#error "STM32F4: flash cache reset bits are not two positions above the enables" +#endif /* RM0090 3.5.1: the instruction and data caches keep lines fetched before an * erase/program, so a read-back through the flash memory map can return - * pre-erase bytes. The reset bits are only writable while the corresponding - * cache is disabled, hence the disable/reset/re-enable dance. */ + * pre-erase bytes. A cache reset bit is only writable while its cache is + * disabled, and the reset bits sit two positions above the enable bits + * (ICEN 9 -> ICRST 11, DCEN 10 -> DCRST 12), so one shift covers both. */ void RAMFUNCTION hal_cache_invalidate(void) { uint32_t acr = FLASH_ACR; + uint32_t en = acr & (FLASH_ACR_ENABLE_INST_CACHE | + FLASH_ACR_ENABLE_DATA_CACHE); + uint32_t off = acr & ~en; - if (acr & FLASH_ACR_ENABLE_INST_CACHE) { - FLASH_ACR &= ~FLASH_ACR_ENABLE_INST_CACHE; - FLASH_ACR |= FLASH_ACR_RESET_INST_CACHE; - FLASH_ACR &= ~FLASH_ACR_RESET_INST_CACHE; - FLASH_ACR |= FLASH_ACR_ENABLE_INST_CACHE; - } - if (acr & FLASH_ACR_ENABLE_DATA_CACHE) { - FLASH_ACR &= ~FLASH_ACR_ENABLE_DATA_CACHE; - FLASH_ACR |= FLASH_ACR_RESET_DATA_CACHE; - FLASH_ACR &= ~FLASH_ACR_RESET_DATA_CACHE; - FLASH_ACR |= FLASH_ACR_ENABLE_DATA_CACHE; - } + if (en == 0) + return; + FLASH_ACR = off; /* disable the caches that were on */ + FLASH_ACR = off | (en << 2); /* set their reset bits */ + FLASH_ACR = off; /* release reset */ + FLASH_ACR = acr; /* restore the original enables */ } void RAMFUNCTION hal_flash_lock(void) diff --git a/hal/stm32g4.c b/hal/stm32g4.c index 137f1436..a880c0ad 100644 --- a/hal/stm32g4.c +++ b/hal/stm32g4.c @@ -98,26 +98,27 @@ void RAMFUNCTION hal_flash_unlock(void) } } +#if ((FLASH_ACR_ICEN << 2) != FLASH_ACR_ICRST) || \ + ((FLASH_ACR_DCEN << 2) != FLASH_ACR_DCRST) +#error "STM32G4: flash cache reset bits are not two positions above the enables" +#endif /* RM0440 3.3.3: the instruction and data caches keep lines fetched before an * erase/program, so a read-back through the flash memory map can return - * pre-erase bytes. The reset bits are only writable while the corresponding - * cache is disabled, hence the disable/reset/re-enable dance. */ + * pre-erase bytes. A cache reset bit is only writable while its cache is + * disabled, and the reset bits sit two positions above the enable bits + * (ICEN 9 -> ICRST 11, DCEN 10 -> DCRST 12), so one shift covers both. */ void RAMFUNCTION hal_cache_invalidate(void) { uint32_t acr = FLASH_ACR; + uint32_t en = acr & (FLASH_ACR_ICEN | FLASH_ACR_DCEN); + uint32_t off = acr & ~en; - if (acr & FLASH_ACR_ICEN) { - FLASH_ACR &= ~FLASH_ACR_ICEN; - FLASH_ACR |= FLASH_ACR_ICRST; - FLASH_ACR &= ~FLASH_ACR_ICRST; - FLASH_ACR |= FLASH_ACR_ICEN; - } - if (acr & FLASH_ACR_DCEN) { - FLASH_ACR &= ~FLASH_ACR_DCEN; - FLASH_ACR |= FLASH_ACR_DCRST; - FLASH_ACR &= ~FLASH_ACR_DCRST; - FLASH_ACR |= FLASH_ACR_DCEN; - } + if (en == 0) + return; + FLASH_ACR = off; /* disable the caches that were on */ + FLASH_ACR = off | (en << 2); /* set their reset bits */ + FLASH_ACR = off; /* release reset */ + FLASH_ACR = acr; /* restore the original enables */ } void RAMFUNCTION hal_flash_lock(void) diff --git a/tools/test.mk b/tools/test.mk index cbc82c3d..baaa3054 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1231,55 +1231,57 @@ test-size-all: # Several limits below are raised relative to upstream. This branch bumps # lib/wolfssl to the ti_c25 merge for CHAR_BIT!=8 wide-byte support, which # costs 4-48 bytes depending on configuration (most of it in the no-ASM - # SP-math and ML-DSA paths). No wolfBoot code changed in these builds, and - # each new value is the measured size, matching upstream's convention. + # SP-math and ML-DSA paths). On top of that, every entry here is an + # STM32F407 build, so each one also carries the flat 48 bytes of + # hal_cache_invalidate() and its call from hal_flash_lock(). Each value + # is the measured size, matching upstream's convention. # Measured with the CI container (ghcr.io/wolfssl/wolfboot-ci-arm). - make test-size SIGN=NONE LIMIT=5116 NO_ARM_ASM=1 + make test-size SIGN=NONE LIMIT=5164 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED25519 LIMIT=12228 NO_ARM_ASM=1 + make test-size SIGN=ED25519 LIMIT=12276 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ECC256 LIMIT=18924 NO_ARM_ASM=1 + make test-size SIGN=ECC256 LIMIT=18972 NO_ARM_ASM=1 make clean - make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13968 NO_ARM_ASM=1 + make test-size SIGN=ECC256 NO_ASM=1 LIMIT=14016 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA2048 LIMIT=11816 NO_ARM_ASM=1 + make test-size SIGN=RSA2048 LIMIT=11864 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=12372 NO_ARM_ASM=1 + make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=12412 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA4096 LIMIT=12116 NO_ARM_ASM=1 + make test-size SIGN=RSA4096 LIMIT=12164 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12660 NO_ARM_ASM=1 + make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12708 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ECC384 LIMIT=19608 NO_ARM_ASM=1 + make test-size SIGN=ECC384 LIMIT=19656 NO_ARM_ASM=1 make clean - make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15328 NO_ARM_ASM=1 + make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15376 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED448 LIMIT=14256 NO_ARM_ASM=1 + make test-size SIGN=ED448 LIMIT=14288 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSA3072 LIMIT=11956 NO_ARM_ASM=1 + make test-size SIGN=RSA3072 LIMIT=12004 NO_ARM_ASM=1 make clean - make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12480 NO_ARM_ASM=1 + make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12528 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS2048 LIMIT=13748 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS2048 LIMIT=13796 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14304 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14344 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS3072 LIMIT=13916 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS3072 LIMIT=13964 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14436 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14484 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS4096 LIMIT=14088 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS4096 LIMIT=14136 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14628 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14676 NO_ARM_ASM=1 make keysclean make test-size SIGN=LMS LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 \ WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2644 \ - IMAGE_HEADER_SIZE?=5288 LIMIT=8120 NO_ARM_ASM=1 + IMAGE_HEADER_SIZE?=5288 LIMIT=8168 NO_ARM_ASM=1 make keysclean make test-size SIGN=XMSS XMSS_PARAMS='XMSS-SHA2_10_256' \ IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE?=4096 \ - LIMIT=8772 NO_ARM_ASM=1 + LIMIT=8820 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19630 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19678 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 diff --git a/tools/unit-tests/unit-pkcs11_store.c b/tools/unit-tests/unit-pkcs11_store.c index b3b5de6c..0daa9a36 100644 --- a/tools/unit-tests/unit-pkcs11_store.c +++ b/tools/unit-tests/unit-pkcs11_store.c @@ -766,14 +766,22 @@ static int vault_obj_write(int type, CK_ULONG tok, CK_ULONG obj, return ret; } +/* "The object is not in the vault" is a legitimate state after a power cut, + * but any other open/read failure means the vault or its metadata is + * damaged. Report the two distinctly so the power-fail test can insist on + * the former and fail on the latter. */ +#define VAULT_OBJ_ABSENT (-1000) + static int vault_obj_read(int type, CK_ULONG tok, CK_ULONG obj, uint8_t *out, int max) { void *store = NULL; int ret = wolfPKCS11_Store_Open(type, tok, obj, 1, &store); + if (ret == NOT_AVAILABLE_E) + return VAULT_OBJ_ABSENT; if (ret != 0) - return -1; + return ret; ret = wolfPKCS11_Store_Read(store, out, max); wolfPKCS11_Store_Close(store); return ret; @@ -846,9 +854,14 @@ START_TEST (test_power_fail_during_rewrite_never_mixes_generations) { "payload", crash); } else { - ck_assert_msg(ret <= 0, - "power fail at op %d: object read back %d bytes, neither " - "generation nor empty", crash, ret); + /* Only two other outcomes are crash-safe: the object was never + * published, or it is present but truncated to empty by the + * Open-time durability commit. Every other return (a negative + * read error, or a partial payload length) means the vault came + * back damaged. */ + ck_assert_msg(ret == VAULT_OBJ_ABSENT || ret == 0, + "power fail at op %d: object read back %d, neither old " + "payload, new payload, empty, nor absent", crash, ret); } } }