Skip to content

Add NXP i.MX95 post-quantum demo for the A55 cluster and Cortex-M7 - #13

Open
dgarske wants to merge 2 commits into
wolfSSL:masterfrom
dgarske:imx95_pqc_demo
Open

dgarske wants to merge 2 commits into
wolfSSL:masterfrom
dgarske:imx95_pqc_demo

Conversation

@dgarske

@dgarske dgarske commented Aug 19, 2026

Copy link
Copy Markdown
Member

Depends on wolfBoot wolfSSL/wolfBoot#863 (the imx95_m7 target the demo builds) and wolfSSL/wolfBoot#888 (the imx95_a55 target, whose DDR log ring the left pane reads). Merge those first.


What this adds

A two-pane demo running entirely on a Toradex SMARC iMX95, on one screen:

pane shows
left wolfBoot's own ML-DSA-87 verified boot of Linux, handing over to wolfCrypt ML-KEM / ML-DSA benchmarks in a container on the Cortex-A55 cluster once the first result lands
right wolfBoot performing ML-DSA-87 verified boot of the Cortex-M7, console relayed to Linux

The pairing is the point: the same post-quantum algorithms at the same security level, on a Linux application cluster and a bare-metal real-time core in one SoC - and with wolfBoot as the A55's BL33, both panes are showing wolfBoot verified boot of the machine you are looking at.

imx95-pqc-demo/
  container/   benchmark container for the A55 cluster
  m7/          Zephyr RPMsg payload wolfBoot verifies and boots, plus its overlay
  demo/        board-side orchestration and the two-pane renderer
  tools/       memtool - mmap /dev/mem access for the M7 and A55 consoles
  charts/      measured figures as images

Measured on hardware

Cortex-M7 at 800 MHz, DWT cycle counter, caches enabled:

wolfBoot text verify + boot at 800 MHz
ECDSA P-256 22,548 B 1,741,288 cycles 2.177 ms
ML-DSA-87 21,608 B 4,201,179 cycles 5.251 ms
ML-DSA-87, Zephyr payload 21,608 B 5,916,540 cycles 7.396 ms

Post-quantum verified boot costs 5.25 ms, and the ML-DSA-87 bootloader is 940 bytes smaller than the ECDSA one - ML-DSA verification is SHAKE plus polynomial arithmetic and never pulls in the big-integer math P-256 needs. Verification also scales well: 1,004 B to 54,080 B is 54x the payload for 1.41x the verify.

Four things the README documents because they cost real time

The payload is deliberately not samples/subsys/ipc/openamp_rsc_table. That sample assumes Linux loaded its ELF: the remote declares vrings with da = FW_RSC_ADDR_ANY and Linux writes the resolved addresses and virtio status back into the table it loaded. Here Linux loads wolfBoot, so those land in wolfBoot's table while the sample polls its own - and it hangs forever in rproc_virtio_wait_remote_ready(). This payload uses fixed vring addresses matching wolfBoot's table and skips the wait, because Linux registers virtio0 before releasing the core.

The M7 starts once per Linux boot. echo stop > .../state fails on this BSP and the core stays running, so replaying the boot is a real board power cycle, not a restart.

Nothing written to a console can reach the screen. Torizon's kernel is built without CONFIG_DRM_FBDEV_EMULATION, so /dev/fb0 never exists and a VT is not a path to the DisplayPort - containers are expected to own the screen through KMS. demo/drmfb.py is the smallest version of that: open /dev/dri/card0, take the connected connector's preferred mode, create one dumb buffer, and draw glyphs into the mapped pixels with pure ctypes, no libdrm and no new packages on a read-only OSTree rootfs. The renderer must also take the screen away from everything else that wants it - the getty, the framebuffer console redrawing over the KMS output, and Torizon's pairing overlay - before it can be the sole DRM master.

wolfBoot's own boot log outlives the handoff. The A55 console is gone by the time anything can render, so IMX95_LOG_RING=1 mirrors it into a ring in DDR using the same header layout as the M7 console. One reader (memtool con <addr>) serves both cores, and the left pane can show the verification that booted the very kernel it is running under.

A serial-console variant

demo/demo-uart.sh (unit: wolfssl-demo-uart.service) runs the same demo as one scrolling stream on the board's serial console instead of the DisplayPort. That is the better shape for recording, and for a reason the display cannot match: wolfBoot's own ML-DSA-87 verification of Linux prints there live, about 21 seconds after power-on, rather than being replayed from the DDR ring once Linux and a renderer exist. The M7's log and the benchmark results are prefixed [M7 ] and [A55] so one stream stays readable. Each line begins with an explicit SGR reset, because systemd writes its status lines as [<green> OK <reset>] and demo output landing between those two codes inherits the color; the script also quiets the kernel console for the duration (dmesg -n 1) so late-boot chatter does not interleave.

Note on the benchmark output

The renderer strips the benchmark's Cycles/op and Cycles per byte columns. On this part they derive from the 24 MHz generic timer rather than the 1.8 GHz core clock and are wrong by roughly 75x - they should not appear on a demo screen. ops/sec and MB/s are correct and are what the pane shows. The A55 boot pane is filtered the same way, down to the chain-of-trust lines, because the full log is far longer than the pane and the verification would otherwise scroll off.

Testing

Built and run on a Toradex SMARC iMX95 Hexa 8GB, Torizon OS 7.7.0, M7 at 800 MHz. The container was verified on the board (docker compose up), and containerized benchmark numbers match the native ones (ML-KEM-768 encap 17,074 vs 17,112 native; ML-DSA-87 verify 1,720 vs 1,711). The Zephyr payload was rebuilt from the packaged copy to confirm the directory is self-contained: 54,240 bytes, linked at 0x80103000. Both panes were confirmed over repeated cold power cycles with wolfBoot as the A55's BL33, which is what the demo's replay beat actually exercises.

No board-specific credentials, addresses or vendor boot binaries are included.

@dgarske dgarske self-assigned this Aug 19, 2026
Copilot AI lite review requested due to automatic review settings August 19, 2026 21:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds an i.MX95 “post-quantum on both clusters” demo that pairs Linux-side wolfCrypt PQC benchmarks (A55) with wolfBoot ML-DSA-87 verified boot and console relay from the Cortex-M7.

Changes:

  • Add a /dev/mem mmap-based memtool utility for staging payloads and dumping the M7 shared-memory console.
  • Add a Zephyr OpenAMP/RPMsg payload for M7 that relays wolfBoot’s shared-memory console to Linux as an RPMsg TTY.
  • Add board orchestration scripts, a two-pane console renderer, and a benchmark container build/runtime setup.

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
imx95-pqc-demo/tools/memtool.c Adds /dev/mem mmap read/write/load/fill and M7 console ring dump tooling.
imx95-pqc-demo/m7/zephyr-app/src/main.c Implements the M7-side OpenAMP/RPMsg endpoint and wolfBoot console relay logic.
imx95-pqc-demo/m7/zephyr-app/prj.conf Configures Zephyr for OpenAMP + minimal logging and documents MPU constraints.
imx95-pqc-demo/m7/zephyr-app/CMakeLists.txt Defines the Zephyr application build.
imx95-pqc-demo/m7/imx95_wolfboot.overlay Relocates the Zephyr image, enables MU, and declares MPU-accessible console/status region.
imx95-pqc-demo/m7/build.sh Provides a reproducible Zephyr build producing a raw binary payload.
imx95-pqc-demo/demo/twopane.py Renders the two-pane demo view on a console without tmux/screen dependencies.
imx95-pqc-demo/demo/stage.sh Stages demo artifacts to the board and builds/stages memtool.
imx95-pqc-demo/demo/m7-start.sh Starts remoteproc and stages the signed payload into the M7 boot partition region.
imx95-pqc-demo/demo/m7-console-tail.sh Provides a console-ring tailer for the M7 pane (alternative to RPMsg TTY).
imx95-pqc-demo/demo/demo-run.sh Orchestrates container start, M7 start, and launches the renderer to a target TTY.
imx95-pqc-demo/container/entrypoint.sh Runs benchmark cycles and selects baseline vs sha3-crypto build at runtime.
imx95-pqc-demo/container/docker-compose.yml Defines the benchmark container service for on-board execution/logging.
imx95-pqc-demo/container/build-image.sh Builds (and optionally pushes) the arm64 demo container image.
imx95-pqc-demo/container/build-aarch64.sh Cross-builds wolfSSL for aarch64 in baseline and sha3-crypto configs.
imx95-pqc-demo/container/Dockerfile Multi-stage image build that cross-compiles wolfSSL and ships benchmarks.
imx95-pqc-demo/README.md Documents the demo purpose, measurements, build steps, and key gotchas.
README.md Adds the new i.MX95 PQC demo to the repository’s example list.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/tools/memtool.c
Comment thread imx95-pqc-demo/m7/zephyr-app/src/main.c
Comment thread imx95-pqc-demo/m7/zephyr-app/src/main.c
Comment thread imx95-pqc-demo/demo/twopane.py
Comment thread imx95-pqc-demo/demo/twopane.py Outdated
@dgarske
dgarske force-pushed the imx95_pqc_demo branch 3 times, most recently from 486acd5 to 68e60a7 Compare August 21, 2026 03:56
@dgarske
dgarske marked this pull request as ready for review September 16, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants