diff --git a/.gitignore b/.gitignore index 3a07d3512c..95fd7b8ea9 100644 --- a/.gitignore +++ b/.gitignore @@ -209,6 +209,7 @@ tools/unit-tests/unit-policy-create tools/unit-tests/unit-sign-encrypted-output tools/unit-tests/unit-update-flash-delta tools/unit-tests/unit-update-flash-self-update +tools/unit-tests/unit-update-flash-self-update-monolithic tools/unit-tests/unit-update-flash-hook tools/unit-tests/unit-loader-tpm-init tools/unit-tests/unit-update-ram-nofixed diff --git a/docs/firmware_update.md b/docs/firmware_update.md index 814847b955..c9186b9412 100644 --- a/docs/firmware_update.md +++ b/docs/firmware_update.md @@ -257,6 +257,15 @@ bytes and the nested application image. Later calls to `fw_size` bytes starting at `ARCH_FLASH_OFFSET`, spanning into the BOOT partition. +The persisted header also sets the version a new payload must exceed. A +staged payload is installed only if its version is greater than both the +running bootloader's `WOLFBOOT_VERSION` and the version in the +self-header. The bootloader inside each payload can therefore keep the +same `WOLFBOOT_VERSION` across releases. Without the self-header only +`WOLFBOOT_VERSION` is checked, so the bootloader in each payload must be +built with `WOLFBOOT_VERSION` equal to the payload's signed version, +otherwise the update is rejected. + ##### Restrictions - **Not power-fail safe.** Like all self-updates, a monolithic update diff --git a/hal/aurix_tc3xx.c b/hal/aurix_tc3xx.c index b8132f8c8d..8e660247f1 100644 --- a/hal/aurix_tc3xx.c +++ b/hal/aurix_tc3xx.c @@ -91,7 +91,9 @@ /* wolfHSM client context and configuration */ #if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) -static int _connectCb(void* context, whCommConnected connect); +/* wolfHSM TC3xx port client, which selects the comm buffer and the bridge + * flags. wolfBoot and then the application on CPU0 both use client 0. */ +#define HSM_CLIENT 0 /* Client configuration/contexts */ static whTransportMemClientContext tmcCtx[1] = {0}; @@ -172,8 +174,8 @@ void wolfBoot_panic(void) TC3_LONGCALL; * to force the HSM core to execute from RAM while the host-driven flash * command completes. * The server app must be running first to process a park request */ -#define HSM_PARK() (void)tchsmHhHost_HsmPark() -#define HSM_RELEASE() (void)tchsmHhHost_HsmRelease() +#define HSM_PARK() (void)tchsmHhHost_HsmPark(HSM_CLIENT) +#define HSM_RELEASE() (void)tchsmHhHost_HsmRelease(HSM_CLIENT) #else #define HSM_PARK() #define HSM_RELEASE() @@ -587,41 +589,11 @@ RAMFUNCTION void ext_flash_unlock(void) #ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT -static int _connectCb(void* context, whCommConnected connect) -{ - int ret; - - switch (connect) { - case WH_COMM_CONNECTED: - ret = tchsmHhHost2Hsm_Notify(TCHSM_HOST2HSM_NOTIFY_CONNECT); - break; - case WH_COMM_DISCONNECTED: - ret = tchsmHhHost2Hsm_Notify(TCHSM_HOST2HSM_NOTIFY_DISCONNECT); - break; - default: - ret = WH_ERROR_BADARGS; - break; - } - - return ret; -} - int hal_hsm_init_connect(void) { - int rc = 0; - size_t i; - - /* init shared memory buffers */ - uint32_t* req = (uint32_t*)hsmShmCore0CommBuf; - uint32_t* resp = - (uint32_t*)hsmShmCore0CommBuf + HSM_SHM_CORE0_COMM_BUF_WORDS / 2; - whTransportMemConfig tmcCfg[1] = {{ - .req = req, - .req_size = HSM_SHM_CORE0_COMM_BUF_SIZE / 2, - .resp = resp, - .resp_size = HSM_SHM_CORE0_COMM_BUF_SIZE / 2, - }}; + int rc = 0; + whTransportMemConfig tmcCfg[1] = {{0}}; /* Client configuration/contexts */ whCommClientConfig cc_conf[1] = {{ @@ -629,21 +601,21 @@ int hal_hsm_init_connect(void) .transport_context = (void*)tmcCtx, .transport_config = (void*)tmcCfg, .client_id = WOLFBOOT_WOLFHSM_CLIENT_ID, - .connect_cb = _connectCb, + .connect_cb = hsm_ipc_get_connect_cb(HSM_CLIENT), }}; whClientConfig c_conf[1] = {{ .comm = cc_conf, }}; - rc = hsm_ipc_init(); + rc = hsm_ipc_init(HSM_CLIENT); if (rc != WH_ERROR_OK) { return rc; } - /* init shared memory buffers */ - for (i = 0; i < HSM_SHM_CORE0_COMM_BUF_WORDS; i++) { - hsmShmCore0CommBuf[i] = 0; + rc = hsm_ipc_transport_config(HSM_CLIENT, tmcCfg); + if (rc != WH_ERROR_OK) { + return rc; } rc = wh_Client_Init(&hsmClientCtx, c_conf); diff --git a/src/update_flash.c b/src/update_flash.c index b1cd03ee9e..976365a25c 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -287,7 +287,17 @@ void RAMFUNCTION wolfBoot_check_self_update(void) (wolfBoot_open_image(&update, PART_UPDATE) == 0) && wolfBoot_get_image_type(PART_UPDATE) == (HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH)) { uint32_t update_version = wolfBoot_update_firmware_version(); - if (update_version <= wolfboot_version) { + uint32_t current_version = wolfboot_version; +#if defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) && defined(WOLFBOOT_SELF_HEADER) + /* The self-header has the installed payload's version, which may be + * newer than the WOLFBOOT_VERSION this bootloader was built with */ + uint32_t self_version = wolfBoot_get_self_version(); + + if (self_version > current_version) { + current_version = self_version; + } +#endif + if (update_version <= current_version) { wolfBoot_erase_partition(PART_UPDATE); return; } diff --git a/test-app/app_aurix_tc3xx.c b/test-app/app_aurix_tc3xx.c index b6c275fd03..fd58d937dd 100644 --- a/test-app/app_aurix_tc3xx.c +++ b/test-app/app_aurix_tc3xx.c @@ -49,7 +49,8 @@ static void hsmUpdate(void) if (wolfBoot_current_firmware_version() > BASE_FW_VERSION) { return; } - rc = tchsmHhHost_HsmUpdateTrigger(); + /* Client 0 is the wolfHSM client of this core */ + rc = tchsmHhHost_HsmUpdateTrigger(0); if (rc == 0) { wolfBoot_printf("HSM update triggered\n"); } diff --git a/tools/test.mk b/tools/test.mk index 8f39c6e66c..3700f0b313 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -304,6 +304,7 @@ test-sim-self-update-monolithic: wolfboot.bin test-app/image_v1_signed.bin FORCE # Same as test-sim-self-update-monolithic, but also verifies that the # self-header was persisted at WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS and # matches the signing tool's --header-only output byte-for-byte. +# A second boot checks that the payload is not installed again. # test-sim-self-update-monolithic-self-header: wolfboot.bin test-app/image_v1_signed.bin FORCE @echo "=== Simulator Monolithic Self-Update + Self-Header Test ===" @@ -341,6 +342,11 @@ test-sim-self-update-monolithic-self-header: wolfboot.bin test-app/image_v1_sign $(Q)dd if=internal_flash.dd bs=1 skip=$$(($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) - $(ARCH_FLASH_OFFSET))) count=$$(($(WOLFBOOT_SECTOR_SIZE))) of=persisted_hdr.dd 2>/dev/null $(Q)cmp -n $$(($(IMAGE_HEADER_SIZE))) persisted_hdr.dd monolithic_payload_v2_header.bin @echo " Self-header persisted correctly: PASSED" + @# Boot again. The self-header shows the payload is already installed, so + @# wolfBoot should erase it from the update partition, not install it again. + $(Q)./wolfboot.elf get_version || true + $(Q)dd if=internal_flash.dd bs=1 skip=$$(($(WOLFBOOT_PARTITION_UPDATE_ADDRESS) - $(ARCH_FLASH_OFFSET))) count=4 2>/dev/null | od -An -tx1 | grep -q "ff ff ff ff" + @echo " Installed payload erased from UPDATE on next boot: PASSED" @echo "=== Monolithic Self-Update + Self-Header Test PASSED ===" # Test that an oversized monolithic self-update is rejected. The payload is diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index f06f10ece0..83faf163fb 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -67,6 +67,7 @@ TESTS:=unit-parser unit-parser-large-header unit-fdt unit-extflash unit-string \ unit-update-flash-hook \ unit-update-flash-self-update \ unit-update-flash-self-update-ecc \ + unit-update-flash-self-update-monolithic \ unit-nsc-update \ unit-update-flash-enc unit-update-flash-enc-full unit-update-flash-enc-intswap unit-update-ram unit-update-ram-fit unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-nofixed-noramboot unit-update-ram-noramboot unit-update-ram-custom-trailer unit-custom-trailer-nopart unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \ unit-update-disk unit-update-disk-confirm unit-update-disk-golden unit-update-disk-fsp unit-update-disk-oob unit-update-disk-fit unit-update-disk-fit-ramdisk unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \ @@ -408,6 +409,17 @@ unit-update-flash-self-update-ecc:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN - -DNVM_FLASH_JOURNAL -DNVM_FLASH_ECC -DWOLFBOOT_FLASH_WRITE_UNIT=32 \ -DWOLFBOOT_SECTOR_SIZE=0x1000 -DWOLFBOOT_SELF_HEADER \ -DWOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0xCD004000 +# Same as unit-update-flash-self-update, plus monolithic mode and a self-header +# (in the unused SWAP sector), to check that an update is only installed if it +# is newer than the version in the self-header. Some addresses are plain +# numbers because image.c is built separately and cannot see MOCK_ADDRESS_*. +unit-update-flash-self-update-monolithic:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ + -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ + -DRAM_CODE -DARCH_SIM -DUNIT_TEST_SELF_UPDATE_ONLY -DWOLFBOOT_SELF_UPDATE_MONOLITHIC \ + -DWOLFBOOT_SELF_HEADER -DWOLFBOOT_SELF_HEADER_EXT \ + -DWOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0xCE000000 \ + -DARCH_FLASH_OFFSET=0xCD000000 -DWOLFBOOT_VERSION=7 \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-update-ram-fit:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \ -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP \ @@ -872,6 +884,11 @@ unit-update-flash-self-update-ecc: ../../include/target.h unit-update-flash.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ $(CFLAGS) $(LDFLAGS) +unit-update-flash-self-update-monolithic: ../../include/target.h unit-update-flash.c + gcc -o $@ unit-update-flash.c ../../src/image.c \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ + $(CFLAGS) $(LDFLAGS) + unit-sdhci-response-bits: ../../include/target.h unit-sdhci-response-bits.c gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \ -Wl,--gc-sections diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 0000d11a88..2eb87f8230 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -586,6 +586,91 @@ START_TEST (test_self_update_ecc_whole_units) } END_TEST #endif + +#if defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) && defined(WOLFBOOT_SELF_HEADER) +/* Write a self-header with only the magic, size and version fields */ +static void set_self_header_version(uint32_t version) +{ + uintptr_t hdr = WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS; + uint32_t magic = WOLFBOOT_MAGIC; + uint32_t size_img = host_to_img_u32(TEST_SIZE_SMALL); + uint32_t word = 4 << 16 | HDR_VERSION; + uint32_t version_img = host_to_img_u32(version); + + ext_flash_unlock(); + ext_flash_erase(hdr, WOLFBOOT_SELF_HEADER_SIZE); + ext_flash_write(hdr, (void *)&magic, 4); + ext_flash_write(hdr + 4, (void *)&size_img, 4); + ext_flash_write(hdr + 8, (void *)&word, 4); + ext_flash_write(hdr + 12, (void *)&version_img, 4); + ext_flash_lock(); +} + +static void stage_self_update(uint32_t version) +{ + add_payload_type(PART_UPDATE, version, TEST_SIZE_SMALL, + HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH); + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + ext_flash_lock(); +} + +/* The update has the same version as the self-header, so it is already + * installed and should be erased, not installed again */ +START_TEST (test_self_update_self_header_sameversion_erased) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + set_self_header_version(WOLFBOOT_VERSION + 2); + stage_self_update(WOLFBOOT_VERSION + 2); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 0); + ck_assert_int_ge(erased_update, 1); + ck_assert_uint_eq(*(uint32_t *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, + 0xFFFFFFFFu); + cleanup_flash(); +} +END_TEST + +/* The update is older than the self-header version, so it should be erased + * even though it is newer than WOLFBOOT_VERSION */ +START_TEST (test_self_update_self_header_oldversion_erased) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + set_self_header_version(WOLFBOOT_VERSION + 2); + stage_self_update(WOLFBOOT_VERSION + 1); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 0); + ck_assert_int_ge(erased_update, 1); + ck_assert_uint_eq(*(uint32_t *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, + 0xFFFFFFFFu); + cleanup_flash(); +} +END_TEST + +START_TEST (test_self_update_self_header_newversion_installs) +{ + reset_mock_stats(); + prepare_flash(); + clear_erase_stats(); + set_self_header_version(WOLFBOOT_VERSION + 2); + stage_self_update(WOLFBOOT_VERSION + 3); + + wolfBoot_check_self_update(); + + ck_assert_int_eq(arch_reboot_called, 1); + ck_assert_uint_eq(wolfBoot_get_self_version(), WOLFBOOT_VERSION + 3); + cleanup_flash(); +} +END_TEST +#endif /* WOLFBOOT_SELF_UPDATE_MONOLITHIC && WOLFBOOT_SELF_HEADER */ #endif #ifndef UNIT_TEST_SELF_UPDATE_ONLY @@ -2284,11 +2369,21 @@ Suite *wolfboot_suite(void) #ifdef NVM_FLASH_ECC TCase *self_update_ecc = tcase_create("Self update on ECC flash"); #endif +#if defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) && defined(WOLFBOOT_SELF_HEADER) + TCase *self_header_sameversion = tcase_create("Self update self-header same version erased"); + TCase *self_header_oldversion = tcase_create("Self update self-header older version erased"); + TCase *self_header_newversion = tcase_create("Self update self-header newer version installs"); +#endif tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); tcase_add_test(self_update_oldversion, test_self_update_oldversion_erased); tcase_add_test(self_update_invalid_integrity, test_self_update_newversion_invalid_integrity_denied); tcase_add_test(self_update_success, test_self_update_newversion_copies_and_reboots); +#if defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) && defined(WOLFBOOT_SELF_HEADER) + tcase_add_test(self_header_sameversion, test_self_update_self_header_sameversion_erased); + tcase_add_test(self_header_oldversion, test_self_update_self_header_oldversion_erased); + tcase_add_test(self_header_newversion, test_self_update_self_header_newversion_installs); +#endif suite_add_tcase(s, self_update_sameversion); suite_add_tcase(s, self_update_oldversion); @@ -2298,6 +2393,11 @@ Suite *wolfboot_suite(void) tcase_add_test(self_update_ecc, test_self_update_ecc_whole_units); suite_add_tcase(s, self_update_ecc); #endif +#if defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC) && defined(WOLFBOOT_SELF_HEADER) + suite_add_tcase(s, self_header_sameversion); + suite_add_tcase(s, self_header_oldversion); + suite_add_tcase(s, self_header_newversion); +#endif #endif return s; #else