tools/kvm/arm: cp15 test.

We catch undef faults, so we can test that we get them where we expect.

diff --git a/tools/kvm/arm/Makefile b/tools/kvm/arm/Makefile
index 016b25a..175773b 100644
--- a/tools/kvm/arm/Makefile
+++ b/tools/kvm/arm/Makefile
@@ -24,7 +24,7 @@
 
 CFLAGS = -Wall -I../../../include -I../../../arch/arm/include -D__EXPORTED_HEADERS__ -marm
 
-TESTS=mmio vfp
+TESTS=mmio vfp cp15
 
 GUESTS=$(TESTS:%=%-guest)
 HOST_DRIVERS=$(TESTS:%=%-host.o)
diff --git a/tools/kvm/arm/cp15-guest.c b/tools/kvm/arm/cp15-guest.c
new file mode 100644
index 0000000..a1d8149
--- /dev/null
+++ b/tools/kvm/arm/cp15-guest.c
@@ -0,0 +1,83 @@
+#include "guest.h"
+
+extern u32 mcr_insn, mrc_insn, mcrr_insn, mrrc_insn;
+
+/* 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 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;
+}
+
+static bool 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;
+}
+
+int test(void)
+{
+	bool faulted;
+
+	print("Perform an mrc\n");
+	faulted = cp15_write(0, 0, 0, 0, 100);
+
+	if (faulted)
+		print("IT FAULTED\n");
+	else
+		print("IT DID NOT FAULT\n");
+
+	return 0;
+}
diff --git a/tools/kvm/arm/cp15-host.c b/tools/kvm/arm/cp15-host.c
new file mode 100644
index 0000000..f0f21c7
--- /dev/null
+++ b/tools/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/kvm/arm/guest.h b/tools/kvm/arm/guest.h
index 1d9d8bd..e857440 100644
--- a/tools/kvm/arm/guest.h
+++ b/tools/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 @@
 			ok();						\
 	} while(0)
 
+typedef uint32_t u32;
+
 /* Each guest needs to write this. */
 int test(void);
 #endif /* GUEST_H */