diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/Kconfig.debug | 13 | ||||
-rw-r--r-- | arch/powerpc/include/asm/emulated_ops.h | 73 | ||||
-rw-r--r-- | arch/powerpc/kernel/align.c | 20 | ||||
-rw-r--r-- | arch/powerpc/kernel/traps.c | 96 |
4 files changed, 196 insertions, 6 deletions
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index a1098e23221f..d79a902d155a 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug | |||
@@ -41,6 +41,19 @@ config HCALL_STATS | |||
41 | This option will add a small amount of overhead to all hypervisor | 41 | This option will add a small amount of overhead to all hypervisor |
42 | calls. | 42 | calls. |
43 | 43 | ||
44 | config PPC_EMULATED_STATS | ||
45 | bool "Emulated instructions tracking" | ||
46 | depends on DEBUG_FS | ||
47 | help | ||
48 | Adds code to keep track of the number of instructions that are | ||
49 | emulated by the in-kernel emulator. Counters for the various classes | ||
50 | of emulated instructions are available under | ||
51 | powerpc/emulated_instructions/ in the root of the debugfs file | ||
52 | system. Optionally (controlled by | ||
53 | powerpc/emulated_instructions/do_warn in debugfs), rate-limited | ||
54 | warnings can be printed to the console when instructions are | ||
55 | emulated. | ||
56 | |||
44 | config CODE_PATCHING_SELFTEST | 57 | config CODE_PATCHING_SELFTEST |
45 | bool "Run self-tests of the code-patching code." | 58 | bool "Run self-tests of the code-patching code." |
46 | depends on DEBUG_KERNEL | 59 | depends on DEBUG_KERNEL |
diff --git a/arch/powerpc/include/asm/emulated_ops.h b/arch/powerpc/include/asm/emulated_ops.h new file mode 100644 index 000000000000..9154e8526732 --- /dev/null +++ b/arch/powerpc/include/asm/emulated_ops.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Copyright 2007 Sony Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; version 2 of the License. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. | ||
15 | * If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef _ASM_POWERPC_EMULATED_OPS_H | ||
19 | #define _ASM_POWERPC_EMULATED_OPS_H | ||
20 | |||
21 | #include <asm/atomic.h> | ||
22 | |||
23 | |||
24 | #ifdef CONFIG_PPC_EMULATED_STATS | ||
25 | |||
26 | struct ppc_emulated_entry { | ||
27 | const char *name; | ||
28 | atomic_t val; | ||
29 | }; | ||
30 | |||
31 | extern struct ppc_emulated { | ||
32 | #ifdef CONFIG_ALTIVEC | ||
33 | struct ppc_emulated_entry altivec; | ||
34 | #endif | ||
35 | struct ppc_emulated_entry dcba; | ||
36 | struct ppc_emulated_entry dcbz; | ||
37 | struct ppc_emulated_entry fp_pair; | ||
38 | struct ppc_emulated_entry isel; | ||
39 | struct ppc_emulated_entry mcrxr; | ||
40 | struct ppc_emulated_entry mfpvr; | ||
41 | struct ppc_emulated_entry multiple; | ||
42 | struct ppc_emulated_entry popcntb; | ||
43 | struct ppc_emulated_entry spe; | ||
44 | struct ppc_emulated_entry string; | ||
45 | struct ppc_emulated_entry unaligned; | ||
46 | #ifdef CONFIG_MATH_EMULATION | ||
47 | struct ppc_emulated_entry math; | ||
48 | #elif defined(CONFIG_8XX_MINIMAL_FPEMU) | ||
49 | struct ppc_emulated_entry 8xx; | ||
50 | #endif | ||
51 | #ifdef CONFIG_VSX | ||
52 | struct ppc_emulated_entry vsx; | ||
53 | #endif | ||
54 | } ppc_emulated; | ||
55 | |||
56 | extern u32 ppc_warn_emulated; | ||
57 | |||
58 | extern void ppc_warn_emulated_print(const char *type); | ||
59 | |||
60 | #define PPC_WARN_EMULATED(type) \ | ||
61 | do { \ | ||
62 | atomic_inc(&ppc_emulated.type.val); \ | ||
63 | if (ppc_warn_emulated) \ | ||
64 | ppc_warn_emulated_print(ppc_emulated.type.name); \ | ||
65 | } while (0) | ||
66 | |||
67 | #else /* !CONFIG_PPC_EMULATED_STATS */ | ||
68 | |||
69 | #define PPC_WARN_EMULATED(type) do { } while (0) | ||
70 | |||
71 | #endif /* !CONFIG_PPC_EMULATED_STATS */ | ||
72 | |||
73 | #endif /* _ASM_POWERPC_EMULATED_OPS_H */ | ||
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 */ | ||