From d892624548edae0ed5dd9a717180ebe7a1594c94 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 12 Jan 2026 10:45:20 -0800 Subject: [PATCH 1/4] Add NXP S32K1xx support with full update functionality - HAL: Flash (FTFC), UART (LPUART1), Clock (FIRC 48MHz), Watchdog - Requires NVM_FLASH_WRITEONCE=1 and RAM_CODE=1 for proper operation - Test app with interactive console: status, trigger, success, reboot - LED indicators: Green (v1), Blue (v2+) - Flash automation script: tools/scripts/nxp-s32k142-flash.sh - Tested on S32K142EVB with sector swap update Supported variants: S32K142 (256KB), S32K144 (512KB), S32K146 (1MB), S32K148 (2MB) --- .github/workflows/test-configs.yml | 24 + .gitignore | 1 + Makefile | 7 +- config/examples/nxp-s32k142.config | 55 ++ config/examples/nxp-s32k144.config | 58 ++ config/examples/nxp-s32k146.config | 58 ++ config/examples/nxp-s32k148.config | 58 ++ docs/Targets.md | 273 +++++++ hal/hal.c | 1 - hal/s32k1xx.c | 635 ++++++++++++++++ hal/s32k1xx.h | 633 ++++++++++++++++ hal/s32k1xx.ld | 86 +++ src/update_flash.c | 1 + test-app/ARM-s32k1xx.ld | 76 ++ test-app/Makefile | 8 + test-app/app_s32k1xx.c | 1089 ++++++++++++++++++++++++++++ test-app/startup_arm.c | 119 ++- tools/scripts/nxp-s32k142-flash.sh | 337 +++++++++ 18 files changed, 3501 insertions(+), 18 deletions(-) create mode 100644 config/examples/nxp-s32k142.config create mode 100644 config/examples/nxp-s32k144.config create mode 100644 config/examples/nxp-s32k146.config create mode 100644 config/examples/nxp-s32k148.config create mode 100644 hal/s32k1xx.c create mode 100644 hal/s32k1xx.h create mode 100644 hal/s32k1xx.ld create mode 100644 test-app/ARM-s32k1xx.ld create mode 100644 test-app/app_s32k1xx.c create mode 100755 tools/scripts/nxp-s32k142-flash.sh diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index e3a2910a6e..d173cdec91 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -216,6 +216,30 @@ jobs: arch: arm config-file: ./config/examples/mcxw-tz.config + nxp_s32k142_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k142.config + + nxp_s32k144_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k144.config + + nxp_s32k146_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k146.config + + nxp_s32k148_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/nxp-s32k148.config + microchip_mpfs250_test: uses: ./.github/workflows/test-build-riscv.yml with: diff --git a/.gitignore b/.gitignore index 61451464e0..d9ec002ac4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ *.rom *.bin *.efi +*.srec # Debug files *.dSYM/ diff --git a/Makefile b/Makefile index bb62d3d320..a975a3f150 100644 --- a/Makefile +++ b/Makefile @@ -462,6 +462,10 @@ endif $(WOLFBOOT_ORIGIN) wolfboot.bin \ $(WOLFBOOT_PARTITION_BOOT_ADDRESS) test-app/image_v1_signed.bin +factory.srec: factory.bin + @echo "\t[BIN2SREC] $@" + $(Q)$(OBJCOPY) -I binary -O srec --change-addresses=$(WOLFBOOT_ORIGIN) $< $@ + factory_wstage1.bin: $(BINASSEMBLE) stage1/loader_stage1.bin wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-app/image_v1_signed.bin @echo "\t[MERGE] $@" $(Q)$(BINASSEMBLE) $@ \ @@ -513,6 +517,7 @@ $(LSCRIPT): $(LSCRIPT_IN) FORCE hex: wolfboot.hex srec: wolfboot.srec +factory-srec: factory.srec %.hex:%.elf @echo "\t[ELF2HEX] $@" @@ -545,7 +550,7 @@ clean: $(Q)rm -f src/wc_secure_calls.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/hal/*.o $(WOLFBOOT_LIB_WOLFTPM)/examples/pcr/*.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/Renesas/*.o - $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex + $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex wolfboot.srec factory.srec $(Q)rm -f $(MACHINE_OBJ) $(MAIN_TARGET) $(LSCRIPT) $(Q)rm -f $(OBJS) $(Q)rm -f tools/keytools/otp/otp-keystore-gen diff --git a/config/examples/nxp-s32k142.config b/config/examples/nxp-s32k142.config new file mode 100644 index 0000000000..7b781dbe2f --- /dev/null +++ b/config/examples/nxp-s32k142.config @@ -0,0 +1,55 @@ +# wolfBoot configuration for NXP S32K142 +# +# S32K142: Cortex-M4F, 256KB Flash, 32KB SRAM +# Flash sector size: 2KB +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k142.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +NO_MPU=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 + +# S32K1xx flash requires erase before write (cannot re-program same location) +# This is required for sector swap trailer flag updates to work correctly +NVM_FLASH_WRITEONCE?=1 + +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# 2KB sectors (S32K142 only - larger variants use 4KB sectors) +WOLFBOOT_SECTOR_SIZE?=0x800 + +# Memory layout for S32K142 (256KB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x00024FFF (100 KB) +# Update Partition: 0x00025000 - 0x0003DFFF (100 KB) +# Swap Sector: 0x0003E000 - 0x0003E7FF (2 KB) +WOLFBOOT_PARTITION_SIZE?=0x19000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x25000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x3E000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG + +# Debugging options (uncomment as needed) +DEBUG?=0 +DEBUG_SYMBOLS?=0 +DEBUG_UART?=0 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT \ No newline at end of file diff --git a/config/examples/nxp-s32k144.config b/config/examples/nxp-s32k144.config new file mode 100644 index 0000000000..5d93640f9d --- /dev/null +++ b/config/examples/nxp-s32k144.config @@ -0,0 +1,58 @@ +# wolfBoot configuration for NXP S32K144 +# +# S32K144: Cortex-M4F, 512KB Flash, 64KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k144.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +NO_MPU=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 + +# S32K1xx flash requires erase before write (cannot re-program same location) +# This is required for sector swap trailer flag updates to work correctly +NVM_FLASH_WRITEONCE?=1 + +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K144 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K144 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K144 (512KB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x00043FFF (224 KB) +# Update Partition: 0x00044000 - 0x0007BFFF (224 KB) +# Swap Sector: 0x0007C000 - 0x0007CFFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0x38000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x44000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x7C000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG + +# Debugging options (uncomment as needed) +DEBUG?=0 +DEBUG_SYMBOLS?=0 +DEBUG_UART?=0 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT \ No newline at end of file diff --git a/config/examples/nxp-s32k146.config b/config/examples/nxp-s32k146.config new file mode 100644 index 0000000000..51e0054611 --- /dev/null +++ b/config/examples/nxp-s32k146.config @@ -0,0 +1,58 @@ +# wolfBoot configuration for NXP S32K146 +# +# S32K146: Cortex-M4F, 1MB Flash, 128KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k146.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +NO_MPU=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 + +# S32K1xx flash requires erase before write (cannot re-program same location) +# This is required for sector swap trailer flag updates to work correctly +NVM_FLASH_WRITEONCE?=1 + +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K146 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K146 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K146 (1MB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x0007FFFF (464 KB) +# Update Partition: 0x00080000 - 0x000F3FFF (464 KB) +# Swap Sector: 0x000F4000 - 0x000F4FFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0x74000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x80000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xF4000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG + +# Debugging options (uncomment as needed) +DEBUG?=0 +DEBUG_SYMBOLS?=0 +DEBUG_UART?=0 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT \ No newline at end of file diff --git a/config/examples/nxp-s32k148.config b/config/examples/nxp-s32k148.config new file mode 100644 index 0000000000..5a75225f38 --- /dev/null +++ b/config/examples/nxp-s32k148.config @@ -0,0 +1,58 @@ +# wolfBoot configuration for NXP S32K148 +# +# S32K148: Cortex-M4F, 2MB Flash, 256KB SRAM +# Flash sector size: 4KB (larger flash variants use 4KB sectors) +# Default: RUN mode at 48 MHz (FIRC - internal RC oscillator) +# +# Build: cp config/examples/nxp-s32k148.config .config && make + +ARCH?=ARM +CORTEX_M4?=1 +NO_MPU=1 +TARGET?=s32k1xx +SIGN?=ECC256 +HASH?=SHA256 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 + +# S32K1xx flash requires erase before write (cannot re-program same location) +# This is required for sector swap trailer flag updates to work correctly +NVM_FLASH_WRITEONCE?=1 + +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Select S32K148 variant for correct flash size and sector size +CFLAGS_EXTRA+=-DS32K148 + +# 4KB sectors (S32K144/146/148 with 512KB+ flash) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# Memory layout for S32K148 (2MB Flash): +# Bootloader: 0x00000000 - 0x0000BFFF (48 KB) +# Boot Partition: 0x0000C000 - 0x000FBFFF (960 KB) +# Update Partition: 0x000FC000 - 0x001EBFFF (960 KB) +# Swap Sector: 0x001EC000 - 0x001ECFFF (4 KB) +WOLFBOOT_PARTITION_SIZE?=0xF0000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xFC000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x1EC000 + +# Clock mode: Default is RUN mode with FIRC (48 MHz internal RC) +# To enable HSRUN mode (112 MHz), uncomment below (requires SOSC + SPLL, not fully implemented): +#CFLAGS_EXTRA+=-DS32K1XX_CLOCK_HSRUN + +# Optionally enable watchdog +#CFLAGS_EXTRA+=-DWATCHDOG + +# Debugging options (uncomment as needed) +DEBUG?=0 +DEBUG_SYMBOLS?=0 +DEBUG_UART?=0 +#CFLAGS_EXTRA+=-DDEBUG_HARDFAULT \ No newline at end of file diff --git a/docs/Targets.md b/docs/Targets.md index 49d4c1e453..ec39a350ff 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -23,6 +23,7 @@ This README describes configuration of supported targets. * [NXP LS1028A](#nxp-ls1028a) * [NXP MCXA153](#nxp-mcxa153) * [NXP MCXW716](#nxp-mcxw716) +* [NXP S32K1XX](#nxp-s32k1xx) * [NXP P1021 PPC](#nxp-qoriq-p1021-ppc) * [NXP T1024 PPC](#nxp-qoriq-t1024-ppc) * [NXP T2080 PPC](#nxp-qoriq-t2080-ppc) @@ -2946,6 +2947,278 @@ c ``` +## NXP S32K1XX + +The NXP S32K1xx family (S32K142, S32K144, S32K146, S32K148) are automotive-grade +Cortex-M4F microcontrollers. wolfBoot support has been tested on the S32K142 with +256KB Flash and 32KB SRAM. + +**Key Features:** +- ARM Cortex-M4F core at up to 112 MHz (HSRUN mode) or 80 MHz (RUN mode) +- Flash sector size: 2KB (4KB when flash is over 256KB) +- 8-byte (phrase) flash programming unit +- Bare-metal implementation (no SDK required) +- LPUART debug output support + +### NXP S32K1XX: Memory Layout + +The default memory layout for S32K142 (256KB Flash): + +| Region | Address Range | Size | +|--------|---------------|------| +| Bootloader | 0x00000000 - 0x0000BFFF | 48 KB | +| Boot Partition | 0x0000C000 - 0x00024FFF | 100 KB | +| Update Partition | 0x00025000 - 0x0003DFFF | 100 KB | +| Swap Sector | 0x0003E000 - 0x0003E7FF | 2 KB | + +### NXP S32K1XX: Configuration + +Example configuration files: +- [/config/examples/nxp-s32k142.config](/config/examples/nxp-s32k142.config) - S32K142 (256KB Flash, 32KB SRAM) +- [/config/examples/nxp-s32k144.config](/config/examples/nxp-s32k144.config) - S32K144 (512KB Flash, 64KB SRAM) +- [/config/examples/nxp-s32k146.config](/config/examples/nxp-s32k146.config) - S32K146 (1MB Flash, 128KB SRAM) +- [/config/examples/nxp-s32k148.config](/config/examples/nxp-s32k148.config) - S32K148 (2MB Flash, 256KB SRAM) + +```sh +# Copy configuration (example for S32K142) +cp config/examples/nxp-s32k142.config .config + +# Build wolfBoot +make clean +make + +# Build test application +make test-app/image.bin +``` + +### NXP S32K1XX: Configuration Options + +The following build options are available for the S32K1xx HAL: + +| Option | Description | +|--------|-------------| +| `NVM_FLASH_WRITEONCE` | **Required for S32K1xx.** Flash can only be written once between erases. Enables proper sector swap trailer management. | +| `RAM_CODE` | **Required for S32K1xx.** Run flash operations from RAM (no read-while-write on same block). | +| `WOLFBOOT_RESTORE_CLOCK` | Restore clock to SIRC (8 MHz) before booting application. Recommended for applications that configure their own clocks. | +| `WOLFBOOT_DISABLE_WATCHDOG_ON_BOOT` | Keep watchdog disabled when jumping to application. By default, the watchdog is re-enabled before boot since it is enabled out of reset. | +| `WATCHDOG` | Enable watchdog during wolfBoot operation. Recommended for production. | +| `WATCHDOG_TIMEOUT_MS` | Watchdog timeout in milliseconds when `WATCHDOG` is enabled (default: 1000ms). | +| `S32K1XX_CLOCK_HSRUN` | Enable HSRUN mode (112 MHz). Requires external crystal and SPLL (not fully implemented). | +| `DEBUG_UART` | Enable LPUART1 debug output. | +| `DEBUG_HARDFAULT` | Enable detailed hard fault debugging output. | +| `S32K144`, `S32K146`, `S32K148` | Select variant (default is S32K142). Affects flash/SRAM size definitions. | + +**IMPORTANT:** Flash sector size depends on the S32K variant: +- **S32K142** (256KB Flash): 2KB sectors (`WOLFBOOT_SECTOR_SIZE=0x800`) +- **S32K144/S32K146/S32K148** (512KB+ Flash): 4KB sectors (`WOLFBOOT_SECTOR_SIZE=0x1000`) + +### NXP S32K1XX: Debug UART + +For UART debug output, connect a USB-to-serial adapter to LPUART1 pins (PTC6=RX, PTC7=TX) and open a terminal at 115200 baud. + +Debug output uses LPUART1 on pins: +- **TX**: PTC7 +- **RX**: PTC6 + +Baud rate: 115200, 8N1 + +Enable with `DEBUG_UART=1` in your configuration. + +### NXP S32K1XX: Programming and Debugging + +The S32K1xx can be programmed and debugged using various tools. The recommended approach uses PEMicro debug probes (commonly found on S32K EVB boards). + +**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) + +2. Start PEMicro GDB Server: +```sh +pegdbserver_console -device=NXP_S32K1xx_S32K142 -startserver -serverport=7224 +``` + +3. In another terminal, connect with GDB and flash: +```sh +arm-none-eabi-gdb --nx wolfboot.elf +target remote :7224 +monitor reset halt +load +monitor reset run +``` + +### NXP S32K1XX: USB Mass Storage Programming + +The S32K EVB boards include an OpenSDA debugger that exposes a USB mass storage interface for easy programming. Simply copy the `.srec` file to the mounted USB drive. + +**Steps:** + +1. Connect the S32K EVB board via USB (OpenSDA port) +2. The board will mount as a USB drive (e.g., `S32K142EVB`) +3. Build the factory image: + +```sh +make factory.srec +``` + +4. Copy the `.srec` file to the mounted drive: + +```sh +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. + +**Features:** +- **LED Indicators**: Green LED for firmware v1, Blue LED for firmware v2+ +- **Interactive Console**: UART-based command interface +- **XMODEM Firmware Update**: Upload new firmware images via XMODEM protocol +- **Partition Information**: Display boot/update partition status and versions +- **Keystore Display**: Show public key information from the bootloader + +**Console Commands:** + +| Command | Description | +|---------|-------------| +| `help` | Show available commands | +| `info` | Display partition and keystore information | +| `status` | Show partition versions and states | +| `success` | Mark current firmware as successful (wolfBoot_success) | +| `trigger` | Set update flag if update image is in flash | +| `update` | Receive firmware via XMODEM and trigger update | +| `timestamp` | Show current system time (ms) | +| `reboot` | Perform software reset | + +**UART Configuration:** +- LPUART1: PTC7 (TX), PTC6 (RX) +- Baud rate: 115200, 8N1 + +**Example Output:** + +``` +======================================== +S32K1xx wolfBoot Test Application +Copyright 2025 wolfSSL Inc. +======================================== +Firmware Version: 1 + +=== Partition Information === +Boot Partition @ 0xC000: + Version: 1 + State: SUCCESS (0x00) +Update Partition @ 0x25000: + Version: 0 (empty) + State: (no trailer) + +=== Keystore Information === +Number of keys: 1 +Key 0: ECDSA P-256 (secp256r1), SHA-256 + +Type 'help' for available commands. + +cmd> +``` + +**Testing Firmware Update:** + +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 +- [ ] **FlexNVM/EEPROM support**: Add support for FlexNVM partitioning and EEPROM emulation +- [ ] **CAN/LIN bootloader**: Add firmware update over CAN or LIN bus for automotive applications + + ## TI Hercules TMS570LC435 See [/config/examples/ti-tms570lc435.config](/config/examples/ti-tms570lc435.config) for example configuration. diff --git a/hal/hal.c b/hal/hal.c index c89e42f3d7..3b79fcccd1 100644 --- a/hal/hal.c +++ b/hal/hal.c @@ -41,7 +41,6 @@ int hal_flash_test(void) { int ret = 0; uint32_t i; - uint8_t* pagePtr = (uint8_t*)TEST_ADDRESS; static uint8_t pageData[TEST_SZ]; wolfBoot_printf("Internal flash test at 0x%x\n", TEST_ADDRESS); diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c new file mode 100644 index 0000000000..6494012db4 --- /dev/null +++ b/hal/s32k1xx.c @@ -0,0 +1,635 @@ +/* nxp_s32k1xx.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * HAL for NXP S32K1xx (S32K142, S32K144, S32K146, S32K148) + * Tested on S32K142: Cortex-M4F, 256KB Flash, 32KB SRAM + */ + +#include +#include +#include "image.h" +#include "hal.h" +#include "printf.h" + +/* Override RAMFUNCTION for test-app: when RAM_CODE is set but not __WOLFBOOT, + * we still need flash functions to run from RAM for self-programming. */ +#if defined(RAM_CODE) && !defined(__WOLFBOOT) + #undef RAMFUNCTION + #define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) +#endif + +/* Assembly helpers */ +#define DMB() __asm__ volatile ("dmb") +#define DSB() __asm__ volatile ("dsb") +#define ISB() __asm__ volatile ("isb") + +#include "s32k1xx.h" + +/* ============== Flash Configuration Field (FCF) ============== */ +/* Located at 0x400-0x40F in flash */ + +#ifdef __WOLFBOOT +#define FCF_LEN (16) +const uint8_t __attribute__((section(".flash_config"))) flash_config[FCF_LEN] = { + /* Backdoor comparison key (8 bytes) */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* Program Flash Protection (4 bytes) - all unprotected */ + 0xFF, 0xFF, 0xFF, 0xFF, + /* Flash Security Byte */ + 0xFE, /* SEC=10 (unsecured), FSLACC=11, MEEN=11, KEYEN=11 */ + /* Flash Option Byte */ + 0xFF, + /* EEPROM Protection Byte */ + 0xFF, + /* Data Flash Protection Byte */ + 0xFF +}; +#endif + +/* ============== Watchdog Functions ============== */ + +/* Disable watchdog - must be called within 128 bus clock cycles after reset + * or after unlocking. The watchdog is enabled by default after reset. + */ +static void watchdog_disable(void) +{ + /* Unlock watchdog by writing unlock key to CNT register */ + WDOG_CNT = WDOG_CNT_UNLOCK; + + /* Wait for unlock to complete (ULK bit set) */ + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Set timeout to max and disable watchdog */ + WDOG_TOVAL = WDOG_TOVAL_DEFAULT; + WDOG_CS = WDOG_CS_DISABLE_CFG; + + /* Wait for reconfiguration to complete (RCS bit set) */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} +} + +#ifdef WATCHDOG +/* Enable watchdog with specified timeout value + * timeout_ms: timeout in milliseconds (max ~512ms with LPO clock) + * LPO clock is 128kHz, so each tick is ~7.8125us + * For longer timeouts, use PRES bit for 256x prescaler + */ +static void watchdog_enable(uint32_t timeout_ms) +{ + uint32_t toval; + uint32_t cs_cfg = WDOG_CS_ENABLE_CFG; + + /* Calculate TOVAL from timeout_ms + * LPO = 128kHz = 128 ticks/ms + * With PRES=0: max timeout = 65535/128 = 512ms + * With PRES=1: max timeout = 65535*256/128 = 131 seconds + */ + if (timeout_ms > 512) { + /* Use prescaler for longer timeouts */ + cs_cfg |= WDOG_CS_PRES; + toval = (timeout_ms * 128) / 256; + } else { + toval = timeout_ms * 128; + } + + /* Clamp to max value */ + if (toval > 0xFFFF) { + toval = 0xFFFF; + } + + /* Unlock watchdog */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Configure and enable */ + WDOG_TOVAL = toval; + WDOG_CS = cs_cfg; + + /* Wait for reconfiguration to complete */ + 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 ============== */ + +/* SIRC - Slow Internal RC (8 MHz) register fields */ +#define SCG_SIRCCSR_SIRCEN (1UL << 0) +#define SCG_SIRCCSR_SIRCVLD (1UL << 24) +#define SCG_xCCR_SCS_SIRC (2UL << SCG_xCCR_SCS_SHIFT) +#define SCG_CSR_SCS_SIRC (2UL << SCG_CSR_SCS_SHIFT) + +#ifdef WOLFBOOT_RESTORE_CLOCK +/* Restore clock to safe default (SIRC 8 MHz) before booting application. + * This allows the application to configure clocks from a known state. + */ +static void clock_restore_sirc(void) +{ + /* Enable SIRC (8 MHz) if not already enabled */ + SCG_SIRCDIV = (1UL << 8) | (1UL << 0); /* SIRCDIV1=/1, SIRCDIV2=/1 */ + SCG_SIRCCFG = 0; /* Range 0: 2 MHz (default) - actually S32K uses 8MHz SIRC */ + SCG_SIRCCSR = SCG_SIRCCSR_SIRCEN; + + /* Wait for SIRC valid */ + while (!(SCG_SIRCCSR & SCG_SIRCCSR_SIRCVLD)) {} + + /* Switch to SIRC as system clock + * SCS = SIRC (2) + * DIVCORE = /1 (8 MHz) + * DIVBUS = /1 (8 MHz) + * DIVSLOW = /1 (8 MHz) + */ + SCG_RCCR = SCG_xCCR_SCS_SIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (0UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_SIRC) {} + + /* Disable FIRC to save power (application can re-enable if needed) */ + SCG_FIRCCSR &= ~SCG_FIRCCSR_FIRCEN; +} +#endif /* WOLFBOOT_RESTORE_CLOCK */ + +static void clock_init_firc(void) +{ + /* Enable FIRC (48 MHz) */ + SCG_FIRCDIV = (1UL << 8) | (1UL << 0); /* FIRCDIV1=/1, FIRCDIV2=/1 */ + SCG_FIRCCFG = 0; /* Range 0: 48 MHz */ + SCG_FIRCCSR = SCG_FIRCCSR_FIRCEN; + + /* Wait for FIRC valid */ + while (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) {} +} + +static void clock_init_spll(void) +{ + /* S32K1xx SPLL requires SOSC as source (FIRC cannot be used directly). + * For 112 MHz with 8 MHz SOSC: PREDIV=0, MULT=28 -> VCO=224 MHz, SPLL=112 MHz + * VCO range: 180-320 MHz, SPLL_CLK = VCO / 2 + * + * Currently using FIRC 48 MHz directly. TODO: Add SOSC + SPLL for 112 MHz. + */ + SCG_SPLLCSR &= ~SCG_SPLLCSR_SPLLEN; + SCG_SPLLDIV = (2UL << 8) | (4UL << 0); /* SPLLDIV1=/2, SPLLDIV2=/4 */ +} + +static void clock_init(void) +{ + /* Initialize FIRC to 48 MHz */ + clock_init_firc(); + + /* Configure Run mode clock control: + * SCS = FIRC (3) + * DIVCORE = /1 (48 MHz) + * DIVBUS = /1 (48 MHz) + * DIVSLOW = /2 (24 MHz for flash) + */ + SCG_RCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) {} + +#ifdef S32K1XX_CLOCK_HSRUN + /* HSRUN mode (112 MHz) - requires SOSC + SPLL (not fully implemented yet) + * TODO: Add SOSC initialization and SPLL configuration for true 112 MHz + * Currently this enters HSRUN mode but still uses FIRC at 48 MHz + */ + + /* Enable HSRUN mode */ + SMC_PMPROT = SMC_PMPROT_AHSRUN; + + /* Configure HSRUN clock control (same as RUN for now with FIRC) */ + SCG_HCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Enter HSRUN mode */ + SMC_PMCTRL = (SMC_PMCTRL & ~(3UL << SMC_PMCTRL_RUNM_SHIFT)) | SMC_PMCTRL_RUNM_HSRUN; + + /* Wait for HSRUN */ + while ((SMC_PMSTAT & 0xFF) != SMC_PMSTAT_HSRUN) {} +#endif +} + +/* ============== UART Functions ============== */ + +#ifdef DEBUG_UART + +#ifndef UART_BAUDRATE +#define UART_BAUDRATE 115200 +#endif + +void uart_init(void) +{ + uint32_t sbr; + uint32_t osr = 16; /* Oversampling ratio */ + uint32_t uart_clock = 48000000UL; /* FIRC 48 MHz */ + + /* Enable clock to TX and RX port(s) + * Note: If TX and RX use different ports, both need clock enabled + */ + DEBUG_UART_TX_PCC_PORT |= PCC_CGC; +#if !DEBUG_UART_SAME_PORT + DEBUG_UART_RX_PCC_PORT |= PCC_CGC; +#endif + + /* Configure pins for selected LPUART */ + DEBUG_UART_RX_PCR = DEBUG_UART_RX_MUX; + DEBUG_UART_TX_PCR = DEBUG_UART_TX_MUX; + + /* Enable clock to selected LPUART, source = FIRC (48 MHz) */ + PCC_LPUART = 0; /* Disable before changing source */ + PCC_LPUART = PCC_PCS_FIRC | PCC_CGC; + + /* Calculate baud rate: + * SBR = UART_CLK / (BAUD * OSR) + */ + sbr = uart_clock / (UART_BAUDRATE * osr); + + /* Disable TX/RX before configuration */ + LPUART_CTRL = 0; + + /* Configure baud rate */ + LPUART_BAUD = ((osr - 1) << LPUART_BAUD_OSR_SHIFT) | + (sbr << LPUART_BAUD_SBR_SHIFT); + + /* Enable transmitter and receiver */ + LPUART_CTRL = LPUART_CTRL_TE | LPUART_CTRL_RE; +} + +/* Transmit a single byte (raw, no conversion) - RAMFUNCTION for use during flash ops */ +void RAMFUNCTION uart_tx(uint8_t byte) +{ + while (!(LPUART1_STAT & LPUART_STAT_TDRE)) {} + LPUART1_DATA = byte; + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} +} + +/* Used for sending ASCII and CRLF conversions */ +void uart_write(const char* buf, unsigned int sz) +{ + unsigned int i; + for (i = 0; i < sz; i++) { + /* Handle newline -> CRLF conversion */ + if (buf[i] == '\n') { + uart_tx('\r'); + } + uart_tx(buf[i]); + } + + /* Wait for transmission complete */ + while (!(LPUART_STAT & LPUART_STAT_TC)) {} +} + +/* Read a single character from UART (non-blocking) - RAMFUNCTION for use during flash ops + * Returns: 1 if character read, 0 if no data available + */ +int RAMFUNCTION uart_read(char* c) +{ + uint32_t stat = LPUART1_STAT; + + /* Clear any error flags first */ + if (stat & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE | LPUART_STAT_PF)) { + LPUART1_STAT = stat; /* Write 1 to clear flags */ + } + + /* Check if data available - read even if there was an error */ + if (stat & LPUART_STAT_RDRF) { + *c = (char)(LPUART1_DATA & 0xFF); + return 1; + } + + return 0; /* No data available */ +} + +#endif /* DEBUG_UART */ + +/* ============== Flash Functions ============== */ + +static void RAMFUNCTION flash_wait_complete(void) +{ + /* Wait for command complete */ + while (!(FTFC_FSTAT & FTFC_FSTAT_CCIF)) {} +} + +static void RAMFUNCTION flash_clear_errors(void) +{ + /* Clear error flags by writing 1 */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL)) { + FTFC_FSTAT = FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL; + } +} + +static int RAMFUNCTION flash_program_phrase(uint32_t address, const uint8_t *data) +{ + /* 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 + */ + FTFC_FCCOB0 = FTFC_CMD_PROGRAM_PHRASE; + FTFC_FCCOB1 = (uint8_t)(address >> 16); + FTFC_FCCOB2 = (uint8_t)(address >> 8); + FTFC_FCCOB3 = (uint8_t)(address); + + /* Data bytes (big-endian order in FCCOB registers) */ + FTFC_FCCOB4 = data[3]; + FTFC_FCCOB5 = data[2]; + FTFC_FCCOB6 = data[1]; + FTFC_FCCOB7 = data[0]; + FTFC_FCCOB8 = data[7]; + FTFC_FCCOB9 = data[6]; + FTFC_FCCOBA = data[5]; + FTFC_FCCOBB = data[4]; + + /* Launch command */ + DSB(); + ISB(); + FTFC_FSTAT = FTFC_FSTAT_CCIF; + + /* Wait for completion */ + flash_wait_complete(); + +#ifdef WATCHDOG + /* Refresh watchdog after flash operation */ + watchdog_refresh(); +#endif + + /* Check for errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL | FTFC_FSTAT_MGSTAT0)) { + return -1; + } + + return 0; +} + +static int RAMFUNCTION flash_erase_sector_internal(uint32_t address) +{ + uint32_t primask; + + /* Wait for previous command to complete */ + flash_wait_complete(); + flash_clear_errors(); + + /* Set up Erase Sector command (0x09) */ + FTFC_FCCOB0 = FTFC_CMD_ERASE_SECTOR; + FTFC_FCCOB1 = (uint8_t)(address >> 16); + FTFC_FCCOB2 = (uint8_t)(address >> 8); + FTFC_FCCOB3 = (uint8_t)(address); + + /* Launch command */ + DSB(); + ISB(); + + /* Disable interrupts during flash operation to prevent code fetch from flash */ + __asm__ volatile ("mrs %0, primask\n\t" + "cpsid i" : "=r" (primask) :: "memory"); + + FTFC_FSTAT = FTFC_FSTAT_CCIF; + + /* Wait for completion */ + flash_wait_complete(); + + /* Re-enable interrupts */ + __asm__ volatile ("msr primask, %0" :: "r" (primask) : "memory"); + +#ifdef WATCHDOG + /* Refresh watchdog after potentially long flash operation */ + watchdog_refresh(); +#endif + + /* Check for errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL | FTFC_FSTAT_MGSTAT0)) { + return -1; + } + + return 0; +} + + +/* ============== HAL Interface Functions ============== */ + +void hal_init(void) +{ + /* Disable watchdog first - must be done early after reset */ + watchdog_disable(); + + /* Initialize clocks */ + clock_init(); + + /* Enable clock to flash controller */ + PCC_FTFC |= PCC_CGC; + +#ifdef DEBUG_UART + uart_init(); + +#ifdef __WOLFBOOT + wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", + LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__); +#endif +#endif + +#ifdef WATCHDOG + watchdog_enable(WATCHDOG_TIMEOUT_MS); +#endif +} + +void hal_prepare_boot(void) +{ +#ifdef DEBUG_UART + /* Wait for any pending UART transmission to complete */ + while (!(LPUART_STAT & LPUART_STAT_TC)) {} +#endif + +#ifdef WOLFBOOT_RESTORE_CLOCK + /* Restore clock to SIRC (8 MHz) before booting application. + * This gives the application a known clock state to start from. + */ + clock_restore_sirc(); +#endif + + /* Re-enable watchdog before booting application. + * The watchdog is enabled by default after reset, so the application + * may expect it to be running. Use a generous timeout to give the + * application time to reconfigure or disable the watchdog. + */ +#ifndef WOLFBOOT_DISABLE_WATCHDOG_ON_BOOT + { + /* Unlock watchdog */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + + /* Enable watchdog with ~2 second timeout (256k ticks at 128kHz LPO) + * Application should either service or reconfigure the watchdog + */ + WDOG_TOVAL = 0xFFFF; /* Max timeout ~512ms without prescaler */ + WDOG_CS = WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_CMD32EN | + WDOG_CS_CLK_LPO | WDOG_CS_PRES; /* With prescaler: ~131 sec */ + + /* Wait for reconfiguration to complete */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} + } +#endif +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int ret = 0; + int 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))) { + 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) { + bytes_to_copy = len; + } + + /* Merge new data */ + 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; + } + } + + address += bytes_to_copy; + i += bytes_to_copy; + len -= bytes_to_copy; + } + 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; + } + } + + address += FLASH_PHRASE_SIZE; + i += FLASH_PHRASE_SIZE; + len -= FLASH_PHRASE_SIZE; + } + } + } + + return 0; +} + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + int ret; + + /* Align address to sector boundary */ + if (address % FLASH_SECTOR_SIZE) { + address -= (address % FLASH_SECTOR_SIZE); + } + + while (len > 0) { + ret = flash_erase_sector_internal(address); + if (ret != 0) { + return ret; + } + + address += FLASH_SECTOR_SIZE; + len -= FLASH_SECTOR_SIZE; + } + + return 0; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ + /* Ensure flash controller clock is enabled */ + PCC_FTFC |= PCC_CGC; + + /* Clear any pending errors */ + if (FTFC_FSTAT & (FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL)) { + FTFC_FSTAT = FTFC_FSTAT_ACCERR | FTFC_FSTAT_FPVIOL; + } +} + +void RAMFUNCTION hal_flash_lock(void) +{ + /* No explicit lock needed */ +} + diff --git a/hal/s32k1xx.h b/hal/s32k1xx.h new file mode 100644 index 0000000000..f105b3a3ba --- /dev/null +++ b/hal/s32k1xx.h @@ -0,0 +1,633 @@ +/* s32k1xx.h + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * Hardware register definitions for NXP S32K1xx (S32K142, S32K144, S32K146, S32K148) + */ + +#ifndef S32K1XX_H +#define S32K1XX_H + +#include + +/* + * Clock configuration + * S32K142 supports: + * - FIRC: 48 MHz Fast Internal RC + * - SIRC: 8 MHz Slow Internal RC + * - SOSC: 8-40 MHz System Oscillator (external crystal) + * - SPLL: System PLL (up to 160 MHz VCO, /2 for SPLL_CLK) + * + * Run modes: + * - RUN: Up to 80 MHz core clock (requires SPLL, currently using FIRC at 48 MHz) + * - HSRUN: Up to 112 MHz core clock (requires SOSC + SPLL, not implemented yet) + * + * Default: RUN mode with FIRC at 48 MHz (no external crystal required) + * To enable HSRUN mode (112 MHz), define S32K1XX_CLOCK_HSRUN (requires SOSC + SPLL) + */ + +/* ============== ARM Cortex-M4 System Registers ============== */ + +/* System Control Block (SCB) */ +#define SCB_BASE (0xE000ED00UL) +#define SCB_CPUID (*(volatile uint32_t *)(SCB_BASE + 0x00UL)) +#define SCB_ICSR (*(volatile uint32_t *)(SCB_BASE + 0x04UL)) +#define SCB_VTOR (*(volatile uint32_t *)(SCB_BASE + 0x08UL)) +#define SCB_AIRCR (*(volatile uint32_t *)(SCB_BASE + 0x0CUL)) +#define SCB_SCR (*(volatile uint32_t *)(SCB_BASE + 0x10UL)) +#define SCB_CCR (*(volatile uint32_t *)(SCB_BASE + 0x14UL)) + +/* AIRCR - Application Interrupt and Reset Control Register */ +#define AIRCR_VECTKEY (0x05FAUL << 16) +#define AIRCR_SYSRESETREQ (1UL << 2) + +/* SysTick Timer */ +#define SYST_BASE (0xE000E010UL) +#define SYST_CSR (*(volatile uint32_t *)(SYST_BASE + 0x00UL)) +#define SYST_RVR (*(volatile uint32_t *)(SYST_BASE + 0x04UL)) +#define SYST_CVR (*(volatile uint32_t *)(SYST_BASE + 0x08UL)) +#define SYST_CALIB (*(volatile uint32_t *)(SYST_BASE + 0x0CUL)) + +#define SYST_CSR_ENABLE (1UL << 0) +#define SYST_CSR_TICKINT (1UL << 1) +#define SYST_CSR_CLKSOURCE (1UL << 2) +#define SYST_CSR_COUNTFLAG (1UL << 16) + +/* Clock speed (FIRC = 48 MHz) */ +#ifndef CLOCK_SPEED +#define CLOCK_SPEED 48000000UL +#endif + +/* ============== NVIC - Nested Vectored Interrupt Controller ============== */ +#define NVIC_BASE (0xE000E100UL) +#define NVIC_ISER(n) (*(volatile uint32_t *)(NVIC_BASE + 0x000UL + 4*(n))) /* Interrupt Set Enable */ +#define NVIC_ICER(n) (*(volatile uint32_t *)(NVIC_BASE + 0x080UL + 4*(n))) /* Interrupt Clear Enable */ +#define NVIC_ISPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x100UL + 4*(n))) /* Interrupt Set Pending */ +#define NVIC_ICPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x180UL + 4*(n))) /* Interrupt Clear Pending */ +#define NVIC_IPR(n) (*(volatile uint32_t *)(NVIC_BASE + 0x300UL + 4*(n))) /* Interrupt Priority */ + +/* S32K142 LPUART IRQ numbers */ +#define LPUART0_IRQn 31 +#define LPUART1_IRQn 33 +#define LPUART2_IRQn 35 + +/* NVIC helper macros */ +#define NVIC_EnableIRQ(irq) NVIC_ISER((irq) >> 5) = (1UL << ((irq) & 0x1F)) +#define NVIC_DisableIRQ(irq) NVIC_ICER((irq) >> 5) = (1UL << ((irq) & 0x1F)) +#define NVIC_SetPriority(irq, prio) \ + do { \ + uint32_t _idx = (irq) >> 2; \ + uint32_t _shift = (((irq) & 0x3) << 3) + 4; \ + NVIC_IPR(_idx) = (NVIC_IPR(_idx) & ~(0xFUL << _shift)) | (((prio) & 0xF) << _shift); \ + } while(0) + +/* ============== System Control Registers ============== */ + +/* SCG - System Clock Generator */ +#define SCG_BASE (0x40064000UL) +#define SCG_CSR (*(volatile uint32_t *)(SCG_BASE + 0x010UL)) /* Clock Status Register */ +#define SCG_RCCR (*(volatile uint32_t *)(SCG_BASE + 0x014UL)) /* Run Clock Control Register */ +#define SCG_VCCR (*(volatile uint32_t *)(SCG_BASE + 0x018UL)) /* VLPR Clock Control Register */ +#define SCG_HCCR (*(volatile uint32_t *)(SCG_BASE + 0x01CUL)) /* HSRUN Clock Control Register */ +#define SCG_CLKOUTCNFG (*(volatile uint32_t *)(SCG_BASE + 0x020UL)) /* Clock Out Configuration */ + +/* SOSC - System OSC */ +#define SCG_SOSCCSR (*(volatile uint32_t *)(SCG_BASE + 0x100UL)) +#define SCG_SOSCDIV (*(volatile uint32_t *)(SCG_BASE + 0x104UL)) +#define SCG_SOSCCFG (*(volatile uint32_t *)(SCG_BASE + 0x108UL)) + +/* SIRC - Slow IRC */ +#define SCG_SIRCCSR (*(volatile uint32_t *)(SCG_BASE + 0x200UL)) +#define SCG_SIRCDIV (*(volatile uint32_t *)(SCG_BASE + 0x204UL)) +#define SCG_SIRCCFG (*(volatile uint32_t *)(SCG_BASE + 0x208UL)) + +/* FIRC - Fast IRC */ +#define SCG_FIRCCSR (*(volatile uint32_t *)(SCG_BASE + 0x300UL)) +#define SCG_FIRCDIV (*(volatile uint32_t *)(SCG_BASE + 0x304UL)) +#define SCG_FIRCCFG (*(volatile uint32_t *)(SCG_BASE + 0x308UL)) + +/* SPLL - System PLL */ +#define SCG_SPLLCSR (*(volatile uint32_t *)(SCG_BASE + 0x600UL)) +#define SCG_SPLLDIV (*(volatile uint32_t *)(SCG_BASE + 0x604UL)) +#define SCG_SPLLCFG (*(volatile uint32_t *)(SCG_BASE + 0x608UL)) + +/* SCG CSR fields */ +#define SCG_CSR_SCS_SHIFT 24 +#define SCG_CSR_SCS_MASK (0xFUL << SCG_CSR_SCS_SHIFT) +#define SCG_CSR_SCS_FIRC (3UL << SCG_CSR_SCS_SHIFT) +#define SCG_CSR_SCS_SPLL (6UL << SCG_CSR_SCS_SHIFT) + +/* SCG xCCR fields */ +#define SCG_xCCR_SCS_SHIFT 24 +#define SCG_xCCR_SCS_FIRC (3UL << SCG_xCCR_SCS_SHIFT) +#define SCG_xCCR_SCS_SPLL (6UL << SCG_xCCR_SCS_SHIFT) +#define SCG_xCCR_DIVCORE_SHIFT 16 +#define SCG_xCCR_DIVBUS_SHIFT 4 +#define SCG_xCCR_DIVSLOW_SHIFT 0 + +/* FIRC CSR fields */ +#define SCG_FIRCCSR_FIRCEN (1UL << 0) +#define SCG_FIRCCSR_FIRCVLD (1UL << 24) + +/* SPLL CSR fields */ +#define SCG_SPLLCSR_SPLLEN (1UL << 0) +#define SCG_SPLLCSR_SPLLVLD (1UL << 24) + +/* SPLL CFG fields */ +#define SCG_SPLLCFG_MULT_SHIFT 16 +#define SCG_SPLLCFG_PREDIV_SHIFT 8 + +/* SMC - System Mode Controller */ +#define SMC_BASE (0x4007E000UL) +#define SMC_PMPROT (*(volatile uint32_t *)(SMC_BASE + 0x000UL)) +#define SMC_PMCTRL (*(volatile uint32_t *)(SMC_BASE + 0x004UL)) +#define SMC_PMSTAT (*(volatile uint32_t *)(SMC_BASE + 0x008UL)) + +#define SMC_PMPROT_AHSRUN (1UL << 7) /* Allow HSRUN */ +#define SMC_PMCTRL_RUNM_SHIFT 5 +#define SMC_PMCTRL_RUNM_RUN (0UL << SMC_PMCTRL_RUNM_SHIFT) +#define SMC_PMCTRL_RUNM_HSRUN (3UL << SMC_PMCTRL_RUNM_SHIFT) +#define SMC_PMSTAT_HSRUN (0x80UL) +#define SMC_PMSTAT_RUN (0x01UL) + +/* PCC - Peripheral Clock Controller */ +#define PCC_BASE (0x40065000UL) +#define PCC_PORTA (*(volatile uint32_t *)(PCC_BASE + 0x124UL)) +#define PCC_PORTB (*(volatile uint32_t *)(PCC_BASE + 0x128UL)) +#define PCC_PORTC (*(volatile uint32_t *)(PCC_BASE + 0x12CUL)) +#define PCC_LPUART0 (*(volatile uint32_t *)(PCC_BASE + 0x1A8UL)) +#define PCC_LPUART1 (*(volatile uint32_t *)(PCC_BASE + 0x1ACUL)) +#define PCC_LPUART2 (*(volatile uint32_t *)(PCC_BASE + 0x1B0UL)) +#define PCC_FTFC (*(volatile uint32_t *)(PCC_BASE + 0x0B0UL)) + +#define PCC_CGC (1UL << 30) /* Clock Gate Control */ +#define PCC_PCS_SHIFT 24 +#define PCC_PCS_FIRC (3UL << PCC_PCS_SHIFT) /* FIRC 48MHz */ +#define PCC_PCS_SPLLDIV2 (6UL << PCC_PCS_SHIFT) /* SPLL DIV2 */ + +/* ============== GPIO / Port Registers ============== */ + +/* PCC for GPIO ports */ +#define PCC_PORTD (*(volatile uint32_t *)(PCC_BASE + 0x130UL)) +#define PCC_PORTE (*(volatile uint32_t *)(PCC_BASE + 0x134UL)) + +/* Port A - LPUART0/LPUART2 alternate pins */ +#define PORTA_BASE (0x40049000UL) +#define PORTA_PCR(n) (*(volatile uint32_t *)(PORTA_BASE + ((n) * 4))) +#define PORTA_PCR2 (*(volatile uint32_t *)(PORTA_BASE + 0x008UL)) /* LPUART0_RX (ALT6) */ +#define PORTA_PCR3 (*(volatile uint32_t *)(PORTA_BASE + 0x00CUL)) /* LPUART0_TX (ALT6) */ +#define PORTA_PCR8 (*(volatile uint32_t *)(PORTA_BASE + 0x020UL)) /* LPUART2_RX (ALT6) */ +#define PORTA_PCR9 (*(volatile uint32_t *)(PORTA_BASE + 0x024UL)) /* LPUART2_TX (ALT6) */ + +/* Port B - LPUART0/LPUART1 alternate pins */ +#define PORTB_BASE (0x4004A000UL) +#define PORTB_PCR(n) (*(volatile uint32_t *)(PORTB_BASE + ((n) * 4))) +#define PORTB_PCR0 (*(volatile uint32_t *)(PORTB_BASE + 0x000UL)) /* LPUART0_RX (ALT2) */ +#define PORTB_PCR1 (*(volatile uint32_t *)(PORTB_BASE + 0x004UL)) /* LPUART0_TX (ALT2) */ + +/* Port C - LPUART1/LPUART2 pins */ +#define PORTC_BASE (0x4004B000UL) +#define PORTC_PCR(n) (*(volatile uint32_t *)(PORTC_BASE + ((n) * 4))) +#define PORTC_PCR6 (*(volatile uint32_t *)(PORTC_BASE + 0x018UL)) /* LPUART1_RX (ALT2) */ +#define PORTC_PCR7 (*(volatile uint32_t *)(PORTC_BASE + 0x01CUL)) /* LPUART1_TX (ALT2) */ +#define PORTC_PCR8 (*(volatile uint32_t *)(PORTC_BASE + 0x020UL)) /* LPUART1_RX (ALT2) alt */ +#define PORTC_PCR9 (*(volatile uint32_t *)(PORTC_BASE + 0x024UL)) /* LPUART1_TX (ALT2) alt */ + +/* Port D - LED pins on S32K142EVB, LPUART2 alternate pins */ +#define PORTD_BASE (0x4004C000UL) +#define PORTD_PCR(n) (*(volatile uint32_t *)(PORTD_BASE + ((n) * 4))) +#define PORTD_PCR0 (*(volatile uint32_t *)(PORTD_BASE + 0x000UL)) /* Blue LED */ +#define PORTD_PCR6 (*(volatile uint32_t *)(PORTD_BASE + 0x018UL)) /* LPUART2_RX (ALT2) */ +#define PORTD_PCR7 (*(volatile uint32_t *)(PORTD_BASE + 0x01CUL)) /* LPUART2_TX (ALT2) */ +#define PORTD_PCR15 (*(volatile uint32_t *)(PORTD_BASE + 0x03CUL)) /* Red LED */ +#define PORTD_PCR16 (*(volatile uint32_t *)(PORTD_BASE + 0x040UL)) /* Green LED */ + +/* Port E - additional peripheral pins */ +#define PORTE_BASE (0x4004D000UL) +#define PORTE_PCR(n) (*(volatile uint32_t *)(PORTE_BASE + ((n) * 4))) + +/* GPIO D registers */ +#define GPIOD_BASE (0x400FF0C0UL) +#define GPIOD_PDOR (*(volatile uint32_t *)(GPIOD_BASE + 0x00UL)) /* Data Output */ +#define GPIOD_PSOR (*(volatile uint32_t *)(GPIOD_BASE + 0x04UL)) /* Set Output */ +#define GPIOD_PCOR (*(volatile uint32_t *)(GPIOD_BASE + 0x08UL)) /* Clear Output */ +#define GPIOD_PTOR (*(volatile uint32_t *)(GPIOD_BASE + 0x0CUL)) /* Toggle Output */ +#define GPIOD_PDIR (*(volatile uint32_t *)(GPIOD_BASE + 0x10UL)) /* Data Input */ +#define GPIOD_PDDR (*(volatile uint32_t *)(GPIOD_BASE + 0x14UL)) /* Data Direction */ + +/* Port Control Register fields */ +#define PORT_PCR_MUX_SHIFT 8 +#define PORT_PCR_MUX_MASK (7UL << PORT_PCR_MUX_SHIFT) +#define PORT_PCR_MUX_GPIO (1UL << PORT_PCR_MUX_SHIFT) /* GPIO mode */ +#define PORT_PCR_MUX_ALT2 (2UL << PORT_PCR_MUX_SHIFT) /* Alternate function 2 */ +#define PORT_PCR_MUX_ALT3 (3UL << PORT_PCR_MUX_SHIFT) /* Alternate function 3 */ +#define PORT_PCR_MUX_ALT4 (4UL << PORT_PCR_MUX_SHIFT) /* Alternate function 4 */ +#define PORT_PCR_MUX_ALT5 (5UL << PORT_PCR_MUX_SHIFT) /* Alternate function 5 */ +#define PORT_PCR_MUX_ALT6 (6UL << PORT_PCR_MUX_SHIFT) /* Alternate function 6 */ +#define PORT_PCR_MUX_ALT7 (7UL << PORT_PCR_MUX_SHIFT) /* Alternate function 7 */ + +/* S32K142EVB LED pins (accent RGB LED accent accent accent accent LED accent accent accent-low accent LED) */ +#define LED_PIN_BLUE 0 /* PTD0 - Blue LED (active low) */ +#define LED_PIN_RED 15 /* PTD15 - Red LED (active low) */ +#define LED_PIN_GREEN 16 /* PTD16 - Green LED (active low) */ + +/* ============== LPUART Registers ============== */ + +/* LPUART base addresses */ +#define LPUART0_BASE (0x4006A000UL) +#define LPUART1_BASE (0x4006B000UL) +#define LPUART2_BASE (0x4006C000UL) + +/* LPUART register offsets */ +#define LPUART_VERID_OFF 0x000UL +#define LPUART_PARAM_OFF 0x004UL +#define LPUART_GLOBAL_OFF 0x008UL +#define LPUART_BAUD_OFF 0x010UL +#define LPUART_STAT_OFF 0x014UL +#define LPUART_CTRL_OFF 0x018UL +#define LPUART_DATA_OFF 0x01CUL + +/* LPUART0 registers */ +#define LPUART0_VERID (*(volatile uint32_t *)(LPUART0_BASE + LPUART_VERID_OFF)) +#define LPUART0_PARAM (*(volatile uint32_t *)(LPUART0_BASE + LPUART_PARAM_OFF)) +#define LPUART0_GLOBAL (*(volatile uint32_t *)(LPUART0_BASE + LPUART_GLOBAL_OFF)) +#define LPUART0_BAUD (*(volatile uint32_t *)(LPUART0_BASE + LPUART_BAUD_OFF)) +#define LPUART0_STAT (*(volatile uint32_t *)(LPUART0_BASE + LPUART_STAT_OFF)) +#define LPUART0_CTRL (*(volatile uint32_t *)(LPUART0_BASE + LPUART_CTRL_OFF)) +#define LPUART0_DATA (*(volatile uint32_t *)(LPUART0_BASE + LPUART_DATA_OFF)) + +/* LPUART1 registers */ +#define LPUART1_VERID (*(volatile uint32_t *)(LPUART1_BASE + LPUART_VERID_OFF)) +#define LPUART1_PARAM (*(volatile uint32_t *)(LPUART1_BASE + LPUART_PARAM_OFF)) +#define LPUART1_GLOBAL (*(volatile uint32_t *)(LPUART1_BASE + LPUART_GLOBAL_OFF)) +#define LPUART1_BAUD (*(volatile uint32_t *)(LPUART1_BASE + LPUART_BAUD_OFF)) +#define LPUART1_STAT (*(volatile uint32_t *)(LPUART1_BASE + LPUART_STAT_OFF)) +#define LPUART1_CTRL (*(volatile uint32_t *)(LPUART1_BASE + LPUART_CTRL_OFF)) +#define LPUART1_DATA (*(volatile uint32_t *)(LPUART1_BASE + LPUART_DATA_OFF)) + +/* LPUART2 registers */ +#define LPUART2_VERID (*(volatile uint32_t *)(LPUART2_BASE + LPUART_VERID_OFF)) +#define LPUART2_PARAM (*(volatile uint32_t *)(LPUART2_BASE + LPUART_PARAM_OFF)) +#define LPUART2_GLOBAL (*(volatile uint32_t *)(LPUART2_BASE + LPUART_GLOBAL_OFF)) +#define LPUART2_BAUD (*(volatile uint32_t *)(LPUART2_BASE + LPUART_BAUD_OFF)) +#define LPUART2_STAT (*(volatile uint32_t *)(LPUART2_BASE + LPUART_STAT_OFF)) +#define LPUART2_CTRL (*(volatile uint32_t *)(LPUART2_BASE + LPUART_CTRL_OFF)) +#define LPUART2_DATA (*(volatile uint32_t *)(LPUART2_BASE + LPUART_DATA_OFF)) + +/* LPUART register field definitions */ +#define LPUART_BAUD_OSR_SHIFT 24 +#define LPUART_BAUD_SBR_SHIFT 0 +#define LPUART_CTRL_TE (1UL << 19) /* Transmitter Enable */ +#define LPUART_CTRL_RE (1UL << 18) /* Receiver Enable */ +#define LPUART_CTRL_RIE (1UL << 21) /* Receiver Interrupt Enable */ +#define LPUART_STAT_TDRE (1UL << 23) /* Transmit Data Register Empty */ +#define LPUART_STAT_TC (1UL << 22) /* Transmission Complete */ +#define LPUART_STAT_RDRF (1UL << 21) /* Receive Data Register Full */ +#define LPUART_STAT_OR (1UL << 19) /* Overrun Flag */ +#define LPUART_STAT_NF (1UL << 18) /* Noise Flag */ +#define LPUART_STAT_FE (1UL << 17) /* Framing Error */ +#define LPUART_STAT_PF (1UL << 16) /* Parity Error */ + +/* ============== LPUART Build-Time Configuration ============== */ +/* + * Select LPUART instance and pins at build time using these defines: + * + * DEBUG_UART_NUM: LPUART instance (0, 1, or 2). Default: 1 + * + * DEBUG_UART_TX_PORT: Port for TX pin (S32K_PORT_A/B/C/D/E). Default depends on LPUART + * DEBUG_UART_TX_PIN: Pin number for TX. Default depends on LPUART + * DEBUG_UART_TX_MUX: Pin mux function. Default: PORT_PCR_MUX_ALT2 + * + * DEBUG_UART_RX_PORT: Port for RX pin (S32K_PORT_A/B/C/D/E). Default depends on LPUART + * DEBUG_UART_RX_PIN: Pin number for RX. Default depends on LPUART + * DEBUG_UART_RX_MUX: Pin mux function. Default: PORT_PCR_MUX_ALT2 + * + * Example pin mappings for S32K1xx: + * + * LPUART0: + * - PTB0 (RX, ALT2), PTB1 (TX, ALT2) - Default + * - PTA2 (RX, ALT6), PTA3 (TX, ALT6) + * + * LPUART1: + * - PTC6 (RX, ALT2), PTC7 (TX, ALT2) - Default (S32K142EVB OpenSDA) + * - PTC8 (RX, ALT2), PTC9 (TX, ALT2) + * + * LPUART2: + * - PTA8 (RX, ALT6), PTA9 (TX, ALT6) + * - PTD6 (RX, ALT2), PTD7 (TX, ALT2) - Default + * + * Usage in .config file: + * CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0 + * CFLAGS_EXTRA+=-DDEBUG_UART_TX_PORT=S32K_PORT_B -DDEBUG_UART_TX_PIN=1 + * CFLAGS_EXTRA+=-DDEBUG_UART_RX_PORT=S32K_PORT_B -DDEBUG_UART_RX_PIN=0 + */ + +/* Default LPUART instance */ +#ifndef DEBUG_UART_NUM +#define DEBUG_UART_NUM 1 +#endif + +/* Map to selected LPUART registers based on DEBUG_UART_NUM */ +#if DEBUG_UART_NUM == 0 + #define LPUART_BAUD LPUART0_BAUD + #define LPUART_STAT LPUART0_STAT + #define LPUART_CTRL LPUART0_CTRL + #define LPUART_DATA LPUART0_DATA + #define PCC_LPUART PCC_LPUART0 +#elif DEBUG_UART_NUM == 2 + #define LPUART_BAUD LPUART2_BAUD + #define LPUART_STAT LPUART2_STAT + #define LPUART_CTRL LPUART2_CTRL + #define LPUART_DATA LPUART2_DATA + #define PCC_LPUART PCC_LPUART2 +#else /* DEBUG_UART_NUM == 1 (default) */ + #define LPUART_BAUD LPUART1_BAUD + #define LPUART_STAT LPUART1_STAT + #define LPUART_CTRL LPUART1_CTRL + #define LPUART_DATA LPUART1_DATA + #define PCC_LPUART PCC_LPUART1 +#endif + +/* Port identifier values for preprocessor comparisons */ +#define S32K_PORT_A 0 +#define S32K_PORT_B 1 +#define S32K_PORT_C 2 +#define S32K_PORT_D 3 +#define S32K_PORT_E 4 + +/* Default pin configuration based on selected LPUART */ +#if DEBUG_UART_NUM == 0 + /* LPUART0 defaults: PTB0 (RX), PTB1 (TX) */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_B + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 1 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_B + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 0 + #endif +#elif DEBUG_UART_NUM == 2 + /* LPUART2 defaults: PTD6 (RX), PTD7 (TX) */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_D + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 7 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_D + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 6 + #endif +#else /* DEBUG_UART_NUM == 1 (default) */ + /* LPUART1 defaults: PTC6 (RX), PTC7 (TX) - S32K142EVB OpenSDA */ + #ifndef DEBUG_UART_TX_PORT + #define DEBUG_UART_TX_PORT S32K_PORT_C + #endif + #ifndef DEBUG_UART_TX_PIN + #define DEBUG_UART_TX_PIN 7 + #endif + #ifndef DEBUG_UART_RX_PORT + #define DEBUG_UART_RX_PORT S32K_PORT_C + #endif + #ifndef DEBUG_UART_RX_PIN + #define DEBUG_UART_RX_PIN 6 + #endif +#endif + +/* Default pin mux - ALT2 for most LPUART pins */ +#ifndef DEBUG_UART_TX_MUX +#define DEBUG_UART_TX_MUX PORT_PCR_MUX_ALT2 +#endif +#ifndef DEBUG_UART_RX_MUX +#define DEBUG_UART_RX_MUX PORT_PCR_MUX_ALT2 +#endif + +/* Map TX port/pin to PCR register and PCC */ +#if DEBUG_UART_TX_PORT == S32K_PORT_A + #define DEBUG_UART_TX_PCC_PORT PCC_PORTA + #define DEBUG_UART_TX_PCR PORTA_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_B + #define DEBUG_UART_TX_PCC_PORT PCC_PORTB + #define DEBUG_UART_TX_PCR PORTB_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_D + #define DEBUG_UART_TX_PCC_PORT PCC_PORTD + #define DEBUG_UART_TX_PCR PORTD_PCR(DEBUG_UART_TX_PIN) +#elif DEBUG_UART_TX_PORT == S32K_PORT_E + #define DEBUG_UART_TX_PCC_PORT PCC_PORTE + #define DEBUG_UART_TX_PCR PORTE_PCR(DEBUG_UART_TX_PIN) +#else /* S32K_PORT_C (default) */ + #define DEBUG_UART_TX_PCC_PORT PCC_PORTC + #define DEBUG_UART_TX_PCR PORTC_PCR(DEBUG_UART_TX_PIN) +#endif + +/* Map RX port/pin to PCR register and PCC */ +#if DEBUG_UART_RX_PORT == S32K_PORT_A + #define DEBUG_UART_RX_PCC_PORT PCC_PORTA + #define DEBUG_UART_RX_PCR PORTA_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_B + #define DEBUG_UART_RX_PCC_PORT PCC_PORTB + #define DEBUG_UART_RX_PCR PORTB_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_D + #define DEBUG_UART_RX_PCC_PORT PCC_PORTD + #define DEBUG_UART_RX_PCR PORTD_PCR(DEBUG_UART_RX_PIN) +#elif DEBUG_UART_RX_PORT == S32K_PORT_E + #define DEBUG_UART_RX_PCC_PORT PCC_PORTE + #define DEBUG_UART_RX_PCR PORTE_PCR(DEBUG_UART_RX_PIN) +#else /* S32K_PORT_C (default) */ + #define DEBUG_UART_RX_PCC_PORT PCC_PORTC + #define DEBUG_UART_RX_PCR PORTC_PCR(DEBUG_UART_RX_PIN) +#endif + +/* Check if TX and RX use the same port (for clock enable optimization) */ +#if DEBUG_UART_TX_PORT == DEBUG_UART_RX_PORT + #define DEBUG_UART_SAME_PORT 1 +#else + #define DEBUG_UART_SAME_PORT 0 +#endif + +/* ============== Flash (FTFC) Registers ============== */ + +#define FTFC_BASE (0x40020000UL) +#define FTFC_FSTAT (*(volatile uint8_t *)(FTFC_BASE + 0x000UL)) +#define FTFC_FCNFG (*(volatile uint8_t *)(FTFC_BASE + 0x001UL)) +#define FTFC_FSEC (*(volatile uint8_t *)(FTFC_BASE + 0x002UL)) +#define FTFC_FOPT (*(volatile uint8_t *)(FTFC_BASE + 0x003UL)) +#define FTFC_FCCOB3 (*(volatile uint8_t *)(FTFC_BASE + 0x004UL)) +#define FTFC_FCCOB2 (*(volatile uint8_t *)(FTFC_BASE + 0x005UL)) +#define FTFC_FCCOB1 (*(volatile uint8_t *)(FTFC_BASE + 0x006UL)) +#define FTFC_FCCOB0 (*(volatile uint8_t *)(FTFC_BASE + 0x007UL)) +#define FTFC_FCCOB7 (*(volatile uint8_t *)(FTFC_BASE + 0x008UL)) +#define FTFC_FCCOB6 (*(volatile uint8_t *)(FTFC_BASE + 0x009UL)) +#define FTFC_FCCOB5 (*(volatile uint8_t *)(FTFC_BASE + 0x00AUL)) +#define FTFC_FCCOB4 (*(volatile uint8_t *)(FTFC_BASE + 0x00BUL)) +#define FTFC_FCCOBB (*(volatile uint8_t *)(FTFC_BASE + 0x00CUL)) +#define FTFC_FCCOBA (*(volatile uint8_t *)(FTFC_BASE + 0x00DUL)) +#define FTFC_FCCOB9 (*(volatile uint8_t *)(FTFC_BASE + 0x00EUL)) +#define FTFC_FCCOB8 (*(volatile uint8_t *)(FTFC_BASE + 0x00FUL)) + +/* FTFC Commands */ +#define FTFC_CMD_PROGRAM_PHRASE 0x07 /* Program 8 bytes (phrase) */ +#define FTFC_CMD_ERASE_SECTOR 0x09 /* Erase flash sector (2KB) */ +#define FTFC_CMD_READ_RESOURCE 0x03 /* Read resource */ + +/* FTFC_FSTAT bits */ +#define FTFC_FSTAT_CCIF (1U << 7) /* Command Complete */ +#define FTFC_FSTAT_RDCOLERR (1U << 6) /* Read Collision Error */ +#define FTFC_FSTAT_ACCERR (1U << 5) /* Access Error */ +#define FTFC_FSTAT_FPVIOL (1U << 4) /* Protection Violation */ +#define FTFC_FSTAT_MGSTAT0 (1U << 0) /* Command Failure */ + +/* Flash programming unit: 8 bytes (double-word / phrase) */ +#define FLASH_PHRASE_SIZE 8 + +/* ============== S32K1xx Variant Flash Sizes ============== */ +/* Define the appropriate variant or use automatic detection based on WOLFBOOT config + * + * S32K142: 256KB Flash (0x00000 - 0x3FFFF), 32KB SRAM, 2KB sectors + * S32K144: 512KB Flash (0x00000 - 0x7FFFF), 64KB SRAM, 4KB sectors + * S32K146: 1MB Flash (0x00000 - 0xFFFFF), 128KB SRAM, 4KB sectors + * S32K148: 2MB Flash (0x00000 - 0x1FFFFF), 256KB SRAM, 4KB sectors + * + * IMPORTANT: Flash sector size depends on total flash size: + * - 256KB Flash (S32K142): 2KB sectors + * - 512KB+ Flash (S32K144/146/148): 4KB sectors + * + * All variants use 8-byte phrase programming. + */ +#if defined(S32K148) + #define FLASH_SIZE (2048 * 1024) /* 2MB */ + #define SRAM_SIZE (256 * 1024) /* 256KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#elif defined(S32K146) + #define FLASH_SIZE (1024 * 1024) /* 1MB */ + #define SRAM_SIZE (128 * 1024) /* 128KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#elif defined(S32K144) + #define FLASH_SIZE (512 * 1024) /* 512KB */ + #define SRAM_SIZE (64 * 1024) /* 64KB */ + #define FLASH_SECTOR_SIZE 4096 /* 4KB sectors */ +#else /* S32K142 (default) */ + #define FLASH_SIZE (256 * 1024) /* 256KB */ + #define SRAM_SIZE (32 * 1024) /* 32KB */ + #define FLASH_SECTOR_SIZE 2048 /* 2KB sectors */ +#endif + +/* Flash base address */ +#define FLASH_BASE_ADDR 0x00000000 + +/* Flash Configuration Field (FCF) region - MUST NOT be modified at runtime! + * Writing incorrect values here can permanently lock the device. + * Address range: 0x400 - 0x40F (16 bytes) + */ +#define FCF_START_ADDR 0x400 +#define FCF_END_ADDR 0x410 + +/* SRAM base address (all S32K1xx variants) */ +#define SRAM_BASE_ADDR 0x1FFF8000 /* Lower SRAM */ +#define SRAM_UPPER_ADDR 0x20000000 /* Upper SRAM */ + +/* ============== Watchdog (WDOG) Registers ============== */ +/* S32K1xx has a software-controlled watchdog timer */ + +#define WDOG_BASE (0x40052000UL) +#define WDOG_CS (*(volatile uint32_t *)(WDOG_BASE + 0x00UL)) /* Control and Status */ +#define WDOG_CNT (*(volatile uint32_t *)(WDOG_BASE + 0x04UL)) /* Counter */ +#define WDOG_TOVAL (*(volatile uint32_t *)(WDOG_BASE + 0x08UL)) /* Timeout Value */ +#define WDOG_WIN (*(volatile uint32_t *)(WDOG_BASE + 0x0CUL)) /* Window */ + +/* WDOG CS Register Bits */ +#define WDOG_CS_STOP (1UL << 0) /* Stop enable */ +#define WDOG_CS_WAIT (1UL << 1) /* Wait enable */ +#define WDOG_CS_DBG (1UL << 2) /* Debug enable */ +#define WDOG_CS_TST_SHIFT 3 +#define WDOG_CS_TST_MASK (3UL << WDOG_CS_TST_SHIFT) /* Test mode */ +#define WDOG_CS_UPDATE (1UL << 5) /* Allow updates */ +#define WDOG_CS_INT (1UL << 6) /* Interrupt enable */ +#define WDOG_CS_EN (1UL << 7) /* Watchdog enable */ +#define WDOG_CS_CLK_SHIFT 8 +#define WDOG_CS_CLK_MASK (3UL << WDOG_CS_CLK_SHIFT) /* Clock source */ +#define WDOG_CS_CLK_BUS (0UL << WDOG_CS_CLK_SHIFT) /* Bus clock */ +#define WDOG_CS_CLK_LPO (1UL << WDOG_CS_CLK_SHIFT) /* LPO clock (128kHz) */ +#define WDOG_CS_CLK_SOSC (2UL << WDOG_CS_CLK_SHIFT) /* SOSC clock */ +#define WDOG_CS_CLK_SIRC (3UL << WDOG_CS_CLK_SHIFT) /* SIRC clock */ +#define WDOG_CS_RCS (1UL << 10) /* Reconfiguration success */ +#define WDOG_CS_ULK (1UL << 11) /* Unlock status */ +#define WDOG_CS_PRES (1UL << 12) /* Prescaler (256 divider) */ +#define WDOG_CS_CMD32EN (1UL << 13) /* 32-bit command support */ +#define WDOG_CS_FLG (1UL << 14) /* Interrupt flag */ +#define WDOG_CS_WIN (1UL << 15) /* Window mode enable */ + +/* WDOG Unlock Key - write to CNT register to unlock */ +#define WDOG_CNT_UNLOCK (0xD928C520UL) + +/* WDOG Refresh Keys - write in sequence to CNT register to refresh */ +#define WDOG_CNT_REFRESH_HI (0xB480UL) +#define WDOG_CNT_REFRESH_LO (0xA602UL) +#define WDOG_CNT_REFRESH (0xB480A602UL) /* For CMD32EN mode */ + +/* Default WDOG timeout value (max = 0xFFFF) */ +#define WDOG_TOVAL_DEFAULT (0xFFFFUL) + +/* Watchdog disable configuration: + * - EN=0 (disabled), UPDATE=1, CMD32EN=1, CLK=LPO + */ +#define WDOG_CS_DISABLE_CFG (WDOG_CS_UPDATE | WDOG_CS_CMD32EN | WDOG_CS_CLK_LPO) + +/* Watchdog enable configuration: + * - EN=1, UPDATE=1, CMD32EN=1, CLK=LPO + * LPO is 128kHz, with PRES=0 (no 256 divider) + * Timeout = TOVAL / 128kHz = TOVAL * 7.8125us + * For ~1 second timeout: TOVAL = 128000 + */ +#define WDOG_CS_ENABLE_CFG (WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_CMD32EN | \ + WDOG_CS_CLK_LPO) + + +/* Default 1 second timeout */ +#ifndef WATCHDOG_TIMEOUT_MS +#define WATCHDOG_TIMEOUT_MS 1000 +#endif + +/* ============== UART Function Declarations ============== */ +/* These functions are implemented in hal/s32k1xx.c when DEBUG_UART is defined. + * They use the LPUART instance and pins configured by the DEBUG_UART_* macros. + */ + +#ifdef DEBUG_UART +/* Initialize the UART with configured LPUART and pins */ +void uart_init(void); + +/* Write data to UART (blocking, with automatic LF -> CRLF conversion) */ +void uart_write(const char* buf, unsigned int sz); + +/* Read a single character from UART (non-blocking) + * Returns: 1 if character read, 0 if no data available, -1 on error + */ +int uart_read(char* c); + +#endif /* DEBUG_UART */ + +#endif /* S32K1XX_H */ + diff --git a/hal/s32k1xx.ld b/hal/s32k1xx.ld new file mode 100644 index 0000000000..e28275919b --- /dev/null +++ b/hal/s32k1xx.ld @@ -0,0 +1,86 @@ +/* + * Linker script for wolfBoot on NXP S32K1xx + * + * S32K142: 256KB Flash (0x00000000 - 0x0003FFFF), 32KB SRAM (split) + * + * SRAM Layout (S32K142 - 32KB total, split across two regions): + * SRAM_L: 0x1FFFC000 - 0x1FFFFFFF (16 KB) - Lower SRAM + * SRAM_U: 0x20000000 - 0x20002FFF (12 KB) - Upper SRAM + * + * Flash Memory Layout: + * 0x00000000 - 0x0000BFFF: Bootloader (48 KB) + * 0x0000C000 - 0x00024FFF: Boot Partition (100 KB) + * 0x00025000 - 0x0003DFFF: Update Partition (100 KB) + * 0x0003E000 - 0x0003E7FF: Swap Sector (2 KB) + * + * Note: Using SRAM_L for data/bss and SRAM_U for stack + * Stack grows down from 0x20003000 + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@ + SRAM_L (rwx) : ORIGIN = 0x1FFFC000, LENGTH = 0x4000 /* 16KB lower SRAM */ + SRAM_U (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000 /* 12KB upper SRAM */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + + /* Flash Configuration Field (FCF) at 0x400 */ + . = 0x400; + KEEP(*(.flash_config)) + . = ALIGN(4); + + *(.text*) + *(.rodata*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > SRAM_L + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > SRAM_L + . = ALIGN(4); +} + +/* Partition addresses for wolfBoot */ +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +/* Stack at end of SRAM_U (grows down from 0x20003000) */ +END_STACK = ORIGIN(SRAM_U) + LENGTH(SRAM_U); diff --git a/src/update_flash.c b/src/update_flash.c index b51f400a97..f576d922df 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1294,6 +1294,7 @@ void RAMFUNCTION wolfBoot_start(void) #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) (void)hal_hsm_server_cleanup(); #endif + hal_prepare_boot(); do_boot((void *)boot.fw_base); diff --git a/test-app/ARM-s32k1xx.ld b/test-app/ARM-s32k1xx.ld new file mode 100644 index 0000000000..4ee8d4014c --- /dev/null +++ b/test-app/ARM-s32k1xx.ld @@ -0,0 +1,76 @@ +/* + * Linker script for wolfBoot test application on NXP S32K1xx + * + * Application runs from boot partition after wolfBoot header + * + * S32K142 SRAM Layout (32KB total, split across two regions): + * SRAM_L: 0x1FFFC000 - 0x1FFFFFFF (16 KB) - Lower SRAM + * SRAM_U: 0x20000000 - 0x20002FFF (12 KB) - Upper SRAM + * + * Using upper SRAM for application data/bss/stack + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000 /* 12KB upper SRAM */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.text*) + *(.rodata*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > RAM + . = ALIGN(4); +} + + + +/* Partition info for libwolfboot */ +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +/* Stack and heap indicators */ +_start_heap = _end; +_end_stack = ORIGIN(RAM) + LENGTH(RAM); + diff --git a/test-app/Makefile b/test-app/Makefile index 135dbfd5ac..d2e39d218b 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -73,6 +73,7 @@ LDFLAGS+=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map OBJCOPY_FLAGS+=--gap-fill $(FILL_BYTE) ifeq ($(DEBUG_UART),1) + CFLAGS+=-DDEBUG_UART APP_OBJS+=../src/string.o endif @@ -348,6 +349,13 @@ ifeq ($(TARGET),mcxa) LDFLAGS+=--specs=nosys.specs endif +ifeq ($(TARGET),s32k1xx) + LSCRIPT_TEMPLATE=ARM-s32k1xx.ld + APP_OBJS+=../src/keystore.o + CFLAGS+=-DAPP_HAS_SYSTICK + CFLAGS+=-DRAM_CODE -DDEBUG_UART +endif + ifeq ($(TARGET),mcxw) ifeq ($(TZEN),1) LSCRIPT_TEMPLATE=ARM-mcxw-ns.ld diff --git a/test-app/app_s32k1xx.c b/test-app/app_s32k1xx.c new file mode 100644 index 0000000000..21e4e6894c --- /dev/null +++ b/test-app/app_s32k1xx.c @@ -0,0 +1,1089 @@ +/* app_s32k1xx.c + * + * Test bare-metal application for NXP S32K1xx + * Features: + * - LED indicator based on firmware version (Green=v1, Blue=v>1) + * - Interactive console with commands + * - XMODEM firmware update support + * - Partition and keystore information display + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include +#include +#include "hal.h" +#include "../hal/s32k1xx.h" +#include "wolfboot/wolfboot.h" +#include "keystore.h" +#include "target.h" +#include "image.h" + +#ifdef TARGET_s32k1xx + +/* RAMFUNCTION for test-app: code that runs during flash operations must be in RAM */ +#ifdef RAM_CODE + #define APP_RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) +#else + #define APP_RAMFUNCTION +#endif + +/* ============== SysTick Timer ============== */ + +static volatile uint32_t jiffies = 0; + +/* SysTick interrupt handler - called isr_systick to match startup_arm.c */ +void isr_systick(void) +{ + jiffies++; +} + +static uint32_t get_time_ms(void) +{ + return jiffies; +} + +static void delay_ms(uint32_t ms) +{ + uint32_t start = jiffies; + while ((jiffies - start) < ms) { + __asm__ volatile ("wfi"); + } +} + +static void systick_init(void) +{ + /* Configure SysTick for 1ms tick */ + SYST_RVR = (CLOCK_SPEED / 1000) - 1; + SYST_CVR = 0; + SYST_CSR = SYST_CSR_ENABLE | SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE; +} + +/* ============== LED Functions ============== */ + +static void led_init(void) +{ + /* Enable clock to PORTD */ + PCC_PORTD |= PCC_CGC; + + /* Configure LED pins as GPIO */ + PORTD_PCR0 = PORT_PCR_MUX_GPIO; /* Blue LED */ + PORTD_PCR15 = PORT_PCR_MUX_GPIO; /* Red LED */ + PORTD_PCR16 = PORT_PCR_MUX_GPIO; /* Green LED */ + + /* Set as outputs */ + GPIOD_PDDR |= (1UL << LED_PIN_BLUE) | (1UL << LED_PIN_RED) | (1UL << LED_PIN_GREEN); + + /* All LEDs off initially (active low) */ + GPIOD_PSOR = (1UL << LED_PIN_BLUE) | (1UL << LED_PIN_RED) | (1UL << LED_PIN_GREEN); +} + +static void led_green_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_GREEN); /* Active low */ +} + +static void led_green_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_GREEN); +} + +static void led_blue_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_BLUE); /* Active low */ +} + +static void led_blue_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_BLUE); +} + +static void APP_RAMFUNCTION led_red_on(void) +{ + GPIOD_PCOR = (1UL << LED_PIN_RED); /* Active low */ +} + +static void APP_RAMFUNCTION led_red_off(void) +{ + GPIOD_PSOR = (1UL << LED_PIN_RED); +} + +static void led_toggle_version(uint32_t version) +{ + if (version == 1) { + GPIOD_PTOR = (1UL << LED_PIN_GREEN); + } else { + GPIOD_PTOR = (1UL << LED_PIN_BLUE); + } +} + +/* Set LED based on version: Green for v1, Blue for v>1 */ +static void led_set_version(uint32_t version) +{ + /* Turn off both first */ + led_green_off(); + led_blue_off(); + + if (version == 1) { + led_green_on(); + } else if (version > 1) { + led_blue_on(); + } +} + +/* ============== System Control ============== */ + +void arch_reboot(void) +{ + SCB_AIRCR = AIRCR_VECTKEY | AIRCR_SYSRESETREQ; + while (1) { + __asm__ volatile ("wfi"); + } +} + +/* ============== UART / Printf Support ============== */ + +#ifdef DEBUG_UART +/* UART functions are declared in s32k1xx.h and implemented in hal/s32k1xx.c */ + +/* Flag to block text output during XMODEM transfer */ +static volatile int xmodem_active = 0; + +/* ============== UART RX Interrupt Buffering ============== */ +#define UART_RX_BUF_SIZE 512 +static volatile uint8_t uart_rx_buf[UART_RX_BUF_SIZE]; +static volatile uint32_t uart_rx_head = 0; /* Write index (ISR writes here) */ +static volatile uint32_t uart_rx_tail = 0; /* Read index (app reads from here) */ + +/* LPUART1 RX Interrupt Handler */ +void isr_lpuart1(void) +{ + uint32_t stat = LPUART1_STAT; + + /* Clear only the error flags (write 1 to clear) - do NOT write other bits */ + uint32_t errors = stat & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE | LPUART_STAT_PF); + if (errors) { + LPUART1_STAT = errors; /* Write 1 to clear only error flags */ + } + + /* Read all available bytes from FIFO */ + while (LPUART1_STAT & LPUART_STAT_RDRF) { + uint8_t c = (uint8_t)(LPUART1_DATA & 0xFF); + uint32_t next_head = (uart_rx_head + 1) % UART_RX_BUF_SIZE; + + /* Store byte if buffer not full */ + if (next_head != uart_rx_tail) { + uart_rx_buf[uart_rx_head] = c; + uart_rx_head = next_head; + } + /* else: buffer full, discard byte */ + } +} + +/* Read from RX buffer (for XMODEM) - returns number of bytes read + * Must be RAMFUNCTION since it's called during flash operations */ +static int APP_RAMFUNCTION uart_rx_isr(uint8_t *buf, int max_len) +{ + int count = 0; + + while (count < max_len && uart_rx_tail != uart_rx_head) { + buf[count++] = uart_rx_buf[uart_rx_tail]; + uart_rx_tail = (uart_rx_tail + 1) % UART_RX_BUF_SIZE; + } + + return count; +} + +/* Check if RX data available */ +static int uart_rx_available(void) +{ + return (uart_rx_head != uart_rx_tail) ? 1 : 0; +} + +/* Read single character from RX buffer (for console) - returns 1 if char read, 0 if none */ +static int uart_getc(char *c) +{ + if (uart_rx_tail != uart_rx_head) { + *c = uart_rx_buf[uart_rx_tail]; + uart_rx_tail = (uart_rx_tail + 1) % UART_RX_BUF_SIZE; + return 1; + } + return 0; +} + +/* Enable LPUART RX interrupt */ +static void uart_rx_irq_enable(void) +{ + /* Set interrupt priority lower than SysTick (higher number = lower priority) + * SysTick defaults to priority 0, so set LPUART to 2 to ensure + * jiffies keeps incrementing even during heavy UART traffic. */ + NVIC_SetPriority(LPUART1_IRQn, 2); + + /* Enable LPUART1 interrupt in NVIC */ + NVIC_EnableIRQ(LPUART1_IRQn); + + /* Enable Receiver Interrupt in LPUART */ + LPUART1_CTRL |= LPUART_CTRL_RIE; +} + +/* Print hex buffer (similar to stm32h5 style) */ +#define LINE_LEN 16 +static void print_hex(const uint8_t* buffer, uint32_t length, int dumpChars) +{ + uint32_t i, sz; + + if (!buffer) { + printf("\tNULL\r\n"); + return; + } + + while (length > 0) { + sz = length; + if (sz > LINE_LEN) + sz = LINE_LEN; + + printf("\t"); + for (i = 0; i < LINE_LEN; i++) { + if (i < length) + printf("%02x ", buffer[i]); + else + printf(" "); + } + if (dumpChars) { + printf("| "); + for (i = 0; i < sz; i++) { + if (buffer[i] > 31 && buffer[i] < 127) + printf("%c", buffer[i]); + else + printf("."); + } + } + printf("\r\n"); + + buffer += sz; + length -= sz; + } +} +#endif /* DEBUG_UART */ + +/* ============== Partition State Names ============== */ + +static const char* part_state_name(uint8_t state, int state_retval) +{ + if (state_retval == 0) { + return "(no trailer)"; + } + switch (state) { + case IMG_STATE_NEW: return "NEW"; + case IMG_STATE_UPDATING: return "UPDATING"; + case IMG_STATE_TESTING: return "TESTING"; + case IMG_STATE_SUCCESS: return "SUCCESS"; + default: return "UNKNOWN"; + } +} + +/* ============== Key Type Names ============== */ + +static const char* key_type_name(uint32_t type) +{ + switch (type) { + case AUTH_KEY_ECC256: return "ECDSA P-256 (secp256r1)"; + case AUTH_KEY_ECC384: return "ECDSA P-384 (secp384r1)"; + case AUTH_KEY_ECC521: return "ECDSA P-521 (secp521r1)"; + case AUTH_KEY_RSA2048: return "RSA-2048"; + case AUTH_KEY_RSA3072: return "RSA-3072"; + case AUTH_KEY_RSA4096: return "RSA-4096"; + case AUTH_KEY_ED25519: return "Ed25519"; + case AUTH_KEY_ED448: return "Ed448"; + case AUTH_KEY_LMS: return "LMS"; + case AUTH_KEY_XMSS: return "XMSS"; + case AUTH_KEY_ML_DSA: return "ML-DSA"; + default: return "Unknown"; + } +} + +static const char* hash_type_name(void) +{ +#ifdef WOLFBOOT_HASH_SHA256 + return "SHA-256"; +#elif defined(WOLFBOOT_HASH_SHA384) + return "SHA-384"; +#elif defined(WOLFBOOT_HASH_SHA512) + return "SHA-512"; +#elif defined(WOLFBOOT_HASH_SHA3_384) + return "SHA3-384"; +#endif + return "Unknown"; +} + +/* ============== Information Display ============== */ + +#ifdef DEBUG_UART +static void print_partition_info(void) +{ + uint32_t boot_ver, update_ver; + uint8_t boot_state = 0, update_state = 0; + int boot_state_valid, update_state_valid; + + boot_ver = wolfBoot_current_firmware_version(); + update_ver = wolfBoot_update_firmware_version(); + + boot_state_valid = wolfBoot_get_partition_state(PART_BOOT, &boot_state); + update_state_valid = wolfBoot_get_partition_state(PART_UPDATE, &update_state); + + printf("\r\n=== Partition Information ===\r\n"); + + printf("Boot Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_BOOT_ADDRESS); + printf(" Version: %lu\r\n", (unsigned long)boot_ver); + printf(" State: %s\r\n", part_state_name(boot_state, boot_state_valid)); + + printf("Update Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + printf(" Version: %lu\r\n", (unsigned long)update_ver); + printf(" State: %s\r\n", part_state_name(update_state, update_state_valid)); + + printf("Swap Partition:\r\n"); + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_SWAP_ADDRESS); + printf(" Size: %lu bytes\r\n", (unsigned long)WOLFBOOT_SECTOR_SIZE); +} + +static void print_keystore_info(void) +{ + uint32_t n_keys; + int i; + + printf("\r\n=== Keystore Information ===\r\n"); + + + n_keys = keystore_num_pubkeys(); + printf("Number of public keys: %lu\r\n", (unsigned long)n_keys); + printf("Hash: %s\r\n", hash_type_name()); + + for (i = 0; i < (int)n_keys; i++) { + uint32_t size = keystore_get_size(i); + uint32_t type = keystore_get_key_type(i); + uint8_t* keybuf = keystore_get_buffer(i); + + printf("\r\nKey #%d:\r\n", i); + printf(" Algorithm: %s\r\n", key_type_name(type)); + printf(" Size: %lu bytes\r\n", (unsigned long)size); + printf(" Data:\r\n"); + print_hex(keybuf, size, 0); + } +} + +/* ============== XMODEM Transfer ============== */ + +#define XSOH 0x01 +#define XEOT 0x04 +#define XACK 0x06 +#define XNAK 0x15 +#define XCAN 0x18 +#define XCRC 'C' /* Request CRC mode */ + +#define XMODEM_PAYLOAD_SIZE 128 +#define XMODEM_PACKET_SIZE_CRC (3 + XMODEM_PAYLOAD_SIZE + 2) /* SOH + blk + ~blk + data + CRC16 */ +#define XMODEM_TIMEOUT_MS 1000 + +/* CRC-16-CCITT for XMODEM-CRC mode */ +static uint16_t APP_RAMFUNCTION crc16_ccitt(const uint8_t* data, int len) +{ + uint16_t crc = 0; + int i, j; + for (i = 0; i < len; i++) { + crc ^= ((uint16_t)data[i] << 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } + } + return crc; +} + +/* Raw byte transmit for XMODEM (declared in hal, runs from RAM) */ +extern void uart_tx(uint8_t byte); + +/* RAM-based memory copy for use during flash operations */ +static void APP_RAMFUNCTION ram_memcpy(void *dst, const void *src, uint32_t len) +{ + uint8_t *d = (uint8_t *)dst; + const uint8_t *s = (const uint8_t *)src; + while (len--) { + *d++ = *s++; + } +} + +static void APP_RAMFUNCTION xmodem_cancel(void) +{ + int i; + for (i = 0; i < 10; i++) { + uart_tx(XCAN); + } +} + +/* XMODEM receive state - passed to RAM function */ +typedef struct { + uint32_t dst_offset; + int result; + /* Debug counters */ + uint32_t pkts_received; + uint32_t pkts_crc_fail; + uint32_t pkts_num_fail; + uint32_t pkts_soh_fail; + uint32_t timeouts; +} xmodem_state_t; + +/* Core XMODEM-CRC receive loop - runs entirely from RAM during flash operations + * Uses XMODEM-CRC mode (133-byte packets with 16-bit CRC) + * Returns: 0 on success, -1 on error + */ +static int APP_RAMFUNCTION xmodem_receive_ram(xmodem_state_t *state) +{ + uint8_t xpkt[XMODEM_PACKET_SIZE_CRC]; + uint8_t payload[XMODEM_PAYLOAD_SIZE]; + uint8_t pkt_num = 0, pkt_num_expected = 0xFF; + uint32_t t_size = 0; + uint32_t now; + uint32_t i = 0; + int transfer_started = 0; + int eot_expected = 0; + int ret = -1; + + state->dst_offset = 0; + state->result = -1; + state->pkts_received = 0; + state->pkts_crc_fail = 0; + state->pkts_num_fail = 0; + state->pkts_soh_fail = 0; + state->timeouts = 0; + + /* Send 'C' to request CRC mode (XMODEM-CRC) */ + uart_tx(XCRC); + + while (1) { + now = jiffies; /* Direct access to volatile - faster than function call */ + i = 0; + + /* Receive packet - uses interrupt-buffered RX to avoid FIFO overflow */ + while (i < XMODEM_PACKET_SIZE_CRC) { + int r = uart_rx_isr(&xpkt[i], XMODEM_PACKET_SIZE_CRC - i); + if (r > 0) { + i += r; + now = jiffies; + if (i >= 1 && xpkt[0] == XEOT) { + break; /* End of transmission */ + } + } else if (jiffies > (now + XMODEM_TIMEOUT_MS)) { + now = jiffies; + state->timeouts++; + if (i == 0) { + uart_tx(XCRC); /* Request CRC mode again */ + } + i = 0; + } + } + + /* Check for EOT */ + if (xpkt[0] == XEOT) { + uart_tx(XACK); + led_red_on(); /* Indicate transfer complete */ + ret = 0; + break; + } else if (eot_expected) { + uart_tx(XNAK); + ret = -1; + break; + } + + /* Validate SOH */ + if (xpkt[0] != XSOH) { + state->pkts_soh_fail++; + continue; + } + state->pkts_received++; + + /* Validate packet number */ + pkt_num = xpkt[1]; + if ((uint8_t)(~xpkt[2]) == pkt_num) { + uint16_t recv_crc, calc_crc; + + if (!transfer_started) { + pkt_num_expected = pkt_num; + transfer_started = 1; + } else if (pkt_num_expected != pkt_num) { + uart_tx(XNAK); + continue; + } + + /* Toggle LED to show activity */ + if ((pkt_num & 0x0F) == 0) { + led_red_on(); + } else if ((pkt_num & 0x0F) == 8) { + led_red_off(); + } + + /* Validate CRC-16 - XMODEM-CRC uses CRC over DATA bytes only */ + recv_crc = ((uint16_t)xpkt[XMODEM_PACKET_SIZE_CRC - 2] << 8) | + xpkt[XMODEM_PACKET_SIZE_CRC - 1]; + calc_crc = crc16_ccitt(xpkt + 3, XMODEM_PAYLOAD_SIZE); + + if (recv_crc == calc_crc) { + /* Copy payload using RAM-based memcpy */ + ram_memcpy(payload, xpkt + 3, XMODEM_PAYLOAD_SIZE); + + /* Send ACK first, then write to flash. + * This allows sender to prepare next packet while we write. + * Risk: if write fails, we've already ACKed - but that's rare. + */ + uart_tx(XACK); + + /* Write to flash */ + ret = hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + state->dst_offset, + payload, XMODEM_PAYLOAD_SIZE); + if (ret != 0) { + xmodem_cancel(); + /* No printf here - we're in RAM */ + break; + } + pkt_num_expected++; + state->dst_offset += XMODEM_PAYLOAD_SIZE; + + /* Get expected size from header (offset 4 = image size) */ + if (t_size == 0 && state->dst_offset >= 8) { + t_size = *(uint32_t*)(payload + 4) + IMAGE_HEADER_SIZE; + } + + if (t_size > 0 && state->dst_offset >= t_size) { + eot_expected = 1; + } + } else { + state->pkts_crc_fail++; + uart_tx(XNAK); + } + } else { + state->pkts_num_fail++; + uart_tx(XNAK); + } + } + + state->result = ret; + return ret; +} + +static int cmd_update_xmodem(void) +{ + xmodem_state_t state; + int ret; + uint32_t erase_addr; + int erase_ret; + + printf("Erasing update partition...\r\n"); +#ifdef DEBUG_FLASH + printf(" Address: 0x%08lX\r\n", (unsigned long)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + printf(" Size: 0x%08lX (%lu bytes)\r\n", + (unsigned long)WOLFBOOT_PARTITION_SIZE, + (unsigned long)WOLFBOOT_PARTITION_SIZE); +#endif + + hal_flash_unlock(); + +#ifdef DEBUG_FLASH + /* Erase sector by sector with debug output */ + erase_addr = WOLFBOOT_PARTITION_UPDATE_ADDRESS; + while (erase_addr < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) { + printf(" Erasing sector at 0x%08lX...", (unsigned long)erase_addr); + fflush(stdout); + + erase_ret = hal_flash_erase(erase_addr, WOLFBOOT_SECTOR_SIZE); + if (erase_ret != 0) { + printf(" FAILED (%d)\r\n", erase_ret); + hal_flash_lock(); + return -1; + } + printf(" OK\r\n"); + erase_addr += WOLFBOOT_SECTOR_SIZE; + } +#else + (void)erase_addr; + (void)erase_ret; + hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); +#endif + + printf("Done.\r\n"); + printf("Waiting for XMODEM transfer...\r\n"); + printf("(Send file now using XMODEM-CRC protocol)\r\n"); + + /* Flush all printf output before starting XMODEM */ + fflush(stdout); + /* Wait for UART TX to complete */ + while (!(LPUART1_STAT & LPUART_STAT_TC)) {} + + /* Drain any pending RX data before starting XMODEM */ + { + char c; + while (uart_getc(&c) > 0) {} /* Use ISR buffer, not hardware */ + } + + /* Small delay to ensure clean start */ + delay_ms(100); + + /* Block all printf output during XMODEM */ + xmodem_active = 1; + + /* Run the receive loop from RAM */ + ret = xmodem_receive_ram(&state); + + /* Re-enable printf output */ + xmodem_active = 0; + + hal_flash_lock(); + + /* Wait for sender to finish and drain any pending RX data. + * This prevents printf output from mixing with XMODEM retransmits. */ + { + char c; + delay_ms(3000); /* Wait for sender to give up */ + while (uart_read(&c) > 0) {} /* Drain RX buffer */ + } + + printf("\r\nTransfer %s\r\n", (ret == 0) ? "complete!" : "failed."); + printf("XMODEM stats: recv=%lu, crc_fail=%lu, num_fail=%lu, soh_fail=%lu, timeouts=%lu\r\n", + (unsigned long)state.pkts_received, (unsigned long)state.pkts_crc_fail, + (unsigned long)state.pkts_num_fail, (unsigned long)state.pkts_soh_fail, + (unsigned long)state.timeouts); + + if (ret == 0) { + uint32_t update_ver = wolfBoot_update_firmware_version(); + if (update_ver != 0) { + printf("New firmware version: %lu\r\n", (unsigned long)update_ver); + printf("Triggering update...\r\n"); + wolfBoot_update_trigger(); + printf("Reboot to apply update.\r\n"); + } else { + printf("Warning: No valid image detected\r\n"); + } + } + + led_red_off(); + return ret; +} + +/* ============== Console Commands ============== */ + +static int cmd_help(const char *args); +static int cmd_info(const char *args); +static int cmd_success(const char *args); +static int cmd_reboot(const char *args); +static int cmd_update(const char *args); +static int cmd_timestamp(const char *args); +static int cmd_trigger(const char *args); +static int cmd_status(const char *args); + +typedef struct { + int (*fn)(const char *args); + const char* name; + const char* help; +} console_cmd_t; + +static const console_cmd_t commands[] = { + {cmd_help, "help", "Show this help message"}, + {cmd_info, "info", "Display partition and key info"}, + {cmd_status, "status", "Show partition versions and states"}, + {cmd_success, "success", "Mark firmware as successful"}, + {cmd_trigger, "trigger", "Trigger update (if update image in flash)"}, + {cmd_update, "update", "Update firmware via XMODEM"}, + {cmd_timestamp, "timestamp", "Show current system time"}, + {cmd_reboot, "reboot", "Reboot the system"}, + {NULL, NULL, NULL} +}; + +static int cmd_help(const char *args) +{ + int i; + (void)args; + printf("\r\nAvailable commands:\r\n"); + for (i = 0; commands[i].name != NULL; i++) { + printf(" %s - %s\r\n", commands[i].name, commands[i].help); + } + return 0; +} + +static int cmd_info(const char *args) +{ + (void)args; + print_partition_info(); + print_keystore_info(); + return 0; +} + +static int cmd_success(const char *args) +{ + (void)args; + wolfBoot_success(); + printf("Firmware marked as successful.\r\n"); + return 0; +} + +static int cmd_timestamp(const char *args) +{ + (void)args; + printf("Current systick: %lu ms\r\n", (unsigned long)jiffies); + return 0; +} + +static int cmd_status(const char *args) +{ + uint32_t boot_ver, update_ver; + uint8_t boot_state, update_state; + int ret; + (void)args; + + boot_ver = wolfBoot_current_firmware_version(); + update_ver = wolfBoot_update_firmware_version(); + + printf("\r\n=== Partition Status ===\r\n"); + printf("Boot Partition: v%lu @ 0x%lX\r\n", + (unsigned long)boot_ver, (unsigned long)WOLFBOOT_PARTITION_BOOT_ADDRESS); + + ret = wolfBoot_get_partition_state(PART_BOOT, &boot_state); + if (ret == 0) { + printf(" State: %s (0x%02X)\r\n", + (boot_state == IMG_STATE_SUCCESS) ? "SUCCESS" : + (boot_state == IMG_STATE_TESTING) ? "TESTING" : + (boot_state == IMG_STATE_UPDATING) ? "UPDATING" : "NEW", + boot_state); + } else { + printf(" State: (no trailer)\r\n"); + } + + printf("Update Partition: v%lu @ 0x%lX\r\n", + (unsigned long)update_ver, (unsigned long)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + + ret = wolfBoot_get_partition_state(PART_UPDATE, &update_state); + if (ret == 0) { + printf(" State: %s (0x%02X)\r\n", + (update_state == IMG_STATE_SUCCESS) ? "SUCCESS" : + (update_state == IMG_STATE_TESTING) ? "TESTING" : + (update_state == IMG_STATE_UPDATING) ? "UPDATING" : "NEW", + update_state); + } else { + printf(" State: (no trailer)\r\n"); + } + + if (update_ver > 0 && update_ver > boot_ver) { + printf("\r\nUpdate available! Use 'trigger' command to start update.\r\n"); + } + + return 0; +} + +static int cmd_trigger(const char *args) +{ + uint32_t update_ver; + (void)args; + + update_ver = wolfBoot_update_firmware_version(); + if (update_ver == 0) { + printf("No update image found in update partition.\r\n"); + return -1; + } + + printf("Update image version: %lu\r\n", (unsigned long)update_ver); + printf("Triggering update...\r\n"); + wolfBoot_update_trigger(); + printf("Update triggered. Use 'reboot' to start update.\r\n"); + return 0; +} + +static int cmd_reboot(const char *args) +{ + (void)args; + printf("Rebooting...\r\n"); + fflush(stdout); + delay_ms(100); /* Allow UART to flush */ + arch_reboot(); + return 0; +} + +static int cmd_update(const char *args) +{ + (void)args; + return cmd_update_xmodem(); +} + +static int parse_command(const char* cmd) +{ + int i; + for (i = 0; commands[i].name != NULL; i++) { + if (strcmp(cmd, commands[i].name) == 0) { + return commands[i].fn(NULL); + } + } + printf("Unknown command: %s\r\n", cmd); + printf("Type 'help' for available commands.\r\n"); + return -1; +} + +#define CMD_BUF_SIZE 64 + +static void console_loop(void) +{ + char cmd[CMD_BUF_SIZE]; + int idx; + char c; + + while (1) { + printf("\r\ncmd> "); + fflush(stdout); + idx = 0; + + while (idx < CMD_BUF_SIZE - 1) { + int ret = uart_getc(&c); + if (ret > 0) { + if (c == '\r' || c == '\n') { + printf("\r\n"); + break; + } else if (c == 0x08 || c == 0x7F) { /* Backspace */ + if (idx > 0) { + printf("\b \b"); + fflush(stdout); + idx--; + } + } else if (c >= 32 && c < 127) { + printf("%c", c); + fflush(stdout); + cmd[idx++] = c; + } + } + /* No delay - tight polling loop for responsive input */ + } + + cmd[idx] = '\0'; + if (idx > 0) { + parse_command(cmd); + } + } +} +#endif /* DEBUG_UART */ + +/* ============== Clock Functions ============== */ + +/* Ensure FIRC (48 MHz) is enabled for UART */ +static void clock_ensure_firc(void) +{ + /* Check if FIRC is valid */ + if (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) { + /* Enable FIRC if not already enabled */ + SCG_FIRCDIV = (1UL << 8) | (1UL << 0); /* FIRCDIV1=/1, FIRCDIV2=/1 */ + SCG_FIRCCFG = 0; /* Range 0: 48 MHz */ + SCG_FIRCCSR = SCG_FIRCCSR_FIRCEN; + + /* Wait for FIRC valid */ + while (!(SCG_FIRCCSR & SCG_FIRCCSR_FIRCVLD)) {} + } + + /* Ensure system is running from FIRC */ + if ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) { + SCG_RCCR = SCG_xCCR_SCS_FIRC | + (0UL << SCG_xCCR_DIVCORE_SHIFT) | + (0UL << SCG_xCCR_DIVBUS_SHIFT) | + (1UL << SCG_xCCR_DIVSLOW_SHIFT); + + /* Wait for clock switch */ + while ((SCG_CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS_FIRC) {} + } +} + +/* ============== Main Entry Point ============== */ + +void main(void) +{ + uint32_t version; + + /* Disable watchdog - bootloader may have enabled it */ + WDOG_CNT = WDOG_CNT_UNLOCK; + while (!(WDOG_CS & WDOG_CS_ULK)) {} + WDOG_TOVAL = 0xFFFF; + WDOG_CS = WDOG_CS_UPDATE | WDOG_CS_CMD32EN | WDOG_CS_CLK_LPO; /* Disabled, but updatable */ + while (!(WDOG_CS & WDOG_CS_RCS)) {} + + /* Ensure FIRC clock is running at 48 MHz for UART */ + clock_ensure_firc(); + +#ifdef DEBUG_UART + /* Reinitialize UART - bootloader may have changed settings in hal_prepare_boot */ + uart_init(); + /* Enable interrupt-based RX buffering for reliable XMODEM transfers */ + uart_rx_irq_enable(); + /* Disable stdout buffering to prevent delayed output during XMODEM */ + setvbuf(stdout, NULL, _IONBF, 0); +#endif + + /* Initialize test-app hardware */ + systick_init(); + led_init(); + + /* Enable interrupts */ + __asm__ volatile ("cpsie i"); + + /* Get current firmware version */ + version = wolfBoot_current_firmware_version(); + + /* Set LED based on version: Green for v1, Blue for v>1 */ + led_set_version(version); + +#ifdef DEBUG_UART + printf("\r\n"); + printf("========================================\r\n"); + printf("S32K1xx wolfBoot Test Application\r\n"); + printf("Copyright 2025 wolfSSL Inc.\r\n"); + printf("========================================\r\n"); + printf("Firmware Version: %lu\r\n", (unsigned long)version); + + /* Auto-mark success for testing if version > 1 */ + if (version > 1) { + uint8_t state = 0; + wolfBoot_get_partition_state(PART_BOOT, &state); + if (state == IMG_STATE_TESTING) { + printf("Testing state detected, marking success...\r\n"); + wolfBoot_success(); + } + } + + /* Show initial info */ + print_partition_info(); + + printf("\r\nType 'help' for available commands.\r\n"); + + /* Enter interactive console */ + console_loop(); +#else + /* No UART - just blink LED */ + while (1) { + led_toggle_version(version); + delay_ms(500); + } +#endif +} + +/* ============== Syscalls for printf support ============== */ + +int _getpid(void) +{ + return 1; +} + +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + return -1; +} + +void _exit(int status) +{ + _kill(status, -1); + while (1) {} +} + +int _read(int file, char *ptr, int len) +{ + (void)file; + (void)ptr; + (void)len; + return -1; +} + +int _write(int file, char *ptr, int len) +{ + (void)file; +#ifdef DEBUG_UART + /* Block text output during XMODEM to prevent protocol interference */ + if (!xmodem_active) { + uart_write(ptr, len); + } +#else + (void)ptr; +#endif + return len; +} + +int _close(int file) +{ + (void)file; + return -1; +} + +int _isatty(int file) +{ + (void)file; + return 1; +} + +int _lseek(int file, int ptr, int dir) +{ + (void)file; + (void)ptr; + (void)dir; + return 0; +} + +int _fstat(int file, struct stat *st) +{ + (void)file; + st->st_mode = S_IFCHR; + return 0; +} + +/* Back-end for malloc, used for printf */ +extern unsigned int _end; /* From linker script: end of BSS */ +extern unsigned int _end_stack; /* From linker script: end of RAM */ + +void *_sbrk(int incr) +{ + static unsigned char *heap = NULL; + unsigned char *prev_heap; + + if (heap == NULL) { + heap = (unsigned char *)&_end; + } + + prev_heap = heap; + + /* Align increment to 4 bytes */ + if (((incr >> 2) << 2) != incr) + incr = ((incr >> 2) + 1) << 2; + + /* Check we don't overflow into the stack */ + if ((heap + incr) > (unsigned char *)&_end_stack) { + return (void *)-1; + } + + heap += incr; + return prev_heap; +} + +#endif /* TARGET_s32k1xx */ diff --git a/test-app/startup_arm.c b/test-app/startup_arm.c index d6a76c6a1b..68e6c37465 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -39,11 +39,16 @@ extern void isr_tim2(void); extern void isr_usart3(void); #endif +#ifdef TARGET_s32k1xx +extern void isr_lpuart1(void); +#endif + #ifdef TARGET_va416x0 -extern void SysTick_Handler(void); -#elif defined(APP_HAS_SYSTICK) -extern void isr_systick(void); +#define isr_systick SysTick_Handler +#elif !defined(APP_HAS_SYSTICK) +#define isr_systick isr_empty #endif +extern void isr_systick(void); #ifndef STACK_PAINTING #define STACK_PAINTING 0 @@ -91,26 +96,26 @@ void isr_reset(void) { void isr_fault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_memfault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_busfault(void) { /* Panic. */ - while(1) ;; + while(1) ; } void isr_usagefault(void) { /* Panic. */ - while(1) ;; + while(1) ; } @@ -119,8 +124,6 @@ void isr_empty(void) } - - __attribute__ ((section(".isr_vector"))) void (* const IV[])(void) = { @@ -139,14 +142,7 @@ void (* const IV[])(void) = isr_empty, // DebugMonitor 0, // reserved isr_empty, // PendSV - -#ifdef TARGET_va416x0 - SysTick_Handler, // SysTick -#elif defined(APP_HAS_SYSTICK) isr_systick, // SysTick -#else - isr_empty, // SysTick -#endif /* Device specific IRQs for LM3S */ @@ -438,6 +434,97 @@ void (* const IV[])(void) = isr_empty, // LPTIM5_IRQHandler isr_empty, // LPTIM6_IRQHandler +#elif defined(TARGET_s32k1xx) /* For NXP S32K1xx */ + isr_empty, // DMA0 0 + isr_empty, // DMA1 1 + isr_empty, // DMA2 2 + isr_empty, // DMA3 3 + isr_empty, // DMA4 4 + isr_empty, // DMA5 5 + isr_empty, // DMA6 6 + isr_empty, // DMA7 7 + isr_empty, // DMA8 8 + isr_empty, // DMA9 9 + isr_empty, // DMA10 10 + isr_empty, // DMA11 11 + isr_empty, // DMA12 12 + isr_empty, // DMA13 13 + isr_empty, // DMA14 14 + isr_empty, // DMA15 15 + isr_empty, // DMA_Error 16 + isr_empty, // MCM 17 + isr_empty, // FTFC 18 + isr_empty, // Read_Collision 19 + isr_empty, // LVD_LVW 20 + isr_empty, // FTFC_Fault 21 + isr_empty, // WDOG_EWM 22 + isr_empty, // RCM 23 + isr_empty, // LPI2C0_Master 24 + isr_empty, // LPI2C0_Slave 25 + isr_empty, // LPSPI0 26 + isr_empty, // LPSPI1 27 + isr_empty, // LPSPI2 28 + isr_empty, // Reserved29 29 + isr_empty, // Reserved30 30 + isr_empty, // LPUART0_RxTx 31 + isr_empty, // Reserved32 32 + isr_lpuart1, // LPUART1_RxTx 33 + isr_empty, // Reserved34 34 + isr_empty, // LPUART2_RxTx 35 + isr_empty, // Reserved36 36 + isr_empty, // Reserved37 37 + isr_empty, // ADC0 38 + isr_empty, // ADC1 39 + isr_empty, // CMP0 40 + isr_empty, // Reserved41 41 + isr_empty, // Reserved42 42 + isr_empty, // ERM_single 43 + isr_empty, // ERM_double 44 + isr_empty, // RTC 45 + isr_empty, // RTC_Seconds 46 + isr_empty, // LPIT0_Ch0 47 + isr_empty, // LPIT0_Ch1 48 + isr_empty, // LPIT0_Ch2 49 + isr_empty, // LPIT0_Ch3 50 + isr_empty, // PDB0 51 + isr_empty, // Reserved52 52 + isr_empty, // Reserved53 53 + isr_empty, // Reserved54 54 + isr_empty, // Reserved55 55 + isr_empty, // SCG 56 + isr_empty, // LPTMR0 57 + isr_empty, // PORTA 58 + isr_empty, // PORTB 59 + isr_empty, // PORTC 60 + isr_empty, // PORTD 61 + isr_empty, // PORTE 62 + isr_empty, // Reserved63 63 + isr_empty, // PDB1 64 + isr_empty, // FLEXIO 65 + isr_empty, // CAN0_ORed 66 + isr_empty, // CAN0_Error 67 + isr_empty, // CAN0_Wake_Up 68 + isr_empty, // CAN0_MB0_15 69 + isr_empty, // CAN0_MB16_31 70 + isr_empty, // FTM0_Ch0_Ch1 71 + isr_empty, // FTM0_Ch2_Ch3 72 + isr_empty, // FTM0_Ch4_Ch5 73 + isr_empty, // FTM0_Ch6_Ch7 74 + isr_empty, // FTM0_Fault 75 + isr_empty, // FTM0_Ovf_Reload 76 + isr_empty, // FTM1_Ch0_Ch1 77 + isr_empty, // FTM1_Ch2_Ch3 78 + isr_empty, // FTM1_Ch4_Ch5 79 + isr_empty, // FTM1_Ch6_Ch7 80 + isr_empty, // FTM1_Fault 81 + isr_empty, // FTM1_Ovf_Reload 82 + isr_empty, // FTM2_Ch0_Ch1 83 + isr_empty, // FTM2_Ch2_Ch3 84 + isr_empty, // FTM2_Ch4_Ch5 85 + isr_empty, // FTM2_Ch6_Ch7 86 + isr_empty, // FTM2_Fault 87 + isr_empty, // FTM2_Ovf_Reload 88 + #elif defined(STM32) /* For STM32 */ isr_empty, // NVIC_WWDG_IRQ 0 isr_empty, // PVD_IRQ 1 diff --git a/tools/scripts/nxp-s32k142-flash.sh b/tools/scripts/nxp-s32k142-flash.sh new file mode 100755 index 0000000000..7c82af3d7e --- /dev/null +++ b/tools/scripts/nxp-s32k142-flash.sh @@ -0,0 +1,337 @@ +#!/bin/bash +# +# NXP S32K142 Flash and UART Monitor Script +# +# This script automates: +# 1. Configure for NXP S32K142 +# 2. Build factory.srec (or update.srec for testing updates) +# 3. Start UART capture (before flash to catch boot messages) +# 4. Flash via USB mass storage +# 5. Capture UART output for specified duration +# + +set -e + +# Configuration +CONFIG_FILE="./config/examples/nxp-s32k142.config" +MOUNT_PATH="/media/davidgarske/S32K142EVB" +UART_DEV="/dev/ttyACM1" +UART_BAUD=115200 +SREC_FILE="factory.srec" +UART_TIMEOUT=5 # Default 5 seconds capture + +# Memory layout (from nxp-s32k142.config) +WOLFBOOT_PARTITION_BOOT_ADDRESS=0xC000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x25000 +WOLFBOOT_PARTITION_SIZE=0x19000 + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Parse command line arguments +SKIP_BUILD=0 +SKIP_FLASH=0 +SKIP_UART=0 +UART_ONLY=0 +INTERACTIVE=0 +BUILD_UPDATE=0 +TEST_UPDATE=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 " --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)" + echo " --interactive Keep UART open until Ctrl+C (default: auto-exit after timeout)" + echo " --timeout SECS UART capture duration in seconds (default: $UART_TIMEOUT)" + echo " --mount PATH Override mount path (default: $MOUNT_PATH)" + echo " --uart DEV Override UART device (default: $UART_DEV)" + echo " --baud RATE Override baud rate (default: $UART_BAUD)" + echo " -h, --help Show this help message" + echo "" + 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 --skip-uart # Flash without UART monitoring" + echo " $0 --uart-only # Just monitor UART" + exit 0 +} + +while [[ $# -gt 0 ]]; do + case $1 in + --test-update) + TEST_UPDATE=1 + SREC_FILE="test-update.srec" + shift + ;; + --update) + BUILD_UPDATE=1 + SREC_FILE="update.srec" + shift + ;; + --skip-build) + SKIP_BUILD=1 + shift + ;; + --skip-flash) + SKIP_FLASH=1 + shift + ;; + --skip-uart) + SKIP_UART=1 + shift + ;; + --uart-only) + SKIP_BUILD=1 + SKIP_FLASH=1 + shift + ;; + --interactive) + INTERACTIVE=1 + shift + ;; + --timeout) + UART_TIMEOUT="$2" + shift 2 + ;; + --mount) + MOUNT_PATH="$2" + shift 2 + ;; + --uart) + UART_DEV="$2" + shift 2 + ;; + --baud) + UART_BAUD="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + usage + ;; + esac +done + +# Cleanup function to kill background UART capture +UART_PID="" +cleanup() { + if [ -n "$UART_PID" ] && kill -0 "$UART_PID" 2>/dev/null; then + kill "$UART_PID" 2>/dev/null || true + wait "$UART_PID" 2>/dev/null || true + fi +} +trap cleanup EXIT + +echo -e "${GREEN}=== NXP S32K142 Flash and Monitor Script ===${NC}" + +# Change to wolfboot root directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +cd "${WOLFBOOT_ROOT}" +echo -e "${YELLOW}Working directory: ${WOLFBOOT_ROOT}${NC}" + +# Step 1: Configure +if [ $SKIP_BUILD -eq 0 ]; then + echo "" + echo -e "${GREEN}[1/4] Configuring for NXP S32K142...${NC}" + if [ ! -f "${CONFIG_FILE}" ]; then + echo -e "${RED}Error: Config file not found: ${CONFIG_FILE}${NC}" + exit 1 + fi + cp "${CONFIG_FILE}" .config + echo "Copied ${CONFIG_FILE} to .config" + + # Step 2: Build + echo "" + if [ $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 + + # Build v2 signed image + 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 + + echo -e "${CYAN}Assembling test-update.bin...${NC}" + echo " wolfboot.bin @ 0x0" + echo " image_v1_signed.bin @ ${WOLFBOOT_PARTITION_BOOT_ADDRESS} (boot partition)" + echo " image_v2_signed.bin @ ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} (update partition)" + echo "" + echo -e "${YELLOW}NOTE: No trigger magic - use test-app 'trigger' command to start update${NC}" + + ./tools/bin-assemble/bin-assemble \ + test-update.bin \ + 0x0 wolfboot.bin \ + ${WOLFBOOT_PARTITION_BOOT_ADDRESS} test-app/image_v1_signed_backup.bin \ + ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} test-app/image_v2_signed.bin + + # Convert to srec + echo -e "${CYAN}Converting to test-update.srec...${NC}" + arm-none-eabi-objcopy -I binary -O srec --srec-forceS3 test-update.bin test-update.srec + + # Cleanup temp files + mv test-app/image_v1_signed_backup.bin test-app/image_v1_signed.bin 2>/dev/null || true + + echo -e "${GREEN}Build successful: test-update.srec${NC}" + echo "" + echo -e "${CYAN}After boot, use these test-app commands:${NC}" + echo " status - Show partition info (should show v1 boot, v2 update)" + echo " trigger - Set update flag and reboot" + echo " reboot - Reboot to start update" + elif [ $BUILD_UPDATE -eq 1 ]; then + echo -e "${GREEN}[2/4] Building update.srec (with v2 image + trigger)...${NC}" + echo -e "${CYAN}NOTE: Auto-trigger starts update on first boot${NC}" + make clean + make factory.srec + + # Build v2 signed image + # Save v1 since sign tool overwrites with default name pattern + 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 + # Sign tool outputs to image_v2_signed.bin directly + + # Create trigger magic: 'p' (IMG_STATE_UPDATING = 0x70) + "BOOT" + echo -e "${CYAN}Creating update trigger magic...${NC}" + printf 'pBOOT' > trigger_magic.bin + + # Calculate trigger offset: update partition end - 5 + # Update end = WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE + # 0x25000 + 0x19000 = 0x3E000, minus 5 = 0x3DFFB + TRIGGER_OFFSET=$((WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 5)) + TRIGGER_OFFSET_HEX=$(printf "0x%X" $TRIGGER_OFFSET) + + echo -e "${CYAN}Assembling update.bin...${NC}" + echo " wolfboot.bin @ 0x0" + echo " image_v1_signed.bin @ ${WOLFBOOT_PARTITION_BOOT_ADDRESS} (boot partition)" + echo " image_v2_signed.bin @ ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} (update partition)" + echo " trigger_magic.bin @ ${TRIGGER_OFFSET_HEX} (update trailer)" + + ./tools/bin-assemble/bin-assemble \ + update.bin \ + 0x0 wolfboot.bin \ + ${WOLFBOOT_PARTITION_BOOT_ADDRESS} test-app/image_v1_signed_backup.bin \ + ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} test-app/image_v2_signed.bin \ + ${TRIGGER_OFFSET_HEX} trigger_magic.bin + + # Convert to srec + echo -e "${CYAN}Converting to update.srec...${NC}" + arm-none-eabi-objcopy -I binary -O srec --srec-forceS3 update.bin update.srec + + # Cleanup temp files + rm -f trigger_magic.bin + mv test-app/image_v1_signed_backup.bin test-app/image_v1_signed.bin 2>/dev/null || true + + echo -e "${GREEN}Build successful: update.srec${NC}" + else + echo -e "${GREEN}[2/4] Building factory.srec...${NC}" + make clean + make factory.srec + fi + + if [ ! -f "${SREC_FILE}" ]; then + echo -e "${RED}Error: Build failed - ${SREC_FILE} not found${NC}" + exit 1 + fi + echo -e "${GREEN}Build successful: ${SREC_FILE}${NC}" +else + echo "" + echo -e "${YELLOW}[1/4] Skipping configure (--skip-build)${NC}" + echo -e "${YELLOW}[2/4] Skipping build (--skip-build)${NC}" +fi + +# Step 3: Start UART capture BEFORE flashing +if [ $SKIP_UART -eq 0 ]; then + echo "" + echo -e "${GREEN}[3/4] Starting UART capture on ${UART_DEV} at ${UART_BAUD} baud...${NC}" + + # Check if UART device exists + if [ ! -c "${UART_DEV}" ]; then + echo -e "${RED}Error: UART device ${UART_DEV} not found${NC}" + echo "Available ttyACM devices:" + ls -la /dev/ttyACM* 2>/dev/null || echo " (none found)" + exit 1 + fi + + # Configure UART + stty -F "${UART_DEV}" "${UART_BAUD}" raw -echo -hupcl + + # Start background UART capture that outputs to terminal + cat "${UART_DEV}" & + UART_PID=$! + echo -e "${CYAN}UART capture started (PID: ${UART_PID})${NC}" +else + echo "" + echo -e "${YELLOW}[3/4] Skipping UART capture (--skip-uart)${NC}" +fi + +# Step 4: Flash +if [ $SKIP_FLASH -eq 0 ]; then + echo "" + echo -e "${GREEN}[4/4] Flashing to S32K142EVB...${NC}" + + if [ ! -f "${SREC_FILE}" ]; then + echo -e "${RED}Error: ${SREC_FILE} not found. Run without --skip-build first.${NC}" + exit 1 + fi + + # Wait for mount point if not available + WAIT_COUNT=0 + while [ ! -d "${MOUNT_PATH}" ]; do + if [ $WAIT_COUNT -eq 0 ]; then + echo -e "${YELLOW}Waiting for ${MOUNT_PATH} to be mounted...${NC}" + echo "(Connect the S32K142EVB board via USB)" + fi + sleep 1 + WAIT_COUNT=$((WAIT_COUNT + 1)) + if [ $WAIT_COUNT -gt 30 ]; then + echo -e "${RED}Error: Timeout waiting for ${MOUNT_PATH}${NC}" + exit 1 + fi + done + + echo "Copying ${SREC_FILE} to ${MOUNT_PATH}..." + cp "${SREC_FILE}" "${MOUNT_PATH}/" + sync + echo -e "${GREEN}Flash complete! Waiting for boot output...${NC}" +else + echo "" + echo -e "${YELLOW}[4/4] Skipping flash (--skip-flash)${NC}" +fi + +# UART monitoring +if [ $SKIP_UART -eq 0 ]; then + echo "" + if [ $INTERACTIVE -eq 1 ]; then + echo -e "${YELLOW}=== UART Output (Press Ctrl+C to exit) ===${NC}" + wait "$UART_PID" 2>/dev/null || true + else + echo -e "${YELLOW}=== UART Output (capturing for ${UART_TIMEOUT} seconds) ===${NC}" + sleep "$UART_TIMEOUT" + + # Kill the background cat process + cleanup + UART_PID="" + fi +fi + +echo "" +echo -e "${GREEN}=== Complete ===${NC}" From d6b4d95e5841c96cf3aaa53135a1d482d6b3bd33 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 14 Jan 2026 09:45:58 -0800 Subject: [PATCH 2/4] Added `WOLFBOOT_REPRODUCIBLE_BUILD`. --- hal/mpfs250.c | 6 ++++++ hal/s32k1xx.c | 6 +++++- tools/scripts/nxp-s32k142-flash.sh | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 2137cebf7f..7e68f36143 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -52,8 +52,14 @@ void hal_init(void) { +#if defined(DEBUG_UART) && defined(__WOLFBOOT) +#ifdef WOLFBOOT_REPRODUCIBLE_BUILD + wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING); +#else wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); +#endif +#endif } /* ============================================================================ diff --git a/hal/s32k1xx.c b/hal/s32k1xx.c index 6494012db4..576438c1b0 100644 --- a/hal/s32k1xx.c +++ b/hal/s32k1xx.c @@ -456,8 +456,12 @@ void hal_init(void) uart_init(); #ifdef __WOLFBOOT +#ifdef WOLFBOOT_REPRODUCIBLE_BUILD + wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING); +#else wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", - LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__); + LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); +#endif #endif #endif diff --git a/tools/scripts/nxp-s32k142-flash.sh b/tools/scripts/nxp-s32k142-flash.sh index 7c82af3d7e..0b49843a44 100755 --- a/tools/scripts/nxp-s32k142-flash.sh +++ b/tools/scripts/nxp-s32k142-flash.sh @@ -15,7 +15,7 @@ set -e # Configuration CONFIG_FILE="./config/examples/nxp-s32k142.config" MOUNT_PATH="/media/davidgarske/S32K142EVB" -UART_DEV="/dev/ttyACM1" +UART_DEV="/dev/ttyACM0" UART_BAUD=115200 SREC_FILE="factory.srec" UART_TIMEOUT=5 # Default 5 seconds capture From 29fbad7db8e3b03f46ab26d304aa43877386df37 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 14 Jan 2026 14:33:52 -0800 Subject: [PATCH 3/4] 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 ec39a350ff..2adf8dfe2f 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:** - -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: +### NXP S32K1XX: TODO -- [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 576438c1b0..ce6d7d4b01 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 76ed3aff1f..ed85f8fa03 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 f576d922df..2ff38eb7d2 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 0b49843a44..0744cbe441 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" From f62e07a60cf6c8ba42ae99c83ee11a903ba9a846 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 14 Jan 2026 15:07:40 -0800 Subject: [PATCH 4/4] Simplify the NXP S32 flash script --- tools/scripts/nxp-s32k142-flash.sh | 331 +++++++++++------------------ 1 file changed, 129 insertions(+), 202 deletions(-) diff --git a/tools/scripts/nxp-s32k142-flash.sh b/tools/scripts/nxp-s32k142-flash.sh index 0744cbe441..2460c15be7 100755 --- a/tools/scripts/nxp-s32k142-flash.sh +++ b/tools/scripts/nxp-s32k142-flash.sh @@ -1,29 +1,47 @@ #!/bin/bash # -# NXP S32K142 Flash and UART Monitor Script +# NXP S32K142 Flash Script # # This script automates: # 1. Configure for NXP S32K142 -# 2. Build factory.srec (or update.srec for testing updates) -# 3. Start UART capture (before flash to catch boot messages) -# 4. Flash via USB mass storage -# 5. Capture UART output for specified duration +# 2. Build factory.srec (or test-update.srec / test-selfupdate.srec) +# 3. Flash via USB mass storage # set -e -# Configuration -CONFIG_FILE="./config/examples/nxp-s32k142.config" -MOUNT_PATH="/media/davidgarske/S32K142EVB" -UART_DEV="/dev/ttyACM0" -UART_BAUD=115200 -SREC_FILE="factory.srec" -UART_TIMEOUT=5 # Default 5 seconds capture +# Configuration (can be overridden via environment variables or command line) +CONFIG_FILE="${CONFIG_FILE:-.config}" +SREC_FILE="${SREC_FILE:-factory.srec}" + +# Function to detect USB mass storage mount point portably +detect_mount_path() { + # Common mount locations for USB mass storage devices + local mount_dirs=( + "/media"/*/S32K142EVB + "/media"/*/S32K142* + "/mnt"/*/S32K142EVB + "/mnt"/*/S32K142* + "/run/media"/*/S32K142EVB + "/run/media"/*/S32K142* + ) + + # Try to find existing mount + for path in "${mount_dirs[@]}"; do + if [ -d "$path" ] && [ -w "$path" ]; then + echo "$path" + return 0 + fi + done -# Memory layout (from nxp-s32k142.config) -WOLFBOOT_PARTITION_BOOT_ADDRESS=0xC000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x25000 -WOLFBOOT_PARTITION_SIZE=0x19000 + # If not found, return empty (will be set via --mount or wait for mount) + return 1 +} + +# Set default mount path (can be overridden via MOUNT_PATH env var or --mount flag) +if [ -z "${MOUNT_PATH}" ]; then + MOUNT_PATH=$(detect_mount_path 2>/dev/null || echo "") +fi # Colors for output RED='\033[0;31m' @@ -35,10 +53,6 @@ NC='\033[0m' # No Color # Parse command line arguments SKIP_BUILD=0 SKIP_FLASH=0 -SKIP_UART=0 -UART_ONLY=0 -INTERACTIVE=0 -BUILD_UPDATE=0 TEST_UPDATE=0 TEST_SELFUPDATE=0 @@ -46,29 +60,20 @@ 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 " --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 " --test-update Build test-update.srec with v2 image in update partition (use 'trigger' command to start update)" + echo " --test-selfupdate Build test-selfupdate.srec with bootloader v1 + v2 bootloader update (use 'trigger' command to start update)" 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)" - echo " --interactive Keep UART open until Ctrl+C (default: auto-exit after timeout)" - echo " --timeout SECS UART capture duration in seconds (default: $UART_TIMEOUT)" - echo " --mount PATH Override mount path (default: $MOUNT_PATH)" - echo " --uart DEV Override UART device (default: $UART_DEV)" - echo " --baud RATE Override baud rate (default: $UART_BAUD)" - echo " -h, --help Show this help message" + echo " --skip-flash Skip flashing (just build)" + echo " -h, --help Show this help message" + echo "" + echo "Environment variables:" + echo " MOUNT_PATH Override mount path (default: auto-detect)" echo "" 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" + echo " $0 --skip-flash # Build without flashing" exit 0 } @@ -79,11 +84,6 @@ while [[ $# -gt 0 ]]; do SREC_FILE="test-update.srec" shift ;; - --update) - BUILD_UPDATE=1 - SREC_FILE="update.srec" - shift - ;; --test-selfupdate) TEST_SELFUPDATE=1 SREC_FILE="test-selfupdate.srec" @@ -97,35 +97,10 @@ while [[ $# -gt 0 ]]; do SKIP_FLASH=1 shift ;; - --skip-uart) - SKIP_UART=1 - shift - ;; - --uart-only) - SKIP_BUILD=1 - SKIP_FLASH=1 - shift - ;; - --interactive) - INTERACTIVE=1 - shift - ;; - --timeout) - UART_TIMEOUT="$2" - shift 2 - ;; --mount) MOUNT_PATH="$2" shift 2 ;; - --uart) - UART_DEV="$2" - shift 2 - ;; - --baud) - UART_BAUD="$2" - shift 2 - ;; -h|--help) usage ;; @@ -136,49 +111,57 @@ while [[ $# -gt 0 ]]; do esac done -# Cleanup function to kill background UART capture -UART_PID="" -cleanup() { - if [ -n "$UART_PID" ] && kill -0 "$UART_PID" 2>/dev/null; then - kill "$UART_PID" 2>/dev/null || true - wait "$UART_PID" 2>/dev/null || true - fi -} -trap cleanup EXIT - -echo -e "${GREEN}=== NXP S32K142 Flash and Monitor Script ===${NC}" +echo -e "${GREEN}=== NXP S32K142 Flash Script ===${NC}" -# Function to parse SIGN and HASH from .config file -parse_config_signing() { +# Function to parse all needed values from .config file +parse_config() { 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}" + # Helper function to extract config value + get_config_value() { + local key="$1" + grep -E "^${key}" "$config_file" | head -1 | sed -E "s/^${key}\??=//" | tr -d '[:space:]' + } + + # Extract SIGN and HASH + SIGN_VALUE=$(get_config_value "SIGN") + HASH_VALUE=$(get_config_value "HASH") + + # Extract partition layout + WOLFBOOT_PARTITION_BOOT_ADDRESS=$(get_config_value "WOLFBOOT_PARTITION_BOOT_ADDRESS") + WOLFBOOT_PARTITION_UPDATE_ADDRESS=$(get_config_value "WOLFBOOT_PARTITION_UPDATE_ADDRESS") + WOLFBOOT_PARTITION_SIZE=$(get_config_value "WOLFBOOT_PARTITION_SIZE") + + # Validate required fields + local missing="" + [ -z "$SIGN_VALUE" ] && missing="${missing}SIGN " + [ -z "$HASH_VALUE" ] && missing="${missing}HASH " + [ -z "$WOLFBOOT_PARTITION_BOOT_ADDRESS" ] && missing="${missing}WOLFBOOT_PARTITION_BOOT_ADDRESS " + [ -z "$WOLFBOOT_PARTITION_UPDATE_ADDRESS" ] && missing="${missing}WOLFBOOT_PARTITION_UPDATE_ADDRESS " + [ -z "$WOLFBOOT_PARTITION_SIZE" ] && missing="${missing}WOLFBOOT_PARTITION_SIZE " + + if [ -n "$missing" ]; then + echo -e "${RED}Error: Missing required config values: ${missing}${NC}" exit 1 fi - # Convert SIGN to lowercase flag format - SIGN_FLAG=$(echo "$SIGN_VALUE" | tr '[:upper:]' '[:lower:]') - SIGN_FLAG="--${SIGN_FLAG}" + # Convert SIGN/HASH to lowercase flag format + SIGN_FLAG="--$(echo "$SIGN_VALUE" | tr '[:upper:]' '[:lower:]')" + HASH_FLAG="--$(echo "$HASH_VALUE" | tr '[:upper:]' '[:lower:]')" - # Convert HASH to lowercase flag format - HASH_FLAG=$(echo "$HASH_VALUE" | tr '[:upper:]' '[:lower:]') - HASH_FLAG="--${HASH_FLAG}" + # Ensure partition addresses have 0x prefix for bash arithmetic + for var in WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS WOLFBOOT_PARTITION_SIZE; do + eval "val=\$$var" + if [[ ! "$val" =~ ^0x ]]; then + eval "$var=\"0x\${val}\"" + fi + done - echo -e "${CYAN}Using signing: ${SIGN_VALUE} / ${HASH_VALUE}${NC}" + echo -e "${CYAN}Config: SIGN=${SIGN_VALUE} HASH=${HASH_VALUE} BOOT=${WOLFBOOT_PARTITION_BOOT_ADDRESS} UPDATE=${WOLFBOOT_PARTITION_UPDATE_ADDRESS} SIZE=${WOLFBOOT_PARTITION_SIZE}${NC}" } # Change to wolfboot root directory @@ -190,21 +173,26 @@ echo -e "${YELLOW}Working directory: ${WOLFBOOT_ROOT}${NC}" # Step 1: Configure if [ $SKIP_BUILD -eq 0 ]; then echo "" - echo -e "${GREEN}[1/4] Configuring for NXP S32K142...${NC}" + echo -e "${GREEN}[1/3] Configuring for NXP S32K142...${NC}" if [ ! -f "${CONFIG_FILE}" ]; then echo -e "${RED}Error: Config file not found: ${CONFIG_FILE}${NC}" exit 1 fi - cp "${CONFIG_FILE}" .config - echo "Copied ${CONFIG_FILE} to .config" + # Only copy if source and destination are different + if [ "${CONFIG_FILE}" != ".config" ]; then + cp "${CONFIG_FILE}" .config + echo "Copied ${CONFIG_FILE} to .config" + else + echo "Using existing .config" + fi - # Parse signing configuration from .config - parse_config_signing .config + # Parse all configuration values from .config + parse_config .config # Step 2: Build echo "" if [ $TEST_SELFUPDATE -eq 1 ]; then - echo -e "${GREEN}[2/4] Building test-selfupdate.srec (bootloader v1 + v2 bootloader update)...${NC}" + echo -e "${GREEN}[2/3] 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 @@ -305,7 +293,7 @@ if [ $SKIP_BUILD -eq 0 ]; then 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}" + echo -e "${GREEN}[2/3] Building test-update.srec (v1 boot + v2 update, no trigger)...${NC}" make clean make factory.srec @@ -341,54 +329,8 @@ if [ $SKIP_BUILD -eq 0 ]; then echo " status - Show partition info (should show v1 boot, v2 update)" echo " trigger - Set update flag and reboot" echo " reboot - Reboot to start update" - elif [ $BUILD_UPDATE -eq 1 ]; then - echo -e "${GREEN}[2/4] Building update.srec (with v2 image + trigger)...${NC}" - echo -e "${CYAN}NOTE: Auto-trigger starts update on first boot${NC}" - make clean - make factory.srec - - # Build v2 signed image - # Save v1 since sign tool overwrites with default name pattern - 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 ${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" - echo -e "${CYAN}Creating update trigger magic...${NC}" - printf 'pBOOT' > trigger_magic.bin - - # Calculate trigger offset: update partition end - 5 - # Update end = WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - # 0x25000 + 0x19000 = 0x3E000, minus 5 = 0x3DFFB - TRIGGER_OFFSET=$((WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 5)) - TRIGGER_OFFSET_HEX=$(printf "0x%X" $TRIGGER_OFFSET) - - echo -e "${CYAN}Assembling update.bin...${NC}" - echo " wolfboot.bin @ 0x0" - echo " image_v1_signed.bin @ ${WOLFBOOT_PARTITION_BOOT_ADDRESS} (boot partition)" - echo " image_v2_signed.bin @ ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} (update partition)" - echo " trigger_magic.bin @ ${TRIGGER_OFFSET_HEX} (update trailer)" - - ./tools/bin-assemble/bin-assemble \ - update.bin \ - 0x0 wolfboot.bin \ - ${WOLFBOOT_PARTITION_BOOT_ADDRESS} test-app/image_v1_signed_backup.bin \ - ${WOLFBOOT_PARTITION_UPDATE_ADDRESS} test-app/image_v2_signed.bin \ - ${TRIGGER_OFFSET_HEX} trigger_magic.bin - - # Convert to srec - echo -e "${CYAN}Converting to update.srec...${NC}" - arm-none-eabi-objcopy -I binary -O srec --srec-forceS3 update.bin update.srec - - # Cleanup temp files - rm -f trigger_magic.bin - mv test-app/image_v1_signed_backup.bin test-app/image_v1_signed.bin 2>/dev/null || true - - echo -e "${GREEN}Build successful: update.srec${NC}" else - echo -e "${GREEN}[2/4] Building factory.srec...${NC}" + echo -e "${GREEN}[2/3] Building factory.srec...${NC}" make clean make factory.srec fi @@ -400,83 +342,68 @@ if [ $SKIP_BUILD -eq 0 ]; then echo -e "${GREEN}Build successful: ${SREC_FILE}${NC}" else echo "" - echo -e "${YELLOW}[1/4] Skipping configure (--skip-build)${NC}" - echo -e "${YELLOW}[2/4] Skipping build (--skip-build)${NC}" + echo -e "${YELLOW}[1/3] Skipping configure (--skip-build)${NC}" + echo -e "${YELLOW}[2/3] Skipping build (--skip-build)${NC}" fi -# Step 3: Start UART capture BEFORE flashing -if [ $SKIP_UART -eq 0 ]; then - echo "" - echo -e "${GREEN}[3/4] Starting UART capture on ${UART_DEV} at ${UART_BAUD} baud...${NC}" - - # Check if UART device exists - if [ ! -c "${UART_DEV}" ]; then - echo -e "${RED}Error: UART device ${UART_DEV} not found${NC}" - echo "Available ttyACM devices:" - ls -la /dev/ttyACM* 2>/dev/null || echo " (none found)" - exit 1 - fi - - # Configure UART - stty -F "${UART_DEV}" "${UART_BAUD}" raw -echo -hupcl - - # Start background UART capture that outputs to terminal - cat "${UART_DEV}" & - UART_PID=$! - echo -e "${CYAN}UART capture started (PID: ${UART_PID})${NC}" -else - echo "" - echo -e "${YELLOW}[3/4] Skipping UART capture (--skip-uart)${NC}" -fi - -# Step 4: Flash +# Step 3: Flash if [ $SKIP_FLASH -eq 0 ]; then echo "" - echo -e "${GREEN}[4/4] Flashing to S32K142EVB...${NC}" + echo -e "${GREEN}[3/3] Flashing to S32K142EVB...${NC}" if [ ! -f "${SREC_FILE}" ]; then echo -e "${RED}Error: ${SREC_FILE} not found. Run without --skip-build first.${NC}" exit 1 fi + # Auto-detect mount path if not set + if [ -z "${MOUNT_PATH}" ]; then + echo -e "${CYAN}Auto-detecting mount path...${NC}" + MOUNT_PATH=$(detect_mount_path 2>/dev/null || echo "") + fi + # Wait for mount point if not available WAIT_COUNT=0 - while [ ! -d "${MOUNT_PATH}" ]; do + while [ -z "${MOUNT_PATH}" ] || [ ! -d "${MOUNT_PATH}" ]; do if [ $WAIT_COUNT -eq 0 ]; then - echo -e "${YELLOW}Waiting for ${MOUNT_PATH} to be mounted...${NC}" + if [ -z "${MOUNT_PATH}" ]; then + echo -e "${YELLOW}Waiting for S32K142EVB to be mounted...${NC}" + else + echo -e "${YELLOW}Waiting for ${MOUNT_PATH} to be mounted...${NC}" + fi echo "(Connect the S32K142EVB board via USB)" + echo " Or specify mount path with: --mount PATH" + echo " Or set environment variable: MOUNT_PATH=/path/to/mount" fi sleep 1 WAIT_COUNT=$((WAIT_COUNT + 1)) + + # Try to detect mount path again + if [ -z "${MOUNT_PATH}" ]; then + MOUNT_PATH=$(detect_mount_path 2>/dev/null || echo "") + fi + if [ $WAIT_COUNT -gt 30 ]; then - echo -e "${RED}Error: Timeout waiting for ${MOUNT_PATH}${NC}" + echo -e "${RED}Error: Timeout waiting for mount point${NC}" + if [ -z "${MOUNT_PATH}" ]; then + echo " Please specify mount path with: --mount PATH" + echo " Or set environment variable: MOUNT_PATH=/path/to/mount" + else + echo " Expected mount at: ${MOUNT_PATH}" + fi exit 1 fi done + echo -e "${CYAN}Using mount path: ${MOUNT_PATH}${NC}" + echo "Copying ${SREC_FILE} to ${MOUNT_PATH}..." cp "${SREC_FILE}" "${MOUNT_PATH}/" sync - echo -e "${GREEN}Flash complete! Waiting for boot output...${NC}" + echo -e "${GREEN}Flash complete!${NC}" else echo "" - echo -e "${YELLOW}[4/4] Skipping flash (--skip-flash)${NC}" -fi - -# UART monitoring -if [ $SKIP_UART -eq 0 ]; then - echo "" - if [ $INTERACTIVE -eq 1 ]; then - echo -e "${YELLOW}=== UART Output (Press Ctrl+C to exit) ===${NC}" - wait "$UART_PID" 2>/dev/null || true - else - echo -e "${YELLOW}=== UART Output (capturing for ${UART_TIMEOUT} seconds) ===${NC}" - sleep "$UART_TIMEOUT" - - # Kill the background cat process - cleanup - UART_PID="" - fi + echo -e "${YELLOW}[3/3] Skipping flash (--skip-flash)${NC}" fi echo ""