diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 130 |
1 files changed, 124 insertions, 6 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 678fbff0d206..6f0ae1a9bfae 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -33,7 +33,9 @@ | |||
33 | #include <linux/backlight.h> | 33 | #include <linux/backlight.h> |
34 | #include <linux/bug.h> | 34 | #include <linux/bug.h> |
35 | #include <linux/kdebug.h> | 35 | #include <linux/kdebug.h> |
36 | #include <linux/debugfs.h> | ||
36 | 37 | ||
38 | #include <asm/emulated_ops.h> | ||
37 | #include <asm/pgtable.h> | 39 | #include <asm/pgtable.h> |
38 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
39 | #include <asm/system.h> | 41 | #include <asm/system.h> |
@@ -757,36 +759,44 @@ static int emulate_instruction(struct pt_regs *regs) | |||
757 | 759 | ||
758 | /* Emulate the mfspr rD, PVR. */ | 760 | /* Emulate the mfspr rD, PVR. */ |
759 | if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) { | 761 | if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) { |
762 | PPC_WARN_EMULATED(mfpvr); | ||
760 | rd = (instword >> 21) & 0x1f; | 763 | rd = (instword >> 21) & 0x1f; |
761 | regs->gpr[rd] = mfspr(SPRN_PVR); | 764 | regs->gpr[rd] = mfspr(SPRN_PVR); |
762 | return 0; | 765 | return 0; |
763 | } | 766 | } |
764 | 767 | ||
765 | /* Emulating the dcba insn is just a no-op. */ | 768 | /* Emulating the dcba insn is just a no-op. */ |
766 | if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) | 769 | if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) { |
770 | PPC_WARN_EMULATED(dcba); | ||
767 | return 0; | 771 | return 0; |
772 | } | ||
768 | 773 | ||
769 | /* Emulate the mcrxr insn. */ | 774 | /* Emulate the mcrxr insn. */ |
770 | if ((instword & PPC_INST_MCRXR_MASK) == PPC_INST_MCRXR) { | 775 | if ((instword & PPC_INST_MCRXR_MASK) == PPC_INST_MCRXR) { |
771 | int shift = (instword >> 21) & 0x1c; | 776 | int shift = (instword >> 21) & 0x1c; |
772 | unsigned long msk = 0xf0000000UL >> shift; | 777 | unsigned long msk = 0xf0000000UL >> shift; |
773 | 778 | ||
779 | PPC_WARN_EMULATED(mcrxr); | ||
774 | regs->ccr = (regs->ccr & ~msk) | ((regs->xer >> shift) & msk); | 780 | regs->ccr = (regs->ccr & ~msk) | ((regs->xer >> shift) & msk); |
775 | regs->xer &= ~0xf0000000UL; | 781 | regs->xer &= ~0xf0000000UL; |
776 | return 0; | 782 | return 0; |
777 | } | 783 | } |
778 | 784 | ||
779 | /* Emulate load/store string insn. */ | 785 | /* Emulate load/store string insn. */ |
780 | if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) | 786 | if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) { |
787 | PPC_WARN_EMULATED(string); | ||
781 | return emulate_string_inst(regs, instword); | 788 | return emulate_string_inst(regs, instword); |
789 | } | ||
782 | 790 | ||
783 | /* Emulate the popcntb (Population Count Bytes) instruction. */ | 791 | /* Emulate the popcntb (Population Count Bytes) instruction. */ |
784 | if ((instword & PPC_INST_POPCNTB_MASK) == PPC_INST_POPCNTB) { | 792 | if ((instword & PPC_INST_POPCNTB_MASK) == PPC_INST_POPCNTB) { |
793 | PPC_WARN_EMULATED(popcntb); | ||
785 | return emulate_popcntb_inst(regs, instword); | 794 | return emulate_popcntb_inst(regs, instword); |
786 | } | 795 | } |
787 | 796 | ||
788 | /* Emulate isel (Integer Select) instruction */ | 797 | /* Emulate isel (Integer Select) instruction */ |
789 | if ((instword & PPC_INST_ISEL_MASK) == PPC_INST_ISEL) { | 798 | if ((instword & PPC_INST_ISEL_MASK) == PPC_INST_ISEL) { |
799 | PPC_WARN_EMULATED(isel); | ||
790 | return emulate_isel(regs, instword); | 800 | return emulate_isel(regs, instword); |
791 | } | 801 | } |
792 | 802 | ||
@@ -984,6 +994,8 @@ void SoftwareEmulation(struct pt_regs *regs) | |||
984 | 994 | ||
985 | #ifdef CONFIG_MATH_EMULATION | 995 | #ifdef CONFIG_MATH_EMULATION |
986 | errcode = do_mathemu(regs); | 996 | errcode = do_mathemu(regs); |
997 | if (errcode >= 0) | ||
998 | PPC_WARN_EMULATED(math); | ||
987 | 999 | ||
988 | switch (errcode) { | 1000 | switch (errcode) { |
989 | case 0: | 1001 | case 0: |
@@ -1005,6 +1017,9 @@ void SoftwareEmulation(struct pt_regs *regs) | |||
1005 | 1017 | ||
1006 | #elif defined(CONFIG_8XX_MINIMAL_FPEMU) | 1018 | #elif defined(CONFIG_8XX_MINIMAL_FPEMU) |
1007 | errcode = Soft_emulate_8xx(regs); | 1019 | errcode = Soft_emulate_8xx(regs); |
1020 | if (errcode >= 0) | ||
1021 | PPC_WARN_EMULATED(8xx); | ||
1022 | |||
1008 | switch (errcode) { | 1023 | switch (errcode) { |
1009 | case 0: | 1024 | case 0: |
1010 | emulate_single_step(regs); | 1025 | emulate_single_step(regs); |
@@ -1026,7 +1041,34 @@ void SoftwareEmulation(struct pt_regs *regs) | |||
1026 | 1041 | ||
1027 | void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) | 1042 | void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) |
1028 | { | 1043 | { |
1029 | if (debug_status & DBSR_IC) { /* instruction completion */ | 1044 | /* Hack alert: On BookE, Branch Taken stops on the branch itself, while |
1045 | * on server, it stops on the target of the branch. In order to simulate | ||
1046 | * the server behaviour, we thus restart right away with a single step | ||
1047 | * instead of stopping here when hitting a BT | ||
1048 | */ | ||
1049 | if (debug_status & DBSR_BT) { | ||
1050 | regs->msr &= ~MSR_DE; | ||
1051 | |||
1052 | /* Disable BT */ | ||
1053 | mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_BT); | ||
1054 | /* Clear the BT event */ | ||
1055 | mtspr(SPRN_DBSR, DBSR_BT); | ||
1056 | |||
1057 | /* Do the single step trick only when coming from userspace */ | ||
1058 | if (user_mode(regs)) { | ||
1059 | current->thread.dbcr0 &= ~DBCR0_BT; | ||
1060 | current->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC; | ||
1061 | regs->msr |= MSR_DE; | ||
1062 | return; | ||
1063 | } | ||
1064 | |||
1065 | if (notify_die(DIE_SSTEP, "block_step", regs, 5, | ||
1066 | 5, SIGTRAP) == NOTIFY_STOP) { | ||
1067 | return; | ||
1068 | } | ||
1069 | if (debugger_sstep(regs)) | ||
1070 | return; | ||
1071 | } else if (debug_status & DBSR_IC) { /* Instruction complete */ | ||
1030 | regs->msr &= ~MSR_DE; | 1072 | regs->msr &= ~MSR_DE; |
1031 | 1073 | ||
1032 | /* Disable instruction completion */ | 1074 | /* Disable instruction completion */ |
@@ -1042,9 +1084,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) | |||
1042 | if (debugger_sstep(regs)) | 1084 | if (debugger_sstep(regs)) |
1043 | return; | 1085 | return; |
1044 | 1086 | ||
1045 | if (user_mode(regs)) { | 1087 | if (user_mode(regs)) |
1046 | current->thread.dbcr0 &= ~DBCR0_IC; | 1088 | current->thread.dbcr0 &= ~(DBCR0_IC); |
1047 | } | ||
1048 | 1089 | ||
1049 | _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); | 1090 | _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); |
1050 | } else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { | 1091 | } else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { |
@@ -1088,6 +1129,7 @@ void altivec_assist_exception(struct pt_regs *regs) | |||
1088 | 1129 | ||
1089 | flush_altivec_to_thread(current); | 1130 | flush_altivec_to_thread(current); |
1090 | 1131 | ||
1132 | PPC_WARN_EMULATED(altivec); | ||
1091 | err = emulate_altivec(regs); | 1133 | err = emulate_altivec(regs); |
1092 | if (err == 0) { | 1134 | if (err == 0) { |
1093 | regs->nip += 4; /* skip emulated instruction */ | 1135 | regs->nip += 4; /* skip emulated instruction */ |
@@ -1286,3 +1328,79 @@ void kernel_bad_stack(struct pt_regs *regs) | |||
1286 | void __init trap_init(void) | 1328 | void __init trap_init(void) |
1287 | { | 1329 | { |
1288 | } | 1330 | } |
1331 | |||
1332 | |||
1333 | #ifdef CONFIG_PPC_EMULATED_STATS | ||
1334 | |||
1335 | #define WARN_EMULATED_SETUP(type) .type = { .name = #type } | ||
1336 | |||
1337 | struct ppc_emulated ppc_emulated = { | ||
1338 | #ifdef CONFIG_ALTIVEC | ||
1339 | WARN_EMULATED_SETUP(altivec), | ||
1340 | #endif | ||
1341 | WARN_EMULATED_SETUP(dcba), | ||
1342 | WARN_EMULATED_SETUP(dcbz), | ||
1343 | WARN_EMULATED_SETUP(fp_pair), | ||
1344 | WARN_EMULATED_SETUP(isel), | ||
1345 | WARN_EMULATED_SETUP(mcrxr), | ||
1346 | WARN_EMULATED_SETUP(mfpvr), | ||
1347 | WARN_EMULATED_SETUP(multiple), | ||
1348 | WARN_EMULATED_SETUP(popcntb), | ||
1349 | WARN_EMULATED_SETUP(spe), | ||
1350 | WARN_EMULATED_SETUP(string), | ||
1351 | WARN_EMULATED_SETUP(unaligned), | ||
1352 | #ifdef CONFIG_MATH_EMULATION | ||
1353 | WARN_EMULATED_SETUP(math), | ||
1354 | #elif defined(CONFIG_8XX_MINIMAL_FPEMU) | ||
1355 | WARN_EMULATED_SETUP(8xx), | ||
1356 | #endif | ||
1357 | #ifdef CONFIG_VSX | ||
1358 | WARN_EMULATED_SETUP(vsx), | ||
1359 | #endif | ||
1360 | }; | ||
1361 | |||
1362 | u32 ppc_warn_emulated; | ||
1363 | |||
1364 | void ppc_warn_emulated_print(const char *type) | ||
1365 | { | ||
1366 | if (printk_ratelimit()) | ||
1367 | pr_warning("%s used emulated %s instruction\n", current->comm, | ||
1368 | type); | ||
1369 | } | ||
1370 | |||
1371 | static int __init ppc_warn_emulated_init(void) | ||
1372 | { | ||
1373 | struct dentry *dir, *d; | ||
1374 | unsigned int i; | ||
1375 | struct ppc_emulated_entry *entries = (void *)&ppc_emulated; | ||
1376 | |||
1377 | if (!powerpc_debugfs_root) | ||
1378 | return -ENODEV; | ||
1379 | |||
1380 | dir = debugfs_create_dir("emulated_instructions", | ||
1381 | powerpc_debugfs_root); | ||
1382 | if (!dir) | ||
1383 | return -ENOMEM; | ||
1384 | |||
1385 | d = debugfs_create_u32("do_warn", S_IRUGO | S_IWUSR, dir, | ||
1386 | &ppc_warn_emulated); | ||
1387 | if (!d) | ||
1388 | goto fail; | ||
1389 | |||
1390 | for (i = 0; i < sizeof(ppc_emulated)/sizeof(*entries); i++) { | ||
1391 | d = debugfs_create_u32(entries[i].name, S_IRUGO | S_IWUSR, dir, | ||
1392 | (u32 *)&entries[i].val.counter); | ||
1393 | if (!d) | ||
1394 | goto fail; | ||
1395 | } | ||
1396 | |||
1397 | return 0; | ||
1398 | |||
1399 | fail: | ||
1400 | debugfs_remove_recursive(dir); | ||
1401 | return -ENOMEM; | ||
1402 | } | ||
1403 | |||
1404 | device_initcall(ppc_warn_emulated_init); | ||
1405 | |||
1406 | #endif /* CONFIG_PPC_EMULATED_STATS */ | ||