From 0b5cb7a3d0e9eb3232183c15a5751d2354ec3de1 Mon Sep 17 00:00:00 2001 From: Deng-Cheng Zhu Date: Tue, 22 Nov 2011 03:28:45 +0800 Subject: [PATCH] --- yaml --- r: 284774 b: refs/heads/master c: 2c1b54d331bde7afbf8da24789cce2402e155495 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/arch/mips/include/asm/branch.h | 5 - trunk/arch/mips/include/asm/kprobes.h | 5 - trunk/arch/mips/kernel/branch.c | 128 +++++---------- trunk/arch/mips/kernel/kprobes.c | 177 ++++----------------- trunk/arch/mips/kernel/perf_event_mipsxx.c | 34 +--- trunk/arch/mips/math-emu/cp1emu.c | 2 +- 7 files changed, 77 insertions(+), 276 deletions(-) diff --git a/[refs] b/[refs] index 62db4609b843..85c21f1ac5c7 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 6457a396bbc20656009eaf950ca165912a943520 +refs/heads/master: 2c1b54d331bde7afbf8da24789cce2402e155495 diff --git a/trunk/arch/mips/include/asm/branch.h b/trunk/arch/mips/include/asm/branch.h index 888766ae1f85..37c6857c8d4a 100644 --- a/trunk/arch/mips/include/asm/branch.h +++ b/trunk/arch/mips/include/asm/branch.h @@ -9,7 +9,6 @@ #define _ASM_BRANCH_H #include -#include static inline int delay_slot(struct pt_regs *regs) { @@ -24,11 +23,7 @@ static inline unsigned long exception_epc(struct pt_regs *regs) return regs->cp0_epc + 4; } -#define BRANCH_LIKELY_TAKEN 0x0001 - extern int __compute_return_epc(struct pt_regs *regs); -extern int __compute_return_epc_for_insn(struct pt_regs *regs, - union mips_instruction insn); static inline int compute_return_epc(struct pt_regs *regs) { diff --git a/trunk/arch/mips/include/asm/kprobes.h b/trunk/arch/mips/include/asm/kprobes.h index 1fbbca01e681..e6ea4d4d7205 100644 --- a/trunk/arch/mips/include/asm/kprobes.h +++ b/trunk/arch/mips/include/asm/kprobes.h @@ -74,8 +74,6 @@ struct prev_kprobe { : MAX_JPROBES_STACK_SIZE) -#define SKIP_DELAYSLOT 0x0001 - /* per-cpu kprobe control block */ struct kprobe_ctlblk { unsigned long kprobe_status; @@ -84,9 +82,6 @@ struct kprobe_ctlblk { unsigned long kprobe_saved_epc; unsigned long jprobe_saved_sp; struct pt_regs jprobe_saved_regs; - /* Per-thread fields, used while emulating branches */ - unsigned long flags; - unsigned long target_epc; u8 jprobes_stack[MAX_JPROBES_STACK_SIZE]; struct prev_kprobe prev_kprobe; }; diff --git a/trunk/arch/mips/kernel/branch.c b/trunk/arch/mips/kernel/branch.c index 4d735d0e58f5..32103cc2a257 100644 --- a/trunk/arch/mips/kernel/branch.c +++ b/trunk/arch/mips/kernel/branch.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -18,22 +17,28 @@ #include #include -/** - * __compute_return_epc_for_insn - Computes the return address and do emulate - * branch simulation, if required. - * - * @regs: Pointer to pt_regs - * @insn: branch instruction to decode - * @returns: -EFAULT on error and forces SIGBUS, and on success - * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after - * evaluating the branch. +/* + * Compute the return address and do emulate branch simulation, if required. */ -int __compute_return_epc_for_insn(struct pt_regs *regs, - union mips_instruction insn) +int __compute_return_epc(struct pt_regs *regs) { + unsigned int __user *addr; unsigned int bit, fcr31, dspcontrol; - long epc = regs->cp0_epc; - int ret = 0; + long epc; + union mips_instruction insn; + + epc = regs->cp0_epc; + if (epc & 3) + goto unaligned; + + /* + * Read the instruction + */ + addr = (unsigned int __user *) epc; + if (__get_user(insn.word, addr)) { + force_sig(SIGSEGV, current); + return -EFAULT; + } switch (insn.i_format.opcode) { /* @@ -59,22 +64,18 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, switch (insn.i_format.rt) { case bltz_op: case bltzl_op: - if ((long)regs->regs[insn.i_format.rs] < 0) { + if ((long)regs->regs[insn.i_format.rs] < 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bltzl_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; case bgez_op: case bgezl_op: - if ((long)regs->regs[insn.i_format.rs] >= 0) { + if ((long)regs->regs[insn.i_format.rs] >= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bgezl_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -82,11 +83,9 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bltzal_op: case bltzall_op: regs->regs[31] = epc + 8; - if ((long)regs->regs[insn.i_format.rs] < 0) { + if ((long)regs->regs[insn.i_format.rs] < 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bltzall_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -94,15 +93,12 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bgezal_op: case bgezall_op: regs->regs[31] = epc + 8; - if ((long)regs->regs[insn.i_format.rs] >= 0) { + if ((long)regs->regs[insn.i_format.rs] >= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bgezall_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; - case bposge32_op: if (!cpu_has_dsp) goto sigill; @@ -137,11 +133,9 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case beq_op: case beql_op: if (regs->regs[insn.i_format.rs] == - regs->regs[insn.i_format.rt]) { + regs->regs[insn.i_format.rt]) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == beql_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -149,11 +143,9 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bne_op: case bnel_op: if (regs->regs[insn.i_format.rs] != - regs->regs[insn.i_format.rt]) { + regs->regs[insn.i_format.rt]) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bnel_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -161,11 +153,9 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case blez_op: /* not really i_format */ case blezl_op: /* rt field assumed to be zero */ - if ((long)regs->regs[insn.i_format.rs] <= 0) { + if ((long)regs->regs[insn.i_format.rs] <= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bnel_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -173,11 +163,9 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, case bgtz_op: case bgtzl_op: /* rt field assumed to be zero */ - if ((long)regs->regs[insn.i_format.rs] > 0) { + if ((long)regs->regs[insn.i_format.rs] > 0) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == bnel_op) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -199,22 +187,18 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, switch (insn.i_format.rt & 3) { case 0: /* bc1f */ case 2: /* bc1fl */ - if (~fcr31 & (1 << bit)) { + if (~fcr31 & (1 << bit)) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == 2) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; case 1: /* bc1t */ case 3: /* bc1tl */ - if (fcr31 & (1 << bit)) { + if (fcr31 & (1 << bit)) epc = epc + 4 + (insn.i_format.simmediate << 2); - if (insn.i_format.rt == 3) - ret = BRANCH_LIKELY_TAKEN; - } else + else epc += 8; regs->cp0_epc = epc; break; @@ -255,39 +239,15 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, #endif } - return ret; - -sigill: - printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); - force_sig(SIGBUS, current); - return -EFAULT; -} -EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); - -int __compute_return_epc(struct pt_regs *regs) -{ - unsigned int __user *addr; - long epc; - union mips_instruction insn; - - epc = regs->cp0_epc; - if (epc & 3) - goto unaligned; - - /* - * Read the instruction - */ - addr = (unsigned int __user *) epc; - if (__get_user(insn.word, addr)) { - force_sig(SIGSEGV, current); - return -EFAULT; - } - - return __compute_return_epc_for_insn(regs, insn); + return 0; unaligned: printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); force_sig(SIGBUS, current); return -EFAULT; +sigill: + printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); + force_sig(SIGBUS, current); + return -EFAULT; } diff --git a/trunk/arch/mips/kernel/kprobes.c b/trunk/arch/mips/kernel/kprobes.c index 158467da9bc1..ee28683fc2ac 100644 --- a/trunk/arch/mips/kernel/kprobes.c +++ b/trunk/arch/mips/kernel/kprobes.c @@ -25,12 +25,10 @@ #include #include -#include #include #include #include -#include #include #include @@ -114,49 +112,17 @@ static int __kprobes insn_has_delayslot(union mips_instruction insn) return 0; } -/* - * insn_has_ll_or_sc function checks whether instruction is ll or sc - * one; putting breakpoint on top of atomic ll/sc pair is bad idea; - * so we need to prevent it and refuse kprobes insertion for such - * instructions; cannot do much about breakpoint in the middle of - * ll/sc pair; it is upto user to avoid those places - */ -static int __kprobes insn_has_ll_or_sc(union mips_instruction insn) -{ - int ret = 0; - - switch (insn.i_format.opcode) { - case ll_op: - case lld_op: - case sc_op: - case scd_op: - ret = 1; - break; - default: - break; - } - return ret; -} - int __kprobes arch_prepare_kprobe(struct kprobe *p) { union mips_instruction insn; union mips_instruction prev_insn; int ret = 0; + prev_insn = p->addr[-1]; insn = p->addr[0]; - if (insn_has_ll_or_sc(insn)) { - pr_notice("Kprobes for ll and sc instructions are not" - "supported\n"); - ret = -EINVAL; - goto out; - } - - if ((probe_kernel_read(&prev_insn, p->addr - 1, - sizeof(mips_instruction)) == 0) && - insn_has_delayslot(prev_insn)) { - pr_notice("Kprobes for branch delayslot are not supported\n"); + if (insn_has_delayslot(insn) || insn_has_delayslot(prev_insn)) { + pr_notice("Kprobes for branch and jump instructions are not supported\n"); ret = -EINVAL; goto out; } @@ -172,20 +138,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) * In the kprobe->ainsn.insn[] array we store the original * instruction at index zero and a break trap instruction at * index one. - * - * On MIPS arch if the instruction at probed address is a - * branch instruction, we need to execute the instruction at - * Branch Delayslot (BD) at the time of probe hit. As MIPS also - * doesn't have single stepping support, the BD instruction can - * not be executed in-line and it would be executed on SSOL slot - * using a normal breakpoint instruction in the next slot. - * So, read the instruction and save it for later execution. */ - if (insn_has_delayslot(insn)) - memcpy(&p->ainsn.insn[0], p->addr + 1, sizeof(kprobe_opcode_t)); - else - memcpy(&p->ainsn.insn[0], p->addr, sizeof(kprobe_opcode_t)); + memcpy(&p->ainsn.insn[0], p->addr, sizeof(kprobe_opcode_t)); p->ainsn.insn[1] = breakpoint2_insn; p->opcode = *p->addr; @@ -236,96 +191,16 @@ static void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, kcb->kprobe_saved_epc = regs->cp0_epc; } -/** - * evaluate_branch_instrucion - - * - * Evaluate the branch instruction at probed address during probe hit. The - * result of evaluation would be the updated epc. The insturction in delayslot - * would actually be single stepped using a normal breakpoint) on SSOL slot. - * - * The result is also saved in the kprobe control block for later use, - * in case we need to execute the delayslot instruction. The latter will be - * false for NOP instruction in dealyslot and the branch-likely instructions - * when the branch is taken. And for those cases we set a flag as - * SKIP_DELAYSLOT in the kprobe control block - */ -static int evaluate_branch_instruction(struct kprobe *p, struct pt_regs *regs, - struct kprobe_ctlblk *kcb) +static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) { - union mips_instruction insn = p->opcode; - long epc; - int ret = 0; - - epc = regs->cp0_epc; - if (epc & 3) - goto unaligned; - - if (p->ainsn.insn->word == 0) - kcb->flags |= SKIP_DELAYSLOT; - else - kcb->flags &= ~SKIP_DELAYSLOT; - - ret = __compute_return_epc_for_insn(regs, insn); - if (ret < 0) - return ret; - - if (ret == BRANCH_LIKELY_TAKEN) - kcb->flags |= SKIP_DELAYSLOT; - - kcb->target_epc = regs->cp0_epc; - - return 0; - -unaligned: - pr_notice("%s: unaligned epc - sending SIGBUS.\n", current->comm); - force_sig(SIGBUS, current); - return -EFAULT; - -} - -static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs, - struct kprobe_ctlblk *kcb) -{ - int ret = 0; - regs->cp0_status &= ~ST0_IE; /* single step inline if the instruction is a break */ if (p->opcode.word == breakpoint_insn.word || p->opcode.word == breakpoint2_insn.word) regs->cp0_epc = (unsigned long)p->addr; - else if (insn_has_delayslot(p->opcode)) { - ret = evaluate_branch_instruction(p, regs, kcb); - if (ret < 0) { - pr_notice("Kprobes: Error in evaluating branch\n"); - return; - } - } - regs->cp0_epc = (unsigned long)&p->ainsn.insn[0]; -} - -/* - * Called after single-stepping. p->addr is the address of the - * instruction whose first byte has been replaced by the "break 0" - * instruction. To avoid the SMP problems that can occur when we - * temporarily put back the original opcode to single-step, we - * single-stepped a copy of the instruction. The address of this - * copy is p->ainsn.insn. - * - * This function prepares to return from the post-single-step - * breakpoint trap. In case of branch instructions, the target - * epc to be restored. - */ -static void __kprobes resume_execution(struct kprobe *p, - struct pt_regs *regs, - struct kprobe_ctlblk *kcb) -{ - if (insn_has_delayslot(p->opcode)) - regs->cp0_epc = kcb->target_epc; - else { - unsigned long orig_epc = kcb->kprobe_saved_epc; - regs->cp0_epc = orig_epc + 4; - } + else + regs->cp0_epc = (unsigned long)&p->ainsn.insn[0]; } static int __kprobes kprobe_handler(struct pt_regs *regs) @@ -364,13 +239,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) save_previous_kprobe(kcb); set_current_kprobe(p, regs, kcb); kprobes_inc_nmissed_count(p); - prepare_singlestep(p, regs, kcb); + prepare_singlestep(p, regs); kcb->kprobe_status = KPROBE_REENTER; - if (kcb->flags & SKIP_DELAYSLOT) { - resume_execution(p, regs, kcb); - restore_previous_kprobe(kcb); - preempt_enable_no_resched(); - } return 1; } else { if (addr->word != breakpoint_insn.word) { @@ -414,16 +284,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) } ss_probe: - prepare_singlestep(p, regs, kcb); - if (kcb->flags & SKIP_DELAYSLOT) { - kcb->kprobe_status = KPROBE_HIT_SSDONE; - if (p->post_handler) - p->post_handler(p, regs, 0); - resume_execution(p, regs, kcb); - preempt_enable_no_resched(); - } else - kcb->kprobe_status = KPROBE_HIT_SS; - + prepare_singlestep(p, regs); + kcb->kprobe_status = KPROBE_HIT_SS; return 1; no_kprobe: @@ -432,6 +294,25 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) } +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "break 0" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->ainsn.insn. + * + * This function prepares to return from the post-single-step + * breakpoint trap. + */ +static void __kprobes resume_execution(struct kprobe *p, + struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + unsigned long orig_epc = kcb->kprobe_saved_epc; + regs->cp0_epc = orig_epc + 4; +} + static inline int post_kprobe_handler(struct pt_regs *regs) { struct kprobe *cur = kprobe_running(); diff --git a/trunk/arch/mips/kernel/perf_event_mipsxx.c b/trunk/arch/mips/kernel/perf_event_mipsxx.c index 4f2971bcf8e5..ab4c761cfedc 100644 --- a/trunk/arch/mips/kernel/perf_event_mipsxx.c +++ b/trunk/arch/mips/kernel/perf_event_mipsxx.c @@ -1380,20 +1380,10 @@ static irqreturn_t mipsxx_pmu_handle_irq(int irq, void *dev) } /* 24K */ -#define IS_UNSUPPORTED_24K_EVENT(r, b) \ - ((b) == 12 || (r) == 151 || (r) == 152 || (b) == 26 || \ - (b) == 27 || (r) == 28 || (r) == 158 || (b) == 31 || \ - (b) == 32 || (b) == 34 || (b) == 36 || (r) == 168 || \ - (r) == 172 || (b) == 47 || ((b) >= 56 && (b) <= 63) || \ - ((b) >= 68 && (b) <= 127)) #define IS_BOTH_COUNTERS_24K_EVENT(b) \ ((b) == 0 || (b) == 1 || (b) == 11) /* 34K */ -#define IS_UNSUPPORTED_34K_EVENT(r, b) \ - ((b) == 12 || (r) == 27 || (r) == 158 || (b) == 36 || \ - (b) == 38 || (r) == 175 || ((b) >= 56 && (b) <= 63) || \ - ((b) >= 68 && (b) <= 127)) #define IS_BOTH_COUNTERS_34K_EVENT(b) \ ((b) == 0 || (b) == 1 || (b) == 11) #ifdef CONFIG_MIPS_MT_SMP @@ -1406,20 +1396,10 @@ static irqreturn_t mipsxx_pmu_handle_irq(int irq, void *dev) #endif /* 74K */ -#define IS_UNSUPPORTED_74K_EVENT(r, b) \ - ((r) == 5 || ((r) >= 135 && (r) <= 137) || \ - ((b) >= 10 && (b) <= 12) || (b) == 22 || (b) == 27 || \ - (b) == 33 || (b) == 34 || ((b) >= 47 && (b) <= 49) || \ - (r) == 178 || (b) == 55 || (b) == 57 || (b) == 60 || \ - (b) == 61 || (r) == 62 || (r) == 191 || \ - ((b) >= 64 && (b) <= 127)) #define IS_BOTH_COUNTERS_74K_EVENT(b) \ ((b) == 0 || (b) == 1) /* 1004K */ -#define IS_UNSUPPORTED_1004K_EVENT(r, b) \ - ((b) == 12 || (r) == 27 || (r) == 158 || (b) == 38 || \ - (r) == 175 || (b) == 63 || ((b) >= 68 && (b) <= 127)) #define IS_BOTH_COUNTERS_1004K_EVENT(b) \ ((b) == 0 || (b) == 1 || (b) == 11) #ifdef CONFIG_MIPS_MT_SMP @@ -1445,11 +1425,10 @@ static const struct mips_perf_event *mipsxx_pmu_map_raw_event(u64 config) unsigned int raw_id = config & 0xff; unsigned int base_id = raw_id & 0x7f; + raw_event.event_id = base_id; + switch (current_cpu_type()) { case CPU_24K: - if (IS_UNSUPPORTED_24K_EVENT(raw_id, base_id)) - return ERR_PTR(-EOPNOTSUPP); - raw_event.event_id = base_id; if (IS_BOTH_COUNTERS_24K_EVENT(base_id)) raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD; else @@ -1464,9 +1443,6 @@ static const struct mips_perf_event *mipsxx_pmu_map_raw_event(u64 config) #endif break; case CPU_34K: - if (IS_UNSUPPORTED_34K_EVENT(raw_id, base_id)) - return ERR_PTR(-EOPNOTSUPP); - raw_event.event_id = base_id; if (IS_BOTH_COUNTERS_34K_EVENT(base_id)) raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD; else @@ -1482,9 +1458,6 @@ static const struct mips_perf_event *mipsxx_pmu_map_raw_event(u64 config) #endif break; case CPU_74K: - if (IS_UNSUPPORTED_74K_EVENT(raw_id, base_id)) - return ERR_PTR(-EOPNOTSUPP); - raw_event.event_id = base_id; if (IS_BOTH_COUNTERS_74K_EVENT(base_id)) raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD; else @@ -1495,9 +1468,6 @@ static const struct mips_perf_event *mipsxx_pmu_map_raw_event(u64 config) #endif break; case CPU_1004K: - if (IS_UNSUPPORTED_1004K_EVENT(raw_id, base_id)) - return ERR_PTR(-EOPNOTSUPP); - raw_event.event_id = base_id; if (IS_BOTH_COUNTERS_1004K_EVENT(base_id)) raw_event.cntr_mask = CNTR_EVEN | CNTR_ODD; else diff --git a/trunk/arch/mips/math-emu/cp1emu.c b/trunk/arch/mips/math-emu/cp1emu.c index a03bf00a1a9c..dbf2f93a5091 100644 --- a/trunk/arch/mips/math-emu/cp1emu.c +++ b/trunk/arch/mips/math-emu/cp1emu.c @@ -245,7 +245,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, */ emulpc = xcp->cp0_epc + 4; /* Snapshot emulation target */ - if (__compute_return_epc(xcp) < 0) { + if (__compute_return_epc(xcp)) { #ifdef CP1DBG printk("failed to emulate branch at %p\n", (void *) (xcp->cp0_epc));