|
@@ -13,11 +13,13 @@
|
|
|
#include <linux/perf_event.h>
|
|
|
#include <linux/percpu.h>
|
|
|
#include <linux/hardirq.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
#include <asm/reg.h>
|
|
|
#include <asm/pmc.h>
|
|
|
#include <asm/machdep.h>
|
|
|
#include <asm/firmware.h>
|
|
|
#include <asm/ptrace.h>
|
|
|
+#include <asm/code-patching.h>
|
|
|
|
|
|
#define BHRB_MAX_ENTRIES 32
|
|
|
#define BHRB_TARGET 0x0000000000000002
|
|
@@ -362,6 +364,32 @@ void power_pmu_flush_branch_stack(void)
|
|
|
if (ppmu->bhrb_nr)
|
|
|
power_pmu_bhrb_reset();
|
|
|
}
|
|
|
+/* Calculate the to address for a branch */
|
|
|
+static __u64 power_pmu_bhrb_to(u64 addr)
|
|
|
+{
|
|
|
+ unsigned int instr;
|
|
|
+ int ret;
|
|
|
+ __u64 target;
|
|
|
+
|
|
|
+ if (is_kernel_addr(addr))
|
|
|
+ return branch_target((unsigned int *)addr);
|
|
|
+
|
|
|
+ /* Userspace: need copy instruction here then translate it */
|
|
|
+ pagefault_disable();
|
|
|
+ ret = __get_user_inatomic(instr, (unsigned int __user *)addr);
|
|
|
+ if (ret) {
|
|
|
+ pagefault_enable();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ pagefault_enable();
|
|
|
+
|
|
|
+ target = branch_target(&instr);
|
|
|
+ if ((!target) || (instr & BRANCH_ABSOLUTE))
|
|
|
+ return target;
|
|
|
+
|
|
|
+ /* Translate relative branch target from kernel to user address */
|
|
|
+ return target - (unsigned long)&instr + addr;
|
|
|
+}
|
|
|
|
|
|
/* Processing BHRB entries */
|
|
|
void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
|
|
@@ -426,7 +454,8 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
|
|
|
/* Branches to immediate field
|
|
|
(ie I or B form) */
|
|
|
cpuhw->bhrb_entries[u_index].from = addr;
|
|
|
- cpuhw->bhrb_entries[u_index].to = 0;
|
|
|
+ cpuhw->bhrb_entries[u_index].to =
|
|
|
+ power_pmu_bhrb_to(addr);
|
|
|
cpuhw->bhrb_entries[u_index].mispred = pred;
|
|
|
cpuhw->bhrb_entries[u_index].predicted = ~pred;
|
|
|
}
|