From 0e6c1f15cd919ce1b96992f7e2f70bfc04b3477d Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 17 Jun 2026 16:44:39 -0700 Subject: [PATCH] Add wolfBoot HAL port for RealTek RTL8735B (AmebaPro2) --- arch.mk | 60 ++++ config/examples/rtl8735b.config | 61 ++++ docs/Targets.md | 122 ++++++++ hal/rtl8735b.c | 488 +++++++++++++++++++++++++++++ hal/rtl8735b.ld | 104 ++++++ hal/rtl8735b/README | 86 +++++ hal/rtl8735b/sdk-shim/cmsis_os.h | 46 +++ hal/rtl8735b/test-app/test_app.c | 123 ++++++++ hal/rtl8735b/test-app/test_app.ld | 36 +++ tools/scripts/amebapro2_package.sh | 99 ++++++ tools/scripts/rtl8735b_build.sh | 69 ++++ 11 files changed, 1294 insertions(+) create mode 100644 config/examples/rtl8735b.config create mode 100644 hal/rtl8735b.c create mode 100644 hal/rtl8735b.ld create mode 100644 hal/rtl8735b/README create mode 100644 hal/rtl8735b/sdk-shim/cmsis_os.h create mode 100644 hal/rtl8735b/test-app/test_app.c create mode 100644 hal/rtl8735b/test-app/test_app.ld create mode 100755 tools/scripts/amebapro2_package.sh create mode 100755 tools/scripts/rtl8735b_build.sh diff --git a/arch.mk b/arch.mk index 2292a90084..0855c9c8ea 100644 --- a/arch.mk +++ b/arch.mk @@ -330,6 +330,66 @@ ifeq ($(ARCH),ARM) CFLAGS+=-I$(PICO_SDK_PATH)/src/common/pico_stdlib_headers/include endif + ifeq ($(TARGET),rtl8735b) + # RealTek RTL8735B SoC (Cortex-M33), e.g. the AmebaPro2 EVB. wolfBoot is + # staged into SRAM by the RealTek bootloader and copies the verified app + # from external SPI NOR into DDR before jumping (src/update_ram.c RAMBOOT). + CORTEX_M33=1 + CFLAGS+=-Ihal + # ASDK 10.3.0 toolchain (system arm-none-eabi-gcc clashes on newlib/lwip). + CROSS_COMPILE:=$(ASDK_PATH)/arm-none-eabi- + # Match the RealTek SDK FPU/ABI (-mfpu=fpv5-sp-d16 -mfloat-abi=softfp) so + # linking the SDK libraries is consistent; wolfBoot enables the FPU at + # hal_init before any SDK code runs. + CFLAGS+=-mfpu=fpv5-sp-d16 -mfloat-abi=softfp + LDFLAGS+=-mfpu=fpv5-sp-d16 -mfloat-abi=softfp + UPDATE_OBJS:=src/update_ram.o + CFLAGS+=-DWOLFBOOT_DUALBOOT + CFLAGS+=-ffunction-sections -fdata-sections + LDFLAGS+=-Wl,--gc-sections + # Flash/UART/cache backend: "sdk" (default, RealTek SDK drivers) or "bare" + # (smaller, no SDK dependency -- not yet implemented). See hal/rtl8735b.c. + HAL_BACKEND?=sdk + ifeq ($(HAL_BACKEND),sdk) + CFLAGS+=-DHAL_BACKEND_SDK + # The SDK backend folds the RealTek SDK driver chain into hal/rtl8735b.o. + # Its objects.h pulls the SDK's "hal.h" (defines flash_t, + # hal_audio_adapter_t, ...); wolfBoot also ships "hal.h". hal/rtl8735b.c + # does not include wolfBoot's hal.h, and the SDK dirs are passed with + # -iquote (searched before the global -I for quoted includes) so the SDK + # objects.h "hal.h" resolves to the SDK one for THIS object only. sdk-shim + # supplies a stub cmsis_os.h so the chain does not pull CMSIS-OS/FreeRTOS + # (wolfBoot never calls it). The CONFIG_* defines mirror the bare-metal + # bootloader build; -mcmse satisfies the SDK cache header (SCB_NS). The + # rest of wolfBoot stays plain M33. + HAL_SDK_IQUOTE=-iquote $(WOLFBOOT_ROOT)/hal/rtl8735b/sdk-shim \ + -iquote $(AMEBA_SDK)/component/mbed/hal_ext \ + -iquote $(AMEBA_SDK)/component/mbed/hal \ + -iquote $(AMEBA_SDK)/component/mbed/api \ + -iquote $(AMEBA_SDK)/component/mbed/targets/hal/rtl8735b \ + -iquote $(AMEBA_SDK)/component/soc/8735b/fwlib/rtl8735b/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/fwlib/rtl8735b/lib/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/cmsis/rtl8735b/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/cmsis/rtl8735b/lib/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/cmsis/cmsis-core/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/app/rtl_printf/include \ + -iquote $(AMEBA_SDK)/component/soc/8735b/app/stdio_port \ + -iquote $(AMEBA_SDK)/component/soc/8735b/misc/utilities/include \ + -iquote $(AMEBA_SDK)/component/os/os_dep/include + # The same dirs as plain -I too, so angle-bracket includes (e.g. some + # CMSIS-Core headers) resolve; -iquote only covers quoted includes. + HAL_SDK_INC=$(patsubst -iquote,-I,$(HAL_SDK_IQUOTE)) + HAL_SDK_DEFS=-DCONFIG_PLATFORM_8735B -DCONFIG_RTL8735B_PLATFORM=1 \ + -DCONFIG_BUILD_RAM=1 + hal/rtl8735b.o: CFLAGS += $(HAL_SDK_IQUOTE) $(HAL_SDK_INC) $(HAL_SDK_DEFS) -mcmse + # Final link also needs the SDK fwlib + ROM symbol table; point at the + # prebuilt SDK lib/objects and ROM symbol linker file during bring-up: + # make TARGET=rtl8735b LIBS+=... LDFLAGS_EXTRA="-T" + else + CFLAGS+=-DHAL_BACKEND_BARE + endif + endif + ifeq ($(TARGET),sama5d3) CORTEX_A5=1 UPDATE_OBJS:=src/update_ram.o diff --git a/config/examples/rtl8735b.config b/config/examples/rtl8735b.config new file mode 100644 index 0000000000..7050c5701d --- /dev/null +++ b/config/examples/rtl8735b.config @@ -0,0 +1,61 @@ +ARCH?=ARM +TARGET?=rtl8735b +SIGN?=ECC256 +HASH?=SHA256 + +# ECC256 + SHA256 header fits in 256 bytes +IMAGE_HEADER_SIZE?=256 + +# Cortex-M33 (single, non-TrustZone secure world for Model A) +CORTEX_M33?=1 +TZEN?=0 +VTOR?=1 +NO_MPU?=1 + +# wolfBoot is staged into SRAM by the RealTek bootloader (not XIP). It reads the +# BOOT/UPDATE/SWAP partitions from external SPI NOR and copies the verified +# application into DDR before jumping (src/update_ram.c RAMBOOT path). +NO_XIP?=1 +EXT_FLASH?=1 +SPI_FLASH?=0 +WOLFBOOT_DUALBOOT?=1 + +# wolfBoot runs entirely from SRAM, so RAM_CODE relocation is unnecessary. +RAM_CODE?=0 + +DEBUG?=0 +DEBUG_UART?=1 +V?=0 +SPMATH?=1 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 + +WOLFBOOT_VERSION?=1 + +# 4 KB SPI NOR sector (flash_erase_sector granularity) +WOLFBOOT_SECTOR_SIZE?=0x1000 + +# DDR address the verified application is copied to and launched from. Must +# match the application's vector-table link address. The DDR memory window is +# 0x70000000 (128 MB); load 1 MB in so the header (placed at LOAD-0x100 by the +# RAMBOOT path) stays inside DDR. 0x70100000 matches the address proven by the +# prior RealTek DDR bring-up on this board. +WOLFBOOT_LOAD_ADDRESS?=0x70100000 + +# External SPI NOR partition layout. These are raw NOR byte offsets addressed +# only by ext_flash_*. wolfBoot itself lives in the RealTek PT_FW1 region +# (0x60000, 4 MB). The BOOT/UPDATE/SWAP partitions reuse the PT_FW2 region +# (0x520000-0x920000, 4 MB) -- unused when wolfBoot is the only firmware -- so +# they stay inside the 16 MB flash and do not overlap PARTBL/boot/PT_FW1/ISP/NN. +WOLFBOOT_PARTITION_SIZE?=0x180000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x520000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x6A0000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x820000 + +# Flash/UART/cache backend: "sdk" (default, RealTek SDK drivers) or "bare" +# (smaller, no SDK dependency -- not yet implemented). +HAL_BACKEND?=sdk + +# RealTek SDK + ASDK toolchain locations (override on the command line). +AMEBA_SDK?=$(HOME)/GitHub/ameba-rtos-pro2 +ASDK_PATH?=$(HOME)/ameba-pro2-workspace/asdk/asdk-10.3.0/linux/newlib/bin diff --git a/docs/Targets.md b/docs/Targets.md index d2634cb372..b2eb00d031 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -34,6 +34,7 @@ This README describes configuration of supported targets. * [NXP T2080 PPC](#nxp-qoriq-t2080-ppc) * [Qemu x86-64 UEFI](#qemu-x86-64-uefi) * [Raspberry Pi pico 2 (rp2350)](#raspberry-pi-pico-rp2350) +* [RealTek RTL8735B (AmebaPro2)](#realtek-rtl8735b-amebapro2) * [Renesas RA6M4](#renesas-ra6m4) * [Renesas RX65N](#renesas-rx65n) * [Renesas RX72N](#renesas-rx72n) @@ -8491,3 +8492,124 @@ Number of public keys: 1 11 0C FA F6 B5 F9 59 BA B9 A5 8E 34 4A CD C5 83 7E 43 EF 61 6E C4 15 88 3C FE D6 76 47 D9 82 A4 ``` + +## RealTek RTL8735B (AmebaPro2) + +Tested on the RealTek RTL8735B (AmebaPro2) EVB. The RTL8735B is an Arm Cortex-M33 AIoT SoC with a vendor secure-boot ROM, an external SPI NOR flash, and DDR/PSRAM. + +wolfBoot does not replace the RealTek secure-boot ROM; it runs as a second-stage verified bootloader and A/B firmware-update engine. The RealTek ROM authenticates `boot.bin`, `boot.bin` stages wolfBoot into SRAM via a RealTek `RAM_FUNCTION_START_TABLE`, and wolfBoot then verifies the application in external SPI NOR, applies any pending update, copies the verified image into DDR, and jumps to it. This is the non-TrustZone "Model A" integration. + +Because wolfBoot runs from SRAM and its partitions live in external SPI NOR, the target uses `EXT_FLASH=1` and `NO_XIP=1`; the application is loaded to `WOLFBOOT_LOAD_ADDRESS` in DDR by the `src/update_ram.c` RAMBOOT path. + +### Flash/UART/cache backend + +The HAL (`hal/rtl8735b.c`) has two backends, selected with `HAL_BACKEND`: + +* `HAL_BACKEND=sdk` (default) - flash and cache use the RealTek SDK drivers (`flash_api`, `hal_cache`); the `DEBUG_UART` console uses a small self-contained UART1 driver in the HAL (it does not depend on the OS-bound SDK `hal_uart_init`). wolfBoot links against the SDK SoC libraries and the ROM symbol table. +* `HAL_BACKEND=bare` - scaffold for a smaller backend with no SDK dependency (direct register/ROM access). It links a standalone `wolfboot.elf`, but the `ext_flash_*` entry points are still stubs that return `-1`, so it is not yet functional. Work in progress. + +### Console (DEBUG_UART) + +`DEBUG_UART=1` routes wolfBoot's log to UART1 (the RealTek "LOGUART" at `0x40040400`, pins PORT_F pin 4 = TX / pin 3 = RX), which reaches the EVB USB serial console at 115200 8N1. wolfBoot brings UART1 up itself (pinmux + clock enable + 115200 8N1). The RealTek boot ROM and `boot.bin` also print on this same UART before wolfBoot, so the vendor boot messages appear first, followed immediately by the wolfBoot banner. A successful boot looks like: + +``` +wolfBoot HAL: RTL8735B (AmebaPro2) init +wolfBoot HAL: flash init done +Versions: Boot 1, Update 0 +Trying Boot partition at 0x520000 +Loading header 256 bytes from 0x520000 to 0x700FFF00 +Loading image 132 bytes from 0x520100 to 0x70100000...done +Checking integrity...done +Verifying signature...done +Booting at 0x70100000 +``` + +### Partition layout + +wolfBoot itself is packaged into the RealTek `PT_FW1` region (`0x60000`). The BOOT/UPDATE partitions are raw SPI NOR offsets (addressed only by `ext_flash_*`) carved out of the unused `PT_FW2` region so they do not collide with the RealTek partition table: + +| Partition | Address | Size | Description | +|-------------|------------|-----------|-------------| +| Boot | 0x520000 | 0x180000 | Running, verified application | +| Update | 0x6A0000 | 0x180000 | Staged incoming update | +| Swap | 0x820000 | 0x1000 | Reserved (see note) | + +These addresses are board-specific; confirm them against the board's `amebapro2_partitiontable.json`. The Swap partition is reserved by the configuration but unused in this RAMBOOT model, which selects between BOOT and UPDATE by version rather than copy-swapping through a swap sector. + +### Application load address + +The verified application is copied into DDR at `WOLFBOOT_LOAD_ADDRESS` (`0x70100000`) and launched there. The application's vector table must be linked to this same address: `do_boot()` reads word[0] as the initial MSP and word[1] as the entry point, and sets `VTOR` to the base. The DDR memory window starts at `0x70000000` (128 MB); the load address is kept 1 MB inside it because the RAMBOOT path places the image header at `WOLFBOOT_LOAD_ADDRESS - IMAGE_HEADER_SIZE`, which must also land in DDR. + +### Building RealTek RTL8735B + +All build settings come from the `.config` file. Use `TARGET=rtl8735b` and the RealTek ASDK toolchain (the system `arm-none-eabi-gcc` fails on newlib/lwip clashes). Point `AMEBA_SDK` at the RealTek `ameba-rtos-pro2` checkout and `ASDK_PATH` at the ASDK toolchain. + +```sh +cp config/examples/rtl8735b.config .config +# Compile wolfBoot and perform the SDK-resolved final link (see note below). +tools/scripts/rtl8735b_build.sh +# Wrap wolfBoot with the RealTek partition table/boot/certs into flash_ntz.bin. +tools/scripts/amebapro2_package.sh wolfboot.elf +``` + +For the default `sdk` backend, `make TARGET=rtl8735b` compiles every wolfBoot object (including the RealTek SDK driver chain folded into `hal/rtl8735b.o`), but the final `wolfboot.elf` link must resolve the SDK SoC libraries (`liboutsrc.a`, `libsoc_ntz.a`) and the ROM symbol table (`romsym_is.so`), which live in the SDK build tree. `tools/scripts/rtl8735b_build.sh` runs that compile-then-SDK-resolved-link (it honors `AMEBA_SDK` and `ASDK_PATH`), and `tools/scripts/amebapro2_package.sh` then packages wolfBoot (in `PT_FW1`) with the RealTek partition table, boot, and certs via `elf2bin` into `flash_ntz.bin` (it needs only `AMEBA_SDK`, plus optional `OUTDIR`; no toolchain). See `hal/rtl8735b/README`. + +The `bare` backend links a standalone `wolfboot.elf` with no SDK dependency (flash stubbed, not yet functional): + +```sh +make TARGET=rtl8735b HAL_BACKEND=bare +``` + +### Building and staging an application + +A minimal bare-metal example application is provided in `hal/rtl8735b/test-app/` (a vector table plus a UART banner, linked at the DDR load address). Build it with the ASDK toolchain, sign it with the wolfBoot key, and write it to the BOOT partition offset: + +```sh +export PATH="$ASDK_PATH:$PATH" +arm-none-eabi-gcc -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=softfp \ + -Os -ffreestanding -nostdlib -nostartfiles \ + -T hal/rtl8735b/test-app/test_app.ld hal/rtl8735b/test-app/test_app.c -o test_app.elf +arm-none-eabi-objcopy -O binary test_app.elf test_app.bin + +# Sign with ECDSA P-256 + SHA-256 as version 1 +tools/keytools/sign --ecc256 --sha256 test_app.bin wolfboot_signing_private_key.der 1 + +# Write the signed image to the BOOT partition offset (no whole-image rebuild). +# uartfwburn's -s flag writes at an arbitrary flash offset. +uartfwburn.linux -p /dev/ttyUSBx -f test_app_v1_signed.bin -s 0x520000 -b 3000000 -U +``` + +To stage an update, sign a newer build with a higher version number and write it to the UPDATE offset (`0x6A0000`) the same way. + +### A/B update and rollback + +wolfBoot uses version-based A/B selection in this RAMBOOT model (there is no swap copy): it boots whichever of BOOT/UPDATE holds the higher version with a valid signature. + +* **Update:** stage a higher-version signed image in UPDATE; on the next boot wolfBoot reports `Versions: Boot x, Update y`, selects the higher version, and boots it. +* **Integrity / authenticity:** a tampered image fails the SHA-256 integrity or ECDSA signature check and is rejected (`Checking integrity...FAILED`). +* **Anti-downgrade (secure default, `ALLOW_DOWNGRADE=0`):** if the newer image is rejected and the only remaining candidate is an older version, wolfBoot does not downgrade -- it stops (`Rollback to lower version not allowed`, then panic) rather than run older firmware. +* **Fault tolerance:** if a valid image of the same (or newer) version exists in the other partition, wolfBoot falls back to it and boots it. + +### Watchdog + +The RealTek boot ROM arms the SoC's vendor watchdog before handing off to wolfBoot, and it resets the SoC if the watchdog is not serviced. Following the usual wolfBoot convention (the bootloader hands a running watchdog to the application), wolfBoot leaves it running, so the **application is responsible for servicing or reconfiguring it** -- an app that never services the watchdog reboots after the timeout. + +The example app in `hal/rtl8735b/test-app/` shows the minimal "pet": a read-modify-write that sets bit 24 (`WDT_CLEAR`) of the secure vendor watchdog register at `0x50002C00`, which reloads the timer while preserving the ROM-configured enable/mode/divisor bits. A real application would service it from a periodic task or reconfigure the timeout to suit its needs. + +With `DEBUG_UART=1`, wolfBoot prints the last reset cause at startup, decoded from the AON boot-reason register (`0x40009104`) -- handy for spotting an unfed watchdog: + +``` +Reset reason: 0x1 VNDR-WDT <- vendor watchdog reset (app did not service it) +Reset reason: 0x10 BOD <- brown-out / power-on reset +``` + +### Status and roadmap + +Verified on the AmebaPro2 EVB today: signed boot (ECDSA P-256 / SHA-256), version-based A/B update, rollback/anti-downgrade, the `DEBUG_UART` console, the reset-reason readout, and the watchdog handoff. This is the non-TrustZone "Model A" integration with the `sdk` backend. + +Outstanding work (TODO): + +- **Device-bound encrypted updates (HUK):** a non-MMU encrypted-RAMBOOT enabler (`wolfBoot_ramboot_decrypt`) and an `rtl8735b-encrypt.config` exist on a separate development branch; they build but are not yet hardware-verified. Binding the partition key to the RTL8735B Hardware Unique Key uses the wolfCrypt RealTek HUK crypto-callback port (wolfSSL PR #10677) and is still to be wired and tested. +- **TrustZone-M ("Model B"):** secure wolfBoot + non-secure application. Feasibility on the AmebaPro2 (does the ROM hand off in Secure state with the SAU free?) needs a spike before a full design; the generic wolfBoot TZ infrastructure (`hal/armv8m_tz.h`, the `blxns` path in `src/boot_arm.c`, `-mcmse`/`--cmse-implib`) would be reused. +- **`bare` backend:** currently a scaffold (`ext_flash_*` return `-1`). A no-SDK flash path must reimplement `spic_init()` (controller training) on the ROM `hal_spic_stubs`, since the bootloader hands off no SPIC adaptor. +- **Measured boot / DICE:** software DICE (HUK-derived UDS + TRNG) is a later follow-on; there is no on-chip TPM. diff --git a/hal/rtl8735b.c b/hal/rtl8735b.c new file mode 100644 index 0000000000..1ab1a02378 --- /dev/null +++ b/hal/rtl8735b.c @@ -0,0 +1,488 @@ +/* rtl8735b.c + * + * HAL for the RealTek RTL8735B SoC (Cortex-M33), as used on the AmebaPro2 EVB + * and compatible boards. + * + * Copyright (C) 2026 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 2 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-1301, USA + * + * + * Model A (non-TrustZone): the RealTek bootloader stages wolfBoot into SRAM via + * the RealTek RAM_FUNCTION_START_TABLE below. wolfBoot then reads the + * BOOT/UPDATE/SWAP partitions from external SPI NOR (ext_flash_*), verifies the + * application, copies it into DDR (src/update_ram.c RAMBOOT path) and jumps. + * + * Flash/UART/cache backend, selected at build time with HAL_BACKEND (arch.mk): + * HAL_BACKEND=sdk (default) - use the RealTek SDK drivers (flash_api / + * hal_uart / hal_pinmux / hal_cache). Compiled with the SDK + * include dirs via -iquote, a stub cmsis_os.h (so the chain + * does not pull FreeRTOS), the bootloader CONFIG_* defines, + * and -mcmse. See hal/rtl8735b/README. + * HAL_BACKEND=bare - a smaller backend with no SDK dependency (direct + * register / ROM access). Scaffold only: links standalone + * but the ext_flash_* entry points return -1. The flash + * path is the hard part -- the ROM exposes the flash leaf + * functions (hal_flash_stubs) and SPIC primitives + * (hal_spic_stubs) via romsym, but the bootloader hands off + * no SPIC adaptor (see hal_init), so a bare backend must + * reimplement spic_init() (controller training/calibration) + * on hal_spic_stubs before it can read flash. Future work. + * + * NOTE: wolfBoot's own "hal.h" is intentionally NOT included here. The RealTek + * SDK also ships a "hal.h" (pulled by its objects.h), and in the SDK backend the + * SDK include dirs are passed via -iquote so that header resolves to the SDK + * one. Including wolfBoot's hal.h in the same translation unit would clash, so + * this file does not include it. The HAL entry points it implements (hal_init, + * ext_flash_*, hal_prepare_boot, ...) are prototyped by wolfBoot's hal.h in the + * translation units that call them; here they are simply defined below. + */ + +#include +#include + +#include "target.h" +#include "wolfboot/wolfboot.h" +#include "printf.h" /* wolfBoot_printf, uart_init, uart_write */ + +#ifdef HAL_BACKEND_SDK + /* The SDK's basic_types.h defines likely()/unlikely() too; drop wolfBoot's + * so the SDK definitions apply without a redefinition warning. */ + #undef likely + #undef unlikely + + /* RealTek SDK drivers: SPI NOR (flash_api.h), I/D cache maintenance + * (hal_cache.h), and the UART/pinmux register-level leaf functions used to + * bring up the DEBUG_UART console (hal_uart.h / hal_pinmux.h). */ + #include "flash_api.h" + #include "hal_cache.h" + #include "hal_uart.h" + #include "hal_pinmux.h" + + /* Set by the SDK flash driver; seeded from the bootloader-provided adaptor. */ + extern hal_spic_adaptor_t *pglob_spic_adaptor; + /* flash_init() is an SDK symbol not declared in flash_api.h. */ + extern void flash_init(flash_t *obj); + + static flash_t hal_flash_obj; +#endif /* HAL_BACKEND_SDK */ + +/* =========================================================================== + * RealTek RAM start-table: launched by the RealTek bootloader (backend common). + * + * Field order/types mirror the SDK rtl8735b_ramstart.h so the bootloader reads + * each field at the right offset. RamStartFun points at the trampoline below, + * which sets MSP and branches to wolfBoot's isr_reset. Embedding the table here + * makes wolfboot.elf directly launchable (no separate shim). + * =========================================================================== + */ + +extern void wolfboot_ram_entry(void); + +/* Scratch SRAM the bootloader writes through the RAM start-table pointers + * (pbl_peri_buf, phal_spic_adaptor, ...). It lives in .ram.noinit, which the + * linker keeps OUTSIDE the _start_bss.._end_bss range that isr_reset + * (src/boot_arm.c) zeroes -- otherwise anything the bootloader stored here + * before jumping to wolfBoot would be wiped before hal_init() runs. This is + * latent today (hal_init does a fresh flash_init and ignores the passed + * adaptor) but is required once the SPIC adaptor reuse is enabled. */ +static uint8_t hal_bl_scratch[2048] + __attribute__((section(".ram.noinit"), aligned(32))); + +/* Exactly the 10 bytes "AmebaPro2\xff" (the trailing 0xff matters; a NUL pad + * fails as "Invalid FW Image Signature"). */ +const unsigned char hal_ram_img_sig[10] + __attribute__((section(".ram.img.signature"), aligned(4))) = { + 'A', 'm', 'e', 'b', 'a', 'P', 'r', 'o', '2', 0xff +}; + +typedef struct { + void *Signature; + void (*RamStartFun)(void); + void (*RamWakeupFun)(void); + void (*RamPatchFun0)(void); + void (*RamPatchFun1)(void); + void *sys_cp_fw_info; + void *pbl_peri_buf; + void *pxip_sce_restore; + uint32_t entry_start; + uint32_t entry_end; + uint8_t *hash_data; + uint32_t ddr_hash_start1; + uint32_t ddr_hash_end1; + uint32_t ddr_hash_start2; + uint32_t ddr_hash_end2; + uint8_t *ddr_hash_data; + uint32_t boot_cfg_w; + uint32_t msp_start; + uint32_t msp_limit; + uint32_t start_tbl_size; + void *phal_spic_adaptor; + uint32_t flash_user_data_offset; + uint32_t flash_user_data_len; + void *pbl_shared_buf; + uint32_t init_flags; + uint32_t boot_status; + uint8_t reserved1; + uint8_t sys_tmr_id; + uint16_t pad; + void *pfw_image_info; + void *pbl_ld_voe_info; + void *pSnand_layout_info; + uint32_t reserved2[2]; +} ram_start_table_t; + +const ram_start_table_t ram_start_table + __attribute__((section(".ram.func.table"), used)) = { + .Signature = (void *)hal_ram_img_sig, + .RamStartFun = wolfboot_ram_entry, + .msp_start = 0x20120000u, /* valid early SRAM stack */ + .msp_limit = 0x2011f000u, + .start_tbl_size = sizeof(ram_start_table_t), + .pbl_peri_buf = &hal_bl_scratch[0], + .pbl_shared_buf = &hal_bl_scratch[256], + .pfw_image_info = &hal_bl_scratch[512], + .pbl_ld_voe_info = &hal_bl_scratch[768], + .pSnand_layout_info = &hal_bl_scratch[1024], + .sys_cp_fw_info = &hal_bl_scratch[1280], + .phal_spic_adaptor = &hal_bl_scratch[1536], +}; + +/* Trampoline the bootloader jumps to: set MSP to the top of wolfBoot's SRAM + * region and branch into wolfBoot's normal reset path (src/boot_arm.c). This + * removes any dependency on the loader honoring the ARM vector table's word0. */ +__attribute__((naked, used, section(".ram.code_text"))) +void wolfboot_ram_entry(void) +{ + __asm__ volatile( + "ldr r0, =END_STACK\n\t" + "msr msp, r0\n\t" + "ldr r0, =isr_reset\n\t" + "bx r0\n\t" + ); +} + +/* =========================================================================== + * Debug UART (wolfBoot banner / logs). + * =========================================================================== + */ +#ifdef DEBUG_UART +/* Bare-metal console on UART1 (0x40040400), which the RealTek Zephyr port uses + * as its "loguart" (DTS serial@40040400, pins PORT_F 4=TX/3=RX = 0xA4/0xA3, + * function PID_UART1). It reaches the EVB FT232 / ttyUSB5. Registers: + * TFLVR @ 0x54 (tx_fifo_lv bits[4:0]), THR @ 0x24, TX FIFO depth 16. + * + * The RealTek ROM printf is NOT used (its ROM stdio_port putc is unregistered + * from wolfBoot -> INVSTATE fault) and the SDK hal_uart_init() hangs (it pulls + * IRQ/GDMA/OS primitives). uart_init() instead brings UART1 up self-containedly: + * init the pinmux manager, route PF4/PF3 to UART1, enable the UART1 clock, then + * program baud + 8N1 via the register-level leaf functions with a hand-populated + * adapter (the leaf set_baudrate only needs the baud tables + sclk). Verified on + * hardware: the wolfBoot banner prints cleanly on ttyUSB5. */ +#ifndef RTL8735B_LOGUART_BASE +#define RTL8735B_LOGUART_BASE 0x40040400UL /* UART1 (Zephyr loguart serial@40040400) */ +#endif +#define RTL8735B_UART_TFLVR (*(volatile uint32_t *)(RTL8735B_LOGUART_BASE + 0x54)) +#define RTL8735B_UART_THR (*(volatile uint32_t *)(RTL8735B_LOGUART_BASE + 0x24)) +#define RTL8735B_UART_TX_FIFO 16u + +/* Console pins (per the RealTek Zephyr board pinctrl): PORT_F pin4 = TXD, + * pin3 = RXD, function PID_UART1. Pin name = (port<<5)|pin; UART1 func id = + * (FUNC_UART<<28)|1 = 0x60000001. The RealTek bootloader muxes these pins to + * its own console peripheral, so wolfBoot must (re)route them to UART1 + * (0x40040400) before its writes reach the FT232/ttyUSB5 console. */ +#define RTL8735B_PF4_TXD 0xA4u +#define RTL8735B_PF3_RXD 0xA3u +#define RTL8735B_PID_UART1 0x60000001u + +#ifdef HAL_BACKEND_SDK +/* UART baud tables for hal_uart_set_baudrate (which only needs the adapter's + * table pointers + sclk populated; it then writes DLL/DLM + OVSR). These are + * the SDK fwlib def_*_40m_patch[] values, EXCEPT index 13 (115200) which is + * retuned for this board's measured UART1 clock (~50 MHz, not the SDK comment's + * 40 MHz PXP value): OVSR 16 * DIV 27 = 432, 50e6/432 ~= 115740 (within 0.5%). + * wolfBoot only ever requests 115200, so the other (40 MHz) entries are + * reference-only. */ +static const uint32_t rtl8735b_baud_tbl[] = { + 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, + 76800, 115200, 128000, 153600, 230400, 380400, 460800, 500000, 921600, + 1000000, 1500000, 1536000, 2000000, 2500000, 3000000, 3500000, 4000000, + 6000000, 8000000, 10000000, 12000000, 16000000, 20000000 +}; +static const uint8_t rtl8735b_ovsr_tbl[] = { + 20, 20, 20, 20, 20, 17, 17, 15, 10, 11, 10, 11, 10, 16, 12, 10, 10, 15, 17, + 20, 14, 20, 14, 9, 13, 7, 20, 19, 7, 13, 6, 5, 5, 10, 6 +}; +static const uint16_t rtl8735b_div_tbl[] = { + 18173, 6664, 3332, 1666, 833, 490, 245, 185, 208, 126, 104, 63, 52, 27, 26, + 26, 17, 7, 5, 4, 3, 2, 2, 3, 2, 3, 1, 1, 2, 1, 2, 2, 2, 1, 1 +}; +static const uint8_t rtl8735b_adj10_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 4, 0, 5, 0, 5, 2, 3, + 2, 0, 0, 2, 3, 1, 4, 3, 0, 7 +}; +static const uint8_t rtl8735b_adj9_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 0, 4, 0, 4, 2, 3, + 2, 0, 0, 2, 3, 1, 4, 3, 0, 6 +}; +static const uint8_t rtl8735b_adj8_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 0, 4, 0, 4, 2, 3, + 2, 0, 0, 2, 3, 1, 3, 3, 0, 5 +}; +static hal_uart_adapter_t rtl8735b_uart1; +static hal_pin_mux_mang_t rtl8735b_pinmux_mgr; +#endif /* HAL_BACKEND_SDK */ + +void uart_init(void) +{ +#ifdef HAL_BACKEND_SDK + /* Initialize the pinmux manager (the bootloader's state does not carry into + * wolfBoot), then route the console pins to UART1 (the bootloader leaves + * them on its own console peripheral). */ + hal_pinmux_manager_init(&rtl8735b_pinmux_mgr); + (void)hal_pinmux_register(RTL8735B_PF4_TXD, RTL8735B_PID_UART1); + (void)hal_pinmux_register(RTL8735B_PF3_RXD, RTL8735B_PID_UART1); + + /* Bring up UART1 without the hanging hal_uart_init: hand-populate the + * adapter (the SDK 40 MHz baud tables + the UartSCLK_40M enum), enable the + * clock, then use the register-level leaf functions for 8N1 format and baud. + * The board's real UART1 clock is ~50 MHz (see the baud-table note above): + * the sclk enum stays 40M, but the 115200 table entry is retuned (OVSR/DIV) + * so the programmed divisor matches the actual clock. */ + memset(&rtl8735b_uart1, 0, sizeof(rtl8735b_uart1)); + rtl8735b_uart1.base_addr = (UART_Type *)RTL8735B_LOGUART_BASE; + rtl8735b_uart1.uart_idx = (uint8_t)Uart1; + rtl8735b_uart1.uart_sclk = (uint8_t)UartSCLK_40M; + rtl8735b_uart1.pdef_baudrate_tbl = rtl8735b_baud_tbl; + rtl8735b_uart1.pdef_ovsr_tbl = rtl8735b_ovsr_tbl; + rtl8735b_uart1.pdef_div_tbl = rtl8735b_div_tbl; + rtl8735b_uart1.pdef_ovsradjbit_tbl10 = rtl8735b_adj10_tbl; + rtl8735b_uart1.pdef_ovsradjbit_tbl9 = rtl8735b_adj9_tbl; + rtl8735b_uart1.pdef_ovsradjbit_tbl8 = rtl8735b_adj8_tbl; + hal_uart_en_ctrl((uint8_t)Uart1, ON); + (void)hal_uart_set_format(&rtl8735b_uart1, 8, 0, 1); + (void)hal_uart_set_baudrate(&rtl8735b_uart1, 115200); +#endif +} + +static void rtl8735b_uart_putc(char c) +{ + uint32_t timeout = 0; + + /* Wait while the TX FIFO is full (level >= depth); bounded so a + * misconfigured base cannot hang. */ + while ((RTL8735B_UART_TFLVR & 0x1Fu) >= RTL8735B_UART_TX_FIFO) { + if (++timeout > 1000000u) { + break; + } + } + RTL8735B_UART_THR = (uint32_t)(unsigned char)c; +} + +void uart_write(const char *buf, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) { + if (buf[i] == '\n') { + rtl8735b_uart_putc('\r'); + } + rtl8735b_uart_putc(buf[i]); + } +} +#endif /* DEBUG_UART */ + +/* =========================================================================== + * HAL init / boot handoff. + * =========================================================================== + */ +void hal_init(void) +{ +#ifdef DEBUG_UART + /* AON boot-reason register (AON_BASE 0x40009000 + 0x104): bit 0 = Vendor + * watchdog, bit 1 = AON watchdog, bit 4 = brown-out. */ + volatile uint32_t *aon_boot_reason = (volatile uint32_t *)0x40009104u; + uint32_t rr; +#endif + + /* Enable the FPU (CP10/CP11 in CPACR) before any other code runs. arch.mk + * builds every backend with -mfpu=fpv5-sp-d16, so the compiler may emit VFP + * instructions (and the RealTek SDK drivers certainly do); they fault if the + * FPU is left disabled after reset. This is backend-independent, so it is + * not gated on HAL_BACKEND_SDK. */ + *((volatile uint32_t *)0xE000ED88) |= (0xFu << 20); + __asm__ volatile("dsb\n\tisb\n\t"); + +#ifdef DEBUG_UART + uart_init(); +#endif + + wolfBoot_printf("wolfBoot HAL: RTL8735B (AmebaPro2) init\n"); + +#ifdef DEBUG_UART + /* Report why the SoC last reset, then W1C the latched status bits so each + * boot shows its own cause (helps spot e.g. an unfed watchdog reset). */ + rr = *aon_boot_reason; + wolfBoot_printf("Reset reason: 0x%x%s%s%s\n", rr, + (rr & 0x1u) ? " VNDR-WDT" : "", + (rr & 0x2u) ? " AON-WDT" : "", + (rr & 0x10u) ? " BOD" : ""); + *aon_boot_reason = rr & 0x33u; +#endif + +#ifdef HAL_BACKEND_SDK + /* Initialize the external SPI NOR with a fresh spic_init() (flash_init() + * binds a new adaptor). The RealTek bootloader does NOT hand off its own + * initialized SPIC adaptor to wolfBoot -- verified on hardware: the RAM + * start-table phal_spic_adaptor field is left at our placeholder buffer, and + * that buffer holds no valid adaptor (a real adaptor has spic_dev pointing at + * the SPIC base 0x40006000; the handoff buffer is unrelated data). So a fresh + * bind is required, not merely preferred -- there is no adaptor to reuse. */ + memset(&hal_flash_obj, 0, sizeof(hal_flash_obj)); + flash_init(&hal_flash_obj); + wolfBoot_printf("wolfBoot HAL: flash init done\n"); +#endif +} + +void hal_prepare_boot(void) +{ + /* The application was written into DDR via ext_flash_read()/memcpy. The + * RTL8735B has its own I/D cache (not Arm SCB): clean+invalidate D-cache so + * the writes hit DDR, then invalidate I-cache so stale lines do not shadow + * the new app, before do_boot() jumps. */ +#ifdef HAL_BACKEND_SDK + dcache_clean_invalidate(); + icache_invalidate(); +#endif + __asm__ volatile("dsb\n\tisb\n\t"); +} + +/* =========================================================================== + * Internal flash: physically unused in this design. wolfBoot is SRAM-resident + * and ALL partitions (BOOT/UPDATE/SWAP) live in external SPI NOR (PART_*_EXT), + * so libwolfboot never routes a write/erase through these entry points -- they + * are intentional no-ops, not unimplemented stubs. (A future config that placed + * a partition in internal flash would need a real implementation here.) + * =========================================================================== + */ +void hal_flash_unlock(void) +{ +} + +void hal_flash_lock(void) +{ +} + +int hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + (void)address; + (void)data; + (void)len; + return 0; +} + +int hal_flash_erase(uint32_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + +/* =========================================================================== + * External SPI NOR (raw byte offsets). + * =========================================================================== + */ +void ext_flash_unlock(void) +{ +#ifdef HAL_BACKEND_SDK + flash_global_unlock(); +#endif +} + +void ext_flash_lock(void) +{ +#ifdef HAL_BACKEND_SDK + flash_global_lock(); +#endif +} + +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + if (len <= 0) { + return -1; + } +#ifdef HAL_BACKEND_SDK + /* flash_stream_read returns 1 on success; propagate a read failure rather + * than handing potentially-stale data to the verifier. */ + if (flash_stream_read(&hal_flash_obj, (uint32_t)address, (uint32_t)len, + data) != 1) { + return -1; + } + return len; +#else + /* TODO(bare): implement via direct SPIC / ROM hal_flash_stream_read. */ + (void)address; + (void)data; + return -1; +#endif +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + if (len <= 0) { + return -1; + } +#ifdef HAL_BACKEND_SDK + /* flash_stream_write returns 1 on success; propagate a write failure. */ + if (flash_stream_write(&hal_flash_obj, (uint32_t)address, (uint32_t)len, + (uint8_t *)data) != 1) { + return -1; + } + return 0; +#else + /* TODO(bare): implement via direct SPIC / ROM hal_flash_burst_write. */ + (void)address; + (void)data; + return -1; +#endif +} + +int ext_flash_erase(uintptr_t address, int len) +{ + uint32_t sector_addr; + uint32_t end_addr; + + if (len <= 0 || (uint32_t)len > UINT32_MAX - (uint32_t)address) { + return -1; + } + + /* Erase every WOLFBOOT_SECTOR_SIZE sector spanning [address, address+len). + * Unlike flash_stream_read/write, the SDK flash_erase_sector returns void + * (no status to propagate), so there is no per-sector failure to check. */ + sector_addr = (uint32_t)address & ~((uint32_t)WOLFBOOT_SECTOR_SIZE - 1); + end_addr = (uint32_t)address + (uint32_t)len; + while (sector_addr < end_addr) { +#ifdef HAL_BACKEND_SDK + flash_erase_sector(&hal_flash_obj, sector_addr); +#else + /* TODO(bare): implement via direct SPIC / ROM hal_flash_sector_erase. */ + return -1; +#endif + sector_addr += WOLFBOOT_SECTOR_SIZE; + } + return 0; +} diff --git a/hal/rtl8735b.ld b/hal/rtl8735b.ld new file mode 100644 index 0000000000..26e95d9b38 --- /dev/null +++ b/hal/rtl8735b.ld @@ -0,0 +1,104 @@ +/* wolfBoot linker script for the RealTek RTL8735B (e.g. AmebaPro2 EVB). + * + * wolfBoot is staged into SRAM by the RealTek bootloader through a RealTek + * RAM_FUNCTION_START_TABLE. This script places that table, its image + * signature, and wolfBoot's text/data/bss at the fixed SRAM addresses the + * bootloader expects (cribbed from the proven zephyr_shim/shim.ld), and + * exports the wolfBoot partition symbols templated by the Makefile. + * + * The image is loaded (not XIP) into SRAM, so .data is placed in SRAM and + * _stored_data == _start_data; isr_reset's flash->RAM .data copy is then a + * no-op while BSS zeroing still runs. + */ + +MEMORY +{ + SRAM (rwx) : ORIGIN = 0x20106200, LENGTH = 0x39E00 /* up to ~0x20140000 */ +} + +ENTRY(isr_reset) + +SECTIONS +{ + /* RealTek RAM start-table at the fixed address the bootloader reads. */ + .ram.func.table 0x20106200 : + { + __ram_start_table_start__ = .; + KEEP(*(.ram.func.table)) + __ram_start_table_end__ = .; + } > SRAM + + /* 10-byte "AmebaPro2\xff" image signature at its fixed address. */ + .ram.img.signature 0x201062f0 : + { + KEEP(*(.ram.img.signature)) + } > SRAM + + .ram.code_text 0x20106300 : + { + _start_text = .; + KEEP(*(.ram.code_text)) /* RamStartFun trampoline */ + KEEP(*(.isr_vector)) /* wolfBoot ARM vector table */ + *(.text*) + *(.rodata*) + *(.keystore*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > SRAM + + _stored_data = .; + .data : + { + _start_data = .; + KEEP(*(.ramcode*)) + KEEP(*(.data*)) + . = ALIGN(4); + _end_data = .; + } > SRAM + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + } > SRAM + + /* Scratch the RealTek bootloader writes through the RAM start-table pointers + * (e.g. the SPIC adaptor it initialized). It MUST stay outside the + * _start_bss.._end_bss range that isr_reset (src/boot_arm.c) zeroes, so the + * bootloader-provided data survives into hal_init(). */ + .ram.noinit (NOLOAD) : + { + . = ALIGN(32); + KEEP(*(.ram.noinit)) + . = ALIGN(4); + } > SRAM + + _end = .; + + /* wolfBoot is bare-metal with no C++/unwinding, so the ARM exception-index + * and unwind tables are dead -- discard them (they would otherwise emit an + * empty .ARM.exidx with an unset sh_link). */ + /DISCARD/ : + { + *(.ARM.exidx*) + *(.ARM.extab*) + *(.ARM.attributes) + *(.comment) + *(.eh_frame*) + } +} + +END_STACK = 0x20140000; + +/* Partition symbols templated by the Makefile (raw external SPI NOR offsets). */ +_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@; diff --git a/hal/rtl8735b/README b/hal/rtl8735b/README new file mode 100644 index 0000000000..5b51f0f516 --- /dev/null +++ b/hal/rtl8735b/README @@ -0,0 +1,86 @@ +RealTek RTL8735B HAL +==================== + +wolfBoot's HAL for the RTL8735B SoC (AmebaPro2 EVB and compatible boards) lives +entirely in hal/rtl8735b.c, with two interchangeable flash/UART/cache backends +selected at build time by HAL_BACKEND (see the .config and arch.mk): + + HAL_BACKEND=sdk (default) - use the RealTek SDK drivers. + HAL_BACKEND=bare - a smaller backend with no SDK dependency + (direct register / ROM access). Stubbed; not + yet implemented. + + hal/rtl8735b.c - the whole HAL: hal_init, ext_flash_*, + hal_prepare_boot, the RealTek RAM start-table + + trampoline, and BOTH backends (gated on + HAL_BACKEND_SDK). + hal/rtl8735b.ld - SRAM layout + RealTek RAM start-table sections. + hal/rtl8735b/sdk-shim/cmsis_os.h - stub CMSIS-OS header (see below). + hal/rtl8735b/test-app/ - minimal bare-metal example app (DDR banner) for + validating the verify -> copy-to-DDR -> jump path. + config/examples/rtl8735b.config - example config (TARGET=rtl8735b). + +The bare backend builds standalone and links a complete wolfboot.elf, but its +ext_flash_* entry points are stubs (return -1) and it is not yet functional. The sdk +backend compiles the RealTek SDK driver chain into hal/rtl8735b.o; its final link +references the SDK fwlib + ROM symbol table (see "Building / linking"). + +Two header tricks make the SDK chain build inside wolfBoot's tree +----------------------------------------------------------------- +1. hal.h name clash. The RealTek SDK and wolfBoot BOTH ship a header named + "hal.h". The SDK's objects.h does #include "hal.h" expecting the SDK + aggregation (flash_t, hal_audio_adapter_t via hal_api.h), but wolfBoot's + include/hal.h would shadow it. So hal/rtl8735b.c does NOT include wolfBoot's + hal.h; the HAL entry points it implements are prototyped by wolfBoot's hal.h + in the translation units that call them, and simply defined here. arch.mk + passes the SDK dirs with -iquote so the SDK's hal.h wins for that quoted + include without disturbing the rest of the tree. + +2. No FreeRTOS. The SDK's cmsis.h unconditionally includes "cmsis_os.h" + (CONFIG_CMSIS_FREERTOS_EN is hardcoded), which cascades into FreeRTOS. + wolfBoot never calls CMSIS-OS, so sdk-shim/cmsis_os.h provides a stub with + just the few CMSIS-OS typedefs the SDK headers reference while parsing (e.g. + diag.h's osMutexId), placed ahead of the real one -- pulling zero FreeRTOS. + arch.mk also mirrors the bootloader's CONFIG_* defines and adds -mcmse (the + SDK cache header needs SCB_NS). + +Console (DEBUG_UART): a small self-contained UART1 driver in hal/rtl8735b.c +(pinmux PF4/PF3 -> UART1, clock enable, 8N1, 115200) built on the SDK's +register-level leaf functions (hal_uart.h / hal_pinmux.h) with a hand-populated +adapter. It deliberately avoids the SDK hal_uart_init (OS-bound, hangs from a +no-OS bootloader) and the ROM rt_printf (faults without SDK console init). UART1 +(0x40040400) is the RealTek "LOGUART" wired to the EVB serial console; the boot +ROM and boot.bin also print there before wolfBoot. The board's UART1 clock is +~50 MHz (not the SDK's "40 MHz" PXP value), so the 115200 entry in the baud +table is retuned (OVSR 16 / DIV 27 -> ~115740 baud). + +Building / linking +------------------ +1. sdk backend (default): tools/scripts/rtl8735b_build.sh + `make TARGET=rtl8735b` compiles every object (the SDK driver chain is folded + into hal/rtl8735b.o), but the wolfboot.elf link needs SDK/ROM symbols that + live in the SDK build tree, so the in-tree link cannot finish. The helper + compiles the objects then runs the SDK-resolved final link: it links against + the SDK SoC libs (liboutsrc.a, libsoc_ntz.a) in -L $SDK/.../application/output + and prepends INCLUDE "romsym_is.so" (from $SDK/.../ROM/GCC) to the linker + script. The remaining undefined symbols it resolves are genuine SDK/ROM ones: + flash_init, flash_stream_read/write, flash_erase_sector, flash_global_*, + pglob_spic_adaptor (SDK mbed flash_api.c), hal_uart_*/hal_pinmux_* (SDK + fwlib), and hal_cache_stubs (ROM symbol table). Set AMEBA_SDK/ASDK_PATH to + override the SDK checkout and toolchain. + + bare backend (standalone, no SDK -- flash stubbed, not yet functional): + make TARGET=rtl8735b HAL_BACKEND=bare + +2. Package: tools/scripts/amebapro2_package.sh wolfboot.elf + (board-level: uses the AmebaPro2 EVB partition table + elf2bin). + +SPIC adaptor reuse +------------------ +The RealTek bootloader initializes the SPIC controller before launching wolfBoot +and hands the adaptor through the RAM start-table field phal_spic_adaptor +(hal/rtl8735b.c). hal_init() seeds pglob_spic_adaptor with it so flash_init() +reuses it instead of re-training the flash. If it is NULL the controller is +re-initialized (safe: the flash is already up). + +Do not relicense or reformat vendored RealTek code; preserve its copyright. diff --git a/hal/rtl8735b/sdk-shim/cmsis_os.h b/hal/rtl8735b/sdk-shim/cmsis_os.h new file mode 100644 index 0000000000..6d89221675 --- /dev/null +++ b/hal/rtl8735b/sdk-shim/cmsis_os.h @@ -0,0 +1,46 @@ +/* cmsis_os.h (wolfBoot AmebaPro2 SDK shim) + * + * The RealTek SDK header chain (cmsis.h) unconditionally includes "cmsis_os.h", + * which in the SDK is the CMSIS-OS wrapper over FreeRTOS. wolfBoot's use of the + * SDK flash / log-UART / cache drivers is bare-metal and never calls any + * CMSIS-OS API, but a few SDK headers (e.g. diag.h: "extern osMutexId + * PrintLock_id;") reference CMSIS-OS types while being parsed. + * + * This stub satisfies those references with opaque types so the SDK headers + * compile WITHOUT pulling FreeRTOS into wolfBoot's standalone build. It is + * placed on the include path ahead of the SDK's real cmsis_os.h, and is used + * only for hal/rtl8735b.o when HAL_BACKEND=sdk. + * + * Copyright (C) 2026 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 2 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-1301, USA + */ +#ifndef WOLFBOOT_AMEBAPRO2_CMSIS_OS_SHIM_H +#define WOLFBOOT_AMEBAPRO2_CMSIS_OS_SHIM_H + +#include + +typedef void *osMutexId; +typedef void *osSemaphoreId; +typedef void *osThreadId; +typedef void *osMessageQId; +typedef void *osMailQId; +typedef void *osTimerId; +typedef void *osPoolId; +typedef int32_t osStatus; + +#endif /* WOLFBOOT_AMEBAPRO2_CMSIS_OS_SHIM_H */ diff --git a/hal/rtl8735b/test-app/test_app.c b/hal/rtl8735b/test-app/test_app.c new file mode 100644 index 0000000000..91f771000a --- /dev/null +++ b/hal/rtl8735b/test-app/test_app.c @@ -0,0 +1,123 @@ +/* test_app.c + * + * Minimal RTL8735B (AmebaPro2) bare-metal application for validating the + * wolfBoot RTL8735B port (Model A: verify -> copy-to-DDR -> jump). + * + * Copyright (C) 2026 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 2 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-1301, USA + * + * + * wolfBoot (in SRAM) verifies this image in a partition, copies it to + * WOLFBOOT_LOAD_ADDRESS (0x70100000 in DDR), then do_boot() sets VTOR to that + * base, loads MSP from word[0] and branches to word[1]. This app prints a + * banner on UART1 (left initialized by wolfBoot -- 115200 8N1) and spins. + * + * Build (ASDK 10.3.0 arm-none-eabi-gcc), default version 1: + * arm-none-eabi-gcc -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 \ + * -mfloat-abi=softfp -Os -ffreestanding -nostdlib -nostartfiles \ + * -DAPP_VERSION=1 -T test_app.ld test_app.c -o test_app.elf + * arm-none-eabi-objcopy -O binary test_app.elf test_app.bin + * Then sign and stage it -- see hal/rtl8735b/README and docs/Targets.md. + */ +#include + +#ifndef APP_VERSION +#define APP_VERSION 1 +#endif + +/* UART1 (RealTek LOGUART) registers, same base wolfBoot uses. */ +#define UART_THR (*(volatile uint32_t *)0x40040424u) /* TX holding */ +#define UART_TFLVR (*(volatile uint32_t *)0x40040454u) /* tx_fifo_lv */ +#define UART_TX_FIFO 16u + +/* Vendor watchdog: the RealTek boot ROM arms the secure vendor WDT (VNDR_S base + * 0x50002C00, VNDR_S_REG_SECURE_WATCH_DOG_TIMER at offset 0x000) before handing + * off to wolfBoot, and it resets the SoC if not serviced. wolfBoot leaves it + * running (the application owns the watchdog), so an app that does not service + * it reboots after the timeout. Bit 24 (WDT_CLEAR, write-1-pulse) reloads the + * counter; pet it with a read-modify-write so the ROM-set enable/mode/divfactor + * bits are preserved. A real application would service or reconfigure it. */ +#define VNDR_S_WDT (*(volatile uint32_t *)0x50002C00u) +#define VNDR_S_WDT_CLEAR (1u << 24) + +extern uint32_t _stack_top; /* provided by the linker script */ +void reset_handler(void); +static void default_handler(void); + +/* Cortex-M33 vector table at the image base. wolfBoot's do_boot sets VTOR here, + * loads MSP from word[0], and branches to word[1] (Reset). The remaining core + * exception vectors point at a spin so a stray fault traps here visibly instead + * of fetching a garbage handler address from .text. This banner-and-spin demo + * never enables interrupts, so the external IRQ vectors are omitted on purpose; + * extend the table if you add interrupt-driven code. */ +__attribute__((section(".vectors"), used)) +const uint32_t vectors[16] = { + (uint32_t)&_stack_top, /* 0: initial MSP */ + (uint32_t)reset_handler, /* 1: Reset */ + (uint32_t)default_handler, /* 2: NMI */ + (uint32_t)default_handler, /* 3: HardFault */ + (uint32_t)default_handler, /* 4: MemManage */ + (uint32_t)default_handler, /* 5: BusFault */ + (uint32_t)default_handler, /* 6: UsageFault */ + (uint32_t)default_handler, /* 7: SecureFault */ + 0u, 0u, 0u, /* 8-10: reserved */ + (uint32_t)default_handler, /* 11: SVCall */ + (uint32_t)default_handler, /* 12: DebugMonitor */ + 0u, /* 13: reserved */ + (uint32_t)default_handler, /* 14: PendSV */ + (uint32_t)default_handler /* 15: SysTick */ +}; + +static void default_handler(void) +{ + for (;;) { + /* trap unexpected exceptions */ + } +} + +static void uart_putc(char c) +{ + uint32_t timeout = 0; + while ((UART_TFLVR & 0x1Fu) >= UART_TX_FIFO) { + if (++timeout > 1000000u) + break; + } + UART_THR = (uint32_t)(uint8_t)c; +} + +static void uart_puts(const char *s) +{ + while (*s != '\0') { + if (*s == '\n') + uart_putc('\r'); + uart_putc(*s++); + } +} + +void reset_handler(void) +{ +#if APP_VERSION >= 2 + uart_puts("\nwolfBoot test app v2: running from DDR at 0x70100000\n"); +#else + uart_puts("\nwolfBoot test app v1: running from DDR at 0x70100000\n"); +#endif + for (;;) { + /* Pet the vendor watchdog so the boot-armed WDT does not reset us. */ + VNDR_S_WDT |= VNDR_S_WDT_CLEAR; + } +} diff --git a/hal/rtl8735b/test-app/test_app.ld b/hal/rtl8735b/test-app/test_app.ld new file mode 100644 index 0000000000..d1716ceb5f --- /dev/null +++ b/hal/rtl8735b/test-app/test_app.ld @@ -0,0 +1,36 @@ +/* Link the wolfBoot RTL8735B test app at the DDR load address (0x70100000), + * which must match WOLFBOOT_LOAD_ADDRESS in config/examples/rtl8735b.config. */ +MEMORY +{ + DDR (rwx) : ORIGIN = 0x70100000, LENGTH = 0x00080000 /* 512 KB */ +} + +ENTRY(reset_handler) + +SECTIONS +{ + .text 0x70100000 : + { + KEEP(*(.vectors)) + *(.text*) + *(.rodata*) + . = ALIGN(4); + } > DDR + + .data : + { + *(.data*) + . = ALIGN(4); + } > DDR + + .bss (NOLOAD) : + { + *(.bss*) + *(COMMON) + . = ALIGN(4); + } > DDR + + _stack_top = 0x70180000; /* MSP: 512 KB above the image base, in DDR */ + + /DISCARD/ : { *(.ARM.exidx*) *(.ARM.extab*) *(.ARM.attributes) *(.comment) } +} diff --git a/tools/scripts/amebapro2_package.sh b/tools/scripts/amebapro2_package.sh new file mode 100755 index 0000000000..9a05285340 --- /dev/null +++ b/tools/scripts/amebapro2_package.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Package wolfboot.elf into a RealTek RTL8735B (AmebaPro2) flash image. +# +# wolfboot.elf already embeds the RealTek RAM start-table (hal/rtl8735b.c), so +# unlike the zephyr_shim flow there is no separate shim.elf and no DDR blob: +# wolfBoot is SRAM-only and loads the application from external SPI NOR itself. +# This runs RealTek's elf2bin convert (wolfBoot -> PT_FW1 firmware.bin) and +# combine (reusing the vendor PARTBL/certs/boot.bin) to produce flash_ntz.bin. +# +# Usage: amebapro2_package.sh [wolfboot.elf] +# +# Override via environment: +# AMEBA_SDK RealTek ameba-rtos-pro2 checkout +# OUTDIR work/output dir (default /tmp/wolfboot_amebapro2_pkg) +# (No toolchain needed here -- packaging runs the prebuilt elf2bin/checksum.) +set -e + +WOLFBOOT_ELF="${1:-wolfboot.elf}" +AMEBA_SDK="${AMEBA_SDK:-$HOME/GitHub/ameba-rtos-pro2}" +OUTDIR="${OUTDIR:-/tmp/wolfboot_amebapro2_pkg}" + +SDK="$AMEBA_SDK/project/realtek_amebapro2_v0_example" +MP="$SDK/GCC-RELEASE/mp" +B="$SDK/GCC-RELEASE/build" + +if [ ! -r "$WOLFBOOT_ELF" ]; then + echo "error: cannot read $WOLFBOOT_ELF" >&2 + exit 1 +fi +if [ ! -d "$MP" ]; then + echo "error: RealTek SDK MP dir not found: $MP" >&2 + echo " set AMEBA_SDK to your ameba-rtos-pro2 checkout" >&2 + exit 1 +fi + +WOLFBOOT_ELF_ABS="$(readlink -f "$WOLFBOOT_ELF")" + +# Guard the recursive delete below: OUTDIR is removed wholesale, so refuse a +# mis-set value (empty, relative, "/", or a shallow path) that could wipe an +# unintended directory. The default (/tmp/wolfboot_amebapro2_pkg) is safe. +case "$OUTDIR" in + /*) ;; + *) echo "error: OUTDIR must be an absolute path: '$OUTDIR'" >&2; exit 1;; +esac +if [ "$OUTDIR" = "/" ] || \ + [ "$(printf '%s' "$OUTDIR" | tr -cd / | wc -c)" -lt 2 ]; then + echo "error: refusing to 'rm -rf' unsafe/shallow OUTDIR '$OUTDIR'" >&2 + exit 1 +fi + +rm -rf "$OUTDIR" +mkdir -p "$OUTDIR" +cd "$OUTDIR" + +# Tooling + vendor partition/cert/boot artifacts (reused as-is). +cp "$MP/elf2bin.linux" "$MP/checksum.linux" . +cp "$MP"/*.json . +cp "$WOLFBOOT_ELF_ABS" wolfboot.elf +cp "$B/partition.bin" "$B/certable.bin" "$B/certificate.bin" \ + "$B/boot.bin" "$B/boot_fcs.bin" "$B/firmware_isp_iq.bin" . +# VOE blob the bootloader loads alongside FW1 (referenced by the firmware json). +VOE=$(find "$AMEBA_SDK" -name 'voe.bin' -path '*video*' 2>/dev/null | head -1) +[ -n "$VOE" ] && cp "$VOE" . +chmod +x elf2bin.linux checksum.linux + +# Build the firmware json: wolfBoot SRAM-only image, entry at the start-table. +python3 - <<'PY' +import json +j = json.load(open('amebapro2_firmware_ntz.json')) +j['FW'] = { + "source": "wolfboot.elf", + "header": {"type": "IMG_FWHS_S", "entry": "__ram_start_table_start__"}, + "blocks": ["sram"], + "sram": { + "type": "SIMG_SRAM", + "sections": [ + ".ram.img.signature", + ".ram.func.table", + ".ram.code_text", + ".data" + ] + } +} +# Keep the IQ_SENSOR + VOE images the RealTek bootloader expects after loading +# FW1 (omitting them makes the bootloader fail "SET BL4VOE DATA" and reset); +# only the FW image is replaced with wolfBoot. +json.dump(j, open('wolfboot_fw.json', 'w'), indent=2) +PY + +./elf2bin.linux convert wolfboot_fw.json FIRMWARE firmware.bin \ + > "$OUTDIR/convert.log" 2>&1 + +./elf2bin.linux combine amebapro2_partitiontable.json flash_ntz.bin \ + PT_PT=partition.bin,CER_TBL=certable.bin,KEY_CER1=certificate.bin,PT_BL_PRI=boot.bin,PT_FW1=firmware.bin,PT_ISP_IQ=firmware_isp_iq.bin,PT_FCSDATA=boot_fcs.bin \ + > "$OUTDIR/combine.log" 2>&1 + +echo "built: $OUTDIR/flash_ntz.bin ($(stat -c %s flash_ntz.bin) bytes)" +echo "note: flash the signed app separately into the BOOT partition offset" +echo " (WOLFBOOT_PARTITION_BOOT_ADDRESS) via uartfwburn." diff --git a/tools/scripts/rtl8735b_build.sh b/tools/scripts/rtl8735b_build.sh new file mode 100755 index 0000000000..eb79838e56 --- /dev/null +++ b/tools/scripts/rtl8735b_build.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Build wolfBoot for the RealTek RTL8735B (AmebaPro2), SDK backend. +# +# `make TARGET=rtl8735b` compiles every wolfBoot object (including the RealTek +# SDK driver chain folded into hal/rtl8735b.o), but the final wolfboot.elf link +# needs the SDK SoC libraries (liboutsrc.a, libsoc_ntz.a) and the ROM symbol +# table (romsym_is.so), which live in the SDK build tree -- the default in-tree +# link cannot resolve them. This script compiles the objects, then performs that +# SDK-resolved final link. Run tools/scripts/amebapro2_package.sh afterward to +# produce the flashable flash_ntz.bin. +# +# Override via environment: +# AMEBA_SDK RealTek ameba-rtos-pro2 checkout +# ASDK_PATH ASDK 10.3.0 toolchain bin dir +set -e + +AMEBA_SDK="${AMEBA_SDK:-$HOME/GitHub/ameba-rtos-pro2}" +ASDK_PATH="${ASDK_PATH:-$HOME/ameba-pro2-workspace/asdk/asdk-10.3.0/linux/newlib/bin}" +SDKREL="$AMEBA_SDK/project/realtek_amebapro2_v0_example/GCC-RELEASE" +CC="$ASDK_PATH/arm-none-eabi-gcc" +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$ROOT" + +# Temp files, cleaned on exit -- avoids clobbering parallel runs and the risk of +# writing through a pre-created symlink at a fixed /tmp path. +MAKE_ERR="$(mktemp)" +OBJS_FILE="$(mktemp)" +LINK_LD="$(mktemp)" +trap 'rm -f "$MAKE_ERR" "$OBJS_FILE" "$LINK_LD"' EXIT + +if [ ! -d "$SDKREL/application/output" ]; then + echo "error: SDK build output not found: $SDKREL/application/output" >&2 + echo " set AMEBA_SDK and build the RealTek SDK first." >&2 + exit 1 +fi + +cp config/examples/rtl8735b.config .config + +# Compile all objects. The default link of wolfboot.elf is expected to fail here +# (unresolved SDK/ROM symbols); capture the object list from its [LD] line. +make TARGET=rtl8735b 2>"$MAKE_ERR" \ + | grep -A1 '\[LD\] wolfboot.elf' | tail -1 > "$OBJS_FILE" || true +OBJS="$(cat "$OBJS_FILE")" +if ! echo "$OBJS" | grep -q 'hal/rtl8735b.o'; then + echo "error: object compile failed (no hal/rtl8735b.o in link line). make stderr:" >&2 + tail -8 "$MAKE_ERR" >&2 + exit 1 +fi + +# Build the final linker script in a temp file rather than mutating the +# generated config/target.ld in place: prepend the ROM symbol table include. +# ld resolves the INCLUDE via the -L paths below, where romsym_is.so lives. +{ echo 'INCLUDE "romsym_is.so"'; cat config/target.ld; } > "$LINK_LD" + +"$CC" -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=softfp \ + -ffreestanding -nostartfiles --specs=nosys.specs \ + -T "$LINK_LD" \ + -L "$SDKREL/ROM/GCC" -L "$SDKREL/application/output" \ + $OBJS \ + -Wl,--start-group \ + "$SDKREL/application/output/liboutsrc.a" \ + "$SDKREL/application/output/libsoc_ntz.a" \ + -lc -lgcc -lnosys -lm \ + -Wl,--end-group \ + -Wl,--gc-sections \ + -o wolfboot.elf + +echo "built: wolfboot.elf ($(stat -c %s wolfboot.elf) bytes)" +echo "next: tools/scripts/amebapro2_package.sh wolfboot.elf"