/* * Test some powerpc instructions */ #include #include static int verbose; static int volatile is_invalid; static int volatile alignment; static void program_check_handler(struct pt_regs *regs, void *opaque) { int *data = opaque; if (verbose) { printf("Detected invalid instruction %#018lx: %08x\n", regs->nip, *(uint32_t*)regs->nip); } /* the result is bit 16 to 19 of SRR1 * bit 0: SRR0 contains the address of the next instruction * bit 1: Trap * bit 2: Privileged instruction * bit 3: Illegal instruction * bit 4: FP enabled exception type */ *data = regs->msr >> 16; regs->nip += 4; } static void heai_handler(struct pt_regs *regs, void *opaque) { int *data = opaque; if (verbose) { printf("Detected invalid instruction %#018lx: %08x\n", regs->nip, *(uint32_t*)regs->nip); } *data = 8; /* Illegal instruction */ regs->nip += 4; } static void alignment_handler(struct pt_regs *regs, void *opaque) { int *data = opaque; if (verbose) { printf("Detected alignment exception %#018lx: %08x\n", regs->nip, *(uint32_t*)regs->nip); } *data = 1; regs->nip += 4; } static void test_illegal(void) { report_prefix_push("invalid"); is_invalid = 0; asm volatile (".long 0"); report(is_invalid == 8, "exception"); /* illegal instruction */ report_prefix_pop(); } static void test_64bit(void) { uint64_t msr; report_prefix_push("64bit"); asm("mfmsr %[msr]": [msr] "=r" (msr)); report(msr & 0x8000000000000000UL, "detected"); report_prefix_pop(); } /* * Test 'Load String Word Immediate' instruction */ static void test_lswi(void) { int i; char addr[128]; uint64_t regs[32]; report_prefix_push("lswi"); /* fill memory with sequence */ for (i = 0; i < 128; i++) addr[i] = 1 + i; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* * lswi is supposed to cause an alignment exception in little endian * mode, but to be able to check this, we also have to specify the * opcode without mnemonic here since newer versions of GCC refuse * "lswi" when compiling in little endian mode. */ alignment = 0; asm volatile ("mr r12,%[addr];" ".long 0x7d6c24aa;" /* lswi r11,r12,4 */ "std r11,0(%[regs]);" :: [addr] "r" (addr), [regs] "r" (regs) : "r11", "r12", "memory"); report(alignment, "alignment"); #else /* check incomplete register filling */ asm volatile ("li r12,-1;" "mr r11, r12;" "lswi r11, %[addr], %[len];" "std r11, 0*8(%[regs]);" "std r12, 1*8(%[regs]);" :: [len] "i" (3), [addr] "b" (addr), [regs] "r" (regs) : "r11", "r12", "memory"); report(regs[0] == 0x01020300 && regs[1] == (uint64_t)-1, "partial"); /* check NB = 0 ==> 32 bytes. */ asm volatile ("li r19,-1;" "mr r11, r19; mr r12, r19; mr r13, r19;" "mr r14, r19; mr r15, r19; mr r16, r19;" "mr r17, r19; mr r18, r19;" "lswi r11, %[addr], %[len];" "std r11, 0*8(%[regs]);" "std r12, 1*8(%[regs]);" "std r13, 2*8(%[regs]);" "std r14, 3*8(%[regs]);" "std r15, 4*8(%[regs]);" "std r16, 5*8(%[regs]);" "std r17, 6*8(%[regs]);" "std r18, 7*8(%[regs]);" "std r19, 8*8(%[regs]);" :: [len] "i" (0), [addr] "b" (addr), [regs] "r" (regs) : /* as 32 is the number of bytes, * we should modify 32/4 = 8 regs, from r11 to r18 * We check r19 is unmodified by filling it with 1s * before the instruction. */ "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "memory"); report(regs[0] == 0x01020304 && regs[1] == 0x05060708 && regs[2] == 0x090a0b0c && regs[3] == 0x0d0e0f10 && regs[4] == 0x11121314 && regs[5] == 0x15161718 && regs[6] == 0x191a1b1c && regs[7] == 0x1d1e1f20 && regs[8] == (uint64_t)-1, "length"); /* check wrap around to r0 */ asm volatile ("li r31,-1;" "mr r0, r31;" "lswi r31, %[addr], %[len];" "std r31, 0*8(%[regs]);" "std r0, 1*8(%[regs]);" :: [len] "i" (8), [addr] "b" (addr), [regs] "r" (regs) : /* modify two registers from r31, wrap around to r0 */ "r31", "r0", "memory"); report(regs[0] == 0x01020304 && regs[1] == 0x05060708, "wrap around to r0"); /* check wrap around doesn't break RA */ asm volatile ("mr r29,r1\n" "li r31,-1\n" "mr r0,r31\n" "mr r1, %[addr]\n" ".long 0x7fe154aa\n" /* lswi r31, r1, 10 */ "std r31, 0*8(%[regs])\n" "std r0, 1*8(%[regs])\n" "std r1, 2*8(%[regs])\n" "mr r1,r29\n" :: [addr] "r" (addr), [regs] "r" (regs) : /* loading three registers from r31 wraps around to r1, * r1 is saved to r29, as adding it to the clobber * list doesn't protect it */ "r0", "r29", "r31", "memory"); /* doc says it is invalid, real proc stops when it comes to * overwrite the register. * In all the cases, the register must stay untouched */ report(regs[2] == (uint64_t)addr, "Don't overwrite Ra"); #endif report_prefix_pop(); } /* * lswx: Load String Word Indexed X-form * * lswx RT,RA,RB * * EA = (RA|0) + RB * n = XER * * Load n bytes from address EA into (n / 4) consecutive registers, * throught RT -> RT + (n / 4) - 1. * - Data are loaded into 4 low order bytes of registers (Word). * - The unfilled bytes are set to 0. * - The sequence of registers wraps around to GPR0. * - if n == 0, content of RT is undefined * - RT <= RA or RB < RT + (n + 4) is invalid or result is undefined * - RT == RA == 0 is invalid * * For lswx in little-endian mode, an alignment interrupt always occurs. * */ static void test_lswx(void) { int i; char addr[128]; uint64_t regs[32]; report_prefix_push("lswx"); /* fill memory with sequence */ for (i = 0; i < 128; i++) addr[i] = 1 + i; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* * lswx is supposed to cause an alignment exception in little endian * mode, but to be able to check this, we also have to specify the * opcode without mnemonic here since newer versions of GCC refuse * "lswx" when compiling in little endian mode. */ alignment = 0; asm volatile ("mtxer %[len];" "mr r11,%[addr];" ".long 0x7d805c2a;" /* lswx r12,0,r11 */ "std r12,0(%[regs]);" :: [len]"r"(4), [addr]"r"(addr), [regs]"r"(regs) : "r11", "r12", "memory"); report(alignment, "alignment"); #else /* check incomplete register filling */ asm volatile ("mtxer %[len];" "li r12,-1;" "mr r11, r12;" "lswx r11, 0, %[addr];" "std r11, 0*8(%[regs]);" "std r12, 1*8(%[regs]);" :: [len] "r" (3), [addr] "r" (addr), [regs] "r" (regs) : "xer", "r11", "r12", "memory"); report(regs[0] == 0x01020300 && regs[1] == (uint64_t)-1, "partial"); /* check an old know bug: the number of bytes is used as * the number of registers, so try 32 bytes. */ asm volatile ("mtxer %[len];" "li r19,-1;" "mr r11, r19; mr r12, r19; mr r13, r19;" "mr r14, r19; mr r15, r19; mr r16, r19;" "mr r17, r19; mr r18, r19;" "lswx r11, 0, %[addr];" "std r11, 0*8(%[regs]);" "std r12, 1*8(%[regs]);" "std r13, 2*8(%[regs]);" "std r14, 3*8(%[regs]);" "std r15, 4*8(%[regs]);" "std r16, 5*8(%[regs]);" "std r17, 6*8(%[regs]);" "std r18, 7*8(%[regs]);" "std r19, 8*8(%[regs]);" :: [len] "r" (32), [addr] "r" (addr), [regs] "r" (regs) : /* as 32 is the number of bytes, * we should modify 32/4 = 8 regs, from r11 to r18 * We check r19 is unmodified by filling it with 1s * before the instruction. */ "xer", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "memory"); report(regs[0] == 0x01020304 && regs[1] == 0x05060708 && regs[2] == 0x090a0b0c && regs[3] == 0x0d0e0f10 && regs[4] == 0x11121314 && regs[5] == 0x15161718 && regs[6] == 0x191a1b1c && regs[7] == 0x1d1e1f20 && regs[8] == (uint64_t)-1, "length"); /* check wrap around to r0 */ asm volatile ("mtxer %[len];" "li r31,-1;" "mr r0, r31;" "lswx r31, 0, %[addr];" "std r31, 0*8(%[regs]);" "std r0, 1*8(%[regs]);" :: [len] "r" (8), [addr] "r" (addr), [regs] "r" (regs) : /* modify two registers from r31, wrap around to r0 */ "xer", "r31", "r0", "memory"); report(regs[0] == 0x01020304 && regs[1] == 0x05060708, "wrap around to r0"); /* check wrap around to r0 over RB doesn't break RB */ asm volatile ("mtxer %[len];" "mr r29,r1;" "li r31,-1;" "mr r1,r31;" "mr r0, %[addr];" "lswx r31, 0, r0;" "std r31, 0*8(%[regs]);" "std r0, 1*8(%[regs]);" "std r1, 2*8(%[regs]);" "mr r1,r29;" :: [len] "r" (12), [addr] "r" (addr), [regs] "r" (regs) : /* loading three registers from r31 wraps around to r1, * r1 is saved to r29, as adding it to the clobber * list doesn't protect it */ "xer", "r31", "r0", "r29", "memory"); /* doc says it is invalid, real proc stops when it comes to * overwrite the register. * In all the cases, the register must stay untouched */ report(regs[1] == (uint64_t)addr, "Don't overwrite Rb"); #endif report_prefix_pop(); } int main(int argc, char **argv) { int i; handle_exception(0x700, program_check_handler, (void *)&is_invalid); if (cpu_has_heai) handle_exception(0xe40, heai_handler, (void *)&is_invalid); handle_exception(0x600, alignment_handler, (void *)&alignment); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-v") == 0) { verbose = 1; } } report_prefix_push("emulator"); test_64bit(); test_illegal(); test_lswx(); test_lswi(); report_prefix_pop(); return report_summary(); }