va416x0: fix vector table alignment and length, FRAM defects and ML-DSA

pull/906/head
David Garske 2026-09-15 11:31:08 -07:00 committed by Daniele Lacamera
parent b6509b1c59
commit e353b20504
11 changed files with 808 additions and 96 deletions

View File

@ -5,9 +5,16 @@ TARGET?=va416x0
# ECDSA P384 and SHA384
SIGN?=ECC384
HASH?=SHA384
IMAGE_HEADER_SIZE=512
# ML-DSA Level 5 (87)
# The application vector table sits at BOOT_ADDRESS + IMAGE_HEADER_SIZE and
# do_boot() writes that into VTOR. The core ORs the vector offset into VTOR
# instead of adding it, so with a 212-entry table any IRQ >= 112 (offset
# >= 0x200) is misdispatched unless that address is 1024-aligned. Keep
# (BOOT_ADDRESS + IMAGE_HEADER_SIZE) % 1024 == 0; hal/va416x0.c checks it.
IMAGE_HEADER_SIZE=1024
# ML-DSA Level 5 (87). Uses ~48KB of the 64KB SRAM and 34KB of the 36KB
# bootloader region, so both are close to full.
#SIGN=ML_DSA
#HASH=SHA256
#ML_DSA_LEVEL=5

View File

@ -8860,19 +8860,46 @@ Tested on VA41620 and VA41630 MCU's.
MCU: Cortex-M4 with Triple-Mode Redundancy (TMR) RAD hardening at up to 100MHz.
FLASH: The VA41630 has 256KB of internal SPI FRAM (for the VA41620 its external). FRAM is Infineon FM25V20A.
Default flash layout:
Default flash layout, which fills the 256KB FRAM exactly:
| Partition | Size | Address | Description |
|-------------|-------|---------|-------------|
| Bootloader | 38KB | 0x0 | Bootloader partition |
| Application | 108KB | 0x9800 | Boot partition |
| Update | 108KB | 0x24800 | Update partition |
| Bootloader | 46KB | 0x0 | Bootloader partition |
| Application | 104KB | 0xB800 | Boot partition |
| Update | 104KB | 0x25800 | Update partition |
| Swap | 2KB | 0x3F800 | Swap area |
The sector size is 2KB (`WOLFBOOT_SECTOR_SIZE=0x800`) and the manifest header is 1KB (`IMAGE_HEADER_SIZE=1024`), so the application image starts at 0xBC00 and has 0x19C00 bytes of usable space.
SRAM: 64KB on-chip SRAM and 256KB on-chip instruction/program memory
The 64KB of SRAM is two contiguous 32KB banks, SRAM_0 at 0x1FFF8000 and SRAM_1 at 0x20000000. The linker scripts pool them into a single region so that data and stack can span both; they remain separate EDAC scrub banks (`RAM0_SCRUB` and `RAM1_SCRUB`). The ML-DSA Level 5 configuration needs about 48KB of it and does not fit in one bank alone.
Boot ROM loads at 20MHz from SPI bus to internal data SRAM.
#### Interrupt vector tables
The VA416xx implements 212 exceptions: the 16 Cortex-M4 system exceptions plus 196 external interrupts, IRQ 0 through `TXEV_IRQn`. Both vector tables are sized for all of them, wolfBoot's in `src/boot_arm.c` and the application's in `test-app/startup_arm.c`. wolfBoot enables the EDAC single-bit and multi-bit error interrupts (76 and 77) when it configures scrubbing, and those vector fetches land at offsets 0x170 and 0x174, so a shorter table sends them into `.text`.
The application's vector table sits at `WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE` and `do_boot()` writes that address into `SCB->VTOR`. ARMv7-M requires a table this size to be aligned to the next power of two at or above (number of exceptions x 4), which is 1024 bytes for 212 entries, and this part enforces it in a way that is easy to miss.
`VTOR` itself accepts a finer value: writing `0xFFFFFFFF` reads back `0xFFFFFF80`, so the implemented field is `VTOR[31:7]`. The vector *fetch*, however, ORs the vector offset into `VTOR` rather than adding it, so any vector whose byte offset shares a set bit with the low bits of `VTOR` resolves to the wrong entry. Measured on a VA41630 with a relocated 212-entry table at a 512-aligned (not 1024-aligned) address:
| IRQ | Vector offset | Result |
|-----|---------------|--------|
| 77 (`EDAC_SBE`) | 0x174 | dispatched |
| 111 | 0x1FC | dispatched |
| 112 | 0x200 | hard fault (fetches vector 0, the initial MSP) |
| 128 (`PORTD2`) | 0x240 | wrong handler |
The same table at a 1024-aligned address dispatches IRQ 128 correctly. So a 512-aligned table appears to work -- SysTick, the EDAC interrupts and anything below IRQ 112 are fine -- while every IRQ from 112 upward is silently broken. That covers the PORTA through PORTG pin interrupts, the DMA interrupts and the ADC/DAC interrupts.
The default layout raises `IMAGE_HEADER_SIZE` to 1024 so that `0xB800 + 0x400 = 0xBC00` satisfies this. That costs 512 bytes of each partition. If you need those bytes back, the alternative is to keep a smaller header and move `WOLFBOOT_PARTITION_BOOT_ADDRESS` instead, so long as the sum stays 1024-aligned; `WOLFBOOT_SECTOR_SIZE` can be reduced to give finer placement, subject to `WOLFBOOT_SECTOR_SIZE >= IMAGE_HEADER_SIZE`. Either way keep `(WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE) % 1024 == 0`. `hal/va416x0.c` checks this at build time, so a layout that breaks the rule fails to compile rather than shipping.
This applies whichever startup file the application uses. Linking against the Vorago SDK's `startup_va416xx.s` rather than `test-app/startup_arm.c` does not change where wolfBoot places the image, and the SDK's `SystemInit()` sets `VTOR` to the same address wolfBoot already wrote.
The demo application prints `VTOR`, whether it is 1024-aligned, and a SysTick liveness check at startup, so a truncated or misplaced table is visible on the console. Note that SysTick alone does not prove the table is placed correctly: it is exception 15 at offset 0x3C, below the bit that alignment affects. Testing dispatch properly means triggering an IRQ at or above 112, for example with `NVIC_SetPendingIRQ()`.
By default the bootloader is built showing logs on UART0. To use UART1 set `DEBUG_UART_NUM=1`. To disable the bootloader UART change `DEBUG_UART=0` in the `.config`.
### Building Vorago VA416x0
@ -8943,6 +8970,10 @@ Example of wolfBoot binary sizes based on algorithms:
| RSA4096 | SHA3-384 | 19,216 |
| ML-DSA 87 | SHA256 | 25,168 |
#### Post-quantum (ML-DSA) configuration
The example config carries a commented-out ML-DSA Level 5 block: uncomment it, comment out the ECC384/SHA384 lines and the default layout, and use the larger sector and partition sizes it lists. The `sign` tool reads `ML_DSA_LEVEL` from the environment and otherwise falls back to level 2, which rejects a level 5 key with `error: unrecognized ml-dsa key size: 7488`. The top-level Makefile and `tools/scripts/va416x0/build_test.sh` both pass it for you; a hand-run `sign` needs it on the command line.
### Flashing Vorago VA416x0
Flash using Segger JLink: `JLinkExe -CommanderScript tools/scripts/va416x0/flash_va416xx.jlink`
@ -8967,14 +8998,16 @@ The `loader.elf` programs the external SPI FRAM with the IRAM image. It is creat
See `tools/scripts/va416x0/build_test.sh clean` for flashing examples.
Example boot ouput on UART 0 (MCU TX):
Example boot output on UART 0 (MCU TX):
```
wolfBoot HAL Init
Boot partition: 0x9800 (sz 5060, ver 0x1, type 0x601)
Partition 1 header magic 0x00000000 invalid at 0x24800
Boot partition: 0x9800 (sz 5060, ver 0x1, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x1, type 0x601)
Partition 1 header magic 0x00000000 invalid at 0x25800
Boot partition: 0xB800 (sz 5600, ver 0x1, type 0x601)
Booting version: 0x1
Checking integrity...done
Verifying signature...done
========================
VA416x0 wolfBoot demo Application
Copyright 2025 wolfSSL Inc
@ -9000,8 +9033,19 @@ Number of public keys: 1
9B BE B7 BB 11 75 01 81 45 14 19 7E B2 BD C0 A6
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
Vector table
====================================
VTOR : 0x0000BC00 (expected 0x0000BC00) OK
SysTick : ticking (113 -> 1738 ms)
```
`VTOR` is the address the core fetches exceptions from, and it must match
`WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE`. The SysTick line is a
liveness check: `HAL_Init()` starts SysTick and `SysTick_Handler()` advances
`HAL_time_ms`, so a counter that never moves means exceptions are not reaching
the application's vector table.
### Debugging Vorago VA416x0
Start the GDB server: `JLinkGDBServer -device VA416XX -if SWD -speed 2000 -port 3333`
@ -9014,7 +9058,7 @@ See `tools/scripts/va416x0/build_test.sh update`:
```sh
# Sign a new test app with version 2
IMAGE_HEADER_SIZE=512 ./tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 2
IMAGE_HEADER_SIZE=1024 ./tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 2
# Create a bin footer with wolfBoot trailer "BOOT" and "p" (ASCII for 0x70 == IMG_STATE_UPDATING)
echo -n "pBOOT" > trigger_magic.bin
@ -9046,17 +9090,17 @@ Example update output:
```
wolfBoot HAL Init
Boot partition: 0x9800 (sz 5060, ver 0x1, type 0x601)
Update partition: 0x24800 (sz 5060, ver 0x2, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x1, type 0x601)
Update partition: 0x25800 (sz 5600, ver 0x2, type 0x601)
Starting Update (fallback allowed 0)
Update partition: 0x24800 (sz 5060, ver 0x2, type 0x601)
Boot partition: 0x9800 (sz 5060, ver 0x1, type 0x601)
Update partition: 0x25800 (sz 5600, ver 0x2, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x1, type 0x601)
Versions: Current 0x1, Update 0x2
Copy sector 0 (part 1->2)
Copy sector 0 (part 0->1)
Copy sector 0 (part 2->0)
Boot partition: 0x9800 (sz 5060, ver 0x2, type 0x601)
Update partition: 0x24800 (sz 5060, ver 0x1, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x2, type 0x601)
Update partition: 0x25800 (sz 5600, ver 0x1, type 0x601)
Copy sector 1 (part 1->2)
Copy sector 1 (part 0->1)
Copy sector 1 (part 2->0)
@ -9064,11 +9108,11 @@ Copy sector 2 (part 1->2)
Copy sector 2 (part 0->1)
Copy sector 2 (part 2->0)
Erasing remainder of partition (50 sectors)...
Boot partition: 0x9800 (sz 5060, ver 0x2, type 0x601)
Update partition: 0x24800 (sz 5060, ver 0x1, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x2, type 0x601)
Update partition: 0x25800 (sz 5600, ver 0x1, type 0x601)
Copy sector 52 (part 0->2)
Copied boot sector to swap
Boot partition: 0x9800 (sz 5060, ver 0x2, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x2, type 0x601)
Booting version: 0x1
========================
VA416x0 wolfBoot demo Application
@ -9105,9 +9149,9 @@ Boot logs after hard reset:
```
wolfBoot HAL Init
Boot partition: 0x9800 (sz 5060, ver 0x2, type 0x601)
Update partition: 0x24800 (sz 5060, ver 0x1, type 0x601)
Boot partition: 0x9800 (sz 5060, ver 0x2, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x2, type 0x601)
Update partition: 0x25800 (sz 5600, ver 0x1, type 0x601)
Boot partition: 0xB800 (sz 5600, ver 0x2, type 0x601)
Booting version: 0x2
========================
VA416x0 wolfBoot demo Application

View File

@ -41,6 +41,15 @@
#include "printf.h"
#include "loader.h"
/* The application vector table must be 1024-aligned: this core ORs the vector
* offset into VTOR rather than adding it, so with 212 entries every IRQ >= 112
* misdispatches otherwise. See docs/Targets.md. */
#if defined(WOLFBOOT_PARTITION_BOOT_ADDRESS) && defined(IMAGE_HEADER_SIZE)
#if (((WOLFBOOT_PARTITION_BOOT_ADDRESS) + (IMAGE_HEADER_SIZE)) & 0x3FF) != 0
#error "VA416x0: application vector table must be 1024-aligned. Raise IMAGE_HEADER_SIZE (1024) or move WOLFBOOT_PARTITION_BOOT_ADDRESS."
#endif
#endif
#endif
#ifndef WOLFBOOT_UNIT_TEST_VA416X0_FRAM
@ -117,22 +126,11 @@ static void UartInit(VOR_UART_Type* uart, uint32_t baudrate)
uart->CTRL |= UART_CTRL_AUTORTS_Msk;
#endif
/* Enable RX interrupts as soon as a character is received */
uart->IRQ_ENB = UART_IRQ_ENB_IRQ_RX_Msk;
uart->RXFIFOIRQTRG = 1;
/* Transmit-only: nothing reads the UART, and __HAL_DISABLE_UART0/1/2
* means the SDK builds no UART IRQ handler to vector to. */
uart->IRQ_ENB = 0;
uart->TXFIFOIRQTRG = 8;
if (VOR_UART0 == uart) {
NVIC_SetPriority(UART0_RX_IRQn, 1);
NVIC_EnableIRQ(UART0_RX_IRQn);
} else if (VOR_UART1 == uart) {
NVIC_SetPriority(UART1_RX_IRQn, 1);
NVIC_EnableIRQ(UART1_RX_IRQn);
} else {
NVIC_SetPriority(UART2_RX_IRQn, 1);
NVIC_EnableIRQ(UART2_RX_IRQn);
}
/* Enable UART */
uart->ENABLE = (UART_ENABLE_RXENABLE_Msk |
UART_ENABLE_TXENABLE_Msk);
@ -181,7 +179,10 @@ void uart_flush(void)
#define FRAM_SLEEP 0xB9
#ifndef USE_HAL_SPI_FRAM
/* One FRAM device at a time: transmits always use spiHandle, so record its
* bank and reject any other rather than drain one bank and transmit on it. */
static hal_spi_handle_t spiHandle;
static uint8_t spiFramBank = SPI_NUM_BANKS; /* invalid until a good init */
static void FRAM_WaitIdle(uint8_t spiBank)
{
@ -213,6 +214,11 @@ hal_status_t FRAM_Init(uint8_t spiBank, uint8_t csNum)
hal_status_t status = hal_status_ok;
uint8_t spiData[2];
/* Bounds check before indexing BANK[], as FRAM_WaitIdle() does */
if (spiBank >= SPI_NUM_BANKS) {
return hal_status_badParam;
}
/* Initialize the SPI handle */
memset(&spiHandle, 0, sizeof(spiHandle));
spiHandle.locked = false;
@ -243,6 +249,16 @@ hal_status_t FRAM_Init(uint8_t spiBank, uint8_t csNum)
FRAM_WaitIdle(spiBank);
spiHandle.state = hal_spi_state_ready;
}
if (status == hal_status_ok) {
/* Only bind on success: a failed init must not leave the gate open */
spiFramBank = spiBank;
}
else {
/* spiHandle was already repointed at spiBank but never brought up.
* Close the gate rather than leave it naming a previously good
* bank that the handle no longer refers to. */
spiFramBank = SPI_NUM_BANKS;
}
wolfBoot_printf("FRAM_Init: status %d\n", status);
return status;
}
@ -254,11 +270,11 @@ hal_status_t FRAM_Write(uint8_t spiBank, uint32_t addr, uint8_t *buf,
uint8_t spiData[4];
/* Validate input parameters */
if (buf == NULL || len == 0) {
if (buf == NULL || len == 0 || spiBank != spiFramBank) {
return hal_status_badParam;
}
/* Bounds check: ensure write doesn't exceed FRAM size */
if (addr >= FRAM_SIZE || (addr + len) > FRAM_SIZE) {
/* Compare against the space remaining: addr + len could wrap */
if (addr >= FRAM_SIZE || len > (uint32_t)FRAM_SIZE - addr) {
return hal_status_badParam;
}
@ -291,11 +307,11 @@ hal_status_t FRAM_Read(uint8_t spiBank, uint32_t addr, uint8_t *buf,
uint8_t spiData[4];
/* Validate input parameters */
if (buf == NULL || len == 0) {
if (buf == NULL || len == 0 || spiBank != spiFramBank) {
return hal_status_badParam;
}
/* Bounds check: ensure read doesn't exceed FRAM size */
if (addr >= FRAM_SIZE || (addr + len) > FRAM_SIZE) {
/* Compare against the space remaining: addr + len could wrap */
if (addr >= FRAM_SIZE || len > (uint32_t)FRAM_SIZE - addr) {
return hal_status_badParam;
}
@ -331,7 +347,7 @@ hal_status_t FRAM_Erase(uint8_t spiBank, uint32_t addr, uint32_t len)
while (len > 0) {
uint32_t erase_len = (len > sizeof(data)) ? sizeof(data) : len;
status = FRAM_Write(ROM_SPI_BANK, addr, data, erase_len);
status = FRAM_Write(spiBank, addr, data, erase_len);
if (status != hal_status_ok) {
/* Return the hal_status_t unmodified; ext_flash_erase() is
* the single negation point to a negative error code. */
@ -355,21 +371,21 @@ void RAMFUNCTION hal_flash_lock(void)
}
/* No internal flash. All partitions are external (PART_*_EXT), so these are
* unreachable; return an error so a config that reaches them fails loudly. */
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
/* not supported - no internal flash */
(void)address;
(void)data;
(void)len;
return 0;
return -1;
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
/* not supported - no internal flash */
(void)address;
(void)len;
return 0;
return -1;
}
#endif /* !WOLFBOOT_UNIT_TEST_VA416X0_FRAM */
@ -448,6 +464,8 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
return len;
}
/* The shadow update below re-syncs IRAM from FRAM, so it is a repair rather
* than a cache fill, and is a no-op unless the caller holds ROM_PROT.WREN. */
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
{
hal_status_t status;
@ -494,7 +512,8 @@ static int test_ext_flash(void)
{
int ret;
uint32_t i;
uint8_t pageData[WOLFBOOT_SECTOR_SIZE] = { 0 };
/* static: WOLFBOOT_SECTOR_SIZE is far too large for the stack */
static uint8_t pageData[WOLFBOOT_SECTOR_SIZE];
#ifndef READONLY
/* Erase sector */
@ -563,7 +582,7 @@ void hal_init(void)
/* Configure PLL to set CPU clock to 100MHz - 40MHz crystal * 2.5 */
status = HAL_Clkgen_PLL(CLK_CTRL0_XTAL_N_PLL2P5X);
if (status != hal_status_ok) {
/* continue anyways */
/* continue anyways: no UART yet, so this cannot be reported */
}
/* Disable Watchdog - should be already disabled out of reset */
@ -581,13 +600,13 @@ void hal_init(void)
/* Call SDK HAL initialization function */
status = HAL_Init();
if (status != hal_status_ok) {
/* continue anyways */
/* continue anyways: no UART yet, so this cannot be reported */
}
/* Configure the pins */
status = HAL_Iocfg_SetupPins(bootDefaultConfig);
if (status != hal_status_ok) {
/* continue anyways */
/* continue anyways: no UART yet, so this cannot be reported */
}
#ifdef DEBUG_UART
@ -600,10 +619,8 @@ void hal_init(void)
/* Init the FRAM SPI device */
status = FRAM_Init(ROM_SPI_BANK, ROM_SPI_CSN);
if (status != hal_status_ok) {
#ifdef DEBUG
wolfBoot_printf("FRAM_Init failed\n");
#endif
/* continue anyways */
wolfBoot_printf("FRAM_Init failed: status %d\n", status);
/* Continue: the image checks fail closed if FRAM is unreadable */
}
#ifdef TEST_EXT_FLASH
@ -616,19 +633,10 @@ void hal_prepare_boot(void)
#ifdef DEBUG_UART
uart_flush();
/* Disable UART to give app a clean state */
/* Disable UART to give app a clean state. UartInit() enables no UART
* interrupt, so there is no NVIC state to unwind here. */
DEBUG_UART_BASE->IRQ_ENB = 0;
DEBUG_UART_BASE->ENABLE = 0;
#if defined(DEBUG_UART_NUM) && DEBUG_UART_NUM == 1
NVIC_DisableIRQ(UART1_RX_IRQn);
NVIC_ClearPendingIRQ(UART1_RX_IRQn);
#elif defined(DEBUG_UART_NUM) && DEBUG_UART_NUM == 2
NVIC_DisableIRQ(UART2_RX_IRQn);
NVIC_ClearPendingIRQ(UART2_RX_IRQn);
#else /* default: UART0 */
NVIC_DisableIRQ(UART0_RX_IRQn);
NVIC_ClearPendingIRQ(UART0_RX_IRQn);
#endif
#endif
#ifdef WOLFBOOT_RESTORE_CLOCK

View File

@ -70,11 +70,13 @@
/** Default pin IOCONFIG register. type: un_iocfg_reg_t - see va416xx_hal_ioconfig.h */
/** A pin's IOCONFIG is set to this by HAL_Iocfg_Init() if that pin is not in the cfg array */
#define DEFAULT_PIN_IOCFG (IOCFG_REG_PULLDN) // internal pulldown enabled for input pin
/* internal pulldown enabled for input pin */
#define DEFAULT_PIN_IOCFG (IOCFG_REG_PULLDN)
/** Default pin direction (input/output) type: en_iocfg_dir_t - see va416xx_hal_ioconfig.h */
/** A pin's DIR is set to this by HAL_Iocfg_Init() if that pin is not in the cfg array */
#define DEFAULT_PIN_DIR (en_iocfg_dir__input) // default pin input
/* default pin direction is input */
#define DEFAULT_PIN_DIR (en_iocfg_dir__input)
/* PEB1-VA416XX-EVK */

View File

@ -6,8 +6,9 @@ MEMORY
{
IRAM (rx) :ORIGIN = 0x00000000, LENGTH = 256K
EBI (xrw) :ORIGIN = 0x10000000, LENGTH = 16M
RAM0 (xrw) :ORIGIN = 0x1FFF8000, LENGTH = 32K
RAM1 (xrw) :ORIGIN = 0x20000000, LENGTH = 32K
/* SRAM_0 (0x1FFF8000) and SRAM_1 (0x20000000) are contiguous, so they are
pooled as one 64K region. They stay separate EDAC scrub banks. */
RAM0 (xrw) :ORIGIN = 0x1FFF8000, LENGTH = 64K
}
/* Define output sections */
@ -132,13 +133,13 @@ SECTIONS
__bss_end__ = _end_bss;
} >RAM0
/* DMA control block, goes into fixed location in RAM1 */
/* DMA control block. Empty unless the SDK DMA driver is linked in; the
engine only requires the 128 byte alignment. */
.dma_blk (NOLOAD) :
{
. = ALIGN(128);
/**(.ARM.__at_0x20000000) */
*(dma_blk)
} >RAM1
} >RAM0
/* User_stack section, used to check that there is enough RAM left */
._user_stack :

View File

@ -837,8 +837,214 @@ void (* const IV[])(void) =
isr_empty,
isr_empty,
isr_empty,
#elif defined(TARGET_va416x0)
/* IRQ 0 through TXEV_IRQn (195), for 212 entries. wolfBoot enables the
* EDAC error IRQs (76, 77), so those slots must exist. */
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
#endif
};
#ifdef TARGET_va416x0
/* 16 system exceptions + 196 external IRQs. A short table sends the EDAC
* error IRQs (76, 77) into .text; fail the build instead. */
typedef char va416x0_iv_len_check[
(sizeof(IV) / sizeof(IV[0]) == 212) ? 1 : -1];
#endif
#endif
#ifdef RAM_CODE

View File

@ -6,8 +6,9 @@ MEMORY
{
IRAM (rx) :ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
EBI (xrw) :ORIGIN = 0x10000000, LENGTH = 16M
RAM0 (xrw) :ORIGIN = 0x1FFF8000, LENGTH = 32K
RAM1 (xrw) :ORIGIN = 0x20000000, LENGTH = 32K
/* SRAM_0 (0x1FFF8000) and SRAM_1 (0x20000000) are contiguous, so they are
pooled as one 64K region. They stay separate EDAC scrub banks. */
RAM0 (xrw) :ORIGIN = 0x1FFF8000, LENGTH = 64K
}
/* Define output sections */
@ -132,13 +133,13 @@ SECTIONS
__bss_end__ = _end_bss;
} >RAM0
/* DMA control block, goes into fixed location in RAM1 */
/* DMA control block. Empty unless the SDK DMA driver is linked in; the
engine only requires the 128 byte alignment. */
.dma_blk (NOLOAD) :
{
. = ALIGN(128);
/**(.ARM.__at_0x20000000) */
*(dma_blk)
} >RAM1
} >RAM0
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :

View File

@ -55,6 +55,44 @@ int benchmark_test(void *args);
#include "va416xx_hal_timer.h"
#include "va416xx_hal_ioconfig.h"
/* Confirm VTOR points at the application's table and that exceptions reach it.
* SysTick advances HAL_time_ms, so a frozen counter means they do not. */
static void print_vector_table_info(void)
{
uint32_t vtor;
uint32_t expect;
uint32_t t0, t1; /* short delta; keeps the print 32-bit */
volatile uint32_t spin;
vtor = SCB->VTOR;
expect = (uint32_t)(WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE);
wolfBoot_printf("\r\n");
wolfBoot_printf("Vector table\r\n");
wolfBoot_printf("====================================\r\n");
wolfBoot_printf("VTOR : 0x%08lx (expected 0x%08lx) %s\r\n",
(unsigned long)vtor, (unsigned long)expect,
(vtor == expect) ? "OK" : "MISMATCH");
/* The core ORs the vector offset into VTOR rather than adding it, so a
* 212-entry table that is not 1024-aligned misdispatches every IRQ from
* 112 up while SysTick and the EDAC IRQs still look healthy. */
wolfBoot_printf("VT alignment : %lu %s\r\n",
(unsigned long)(vtor & 0x3FFU),
((vtor & 0x3FFU) == 0) ? "(1024-aligned) OK"
: "*** NOT 1024-ALIGNED: IRQ >= 112 broken ***");
t0 = (uint32_t)HAL_time_ms;
/* Bounded: HAL_Timer_DelayMs() spins on HAL_time_ms and would hang */
for (spin = 0; spin < 2000000UL; spin++) {
}
t1 = (uint32_t)HAL_time_ms;
wolfBoot_printf("SysTick : %s (%lu -> %lu ms)\r\n",
(t1 != t0) ? "ticking" : "STOPPED",
(unsigned long)t0, (unsigned long)t1);
}
static uint8_t boot_part_state = IMG_STATE_NEW;
static uint8_t update_part_state = IMG_STATE_NEW;
@ -87,10 +125,9 @@ static const char *part_state_name(uint8_t state)
static int print_info(void)
{
int i, j;
uint32_t i, j;
uint32_t cur_fw_version, update_fw_version;
uint32_t n_keys;
uint16_t hdrSz;
cur_fw_version = wolfBoot_current_firmware_version();
update_fw_version = wolfBoot_update_firmware_version();
@ -130,8 +167,8 @@ static int print_info(void)
uint8_t *keybuf = keystore_get_buffer(i);
wolfBoot_printf("\r\n");
wolfBoot_printf(" Public Key #%d: size %lu, type %lx, mask %08lx\r\n", i,
size, type, mask);
wolfBoot_printf(" Public Key #%lu: size %lu, type %lx, mask %08lx\r\n",
(unsigned long)i, size, type, mask);
wolfBoot_printf(" ====================================\r\n ");
for (j = 0; j < size; j++) {
wolfBoot_printf("%02X ", keybuf[j]);
@ -164,6 +201,8 @@ void main(void)
print_info();
print_vector_table_info();
#ifdef WOLFCRYPT_TEST
wolfBoot_printf("\r\nRunning wolfCrypt tests...\r\n");
wolfCrypt_Init();

View File

@ -582,5 +582,211 @@ void (* const IV[])(void) =
isr_empty, // TIM8_CC_IRQ 46
isr_empty, // DMA1_STREAM7_IRQ 47
#elif defined(TARGET_va416x0) /* For Vorago VA416xx */
/* IRQ 0 through TXEV_IRQn (195), for 212 entries. wolfBoot points VTOR
* at this table; see docs/Targets.md for the alignment it needs. */
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
isr_empty,
#endif
};
#ifdef TARGET_va416x0
/* 16 system exceptions + 196 external IRQs; see src/boot_arm.c. */
typedef char va416x0_app_iv_len_check[
(sizeof(IV) / sizeof(IV[0]) == 212) ? 1 : -1];
#endif

View File

@ -30,6 +30,13 @@ else
fi
fi
# Select a specific probe when more than one J-Link is attached:
# JLINK_SN=660016516 tools/scripts/va416x0/build_test.sh clean
JLINK_SEL=""
if [ -n "${JLINK_SN}" ]; then
JLINK_SEL="-SelectEmuBySN ${JLINK_SN}"
fi
# Function to get value from .config file
get_config_value() {
grep "^${1}" .config | sed -E "s/^${1}[?]?=//" | head -n1
@ -46,15 +53,35 @@ HASH=$(get_config_value "HASH")
SIGN_ARG="--$(echo "${SIGN}" | tr '[:upper:]' '[:lower:]')"
HASH_ARG="--$(echo "${HASH}" | tr '[:upper:]' '[:lower:]')"
# Common build steps
make clean && make wolfboot.bin && make test-app/image.bin
# Keep as separate statements: under `set -e` a failure mid `a && b` does not
# exit, which would let a broken build sign and flash a stale image.
make clean
make wolfboot.bin
make test-app/image.bin
# The sign tool reads several settings from the environment at run time, so
# mirror SIGN_ENV from the top-level Makefile. Only non-empty values are
# forwarded: sign.c uses getenv(), where an empty string is not the same as
# unset -- ML_DSA_LEVEL="" would parse as level 0 rather than the built-in
# default. Without ML_DSA_LEVEL an ML-DSA key is rejected with
# "unrecognized ml-dsa key size".
SIGN_ENV=""
for _sign_var in ML_DSA_LEVEL IMAGE_SIGNATURE_SIZE NVM_FLASH_WRITEONCE \
WOLFBOOT_PARTITION_UPDATE_SIZE LMS_LEVELS LMS_HEIGHT \
LMS_WINTERNITZ XMSS_PARAMS; do
_sign_val=$(get_config_value "${_sign_var}")
if [ -n "${_sign_val}" ]; then
SIGN_ENV="${SIGN_ENV} ${_sign_var}=${_sign_val}"
fi
done
# Function to sign image
sign_image() {
IMAGE_HEADER_SIZE="${IMAGE_HEADER_SIZE}" \
WOLFBOOT_PARTITION_SIZE="${PARTITION_SIZE}" \
WOLFBOOT_SECTOR_SIZE="${SECTOR_SIZE}" \
./tools/keytools/sign "${SIGN_ARG}" "${HASH_ARG}" test-app/image.bin wolfboot_signing_private_key.der "$1"
env IMAGE_HEADER_SIZE="${IMAGE_HEADER_SIZE}" \
WOLFBOOT_PARTITION_SIZE="${PARTITION_SIZE}" \
WOLFBOOT_SECTOR_SIZE="${SECTOR_SIZE}" \
${SIGN_ENV} \
./tools/keytools/sign "${SIGN_ARG}" "${HASH_ARG}" test-app/image.bin wolfboot_signing_private_key.der "$1"
}
# Function to print summary
@ -72,21 +99,25 @@ print_summary() {
if [ "$MODE" = "clean" ]; then
sign_image "${VERSION}"
dd if=/dev/zero of=blank_update.bin bs=1K count=108
# Blank the update partition so the header magic reads invalid. Exactly
# PARTITION_SIZE bytes: rounding up would overrun the partition into swap.
dd if=/dev/zero of=blank_update.bin bs=$((PARTITION_SIZE)) \
count=1 2>/dev/null
./tools/bin-assemble/bin-assemble factory.bin 0x0 wolfboot.bin \
"${BOOT_ADDRESS}" test-app/image_v${VERSION}_signed.bin \
"${UPDATE_ADDRESS}" blank_update.bin
"${JLINK}" -CommanderScript tools/scripts/va416x0/flash_va416xx.jlink
"${JLINK}" ${JLINK_SEL} -CommanderScript tools/scripts/va416x0/flash_va416xx.jlink
print_summary
else
TRIGGER_ADDRESS=$(printf "0x%X" $(("${UPDATE_ADDRESS}" + "${PARTITION_SIZE}" - 5)))
PREV_VERSION=$(("${VERSION}" - 1))
sign_image "${PREV_VERSION}" && sign_image "${VERSION}"
sign_image "${PREV_VERSION}"
sign_image "${VERSION}"
echo -n "pBOOT" > trigger_magic.bin
./tools/bin-assemble/bin-assemble update.bin 0x0 wolfboot.bin \
"${BOOT_ADDRESS}" test-app/image_v${PREV_VERSION}_signed.bin \
"${UPDATE_ADDRESS}" test-app/image_v${VERSION}_signed.bin \
"${TRIGGER_ADDRESS}" trigger_magic.bin
"${JLINK}" -CommanderScript tools/scripts/va416x0/flash_va416xx_update.jlink
"${JLINK}" ${JLINK_SEL} -CommanderScript tools/scripts/va416x0/flash_va416xx_update.jlink
print_summary "${TRIGGER_ADDRESS}" "${PREV_VERSION}"
fi

View File

@ -10,8 +10,8 @@
* FRAM bounds check from rejecting the test buffer (the hardware value
* is 256 KiB). */
#define FRAM_SIZE (0xFFFFFFFFU)
#define ROM_SPI_BANK 0
#define SPI_NUM_BANKS 1
#define ROM_SPI_BANK 3
#define SPI_NUM_BANKS 4
#define SPI_STATUS_TFE_Msk 0x01U
#define SPI_STATUS_BUSY_Msk 0x02U
#define SPI_FIFO_CLR_RXFIFO_Msk 0x01U
@ -142,8 +142,11 @@ int wolfBoot_printf(const char *fmt, ...)
static void reset_spi_mocks(void)
{
int i;
memset(&mock_vor_spi, 0, sizeof(mock_vor_spi));
mock_vor_spi.BANK[0].STATUS = SPI_STATUS_TFE_Msk;
for (i = 0; i < SPI_NUM_BANKS; i++)
mock_vor_spi.BANK[i].STATUS = SPI_STATUS_TFE_Msk;
}
START_TEST(test_fram_write_command_failure_aborts_split_transaction)
@ -232,12 +235,19 @@ END_TEST
*/
START_TEST(test_ext_flash_erase_success_fills_iram)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
hal_status_t ok_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok,
hal_status_ok, hal_status_ok, hal_status_ok
};
int i;
reset_spi_mocks();
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(ROM_SPI_BANK, 0), hal_status_ok);
reset_spi_mocks();
memset(g_iram, 0xA5, sizeof(g_iram));
@ -256,12 +266,19 @@ END_TEST
* diag_erase in src/libwolfboot.c). */
START_TEST(test_ext_flash_erase_failure_returns_negative)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
hal_status_t fail_script[] = {
hal_status_ok, hal_status_ok, hal_status_err
};
int i;
int ret;
reset_spi_mocks();
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(ROM_SPI_BANK, 0), hal_status_ok);
reset_spi_mocks();
memset(g_iram, 0xA5, sizeof(g_iram));
@ -275,6 +292,150 @@ START_TEST(test_ext_flash_erase_failure_returns_negative)
}
END_TEST
/* FRAM_Erase() must drive the bank it was handed; pre-fix it hardcoded
* ROM_SPI_BANK. FRAM_WaitIdle() clears that bank's FIFO, so it is visible. */
START_TEST(test_fram_erase_uses_requested_bank)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
hal_status_t ok_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
reset_spi_mocks();
/* Bind the driver to bank 1, not the default ROM_SPI_BANK. */
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(1, 0), hal_status_ok);
reset_spi_mocks();
set_transmit_script(ok_script, 3);
ck_assert_int_eq(FRAM_Erase(1, 0x40, 32), hal_status_ok);
ck_assert_uint_eq(mock_vor_spi.BANK[1].FIFO_CLR,
SPI_FIFO_CLR_RXFIFO_Msk | SPI_FIFO_CLR_TXFIFO_Msk);
ck_assert_uint_eq(mock_vor_spi.BANK[ROM_SPI_BANK].FIFO_CLR, 0);
}
END_TEST
/* One device at a time: a bank other than the one FRAM_Init() bound to must
* be rejected, not drained while transmitting on the init handle's bank. */
START_TEST(test_fram_rejects_bank_other_than_initialized)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
uint8_t buf[4] = { 1, 2, 3, 4 };
reset_spi_mocks();
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(1, 0), hal_status_ok);
reset_spi_mocks();
set_transmit_script(init_script, 0);
ck_assert_int_eq(FRAM_Write(2, 0x40, buf, sizeof(buf)),
hal_status_badParam);
ck_assert_int_eq(FRAM_Read(2, 0x40, buf, sizeof(buf)),
hal_status_badParam);
/* No SPI traffic at all on a rejected request. */
ck_assert_int_eq(transmit_call_count, 0);
}
END_TEST
/* FRAM_Init() must bounds check spiBank before indexing BANK[], as
* FRAM_WaitIdle() already did. */
START_TEST(test_fram_init_rejects_out_of_range_bank)
{
reset_spi_mocks();
set_transmit_script(NULL, 0);
ck_assert_int_eq(FRAM_Init(SPI_NUM_BANKS, 0), hal_status_badParam);
ck_assert_int_eq(transmit_call_count, 0);
}
END_TEST
/* NULL buffer and zero length must be rejected before any SPI traffic. */
START_TEST(test_fram_rejects_null_buffer_and_zero_length)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
uint8_t buf[4] = { 0 };
reset_spi_mocks();
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(ROM_SPI_BANK, 0), hal_status_ok);
reset_spi_mocks();
set_transmit_script(init_script, 0);
ck_assert_int_eq(FRAM_Write(ROM_SPI_BANK, 0x40, NULL, 4),
hal_status_badParam);
ck_assert_int_eq(FRAM_Write(ROM_SPI_BANK, 0x40, buf, 0),
hal_status_badParam);
ck_assert_int_eq(FRAM_Read(ROM_SPI_BANK, 0x40, NULL, 4),
hal_status_badParam);
ck_assert_int_eq(FRAM_Read(ROM_SPI_BANK, 0x40, buf, 0),
hal_status_badParam);
ck_assert_int_eq(transmit_call_count, 0);
}
END_TEST
/* addr 0xFFFFFFF0 leaves 15 bytes, so a 32 byte request must be rejected.
* The pre-fix `(addr + len) > FRAM_SIZE` wrapped to 0x10 and allowed it. */
START_TEST(test_fram_bounds_check_survives_address_wrap)
{
hal_status_t init_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
uint8_t buf[32] = { 0 };
reset_spi_mocks();
set_transmit_script(init_script, 3);
ck_assert_int_eq(FRAM_Init(ROM_SPI_BANK, 0), hal_status_ok);
reset_spi_mocks();
set_transmit_script(init_script, 0);
ck_assert_int_eq(
FRAM_Write(ROM_SPI_BANK, 0xFFFFFFF0U, buf, sizeof(buf)),
hal_status_badParam);
ck_assert_int_eq(
FRAM_Read(ROM_SPI_BANK, 0xFFFFFFF0U, buf, sizeof(buf)),
hal_status_badParam);
ck_assert_int_eq(transmit_call_count, 0);
}
END_TEST
/* A failed FRAM_Init() must not bind the bank: doing so would let a later
* transfer through the gate onto an incompletely initialized handle. */
START_TEST(test_failed_init_does_not_bind_bank)
{
hal_status_t ok_script[] = {
hal_status_ok, hal_status_ok, hal_status_ok
};
hal_status_t fail_script[] = { hal_status_err };
uint8_t buf[4] = { 1, 2, 3, 4 };
reset_spi_mocks();
set_transmit_script(ok_script, 3);
ck_assert_int_eq(FRAM_Init(ROM_SPI_BANK, 0), hal_status_ok);
/* Now fail an init on a different bank. */
reset_spi_mocks();
set_transmit_script(fail_script, 1);
ck_assert_int_ne(FRAM_Init(1, 0), hal_status_ok);
/* The failed init leaves spiHandle pointing at bank 1, so the gate must
* close entirely rather than keep naming ROM_SPI_BANK. */
reset_spi_mocks();
set_transmit_script(fail_script, 0);
ck_assert_int_eq(FRAM_Write(1, 0x40, buf, sizeof(buf)),
hal_status_badParam);
ck_assert_int_eq(FRAM_Write(ROM_SPI_BANK, 0x40, buf, sizeof(buf)),
hal_status_badParam);
ck_assert_int_eq(transmit_call_count, 0);
}
END_TEST
Suite *va416x0_fram_suite(void)
{
Suite *s = suite_create("va416x0-fram");
@ -285,6 +446,12 @@ Suite *va416x0_fram_suite(void)
tcase_add_test(tc, test_iram_fill_unaligned_64bit_addr);
tcase_add_test(tc, test_ext_flash_erase_success_fills_iram);
tcase_add_test(tc, test_ext_flash_erase_failure_returns_negative);
tcase_add_test(tc, test_fram_erase_uses_requested_bank);
tcase_add_test(tc, test_fram_rejects_bank_other_than_initialized);
tcase_add_test(tc, test_fram_init_rejects_out_of_range_bank);
tcase_add_test(tc, test_fram_rejects_null_buffer_and_zero_length);
tcase_add_test(tc, test_fram_bounds_check_survives_address_wrap);
tcase_add_test(tc, test_failed_init_does_not_bind_bank);
suite_add_tcase(s, tc);
return s;