diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 96 |
1 files changed, 94 insertions, 2 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 678fbff0d206..6a5b2b731f43 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); |
@@ -1088,6 +1103,7 @@ void altivec_assist_exception(struct pt_regs *regs) | |||
1088 | 1103 | ||
1089 | flush_altivec_to_thread(current); | 1104 | flush_altivec_to_thread(current); |
1090 | 1105 | ||
1106 | PPC_WARN_EMULATED(altivec); | ||
1091 | err = emulate_altivec(regs); | 1107 | err = emulate_altivec(regs); |
1092 | if (err == 0) { | 1108 | if (err == 0) { |
1093 | regs->nip += 4; /* skip emulated instruction */ | 1109 | regs->nip += 4; /* skip emulated instruction */ |
@@ -1286,3 +1302,79 @@ void kernel_bad_stack(struct pt_regs *regs) | |||
1286 | void __init trap_init(void) | 1302 | void __init trap_init(void) |
1287 | { | 1303 | { |
1288 | } | 1304 | } |
1305 | |||
1306 | |||
1307 | #ifdef CONFIG_PPC_EMULATED_STATS | ||
1308 | |||
1309 | #define WARN_EMULATED_SETUP(type) .type = { .name = #type } | ||
1310 | |||
1311 | struct ppc_emulated ppc_emulated = { | ||
1312 | #ifdef CONFIG_ALTIVEC | ||
1313 | WARN_EMULATED_SETUP(altivec), | ||
1314 | #endif | ||
1315 | WARN_EMULATED_SETUP(dcba), | ||
1316 | WARN_EMULATED_SETUP(dcbz), | ||
1317 | WARN_EMULATED_SETUP(fp_pair), | ||
1318 | WARN_EMULATED_SETUP(isel), | ||
1319 | WARN_EMULATED_SETUP(mcrxr), | ||
1320 | WARN_EMULATED_SETUP(mfpvr), | ||
1321 | WARN_EMULATED_SETUP(multiple), | ||
1322 | WARN_EMULATED_SETUP(popcntb), | ||
1323 | WARN_EMULATED_SETUP(spe), | ||
1324 | WARN_EMULATED_SETUP(string), | ||
1325 | WARN_EMULATED_SETUP(unaligned), | ||
1326 | #ifdef CONFIG_MATH_EMULATION | ||
1327 | WARN_EMULATED_SETUP(math), | ||
1328 | #elif defined(CONFIG_8XX_MINIMAL_FPEMU) | ||
1329 | WARN_EMULATED_SETUP(8xx), | ||
1330 | #endif | ||
1331 | #ifdef CONFIG_VSX | ||
1332 | WARN_EMULATED_SETUP(vsx), | ||
1333 | #endif | ||
1334 | }; | ||
1335 | |||
1336 | u32 ppc_warn_emulated; | ||
1337 | |||
1338 | void ppc_warn_emulated_print(const char *type) | ||
1339 | { | ||
1340 | if (printk_ratelimit()) | ||
1341 | pr_warning("%s used emulated %s instruction\n", current->comm, | ||
1342 | type); | ||
1343 | } | ||
1344 | |||
1345 | static int __init ppc_warn_emulated_init(void) | ||
1346 | { | ||
1347 | struct dentry *dir, *d; | ||
1348 | unsigned int i; | ||
1349 | struct ppc_emulated_entry *entries = (void *)&ppc_emulated; | ||
1350 | |||
1351 | if (!powerpc_debugfs_root) | ||
1352 | return -ENODEV; | ||
1353 | |||
1354 | dir = debugfs_create_dir("emulated_instructions", | ||
1355 | powerpc_debugfs_root); | ||
1356 | if (!dir) | ||
1357 | return -ENOMEM; | ||
1358 | |||
1359 | d = debugfs_create_u32("do_warn", S_IRUGO | S_IWUSR, dir, | ||
1360 | &ppc_warn_emulated); | ||
1361 | if (!d) | ||
1362 | goto fail; | ||
1363 | |||
1364 | for (i = 0; i < sizeof(ppc_emulated)/sizeof(*entries); i++) { | ||
1365 | d = debugfs_create_u32(entries[i].name, S_IRUGO | S_IWUSR, dir, | ||
1366 | (u32 *)&entries[i].val.counter); | ||
1367 | if (!d) | ||
1368 | goto fail; | ||
1369 | } | ||
1370 | |||
1371 | return 0; | ||
1372 | |||
1373 | fail: | ||
1374 | debugfs_remove_recursive(dir); | ||
1375 | return -ENOMEM; | ||
1376 | } | ||
1377 | |||
1378 | device_initcall(ppc_warn_emulated_init); | ||
1379 | |||
1380 | #endif /* CONFIG_PPC_EMULATED_STATS */ | ||