aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2009-05-17 22:10:05 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2009-05-21 01:44:26 -0400
commit80947e7c99c29ce3a78bdc1933b310468455a82f (patch)
treed0403ed66d03aa7f6498c65193e1f56def6af13f /arch/powerpc/kernel
parentf312deb4cd0c88196edf6dab192b7d42514398d6 (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.c20
-rw-r--r--arch/powerpc/kernel/traps.c96
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
28struct aligninfo { 29struct 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)
1286void __init trap_init(void) 1302void __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
1311struct 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
1336u32 ppc_warn_emulated;
1337
1338void 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
1345static 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
1373fail:
1374 debugfs_remove_recursive(dir);
1375 return -ENOMEM;
1376}
1377
1378device_initcall(ppc_warn_emulated_init);
1379
1380#endif /* CONFIG_PPC_EMULATED_STATS */