From d073ae4ddba9936a3c1c48cc1675fe454c72c771 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Jul 2026 08:40:34 -0700 Subject: [PATCH] watchdog: add generic feed hook and RX driver --- config/examples/renesas-rx65n.config | 4 ++++ config/examples/renesas-rx72n.config | 4 ++++ docs/Renesas.md | 11 +++++++++++ docs/Targets.md | 2 +- hal/renesas-rx.c | 29 ++++++++++++++++++++++++++++ include/hal.h | 9 +++++++++ src/image.c | 3 +++ src/libwolfboot.c | 9 +++++++++ src/update_flash.c | 4 ++++ 9 files changed, 74 insertions(+), 1 deletion(-) diff --git a/config/examples/renesas-rx65n.config b/config/examples/renesas-rx65n.config index 391bfe07..a7cf8e08 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 971fedc2..8ad665ae 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 944eefa0..6f4ad2e0 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 032040ce..09498e63 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -6765,7 +6765,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 1b4dc8be..909b5b53 100644 --- a/hal/renesas-rx.c +++ b/hal/renesas-rx.c @@ -472,6 +472,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) @@ -494,6 +519,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/include/hal.h b/include/hal.h index 701e4001..621ca68e 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/src/image.c b/src/image.c index 6e24f3da..88f8b180 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 51a45777..62fa63e5 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 0bc67111..0cfeaab6 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 */