diff options
author | Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> | 2009-05-17 22:10:05 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-05-21 01:44:26 -0400 |
commit | 80947e7c99c29ce3a78bdc1933b310468455a82f (patch) | |
tree | d0403ed66d03aa7f6498c65193e1f56def6af13f /arch/powerpc/kernel | |
parent | f312deb4cd0c88196edf6dab192b7d42514398d6 (diff) |
powerpc: Keep track of emulated instructions
If CONFIG_PPC_EMULATED_STATS is enabled, make available counters for the
various classes of emulated instructions under
/sys/kernel/debug/powerpc/emulated_instructions/ (assumed debugfs is mounted on
/sys/kernel/debug). Optionally (controlled by
/sys/kernel/debug/powerpc/emulated_instructions/do_warn), rate-limited warnings
can be printed to the console when instructions are emulated.
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/align.c | 20 | ||||
-rw-r--r-- | arch/powerpc/kernel/traps.c | 96 |
2 files changed, 110 insertions, 6 deletions
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 5ffcfaa77d6a..a5b632e52fae 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <asm/system.h> | 24 | #include <asm/system.h> |
25 | #include <asm/cache.h> | 25 | #include <asm/cache.h> |
26 | #include <asm/cputable.h> | 26 | #include <asm/cputable.h> |
27 | #include <asm/emulated_ops.h> | ||
27 | 28 | ||
28 | struct aligninfo { | 29 | struct aligninfo { |
29 | unsigned char len; | 30 | unsigned char len; |
@@ -730,8 +731,10 @@ int fix_alignment(struct pt_regs *regs) | |||
730 | areg = dsisr & 0x1f; /* register to update */ | 731 | areg = dsisr & 0x1f; /* register to update */ |
731 | 732 | ||
732 | #ifdef CONFIG_SPE | 733 | #ifdef CONFIG_SPE |
733 | if ((instr >> 26) == 0x4) | 734 | if ((instr >> 26) == 0x4) { |
735 | PPC_WARN_EMULATED(spe); | ||
734 | return emulate_spe(regs, reg, instr); | 736 | return emulate_spe(regs, reg, instr); |
737 | } | ||
735 | #endif | 738 | #endif |
736 | 739 | ||
737 | instr = (dsisr >> 10) & 0x7f; | 740 | instr = (dsisr >> 10) & 0x7f; |
@@ -783,23 +786,28 @@ int fix_alignment(struct pt_regs *regs) | |||
783 | flags |= SPLT; | 786 | flags |= SPLT; |
784 | nb = 8; | 787 | nb = 8; |
785 | } | 788 | } |
789 | PPC_WARN_EMULATED(vsx); | ||
786 | return emulate_vsx(addr, reg, areg, regs, flags, nb); | 790 | return emulate_vsx(addr, reg, areg, regs, flags, nb); |
787 | } | 791 | } |
788 | #endif | 792 | #endif |
789 | /* A size of 0 indicates an instruction we don't support, with | 793 | /* A size of 0 indicates an instruction we don't support, with |
790 | * the exception of DCBZ which is handled as a special case here | 794 | * the exception of DCBZ which is handled as a special case here |
791 | */ | 795 | */ |
792 | if (instr == DCBZ) | 796 | if (instr == DCBZ) { |
797 | PPC_WARN_EMULATED(dcbz); | ||
793 | return emulate_dcbz(regs, addr); | 798 | return emulate_dcbz(regs, addr); |
799 | } | ||
794 | if (unlikely(nb == 0)) | 800 | if (unlikely(nb == 0)) |
795 | return 0; | 801 | return 0; |
796 | 802 | ||
797 | /* Load/Store Multiple instructions are handled in their own | 803 | /* Load/Store Multiple instructions are handled in their own |
798 | * function | 804 | * function |
799 | */ | 805 | */ |
800 | if (flags & M) | 806 | if (flags & M) { |
807 | PPC_WARN_EMULATED(multiple); | ||
801 | return emulate_multiple(regs, addr, reg, nb, | 808 | return emulate_multiple(regs, addr, reg, nb, |
802 | flags, instr, swiz); | 809 | flags, instr, swiz); |
810 | } | ||
803 | 811 | ||
804 | /* Verify the address of the operand */ | 812 | /* Verify the address of the operand */ |
805 | if (unlikely(user_mode(regs) && | 813 | if (unlikely(user_mode(regs) && |
@@ -816,8 +824,12 @@ int fix_alignment(struct pt_regs *regs) | |||
816 | } | 824 | } |
817 | 825 | ||
818 | /* Special case for 16-byte FP loads and stores */ | 826 | /* Special case for 16-byte FP loads and stores */ |
819 | if (nb == 16) | 827 | if (nb == 16) { |
828 | PPC_WARN_EMULATED(fp_pair); | ||
820 | return emulate_fp_pair(addr, reg, flags); | 829 | return emulate_fp_pair(addr, reg, flags); |
830 | } | ||
831 | |||
832 | PPC_WARN_EMULATED(unaligned); | ||
821 | 833 | ||
822 | /* If we are loading, get the data from user space, else | 834 | /* If we are loading, get the data from user space, else |
823 | * get it from register values | 835 | * get it from register values |
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 */ | ||