From 7ca8a45832b6a5dedd7410ddc2bf581c83030b08 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Sun, 3 Aug 2025 04:41:07 +0100 Subject: [PATCH 1/2] autogen: Generalize exceptions for macro patterns Rather than hardcoding certain macro patterns `MLK_FOO_BAR_XXX` as acceptable, detect them and check that there exists at least one macro following the provided pattern (i.e., for `MLK_FOO_BAR_XXX`, exist that there is at least one macro starting with `MLK_FOO_BAR_`). Signed-off-by: Hanno Becker --- scripts/autogen | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/scripts/autogen b/scripts/autogen index 819f00def..872abb098 100755 --- a/scripts/autogen +++ b/scripts/autogen @@ -1572,13 +1572,28 @@ def get_config_options(): "MLK_BREAK_PCT", # Use in PCT breakage test] "MLK_CONFIG_NO_ASM_VALUE_BARRIER", # TODO: Add to config? "MLK_CHECK_APIS", - "MLK_CONFIG_API_XXX", - "MLK_USE_NATIVE_XXX", ] return configs +def is_macro_pattern(m): + if not m.endswith("_XXX"): + return None + return m[:-4] + + +def is_valid_macro_pattern(m, macros): + pattern = is_macro_pattern(m) + if pattern is None: + return False + # Check that there is at least one macro following the pattern + for mp in macros: + if mp.startswith(pattern): + return True + return False + + def check_macro_typos_in_file(filename, macro_check): """Checks for typos in MLK_XXX and MLKEM_XXX identifiers.""" status_update("check-macros", filename) @@ -1591,9 +1606,14 @@ def check_macro_typos_in_file(filename, macro_check): rest = m.group(2) if macro_check(txt, rest, filename) is False: line_no = content[: m.start()].count("\n") + 1 - raise Exception( - f"Likely typo {txt} in {filename}:{line_no}? Not a defined macro." - ) + if is_macro_pattern(txt): + raise Exception( + f"Likely typo {txt} in {filename}:{line_no}? No macro following the pattern exists" + ) + else: + raise Exception( + f"Likely typo {txt} in {filename}:{line_no}? Not a defined macro nor valid pattern" + ) def get_syscaps(): @@ -1610,7 +1630,7 @@ def check_macro_typos(): macros.update(get_config_options()) def macro_check(m, rest, filename): - if m in macros: + if m in macros or is_valid_macro_pattern(m, macros): return True is_autogen = filename == "scripts/autogen" @@ -1638,12 +1658,7 @@ def check_macro_typos(): if filename.endswith(".ml"): return True - # 4. Exclude regexp patterns in `autogen` - if is_autogen: - if rest.startswith("\\") or m in ["MLK_XXX", "MLK_SOURCE_XXX"]: - return True - - # 5. AWS-LC importer patch + # 4. AWS-LC importer patch if is_autogen or filename == "integration/awslc/awslc.patch": return True From d56d9c328233e88c14787b1116418a7c648018e0 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 30 Jul 2025 16:16:27 +0100 Subject: [PATCH 2/2] AArch64: Add ABI checker This commit adds an ABI checker for all AArch64 assembly in mlkem-native. The purpose of an ABI checker is to ensure that assembly files obey the required calling conventions. For AArch64 specifically, it is required by the AAPCS that x19-x30 and d8-d15 are unmodified. It is important to test this since failure to obey the AAPCS can lead to rare but fatal bugs depending on the exact compiler and compilation settings. On a high level, the ABI checker works as follows: 1. An assembly callstub ```c void asm_call_stub(struct register_state *input, struct register_state *output, void (*function_ptr)(void)) ``` is implemented which calls the function specified by `function_ptr` on the input register state `input`, and returns the output register state in `output`. Importantly, it does _not_ assume that `function_ptr` obeys the AAPCS, so must be implemented in assembly. This is done for AArch64 only so far. 2. Randomized ABI checker Based on `asm_call_stub`, we implement an ABI checker which calls the target function with random register inputs and checks that the output register state has retained the callee saved registers. 3. Pointer inputs Most functions read/write memory specified via parameters. Calling those functions with random input would lead to segmentation faults. The ABI checker therefore does not randomize those arguments, but instead sets up valid pointers of the right size for them. For now, we always align the input pointers via MLK_ALIGN. A future extension should take alignment constraints into account and vary the alignment of the inputs within those constraints. The ABI checker gets the information about which inputs are pointers from the recently introduced YAML specification of AArch64 assembly. Some implementation details: - `autogen` is extended to generate one `test/abicheck/check_FUNCNAME.c` file per function under test. All tests are declared and enumerated in the autogenerated `test/abicheck/checks_all.h`. - The handwritten `test/abicheck/abicheck.c` invokes all per-function ABI checks. - We integrate the abicheck build by extending `test/mk/components.mk` with specific build rules. This allows us to partly piggy back on existing Makefile infrastructure, such as setting execution wrappers or cross prefixes. On the other hand, a lot of custom hackery is currently needed to get past the preprocessor directives guarding individual assembly files. This should be improved in the future. - `scripts/tests` is extended to allow for `tests abicheck`, and `tests all` supports `--abicheck` and `--no-abicheck`. By default, the abicheck _is_ run with `tests all`. Signed-off-by: Hanno Becker --- .github/actions/functest/action.yml | 8 +- Makefile | 15 +- scripts/autogen | 298 ++++++++++++++++++ scripts/format | 2 +- scripts/tests | 44 +++ test/abicheck/aarch64_callstub.S | 201 ++++++++++++ test/abicheck/abicheck.c | 50 +++ test/abicheck/abicheckutil.c | 68 ++++ test/abicheck/abicheckutil.h | 45 +++ test/abicheck/check_intt_asm.c | 79 +++++ .../check_keccak_f1600_x1_scalar_asm.c | 76 +++++ .../abicheck/check_keccak_f1600_x1_v84a_asm.c | 85 +++++ .../abicheck/check_keccak_f1600_x2_v84a_asm.c | 86 +++++ ...ck_keccak_f1600_x4_v8a_scalar_hybrid_asm.c | 78 +++++ ...ccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.c | 88 ++++++ test/abicheck/check_ntt_asm.c | 79 +++++ .../check_poly_mulcache_compute_asm.c | 87 +++++ test/abicheck/check_poly_reduce_asm.c | 72 +++++ test/abicheck/check_poly_tobytes_asm.c | 76 +++++ test/abicheck/check_poly_tomont_asm.c | 72 +++++ ...vec_basemul_acc_montgomery_cached_asm_k2.c | 88 ++++++ ...vec_basemul_acc_montgomery_cached_asm_k3.c | 88 ++++++ ...vec_basemul_acc_montgomery_cached_asm_k4.c | 88 ++++++ test/abicheck/check_rej_uniform_asm.c | 82 +++++ test/abicheck/checks_all.h | 90 ++++++ test/mk/components.mk | 54 ++++ test/mk/config.mk | 37 +++ test/mk/rules.mk | 15 + 28 files changed, 2146 insertions(+), 5 deletions(-) create mode 100644 test/abicheck/aarch64_callstub.S create mode 100644 test/abicheck/abicheck.c create mode 100644 test/abicheck/abicheckutil.c create mode 100644 test/abicheck/abicheckutil.h create mode 100644 test/abicheck/check_intt_asm.c create mode 100644 test/abicheck/check_keccak_f1600_x1_scalar_asm.c create mode 100644 test/abicheck/check_keccak_f1600_x1_v84a_asm.c create mode 100644 test/abicheck/check_keccak_f1600_x2_v84a_asm.c create mode 100644 test/abicheck/check_keccak_f1600_x4_v8a_scalar_hybrid_asm.c create mode 100644 test/abicheck/check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.c create mode 100644 test/abicheck/check_ntt_asm.c create mode 100644 test/abicheck/check_poly_mulcache_compute_asm.c create mode 100644 test/abicheck/check_poly_reduce_asm.c create mode 100644 test/abicheck/check_poly_tobytes_asm.c create mode 100644 test/abicheck/check_poly_tomont_asm.c create mode 100644 test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k2.c create mode 100644 test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k3.c create mode 100644 test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k4.c create mode 100644 test/abicheck/check_rej_uniform_asm.c create mode 100644 test/abicheck/checks_all.h diff --git a/.github/actions/functest/action.yml b/.github/actions/functest/action.yml index cf3944238..a5727bdb3 100644 --- a/.github/actions/functest/action.yml +++ b/.github/actions/functest/action.yml @@ -49,6 +49,9 @@ inputs: stack: description: Determine whether to run stack analysis or not default: "false" + abicheck: + description: Determine whether to run ABI compliance tests or not + default: "true" extra_args: description: Additional arguments to pass to the tests script default: "" @@ -65,6 +68,7 @@ runs: echo ACVP="${{ inputs.acvp == 'true' && 'acvp' || 'no-acvp' }}" >> $GITHUB_ENV echo EXAMPLES="${{ inputs.examples == 'true' && 'examples' || 'no-examples' }}" >> $GITHUB_ENV echo STACK="${{ inputs.stack == 'true' && 'stack' || 'no-stack' }}" >> $GITHUB_ENV + echo ABICHECK="${{ inputs.abicheck == 'true' && 'abicheck' || 'no-abicheck' }}" >> $GITHUB_ENV - name: Setup nix uses: ./.github/actions/setup-shell with: @@ -95,11 +99,11 @@ runs: - $(python3 --version) - $(${{ inputs.cross_prefix }}${CC} --version | grep -m1 "") EOF - - name: ${{ env.MODE }} ${{ inputs.opt }} tests (${{ env.FUNC }}, ${{ env.KAT }}, ${{ env.EXAMPLES }}, ${{ env.STACK }}) + - name: ${{ env.MODE }} ${{ inputs.opt }} tests (${{ env.FUNC }}, ${{ env.KAT }}, ${{ env.EXAMPLES }}, ${{ env.STACK }}, ${{ env.ABICHECK }}) shell: ${{ env.SHELL }} run: | make clean - ./scripts/tests all ${{ inputs.check_namespace == 'true' && '--check-namespace' || ''}} --exec-wrapper="${{ inputs.exec_wrapper }}" --cross-prefix="${{ inputs.cross_prefix }}" --cflags="${{ inputs.cflags }}" --opt=${{ inputs.opt }} --${{ env.FUNC }} --${{ env.KAT }} --${{ env.ACVP }} --${{ env.EXAMPLES }} --${{ env.STACK }} -v ${{ inputs.extra_args }} + ./scripts/tests all ${{ inputs.check_namespace == 'true' && '--check-namespace' || ''}} --exec-wrapper="${{ inputs.exec_wrapper }}" --cross-prefix="${{ inputs.cross_prefix }}" --cflags="${{ inputs.cflags }}" --opt=${{ inputs.opt }} --${{ env.FUNC }} --${{ env.KAT }} --${{ env.ACVP }} --${{ env.EXAMPLES }} --${{ env.STACK }} --${{ env.ABICHECK }} -v ${{ inputs.extra_args }} - name: Post ${{ env.MODE }} Tests shell: ${{ env.SHELL }} if: success() || failure() diff --git a/Makefile b/Makefile index 058f0a7f4..33a0a72f7 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ clean quickcheck check-defined-CYCLES \ size_512 size_768 size_1024 size \ run_size_512 run_size_768 run_size_1024 run_size \ - host_info + host_info abicheck run_abicheck SHELL := /bin/bash .DEFAULT_GOAL := build @@ -37,7 +37,7 @@ quickcheck: test build: func kat acvp $(Q)echo " Everything builds fine!" -test: run_kat run_func run_acvp +test: run_kat run_func run_acvp run_abicheck $(Q)echo " Everything checks fine!" # Detect available SHA256 command @@ -182,6 +182,17 @@ run_size_1024: size_1024 $(Q)echo "size $(BUILD_DIR)/libmlkem1024.a" $(Q)$(SIZE) $(BUILD_DIR)/libmlkem1024.a | (read header; echo "$$header"; awk '$$5 != 0' | sort -k5 -n -r) +ifeq ($(OPT),1) +# ABI checker for assembly functions +abicheck: $(ABICHECK_DIR)/bin/abicheck + +run_abicheck: abicheck + $(W) $(ABICHECK_DIR)/bin/abicheck +else +# Skip ABI checker for builds without assembly +abicheck: +run_abicheck: +endif run_size: \ run_size_512 \ diff --git a/scripts/autogen b/scripts/autogen index 872abb098..c225c70db 100755 --- a/scripts/autogen +++ b/scripts/autogen @@ -2685,6 +2685,301 @@ def gen_citations(dry_run=False): gen_bib_file(bibliography, dry_run=False) +def gen_abicheck(dry_run): + """Generate ABI checker tests for all aarch64 assembly functions.""" + + # Find all aarch64 assembly files in mlkem/ + aarch64_asm_files = [] + + # Find all .S files in mlkem/src/*/native/aarch64/src/ + base_path = pathlib.Path(".") + patterns = [ + "mlkem/src/fips202/native/aarch64/src/*.S", + "mlkem/src/native/aarch64/src/*.S", + ] + + for pattern in patterns: + aarch64_asm_files.extend(base_path.glob(pattern)) + + if not aarch64_asm_files: + print("No aarch64 assembly files found") + return + + # Sort for consistent ordering and convert to strings + aarch64_asm_files = sorted(str(f) for f in aarch64_asm_files) + + print(f"Found {len(aarch64_asm_files)} aarch64 assembly files") + + generated_functions = [] + + # Generate ABI check for each assembly file + for assembly_file in aarch64_asm_files: + try: + yaml_data = extract_yaml_from_assembly(assembly_file) + arch_flags = extract_arch_flags_from_assembly(assembly_file) + + # Extract function name from the YAML Name field + function_name = yaml_data.get("Name") + c_function_name = "mlk_" + function_name + + def gen_c_test(): + yield from gen_header() + yield from [ + "", + '#include "../../mlkem/src/sys.h"', + "", + "#if defined(MLK_SYS_AARCH64)", + "", + ] + + # Add architecture-specific guards if needed + for flag in arch_flags: + yield f"#if defined({flag})" + yield "" + + yield from [ + "#include ", + "#include ", + "", + ] + + yield from [ + '#include "../notrandombytes/notrandombytes.h"', + '#include "abicheckutil.h"', + '#include "checks_all.h"', + "", + "typedef struct register_state register_state;", + "", + "#define NUM_TESTS 3", + "", + yaml_data.get("Signature") + ";", + "", + f"int check_{function_name}(void)", + "{", + ] + + # C90 compliance: All declarations at the top + yield " int test_iter;" + yield " register_state input_state, output_state;" + yield " int violations;" + + # Generate buffer declarations with MLK_ALIGN + abi_data = yaml_data.get("ABI", {}) + sorted_registers = sorted(abi_data.items()) + + buffer_info = [] + for reg_name, reg_info in sorted_registers: + reg_type = reg_info.get("type", "scalar") + if reg_type == "buffer": + size_bytes = resolve_buffer_size(reg_info, abi_data) + buffer_name = f"buf_{reg_name}" + description = reg_info.get("description", "no description") + + # Use uint8_t arrays for byte-level access with MLK_ALIGN + yield f" MLK_ALIGN uint8_t {buffer_name}[{size_bytes}]; /* {description} */" + buffer_info.append((reg_name, buffer_name, size_bytes)) + + yield "" + yield " for (test_iter = 0; test_iter < NUM_TESTS; test_iter++)" + yield " {" + yield " /* Initialize random register state */" + yield " init_random_register_state(&input_state);" + yield "" + + # Generate buffer initialization + for reg_name, buffer_name, size_bytes in buffer_info: + yield f" /* Initialize buffer for {reg_name} */" + yield f" randombytes({buffer_name}, {size_bytes});" + + yield "" + yield " /* Set up register state for function arguments */" + + # Generate register setups + for reg_name, reg_info in sorted_registers: + reg_type = reg_info.get("type", "scalar") + reg_num = int(reg_name[1:]) + + if reg_type == "buffer": + buffer_name = f"buf_{reg_name}" + yield f" input_state.gpr[{reg_num}] = (uint64_t){buffer_name};" + elif reg_type == "scalar": + test_with = reg_info.get("test_with") + if test_with: + yield f" input_state.gpr[{reg_num}] = {test_with};" + + yield from [ + "", + " /* Call function through ABI test stub */", + f" asm_call_stub(&input_state, &output_state, (void (*)(void)){c_function_name});", + "", + " /* Check ABI compliance */", + " violations = check_aarch64_aapcs_compliance(&input_state, &output_state);", + " if (violations > 0) {", + f' fprintf(stderr, "ABI test FAILED for {function_name} (iteration %d): %d violations\\n",', + " test_iter + 1, violations);", + " return 1;", + " }", + " }", + "", + " return 0;", + "}", + "", + ] + + # Close architecture-specific guards if needed + for flag in reversed(arch_flags): + yield f"#else /* !{flag} */" + yield "" + yield '#include "../../mlkem/src/common.h"' + yield f"MLK_EMPTY_CU(check_{function_name})" + yield "" + yield f"#endif /* {flag} */" + yield "" + + yield from [ + "#else /* !MLK_SYS_AARCH64 */", + "", + '#include "../../mlkem/src/common.h"', + f"MLK_EMPTY_CU(check_{function_name})", + "", + "#endif /* MLK_SYS_AARCH64 */", + "", + ] + + # Write the file using autogen's update_file + output_file = f"test/abicheck/check_{function_name}.c" + update_file( + output_file, "\n".join(gen_c_test()), dry_run=dry_run, force_format=True + ) + + generated_functions.append((function_name, arch_flags)) + + except Exception as e: + print(f"Error processing {assembly_file}: {e}") + + # Generate checks_all.h header file + def gen_checks_all_header(): + yield from gen_header() + yield from [ + "", + "#ifndef CHECKS_ALL_H", + "#define CHECKS_ALL_H", + "", + "#include ", + '#include "../../mlkem/src/sys.h"', + "", + "/* Array of all check functions */", + "typedef struct", + "{", + " const char *name;", + " int (*check_func)(void);", + "} abicheck_entry_t;", + "", + "#if defined(MLK_SYS_AARCH64)", + "", + "/* Function prototypes for all ABI checks (only on AArch64) */", + ] + + for func_name, arch_flags in generated_functions: + # Add architecture-specific guards for prototypes + for flag in arch_flags: + yield f"#if defined({flag})" + yield f"int check_{func_name}(void);" + for flag in reversed(arch_flags): + yield f"#endif /* {flag} */" + + yield from [ + "", + "static const abicheck_entry_t all_checks[] = {", + ] + + for func_name, arch_flags in generated_functions: + # Add architecture-specific guards for array entries + for flag in arch_flags: + yield f"#if defined({flag})" + yield f' {{"{func_name}", check_{func_name}}},' + for flag in reversed(arch_flags): + yield f"#endif /* {flag} */" + + yield from [ + " {NULL, NULL} /* Sentinel */", + "};", + "", + "#else /* !MLK_SYS_AARCH64 */", + "", + "/* Empty array for non-AArch64 platforms */", + "static const abicheck_entry_t all_checks[] = {", + " {NULL, NULL} /* Sentinel */", + "};", + "", + "#endif /* MLK_SYS_AARCH64 */", + "", + "#endif /* CHECKS_ALL_H */", + "", + ] + + update_file( + "test/abicheck/checks_all.h", + "\n".join(gen_checks_all_header()), + dry_run=dry_run, + force_format=True, + ) + + +def extract_yaml_from_assembly(assembly_file): + """Extract YAML metadata from assembly file.""" + with open(assembly_file, "r") as f: + content = f.read() + + # Look for YAML block in the new format (without asterisks) + yaml_match = re.search(r"/\*yaml\s*\n(.*?)\n\*/", content, re.DOTALL) + if not yaml_match: + raise ValueError(f"No YAML metadata found in {assembly_file}") + + yaml_content = yaml_match.group(1) + + try: + return yaml.safe_load(yaml_content) + except yaml.YAMLError as e: + raise ValueError(f"Invalid YAML in {assembly_file}: {e}") + + +def extract_arch_flags_from_assembly(assembly_file): + """Extract architecture-specific flags from assembly file.""" + with open(assembly_file, "r") as f: + content = f.read() + + # Look for dedicated architecture flags + arch_flags = [] + + # Check for __ARM_FEATURE_SHA3 + if re.search(r"#if\s+defined\(__ARM_FEATURE_SHA3\)", content): + arch_flags.append("__ARM_FEATURE_SHA3") + + return arch_flags + + +def resolve_buffer_size(reg_info, yaml_data): + """Resolve buffer size from register info.""" + size = reg_info.get("size_bytes") + if isinstance(size, str): + # Handle direct references to other registers (e.g., "x2") + if size in yaml_data: + ref_reg = yaml_data[size] + # If it's a scalar with test_with, use that value + if ref_reg.get("type") == "scalar" and "test_with" in ref_reg: + return ref_reg["test_with"] + # Otherwise fail - we don't expect buffer-to-buffer references + raise ValueError(f"Buffer {reg_info} references non-scalar register {size}") + + # Assume numeric string + return int(size) + elif isinstance(size, int): + return size + + raise ValueError(f"Cannot resolve buffer size from {reg_info}") + + def _main(): slothy_choices = [ "ntt", @@ -2776,6 +3071,9 @@ def _main(): check_macro_typos() high_level_status("Checked macro typos") + gen_abicheck(args.dry_run) + high_level_status("Generated ABI checker tests") + if __name__ == "__main__": _main() diff --git a/scripts/format b/scripts/format index efdec3b9f..0367118d5 100755 --- a/scripts/format +++ b/scripts/format @@ -48,7 +48,7 @@ if ! command -v black 2>&1 >/dev/null; then error "black not found. Are you running in a nix shell? See BUILDING.md." exit 1 fi -black --include "(scripts/tests|scripts/simpasm|scripts/cfify|scripts/autogen|scripts/check-namespace|\.py$)" "$ROOT" +black --include "(scripts/tests|scripts/simpasm|scripts/autogen|scripts/cfify|scripts/check-namespace|scripts/abicheckgen|\.py$)" "$ROOT" info "Formatting c files" if ! command -v clang-format 2>&1 >/dev/null; then diff --git a/scripts/tests b/scripts/tests index 40b35da7f..5944e2693 100755 --- a/scripts/tests +++ b/scripts/tests @@ -208,6 +208,7 @@ class TEST_TYPES(Enum): MONOLITHIC_BUILD_NATIVE = 14 STACK = 15 SIZE = 16 + ABICHECK = 17 def is_benchmark(self): return self in [TEST_TYPES.BENCH, TEST_TYPES.BENCH_COMPONENTS] @@ -274,6 +275,8 @@ class TEST_TYPES(Enum): return "Example (multilevel build, native)" if self == TEST_TYPES.SIZE: return "Measurement Code Size" + if self == TEST_TYPES.ABICHECK: + return "ABI Compliance Test" def make_dir(self): if self == TEST_TYPES.BRING_YOUR_OWN_FIPS202: @@ -329,6 +332,8 @@ class TEST_TYPES(Enum): return "" if self == TEST_TYPES.SIZE: return "size" + if self == TEST_TYPES.ABICHECK: + return "abicheck" def make_run_target(self, scheme): t = self.make_target() @@ -731,12 +736,24 @@ class Tests: if resultss is None: self.check_fail() + def abicheck(self): + """Run ABI compliance tests for assembly functions.""" + if not self.do_opt(): + return + + self._compile_schemes(TEST_TYPES.ABICHECK, True) + if self.args.run: + self._run_scheme(TEST_TYPES.ABICHECK, True, None) + + self.check_fail() + def all(self): func = self.args.func kat = self.args.kat acvp = self.args.acvp examples = self.args.examples stack = self.args.stack + abicheck = self.args.abicheck def _all(opt): if func is True: @@ -768,6 +785,9 @@ class Tests: if examples is True: self.examples() + if abicheck is True: + self.abicheck() + self.check_fail() def cbmc(self): @@ -1082,6 +1102,21 @@ def cli(): help="Do not run stack analysis", ) + abicheck_group = all_parser.add_mutually_exclusive_group() + abicheck_group.add_argument( + "--abicheck", + action="store_true", + dest="abicheck", + help="Run ABI compliance tests", + default=True, + ) + abicheck_group.add_argument( + "--no-abicheck", + action="store_false", + dest="abicheck", + help="Do not run ABI compliance tests", + ) + # acvp arguments acvp_parser = cmd_subparsers.add_parser( "acvp", help="Run ACVP client", parents=[common_parser] @@ -1271,6 +1306,13 @@ def cli(): default=False, ) + # abicheck arguments + abicheck_parser = cmd_subparsers.add_parser( + "abicheck", + help="Run ABI compliance tests for assembly functions", + parents=[common_parser], + ) + args = main_parser.parse_args() if not hasattr(args, "mac_taskpolicy"): @@ -1300,6 +1342,8 @@ def cli(): Tests(args).stack() elif args.cmd == "size": Tests(args).size() + elif args.cmd == "abicheck": + Tests(args).abicheck() if __name__ == "__main__": diff --git a/test/abicheck/aarch64_callstub.S b/test/abicheck/aarch64_callstub.S new file mode 100644 index 000000000..df7840354 --- /dev/null +++ b/test/abicheck/aarch64_callstub.S @@ -0,0 +1,201 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#include "../../mlkem/src/sys.h" +#if defined(MLK_SYS_AARCH64) + +/* + * Function: asm_call_stub + * Description: AArch64 ABI compliance testing stub that captures register state + * before and after function calls to verify AAPCS compliance + * + * C Signature: void asm_call_stub(struct register_state *input, + * struct register_state *output, + * void *function_ptr) + * + * Register ABI: + * ```yaml + * x0: + * type: buffer + * size_bytes: 1280 + * permissions: read-only + * c_parameter: struct register_state *input + * description: Input register state to load before function call + * x1: + * type: buffer + * size_bytes: 1280 + * permissions: write-only + * c_parameter: struct register_state *output + * description: Output buffer to store register state after function call + * x2: + * type: function_pointer + * c_parameter: void *function_ptr + * description: Pointer to function under test + * ``` + * + * Stack Usage: 192 bytes total (16-byte aligned) + * - 112 bytes for saving callee-saved GPRs (x18-x30: 7 pairs * 16 bytes) + * - 64 bytes for saving callee-saved NEON registers (d8-d15: 8 regs * 8 bytes) + * - 16 bytes for local variables (output state ptr, function ptr) + * + */ + +/* + * Stack layout constants + */ +#define STACK_SIZE_GPRS 112 /* Space for x18-x30 (7 pairs * 16 bytes = 112 bytes) */ +#define STACK_SIZE_VREGS 64 /* Space for d8-d15 (8 registers * 8 bytes = 64 bytes) */ +#define STACK_SIZE_LOCALS 16 /* Space for local variables */ + +#define STACK_SIZE 192 +#define STACK_BASE_GPRS 0 +#define STACK_BASE_VREGS STACK_SIZE_GPRS +#define STACK_BASE_LOCALS (STACK_SIZE_GPRS + STACK_SIZE_VREGS) + +.macro save_gprs + stp xzr, x18, [sp, #(STACK_BASE_GPRS + 16*0)] + stp x19, x20, [sp, #(STACK_BASE_GPRS + 16*1)] + stp x21, x22, [sp, #(STACK_BASE_GPRS + 16*2)] + stp x23, x24, [sp, #(STACK_BASE_GPRS + 16*3)] + stp x25, x26, [sp, #(STACK_BASE_GPRS + 16*4)] + stp x27, x28, [sp, #(STACK_BASE_GPRS + 16*5)] + stp x29, x30, [sp, #(STACK_BASE_GPRS + 16*6)] +.endm + +.macro restore_gprs + ldp xzr, x18, [sp, #(STACK_BASE_GPRS + 16*0)] + ldp x19, x20, [sp, #(STACK_BASE_GPRS + 16*1)] + ldp x21, x22, [sp, #(STACK_BASE_GPRS + 16*2)] + ldp x23, x24, [sp, #(STACK_BASE_GPRS + 16*3)] + ldp x25, x26, [sp, #(STACK_BASE_GPRS + 16*4)] + ldp x27, x28, [sp, #(STACK_BASE_GPRS + 16*5)] + ldp x29, x30, [sp, #(STACK_BASE_GPRS + 16*6)] +.endm + +.macro save_vregs + stp d8, d9, [sp, #(STACK_BASE_VREGS + 16*0)] + stp d10, d11, [sp, #(STACK_BASE_VREGS + 16*1)] + stp d12, d13, [sp, #(STACK_BASE_VREGS + 16*2)] + stp d14, d15, [sp, #(STACK_BASE_VREGS + 16*3)] +.endm + +.macro restore_vregs + ldp d8, d9, [sp, #(STACK_BASE_VREGS + 16*0)] + ldp d10, d11, [sp, #(STACK_BASE_VREGS + 16*1)] + ldp d12, d13, [sp, #(STACK_BASE_VREGS + 16*2)] + ldp d14, d15, [sp, #(STACK_BASE_VREGS + 16*3)] +.endm + +.macro alloc_stack + sub sp, sp, #(STACK_SIZE) +.endm + +.macro free_stack + add sp, sp, #(STACK_SIZE) +.endm + +.text +.balign 4 +#ifdef __APPLE__ +.global _asm_call_stub +_asm_call_stub: +#else +.global asm_call_stub +asm_call_stub: +#endif + alloc_stack + save_gprs + save_vregs + + /* Save output state address and target function pointer to stack locals. + * We need to preserve these across register state loading */ + stp x1, x2, [sp, #(STACK_BASE_LOCALS)] + + // Load NEON registers from input state + add x30, x0, #256 + ldp q0, q1, [x30, #(16*0)] + ldp q2, q3, [x30, #(16*2)] + ldp q4, q5, [x30, #(16*4)] + ldp q6, q7, [x30, #(16*6)] + ldp q8, q9, [x30, #(16*8)] + ldp q10, q11, [x30, #(16*10)] + ldp q12, q13, [x30, #(16*12)] + ldp q14, q15, [x30, #(16*14)] + ldp q16, q17, [x30, #(16*16)] + ldp q18, q19, [x30, #(16*18)] + ldp q20, q21, [x30, #(16*20)] + ldp q22, q23, [x30, #(16*22)] + ldp q24, q25, [x30, #(16*24)] + ldp q26, q27, [x30, #(16*26)] + ldp q28, q29, [x30, #(16*28)] + ldp q30, q31, [x30, #(16*30)] + // Load GPRs from input state + sub x30, x30, #256 + ldp x0, x1, [x0, #(8*0)] + ldp x2, x3, [x30, #(8*2)] + ldp x4, x5, [x30, #(8*4)] + ldp x6, x7, [x30, #(8*6)] + ldp x8, x9, [x30, #(8*8)] + ldp x10, x11, [x30, #(8*10)] + ldp x12, x13, [x30, #(8*12)] + ldp x14, x15, [x30, #(8*14)] + ldp x16, x17, [x30, #(8*16)] + ldp x18, x19, [x30, #(8*18)] + ldp x20, x21, [x30, #(8*20)] + ldp x22, x23, [x30, #(8*22)] + ldp x24, x25, [x30, #(8*24)] + ldp x26, x27, [x30, #(8*26)] + ldp x28, x29, [x30, #(8*28)] + + // Reload target function pointer (overwriting x30/LR is fine - blr will set it) + ldr x30, [sp, #(STACK_BASE_LOCALS + 8)] + // Call target + blr x30 + // Load output state address (overwrite x30/LR again) + ldr x30, [sp, #(STACK_BASE_LOCALS + 0)] + + // Store final GPR state to output state + stp x0, x1, [x30, #(8*0)] + stp x2, x3, [x30, #(8*2)] + stp x4, x5, [x30, #(8*4)] + stp x6, x7, [x30, #(8*6)] + stp x8, x9, [x30, #(8*8)] + stp x10, x11, [x30, #(8*10)] + stp x12, x13, [x30, #(8*12)] + stp x14, x15, [x30, #(8*14)] + stp x16, x17, [x30, #(8*16)] + stp x18, x19, [x30, #(8*18)] + stp x20, x21, [x30, #(8*20)] + stp x22, x23, [x30, #(8*22)] + stp x24, x25, [x30, #(8*24)] + stp x26, x27, [x30, #(8*26)] + stp x28, x29, [x30, #(8*28)] + // Note: x30 (LR) not stored as it's meaningless after function call + + // Store final NEON state to output state + add x30, x30, #256 + stp q0, q1, [x30, #(16*0)] + stp q2, q3, [x30, #(16*2)] + stp q4, q5, [x30, #(16*4)] + stp q6, q7, [x30, #(16*6)] + stp q8, q9, [x30, #(16*8)] + stp q10, q11, [x30, #(16*10)] + stp q12, q13, [x30, #(16*12)] + stp q14, q15, [x30, #(16*14)] + stp q16, q17, [x30, #(16*16)] + stp q18, q19, [x30, #(16*18)] + stp q20, q21, [x30, #(16*20)] + stp q22, q23, [x30, #(16*22)] + stp q24, q25, [x30, #(16*24)] + stp q26, q27, [x30, #(16*26)] + stp q28, q29, [x30, #(16*28)] + stp q30, q31, [x30, #(16*30)] + + restore_vregs + restore_gprs + free_stack + ret + +#endif /* MLK_SYS_AARCH64 */ diff --git a/test/abicheck/abicheck.c b/test/abicheck/abicheck.c new file mode 100644 index 000000000..24f9b520f --- /dev/null +++ b/test/abicheck/abicheck.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#include +#include +#include "../../mlkem/src/sys.h" +#include "checks_all.h" + +int main(void) +{ +#ifdef MLK_SYS_AARCH64 + int result; + int failed_tests = 0; + const abicheck_entry_t *entry; + + for (entry = all_checks; entry->name != NULL; entry++) + { + printf("Running ABI check for %s... ", entry->name); + fflush(stdout); + + result = entry->check_func(); + if (result != 0) + { + printf("FAILED\n"); + failed_tests++; + } + else + { + printf("PASSED\n"); + } + } + + if (failed_tests > 0) + { + return 1; + } + else + { + return 0; + } +#else /* MLK_SYS_AARCH64 */ + printf( + "ABI check is not yet implemented for architectures other than " + "AArch64\n"); + printf("Skipping ABI check...\n"); + return 0; +#endif /* !MLK_SYS_AARCH64 */ +} diff --git a/test/abicheck/abicheckutil.c b/test/abicheck/abicheckutil.c new file mode 100644 index 000000000..3b6911ae9 --- /dev/null +++ b/test/abicheck/abicheckutil.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#include +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" + +/* AArch64 AAPCS callee-saved registers: */ +/* - General-purpose: x19-x28, x29 (FP), x30 (LR) */ +/* - NEON: d8-d15 (lower 64 bits of q8-q15 only) */ + +int check_aarch64_aapcs_compliance(struct register_state *before, + struct register_state *after) +{ + int violations = 0; + int i; + + /* Check callee-saved general-purpose registers (x19-x30) */ + /* Also, check that x18 has not been touched */ + for (i = 18; i <= 29; i++) + { + if (before->gpr[i] != after->gpr[i]) + { + printf("ABI violation: x%d changed from 0x%016" PRIx64 " to 0x%016" PRIx64 + "\n", + i, before->gpr[i], after->gpr[i]); + violations++; + } + } + + /* Check callee-saved NEON registers (d8-d15, lower 64 bits only) */ + /* AAPCS only requires preservation of d8-d15, not the full q8-q15 */ + for (i = 8; i <= 15; i++) + { + if (before->neon[i][0] != after->neon[i][0]) + { + printf("ABI violation: d%d changed from 0x%016" PRIx64 " to 0x%016" PRIx64 + "\n", + i, before->neon[i][0], after->neon[i][0]); + violations++; + } + } + + return violations; +} + +void init_random_register_state(struct register_state *state) +{ + int i; + + /* Set Xi = i for GPRs */ + for (i = 0; i < 31; i++) + { + state->gpr[i] = i; + } + + /* Set NEON registers to predictable values */ + for (i = 0; i < 32; i++) + { + state->neon[i][0] = i; /* Lower 64 bits = i */ + state->neon[i][1] = i + 100; /* Upper 64 bits = i + 100 */ + } +} diff --git a/test/abicheck/abicheckutil.h b/test/abicheck/abicheckutil.h new file mode 100644 index 000000000..582f88f90 --- /dev/null +++ b/test/abicheck/abicheckutil.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#ifndef ABICHECKUTIL_H +#define ABICHECKUTIL_H + +#include +#include "../../mlkem/src/sys.h" + +struct register_state +{ + uint64_t gpr[32]; /* x0-x30 */ + uint64_t neon[32][2]; /* q0-q31 (full 128-bit NEON registers as two 64-bit + values) */ +}; + +/* ABI compliance checking */ +int check_aarch64_aapcs_compliance(struct register_state *before, + struct register_state *after); + +/* Utility functions */ +void init_random_register_state(struct register_state *state); + +/* Assembly stub function */ +#ifdef MLK_SYS_AARCH64 +extern void asm_call_stub(struct register_state *input, + struct register_state *output, + void (*function_ptr)(void)); +#else +/* Stub implementation for non-AArch64 platforms */ +static MLK_INLINE void asm_call_stub(struct register_state *input, + struct register_state *output, + void (*function_ptr)(void)) +{ + (void)input; + (void)output; + (void)function_ptr; + /* This should never be called on non-AArch64 platforms */ + /* The main abicheck program checks architecture before running tests */ +} +#endif /* !MLK_SYS_AARCH64 */ + +#endif /* !ABICHECKUTIL_H */ diff --git a/test/abicheck/check_intt_asm.c b/test/abicheck/check_intt_asm.c new file mode 100644 index 000000000..38875b7a6 --- /dev/null +++ b/test/abicheck/check_intt_asm.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_intt_asm(int16_t p[256], const int16_t twiddles12345[80], + const int16_t twiddles56[384]); + +int check_intt_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Input/output polynomial */ + MLK_ALIGN uint8_t buf_x1[160]; /* Twiddle factors for layers 1-5 */ + MLK_ALIGN uint8_t buf_x2[768]; /* Twiddle factors for layers 6-7 */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 160); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 768); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, (void (*)(void))mlk_intt_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for intt_asm (iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_intt_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_keccak_f1600_x1_scalar_asm.c b/test/abicheck/check_keccak_f1600_x1_scalar_asm.c new file mode 100644 index 000000000..38c028f1f --- /dev/null +++ b/test/abicheck/check_keccak_f1600_x1_scalar_asm.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_keccak_f1600_x1_scalar_asm(uint64_t state[25], const uint64_t rc[24]); + +int check_keccak_f1600_x1_scalar_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[200]; /* Keccak state (25 x uint64_t) */ + MLK_ALIGN uint8_t buf_x1[192]; /* Round constants (24 x uint64_t) */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 200); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 192); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_keccak_f1600_x1_scalar_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for keccak_f1600_x1_scalar_asm (iteration %d): " + "%d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x1_scalar_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_keccak_f1600_x1_v84a_asm.c b/test/abicheck/check_keccak_f1600_x1_v84a_asm.c new file mode 100644 index 000000000..74dfdd2fa --- /dev/null +++ b/test/abicheck/check_keccak_f1600_x1_v84a_asm.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#if defined(__ARM_FEATURE_SHA3) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_keccak_f1600_x1_v84a_asm(uint64_t state[25], const uint64_t rc[24]); + +int check_keccak_f1600_x1_v84a_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[200]; /* Keccak state (25 x uint64_t) */ + MLK_ALIGN uint8_t buf_x1[192]; /* Round constants (24 x uint64_t) */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 200); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 192); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_keccak_f1600_x1_v84a_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for keccak_f1600_x1_v84a_asm (iteration %d): %d " + "violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* __ARM_FEATURE_SHA3 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x1_v84a_asm) + +#endif /* !__ARM_FEATURE_SHA3 */ + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x1_v84a_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_keccak_f1600_x2_v84a_asm.c b/test/abicheck/check_keccak_f1600_x2_v84a_asm.c new file mode 100644 index 000000000..cfe3f73fb --- /dev/null +++ b/test/abicheck/check_keccak_f1600_x2_v84a_asm.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#if defined(__ARM_FEATURE_SHA3) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_keccak_f1600_x2_v84a_asm(uint64_t state[50], const uint64_t rc[24]); + +int check_keccak_f1600_x2_v84a_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t + buf_x0[400]; /* Two sequential Keccak states (state0[25], state1[25]) */ + MLK_ALIGN uint8_t buf_x1[192]; /* Round constants (24 x uint64_t) */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 400); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 192); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_keccak_f1600_x2_v84a_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for keccak_f1600_x2_v84a_asm (iteration %d): %d " + "violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* __ARM_FEATURE_SHA3 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x2_v84a_asm) + +#endif /* !__ARM_FEATURE_SHA3 */ + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x2_v84a_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_keccak_f1600_x4_v8a_scalar_hybrid_asm.c b/test/abicheck/check_keccak_f1600_x4_v8a_scalar_hybrid_asm.c new file mode 100644 index 000000000..7b66afb41 --- /dev/null +++ b/test/abicheck/check_keccak_f1600_x4_v8a_scalar_hybrid_asm.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_keccak_f1600_x4_v8a_scalar_hybrid_asm(uint64_t state[100], + const uint64_t rc[24]); + +int check_keccak_f1600_x4_v8a_scalar_hybrid_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[800]; /* Four sequential Keccak states (state0[25], + state1[25], state2[25], state3[25]) */ + MLK_ALIGN uint8_t buf_x1[192]; /* Round constants (24 x uint64_t) */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 800); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 192); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_keccak_f1600_x4_v8a_scalar_hybrid_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for keccak_f1600_x4_v8a_scalar_hybrid_asm " + "(iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x4_v8a_scalar_hybrid_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.c b/test/abicheck/check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.c new file mode 100644 index 000000000..89a80d353 --- /dev/null +++ b/test/abicheck/check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#if defined(__ARM_FEATURE_SHA3) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm(uint64_t state[100], + const uint64_t rc[24]); + +int check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[800]; /* Four sequential Keccak states (state0[25], + state1[25], state2[25], state3[25]) */ + MLK_ALIGN uint8_t buf_x1[192]; /* Round constants (24 x uint64_t) */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 800); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 192); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub( + &input_state, &output_state, + (void (*)(void))mlk_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm " + "(iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* __ARM_FEATURE_SHA3 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm) + +#endif /* !__ARM_FEATURE_SHA3 */ + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_ntt_asm.c b/test/abicheck/check_ntt_asm.c new file mode 100644 index 000000000..7a017f969 --- /dev/null +++ b/test/abicheck/check_ntt_asm.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_ntt_asm(int16_t p[256], const int16_t twiddles12345[80], + const int16_t twiddles56[384]); + +int check_ntt_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Input/output polynomial */ + MLK_ALIGN uint8_t buf_x1[160]; /* Twiddle factors for layers 1-5 */ + MLK_ALIGN uint8_t buf_x2[768]; /* Twiddle factors for layers 6-7 */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 160); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 768); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, (void (*)(void))mlk_ntt_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for ntt_asm (iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_ntt_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_poly_mulcache_compute_asm.c b/test/abicheck/check_poly_mulcache_compute_asm.c new file mode 100644 index 000000000..9168117ec --- /dev/null +++ b/test/abicheck/check_poly_mulcache_compute_asm.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_poly_mulcache_compute_asm(int16_t cache[128], + const int16_t mlk_poly[256], + const int16_t zetas[128], + const int16_t zetas_twisted[128]); + +int check_poly_mulcache_compute_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[256]; /* Output cache */ + MLK_ALIGN uint8_t buf_x1[512]; /* Input polynomial */ + MLK_ALIGN uint8_t buf_x2[256]; /* Zeta values */ + MLK_ALIGN uint8_t buf_x3[256]; /* Twisted zeta values */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 256); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 512); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 256); + /* Initialize buffer for x3 */ + randombytes(buf_x3, 256); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + input_state.gpr[3] = (uint64_t)buf_x3; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_poly_mulcache_compute_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for poly_mulcache_compute_asm (iteration %d): " + "%d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_poly_mulcache_compute_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_poly_reduce_asm.c b/test/abicheck/check_poly_reduce_asm.c new file mode 100644 index 000000000..68f68ad48 --- /dev/null +++ b/test/abicheck/check_poly_reduce_asm.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_poly_reduce_asm(int16_t p[256]); + +int check_poly_reduce_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Input/output polynomial */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_poly_reduce_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for poly_reduce_asm (iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_poly_reduce_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_poly_tobytes_asm.c b/test/abicheck/check_poly_tobytes_asm.c new file mode 100644 index 000000000..ba7c8f251 --- /dev/null +++ b/test/abicheck/check_poly_tobytes_asm.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_poly_tobytes_asm(uint8_t r[384], const int16_t a[256]); + +int check_poly_tobytes_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[384]; /* Output byte array */ + MLK_ALIGN uint8_t buf_x1[512]; /* Input polynomial */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 384); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 512); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_poly_tobytes_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf(stderr, + "ABI test FAILED for poly_tobytes_asm (iteration %d): %d " + "violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_poly_tobytes_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_poly_tomont_asm.c b/test/abicheck/check_poly_tomont_asm.c new file mode 100644 index 000000000..56b33a8e8 --- /dev/null +++ b/test/abicheck/check_poly_tomont_asm.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_poly_tomont_asm(int16_t p[256]); + +int check_poly_tomont_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Input/output polynomial */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_poly_tomont_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for poly_tomont_asm (iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_poly_tomont_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k2.c b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k2.c new file mode 100644 index 000000000..57b6e638a --- /dev/null +++ b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k2.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_polyvec_basemul_acc_montgomery_cached_asm_k2( + int16_t r[256], const int16_t a[512], const int16_t b[512], + const int16_t b_cache[256]); + +int check_polyvec_basemul_acc_montgomery_cached_asm_k2(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Output polynomial */ + MLK_ALIGN uint8_t buf_x1[1024]; /* Input polynomial vector a */ + MLK_ALIGN uint8_t buf_x2[1024]; /* Input polynomial vector b */ + MLK_ALIGN uint8_t buf_x3[512]; /* Cached values for b */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 1024); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 1024); + /* Initialize buffer for x3 */ + randombytes(buf_x3, 512); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + input_state.gpr[3] = (uint64_t)buf_x3; + + /* Call function through ABI test stub */ + asm_call_stub( + &input_state, &output_state, + (void (*)(void))mlk_polyvec_basemul_acc_montgomery_cached_asm_k2); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for polyvec_basemul_acc_montgomery_cached_asm_k2 " + "(iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_polyvec_basemul_acc_montgomery_cached_asm_k2) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k3.c b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k3.c new file mode 100644 index 000000000..f2c12f95f --- /dev/null +++ b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k3.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_polyvec_basemul_acc_montgomery_cached_asm_k3( + int16_t r[256], const int16_t a[768], const int16_t b[768], + const int16_t b_cache[384]); + +int check_polyvec_basemul_acc_montgomery_cached_asm_k3(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Output polynomial */ + MLK_ALIGN uint8_t buf_x1[1536]; /* Input polynomial vector a */ + MLK_ALIGN uint8_t buf_x2[1536]; /* Input polynomial vector b */ + MLK_ALIGN uint8_t buf_x3[768]; /* Cached values for b */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 1536); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 1536); + /* Initialize buffer for x3 */ + randombytes(buf_x3, 768); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + input_state.gpr[3] = (uint64_t)buf_x3; + + /* Call function through ABI test stub */ + asm_call_stub( + &input_state, &output_state, + (void (*)(void))mlk_polyvec_basemul_acc_montgomery_cached_asm_k3); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for polyvec_basemul_acc_montgomery_cached_asm_k3 " + "(iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_polyvec_basemul_acc_montgomery_cached_asm_k3) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k4.c b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k4.c new file mode 100644 index 000000000..39edfb191 --- /dev/null +++ b/test/abicheck/check_polyvec_basemul_acc_montgomery_cached_asm_k4.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +void mlk_polyvec_basemul_acc_montgomery_cached_asm_k4( + int16_t r[256], const int16_t a[1024], const int16_t b[1024], + const int16_t b_cache[512]); + +int check_polyvec_basemul_acc_montgomery_cached_asm_k4(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Output polynomial */ + MLK_ALIGN uint8_t buf_x1[2048]; /* Input polynomial vector a */ + MLK_ALIGN uint8_t buf_x2[2048]; /* Input polynomial vector b */ + MLK_ALIGN uint8_t buf_x3[1024]; /* Cached values for b */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 2048); + /* Initialize buffer for x2 */ + randombytes(buf_x2, 2048); + /* Initialize buffer for x3 */ + randombytes(buf_x3, 1024); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = (uint64_t)buf_x2; + input_state.gpr[3] = (uint64_t)buf_x3; + + /* Call function through ABI test stub */ + asm_call_stub( + &input_state, &output_state, + (void (*)(void))mlk_polyvec_basemul_acc_montgomery_cached_asm_k4); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for polyvec_basemul_acc_montgomery_cached_asm_k4 " + "(iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_polyvec_basemul_acc_montgomery_cached_asm_k4) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/check_rej_uniform_asm.c b/test/abicheck/check_rej_uniform_asm.c new file mode 100644 index 000000000..1a5b98511 --- /dev/null +++ b/test/abicheck/check_rej_uniform_asm.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#include "../../mlkem/src/sys.h" + +#if defined(MLK_SYS_AARCH64) + +#include +#include + +#include "../notrandombytes/notrandombytes.h" +#include "abicheckutil.h" +#include "checks_all.h" + +typedef struct register_state register_state; + +#define NUM_TESTS 3 + +uint64_t mlk_rej_uniform_asm(int16_t r[256], const uint8_t *buf, + unsigned buflen, const uint8_t table[2048]); + +int check_rej_uniform_asm(void) +{ + int test_iter; + register_state input_state, output_state; + int violations; + MLK_ALIGN uint8_t buf_x0[512]; /* Output buffer */ + MLK_ALIGN uint8_t buf_x1[504]; /* Input buffer */ + MLK_ALIGN uint8_t buf_x3[2048]; /* Lookup table */ + + for (test_iter = 0; test_iter < NUM_TESTS; test_iter++) + { + /* Initialize random register state */ + init_random_register_state(&input_state); + + /* Initialize buffer for x0 */ + randombytes(buf_x0, 512); + /* Initialize buffer for x1 */ + randombytes(buf_x1, 504); + /* Initialize buffer for x3 */ + randombytes(buf_x3, 2048); + + /* Set up register state for function arguments */ + input_state.gpr[0] = (uint64_t)buf_x0; + input_state.gpr[1] = (uint64_t)buf_x1; + input_state.gpr[2] = 504; + input_state.gpr[3] = (uint64_t)buf_x3; + + /* Call function through ABI test stub */ + asm_call_stub(&input_state, &output_state, + (void (*)(void))mlk_rej_uniform_asm); + + /* Check ABI compliance */ + violations = check_aarch64_aapcs_compliance(&input_state, &output_state); + if (violations > 0) + { + fprintf( + stderr, + "ABI test FAILED for rej_uniform_asm (iteration %d): %d violations\n", + test_iter + 1, violations); + return 1; + } + } + + return 0; +} + +#else /* MLK_SYS_AARCH64 */ + +#include "../../mlkem/src/common.h" +MLK_EMPTY_CU(check_rej_uniform_asm) + +#endif /* !MLK_SYS_AARCH64 */ diff --git a/test/abicheck/checks_all.h b/test/abicheck/checks_all.h new file mode 100644 index 000000000..2817d96c7 --- /dev/null +++ b/test/abicheck/checks_all.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) The mlkem-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mlkem-native repository. + * Do not modify it directly. + */ + + +#ifndef CHECKS_ALL_H +#define CHECKS_ALL_H + +#include +#include "../../mlkem/src/sys.h" + +/* Array of all check functions */ +typedef struct +{ + const char *name; + int (*check_func)(void); +} abicheck_entry_t; + +#if defined(MLK_SYS_AARCH64) + +/* Function prototypes for all ABI checks (only on AArch64) */ +int check_keccak_f1600_x1_scalar_asm(void); +#if defined(__ARM_FEATURE_SHA3) +int check_keccak_f1600_x1_v84a_asm(void); +#endif +#if defined(__ARM_FEATURE_SHA3) +int check_keccak_f1600_x2_v84a_asm(void); +#endif +int check_keccak_f1600_x4_v8a_scalar_hybrid_asm(void); +#if defined(__ARM_FEATURE_SHA3) +int check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm(void); +#endif +int check_intt_asm(void); +int check_ntt_asm(void); +int check_poly_mulcache_compute_asm(void); +int check_poly_reduce_asm(void); +int check_poly_tobytes_asm(void); +int check_poly_tomont_asm(void); +int check_polyvec_basemul_acc_montgomery_cached_asm_k2(void); +int check_polyvec_basemul_acc_montgomery_cached_asm_k3(void); +int check_polyvec_basemul_acc_montgomery_cached_asm_k4(void); +int check_rej_uniform_asm(void); + +static const abicheck_entry_t all_checks[] = { + {"keccak_f1600_x1_scalar_asm", check_keccak_f1600_x1_scalar_asm}, +#if defined(__ARM_FEATURE_SHA3) + {"keccak_f1600_x1_v84a_asm", check_keccak_f1600_x1_v84a_asm}, +#endif +#if defined(__ARM_FEATURE_SHA3) + {"keccak_f1600_x2_v84a_asm", check_keccak_f1600_x2_v84a_asm}, +#endif + {"keccak_f1600_x4_v8a_scalar_hybrid_asm", + check_keccak_f1600_x4_v8a_scalar_hybrid_asm}, +#if defined(__ARM_FEATURE_SHA3) + {"keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm", + check_keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm}, +#endif + {"intt_asm", check_intt_asm}, + {"ntt_asm", check_ntt_asm}, + {"poly_mulcache_compute_asm", check_poly_mulcache_compute_asm}, + {"poly_reduce_asm", check_poly_reduce_asm}, + {"poly_tobytes_asm", check_poly_tobytes_asm}, + {"poly_tomont_asm", check_poly_tomont_asm}, + {"polyvec_basemul_acc_montgomery_cached_asm_k2", + check_polyvec_basemul_acc_montgomery_cached_asm_k2}, + {"polyvec_basemul_acc_montgomery_cached_asm_k3", + check_polyvec_basemul_acc_montgomery_cached_asm_k3}, + {"polyvec_basemul_acc_montgomery_cached_asm_k4", + check_polyvec_basemul_acc_montgomery_cached_asm_k4}, + {"rej_uniform_asm", check_rej_uniform_asm}, + {NULL, NULL} /* Sentinel */ +}; + +#else /* MLK_SYS_AARCH64 */ + +/* Empty array for non-AArch64 platforms */ +static const abicheck_entry_t all_checks[] = { + {NULL, NULL} /* Sentinel */ +}; + +#endif /* !MLK_SYS_AARCH64 */ + +#endif /* !CHECKS_ALL_H */ diff --git a/test/mk/components.mk b/test/mk/components.mk index fabe5b412..2b371d36d 100644 --- a/test/mk/components.mk +++ b/test/mk/components.mk @@ -70,3 +70,57 @@ $(foreach scheme,mlkem512 mlkem768 mlkem1024, \ $(ALL_TESTS:%=$(MLKEM512_DIR)/bin/%512): $(call MAKE_OBJS, $(MLKEM512_DIR), $(wildcard test/notrandombytes/*.c)) $(ALL_TESTS:%=$(MLKEM768_DIR)/bin/%768): $(call MAKE_OBJS, $(MLKEM768_DIR), $(wildcard test/notrandombytes/*.c)) $(ALL_TESTS:%=$(MLKEM1024_DIR)/bin/%1024): $(call MAKE_OBJS, $(MLKEM1024_DIR), $(wildcard test/notrandombytes/*.c)) + +ABICHECK_DIR = $(BUILD_DIR)/abicheck + +# ABI checker sources +ABICHECK_SOURCES = test/abicheck/abicheck.c test/abicheck/abicheckutil.c test/abicheck/aarch64_callstub.S +ABICHECK_SOURCES += $(wildcard test/abicheck/check_*.c) +ABICHECK_SOURCES += $(wildcard test/notrandombytes/*.c) + +# Assembly sources for the functions under test (only aarch64 for now) +# Only include AArch64 assembly sources on AArch64 platforms +ABICHECK_ASM_SOURCES = + +ifeq ($(IS_AARCH64),1) + ABICHECK_ASM_SOURCES += $(wildcard mlkem/src/fips202/native/aarch64/src/*.S) + ABICHECK_ASM_SOURCES += $(wildcard mlkem/src/native/aarch64/src/*.S) +else ifeq ($(IS_X86_64),1) +# NOTE: We need this even though the ABI checker is currently AArch64 only, +# as common.h pulls in the backend. + ABICHECK_ASM_SOURCES += $(wildcard mlkem/src/fips202/native/x86_64/src/*.S) + ABICHECK_ASM_SOURCES += $(wildcard mlkem/src/native/x86_64/src/*.S) +endif + +# All ABI checker sources +ABICHECK_ALL_SOURCES = $(ABICHECK_SOURCES) $(ABICHECK_ASM_SOURCES) + +# ABI checker objects +ABICHECK_OBJS = $(call MAKE_OBJS,$(ABICHECK_DIR),$(ABICHECK_ALL_SOURCES)) + +$(ABICHECK_OBJS): CFLAGS += -DMLK_CONFIG_NAMESPACE_PREFIX=mlk + +# The hackery below is to work around the preprocessor guards in the ASM files. +# TODO: We should really be working with 'raw' ASM files without guards. +ifeq ($(IS_AARCH64),1) +# The ASM files pull in common.h, which pulls in the backend meta files if +# MLK_CONFIG_USE_NATIVE_BACKEND_XXX is set. This, in turn, selects a subset +# of MLK_FIPS202_AARCH64_NEED_XXX via `#define ...` in the source, conflicting +# with us setting all of them below using `-D...` which leads to `#define .. 1`. +# To work around, we disable the backends in the config but explicitly enable +# the directives that guard the ASM files. Again, this is hacky and should be +# improved in the future. +$(ABICHECK_DIR)/mlkem/src/native/aarch64/%.S.o: CFLAGS += -DMLK_CONFIG_MULTILEVEL_WITH_SHARED +$(ABICHECK_DIR)/mlkem/src/native/aarch64/%.S.o: CFLAGS += -UMLK_CONFIG_USE_NATIVE_BACKEND_ARITH +$(ABICHECK_DIR)/mlkem/src/native/aarch64/%.S.o: CFLAGS += -DMLK_ARITH_BACKEND_AARCH64 +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -UMLK_CONFIG_USE_NATIVE_BACKEND_FIPS202 +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X1_SCALAR +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X1_SCALAR +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X1_V84A +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X2_V84A +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X4_V8A_SCALAR_HYBRID +$(ABICHECK_DIR)/mlkem/src/fips202/native/aarch64/%.S.o: CFLAGS += -DMLK_FIPS202_AARCH64_NEED_X4_V8A_V84A_SCALAR_HYBRID +endif + +# ABI checker binary +$(ABICHECK_DIR)/bin/abicheck: $(ABICHECK_OBJS) diff --git a/test/mk/config.mk b/test/mk/config.mk index 832e89608..1cfbfea55 100644 --- a/test/mk/config.mk +++ b/test/mk/config.mk @@ -60,6 +60,7 @@ ifeq ($(HOST_PLATFORM),Linux-x86_64) CFLAGS += -z noexecstack endif + ifeq ($(CYCLES),PMU) CFLAGS += -DPMU_CYCLES endif @@ -72,6 +73,42 @@ ifeq ($(CYCLES),MAC) CFLAGS += -DMAC_CYCLES endif +########################## +# Architecture Detection # +########################## + +# Check if we're building for AArch64 (native or cross-compilation) +IS_AARCH64 := 0 +ifeq ($(CROSS_PREFIX),) +# Native compilation - check HOST_PLATFORM +ifeq ($(HOST_PLATFORM),Linux-aarch64) + IS_AARCH64 := 1 +else ifeq ($(HOST_PLATFORM),Darwin-arm64) + IS_AARCH64 := 1 +endif +else +# Cross compilation - check CROSS_PREFIX for aarch64- (not aarch64_be-) +ifneq ($(findstring aarch64-, $(CROSS_PREFIX)),) + IS_AARCH64 := 1 +endif +endif + +# Check if we're building for x86_64 (native or cross-compilation) +IS_X86_64 := 0 +ifeq ($(CROSS_PREFIX),) +# Native compilation - check HOST_PLATFORM +ifeq ($(HOST_PLATFORM),Linux-x86_64) + IS_X86_64 := 1 +else ifeq ($(HOST_PLATFORM),Darwin-x86_64) + IS_X86_64 := 1 +endif +else +# Cross compilation - check CROSS_PREFIX for x86_64- +ifneq ($(findstring x86_64-, $(CROSS_PREFIX)),) + IS_X86_64 := 1 +endif +endif + ############################## # Include retained variables # ############################## diff --git a/test/mk/rules.mk b/test/mk/rules.mk index b68a9b17e..37d13e328 100644 --- a/test/mk/rules.mk +++ b/test/mk/rules.mk @@ -15,6 +15,11 @@ $(BUILD_DIR)/mlkem1024/bin/%: $(CONFIG) $(Q)[ -d $(@D) ] || mkdir -p $(@D) $(Q)$(LD) $(CFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) +$(BUILD_DIR)/abicheck/bin/%: $(CONFIG) + $(Q)echo " LD $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(LD) $(CFLAGS) -o $@ $(filter %.o,$^) $(LDLIBS) + $(BUILD_DIR)/%.a: $(CONFIG) $(Q)echo " AR $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) @@ -50,3 +55,13 @@ $(BUILD_DIR)/mlkem1024/%.S.o: %.S $(CONFIG) $(Q)echo " AS $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/abicheck/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/abicheck/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $<