/* * Test low-level exception handling code: * * - hook an exception handler * - clobber (almost) all registers with a known value * - raise exception * - from exception handler, increment all saved register * contents by one (to ensure registers are not only * saved properly but also restored properly). * - resume execution * - verify registers are now 'clobber_value + 1' * * NOTE: cannot be used on PSIM because SYS exception is used * internally by simulator (but we could use a trap or * something else). * * Author: Till Straumann , 2007 * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. */ #include #include #include #include #include "vectors.h" typedef struct regs_ { uint32_t cr, xer, lr, ctr; uint32_t gpr0; uint32_t gpr1; uint32_t gpr2; uint32_t gpr3; uint32_t gpr4; uint32_t gpr5; uint32_t gpr6; uint32_t gpr7; uint32_t gpr8; uint32_t gpr9; uint32_t gpr10; uint32_t gpr11; uint32_t gpr12; uint32_t gpr13; uint32_t gpr14; uint32_t gpr15; uint32_t gpr16; uint32_t gpr17; uint32_t gpr18; uint32_t gpr19; uint32_t gpr20; uint32_t gpr21; uint32_t gpr22; uint32_t gpr23; uint32_t gpr24; uint32_t gpr25; uint32_t gpr26; uint32_t gpr27; uint32_t gpr28; uint32_t gpr29; uint32_t gpr30; uint32_t gpr31; } ppc_exc_int_regs; #define OFF(x) (uintptr_t)(&((ppc_exc_int_regs*)0)->x) void storegs(ppc_exc_int_regs *p0, ppc_exc_int_regs *p1) { asm volatile( " stmw 0, %6(%0) ;" " mfcr 0 ;" " stw 0, %2(%0) ;" " mflr 0 ;" " stw 0, %3(%0) ;" " mfxer 0 ;" " stw 0, %4(%0) ;" " mfctr 0 ;" " stw 0, %5(%0) ;" " lwz 0, %6(%0) ;" " trap ;" " stmw 0, %6(%1) ;" " mfcr 0 ;" " stw 0, %2(%1) ;" " mflr 0 ;" " stw 0, %3(%1) ;" " mfxer 0 ;" " stw 0, %4(%1) ;" " mfctr 0 ;" " stw 0, %5(%1) ;" : :"b"(p0),"b"(p1), "i"(OFF(cr)), "i"(OFF(lr)), "i"(OFF(xer)), "i"(OFF(ctr)), "i"(OFF(gpr0)) :"r0"); } /* Load up all registers from 'pre' issue system call and store * registers in 'post' */ ppc_exc_int_regs pre; ppc_exc_int_regs pst; void clobber() { asm volatile( " lis 2, pre@h ;" " ori 2, 2, pre@l ;" " lwz 3, %0(2) ;" " mtcr 3 ;" " lwz 3, %1(2) ;" " mtlr 3 ;" " lwz 3, %2(2) ;" " mtxer 3 ;" /* don't know which ones stick */ " mfxer 3 ;" " stw 3, %2(2) ;" " lwz 3, %3(2) ;" " mtctr 3 ;" " lwz 0, %4(2) ;" /* must not clobber R13, R1, R2 */ " stw 13, %6(2) ;" " lmw 3, %5(2) ;" " trap ;" " stmw 0, %4(2) ;" " mfcr 0 ;" " stw 0, %0(2) ;" " mflr 0 ;" " stw 0, %1(2) ;" " mfxer 0 ;" " stw 0, %2(2) ;" " mfctr 0 ;" " stw 0, %3(2) ;" : :"i"(OFF(cr)), "i"(OFF(lr)), "i"(OFF(xer)), "i"(OFF(ctr)), "i"(OFF(gpr0)), "i"(OFF(gpr3)), "i"(OFF(gpr13)) :"r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "xer","lr","ctr", "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7", "memory"); } typedef union { uint32_t u; uint8_t c[4]; } u32_a_t; /* exception handler; adds 1 to all register contents (except r1,r2,r13) */ int handle_clobber_exc(BSP_Exception_frame *f, unsigned vector) { int i; u32_a_t *p = (u32_a_t*)&f->GPR0; for ( i=0; i<32; i++ ) { switch (i) { case 1: case 2: case 13: break; default: p[i].u++; break; } } f->GPR2 = (uint32_t)&pst; f->EXC_CR++; f->EXC_CTR++; f->EXC_XER++; f->EXC_LR++; f->EXC_SRR0 += 4; return 0; } /* This routine tests the raw exception code; * - hook 'handle_clobber_exc' to SYS exception handler * - clobber all registers with 0xaffe0000 + * (except: r1, r2, r13, non-sticky bits in xer) * R2 is clobbered with the address of the pre area. * - issue 'trap' -> PROG exception * - exception handler increments all reg. contents by 1, * stores address of 'pst' area in R2 and returns control * to ppc_exc_clobber(). * - save all register contents to *R2 (should be &pst). * - test for mismatches (except R1, R2, R13 and parts of xer) */ void ppc_exc_clobber() { u32_a_t *a, *b; int i; a = (u32_a_t*)⪯ b = (u32_a_t*)&pst; for ( i=0; i< sizeof(pre)/sizeof(uint32_t); i++ ) { a[i].u = 0xaffe0000 + i; } ppc_exc_set_handler(ASM_PROG_VECTOR, handle_clobber_exc); clobber(); ppc_exc_set_handler(ASM_PROG_VECTOR, 0); for ( i=0; i< sizeof(pre)/sizeof(uint32_t); i++ ) { switch (i) { case OFF(gpr1)/sizeof(uint32_t): case OFF(gpr2)/sizeof(uint32_t): case OFF(gpr13)/sizeof(uint32_t): break; default: if ( a[i].u != b[i].u - 1 ) { printf("MISMATCH at %i: 0x%08"PRIx32" -- 0x%08"PRIx32"\n", i, a[i].u, b[i].u); } } } } #if 0 void ppc_exc_test() { ppc_exc_int_regs a, b; int i; memset(&a, 0xaa, sizeof(a)); memset(&b, 0x55, sizeof(b)); storegs(&a, &b); if ( memcmp(&a, &b, sizeof(a)) ) { printf("FAILURE: context prior and after exception don't match!\n"); } for ( i=0; i< sizeof(a)/sizeof(uint32_t); i++ ) { printf("0x%08"PRIx32" -- 0x%08"PRIx32"\n", ((uint32_t __attribute__((may_alias)) *)&a)[i], ((uint32_t __attribute__((may_alias)) *)&b)[i]); } } #endif