diff options
author | Rusty Russell <rusty.russell@linaro.org> | 2012-08-14 15:40:52 +0930 |
---|---|---|
committer | Rusty Russell <rusty.russell@linaro.org> | 2012-08-14 15:40:52 +0930 |
commit | 9660a25138de2a7973641e4494eaa53487b6ba38 (patch) | |
tree | 53e6401a35a2a5ae5c724a6a530dedbabcb2fb6f | |
parent | 3bab6702e517e9bc2ca6aca2a0e159f3be742806 (diff) | |
download | linux-kvm-arm-selftest.tar.gz |
tools/testing/selftests/kvm/arm: cp15 test.selftest
This currently tests our CP15 regs are what we expect (unfinished).
I need to also check save/restore of CP15 regs.
-rw-r--r-- | tools/testing/selftests/kvm/arm/Makefile | 2 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/arm/cp15-guest.c | 411 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/arm/cp15-host.c | 8 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/arm/guest.h | 4 |
4 files changed, 424 insertions, 1 deletions
diff --git a/tools/testing/selftests/kvm/arm/Makefile b/tools/testing/selftests/kvm/arm/Makefile index 95af31bf78931..812a19005d4fc 100644 --- a/tools/testing/selftests/kvm/arm/Makefile +++ b/tools/testing/selftests/kvm/arm/Makefile @@ -24,7 +24,7 @@ LDFLAGS = -static CFLAGS = -Wall -I../../../../../include -I../../../../../arch/arm/include -D__EXPORTED_HEADERS__ -marm -TESTS=mmio vfp cond +TESTS=mmio vfp cond cp15 GUESTS=$(TESTS:%=%-guest) HOST_DRIVERS=$(TESTS:%=%-host.o) diff --git a/tools/testing/selftests/kvm/arm/cp15-guest.c b/tools/testing/selftests/kvm/arm/cp15-guest.c new file mode 100644 index 0000000000000..06525e1e830d7 --- /dev/null +++ b/tools/testing/selftests/kvm/arm/cp15-guest.c @@ -0,0 +1,411 @@ +#include "guest.h" +#include <stdarg.h> + +/* Don't test things we know fail. */ +#define XFAIL 1 + +/* Turning this on tests UNPREDICTABLE instructions, too. */ +//#define TEST_UNPREDICTABLE 1 + +struct test32 { + const char *name; + unsigned int crn, opc1, crm, opc2; + void (*test)(const struct test32 *); + u32 val; + u32 mask; +}; + +extern u32 mcr_insn, mrc_insn, mcrr_insn, mrrc_insn; + +/* Only understands %u, %s */ +static void printf(const char *fmt, ...) +{ + va_list ap; + unsigned val; + char intbuf[20], *p; + + va_start(ap, fmt); + while (*fmt) { + if (*fmt != '%') { + putc(*(fmt++)); + continue; + } + fmt++; + switch (*fmt) { + case 'u': + fmt++; + val = va_arg(ap, int); + if (!val) { + putc('0'); + continue; + } + p = &intbuf[19]; + *(p--) = '\0'; + while (val) { + *(p--) = (val % 10) + '0'; + val /= 10; + } + print(p+1); + break; + case 's': + fmt++; + p = va_arg(ap, char *); + print(p); + break; + default: + putc('%'); + continue; + } + } + va_end(ap); +} + +/* Alter mcr or mrc instruction */ +static void alter_insn32(u32 *insn, + unsigned int opc1, + unsigned int crn, + unsigned int crm, + unsigned int opc2) +{ + /* This actually works in both ARM and Thumb mode. */ + *insn &= 0xFF10FF10; + *insn |= (opc1 << 21) | (crn << 16) | (opc2 << 5) | crm; + /* ICIALLU */ + asm("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); +} + +/* Alter mcrr or mrrc instruction */ +static void alter_insn64(u32 *insn, + unsigned int opc1, + unsigned int crm) +{ + /* This actually works in both ARM and Thumb mode. */ + *insn &= 0xFFFFFF00; + *insn |= (opc1 << 4) | crm; + /* ICIALLU */ + asm("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); +} + +static bool __attribute__((noinline)) cp15_write(unsigned int opc1, + unsigned int crn, + unsigned int crm, + unsigned int opc2, + u32 val) +{ + alter_insn32(&mcr_insn, opc1, crn, crm, opc2); + + skip_undef++; + undef_count = 0; + asm volatile(".globl mcr_insn\n" + "mcr_insn:\n" + " mcr p15, 0, %0, c0, c0, 0" : : "r"(val) : "memory"); + skip_undef--; + + /* This is incremented if we fault. */ + return undef_count == 0; +} + +static bool __attribute__((noinline)) cp15_read(unsigned int opc1, + unsigned int crn, + unsigned int crm, + unsigned int opc2, + u32 *val) +{ + alter_insn32(&mrc_insn, opc1, crn, crm, opc2); + *val = 0xdeadbeef; + + skip_undef++; + undef_count = 0; + asm volatile(".globl mrc_insn\n" + "mrc_insn:\n" + " mrc p15, 0, %0, c0, c0, 0" : "=r"(*val) : : "memory"); + skip_undef--; + + /* This is incremented if we fault. */ + return undef_count == 0; +} + +static const char *reg_name(const struct test32 *t) +{ + if (!t->name) + return "Unnamed reg"; + return t->name; +} + +static void read_expect(const struct test32 *test, u32 expect, u32 mask) +{ + u32 val; + if (!cp15_read(test->opc1, test->crn, test->crm, test->opc2, &val)) { + printf("Unexpected fault" + " on mrc p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test)); + fail(); + } else if ((val & mask) != expect) { + printf("Unexpected mrc p15, %u, <Rt>, c%u, c%u, %u (%s):" + " got %u expected %u\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test), val, expect); + fail(); + } else + ok(); +} + +static void write_ignored(const struct test32 *test) +{ + u32 val = 0xabadc0ff; + if (!cp15_write(test->opc1, test->crn, test->crm, test->opc2, val)) { + printf("Unexpected fault" + " on mcr p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test)); + fail(); + } else + ok(); +} + +static void read_unpredictable(const struct test32 *test) +{ +#ifdef TEST_UNPREDICTABLE + u32 val; + if (cp15_read(test->opc1, test->crn, test->crm, test->opc2, &val)) { + printf("Expected fault on" + " mrc p15, %u, <Rt>, c%u, c%u, %u (%s): got %u\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test), val); + fail(); + } else + ok(); +#endif +} + +static void write_unpredictable(const struct test32 *test) +{ +#ifdef TEST_UNPREDICTABLE + if (cp15_write(test->opc1, test->crn, test->crm, test->opc2, 0xabadc0ff)) { + printf("Expected fault on mcr p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, reg_name(test)); + fail(); + } else + ok(); +#endif +} + +static void ro_val(const struct test32 *test) +{ + read_expect(test, test->val, test->mask); + write_unpredictable(test); +} + +/* IminLine could be 3 or 4 (lowest bits). */ +static void test_ctr(const struct test32 *test) +{ + u32 val; + if (cp15_read(test->opc1, test->crn, test->crm, test->opc2, &val) + && ((val & 0xF) == 3)) + read_expect(test, test->val | 0x3, test->mask); + else + read_expect(test, test->val | 0x4, test->mask); + write_unpredictable(test); +} + +static void ro_wi(const struct test32 *test) +{ + read_expect(test, test->val, test->mask); + write_ignored(test); +} + +static void raz_wi(const struct test32 *test) +{ + read_expect(test, 0, 0xFFFFFFFF); + write_ignored(test); +} + +static u32 read_unknown(const struct test32 *test) +{ + u32 val; + if (!cp15_read(test->opc1, test->crn, test->crm, test->opc2, &val)) { + printf("Unexpected fault on mrc p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, reg_name(test)); + fail(); + } else + ok(); + return val; +} + +static void unpredictable(const struct test32 *test) +{ + read_unpredictable(test); + write_unpredictable(test); +} + +/* FIXME: This is OK, for now, but if a guest is migrated to a future CPU, + * it might expect this to be constant. */ +static void ro_as_host(const struct test32 *test) +{ + read_unknown(test); + write_unpredictable(test); +} + +/* This usually means it's tied to another register, and we test there. */ +static void ignore(const struct test32 *test) +{ +} + +static void test_csselr_and_ccsidr(const struct test32 *test) +{ + /* FIXME */ + ignore(test); +} + +/* TCMTR should be WI, but it faults (at least under fm, try real hw?) */ +static void raz_wi_tcmtr(const struct test32 *test) +{ + read_expect(test, 0, 0xFFFFFFFF); +#ifndef XFAIL + write_ignored(test); +#endif +} + +static void test_sctlr(const struct test32 *test) +{ + /* We assume SBZ means 0. Complain if it's wrong. */ + read_expect(test, test->val, test->mask); + + /* FIXME: Try frobbing bits (though we write SCTLR.V in guest-base.S) */ +} + +static void test_ttbr(const struct test32 *test) +{ + /* Initial value is unknown, but values we write there should stick. */ + if (!cp15_write(test->opc1, test->crn, test->crm, test->opc2, + 0xFFFE0000)) { + printf("Unexpected fault" + " on mcr p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test)); + fail(); + return; + } + read_expect(test, 0xFFFE0000, 0xFFFFFFFF); +} + +static void test_ttbcr(const struct test32 *test) +{ + read_expect(test, 0, 0xFFFFFFFF); +} + +static void test_dacr(const struct test32 *test) +{ + u32 val; + + val = read_unknown(test); + /* Frob top two domains. */ + val ^= 0x90000000; + if (!cp15_write(test->opc1, test->crn, test->crm, test->opc2, val)) { + printf("Unexpected fault" + " on mrc p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test)); + fail(); + } +} + +static void test_dfsr_ifsr(const struct test32 *test) +{ + u32 val; + + read_unknown(test); + val = 0x00000005; /* A valid value for either DFSR or IFSR. */ + if (!cp15_write(test->opc1, test->crn, test->crm, test->opc2, val)) { + printf("Unexpected fault" + " on mrc p15, %u, <Rt>, c%u, c%u, %u (%s)\n", + test->opc1, test->crn, test->crm, test->opc2, + reg_name(test)); + fail(); + } else + read_expect(test, val, 0xFFFFFFFF); +} + +/* Everything not in this table is undefined. */ +static struct test32 test32_table[] = { + { "MIDR", 0, 0, 0, 0, ro_val, 0x412FC0F0, 0xFFFFFFFF }, + { "CTR", 0, 0, 0, 1, test_ctr, 0x8444C000, 0xFFFFFFFF }, + { "TCMTR", 0, 0, 0, 2, raz_wi_tcmtr }, + { "TLBTR", 0, 0, 0, 3, ro_val, 0x00000000, 0xFFFFFFFF }, + { "MIDR alias", 0, 0, 0, 4, ro_val, 0x412FC0F0, 0xFFFFFFFF }, + { "MPIDR", 0, 0, 0, 5, ro_val, 0x80000000, 0xFFFFFFFF }, + { "REVIDR", 0, 0, 0, 6, ro_as_host }, + { "MIDR alias", 0, 0, 0, 7, ro_val, 0x412FC0F0, 0xFFFFFFFF }, + { "ID_PFR0", 0, 0, 1, 0, ro_as_host }, + { "ID_PFR1", 0, 0, 1, 1, ro_as_host }, + { "ID_DFR0", 0, 0, 1, 2, ro_as_host }, + { "ID_AFR0", 0, 0, 1, 3, ro_as_host }, + { "ID_MMFR0", 0, 0, 1, 4, ro_as_host }, + { "ID_MMFR1", 0, 0, 1, 5, ro_as_host }, + { "ID_MMFR2", 0, 0, 1, 6, ro_as_host }, + { "ID_MMFR3", 0, 0, 1, 7, ro_as_host }, + { "ID_ISAR0", 0, 0, 2, 0, ro_as_host }, + { "ID_ISAR1", 0, 0, 2, 1, ro_as_host }, + { "ID_ISAR2", 0, 0, 2, 2, ro_as_host }, + { "ID_ISAR3", 0, 0, 2, 3, ro_as_host }, + { "ID_ISAR4", 0, 0, 2, 4, ro_as_host }, + { "ID_ISAR5", 0, 0, 2, 5, ro_as_host }, + { "CCSIDR", 0, 1, 0, 0, ignore /* see CSSELR */}, + { "CLIDR", 0, 1, 0, 1, ro_as_host }, + { "AIDR", 0, 1, 0, 7, ro_as_host }, + { "CSSELR", 0, 2, 0, 0, test_csselr_and_ccsidr }, + + { "SCTLR", 1, 0, 0, 0, test_sctlr, 0x00C50078, 0xBDFFDFFF }, + { "ACTLR", 1, 0, 0, 1, ro_wi, 0, 0xFFFFFFFF }, + { "CPACR", 1, 0, 0, 2, ro_wi, 0, 0xFFFFFFFF }, /* Assume VFP+NEON */ + + { "TTBR0", 2, 0, 0, 0, test_ttbr }, + { "TTBR1", 2, 0, 0, 1, test_ttbr }, + { "TTBCR", 2, 0, 0, 2, test_ttbcr }, + + { "DACR", 3, 0, 0, 0, test_dacr }, + + { "DFSR", 5, 0, 0, 0, test_dfsr_ifsr }, + { "IFSR", 5, 0, 0, 1, test_dfsr_ifsr }, + +}; + +int test(void) +{ + const struct test32 *i, *end; + unsigned int opc1, crn, crm, opc2; + + i = test32_table; + end = test32_table + sizeof(test32_table)/sizeof(test32_table[0]); + + for (crn = 0; crn < 16; crn++) { + for (opc1 = 0; opc1 < 8; opc1++) { + for (crm = 0; crm < 16; crm++) { + for (opc2 = 0; opc2 < 8; opc2++) { + if (i < end + && i->opc1 == opc1 + && i->crn == crn + && i->crm == crm + && i->opc2 == opc2) { + i->test(i); + i++; + } else { + struct test32 t; + t.opc1 = opc1; + t.crn = crn; + t.crm = crm; + t.opc2 = opc2; + unpredictable(&t); + } + } + } + } + } + + if (i != end) { + printf("What? Still got %u remaining!\n", end - i); + return 2; + } + return 0; +} diff --git a/tools/testing/selftests/kvm/arm/cp15-host.c b/tools/testing/selftests/kvm/arm/cp15-host.c new file mode 100644 index 0000000000000..f0f21c74c61b3 --- /dev/null +++ b/tools/testing/selftests/kvm/arm/cp15-host.c @@ -0,0 +1,8 @@ +#include <stdbool.h> +#include <string.h> +#include <linux/kvm.h> + +#include "guest-driver.h" + +/* We don't have any special mmio addresses for cp15 testing. */ +GUEST_TEST(cp15, NULL); diff --git a/tools/testing/selftests/kvm/arm/guest.h b/tools/testing/selftests/kvm/arm/guest.h index 1d9d8bd2eca4d..e8574404ebe31 100644 --- a/tools/testing/selftests/kvm/arm/guest.h +++ b/tools/testing/selftests/kvm/arm/guest.h @@ -1,5 +1,7 @@ #ifndef GUEST_H #define GUEST_H +#include <stdint.h> +#include <stdbool.h> void ok(void); void fail(void); @@ -26,6 +28,8 @@ static inline void print(const char *p) ok(); \ } while(0) +typedef uint32_t u32; + /* Each guest needs to write this. */ int test(void); #endif /* GUEST_H */ |