aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-02-10 11:36:27 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-02-10 11:36:27 +0000
commit828f1765580e76573eb516be27204370f11f593d (patch)
tree455a9bf33ffb2dbc5f52af5c1e38a81553072435
parent334dd7f18bd4be49af6d2bc499125f71a164a397 (diff)
test7: Complete rewrite to cover all exception return checks
Rewrite test7 and expand it to cover all the architectural checks on exception return integrity. The original test7 test (jump to magic exception-return address from thread mode) is retained, but for the moment it is ifdeffed out, because QEMU doesn't implement it correctly yet. The test is also updated to use tapit output and added to the set run by runtests.sh. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--Makefile2
-rw-r--r--README2
-rwxr-xr-xruntests.sh1
-rw-r--r--test7.c239
4 files changed, 228 insertions, 16 deletions
diff --git a/Makefile b/Makefile
index 37623fe..2b2f51b 100644
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,7 @@ test3-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o testme.o test3.o
test4-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o testme.o test4.o
test5-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o testme.o test5.o
test6-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o test6.o
-test7-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o test7.o
+test7-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o testme.o test7.o
test8-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m.o test8.o inst_skip.o
test9-kern.elf: cortexm.ld common.ld setup.o armv7m.o init-m-test9.o testme.o test9.o
test10-kern.elf:cortexm.ld common.ld setup.o armv7m.o init-m.o testme.o test10.o
diff --git a/README b/README
index c294872..e3ee292 100644
--- a/README
+++ b/README
@@ -28,11 +28,11 @@ test5-kern.bin: thread mode unprivileged and process stack handling
test13-kern.bin: UsageFaults
test3-kern.bin: exception masking and escalation
test12-kern.bin: invalid exception table entry (not Thumb mode)
+test7-kern.bin: exception return integrity checks
Tests which aren't currently run:
test6-kern.bin: unrecoverable exception
-test7-kern.bin: magic exception return from thread mode
test8-kern.bin: test MPU
test11-kern.bin: various access fault handling and recovery
diff --git a/runtests.sh b/runtests.sh
index f04002f..eea869f 100755
--- a/runtests.sh
+++ b/runtests.sh
@@ -59,4 +59,5 @@ dotest test5-kern.bin
dotest test13-kern.bin
dotest test3-kern.bin
dotest test12-kern.bin
+dotest test7-kern.bin
exit $RET
diff --git a/test7.c b/test7.c
index f03bbba..fdbd46c 100644
--- a/test7.c
+++ b/test7.c
@@ -1,26 +1,237 @@
-/* Test exception return from thread mode
+/* Test exception return integrity checks.
*/
#include "armv7m.h"
+#include "testme.h"
-static
-void hard(void)
+static unsigned testseq;
+
+static uint32_t saved_real_lr;
+
+#define SEQ() __atomic_add_fetch(&testseq, 1, __ATOMIC_RELAXED)
+
+#define CHECK_SEQ(N) testEqI(N, SEQ(), "SEQ " #N)
+
+static inline void test_equal(const char *m, uint32_t expect, uint32_t actual)
+{
+ testEqI(expect, actual, "%s", m);
+}
+
+static void hard(void)
+{
+ testDiag("Unexpected HardFault");
+ abort();
+}
+
+/* Assembly language function body which wraps a call to the named
+ * function() and uses the return value as the LR to use for return
+ * from exception. We need this so we can be sure we've not got stray
+ * extra junk on the stack when we attempt to do a return with a
+ * particular magic LR value.
+ * We also pass the function a pointer to the stack frame so it can
+ * manipulate it if necessary.
+ */
+#define MAKE_WRAPPER(FN) \
+ extern void FN##_wrap(void); \
+ __asm__( \
+ ".thumb_func\n" \
+ #FN "_wrap:\n" \
+ "mov r0, lr\n" \
+ "mov r1, sp\n" \
+ "blx " #FN "\n" \
+ "mov lr, r0\n" \
+ "bx lr\n")
+
+uint32_t usage(uint32_t old_lr, uint32_t *sframe)
+{
+ int test = SEQ();
+ uint32_t cfsr = in32(SCB(0xd28));
+ out32(SCB(0xd28), 0xff00); /* W1C */
+
+ switch (test) {
+ case 2:
+ testDiag("In UsageFault handler, checking we got INVPC fault");
+ test_equal("CFSR", 0x040000, cfsr);
+ break;
+ case 5:
+ testDiag("In UsageFault handler, checking we got INVPC fault");
+ test_equal("CFSR", 0x040000, cfsr);
+ testDiag("Ignoring bogus LR %x, doing return to handler", old_lr);
+ return 0xfffffff1;
+ break;
+ case 9:
+ testDiag("In UsageFault handler, checking we got INVPC fault");
+ test_equal("CFSR", 0x040000, cfsr);
+ testDiag("Undoing the change to xPSR");
+ sframe[7] &= ~0x1ff;
+ break;
+ case 0xd:
+ testDiag("In UsageFault handler, checking we got INVPC fault");
+ test_equal("CFSR", 0x040000, cfsr);
+ testDiag("Undoing the change to xPSR");
+ sframe[7] |= 0x10; /* IRQ0 active */
+ break;
+ case 0x11:
+ testDiag("In UsageFault handler, checking we got INVPC fault");
+ test_equal("CFSR", 0x040000, cfsr);
+ testDiag("Ignoring bogus LR %x, doing return to thread", old_lr);
+ return 0xfffffff9;
+ break;
+ default:
+ testFail("Fail: unexpected UsageFault (seq %x)", test);
+ abort();
+ }
+ return old_lr;
+}
+
+uint32_t svc(uint32_t old_lr, uint32_t *sframe)
{
- puts("In HardFault (as expected)\n");
- abort();
+ int test = SEQ();
+
+ switch (test) {
+ case 1:
+ {
+ /* Mark SVC as not active via the SHCSR */
+ uint32_t old_shcsr = in32(SCB(0xd24));
+ testDiag("In SVC handler, clearing SHCSR.SVCALLACT");
+ out32(SCB(0xd24), old_shcsr & ~0x80);
+ break;
+ }
+ case 4:
+ testDiag("In SVC handler, attempting return to thread mode");
+ return 0xfffffff9;
+ case 8:
+ testDiag("In SVC handler, writing non-0 to xPSR.exception in sframe");
+ sframe[7] |= 0x3;
+ testDiag("New xPSR %x", sframe[7]);
+ break;
+ case 0xc:
+ testDiag("In SVC handler, writing 0 to xPSR.exception in sframe");
+ testDiag("old xPSR %x", sframe[7]);
+ sframe[7] &= ~0x1ff;
+ break;
+ case 0x10:
+ testDiag("In SVC handler, attempting return with reserved value");
+ return 0xfffffff0;
+ case 0x13:
+ testDiag("In SVC handler (direct call from main with LR %x)", old_lr);
+ saved_real_lr = old_lr;
+ return 0xfffffff9;
+ default:
+ testFail("Fail: unexpected SVC (seq %x)", test);
+ abort();
+ }
+ return old_lr;
+}
+
+static void irq0(void)
+{
+ unsigned test = SEQ();
+
+ switch (test) {
+ case 3:
+ testDiag("In IRQ0 handler, triggering SVC");
+ SVC(42);
+ CHECK_SEQ(6);
+ testDiag("Back in IRQ0 handler");
+ break;
+ case 0xb:
+ testDiag("In IRQ0 handler, triggering SVC");
+ SVC(42);
+ CHECK_SEQ(0xe);
+ testDiag("Back in IRQ0 handler");
+ break;
+ default:
+ testFail("Fail: unexpected IRQ0 (seq %x)", test);
+ abort();
+ }
}
-static
-void func(void)
+uint32_t mem(uint32_t old_lr, uint32_t *sframe)
{
- uint32_t temp = 0xfffffff9; /* return to thread mode, main stack */
- __asm__ volatile ("mov lr,%0; bx lr" ::"r"(temp):);
- return;
+ unsigned test = SEQ();
+
+ switch (test) {
+ case 0x14:
+ testDiag("In MemManage handler");
+ /* Fix up the return address in the stack frame (remembering
+ * to clear its low bit since the stack frame doesn't want
+ * an interworking address)
+ */
+ sframe[6] = saved_real_lr & ~1;
+ break;
+ default:
+ testFail("Fail: unexpected MemManage (seq %x)", test);
+ abort();
+ }
+ return old_lr;
}
+MAKE_WRAPPER(svc);
+MAKE_WRAPPER(usage);
+MAKE_WRAPPER(mem);
+
void main(void)
{
- run_table.hard = &hard;
- puts("Starting (expect HardFault)\n");
- func();
- puts("oops, I shouldn't be here\n");
+ run_table.hard = hard;
+ run_table.mem = mem_wrap;
+ run_table.svc = svc_wrap;
+ run_table.usage = usage_wrap;
+ run_table.irq[0] = irq0;
+
+ testInit(12);
+
+ out32(SCB(0xd24), 0x70000); /* Enable Bus, Mem, and Usage Faults */
+ out32(SCB(0x100), 1); /* Enable IRQ0 */
+ out32(SCB(0x400), PRIO(2,0)); /* Set IRQ0 to priority 2 (below SVC) */
+
+ testDiag("Test: return from exception which is not active");
+ SVC(42);
+ testDiag("Returned to main");
+
+ testDiag("Test: return to thread mode from nested exception");
+ out32(SCB(0x200), 1); /* pend IRQ0 */
+ testDiag("Returned to main");
+ CHECK_SEQ(7);
+
+ testDiag("Test: return to thread mode with non-zero IPSR Exception field");
+ SVC(42);
+ testDiag("Returned to main");
+ CHECK_SEQ(10);
+
+ testDiag("Test: return to handler mode with zero IPSR exception field");
+ out32(SCB(0x200), 1); /* pend IRQ0 */
+ testDiag("Returned to main");
+ CHECK_SEQ(0xf);
+
+ testDiag("Test: return from exception with reserved EXC_RETURN value");
+ SVC(42);
+ testDiag("Returned to main");
+ CHECK_SEQ(0x12);
+
+#if 0
+ /* Attempt a jump to the magic address for an exception return.
+ * This is not supposed to have magic behaviour in thread mode,
+ * and on real hardware the result is an attempt to execute at
+ * address 0xfffffff9 which is always NX (even if no MPU present)
+ * and thus an IACCVIOL MemManage fault (which may escalate to
+ * HardFault if MemManage is disabled in the SHCSR).
+ * QEMU doesn't currently do this -- you will get the "tried to
+ * execute outside RAM or ROM codepath" because we don't enforce
+ * that NX handling.
+ * The MemManage fault handler code path has been tested with a
+ * hacked QEMU so it should work correctly.
+ */
+ testDiag("Attempt exception return when in thread mode");
+ svc_wrap();
+ testDiag("Returned to main");
+#else
+ /* Write the SEQs that we would otherwise take in the SVC and MemManage
+ * handlers.
+ */
+ SEQ();
+ SEQ();
+#endif
+ CHECK_SEQ(0x15);
+
+ testDiag("Done.");
}