diff --git a/.github/workflows/test-build-renesas-rx.yml b/.github/workflows/test-build-renesas-rx.yml new file mode 100644 index 0000000000..93fc9041ed --- /dev/null +++ b/.github/workflows/test-build-renesas-rx.yml @@ -0,0 +1,53 @@ +name: Wolfboot Reusable Build Workflow (Renesas RX) + +# Builds a Renesas RX example config with the GNU RX (GNURX) toolchain. +# GNURX is login-gated (no public URL), so it is provided via a prebuilt +# container image, as done for PowerPC. To enable: publish +# ghcr.io/wolfssl/wolfboot-ci-rx (GNURX rx-elf-* on PATH) and set repo +# variable ENABLE_RENESAS_RX_CI=true (see test-configs.yml). + +on: + + workflow_call: + inputs: + arch: + required: true + type: string + config-file: + required: true + type: string + make-args: + required: false + type: string + +jobs: + + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/wolfssl/wolfboot-ci-rx:v1 + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Trust workspace + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: make clean + run: | + make distclean + + - name: Select config + run: | + cp ${{inputs.config-file}} .config + + - name: Build tools + run: | + make -C tools/keytools && make -C tools/bin-assemble + + - name: Build wolfboot + run: | + make USE_GCC=0 CROSS_COMPILE=rx-elf- ${{inputs.make-args}} diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 411299218a..d388c610b8 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -862,3 +862,27 @@ jobs: arch: arm config-file: ./config/examples/rp2350.config target: rp2350 + + # Renesas RX builds (GNURX). Gated on ENABLE_RENESAS_RX_CI until the + # wolfboot-ci-rx image is published (GNURX is login-gated). + renesas_rx65n_test: + if: ${{ vars.ENABLE_RENESAS_RX_CI == 'true' }} + uses: ./.github/workflows/test-build-renesas-rx.yml + with: + arch: rx + config-file: ./config/examples/renesas-rx65n.config + + renesas_rx65n_bigendian_test: + if: ${{ vars.ENABLE_RENESAS_RX_CI == 'true' }} + uses: ./.github/workflows/test-build-renesas-rx.yml + with: + arch: rx + config-file: ./config/examples/renesas-rx65n.config + make-args: BIG_ENDIAN=1 DEBUG_SYMBOLS=1 + + renesas_rx72n_test: + if: ${{ vars.ENABLE_RENESAS_RX_CI == 'true' }} + uses: ./.github/workflows/test-build-renesas-rx.yml + with: + arch: rx + config-file: ./config/examples/renesas-rx72n.config diff --git a/arch.mk b/arch.mk index a507f73226..354e210775 100644 --- a/arch.mk +++ b/arch.mk @@ -721,8 +721,7 @@ ifeq ($(ARCH),RENESAS_RX) CFLAGS+=-ffunction-sections -fdata-sections CFLAGS+=-B$(dir $(CROSS_COMPILE)) LDFLAGS+=-gc-sections -Map=wolfboot.map - LDFLAGS+=-T $(LSCRIPT) -L$(dir $(CROSS_COMPILE))../lib - LIBS+=-lgcc + LDFLAGS+=-T $(LSCRIPT) endif # Renesas specific files @@ -751,10 +750,20 @@ ifeq ($(ARCH),RENESAS_RX) endif endif + # ld is invoked directly, so add libgcc for the selected multilib (GNU RX + # stores it per-endianness/-nofpu under lib/gcc/..., not in ../lib). + ifeq ($(USE_GCC),0) + LIBS+=$(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + endif + ifeq ($(PKA),1) CFLAGS+=-DWOLFBOOT_RENESAS_TSIP CFLAGS+=-DWOLFBOOT_DEVID_PUBKEY=7890 CFLAGS+=-DWOLFBOOT_DEVID_CRYPT=7891 + # GCC RX 14.2 -Werror=enum-conversion trips on the wolfSSL TSIP port; add + # the relaxation only if the compiler knows the warning (RX 8.3 does not). + CFLAGS+=$(shell echo '' | $(CC) -xc -fsyntax-only \ + -Wno-error=enum-conversion - >/dev/null 2>&1 && echo -Wno-error=enum-conversion) RX_DRIVER_PATH?=./lib OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ diff --git a/config/examples/renesas-rx65n.config b/config/examples/renesas-rx65n.config index 64eb33d14e..348d155fd4 100644 --- a/config/examples/renesas-rx65n.config +++ b/config/examples/renesas-rx65n.config @@ -54,3 +54,7 @@ PKA?=0 # Location of reset entry point from start of flash #CFLAGS_EXTRA+=-DBOOT_ENTRY_OFFSET=0x2C + +# External watchdog (e.g. MAX6316-MAX6322): toggle WDI from wolfBoot's long +# loops. WATCHDOG_WDI_PORT = RX port, WATCHDOG_WDI_PIN = bit (see renesas-rx.c). +#CFLAGS_EXTRA+=-DWATCHDOG -DWATCHDOG_WDI_PORT=0 -DWATCHDOG_WDI_PIN=5 diff --git a/config/examples/renesas-rx72n.config b/config/examples/renesas-rx72n.config index 971fedc29e..8ad665ae82 100644 --- a/config/examples/renesas-rx72n.config +++ b/config/examples/renesas-rx72n.config @@ -54,3 +54,7 @@ PKA?=0 # Location of reset entry point from start of flash #CFLAGS_EXTRA+=-DBOOT_ENTRY_OFFSET=0x2C + +# External watchdog (e.g. MAX6316-MAX6322): toggle WDI from wolfBoot's long +# loops. WATCHDOG_WDI_PORT = RX port, WATCHDOG_WDI_PIN = bit (see renesas-rx.c). +#CFLAGS_EXTRA+=-DWATCHDOG -DWATCHDOG_WDI_PORT=0 -DWATCHDOG_WDI_PIN=5 diff --git a/docs/Renesas.md b/docs/Renesas.md index 944eefa0d6..6f4ad2e0af 100644 --- a/docs/Renesas.md +++ b/docs/Renesas.md @@ -203,6 +203,17 @@ The key needed for the firmware signing tool is the 32 byte AES Key + 16 byte IV | RX65N | 120MHz | ECDSA Verify P256 | 2.95 ms | 1208 ms | 602 ms | 517 ms | +## RX External Watchdog (MAX6316-MAX6322) + +An external windowed watchdog resets the MCU unless its `WDI` input sees an edge each timeout period, which image verification or a swap can exceed. Build with `WATCHDOG` and point it at the GPIO wired to `WDI`: + +``` +CFLAGS_EXTRA+=-DWATCHDOG -DWATCHDOG_WDI_PORT=0 -DWATCHDOG_WDI_PIN=5 +``` + +`WATCHDOG_WDI_PORT` is the RX port number and `WATCHDOG_WDI_PIN` the bit (0-7). wolfBoot calls `wolfBoot_watchdog_feed()` from its hash and flash copy/erase loops; the RX HAL toggles `WDI` to restart the timer. The application must keep servicing `WDI` after boot. `wolfBoot_watchdog_feed()` is a weak no-op by default (`include/hal.h`), so any port can override it for a different watchdog. + + ## RX Production Protection (recommendations) 1) Lockdown external serial programmer `SPCC.SPE = 0` diff --git a/docs/Targets.md b/docs/Targets.md index 35324a7163..ae9eb27b8e 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -6915,7 +6915,7 @@ The following build options are available for the S32K1xx HAL: | `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` | Enable the watchdog during wolfBoot operation. wolfBoot calls `wolfBoot_watchdog_feed()` from its hash and copy/erase loops -- a weak no-op a port overrides to service its watchdog (e.g. external MAX6316-MAX6322, see `hal/renesas-rx.c`). | | `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. | diff --git a/hal/renesas-rx.c b/hal/renesas-rx.c index 7902d0b7b8..e92c75c228 100644 --- a/hal/renesas-rx.c +++ b/hal/renesas-rx.c @@ -464,6 +464,31 @@ int hal_renesas_init(void) #endif /* TSIP */ +#ifdef WATCHDOG +/* External watchdog (e.g. MAX6316-MAX6322): toggle the WDI GPIO so a signal + * edge restarts its timer. Pin set by WATCHDOG_WDI_PORT/WATCHDOG_WDI_PIN. */ +#ifndef WATCHDOG_WDI_PORT +#define WATCHDOG_WDI_PORT 0 /* PORT0 */ +#endif +#ifndef WATCHDOG_WDI_PIN +#define WATCHDOG_WDI_PIN 0 +#endif + +static void hal_watchdog_init(void) +{ + /* Drive WDI as a general-purpose CMOS output */ + PORT_PMR(WATCHDOG_WDI_PORT) &= (uint8_t)~(1U << WATCHDOG_WDI_PIN); + PORT_PDR(WATCHDOG_WDI_PORT) |= (uint8_t) (1U << WATCHDOG_WDI_PIN); +} + +/* RAMFUNCTION: callable from the RAM-resident flash paths. */ +void RAMFUNCTION wolfBoot_watchdog_feed(void) +{ + /* Toggle WDI: any transition restarts the external watchdog timer */ + PORT_PODR(WATCHDOG_WDI_PORT) ^= (uint8_t)(1U << WATCHDOG_WDI_PIN); +} +#endif /* WATCHDOG */ + void hal_init(void) { #if defined(WOLFBOOT_RENESAS_TSIP) && !defined(WOLFBOOT_RENESAS_APP) @@ -486,6 +511,10 @@ void hal_init(void) hal_flash_init(); +#ifdef WATCHDOG + hal_watchdog_init(); +#endif + #if defined(WOLFBOOT_RENESAS_TSIP) && !defined(WOLFBOOT_RENESAS_APP) err = hal_renesas_init(); if (err != 0) { diff --git a/hal/rx65n.ld b/hal/rx65n.ld index 74b6599ec2..99a8c1dcc2 100644 --- a/hal/rx65n.ld +++ b/hal/rx65n.ld @@ -134,7 +134,9 @@ SECTIONS _edata = .; } > RAM - .bss : + /* Pin .bss LMA to its VMA; else it chains past ROM at the top of the + * address space and newer ld errors "LMA wraps around address space". */ + .bss (NOLOAD) : AT (ADDR (.bss)) { _bss = .; *(.dynbss) diff --git a/hal/rx72n.ld b/hal/rx72n.ld index 6e632c01a8..65a009b01d 100644 --- a/hal/rx72n.ld +++ b/hal/rx72n.ld @@ -133,7 +133,9 @@ SECTIONS _edata = .; } > RAM - .bss : + /* Pin .bss LMA to its VMA; else it chains past ROM at the top of the + * address space and newer ld errors "LMA wraps around address space". */ + .bss (NOLOAD) : AT (ADDR (.bss)) { _bss = .; *(.dynbss) diff --git a/include/hal.h b/include/hal.h index 701e400144..621ca68e63 100644 --- a/include/hal.h +++ b/include/hal.h @@ -119,6 +119,15 @@ void hal_prepare_boot(void); const char* hal_fit_config_name(void); #endif +/* Optional watchdog kick. With -DWATCHDOG, wolfBoot calls this from its long + * hash and flash copy/erase loops; a port overrides the weak no-op default + * (see libwolfboot.c, hal/renesas-rx.c). Compiles out when WATCHDOG is unset. */ +#ifdef WATCHDOG +void wolfBoot_watchdog_feed(void); +#else +#define wolfBoot_watchdog_feed() do {} while (0) +#endif + /* FPGA load mode constants + hal_fpga_load() prototype (kept in a standalone * header so the per-target HAL .c files can include just this, not all of * hal.h). Gated internally by WOLFBOOT_FPGA_BITSTREAM. */ diff --git a/include/user_settings.h b/include/user_settings.h index 5c59ae3ed1..acd0b57e2b 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -198,9 +198,9 @@ extern int tolower(int c); # if !defined(WOLFBOOT_TPM) # define NO_ECC_SIGN # define NO_ECC_DHE - /* For Renesas RX do not enable the misc.c constant time code - * due to issue with 64-bit types */ -# if defined(__RX__) + /* Old GNU RX 4.x miscompiled misc.c 64-bit constant-time ops; + * newer RX needs them (sp_int.c uses ctMaskLT). */ +# if defined(__RX__) && defined(__GNUC__) && (__GNUC__ < 5) # define WOLFSSL_NO_CT_OPS /* don't use constant time ops in misc.c */ # endif # if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ diff --git a/options.mk b/options.mk index c3d6b75bd6..c5b621a5fc 100644 --- a/options.mk +++ b/options.mk @@ -933,7 +933,8 @@ ifeq ($(DEBUG_SYMBOLS),1) CFLAGS+=-ggdb3 else ifneq ($(ARCH),AURIX_TC3) ifneq ($(USE_CLANG),1) - CFLAGS+=-gstabs + # -gstabs was removed in GCC 12; -gdwarf-4 works on old and new GCC + CFLAGS+=-gdwarf-4 endif endif endif diff --git a/src/boot_renesas_start.S b/src/boot_renesas_start.S index 1d2788852f..a499bd9e80 100644 --- a/src/boot_renesas_start.S +++ b/src/boot_renesas_start.S @@ -127,13 +127,9 @@ __rx_fini: .global _exit .type _exit, @function -/* call to exit */ +/* main never returns; halt. Don't call newlib __call_exitprocs -- wolfBoot + * does not link libc, so it fails to link on newer binutils. */ _exit: - mov #0, r2 -#ifndef NO_LEADING_UNDERSCORE - mov #___call_exitprocs, r7 -#endif - jsr r7 _loop_here: bra _loop_here diff --git a/src/image.c b/src/image.c index 6e24f3daf9..88f8b180fb 100644 --- a/src/image.c +++ b/src/image.c @@ -1094,6 +1094,7 @@ static int image_sha256(struct wolfBoot_image *img, uint8_t *hash) blksz = img->fw_size - position; wc_Sha256Update(&sha256_ctx, p, blksz); position += blksz; + wolfBoot_watchdog_feed(); } while (position < img->fw_size); } #endif @@ -1205,6 +1206,7 @@ static int image_sha384(struct wolfBoot_image *img, uint8_t *hash) blksz = img->fw_size - position; wc_Sha384Update(&sha384_ctx, p, blksz); position += blksz; + wolfBoot_watchdog_feed(); } while (position < img->fw_size); } #endif @@ -1322,6 +1324,7 @@ static int image_sha3_384(struct wolfBoot_image *img, uint8_t *hash) blksz = img->fw_size - position; wc_Sha3_384_Update(&sha3_ctx, p, blksz); position += blksz; + wolfBoot_watchdog_feed(); } while (position < img->fw_size); } #endif diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 51a4577775..62fa63e55c 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -32,6 +32,15 @@ #include "image.h" #include "printf.h" +#ifdef WATCHDOG +/* Weak no-op default; a port HAL overrides this to service the watchdog. + * RAMFUNCTION so the fallback is safe when called from the RAM-resident flash + * paths (update_flash.c) on RAM_CODE targets. */ +void RAMFUNCTION WEAKFUNCTION wolfBoot_watchdog_feed(void) +{ +} +#endif + #ifdef UNIT_TEST /** * @def unit_dbg diff --git a/src/update_flash.c b/src/update_flash.c index 0bc6711115..0cfeaab62e 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -279,6 +279,9 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src, wolfBoot_printf("Copy sector %d (part %d->%d)\n", sector, src->part, dst->part); + /* Kick the watchdog once per sector copy (no-op unless -DWATCHDOG) */ + wolfBoot_watchdog_feed(); + if (src->part == PART_SWAP) src_sector_offset = 0; if (dst->part == PART_SWAP) @@ -1213,6 +1216,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) ) { wb_flash_erase(&boot, sector * sector_size, sector_size); wb_flash_erase(&update, sector * sector_size, sector_size); + wolfBoot_watchdog_feed(); sector++; } #endif /* WOLFBOOT_FLASH_MULTI_SECTOR_ERASE */ diff --git a/tools/bin-assemble/bin-assemble.c b/tools/bin-assemble/bin-assemble.c index c65585bb7b..02bfede935 100644 --- a/tools/bin-assemble/bin-assemble.c +++ b/tools/bin-assemble/bin-assemble.c @@ -72,7 +72,7 @@ int binentry_address_compare(const void* a, const void* b) } } -#ifdef _WIN32 +#ifdef _MSC_VER /* 'strerror' has been explicitly marked deprecated */ static const char* win_strerror(int err, char* buf, size_t bufsz) { if (strerror_s(buf, bufsz, err) != 0) { @@ -83,7 +83,7 @@ static const char* win_strerror(int err, char* buf, size_t bufsz) { #endif int main(int argc, const char* argv[]) { -#ifdef _WIN32 +#ifdef _MSC_VER char errbuf[128] = { 0 }; errno_t fe; #endif @@ -127,7 +127,7 @@ int main(int argc, const char* argv[]) { entries[i].fname = argv[2 * i + 3]; if (stat(entries[i].fname, &st)) { -#ifdef _WIN32 +#ifdef _MSC_VER fprintf(stderr, "unable to stat %s: %s\n", entries[i].fname, win_strerror(errno, errbuf, sizeof errbuf)); #else @@ -166,7 +166,7 @@ int main(int argc, const char* argv[]) { } // TODO: consider handling stdout "-" -#ifdef _WIN32 +#ifdef _MSC_VER fe = fopen_s(&fo, outname, "wb"); if (fo == NULL || fe != 0) { fprintf(stderr, "opening %s failed %s\n", @@ -184,7 +184,7 @@ int main(int argc, const char* argv[]) { cur_add = entries[0].address; for (i=0; i (uintmax_t)MAX_TLV_LEN) { printf("Error: Certificate chain too large for TLV encoding " - "(%ju > %u)\n", (uintmax_t)chain_file_sz, MAX_TLV_LEN); + "(%" PRIuMAX " > %u)\n", (uintmax_t)chain_file_sz, MAX_TLV_LEN); goto failure; } else { @@ -1584,7 +1584,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if ((file_stat.st_size < 0) || ((uintmax_t)file_stat.st_size > (uintmax_t)UINT32_MAX)) { - printf("Error: Invalid certificate chain file size (%jd)\n", + printf("Error: Invalid certificate chain file size (%" PRIdMAX ")\n", (intmax_t)file_stat.st_size); fclose(f); f = NULL; @@ -2563,7 +2563,7 @@ static int base_diff(const char *f_base, uint8_t *pubkey, uint32_t pubkey_sz, in (cc_stat.st_size >= 0)) { if ((uintmax_t)cc_stat.st_size > (uintmax_t)MAX_TLV_LEN) { printf("Error: Certificate chain too large for TLV encoding " - "(%ju > %u)\n", (uintmax_t)cc_stat.st_size, MAX_TLV_LEN); + "(%" PRIuMAX " > %u)\n", (uintmax_t)cc_stat.st_size, MAX_TLV_LEN); goto cleanup; } cert_chain_sz = (uint32_t)cc_stat.st_size;