Skip to content

Commit 336ec0d

Browse files
committed
Add compressed A/B firmware images executed from RAM
1 parent 213d7d7 commit 336ec0d

34 files changed

Lines changed: 2519 additions & 22 deletions
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
name: Compressed images - test with simulator target
2+
3+
on:
4+
push:
5+
branches: [ 'master', 'main', 'release/**' ]
6+
pull_request:
7+
branches: [ '*' ]
8+
9+
jobs:
10+
compressed_simulator_tests:
11+
runs-on: ubuntu-latest
12+
container:
13+
image: ghcr.io/wolfssl/wolfboot-ci-sim:v1.0
14+
timeout-minutes: 20
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
# GZIP_SMALL selects a different Huffman decoder in src/gzip.c, so
19+
# both have to be exercised and not merely built.
20+
sign: [ ED25519, ECC256 ]
21+
gzip_small: [ 0, 1 ]
22+
23+
steps:
24+
- uses: actions/checkout@v4
25+
with:
26+
submodules: true
27+
28+
- name: Trust workspace
29+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
30+
31+
- name: make clean
32+
run: |
33+
make distclean
34+
35+
- name: Select config
36+
run: |
37+
cp config/examples/sim-compressed-ram.config .config
38+
sed -i "s/^SIGN?=.*/SIGN?=${{ matrix.sign }}/" .config
39+
sed -i "s/^GZIP_SMALL=.*//" .config
40+
echo "GZIP_SMALL=${{ matrix.gzip_small }}" >> .config
41+
42+
- name: Build tools
43+
run: |
44+
make -C tools/keytools && make -C tools/bin-assemble
45+
46+
- name: Build wolfboot.elf and test-app/image.elf
47+
run: |
48+
make clean && make
49+
50+
- name: Assemble the compressed A/B slots
51+
run: |
52+
make test-sim-compressed-ram-with-update
53+
54+
- name: Run the compressed image simulator test
55+
run: |
56+
./tools/scripts/sim-compressed-ram.sh
57+
58+
compressed_encrypted_simulator_tests:
59+
runs-on: ubuntu-latest
60+
container:
61+
image: ghcr.io/wolfssl/wolfboot-ci-sim:v1.0
62+
timeout-minutes: 20
63+
strategy:
64+
fail-fast: false
65+
matrix:
66+
# Each cipher has its own block size, which the per-slot keystream
67+
# stride is derived from, so all three have to run.
68+
cipher: [ chacha, aes128, aes256 ]
69+
70+
steps:
71+
- uses: actions/checkout@v4
72+
with:
73+
submodules: true
74+
75+
- name: Trust workspace
76+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
77+
78+
- name: make clean
79+
run: |
80+
make distclean
81+
82+
- name: Select config
83+
run: |
84+
cp config/examples/sim-compressed-ram-enc.config .config
85+
case "${{ matrix.cipher }}" in
86+
aes128) echo "ENCRYPT_WITH_AES128=1" >> .config ;;
87+
aes256) echo "ENCRYPT_WITH_AES256=1" >> .config ;;
88+
esac
89+
90+
- name: Build tools
91+
run: |
92+
make -C tools/keytools && make -C tools/bin-assemble
93+
94+
- name: Build wolfboot.elf and test-app/image.elf
95+
run: |
96+
make clean && make
97+
98+
- name: Assemble the encrypted compressed A/B slots
99+
run: |
100+
make test-sim-compressed-ram-enc-with-update
101+
102+
- name: Run the encrypted compressed image simulator test
103+
run: |
104+
./tools/scripts/sim-compressed-ram-enc.sh
105+
106+
compressed_cross_build:
107+
runs-on: ubuntu-latest
108+
container:
109+
image: ghcr.io/wolfssl/wolfboot-ci-arm:v1.0
110+
timeout-minutes: 20
111+
strategy:
112+
fail-fast: false
113+
matrix:
114+
config: [ stm32h7-compressed-ram, stm32f407-compressed-ram ]
115+
116+
steps:
117+
- uses: actions/checkout@v4
118+
with:
119+
submodules: true
120+
121+
- name: Trust workspace
122+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
123+
124+
- name: Select config
125+
run: |
126+
cp config/examples/${{ matrix.config }}.config .config
127+
128+
- name: Build
129+
run: |
130+
make clean && make keysclean && make
131+
132+
# A link that succeeds but places the vector table somewhere other
133+
# than the load address boots into garbage, and nothing catches that
134+
# without hardware. Assert the layout from the ELF instead.
135+
- name: Check the application is linked for the load address
136+
run: |
137+
LOAD=$(grep -E '^WOLFBOOT_LOAD_ADDRESS\??=' .config | tail -1 | cut -d= -f2)
138+
SIZE=$(grep -E '^WOLFBOOT_RAMBOOT_MAX_SIZE\??=' .config | tail -1 | cut -d= -f2)
139+
echo "load address $LOAD, region size $SIZE"
140+
# .text must start exactly at the load address, with LMA == VMA
141+
arm-none-eabi-objdump -h test-app/image.elf | \
142+
awk -v want=$(printf "%08x" $((LOAD))) '
143+
$2==".text" { if ($4!=want || $5!=want) {
144+
printf "`.text` at vma %s lma %s, expected %s\n",$4,$5,want; exit 1 }
145+
print "ok: .text at " $4 " (lma " $5 ")" }'
146+
# word 0 of the image is the initial stack pointer and must be the
147+
# top of the load region
148+
SP=$(od -An -t x4 -N 4 test-app/image.bin | tr -d ' ')
149+
WANT_SP=$(printf "%08x" $((LOAD + SIZE)))
150+
if [ "$SP" != "$WANT_SP" ]; then
151+
echo "initial SP is 0x$SP, expected 0x$WANT_SP"; exit 1
152+
fi
153+
echo "ok: initial SP 0x$SP"
154+
155+
- name: Check wolfBoot does not overlap the load region
156+
run: |
157+
LOAD=$(grep -E '^WOLFBOOT_LOAD_ADDRESS\??=' .config | tail -1 | cut -d= -f2)
158+
END=$(arm-none-eabi-nm wolfboot.elf | awk '$3=="_end" {print $1}')
159+
# Only meaningful when wolfBoot's RAM and the load region share an
160+
# address space (STM32F4); on the H7 they are different SRAMs.
161+
if [ $((0x$END)) -ge $((LOAD)) ] && \
162+
[ $((0x$END)) -lt $((LOAD + 0x100000)) ]; then
163+
echo "wolfBoot _end 0x$END is inside the load region at $LOAD"; exit 1
164+
fi
165+
echo "ok: wolfBoot _end 0x$END clear of the load region at $LOAD"

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ bootmode1/
453453

454454
# Emulator test: generated linker scripts
455455
test-app/emu-test-apps/*/target.ld
456+
test-app/*.gz
456457

457458
# AMD/Xilinx Versal test artifacts
458459
uart_log*.txt

‎arch.mk‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3177,9 +3177,22 @@ else
31773177
endif
31783178
endif
31793179
else
3180-
ifeq ($(DUALBANK_SWAP),1)
3180+
ifeq ($(COMPRESSED_RAM_SLOTS),1)
3181+
# Compressed A/B slots, execute from RAM. Both slots hold a signed,
3182+
# gzip-compressed image; the RAM loader picks the highest valid version,
3183+
# verifies the signature over the compressed bytes and inflates to
3184+
# WOLFBOOT_LOAD_ADDRESS. There is no swap or install step, so the slot
3185+
# roles are decided per boot by version, as for the hardware bank swap.
3186+
ifeq ($(DUALBANK_SWAP),1)
3187+
$(error COMPRESSED_RAM_SLOTS=1 is incompatible with DUALBANK_SWAP=1)
3188+
endif
31813189
CFLAGS+=-DWOLFBOOT_DUALBOOT
3182-
UPDATE_OBJS:=src/update_flash_hwswap.o
3190+
UPDATE_OBJS:=src/update_ram.o
3191+
else
3192+
ifeq ($(DUALBANK_SWAP),1)
3193+
CFLAGS+=-DWOLFBOOT_DUALBOOT
3194+
UPDATE_OBJS:=src/update_flash_hwswap.o
3195+
endif
31833196
endif
31843197
endif
31853198

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
ARCH=sim
2+
TARGET=sim
3+
SIGN?=ED25519
4+
HASH?=SHA256
5+
WOLFBOOT_SMALL_STACK?=0
6+
SPI_FLASH=0
7+
DEBUG=1
8+
9+
# Compressed A/B slots, executed from RAM, encrypted at rest.
10+
COMPRESSED_RAM_SLOTS=1
11+
COMPRESSED_ENCRYPT=1
12+
GZIP=1
13+
14+
# sizes should be multiple of system page size
15+
WOLFBOOT_PARTITION_SIZE=0x40000
16+
WOLFBOOT_SECTOR_SIZE=0x1000
17+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
18+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000
19+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000
20+
21+
# See sim-compressed-ram.config for why the load region sits above wolfboot.elf.
22+
WOLFBOOT_LOAD_ADDRESS=0x1000000
23+
WOLFBOOT_RAMBOOT_MAX_SIZE=0x40000
24+
25+
# An encrypted slot cannot be verified in place, so it is decrypted into a
26+
# staging region first. Must be disjoint from the load region, or the
27+
# decompressor would overwrite its own input.
28+
WOLFBOOT_COMPRESSED_STAGE_ADDRESS=0x1100000
29+
30+
# required for keytools
31+
WOLFBOOT_FIXED_PARTITIONS=1
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
ARCH=sim
2+
TARGET=sim
3+
SIGN?=ED25519
4+
HASH?=SHA256
5+
WOLFBOOT_SMALL_STACK?=0
6+
SPI_FLASH=0
7+
DEBUG=1
8+
9+
# Compressed A/B slots, executed from RAM. Both slots hold a signed gzip
10+
# stream; wolfBoot verifies the compressed bytes in place, inflates to
11+
# WOLFBOOT_LOAD_ADDRESS and boots there.
12+
COMPRESSED_RAM_SLOTS=1
13+
GZIP=1
14+
15+
# sizes should be multiple of system page size
16+
WOLFBOOT_PARTITION_SIZE=0x40000
17+
WOLFBOOT_SECTOR_SIZE=0x1000
18+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
19+
# if on external flash, it should be multiple of system page size
20+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000
21+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000
22+
23+
# The simulator's do_boot() writes a whole partition from the load address,
24+
# so the load region must be at least WOLFBOOT_PARTITION_SIZE.
25+
#
26+
# WOLFBOOT_ORIGIN is 0 here (simulator flash lives at the runtime
27+
# sim_ram_base), so the loader's bootloader-overlap guard degenerates to
28+
# "below _end is suspect". wolfboot.elf is a non-PIE host binary loaded at
29+
# 0x400000, so keep the load region above it.
30+
WOLFBOOT_LOAD_ADDRESS=0x1000000
31+
WOLFBOOT_RAMBOOT_MAX_SIZE=0x40000
32+
33+
# required for keytools
34+
WOLFBOOT_FIXED_PARTITIONS=1
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# STM32F407-DISCOVERY with compressed A/B images executed from RAM.
2+
#
3+
# The F407 has one contiguous 128KB SRAM shared between wolfBoot and the
4+
# inflated application, so hal/stm32f4_ramboot.ld caps wolfBoot at the
5+
# bottom 16KB and the application occupies the remaining 112KB. That is the
6+
# ceiling on this part: the CCM RAM at 0x10000000 cannot be used, because
7+
# on the F4 it is reachable only by the data bus and instruction fetch from
8+
# it faults.
9+
#
10+
# GZIP_SMALL=1 selects the bit-serial Huffman decoder: a few KB less code
11+
# and no static tables, which matters more than throughput on a 1MB part.
12+
#
13+
# Note that hal/stm32f4.c has no debug UART, so DEBUG=1 only buys symbols
14+
# here: there is no console for the "Inflating ... done" progress line.
15+
# Inspect the load region over SWD instead, or add a UART to the HAL.
16+
ARCH?=ARM
17+
TARGET?=stm32f4
18+
SIGN?=ED25519
19+
HASH?=SHA256
20+
DEBUG?=1
21+
VTOR?=1
22+
SPMATH?=1
23+
24+
COMPRESSED_RAM_SLOTS?=1
25+
GZIP?=1
26+
GZIP_SMALL?=1
27+
28+
WOLFBOOT_PARTITION_SIZE?=0x20000
29+
WOLFBOOT_SECTOR_SIZE?=0x20000
30+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08020000
31+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08040000
32+
# Unused in this mode (there is no swap or install step), but still
33+
# required by the keytools and the partition trailer layout.
34+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x08060000
35+
36+
# SRAM above wolfBoot's 16KB region. 0x400-aligned for the SCB->VTOR write.
37+
WOLFBOOT_LOAD_ADDRESS?=0x20004000
38+
WOLFBOOT_RAMBOOT_MAX_SIZE?=0x1C000
39+
40+
CLOCK_SPEED?=160000000
41+
STM32_PLLM?=8
42+
STM32_PLLN?=336
43+
STM32_PLLP?=2
44+
STM32_PLLQ?=7
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# NUCLEO-F439ZI with compressed A/B images executed from RAM.
2+
#
3+
# The F439ZI has 2MB of flash and 256KB of SRAM, of which 192KB is
4+
# contiguous at 0x20000000 (SRAM1 112KB + SRAM2 16KB + SRAM3 64KB).
5+
# hal/stm32f4_ramboot.ld caps wolfBoot at the bottom 16KB, leaving 176KB
6+
# for the inflated application. The 64KB CCM at 0x10000000 cannot be used:
7+
# on the F4 it is reachable only by the data bus, so an instruction fetch
8+
# from it faults.
9+
#
10+
# Console is USART3 on PD8/PD9, the ST-LINK virtual COM port on the
11+
# NUCLEO-144 boards, via hal/uart/uart_drv_stm32f4.c.
12+
ARCH?=ARM
13+
TARGET?=stm32f4
14+
SIGN?=ED25519
15+
HASH?=SHA256
16+
DEBUG?=1
17+
DEBUG_UART?=1
18+
CFLAGS_EXTRA+=-DUSE_UART3
19+
VTOR?=1
20+
SPMATH?=1
21+
22+
COMPRESSED_RAM_SLOTS?=1
23+
GZIP?=1
24+
# Required at a 16KB bootloader RAM region: the table-driven decoder needs
25+
# about 19KB of .bss for its Huffman tables and fails to link.
26+
GZIP_SMALL?=1
27+
28+
WOLFBOOT_PARTITION_SIZE?=0x20000
29+
WOLFBOOT_SECTOR_SIZE?=0x20000
30+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08020000
31+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08040000
32+
# Unused in this mode (there is no swap or install step), but still
33+
# required by the keytools and the partition trailer layout.
34+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x08060000
35+
36+
# SRAM above wolfBoot's 16KB region. 0x400-aligned for the SCB->VTOR write.
37+
# This uses all the remaining SRAM, which is the right choice when the slots
38+
# are not encrypted; the encrypted variant below has to give some back.
39+
WOLFBOOT_LOAD_ADDRESS?=0x20004000
40+
WOLFBOOT_RAMBOOT_MAX_SIZE?=0x2C000
41+
42+
# Encryption of both slots at rest. Commented out because it needs a key
43+
# provisioned on the device first: hal_get_image_encrypt_key() reads
44+
# WOLFBOOT_COMPRESSED_KEY_ADDRESS, expecting the key followed by the nonce,
45+
# and rejects an erased or all-zero region. Override that hook in the HAL to
46+
# take the key from OTP or a secure element rather than flash.
47+
#
48+
# An encrypted slot cannot be verified in place, so it is decrypted into a
49+
# staging region first, and that region must be disjoint from the load
50+
# region. On this part the 176KB above wolfBoot therefore has to be split:
51+
# 112KB of load region and a 64KB staging region at the top of SRAM, which
52+
# bounds the compressed image to 64KB.
53+
#
54+
# Sign slot B with --encrypt-iv-offset $((WOLFBOOT_PARTITION_SIZE / 64)) for
55+
# ChaCha20, or / 16 for AES-CTR, so the two slots never share a keystream.
56+
#COMPRESSED_ENCRYPT?=1
57+
#WOLFBOOT_RAMBOOT_MAX_SIZE?=0x1C000
58+
#WOLFBOOT_COMPRESSED_STAGE_ADDRESS?=0x20020000
59+
#WOLFBOOT_COMPRESSED_KEY_ADDRESS?=0x080E0000
60+
61+
# HSE 8MHz from the ST-LINK MCO -> 168MHz SYSCLK, PCLK1 42MHz
62+
CLOCK_SPEED?=168000000
63+
STM32_PLLM?=8
64+
STM32_PLLN?=336
65+
STM32_PLLP?=2
66+
STM32_PLLQ?=7

0 commit comments

Comments
 (0)