From 59d1f5f920d9e8efff50320951307c48c4261a22 Mon Sep 17 00:00:00 2001 From: "Matthias J. Kannwischer" Date: Tue, 16 Jun 2026 13:38:13 +0800 Subject: [PATCH 1/7] Add Zephyr-based test platform for QEMU MPS boards Build the mlkem-native tests as Zephyr applications so they run on QEMU-emulated Arm MPS boards spanning Cortex-M3/M4/M7/M33/M55. This greatly simplifies supporting more hardware without the need for dedicated hardware abstraction layers. Signed-off-by: Matthias J. Kannwischer --- .github/workflows/all.yml | 6 +++ .github/workflows/zephyr.yml | 54 +++++++++++++++++++ flake.nix | 10 ++++ nix/util.nix | 11 ++++ nix/zephyr/default.nix | 53 +++++++++++++++++++ scripts/lint | 2 +- test/hal/hal.c | 15 ++++-- test/zephyr/app/CMakeLists.txt | 53 +++++++++++++++++++ test/zephyr/app/Kconfig | 14 +++++ test/zephyr/app/prj.conf | 4 ++ test/zephyr/app/shim.c | 94 +++++++++++++++++++++++++++++++++ test/zephyr/exec_wrapper.py | 38 ++++++++++++++ test/zephyr/platform.mk | 96 ++++++++++++++++++++++++++++++++++ 13 files changed, 446 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/zephyr.yml create mode 100644 nix/zephyr/default.nix create mode 100644 test/zephyr/app/CMakeLists.txt create mode 100644 test/zephyr/app/Kconfig create mode 100644 test/zephyr/app/prj.conf create mode 100644 test/zephyr/app/shim.c create mode 100755 test/zephyr/exec_wrapper.py create mode 100644 test/zephyr/platform.mk diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml index f28879790..bd607ec59 100644 --- a/.github/workflows/all.yml +++ b/.github/workflows/all.yml @@ -108,3 +108,9 @@ jobs: needs: [ base ] uses: ./.github/workflows/baremetal.yml secrets: inherit + zephyr: + name: Zephyr + permissions: + contents: 'read' + needs: [ base ] + uses: ./.github/workflows/zephyr.yml diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml new file mode 100644 index 000000000..05b707075 --- /dev/null +++ b/.github/workflows/zephyr.yml @@ -0,0 +1,54 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +name: Zephyr +permissions: + contents: read +on: + workflow_call: + workflow_dispatch: + +jobs: + zephyr_tests: + name: Zephyr tests (${{ matrix.target.board }}, ${{ matrix.target.cpu }}) + strategy: + fail-fast: false + matrix: + target: + - { board: mps2-an385, cpu: Cortex-M3, opt: no_opt } + - { board: mps2-an386, cpu: Cortex-M4, opt: no_opt } + - { board: mps2-an500, cpu: Cortex-M7, opt: no_opt } + - { board: mps2-an521, cpu: Cortex-M33, opt: no_opt } + - { board: mps3-an547, cpu: Cortex-M55, opt: all } + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - name: zephyr build + test + uses: ./.github/actions/functest + env: + EXTRA_MAKEFILE: test/zephyr/platform.mk + ZEPHYR_TARGET: ${{ matrix.target.board }} + with: + nix-shell: zephyr + gh_token: ${{ secrets.GITHUB_TOKEN }} + opt: ${{ matrix.target.opt }} + func: true + kat: true + acvp: true + wycheproof: false + examples: false + unit: false + stack: false + alloc: false + rng_fail: false + check_namespace: false + # Smoke only: QEMU doesn't model cycle counts (real numbers come from the + # FPGA); this just exercises the bench build + run. + - name: bench (smoke) + env: + EXTRA_MAKEFILE: test/zephyr/platform.mk + ZEPHYR_TARGET: ${{ matrix.target.board }} + run: | + opt=${{ matrix.target.opt == 'all' && 'opt' || 'no_opt' }} + nix develop .#zephyr --command ./scripts/tests bench -c PMU --opt=$opt + nix develop .#zephyr --command ./scripts/tests bench --components -c PMU --opt=$opt diff --git a/flake.nix b/flake.nix index 1e6d3b3f3..0eb8a0ddc 100644 --- a/flake.nix +++ b/flake.nix @@ -148,6 +148,16 @@ ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isAarch64 [ config.packages.toolchain_x86_64 ]; }; + # Zephyr build environment (board chosen at make time via EXTRA_MAKEFILE) + packages.zephyr = util.zephyr; + devShells.zephyr = util.mkShell { + packages = builtins.attrValues + { + inherit (util) zephyr; + inherit (pkgs) gcc-arm-embedded qemu cmake ninja dtc gperf coreutils git; + } ++ [ util.zephyrPythonEnv ]; + }; + # arm-none-eabi-gcc + platform files from pqmx devShells.cross-arm-embedded = util.mkShell { packages = builtins.attrValues diff --git a/nix/util.nix b/nix/util.nix index bbe0d8688..443c3dcb8 100644 --- a/nix/util.nix +++ b/nix/util.nix @@ -108,6 +108,17 @@ rec { s2n_bignum = pkgs.callPackage ./s2n_bignum { }; slothy = pkgs.callPackage ./slothy { }; pqmx = pkgs.callPackage ./pqmx { }; + zephyr = pkgs.callPackage ./zephyr { }; + zephyrPythonEnv = pkgs.python3.withPackages (ps: with ps; [ + pyelftools + pyyaml + packaging + pykwalify + jsonschema + anytree + intelhex + colorama + ]); avr-toolchain = pkgs.callPackage ./avr { }; # Helper function to build individual cross toolchains diff --git a/nix/zephyr/default.nix b/nix/zephyr/default.nix new file mode 100644 index 000000000..4f5e8fdf3 --- /dev/null +++ b/nix/zephyr/default.nix @@ -0,0 +1,53 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +{ stdenvNoCC +, fetchFromGitHub +, gcc-arm-embedded +, writeText +}: + +# Board-agnostic Zephyr build environment: a pinned Zephyr tree plus the +# modules needed by the boards we target, exposed via a setup hook so a plain +# `cmake` build works with no west workspace. CMSIS-6 covers the Cortex-M +# boards; add further modules here as more boards are wired up. +let + zephyr = fetchFromGitHub { + owner = "zephyrproject-rtos"; + repo = "zephyr"; + rev = "v4.4.1"; + hash = "sha256-8bzykJs6fFGiofCxRKh8M9jdXr5R8FM0lAbA28yanGk="; + }; + + # Revision pinned by the Zephyr v4.4.1 manifest (west.yml). + cmsis_6 = fetchFromGitHub { + owner = "zephyrproject-rtos"; + repo = "CMSIS_6"; + rev = "30a859f44ef8ab4dc8f84b03ed586fd16ccf9d74"; + hash = "sha256-nTehISN0pu9gnOZMpGaBQ3DFmNxAqAZPGpvbKfEM35o="; + }; +in +stdenvNoCC.mkDerivation { + pname = "mlkem-native-zephyr"; + version = "4.4.1"; + + dontUnpack = true; + + installPhase = '' + mkdir -p $out + ln -s ${zephyr} $out/zephyr + ln -s ${cmsis_6} $out/cmsis_6 + ''; + + setupHook = writeText "setup-hook.sh" '' + export ZEPHYR_BASE="$1/zephyr" + export ZEPHYR_MODULES="$1/cmsis_6" + export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb + export GNUARMEMB_TOOLCHAIN_PATH=${gcc-arm-embedded} + ''; + + meta = { + description = "Pinned Zephyr tree and modules for the Zephyr-based test flows"; + homepage = "https://www.zephyrproject.org/"; + }; +} diff --git a/scripts/lint b/scripts/lint index d85b9134a..4e96405d9 100755 --- a/scripts/lint +++ b/scripts/lint @@ -268,7 +268,7 @@ gh_group_end check-tabs() { - for file in $(git ls-files -- ":/" ":/!:Makefile" ":/!:**/Makefile" ":/!:**/Makefile.*" ":/!:Makefile.*" ":/!:*.mk" ":/!:*.patch" ":/!:*.S" ":/!:*.inc" ":/!:nix/valgrind/*.txt"); do + for file in $(git ls-files -- ":/" ":/!:Makefile" ":/!:**/Makefile" ":/!:**/Makefile.*" ":/!:Makefile.*" ":/!:**/Kconfig" ":/!:*.mk" ":/!:*.patch" ":/!:*.S" ":/!:*.inc" ":/!:nix/valgrind/*.txt"); do if [[ ! -L $file ]] && grep -Pq '\t' "$file"; then l=$(grep -Pn '\t' "$file" | head -1 | cut -d: -f1) echo "$file $l" diff --git a/test/hal/hal.c b/test/hal/hal.c index 0a1b9f23f..9dfc73d95 100644 --- a/test/hal/hal.c +++ b/test/hal/hal.c @@ -42,7 +42,16 @@ #include "hal.h" -#if defined(PMU_CYCLES) +#if defined(__ZEPHYR__) + +/* Zephyr: use the kernel cycle counter. */ +#include + +void enable_cyclecounter(void) {} +void disable_cyclecounter(void) {} +uint64_t get_cyclecounter(void) { return k_cycle_get_32(); } + +#elif defined(PMU_CYCLES) #if defined(__x86_64__) @@ -368,10 +377,10 @@ uint64_t get_cyclecounter(void) return g_counters[2]; } -#else /* !PMU_CYCLES && !PERF_CYCLES && MAC_CYCLES */ +#else /* !__ZEPHYR__ && !PMU_CYCLES && !PERF_CYCLES && MAC_CYCLES */ void enable_cyclecounter(void) { return; } void disable_cyclecounter(void) { return; } uint64_t get_cyclecounter(void) { return (0); } -#endif /* !PMU_CYCLES && !PERF_CYCLES && !MAC_CYCLES */ +#endif /* !__ZEPHYR__ && !PMU_CYCLES && !PERF_CYCLES && !MAC_CYCLES */ diff --git a/test/zephyr/app/CMakeLists.txt b/test/zephyr/app/CMakeLists.txt new file mode 100644 index 000000000..48ebf01e7 --- /dev/null +++ b/test/zephyr/app/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mlkem_native_zephyr) + +# Parameters supplied via -D from platform.mk: +# ZEPHYR_NATIVE_ROOT - mlkem-native checkout the test is built from +# ZEPHYR_LEVEL - ML-KEM parameter set: 512 | 768 | 1024 +# ZEPHYR_TEST_SRC - test entrypoint, relative to ZEPHYR_NATIVE_ROOT +# ZEPHYR_TEST_DEFS - optional extra -D defines (space separated) +set(R ${ZEPHYR_NATIVE_ROOT}) + +target_sources(app PRIVATE + ${R}/mlkem/mlkem_native.c + ${R}/${ZEPHYR_TEST_SRC} + ${R}/test/notrandombytes/notrandombytes.c + ${CMAKE_CURRENT_SOURCE_DIR}/shim.c +) + +target_include_directories(app PRIVATE + ${R}/mlkem + ${R}/test/notrandombytes +) + +target_compile_definitions(app PRIVATE MLK_CONFIG_PARAMETER_SET=${ZEPHYR_LEVEL}) + +if(ZEPHYR_TEST_DEFS) + separate_arguments(_defs UNIX_COMMAND "${ZEPHYR_TEST_DEFS}") + target_compile_definitions(app PRIVATE ${_defs}) +endif() + +# Optional native FIPS202 backend (e.g. Armv8.1-M MVE on Cortex-M55). The +# monolithic mlkem_native.c already includes the backend C sources; its +# assembly counterpart comes from mlkem_native_asm.S. +if(ZEPHYR_FIPS202_BACKEND) + target_sources(app PRIVATE ${R}/mlkem/mlkem_native_asm.S) + target_compile_definitions(app PRIVATE + MLK_CONFIG_USE_NATIVE_BACKEND_FIPS202 + "MLK_CONFIG_FIPS202_BACKEND_FILE=\"${ZEPHYR_FIPS202_BACKEND}\"") +endif() + +# The bench tests need the cycle-counting HAL. +if(ZEPHYR_TEST_HAL) + target_sources(app PRIVATE ${R}/test/hal/hal.c) + target_include_directories(app PRIVATE ${R}/test/hal) +endif() + +# Each test brings its own int main(void); rename it so the Zephyr shim +# (shim.c) owns main() and can stop QEMU with the test's exit code. +set_source_files_properties(${R}/${ZEPHYR_TEST_SRC} + PROPERTIES COMPILE_DEFINITIONS "main=mlk_test_main") diff --git a/test/zephyr/app/Kconfig b/test/zephyr/app/Kconfig new file mode 100644 index 000000000..baaa0ecbc --- /dev/null +++ b/test/zephyr/app/Kconfig @@ -0,0 +1,14 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +# Corstone-300's Zephyr SoC doesn't advertise MVE, but its Cortex-M55 (and +# QEMU's mps3-an547) has it. Force-select it to build the Armv8.1-M MVE FIPS202 +# backend. Enabled per target by platform.mk. +config FIPS202_MVE_BACKEND + bool "Build with the Armv8.1-M MVE FIPS202 backend" + select FPU + select ARMV8_M_DSP + select ARMV8_1_M_MVEI + select ARMV8_1_M_MVEF + +source "Kconfig.zephyr" diff --git a/test/zephyr/app/prj.conf b/test/zephyr/app/prj.conf new file mode 100644 index 000000000..0e42e22a6 --- /dev/null +++ b/test/zephyr/app/prj.conf @@ -0,0 +1,4 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_BOOT_BANNER=n diff --git a/test/zephyr/app/shim.c b/test/zephyr/app/shim.c new file mode 100644 index 000000000..b87a21391 --- /dev/null +++ b/test/zephyr/app/shim.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * Zephyr entrypoint shim for the mlkem-native tests. + * + * Each test's main() is renamed to mlk_test_main (-Dmain=mlk_test_main) and + * called from here. argv is fetched from the host via semihosting (needed by + * the acvp/wycheproof tests); on return we exit QEMU with the test's return + * code, again via semihosting. + */ + +#include + +extern int mlk_test_main(int argc, char **argv); + +/* Arm semihosting operations, trapped by QEMU via `bkpt 0xab`. */ +#define SYS_GET_CMDLINE 0x15 +#define SYS_EXIT_EXTENDED 0x20 +#define ADP_STOPPED_APPLICATION_EXIT 0x20026UL + +#define MAX_ARGS 32 +#define CMDLINE_BUF_SIZE 65536 + +static long semihost(unsigned op, void *arg) +{ + register unsigned r0 __asm__("r0") = op; + register void *r1 __asm__("r1") = arg; + __asm__ volatile("bkpt 0xab" : "+r"(r0) : "r"(r1) : "memory"); + return (long)r0; +} + +static char cmdline[CMDLINE_BUF_SIZE]; +static char *argv_buf[MAX_ARGS + 1]; + +/* Fetch the host command line via semihosting and split it into argv. */ +static int get_args(char ***argv_out) +{ + struct + { + char *buf; + size_t len; + } block = {cmdline, sizeof(cmdline) - 1}; + int argc = 0; + char *p = cmdline; + + *argv_out = argv_buf; + if (semihost(SYS_GET_CMDLINE, &block) != 0) + { + argv_buf[0] = "test"; + argv_buf[1] = NULL; + return 1; + } + + while (*p != '\0' && argc < MAX_ARGS) + { + while (*p == ' ' || *p == '\t') + { + p++; + } + if (*p == '\0') + { + break; + } + argv_buf[argc++] = p; + while (*p != '\0' && *p != ' ' && *p != '\t') + { + p++; + } + if (*p != '\0') + { + *p++ = '\0'; + } + } + argv_buf[argc] = NULL; + return argc; +} + +static void semihost_exit(int code) +{ + unsigned long block[2] = {ADP_STOPPED_APPLICATION_EXIT, (unsigned long)code}; + semihost(SYS_EXIT_EXTENDED, block); +} + +int main(void) +{ + char **argv; + int argc = get_args(&argv); + int rc = mlk_test_main(argc, argv); + semihost_exit(rc); + return rc; +} diff --git a/test/zephyr/exec_wrapper.py b/test/zephyr/exec_wrapper.py new file mode 100755 index 000000000..d4fd686e8 --- /dev/null +++ b/test/zephyr/exec_wrapper.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +# Runs a Zephyr ELF under QEMU (machine from QEMU_MACHINE, set in platform.mk). +# Extra args reach the guest via semihosting (see app/shim.c), the UART console +# goes to stdout via -nographic, and the guest's semihosting exit sets QEMU's +# return code. + +import os +import sys +import subprocess + +binpath = sys.argv[1] +args = sys.argv[2:] +machine = os.environ.get("QEMU_MACHINE", "mps3-an547") + +semihosting_args = [binpath] + args +semihosting_config = "enable=on," + ",".join(f"arg={a}" for a in semihosting_args) + +qemu_cmd = [ + "qemu-system-arm", + "-M", + machine, + "-monitor", + "none", + "-nographic", + "-semihosting-config", + semihosting_config, + "-kernel", + binpath, +] + +result = subprocess.run(qemu_cmd, encoding="utf-8", capture_output=True, timeout=300) +sys.stdout.write(result.stdout) +if result.returncode != 0: + sys.stderr.write(result.stderr) +sys.exit(result.returncode) diff --git a/test/zephyr/platform.mk b/test/zephyr/platform.mk new file mode 100644 index 000000000..b78c9184f --- /dev/null +++ b/test/zephyr/platform.mk @@ -0,0 +1,96 @@ +# Copyright (c) The mlkem-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +PLATFORM_PATH := test/zephyr + +# BUILD_DIR is set by the top-level Makefile after this file is included; +# define it here too so the explicit bin rules below expand to the right path. +BUILD_DIR ?= test/build + +# ZEPHYR_TARGET= selects a target. Each key maps to a Zephyr board and the +# QEMU machine emulating it; add a board with a row below. +ZEPHYR_TARGET ?= mps3-an547 + +ZEPHYR_BOARD_mps2-an385 := mps2/an385 +ZEPHYR_QEMU_mps2-an385 := mps2-an385 # Cortex-M3 +ZEPHYR_BOARD_mps2-an386 := mps2/an386 +ZEPHYR_QEMU_mps2-an386 := mps2-an386 # Cortex-M4 +ZEPHYR_BOARD_mps2-an500 := mps2/an500 +ZEPHYR_QEMU_mps2-an500 := mps2-an500 # Cortex-M7 +ZEPHYR_BOARD_mps2-an521 := mps2/an521/cpu0 +ZEPHYR_QEMU_mps2-an521 := mps2-an521 # Cortex-M33 +ZEPHYR_BOARD_mps3-an547 := mps3/corstone300/an547 +ZEPHYR_QEMU_mps3-an547 := mps3-an547 # Cortex-M55 + +ZEPHYR_FIPS202_BACKEND_mps3-an547 := fips202/native/armv81m/mve.h + +ZEPHYR_TARGETS := mps2-an385 mps2-an386 mps2-an500 mps2-an521 mps3-an547 + +ZEPHYR_BOARD := $(ZEPHYR_BOARD_$(ZEPHYR_TARGET)) +export QEMU_MACHINE := $(strip $(ZEPHYR_QEMU_$(ZEPHYR_TARGET))) + +ifeq ($(ZEPHYR_BOARD),) +$(error Unknown ZEPHYR_TARGET '$(ZEPHYR_TARGET)'. Supported: $(ZEPHYR_TARGETS)) +endif + +# The test binaries are built by Zephyr's CMake (which uses its own arm +# toolchain via the .#zephyr dev shell), not the generic Make rules. The +# top-level targets still attach the usual object/library prerequisites to the +# bin paths; with OPT=0 those are portable host objects that compile cleanly +# and are simply discarded (the Zephyr ELF is copied over them). +OPT ?= 0 + +# Native backends are an OPT=1 feature (an547 builds the Armv8.1-M MVE backend). +ZEPHYR_FIPS202_BACKEND := $(if $(filter 1,$(OPT)),$(strip $(ZEPHYR_FIPS202_BACKEND_$(ZEPHYR_TARGET)))) + +ZEPHYR_APP := $(PLATFORM_PATH)/app +ZEPHYR_BUILD_DIR := $(BUILD_DIR)/zephyr/$(ZEPHYR_TARGET) + +# Build a test as a Zephyr application and drop the resulting ELF at the path +# the top-level Makefile expects. An explicit rule for the exact bin path wins +# over the generic link pattern rule in test/mk/rules.mk. +# $(1) level $(2) bin name $(3) test source (repo-relative) $(4) extra -D +define ZEPHYR_BIN +$(BUILD_DIR)/mlkem$(1)/bin/$(2): + $$(Q)echo " ZEPHYR $(ZEPHYR_TARGET) ML-KEM-$(1): $(3)" + $$(Q)cmake -GNinja -S $(ZEPHYR_APP) -B $(ZEPHYR_BUILD_DIR)/$(2) \ + -DBOARD=$(ZEPHYR_BOARD) \ + -DZEPHYR_NATIVE_ROOT=$(CURDIR) \ + -DZEPHYR_LEVEL=$(1) \ + -DZEPHYR_TEST_SRC=$(3) \ + -DZEPHYR_TEST_DEFS="NTESTS_FUNC=3 NTESTS_KAT=100 MLK_BENCHMARK_NTESTS=10 MLK_BENCHMARK_NITERATIONS=10 MLK_BENCHMARK_NWARMUP=10" \ + -DZEPHYR_FIPS202_BACKEND=$(ZEPHYR_FIPS202_BACKEND) \ + $(if $(ZEPHYR_FIPS202_BACKEND),-DCONFIG_FIPS202_MVE_BACKEND=y) \ + $(4) \ + -DUSER_CACHE_DIR=$(abspath $(ZEPHYR_BUILD_DIR)/$(2)/.cache) \ + >/dev/null + $$(Q)cmake --build $(ZEPHYR_BUILD_DIR)/$(2) >/dev/null + $$(Q)[ -d $$(@D) ] || mkdir -p $$(@D) + $$(Q)cp $(ZEPHYR_BUILD_DIR)/$(2)/zephyr/zephyr.elf $$@ +endef + +$(eval $(call ZEPHYR_BIN,512,test_mlkem512,test/src/test_mlkem.c)) +$(eval $(call ZEPHYR_BIN,768,test_mlkem768,test/src/test_mlkem.c)) +$(eval $(call ZEPHYR_BIN,1024,test_mlkem1024,test/src/test_mlkem.c)) + +$(eval $(call ZEPHYR_BIN,512,gen_KAT512,test/src/gen_KAT.c)) +$(eval $(call ZEPHYR_BIN,768,gen_KAT768,test/src/gen_KAT.c)) +$(eval $(call ZEPHYR_BIN,1024,gen_KAT1024,test/src/gen_KAT.c)) + +$(eval $(call ZEPHYR_BIN,512,acvp_mlkem512,test/acvp/acvp_mlkem.c)) +$(eval $(call ZEPHYR_BIN,768,acvp_mlkem768,test/acvp/acvp_mlkem.c)) +$(eval $(call ZEPHYR_BIN,1024,acvp_mlkem1024,test/acvp/acvp_mlkem.c)) + +$(eval $(call ZEPHYR_BIN,512,wycheproof_mlkem512,test/wycheproof/wycheproof_mlkem.c)) +$(eval $(call ZEPHYR_BIN,768,wycheproof_mlkem768,test/wycheproof/wycheproof_mlkem.c)) +$(eval $(call ZEPHYR_BIN,1024,wycheproof_mlkem1024,test/wycheproof/wycheproof_mlkem.c)) + +$(eval $(call ZEPHYR_BIN,512,bench_mlkem512,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) +$(eval $(call ZEPHYR_BIN,768,bench_mlkem768,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) +$(eval $(call ZEPHYR_BIN,1024,bench_mlkem1024,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) + +$(eval $(call ZEPHYR_BIN,512,bench_components_mlkem512,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) +$(eval $(call ZEPHYR_BIN,768,bench_components_mlkem768,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) +$(eval $(call ZEPHYR_BIN,1024,bench_components_mlkem1024,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) + +EXEC_WRAPPER := $(abspath $(PLATFORM_PATH)/exec_wrapper.py) From 20b529294f32a911e6084a6a2e3d707cec6e95c2 Mon Sep 17 00:00:00 2001 From: "Matthias J. Kannwischer" Date: Wed, 17 Jun 2026 17:57:51 +0800 Subject: [PATCH 2/7] Baremetal: Remove baremetal MPS platforms and pqmx The Zephyr platform now covers the QEMU MPS boards, so the baremetal m55-an547 and m33-an524 platforms and the pqmx package they used are no longer needed. Also drops the cross-arm-embedded dev shell and the two baremetal.yml jobs. Signed-off-by: Matthias J. Kannwischer --- .github/workflows/baremetal.yml | 21 ----- flake.nix | 9 --- nix/pqmx/default.nix | 44 ----------- nix/util.nix | 1 - .../platform/m33-an524/exec_wrapper.py | 55 ------------- test/baremetal/platform/m33-an524/platform.mk | 77 ------------------ .../platform/m55-an547/exec_wrapper.py | 48 ------------ test/baremetal/platform/m55-an547/platform.mk | 78 ------------------- 8 files changed, 333 deletions(-) delete mode 100644 nix/pqmx/default.nix delete mode 100755 test/baremetal/platform/m33-an524/exec_wrapper.py delete mode 100644 test/baremetal/platform/m33-an524/platform.mk delete mode 100755 test/baremetal/platform/m55-an547/exec_wrapper.py delete mode 100644 test/baremetal/platform/m55-an547/platform.mk diff --git a/.github/workflows/baremetal.yml b/.github/workflows/baremetal.yml index 5be619b38..2c83c7b7a 100644 --- a/.github/workflows/baremetal.yml +++ b/.github/workflows/baremetal.yml @@ -16,27 +16,6 @@ jobs: fail-fast: false matrix: target: - - runner: ubuntu-latest - name: 'M55-AN547' - makefile: test/baremetal/platform/m55-an547/platform.mk - nix-shell: cross-arm-embedded - func: true - kat: true - acvp: true - wycheproof: false - alloc: true - bench: true - opt: all - - runner: ubuntu-latest - name: 'M33-AN524' - makefile: test/baremetal/platform/m33-an524/platform.mk - nix-shell: cross-arm-embedded - func: true - kat: true - acvp: true - alloc: true - bench: true - opt: no_opt - runner: ubuntu-latest name: 'AVR ATmega128RFR2 (modified for 32K RAM)' makefile: test/baremetal/platform/avr/platform.mk diff --git a/flake.nix b/flake.nix index 0eb8a0ddc..5a9a96e25 100644 --- a/flake.nix +++ b/flake.nix @@ -158,15 +158,6 @@ } ++ [ util.zephyrPythonEnv ]; }; - # arm-none-eabi-gcc + platform files from pqmx - devShells.cross-arm-embedded = util.mkShell { - packages = builtins.attrValues - { - inherit (util) pqmx; - inherit (config.packages) linters; - inherit (pkgs) gcc-arm-embedded qemu coreutils git; - }; - }; devShells.cross-aarch64-embedded = util.mkShell { packages = builtins.attrValues { diff --git a/nix/pqmx/default.nix b/nix/pqmx/default.nix deleted file mode 100644 index 7794c5c79..000000000 --- a/nix/pqmx/default.nix +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) The mldsa-native project authors -# Copyright (c) The mlkem-native project authors -# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT - -{ stdenvNoCC -, fetchFromGitHub -, writeText -}: - -stdenvNoCC.mkDerivation { - pname = "mlkem-native-pqmx"; - version = "main-2026-02-10"; - - - # Fetch platform files from pqmx - src = fetchFromGitHub { - owner = "slothy-optimizer"; - repo = "pqmx"; - rev = "904451a615dc7926eba07b4f3d1a4137c368bb4a"; - hash = "sha256-BjsToEWGlykKIKRfPom84BkD5RfetUKKwRAw3PecebU="; - }; - - dontBuild = true; - - installPhase = '' - mkdir -p $out/platform/m33-an524/src/platform/ - cp -r envs/m33-an524/src/platform/. $out/platform/m33-an524/src/platform/ - cp integration/*.c $out/platform/m33-an524/src/platform/ - - mkdir -p $out/platform/m55-an547/src/platform/ - cp -r envs/m55-an547/src/platform/. $out/platform/m55-an547/src/platform/ - cp integration/*.c $out/platform/m55-an547/src/platform/ - ''; - - setupHook = writeText "setup-hook.sh" '' - export M33_AN524_PATH="$1/platform/m33-an524/src/platform/" - export M55_AN547_PATH="$1/platform/m55-an547/src/platform/" - ''; - - meta = { - description = "Platform files from pqmx for baremetal targets"; - homepage = "https://github.com/slothy-optimizer/pqmx"; - }; -} diff --git a/nix/util.nix b/nix/util.nix index 443c3dcb8..0a70ab47c 100644 --- a/nix/util.nix +++ b/nix/util.nix @@ -107,7 +107,6 @@ rec { hol_server = pkgs.callPackage ./hol_light/hol_server.nix { inherit hol_light'; }; s2n_bignum = pkgs.callPackage ./s2n_bignum { }; slothy = pkgs.callPackage ./slothy { }; - pqmx = pkgs.callPackage ./pqmx { }; zephyr = pkgs.callPackage ./zephyr { }; zephyrPythonEnv = pkgs.python3.withPackages (ps: with ps; [ pyelftools diff --git a/test/baremetal/platform/m33-an524/exec_wrapper.py b/test/baremetal/platform/m33-an524/exec_wrapper.py deleted file mode 100755 index 28493a539..000000000 --- a/test/baremetal/platform/m33-an524/exec_wrapper.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) The mldsa-native project authors -# Copyright (c) The mlkem-native project authors -# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT - -"""QEMU wrapper for executing Cortex-M33 bare-metal ELF binaries (mps3-an524).""" - -import struct as st -import sys -import subprocess -import tempfile -import os - - -def err(msg, **kwargs): - print(msg, file=sys.stderr, **kwargs) - - -binpath = sys.argv[1] -args = sys.argv[1:] - -# Memory layout: [argc] [offset1] [offset2] ... [string1\0] [string2\0] ... -# M33-AN524 RAM: 0x20000000-0x2001FFFF (128KB) -# Heap ends at: ~0x20000b20 -# Stack: 0x20008000-0x2001FFFF (96KB, grows downward) -# Use address after heap but before stack -# cmdline.c CMDLINE_ADDR must match this value -cmdline_offset = 0x20007000 -arg0_offset = cmdline_offset + 4 + len(args) * 4 -arg_offsets = [sum(map(len, args[:i])) + i + arg0_offset for i in range(len(args))] - -binargs = st.pack( - f"<{1 + len(args)}I" + "".join(f"{len(a) + 1}s" for a in args), - len(args), - *arg_offsets, - *map(lambda x: x.encode("utf-8"), args), -) - -with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=".bin") as fd: - args_file = fd.name - fd.write(binargs) - -try: - qemu_cmd = f"qemu-system-arm -M mps3-an524 -cpu cortex-m33 -nographic -semihosting -kernel {binpath} -device loader,file={args_file},addr=0x{cmdline_offset:x}".split() - result = subprocess.run(qemu_cmd, encoding="utf-8", capture_output=True) -finally: - os.unlink(args_file) -if result.returncode != 0: - err("FAIL!") - err(f"{qemu_cmd} failed with error code {result.returncode}") - err(result.stderr) - exit(1) - -for line in result.stdout.splitlines(): - print(line) diff --git a/test/baremetal/platform/m33-an524/platform.mk b/test/baremetal/platform/m33-an524/platform.mk deleted file mode 100644 index 7c51c5a45..000000000 --- a/test/baremetal/platform/m33-an524/platform.mk +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) The mldsa-native project authors -# Copyright (c) The mlkem-native project authors -# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT - -PLATFORM_PATH:=test/baremetal/platform/m33-an524 - -CROSS_PREFIX=arm-none-eabi- -CC=gcc - -# Use PMU cycle counting by default -CYCLES ?= PMU - -# Reduce iterations for benchmarking and functional tests -CFLAGS += -DMLK_BENCHMARK_NTESTS=10 -DMLK_BENCHMARK_NITERATIONS=10 -DMLK_BENCHMARK_NWARMUP=10 -CFLAGS += -DNTESTS_FUNC=100 - - -CFLAGS += -DMLK_BUMP_ALLOC_SIZE=65536 -CFLAGS += \ - -O3 \ - -Wall -Wextra -Wshadow \ - -Wno-pedantic \ - -Wno-redundant-decls \ - -Wno-missing-prototypes \ - -Wno-conversion \ - -Wno-sign-conversion \ - -fno-common \ - -ffunction-sections \ - -fdata-sections \ - --sysroot=$(SYSROOT) \ - -DDEVICE=an524 \ - -I$(M33_AN524_PATH) \ - -I$(M33_AN524_PATH)/m-profile \ - -DARMCM33 \ - -DSEMIHOSTING \ - -DCMDLINE_BASE_ADDR=0x20007000 - -ARCH_FLAGS += \ - -mcpu=cortex-m33+nodsp \ - -mthumb \ - -mfloat-abi=soft - -CFLAGS += \ - $(ARCH_FLAGS) \ - --specs=nosys.specs - -CFLAGS += $(CFLAGS_EXTRA) - -LDSCRIPT = $(M33_AN524_PATH)/m33-an524.ld - -LDFLAGS += \ - -Wl,--gc-sections \ - -Wl,--no-warn-rwx-segments \ - -L. - -LDFLAGS += \ - --specs=nosys.specs \ - -Wl,--wrap=_open \ - -Wl,--wrap=_close \ - -Wl,--wrap=_read \ - -Wl,--wrap=_write \ - -Wl,--wrap=_fstat \ - -Wl,--wrap=_getpid \ - -Wl,--wrap=_isatty \ - -Wl,--wrap=_kill \ - -Wl,--wrap=_lseek \ - -Wl,--wrap=main \ - -ffreestanding \ - -T$(LDSCRIPT) \ - $(ARCH_FLAGS) - -# Extra sources to be included in test binaries -EXTRA_SOURCES = $(wildcard $(M33_AN524_PATH)/*.c) -# The CMSIS files fail compilation if conversion warnings are enabled -EXTRA_SOURCES_CFLAGS = -Wno-conversion -Wno-sign-conversion - -EXEC_WRAPPER := $(realpath $(PLATFORM_PATH)/exec_wrapper.py) diff --git a/test/baremetal/platform/m55-an547/exec_wrapper.py b/test/baremetal/platform/m55-an547/exec_wrapper.py deleted file mode 100755 index 820674b34..000000000 --- a/test/baremetal/platform/m55-an547/exec_wrapper.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) The mldsa-native project authors -# Copyright (c) The mlkem-native project authors -# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT - -import struct as st -import sys -import subprocess -import tempfile -import os - - -def err(msg, **kwargs): - print(msg, file=sys.stderr, **kwargs) - - -binpath = sys.argv[1] -args = sys.argv[1:] -cmdline_offset = 0x70000 - -arg0_offset = cmdline_offset + 4 + len(args) * 4 - -arg_offsets = [sum(map(len, args[:i])) + i + arg0_offset for i in range(len(args))] - -binargs = st.pack( - f"<{1 + len(args)}I" + "".join(f"{len(a) + 1}s" for a in args), - len(args), - *arg_offsets, - *map(lambda x: x.encode("utf-8"), args), -) - -with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=".bin") as fd: - args_file = fd.name - fd.write(binargs) - -try: - qemu_cmd = f"qemu-system-arm -M mps3-an547 -monitor none -nographic -semihosting -kernel {binpath} -device loader,file={args_file},addr=0x{cmdline_offset:x}".split() - result = subprocess.run(qemu_cmd, encoding="utf-8", capture_output=True) -finally: - os.unlink(args_file) -if result.returncode != 0: - err("FAIL!") - err(f"{qemu_cmd} failed with error code {result.returncode}") - err(result.stderr) - exit(1) - -for line in result.stdout.splitlines(): - print(line) diff --git a/test/baremetal/platform/m55-an547/platform.mk b/test/baremetal/platform/m55-an547/platform.mk deleted file mode 100644 index 85985c907..000000000 --- a/test/baremetal/platform/m55-an547/platform.mk +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) The mldsa-native project authors -# Copyright (c) The mlkem-native project authors -# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT - -PLATFORM_PATH:=test/baremetal/platform/m55-an547 - -CROSS_PREFIX=arm-none-eabi- -CC=gcc - -# Use PMU cycle counting by default -CYCLES ?= PMU - -# Reduce iterations for benchmarking and functional tests -CFLAGS += -DMLK_BENCHMARK_NTESTS=10 -DMLK_BENCHMARK_NITERATIONS=10 -DMLK_BENCHMARK_NWARMUP=10 -CFLAGS += -DNTESTS_FUNC=100 - -# Explicitly include experimental Armv8.1-M + MVE backend -# Remove this once backend is finalized and enabled by default. -CFLAGS += "-DMLK_CONFIG_FIPS202_BACKEND_FILE=\"fips202/native/armv81m/mve.h\"" - -CFLAGS += \ - -O3 \ - -Wall -Wextra -Wshadow \ - -Wno-pedantic \ - -Wno-redundant-decls \ - -Wno-missing-prototypes \ - -Wno-conversion \ - -Wno-sign-conversion \ - -fno-common \ - -ffunction-sections \ - -fdata-sections \ - --sysroot=$(SYSROOT) \ - -DDEVICE=an547 \ - -I$(M55_AN547_PATH) \ - -DARMCM55 \ - -DSEMIHOSTING - -ARCH_FLAGS += \ - -march=armv8.1-m.main+mve.fp \ - -mcpu=cortex-m55 \ - -mthumb \ - -mfloat-abi=hard -mfpu=fpv4-sp-d16 - -CFLAGS += \ - $(ARCH_FLAGS) \ - --specs=nosys.specs - -CFLAGS += $(CFLAGS_EXTRA) - -LDSCRIPT = $(M55_AN547_PATH)/mps3.ld - -LDFLAGS += \ - -Wl,--gc-sections \ - -Wl,--no-warn-rwx-segments \ - -L. - -LDFLAGS += \ - --specs=nosys.specs \ - -Wl,--wrap=_open \ - -Wl,--wrap=_close \ - -Wl,--wrap=_read \ - -Wl,--wrap=_write \ - -Wl,--wrap=_fstat \ - -Wl,--wrap=_getpid \ - -Wl,--wrap=_isatty \ - -Wl,--wrap=_kill \ - -Wl,--wrap=_lseek \ - -Wl,--wrap=main \ - -ffreestanding \ - -T$(LDSCRIPT) \ - $(ARCH_FLAGS) - -# Extra sources to be included in test binaries -EXTRA_SOURCES = $(wildcard $(M55_AN547_PATH)/*.c) -# The CMSIS files fail compilation if conversion warnings are enabled -EXTRA_SOURCES_CFLAGS = -Wno-conversion -Wno-sign-conversion - -EXEC_WRAPPER := $(realpath $(PLATFORM_PATH)/exec_wrapper.py) From 7eb03b2c9313ee47dc97947489c5f22525b507ea Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Fri, 26 Jun 2026 11:37:19 +0100 Subject: [PATCH 3/7] Tests: Add function prototype for `main` Ordinarily, `main` is exempt from -Wmissing-prototypes. However, if it's re-#define'd, it isn't. To allow -Wmissing-prototypes in baremetal platforms wrapping `main`, this commit adds an explicit prototype for `main` in all test applications. Signed-off-by: Hanno Becker --- test/acvp/acvp_mlkem.c | 5 +++++ test/bench/bench_components_mlkem.c | 5 +++++ test/bench/bench_mlkem.c | 5 +++++ test/src/gen_KAT.c | 5 +++++ test/src/test_alloc.c | 5 +++++ test/src/test_mlkem.c | 5 +++++ test/src/test_rng_fail.c | 5 +++++ test/src/test_stack.c | 5 +++++ test/src/test_unit.c | 14 +++++++++++++- test/wycheproof/wycheproof_mlkem.c | 5 +++++ 10 files changed, 58 insertions(+), 1 deletion(-) diff --git a/test/acvp/acvp_mlkem.c b/test/acvp/acvp_mlkem.c index df2622f4e..df52700eb 100644 --- a/test/acvp/acvp_mlkem.c +++ b/test/acvp/acvp_mlkem.c @@ -192,6 +192,11 @@ static MLK_NOINLINE void acvp_mlkem_keyGen_AFT( print_hex("dk", dk, sizeof(dk)); } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(int argc, char *argv[]); +#endif int main(int argc, char *argv[]) { acvp_mode mode; diff --git a/test/bench/bench_components_mlkem.c b/test/bench/bench_components_mlkem.c index 8accc27b4..6247f2ee7 100644 --- a/test/bench/bench_components_mlkem.c +++ b/test/bench/bench_components_mlkem.c @@ -362,6 +362,11 @@ static int bench(void) return 0; } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { enable_cyclecounter(); diff --git a/test/bench/bench_mlkem.c b/test/bench/bench_mlkem.c index ee5c346fb..e2d884a90 100644 --- a/test/bench/bench_mlkem.c +++ b/test/bench/bench_mlkem.c @@ -157,6 +157,11 @@ static int bench(void) return 0; } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { enable_cyclecounter(); diff --git a/test/src/gen_KAT.c b/test/src/gen_KAT.c index 494267e2c..af656b932 100644 --- a/test/src/gen_KAT.c +++ b/test/src/gen_KAT.c @@ -42,6 +42,11 @@ static void print_hex(const char *label, const uint8_t *data, size_t size) printf("\n"); } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { unsigned i; diff --git a/test/src/test_alloc.c b/test/src/test_alloc.c index 3fcdaa4e1..16bc09a08 100644 --- a/test/src/test_alloc.c +++ b/test/src/test_alloc.c @@ -436,6 +436,11 @@ static int test_check_sk_alloc_failure(test_ctx_t *ctx) } \ } while (0) +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { MLK_ALIGN uint8_t bump_buffer[MLK_BUMP_ALLOC_SIZE]; diff --git a/test/src/test_mlkem.c b/test/src/test_mlkem.c index 88564b4df..c9a0cf5fb 100644 --- a/test/src/test_mlkem.c +++ b/test/src/test_mlkem.c @@ -160,6 +160,11 @@ static int test_invalid_ciphertext(void) return 0; } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { unsigned i; diff --git a/test/src/test_rng_fail.c b/test/src/test_rng_fail.c index 407a86b83..b0d0320a8 100644 --- a/test/src/test_rng_fail.c +++ b/test/src/test_rng_fail.c @@ -199,6 +199,11 @@ static int test_check_sk_rng_failure(void) return 0; } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { if (test_keygen_rng_failure() != 0) diff --git a/test/src/test_stack.c b/test/src/test_stack.c index 4472d39b3..9b3149469 100644 --- a/test/src/test_stack.c +++ b/test/src/test_stack.c @@ -43,6 +43,11 @@ static void test_decaps_only(void) (void)ret; /* Ignore return value - we only care about stack measurement */ } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(int argc, char *argv[]); +#endif int main(int argc, char *argv[]) { if (argc != 2) diff --git a/test/src/test_unit.c b/test/src/test_unit.c index 55430a546..e87793e03 100644 --- a/test/src/test_unit.c +++ b/test/src/test_unit.c @@ -30,6 +30,13 @@ #endif #endif /* !NUM_RANDOM_TESTS_REJ_UNIFORM */ +/* Largest absolute constant-coefficient value the invNTT overflow test sweeps. + * Defaults to a full INT16 sweep; an emulated target (e.g. QEMU) can lower it + * to keep the run within its time budget. */ +#ifndef MAX_INTT_CONSTANT_COEFF +#define MAX_INTT_CONSTANT_COEFF INT16_MAX +#endif + /* Declarations for _c functions exposed by MLK_STATIC_TESTABLE= */ void mlk_poly_reduce_c(mlk_poly *r); @@ -540,7 +547,7 @@ static int test_native_intt(void) * * Gradually increase absolute value to find smallest failure first. */ - for (coeff = 0; coeff <= INT16_MAX; coeff++) + for (coeff = 0; coeff <= MAX_INTT_CONSTANT_COEFF; coeff++) { generate_i16_array_constant(test_data, MLKEM_N, (int16_t)coeff); CHECK(test_intt_core(test_data, "intt_constant") == 0); @@ -1216,6 +1223,11 @@ static int test_poly_rej_uniform_consistency(void) +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(void); +#endif int main(void) { /* WARNING: Test-only diff --git a/test/wycheproof/wycheproof_mlkem.c b/test/wycheproof/wycheproof_mlkem.c index b1f762241..4af40dc27 100644 --- a/test/wycheproof/wycheproof_mlkem.c +++ b/test/wycheproof/wycheproof_mlkem.c @@ -100,6 +100,11 @@ static void print_hex(const char *name, const unsigned char *raw, size_t len) printf("\n"); } +/* Ensure main has a prototype even if re-#define'd, avoiding + * -Wmissing-prototypes failure */ +#if defined(main) +int main(int argc, char *argv[]); +#endif int main(int argc, char *argv[]) { if (argc < 2) From 7c542444b1c4ef19ecba5f8c19e343161210121f Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Fri, 26 Jun 2026 11:39:10 +0100 Subject: [PATCH 4/7] Armv81M: Remove redundant backend declarations Signed-off-by: Hanno Becker --- dev/fips202/armv81m/mve.h | 24 +++++------------------- mlkem/mlkem_native.c | 2 -- mlkem/mlkem_native_asm.S | 2 -- mlkem/src/fips202/native/armv81m/mve.h | 24 +++++------------------- 4 files changed, 10 insertions(+), 42 deletions(-) diff --git a/dev/fips202/armv81m/mve.h b/dev/fips202/armv81m/mve.h index a86dc156d..f2a4b5626 100644 --- a/dev/fips202/armv81m/mve.h +++ b/dev/fips202/armv81m/mve.h @@ -17,6 +17,7 @@ #if !defined(__ASSEMBLER__) #include "../api.h" +#include "src/fips202_native_armv81m.h" /* * Native x4 permutation @@ -35,42 +36,27 @@ static MLK_INLINE int mlk_keccak_f1600_x4_native(uint64_t *state) /* * Native x4 XOR bytes (with on-the-fly bit interleaving) */ -#define mlk_keccak_f1600_x4_state_xor_bytes \ - MLK_NAMESPACE(keccak_f1600_x4_state_xor_bytes_asm) -void mlk_keccak_f1600_x4_state_xor_bytes(void *state, const uint8_t *data0, - const uint8_t *data1, - const uint8_t *data2, - const uint8_t *data3, unsigned offset, - unsigned length); - MLK_MUST_CHECK_RETURN_VALUE static MLK_INLINE int mlk_keccakf1600_xor_bytes_x4_native( uint64_t *state, const uint8_t *data0, const uint8_t *data1, const uint8_t *data2, const uint8_t *data3, unsigned offset, unsigned length) { - mlk_keccak_f1600_x4_state_xor_bytes(state, data0, data1, data2, data3, offset, - length); + mlk_keccak_f1600_x4_state_xor_bytes_asm(state, data0, data1, data2, data3, + offset, length); return MLK_NATIVE_FUNC_SUCCESS; } /* * Native x4 extract bytes (with on-the-fly bit de-interleaving) */ -#define mlk_keccak_f1600_x4_state_extract_bytes \ - MLK_NAMESPACE(keccak_f1600_x4_state_extract_bytes_asm) -void mlk_keccak_f1600_x4_state_extract_bytes(void *state, uint8_t *data0, - uint8_t *data1, uint8_t *data2, - uint8_t *data3, unsigned offset, - unsigned length); - MLK_MUST_CHECK_RETURN_VALUE static MLK_INLINE int mlk_keccakf1600_extract_bytes_x4_native( uint64_t *state, uint8_t *data0, uint8_t *data1, uint8_t *data2, uint8_t *data3, unsigned offset, unsigned length) { - mlk_keccak_f1600_x4_state_extract_bytes(state, data0, data1, data2, data3, - offset, length); + mlk_keccak_f1600_x4_state_extract_bytes_asm(state, data0, data1, data2, data3, + offset, length); return MLK_NATIVE_FUNC_SUCCESS; } diff --git a/mlkem/mlkem_native.c b/mlkem/mlkem_native.c index d8b0fd539..e2c49cffa 100644 --- a/mlkem/mlkem_native.c +++ b/mlkem/mlkem_native.c @@ -508,8 +508,6 @@ #undef MLK_USE_FIPS202_X4_NATIVE #undef MLK_USE_FIPS202_X4_XOR_BYTES_NATIVE #undef mlk_keccak_f1600_x4_native_impl -#undef mlk_keccak_f1600_x4_state_extract_bytes -#undef mlk_keccak_f1600_x4_state_xor_bytes /* mlkem/src/fips202/native/armv81m/src/fips202_native_armv81m.h */ #undef MLK_FIPS202_NATIVE_ARMV81M_SRC_FIPS202_NATIVE_ARMV81M_H #undef mlk_keccak_f1600_x4_mve_asm diff --git a/mlkem/mlkem_native_asm.S b/mlkem/mlkem_native_asm.S index c2edd2b16..18adf0be0 100644 --- a/mlkem/mlkem_native_asm.S +++ b/mlkem/mlkem_native_asm.S @@ -532,8 +532,6 @@ #undef MLK_USE_FIPS202_X4_NATIVE #undef MLK_USE_FIPS202_X4_XOR_BYTES_NATIVE #undef mlk_keccak_f1600_x4_native_impl -#undef mlk_keccak_f1600_x4_state_extract_bytes -#undef mlk_keccak_f1600_x4_state_xor_bytes /* mlkem/src/fips202/native/armv81m/src/fips202_native_armv81m.h */ #undef MLK_FIPS202_NATIVE_ARMV81M_SRC_FIPS202_NATIVE_ARMV81M_H #undef mlk_keccak_f1600_x4_mve_asm diff --git a/mlkem/src/fips202/native/armv81m/mve.h b/mlkem/src/fips202/native/armv81m/mve.h index e2f19ec24..3b595e476 100644 --- a/mlkem/src/fips202/native/armv81m/mve.h +++ b/mlkem/src/fips202/native/armv81m/mve.h @@ -17,6 +17,7 @@ #if !defined(__ASSEMBLER__) #include "../api.h" +#include "src/fips202_native_armv81m.h" /* * Native x4 permutation @@ -35,42 +36,27 @@ static MLK_INLINE int mlk_keccak_f1600_x4_native(uint64_t *state) /* * Native x4 XOR bytes (with on-the-fly bit interleaving) */ -#define mlk_keccak_f1600_x4_state_xor_bytes \ - MLK_NAMESPACE(keccak_f1600_x4_state_xor_bytes_asm) -void mlk_keccak_f1600_x4_state_xor_bytes(void *state, const uint8_t *data0, - const uint8_t *data1, - const uint8_t *data2, - const uint8_t *data3, unsigned offset, - unsigned length); - MLK_MUST_CHECK_RETURN_VALUE static MLK_INLINE int mlk_keccakf1600_xor_bytes_x4_native( uint64_t *state, const uint8_t *data0, const uint8_t *data1, const uint8_t *data2, const uint8_t *data3, unsigned offset, unsigned length) { - mlk_keccak_f1600_x4_state_xor_bytes(state, data0, data1, data2, data3, offset, - length); + mlk_keccak_f1600_x4_state_xor_bytes_asm(state, data0, data1, data2, data3, + offset, length); return MLK_NATIVE_FUNC_SUCCESS; } /* * Native x4 extract bytes (with on-the-fly bit de-interleaving) */ -#define mlk_keccak_f1600_x4_state_extract_bytes \ - MLK_NAMESPACE(keccak_f1600_x4_state_extract_bytes_asm) -void mlk_keccak_f1600_x4_state_extract_bytes(void *state, uint8_t *data0, - uint8_t *data1, uint8_t *data2, - uint8_t *data3, unsigned offset, - unsigned length); - MLK_MUST_CHECK_RETURN_VALUE static MLK_INLINE int mlk_keccakf1600_extract_bytes_x4_native( uint64_t *state, uint8_t *data0, uint8_t *data1, uint8_t *data2, uint8_t *data3, unsigned offset, unsigned length) { - mlk_keccak_f1600_x4_state_extract_bytes(state, data0, data1, data2, data3, - offset, length); + mlk_keccak_f1600_x4_state_extract_bytes_asm(state, data0, data1, data2, data3, + offset, length); return MLK_NATIVE_FUNC_SUCCESS; } From 106f2d82155d1a22faf2c2d995cc22d25953f916 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Fri, 26 Jun 2026 12:29:33 +0100 Subject: [PATCH 5/7] Tests: Add CUSTOM_BUILD hook for source-driven platforms The generic test build links each binary from prebuilt objects and the per-scheme library. A platform that builds its binaries differently (e.g. the Zephyr platform, which hands the sources to its own CMake build) had no way to hook into this without per-binary recipes. This commit introduces a CUSTOM_BUILD hook in test/mk/rules.mk: when set, it replaces the default link step, and test/mk/components.mk attaches each binary's sources -- via the new per-binary TEST_SRCS variable -- as its prerequisites rather than prebuilt objects and libraries. TEST_SRCS is the single source of truth for a binary's test sources, so a custom platform needs no per-binary wiring. As part of this, the ADD_SOURCE_UNIT / ADD_SOURCE_ALLOC special cases are folded into ADD_SOURCE (parameterized by a library suffix), and the per-test defines (MLK_STATIC_TESTABLE=, MLK_CONFIG_FILE=...) move from the test object to the test binary. Target-specific CFLAGS propagate to a target's prerequisites, so the object still gets them; attaching to the binary lets a custom build read them off CFLAGS too. No change to the default build: with CUSTOM_BUILD unset, the link step and prerequisites are as before. Signed-off-by: Hanno Becker --- test/mk/components.mk | 123 +++++++++++++++++++++++------------------- test/mk/rules.mk | 19 ++++--- 2 files changed, 82 insertions(+), 60 deletions(-) diff --git a/test/mk/components.mk b/test/mk/components.mk index a12c6750d..2833914d1 100644 --- a/test/mk/components.mk +++ b/test/mk/components.mk @@ -3,15 +3,25 @@ FIPS202_SRCS = $(wildcard mlkem/src/fips202/*.c) ifeq ($(OPT),1) - FIPS202_SRCS += $(wildcard mlkem/src/fips202/native/aarch64/src/*.S) $(wildcard mlkem/src/fips202/native/aarch64/src/*.c) $(wildcard mlkem/src/fips202/native/x86_64/src/*.c) $(wildcard mlkem/src/fips202/native/x86_64/src/*.S) $(wildcard mlkem/src/fips202/native/armv81m/src/*.[csS]) + FIPS202_SRCS += $(wildcard mlkem/src/fips202/native/aarch64/src/*.S) \ + $(wildcard mlkem/src/fips202/native/aarch64/src/*.c) \ + $(wildcard mlkem/src/fips202/native/x86_64/src/*.c) \ + $(wildcard mlkem/src/fips202/native/x86_64/src/*.S) \ + $(wildcard mlkem/src/fips202/native/armv81m/src/*.[csS]) endif SOURCES += $(wildcard mlkem/src/*.c) ifeq ($(OPT),1) - SOURCES += $(wildcard mlkem/src/native/aarch64/src/*.[csS]) $(wildcard mlkem/src/native/x86_64/src/*.[csS]) $(wildcard mlkem/src/native/riscv64/src/*.[csS]) $(wildcard mlkem/src/native/ppc64le/src/*.[csS]) - CFLAGS += -DMLK_CONFIG_USE_NATIVE_BACKEND_ARITH -DMLK_CONFIG_USE_NATIVE_BACKEND_FIPS202 + SOURCES += $(wildcard mlkem/src/native/aarch64/src/*.[csS]) \ + $(wildcard mlkem/src/native/x86_64/src/*.[csS]) \ + $(wildcard mlkem/src/native/riscv64/src/*.[csS]) \ + $(wildcard mlkem/src/native/ppc64le/src/*.[csS]) + CFLAGS += -DMLK_CONFIG_USE_NATIVE_BACKEND_ARITH \ + -DMLK_CONFIG_USE_NATIVE_BACKEND_FIPS202 endif +LIB_SRCS := $(SOURCES) $(FIPS202_SRCS) + BASIC_TESTS = test_mlkem gen_KAT test_stack ACVP_TESTS = acvp_mlkem WYCHEPROOF_TESTS = wycheproof_mlkem @@ -25,29 +35,29 @@ MLKEM512_DIR = $(BUILD_DIR)/mlkem512 MLKEM768_DIR = $(BUILD_DIR)/mlkem768 MLKEM1024_DIR = $(BUILD_DIR)/mlkem1024 -MLKEM512_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR),$(SOURCES) $(FIPS202_SRCS)) +MLKEM512_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR),$(LIB_SRCS)) $(MLKEM512_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=512 -MLKEM768_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR),$(SOURCES) $(FIPS202_SRCS)) +MLKEM768_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR),$(LIB_SRCS)) $(MLKEM768_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=768 -MLKEM1024_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR),$(SOURCES) $(FIPS202_SRCS)) +MLKEM1024_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR),$(LIB_SRCS)) $(MLKEM1024_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=1024 # Unit test object files - same sources but with MLK_STATIC_TESTABLE= UNIT_CFLAGS = -DMLK_STATIC_TESTABLE= -Wno-missing-prototypes -MLKEM512_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR)/unit,$(SOURCES) $(FIPS202_SRCS)) +MLKEM512_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR)/unit,$(LIB_SRCS)) $(MLKEM512_UNIT_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=512 $(UNIT_CFLAGS) -MLKEM768_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR)/unit,$(SOURCES) $(FIPS202_SRCS)) +MLKEM768_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR)/unit,$(LIB_SRCS)) $(MLKEM768_UNIT_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=768 $(UNIT_CFLAGS) -MLKEM1024_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR)/unit,$(SOURCES) $(FIPS202_SRCS)) +MLKEM1024_UNIT_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR)/unit,$(LIB_SRCS)) $(MLKEM1024_UNIT_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=1024 $(UNIT_CFLAGS) # Alloc test object files - same sources but with custom alloc config -MLKEM512_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR)/alloc,$(SOURCES) $(FIPS202_SRCS)) +MLKEM512_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM512_DIR)/alloc,$(LIB_SRCS)) $(MLKEM512_ALLOC_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=512 -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" -MLKEM768_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR)/alloc,$(SOURCES) $(FIPS202_SRCS)) +MLKEM768_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM768_DIR)/alloc,$(LIB_SRCS)) $(MLKEM768_ALLOC_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=768 -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" -MLKEM1024_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR)/alloc,$(SOURCES) $(FIPS202_SRCS)) +MLKEM1024_ALLOC_OBJS = $(call MAKE_OBJS,$(MLKEM1024_DIR)/alloc,$(LIB_SRCS)) $(MLKEM1024_ALLOC_OBJS): CFLAGS += -DMLK_CONFIG_PARAMETER_SET=1024 -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" CFLAGS += -Imlkem @@ -79,50 +89,36 @@ $(MLKEM512_DIR)/bin/test_stack512: CFLAGS += -Imlkem/src -fstack-usage $(MLKEM768_DIR)/bin/test_stack768: CFLAGS += -Imlkem/src -fstack-usage $(MLKEM1024_DIR)/bin/test_stack1024: CFLAGS += -Imlkem/src -fstack-usage -$(MLKEM512_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" -$(MLKEM768_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" -$(MLKEM1024_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" - -$(MLKEM512_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) -$(MLKEM768_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) -$(MLKEM1024_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) - +# Per-test CFLAGS. Attach to the binary so it automatically propagates to all +# dependencies. Applying to .o files would prevent propagation to custom builds. $(MLKEM512_DIR)/bin/test_unit512: CFLAGS += $(UNIT_CFLAGS) $(MLKEM768_DIR)/bin/test_unit768: CFLAGS += $(UNIT_CFLAGS) $(MLKEM1024_DIR)/bin/test_unit1024: CFLAGS += $(UNIT_CFLAGS) +$(MLKEM512_DIR)/bin/test_alloc512: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" +$(MLKEM768_DIR)/bin/test_alloc768: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" +$(MLKEM1024_DIR)/bin/test_alloc1024: CFLAGS += -DMLK_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" + # Unit library object files compiled with MLK_STATIC_TESTABLE= $(MLKEM512_DIR)/unit_%: CFLAGS += -DMLK_STATIC_TESTABLE= -Wno-missing-prototypes $(MLKEM768_DIR)/unit_%: CFLAGS += -DMLK_STATIC_TESTABLE= -Wno-missing-prototypes $(MLKEM1024_DIR)/unit_%: CFLAGS += -DMLK_STATIC_TESTABLE= -Wno-missing-prototypes -$(MLKEM512_DIR)/bin/bench_mlkem512: $(MLKEM512_DIR)/test/hal/hal.c.o -$(MLKEM768_DIR)/bin/bench_mlkem768: $(MLKEM768_DIR)/test/hal/hal.c.o -$(MLKEM1024_DIR)/bin/bench_mlkem1024: $(MLKEM1024_DIR)/test/hal/hal.c.o -$(MLKEM512_DIR)/bin/bench_components_mlkem512: $(MLKEM512_DIR)/test/hal/hal.c.o -$(MLKEM768_DIR)/bin/bench_components_mlkem768: $(MLKEM768_DIR)/test/hal/hal.c.o -$(MLKEM1024_DIR)/bin/bench_components_mlkem1024: $(MLKEM1024_DIR)/test/hal/hal.c.o - $(MLKEM512_DIR)/bin/%: CFLAGS += -DMLK_CONFIG_PARAMETER_SET=512 $(MLKEM768_DIR)/bin/%: CFLAGS += -DMLK_CONFIG_PARAMETER_SET=768 $(MLKEM1024_DIR)/bin/%: CFLAGS += -DMLK_CONFIG_PARAMETER_SET=1024 -# Link tests with respective library (except test_unit which includes sources directly) define ADD_SOURCE -$(BUILD_DIR)/$(1)/bin/$(2)$(subst mlkem,,$(1)): LDLIBS += -L$(BUILD_DIR) -l$(1) -$(BUILD_DIR)/$(1)/bin/$(2)$(subst mlkem,,$(1)): $(BUILD_DIR)/$(1)/test/$(3)/$(2).c.o $(BUILD_DIR)/lib$(1).a -endef - -# Special rule for test_unit - link against unit libraries with exposed internal functions -define ADD_SOURCE_UNIT -$(BUILD_DIR)/$(1)/bin/test_unit$(subst mlkem,,$(1)): LDLIBS += -L$(BUILD_DIR) -l$(1)_unit -$(BUILD_DIR)/$(1)/bin/test_unit$(subst mlkem,,$(1)): $(BUILD_DIR)/$(1)/test/src/test_unit.c.o $(BUILD_DIR)/lib$(1)_unit.a $(call MAKE_OBJS, $(BUILD_DIR)/$(1), $(wildcard test/notrandombytes/*.c)) -endef - -# Special rule for test_alloc - link against alloc libraries with custom alloc config -define ADD_SOURCE_ALLOC -$(BUILD_DIR)/$(1)/bin/test_alloc$(subst mlkem,,$(1)): LDLIBS += -L$(BUILD_DIR) -l$(1)_alloc -$(BUILD_DIR)/$(1)/bin/test_alloc$(subst mlkem,,$(1)): $(BUILD_DIR)/$(1)/test/src/test_alloc.c.o $(BUILD_DIR)/lib$(1)_alloc.a $(call MAKE_OBJS, $(BUILD_DIR)/$(1), $(wildcard test/notrandombytes/*.c)) +# Record each test binary's test sources in the per-binary TEST_SRCS variable: +# its entrypoint test/$(3)/$(2).c plus any extra test sources $(4). +# TEST_SRCS is the source of truth for "which sources does this test need". +$(BUILD_DIR)/$(1)/bin/$(2)$(subst mlkem,,$(1)): TEST_SRCS += test/$(3)/$(2).c $(4) +# In a normal build, the library sources are compiled into a .a and linked in. +# A custom build (CUSTOM_BUILD set, see test/mk/rules.mk) may do it differently. +ifndef CUSTOM_BUILD +$(BUILD_DIR)/$(1)/bin/$(2)$(subst mlkem,,$(1)): LDLIBS += -L$(BUILD_DIR) -l$(1)$(5) +$(BUILD_DIR)/$(1)/bin/$(2)$(subst mlkem,,$(1)): $(BUILD_DIR)/lib$(1)$(5).a +endif endef $(foreach scheme,mlkem512 mlkem768 mlkem1024, \ @@ -133,25 +129,44 @@ $(foreach scheme,mlkem512 mlkem768 mlkem1024, \ $(eval $(call ADD_SOURCE,$(scheme),$(test),wycheproof)) \ ) \ $(foreach test,$(BENCH_TESTS), \ - $(eval $(call ADD_SOURCE,$(scheme),$(test),bench)) \ + $(eval $(call ADD_SOURCE,$(scheme),$(test),bench,test/hal/hal.c)) \ ) \ $(foreach test,$(BASIC_TESTS), \ $(eval $(call ADD_SOURCE,$(scheme),$(test),src)) \ ) \ $(eval $(call ADD_SOURCE,$(scheme),test_rng_fail,src)) \ - $(eval $(call ADD_SOURCE_UNIT,$(scheme))) \ - $(eval $(call ADD_SOURCE_ALLOC,$(scheme))) \ + $(eval $(call ADD_SOURCE,$(scheme),test_unit,src,,_unit)) \ + $(eval $(call ADD_SOURCE,$(scheme),test_alloc,src,,_alloc)) \ ) -# All tests get EXTRA_SOURCES -$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512): $(call MAKE_OBJS, $(MLKEM512_DIR), $(EXTRA_SOURCES)) -$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768): $(call MAKE_OBJS, $(MLKEM768_DIR), $(EXTRA_SOURCES)) -$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024): $(call MAKE_OBJS, $(MLKEM1024_DIR), $(EXTRA_SOURCES)) - -# All tests except rng_fail get notrandombytes (rng_fail provides its own) -$(filter-out %test_rng_fail512,$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512)): $(call MAKE_OBJS, $(MLKEM512_DIR), $(wildcard test/notrandombytes/*.c)) -$(filter-out %test_rng_fail768,$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768)): $(call MAKE_OBJS, $(MLKEM768_DIR), $(wildcard test/notrandombytes/*.c)) -$(filter-out %test_rng_fail1024,$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024)): $(call MAKE_OBJS, $(MLKEM1024_DIR), $(wildcard test/notrandombytes/*.c)) +# All tests get EXTRA_SOURCES; all except rng_fail get notrandombytes (rng_fail +# provides its own). Both are just more test sources, so record them in +# TEST_SRCS alongside the entrypoints set by ADD_SOURCE. +NOTRANDOMBYTES_SRCS := $(wildcard test/notrandombytes/*.c) + +$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512): TEST_SRCS += $(EXTRA_SOURCES) +$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768): TEST_SRCS += $(EXTRA_SOURCES) +$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024): TEST_SRCS += $(EXTRA_SOURCES) + +$(filter-out %test_rng_fail512,$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512)): TEST_SRCS += $(NOTRANDOMBYTES_SRCS) +$(filter-out %test_rng_fail768,$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768)): TEST_SRCS += $(NOTRANDOMBYTES_SRCS) +$(filter-out %test_rng_fail1024,$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024)): TEST_SRCS += $(NOTRANDOMBYTES_SRCS) + +# Turn each binary's TEST_SRCS into its prerequisites (resolved via +# .SECONDEXPANSION, as TEST_SRCS is a per-target variable). In a normal build +# this is redundant: a dependency is implicitly created via .a -> .o -> .c. +# Nontheless, we explicitly register the source files as dependencies to allow +# custom platforms to implement a separate build +.SECONDEXPANSION: +ifndef CUSTOM_BUILD +$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512): $$(call MAKE_OBJS,$(MLKEM512_DIR),$$(TEST_SRCS)) +$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768): $$(call MAKE_OBJS,$(MLKEM768_DIR),$$(TEST_SRCS)) +$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024): $$(call MAKE_OBJS,$(MLKEM1024_DIR),$$(TEST_SRCS)) +else +$(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512): $$(TEST_SRCS) $(LIB_SRCS) +$(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768): $$(TEST_SRCS) $(LIB_SRCS) +$(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024): $$(TEST_SRCS) $(LIB_SRCS) +endif # Apply EXTRA_CFLAGS to EXTRA_SOURCES object files ifneq ($(EXTRA_SOURCES),) diff --git a/test/mk/rules.mk b/test/mk/rules.mk index 51c8c84dc..feff75942 100644 --- a/test/mk/rules.mk +++ b/test/mk/rules.mk @@ -1,20 +1,27 @@ # Copyright (c) The mlkem-native project authors # SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT +# Final step that turns a binary's prerequisites into the bin at $@. By default +# this links the prerequisite objects, but a platform may override it by setting +# CUSTOM_BUILD -- e.g. the Zephyr platform builds a firmware image from the +# binary's sources instead. When CUSTOM_BUILD is set, test/mk/components.mk also +# attaches the binary's sources (rather than prebuilt objects/libraries) as its +# prerequisites. CUSTOM_BUILD is expanded in the recipe context, so it can use +# the target's automatic variables ($@) and target-specific variables (e.g. +# TEST_SRCS, set per bin in test/mk/components.mk). +LINK = $(if $(strip $(CUSTOM_BUILD)),$(CUSTOM_BUILD),echo " LD $@" && $(LD) $(LDFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS)) + $(BUILD_DIR)/mlkem512/bin/%: $(CONFIG) - $(Q)echo " LD $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) - $(Q)$(LD) $(LDFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + $(Q)$(LINK) $(BUILD_DIR)/mlkem768/bin/%: $(CONFIG) - $(Q)echo " LD $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) - $(Q)$(LD) $(LDFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + $(Q)$(LINK) $(BUILD_DIR)/mlkem1024/bin/%: $(CONFIG) - $(Q)echo " LD $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) - $(Q)$(LD) $(LDFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + $(Q)$(LINK) $(BUILD_DIR)/%.a: $(CONFIG) $(Q)echo " AR $@" From c5e6131cc316f222f3eb94e05b75efc9a323956f Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Fri, 26 Jun 2026 12:29:49 +0100 Subject: [PATCH 6/7] Zephyr: Build test binaries source-driven via CUSTOM_BUILD The Zephyr platform previously hand-enumerated every test binary with a per-binary ZEPHYR_BIN recipe. This commit rewrites it on top of the generic CUSTOM_BUILD hook: a single CUSTOM_BUILD recipe drives the CMake build for whichever binary is being made, taking its test sources from the per-binary TEST_SRCS. Adding a test binary now needs no change here. The test binary's full CFLAGS are forwarded to the CMake build and applied to the mlkem amalgamation and the test sources alike, so the firmware is built with the project's own warning/optimization/standard policy and feature defines (including the parameter set) rather than a divergent Zephyr default. Two flags are rewritten for the CMake build dir: -Imlkem is made absolute, and the embedded quotes of -DMLK_CONFIG_FILE="..." are escaped so they survive the recipe shell and CMake's separate_arguments. The QEMU test-tuning defines (smaller iteration counts) are amended onto CFLAGS and forwarded the same way, replacing the separate ZEPHYR_TEST_DEFS / ZEPHYR_LEVEL parameters. The app CMakeLists consumes a ZEPHYR_TEST_SRCS list (entrypoint plus support sources) instead of a single ZEPHYR_TEST_SRC. Also adds a README documenting the platform. Signed-off-by: Hanno Becker --- .github/workflows/zephyr.yml | 7 +- test/zephyr/README.md | 68 ++++++++++++++++++ test/zephyr/app/CMakeLists.txt | 45 +++++++----- test/zephyr/app/Kconfig | 10 +-- test/zephyr/platform.mk | 121 ++++++++++++++++++--------------- 5 files changed, 173 insertions(+), 78 deletions(-) create mode 100644 test/zephyr/README.md diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index 05b707075..c9f8dd0f9 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -42,6 +42,9 @@ jobs: alloc: false rng_fail: false check_namespace: false + # Zephyr's CMake selects the target arch; disable the host-arch + # auto-detection that would otherwise leak into the forwarded CFLAGS. + extra_args: --no-auto # Smoke only: QEMU doesn't model cycle counts (real numbers come from the # FPGA); this just exercises the bench build + run. - name: bench (smoke) @@ -50,5 +53,5 @@ jobs: ZEPHYR_TARGET: ${{ matrix.target.board }} run: | opt=${{ matrix.target.opt == 'all' && 'opt' || 'no_opt' }} - nix develop .#zephyr --command ./scripts/tests bench -c PMU --opt=$opt - nix develop .#zephyr --command ./scripts/tests bench --components -c PMU --opt=$opt + nix develop .#zephyr --command ./scripts/tests bench --no-auto -c PMU --opt=$opt + nix develop .#zephyr --command ./scripts/tests bench --no-auto --components -c PMU --opt=$opt diff --git a/test/zephyr/README.md b/test/zephyr/README.md new file mode 100644 index 000000000..fa9f117e5 --- /dev/null +++ b/test/zephyr/README.md @@ -0,0 +1,68 @@ +[//]: # (SPDX-License-Identifier: CC-BY-4.0) + +# Zephyr test platform + +This is a test platform that builds the mlkem-native test applications as +[Zephyr](https://www.zephyrproject.org/) applications, so they can run on +QEMU-emulated Arm MPS boards. It covers Cortex-M3/M4/M7/M33/M55 through a +single platform, without the need for per-board hardware abstraction layers. + +## Usage + +The platform is selected through the `EXTRA_MAKEFILE` environment variable and +driven by the usual test targets from the top-level `Makefile`. It must run +inside the `zephyr` Nix development shell, which provides the Arm toolchain, +QEMU, and the Zephyr SDK (`ZEPHYR_BASE`). The board is chosen with the +`ZEPHYR_TARGET` environment variable (default `mps3-an547`): + +``` +export EXTRA_MAKEFILE=test/zephyr/platform.mk +export ZEPHYR_TARGET=mps3-an547 +nix develop .#zephyr --command ./scripts/tests func --opt=opt +``` + +Supported targets: + +| `ZEPHYR_TARGET` | Zephyr board | QEMU machine | Core | +| --------------- | --------------------- | ------------ | ---------- | +| `mps2-an385` | `mps2/an385` | `mps2-an385` | Cortex-M3 | +| `mps2-an386` | `mps2/an386` | `mps2-an386` | Cortex-M4 | +| `mps2-an500` | `mps2/an500` | `mps2-an500` | Cortex-M7 | +| `mps2-an521` | `mps2/an521/cpu0` | `mps2-an521` | Cortex-M33 | +| `mps3-an547` | `mps3/corstone300/an547` | `mps3-an547` | Cortex-M55 | + +The Armv8.1-M MVE FIPS202 backend is an `OPT=1` feature and is built for +`mps3-an547` only (the only listed core with MVE). + +## How it works + +The test binaries are built by Zephyr's CMake rather than the generic +`test/mk/rules.mk` link rule. This is wired through two generic hooks in the +test build (see `test/mk/components.mk` and `test/mk/rules.mk`): + +- `CUSTOM_BUILD` -- a recipe that replaces the default link step. When set, the + generic rules also attach each binary's *sources* (rather than prebuilt + objects/libraries) as its prerequisites, via the per-binary `TEST_SRCS` + variable. Setting it is the only thing that makes a platform "source-driven"; + adding a new test binary in `components.mk` needs no change here. + +- `TEST_SRCS` -- the per-binary list of test sources (entrypoint plus support + sources such as `notrandombytes` and, for the benchmarks, the cycle HAL). + `platform.mk`'s `CUSTOM_BUILD` passes it to CMake as `ZEPHYR_TEST_SRCS`. + +The Zephyr application lives in `app/`: + +- `app/CMakeLists.txt` compiles the `mlkem_native.c` amalgamation, the test + sources, and `shim.c`. The test's `main()` is renamed to `mlk_test_main` via + `-Dmain=mlk_test_main` so the shim can own `main()`. +- `app/shim.c` fetches argv via semihosting, calls `mlk_test_main`, and exits + QEMU with the test's return code (again via semihosting). +- `app/Kconfig` force-selects the FPU/MVE features for Corstone-300, whose + Zephyr SoC does not otherwise advertise MVE. + +`platform.mk` forwards the test binary's full `CFLAGS` to the CMake build, so +the firmware is compiled with the project's own warning/optimization/standard +policy and feature defines rather than a divergent Zephyr default. + +`exec_wrapper.py` runs a built ELF under QEMU (`-nographic`, semihosting on), +forwarding the guest's exit code; it is wired in as `EXEC_WRAPPER`. diff --git a/test/zephyr/app/CMakeLists.txt b/test/zephyr/app/CMakeLists.txt index 48ebf01e7..71ff57129 100644 --- a/test/zephyr/app/CMakeLists.txt +++ b/test/zephyr/app/CMakeLists.txt @@ -7,28 +7,41 @@ project(mlkem_native_zephyr) # Parameters supplied via -D from platform.mk: # ZEPHYR_NATIVE_ROOT - mlkem-native checkout the test is built from -# ZEPHYR_LEVEL - ML-KEM parameter set: 512 | 768 | 1024 -# ZEPHYR_TEST_SRC - test entrypoint, relative to ZEPHYR_NATIVE_ROOT -# ZEPHYR_TEST_DEFS - optional extra -D defines (space separated) +# ZEPHYR_TEST_SRCS - test sources (entrypoint + support, e.g. notrandombytes +# and -- for bench -- the HAL), relative to +# ZEPHYR_NATIVE_ROOT, space separated +# ZEPHYR_TEST_CFLAGS - the test binary's CFLAGS (see below); includes the +# parameter set (-DMLK_CONFIG_PARAMETER_SET=...) set(R ${ZEPHYR_NATIVE_ROOT}) +separate_arguments(_test_srcs UNIX_COMMAND "${ZEPHYR_TEST_SRCS}") +list(TRANSFORM _test_srcs PREPEND ${R}/) + target_sources(app PRIVATE ${R}/mlkem/mlkem_native.c - ${R}/${ZEPHYR_TEST_SRC} - ${R}/test/notrandombytes/notrandombytes.c + ${_test_srcs} ${CMAKE_CURRENT_SOURCE_DIR}/shim.c ) target_include_directories(app PRIVATE ${R}/mlkem ${R}/test/notrandombytes + ${R}/test/hal + # So a test that sets MLK_CONFIG_FILE (e.g. test_alloc) finds its config by + # name; the flag is forwarded in ZEPHYR_TEST_CFLAGS (see test/zephyr/platform.mk). + ${R}/test/configs ) -target_compile_definitions(app PRIVATE MLK_CONFIG_PARAMETER_SET=${ZEPHYR_LEVEL}) - -if(ZEPHYR_TEST_DEFS) - separate_arguments(_defs UNIX_COMMAND "${ZEPHYR_TEST_DEFS}") - target_compile_definitions(app PRIVATE ${_defs}) +# The test binary's CFLAGS, forwarded from the build (test/zephyr/platform.mk) +# and applied to the mlkem amalgamation and the test sources alike, so they are +# built with the project's own warning/opt/std policy and feature defines. This +# includes the per-test defines -- e.g. -DMLK_STATIC_TESTABLE= (test_unit) or +# -DMLK_CONFIG_FILE= (test_alloc; angle brackets so the +# header, found via the test/configs include dir, needs no quoting to survive +# the make/shell/CMake layers). +if(ZEPHYR_TEST_CFLAGS) + separate_arguments(_cflags UNIX_COMMAND "${ZEPHYR_TEST_CFLAGS}") + target_compile_options(app PRIVATE ${_cflags}) endif() # Optional native FIPS202 backend (e.g. Armv8.1-M MVE on Cortex-M55). The @@ -41,13 +54,9 @@ if(ZEPHYR_FIPS202_BACKEND) "MLK_CONFIG_FIPS202_BACKEND_FILE=\"${ZEPHYR_FIPS202_BACKEND}\"") endif() -# The bench tests need the cycle-counting HAL. -if(ZEPHYR_TEST_HAL) - target_sources(app PRIVATE ${R}/test/hal/hal.c) - target_include_directories(app PRIVATE ${R}/test/hal) -endif() - # Each test brings its own int main(void); rename it so the Zephyr shim -# (shim.c) owns main() and can stop QEMU with the test's exit code. -set_source_files_properties(${R}/${ZEPHYR_TEST_SRC} +# (shim.c) owns main() and can stop QEMU with the test's exit code. Only the +# test entrypoint in ZEPHYR_TEST_SRCS defines main(); the support sources +# (notrandombytes, hal) don't, so applying the define to all is harmless. +set_source_files_properties(${_test_srcs} PROPERTIES COMPILE_DEFINITIONS "main=mlk_test_main") diff --git a/test/zephyr/app/Kconfig b/test/zephyr/app/Kconfig index baaa0ecbc..444b088ac 100644 --- a/test/zephyr/app/Kconfig +++ b/test/zephyr/app/Kconfig @@ -5,10 +5,10 @@ # QEMU's mps3-an547) has it. Force-select it to build the Armv8.1-M MVE FIPS202 # backend. Enabled per target by platform.mk. config FIPS202_MVE_BACKEND - bool "Build with the Armv8.1-M MVE FIPS202 backend" - select FPU - select ARMV8_M_DSP - select ARMV8_1_M_MVEI - select ARMV8_1_M_MVEF + bool "Build with the Armv8.1-M MVE FIPS202 backend" + select FPU + select ARMV8_M_DSP + select ARMV8_1_M_MVEI + select ARMV8_1_M_MVEF source "Kconfig.zephyr" diff --git a/test/zephyr/platform.mk b/test/zephyr/platform.mk index b78c9184f..3b84a9608 100644 --- a/test/zephyr/platform.mk +++ b/test/zephyr/platform.mk @@ -1,10 +1,19 @@ # Copyright (c) The mlkem-native project authors # SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT +# Zephyr test platform for QEMU-emulated Arm MPS boards. +# +# Each test binary is built as a Zephyr application by CMake (which owns the arm +# toolchain, the per-board arch flags and libc via the .#zephyr dev shell). The +# generic make rules don't link these binaries; instead CUSTOM_BUILD (see +# test/mk/rules.mk) drives the CMake build. The list of test sources for each +# binary comes from TEST_SRCS, a target-specific variable set by the generic +# test/mk/components.mk -- adding a new test binary there needs no change here. + PLATFORM_PATH := test/zephyr # BUILD_DIR is set by the top-level Makefile after this file is included; -# define it here too so the explicit bin rules below expand to the right path. +# define it here too so CUSTOM_BUILD below expands to the right path. BUILD_DIR ?= test/build # ZEPHYR_TARGET= selects a target. Each key maps to a Zephyr board and the @@ -12,15 +21,15 @@ BUILD_DIR ?= test/build ZEPHYR_TARGET ?= mps3-an547 ZEPHYR_BOARD_mps2-an385 := mps2/an385 -ZEPHYR_QEMU_mps2-an385 := mps2-an385 # Cortex-M3 +ZEPHYR_QEMU_mps2-an385 := mps2-an385 # Cortex-M3 ZEPHYR_BOARD_mps2-an386 := mps2/an386 -ZEPHYR_QEMU_mps2-an386 := mps2-an386 # Cortex-M4 +ZEPHYR_QEMU_mps2-an386 := mps2-an386 # Cortex-M4 ZEPHYR_BOARD_mps2-an500 := mps2/an500 -ZEPHYR_QEMU_mps2-an500 := mps2-an500 # Cortex-M7 +ZEPHYR_QEMU_mps2-an500 := mps2-an500 # Cortex-M7 ZEPHYR_BOARD_mps2-an521 := mps2/an521/cpu0 -ZEPHYR_QEMU_mps2-an521 := mps2-an521 # Cortex-M33 +ZEPHYR_QEMU_mps2-an521 := mps2-an521 # Cortex-M33 ZEPHYR_BOARD_mps3-an547 := mps3/corstone300/an547 -ZEPHYR_QEMU_mps3-an547 := mps3-an547 # Cortex-M55 +ZEPHYR_QEMU_mps3-an547 := mps3-an547 # Cortex-M55 ZEPHYR_FIPS202_BACKEND_mps3-an547 := fips202/native/armv81m/mve.h @@ -33,11 +42,12 @@ ifeq ($(ZEPHYR_BOARD),) $(error Unknown ZEPHYR_TARGET '$(ZEPHYR_TARGET)'. Supported: $(ZEPHYR_TARGETS)) endif -# The test binaries are built by Zephyr's CMake (which uses its own arm -# toolchain via the .#zephyr dev shell), not the generic Make rules. The -# top-level targets still attach the usual object/library prerequisites to the -# bin paths; with OPT=0 those are portable host objects that compile cleanly -# and are simply discarded (the Zephyr ELF is copied over them). +# The test binaries are built by Zephyr's CMake, not the generic Make rules, so +# each binary depends on its sources rather than on objects/libraries built (and +# then discarded) here. Setting CUSTOM_BUILD (below) switches test/mk/components.mk +# to attach source prerequisites, and overrides the link step to consume them via +# TEST_SRCS. It must be defined before components.mk is included (it is: the +# top-level Makefile includes this platform file first). OPT ?= 0 # Native backends are an OPT=1 feature (an547 builds the Armv8.1-M MVE backend). @@ -46,51 +56,56 @@ ZEPHYR_FIPS202_BACKEND := $(if $(filter 1,$(OPT)),$(strip $(ZEPHYR_FIPS202_BACKE ZEPHYR_APP := $(PLATFORM_PATH)/app ZEPHYR_BUILD_DIR := $(BUILD_DIR)/zephyr/$(ZEPHYR_TARGET) -# Build a test as a Zephyr application and drop the resulting ELF at the path -# the top-level Makefile expects. An explicit rule for the exact bin path wins -# over the generic link pattern rule in test/mk/rules.mk. -# $(1) level $(2) bin name $(3) test source (repo-relative) $(4) extra -D -define ZEPHYR_BIN -$(BUILD_DIR)/mlkem$(1)/bin/$(2): - $$(Q)echo " ZEPHYR $(ZEPHYR_TARGET) ML-KEM-$(1): $(3)" - $$(Q)cmake -GNinja -S $(ZEPHYR_APP) -B $(ZEPHYR_BUILD_DIR)/$(2) \ +# Build the binary at $@ as a Zephyr application and drop the resulting ELF +# there. The bin's parameter set is the trailing digits of its name; its test +# sources come from the binary's TEST_SRCS (set by test/mk/components.mk); the +# mlkem sources are also prerequisites but are built by the app's own +# mlkem_native.c amalgamation, so they are not passed here. This is expanded in +# the recipe context, so $@ and $(TEST_SRCS) resolve to the specific bin being +# built -- a per-binary build dir keyed on $(notdir $@) lets binaries build in +# parallel. +# $@ the bin path being built +# $(TEST_SRCS) test entrypoint + notrandombytes (+ hal) sources for this bin +ZEPHYR_OUT = $(ZEPHYR_BUILD_DIR)/$(notdir $@) + +# Test-tuning defines. The test sources default these (via #ifndef) to values +# sized for native hardware; QEMU is far slower, so shrink the iteration counts. +# Amended onto CFLAGS so they forward with everything else below. +CFLAGS += -DNTESTS_FUNC=3 -DNTESTS_KAT=100 \ + -DMLK_BENCHMARK_NTESTS=10 -DMLK_BENCHMARK_NITERATIONS=10 -DMLK_BENCHMARK_NWARMUP=10 \ + -DNUM_RANDOM_TESTS=100 -DNUM_RANDOM_TESTS_REJ_UNIFORM=100 -DMAX_INTT_CONSTANT_COEFF=512 + +# Forward the test binary's CFLAGS to the Zephyr CMake build, which applies them +# (via target_compile_options) to the mlkem amalgamation and the test sources -- +# the same flags the normal build uses, so the firmware is built with the +# project's own warning/opt/std policy and feature defines (including the +# parameter set) rather than a divergent Zephyr default. This uses '=' (not ':=') +# and is evaluated in the CUSTOM_BUILD recipe context, so $(CFLAGS) includes the +# binary's target-specific additions (e.g. -DMLK_STATIC_TESTABLE= for test_unit, +# -DMLK_CONFIG_FILE="..." for test_alloc). Two rewrites: +# - -Imlkem -> absolute, as the CMake build runs from its own build dir, not +# the repo root (and the alloc config path is relative to -Imlkem); +# - the embedded quotes of -DMLK_CONFIG_FILE="..." would be eaten twice on the +# way to the compiler (once by the recipe shell, once by CMake's +# separate_arguments), leaving a bare token #include rejects; doubling the +# backslash escaping (\" -> \\") makes one level survive each layer. +# The platform is run with AUTO=0 (see .github/workflows/zephyr.yml), so the +# host-arch flags that AUTO=1 would add to CFLAGS -- which the Zephyr toolchain +# must not see, as it selects the target arch itself -- are not present. +ZEPHYR_TEST_CFLAGS = $(subst \",\\\",$(patsubst -Imlkem,-I$(abspath mlkem),$(CFLAGS))) + +CUSTOM_BUILD = \ + echo " ZEPHYR $(ZEPHYR_TARGET): $(notdir $@)" && \ + cmake -GNinja -S $(ZEPHYR_APP) -B $(ZEPHYR_OUT) \ -DBOARD=$(ZEPHYR_BOARD) \ -DZEPHYR_NATIVE_ROOT=$(CURDIR) \ - -DZEPHYR_LEVEL=$(1) \ - -DZEPHYR_TEST_SRC=$(3) \ - -DZEPHYR_TEST_DEFS="NTESTS_FUNC=3 NTESTS_KAT=100 MLK_BENCHMARK_NTESTS=10 MLK_BENCHMARK_NITERATIONS=10 MLK_BENCHMARK_NWARMUP=10" \ + -DZEPHYR_TEST_SRCS="$(strip $(TEST_SRCS))" \ + -DZEPHYR_TEST_CFLAGS="$(ZEPHYR_TEST_CFLAGS)" \ -DZEPHYR_FIPS202_BACKEND=$(ZEPHYR_FIPS202_BACKEND) \ $(if $(ZEPHYR_FIPS202_BACKEND),-DCONFIG_FIPS202_MVE_BACKEND=y) \ - $(4) \ - -DUSER_CACHE_DIR=$(abspath $(ZEPHYR_BUILD_DIR)/$(2)/.cache) \ - >/dev/null - $$(Q)cmake --build $(ZEPHYR_BUILD_DIR)/$(2) >/dev/null - $$(Q)[ -d $$(@D) ] || mkdir -p $$(@D) - $$(Q)cp $(ZEPHYR_BUILD_DIR)/$(2)/zephyr/zephyr.elf $$@ -endef - -$(eval $(call ZEPHYR_BIN,512,test_mlkem512,test/src/test_mlkem.c)) -$(eval $(call ZEPHYR_BIN,768,test_mlkem768,test/src/test_mlkem.c)) -$(eval $(call ZEPHYR_BIN,1024,test_mlkem1024,test/src/test_mlkem.c)) - -$(eval $(call ZEPHYR_BIN,512,gen_KAT512,test/src/gen_KAT.c)) -$(eval $(call ZEPHYR_BIN,768,gen_KAT768,test/src/gen_KAT.c)) -$(eval $(call ZEPHYR_BIN,1024,gen_KAT1024,test/src/gen_KAT.c)) - -$(eval $(call ZEPHYR_BIN,512,acvp_mlkem512,test/acvp/acvp_mlkem.c)) -$(eval $(call ZEPHYR_BIN,768,acvp_mlkem768,test/acvp/acvp_mlkem.c)) -$(eval $(call ZEPHYR_BIN,1024,acvp_mlkem1024,test/acvp/acvp_mlkem.c)) - -$(eval $(call ZEPHYR_BIN,512,wycheproof_mlkem512,test/wycheproof/wycheproof_mlkem.c)) -$(eval $(call ZEPHYR_BIN,768,wycheproof_mlkem768,test/wycheproof/wycheproof_mlkem.c)) -$(eval $(call ZEPHYR_BIN,1024,wycheproof_mlkem1024,test/wycheproof/wycheproof_mlkem.c)) - -$(eval $(call ZEPHYR_BIN,512,bench_mlkem512,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) -$(eval $(call ZEPHYR_BIN,768,bench_mlkem768,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) -$(eval $(call ZEPHYR_BIN,1024,bench_mlkem1024,test/bench/bench_mlkem.c,-DZEPHYR_TEST_HAL=ON)) - -$(eval $(call ZEPHYR_BIN,512,bench_components_mlkem512,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) -$(eval $(call ZEPHYR_BIN,768,bench_components_mlkem768,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) -$(eval $(call ZEPHYR_BIN,1024,bench_components_mlkem1024,test/bench/bench_components_mlkem.c,-DZEPHYR_TEST_HAL=ON)) + -DUSER_CACHE_DIR=$(abspath $(ZEPHYR_OUT)/.cache) \ + >/dev/null && \ + cmake --build $(ZEPHYR_OUT) >/dev/null && \ + cp $(ZEPHYR_OUT)/zephyr/zephyr.elf $@ EXEC_WRAPPER := $(abspath $(PLATFORM_PATH)/exec_wrapper.py) From c14708013e27fb53169ac8800bbc524394fe5bee Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Fri, 26 Jun 2026 15:53:24 +0100 Subject: [PATCH 7/7] Zephyr: Drop -Werror for the benchmark HAL The Zephyr platform forwards the test binary's full CFLAGS -- including the project's strict, -Werror'd warning set -- to the CMake build. Our own sources are clean under it, but the benchmark cycle HAL (test/hal/hal.c) is the one test source that includes Zephyr SDK headers (), and those are not -Werror-clean (-Wconversion, -Wsign-conversion, -pedantic). This broke the benchmark build on all boards. Drop -Werror for hal.c only, via a per-source COMPILE_OPTIONS property, so the SDK-header warnings no longer fail the build while our own sources stay strict. Signed-off-by: Hanno Becker --- test/zephyr/app/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/zephyr/app/CMakeLists.txt b/test/zephyr/app/CMakeLists.txt index 71ff57129..a7745f1b3 100644 --- a/test/zephyr/app/CMakeLists.txt +++ b/test/zephyr/app/CMakeLists.txt @@ -44,6 +44,14 @@ if(ZEPHYR_TEST_CFLAGS) target_compile_options(app PRIVATE ${_cflags}) endif() +# The benchmark HAL is the only test source that pulls in Zephyr SDK headers +# ( for the cycle counter). Those headers are not clean under +# the project's strict, -Werror'd warning set forwarded above (e.g. +# -Wconversion, -Wsign-conversion, -pedantic), so drop -Werror just for this +# file -- our own sources stay strict. +set_source_files_properties(${R}/test/hal/hal.c + PROPERTIES COMPILE_OPTIONS "-Wno-error") + # Optional native FIPS202 backend (e.g. Armv8.1-M MVE on Cortex-M55). The # monolithic mlkem_native.c already includes the backend C sources; its # assembly counterpart comes from mlkem_native_asm.S.