From 4bbe28920cd16ae64376b441cea22880a5824231 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 14 Jan 2026 14:33:52 -0800 Subject: [PATCH] Fixes to wolfBoot self-update (memcmp was used which is not a RAMFUNCTION). --- docs/Targets.md | 121 +++++---------------- hal/s32k1xx.c | 122 ++++++++-------------- src/string.c | 1 + src/update_flash.c | 37 +++---- tools/scripts/nxp-s32k142-flash.sh | 162 +++++++++++++++++++++++++++-- 5 files changed, 234 insertions(+), 209 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index ec39a350..2adf8dfe 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -3031,10 +3031,11 @@ The S32K1xx can be programmed and debugged using various tools. The recommended **Using PEMicro (recommended for S32K EVB boards):** 1. Install PEMicro GDB Server from [pemicro.com](https://www.pemicro.com/products/product_viewDetails.cfm?product_id=15320167) +Linux: `~/.local/pemicro/` 2. Start PEMicro GDB Server: ```sh -pegdbserver_console -device=NXP_S32K1xx_S32K142 -startserver -serverport=7224 +pegdbserver_console -device=NXP_S32K1xx_S32K142F256M15 -startserver -interface=OPENSDA -port=USB1 -serverport=7224 -speed=5000 ``` 3. In another terminal, connect with GDB and flash: @@ -3068,41 +3069,6 @@ cp factory.srec /media//S32K142EVB/ The board will automatically program the flash and reset. -### NXP S32K1XX: Flash Script - -A convenience script is provided for building and flashing S32K142: - -```sh -./tools/scripts/nxp-s32k142-flash.sh [OPTIONS] -``` - -**Options:** - -| Option | Description | -|--------|-------------| -| (none) | Build and flash `factory.srec` (v1 only) | -| `--test-update` | Build with v2 in update partition (use `trigger` command to start update) | -| `--update` | Build with v2 and auto-trigger (starts update on boot) | -| `--skip-build` | Skip build, use existing `.srec` file | -| `--skip-flash` | Skip flashing (just build) | -| `--skip-uart` | Skip UART monitoring | -| `--uart-only` | Only monitor UART (no build/flash) | -| `--interactive` | Keep UART open until Ctrl+C | -| `--timeout SECS` | UART capture duration (default: 5s) | - -**Examples:** - -```sh -# Build and flash factory image, monitor UART for 5 seconds -./tools/scripts/nxp-s32k142-flash.sh - -# Build with v2 update, flash, and stay on UART -./tools/scripts/nxp-s32k142-flash.sh --test-update --interactive - -# Just monitor UART -./tools/scripts/nxp-s32k142-flash.sh --uart-only --interactive -``` - ### NXP S32K1XX: Test Application The S32K1xx test application (`test-app/app_s32k1xx.c`) provides a feature-rich demo application for testing wolfBoot functionality. @@ -3141,77 +3107,38 @@ Copyright 2025 wolfSSL Inc. Firmware Version: 1 === Partition Information === -Boot Partition @ 0xC000: +Boot Partition: + Address: 0x0000C000 Version: 1 - State: SUCCESS (0x00) -Update Partition @ 0x25000: - Version: 0 (empty) - State: (no trailer) + State: SUCCESS +Update Partition: + Address: 0x00025000 + Version: 0 + State: SUCCESS +Swap Partition: + Address: 0x0003E000 + Size: 2048 bytes === Keystore Information === -Number of keys: 1 -Key 0: ECDSA P-256 (secp256r1), SHA-256 +Number of public keys: 1 +Hash: SHA-256 + +Key #0: + Algorithm: ECDSA P-256 (secp256r1) + Size: 64 bytes + Data: + 9a 33 e0 18 24 4b a7 29 51 90 15 f0 74 6e e4 a6 + bf 2d 00 47 32 1f 32 5a d6 9a 30 32 d1 c3 30 3f + 0a e3 1b 0d 0f 98 b2 e6 5c eb 42 1c 64 2b 32 db + a4 48 75 5b e3 49 94 45 12 64 e3 57 b4 5b 81 73 Type 'help' for available commands. cmd> ``` -**Testing Firmware Update:** +### NXP S32K1XX: TODO -1. Flash with v2 image: `./tools/scripts/nxp-s32k142-flash.sh --test-update` -2. Connect to UART: `picocom -b 115200 /dev/ttyACM1` -3. Run `status` to verify v1 in boot, v2 in update -4. Run `trigger` to set update flag -5. Run `reboot` to start update -6. After reboot, LED changes from Green (v1) to Blue (v2) -7. Run `success` to mark v2 as good - -### NXP S32K1XX: Flash Configuration Field (FCF) - -The bootloader includes the Flash Configuration Field (FCF) at address 0x400-0x40F with the following settings: -- Flash security: Unsecured -- Flash protection: All regions unprotected -- Backdoor key access: Enabled - -**CRITICAL WARNING:** The FCF region at 0x400-0x40F controls device security settings. Writing incorrect values can **permanently lock the device**, making it irrecoverable. The wolfBoot HAL includes protection to prevent accidental writes to this region. - -### NXP S32K1XX: Recovering a Locked/Unresponsive Device - -If your S32K device becomes locked or unresponsive (e.g., stuck in reset with D1 LED illuminated on S32K-EVB boards), try these recovery procedures: - -**Symptoms of a locked device:** -- Debugger cannot connect ("Soft reset failed", "Failed to enter debug mode") -- Device stuck in reset (D1 LED constantly on for S32K-EVB) -- J-Link reports "Readout protection is set" at address 0x400-0x40F - -**Recovery Option 1: PEMicro Force Mass Erase** - -```sh -pegdbserver_console -device=NXP_S32K1xx_S32K142 -interface=OPENSDA -port=USB1 -forcemasserase -singlesession -``` - -After mass erase completes, power cycle the board before attempting to reconnect. - -**Recovery Option 2: J-Link Unlock** - -```sh -JLinkExe -if swd -Device S32K142 -unlock Kinetis -erase -r -q -``` - -Then power cycle the board. - -### NXP S32K1XX: TODO / Future Enhancements - -The following features are planned or available for contribution: - -- [x] **Sector swap update**: Full firmware update with sector swapping (completed) -- [x] **Interactive test application**: Console with status, trigger, success commands (completed) -- [x] **Flash automation script**: `tools/scripts/nxp-s32k142-flash.sh` (completed) - [ ] **XMODEM improvements**: ISR-based UART RX for reliable high-speed transfers - [ ] **SPLL + SOSC support**: Add external crystal oscillator and SPLL configuration for true 112 MHz operation in HSRUN mode - [ ] **Hardware crypto acceleration**: Integrate CSEc (Cryptographic Services Engine) for hardware-accelerated crypto operations diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c index 576438c1..ce6d7d4b 100644 --- a/hal/s32k1xx.c +++ b/hal/s32k1xx.c @@ -40,6 +40,22 @@ #define DSB() __asm__ volatile ("dsb") #define ISB() __asm__ volatile ("isb") +/* PRIMASK helpers for critical sections */ +#define __get_PRIMASK() ({ \ + uint32_t primask; \ + __asm__ volatile ("mrs %0, primask" : "=r" (primask)); \ + primask; \ +}) + +#define __set_PRIMASK(primask) \ + __asm__ volatile ("msr primask, %0" :: "r" (primask) : "memory") + +#define __disable_irq() \ + __asm__ volatile ("cpsid i" ::: "memory") + +#define __enable_irq() \ + __asm__ volatile ("cpsie i" ::: "memory") + #include "s32k1xx.h" /* ============== Flash Configuration Field (FCF) ============== */ @@ -125,14 +141,6 @@ static void watchdog_enable(uint32_t timeout_ms) while (!(WDOG_CS & WDOG_CS_RCS)) {} } -/* Refresh (kick) the watchdog to prevent reset - * Must be called periodically before timeout expires - */ -static void watchdog_refresh(void) -{ - /* For CMD32EN mode, write refresh key as 32-bit value */ - WDOG_CNT = WDOG_CNT_REFRESH; -} #endif /* WATCHDOG */ /* ============== Clock Configuration ============== */ @@ -352,13 +360,18 @@ static void RAMFUNCTION flash_clear_errors(void) static int RAMFUNCTION flash_program_phrase(uint32_t address, const uint8_t *data) { + /* Skip if phrase is all 0xFF (erased) */ + if (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF && + data[4] == 0xFF && data[5] == 0xFF && data[6] == 0xFF && data[7] == 0xFF) { + return 0; + } + /* Wait for previous command to complete */ flash_wait_complete(); flash_clear_errors(); - /* Set up Program Phrase command (0x07) - * Programs 8 bytes at the specified address - */ + /* Set up Program Phrase command (0x07) */ + /* Programs 8 bytes at the specified address */ FTFC_FCCOB0 = FTFC_CMD_PROGRAM_PHRASE; FTFC_FCCOB1 = (uint8_t)(address >> 16); FTFC_FCCOB2 = (uint8_t)(address >> 8); @@ -384,7 +397,7 @@ static int RAMFUNCTION flash_program_phrase(uint32_t address, const uint8_t *dat #ifdef WATCHDOG /* Refresh watchdog after flash operation */ - watchdog_refresh(); + WDOG_CNT = WDOG_CNT_REFRESH; #endif /* Check for errors */ @@ -414,8 +427,8 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) ISB(); /* Disable interrupts during flash operation to prevent code fetch from flash */ - __asm__ volatile ("mrs %0, primask\n\t" - "cpsid i" : "=r" (primask) :: "memory"); + primask = __get_PRIMASK(); + __disable_irq(); FTFC_FSTAT = FTFC_FSTAT_CCIF; @@ -423,11 +436,11 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) flash_wait_complete(); /* Re-enable interrupts */ - __asm__ volatile ("msr primask, %0" :: "r" (primask) : "memory"); + __set_PRIMASK(primask); #ifdef WATCHDOG /* Refresh watchdog after potentially long flash operation */ - watchdog_refresh(); + WDOG_CNT = WDOG_CNT_REFRESH; #endif /* Check for errors */ @@ -438,7 +451,6 @@ static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) return 0; } - /* ============== HAL Interface Functions ============== */ void hal_init(void) @@ -510,92 +522,40 @@ void hal_prepare_boot(void) int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { - int ret = 0; - int i = 0; + int ret, i = 0; uint8_t phrase_buf[FLASH_PHRASE_SIZE]; - const uint8_t empty_phrase[FLASH_PHRASE_SIZE] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - - /* CRITICAL: Protect the Flash Configuration Field (FCF) region. - * Writing incorrect values to 0x400-0x40F can permanently lock the device! - * The FCF is programmed once in the flash_config section and should never - * be modified at runtime. - */ - if ((address < FCF_END_ADDR) && ((address + len) > FCF_START_ADDR)) { - /* Requested write overlaps with FCF region - this is dangerous! - * Skip the FCF portion to prevent device locking. - */ - if (address < FCF_START_ADDR) { - /* Write portion before FCF */ - int pre_fcf_len = FCF_START_ADDR - address; - ret = hal_flash_write(address, data, pre_fcf_len); - if (ret != 0) return ret; - address = FCF_END_ADDR; - data += pre_fcf_len + (FCF_END_ADDR - FCF_START_ADDR); - len -= pre_fcf_len + (FCF_END_ADDR - FCF_START_ADDR); - } else if (address >= FCF_START_ADDR && address < FCF_END_ADDR) { - /* Skip entirely within FCF region */ - int skip = FCF_END_ADDR - address; - if (skip >= len) { - return 0; /* Entire write is within FCF - skip it */ - } - address = FCF_END_ADDR; - data += skip; - len -= skip; - } - if (len <= 0) return 0; - } while (len > 0) { - /* Handle unaligned start or partial phrase */ if ((len < FLASH_PHRASE_SIZE) || (address & (FLASH_PHRASE_SIZE - 1))) { + /* Handle unaligned start or partial phrase */ uint32_t aligned_addr = address & ~(FLASH_PHRASE_SIZE - 1); uint32_t offset = address - aligned_addr; - int bytes_to_copy; - - /* Read current phrase data */ - memcpy(phrase_buf, (void*)aligned_addr, FLASH_PHRASE_SIZE); - - /* Calculate bytes to copy */ - bytes_to_copy = FLASH_PHRASE_SIZE - offset; - if (bytes_to_copy > len) { + int bytes_to_copy = FLASH_PHRASE_SIZE - offset; + if (bytes_to_copy > len) bytes_to_copy = len; - } - /* Merge new data */ + memcpy(phrase_buf, (void*)aligned_addr, FLASH_PHRASE_SIZE); memcpy(phrase_buf + offset, data + i, bytes_to_copy); - /* Only program if not all 0xFF */ - if (memcmp(phrase_buf, empty_phrase, FLASH_PHRASE_SIZE) != 0) { - ret = flash_program_phrase(aligned_addr, phrase_buf); - if (ret != 0) { - return ret; - } - } + ret = flash_program_phrase(aligned_addr, phrase_buf); + if (ret != 0) + return ret; address += bytes_to_copy; i += bytes_to_copy; len -= bytes_to_copy; - } - else { + } else { /* Program full phrases */ while (len >= FLASH_PHRASE_SIZE) { - /* Only program if not all 0xFF */ - if (memcmp(data + i, empty_phrase, FLASH_PHRASE_SIZE) != 0) { - ret = flash_program_phrase(address, data + i); - if (ret != 0) { - return ret; - } - } - + ret = flash_program_phrase(address, data + i); + if (ret != 0) + return ret; address += FLASH_PHRASE_SIZE; i += FLASH_PHRASE_SIZE; len -= FLASH_PHRASE_SIZE; } } } - return 0; } diff --git a/src/string.c b/src/string.c index 76ed3aff..ed85f8fa 100644 --- a/src/string.c +++ b/src/string.c @@ -258,6 +258,7 @@ size_t strlen(const char *s) #endif #if !defined(__IAR_SYSTEMS_ICC__) && !defined(TARGET_X86_64_EFI) +/* some of the hal_flash_ functions need this during updates */ void RAMFUNCTION *memcpy(void *dst, const void *src, size_t n) { size_t i; diff --git a/src/update_flash.c b/src/update_flash.c index d1580f09..0524f4b4 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -76,6 +76,11 @@ static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src) { uintptr_t pos = 0; uintptr_t src_offset = IMAGE_HEADER_SIZE; +#ifdef ARCH_SIM + uintptr_t start_text = ARCH_FLASH_OFFSET; +#else + uintptr_t start_text = (uintptr_t)&_start_text; /* save off before erase */ +#endif hal_flash_unlock(); wolfBoot_erase_bootloader(); @@ -84,37 +89,23 @@ static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src) while (pos < src->fw_size) { uint8_t buffer[FLASHBUFFER_SIZE]; if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { -#ifdef ARCH_SIM - /* Use ARCH_FLASH_OFFSET for simulator: flash is mmap'd at runtime, - * so the linker symbol _start_text does not point to simulated flash */ - uintptr_t opos = pos + ARCH_FLASH_OFFSET; -#else - uintptr_t opos = pos + ((uintptr_t)&_start_text); -#endif ext_flash_check_read((uintptr_t)(src->hdr) + src_offset + pos, (void*)buffer, FLASHBUFFER_SIZE); - hal_flash_write(opos, buffer, FLASHBUFFER_SIZE); + hal_flash_write(start_text + pos, buffer, FLASHBUFFER_SIZE); } pos += FLASHBUFFER_SIZE; } - goto lock_and_reset; } + else #endif - while (pos < src->fw_size) { - if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { - uint8_t *orig = (uint8_t*)(src->hdr + src_offset + pos); -#ifdef ARCH_SIM - /* Use ARCH_FLASH_OFFSET for simulator: flash is mmap'd at runtime, - * so the linker symbol _start_text does not point to simulated flash */ - hal_flash_write(pos + ARCH_FLASH_OFFSET, orig, FLASHBUFFER_SIZE); -#else - hal_flash_write(pos + (uintptr_t)&_start_text, orig, FLASHBUFFER_SIZE); -#endif + { + while (pos < src->fw_size) { + if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { + uint8_t *orig = (uint8_t*)(src->hdr + src_offset + pos); + hal_flash_write(pos + start_text, orig, FLASHBUFFER_SIZE); + } + pos += FLASHBUFFER_SIZE; } - pos += FLASHBUFFER_SIZE; } -#ifdef EXT_FLASH -lock_and_reset: -#endif hal_flash_lock(); arch_reboot(); } diff --git a/tools/scripts/nxp-s32k142-flash.sh b/tools/scripts/nxp-s32k142-flash.sh index 0b49843a..0744cbe4 100755 --- a/tools/scripts/nxp-s32k142-flash.sh +++ b/tools/scripts/nxp-s32k142-flash.sh @@ -40,16 +40,19 @@ UART_ONLY=0 INTERACTIVE=0 BUILD_UPDATE=0 TEST_UPDATE=0 +TEST_SELFUPDATE=0 usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" - echo " --test-update Build test-update.srec with v2 image in update partition" - echo " (no trigger - use test-app 'trigger' command to start update)" - echo " --update Build update.srec with v2 image + trigger magic" - echo " (auto-starts update on boot - may cause issues)" - echo " --skip-build Skip the build step (use existing .srec)" + echo " --test-update Build test-update.srec with v2 image in update partition" + echo " (no trigger - use test-app 'trigger' command to start update)" + echo " --test-selfupdate Build test-selfupdate.srec with bootloader v1 + v2 bootloader update" + echo " (tests bootloader self-update - requires RAM_CODE=1)" + echo " --update Build update.srec with v2 image + trigger magic" + echo " (auto-starts update on boot - may cause issues)" + echo " --skip-build Skip the build step (use existing .srec)" echo " --skip-flash Skip flashing (just monitor UART)" echo " --skip-uart Skip UART monitoring (just build and flash)" echo " --uart-only Only monitor UART (same as --skip-build --skip-flash)" @@ -63,6 +66,7 @@ usage() { echo "Examples:" echo " $0 # Build and flash factory.srec (v1 only)" echo " $0 --test-update # Build with v2 in update partition, use 'trigger' cmd" + echo " $0 --test-selfupdate # Build with bootloader v2 update, tests self-update" echo " $0 --skip-uart # Flash without UART monitoring" echo " $0 --uart-only # Just monitor UART" exit 0 @@ -80,6 +84,11 @@ while [[ $# -gt 0 ]]; do SREC_FILE="update.srec" shift ;; + --test-selfupdate) + TEST_SELFUPDATE=1 + SREC_FILE="test-selfupdate.srec" + shift + ;; --skip-build) SKIP_BUILD=1 shift @@ -139,6 +148,39 @@ trap cleanup EXIT echo -e "${GREEN}=== NXP S32K142 Flash and Monitor Script ===${NC}" +# Function to parse SIGN and HASH from .config file +parse_config_signing() { + local config_file="$1" + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Config file not found: ${config_file}${NC}" + exit 1 + fi + + # Extract SIGN value (e.g., SIGN?=ECC256 -> ECC256) + SIGN_VALUE=$(grep -E "^SIGN" "$config_file" | head -1 | sed -E 's/^SIGN\??=//' | tr -d '[:space:]') + # Extract HASH value (e.g., HASH?=SHA256 -> SHA256) + HASH_VALUE=$(grep -E "^HASH" "$config_file" | head -1 | sed -E 's/^HASH\??=//' | tr -d '[:space:]') + + if [ -z "$SIGN_VALUE" ]; then + echo -e "${RED}Error: SIGN not found in config file${NC}" + exit 1 + fi + if [ -z "$HASH_VALUE" ]; then + echo -e "${RED}Error: HASH not found in config file${NC}" + exit 1 + fi + + # Convert SIGN to lowercase flag format + SIGN_FLAG=$(echo "$SIGN_VALUE" | tr '[:upper:]' '[:lower:]') + SIGN_FLAG="--${SIGN_FLAG}" + + # Convert HASH to lowercase flag format + HASH_FLAG=$(echo "$HASH_VALUE" | tr '[:upper:]' '[:lower:]') + HASH_FLAG="--${HASH_FLAG}" + + echo -e "${CYAN}Using signing: ${SIGN_VALUE} / ${HASH_VALUE}${NC}" +} + # Change to wolfboot root directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" @@ -156,9 +198,113 @@ if [ $SKIP_BUILD -eq 0 ]; then cp "${CONFIG_FILE}" .config echo "Copied ${CONFIG_FILE} to .config" + # Parse signing configuration from .config + parse_config_signing .config + # Step 2: Build echo "" - if [ $TEST_UPDATE -eq 1 ]; then + if [ $TEST_SELFUPDATE -eq 1 ]; then + echo -e "${GREEN}[2/4] Building test-selfupdate.srec (bootloader v1 + v2 bootloader update)...${NC}" + echo -e "${CYAN}NOTE: This tests bootloader self-update functionality${NC}" + + # Step 2a: Build bootloader v1 + echo "" + echo -e "${CYAN}Building bootloader v1...${NC}" + make clean + make wolfboot.bin RAM_CODE=1 WOLFBOOT_VERSION=1 + if [ ! -f "wolfboot.bin" ]; then + echo -e "${RED}Error: Failed to build wolfboot.bin v1${NC}" + exit 1 + fi + cp wolfboot.bin wolfboot_v1.bin + echo -e "${GREEN}Bootloader v1 built successfully${NC}" + + # Build factory.srec to get the v1 application image + echo "" + echo -e "${CYAN}Building factory image with v1 application...${NC}" + make factory.srec + if [ ! -f "factory.srec" ]; then + echo -e "${RED}Error: Failed to build factory.srec${NC}" + exit 1 + fi + if [ ! -f "test-app/image_v1_signed.bin" ]; then + echo -e "${RED}Error: test-app/image_v1_signed.bin not found${NC}" + exit 1 + fi + # Preserve v1 application image before clean (copy outside test-app to survive clean) + cp test-app/image_v1_signed.bin image_v1_signed_backup.bin + echo -e "${GREEN}Preserved v1 application image${NC}" + + # Step 2b: Build bootloader v2 + echo "" + echo -e "${CYAN}Building bootloader v2...${NC}" + make clean + make wolfboot.bin RAM_CODE=1 WOLFBOOT_VERSION=2 + if [ ! -f "wolfboot.bin" ]; then + echo -e "${RED}Error: Failed to build wolfboot.bin v2${NC}" + exit 1 + fi + + # Step 2c: Sign bootloader v2 with --wolfboot-update flag + echo "" + echo -e "${CYAN}Signing bootloader v2 with --wolfboot-update flag...${NC}" + # Ensure sign tool is built + if [ ! -f "tools/keytools/sign" ]; then + echo -e "${YELLOW}Building sign tool...${NC}" + make -C tools/keytools + fi + # Check if key exists + if [ ! -f "wolfboot_signing_private_key.der" ]; then + echo -e "${RED}Error: wolfboot_signing_private_key.der not found${NC}" + echo "Please generate keys first with: make keys" + exit 1 + fi + + # Sign with --wolfboot-update flag (version 2) using config values + ./tools/keytools/sign ${SIGN_FLAG} ${HASH_FLAG} --wolfboot-update wolfboot.bin wolfboot_signing_private_key.der 2 + if [ ! -f "wolfboot_v2_signed.bin" ]; then + echo -e "${RED}Error: Failed to sign bootloader v2${NC}" + exit 1 + fi + echo -e "${GREEN}Bootloader v2 signed successfully${NC}" + + # Step 2e: Assemble test-selfupdate.bin + echo "" + echo -e "${CYAN}Assembling test-selfupdate.bin...${NC}" + echo " wolfboot_v1.bin @ 0x0 (bootloader v1)" + echo " image_v1_signed.bin @ ${WOLFBOOT_PARTITION_BOOT_ADDRESS} (boot partition)" + echo " wolfboot_v2_signed.bin @ ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} (update partition)" + + # Ensure bin-assemble tool is built + if [ ! -f "tools/bin-assemble/bin-assemble" ]; then + echo -e "${YELLOW}Building bin-assemble tool...${NC}" + make -C tools/bin-assemble + fi + + ./tools/bin-assemble/bin-assemble \ + test-selfupdate.bin \ + 0x0 wolfboot_v1.bin \ + ${WOLFBOOT_PARTITION_BOOT_ADDRESS} image_v1_signed_backup.bin \ + ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} wolfboot_v2_signed.bin + + if [ ! -f "test-selfupdate.bin" ]; then + echo -e "${RED}Error: Failed to assemble test-selfupdate.bin${NC}" + exit 1 + fi + + # Step 2f: Convert to SREC + echo "" + echo -e "${CYAN}Converting to test-selfupdate.srec...${NC}" + arm-none-eabi-objcopy -I binary -O srec --srec-forceS3 test-selfupdate.bin test-selfupdate.srec + + # Cleanup temp files + rm -f trigger_magic.bin + rm -f wolfboot_v1.bin + rm -f image_v1_signed_backup.bin + + echo -e "${GREEN}Build successful: test-selfupdate.srec${NC}" + + elif [ $TEST_UPDATE -eq 1 ]; then echo -e "${GREEN}[2/4] Building test-update.srec (v1 boot + v2 update, no trigger)...${NC}" make clean make factory.srec @@ -167,7 +313,7 @@ if [ $SKIP_BUILD -eq 0 ]; then echo "" echo -e "${CYAN}Signing test-app with version 2...${NC}" cp test-app/image_v1_signed.bin test-app/image_v1_signed_backup.bin - ./tools/keytools/sign --ecc256 --sha256 test-app/image.bin wolfboot_signing_private_key.der 2 + ./tools/keytools/sign ${SIGN_FLAG} ${HASH_FLAG} test-app/image.bin wolfboot_signing_private_key.der 2 echo -e "${CYAN}Assembling test-update.bin...${NC}" echo " wolfboot.bin @ 0x0" @@ -206,7 +352,7 @@ if [ $SKIP_BUILD -eq 0 ]; then echo "" echo -e "${CYAN}Signing test-app with version 2...${NC}" cp test-app/image_v1_signed.bin test-app/image_v1_signed_backup.bin - ./tools/keytools/sign --ecc256 --sha256 test-app/image.bin wolfboot_signing_private_key.der 2 + ./tools/keytools/sign ${SIGN_FLAG} ${HASH_FLAG} test-app/image.bin wolfboot_signing_private_key.der 2 # Sign tool outputs to image_v2_signed.bin directly # Create trigger magic: 'p' (IMG_STATE_UPDATING = 0x70) + "BOOT"