From 2a0c9b08d7ee0c1b0eb5b593d18f68d0c910927d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 20 Jan 2010 11:07:42 -0800 Subject: [PATCH] Cortex-M3 vector_catch testing support The "cm3-ftest.cfg" can be used to verify that OpenOCD handles certain faults correctly: - Test #1: it ignores faults that it wasn't told to catch - Test #2: if vector_catch is told to catch, it catches The "fault.c" generates ASM code to trigger faults, while the config script loads and runs pre-compiled code. This covers most, but not all, of the vector_catch options. Signed-off-by: David Brownell --- testing/examples/cortex/cm3-ftest.cfg | 143 ++++++++++++++++++++++++ testing/examples/cortex/fault.c | 152 ++++++++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 testing/examples/cortex/cm3-ftest.cfg create mode 100644 testing/examples/cortex/fault.c diff --git a/testing/examples/cortex/cm3-ftest.cfg b/testing/examples/cortex/cm3-ftest.cfg new file mode 100644 index 0000000000..2dae2493f8 --- /dev/null +++ b/testing/examples/cortex/cm3-ftest.cfg @@ -0,0 +1,143 @@ +# +# For each named Cortex-M3 vector_catch flag VECTOR ... +# bus_err state_err +# chk_err nocp_err +# mm_err reset +# +# BUT NYET hard_err, int_err (their test cases don't yet work) ... +# +# Do the following: +# +# - Test #1: verify that OpenOCD ignores exceptions by default +# + l_VECTOR (loads testcase to RAM) +# + fault triggers loop-to-self exception "handler" +# + "halt" +# + observe fault "handling" -- loop-to-self from load_and_run (below) +# +# - Test #2: verify that "vector_catch" makes OpenOCD stops ignoring them +# + cortex_m3 vector_catch none +# + cortex_m3 vector_catch VECTOR +# + l_VECTOR (loads testcase to RAM) +# + fault triggers vector catch hardware +# + observe OpenOCD entering debug state with no assistance +# +# NOTE "reset" includes the NVIC, so that test case gets its reset vector +# from the flash, not from the vector table set up here. Which means that +# for that vector_catch option, the Test #1 (above) "observe" step won't +# use the SRAM address. +# + +# we can fully automate test #2 +proc vector_test {tag} { + halt + # REVISIT -- annoying, we'd like to scrap vector_catch output + cortex_m3 vector_catch none + cortex_m3 vector_catch $tag + eval "l_$tag" +} + +# +# Load and start one vector_catch test case. +# +# name -- tag for the vector_catch flag being tested +# halfwords -- array of instructions (some wide, some narrow) +# n_instr -- how many instructions are in $halfwords +# +proc load_and_run { name halfwords n_instr } { + reset halt + + # Load code at beginning of SRAM. + echo "# code to trigger $name vector" + set addr 0x20000000 + + # ocd_array2mem should be faster, though we'd need to + # compute the resulting $addr ourselves + foreach opcode $halfwords { + mwh $addr $opcode + incr addr 2 + } + + # create default loop-to-self at $addr ... it serves as + # (a) "main loop" on error + # (b) handler for all exceptions that get triggered + mwh $addr 0xe7fe + + # disassemble, as sanity check and what's-happening trace + cortex_m3 disassemble 0x20000000 [expr 1 + $n_instr ] + + # Assume that block of code is at most 16 halfwords long. + # Create a basic table of loop-to-self exception handlers. + mww 0x20000020 $addr 16 + # Store its address in VTOR + mww 0xe000ed08 0x20000020 + # Use SHCSR to ensure nothing escalates to a HardFault + mww 0xe000ed24 0x00070000 + + # now start, trigering the $name vector catch logic + resume 0x20000000 +} + +#proc l_hard_err {} { +# IMPLEMENT ME +# FORCED -- escalate something to HardFault +#} + +#proc l_int_err {} { +# IMPLEMENT ME +# STKERR -- exception stack BusFault +#} + +# BusFault, escalates to HardFault +proc l_bus_err {} { + # PRECISERR -- assume less than 512 MBytes of SRAM + load_and_run bus_err { + 0xf06f 0x4040 + 0x7800 + } 2 +} + +# UsageFault, escalates to HardFault +proc l_state_err {} { + # UNDEFINSTR -- issue architecturally undefined instruction + load_and_run state_err { + 0xde00 + } 1 +} + +# UsageFault, escalates to HardFault +proc l_chk_err {} { + # UNALIGNED -- LDM through unaligned pointer + load_and_run chk_err { + 0xf04f 0x0001 + 0xe890 0x0006 + } 2 +} + +# UsageFault, escalates to HardFault +proc l_nocp_err {} { + # NOCP -- issue cp14 DCC instruction + load_and_run nocp_err { + 0xee10 0x0e15 + } 1 +} + +# MemManage, escalates to HardFault +proc l_mm_err {} { + # IACCVIOL -- instruction fetch from an XN region + load_and_run mm_err { + 0xf04f 0x4060 + 0x4687 + } 2 +} + +proc l_reset {} { + # issue SYSRESETREQ via AIRCR + load_and_run reset { + 0xf04f 0x0104 + 0xf2c0 0x51fa + 0xf44f 0x406d + 0xf100 0x000c + 0xf2ce 0x0000 + 0x6001 + } 6 +} diff --git a/testing/examples/cortex/fault.c b/testing/examples/cortex/fault.c new file mode 100644 index 0000000000..9a5fe194f9 --- /dev/null +++ b/testing/examples/cortex/fault.c @@ -0,0 +1,152 @@ +/* + * COMPILE: arm-none-eabi-gcc -mthumb -march=armv7-m ... + * ... plus, provide at least a default exception vector table. + * + * RUN: this is best run from SRAM. It starts at main() then triggers + * a fault before more than a handful of instructions have executed. + * Run each test case in two modes: + * + * (1) Faults caught on the Cortex-M3. Default handlers are usually + * loop-to-self NOPs, so a debugger won't notice faults until they + * halt the core and examine xSPR and other registers. + * + * To verify the fault triggered, issue "halt" from OpenOCD; you + * should be told about the fault and (some of) its details. + * Then it's time to run the next test. + * + * NOTE however that "reset" will restart everything; verify that + * case by observing your reset handler doing its normal work. + * + * (2) Faults intercepted by OpenOCD "vector_catch ..." commands. + * + * OpenOCD should tell you about the fault, and show the same + * details, without your "halt" command. + * + * Someday, a fancy version of this code could provide a vector table and + * fault handlers which use semihosting (when that works on Cortex-M3) to + * report what happened, again without needing a "halt" command. + */ + + +/* These symbols match the OpenOCD "cortex_m3 vector_catch" bit names. */ +enum vc_case { + hard_err, + int_err, + bus_err, + state_err, + chk_err, + nocp_err, + mm_err, + reset, +}; + +/* REVISIT come up with a way to avoid recompiling, maybe: + * - write it in RAM before starting + * - compiled-in BKPT, manual patch of r0, then resume + * - ... + */ + +#ifndef VC_ID +#warning "no VC_ID ... using reset" +#define VC_ID reset +#endif + +int main(void) __attribute__ ((externally_visible, noreturn)); + +/* + * Trigger various Cortex-M3 faults to verify that OpenOCD behaves OK + * in terms of its vector_catch handling. + * + * Fault handling should be left entirely up to the application code + * UNLESS a "vector_catch" command tells OpenOCD to intercept a fault. + * + * See ARMv7-M architecure spec table B1-9 for the list of faults and + * their mappings to the vector catch bits. + */ +int main(void) +{ + /* One test case for each vector catch bit. We're not doing + * hardware testing; so it doesn't matter when some DEMCR bits + * could apply in multiple ways. + */ + switch (VC_ID) { + + /* "cortex_m3 vector_catch hard_err" */ + case hard_err: + /* FORCED - Fault escalation */ + + /* FIXME code this */ + break; + + /* "cortex_m3 vector_catch int_err" */ + case int_err: + /* STKERR -- Exception stack BusFault */ + + /* FIXME code this */ + break; + + /* "cortex_m3 vector_catch bus_err" */ + case bus_err: + /* PRECISERR -- precise data bus read + * Here we assume a Cortex-M3 with 512 MBytes SRAM is very + * unlikely, so the last SRAM byte isn't a valid address. + */ + __asm__ volatile( + "mov r0, #0x3fffffff\n" + "ldrb r0, [r0]\n" + ); + break; + + /* "cortex_m3 vector_catch state_err" */ + case state_err: + /* UNDEFINSTR -- architectural undefined instruction */ + __asm__ volatile(".hword 0xde00"); + break; + + /* "cortex_m3 vector_catch chk_err" */ + case chk_err: + /* UNALIGNED ldm */ + __asm__ volatile( + "mov r0, #1\n" + "ldm r0, {r1, r2}\n" + ); + break; + + /* "cortex_m3 vector_catch nocp_err" */ + case nocp_err: + /* NOCP ... Cortex-M3 has no coprocessors (like CP14 DCC), + * but these instructions are allowed by ARMv7-M. + */ + __asm__ volatile("mrc p14, 0, r0, c0, c5, 0"); + break; + + /* "cortex_m3 vector_catch mm_err" */ + case mm_err: + /* IACCVIOL -- instruction fetch from an XN region */ + __asm__ volatile( + "mov r0, #0xe0000000\n" + "mov pc, r0\n" + ); + break; + + /* "cortex_m3 vector_catch reset" */ + case reset: + __asm__ volatile( + /* r1 = SYSRESETREQ */ + "mov r1, #0x0004\n" + /* r1 |= VECTKEY */ + "movt r1, #0x05fa\n" + /* r0 = &AIRCR */ + "mov r0, #0xed00\n" + "add r0, #0xc\n" + "movt r0, #0xe000\n" + /* AIRCR = ... */ + "str r1, [r0, #0]\n" + ); + break; + } + + /* don't return */ + while (1) + continue; +} -- 2.30.2