Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/docker/renesas-rx/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Login-gated GNURX toolchain, supplied at image build time (see README.md).
# Not redistributable here.
gnurx-linux.tar.gz
gnurx/
34 changes: 34 additions & 0 deletions .github/docker/renesas-rx/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
62 changes: 62 additions & 0 deletions .github/docker/renesas-rx/README.md
Original file line number Diff line number Diff line change
@@ -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 <path>` 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 <your-user> --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-`.
31 changes: 31 additions & 0 deletions .github/docker/renesas-rx/docker-build-push.sh
Original file line number Diff line number Diff line change
@@ -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 <user> --password-stdin
# (or: gh auth refresh -s write:packages && gh auth token | docker login \
# ghcr.io -u <user> --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."
56 changes: 56 additions & 0 deletions .github/workflows/test-build-renesas-rx.yml
Original file line number Diff line number Diff line change
@@ -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}}
21 changes: 21 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 11 additions & 2 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 \
Expand Down
6 changes: 5 additions & 1 deletion config/examples/renesas-rx65n.config
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
4 changes: 4 additions & 0 deletions config/examples/renesas-rx72n.config
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions docs/Renesas.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
37 changes: 37 additions & 0 deletions hal/renesas-rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion hal/rx65n.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion hal/rx72n.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions include/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Loading
Loading