aboutsummaryrefslogtreecommitdiff
path: root/tests/tcg/aarch64
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tcg/aarch64')
-rw-r--r--tests/tcg/aarch64/Makefile.target32
-rw-r--r--tests/tcg/aarch64/gdbstub/test-sve-ioctl.py85
-rw-r--r--tests/tcg/aarch64/gdbstub/test-sve.py84
-rw-r--r--tests/tcg/aarch64/sve-ioctls.c70
-rw-r--r--tests/tcg/aarch64/sysregs.c172
5 files changed, 443 insertions, 0 deletions
diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target
index 8ed477d0d5..d99b2a9ece 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -42,4 +42,36 @@ run-semiconsole: semiconsole
run-plugin-semiconsole-with-%:
$(call skip-test, $<, "MANUAL ONLY")
+ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_SVE),)
+# System Registers Tests
+AARCH64_TESTS += sysregs
+sysregs: CFLAGS+=-march=armv8.1-a+sve
+
+# SVE ioctl test
+AARCH64_TESTS += sve-ioctls
+sve-ioctls: CFLAGS+=-march=armv8.1-a+sve
+
+ifneq ($(HAVE_GDB_BIN),)
+GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
+
+AARCH64_TESTS += gdbstub-sysregs gdbstub-sve-ioctls
+
+.PHONY: gdbstub-sysregs gdbstub-sve-ioctls
+run-gdbstub-sysregs: sysregs
+ $(call run-test, $@, $(GDB_SCRIPT) \
+ --gdb $(HAVE_GDB_BIN) \
+ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+ --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve.py, \
+ "basic gdbstub SVE support")
+
+run-gdbstub-sve-ioctls: sve-ioctls
+ $(call run-test, $@, $(GDB_SCRIPT) \
+ --gdb $(HAVE_GDB_BIN) \
+ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+ --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \
+ "basic gdbstub SVE ZLEN support")
+endif
+
+endif
+
TESTS += $(AARCH64_TESTS)
diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py
new file mode 100644
index 0000000000..984fbeb277
--- /dev/null
+++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py
@@ -0,0 +1,85 @@
+from __future__ import print_function
+#
+# Test the SVE ZReg reports the right amount of data. It uses the
+# sve-ioctl test and examines the register data each time the
+# __sve_ld_done breakpoint is hit.
+#
+# This is launched via tests/guest-debug/run-test.py
+#
+
+import gdb
+import sys
+
+initial_vlen = 0
+failcount = 0
+
+def report(cond, msg):
+ "Report success/fail of test"
+ if cond:
+ print ("PASS: %s" % (msg))
+ else:
+ print ("FAIL: %s" % (msg))
+ global failcount
+ failcount += 1
+
+class TestBreakpoint(gdb.Breakpoint):
+ def __init__(self, sym_name="__sve_ld_done"):
+ super(TestBreakpoint, self).__init__(sym_name)
+ # self.sym, ok = gdb.lookup_symbol(sym_name)
+
+ def stop(self):
+ val_i = gdb.parse_and_eval('i')
+ global initial_vlen
+ try:
+ for i in range(0, int(val_i)):
+ val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i)
+ report(int(val_z) == i, "z0.b.u[%d] == %d" % (i, i))
+ for i in range(i + 1, initial_vlen):
+ val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i)
+ report(int(val_z) == 0, "z0.b.u[%d] == 0" % (i))
+ except gdb.error:
+ report(False, "checking zregs (out of range)")
+
+
+def run_test():
+ "Run through the tests one by one"
+
+ print ("Setup breakpoint")
+ bp = TestBreakpoint()
+
+ global initial_vlen
+ vg = gdb.parse_and_eval("$vg")
+ initial_vlen = int(vg) * 8
+
+ gdb.execute("c")
+
+#
+# This runs as the script it sourced (via -x, via run-test.py)
+#
+try:
+ inferior = gdb.selected_inferior()
+ if inferior.was_attached == False:
+ print("SKIPPING (failed to attach)", file=sys.stderr)
+ exit(0)
+ arch = inferior.architecture()
+ report(arch.name() == "aarch64", "connected to aarch64")
+except (gdb.error, AttributeError):
+ print("SKIPPING (not connected)", file=sys.stderr)
+ exit(0)
+
+try:
+ # These are not very useful in scripts
+ gdb.execute("set pagination off")
+ gdb.execute("set confirm off")
+
+ # Run the actual tests
+ run_test()
+except:
+ print ("GDB Exception: %s" % (sys.exc_info()[0]))
+ failcount += 1
+ import code
+ code.InteractiveConsole(locals=globals()).interact()
+ raise
+
+print("All tests complete: %d failures" % failcount)
+exit(failcount)
diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py
new file mode 100644
index 0000000000..dbe7f2aa93
--- /dev/null
+++ b/tests/tcg/aarch64/gdbstub/test-sve.py
@@ -0,0 +1,84 @@
+from __future__ import print_function
+#
+# Test the SVE registers are visable and changeable via gdbstub
+#
+# This is launched via tests/guest-debug/run-test.py
+#
+
+import gdb
+import sys
+
+MAGIC = 0xDEADBEEF
+
+failcount = 0
+
+def report(cond, msg):
+ "Report success/fail of test"
+ if cond:
+ print ("PASS: %s" % (msg))
+ else:
+ print ("FAIL: %s" % (msg))
+ global failcount
+ failcount += 1
+
+def run_test():
+ "Run through the tests one by one"
+
+ gdb.execute("info registers")
+ report(True, "info registers")
+
+ gdb.execute("info registers vector")
+ report(True, "info registers vector")
+
+ # Now all the zregs
+ frame = gdb.selected_frame()
+ for i in range(0, 32):
+ rname = "z%d" % (i)
+ zreg = frame.read_register(rname)
+ report(True, "Reading %s" % rname)
+ for j in range(0, 4):
+ cmd = "set $%s.q.u[%d] = 0x%x" % (rname, j, MAGIC)
+ gdb.execute(cmd)
+ report(True, "%s" % cmd)
+ for j in range(0, 4):
+ reg = "$%s.q.u[%d]" % (rname, j)
+ v = gdb.parse_and_eval(reg)
+ report(str(v.type) == "uint128_t", "size of %s" % (reg))
+ for j in range(0, 8):
+ cmd = "set $%s.d.u[%d] = 0x%x" % (rname, j, MAGIC)
+ gdb.execute(cmd)
+ report(True, "%s" % cmd)
+ for j in range(0, 8):
+ reg = "$%s.d.u[%d]" % (rname, j)
+ v = gdb.parse_and_eval(reg)
+ report(str(v.type) == "uint64_t", "size of %s" % (reg))
+ report(int(v) == MAGIC, "%s is 0x%x" % (reg, MAGIC))
+
+#
+# This runs as the script it sourced (via -x, via run-test.py)
+#
+try:
+ inferior = gdb.selected_inferior()
+ if inferior.was_attached == False:
+ print("SKIPPING (failed to attach)", file=sys.stderr)
+ exit(0)
+ arch = inferior.architecture()
+ report(arch.name() == "aarch64", "connected to aarch64")
+except (gdb.error, AttributeError):
+ print("SKIPPING (not connected)", file=sys.stderr)
+ exit(0)
+
+try:
+ # These are not very useful in scripts
+ gdb.execute("set pagination off")
+ gdb.execute("set confirm off")
+
+ # Run the actual tests
+ run_test()
+except:
+ print ("GDB Exception: %s" % (sys.exc_info()[0]))
+ failcount += 1
+
+print("All tests complete: %d failures" % failcount)
+
+exit(failcount)
diff --git a/tests/tcg/aarch64/sve-ioctls.c b/tests/tcg/aarch64/sve-ioctls.c
new file mode 100644
index 0000000000..9544dffa0e
--- /dev/null
+++ b/tests/tcg/aarch64/sve-ioctls.c
@@ -0,0 +1,70 @@
+/*
+ * SVE ioctls tests
+ *
+ * Test the SVE width setting ioctls work and provide a base for
+ * testing the gdbstub.
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <sys/prctl.h>
+#include <asm/hwcap.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifndef HWCAP_CPUID
+#define HWCAP_CPUID (1 << 11)
+#endif
+
+#define SVE_MAX_QUADS (2048 / 128)
+#define BYTES_PER_QUAD (128 / 8)
+
+#define get_cpu_reg(id) ({ \
+ unsigned long __val; \
+ asm("mrs %0, "#id : "=r" (__val)); \
+ __val; \
+ })
+
+static int do_sve_ioctl_test(void)
+{
+ int i, res, init_vq;
+
+ res = prctl(PR_SVE_GET_VL, 0, 0, 0, 0);
+ if (res < 0) {
+ printf("FAILED to PR_SVE_GET_VL (%d)", res);
+ return -1;
+ }
+ init_vq = res & PR_SVE_VL_LEN_MASK;
+
+ for (i = init_vq; i > 15; i /= 2) {
+ printf("Checking PR_SVE_SET_VL=%d\n", i);
+ res = prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0);
+ if (res < 0) {
+ printf("FAILED to PR_SVE_SET_VL (%d)", res);
+ return -1;
+ }
+ asm("index z0.b, #0, #1\n"
+ ".global __sve_ld_done\n"
+ "__sve_ld_done:\n"
+ "mov z0.b, #0\n"
+ : /* no outputs kept */
+ : /* no inputs */
+ : "memory", "z0");
+ }
+ printf("PASS\n");
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ /* we also need to probe for the ioctl support */
+ if (getauxval(AT_HWCAP) & HWCAP_SVE) {
+ return do_sve_ioctl_test();
+ } else {
+ printf("SKIP: no HWCAP_SVE on this system\n");
+ return 0;
+ }
+}
diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c
new file mode 100644
index 0000000000..40cf8d2877
--- /dev/null
+++ b/tests/tcg/aarch64/sysregs.c
@@ -0,0 +1,172 @@
+/*
+ * Check emulated system register access for linux-user mode.
+ *
+ * See: https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
+ *
+ * Copyright (c) 2019 Linaro
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <asm/hwcap.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+#include <signal.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef HWCAP_CPUID
+#define HWCAP_CPUID (1 << 11)
+#endif
+
+int failed_bit_count;
+
+/* Read and print system register `id' value */
+#define get_cpu_reg(id) ({ \
+ unsigned long __val = 0xdeadbeef; \
+ asm("mrs %0, "#id : "=r" (__val)); \
+ printf("%-20s: 0x%016lx\n", #id, __val); \
+ __val; \
+ })
+
+/* As above but also check no bits outside of `mask' are set*/
+#define get_cpu_reg_check_mask(id, mask) ({ \
+ unsigned long __cval = get_cpu_reg(id); \
+ unsigned long __extra = __cval & ~mask; \
+ if (__extra) { \
+ printf("%-20s: 0x%016lx\n", " !!extra bits!!", __extra); \
+ failed_bit_count++; \
+ } \
+})
+
+/* As above but check RAZ */
+#define get_cpu_reg_check_zero(id) ({ \
+ unsigned long __val = 0xdeadbeef; \
+ asm("mrs %0, "#id : "=r" (__val)); \
+ if (__val) { \
+ printf("%-20s: 0x%016lx (not RAZ!)\n", #id, __val); \
+ failed_bit_count++; \
+ } \
+})
+
+/* Chunk up mask into 63:48, 47:32, 31:16, 15:0 to ease counting */
+#define _m(a, b, c, d) (0x ## a ## b ## c ## d ##ULL)
+
+bool should_fail;
+int should_fail_count;
+int should_not_fail_count;
+uintptr_t failed_pc[10];
+
+void sigill_handler(int signo, siginfo_t *si, void *data)
+{
+ ucontext_t *uc = (ucontext_t *)data;
+
+ if (should_fail) {
+ should_fail_count++;
+ } else {
+ uintptr_t pc = (uintptr_t) uc->uc_mcontext.pc;
+ failed_pc[should_not_fail_count++] = pc;
+ }
+ uc->uc_mcontext.pc += 4;
+}
+
+int main(void)
+{
+ struct sigaction sa;
+
+ /* Hook in a SIGILL handler */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = &sigill_handler;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(SIGILL, &sa, 0) != 0) {
+ perror("sigaction");
+ return 1;
+ }
+
+ /* Counter values have been exposed since Linux 4.12 */
+ printf("Checking Counter registers\n");
+
+ get_cpu_reg(ctr_el0);
+ get_cpu_reg(cntvct_el0);
+ get_cpu_reg(cntfrq_el0);
+
+ /* HWCAP_CPUID indicates we can read feature registers, since Linux 4.11 */
+ if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) {
+ printf("CPUID registers unavailable\n");
+ return 1;
+ } else {
+ printf("Checking CPUID registers\n");
+ }
+
+ /*
+ * Some registers only expose some bits to user-space. Anything
+ * that is IMPDEF is exported as 0 to user-space. The _mask checks
+ * assert no extra bits are set.
+ *
+ * This check is *not* comprehensive as some fields are set to
+ * minimum valid fields - for the purposes of this check allowed
+ * to have non-zero values.
+ */
+ get_cpu_reg_check_mask(id_aa64isar0_el1, _m(00ff,ffff,f0ff,fff0));
+ get_cpu_reg_check_mask(id_aa64isar1_el1, _m(0000,00f0,ffff,ffff));
+ /* TGran4 & TGran64 as pegged to -1 */
+ get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(0000,0000,ff00,0000));
+ get_cpu_reg_check_zero(id_aa64mmfr1_el1);
+ /* EL1/EL0 reported as AA64 only */
+ get_cpu_reg_check_mask(id_aa64pfr0_el1, _m(000f,000f,00ff,0011));
+ get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0000,00f0));
+ /* all hidden, DebugVer fixed to 0x6 (ARMv8 debug architecture) */
+ get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006));
+ get_cpu_reg_check_zero(id_aa64dfr1_el1);
+ get_cpu_reg_check_zero(id_aa64zfr0_el1);
+
+ get_cpu_reg_check_zero(id_aa64afr0_el1);
+ get_cpu_reg_check_zero(id_aa64afr1_el1);
+
+ get_cpu_reg_check_mask(midr_el1, _m(0000,0000,ffff,ffff));
+ /* mpidr sets bit 31, everything else hidden */
+ get_cpu_reg_check_mask(mpidr_el1, _m(0000,0000,8000,0000));
+ /* REVIDR is all IMPDEF so should be all zeros to user-space */
+ get_cpu_reg_check_zero(revidr_el1);
+
+ /*
+ * There are a block of more registers that are RAZ in the rest of
+ * the Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 space. However for
+ * brevity we don't check stuff that is currently un-allocated
+ * here. Feel free to add them ;-)
+ */
+
+ printf("Remaining registers should fail\n");
+ should_fail = true;
+
+ /* Unexposed register access causes SIGILL */
+ get_cpu_reg(id_mmfr0_el1);
+ get_cpu_reg(id_mmfr1_el1);
+ get_cpu_reg(id_mmfr2_el1);
+ get_cpu_reg(id_mmfr3_el1);
+
+ get_cpu_reg(mvfr0_el1);
+ get_cpu_reg(mvfr1_el1);
+
+ if (should_not_fail_count > 0) {
+ int i;
+ for (i = 0; i < should_not_fail_count; i++) {
+ uintptr_t pc = failed_pc[i];
+ uint32_t insn = *(uint32_t *) pc;
+ printf("insn %#x @ %#lx unexpected FAIL\n", insn, pc);
+ }
+ return 1;
+ }
+
+ if (failed_bit_count > 0) {
+ printf("Extra information leaked to user-space!\n");
+ return 1;
+ }
+
+ return should_fail_count == 6 ? 0 : 1;
+}