feat: STM32H563 UDS service-based OTA bootloader example#74
Merged
Conversation
Add flash_h5.{c,h} for STM32H563: NSKEYR unlock, quad-word program,
sector erase, OPTSR_PRG bank-swap via OPTSTRT + SystemReset (no
OBL_LAUNCH on H5), and flash_active_bank() via OPTSR_CUR. Register
map from RM0481 §7 / stm32h563.svd. Builds clean under -Wall -Wextra
-Werror; flash_h5.c added to APP_SRCS in bootloader Makefile.
…ader Integrates mbedTLS 3.6 AES-128-CMAC into the freestanding Cortex-M33 bootloader build: - bootloader_mbedtls_config.h: minimal config (AES_C + CIPHER_C + CMAC_C + MEMORY_BUFFER_ALLOC_C); named to avoid shadowing mbedtls/mbedtls_config.h from within the mbedTLS include tree - sec_cmac.h / sec_cmac.c: one-shot aes_cmac() + sec_crypto_init() over a 4096-byte static allocator buffer; handles NULL input for zero-length CMAC - sec_cmac_test.c: RFC 4493 test vectors (Mlen=0, Mlen=16); run via `make cmac-test` (host gcc) - Makefile: MBEDTLS_SRCS (9 files in build/mbedtls/), MBEDTLS_CFLAGS with -Wno-unused-function/-parameter/-Wno-missing-field-initializers (no -Werror for upstream sources), cmac-test target - include/: freestanding assert.h, stdio.h, stdlib.h shims; string.h extended with strcmp/strlen; libc_stubs.c extended with strlen, exit, abort stubs RFC 4493 host test: PASS (both vectors) ARM Cortex-M33 clean build: PASS, no warnings
…l shims Replace the freestanding no-libc build (clang + rust-lld, fake string/stdlib/ stdio/assert headers, hand libc + __aeabi_* stubs, mbedTLS static buffer allocator) with arm-none-eabi-gcc + newlib-nano so the standard headers and functions are real. - Makefile: CC=arm-none-eabi-gcc, drop -ffreestanding/-fno-builtin and the shim include path; link with --specs=nano.specs --specs=nosys.specs -nostartfiles -T bootloader.ld. - Delete include/ fake headers and libc_stubs.c; newlib supplies mem*/str*, libgcc supplies __aeabi_*. - Add syscalls.c with a real _sbrk (heap from linker end toward _estack with a stack guard gap) backing newlib malloc/calloc/free. - mbedTLS config: enable AES/CIPHER/CMAC/AES_ROM_TABLES, drop the buffer allocator + platform memory; use newlib calloc/free directly. - sec_cmac: drop sec_crypto_init + static buffer, keep AES-128-CMAC one-shot. RFC 4493 CMAC vectors still pass; ARM build is warning-clean under -Werror.
…flash reprogramming callbacks
… on 0x27 security - bounds check now tests size against the region span before the addition so a crafted size cannot wrap uint32_t and bypass the guard - bl_request_download / bl_transfer_data / bl_transfer_exit / bl_routine_control all reject with NRC 0x33 when security_level < 1 (0x27 not yet completed) - 0xFF00 EraseMemory routine zeroes only transfer-state fields, not the bank-target fields (inactive_bank / app_base / app_end) - key-length early-return in bl_security_key documented as intentional
Add examples/h563_uds_bootloader/app/ containing: - app.ld: FLASH ORIGIN 0x08018010 (active-bank app slot past the 16-byte OTA header), LENGTH 0xE7FF0; RAM 0x20000000 640K - startup.c: freestanding Cortex-M33 vector table + Reset handler (no libc) - main.c: USART3 MMIO banner; -DAPP_VARIANT=A → "APP-A v1\n", -DAPP_VARIANT=B → "APP-B v2\n" - mkimage.py: packs raw .bin into [ota_image_header_t][payload] using zlib.crc32 (= CRC-32/ISO-HDLC, matching ota_crc32) - Makefile: make APP=A|B, make both, make image-verify (host cross-check via bootloader ota_crc.c confirms magic + CRC + SP validity)
app/Makefile emits app_*.elf/.bin and an auto-generated image_verify.c into the example root; these are build products, not source.
…TOR-aligned OTA_IMAGE_HDR_SIZE: 16 → 0x400. Cortex-M33 VTOR[6:0] are RES0; a full STM32H563 vector table (~147 entries, 588 bytes) requires the next pow2 = 1024-byte alignment. app_base+0x10 silently truncated to app_base, causing HardFault on first IRQ in any interrupt-using app. app_base (0x08018000) is 0x400-aligned, so app_base + OTA_IMAGE_HDR_SIZE = 0x08018400, and 0x08018400 & 0x3FF == 0. Changes: - ota_image.h: OTA_IMAGE_HDR_SIZE 16→0x400; MAX_PAYLOAD 0xE7FF0→0xE7C00; docs - app.ld: FLASH ORIGIN 0x08018010→0x08018400, LENGTH 0xE7FF0→0xE7C00 - mkimage.py: emit 0x400-byte header (16-byte struct + 1008 bytes 0xFF padding) - bootloader/main.c: update format comments (no code change; uses macro) - app/main.c: fix APP_VARIANT comment (=A/=B → =1/=2) All code paths already used OTA_IMAGE_HDR_SIZE macro; no literal 16 in logic. cmac-test, image-test, make both, objdump, image-verify all PASS.
…lock-size - isotp_recv: detect escape-SF ([0x00][len][data]) so 0x27 seed responses (18 bytes) are correctly parsed instead of failing with "SF len 0" - isotp_send: parse FC block_size and STmin; wait for next FC after each block and usleep() the decoded inter-frame gap (ms or 100-µs resolution) - canfd_round_len: guard zero-length input (map to 1) - max_block_len: reject mbl <= 1 to prevent chunk_data_size underflow - test_vcan.sh: add escape-SF and classic-SF isotp_recv unit tests
…y app
Reserve sector 11 (bank-relative 0x16000, 8 KB) of each bank for a
boot_state_t record: magic + pending flag + attempt counter.
ActivateSoftware (0xFF02) marks the inactive bank pending before
flash_set_swap_and_reset(). On the next boot the bootloader reads the
active bank's boot-state and decides:
- JUMP : not pending or no record (confirmed / erased sector)
- BUMP_AND_JUMP: pending, attempts < MAX_BOOT_ATTEMPTS (= 3)
- ROLLBACK : pending, attempts >= MAX — clear flag, swap back
App-side boot_confirm() erases sector 11 of the active bank once the
app is healthy; the all-0xFF sector is treated as confirmed on next boot.
App B-bad (APP=Bbad, variant 3) deliberately skips boot_confirm() to
demonstrate the rollback path.
Host tests:
bootstate-test: 12/12 PASS (all FSM transitions + full A→Bbad→rollback
sequence exercised via pure boot_state_decide())
cmac-test: 2/2 PASS
image-test: 6/6 PASS
BL code ~27 KB; boot-state sector at 0x08016000 (~62 KB headroom).
Same-bank erase is safe in the LabWired H563 simulator; production
firmware must execute the flash driver from ITCM/SRAM (RM0481 §7.3.4).
|
well done , you can read this |
…ollback) — closes #75 Add flash_swap_to_bank_and_reset() to flash_h5 (explicit SWAP_BANK set/clear) since flash_set_swap_and_reset() always sets the bit and would be a no-op when rolling back from bank 1 to bank 0. Add 0xFF03 PerformRollback to bl_routine_control: validates the other bank via app_is_valid() (returns 0x22 if invalid), clears its boot-state, then calls flash_swap_to_bank_and_reset(other) — does not return. Add --rollback mode to host/flash_tool (session + security access + 0xFF03). Add three bootstate-test cases for the 0xFF03 post-rollback boot-state invariant. Update README with routine table and automatic-vs-tester-commanded rollback note.
…A (bank1->0) swaps
…m builds The SIM_OTA_TESTER build bypassed aes_cmac() and memcpy'd a host-precomputed key, on the assumption the simulator could not run mbedTLS AES. The real cause was a simulator byte-load sign-extension bug (now fixed): mbedTLS reloads AES T-table state via ldrb.w [sp], which the sim sign-extended, corrupting the ciphertext. Drop the bypass and run aes_cmac(DEMO_SECRET, seed) unconditionally so the sim OTA smoke exercises the genuine on-chip crypto path. The H563 OTA sim smoke now passes all 14 milestones with real in-sim AES-CMAC.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #64.
A complete, real STM32H563 UDS service-based OTA bootloader example: dual-bank A/B with hardware
SWAP_BANK, AES-CMAC security access, and the full ISO-14229 reprogramming flow over CAN-FD — built on udslib's flash/security/session services.What's included (
examples/h563_uds_bootloader/)bootloader/-> the OTA bootloader firmware (arm-none-eabi-gcc+ newlib-nano):restrict_sessions) + 0x27 AES-CMAC security (real mbedTLS, bare-metal) enforced on every reprogramming service.0x34RequestDownload (validated to the inactive bank's app region, overflow-safe) →0x36TransferData →0x37TransferExit → RoutineControl0xFF00Erase /0xFF01CheckProgrammingDependencies (CRC-32) /0xFF02ActivateSoftware (SWAP_BANK).OPTSR_PRG.SWAP_BANK+OPTCR.OPTSTRT) — register offsets/bits SVD-confirmed against RM0481.app/demo App A / App B (+ a non-confirming "App B-bad" to demonstrate rollback) andmkimage.py(prepends the image header).host/a Linux SocketCAN CAN-FD flash tool that drives the full sequence (for users with CAN hardware, e.g. PCAN).Validation
-Werror); host self-tests pass:cmac-test(RFC 4493 AES-CMAC vectors),image-test,bootstate-test(rollback state machine),image-verify(mkimage output validated through the bootloader's own CRC).SWAP_BANK→ boot-new-app → rollback loop is validated in the labwired-core H563 simulation, whose H5 flash model was extended for this (depends on feat(flash): model STM32H5 sector-erase + bank-swap (UDS OTA bootloader enablement) labwired-core#326 — the sim smoke lands once that merges).uart_initneeds RCC/GPIO on real silicon; demo 0x27 secret is an example).Design + plan:
docs/superpowers/specs/anddocs/superpowers/plans/.