diff --git a/.github/docker/renesas-rx/.gitignore b/.github/docker/renesas-rx/.gitignore new file mode 100644 index 0000000000..e292fdd73b --- /dev/null +++ b/.github/docker/renesas-rx/.gitignore @@ -0,0 +1,4 @@ +# Login-gated GNURX toolchain, supplied at image build time (see README.md). +# Not redistributable here. +gnurx-linux.tar.gz +gnurx/ diff --git a/.github/docker/renesas-rx/Dockerfile b/.github/docker/renesas-rx/Dockerfile new file mode 100644 index 0000000000..bc5f28c90c --- /dev/null +++ b/.github/docker/renesas-rx/Dockerfile @@ -0,0 +1,34 @@ +# wolfBoot Renesas RX CI image: Ubuntu + GNU RX (GNURX) ELF toolchain. +# +# Used by .github/workflows/test-build-renesas-rx.yml to build the RX example +# configs. The GNURX toolchain is login-gated at the vendor +# (llvm-gcc-renesas.com) with no public URL, so it is supplied at build time as +# gnurx-linux.tar.gz rather than fetched. See README.md to build/refresh/push. +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential git make curl ca-certificates xz-utils \ + && rm -rf /var/lib/apt/lists/* + +# A gzipped tar of the GNURX ELF toolchain tree (contains bin/rx-elf-gcc etc.), +# produced from the login-gated Linux download. See README.md. +ARG GNURX_TARBALL=gnurx-linux.tar.gz +COPY ${GNURX_TARBALL} /tmp/gnurx.tar.gz +RUN mkdir -p /opt/gnurx \ + && tar xf /tmp/gnurx.tar.gz -C /opt/gnurx \ + && rm /tmp/gnurx.tar.gz \ + # Locate the bin/ that holds rx-elf-gcc (tarball nesting varies by release) + # and expose it at a fixed path so PATH is stable across toolchain versions. + && RXBIN="$(dirname "$(find /opt/gnurx -type f -name 'rx-elf-gcc' -print -quit)")" \ + && test -n "$RXBIN" \ + && ln -s "$RXBIN" /opt/rx-elf-bin + +ENV PATH="/opt/rx-elf-bin:${PATH}" + +# Fail the build early if the toolchain is not usable. +RUN rx-elf-gcc --version | head -1 + +# wolfBoot builds RX via the LD-direct path: +# make USE_GCC=0 CROSS_COMPILE=rx-elf- wolfboot.srec diff --git a/.github/docker/renesas-rx/README.md b/.github/docker/renesas-rx/README.md new file mode 100644 index 0000000000..03289b5e24 --- /dev/null +++ b/.github/docker/renesas-rx/README.md @@ -0,0 +1,62 @@ +# wolfBoot Renesas RX CI image + +Builds `ghcr.io/wolfssl/wolfboot-ci-rx`, the container used by +`.github/workflows/test-build-renesas-rx.yml` to build the Renesas RX example +configs (`config/examples/renesas-rx65n.config`, `renesas-rx72n.config`) with a +real GNU RX (GNURX) toolchain. + +The GNURX ELF toolchain is **login-gated** at the vendor (llvm-gcc-renesas.com) +and has no public download URL, so unlike the ARM CI it cannot be fetched inline +in the workflow. It is baked into this image instead. Because the toolchain +cannot be redistributed here, this image is built and pushed by hand whenever the +pinned GNURX version changes. + +## Build and push (or refresh) + +1. Download the GNURX **Linux** package (free account required) from + https://llvm-gcc-renesas.com/downloads/ -- the RX ELF release (`v1.0` ships + `14.2.0.202511`; `8.3.0.202311` also works). + +2. Produce `gnurx-linux.tar.gz` (a gzipped tar of the toolchain tree containing + `bin/rx-elf-gcc`) in this directory. The Linux download is a **native x86_64 + ELF installer** (`-p ` install path, `-y` silent), so it must run on a + **real x86_64 Linux host** -- extracting under qemu/Rosetta emulation (e.g. an + Apple-Silicon Mac) silently fails ("Cleaning up leftover files..." then an + empty prefix). On x86_64 Linux: + ```sh + chmod +x gcc-*-GNURX-ELF.run + ./gcc-*-GNURX-ELF.run -p "$PWD/gnurx" -y + tar czf gnurx-linux.tar.gz -C "$PWD/gnurx" . + ``` + +3. Log in to GHCR with a token that has `write:packages` on the wolfSSL org: + ```sh + gh auth refresh -h github.com -s write:packages,read:packages + gh auth token | docker login ghcr.io -u --password-stdin + ``` + In the browser flow, also approve the GitHub CLI app for the **wolfSSL org** + (SSO/authorize) -- the org restricts third-party OAuth apps. If that route is + blocked, use a classic PAT with `write:packages` instead. + +4. Build and push (tag must match the `image:` tag in + `../../workflows/test-build-renesas-rx.yml`; run on x86_64 Linux): + ```sh + ./docker-build-push.sh v1.0 + ``` + The Dockerfile locates `rx-elf-gcc` inside the tarball automatically, so the + internal nesting of the release does not matter. + +5. Make the package **public** (one-time), matching `wolfboot-ci-arm` / + `wolfboot-ci-powerpc`. There is no REST API for this -- use the UI at + https://github.com/orgs/wolfssl/packages/container/wolfboot-ci-rx/settings + (Danger Zone -> Change visibility -> Public). A fork pull request cannot pull + an `internal` image, so public is required for PR CI. + +## Notes + +- `gnurx-linux.tar.gz` is intentionally not committed (see `.gitignore` here). +- Bumping the toolchain: rebuild with the new download, push a new tag (e.g. + `v1.1`), update the `image:` tag in `test-build-renesas-rx.yml`, and make the + new version public. +- The image only needs `rx-elf-*` on `PATH`; wolfBoot builds RX with + `make USE_GCC=0 CROSS_COMPILE=rx-elf-`. diff --git a/.github/docker/renesas-rx/docker-build-push.sh b/.github/docker/renesas-rx/docker-build-push.sh new file mode 100755 index 0000000000..eaf72bdc37 --- /dev/null +++ b/.github/docker/renesas-rx/docker-build-push.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Build and push the wolfBoot Renesas RX CI container image. +# +# Prerequisites (one-time / per refresh): +# 1. Download the GNURX ELF Linux toolchain (login required, free account) from +# https://llvm-gcc-renesas.com (e.g. gcc-14.2.0.202511-GNURX-ELF for Linux) +# and produce a gzipped tar of the toolchain tree named gnurx-linux.tar.gz +# in this directory. If the download is a .run self-extractor: +# sh gcc-*-GNURX-ELF.run --noexec --keep --target ./gnurx +# tar czf gnurx-linux.tar.gz -C gnurx . +# If it is already a .tar.gz of the toolchain, just rename/copy it here. +# 2. Log in to GHCR with a token that has write:packages for the wolfSSL org: +# echo "$GHCR_TOKEN" | docker login ghcr.io -u --password-stdin +# (or: gh auth refresh -s write:packages && gh auth token | docker login \ +# ghcr.io -u --password-stdin) +# +# Usage: ./docker-build-push.sh [tag] (tag defaults to v1.0; must match the tag +# in ../../workflows/test-build-renesas-rx.yml) +set -eu +IMAGE=ghcr.io/wolfssl/wolfboot-ci-rx +TAG="${1:-v1.0}" + +cd "$(dirname "$0")" +[ -f gnurx-linux.tar.gz ] || { + echo "error: gnurx-linux.tar.gz not found in $(pwd) (see step 1 above)" >&2 + exit 1 +} + +docker buildx build --platform linux/amd64 -t "${IMAGE}:${TAG}" --push . +echo "Pushed ${IMAGE}:${TAG}" +echo "Now enable the jobs: set repo variable ENABLE_RENESAS_RX_CI=true on wolfSSL/wolfBoot." diff --git a/.github/workflows/test-build-renesas-rx.yml b/.github/workflows/test-build-renesas-rx.yml new file mode 100644 index 0000000000..446b601e38 --- /dev/null +++ b/.github/workflows/test-build-renesas-rx.yml @@ -0,0 +1,56 @@ +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 built from .github/docker/renesas-rx/ (as done for PowerPC). +# Refresh the image when bumping the toolchain version: see +# .github/docker/renesas-rx/README.md. + +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.0 + 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 and test-app + # Build the default target: the bootloader plus the signed RX test-app + # image. The app links via the ld-direct path (USE_GCC=0) using the + # in-tree syscall/stdio stubs in test-app/syscalls.c (no newlib libc). + 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..a3e51075ce 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -862,3 +862,24 @@ jobs: arch: arm config-file: ./config/examples/rp2350.config target: rp2350 + + # Renesas RX builds using the ghcr.io/wolfssl/wolfboot-ci-rx image + # (GNURX toolchain; built from .github/docker/renesas-rx/). + renesas_rx65n_test: + uses: ./.github/workflows/test-build-renesas-rx.yml + with: + arch: rx + config-file: ./config/examples/renesas-rx65n.config + + renesas_rx65n_bigendian_test: + 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: + 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..a7cf8e0826 100644 --- a/config/examples/renesas-rx65n.config +++ b/config/examples/renesas-rx65n.config @@ -3,7 +3,7 @@ TARGET?=rx65n SIGN?=ECC256 HASH?=SHA256 DEBUG?=0 -DEBUG_UART?=0 +DEBUG_UART?=1 VTOR?=1 NO_ASM?=0 NO_MPU=1 @@ -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..909b5b5378 100644 --- a/hal/renesas-rx.c +++ b/hal/renesas-rx.c @@ -214,6 +214,14 @@ void uart_write(const char* buf, unsigned int sz) SCI_TDR(DEBUG_UART_SCI) = c; } } +void uart_read(char* buf, unsigned int sz) +{ + uint32_t pos = 0; + while (sz-- > 0) { + while ((SCI_SSR(DEBUG_UART_SCI) & SCI_SSR_RDRF) == 0); + buf[pos++] = (char)SCI_RDR(DEBUG_UART_SCI); + } +} #endif /* DEBUG_UART */ /* LOCO clock is used out of reset */ @@ -464,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) @@ -486,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/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/printf.h b/include/printf.h index 758403f591..6a5661edf9 100644 --- a/include/printf.h +++ b/include/printf.h @@ -39,6 +39,9 @@ void uart_init(void); #endif void uart_write(const char* buf, unsigned int sz); + #if defined(__RX__) + void uart_read(char* buf, unsigned int sz); + #endif /* turn on small printf support in string.c */ #if !defined(PRINTF_ENABLED) && !defined(NO_PRINTF_UART) 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/test-app/Makefile b/test-app/Makefile index c8c6fed54e..f25e39e14e 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -86,13 +86,19 @@ else 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 ifeq ($(ARCH),RENESAS_RX) APP_OBJS:=app_renesas_rx.o ../test-app/libwolfboot.o ../src/boot_renesas.o ../src/boot_renesas_start.o ../hal/renesas-rx.o - LDFLAGS+=-ffreestanding -nostartfiles + # -ffreestanding/-nostartfiles are gcc-driver flags. Only pass them when + # linking via gcc (USE_GCC=1); for USE_GCC=0 rx-elf-ld is invoked directly + # and rejects them (-ffreestanding is already in CFLAGS; ld adds no crt). + ifeq ($(USE_GCC),1) + LDFLAGS+=-ffreestanding -nostartfiles + endif CFLAGS+=-DWOLFBOOT_RENESAS_APP else ifeq ($(WOLFHAL),1) APP_OBJS:=app_wolfhal.o led.o system.o timer.o ../test-app/libwolfboot.o @@ -103,6 +109,13 @@ endif include ../arch.mk +# The Renesas RX app links with ld directly (USE_GCC=0), with no gcc driver to +# pull in newlib. syscalls.o provides a minimal stdio (printf/putchar/snprintf/ +# malloc) on top of uart_write, so the app links without libc. +ifeq ($(ARCH),RENESAS_RX) + APP_OBJS+=syscalls.o +endif + ifeq ($(USE_CLANG),1) APP_NEEDS_STRING:=1 endif @@ -123,8 +136,12 @@ ifeq ($(WOLFCRYPT_TZ_WOLFHSM),1) WOLFCRYPT_TZ_WOLFHSM=1 endif -# Setup default linker flags +# Setup default linker flags (gcc-driver form). Renesas RX links with ld +# directly (USE_GCC=0) and gets its -T/-gc-sections/oformat from arch.mk, so +# these gcc-driver flags (-Wl,/-nostartfiles and a duplicate -T) are skipped. +ifneq ($(ARCH),RENESAS_RX) LDFLAGS+=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map -nostartfiles +endif # Setup default objcopy flags OBJCOPY_FLAGS+=--gap-fill $(FILL_BYTE) diff --git a/test-app/app_renesas_rx.c b/test-app/app_renesas_rx.c index d2173da731..ab8073bc02 100644 --- a/test-app/app_renesas_rx.c +++ b/test-app/app_renesas_rx.c @@ -34,10 +34,26 @@ int write(int fileno, char *buf, int count) { (void)fileno; +#ifdef DEBUG_UART uart_write(buf, count); +#else + (void)buf; +#endif return count; } +/* wait for a single key from the UART (used to pause the demo) */ +int getchar(void) +{ +#ifdef DEBUG_UART + char c = 0; + uart_read(&c, 1); + return (int)(unsigned char)c; +#else + return -1; +#endif +} + static const char* state2str(uint8_t s) { switch (s) { @@ -90,7 +106,9 @@ int main(void) { uint8_t firmware_version = 0; +#ifdef DEBUG_UART uart_init(); +#endif #if !defined(WOLFBOOT_RENESAS_TSIP) printf("| ------------------------------------------------------------------- |\n"); diff --git a/test-app/syscalls.c b/test-app/syscalls.c index 77e0cbe5c3..0a1559d764 100644 --- a/test-app/syscalls.c +++ b/test-app/syscalls.c @@ -125,6 +125,15 @@ void _exit(int status) } } +#if defined(__RX__) +/* GCC emits calls to abort() for trap paths; the RX app links without libc, + * so provide it here (halt). */ +void abort(void) +{ + _exit(1); +} +#endif + int _kill(int pid, int sig) { errno = EINVAL; 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;