mirror of https://github.com/wolfSSL/wolfBoot.git
Add NXP RW612 target with TrustZone support
parent
a16e2e8f11
commit
d147db6dfd
|
|
@ -359,6 +359,37 @@ jobs:
|
|||
config-file: ./config/examples/mcxn-tz-psa-hw.config
|
||||
board-name: frdmmcxn947
|
||||
|
||||
nxp_rw612_test:
|
||||
uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml
|
||||
with:
|
||||
arch: arm
|
||||
config-file: ./config/examples/rw612.config
|
||||
board-name: frdmrw612
|
||||
|
||||
nxp_rw612_tz_test:
|
||||
uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml
|
||||
with:
|
||||
arch: arm
|
||||
config-file: ./config/examples/rw612-tz.config
|
||||
board-name: frdmrw612
|
||||
|
||||
nxp_rw612_wolfcrypt_tz_test:
|
||||
uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml
|
||||
with:
|
||||
arch: arm
|
||||
config-file: ./config/examples/rw612-tz.config
|
||||
board-name: frdmrw612
|
||||
make-args: "WOLFCRYPT_TZ=1 WOLFCRYPT_TZ_PKCS11=1"
|
||||
|
||||
# Without the console the TRNG still needs the reset driver linked in
|
||||
nxp_rw612_wolfcrypt_tz_no_uart_test:
|
||||
uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml
|
||||
with:
|
||||
arch: arm
|
||||
config-file: ./config/examples/rw612-tz.config
|
||||
board-name: frdmrw612
|
||||
make-args: "WOLFCRYPT_TZ=1 WOLFCRYPT_TZ_PKCS11=1 DEBUG_UART=0"
|
||||
|
||||
nxp_s32k142_test:
|
||||
uses: ./.github/workflows/test-build.yml
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -25,10 +25,14 @@ jobs:
|
|||
run: |
|
||||
make keysclean && make -C tools/keytools clean && rm -f include/target.h
|
||||
|
||||
- name: Fetch NXP MCUXpresso SDK headers
|
||||
run: |
|
||||
tools/ci/fetch-mcuxsdk-headers.sh "$GITHUB_WORKSPACE/mcuxsdk"
|
||||
|
||||
- name: Build unit tests
|
||||
run: |
|
||||
make -C tools/unit-tests
|
||||
make -C tools/unit-tests MCUXSDK="$GITHUB_WORKSPACE/mcuxsdk"
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
make -C tools/unit-tests run
|
||||
make -C tools/unit-tests run MCUXSDK="$GITHUB_WORKSPACE/mcuxsdk"
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ tools/unit-tests/unit-ahci-unlock-panic
|
|||
tools/unit-tests/unit-ata-security-passphrase-zeroize
|
||||
tools/unit-tests/unit-arm-tee-psa-ipc
|
||||
tools/unit-tests/unit-flash-write-mcxa
|
||||
tools/unit-tests/unit-rw612-flash
|
||||
tools/unit-tests/unit-rw612-tz
|
||||
tools/unit-tests/unit-rw612-uart
|
||||
tools/unit-tests/unit-flash-write-nrf52
|
||||
tools/unit-tests/unit-flash-write-same51
|
||||
tools/unit-tests/unit-flash-write-samr21
|
||||
|
|
|
|||
43
arch.mk
43
arch.mk
|
|
@ -1511,6 +1511,49 @@ ifeq ($(TARGET),mcxn)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),rw612)
|
||||
CORTEX_M33=1
|
||||
ARCH_FLASH_OFFSET=0x08000000
|
||||
ifneq ($(TZEN),1)
|
||||
LSCRIPT_IN=hal/$(TARGET)-ns.ld
|
||||
endif
|
||||
MCUXPRESSO_FLASH_CONFIG?=$(MCUXPRESSO_PROJECT_TEMPLATE)/../flash_config
|
||||
CFLAGS+=\
|
||||
-I$(MCUXPRESSO_DRIVERS) \
|
||||
-I$(MCUXPRESSO_DRIVERS)/drivers \
|
||||
-I$(MCUXPRESSO_DRIVERS)/drivers/romapi/flexspi \
|
||||
-I$(MCUXPRESSO_DRIVERS)/../periph \
|
||||
-I$(MCUXPRESSO) \
|
||||
-I$(MCUXPRESSO)/drivers \
|
||||
-I$(MCUXPRESSO)/drivers/common \
|
||||
-I$(MCUXPRESSO)/drivers/flexcomm \
|
||||
-I$(MCUXPRESSO)/drivers/flexcomm/usart \
|
||||
-I$(MCUXPRESSO)/drivers/trng \
|
||||
-I$(MCUXPRESSO_FLASH_CONFIG) \
|
||||
-I$(MCUXPRESSO_PROJECT_TEMPLATE) \
|
||||
-I$(MCUXPRESSO_CMSIS)/Include \
|
||||
-I$(MCUXPRESSO_CMSIS)/Core/Include
|
||||
CFLAGS+=-DCPU_$(MCUXPRESSO_CPU) -DBOOT_HEADER_ENABLE=1 -DWOLFBOOT_ROM_IMAGE_HEADER
|
||||
CFLAGS+=-DDEBUG_CONSOLE_ASSERT_DISABLE=1
|
||||
# The RW612 Cortex-M33 has no DSP extension
|
||||
CFLAGS+=-mcpu=cortex-m33+nodsp -DCORTEX_M33
|
||||
LDFLAGS+=-mcpu=cortex-m33+nodsp
|
||||
OBJS+=\
|
||||
$(MCUXPRESSO_DRIVERS)/drivers/fsl_clock.o \
|
||||
$(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.o \
|
||||
$(MCUXPRESSO_FLASH_CONFIG)/flash_config.o
|
||||
|
||||
ifeq ($(DEBUG_UART),1)
|
||||
OBJS+=\
|
||||
$(MCUXPRESSO)/drivers/flexcomm/fsl_flexcomm.o \
|
||||
$(MCUXPRESSO)/drivers/flexcomm/usart/fsl_usart.o
|
||||
endif
|
||||
|
||||
ifeq ($(WOLFCRYPT_TZ),1)
|
||||
OBJS+=$(MCUXPRESSO)/drivers/trng/fsl_trng.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),nrf5340)
|
||||
ifneq ($(TZEN), 1)
|
||||
LSCRIPT_IN=hal/$(TARGET)-ns.ld
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
ARCH?=ARM
|
||||
TZEN?=1
|
||||
TARGET?=rw612
|
||||
SIGN?=ECC256
|
||||
HASH?=SHA256
|
||||
MCUXSDK?=1
|
||||
MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk
|
||||
MCUXPRESSO_CMSIS?=$(MCUXPRESSO)/arch/arm/CMSIS
|
||||
MCUXPRESSO_CPU?=RW612ETA2I
|
||||
MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/Wireless/RW/RW612
|
||||
MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmrw612/project_template
|
||||
DEBUG?=0
|
||||
DEBUG_UART?=1
|
||||
VTOR?=1
|
||||
CORTEX_M0?=0
|
||||
CORTEX_M33?=1
|
||||
NO_ASM?=0
|
||||
NO_MPU=1
|
||||
EXT_FLASH?=0
|
||||
SPI_FLASH?=0
|
||||
ALLOW_DOWNGRADE?=0
|
||||
NVM_FLASH_WRITEONCE?=1
|
||||
NO_ARM_ASM?=0
|
||||
WOLFBOOT_VERSION?=0
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=1
|
||||
DUALBANK_SWAP?=0
|
||||
|
||||
# 4KB sectors on the external FlexSPI NOR (W25Q512JV)
|
||||
WOLFBOOT_SECTOR_SIZE?=0x1000
|
||||
|
||||
# 384KB wolfBoot, 8KB NSC, 1MB partitions, 4KB swap, 128KB keyvault
|
||||
# The keyvault sits outside the range erased by a bootloader self-update
|
||||
WOLFBOOT_KEYVAULT_ADDRESS?=0x08400000
|
||||
WOLFBOOT_KEYVAULT_SIZE?=0x20000
|
||||
WOLFBOOT_NSC_ADDRESS?=0x08060000
|
||||
WOLFBOOT_NSC_SIZE?=0x2000
|
||||
WOLFBOOT_PARTITION_SIZE?=0x100000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08100000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08200000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x08300000
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
ARCH?=ARM
|
||||
TZEN?=0
|
||||
TARGET?=rw612
|
||||
SIGN?=ECC256
|
||||
HASH?=SHA256
|
||||
MCUXSDK?=1
|
||||
MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk
|
||||
MCUXPRESSO_CMSIS?=$(MCUXPRESSO)/arch/arm/CMSIS
|
||||
MCUXPRESSO_CPU?=RW612ETA2I
|
||||
MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/Wireless/RW/RW612
|
||||
MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmrw612/project_template
|
||||
DEBUG?=0
|
||||
DEBUG_UART?=1
|
||||
VTOR?=1
|
||||
CORTEX_M0?=0
|
||||
CORTEX_M33?=1
|
||||
NO_ASM?=0
|
||||
NO_MPU=1
|
||||
EXT_FLASH?=0
|
||||
SPI_FLASH?=0
|
||||
ALLOW_DOWNGRADE?=0
|
||||
NVM_FLASH_WRITEONCE?=1
|
||||
NO_ARM_ASM?=0
|
||||
WOLFBOOT_VERSION?=0
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=1
|
||||
DUALBANK_SWAP?=0
|
||||
|
||||
# 4KB sectors on the external FlexSPI NOR (W25Q512JV)
|
||||
WOLFBOOT_SECTOR_SIZE?=0x1000
|
||||
|
||||
# 1MB boot, 1MB partitions, 4KB swap
|
||||
WOLFBOOT_PARTITION_SIZE?=0x100000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08100000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08200000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x08300000
|
||||
178
docs/Targets.md
178
docs/Targets.md
|
|
@ -35,6 +35,7 @@ This README describes configuration of supported targets.
|
|||
* [NXP MCXA153](#nxp-mcxa153)
|
||||
* [NXP MCXW716](#nxp-mcxw716)
|
||||
* [NXP MCXN947](#nxp-mcxn947)
|
||||
* [NXP RW612](#nxp-rw612)
|
||||
* [NXP S32K1XX](#nxp-s32k1xx)
|
||||
* [NXP P1021 PPC](#nxp-qoriq-p1021-ppc)
|
||||
* [NXP T10xx PPC (T1024 / T1040)](#nxp-qoriq-t10xx-ppc-t1024--t1040)
|
||||
|
|
@ -7261,6 +7262,183 @@ c
|
|||
Sample application for DICE attestation is available on MCXN947.
|
||||
Please find the details in `docs/MCXN947-DICE.md`.
|
||||
|
||||
## NXP RW612
|
||||
|
||||
The NXP RW612 is a wireless MCU (Wi-Fi 6, Bluetooth LE, 802.15.4) with a
|
||||
260MHz Cortex-M33 and TrustZone-M. Code executes in place from an external
|
||||
FlexSPI NOR flash mapped at `0x08000000` (secure alias `0x18000000`). The
|
||||
support has been tested on the FRDM-RW612 board (64MB Winbond W25Q512JV NOR,
|
||||
4KB sectors) with its on-board MCU-Link.
|
||||
|
||||
`wolfboot.bin` contains the FlexSPI configuration block (FCB) at offset `0x400`
|
||||
and the boot ROM image header in its vector table at `0x1000`, so it boots from
|
||||
the start of the flash without any `nxpimage` wrapping. Flash erase and program
|
||||
use the boot ROM FlexSPI API from RAM. The RW612 Cortex-M33 has no DSP
|
||||
extension, so wolfBoot is built with `-mcpu=cortex-m33+nodsp`.
|
||||
|
||||
This requires the NXP MCUXpresso SDK, placed under `../NXP` by default:
|
||||
|
||||
```sh
|
||||
mkdir -p ../NXP && cd ../NXP
|
||||
python3 -m venv west-venv
|
||||
. west-venv/bin/activate
|
||||
pip install west
|
||||
west init -m https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git mcuxpresso-sdk
|
||||
cd mcuxpresso-sdk
|
||||
west update_board --set board frdmrw612
|
||||
deactivate
|
||||
```
|
||||
|
||||
`update_board` fetches every project the board's SDK examples use. The wolfBoot
|
||||
build only needs four of them (drivers, CMSIS, the board's project template and
|
||||
flash configuration, and the RW612 device files), so this is enough instead:
|
||||
|
||||
```sh
|
||||
west update core CMSIS mcu-sdk-examples mcux-devices-wireless
|
||||
```
|
||||
|
||||
### RW612: Configuring and compiling
|
||||
|
||||
```sh
|
||||
cp config/examples/rw612.config .config
|
||||
make
|
||||
```
|
||||
|
||||
We provide two configuration files:
|
||||
- `rw612.config`: both wolfBoot and the application run in the secure world.
|
||||
- `rw612-tz.config`: wolfBoot runs in the secure world, the application runs
|
||||
in the non-secure world.
|
||||
|
||||
To add a non-secure callable wolfPKCS11 API to wolfCrypt and a secure keyvault
|
||||
provided by wolfBoot, build `rw612-tz.config` with
|
||||
`make WOLFCRYPT_TZ=1 WOLFCRYPT_TZ_PKCS11=1`.
|
||||
|
||||
Both sign with ECC256/SHA256; any other algorithm supported by wolfBoot can be
|
||||
selected with `SIGN=` and `HASH=`. Signatures that need a larger header must
|
||||
also raise `WOLFBOOT_SECTOR_SIZE` to at least `IMAGE_HEADER_SIZE`, for example
|
||||
ML-DSA-44:
|
||||
|
||||
```sh
|
||||
make SIGN=ML_DSA ML_DSA_LEVEL=2 IMAGE_SIGNATURE_SIZE=2420 \
|
||||
IMAGE_HEADER_SIZE=8192 WOLFBOOT_SECTOR_SIZE=0x2000
|
||||
```
|
||||
|
||||
| Region | Address | Size |
|
||||
|---------------------------|--------------|-------|
|
||||
| FCB | `0x08000400` | 512B |
|
||||
| wolfBoot | `0x08001000` | - |
|
||||
| NSC veneers (TZ) | `0x08060000` | 8KB |
|
||||
| Boot partition | `0x08100000` | 1MB |
|
||||
| Update partition | `0x08200000` | 1MB |
|
||||
| Swap sector | `0x08300000` | 4KB |
|
||||
| Keyvault (TZ) | `0x08400000` | 128KB |
|
||||
|
||||
With TrustZone, wolfBoot keeps its data in secure SRAM at `0x30000000` and
|
||||
the non-secure application uses SRAM from `0x20040000`. Only the boot
|
||||
partition, that SRAM, the group 1 clock and reset controls (`CLKCTL1`,
|
||||
`RSTCTL1`), GPIO and FLEXCOMM0-3 are non-secure; extend `hal_sau_init()` in
|
||||
`hal/rw612.c` to give the application more peripherals.
|
||||
|
||||
### RW612: Loading the firmware
|
||||
|
||||
Program the flash with [pyOCD](https://pyocd.io) through the MCU-Link
|
||||
(CMSIS-DAP firmware, as shipped). `factory.bin` holds wolfBoot and the signed
|
||||
v1 application in one image laid out from `0x08000000`:
|
||||
|
||||
```sh
|
||||
pip install pyocd
|
||||
pyocd pack install rw612eta2i
|
||||
pyocd flash -t rw612eta2i -a 0x08000000 -e sector factory.bin
|
||||
```
|
||||
|
||||
or write them separately:
|
||||
|
||||
```sh
|
||||
pyocd flash -t rw612eta2i -a 0x08000000 -e sector wolfboot.bin
|
||||
pyocd flash -t rw612eta2i -a 0x08100000 -e sector test-app/image_v1_signed.bin
|
||||
```
|
||||
|
||||
If the MCU-Link has been updated to J-Link firmware, pyOCD drives it through
|
||||
the SEGGER J-Link software: install the J-Link Software and Documentation Pack,
|
||||
`pip install pylink-square`, and add `-O jlink.device=RW612` to the commands
|
||||
above (on Linux, put the J-Link directory on `LD_LIBRARY_PATH` if pyOCD cannot
|
||||
find `libjlinkarm`). J-Link Commander works as well (add `-USB <serial>` when
|
||||
more than one J-Link is attached):
|
||||
|
||||
```sh
|
||||
JLinkExe -device RW612 -if SWD -speed 4000 -NoGui 1 -CommanderScript flash.jlink
|
||||
```
|
||||
|
||||
```
|
||||
exec EnableEraseAllFlashBanks
|
||||
connect
|
||||
h
|
||||
erase 0x08100000 0x08300FFF
|
||||
loadbin factory.bin 0x08000000
|
||||
r
|
||||
g
|
||||
qc
|
||||
```
|
||||
|
||||
The FlexSPI NOR is not an internal flash bank, so an explicit `erase` range
|
||||
needs `exec EnableEraseAllFlashBanks`; without it J-Link reports `No Flash bank
|
||||
within given address range` and erases nothing. The `erase` clears the boot,
|
||||
update and swap areas so no stale partition state is left (add
|
||||
`erase 0x08400000 0x0841FFFF` to also clear the TrustZone keyvault), and
|
||||
`loadbin` erases the sectors it writes. Keep `connect` ahead of every command
|
||||
that talks to the target: some J-Link Commander versions stop processing the
|
||||
script after an implicit connect, so `loadbin` never runs.
|
||||
|
||||
The debug console is FLEXCOMM3 on the MCU-Link virtual COM port at 115200
|
||||
baud. After reset:
|
||||
|
||||
```
|
||||
Hello from RW612 firmware version 1
|
||||
```
|
||||
|
||||
### RW612: Testing firmware update
|
||||
|
||||
1) Sign the test-app with version 2:
|
||||
|
||||
```sh
|
||||
./tools/keytools/sign --ecc256 test-app/image.bin wolfboot_signing_private_key.der 2
|
||||
```
|
||||
|
||||
2) Create the update trigger and assemble the update partition image
|
||||
(`0xFFFFB` is `WOLFBOOT_PARTITION_SIZE` minus 5):
|
||||
|
||||
```sh
|
||||
echo -n "pBOOT" > trigger_magic.bin
|
||||
./tools/bin-assemble/bin-assemble \
|
||||
update.bin \
|
||||
0x0 test-app/image_v2_signed.bin \
|
||||
0xFFFFB trigger_magic.bin
|
||||
```
|
||||
|
||||
3) Flash `update.bin` to the update partition:
|
||||
|
||||
```sh
|
||||
pyocd flash -t rw612eta2i -a 0x08200000 -e sector update.bin
|
||||
```
|
||||
|
||||
With J-Link Commander, `loadbin update.bin 0x08200000` followed by `r` and `g`
|
||||
does the same.
|
||||
|
||||
wolfBoot verifies and installs version 2, which confirms the update with
|
||||
`wolfBoot_success()`:
|
||||
|
||||
```
|
||||
Hello from RW612 firmware version 2
|
||||
Update successful, firmware version 2 confirmed
|
||||
```
|
||||
|
||||
### RW612: Debugging
|
||||
|
||||
```sh
|
||||
pyocd gdbserver -t rw612eta2i
|
||||
arm-none-eabi-gdb wolfboot.elf -ex "target remote :3333"
|
||||
```
|
||||
|
||||
## NXP S32K1XX
|
||||
|
||||
The NXP S32K1xx family (S32K142, S32K144, S32K146, S32K148) are automotive-grade
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @ARCH_FLASH_OFFSET@, LENGTH = @BOOTLOADER_PARTITION_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
. = 0x400;
|
||||
KEEP(*(.flash_conf))
|
||||
. = 0x1000;
|
||||
_rom_image_start = .;
|
||||
KEEP(*(.isr_vector))
|
||||
*(.keystore*)
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
*(.init*)
|
||||
*(.fini*)
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > FLASH
|
||||
|
||||
.edidx :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx*)
|
||||
} > FLASH
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.ramcode*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > RAM
|
||||
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
_start_bss = .;
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
__bss_end__ = .;
|
||||
_end = .;
|
||||
} > RAM
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
END_STACK = ORIGIN(RAM) + LENGTH(RAM);
|
||||
|
||||
_rom_image_size = _stored_data + SIZEOF(.data) - _rom_image_start;
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
/* rw612.c
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <target.h>
|
||||
#include "fsl_common.h"
|
||||
#include "image.h"
|
||||
#include "loader.h"
|
||||
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_romapi_flexspi.h"
|
||||
|
||||
#ifdef DEBUG_UART
|
||||
#include "fsl_io_mux.h"
|
||||
#include "fsl_usart.h"
|
||||
#endif
|
||||
|
||||
#ifdef TZEN
|
||||
#include "hal/armv8m_tz.h"
|
||||
#endif
|
||||
|
||||
#if defined(WOLFCRYPT_SECURE_MODE) && !defined(NONSECURE_APP)
|
||||
#include "fsl_trng.h"
|
||||
#endif
|
||||
|
||||
#include "rw612.h"
|
||||
|
||||
#if (WOLFBOOT_SECTOR_SIZE % RW612_FLASH_SECTOR_SIZE) != 0
|
||||
#error WOLFBOOT_SECTOR_SIZE must be a multiple of the 4KB NOR erase sector
|
||||
#endif
|
||||
|
||||
uint32_t SystemCoreClock;
|
||||
|
||||
static flexspi_nor_config_t flash_cfg;
|
||||
static const flexspi_nor_flash_driver_t *rom_flexspi;
|
||||
|
||||
#ifdef WOLFCRYPT_SECURE_MODE
|
||||
void hal_trng_init(void);
|
||||
int hal_trng_get_entropy(unsigned char *out, unsigned int len);
|
||||
#endif
|
||||
|
||||
#ifdef TZEN
|
||||
static void hal_sau_init(void)
|
||||
{
|
||||
/* Non-secure callable area */
|
||||
sau_init_region(0, WOLFBOOT_NSC_ADDRESS,
|
||||
WOLFBOOT_NSC_ADDRESS + WOLFBOOT_NSC_SIZE - 1, 1);
|
||||
|
||||
/* Non-secure: application flash area (boot partition) */
|
||||
sau_init_region(1, WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 1, 0);
|
||||
|
||||
/* Non-secure RAM, above the physical SRAM used by wolfBoot */
|
||||
sau_init_region(2, 0x20040000, 0x2012FFFF, 0);
|
||||
|
||||
/* Group 0 clock/reset, FlexSPI, IO_MUX, DMA and crypto stay secure */
|
||||
sau_init_region(3, 0x40020000, 0x40021FFF, 0); /* RSTCTL1, CLKCTL1 */
|
||||
sau_init_region(4, 0x40100000, 0x40103FFF, 0); /* GPIO */
|
||||
sau_init_region(5, 0x40106000, 0x40109FFF, 0); /* FLEXCOMM0..3 */
|
||||
|
||||
/* Their interrupts target the non-secure world */
|
||||
NVIC->ITNS[0] |= (1UL << GPIO_INTA_IRQn) | (1UL << GPIO_INTB_IRQn) |
|
||||
(0xFUL << FLEXCOMM0_IRQn);
|
||||
|
||||
SAU_CTRL = SAU_INIT_CTRL_ENABLE;
|
||||
SCB_SHCSR |= SCB_SHCSR_SECUREFAULT_EN;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_UART
|
||||
void uart_init(void)
|
||||
{
|
||||
usart_config_t config;
|
||||
|
||||
CLOCK_AttachClk(kSFRO_to_FLEXCOMM3);
|
||||
IO_MUX_SetPinMux(IO_MUX_FC3_USART_DATA);
|
||||
|
||||
USART_GetDefaultConfig(&config);
|
||||
config.baudRate_Bps = RW612_UART_BAUD;
|
||||
config.enableTx = true;
|
||||
config.enableRx = true;
|
||||
(void)USART_Init(USART3, &config, RW612_UART_CLK_HZ);
|
||||
}
|
||||
|
||||
void uart_write(const char *buf, unsigned int sz)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
if (buf[i] == '\n') {
|
||||
(void)USART_WriteBlocking(USART3, (const uint8_t *)"\r", 1U);
|
||||
}
|
||||
(void)USART_WriteBlocking(USART3, (const uint8_t *)&buf[i], 1U);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
/* The boot ROM leaves the FlexSPI XIP cache off; enable it like SystemInit */
|
||||
static void rw612_xip_cache_enable(void)
|
||||
{
|
||||
if ((CACHE64_CTRL0->CCR & CACHE64_CTRL_CCR_ENCACHE_MASK) != 0U) {
|
||||
return;
|
||||
}
|
||||
CACHE64_CTRL0->CCR = CACHE64_CTRL_CCR_INVW1_MASK |
|
||||
CACHE64_CTRL_CCR_INVW0_MASK;
|
||||
CACHE64_CTRL0->CCR |= CACHE64_CTRL_CCR_GO_MASK;
|
||||
while ((CACHE64_CTRL0->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0U) {
|
||||
}
|
||||
CACHE64_CTRL0->CCR = CACHE64_CTRL_CCR_ENWRBUF_MASK |
|
||||
CACHE64_CTRL_CCR_ENCACHE_MASK;
|
||||
CACHE64_POLSEL0->REG0_TOP = 0x07FFFC00U;
|
||||
CACHE64_POLSEL0->REG1_TOP = 0x0U;
|
||||
CACHE64_POLSEL0->POLSEL = 0x1U;
|
||||
__ISB();
|
||||
__DSB();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BOOT_BENCHMARK) && defined(__WOLFBOOT)
|
||||
/* DWT CYCCNT stops without a debugger; SysTick on the 1MHz LPOSC ticks in us */
|
||||
uint64_t hal_get_timer_us(void)
|
||||
{
|
||||
static uint64_t elapsed_us;
|
||||
static uint32_t last;
|
||||
static int started;
|
||||
uint32_t now;
|
||||
|
||||
if (!started) {
|
||||
CLOCK_AttachClk(kLPOSC_to_SYSTICK_CLK);
|
||||
SysTick->LOAD = SysTick_LOAD_RELOAD_Msk;
|
||||
SysTick->VAL = 0U;
|
||||
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
|
||||
last = SysTick->VAL;
|
||||
started = 1;
|
||||
}
|
||||
now = SysTick->VAL;
|
||||
elapsed_us += (last - now) & SysTick_LOAD_RELOAD_Msk;
|
||||
last = now;
|
||||
return elapsed_us;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hal_init(void)
|
||||
{
|
||||
#ifdef __WOLFBOOT
|
||||
rw612_xip_cache_enable();
|
||||
#endif
|
||||
#if defined(__WOLFBOOT) && defined(DEBUG_UART)
|
||||
uart_init();
|
||||
#endif
|
||||
|
||||
#if defined(TZEN) && !defined(NONSECURE_APP)
|
||||
hal_sau_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
void __assert_func(const char *a, int b, const char *c, const char *d)
|
||||
{
|
||||
(void)a;
|
||||
(void)b;
|
||||
(void)c;
|
||||
(void)d;
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t RAMFUNCTION rw612_flash_offset(uint32_t address)
|
||||
{
|
||||
return (address & RW612_FLASH_ALIAS_MASK) - RW612_FLASH_BASE;
|
||||
}
|
||||
|
||||
static void RAMFUNCTION rw612_flash_cache_flush(void)
|
||||
{
|
||||
FLEXSPI->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK;
|
||||
FLEXSPI->AHBCR &= ~FLEXSPI_AHBCR_CLRAHBRXBUF_MASK;
|
||||
|
||||
CACHE64_CTRL0->CCR |= CACHE64_CTRL_CCR_INVW0_MASK |
|
||||
CACHE64_CTRL_CCR_INVW1_MASK |
|
||||
CACHE64_CTRL_CCR_GO_MASK;
|
||||
while ((CACHE64_CTRL0->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0U) {
|
||||
}
|
||||
CACHE64_CTRL0->CCR &= ~(CACHE64_CTRL_CCR_INVW0_MASK |
|
||||
CACHE64_CTRL_CCR_INVW1_MASK);
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
static int RAMFUNCTION rw612_flash_init(void)
|
||||
{
|
||||
uint32_t tree;
|
||||
uint32_t primask;
|
||||
status_t st = kStatus_Success;
|
||||
|
||||
if (rom_flexspi != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tree = ((SOCCTRL->CHIP_INFO & 0x0FU) == 0U) ?
|
||||
RW612_ROM_API_TREE_A0 : RW612_ROM_API_TREE_A1;
|
||||
memcpy(&flash_cfg, (const void *)RW612_FCB_ADDRESS, sizeof(flash_cfg));
|
||||
|
||||
primask = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
rom_flexspi = (const flexspi_nor_flash_driver_t *)(uintptr_t)
|
||||
((const uint32_t *)(uintptr_t)tree)[RW612_ROM_API_FLEXSPI_IDX];
|
||||
st = rom_flexspi->init(RW612_FLEXSPI_INSTANCE, &flash_cfg);
|
||||
rw612_flash_cache_flush();
|
||||
__set_PRIMASK(primask);
|
||||
|
||||
if (st != kStatus_Success) {
|
||||
rom_flexspi = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
||||
{
|
||||
uint32_t page[RW612_FLASH_PAGE_SIZE / sizeof(uint32_t)];
|
||||
uint32_t offset;
|
||||
uint32_t page_off;
|
||||
uint32_t chunk;
|
||||
uint32_t primask;
|
||||
uint32_t i;
|
||||
status_t st = kStatus_Success;
|
||||
int ret = 0;
|
||||
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (rw612_flash_init() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset = rw612_flash_offset(address);
|
||||
while ((len > 0) && (ret == 0)) {
|
||||
page_off = offset & (RW612_FLASH_PAGE_SIZE - 1U);
|
||||
chunk = RW612_FLASH_PAGE_SIZE - page_off;
|
||||
if (chunk > (uint32_t)len) {
|
||||
chunk = (uint32_t)len;
|
||||
}
|
||||
|
||||
/* Pad with 0xFF in a loop: memset is not in RAM during self-update */
|
||||
for (i = 0; i < (RW612_FLASH_PAGE_SIZE / sizeof(uint32_t)); i++) {
|
||||
page[i] = 0xFFFFFFFFU;
|
||||
}
|
||||
memcpy((uint8_t *)page + page_off, data, chunk);
|
||||
|
||||
primask = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
st = rom_flexspi->page_program(RW612_FLEXSPI_INSTANCE, &flash_cfg,
|
||||
offset - page_off, page, false);
|
||||
rw612_flash_cache_flush();
|
||||
__set_PRIMASK(primask);
|
||||
|
||||
if (st != kStatus_Success) {
|
||||
ret = -1;
|
||||
}
|
||||
offset += chunk;
|
||||
data += chunk;
|
||||
len -= (int)chunk;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t end;
|
||||
uint32_t primask;
|
||||
status_t st = kStatus_Success;
|
||||
int ret = 0;
|
||||
|
||||
if (len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (rw612_flash_init() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset = rw612_flash_offset(address);
|
||||
end = offset + (uint32_t)len;
|
||||
offset -= offset % RW612_FLASH_SECTOR_SIZE;
|
||||
|
||||
while ((offset < end) && (ret == 0)) {
|
||||
primask = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
st = rom_flexspi->erase_sector(RW612_FLEXSPI_INSTANCE, &flash_cfg,
|
||||
offset);
|
||||
rw612_flash_cache_flush();
|
||||
__set_PRIMASK(primask);
|
||||
|
||||
if (st != kStatus_Success) {
|
||||
ret = -1;
|
||||
}
|
||||
offset += RW612_FLASH_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(WOLFCRYPT_SECURE_MODE) && !defined(NONSECURE_APP)
|
||||
static int trng_ready;
|
||||
|
||||
void hal_trng_init(void)
|
||||
{
|
||||
trng_config_t config;
|
||||
|
||||
trng_ready = 0;
|
||||
if ((TRNG_GetDefaultConfig(&config) == kStatus_Success) &&
|
||||
(TRNG_Init(TRNG, &config) == kStatus_Success)) {
|
||||
trng_ready = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void hal_trng_fini(void)
|
||||
{
|
||||
trng_ready = 0;
|
||||
}
|
||||
|
||||
int hal_trng_get_entropy(unsigned char *out, unsigned int len)
|
||||
{
|
||||
if (!trng_ready) {
|
||||
return -1;
|
||||
}
|
||||
return (TRNG_GetRandomData(TRNG, out, len) == kStatus_Success) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* rw612.h
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#ifndef RW612_H
|
||||
#define RW612_H
|
||||
|
||||
#define RW612_FLASH_BASE 0x08000000U
|
||||
#define RW612_FLASH_ALIAS_MASK 0x0FFFFFFFU
|
||||
#ifndef RW612_FCB_ADDRESS
|
||||
#define RW612_FCB_ADDRESS (RW612_FLASH_BASE + 0x400U)
|
||||
#endif
|
||||
#define RW612_FLASH_PAGE_SIZE 256U
|
||||
/* NOR erase unit; WOLFBOOT_SECTOR_SIZE may be any multiple of it */
|
||||
#define RW612_FLASH_SECTOR_SIZE 0x1000U
|
||||
#define RW612_FLEXSPI_INSTANCE 0U
|
||||
|
||||
#ifndef RW612_ROM_API_TREE_A0
|
||||
#define RW612_ROM_API_TREE_A0 0x13024100U
|
||||
#endif
|
||||
#ifndef RW612_ROM_API_TREE_A1
|
||||
#define RW612_ROM_API_TREE_A1 0x13030000U
|
||||
#endif
|
||||
#define RW612_ROM_API_FLEXSPI_IDX 5U
|
||||
|
||||
#define RW612_UART_CLK_HZ 16000000U
|
||||
#define RW612_UART_BAUD 115200U
|
||||
|
||||
#endif /* RW612_H */
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @ARCH_FLASH_OFFSET@, LENGTH = @WOLFBOOT_NSC_ADDRESS@ - @ARCH_FLASH_OFFSET@
|
||||
RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 0x20000
|
||||
RAM_HEAP (rwx) : ORIGIN = 0x30020000, LENGTH = 0x20000
|
||||
FLASH_KEYVAULT (rw) : ORIGIN = @WOLFBOOT_KEYVAULT_ADDRESS@, LENGTH = @WOLFBOOT_KEYVAULT_SIZE@
|
||||
FLASH_NSC (rx) : ORIGIN = @WOLFBOOT_NSC_ADDRESS@, LENGTH = @WOLFBOOT_NSC_SIZE@
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
. = 0x400;
|
||||
KEEP(*(.flash_conf))
|
||||
. = 0x1000;
|
||||
_rom_image_start = .;
|
||||
KEEP(*(.isr_vector))
|
||||
*(.keystore*)
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
*(.init*)
|
||||
*(.fini*)
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > FLASH
|
||||
|
||||
.edidx :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx*)
|
||||
} > FLASH
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.ramcode*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > RAM
|
||||
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
_start_bss = .;
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
__bss_end__ = .;
|
||||
_end = .;
|
||||
} > RAM
|
||||
|
||||
.gnu.sgstubs ORIGIN(FLASH_NSC) :
|
||||
{
|
||||
. += 0x400;
|
||||
. = ALIGN(4);
|
||||
*(.gnu.sgstubs*) /* Secure Gateway Stubs */
|
||||
. = ALIGN(4);
|
||||
} > FLASH_NSC
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
END_STACK = ORIGIN(RAM) + LENGTH(RAM);
|
||||
|
||||
_flash_keyvault = ORIGIN(FLASH_KEYVAULT);
|
||||
_flash_keyvault_size = LENGTH(FLASH_KEYVAULT);
|
||||
|
||||
_start_heap = ORIGIN(RAM_HEAP);
|
||||
_heap_size = LENGTH(RAM_HEAP);
|
||||
|
||||
_rom_image_size = _stored_data + SIZEOF(.data) - _rom_image_start;
|
||||
|
|
@ -620,6 +620,18 @@ typedef void(*NMIHANDLER)(void);
|
|||
# define isr_NMI isr_empty
|
||||
#endif
|
||||
|
||||
#ifdef WOLFBOOT_ROM_IMAGE_HEADER
|
||||
/* Boot ROMs such as the RW612's read the image length at 0x20 and the load
|
||||
* address at 0x34 of the vector table */
|
||||
extern uint32_t _rom_image_size;
|
||||
extern uint32_t _rom_image_start;
|
||||
# define IV_IMAGE_LENGTH ((void (*)(void))(&_rom_image_size))
|
||||
# define IV_IMAGE_ADDRESS ((void (*)(void))(&_rom_image_start))
|
||||
#else
|
||||
# define IV_IMAGE_LENGTH 0
|
||||
# define IV_IMAGE_ADDRESS 0
|
||||
#endif
|
||||
|
||||
#ifdef CORTEX_R5
|
||||
asm volatile (
|
||||
" .sect \".isr_vector\"\n"
|
||||
|
|
@ -646,12 +658,12 @@ void (* const IV[])(void) =
|
|||
isr_fault, // BusFault
|
||||
isr_fault, // UsageFault
|
||||
isr_securefault, // SecureFault on M23/33, reserved otherwise (0)
|
||||
0, // reserved
|
||||
IV_IMAGE_LENGTH, // reserved (ROM image header: length)
|
||||
0, // reserved
|
||||
0, // reserved
|
||||
isr_empty, // SVC
|
||||
isr_empty, // DebugMonitor
|
||||
0, // reserved
|
||||
IV_IMAGE_ADDRESS, // reserved (ROM image header: load address)
|
||||
isr_empty, // PendSV
|
||||
|
||||
#ifdef TARGET_va416x0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20040000, LENGTH = 256K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.isr_vector))
|
||||
*(.init)
|
||||
*(.fini)
|
||||
*(.text*)
|
||||
KEEP(*(.rodata*))
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > FLASH
|
||||
|
||||
.ARM :
|
||||
{
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} > FLASH
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.ramcode))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
_start_bss = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
_end = .;
|
||||
} > RAM
|
||||
}
|
||||
|
||||
_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@;
|
||||
|
||||
PROVIDE(_start_heap = _end);
|
||||
PROVIDE(end = _end);
|
||||
PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM));
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.isr_vector))
|
||||
*(.init)
|
||||
*(.fini)
|
||||
*(.text*)
|
||||
KEEP(*(.rodata*))
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > FLASH
|
||||
|
||||
.ARM :
|
||||
{
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} > FLASH
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.ramcode))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
_start_bss = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
_end = .;
|
||||
} > RAM
|
||||
}
|
||||
|
||||
_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@;
|
||||
|
||||
PROVIDE(_start_heap = _end);
|
||||
PROVIDE(end = _end);
|
||||
PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM));
|
||||
|
|
@ -33,7 +33,7 @@ ifeq ($(ARCH),PPC)
|
|||
endif
|
||||
endif
|
||||
MCUXPRESSO_CMSIS?=$(MCUXPRESSO)/CMSIS
|
||||
ifneq (,$(filter $(TARGET),mcxa mcxw mcxn))
|
||||
ifneq (,$(filter $(TARGET),mcxa mcxw mcxn rw612))
|
||||
ifneq ($(WOLFCRYPT_TZ_PSA),1)
|
||||
TEST_APP_NO_RNG?=1
|
||||
endif
|
||||
|
|
@ -1036,6 +1036,30 @@ ifeq ($(TARGET),mcxn)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),rw612)
|
||||
RW612_SDK_SRCS:=\
|
||||
$(MCUXPRESSO_DRIVERS)/drivers/fsl_clock.c \
|
||||
$(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.c \
|
||||
$(MCUXPRESSO)/drivers/flexcomm/fsl_flexcomm.c \
|
||||
$(MCUXPRESSO)/drivers/flexcomm/usart/fsl_usart.c
|
||||
ifeq ($(TZEN),1)
|
||||
LSCRIPT_TEMPLATE=ARM-rw612-ns.ld
|
||||
CFLAGS:=$(filter-out -mcmse, $(CFLAGS))
|
||||
# wolfBoot's copies are built with -mcmse, which selects secure bases
|
||||
APP_OBJS+=$(addprefix rw612_ns/,$(notdir $(RW612_SDK_SRCS:.c=.o)))
|
||||
vpath %.c $(sort $(dir $(RW612_SDK_SRCS)))
|
||||
else
|
||||
LSCRIPT_TEMPLATE=ARM-rw612.ld
|
||||
# Flash writes run from the same NOR the app executes from
|
||||
../hal/$(TARGET).o: CFLAGS+=-DRAM_CODE -D__WOLFBOOT
|
||||
../test-app/libwolfboot.o: CFLAGS+=-DRAM_CODE -D__WOLFBOOT
|
||||
APP_OBJS+=$(RW612_SDK_SRCS:.c=.o)
|
||||
endif
|
||||
ifeq (,$(findstring nosys.specs,$(LDFLAGS)))
|
||||
LDFLAGS+=--specs=nosys.specs
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),lpc55s69)
|
||||
CFLAGS+=-ffunction-sections -fdata-sections
|
||||
CFLAGS+=-DWOLFSSL_NXP_RNG_1
|
||||
|
|
@ -1581,6 +1605,11 @@ delta-extra-data: image.bin
|
|||
../hal/uart/uart_drv_$(UART_TARGET)_ns.o: ../hal/uart/uart_drv_$(UART_TARGET).c FORCE
|
||||
$(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/uart/uart_drv_$(UART_TARGET).c -DNONSECURE_APP
|
||||
|
||||
rw612_ns/%.o: %.c
|
||||
@echo "\t[CC-$(ARCH)] $@"
|
||||
$(Q)mkdir -p $(dir $@)
|
||||
$(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $<
|
||||
|
||||
board_$(BOARD).o: ../hal/boards/$(BOARD)/wolfHAL_board.c
|
||||
@echo "\t[CC-$(ARCH)] $@"
|
||||
$(Q)$(CC) $(CFLAGS) -DDEBUG_UART -c $(OUTPUT_FLAG) $@ $<
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
/* app_rw612.c
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "target.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
#include "printf.h"
|
||||
|
||||
#ifdef WOLFCRYPT_SECURE_MODE
|
||||
#include "wolfssl/wolfcrypt/types.h"
|
||||
#include "wolfssl/wolfcrypt/random.h"
|
||||
#endif
|
||||
|
||||
extern void hal_init(void);
|
||||
|
||||
#ifdef WOLFCRYPT_SECURE_MODE
|
||||
static void print_random_number(void)
|
||||
{
|
||||
uint8_t rnd;
|
||||
int ret;
|
||||
|
||||
ret = wcs_get_random(&rnd, sizeof(rnd));
|
||||
if (ret != 0)
|
||||
wolfBoot_printf("Random number: generate failed (%d)\n", ret);
|
||||
else
|
||||
wolfBoot_printf("Today's lucky number: 0x%02x\n", rnd);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uint32_t boot_ver;
|
||||
|
||||
hal_init();
|
||||
|
||||
#ifdef TZEN
|
||||
boot_ver = wolfBoot_nsc_current_firmware_version();
|
||||
#else
|
||||
boot_ver = wolfBoot_current_firmware_version();
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Hello from RW612 firmware version %d\n", boot_ver);
|
||||
|
||||
#ifdef WOLFCRYPT_SECURE_MODE
|
||||
print_random_number();
|
||||
#endif
|
||||
|
||||
if (boot_ver != 1) {
|
||||
#ifdef TZEN
|
||||
wolfBoot_nsc_success();
|
||||
#else
|
||||
wolfBoot_success();
|
||||
#endif
|
||||
wolfBoot_printf("Update successful, firmware version %d confirmed\n",
|
||||
boot_ver);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
__asm__ volatile ("wfi");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
# fetch-mcuxsdk-headers.sh -- fetch the NXP MCUXpresso SDK sources the NXP HAL
|
||||
# unit tests build against, laid out like a west checkout, at pinned commits.
|
||||
#
|
||||
# usage: tools/ci/fetch-mcuxsdk-headers.sh <new-dir>
|
||||
# make -C tools/unit-tests MCUXSDK=<new-dir>
|
||||
|
||||
set -e
|
||||
|
||||
OUT="${1:?usage: fetch-mcuxsdk-headers.sh <new-dir>}"
|
||||
NXP=https://github.com/nxp-mcuxpresso
|
||||
|
||||
# fetch <dest> <repo> <commit> <paths...>: sparse, depth 1, blobs on demand
|
||||
fetch() {
|
||||
dest="$OUT${1:+/$1}"; url="$NXP/$2"; rev="$3"
|
||||
shift 3
|
||||
git init -q "$dest"
|
||||
git -C "$dest" remote add origin "$url"
|
||||
git -C "$dest" sparse-checkout set --no-cone "$@"
|
||||
git -C "$dest" fetch -q --depth 1 --filter=blob:none origin "$rev"
|
||||
git -C "$dest" checkout -q FETCH_HEAD
|
||||
}
|
||||
|
||||
if [ -e "$OUT" ]; then
|
||||
echo "$OUT already exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fetch "" mcuxsdk-core c6f4223f45fdab59509f25ee3ea63a71bd29d8aa \
|
||||
/drivers/common/ /drivers/trng/ /drivers/mcx_spc/ /drivers/flexcomm/
|
||||
fetch devices/Wireless mcux-devices-wireless \
|
||||
0d2c97770475558c72f5b6947f468bf97282df79 /RW/periph/ /RW/RW612/
|
||||
fetch devices/MCX mcux-devices-mcx \
|
||||
7c0e68e9094e3943edbe1c9bf68305556adaa2c8 /MCXA/periph/ /MCXA/MCXA153/
|
||||
|
|
@ -201,7 +201,23 @@ else
|
|||
$(info Skipping x86-64 EFI unit test: gnu-efi headers not found (set ENABLE_EFI_TESTS=1 to force))
|
||||
endif
|
||||
|
||||
# NXP HAL tests build against the real MCUXpresso SDK headers, fetched into
|
||||
# MCUXSDK by tools/ci/fetch-mcuxsdk-headers.sh, with only CMSIS mocked.
|
||||
MCUXSDK?=
|
||||
MCXA_SDK=$(MCUXSDK)/devices/MCX/MCXA/MCXA153
|
||||
RW612_SDK=$(MCUXSDK)/devices/Wireless/RW/RW612
|
||||
ifneq ($(wildcard $(MCXA_SDK)/MCXA153.h),)
|
||||
TESTS+=unit-flash-write-mcxa
|
||||
else
|
||||
$(info Skipping MCXA unit test: set MCUXSDK (see tools/ci/fetch-mcuxsdk-headers.sh))
|
||||
endif
|
||||
ifneq ($(wildcard $(RW612_SDK)/RW612.h),)
|
||||
TESTS+=unit-rw612-flash
|
||||
TESTS+=unit-rw612-tz
|
||||
TESTS+=unit-rw612-uart
|
||||
else
|
||||
$(info Skipping RW612 unit tests: set MCUXSDK (see tools/ci/fetch-mcuxsdk-headers.sh))
|
||||
endif
|
||||
TESTS+=unit-flash-write-nrf52
|
||||
TESTS+=unit-nrf5340-uart-crlf
|
||||
TESTS+=unit-flash-write-samr21
|
||||
|
|
@ -1917,10 +1933,29 @@ unit-t10xx-dts-memac: unit-t10xx-dts-memac.c nxp_t10xx_fixup_extract.h ../../src
|
|||
unit-efi-x86-open-image: unit-efi-x86-open-image.c ../../hal/x86_64_efi.c
|
||||
gcc -o $@ unit-efi-x86-open-image.c -I/usr/include/efi -I/usr/include/efi/x86_64 -DTARGET_X86_64_EFI -fshort-wchar $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
# unit-flash-write-mcxa includes hal/mcxa.c directly, with mcxa_fsl_stub/
|
||||
# standing in for the (not vendored) NXP MCUXpresso SDK headers it includes.
|
||||
# NXP HAL tests include hal/<chip>.c directly. SDK headers go in as -isystem
|
||||
# so their own 64-bit-host warnings stay out of the test build.
|
||||
MCUXSDK_TEST_INC=-Icmsis_stub -isystem $(MCUXSDK)/drivers/common
|
||||
MCXA_TEST_INC=-DCPU_MCXA153VLH $(MCUXSDK_TEST_INC) -isystem $(MCXA_SDK) \
|
||||
-isystem $(MCXA_SDK)/../periph -isystem $(MCXA_SDK)/drivers \
|
||||
-isystem $(MCXA_SDK)/drivers/romapi -isystem $(MCUXSDK)/drivers/mcx_spc
|
||||
RW612_TEST_INC=-DCPU_RW612ETA2I $(MCUXSDK_TEST_INC) -isystem $(RW612_SDK) \
|
||||
-isystem $(RW612_SDK)/../periph -isystem $(RW612_SDK)/drivers \
|
||||
-isystem $(RW612_SDK)/drivers/romapi/flexspi -isystem $(MCUXSDK)/drivers/trng
|
||||
|
||||
unit-flash-write-mcxa: unit-flash-write-mcxa.c ../../hal/mcxa.c
|
||||
gcc -o $@ unit-flash-write-mcxa.c -Imcxa_fsl_stub $(CFLAGS) $(LDFLAGS)
|
||||
gcc -o $@ unit-flash-write-mcxa.c $(MCXA_TEST_INC) $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-rw612-flash: unit-rw612-flash.c ../../hal/rw612.c
|
||||
gcc -o $@ unit-rw612-flash.c $(RW612_TEST_INC) $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-rw612-tz: unit-rw612-tz.c ../../hal/rw612.c
|
||||
gcc -o $@ unit-rw612-tz.c $(RW612_TEST_INC) -I../.. $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-rw612-uart: unit-rw612-uart.c ../../hal/rw612.c
|
||||
gcc -o $@ unit-rw612-uart.c -DDEBUG_UART $(RW612_TEST_INC) \
|
||||
-isystem $(MCUXSDK)/drivers/flexcomm \
|
||||
-isystem $(MCUXSDK)/drivers/flexcomm/usart $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-flash-write-nrf52: unit-flash-write-nrf52.c ../../hal/nrf52.c
|
||||
gcc -o $@ unit-flash-write-nrf52.c -DTARGET_nrf52 -I../../hal $(CFLAGS) $(LDFLAGS)
|
||||
|
|
@ -1998,9 +2033,10 @@ GENERATED_SRC:=aurix_erased_extract.h \
|
|||
|
||||
# Tests that are only in $(TESTS) when their toolchain/headers are
|
||||
# present; clean must remove them regardless, or a host that has since
|
||||
# lost gnu-efi/multilib keeps a stale binary forever.
|
||||
# lost gnu-efi/multilib/the MCUXpresso SDK keeps a stale binary forever.
|
||||
CONDITIONAL_TESTS:=unit-efi-x86-open-image unit-linux-loader-e820 \
|
||||
unit-linux-loader-syssize
|
||||
unit-linux-loader-syssize unit-flash-write-mcxa unit-rw612-flash \
|
||||
unit-rw612-tz unit-rw612-uart
|
||||
|
||||
clean: covclean
|
||||
rm -f $(TESTS) $(CONDITIONAL_TESTS) $(GENERATED_SRC) *.o *.gcno *.gcda \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
/* core_cm33.h
|
||||
*
|
||||
* Host stand-in for the Arm CMSIS core header, so the HAL unit tests build
|
||||
* against a vendor's real device headers: core registers and intrinsics are
|
||||
* mock objects the tests define and inspect.
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#ifndef CORE_CM33_STUB_H
|
||||
#define CORE_CM33_STUB_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define __I volatile const
|
||||
#define __O volatile
|
||||
#define __IO volatile
|
||||
#define __IM volatile const
|
||||
#define __OM volatile
|
||||
#define __IOM volatile
|
||||
#define __STATIC_INLINE static inline
|
||||
#define __STATIC_FORCEINLINE static inline
|
||||
#define __WEAK __attribute__((weak))
|
||||
#define __ASM __asm__
|
||||
#define __CORTEX_M 33U
|
||||
|
||||
typedef struct {
|
||||
__IOM uint32_t ISER[16];
|
||||
uint32_t RESERVED0[16];
|
||||
__IOM uint32_t ICER[16];
|
||||
uint32_t RESERVED1[16];
|
||||
__IOM uint32_t ISPR[16];
|
||||
uint32_t RESERVED2[16];
|
||||
__IOM uint32_t ICPR[16];
|
||||
uint32_t RESERVED3[16];
|
||||
__IOM uint32_t IABR[16];
|
||||
uint32_t RESERVED4[16];
|
||||
__IOM uint32_t ITNS[16];
|
||||
uint32_t RESERVED5[16];
|
||||
__IOM uint8_t IPR[496];
|
||||
} NVIC_Type;
|
||||
|
||||
typedef struct {
|
||||
__IOM uint32_t CTRL;
|
||||
__IOM uint32_t LOAD;
|
||||
__IOM uint32_t VAL;
|
||||
__IM uint32_t CALIB;
|
||||
} SysTick_Type;
|
||||
#define SysTick_CTRL_ENABLE_Msk (1UL << 0)
|
||||
#define SysTick_CTRL_TICKINT_Msk (1UL << 1)
|
||||
#define SysTick_LOAD_RELOAD_Msk 0xFFFFFFUL
|
||||
|
||||
extern NVIC_Type mock_nvic;
|
||||
extern SysTick_Type mock_systick;
|
||||
#define NVIC (&mock_nvic)
|
||||
#define SysTick (&mock_systick)
|
||||
|
||||
extern uint32_t mock_primask;
|
||||
extern int mock_barriers;
|
||||
static inline uint32_t __get_PRIMASK(void) { return mock_primask; }
|
||||
static inline void __set_PRIMASK(uint32_t v) { mock_primask = v; }
|
||||
static inline void __disable_irq(void) { mock_primask = 1; }
|
||||
static inline void __enable_irq(void) { mock_primask = 0; }
|
||||
static inline void __DSB(void) { mock_barriers++; }
|
||||
static inline void __ISB(void) { }
|
||||
static inline void __DMB(void) { }
|
||||
static inline void __NOP(void) { }
|
||||
static inline uint32_t __get_IPSR(void) { return 0; }
|
||||
|
||||
static inline void NVIC_EnableIRQ(IRQn_Type irq)
|
||||
{
|
||||
NVIC->ISER[(uint32_t)irq >> 5] = 1UL << ((uint32_t)irq & 0x1FUL);
|
||||
}
|
||||
static inline void NVIC_DisableIRQ(IRQn_Type irq)
|
||||
{
|
||||
NVIC->ICER[(uint32_t)irq >> 5] = 1UL << ((uint32_t)irq & 0x1FUL);
|
||||
}
|
||||
static inline void NVIC_ClearPendingIRQ(IRQn_Type irq)
|
||||
{
|
||||
NVIC->ICPR[(uint32_t)irq >> 5] = 1UL << ((uint32_t)irq & 0x1FUL);
|
||||
}
|
||||
static inline void NVIC_SetPriority(IRQn_Type irq, uint32_t priority)
|
||||
{
|
||||
NVIC->IPR[(uint32_t)irq] = (uint8_t)priority;
|
||||
}
|
||||
static inline void NVIC_SetTargetState(IRQn_Type irq)
|
||||
{
|
||||
NVIC->ITNS[(uint32_t)irq >> 5] |= 1UL << ((uint32_t)irq & 0x1FUL);
|
||||
}
|
||||
|
||||
#endif /* CORE_CM33_STUB_H */
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
/* Stub: hal/mcxa.c only uses this under #ifdef __WOLFBOOT, which the unit
|
||||
* test does not define. */
|
||||
#ifndef FSL_CLOCK_STUB_H
|
||||
#define FSL_CLOCK_STUB_H
|
||||
#endif /* FSL_CLOCK_STUB_H */
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/* Minimal stand-in for the NXP MCUXpresso SDK header of the same name.
|
||||
* hal/mcxa.c only needs the flash config type and a status code from it;
|
||||
* the real SDK is not vendored in this source tree. */
|
||||
#ifndef FSL_COMMON_STUB_H
|
||||
#define FSL_COMMON_STUB_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int status_t;
|
||||
#define kStatus_Success 0
|
||||
|
||||
/* Opaque driver state in the real SDK; the stub needs no fields. */
|
||||
typedef struct {
|
||||
uint32_t unused;
|
||||
} flash_config_t;
|
||||
|
||||
#endif /* FSL_COMMON_STUB_H */
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
/* Minimal stand-in for the NXP MCUXpresso SDK ROM flash API declarations
|
||||
* used by hal/mcxa.c's hal_flash_write()/hal_flash_erase(). The unit test
|
||||
* provides the definitions of these functions. Parameter names match the
|
||||
* real MCXA ROM API (fsl_romapi.h): FLASH_EraseSector takes a length in
|
||||
* bytes, not a sector count. */
|
||||
#ifndef FSL_ROMAPI_STUB_H
|
||||
#define FSL_ROMAPI_STUB_H
|
||||
|
||||
status_t FLASH_ProgramPhrase(flash_config_t *config, uint32_t start,
|
||||
uint8_t *src, uint32_t length_in_bytes);
|
||||
status_t FLASH_EraseSector(flash_config_t *config, uint32_t start,
|
||||
uint32_t length_in_bytes, uint32_t key);
|
||||
|
||||
#define kFLASH_ApiEraseKey 0x6b65796b
|
||||
|
||||
#endif /* FSL_ROMAPI_STUB_H */
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
/* Stub: hal/mcxa.c only uses this under #ifdef __WOLFBOOT, which the unit
|
||||
* test does not define. */
|
||||
#ifndef FSL_SPC_STUB_H
|
||||
#define FSL_SPC_STUB_H
|
||||
#endif /* FSL_SPC_STUB_H */
|
||||
|
|
@ -41,10 +41,11 @@
|
|||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* NXP MCUXpresso SDK stand-ins (mcxa_fsl_stub/) provide flash_config_t,
|
||||
* status_t and the FLASH_* prototypes hal/mcxa.c expects; this test supplies
|
||||
* their bodies below, simulating flash programming as a plain memcpy onto
|
||||
* the mock buffer that "address" points into. */
|
||||
/* The NXP SDK ROM API calls through a fixed ROM address; point it at a mock
|
||||
* tree whose flash driver simulates programming as a memcpy onto the buffer
|
||||
* that "address" points into. */
|
||||
extern struct _bootloader_tree mock_rom_api;
|
||||
#define FSL_FEATURE_ROMAPI_BASE ((uintptr_t)&mock_rom_api)
|
||||
#include "fsl_common.h"
|
||||
#include "fsl_romapi.h"
|
||||
#include "image.h"
|
||||
|
|
@ -54,7 +55,7 @@ static int erase_calls;
|
|||
static uint32_t last_erase_start;
|
||||
static uint32_t last_erase_len;
|
||||
|
||||
status_t FLASH_ProgramPhrase(flash_config_t *config, uint32_t start,
|
||||
static status_t mock_program_phrase(flash_config_t *config, uint32_t start,
|
||||
uint8_t *src, uint32_t len)
|
||||
{
|
||||
(void)config;
|
||||
|
|
@ -62,7 +63,7 @@ status_t FLASH_ProgramPhrase(flash_config_t *config, uint32_t start,
|
|||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t FLASH_EraseSector(flash_config_t *config, uint32_t start,
|
||||
static status_t mock_erase_sector(flash_config_t *config, uint32_t start,
|
||||
uint32_t length_in_bytes, uint32_t key)
|
||||
{
|
||||
(void)config; (void)key;
|
||||
|
|
@ -72,22 +73,35 @@ status_t FLASH_EraseSector(flash_config_t *config, uint32_t start,
|
|||
return kStatus_Success;
|
||||
}
|
||||
|
||||
static flash_driver_interface_t mock_flash_driver;
|
||||
bootloader_tree_t mock_rom_api;
|
||||
|
||||
#include "../../hal/mcxa.c"
|
||||
|
||||
/* hal_flash_write() treats "address" as a real pointer into memory-mapped
|
||||
* flash (it memcpy()s from it directly). struct fields carrying such
|
||||
* addresses are uint32_t, matching the real 32-bit target. MAP_32BIT keeps
|
||||
* the mock flash buffer inside that range on a 64-bit test host, exactly as
|
||||
* unit-ata-security-passphrase-zeroize.c does for 32-bit DMA pointers. */
|
||||
* addresses are uint32_t, matching the real 32-bit target, so the mock flash
|
||||
* buffer is mapped below 4GB (MAP_32BIT on x86-64, a low hint elsewhere). */
|
||||
#ifdef MAP_32BIT
|
||||
#define MOCK_FLASH_HINT NULL
|
||||
#define MOCK_FLASH_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT)
|
||||
#else
|
||||
#define MOCK_FLASH_HINT ((void *)0x20000000UL)
|
||||
#define MOCK_FLASH_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
|
||||
#endif
|
||||
#define MOCK_FLASH_SIZE 64
|
||||
static uint8_t *mock_flash;
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
mock_flash = mmap(NULL, MOCK_FLASH_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||
mock_flash = mmap(MOCK_FLASH_HINT, MOCK_FLASH_SIZE, PROT_READ | PROT_WRITE,
|
||||
MOCK_FLASH_FLAGS, -1, 0);
|
||||
ck_assert_ptr_ne(mock_flash, MAP_FAILED);
|
||||
ck_assert_uint_lt((uintptr_t)mock_flash, 0xFFFFFFFFUL - MOCK_FLASH_SIZE);
|
||||
memset(mock_flash, 0xFF, MOCK_FLASH_SIZE);
|
||||
mock_flash_driver.flash_program_phrase = mock_program_phrase;
|
||||
mock_flash_driver.flash_erase_sector = mock_erase_sector;
|
||||
mock_rom_api.flash_driver = &mock_flash_driver;
|
||||
erase_calls = 0;
|
||||
last_erase_start = 0;
|
||||
last_erase_len = 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,442 @@
|
|||
/* unit-rw612-flash.c
|
||||
*
|
||||
* Unit tests for the RW612 FlexSPI NOR flash HAL (hal/rw612.c), run against
|
||||
* a mock boot ROM FlexSPI driver and a mock NOR array.
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* RW612 layout (config/examples/rw612*.config) with the 8KB logical sector
|
||||
* ML-DSA builds use over the 4KB NOR erase sector, in place of target.h */
|
||||
#define H_TARGETS_TARGET_
|
||||
#define WOLFBOOT_FIXED_PARTITIONS
|
||||
#define WOLFBOOT_SECTOR_SIZE 0x2000
|
||||
#define WOLFBOOT_PARTITION_SIZE 0x100000
|
||||
#define WOLFBOOT_PARTITION_BOOT_ADDRESS 0x08100000
|
||||
#define WOLFBOOT_PARTITION_UPDATE_ADDRESS 0x08200000
|
||||
#define WOLFBOOT_PARTITION_SWAP_ADDRESS 0x08300000
|
||||
#define WOLFBOOT_NSC_ADDRESS 0x08060000
|
||||
#define WOLFBOOT_NSC_SIZE 0x2000
|
||||
#define WOLFBOOT_KEYVAULT_ADDRESS 0x08400000
|
||||
#define WOLFBOOT_KEYVAULT_SIZE 0x20000
|
||||
|
||||
#include "fsl_common.h"
|
||||
#include "fsl_romapi_flexspi.h"
|
||||
|
||||
static FLEXSPI_Type mock_flexspi;
|
||||
static CACHE64_CTRL_Type mock_cache64;
|
||||
CACHE64_POLSEL_Type mock_cache64_polsel;
|
||||
SOCCIU_Type mock_socctrl;
|
||||
NVIC_Type mock_nvic;
|
||||
uint32_t mock_primask;
|
||||
int mock_barriers;
|
||||
static int ahb_rx_clears;
|
||||
static int cache_invalidations;
|
||||
static uint32_t cache64_seen;
|
||||
|
||||
FLEXSPI_Type *mock_flexspi_regs(void)
|
||||
{
|
||||
if ((mock_flexspi.AHBCR & FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) != 0U)
|
||||
ahb_rx_clears++;
|
||||
return &mock_flexspi;
|
||||
}
|
||||
|
||||
CACHE64_CTRL_Type *mock_cache64_regs(void)
|
||||
{
|
||||
cache64_seen |= mock_cache64.CCR;
|
||||
if ((mock_cache64.CCR & CACHE64_CTRL_CCR_GO_MASK) != 0U) {
|
||||
cache_invalidations++;
|
||||
mock_cache64.CCR &= ~CACHE64_CTRL_CCR_GO_MASK;
|
||||
}
|
||||
return &mock_cache64;
|
||||
}
|
||||
|
||||
/* The HAL reads 32-bit ROM table pointers, so the mock tables live below 4GB */
|
||||
static uint8_t *mock_rom;
|
||||
static uint8_t mock_fcb[sizeof(flexspi_nor_config_t)];
|
||||
|
||||
#define RW612_ROM_API_TREE_A0 ((uint32_t)(uintptr_t)mock_rom)
|
||||
#define RW612_ROM_API_TREE_A1 ((uint32_t)(uintptr_t)(mock_rom + 0x40))
|
||||
#define RW612_FCB_ADDRESS ((uintptr_t)mock_fcb)
|
||||
|
||||
/* Point the SDK peripheral instances at the mocks above */
|
||||
#undef FLEXSPI
|
||||
#undef CACHE64_CTRL0
|
||||
#undef CACHE64_POLSEL0
|
||||
#undef SOCCTRL
|
||||
#define FLEXSPI (mock_flexspi_regs())
|
||||
#define CACHE64_CTRL0 (mock_cache64_regs())
|
||||
#define CACHE64_POLSEL0 (&mock_cache64_polsel)
|
||||
#define SOCCTRL (&mock_socctrl)
|
||||
|
||||
#include "../../hal/rw612.c"
|
||||
|
||||
#ifdef MAP_32BIT
|
||||
#define MOCK_ROM_HINT NULL
|
||||
#define MOCK_ROM_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT)
|
||||
#else
|
||||
#define MOCK_ROM_HINT ((void *)0x10000000UL)
|
||||
#define MOCK_ROM_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
|
||||
#endif
|
||||
|
||||
#define MOCK_NOR_SIZE 0x8000
|
||||
#define MOCK_MAX_CALLS 16
|
||||
#define TEST_BASE 0x08001000U
|
||||
|
||||
static uint8_t mock_nor[MOCK_NOR_SIZE];
|
||||
static flexspi_nor_flash_driver_t *drv_a0;
|
||||
static flexspi_nor_flash_driver_t *drv_a1;
|
||||
|
||||
static int init_calls;
|
||||
static int program_calls;
|
||||
static int erase_calls;
|
||||
static status_t init_ret;
|
||||
static int fail_program_at;
|
||||
static int fail_erase_at;
|
||||
static int init_cfg_ok;
|
||||
static int rom_irq_enabled;
|
||||
static uint32_t program_dst[MOCK_MAX_CALLS];
|
||||
static uint32_t erase_addr[MOCK_MAX_CALLS];
|
||||
|
||||
static status_t mock_init(uint32_t instance, flexspi_nor_config_t *config)
|
||||
{
|
||||
(void)instance;
|
||||
init_calls++;
|
||||
if (mock_primask == 0U)
|
||||
rom_irq_enabled = 1;
|
||||
init_cfg_ok = (memcmp(config, mock_fcb, sizeof(*config)) == 0);
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
static status_t mock_page_program(uint32_t instance,
|
||||
flexspi_nor_config_t *config, uint32_t dst, const uint32_t *src,
|
||||
bool keepState)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint32_t i;
|
||||
|
||||
(void)instance; (void)config; (void)keepState;
|
||||
if (mock_primask == 0U)
|
||||
rom_irq_enabled = 1;
|
||||
if (program_calls < MOCK_MAX_CALLS)
|
||||
program_dst[program_calls] = dst;
|
||||
program_calls++;
|
||||
if (program_calls == fail_program_at)
|
||||
return kStatus_Fail;
|
||||
ck_assert_uint_eq(dst % RW612_FLASH_PAGE_SIZE, 0);
|
||||
ck_assert_uint_le(dst + RW612_FLASH_PAGE_SIZE, MOCK_NOR_SIZE);
|
||||
/* NOR programming can only clear bits */
|
||||
for (i = 0; i < RW612_FLASH_PAGE_SIZE; i++)
|
||||
mock_nor[dst + i] &= p[i];
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
static status_t mock_erase_sector(uint32_t instance,
|
||||
flexspi_nor_config_t *config, uint32_t address)
|
||||
{
|
||||
(void)instance; (void)config;
|
||||
if (mock_primask == 0U)
|
||||
rom_irq_enabled = 1;
|
||||
if (erase_calls < MOCK_MAX_CALLS)
|
||||
erase_addr[erase_calls] = address;
|
||||
erase_calls++;
|
||||
if (erase_calls == fail_erase_at)
|
||||
return kStatus_Fail;
|
||||
ck_assert_uint_eq(address % RW612_FLASH_SECTOR_SIZE, 0);
|
||||
ck_assert_uint_le(address + RW612_FLASH_SECTOR_SIZE, MOCK_NOR_SIZE);
|
||||
memset(mock_nor + address, 0xFF, RW612_FLASH_SECTOR_SIZE);
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
mock_rom = mmap(MOCK_ROM_HINT, 4096, PROT_READ | PROT_WRITE,
|
||||
MOCK_ROM_FLAGS, -1, 0);
|
||||
ck_assert_ptr_ne(mock_rom, MAP_FAILED);
|
||||
ck_assert_uint_lt((uintptr_t)mock_rom, 0xFFFFF000UL);
|
||||
drv_a0 = (flexspi_nor_flash_driver_t *)(mock_rom + 0x100);
|
||||
drv_a1 = (flexspi_nor_flash_driver_t *)(mock_rom + 0x200);
|
||||
drv_a0->init = drv_a1->init = mock_init;
|
||||
drv_a0->page_program = drv_a1->page_program = mock_page_program;
|
||||
drv_a0->erase_sector = drv_a1->erase_sector = mock_erase_sector;
|
||||
((uint32_t *)(uintptr_t)RW612_ROM_API_TREE_A0)[RW612_ROM_API_FLEXSPI_IDX] =
|
||||
(uint32_t)(uintptr_t)drv_a0;
|
||||
((uint32_t *)(uintptr_t)RW612_ROM_API_TREE_A1)[RW612_ROM_API_FLEXSPI_IDX] =
|
||||
(uint32_t)(uintptr_t)drv_a1;
|
||||
|
||||
for (i = 0; i < sizeof(mock_fcb); i++)
|
||||
mock_fcb[i] = (uint8_t)i;
|
||||
memset(mock_nor, 0xFF, sizeof(mock_nor));
|
||||
*(uint32_t *)&mock_socctrl.CHIP_INFO = 1U;
|
||||
mock_primask = 0U;
|
||||
mock_barriers = 0;
|
||||
memset(&mock_flexspi, 0, sizeof(mock_flexspi));
|
||||
memset(&mock_cache64, 0, sizeof(mock_cache64));
|
||||
ahb_rx_clears = cache_invalidations = 0;
|
||||
cache64_seen = 0;
|
||||
rom_flexspi = NULL;
|
||||
init_calls = program_calls = erase_calls = 0;
|
||||
init_ret = kStatus_Success;
|
||||
fail_program_at = fail_erase_at = 0;
|
||||
init_cfg_ok = 0;
|
||||
rom_irq_enabled = 0;
|
||||
}
|
||||
|
||||
static void teardown(void)
|
||||
{
|
||||
munmap(mock_rom, 4096);
|
||||
}
|
||||
|
||||
START_TEST(test_write_unaligned_keeps_page_neighbours)
|
||||
{
|
||||
uint8_t data[8];
|
||||
int i;
|
||||
|
||||
memset(mock_nor + 0x1000, 0x5A, 16);
|
||||
for (i = 0; i < 8; i++)
|
||||
data[i] = (uint8_t)(0xA0 + i);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE + 0x20, data, 8), 0);
|
||||
ck_assert_int_eq(program_calls, 1);
|
||||
ck_assert_uint_eq(program_dst[0], 0x1000);
|
||||
for (i = 0; i < 16; i++)
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + i], 0x5A);
|
||||
for (i = 0; i < 8; i++)
|
||||
ck_assert_uint_eq(mock_nor[0x1020 + i], data[i]);
|
||||
for (i = 0x1028; i < 0x1100; i++)
|
||||
ck_assert_uint_eq(mock_nor[i], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_write_spanning_pages)
|
||||
{
|
||||
uint8_t data[20];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
data[i] = (uint8_t)(i + 1);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE + 250, data, 20), 0);
|
||||
ck_assert_int_eq(program_calls, 2);
|
||||
ck_assert_uint_eq(program_dst[0], 0x1000);
|
||||
ck_assert_uint_eq(program_dst[1], 0x1100);
|
||||
for (i = 0; i < 20; i++)
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + 250 + i], data[i]);
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + 249], 0xFF);
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + 270], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_write_secure_alias)
|
||||
{
|
||||
uint8_t data[4] = { 0x11, 0x22, 0x33, 0x44 };
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(0x18001010U, data, 4), 0);
|
||||
ck_assert_int_eq(program_calls, 1);
|
||||
ck_assert_uint_eq(program_dst[0], 0x1000);
|
||||
ck_assert_mem_eq(mock_nor + 0x1010, data, 4);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_write_invalid_len)
|
||||
{
|
||||
uint8_t data[1] = { 0 };
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, -1), -1);
|
||||
ck_assert_int_eq(init_calls, 0);
|
||||
ck_assert_int_eq(program_calls, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_write_stops_on_failure)
|
||||
{
|
||||
uint8_t data[600];
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
fail_program_at = 1;
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, (int)sizeof(data)), -1);
|
||||
ck_assert_int_eq(program_calls, 1);
|
||||
ck_assert_uint_eq(mock_primask, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_init_once_with_fcb_copy)
|
||||
{
|
||||
uint8_t data[4] = { 0 };
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, 4), 0);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, WOLFBOOT_SECTOR_SIZE), 0);
|
||||
ck_assert_int_eq(init_calls, 1);
|
||||
ck_assert_int_eq(init_cfg_ok, 1);
|
||||
ck_assert_ptr_eq(rom_flexspi, drv_a1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_init_selects_a0_tree)
|
||||
{
|
||||
*(uint32_t *)&mock_socctrl.CHIP_INFO = 0U;
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, WOLFBOOT_SECTOR_SIZE), 0);
|
||||
ck_assert_ptr_eq(rom_flexspi, drv_a0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_init_failure_retries)
|
||||
{
|
||||
uint8_t data[4] = { 0 };
|
||||
|
||||
init_ret = kStatus_Fail;
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, 4), -1);
|
||||
ck_assert_int_eq(program_calls, 0);
|
||||
ck_assert_ptr_null(rom_flexspi);
|
||||
ck_assert_uint_eq(mock_primask, 0);
|
||||
|
||||
init_ret = kStatus_Success;
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, 4), 0);
|
||||
ck_assert_int_eq(init_calls, 2);
|
||||
ck_assert_int_eq(program_calls, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_rounds_to_sectors)
|
||||
{
|
||||
memset(mock_nor + 0x1000, 0x00, 3 * RW612_FLASH_SECTOR_SIZE);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE + RW612_FLASH_SECTOR_SIZE / 2,
|
||||
RW612_FLASH_SECTOR_SIZE), 0);
|
||||
ck_assert_int_eq(erase_calls, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], 0x1000);
|
||||
ck_assert_uint_eq(erase_addr[1], 0x1000 + RW612_FLASH_SECTOR_SIZE);
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + 2 * RW612_FLASH_SECTOR_SIZE], 0x00);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_covers_whole_logical_sector)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(mock_nor + 0x1000, 0x00, WOLFBOOT_SECTOR_SIZE + 1);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, WOLFBOOT_SECTOR_SIZE), 0);
|
||||
ck_assert_int_eq(erase_calls,
|
||||
(int)(WOLFBOOT_SECTOR_SIZE / RW612_FLASH_SECTOR_SIZE));
|
||||
for (i = 0; i < WOLFBOOT_SECTOR_SIZE; i++)
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + i], 0xFF);
|
||||
ck_assert_uint_eq(mock_nor[0x1000 + WOLFBOOT_SECTOR_SIZE], 0x00);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_invalid_len)
|
||||
{
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, 0), -1);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, -1), -1);
|
||||
ck_assert_int_eq(init_calls, 0);
|
||||
ck_assert_int_eq(erase_calls, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_stops_on_failure)
|
||||
{
|
||||
fail_erase_at = 1;
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, 2 * WOLFBOOT_SECTOR_SIZE), -1);
|
||||
ck_assert_int_eq(erase_calls, 1);
|
||||
ck_assert_uint_eq(mock_primask, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_primask_preserved)
|
||||
{
|
||||
uint8_t data[300];
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE, data, (int)sizeof(data)), 0);
|
||||
ck_assert_uint_eq(mock_primask, 0);
|
||||
ck_assert_int_eq(rom_irq_enabled, 0);
|
||||
|
||||
mock_primask = 1U;
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE + 0x400, data,
|
||||
(int)sizeof(data)), 0);
|
||||
ck_assert_uint_eq(mock_primask, 1);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, WOLFBOOT_SECTOR_SIZE), 0);
|
||||
ck_assert_uint_eq(mock_primask, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_cache_flushed_after_each_rom_call)
|
||||
{
|
||||
uint8_t data[20] = { 0 };
|
||||
const uint32_t inv = CACHE64_CTRL_CCR_INVW0_MASK |
|
||||
CACHE64_CTRL_CCR_INVW1_MASK;
|
||||
int rom_ops;
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(TEST_BASE + 250, data, 20), 0);
|
||||
ck_assert_int_eq(hal_flash_erase(TEST_BASE, WOLFBOOT_SECTOR_SIZE), 0);
|
||||
rom_ops = init_calls + program_calls + erase_calls;
|
||||
ck_assert_int_eq(rom_ops,
|
||||
3 + (int)(WOLFBOOT_SECTOR_SIZE / RW612_FLASH_SECTOR_SIZE));
|
||||
ck_assert_int_eq(ahb_rx_clears, rom_ops);
|
||||
ck_assert_int_eq(cache_invalidations, rom_ops);
|
||||
ck_assert_int_eq(mock_barriers, rom_ops);
|
||||
ck_assert_uint_eq(cache64_seen & inv, inv);
|
||||
ck_assert_uint_eq(mock_cache64.CCR & inv, 0);
|
||||
ck_assert_uint_eq(mock_flexspi.AHBCR & FLEXSPI_AHBCR_CLRAHBRXBUF_MASK, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *rw612_flash_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("rw612-flash");
|
||||
TCase *tc = tcase_create("rw612-flash");
|
||||
|
||||
tcase_add_checked_fixture(tc, setup, teardown);
|
||||
tcase_add_test(tc, test_write_unaligned_keeps_page_neighbours);
|
||||
tcase_add_test(tc, test_write_spanning_pages);
|
||||
tcase_add_test(tc, test_write_secure_alias);
|
||||
tcase_add_test(tc, test_write_invalid_len);
|
||||
tcase_add_test(tc, test_write_stops_on_failure);
|
||||
tcase_add_test(tc, test_init_once_with_fcb_copy);
|
||||
tcase_add_test(tc, test_init_selects_a0_tree);
|
||||
tcase_add_test(tc, test_init_failure_retries);
|
||||
tcase_add_test(tc, test_erase_rounds_to_sectors);
|
||||
tcase_add_test(tc, test_erase_covers_whole_logical_sector);
|
||||
tcase_add_test(tc, test_erase_invalid_len);
|
||||
tcase_add_test(tc, test_erase_stops_on_failure);
|
||||
tcase_add_test(tc, test_primask_preserved);
|
||||
tcase_add_test(tc, test_cache_flushed_after_each_rom_call);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
Suite *s = rw612_flash_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -0,0 +1,427 @@
|
|||
/* unit-rw612-tz.c
|
||||
*
|
||||
* Unit tests for the RW612 secure-world HAL in hal/rw612.c: the TrustZone
|
||||
* attribution map and the SA_TRNG entropy source used by wolfCrypt.
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TZEN
|
||||
#define WOLFCRYPT_SECURE_MODE
|
||||
/* Build the bootloader side of the HAL, including the XIP cache setup */
|
||||
#define __WOLFBOOT
|
||||
#define WOLFBOOT_HASH_SHA256
|
||||
#define WOLFBOOT_SIGN_ECC256
|
||||
#define BOOT_BENCHMARK
|
||||
/* Replace hal/armv8m_tz.h with a recording SAU */
|
||||
#define TZ_INCLUDED
|
||||
|
||||
#define RW612_SAU_REGIONS 8
|
||||
|
||||
struct sau_region {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
int nsc;
|
||||
int set;
|
||||
};
|
||||
|
||||
static struct sau_region sau[RW612_SAU_REGIONS];
|
||||
static int sau_bad_region;
|
||||
static uint32_t mock_sau_ctrl;
|
||||
static uint32_t mock_shcsr;
|
||||
|
||||
#define SAU_CTRL mock_sau_ctrl
|
||||
#define SAU_INIT_CTRL_ENABLE (1U << 0)
|
||||
#define SCB_SHCSR mock_shcsr
|
||||
#define SCB_SHCSR_SECUREFAULT_EN (1U << 19)
|
||||
|
||||
static void sau_init_region(uint32_t region, uint32_t start_addr,
|
||||
uint32_t end_addr, int secure)
|
||||
{
|
||||
if (region >= RW612_SAU_REGIONS) {
|
||||
sau_bad_region = 1;
|
||||
return;
|
||||
}
|
||||
sau[region].start = start_addr;
|
||||
sau[region].end = end_addr;
|
||||
sau[region].nsc = secure;
|
||||
sau[region].set = 1;
|
||||
}
|
||||
|
||||
/* RW612 layout (config/examples/rw612*.config) with the 8KB logical sector
|
||||
* ML-DSA builds use over the 4KB NOR erase sector, in place of target.h */
|
||||
#define H_TARGETS_TARGET_
|
||||
#define WOLFBOOT_FIXED_PARTITIONS
|
||||
#define WOLFBOOT_SECTOR_SIZE 0x2000
|
||||
#define WOLFBOOT_PARTITION_SIZE 0x100000
|
||||
#define WOLFBOOT_PARTITION_BOOT_ADDRESS 0x08100000
|
||||
#define WOLFBOOT_PARTITION_UPDATE_ADDRESS 0x08200000
|
||||
#define WOLFBOOT_PARTITION_SWAP_ADDRESS 0x08300000
|
||||
#define WOLFBOOT_NSC_ADDRESS 0x08060000
|
||||
#define WOLFBOOT_NSC_SIZE 0x2000
|
||||
#define WOLFBOOT_KEYVAULT_ADDRESS 0x08400000
|
||||
#define WOLFBOOT_KEYVAULT_SIZE 0x20000
|
||||
|
||||
#include "fsl_common.h"
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_romapi_flexspi.h"
|
||||
#include "fsl_trng.h"
|
||||
|
||||
TRNG_Type mock_trng;
|
||||
static int trng_init_calls;
|
||||
static int trng_read_calls;
|
||||
static status_t trng_init_ret;
|
||||
static status_t trng_read_ret;
|
||||
static uint32_t trng_init_mode;
|
||||
|
||||
status_t TRNG_GetDefaultConfig(trng_config_t *userConfig)
|
||||
{
|
||||
userConfig->sampleMode = 0x5AU;
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t TRNG_Init(TRNG_Type *base, const trng_config_t *userConfig)
|
||||
{
|
||||
ck_assert_ptr_eq(base, &mock_trng);
|
||||
trng_init_calls++;
|
||||
trng_init_mode = userConfig->sampleMode;
|
||||
return trng_init_ret;
|
||||
}
|
||||
|
||||
status_t TRNG_GetRandomData(TRNG_Type *base, void *data, size_t dataSize)
|
||||
{
|
||||
ck_assert_ptr_eq(base, &mock_trng);
|
||||
trng_read_calls++;
|
||||
memset(data, 0xA5, dataSize);
|
||||
return trng_read_ret;
|
||||
}
|
||||
|
||||
static FLEXSPI_Type mock_flexspi;
|
||||
static CACHE64_CTRL_Type mock_cache64;
|
||||
SOCCIU_Type mock_socctrl;
|
||||
NVIC_Type mock_nvic;
|
||||
SysTick_Type mock_systick;
|
||||
uint32_t mock_primask;
|
||||
int mock_barriers;
|
||||
|
||||
FLEXSPI_Type *mock_flexspi_regs(void)
|
||||
{
|
||||
return &mock_flexspi;
|
||||
}
|
||||
|
||||
CACHE64_POLSEL_Type mock_cache64_polsel;
|
||||
static int cache_invalidations;
|
||||
static uint32_t cache64_seen;
|
||||
|
||||
CACHE64_CTRL_Type *mock_cache64_regs(void)
|
||||
{
|
||||
cache64_seen |= mock_cache64.CCR;
|
||||
if ((mock_cache64.CCR & CACHE64_CTRL_CCR_GO_MASK) != 0U) {
|
||||
cache_invalidations++;
|
||||
mock_cache64.CCR &= ~CACHE64_CTRL_CCR_GO_MASK;
|
||||
}
|
||||
return &mock_cache64;
|
||||
}
|
||||
|
||||
static int systick_attach_calls;
|
||||
static clock_attach_id_t systick_attach_id;
|
||||
|
||||
void CLOCK_AttachClk(clock_attach_id_t connection)
|
||||
{
|
||||
systick_attach_calls++;
|
||||
systick_attach_id = connection;
|
||||
}
|
||||
|
||||
/* Point the SDK peripheral instances at the mocks above */
|
||||
#undef FLEXSPI
|
||||
#undef CACHE64_CTRL0
|
||||
#undef CACHE64_POLSEL0
|
||||
#undef SOCCTRL
|
||||
#define FLEXSPI (mock_flexspi_regs())
|
||||
#define CACHE64_CTRL0 (mock_cache64_regs())
|
||||
#define CACHE64_POLSEL0 (&mock_cache64_polsel)
|
||||
#define SOCCTRL (&mock_socctrl)
|
||||
#undef TRNG
|
||||
#define TRNG (&mock_trng)
|
||||
|
||||
#include "../../hal/rw612.c"
|
||||
|
||||
static int sau_covers(uint32_t addr, int nsc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RW612_SAU_REGIONS; i++) {
|
||||
if (sau[i].set && (sau[i].nsc == nsc) &&
|
||||
(addr >= sau[i].start) && (addr <= sau[i].end))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
memset(sau, 0, sizeof(sau));
|
||||
memset(&mock_nvic, 0, sizeof(mock_nvic));
|
||||
sau_bad_region = 0;
|
||||
mock_sau_ctrl = 0;
|
||||
mock_shcsr = 0;
|
||||
memset(&mock_cache64, 0, sizeof(mock_cache64));
|
||||
memset(&mock_cache64_polsel, 0, sizeof(mock_cache64_polsel));
|
||||
cache_invalidations = 0;
|
||||
cache64_seen = 0;
|
||||
hal_init();
|
||||
hal_trng_fini();
|
||||
trng_init_calls = trng_read_calls = 0;
|
||||
trng_init_ret = trng_read_ret = kStatus_Success;
|
||||
trng_init_mode = 0;
|
||||
}
|
||||
|
||||
START_TEST(test_sau_enabled_and_well_formed)
|
||||
{
|
||||
int i;
|
||||
|
||||
ck_assert_int_eq(sau_bad_region, 0);
|
||||
ck_assert_uint_ne(mock_sau_ctrl & SAU_INIT_CTRL_ENABLE, 0);
|
||||
ck_assert_uint_ne(mock_shcsr & SCB_SHCSR_SECUREFAULT_EN, 0);
|
||||
for (i = 0; i < RW612_SAU_REGIONS; i++) {
|
||||
if (!sau[i].set)
|
||||
continue;
|
||||
ck_assert_uint_le(sau[i].start, sau[i].end);
|
||||
ck_assert_uint_eq(sau[i].start & 0x1FU, 0);
|
||||
ck_assert_uint_eq(sau[i].end & 0x1FU, 0x1FU);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_nsc_region_is_veneers_only)
|
||||
{
|
||||
int i;
|
||||
int nsc_regions = 0;
|
||||
|
||||
for (i = 0; i < RW612_SAU_REGIONS; i++) {
|
||||
if (sau[i].set && sau[i].nsc) {
|
||||
nsc_regions++;
|
||||
ck_assert_uint_eq(sau[i].start, WOLFBOOT_NSC_ADDRESS);
|
||||
ck_assert_uint_eq(sau[i].end,
|
||||
WOLFBOOT_NSC_ADDRESS + WOLFBOOT_NSC_SIZE - 1);
|
||||
}
|
||||
}
|
||||
ck_assert_int_eq(nsc_regions, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_application_resources_non_secure)
|
||||
{
|
||||
static const uint32_t ns_addrs[] = {
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 1,
|
||||
0x20040000, 0x2012FFFF, /* non-secure SRAM */
|
||||
0x40020000, 0x40021000, /* RSTCTL1, CLKCTL1 */
|
||||
0x40100000, /* GPIO */
|
||||
0x40106000, 0x40109000 /* FLEXCOMM0, FLEXCOMM3 (console) */
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sizeof(ns_addrs) / sizeof(ns_addrs[0]); i++)
|
||||
ck_assert_msg(sau_covers(ns_addrs[i], 0), "0x%08x not non-secure",
|
||||
ns_addrs[i]);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_secure_resources_not_non_secure)
|
||||
{
|
||||
static const uint32_t s_addrs[] = {
|
||||
0x08000000, 0x08000400, /* wolfBoot, FCB */
|
||||
WOLFBOOT_NSC_ADDRESS,
|
||||
WOLFBOOT_KEYVAULT_ADDRESS,
|
||||
WOLFBOOT_KEYVAULT_ADDRESS + WOLFBOOT_KEYVAULT_SIZE - 1,
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS,
|
||||
0x20000000, 0x2003FFFF, /* wolfBoot SRAM */
|
||||
0x40000000, 0x40000040, /* RSTCTL0, PRSTCTL0_SET */
|
||||
0x40001000, /* CLKCTL0 */
|
||||
0x40004000, /* IO_MUX */
|
||||
0x40006000, 0x40007000, /* PUF, ELS */
|
||||
0x40009000, 0x4000A000, /* PKC, OCOTP */
|
||||
0x40014000, 0x40024000, /* TRNG, ITRC */
|
||||
0x40033000, /* CACHE64_CTRL0 */
|
||||
0x40104000, 0x40105000, /* DMA0, DMA1 */
|
||||
0x4010A000, /* past FLEXCOMM3 */
|
||||
0x4010F000, /* DBGMAILBOX */
|
||||
0x40134000, /* FLEXSPI */
|
||||
0x40148000, /* AHB_SECURE_CTRL */
|
||||
0x4014E000, /* GDMA */
|
||||
0x48000000, /* uncached FlexSPI alias */
|
||||
0x48000000 + (WOLFBOOT_KEYVAULT_ADDRESS - 0x08000000)
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sizeof(s_addrs) / sizeof(s_addrs[0]); i++)
|
||||
ck_assert_msg(!sau_covers(s_addrs[i], 0), "0x%08x is non-secure",
|
||||
s_addrs[i]);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_non_secure_peripheral_irqs_targeted)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
ck_assert_uint_eq(mock_nvic.ITNS[0],
|
||||
(1UL << GPIO_INTA_IRQn) | (1UL << GPIO_INTB_IRQn) |
|
||||
(0xFUL << FLEXCOMM0_IRQn));
|
||||
for (i = 1; i < sizeof(mock_nvic.ITNS) / sizeof(mock_nvic.ITNS[0]); i++)
|
||||
ck_assert_uint_eq(mock_nvic.ITNS[i], 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_xip_cache_enabled_at_init)
|
||||
{
|
||||
const uint32_t inv = CACHE64_CTRL_CCR_INVW0_MASK |
|
||||
CACHE64_CTRL_CCR_INVW1_MASK;
|
||||
|
||||
ck_assert_int_eq(cache_invalidations, 1);
|
||||
ck_assert_uint_eq(cache64_seen & inv, inv);
|
||||
ck_assert_uint_eq(mock_cache64.CCR,
|
||||
CACHE64_CTRL_CCR_ENWRBUF_MASK | CACHE64_CTRL_CCR_ENCACHE_MASK);
|
||||
ck_assert_uint_eq(mock_cache64_polsel.REG0_TOP, 0x07FFFC00U);
|
||||
ck_assert_uint_eq(mock_cache64_polsel.REG1_TOP, 0U);
|
||||
ck_assert_uint_eq(mock_cache64_polsel.POLSEL, 1U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_xip_cache_already_on_left_alone)
|
||||
{
|
||||
mock_cache64.CCR = CACHE64_CTRL_CCR_ENCACHE_MASK;
|
||||
mock_cache64_polsel.POLSEL = 0xA5U;
|
||||
cache_invalidations = 0;
|
||||
hal_init();
|
||||
ck_assert_int_eq(cache_invalidations, 0);
|
||||
ck_assert_uint_eq(mock_cache64.CCR, CACHE64_CTRL_CCR_ENCACHE_MASK);
|
||||
ck_assert_uint_eq(mock_cache64_polsel.POLSEL, 0xA5U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_benchmark_timer_counts_lposc_ticks)
|
||||
{
|
||||
memset(&mock_systick, 0, sizeof(mock_systick));
|
||||
ck_assert_uint_eq(hal_get_timer_us(), 0);
|
||||
ck_assert_int_eq(systick_attach_calls, 1);
|
||||
ck_assert_int_eq(systick_attach_id, kLPOSC_to_SYSTICK_CLK);
|
||||
ck_assert_uint_eq(mock_systick.LOAD, SysTick_LOAD_RELOAD_Msk);
|
||||
ck_assert_uint_eq(mock_systick.CTRL, SysTick_CTRL_ENABLE_Msk);
|
||||
|
||||
/* Down-counter wraps from 0 to the 24-bit reload value */
|
||||
mock_systick.VAL = SysTick_LOAD_RELOAD_Msk - 99U;
|
||||
ck_assert_uint_eq(hal_get_timer_us(), 100);
|
||||
mock_systick.VAL -= 400U;
|
||||
ck_assert_uint_eq(hal_get_timer_us(), 500);
|
||||
ck_assert_int_eq(systick_attach_calls, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_trng_needs_init)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
|
||||
ck_assert_int_eq(hal_trng_get_entropy(buf, sizeof(buf)), -1);
|
||||
ck_assert_int_eq(trng_read_calls, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_trng_init_and_read)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
unsigned int i;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
hal_trng_init();
|
||||
ck_assert_int_eq(trng_init_calls, 1);
|
||||
ck_assert_uint_eq(trng_init_mode, 0x5AU);
|
||||
ck_assert_int_eq(hal_trng_get_entropy(buf, sizeof(buf)), 0);
|
||||
ck_assert_int_eq(trng_read_calls, 1);
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
ck_assert_uint_eq(buf[i], 0xA5);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_trng_init_failure)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
|
||||
trng_init_ret = kStatus_Fail;
|
||||
hal_trng_init();
|
||||
ck_assert_int_eq(hal_trng_get_entropy(buf, sizeof(buf)), -1);
|
||||
ck_assert_int_eq(trng_read_calls, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_trng_read_failure)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
|
||||
hal_trng_init();
|
||||
trng_read_ret = kStatus_Fail;
|
||||
ck_assert_int_eq(hal_trng_get_entropy(buf, sizeof(buf)), -1);
|
||||
|
||||
hal_trng_fini();
|
||||
trng_read_ret = kStatus_Success;
|
||||
ck_assert_int_eq(hal_trng_get_entropy(buf, sizeof(buf)), -1);
|
||||
ck_assert_int_eq(trng_read_calls, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *rw612_tz_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("rw612-tz");
|
||||
TCase *tc = tcase_create("rw612-tz");
|
||||
|
||||
tcase_add_checked_fixture(tc, setup, NULL);
|
||||
tcase_add_test(tc, test_sau_enabled_and_well_formed);
|
||||
tcase_add_test(tc, test_nsc_region_is_veneers_only);
|
||||
tcase_add_test(tc, test_application_resources_non_secure);
|
||||
tcase_add_test(tc, test_secure_resources_not_non_secure);
|
||||
tcase_add_test(tc, test_non_secure_peripheral_irqs_targeted);
|
||||
tcase_add_test(tc, test_xip_cache_enabled_at_init);
|
||||
tcase_add_test(tc, test_xip_cache_already_on_left_alone);
|
||||
tcase_add_test(tc, test_benchmark_timer_counts_lposc_ticks);
|
||||
tcase_add_test(tc, test_trng_needs_init);
|
||||
tcase_add_test(tc, test_trng_init_and_read);
|
||||
tcase_add_test(tc, test_trng_init_failure);
|
||||
tcase_add_test(tc, test_trng_read_failure);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
Suite *s = rw612_tz_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/* unit-rw612-uart.c
|
||||
*
|
||||
* Unit tests for the RW612 debug console in hal/rw612.c (DEBUG_UART): the
|
||||
* FLEXCOMM3 clock, pin mux and USART setup, and the newline to CRLF output.
|
||||
*
|
||||
* 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 3 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-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* RW612 layout (config/examples/rw612.config), in place of target.h */
|
||||
#define H_TARGETS_TARGET_
|
||||
#define WOLFBOOT_FIXED_PARTITIONS
|
||||
#define WOLFBOOT_SECTOR_SIZE 0x1000
|
||||
#define WOLFBOOT_PARTITION_SIZE 0x100000
|
||||
#define WOLFBOOT_PARTITION_BOOT_ADDRESS 0x08100000
|
||||
#define WOLFBOOT_PARTITION_UPDATE_ADDRESS 0x08200000
|
||||
#define WOLFBOOT_PARTITION_SWAP_ADDRESS 0x08300000
|
||||
|
||||
#include "fsl_common.h"
|
||||
|
||||
static USART_Type mock_usart3;
|
||||
static MCI_IO_MUX_Type mock_iomux;
|
||||
static SOCCIU_Type mock_socctrl;
|
||||
static AON_SOC_CIU_Type mock_aon;
|
||||
|
||||
/* Before fsl_io_mux.h: its inline IO_MUX_SetPinMux() expands these */
|
||||
#undef USART3
|
||||
#undef MCI_IO_MUX
|
||||
#undef SOCCTRL
|
||||
#undef AON_SOC_CIU
|
||||
#define USART3 (&mock_usart3)
|
||||
#define MCI_IO_MUX (&mock_iomux)
|
||||
#define SOCCTRL (&mock_socctrl)
|
||||
#define AON_SOC_CIU (&mock_aon)
|
||||
|
||||
#include "fsl_io_mux.h"
|
||||
#include "fsl_usart.h"
|
||||
|
||||
#define MOCK_TX_MAX 64
|
||||
|
||||
uint32_t mock_primask;
|
||||
int mock_barriers;
|
||||
|
||||
static clock_attach_id_t attached_clk;
|
||||
static int attach_calls;
|
||||
static usart_config_t init_cfg;
|
||||
static uint32_t init_clk_hz;
|
||||
static USART_Type *init_base;
|
||||
static int init_calls;
|
||||
static uint8_t tx[MOCK_TX_MAX];
|
||||
static unsigned int tx_len;
|
||||
static int tx_bad_base;
|
||||
|
||||
void CLOCK_AttachClk(clock_attach_id_t connection)
|
||||
{
|
||||
attached_clk = connection;
|
||||
attach_calls++;
|
||||
}
|
||||
|
||||
void USART_GetDefaultConfig(usart_config_t *config)
|
||||
{
|
||||
memset(config, 0, sizeof(*config));
|
||||
config->baudRate_Bps = 9600U;
|
||||
}
|
||||
|
||||
status_t USART_Init(USART_Type *base, const usart_config_t *config,
|
||||
uint32_t srcClock_Hz)
|
||||
{
|
||||
init_base = base;
|
||||
init_cfg = *config;
|
||||
init_clk_hz = srcClock_Hz;
|
||||
init_calls++;
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t USART_WriteBlocking(USART_Type *base, const uint8_t *data,
|
||||
size_t length)
|
||||
{
|
||||
if (base != &mock_usart3)
|
||||
tx_bad_base = 1;
|
||||
while ((length > 0U) && (tx_len < MOCK_TX_MAX)) {
|
||||
tx[tx_len++] = *data++;
|
||||
length--;
|
||||
}
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
#include "../../hal/rw612.c"
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
memset(&mock_iomux, 0xFF, sizeof(mock_iomux));
|
||||
memset(&mock_socctrl, 0, sizeof(mock_socctrl));
|
||||
memset(&mock_aon, 0, sizeof(mock_aon));
|
||||
memset(&init_cfg, 0, sizeof(init_cfg));
|
||||
memset(tx, 0, sizeof(tx));
|
||||
tx_len = 0;
|
||||
tx_bad_base = 0;
|
||||
attach_calls = init_calls = 0;
|
||||
init_base = NULL;
|
||||
init_clk_hz = 0;
|
||||
}
|
||||
|
||||
START_TEST(test_uart_init_flexcomm3_115200)
|
||||
{
|
||||
uart_init();
|
||||
ck_assert_int_eq(attach_calls, 1);
|
||||
ck_assert_int_eq(attached_clk, kSFRO_to_FLEXCOMM3);
|
||||
ck_assert_int_eq(init_calls, 1);
|
||||
ck_assert_ptr_eq(init_base, &mock_usart3);
|
||||
ck_assert_uint_eq(init_cfg.baudRate_Bps, 115200U);
|
||||
ck_assert(init_cfg.enableTx);
|
||||
ck_assert(init_cfg.enableRx);
|
||||
ck_assert_uint_eq(init_clk_hz, 16000000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_uart_init_muxes_fc3_pins)
|
||||
{
|
||||
/* FC3 USART data is GPIO24/GPIO26, enabled in the always-on domain */
|
||||
uart_init();
|
||||
ck_assert_uint_eq(mock_iomux.GPIO_GRP0 & 0x05000000U, 0U);
|
||||
ck_assert_uint_ne(mock_iomux.FC3, 0U);
|
||||
ck_assert_uint_eq(mock_aon.MCI_IOMUX_EN0 & 0x05000000U, 0x05000000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_uart_write_crlf)
|
||||
{
|
||||
static const char out[] = "\r\nab\r\n\r\nc";
|
||||
|
||||
uart_write("\nab\n\nc", 6);
|
||||
ck_assert_int_eq(tx_bad_base, 0);
|
||||
ck_assert_uint_eq(tx_len, sizeof(out) - 1U);
|
||||
ck_assert_mem_eq(tx, out, sizeof(out) - 1U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_uart_write_plain_and_empty)
|
||||
{
|
||||
uart_write("abc", 3);
|
||||
ck_assert_uint_eq(tx_len, 3);
|
||||
ck_assert_mem_eq(tx, "abc", 3);
|
||||
uart_write("x", 0);
|
||||
ck_assert_uint_eq(tx_len, 3);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *rw612_uart_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("rw612-uart");
|
||||
TCase *tc = tcase_create("uart");
|
||||
|
||||
tcase_add_checked_fixture(tc, setup, NULL);
|
||||
tcase_add_test(tc, test_uart_init_flexcomm3_115200);
|
||||
tcase_add_test(tc, test_uart_init_muxes_fc3_pins);
|
||||
tcase_add_test(tc, test_uart_write_crlf);
|
||||
tcase_add_test(tc, test_uart_write_plain_and_empty);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
SRunner *sr = srunner_create(rw612_uart_suite());
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return fails == 0 ? 0 : 1;
|
||||
}
|
||||
Loading…
Reference in New Issue