Merge tag 'powerpc-4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[linux-block.git] / arch / powerpc / perf / core-book3s.c
index f8908ea4ea736487a719f52650f6bff380961e62..3f66fcf8ad99ba06a45af3ff7f9f74c254fe3acd 100644 (file)
@@ -198,6 +198,10 @@ static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp)
 
        if (!(mmcra & MMCRA_SAMPLE_ENABLE) || sdar_valid)
                *addrp = mfspr(SPRN_SDAR);
+
+       if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN) &&
+               is_kernel_addr(mfspr(SPRN_SDAR)))
+               *addrp = 0;
 }
 
 static bool regs_sihv(struct pt_regs *regs)
@@ -457,6 +461,16 @@ static void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
                                /* invalid entry */
                                continue;
 
+                       /*
+                        * BHRB rolling buffer could very much contain the kernel
+                        * addresses at this point. Check the privileges before
+                        * exporting it to userspace (avoid exposure of regions
+                        * where we could have speculative execution)
+                        */
+                       if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN) &&
+                               is_kernel_addr(addr))
+                               continue;
+
                        /* Branches are read most recent first (ie. mfbhrb 0 is
                         * the most recent branch).
                         * There are two types of valid entries:
@@ -1226,6 +1240,7 @@ static void power_pmu_disable(struct pmu *pmu)
                 */
                write_mmcr0(cpuhw, val);
                mb();
+               isync();
 
                /*
                 * Disable instruction sampling if it was enabled
@@ -1234,12 +1249,26 @@ static void power_pmu_disable(struct pmu *pmu)
                        mtspr(SPRN_MMCRA,
                              cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE);
                        mb();
+                       isync();
                }
 
                cpuhw->disabled = 1;
                cpuhw->n_added = 0;
 
                ebb_switch_out(mmcr0);
+
+#ifdef CONFIG_PPC64
+               /*
+                * These are readable by userspace, may contain kernel
+                * addresses and are not switched by context switch, so clear
+                * them now to avoid leaking anything to userspace in general
+                * including to another process.
+                */
+               if (ppmu->flags & PPMU_ARCH_207S) {
+                       mtspr(SPRN_SDAR, 0);
+                       mtspr(SPRN_SIAR, 0);
+               }
+#endif
        }
 
        local_irq_restore(flags);
@@ -1810,6 +1839,18 @@ static int hw_perf_cache_event(u64 config, u64 *eventp)
        return 0;
 }
 
+static bool is_event_blacklisted(u64 ev)
+{
+       int i;
+
+       for (i=0; i < ppmu->n_blacklist_ev; i++) {
+               if (ppmu->blacklist_ev[i] == ev)
+                       return true;
+       }
+
+       return false;
+}
+
 static int power_pmu_event_init(struct perf_event *event)
 {
        u64 ev;
@@ -1835,15 +1876,24 @@ static int power_pmu_event_init(struct perf_event *event)
                ev = event->attr.config;
                if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0)
                        return -EOPNOTSUPP;
+
+               if (ppmu->blacklist_ev && is_event_blacklisted(ev))
+                       return -EINVAL;
                ev = ppmu->generic_events[ev];
                break;
        case PERF_TYPE_HW_CACHE:
                err = hw_perf_cache_event(event->attr.config, &ev);
                if (err)
                        return err;
+
+               if (ppmu->blacklist_ev && is_event_blacklisted(ev))
+                       return -EINVAL;
                break;
        case PERF_TYPE_RAW:
                ev = event->attr.config;
+
+               if (ppmu->blacklist_ev && is_event_blacklisted(ev))
+                       return -EINVAL;
                break;
        default:
                return -ENOENT;