diff options
author | Jon Medhurst <tixy@yxit.co.uk> | 2011-04-06 06:17:11 -0400 |
---|---|---|
committer | Nicolas Pitre <nicolas.pitre@linaro.org> | 2011-04-28 23:40:55 -0400 |
commit | ad111ce46674a473370d302325db8f418ac4fe92 (patch) | |
tree | f99fc1a1dd357e431f8be4ca0b9784d8ad7797f8 /arch/arm | |
parent | a539f5d46c868cb1f37b92e62e040b400571aed4 (diff) |
ARM: kprobes: Fix emulation of CMP, CMN, TST and TEQ instructions.
Probing these instructions was corrupting R0 because the emulation code
didn't account for the fact that they don't write a result to a
register.
Signed-off-by: Jon Medhurst <tixy@yxit.co.uk>
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/kernel/kprobes-decode.c | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c index f52fac0c59f0..ee29d3350437 100644 --- a/arch/arm/kernel/kprobes-decode.c +++ b/arch/arm/kernel/kprobes-decode.c | |||
@@ -808,6 +808,17 @@ emulate_alu_imm_rwflags(struct kprobe *p, struct pt_regs *regs) | |||
808 | } | 808 | } |
809 | 809 | ||
810 | static void __kprobes | 810 | static void __kprobes |
811 | emulate_alu_tests_imm(struct kprobe *p, struct pt_regs *regs) | ||
812 | { | ||
813 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | ||
814 | kprobe_opcode_t insn = p->opcode; | ||
815 | int rn = (insn >> 16) & 0xf; | ||
816 | long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; | ||
817 | |||
818 | insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn); | ||
819 | } | ||
820 | |||
821 | static void __kprobes | ||
811 | emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs) | 822 | emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs) |
812 | { | 823 | { |
813 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | 824 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; |
@@ -843,6 +854,22 @@ emulate_alu_rwflags(struct kprobe *p, struct pt_regs *regs) | |||
843 | insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn); | 854 | insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn); |
844 | } | 855 | } |
845 | 856 | ||
857 | static void __kprobes | ||
858 | emulate_alu_tests(struct kprobe *p, struct pt_regs *regs) | ||
859 | { | ||
860 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | ||
861 | kprobe_opcode_t insn = p->opcode; | ||
862 | long ppc = (long)p->addr + 8; | ||
863 | int rn = (insn >> 16) & 0xf; | ||
864 | int rs = (insn >> 8) & 0xf; /* rs/rsv may be invalid, don't care. */ | ||
865 | int rm = insn & 0xf; | ||
866 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | ||
867 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | ||
868 | long rsv = regs->uregs[rs]; | ||
869 | |||
870 | insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn); | ||
871 | } | ||
872 | |||
846 | static enum kprobe_insn __kprobes | 873 | static enum kprobe_insn __kprobes |
847 | prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi) | 874 | prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi) |
848 | { | 875 | { |
@@ -1142,8 +1169,20 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
1142 | insn |= 0x00000200; /* Rs = r2 */ | 1169 | insn |= 0x00000200; /* Rs = r2 */ |
1143 | } | 1170 | } |
1144 | asi->insn[0] = insn; | 1171 | asi->insn[0] = insn; |
1145 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | 1172 | |
1173 | if ((insn & 0x0f900000) == 0x01100000) { | ||
1174 | /* | ||
1175 | * TST : cccc 0001 0001 xxxx xxxx xxxx xxxx xxxx | ||
1176 | * TEQ : cccc 0001 0011 xxxx xxxx xxxx xxxx xxxx | ||
1177 | * CMP : cccc 0001 0101 xxxx xxxx xxxx xxxx xxxx | ||
1178 | * CMN : cccc 0001 0111 xxxx xxxx xxxx xxxx xxxx | ||
1179 | */ | ||
1180 | asi->insn_handler = emulate_alu_tests; | ||
1181 | } else { | ||
1182 | /* ALU ops which write to Rd */ | ||
1183 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | ||
1146 | emulate_alu_rwflags : emulate_alu_rflags; | 1184 | emulate_alu_rwflags : emulate_alu_rflags; |
1185 | } | ||
1147 | return INSN_GOOD; | 1186 | return INSN_GOOD; |
1148 | } | 1187 | } |
1149 | 1188 | ||
@@ -1170,8 +1209,20 @@ space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
1170 | */ | 1209 | */ |
1171 | insn &= 0xffff0fff; /* Rd = r0 */ | 1210 | insn &= 0xffff0fff; /* Rd = r0 */ |
1172 | asi->insn[0] = insn; | 1211 | asi->insn[0] = insn; |
1173 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | 1212 | |
1213 | if ((insn & 0x0f900000) == 0x03100000) { | ||
1214 | /* | ||
1215 | * TST : cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx | ||
1216 | * TEQ : cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx | ||
1217 | * CMP : cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx | ||
1218 | * CMN : cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx | ||
1219 | */ | ||
1220 | asi->insn_handler = emulate_alu_tests_imm; | ||
1221 | } else { | ||
1222 | /* ALU ops which write to Rd */ | ||
1223 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | ||
1174 | emulate_alu_imm_rwflags : emulate_alu_imm_rflags; | 1224 | emulate_alu_imm_rwflags : emulate_alu_imm_rflags; |
1225 | } | ||
1175 | return INSN_GOOD; | 1226 | return INSN_GOOD; |
1176 | } | 1227 | } |
1177 | 1228 | ||