@@ -20,4 +20,7 @@ config ARM64_RELOC_TEST
depends on m
tristate "Relocation testing module"
+config TEST_INSN
+ tristate "Test functions located in the aarch64 instruction encoder at runtime"
+
source "drivers/hwtracing/coresight/Kconfig"
@@ -176,7 +176,9 @@ vdso_install:
archprepare:
$(Q)$(MAKE) $(build)=arch/arm64/tools kapi
+#ifdef CONFIG_TEST_INSN
$(Q)$(MAKE) $(build)=arch/arm64/tools tests
+#endif
ifeq ($(CONFIG_ARM64_ERRATUM_843419),y)
ifneq ($(CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419),y)
@echo "warning: ld does not support --fix-cortex-a53-843419; kernel may be susceptible to erratum" >&2
@@ -22,3 +22,5 @@ obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
obj-$(CONFIG_ARM64_MTE) += mte.o
obj-$(CONFIG_KASAN_SW_TAGS) += kasan_sw_tags.o
+
+obj-$(CONFIG_TEST_INSN) += test_insn.o
@@ -193,6 +193,7 @@ u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn)
return (insn >> shift) & mask;
}
+EXPORT_SYMBOL_GPL(aarch64_insn_decode_immediate);
u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
u32 insn, u64 imm)
@@ -256,6 +257,7 @@ u32 aarch64_insn_decode_register(enum aarch64_insn_register_type type,
return (insn >> shift) & GENMASK(4, 0);
}
+EXPORT_SYMBOL_GPL(aarch64_insn_decode_register);
static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
u32 insn,
@@ -1424,6 +1426,7 @@ u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
return aarch64_encode_immediate(imm, variant, insn);
}
+EXPORT_SYMBOL_GPL(aarch64_insn_gen_logical_immediate);
u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
enum aarch64_insn_register Rm,
new file mode 100644
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test cases for the aarch64 insn encoder.
+ *
+ * Copyright (C) 2021 ARM Limited.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include <asm/debug-monitors.h>
+#include <asm/insn.h>
+
+#include "../../../tools/testing/selftests/kselftest_module.h"
+
+struct bitmask_test_case {
+ /* input */
+ u64 imm;
+
+ /* expected output */
+ u64 n, immr, imms;
+};
+struct bitmask_test_case aarch64_logic_imm_test[] = {
+#include <asm/test_logic_imm_generated.h>
+};
+
+KSTM_MODULE_GLOBALS();
+
+static void __init test_logic_imm(void)
+{
+ int i;
+ u8 rd, rn;
+ u32 insn;
+
+ for (i = 0; i < ARRAY_SIZE(aarch64_logic_imm_test); i++) {
+ total_tests++;
+
+ rd = i % 30;
+ rn = (i + 1) % 30;
+
+ insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_AND,
+ AARCH64_INSN_VARIANT_64BIT,
+ rn, rd, aarch64_logic_imm_test[i].imm);
+
+ if (!aarch64_insn_is_and_imm(insn) ||
+ rd != aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn) ||
+ rn != aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RN, insn) ||
+ aarch64_logic_imm_test[i].imms != aarch64_insn_decode_immediate(AARCH64_INSN_IMM_S, insn) ||
+ aarch64_logic_imm_test[i].immr != aarch64_insn_decode_immediate(AARCH64_INSN_IMM_R, insn) ||
+ aarch64_logic_imm_test[i].n != aarch64_insn_decode_immediate(AARCH64_INSN_IMM_N, insn)) {
+ failed_tests++;
+ pr_warn_once("[%s:%u] Failed to encode immediate 0x%llx (got insn 0x%x))\n",
+ __FILE__, __LINE__, aarch64_logic_imm_test[i].imm, insn);
+ continue;
+ }
+ }
+}
+
+static void __init do_test_bad_logic_imm(u64 imm, enum aarch64_insn_variant var)
+{
+ u32 insn;
+
+ total_tests++;
+ insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_AND,
+ var, 0, 0, imm);
+ if (insn != AARCH64_BREAK_FAULT)
+ failed_tests++;
+}
+
+static void __init test_bad_logic_imm(void)
+{
+ do_test_bad_logic_imm(0, AARCH64_INSN_VARIANT_64BIT);
+ do_test_bad_logic_imm(0x1234, AARCH64_INSN_VARIANT_64BIT);
+ do_test_bad_logic_imm(0xffffffffffffffff, AARCH64_INSN_VARIANT_64BIT);
+ do_test_bad_logic_imm((1ULL<<32), AARCH64_INSN_VARIANT_32BIT);
+}
+
+static void __init selftest(void)
+{
+ test_logic_imm();
+ test_bad_logic_imm();
+}
+
+KSTM_MODULE_LOADERS(test_insn);
+MODULE_AUTHOR("James Morse <james.morse@arm.com>");
+MODULE_LICENSE("GPL");
@@ -4,7 +4,7 @@
ARCH ?= $(shell uname -m 2>/dev/null || echo not)
ifneq (,$(filter $(ARCH),aarch64 arm64))
-ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi
+ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi lib
else
ARM64_SUBTARGETS :=
endif
new file mode 100644
@@ -0,0 +1,6 @@
+# Copyright (C) 2022 ARM Limited
+# Makefile for arm64/lib/ function selftests
+
+TEST_PROGS := insn.sh
+
+include ../../lib.mk
new file mode 100644
@@ -0,0 +1 @@
+CONFIG_TEST_INSN=m
new file mode 100755
@@ -0,0 +1,5 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Tests the aarch64 instruction generation infrastructure using test_insn
+# kernel module.
+$(dirname $0)/../../kselftest/module.sh "insn" test_insn
Aarch64 has instructions to generate reasonably complicated 32 or 64 bit masks from only 13 bits of information. aarch64_insn_gen_logical_immediate() has to created the immediate encoding by spotting the patterns in the 32 or 64 bit immediate. Despite attempts to validate or model this code, or use it as-is outside the kernel tree, bugs still exist. Add a self test module that tests this code in place against a golden set of values. Signed-off-by: James Morse <james.morse@arm.com> --- arch/arm64/Kconfig.debug | 3 + arch/arm64/Makefile | 2 + arch/arm64/lib/Makefile | 2 + arch/arm64/lib/insn.c | 3 + arch/arm64/lib/test_insn.c | 90 ++++++++++++++++++++++ tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/lib/Makefile | 6 ++ tools/testing/selftests/arm64/lib/config | 1 + tools/testing/selftests/arm64/lib/insn.sh | 5 ++ 9 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/lib/test_insn.c create mode 100644 tools/testing/selftests/arm64/lib/Makefile create mode 100644 tools/testing/selftests/arm64/lib/config create mode 100755 tools/testing/selftests/arm64/lib/insn.sh