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 819f00def..c225c70db 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 @@ -2670,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", @@ -2761,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) $<