diff --git a/sys/dev/hwpmc/hwpmc_core.c b/sys/dev/hwpmc/hwpmc_core.c index afd587296e01..507b20488132 100644 --- a/sys/dev/hwpmc/hwpmc_core.c +++ b/sys/dev/hwpmc/hwpmc_core.c @@ -1,1344 +1,1347 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Joseph Koshy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Intel Core PMCs. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #define CORE_CPUID_REQUEST 0xA #define CORE_CPUID_REQUEST_SIZE 0x4 #define CORE_CPUID_EAX 0x0 #define CORE_CPUID_EBX 0x1 #define CORE_CPUID_ECX 0x2 #define CORE_CPUID_EDX 0x3 #define IAF_PMC_CAPS \ (PMC_CAP_READ | PMC_CAP_WRITE | PMC_CAP_INTERRUPT | \ PMC_CAP_USER | PMC_CAP_SYSTEM) #define IAF_RI_TO_MSR(RI) ((RI) + (1 << 30)) #define IAP_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \ PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \ PMC_CAP_INVERT | PMC_CAP_QUALIFIER | PMC_CAP_PRECISE) #define EV_IS_NOTARCH 0 #define EV_IS_ARCH_SUPP 1 #define EV_IS_ARCH_NOTSUPP -1 /* * "Architectural" events defined by Intel. The values of these * symbols correspond to positions in the bitmask returned by * the CPUID.0AH instruction. */ enum core_arch_events { CORE_AE_BRANCH_INSTRUCTION_RETIRED = 5, CORE_AE_BRANCH_MISSES_RETIRED = 6, CORE_AE_INSTRUCTION_RETIRED = 1, CORE_AE_LLC_MISSES = 4, CORE_AE_LLC_REFERENCE = 3, CORE_AE_UNHALTED_REFERENCE_CYCLES = 2, CORE_AE_UNHALTED_CORE_CYCLES = 0 }; static enum pmc_cputype core_cputype; struct core_cpu { volatile uint32_t pc_resync; volatile uint32_t pc_iafctrl; /* Fixed function control. */ volatile uint64_t pc_globalctrl; /* Global control register. */ struct pmc_hw pc_corepmcs[]; }; static struct core_cpu **core_pcpu; static uint32_t core_architectural_events; static uint64_t core_pmcmask; static int core_iaf_ri; /* relative index of fixed counters */ static int core_iaf_width; static int core_iaf_npmc; static int core_iap_width; static int core_iap_npmc; static int core_iap_wroffset; static u_int pmc_alloc_refs; static bool pmc_tsx_force_abort_set; static int core_pcpu_noop(struct pmc_mdep *md, int cpu) { (void) md; (void) cpu; return (0); } static int core_pcpu_init(struct pmc_mdep *md, int cpu) { struct pmc_cpu *pc; struct core_cpu *cc; struct pmc_hw *phw; int core_ri, n, npmc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[iaf,%d] insane cpu number %d", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"core-init cpu=%d", cpu); core_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAP].pcd_ri; npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAP].pcd_num; if (core_cputype != PMC_CPU_INTEL_CORE) npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAF].pcd_num; cc = malloc(sizeof(struct core_cpu) + npmc * sizeof(struct pmc_hw), M_PMC, M_WAITOK | M_ZERO); core_pcpu[cpu] = cc; pc = pmc_pcpu[cpu]; KASSERT(pc != NULL && cc != NULL, ("[core,%d] NULL per-cpu structures cpu=%d", __LINE__, cpu)); for (n = 0, phw = cc->pc_corepmcs; n < npmc; n++, phw++) { phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n + core_ri); phw->phw_pmc = NULL; pc->pc_hwpmcs[n + core_ri] = phw; } return (0); } static int core_pcpu_fini(struct pmc_mdep *md, int cpu) { int core_ri, n, npmc; struct pmc_cpu *pc; struct core_cpu *cc; uint64_t msr = 0; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] insane cpu number (%d)", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"core-pcpu-fini cpu=%d", cpu); if ((cc = core_pcpu[cpu]) == NULL) return (0); core_pcpu[cpu] = NULL; pc = pmc_pcpu[cpu]; KASSERT(pc != NULL, ("[core,%d] NULL per-cpu %d state", __LINE__, cpu)); npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAP].pcd_num; core_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAP].pcd_ri; for (n = 0; n < npmc; n++) { msr = rdmsr(IAP_EVSEL0 + n) & ~IAP_EVSEL_MASK; wrmsr(IAP_EVSEL0 + n, msr); } if (core_cputype != PMC_CPU_INTEL_CORE) { msr = rdmsr(IAF_CTRL) & ~IAF_CTRL_MASK; wrmsr(IAF_CTRL, msr); npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAF].pcd_num; } for (n = 0; n < npmc; n++) pc->pc_hwpmcs[n + core_ri] = NULL; free(cc, M_PMC); return (0); } /* * Fixed function counters. */ static pmc_value_t iaf_perfctr_value_to_reload_count(pmc_value_t v) { /* If the PMC has overflowed, return a reload count of zero. */ if ((v & (1ULL << (core_iaf_width - 1))) == 0) return (0); v &= (1ULL << core_iaf_width) - 1; return (1ULL << core_iaf_width) - v; } static pmc_value_t iaf_reload_count_to_perfctr_value(pmc_value_t rlc) { return (1ULL << core_iaf_width) - rlc; } static int iaf_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { uint8_t ev, umask; uint32_t caps, flags, config; const struct pmc_md_iap_op_pmcallocate *iap; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU %d", __LINE__, cpu)); PMCDBG2(MDP,ALL,1, "iaf-allocate ri=%d reqcaps=0x%x", ri, pm->pm_caps); if (ri < 0 || ri > core_iaf_npmc) return (EINVAL); caps = a->pm_caps; if (a->pm_class != PMC_CLASS_IAF || (caps & IAF_PMC_CAPS) != caps) return (EINVAL); iap = &a->pm_md.pm_iap; config = iap->pm_iap_config; ev = IAP_EVSEL_GET(config); umask = IAP_UMASK_GET(config); /* INST_RETIRED.ANY */ if (ev == 0xC0 && ri != 0) return (EINVAL); /* CPU_CLK_UNHALTED.THREAD */ if (ev == 0x3C && ri != 1) return (EINVAL); /* CPU_CLK_UNHALTED.REF */ if (ev == 0x0 && umask == 0x3 && ri != 2) return (EINVAL); pmc_alloc_refs++; if ((cpu_stdext_feature3 & CPUID_STDEXT3_TSXFA) != 0 && !pmc_tsx_force_abort_set) { pmc_tsx_force_abort_set = true; x86_msr_op(MSR_TSX_FORCE_ABORT, MSR_OP_RENDEZVOUS_ALL | MSR_OP_WRITE, 1, NULL); } flags = 0; if (config & IAP_OS) flags |= IAF_OS; if (config & IAP_USR) flags |= IAF_USR; if (config & IAP_ANY) flags |= IAF_ANY; if (config & IAP_INT) flags |= IAF_PMI; if (caps & PMC_CAP_INTERRUPT) flags |= IAF_PMI; if (caps & PMC_CAP_SYSTEM) flags |= IAF_OS; if (caps & PMC_CAP_USER) flags |= IAF_USR; if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) flags |= (IAF_OS | IAF_USR); pm->pm_md.pm_iaf.pm_iaf_ctrl = (flags << (ri * 4)); PMCDBG1(MDP,ALL,2, "iaf-allocate config=0x%jx", (uintmax_t) pm->pm_md.pm_iaf.pm_iaf_ctrl); return (0); } static int iaf_config_pmc(int cpu, int ri, struct pmc *pm) { KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); PMCDBG3(MDP,CFG,1, "iaf-config cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(core_pcpu[cpu] != NULL, ("[core,%d] null per-cpu %d", __LINE__, cpu)); core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri].phw_pmc = pm; return (0); } static int iaf_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char iaf_name[PMC_NAME_MAX]; phw = &core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri]; (void) snprintf(iaf_name, sizeof(iaf_name), "IAF-%d", ri); if ((error = copystr(iaf_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return (error); pi->pm_class = PMC_CLASS_IAF; if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { pi->pm_enabled = TRUE; *ppmc = phw->phw_pmc; } else { pi->pm_enabled = FALSE; *ppmc = NULL; } return (0); } static int iaf_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri].phw_pmc; return (0); } static int iaf_get_msr(int ri, uint32_t *msr) { KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[iaf,%d] ri %d out of range", __LINE__, ri)); *msr = IAF_RI_TO_MSR(ri); return (0); } static int iaf_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); pm = core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu %d ri %d(%d) pmc not configured", __LINE__, cpu, ri, ri + core_iaf_ri)); tmp = rdpmc(IAF_RI_TO_MSR(ri)); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = iaf_perfctr_value_to_reload_count(tmp); else *v = tmp & ((1ULL << core_iaf_width) - 1); PMCDBG4(MDP,REA,1, "iaf-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri, IAF_RI_TO_MSR(ri), *v); return (0); } static int iaf_release_pmc(int cpu, int ri, struct pmc *pmc) { PMCDBG3(MDP,REL,1, "iaf-release cpu=%d ri=%d pm=%p", cpu, ri, pmc); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); KASSERT(core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri].phw_pmc == NULL, ("[core,%d] PHW pmc non-NULL", __LINE__)); MPASS(pmc_alloc_refs > 0); if (pmc_alloc_refs-- == 1 && pmc_tsx_force_abort_set) { pmc_tsx_force_abort_set = false; x86_msr_op(MSR_TSX_FORCE_ABORT, MSR_OP_RENDEZVOUS_ALL | MSR_OP_WRITE, 0, NULL); } return (0); } static int iaf_start_pmc(int cpu, int ri) { struct pmc *pm; struct core_cpu *iafc; uint64_t msr = 0; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); PMCDBG2(MDP,STA,1,"iaf-start cpu=%d ri=%d", cpu, ri); iafc = core_pcpu[cpu]; pm = iafc->pc_corepmcs[ri + core_iaf_ri].phw_pmc; iafc->pc_iafctrl |= pm->pm_md.pm_iaf.pm_iaf_ctrl; msr = rdmsr(IAF_CTRL) & ~IAF_CTRL_MASK; wrmsr(IAF_CTRL, msr | (iafc->pc_iafctrl & IAF_CTRL_MASK)); do { iafc->pc_resync = 0; iafc->pc_globalctrl |= (1ULL << (ri + IAF_OFFSET)); msr = rdmsr(IA_GLOBAL_CTRL) & ~IAF_GLOBAL_CTRL_MASK; wrmsr(IA_GLOBAL_CTRL, msr | (iafc->pc_globalctrl & IAF_GLOBAL_CTRL_MASK)); } while (iafc->pc_resync != 0); PMCDBG4(MDP,STA,1,"iafctrl=%x(%x) globalctrl=%jx(%jx)", iafc->pc_iafctrl, (uint32_t) rdmsr(IAF_CTRL), iafc->pc_globalctrl, rdmsr(IA_GLOBAL_CTRL)); return (0); } static int iaf_stop_pmc(int cpu, int ri) { uint32_t fc; struct core_cpu *iafc; uint64_t msr = 0; PMCDBG2(MDP,STO,1,"iaf-stop cpu=%d ri=%d", cpu, ri); iafc = core_pcpu[cpu]; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); fc = (IAF_MASK << (ri * 4)); iafc->pc_iafctrl &= ~fc; PMCDBG1(MDP,STO,1,"iaf-stop iafctrl=%x", iafc->pc_iafctrl); msr = rdmsr(IAF_CTRL) & ~IAF_CTRL_MASK; wrmsr(IAF_CTRL, msr | (iafc->pc_iafctrl & IAF_CTRL_MASK)); do { iafc->pc_resync = 0; iafc->pc_globalctrl &= ~(1ULL << (ri + IAF_OFFSET)); msr = rdmsr(IA_GLOBAL_CTRL) & ~IAF_GLOBAL_CTRL_MASK; wrmsr(IA_GLOBAL_CTRL, msr | (iafc->pc_globalctrl & IAF_GLOBAL_CTRL_MASK)); } while (iafc->pc_resync != 0); PMCDBG4(MDP,STO,1,"iafctrl=%x(%x) globalctrl=%jx(%jx)", iafc->pc_iafctrl, (uint32_t) rdmsr(IAF_CTRL), iafc->pc_globalctrl, rdmsr(IA_GLOBAL_CTRL)); return (0); } static int iaf_write_pmc(int cpu, int ri, pmc_value_t v) { struct core_cpu *cc; struct pmc *pm; uint64_t msr; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iaf_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); cc = core_pcpu[cpu]; pm = cc->pc_corepmcs[ri + core_iaf_ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = iaf_reload_count_to_perfctr_value(v); /* Turn off fixed counters */ msr = rdmsr(IAF_CTRL) & ~IAF_CTRL_MASK; wrmsr(IAF_CTRL, msr); wrmsr(IAF_CTR0 + ri, v & ((1ULL << core_iaf_width) - 1)); /* Turn on fixed counters */ msr = rdmsr(IAF_CTRL) & ~IAF_CTRL_MASK; wrmsr(IAF_CTRL, msr | (cc->pc_iafctrl & IAF_CTRL_MASK)); PMCDBG6(MDP,WRI,1, "iaf-write cpu=%d ri=%d msr=0x%x v=%jx iafctrl=%jx " "pmc=%jx", cpu, ri, IAF_RI_TO_MSR(ri), v, (uintmax_t) rdmsr(IAF_CTRL), (uintmax_t) rdpmc(IAF_RI_TO_MSR(ri))); return (0); } static void iaf_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth) { struct pmc_classdep *pcd; KASSERT(md != NULL, ("[iaf,%d] md is NULL", __LINE__)); PMCDBG0(MDP,INI,1, "iaf-initialize"); pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAF]; pcd->pcd_caps = IAF_PMC_CAPS; pcd->pcd_class = PMC_CLASS_IAF; pcd->pcd_num = npmc; pcd->pcd_ri = md->pmd_npmc; pcd->pcd_width = pmcwidth; pcd->pcd_allocate_pmc = iaf_allocate_pmc; pcd->pcd_config_pmc = iaf_config_pmc; pcd->pcd_describe = iaf_describe; pcd->pcd_get_config = iaf_get_config; pcd->pcd_get_msr = iaf_get_msr; pcd->pcd_pcpu_fini = core_pcpu_noop; pcd->pcd_pcpu_init = core_pcpu_noop; pcd->pcd_read_pmc = iaf_read_pmc; pcd->pcd_release_pmc = iaf_release_pmc; pcd->pcd_start_pmc = iaf_start_pmc; pcd->pcd_stop_pmc = iaf_stop_pmc; pcd->pcd_write_pmc = iaf_write_pmc; md->pmd_npmc += npmc; } /* * Intel programmable PMCs. */ /* Sub fields of UMASK that this event supports. */ #define IAP_M_CORE (1 << 0) /* Core specificity */ #define IAP_M_AGENT (1 << 1) /* Agent specificity */ #define IAP_M_PREFETCH (1 << 2) /* Prefetch */ #define IAP_M_MESI (1 << 3) /* MESI */ #define IAP_M_SNOOPRESPONSE (1 << 4) /* Snoop response */ #define IAP_M_SNOOPTYPE (1 << 5) /* Snoop type */ #define IAP_M_TRANSITION (1 << 6) /* Transition */ #define IAP_F_CORE (0x3 << 14) /* Core specificity */ #define IAP_F_AGENT (0x1 << 13) /* Agent specificity */ #define IAP_F_PREFETCH (0x3 << 12) /* Prefetch */ #define IAP_F_MESI (0xF << 8) /* MESI */ #define IAP_F_SNOOPRESPONSE (0xB << 8) /* Snoop response */ #define IAP_F_SNOOPTYPE (0x3 << 8) /* Snoop type */ #define IAP_F_TRANSITION (0x1 << 12) /* Transition */ #define IAP_PREFETCH_RESERVED (0x2 << 12) #define IAP_CORE_THIS (0x1 << 14) #define IAP_CORE_ALL (0x3 << 14) #define IAP_F_CMASK 0xFF000000 static pmc_value_t iap_perfctr_value_to_reload_count(pmc_value_t v) { /* If the PMC has overflowed, return a reload count of zero. */ if ((v & (1ULL << (core_iap_width - 1))) == 0) return (0); v &= (1ULL << core_iap_width) - 1; return (1ULL << core_iap_width) - v; } static pmc_value_t iap_reload_count_to_perfctr_value(pmc_value_t rlc) { return (1ULL << core_iap_width) - rlc; } static int iap_pmc_has_overflowed(int ri) { uint64_t v; /* * We treat a Core (i.e., Intel architecture v1) PMC as has * having overflowed if its MSB is zero. */ v = rdpmc(ri); return ((v & (1ULL << (core_iap_width - 1))) == 0); } static int iap_event_corei7_ok_on_counter(uint8_t evsel, int ri) { uint32_t mask; switch (evsel) { /* * Events valid only on counter 0, 1. */ case 0x40: case 0x41: case 0x42: case 0x43: case 0x51: case 0x63: mask = 0x3; break; default: mask = ~0; /* Any row index is ok. */ } return (mask & (1 << ri)); } static int iap_event_westmere_ok_on_counter(uint8_t evsel, int ri) { uint32_t mask; switch (evsel) { /* * Events valid only on counter 0. */ case 0x60: case 0xB3: mask = 0x1; break; /* * Events valid only on counter 0, 1. */ case 0x4C: case 0x4E: case 0x51: case 0x63: mask = 0x3; break; default: mask = ~0; /* Any row index is ok. */ } return (mask & (1 << ri)); } static int iap_event_sb_sbx_ib_ibx_ok_on_counter(uint8_t evsel, int ri) { uint32_t mask; switch (evsel) { /* Events valid only on counter 0. */ case 0xB7: mask = 0x1; break; /* Events valid only on counter 1. */ case 0xC0: mask = 0x2; break; /* Events valid only on counter 2. */ case 0x48: case 0xA2: case 0xA3: mask = 0x4; break; /* Events valid only on counter 3. */ case 0xBB: case 0xCD: mask = 0x8; break; default: mask = ~0; /* Any row index is ok. */ } return (mask & (1 << ri)); } static int iap_event_ok_on_counter(uint8_t evsel, int ri) { uint32_t mask; switch (evsel) { /* * Events valid only on counter 0. */ case 0x10: case 0x14: case 0x18: case 0xB3: case 0xC1: case 0xCB: mask = (1 << 0); break; /* * Events valid only on counter 1. */ case 0x11: case 0x12: case 0x13: mask = (1 << 1); break; default: mask = ~0; /* Any row index is ok. */ } return (mask & (1 << ri)); } static int iap_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { uint8_t ev; uint32_t caps; const struct pmc_md_iap_op_pmcallocate *iap; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row-index value %d", __LINE__, ri)); + if (a->pm_class != PMC_CLASS_IAP) + return (EINVAL); + /* check requested capabilities */ caps = a->pm_caps; if ((IAP_PMC_CAPS & caps) != caps) return (EPERM); iap = &a->pm_md.pm_iap; ev = IAP_EVSEL_GET(iap->pm_iap_config); switch (core_cputype) { case PMC_CPU_INTEL_COREI7: case PMC_CPU_INTEL_NEHALEM_EX: if (iap_event_corei7_ok_on_counter(ev, ri) == 0) return (EINVAL); break; case PMC_CPU_INTEL_SKYLAKE: case PMC_CPU_INTEL_SKYLAKE_XEON: case PMC_CPU_INTEL_BROADWELL: case PMC_CPU_INTEL_BROADWELL_XEON: case PMC_CPU_INTEL_SANDYBRIDGE: case PMC_CPU_INTEL_SANDYBRIDGE_XEON: case PMC_CPU_INTEL_IVYBRIDGE: case PMC_CPU_INTEL_IVYBRIDGE_XEON: case PMC_CPU_INTEL_HASWELL: case PMC_CPU_INTEL_HASWELL_XEON: if (iap_event_sb_sbx_ib_ibx_ok_on_counter(ev, ri) == 0) return (EINVAL); break; case PMC_CPU_INTEL_WESTMERE: case PMC_CPU_INTEL_WESTMERE_EX: if (iap_event_westmere_ok_on_counter(ev, ri) == 0) return (EINVAL); break; default: if (iap_event_ok_on_counter(ev, ri) == 0) return (EINVAL); } pm->pm_md.pm_iap.pm_iap_evsel = iap->pm_iap_config; return (0); } static int iap_config_pmc(int cpu, int ri, struct pmc *pm) { KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); PMCDBG3(MDP,CFG,1, "iap-config cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(core_pcpu[cpu] != NULL, ("[core,%d] null per-cpu %d", __LINE__, cpu)); core_pcpu[cpu]->pc_corepmcs[ri].phw_pmc = pm; return (0); } static int iap_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char iap_name[PMC_NAME_MAX]; phw = &core_pcpu[cpu]->pc_corepmcs[ri]; (void) snprintf(iap_name, sizeof(iap_name), "IAP-%d", ri); if ((error = copystr(iap_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return (error); pi->pm_class = PMC_CLASS_IAP; if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { pi->pm_enabled = TRUE; *ppmc = phw->phw_pmc; } else { pi->pm_enabled = FALSE; *ppmc = NULL; } return (0); } static int iap_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = core_pcpu[cpu]->pc_corepmcs[ri].phw_pmc; return (0); } static int iap_get_msr(int ri, uint32_t *msr) { KASSERT(ri >= 0 && ri < core_iap_npmc, ("[iap,%d] ri %d out of range", __LINE__, ri)); *msr = ri; return (0); } static int iap_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); pm = core_pcpu[cpu]->pc_corepmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); tmp = rdpmc(ri); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = iap_perfctr_value_to_reload_count(tmp); else *v = tmp & ((1ULL << core_iap_width) - 1); PMCDBG4(MDP,REA,1, "iap-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri, IAP_PMC0 + ri, *v); return (0); } static int iap_release_pmc(int cpu, int ri, struct pmc *pm) { (void) pm; PMCDBG3(MDP,REL,1, "iap-release cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); KASSERT(core_pcpu[cpu]->pc_corepmcs[ri].phw_pmc == NULL, ("[core,%d] PHW pmc non-NULL", __LINE__)); return (0); } static int iap_start_pmc(int cpu, int ri) { struct pmc *pm; uint32_t evsel; struct core_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row-index %d", __LINE__, ri)); cc = core_pcpu[cpu]; pm = cc->pc_corepmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] starting cpu%d,ri%d with no pmc configured", __LINE__, cpu, ri)); PMCDBG2(MDP,STA,1, "iap-start cpu=%d ri=%d", cpu, ri); evsel = pm->pm_md.pm_iap.pm_iap_evsel; PMCDBG4(MDP,STA,2, "iap-start/2 cpu=%d ri=%d evselmsr=0x%x evsel=0x%x", cpu, ri, IAP_EVSEL0 + ri, evsel); /* Event specific configuration. */ switch (IAP_EVSEL_GET(evsel)) { case 0xB7: wrmsr(IA_OFFCORE_RSP0, pm->pm_md.pm_iap.pm_iap_rsp); break; case 0xBB: wrmsr(IA_OFFCORE_RSP1, pm->pm_md.pm_iap.pm_iap_rsp); break; default: break; } wrmsr(IAP_EVSEL0 + ri, evsel | IAP_EN); if (core_cputype == PMC_CPU_INTEL_CORE) return (0); do { cc->pc_resync = 0; cc->pc_globalctrl |= (1ULL << ri); wrmsr(IA_GLOBAL_CTRL, cc->pc_globalctrl); } while (cc->pc_resync != 0); return (0); } static int iap_stop_pmc(int cpu, int ri) { struct pmc *pm __diagused; struct core_cpu *cc; uint64_t msr; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row index %d", __LINE__, ri)); cc = core_pcpu[cpu]; pm = cc->pc_corepmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu%d ri%d no configured PMC to stop", __LINE__, cpu, ri)); PMCDBG2(MDP,STO,1, "iap-stop cpu=%d ri=%d", cpu, ri); msr = rdmsr(IAP_EVSEL0 + ri) & ~IAP_EVSEL_MASK; wrmsr(IAP_EVSEL0 + ri, msr); /* stop hw */ if (core_cputype == PMC_CPU_INTEL_CORE) return (0); do { cc->pc_resync = 0; cc->pc_globalctrl &= ~(1ULL << ri); msr = rdmsr(IA_GLOBAL_CTRL) & ~IA_GLOBAL_CTRL_MASK; wrmsr(IA_GLOBAL_CTRL, cc->pc_globalctrl); } while (cc->pc_resync != 0); return (0); } static int iap_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; struct core_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[core,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < core_iap_npmc, ("[core,%d] illegal row index %d", __LINE__, ri)); cc = core_pcpu[cpu]; pm = cc->pc_corepmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu%d ri%d no configured PMC to stop", __LINE__, cpu, ri)); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = iap_reload_count_to_perfctr_value(v); v &= (1ULL << core_iap_width) - 1; PMCDBG4(MDP,WRI,1, "iap-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri, IAP_PMC0 + ri, v); /* * Write the new value to the counter (or it's alias). The * counter will be in a stopped state when the pcd_write() * entry point is called. */ wrmsr(core_iap_wroffset + IAP_PMC0 + ri, v); return (0); } static void iap_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth, int flags) { struct pmc_classdep *pcd; KASSERT(md != NULL, ("[iap,%d] md is NULL", __LINE__)); PMCDBG0(MDP,INI,1, "iap-initialize"); /* Remember the set of architectural events supported. */ core_architectural_events = ~flags; pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IAP]; pcd->pcd_caps = IAP_PMC_CAPS; pcd->pcd_class = PMC_CLASS_IAP; pcd->pcd_num = npmc; pcd->pcd_ri = md->pmd_npmc; pcd->pcd_width = pmcwidth; pcd->pcd_allocate_pmc = iap_allocate_pmc; pcd->pcd_config_pmc = iap_config_pmc; pcd->pcd_describe = iap_describe; pcd->pcd_get_config = iap_get_config; pcd->pcd_get_msr = iap_get_msr; pcd->pcd_pcpu_fini = core_pcpu_fini; pcd->pcd_pcpu_init = core_pcpu_init; pcd->pcd_read_pmc = iap_read_pmc; pcd->pcd_release_pmc = iap_release_pmc; pcd->pcd_start_pmc = iap_start_pmc; pcd->pcd_stop_pmc = iap_stop_pmc; pcd->pcd_write_pmc = iap_write_pmc; md->pmd_npmc += npmc; } static int core_intr(struct trapframe *tf) { pmc_value_t v; struct pmc *pm; struct core_cpu *cc; int error, found_interrupt, ri; uint64_t msr; PMCDBG3(MDP,INT, 1, "cpu=%d tf=0x%p um=%d", curcpu, (void *) tf, TRAPF_USERMODE(tf)); found_interrupt = 0; cc = core_pcpu[curcpu]; for (ri = 0; ri < core_iap_npmc; ri++) { if ((pm = cc->pc_corepmcs[ri].phw_pmc) == NULL || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) continue; if (!iap_pmc_has_overflowed(ri)) continue; found_interrupt = 1; if (pm->pm_state != PMC_STATE_RUNNING) continue; error = pmc_process_interrupt(PMC_HR, pm, tf); v = pm->pm_sc.pm_reloadcount; v = iap_reload_count_to_perfctr_value(v); /* * Stop the counter, reload it but only restart it if * the PMC is not stalled. */ msr = rdmsr(IAP_EVSEL0 + ri) & ~IAP_EVSEL_MASK; wrmsr(IAP_EVSEL0 + ri, msr); wrmsr(core_iap_wroffset + IAP_PMC0 + ri, v); if (error) continue; wrmsr(IAP_EVSEL0 + ri, msr | (pm->pm_md.pm_iap.pm_iap_evsel | IAP_EN)); } if (found_interrupt) lapic_reenable_pmc(); if (found_interrupt) counter_u64_add(pmc_stats.pm_intr_processed, 1); else counter_u64_add(pmc_stats.pm_intr_ignored, 1); return (found_interrupt); } static int core2_intr(struct trapframe *tf) { int error, found_interrupt, n, cpu; uint64_t flag, intrstatus, intrenable, msr; struct pmc *pm; struct core_cpu *cc; pmc_value_t v; cpu = curcpu; PMCDBG3(MDP,INT, 1, "cpu=%d tf=0x%p um=%d", cpu, (void *) tf, TRAPF_USERMODE(tf)); /* * The IA_GLOBAL_STATUS (MSR 0x38E) register indicates which * PMCs have a pending PMI interrupt. We take a 'snapshot' of * the current set of interrupting PMCs and process these * after stopping them. */ intrstatus = rdmsr(IA_GLOBAL_STATUS); intrenable = intrstatus & core_pmcmask; PMCDBG2(MDP,INT, 1, "cpu=%d intrstatus=%jx", cpu, (uintmax_t) intrstatus); found_interrupt = 0; cc = core_pcpu[cpu]; KASSERT(cc != NULL, ("[core,%d] null pcpu", __LINE__)); cc->pc_globalctrl &= ~intrenable; cc->pc_resync = 1; /* MSRs now potentially out of sync. */ /* * Stop PMCs and clear overflow status bits. */ msr = rdmsr(IA_GLOBAL_CTRL) & ~IA_GLOBAL_CTRL_MASK; wrmsr(IA_GLOBAL_CTRL, msr); wrmsr(IA_GLOBAL_OVF_CTRL, intrenable | IA_GLOBAL_STATUS_FLAG_OVFBUF | IA_GLOBAL_STATUS_FLAG_CONDCHG); /* * Look for interrupts from fixed function PMCs. */ for (n = 0, flag = (1ULL << IAF_OFFSET); n < core_iaf_npmc; n++, flag <<= 1) { if ((intrstatus & flag) == 0) continue; found_interrupt = 1; pm = cc->pc_corepmcs[n + core_iaf_ri].phw_pmc; if (pm == NULL || pm->pm_state != PMC_STATE_RUNNING || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) continue; error = pmc_process_interrupt(PMC_HR, pm, tf); if (error) intrenable &= ~flag; v = iaf_reload_count_to_perfctr_value(pm->pm_sc.pm_reloadcount); /* Reload sampling count. */ wrmsr(IAF_CTR0 + n, v); PMCDBG4(MDP,INT, 1, "iaf-intr cpu=%d error=%d v=%jx(%jx)", curcpu, error, (uintmax_t) v, (uintmax_t) rdpmc(IAF_RI_TO_MSR(n))); } /* * Process interrupts from the programmable counters. */ for (n = 0, flag = 1; n < core_iap_npmc; n++, flag <<= 1) { if ((intrstatus & flag) == 0) continue; found_interrupt = 1; pm = cc->pc_corepmcs[n].phw_pmc; if (pm == NULL || pm->pm_state != PMC_STATE_RUNNING || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) continue; error = pmc_process_interrupt(PMC_HR, pm, tf); if (error) intrenable &= ~flag; v = iap_reload_count_to_perfctr_value(pm->pm_sc.pm_reloadcount); PMCDBG3(MDP,INT, 1, "iap-intr cpu=%d error=%d v=%jx", cpu, error, (uintmax_t) v); /* Reload sampling count. */ wrmsr(core_iap_wroffset + IAP_PMC0 + n, v); } /* * Reenable all non-stalled PMCs. */ PMCDBG2(MDP,INT, 1, "cpu=%d intrenable=%jx", cpu, (uintmax_t) intrenable); cc->pc_globalctrl |= intrenable; wrmsr(IA_GLOBAL_CTRL, cc->pc_globalctrl & IA_GLOBAL_CTRL_MASK); PMCDBG5(MDP,INT, 1, "cpu=%d fixedctrl=%jx globalctrl=%jx status=%jx " "ovf=%jx", cpu, (uintmax_t) rdmsr(IAF_CTRL), (uintmax_t) rdmsr(IA_GLOBAL_CTRL), (uintmax_t) rdmsr(IA_GLOBAL_STATUS), (uintmax_t) rdmsr(IA_GLOBAL_OVF_CTRL)); if (found_interrupt) lapic_reenable_pmc(); if (found_interrupt) counter_u64_add(pmc_stats.pm_intr_processed, 1); else counter_u64_add(pmc_stats.pm_intr_ignored, 1); return (found_interrupt); } int pmc_core_initialize(struct pmc_mdep *md, int maxcpu, int version_override) { int cpuid[CORE_CPUID_REQUEST_SIZE]; int ipa_version, flags, nflags; do_cpuid(CORE_CPUID_REQUEST, cpuid); ipa_version = (version_override > 0) ? version_override : cpuid[CORE_CPUID_EAX] & 0xFF; core_cputype = md->pmd_cputype; PMCDBG3(MDP,INI,1,"core-init cputype=%d ncpu=%d ipa-version=%d", core_cputype, maxcpu, ipa_version); if (ipa_version < 1 || ipa_version > 4 || (core_cputype != PMC_CPU_INTEL_CORE && ipa_version == 1)) { /* Unknown PMC architecture. */ printf("hwpc_core: unknown PMC architecture: %d\n", ipa_version); return (EPROGMISMATCH); } core_iap_wroffset = 0; if (cpu_feature2 & CPUID2_PDCM) { if (rdmsr(IA32_PERF_CAPABILITIES) & PERFCAP_FW_WRITE) { PMCDBG0(MDP, INI, 1, "core-init full-width write supported"); core_iap_wroffset = IAP_A_PMC0 - IAP_PMC0; } else PMCDBG0(MDP, INI, 1, "core-init full-width write NOT supported"); } else PMCDBG0(MDP, INI, 1, "core-init pdcm not supported"); core_pmcmask = 0; /* * Initialize programmable counters. */ core_iap_npmc = (cpuid[CORE_CPUID_EAX] >> 8) & 0xFF; core_iap_width = (cpuid[CORE_CPUID_EAX] >> 16) & 0xFF; core_pmcmask |= ((1ULL << core_iap_npmc) - 1); nflags = (cpuid[CORE_CPUID_EAX] >> 24) & 0xFF; flags = cpuid[CORE_CPUID_EBX] & ((1 << nflags) - 1); iap_initialize(md, maxcpu, core_iap_npmc, core_iap_width, flags); /* * Initialize fixed function counters, if present. */ if (core_cputype != PMC_CPU_INTEL_CORE) { core_iaf_ri = core_iap_npmc; core_iaf_npmc = cpuid[CORE_CPUID_EDX] & 0x1F; core_iaf_width = (cpuid[CORE_CPUID_EDX] >> 5) & 0xFF; iaf_initialize(md, maxcpu, core_iaf_npmc, core_iaf_width); core_pmcmask |= ((1ULL << core_iaf_npmc) - 1) << IAF_OFFSET; } PMCDBG2(MDP,INI,1,"core-init pmcmask=0x%jx iafri=%d", core_pmcmask, core_iaf_ri); core_pcpu = malloc(sizeof(*core_pcpu) * maxcpu, M_PMC, M_ZERO | M_WAITOK); /* * Choose the appropriate interrupt handler. */ if (ipa_version == 1) md->pmd_intr = core_intr; else md->pmd_intr = core2_intr; md->pmd_pcpu_fini = NULL; md->pmd_pcpu_init = NULL; return (0); } void pmc_core_finalize(struct pmc_mdep *md) { PMCDBG0(MDP,INI,1, "core-finalize"); free(core_pcpu, M_PMC); core_pcpu = NULL; } diff --git a/sys/dev/hwpmc/hwpmc_e500.c b/sys/dev/hwpmc/hwpmc_e500.c index 72c0868f08b5..b82bada95fe3 100644 --- a/sys/dev/hwpmc/hwpmc_e500.c +++ b/sys/dev/hwpmc/hwpmc_e500.c @@ -1,472 +1,475 @@ /*- * Copyright (c) 2015 Justin Hibbits * Copyright (c) 2005, Joseph Koshy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "hwpmc_powerpc.h" struct e500_event_code_map { enum pmc_event pe_ev; /* enum value */ uint8_t pe_counter_mask; /* Which counter this can be counted in. */ uint8_t pe_code; /* numeric code */ uint8_t pe_cpu; /* e500 core (v1,v2,mc), mask */ }; #define E500_MAX_PMCS 4 #define PMC_PPC_MASK0 0 #define PMC_PPC_MASK1 1 #define PMC_PPC_MASK2 2 #define PMC_PPC_MASK3 3 #define PMC_PPC_MASK_ALL 0x0f #define PMC_PPC_E500V1 1 #define PMC_PPC_E500V2 2 #define PMC_PPC_E500MC 4 #define PMC_PPC_E500_ANY 7 #define PMC_E500_EVENT(id, mask, number, core) \ [PMC_EV_E500_##id - PMC_EV_E500_FIRST] = \ { .pe_ev = PMC_EV_E500_##id, .pe_counter_mask = mask, \ .pe_code = number, .pe_cpu = core } #define PMC_E500MC_ONLY(id, number) \ PMC_E500_EVENT(id, PMC_PPC_MASK_ALL, number, PMC_PPC_E500MC) #define PMC_E500_COMMON(id, number) \ PMC_E500_EVENT(id, PMC_PPC_MASK_ALL, number, PMC_PPC_E500_ANY) static struct e500_event_code_map e500_event_codes[] = { PMC_E500_COMMON(CYCLES, 1), PMC_E500_COMMON(INSTR_COMPLETED, 2), PMC_E500_COMMON(UOPS_COMPLETED, 3), PMC_E500_COMMON(INSTR_FETCHED, 4), PMC_E500_COMMON(UOPS_DECODED, 5), PMC_E500_COMMON(PM_EVENT_TRANSITIONS, 6), PMC_E500_COMMON(PM_EVENT_CYCLES, 7), PMC_E500_COMMON(BRANCH_INSTRS_COMPLETED, 8), PMC_E500_COMMON(LOAD_UOPS_COMPLETED, 9), PMC_E500_COMMON(STORE_UOPS_COMPLETED, 10), PMC_E500_COMMON(CQ_REDIRECTS, 11), PMC_E500_COMMON(BRANCHES_FINISHED, 12), PMC_E500_COMMON(TAKEN_BRANCHES_FINISHED, 13), PMC_E500_COMMON(FINISHED_UNCOND_BRANCHES_MISS_BTB, 14), PMC_E500_COMMON(BRANCH_MISPRED, 15), PMC_E500_COMMON(BTB_BRANCH_MISPRED_FROM_DIRECTION, 16), PMC_E500_COMMON(BTB_HITS_PSEUDO_HITS, 17), PMC_E500_COMMON(CYCLES_DECODE_STALLED, 18), PMC_E500_COMMON(CYCLES_ISSUE_STALLED, 19), PMC_E500_COMMON(CYCLES_BRANCH_ISSUE_STALLED, 20), PMC_E500_COMMON(CYCLES_SU1_SCHED_STALLED, 21), PMC_E500_COMMON(CYCLES_SU2_SCHED_STALLED, 22), PMC_E500_COMMON(CYCLES_MU_SCHED_STALLED, 23), PMC_E500_COMMON(CYCLES_LRU_SCHED_STALLED, 24), PMC_E500_COMMON(CYCLES_BU_SCHED_STALLED, 25), PMC_E500_COMMON(TOTAL_TRANSLATED, 26), PMC_E500_COMMON(LOADS_TRANSLATED, 27), PMC_E500_COMMON(STORES_TRANSLATED, 28), PMC_E500_COMMON(TOUCHES_TRANSLATED, 29), PMC_E500_COMMON(CACHEOPS_TRANSLATED, 30), PMC_E500_COMMON(CACHE_INHIBITED_ACCESS_TRANSLATED, 31), PMC_E500_COMMON(GUARDED_LOADS_TRANSLATED, 32), PMC_E500_COMMON(WRITE_THROUGH_STORES_TRANSLATED, 33), PMC_E500_COMMON(MISALIGNED_LOAD_STORE_ACCESS_TRANSLATED, 34), PMC_E500_COMMON(TOTAL_ALLOCATED_TO_DLFB, 35), PMC_E500_COMMON(LOADS_TRANSLATED_ALLOCATED_TO_DLFB, 36), PMC_E500_COMMON(STORES_COMPLETED_ALLOCATED_TO_DLFB, 37), PMC_E500_COMMON(TOUCHES_TRANSLATED_ALLOCATED_TO_DLFB, 38), PMC_E500_COMMON(STORES_COMPLETED, 39), PMC_E500_COMMON(DATA_L1_CACHE_LOCKS, 40), PMC_E500_COMMON(DATA_L1_CACHE_RELOADS, 41), PMC_E500_COMMON(DATA_L1_CACHE_CASTOUTS, 42), PMC_E500_COMMON(LOAD_MISS_DLFB_FULL, 43), PMC_E500_COMMON(LOAD_MISS_LDQ_FULL, 44), PMC_E500_COMMON(LOAD_GUARDED_MISS, 45), PMC_E500_COMMON(STORE_TRANSLATE_WHEN_QUEUE_FULL, 46), PMC_E500_COMMON(ADDRESS_COLLISION, 47), PMC_E500_COMMON(DATA_MMU_MISS, 48), PMC_E500_COMMON(DATA_MMU_BUSY, 49), PMC_E500_COMMON(PART2_MISALIGNED_CACHE_ACCESS, 50), PMC_E500_COMMON(LOAD_MISS_DLFB_FULL_CYCLES, 51), PMC_E500_COMMON(LOAD_MISS_LDQ_FULL_CYCLES, 52), PMC_E500_COMMON(LOAD_GUARDED_MISS_CYCLES, 53), PMC_E500_COMMON(STORE_TRANSLATE_WHEN_QUEUE_FULL_CYCLES, 54), PMC_E500_COMMON(ADDRESS_COLLISION_CYCLES, 55), PMC_E500_COMMON(DATA_MMU_MISS_CYCLES, 56), PMC_E500_COMMON(DATA_MMU_BUSY_CYCLES, 57), PMC_E500_COMMON(PART2_MISALIGNED_CACHE_ACCESS_CYCLES, 58), PMC_E500_COMMON(INSTR_L1_CACHE_LOCKS, 59), PMC_E500_COMMON(INSTR_L1_CACHE_RELOADS, 60), PMC_E500_COMMON(INSTR_L1_CACHE_FETCHES, 61), PMC_E500_COMMON(INSTR_MMU_TLB4K_RELOADS, 62), PMC_E500_COMMON(INSTR_MMU_VSP_RELOADS, 63), PMC_E500_COMMON(DATA_MMU_TLB4K_RELOADS, 64), PMC_E500_COMMON(DATA_MMU_VSP_RELOADS, 65), PMC_E500_COMMON(L2MMU_MISSES, 66), PMC_E500_COMMON(BIU_MASTER_REQUESTS, 67), PMC_E500_COMMON(BIU_MASTER_INSTR_SIDE_REQUESTS, 68), PMC_E500_COMMON(BIU_MASTER_DATA_SIDE_REQUESTS, 69), PMC_E500_COMMON(BIU_MASTER_DATA_SIDE_CASTOUT_REQUESTS, 70), PMC_E500_COMMON(BIU_MASTER_RETRIES, 71), PMC_E500_COMMON(SNOOP_REQUESTS, 72), PMC_E500_COMMON(SNOOP_HITS, 73), PMC_E500_COMMON(SNOOP_PUSHES, 74), PMC_E500_COMMON(SNOOP_RETRIES, 75), PMC_E500_EVENT(DLFB_LOAD_MISS_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 76, PMC_PPC_E500_ANY), PMC_E500_EVENT(ILFB_FETCH_MISS_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 77, PMC_PPC_E500_ANY), PMC_E500_EVENT(EXT_INPU_INTR_LATENCY_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 78, PMC_PPC_E500_ANY), PMC_E500_EVENT(CRIT_INPUT_INTR_LATENCY_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 79, PMC_PPC_E500_ANY), PMC_E500_EVENT(EXT_INPUT_INTR_PENDING_LATENCY_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 80, PMC_PPC_E500_ANY), PMC_E500_EVENT(CRIT_INPUT_INTR_PENDING_LATENCY_CYCLES, PMC_PPC_MASK0|PMC_PPC_MASK1, 81, PMC_PPC_E500_ANY), PMC_E500_COMMON(PMC0_OVERFLOW, 82), PMC_E500_COMMON(PMC1_OVERFLOW, 83), PMC_E500_COMMON(PMC2_OVERFLOW, 84), PMC_E500_COMMON(PMC3_OVERFLOW, 85), PMC_E500_COMMON(INTERRUPTS_TAKEN, 86), PMC_E500_COMMON(EXT_INPUT_INTR_TAKEN, 87), PMC_E500_COMMON(CRIT_INPUT_INTR_TAKEN, 88), PMC_E500_COMMON(SYSCALL_TRAP_INTR, 89), PMC_E500_EVENT(TLB_BIT_TRANSITIONS, PMC_PPC_MASK_ALL, 90, PMC_PPC_E500V2 | PMC_PPC_E500MC), PMC_E500MC_ONLY(L2_LINEFILL_BUFFER, 91), PMC_E500MC_ONLY(LV2_VS, 92), PMC_E500MC_ONLY(CASTOUTS_RELEASED, 93), PMC_E500MC_ONLY(INTV_ALLOCATIONS, 94), PMC_E500MC_ONLY(DLFB_RETRIES_TO_MBAR, 95), PMC_E500MC_ONLY(STORE_RETRIES, 96), PMC_E500MC_ONLY(STASH_L1_HITS, 97), PMC_E500MC_ONLY(STASH_L2_HITS, 98), PMC_E500MC_ONLY(STASH_BUSY_1, 99), PMC_E500MC_ONLY(STASH_BUSY_2, 100), PMC_E500MC_ONLY(STASH_BUSY_3, 101), PMC_E500MC_ONLY(STASH_HITS, 102), PMC_E500MC_ONLY(STASH_HIT_DLFB, 103), PMC_E500MC_ONLY(STASH_REQUESTS, 106), PMC_E500MC_ONLY(STASH_REQUESTS_L1, 107), PMC_E500MC_ONLY(STASH_REQUESTS_L2, 108), PMC_E500MC_ONLY(STALLS_NO_CAQ_OR_COB, 109), PMC_E500MC_ONLY(L2_CACHE_ACCESSES, 110), PMC_E500MC_ONLY(L2_HIT_CACHE_ACCESSES, 111), PMC_E500MC_ONLY(L2_CACHE_DATA_ACCESSES, 112), PMC_E500MC_ONLY(L2_CACHE_DATA_HITS, 113), PMC_E500MC_ONLY(L2_CACHE_INSTR_ACCESSES, 114), PMC_E500MC_ONLY(L2_CACHE_INSTR_HITS, 115), PMC_E500MC_ONLY(L2_CACHE_ALLOCATIONS, 116), PMC_E500MC_ONLY(L2_CACHE_DATA_ALLOCATIONS, 117), PMC_E500MC_ONLY(L2_CACHE_DIRTY_DATA_ALLOCATIONS, 118), PMC_E500MC_ONLY(L2_CACHE_INSTR_ALLOCATIONS, 119), PMC_E500MC_ONLY(L2_CACHE_UPDATES, 120), PMC_E500MC_ONLY(L2_CACHE_CLEAN_UPDATES, 121), PMC_E500MC_ONLY(L2_CACHE_DIRTY_UPDATES, 122), PMC_E500MC_ONLY(L2_CACHE_CLEAN_REDUNDANT_UPDATES, 123), PMC_E500MC_ONLY(L2_CACHE_DIRTY_REDUNDANT_UPDATES, 124), PMC_E500MC_ONLY(L2_CACHE_LOCKS, 125), PMC_E500MC_ONLY(L2_CACHE_CASTOUTS, 126), PMC_E500MC_ONLY(L2_CACHE_DATA_DIRTY_HITS, 127), PMC_E500MC_ONLY(INSTR_LFB_WENT_HIGH_PRIORITY, 128), PMC_E500MC_ONLY(SNOOP_THROTTLING_TURNED_ON, 129), PMC_E500MC_ONLY(L2_CLEAN_LINE_INVALIDATIONS, 130), PMC_E500MC_ONLY(L2_INCOHERENT_LINE_INVALIDATIONS, 131), PMC_E500MC_ONLY(L2_COHERENT_LINE_INVALIDATIONS, 132), PMC_E500MC_ONLY(COHERENT_LOOKUP_MISS_DUE_TO_VALID_BUT_INCOHERENT_MATCHES, 133), PMC_E500MC_ONLY(IAC1S_DETECTED, 140), PMC_E500MC_ONLY(IAC2S_DETECTED, 141), PMC_E500MC_ONLY(DAC1S_DTECTED, 144), PMC_E500MC_ONLY(DAC2S_DTECTED, 145), PMC_E500MC_ONLY(DVT0_DETECTED, 148), PMC_E500MC_ONLY(DVT1_DETECTED, 149), PMC_E500MC_ONLY(DVT2_DETECTED, 150), PMC_E500MC_ONLY(DVT3_DETECTED, 151), PMC_E500MC_ONLY(DVT4_DETECTED, 152), PMC_E500MC_ONLY(DVT5_DETECTED, 153), PMC_E500MC_ONLY(DVT6_DETECTED, 154), PMC_E500MC_ONLY(DVT7_DETECTED, 155), PMC_E500MC_ONLY(CYCLES_COMPLETION_STALLED_NEXUS_FIFO_FULL, 156), PMC_E500MC_ONLY(FPU_DOUBLE_PUMP, 160), PMC_E500MC_ONLY(FPU_FINISH, 161), PMC_E500MC_ONLY(FPU_DIVIDE_CYCLES, 162), PMC_E500MC_ONLY(FPU_DENORM_INPUT_CYCLES, 163), PMC_E500MC_ONLY(FPU_RESULT_STALL_CYCLES, 164), PMC_E500MC_ONLY(FPU_FPSCR_FULL_STALL, 165), PMC_E500MC_ONLY(FPU_PIPE_SYNC_STALLS, 166), PMC_E500MC_ONLY(FPU_INPUT_DATA_STALLS, 167), PMC_E500MC_ONLY(DECORATED_LOADS, 176), PMC_E500MC_ONLY(DECORATED_STORES, 177), PMC_E500MC_ONLY(LOAD_RETRIES, 178), PMC_E500MC_ONLY(STWCX_SUCCESSES, 179), PMC_E500MC_ONLY(STWCX_FAILURES, 180), }; static pmc_value_t e500_pmcn_read(unsigned int pmc) { switch (pmc) { case 0: return (mfpmr(PMR_PMC0)); case 1: return (mfpmr(PMR_PMC1)); case 2: return (mfpmr(PMR_PMC2)); case 3: return (mfpmr(PMR_PMC3)); default: panic("Invalid PMC number: %d\n", pmc); } } static void e500_pmcn_write(unsigned int pmc, uint32_t val) { switch (pmc) { case 0: mtpmr(PMR_PMC0, val); break; case 1: mtpmr(PMR_PMC1, val); break; case 2: mtpmr(PMR_PMC2, val); break; case 3: mtpmr(PMR_PMC3, val); break; default: panic("Invalid PMC number: %d\n", pmc); } } static void e500_set_pmc(int cpu, int ri, int config) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_pmlc; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; config &= ~POWERPC_PMC_ENABLE; if (config != PMCN_NONE) { if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) config |= PMLCax_CE; /* Enable the PMC. */ switch (ri) { case 0: mtpmr(PMR_PMLCa0, config); break; case 1: mtpmr(PMR_PMLCa1, config); break; case 2: mtpmr(PMR_PMLCa2, config); break; case 3: mtpmr(PMR_PMLCa3, config); break; } } else { /* Disable the PMC. */ switch (ri) { case 0: pmc_pmlc = mfpmr(PMR_PMLCa0); pmc_pmlc |= PMLCax_FC; mtpmr(PMR_PMLCa0, pmc_pmlc); break; case 1: pmc_pmlc = mfpmr(PMR_PMLCa1); pmc_pmlc |= PMLCax_FC; mtpmr(PMR_PMLCa1, pmc_pmlc); break; case 2: pmc_pmlc = mfpmr(PMR_PMLCa2); pmc_pmlc |= PMLCax_FC; mtpmr(PMR_PMLCa2, pmc_pmlc); break; case 3: pmc_pmlc = mfpmr(PMR_PMLCa3); pmc_pmlc |= PMLCax_FC; mtpmr(PMR_PMLCa3, pmc_pmlc); break; } } } static int e500_pcpu_init(struct pmc_mdep *md, int cpu) { int i; powerpc_pcpu_init(md, cpu); /* Freeze all counters. */ mtpmr(PMR_PMGC0, PMGC_FAC | PMGC_PMIE | PMGC_FCECE); for (i = 0; i < E500_MAX_PMCS; i++) /* Initialize the PMC to stopped */ powerpc_stop_pmc(cpu, i); /* Unfreeze global register. */ mtpmr(PMR_PMGC0, PMGC_PMIE | PMGC_FCECE); return (0); } static int e500_pcpu_fini(struct pmc_mdep *md, int cpu) { uint32_t pmgc0 = mfpmr(PMR_PMGC0); pmgc0 |= PMGC_FAC; mtpmr(PMR_PMGC0, pmgc0); mtmsr(mfmsr() & ~PSL_PMM); return (powerpc_pcpu_fini(md, cpu)); } static int e500_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { enum pmc_event pe; uint32_t caps, config, counter; struct e500_event_code_map *ev; uint16_t vers; uint8_t pe_cpu_mask; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < E500_MAX_PMCS, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); + if (a->pm_class != PMC_CLASS_E500) + return (EINVAL); + caps = a->pm_caps; pe = a->pm_ev; config = PMLCax_FCS | PMLCax_FCU | PMLCax_FCM1 | PMLCax_FCM1; if (pe < PMC_EV_E500_FIRST || pe > PMC_EV_E500_LAST) return (EINVAL); ev = &e500_event_codes[pe-PMC_EV_E500_FIRST]; if (ev->pe_code == 0) return (EINVAL); vers = mfpvr() >> 16; switch (vers) { case FSL_E500v1: pe_cpu_mask = ev->pe_cpu & PMC_PPC_E500V1; break; case FSL_E500v2: pe_cpu_mask = ev->pe_cpu & PMC_PPC_E500V2; break; case FSL_E500mc: case FSL_E5500: pe_cpu_mask = ev->pe_cpu & PMC_PPC_E500MC; break; } if (pe_cpu_mask == 0) return (EINVAL); config |= PMLCax_EVENT(ev->pe_code); counter = ev->pe_counter_mask; if ((counter & (1 << ri)) == 0) return (EINVAL); if (caps & PMC_CAP_SYSTEM) config &= ~PMLCax_FCS; if (caps & PMC_CAP_USER) config &= ~PMLCax_FCU; if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) config &= ~(PMLCax_FCS|PMLCax_FCU); pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; PMCDBG2(MDP,ALL,2,"powerpc-allocate ri=%d -> config=0x%x", ri, config); return 0; } static void e500_resume_pmc(bool ie) { /* Re-enable PERF exceptions. */ if (ie) mtpmr(PMR_PMGC0, (mfpmr(PMR_PMGC0) & ~PMGC_FAC) | PMGC_PMIE); } int pmc_e500_initialize(struct pmc_mdep *pmc_mdep) { struct pmc_classdep *pcd; pmc_mdep->pmd_cputype = PMC_CPU_PPC_E500; pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; pcd->pcd_caps = POWERPC_PMC_CAPS; pcd->pcd_class = PMC_CLASS_E500; pcd->pcd_num = E500_MAX_PMCS; pcd->pcd_ri = pmc_mdep->pmd_npmc; pcd->pcd_width = 32; pcd->pcd_allocate_pmc = e500_allocate_pmc; pcd->pcd_config_pmc = powerpc_config_pmc; pcd->pcd_pcpu_fini = e500_pcpu_fini; pcd->pcd_pcpu_init = e500_pcpu_init; pcd->pcd_describe = powerpc_describe; pcd->pcd_get_config = powerpc_get_config; pcd->pcd_read_pmc = powerpc_read_pmc; pcd->pcd_release_pmc = powerpc_release_pmc; pcd->pcd_start_pmc = powerpc_start_pmc; pcd->pcd_stop_pmc = powerpc_stop_pmc; pcd->pcd_write_pmc = powerpc_write_pmc; pmc_mdep->pmd_npmc += E500_MAX_PMCS; pmc_mdep->pmd_intr = powerpc_pmc_intr; ppc_max_pmcs = E500_MAX_PMCS; powerpc_set_pmc = e500_set_pmc; powerpc_pmcn_read = e500_pmcn_read; powerpc_pmcn_write = e500_pmcn_write; powerpc_resume_pmc = e500_resume_pmc; return (0); } diff --git a/sys/dev/hwpmc/hwpmc_mpc7xxx.c b/sys/dev/hwpmc/hwpmc_mpc7xxx.c index 987480e89efd..c62c0791af01 100644 --- a/sys/dev/hwpmc/hwpmc_mpc7xxx.c +++ b/sys/dev/hwpmc/hwpmc_mpc7xxx.c @@ -1,481 +1,482 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Justin Hibbits * Copyright (c) 2005, Joseph Koshy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "hwpmc_powerpc.h" #define PPC_SET_PMC1SEL(r, x) ((r & ~(SPR_MMCR0_74XX_PMC1SEL(0x3f))) | \ SPR_MMCR0_74XX_PMC1SEL(x)) #define PPC_SET_PMC2SEL(r, x) ((r & ~(SPR_MMCR0_74XX_PMC2SEL(0x3f))) | \ SPR_MMCR0_74XX_PMC2SEL(x)) #define PPC_SET_PMC3SEL(r, x) ((r & ~(SPR_MMCR1_PMC3SEL(0x1f))) | SPR_MMCR1_PMC3SEL(x)) #define PPC_SET_PMC4SEL(r, x) ((r & ~(SPR_MMCR1_PMC4SEL(0x1f))) | SPR_MMCR1_PMC4SEL(x)) #define PPC_SET_PMC5SEL(r, x) ((r & ~(SPR_MMCR1_PMC5SEL(0x1f))) | SPR_MMCR1_PMC5SEL(x)) #define PPC_SET_PMC6SEL(r, x) ((r & ~(SPR_MMCR1_74XX_PMC6SEL(0x3f))) | \ SPR_MMCR1_74XX_PMC6SEL(x)) /* Change this when we support more than just the 7450. */ #define MPC7XXX_MAX_PMCS 6 /* * Things to improve on this: * - It stops (clears to 0) the PMC and resets it at every context switch * currently. */ /* * This should work for every 32-bit PowerPC implementation I know of (G3 and G4 * specifically). */ #define PPC_PMC_MASK_ALL 0x3f #define PMC_POWERPC_EVENT(id, mask, number) \ { .pe_event = PMC_EV_PPC7450_##id, .pe_flags = mask, .pe_code = number } static struct pmc_ppc_event mpc7xxx_event_codes[] = { PMC_POWERPC_EVENT(CYCLE,PPC_PMC_MASK_ALL, 1), PMC_POWERPC_EVENT(INSTR_COMPLETED, 0x0f, 2), PMC_POWERPC_EVENT(TLB_BIT_TRANSITIONS, 0x0f, 3), PMC_POWERPC_EVENT(INSTR_DISPATCHED, 0x0f, 4), PMC_POWERPC_EVENT(PMON_EXCEPT, 0x0f, 5), PMC_POWERPC_EVENT(PMON_SIG, 0x0f, 7), PMC_POWERPC_EVENT(VPU_INSTR_COMPLETED, 0x03, 8), PMC_POWERPC_EVENT(VFPU_INSTR_COMPLETED, 0x03, 9), PMC_POWERPC_EVENT(VIU1_INSTR_COMPLETED, 0x03, 10), PMC_POWERPC_EVENT(VIU2_INSTR_COMPLETED, 0x03, 11), PMC_POWERPC_EVENT(MTVSCR_INSTR_COMPLETED, 0x03, 12), PMC_POWERPC_EVENT(MTVRSAVE_INSTR_COMPLETED, 0x03, 13), PMC_POWERPC_EVENT(VPU_INSTR_WAIT_CYCLES, 0x03, 14), PMC_POWERPC_EVENT(VFPU_INSTR_WAIT_CYCLES, 0x03, 15), PMC_POWERPC_EVENT(VIU1_INSTR_WAIT_CYCLES, 0x03, 16), PMC_POWERPC_EVENT(VIU2_INSTR_WAIT_CYCLES, 0x03, 17), PMC_POWERPC_EVENT(MFVSCR_SYNC_CYCLES, 0x03, 18), PMC_POWERPC_EVENT(VSCR_SAT_SET, 0x03, 19), PMC_POWERPC_EVENT(STORE_INSTR_COMPLETED, 0x03, 20), PMC_POWERPC_EVENT(L1_INSTR_CACHE_MISSES, 0x03, 21), PMC_POWERPC_EVENT(L1_DATA_SNOOPS, 0x03, 22), PMC_POWERPC_EVENT(UNRESOLVED_BRANCHES, 0x01, 23), PMC_POWERPC_EVENT(SPEC_BUFFER_CYCLES, 0x01, 24), PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_CYCLES, 0x01, 25), PMC_POWERPC_EVENT(TRUE_BRANCH_TARGET_HITS, 0x01, 26), PMC_POWERPC_EVENT(BRANCH_LINK_STAC_PREDICTED, 0x01, 27), PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_DISPATCHES, 0x01, 28), PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_DISPATCHED, 0x01, 29), PMC_POWERPC_EVENT(THRESHOLD_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 30), PMC_POWERPC_EVENT(THRESHOLD_VEC_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 31), PMC_POWERPC_EVENT(CYCLES_NO_COMPLETED_INSTRS, 0x01, 32), PMC_POWERPC_EVENT(IU2_INSTR_COMPLETED, 0x01, 33), PMC_POWERPC_EVENT(BRANCHES_COMPLETED, 0x01, 34), PMC_POWERPC_EVENT(EIEIO_INSTR_COMPLETED, 0x01, 35), PMC_POWERPC_EVENT(MTSPR_INSTR_COMPLETED, 0x01, 36), PMC_POWERPC_EVENT(SC_INSTR_COMPLETED, 0x01, 37), PMC_POWERPC_EVENT(LS_LM_COMPLETED, 0x01, 38), PMC_POWERPC_EVENT(ITLB_HW_TABLE_SEARCH_CYCLES, 0x01, 39), PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x01, 40), PMC_POWERPC_EVENT(L1_INSTR_CACHE_ACCESSES, 0x01, 41), PMC_POWERPC_EVENT(INSTR_BKPT_MATCHES, 0x01, 42), PMC_POWERPC_EVENT(L1_DATA_CACHE_LOAD_MISS_CYCLES_OVER_THRESHOLD, 0x01, 43), PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_ON_MODIFIED, 0x01, 44), PMC_POWERPC_EVENT(LOAD_MISS_ALIAS, 0x01, 45), PMC_POWERPC_EVENT(LOAD_MISS_ALIAS_ON_TOUCH, 0x01, 46), PMC_POWERPC_EVENT(TOUCH_ALIAS, 0x01, 47), PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT_QUEUE, 0x01, 48), PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT, 0x01, 49), PMC_POWERPC_EVENT(L1_DATA_SNOOP_HITS, 0x01, 50), PMC_POWERPC_EVENT(WRITE_THROUGH_STORES, 0x01, 51), PMC_POWERPC_EVENT(CACHE_INHIBITED_STORES, 0x01, 52), PMC_POWERPC_EVENT(L1_DATA_LOAD_HIT, 0x01, 53), PMC_POWERPC_EVENT(L1_DATA_TOUCH_HIT, 0x01, 54), PMC_POWERPC_EVENT(L1_DATA_STORE_HIT, 0x01, 55), PMC_POWERPC_EVENT(L1_DATA_TOTAL_HITS, 0x01, 56), PMC_POWERPC_EVENT(DST_INSTR_DISPATCHED, 0x01, 57), PMC_POWERPC_EVENT(REFRESHED_DSTS, 0x01, 58), PMC_POWERPC_EVENT(SUCCESSFUL_DST_TABLE_SEARCHES, 0x01, 59), PMC_POWERPC_EVENT(DSS_INSTR_COMPLETED, 0x01, 60), PMC_POWERPC_EVENT(DST_STREAM_0_CACHE_LINE_FETCHES, 0x01, 61), PMC_POWERPC_EVENT(VTQ_SUSPENDS_DUE_TO_CTX_CHANGE, 0x01, 62), PMC_POWERPC_EVENT(VTQ_LINE_FETCH_HIT, 0x01, 63), PMC_POWERPC_EVENT(VEC_LOAD_INSTR_COMPLETED, 0x01, 64), PMC_POWERPC_EVENT(FP_STORE_INSTR_COMPLETED_IN_LSU, 0x01, 65), PMC_POWERPC_EVENT(FPU_RENORMALIZATION, 0x01, 66), PMC_POWERPC_EVENT(FPU_DENORMALIZATION, 0x01, 67), PMC_POWERPC_EVENT(FP_STORE_CAUSES_STALL_IN_LSU, 0x01, 68), PMC_POWERPC_EVENT(LD_ST_TRUE_ALIAS_STALL, 0x01, 70), PMC_POWERPC_EVENT(LSU_INDEXED_ALIAS_STALL, 0x01, 71), PMC_POWERPC_EVENT(LSU_ALIAS_VS_FSQ_WB0_WB1, 0x01, 72), PMC_POWERPC_EVENT(LSU_ALIAS_VS_CSQ, 0x01, 73), PMC_POWERPC_EVENT(LSU_LOAD_HIT_LINE_ALIAS_VS_CSQ0, 0x01, 74), PMC_POWERPC_EVENT(LSU_LOAD_MISS_LINE_ALIAS_VS_CSQ0, 0x01, 75), PMC_POWERPC_EVENT(LSU_TOUCH_LINE_ALIAS_VS_FSQ_WB0_WB1, 0x01, 76), PMC_POWERPC_EVENT(LSU_TOUCH_ALIAS_VS_CSQ, 0x01, 77), PMC_POWERPC_EVENT(LSU_LMQ_FULL_STALL, 0x01, 78), PMC_POWERPC_EVENT(FP_LOAD_INSTR_COMPLETED_IN_LSU, 0x01, 79), PMC_POWERPC_EVENT(FP_LOAD_SINGLE_INSTR_COMPLETED_IN_LSU, 0x01, 80), PMC_POWERPC_EVENT(FP_LOAD_DOUBLE_COMPLETED_IN_LSU, 0x01, 81), PMC_POWERPC_EVENT(LSU_RA_LATCH_STALL, 0x01, 82), PMC_POWERPC_EVENT(LSU_LOAD_VS_STORE_QUEUE_ALIAS_STALL, 0x01, 83), PMC_POWERPC_EVENT(LSU_LMQ_INDEX_ALIAS, 0x01, 84), PMC_POWERPC_EVENT(LSU_STORE_QUEUE_INDEX_ALIAS, 0x01, 85), PMC_POWERPC_EVENT(LSU_CSQ_FORWARDING, 0x01, 86), PMC_POWERPC_EVENT(LSU_MISALIGNED_LOAD_FINISH, 0x01, 87), PMC_POWERPC_EVENT(LSU_MISALIGN_STORE_COMPLETED, 0x01, 88), PMC_POWERPC_EVENT(LSU_MISALIGN_STALL, 0x01, 89), PMC_POWERPC_EVENT(FP_ONE_QUARTER_FPSCR_RENAMES_BUSY, 0x01, 90), PMC_POWERPC_EVENT(FP_ONE_HALF_FPSCR_RENAMES_BUSY, 0x01, 91), PMC_POWERPC_EVENT(FP_THREE_QUARTERS_FPSCR_RENAMES_BUSY, 0x01, 92), PMC_POWERPC_EVENT(FP_ALL_FPSCR_RENAMES_BUSY, 0x01, 93), PMC_POWERPC_EVENT(FP_DENORMALIZED_RESULT, 0x01, 94), PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISSES, 0x02, 23), PMC_POWERPC_EVENT(DISPATCHES_TO_FPR_ISSUE_QUEUE, 0x02, 24), PMC_POWERPC_EVENT(LSU_INSTR_COMPLETED, 0x02, 25), PMC_POWERPC_EVENT(LOAD_INSTR_COMPLETED, 0x02, 26), PMC_POWERPC_EVENT(SS_SM_INSTR_COMPLETED, 0x02, 27), PMC_POWERPC_EVENT(TLBIE_INSTR_COMPLETED, 0x02, 28), PMC_POWERPC_EVENT(LWARX_INSTR_COMPLETED, 0x02, 29), PMC_POWERPC_EVENT(MFSPR_INSTR_COMPLETED, 0x02, 30), PMC_POWERPC_EVENT(REFETCH_SERIALIZATION, 0x02, 31), PMC_POWERPC_EVENT(COMPLETION_QUEUE_ENTRIES_OVER_THRESHOLD, 0x02, 32), PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x02, 33), PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x02, 34), PMC_POWERPC_EVENT(ITLB_NON_SPECULATIVE_MISSES, 0x02, 35), PMC_POWERPC_EVENT(CYCLES_WAITING_FROM_L1_INSTR_CACHE_MISS, 0x02, 36), PMC_POWERPC_EVENT(L1_DATA_LOAD_ACCESS_MISS, 0x02, 37), PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS, 0x02, 38), PMC_POWERPC_EVENT(L1_DATA_STORE_MISS, 0x02, 39), PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS_CYCLES, 0x02, 40), PMC_POWERPC_EVENT(L1_DATA_CYCLES_USED, 0x02, 41), PMC_POWERPC_EVENT(DST_STREAM_1_CACHE_LINE_FETCHES, 0x02, 42), PMC_POWERPC_EVENT(VTQ_STREAM_CANCELED_PREMATURELY, 0x02, 43), PMC_POWERPC_EVENT(VTQ_RESUMES_DUE_TO_CTX_CHANGE, 0x02, 44), PMC_POWERPC_EVENT(VTQ_LINE_FETCH_MISS, 0x02, 45), PMC_POWERPC_EVENT(VTQ_LINE_FETCH, 0x02, 46), PMC_POWERPC_EVENT(TLBIE_SNOOPS, 0x02, 47), PMC_POWERPC_EVENT(L1_INSTR_CACHE_RELOADS, 0x02, 48), PMC_POWERPC_EVENT(L1_DATA_CACHE_RELOADS, 0x02, 49), PMC_POWERPC_EVENT(L1_DATA_CACHE_CASTOUTS_TO_L2, 0x02, 50), PMC_POWERPC_EVENT(STORE_MERGE_GATHER, 0x02, 51), PMC_POWERPC_EVENT(CACHEABLE_STORE_MERGE_TO_32_BYTES, 0x02, 52), PMC_POWERPC_EVENT(DATA_BKPT_MATCHES, 0x02, 53), PMC_POWERPC_EVENT(FALL_THROUGH_BRANCHES_PROCESSED, 0x02, 54), PMC_POWERPC_EVENT(FIRST_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x02, 55), PMC_POWERPC_EVENT(SECOND_SPECULATION_BUFFER_ACTIVE, 0x02, 56), PMC_POWERPC_EVENT(BPU_STALL_ON_LR_DEPENDENCY, 0x02, 57), PMC_POWERPC_EVENT(BTIC_MISS, 0x02, 58), PMC_POWERPC_EVENT(BRANCH_LINK_STACK_CORRECTLY_RESOLVED, 0x02, 59), PMC_POWERPC_EVENT(FPR_ISSUE_STALLED, 0x02, 60), PMC_POWERPC_EVENT(SWITCHES_BETWEEN_PRIV_USER, 0x02, 61), PMC_POWERPC_EVENT(LSU_COMPLETES_FP_STORE_SINGLE, 0x02, 62), PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x04, 8), PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x04, 9), PMC_POWERPC_EVENT(VR_ISSUE_QUEUE_DISPATCHES, 0x04, 10), PMC_POWERPC_EVENT(VR_STALLS, 0x04, 11), PMC_POWERPC_EVENT(GPR_RENAME_BUFFER_ENTRIES_OVER_THRESHOLD, 0x04, 12), PMC_POWERPC_EVENT(FPR_ISSUE_QUEUE_ENTRIES, 0x04, 13), PMC_POWERPC_EVENT(FPU_INSTR_COMPLETED, 0x04, 14), PMC_POWERPC_EVENT(STWCX_INSTR_COMPLETED, 0x04, 15), PMC_POWERPC_EVENT(LS_LM_INSTR_PIECES, 0x04, 16), PMC_POWERPC_EVENT(ITLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x04, 17), PMC_POWERPC_EVENT(DTLB_MISSES, 0x04, 18), PMC_POWERPC_EVENT(CANCELLED_L1_INSTR_CACHE_MISSES, 0x04, 19), PMC_POWERPC_EVENT(L1_DATA_CACHE_OP_HIT, 0x04, 20), PMC_POWERPC_EVENT(L1_DATA_LOAD_MISS_CYCLES, 0x04, 21), PMC_POWERPC_EVENT(L1_DATA_PUSHES, 0x04, 22), PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISS, 0x04, 23), PMC_POWERPC_EVENT(VT2_FETCHES, 0x04, 24), PMC_POWERPC_EVENT(TAKEN_BRANCHES_PROCESSED, 0x04, 25), PMC_POWERPC_EVENT(BRANCH_FLUSHES, 0x04, 26), PMC_POWERPC_EVENT(SECOND_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x04, 27), PMC_POWERPC_EVENT(THIRD_SPECULATION_BUFFER_ACTIVE, 0x04, 28), PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_ON_CTR_DEPENDENCY, 0x04, 29), PMC_POWERPC_EVENT(FAST_BTIC_HIT, 0x04, 30), PMC_POWERPC_EVENT(BRANCH_LINK_STACK_MISPREDICTED, 0x04, 31), PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_COMPLETED, 0x08, 14), PMC_POWERPC_EVENT(CYCLES_NO_INSTR_DISPATCHED, 0x08, 15), PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_ENTRIES_OVER_THRESHOLD, 0x08, 16), PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_STALLED, 0x08, 17), PMC_POWERPC_EVENT(IU1_INSTR_COMPLETED, 0x08, 18), PMC_POWERPC_EVENT(DSSALL_INSTR_COMPLETED, 0x08, 19), PMC_POWERPC_EVENT(TLBSYNC_INSTR_COMPLETED, 0x08, 20), PMC_POWERPC_EVENT(SYNC_INSTR_COMPLETED, 0x08, 21), PMC_POWERPC_EVENT(SS_SM_INSTR_PIECES, 0x08, 22), PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES, 0x08, 23), PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x08, 24), PMC_POWERPC_EVENT(SUCCESSFUL_STWCX, 0x08, 25), PMC_POWERPC_EVENT(DST_STREAM_3_CACHE_LINE_FETCHES, 0x08, 26), PMC_POWERPC_EVENT(THIRD_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x08, 27), PMC_POWERPC_EVENT(MISPREDICTED_BRANCHES, 0x08, 28), PMC_POWERPC_EVENT(FOLDED_BRANCHES, 0x08, 29), PMC_POWERPC_EVENT(FP_STORE_DOUBLE_COMPLETES_IN_LSU, 0x08, 30), PMC_POWERPC_EVENT(L2_CACHE_HITS, 0x30, 2), PMC_POWERPC_EVENT(L3_CACHE_HITS, 0x30, 3), PMC_POWERPC_EVENT(L2_INSTR_CACHE_MISSES, 0x30, 4), PMC_POWERPC_EVENT(L3_INSTR_CACHE_MISSES, 0x30, 5), PMC_POWERPC_EVENT(L2_DATA_CACHE_MISSES, 0x30, 6), PMC_POWERPC_EVENT(L3_DATA_CACHE_MISSES, 0x30, 7), PMC_POWERPC_EVENT(L2_LOAD_HITS, 0x10, 8), PMC_POWERPC_EVENT(L2_STORE_HITS, 0x10, 9), PMC_POWERPC_EVENT(L3_LOAD_HITS, 0x10, 10), PMC_POWERPC_EVENT(L3_STORE_HITS, 0x10, 11), PMC_POWERPC_EVENT(L2_TOUCH_HITS, 0x30, 13), PMC_POWERPC_EVENT(L3_TOUCH_HITS, 0x30, 14), PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x30, 15), PMC_POWERPC_EVENT(SNOOP_MODIFIED, 0x10, 16), PMC_POWERPC_EVENT(SNOOP_VALID, 0x10, 17), PMC_POWERPC_EVENT(INTERVENTION, 0x30, 18), PMC_POWERPC_EVENT(L2_CACHE_MISSES, 0x10, 19), PMC_POWERPC_EVENT(L3_CACHE_MISSES, 0x10, 20), PMC_POWERPC_EVENT(L2_CACHE_CASTOUTS, 0x20, 8), PMC_POWERPC_EVENT(L3_CACHE_CASTOUTS, 0x20, 9), PMC_POWERPC_EVENT(L2SQ_FULL_CYCLES, 0x20, 10), PMC_POWERPC_EVENT(L3SQ_FULL_CYCLES, 0x20, 11), PMC_POWERPC_EVENT(RAQ_FULL_CYCLES, 0x20, 16), PMC_POWERPC_EVENT(WAQ_FULL_CYCLES, 0x20, 17), PMC_POWERPC_EVENT(L1_EXTERNAL_INTERVENTIONS, 0x20, 19), PMC_POWERPC_EVENT(L2_EXTERNAL_INTERVENTIONS, 0x20, 20), PMC_POWERPC_EVENT(L3_EXTERNAL_INTERVENTIONS, 0x20, 21), PMC_POWERPC_EVENT(EXTERNAL_INTERVENTIONS, 0x20, 22), PMC_POWERPC_EVENT(EXTERNAL_PUSHES, 0x20, 23), PMC_POWERPC_EVENT(EXTERNAL_SNOOP_RETRY, 0x20, 24), PMC_POWERPC_EVENT(DTQ_FULL_CYCLES, 0x20, 25), PMC_POWERPC_EVENT(BUS_RETRY, 0x20, 26), PMC_POWERPC_EVENT(L2_VALID_REQUEST, 0x20, 27), PMC_POWERPC_EVENT(BORDQ_FULL, 0x20, 28), PMC_POWERPC_EVENT(BUS_TAS_FOR_READS, 0x20, 42), PMC_POWERPC_EVENT(BUS_TAS_FOR_WRITES, 0x20, 43), PMC_POWERPC_EVENT(BUS_READS_NOT_RETRIED, 0x20, 44), PMC_POWERPC_EVENT(BUS_WRITES_NOT_RETRIED, 0x20, 45), PMC_POWERPC_EVENT(BUS_READS_WRITES_NOT_RETRIED, 0x20, 46), PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_L1_RETRY, 0x20, 47), PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_PREVIOUS_ADJACENT, 0x20, 48), PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_COLLISION, 0x20, 49), PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_INTERVENTION_ORDERING, 0x20, 50), PMC_POWERPC_EVENT(SNOOP_REQUESTS, 0x20, 51), PMC_POWERPC_EVENT(PREFETCH_ENGINE_REQUEST, 0x20, 52), PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD, 0x20, 53), PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_STORE, 0x20, 54), PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_INSTR_FETCH, 0x20, 55), PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD_STORE_INSTR_FETCH, 0x20, 56), PMC_POWERPC_EVENT(PREFETCH_ENGINE_FULL, 0x20, 57) }; static size_t mpc7xxx_event_codes_size = nitems(mpc7xxx_event_codes); static pmc_value_t mpc7xxx_pmcn_read(unsigned int pmc) { switch (pmc) { case 0: return (mfspr(SPR_PMC1_74XX)); case 1: return (mfspr(SPR_PMC2_74XX)); case 2: return (mfspr(SPR_PMC3_74XX)); case 3: return (mfspr(SPR_PMC4_74XX)); case 4: return (mfspr(SPR_PMC5_74XX)); case 5: return (mfspr(SPR_PMC6_74XX)); default: panic("Invalid PMC number: %d\n", pmc); } } static void mpc7xxx_pmcn_write(unsigned int pmc, uint32_t val) { switch (pmc) { case 0: mtspr(SPR_PMC1_74XX, val); break; case 1: mtspr(SPR_PMC2_74XX, val); break; case 2: mtspr(SPR_PMC3_74XX, val); break; case 3: mtspr(SPR_PMC4_74XX, val); break; case 4: mtspr(SPR_PMC5_74XX, val); break; case 5: mtspr(SPR_PMC6_74XX, val); break; default: panic("Invalid PMC number: %d\n", pmc); } } static void mpc7xxx_set_pmc(int cpu, int ri, int config) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; uint32_t config_mask; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; /* The mask is inverted (enable is 1) compared to the flags in * MMCR0, which are Freeze flags. */ config_mask = ~config & POWERPC_PMC_ENABLE; config &= ~POWERPC_PMC_ENABLE; /* Enable/disable the PMC. */ switch (ri) { case 0: pmc_mmcr = mfspr(SPR_MMCR0_74XX); pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, config); mtspr(SPR_MMCR0_74XX, pmc_mmcr); break; case 1: pmc_mmcr = mfspr(SPR_MMCR0_74XX); pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, config); mtspr(SPR_MMCR0_74XX, pmc_mmcr); break; case 2: pmc_mmcr = mfspr(SPR_MMCR1_74XX); pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, config); mtspr(SPR_MMCR1_74XX, pmc_mmcr); break; case 3: pmc_mmcr = mfspr(SPR_MMCR0_74XX); pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, config); mtspr(SPR_MMCR0_74XX, pmc_mmcr); break; case 4: pmc_mmcr = mfspr(SPR_MMCR1_74XX); pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, config); mtspr(SPR_MMCR1_74XX, pmc_mmcr); break; case 5: pmc_mmcr = mfspr(SPR_MMCR1_74XX); pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, config); mtspr(SPR_MMCR1_74XX, pmc_mmcr); break; } if (config != PMCN_NONE) { pmc_mmcr = mfspr(SPR_MMCR0_74XX); pmc_mmcr &= ~SPR_MMCR0_FC; pmc_mmcr |= config; mtspr(SPR_MMCR0_74XX, pmc_mmcr); } } static int mpc7xxx_pcpu_init(struct pmc_mdep *md, int cpu) { powerpc_pcpu_init(md, cpu); /* Clear the MMCRs, and set FC, to disable all PMCs. */ mtspr(SPR_MMCR0_74XX, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE); mtspr(SPR_MMCR1_74XX, 0); return (0); } static int mpc7xxx_pcpu_fini(struct pmc_mdep *md, int cpu) { uint32_t mmcr0 = mfspr(SPR_MMCR0_74XX); mtmsr(mfmsr() & ~PSL_PMM); mmcr0 |= SPR_MMCR0_FC; mtspr(SPR_MMCR0_74XX, mmcr0); return (powerpc_pcpu_fini(md, cpu)); } static void mpc7xxx_resume_pmc(bool ie) { /* Re-enable PERF exceptions. */ if (ie) mtspr(SPR_MMCR0_74XX, (mfspr(SPR_MMCR0_74XX) & ~SPR_MMCR0_FC) | SPR_MMCR0_PMXE); } int pmc_mpc7xxx_initialize(struct pmc_mdep *pmc_mdep) { struct pmc_classdep *pcd; pmc_mdep->pmd_cputype = PMC_CPU_PPC_7450; pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; pcd->pcd_caps = POWERPC_PMC_CAPS; pcd->pcd_class = PMC_CLASS_PPC7450; pcd->pcd_num = MPC7XXX_MAX_PMCS; pcd->pcd_ri = pmc_mdep->pmd_npmc; pcd->pcd_width = 32; /* All PMCs, even in ppc970, are 32-bit */ pcd->pcd_allocate_pmc = powerpc_allocate_pmc; pcd->pcd_config_pmc = powerpc_config_pmc; pcd->pcd_pcpu_fini = mpc7xxx_pcpu_fini; pcd->pcd_pcpu_init = mpc7xxx_pcpu_init; pcd->pcd_describe = powerpc_describe; pcd->pcd_get_config = powerpc_get_config; pcd->pcd_read_pmc = powerpc_read_pmc; pcd->pcd_release_pmc = powerpc_release_pmc; pcd->pcd_start_pmc = powerpc_start_pmc; pcd->pcd_stop_pmc = powerpc_stop_pmc; pcd->pcd_write_pmc = powerpc_write_pmc; pmc_mdep->pmd_npmc += MPC7XXX_MAX_PMCS; pmc_mdep->pmd_intr = powerpc_pmc_intr; ppc_event_codes = mpc7xxx_event_codes; ppc_event_codes_size = mpc7xxx_event_codes_size; ppc_event_first = PMC_EV_PPC7450_FIRST; ppc_event_last = PMC_EV_PPC7450_LAST; ppc_max_pmcs = MPC7XXX_MAX_PMCS; + ppc_class = pcd->pcd_class; powerpc_set_pmc = mpc7xxx_set_pmc; powerpc_pmcn_read = mpc7xxx_pmcn_read; powerpc_pmcn_write = mpc7xxx_pmcn_write; powerpc_resume_pmc = mpc7xxx_resume_pmc; return (0); } diff --git a/sys/dev/hwpmc/hwpmc_power8.c b/sys/dev/hwpmc/hwpmc_power8.c index 0d93e4ae2964..287aa45a966d 100644 --- a/sys/dev/hwpmc/hwpmc_power8.c +++ b/sys/dev/hwpmc/hwpmc_power8.c @@ -1,240 +1,243 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Justin Hibbits * Copyright (c) 2020 Leandro Lupori * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "hwpmc_powerpc.h" #define POWER8_MAX_PMCS 6 #define PM_EVENT_CODE(pe) (pe & 0xffff) #define PM_EVENT_COUNTER(pe) ((pe >> 16) & 0xffff) #define PM_CYC 0x1e #define PM_INST_CMPL 0x02 static void power8_set_pmc(int cpu, int ri, int config) { register_t mmcr; /* Select event */ switch (ri) { case 0: case 1: case 2: case 3: mmcr = mfspr(SPR_MMCR1); mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri); mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE); mtspr(SPR_MMCR1, mmcr); break; } /* * By default, freeze counter in all states. * If counter is being started, unfreeze it in selected states. */ mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri); if (config != PMCN_NONE) { if (config & POWERPC_PMC_USER_ENABLE) mmcr &= ~(SPR_MMCR2_FCNP0(ri) | SPR_MMCR2_FCNP1(ri)); if (config & POWERPC_PMC_KERNEL_ENABLE) mmcr &= ~(SPR_MMCR2_FCNH(ri) | SPR_MMCR2_FCNS(ri)); } mtspr(SPR_MMCR2, mmcr); } static int power8_pcpu_init(struct pmc_mdep *md, int cpu) { register_t mmcr0; int i; powerpc_pcpu_init(md, cpu); /* Freeze all counters before modifying PMC registers */ mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC; mtspr(SPR_MMCR0, mmcr0); /* * Now setup MMCR0: * - PMAO=0: clear alerts * - FCPC=0, FCP=0: don't freeze counters in problem state * - FCECE: Freeze Counters on Enabled Condition or Event * - PMC1CE/PMCNCE: PMC1/N Condition Enable */ mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP); mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE; mtspr(SPR_MMCR0, mmcr0); /* Clear all PMCs to prevent enabled condition interrupts */ for (i = 0; i < POWER8_MAX_PMCS; i++) powerpc_pmcn_write(i, 0); /* Disable events in PMCs 1-4 */ mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL); /* Freeze each counter, in all states */ mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) | SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5)); /* Enable interrupts, unset global freeze */ mmcr0 &= ~SPR_MMCR0_FC; mmcr0 |= SPR_MMCR0_PMAE; mtspr(SPR_MMCR0, mmcr0); return (0); } static int power8_pcpu_fini(struct pmc_mdep *md, int cpu) { register_t mmcr0; /* Freeze counters, disable interrupts */ mmcr0 = mfspr(SPR_MMCR0); mmcr0 &= ~SPR_MMCR0_PMAE; mmcr0 |= SPR_MMCR0_FC; mtspr(SPR_MMCR0, mmcr0); return (powerpc_pcpu_fini(md, cpu)); } static void power8_resume_pmc(bool ie) { register_t mmcr0; /* Unfreeze counters and re-enable PERF exceptions if requested. */ mmcr0 = mfspr(SPR_MMCR0); mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE); if (ie) mmcr0 |= SPR_MMCR0_PMAE; mtspr(SPR_MMCR0, mmcr0); } static int power8_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { uint32_t caps, config, counter, pe; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); pe = a->pm_md.pm_event; counter = PM_EVENT_COUNTER(pe); config = PM_EVENT_CODE(pe); + if (a->pm_class != PMC_CLASS_POWER8) + return (EINVAL); + /* * PMC5 and PMC6 are not programmable and always count instructions * completed and cycles, respectively. * * When counter is 0 any of the 4 programmable PMCs may be used for * the specified event, otherwise it must match ri + 1. */ if (counter == 0 && config == PM_INST_CMPL) counter = 5; else if (counter == 0 && config == PM_CYC) counter = 6; else if (counter > 4) return (EINVAL); if (counter != 0 && counter != ri + 1) return (EINVAL); caps = a->pm_caps; if (caps & PMC_CAP_SYSTEM) config |= POWERPC_PMC_KERNEL_ENABLE; if (caps & PMC_CAP_USER) config |= POWERPC_PMC_USER_ENABLE; if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) config |= POWERPC_PMC_ENABLE; pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x", cpu, ri, config); return (0); } int pmc_power8_initialize(struct pmc_mdep *pmc_mdep) { struct pmc_classdep *pcd; pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8; pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; pcd->pcd_caps = POWERPC_PMC_CAPS; pcd->pcd_class = PMC_CLASS_POWER8; pcd->pcd_num = POWER8_MAX_PMCS; pcd->pcd_ri = pmc_mdep->pmd_npmc; pcd->pcd_width = 32; pcd->pcd_pcpu_init = power8_pcpu_init; pcd->pcd_pcpu_fini = power8_pcpu_fini; pcd->pcd_allocate_pmc = power8_allocate_pmc; pcd->pcd_release_pmc = powerpc_release_pmc; pcd->pcd_start_pmc = powerpc_start_pmc; pcd->pcd_stop_pmc = powerpc_stop_pmc; pcd->pcd_get_config = powerpc_get_config; pcd->pcd_config_pmc = powerpc_config_pmc; pcd->pcd_describe = powerpc_describe; pcd->pcd_read_pmc = powerpc_read_pmc; pcd->pcd_write_pmc = powerpc_write_pmc; pmc_mdep->pmd_npmc += POWER8_MAX_PMCS; pmc_mdep->pmd_intr = powerpc_pmc_intr; ppc_max_pmcs = POWER8_MAX_PMCS; powerpc_set_pmc = power8_set_pmc; powerpc_pmcn_read = powerpc_pmcn_read_default; powerpc_pmcn_write = powerpc_pmcn_write_default; powerpc_resume_pmc = power8_resume_pmc; return (0); } diff --git a/sys/dev/hwpmc/hwpmc_powerpc.c b/sys/dev/hwpmc/hwpmc_powerpc.c index 0f14f93e6415..4377c3075bca 100644 --- a/sys/dev/hwpmc/hwpmc_powerpc.c +++ b/sys/dev/hwpmc/hwpmc_powerpc.c @@ -1,651 +1,655 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011,2013 Justin Hibbits * Copyright (c) 2005, Joseph Koshy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "hwpmc_powerpc.h" #ifdef __powerpc64__ #define OFFSET 4 /* Account for the TOC reload slot */ #else #define OFFSET 0 #endif struct powerpc_cpu **powerpc_pcpu; struct pmc_ppc_event *ppc_event_codes; size_t ppc_event_codes_size; int ppc_event_first; int ppc_event_last; int ppc_max_pmcs; +enum pmc_class ppc_class; void (*powerpc_set_pmc)(int cpu, int ri, int config); pmc_value_t (*powerpc_pmcn_read)(unsigned int pmc); void (*powerpc_pmcn_write)(unsigned int pmc, uint32_t val); void (*powerpc_resume_pmc)(bool ie); int pmc_save_kernel_callchain(uintptr_t *cc, int maxsamples, struct trapframe *tf) { uintptr_t *osp, *sp; uintptr_t pc; int frames = 0; cc[frames++] = PMC_TRAPFRAME_TO_PC(tf); sp = (uintptr_t *)PMC_TRAPFRAME_TO_FP(tf); osp = (uintptr_t *)PAGE_SIZE; for (; frames < maxsamples; frames++) { if (sp <= osp) break; #ifdef __powerpc64__ pc = sp[2]; #else pc = sp[1]; #endif if ((pc & 3) || (pc < 0x100)) break; /* * trapexit() and asttrapexit() are sentinels * for kernel stack tracing. * */ if (pc + OFFSET == (uintptr_t) &trapexit || pc + OFFSET == (uintptr_t) &asttrapexit) break; cc[frames] = pc; osp = sp; sp = (uintptr_t *)*sp; } return (frames); } static int powerpc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) { return (0); } static int powerpc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) { return (0); } int powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char powerpc_name[PMC_NAME_MAX]; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d], illegal CPU %d", __LINE__, cpu)); phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; snprintf(powerpc_name, sizeof(powerpc_name), "POWERPC-%d", ri); if ((error = copystr(powerpc_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return error; pi->pm_class = powerpc_pcpu[cpu]->pc_class; if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { pi->pm_enabled = TRUE; *ppmc = phw->phw_pmc; } else { pi->pm_enabled = FALSE; *ppmc = NULL; } return (0); } int powerpc_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; return (0); } int powerpc_pcpu_init(struct pmc_mdep *md, int cpu) { struct pmc_cpu *pc; struct powerpc_cpu *pac; struct pmc_hw *phw; int first_ri, i; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] wrong cpu number %d", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"powerpc-init cpu=%d", cpu); powerpc_pcpu[cpu] = pac = malloc(sizeof(struct powerpc_cpu), M_PMC, M_WAITOK|M_ZERO); pac->pc_ppcpmcs = malloc(sizeof(struct pmc_hw) * ppc_max_pmcs, M_PMC, M_WAITOK|M_ZERO); pac->pc_class = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC].pcd_class; pc = pmc_pcpu[cpu]; first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC].pcd_ri; KASSERT(pc != NULL, ("[powerpc,%d] NULL per-cpu pointer", __LINE__)); for (i = 0, phw = pac->pc_ppcpmcs; i < ppc_max_pmcs; i++, phw++) { phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); phw->phw_pmc = NULL; pc->pc_hwpmcs[i + first_ri] = phw; } return (0); } int powerpc_pcpu_fini(struct pmc_mdep *md, int cpu) { PMCDBG1(MDP,INI,1,"powerpc-fini cpu=%d", cpu); free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); free(powerpc_pcpu[cpu], M_PMC); return (0); } int powerpc_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { enum pmc_event pe; uint32_t caps, config = 0, counter = 0; int i; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); + if (a->pm_class != ppc_class) + return (EINVAL); + caps = a->pm_caps; pe = a->pm_ev; if (pe < ppc_event_first || pe > ppc_event_last) return (EINVAL); for (i = 0; i < ppc_event_codes_size; i++) { if (ppc_event_codes[i].pe_event == pe) { config = ppc_event_codes[i].pe_code; counter = ppc_event_codes[i].pe_flags; break; } } if (i == ppc_event_codes_size) return (EINVAL); if ((counter & (1 << ri)) == 0) return (EINVAL); if (caps & PMC_CAP_SYSTEM) config |= POWERPC_PMC_KERNEL_ENABLE; if (caps & PMC_CAP_USER) config |= POWERPC_PMC_USER_ENABLE; if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) config |= POWERPC_PMC_ENABLE; pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x", cpu, ri, config); return (0); } int powerpc_release_pmc(int cpu, int ri, struct pmc *pmc) { struct pmc_hw *phw; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; KASSERT(phw->phw_pmc == NULL, ("[powerpc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); return (0); } int powerpc_start_pmc(int cpu, int ri) { struct pmc *pm; PMCDBG2(MDP,STA,1,"powerpc-start cpu=%d ri=%d", cpu, ri); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; powerpc_set_pmc(cpu, ri, pm->pm_md.pm_powerpc.pm_powerpc_evsel); return (0); } int powerpc_stop_pmc(int cpu, int ri) { PMCDBG2(MDP,STO,1, "powerpc-stop cpu=%d ri=%d", cpu, ri); powerpc_set_pmc(cpu, ri, PMCN_NONE); return (0); } int powerpc_config_pmc(int cpu, int ri, struct pmc *pm) { struct pmc_hw *phw; PMCDBG3(MDP,CFG,1, "powerpc-config cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; KASSERT(pm == NULL || phw->phw_pmc == NULL, ("[powerpc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__, pm, phw->phw_pmc)); phw->phw_pmc = pm; return (0); } pmc_value_t powerpc_pmcn_read_default(unsigned int pmc) { pmc_value_t val; if (pmc > ppc_max_pmcs) panic("Invalid PMC number: %d\n", pmc); switch (pmc) { case 0: val = mfspr(SPR_PMC1); break; case 1: val = mfspr(SPR_PMC2); break; case 2: val = mfspr(SPR_PMC3); break; case 3: val = mfspr(SPR_PMC4); break; case 4: val = mfspr(SPR_PMC5); break; case 5: val = mfspr(SPR_PMC6); break; case 6: val = mfspr(SPR_PMC7); break; case 7: val = mfspr(SPR_PMC8); break; } return (val); } void powerpc_pmcn_write_default(unsigned int pmc, uint32_t val) { if (pmc > ppc_max_pmcs) panic("Invalid PMC number: %d\n", pmc); switch (pmc) { case 0: mtspr(SPR_PMC1, val); break; case 1: mtspr(SPR_PMC2, val); break; case 2: mtspr(SPR_PMC3, val); break; case 3: mtspr(SPR_PMC4, val); break; case 4: mtspr(SPR_PMC5, val); break; case 5: mtspr(SPR_PMC6, val); break; case 6: mtspr(SPR_PMC7, val); break; case 7: mtspr(SPR_PMC8, val); break; } } int powerpc_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t p, r, tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); /* * After an interrupt occurs because of a PMC overflow, the PMC value * is not always MAX_PMC_VALUE + 1, but may be a little above it. * This may mess up calculations and frustrate machine independent * layer expectations, such as that no value read should be greater * than reload count in sampling mode. * To avoid these issues, use MAX_PMC_VALUE as an upper limit. */ p = MIN(powerpc_pmcn_read(ri), POWERPC_MAX_PMC_VALUE); r = pm->pm_sc.pm_reloadcount; if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { /* * Special case 1: r is too big * This usually happens when a PMC write fails, the PMC is * stopped and then it is read. * * Special case 2: PMC was reseted or has a value * that should not be possible with current r. * * In the above cases, just return 0 instead of an arbitrary * value. */ if (r > POWERPC_MAX_PMC_VALUE || p + r <= POWERPC_MAX_PMC_VALUE) tmp = 0; else tmp = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(p); } else tmp = p + (POWERPC_MAX_PMC_VALUE + 1) * PPC_OVERFLOWCNT(pm); PMCDBG5(MDP,REA,1,"ppc-read cpu=%d ri=%d -> %jx (%jx,%jx)", cpu, ri, (uintmax_t)tmp, (uintmax_t)PPC_OVERFLOWCNT(pm), (uintmax_t)p); *v = tmp; return (0); } int powerpc_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; pmc_value_t vlo; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < ppc_max_pmcs, ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; if (PMC_IS_COUNTING_MODE(PMC_TO_MODE(pm))) { PPC_OVERFLOWCNT(pm) = v / (POWERPC_MAX_PMC_VALUE + 1); vlo = v % (POWERPC_MAX_PMC_VALUE + 1); } else if (v > POWERPC_MAX_PMC_VALUE) { PMCDBG3(MDP,WRI,2, "powerpc-write cpu=%d ri=%d: PMC value is too big: %jx", cpu, ri, (uintmax_t)v); return (EINVAL); } else vlo = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); PMCDBG5(MDP,WRI,1,"powerpc-write cpu=%d ri=%d -> %jx (%jx,%jx)", cpu, ri, (uintmax_t)v, (uintmax_t)PPC_OVERFLOWCNT(pm), (uintmax_t)vlo); powerpc_pmcn_write(ri, vlo); return (0); } int powerpc_pmc_intr(struct trapframe *tf) { struct pmc *pm; struct powerpc_cpu *pc; int cpu, error, i, retval; cpu = curcpu; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] out of range CPU %d", __LINE__, cpu)); PMCDBG3(MDP,INT,1, "cpu=%d tf=%p um=%d", cpu, (void *) tf, TRAPF_USERMODE(tf)); retval = 0; pc = powerpc_pcpu[cpu]; /* * Look for a running, sampling PMC which has overflowed * and which has a valid 'struct pmc' association. */ for (i = 0; i < ppc_max_pmcs; i++) { if (!POWERPC_PMC_HAS_OVERFLOWED(i)) continue; retval = 1; /* Found an interrupting PMC. */ /* * Always clear the PMC, to make it stop interrupting. * If pm is available and in sampling mode, use reload * count, to make PMC read after stop correct. * Otherwise, just reset the PMC. */ if ((pm = pc->pc_ppcpmcs[i].phw_pmc) != NULL && PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { if (pm->pm_state != PMC_STATE_RUNNING) { powerpc_write_pmc(cpu, i, pm->pm_sc.pm_reloadcount); continue; } } else { if (pm != NULL) { /* !PMC_IS_SAMPLING_MODE */ PPC_OVERFLOWCNT(pm) = (PPC_OVERFLOWCNT(pm) + 1) % PPC_OVERFLOWCNT_MAX; PMCDBG3(MDP,INT,2, "cpu=%d ri=%d: overflowcnt=%d", cpu, i, PPC_OVERFLOWCNT(pm)); } powerpc_pmcn_write(i, 0); continue; } error = pmc_process_interrupt(PMC_HR, pm, tf); if (error != 0) { PMCDBG3(MDP,INT,3, "cpu=%d ri=%d: error %d processing interrupt", cpu, i, error); powerpc_stop_pmc(cpu, i); } /* Reload sampling count */ powerpc_write_pmc(cpu, i, pm->pm_sc.pm_reloadcount); } if (retval) counter_u64_add(pmc_stats.pm_intr_processed, 1); else counter_u64_add(pmc_stats.pm_intr_ignored, 1); /* * Re-enable PERF exceptions if we were able to find the interrupt * source and handle it. Otherwise, it's better to disable PERF * interrupts, to avoid the risk of processing the same interrupt * forever. */ powerpc_resume_pmc(retval != 0); if (retval == 0) log(LOG_WARNING, "pmc_intr: couldn't find interrupting PMC on cpu %d - " "disabling PERF interrupts\n", cpu); return (retval); } struct pmc_mdep * pmc_md_initialize() { struct pmc_mdep *pmc_mdep; int error; uint16_t vers; /* * Allocate space for pointers to PMC HW descriptors and for * the MDEP structure used by MI code. */ powerpc_pcpu = malloc(sizeof(struct powerpc_cpu *) * pmc_cpu_max(), M_PMC, M_WAITOK|M_ZERO); /* Just one class */ pmc_mdep = pmc_mdep_alloc(1); vers = mfpvr() >> 16; pmc_mdep->pmd_switch_in = powerpc_switch_in; pmc_mdep->pmd_switch_out = powerpc_switch_out; switch (vers) { case MPC7447A: case MPC7448: case MPC7450: case MPC7455: case MPC7457: error = pmc_mpc7xxx_initialize(pmc_mdep); break; case IBM970: case IBM970FX: case IBM970MP: error = pmc_ppc970_initialize(pmc_mdep); break; case IBMPOWER8E: case IBMPOWER8NVL: case IBMPOWER8: case IBMPOWER9: error = pmc_power8_initialize(pmc_mdep); break; case FSL_E500v1: case FSL_E500v2: case FSL_E500mc: case FSL_E5500: error = pmc_e500_initialize(pmc_mdep); break; default: error = -1; break; } if (error != 0) { pmc_mdep_free(pmc_mdep); pmc_mdep = NULL; } /* Set the value for kern.hwpmc.cpuid */ snprintf(pmc_cpuid, sizeof(pmc_cpuid), "%08x", mfpvr()); return (pmc_mdep); } void pmc_md_finalize(struct pmc_mdep *md) { free(powerpc_pcpu, M_PMC); powerpc_pcpu = NULL; } int pmc_save_user_callchain(uintptr_t *cc, int maxsamples, struct trapframe *tf) { uintptr_t *osp, *sp; int frames = 0; cc[frames++] = PMC_TRAPFRAME_TO_PC(tf); sp = (uintptr_t *)PMC_TRAPFRAME_TO_FP(tf); osp = NULL; for (; frames < maxsamples; frames++) { if (sp <= osp) break; osp = sp; #ifdef __powerpc64__ /* Check if 32-bit mode. */ if (!(tf->srr1 & PSL_SF)) { cc[frames] = fuword32((uint32_t *)sp + 1); sp = (uintptr_t *)(uintptr_t)fuword32(sp); } else { cc[frames] = fuword(sp + 2); sp = (uintptr_t *)fuword(sp); } #else cc[frames] = fuword32((uint32_t *)sp + 1); sp = (uintptr_t *)fuword32(sp); #endif } return (frames); } diff --git a/sys/dev/hwpmc/hwpmc_powerpc.h b/sys/dev/hwpmc/hwpmc_powerpc.h index 31c20592a21f..10aa0ad75b14 100644 --- a/sys/dev/hwpmc/hwpmc_powerpc.h +++ b/sys/dev/hwpmc/hwpmc_powerpc.h @@ -1,114 +1,115 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Justin Hibbits * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_HWPMC_POWERPC_H_ #define _DEV_HWPMC_POWERPC_H_ 1 #ifdef _KERNEL #define POWERPC_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \ PMC_CAP_SYSTEM | PMC_CAP_EDGE | \ PMC_CAP_THRESHOLD | PMC_CAP_READ | \ PMC_CAP_WRITE | PMC_CAP_INVERT | \ PMC_CAP_QUALIFIER) #define POWERPC_PMC_KERNEL_ENABLE (0x1 << 30) #define POWERPC_PMC_USER_ENABLE (0x1 << 31) #define POWERPC_PMC_ENABLE (POWERPC_PMC_KERNEL_ENABLE | POWERPC_PMC_USER_ENABLE) #define POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (0x80000000-(V)) #define POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (0x80000000-(P)) #define POWERPC_MAX_PMC_VALUE 0x7fffffffUL #define POWERPC_PMC_HAS_OVERFLOWED(n) (powerpc_pmcn_read(n) & (0x1 << 31)) /* * PMC value is used with OVERFLOWCNT to simulate a 64-bit counter to the * machine independent part of hwpmc. */ #define PPC_OVERFLOWCNT(pm) (pm)->pm_md.pm_powerpc.pm_powerpc_overflowcnt #define PPC_OVERFLOWCNT_MAX 0x200000000UL struct powerpc_cpu { struct pmc_hw *pc_ppcpmcs; enum pmc_class pc_class; }; struct pmc_ppc_event { enum pmc_event pe_event; uint32_t pe_flags; #define PMC_FLAG_PMC1 0x01 #define PMC_FLAG_PMC2 0x02 #define PMC_FLAG_PMC3 0x04 #define PMC_FLAG_PMC4 0x08 #define PMC_FLAG_PMC5 0x10 #define PMC_FLAG_PMC6 0x20 #define PMC_FLAG_PMC7 0x40 #define PMC_FLAG_PMC8 0x80 uint32_t pe_code; }; extern struct powerpc_cpu **powerpc_pcpu; extern struct pmc_ppc_event *ppc_event_codes; extern size_t ppc_event_codes_size; extern int ppc_event_first; extern int ppc_event_last; extern int ppc_max_pmcs; +extern enum pmc_class ppc_class; extern void (*powerpc_set_pmc)(int cpu, int ri, int config); extern pmc_value_t (*powerpc_pmcn_read)(unsigned int pmc); extern void (*powerpc_pmcn_write)(unsigned int pmc, uint32_t val); extern void (*powerpc_resume_pmc)(bool ie); int pmc_e500_initialize(struct pmc_mdep *pmc_mdep); int pmc_mpc7xxx_initialize(struct pmc_mdep *pmc_mdep); int pmc_ppc970_initialize(struct pmc_mdep *pmc_mdep); int pmc_power8_initialize(struct pmc_mdep *pmc_mdep); int powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc); int powerpc_get_config(int cpu, int ri, struct pmc **ppm); int powerpc_pcpu_init(struct pmc_mdep *md, int cpu); int powerpc_pcpu_fini(struct pmc_mdep *md, int cpu); int powerpc_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a); int powerpc_release_pmc(int cpu, int ri, struct pmc *pmc); int powerpc_start_pmc(int cpu, int ri); int powerpc_stop_pmc(int cpu, int ri); int powerpc_config_pmc(int cpu, int ri, struct pmc *pm); pmc_value_t powerpc_pmcn_read_default(unsigned int pmc); void powerpc_pmcn_write_default(unsigned int pmc, uint32_t val); int powerpc_read_pmc(int cpu, int ri, pmc_value_t *v); int powerpc_write_pmc(int cpu, int ri, pmc_value_t v); int powerpc_pmc_intr(struct trapframe *tf); #endif /* _KERNEL */ #endif /* _DEV_HWPMC_POWERPC_H_ */ diff --git a/sys/dev/hwpmc/hwpmc_ppc970.c b/sys/dev/hwpmc/hwpmc_ppc970.c index 39dc4efb290a..3e92c4dc6aa2 100644 --- a/sys/dev/hwpmc/hwpmc_ppc970.c +++ b/sys/dev/hwpmc/hwpmc_ppc970.c @@ -1,390 +1,391 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Justin Hibbits * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "hwpmc_powerpc.h" #define PPC970_MAX_PMCS 8 #define PMC_PPC970_FLAG_PMCS 0x000000ff /* MMCR0, PMC1 is 8 bytes in, PMC2 is 1 byte in. */ #define PPC970_SET_MMCR0_PMCSEL(r, x, i) \ ((r & ~(0x1f << (7 * (1 - i) + 1))) | (x << (7 * (1 - i) + 1))) /* MMCR1 has 6 PMC*SEL items (PMC3->PMC8), in sequence. */ #define PPC970_SET_MMCR1_PMCSEL(r, x, i) \ ((r & ~(0x1f << (5 * (7 - i) + 2))) | (x << (5 * (7 - i) + 2))) /* How PMC works on PPC970: * * Any PMC can count a direct event. Indirect events are handled specially. * Direct events: As published. * * Encoding 00 000 -- Add byte lane bit counters * MMCR1[24:31] -- select bit matching PMC being an adder. * Bus events: * PMCxSEL: 1x -- select from byte lane: 10 == lower lane (0/1), 11 == upper * lane (2/3). * PMCxSEL[2:4] -- bit in the byte lane selected. * * PMC[1,2,5,6] == lane 0/lane 2 * PMC[3,4,7,8] == lane 1,3 * * * Lanes: * Lane 0 -- TTM0(FPU,ISU,IFU,VPU) * TTM1(IDU,ISU,STS) * LSU0 byte 0 * LSU1 byte 0 * Lane 1 -- TTM0 * TTM1 * LSU0 byte 1 * LSU1 byte 1 * Lane 2 -- TTM0 * TTM1 * LSU0 byte 2 * LSU1 byte 2 or byte 6 * Lane 3 -- TTM0 * TTM1 * LSU0 byte 3 * LSU1 byte 3 or byte 7 * * Adders: * Add byte lane for PMC (above), bit 0+4, 1+5, 2+6, 3+7 */ static struct pmc_ppc_event ppc970_event_codes[] = { {PMC_EV_PPC970_INSTR_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMCS, .pe_code = 0x09 }, {PMC_EV_PPC970_MARKED_GROUP_DISPATCH, .pe_flags = PMC_FLAG_PMC1, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_COMPLETED, .pe_flags = PMC_FLAG_PMC1, .pe_code = 0x03 }, {PMC_EV_PPC970_GCT_EMPTY, .pe_flags = PMC_FLAG_PMC1, .pe_code = 0x04 }, {PMC_EV_PPC970_RUN_CYCLES, .pe_flags = PMC_FLAG_PMC1, .pe_code = 0x05 }, {PMC_EV_PPC970_OVERFLOW, .pe_flags = PMC_PPC970_FLAG_PMCS, .pe_code = 0x0a }, {PMC_EV_PPC970_CYCLES, .pe_flags = PMC_PPC970_FLAG_PMCS, .pe_code = 0x0f }, {PMC_EV_PPC970_THRESHOLD_TIMEOUT, .pe_flags = PMC_FLAG_PMC2, .pe_code = 0x3 }, {PMC_EV_PPC970_GROUP_DISPATCH, .pe_flags = PMC_FLAG_PMC2, .pe_code = 0x4 }, {PMC_EV_PPC970_BR_MARKED_INSTR_FINISH, .pe_flags = PMC_FLAG_PMC2, .pe_code = 0x5 }, {PMC_EV_PPC970_GCT_EMPTY_BY_SRQ_FULL, .pe_flags = PMC_FLAG_PMC2, .pe_code = 0xb }, {PMC_EV_PPC970_STOP_COMPLETION, .pe_flags = PMC_FLAG_PMC3, .pe_code = 0x1 }, {PMC_EV_PPC970_LSU_EMPTY, .pe_flags = PMC_FLAG_PMC3, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_WITH_INTR, .pe_flags = PMC_FLAG_PMC3, .pe_code = 0x3 }, {PMC_EV_PPC970_CYCLES_IN_SUPER, .pe_flags = PMC_FLAG_PMC3, .pe_code = 0x4 }, {PMC_EV_PPC970_VPU_MARKED_INSTR_COMPLETED, .pe_flags = PMC_FLAG_PMC3, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_IDLE_FXU1_BUSY, .pe_flags = PMC_FLAG_PMC4, .pe_code = 0x2 }, {PMC_EV_PPC970_SRQ_EMPTY, .pe_flags = PMC_FLAG_PMC4, .pe_code = 0x3 }, {PMC_EV_PPC970_MARKED_GROUP_COMPLETED, .pe_flags = PMC_FLAG_PMC4, .pe_code = 0x4 }, {PMC_EV_PPC970_CR_MARKED_INSTR_FINISH, .pe_flags = PMC_FLAG_PMC4, .pe_code = 0x5 }, {PMC_EV_PPC970_DISPATCH_SUCCESS, .pe_flags = PMC_FLAG_PMC5, .pe_code = 0x1 }, {PMC_EV_PPC970_FXU0_IDLE_FXU1_IDLE, .pe_flags = PMC_FLAG_PMC5, .pe_code = 0x2 }, {PMC_EV_PPC970_ONE_PLUS_INSTR_COMPLETED, .pe_flags = PMC_FLAG_PMC5, .pe_code = 0x3 }, {PMC_EV_PPC970_GROUP_MARKED_IDU, .pe_flags = PMC_FLAG_PMC5, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_GROUP_COMPLETE_TIMEOUT, .pe_flags = PMC_FLAG_PMC5, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_BUSY_FXU1_BUSY, .pe_flags = PMC_FLAG_PMC6, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_SENT_TO_STS, .pe_flags = PMC_FLAG_PMC6, .pe_code = 0x3 }, {PMC_EV_PPC970_FXU_MARKED_INSTR_FINISHED, .pe_flags = PMC_FLAG_PMC6, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_GROUP_ISSUED, .pe_flags = PMC_FLAG_PMC6, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_BUSY_FXU1_IDLE, .pe_flags = PMC_FLAG_PMC7, .pe_code = 0x2 }, {PMC_EV_PPC970_GROUP_COMPLETED, .pe_flags = PMC_FLAG_PMC7, .pe_code = 0x3 }, {PMC_EV_PPC970_FPU_MARKED_INSTR_COMPLETED, .pe_flags = PMC_FLAG_PMC7, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_INSTR_FINISH_ANY_UNIT, .pe_flags = PMC_FLAG_PMC7, .pe_code = 0x5 }, {PMC_EV_PPC970_EXTERNAL_INTERRUPT, .pe_flags = PMC_FLAG_PMC8, .pe_code = 0x2 }, {PMC_EV_PPC970_GROUP_DISPATCH_REJECT, .pe_flags = PMC_FLAG_PMC8, .pe_code = 0x3 }, {PMC_EV_PPC970_LSU_MARKED_INSTR_FINISH, .pe_flags = PMC_FLAG_PMC8, .pe_code = 0x4 }, {PMC_EV_PPC970_TIMEBASE_EVENT, .pe_flags = PMC_FLAG_PMC8, .pe_code = 0x5 }, #if 0 {PMC_EV_PPC970_LSU_COMPLETION_STALL, }, {PMC_EV_PPC970_FXU_COMPLETION_STALL, }, {PMC_EV_PPC970_DCACHE_MISS_COMPLETION_STALL, }, {PMC_EV_PPC970_FPU_COMPLETION_STALL, }, {PMC_EV_PPC970_FXU_LONG_INSTR_COMPLETION_STALL, }, {PMC_EV_PPC970_REJECT_COMPLETION_STALL, }, {PMC_EV_PPC970_FPU_LONG_INSTR_COMPLETION_STALL, }, {PMC_EV_PPC970_GCT_EMPTY_BY_ICACHE_MISS, }, {PMC_EV_PPC970_REJECT_COMPLETION_STALL_ERAT_MISS, }, {PMC_EV_PPC970_GCT_EMPTY_BY_BRANCH_MISS_PREDICT, }, #endif }; static size_t ppc970_event_codes_size = nitems(ppc970_event_codes); static void ppc970_set_pmc(int cpu, int ri, int config) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; int config_mask; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; if (config == PMCN_NONE) config = PMC970N_NONE; /* * The mask is inverted (enable is 1) compared to the flags in MMCR0, * which are Freeze flags. */ config_mask = ~config & POWERPC_PMC_ENABLE; config &= ~POWERPC_PMC_ENABLE; /* * Disable the PMCs. */ switch (ri) { case 0: case 1: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC970_SET_MMCR0_PMCSEL(pmc_mmcr, config, ri); mtspr(SPR_MMCR0, pmc_mmcr); break; case 2: case 3: case 4: case 5: case 6: case 7: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC970_SET_MMCR1_PMCSEL(pmc_mmcr, config, ri); mtspr(SPR_MMCR1, pmc_mmcr); break; } if (config != PMC970N_NONE) { pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr &= ~SPR_MMCR0_FC; pmc_mmcr |= config_mask; mtspr(SPR_MMCR0, pmc_mmcr); } } static int ppc970_pcpu_init(struct pmc_mdep *md, int cpu) { powerpc_pcpu_init(md, cpu); /* Clear the MMCRs, and set FC, to disable all PMCs. */ /* 970 PMC is not counted when set to 0x08 */ mtspr(SPR_MMCR0, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE | SPR_MMCR0_PMC1SEL(0x8) | SPR_MMCR0_PMC2SEL(0x8)); mtspr(SPR_MMCR1, 0x4218420); return (0); } static int ppc970_pcpu_fini(struct pmc_mdep *md, int cpu) { register_t mmcr0; /* Freeze counters, disable interrupts */ mmcr0 = mfspr(SPR_MMCR0); mmcr0 &= ~SPR_MMCR0_PMXE; mmcr0 |= SPR_MMCR0_FC; mtspr(SPR_MMCR0, mmcr0); return (powerpc_pcpu_fini(md, cpu)); } static void ppc970_resume_pmc(bool ie) { register_t mmcr0; /* Unfreeze counters and re-enable PERF exceptions if requested. */ mmcr0 = mfspr(SPR_MMCR0); mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMXE); if (ie) mmcr0 |= SPR_MMCR0_PMXE; mtspr(SPR_MMCR0, mmcr0); } int pmc_ppc970_initialize(struct pmc_mdep *pmc_mdep) { struct pmc_classdep *pcd; pmc_mdep->pmd_cputype = PMC_CPU_PPC_970; pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; pcd->pcd_caps = POWERPC_PMC_CAPS; pcd->pcd_class = PMC_CLASS_PPC970; pcd->pcd_num = PPC970_MAX_PMCS; pcd->pcd_ri = pmc_mdep->pmd_npmc; pcd->pcd_width = 32; pcd->pcd_allocate_pmc = powerpc_allocate_pmc; pcd->pcd_config_pmc = powerpc_config_pmc; pcd->pcd_pcpu_fini = ppc970_pcpu_fini; pcd->pcd_pcpu_init = ppc970_pcpu_init; pcd->pcd_describe = powerpc_describe; pcd->pcd_get_config = powerpc_get_config; pcd->pcd_read_pmc = powerpc_read_pmc; pcd->pcd_release_pmc = powerpc_release_pmc; pcd->pcd_start_pmc = powerpc_start_pmc; pcd->pcd_stop_pmc = powerpc_stop_pmc; pcd->pcd_write_pmc = powerpc_write_pmc; pmc_mdep->pmd_npmc += PPC970_MAX_PMCS; pmc_mdep->pmd_intr = powerpc_pmc_intr; ppc_event_codes = ppc970_event_codes; ppc_event_codes_size = ppc970_event_codes_size; ppc_event_first = PMC_EV_PPC970_FIRST; ppc_event_last = PMC_EV_PPC970_LAST; ppc_max_pmcs = PPC970_MAX_PMCS; + ppc_class = pcd->pcd_class; powerpc_set_pmc = ppc970_set_pmc; powerpc_pmcn_read = powerpc_pmcn_read_default; powerpc_pmcn_write = powerpc_pmcn_write_default; powerpc_resume_pmc = ppc970_resume_pmc; return (0); } diff --git a/sys/dev/hwpmc/hwpmc_uncore.c b/sys/dev/hwpmc/hwpmc_uncore.c index 2c638833dcd9..19017cabddd9 100644 --- a/sys/dev/hwpmc/hwpmc_uncore.c +++ b/sys/dev/hwpmc/hwpmc_uncore.c @@ -1,859 +1,862 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010 Fabien Thomas * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Intel Uncore PMCs. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #if (__FreeBSD_version >= 1100000) #include #else #include #endif #include #include #include #define UCF_PMC_CAPS \ (PMC_CAP_READ | PMC_CAP_WRITE) #define UCP_PMC_CAPS \ (PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \ PMC_CAP_INVERT | PMC_CAP_QUALIFIER | PMC_CAP_PRECISE) #define SELECTSEL(x) \ (((x) == PMC_CPU_INTEL_SANDYBRIDGE || (x) == PMC_CPU_INTEL_HASWELL) ? \ UCP_CB0_EVSEL0 : UCP_EVSEL0) #define SELECTOFF(x) \ (((x) == PMC_CPU_INTEL_SANDYBRIDGE || (x) == PMC_CPU_INTEL_HASWELL) ? \ UCF_OFFSET_SB : UCF_OFFSET) static enum pmc_cputype uncore_cputype; struct uncore_cpu { volatile uint32_t pc_resync; volatile uint32_t pc_ucfctrl; /* Fixed function control. */ volatile uint64_t pc_globalctrl; /* Global control register. */ struct pmc_hw pc_uncorepmcs[]; }; static struct uncore_cpu **uncore_pcpu; static uint64_t uncore_pmcmask; static int uncore_ucf_ri; /* relative index of fixed counters */ static int uncore_ucf_width; static int uncore_ucf_npmc; static int uncore_ucp_width; static int uncore_ucp_npmc; static int uncore_pcpu_noop(struct pmc_mdep *md, int cpu) { (void) md; (void) cpu; return (0); } static int uncore_pcpu_init(struct pmc_mdep *md, int cpu) { struct pmc_cpu *pc; struct uncore_cpu *cc; struct pmc_hw *phw; int uncore_ri, n, npmc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[ucf,%d] insane cpu number %d", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"uncore-init cpu=%d", cpu); uncore_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_ri; npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_num; npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF].pcd_num; cc = malloc(sizeof(struct uncore_cpu) + npmc * sizeof(struct pmc_hw), M_PMC, M_WAITOK | M_ZERO); uncore_pcpu[cpu] = cc; pc = pmc_pcpu[cpu]; KASSERT(pc != NULL && cc != NULL, ("[uncore,%d] NULL per-cpu structures cpu=%d", __LINE__, cpu)); for (n = 0, phw = cc->pc_uncorepmcs; n < npmc; n++, phw++) { phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n + uncore_ri); phw->phw_pmc = NULL; pc->pc_hwpmcs[n + uncore_ri] = phw; } return (0); } static int uncore_pcpu_fini(struct pmc_mdep *md, int cpu) { int uncore_ri, n, npmc; struct pmc_cpu *pc; struct uncore_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] insane cpu number (%d)", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"uncore-pcpu-fini cpu=%d", cpu); if ((cc = uncore_pcpu[cpu]) == NULL) return (0); uncore_pcpu[cpu] = NULL; pc = pmc_pcpu[cpu]; KASSERT(pc != NULL, ("[uncore,%d] NULL per-cpu %d state", __LINE__, cpu)); npmc = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_num; uncore_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP].pcd_ri; for (n = 0; n < npmc; n++) wrmsr(SELECTSEL(uncore_cputype) + n, 0); wrmsr(UCF_CTRL, 0); npmc += md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF].pcd_num; for (n = 0; n < npmc; n++) pc->pc_hwpmcs[n + uncore_ri] = NULL; free(cc, M_PMC); return (0); } /* * Fixed function counters. */ static pmc_value_t ucf_perfctr_value_to_reload_count(pmc_value_t v) { v &= (1ULL << uncore_ucf_width) - 1; return (1ULL << uncore_ucf_width) - v; } static pmc_value_t ucf_reload_count_to_perfctr_value(pmc_value_t rlc) { return (1ULL << uncore_ucf_width) - rlc; } static int ucf_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { uint32_t caps, flags; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU %d", __LINE__, cpu)); PMCDBG2(MDP,ALL,1, "ucf-allocate ri=%d reqcaps=0x%x", ri, pm->pm_caps); if (ri < 0 || ri > uncore_ucf_npmc) return (EINVAL); caps = a->pm_caps; if (a->pm_class != PMC_CLASS_UCF || (caps & UCF_PMC_CAPS) != caps) return (EINVAL); flags = UCF_EN; pm->pm_md.pm_ucf.pm_ucf_ctrl = (flags << (ri * 4)); PMCDBG1(MDP,ALL,2, "ucf-allocate config=0x%jx", (uintmax_t) pm->pm_md.pm_ucf.pm_ucf_ctrl); return (0); } static int ucf_config_pmc(int cpu, int ri, struct pmc *pm) { KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); PMCDBG3(MDP,CFG,1, "ucf-config cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(uncore_pcpu[cpu] != NULL, ("[uncore,%d] null per-cpu %d", __LINE__, cpu)); uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc = pm; return (0); } static int ucf_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char ucf_name[PMC_NAME_MAX]; phw = &uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri]; (void) snprintf(ucf_name, sizeof(ucf_name), "UCF-%d", ri); if ((error = copystr(ucf_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return (error); pi->pm_class = PMC_CLASS_UCF; if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { pi->pm_enabled = TRUE; *ppmc = phw->phw_pmc; } else { pi->pm_enabled = FALSE; *ppmc = NULL; } return (0); } static int ucf_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc; return (0); } static int ucf_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); pm = uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc; KASSERT(pm, ("[uncore,%d] cpu %d ri %d(%d) pmc not configured", __LINE__, cpu, ri, ri + uncore_ucf_ri)); tmp = rdmsr(UCF_CTR0 + ri); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = ucf_perfctr_value_to_reload_count(tmp); else *v = tmp; PMCDBG3(MDP,REA,1, "ucf-read cpu=%d ri=%d -> v=%jx", cpu, ri, *v); return (0); } static int ucf_release_pmc(int cpu, int ri, struct pmc *pmc) { PMCDBG3(MDP,REL,1, "ucf-release cpu=%d ri=%d pm=%p", cpu, ri, pmc); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); KASSERT(uncore_pcpu[cpu]->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc == NULL, ("[uncore,%d] PHW pmc non-NULL", __LINE__)); return (0); } static int ucf_start_pmc(int cpu, int ri) { struct pmc *pm; struct uncore_cpu *ucfc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); PMCDBG2(MDP,STA,1,"ucf-start cpu=%d ri=%d", cpu, ri); ucfc = uncore_pcpu[cpu]; pm = ucfc->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc; ucfc->pc_ucfctrl |= pm->pm_md.pm_ucf.pm_ucf_ctrl; wrmsr(UCF_CTRL, ucfc->pc_ucfctrl); do { ucfc->pc_resync = 0; ucfc->pc_globalctrl |= (1ULL << (ri + SELECTOFF(uncore_cputype))); wrmsr(UC_GLOBAL_CTRL, ucfc->pc_globalctrl); } while (ucfc->pc_resync != 0); PMCDBG4(MDP,STA,1,"ucfctrl=%x(%x) globalctrl=%jx(%jx)", ucfc->pc_ucfctrl, (uint32_t) rdmsr(UCF_CTRL), ucfc->pc_globalctrl, rdmsr(UC_GLOBAL_CTRL)); return (0); } static int ucf_stop_pmc(int cpu, int ri) { uint32_t fc; struct uncore_cpu *ucfc; PMCDBG2(MDP,STO,1,"ucf-stop cpu=%d ri=%d", cpu, ri); ucfc = uncore_pcpu[cpu]; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); fc = (UCF_MASK << (ri * 4)); ucfc->pc_ucfctrl &= ~fc; PMCDBG1(MDP,STO,1,"ucf-stop ucfctrl=%x", ucfc->pc_ucfctrl); wrmsr(UCF_CTRL, ucfc->pc_ucfctrl); do { ucfc->pc_resync = 0; ucfc->pc_globalctrl &= ~(1ULL << (ri + SELECTOFF(uncore_cputype))); wrmsr(UC_GLOBAL_CTRL, ucfc->pc_globalctrl); } while (ucfc->pc_resync != 0); PMCDBG4(MDP,STO,1,"ucfctrl=%x(%x) globalctrl=%jx(%jx)", ucfc->pc_ucfctrl, (uint32_t) rdmsr(UCF_CTRL), ucfc->pc_globalctrl, rdmsr(UC_GLOBAL_CTRL)); return (0); } static int ucf_write_pmc(int cpu, int ri, pmc_value_t v) { struct uncore_cpu *cc; struct pmc *pm; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucf_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); cc = uncore_pcpu[cpu]; pm = cc->pc_uncorepmcs[ri + uncore_ucf_ri].phw_pmc; KASSERT(pm, ("[uncore,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = ucf_reload_count_to_perfctr_value(v); wrmsr(UCF_CTRL, 0); /* Turn off fixed counters */ wrmsr(UCF_CTR0 + ri, v); wrmsr(UCF_CTRL, cc->pc_ucfctrl); PMCDBG4(MDP,WRI,1, "ucf-write cpu=%d ri=%d v=%jx ucfctrl=%jx ", cpu, ri, v, (uintmax_t) rdmsr(UCF_CTRL)); return (0); } static void ucf_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth) { struct pmc_classdep *pcd; KASSERT(md != NULL, ("[ucf,%d] md is NULL", __LINE__)); PMCDBG0(MDP,INI,1, "ucf-initialize"); pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCF]; pcd->pcd_caps = UCF_PMC_CAPS; pcd->pcd_class = PMC_CLASS_UCF; pcd->pcd_num = npmc; pcd->pcd_ri = md->pmd_npmc; pcd->pcd_width = pmcwidth; pcd->pcd_allocate_pmc = ucf_allocate_pmc; pcd->pcd_config_pmc = ucf_config_pmc; pcd->pcd_describe = ucf_describe; pcd->pcd_get_config = ucf_get_config; pcd->pcd_get_msr = NULL; pcd->pcd_pcpu_fini = uncore_pcpu_noop; pcd->pcd_pcpu_init = uncore_pcpu_noop; pcd->pcd_read_pmc = ucf_read_pmc; pcd->pcd_release_pmc = ucf_release_pmc; pcd->pcd_start_pmc = ucf_start_pmc; pcd->pcd_stop_pmc = ucf_stop_pmc; pcd->pcd_write_pmc = ucf_write_pmc; md->pmd_npmc += npmc; } /* * Intel programmable PMCs. */ /* * Event descriptor tables. * * For each event id, we track: * * 1. The CPUs that the event is valid for. * * 2. If the event uses a fixed UMASK, the value of the umask field. * If the event doesn't use a fixed UMASK, a mask of legal bits * to check against. */ struct ucp_event_descr { enum pmc_event ucp_ev; unsigned char ucp_evcode; unsigned char ucp_umask; unsigned char ucp_flags; }; #define UCP_F_I7 (1 << 0) /* CPU: Core i7 */ #define UCP_F_WM (1 << 1) /* CPU: Westmere */ #define UCP_F_SB (1 << 2) /* CPU: Sandy Bridge */ #define UCP_F_HW (1 << 3) /* CPU: Haswell */ #define UCP_F_FM (1 << 4) /* Fixed mask */ #define UCP_F_ALLCPUS \ (UCP_F_I7 | UCP_F_WM) #define UCP_F_CMASK 0xFF000000 static pmc_value_t ucp_perfctr_value_to_reload_count(pmc_value_t v) { v &= (1ULL << uncore_ucp_width) - 1; return (1ULL << uncore_ucp_width) - v; } static pmc_value_t ucp_reload_count_to_perfctr_value(pmc_value_t rlc) { return (1ULL << uncore_ucp_width) - rlc; } /* * Counter specific event information for Sandybridge and Haswell */ static int ucp_event_sb_hw_ok_on_counter(uint8_t ev, int ri) { uint32_t mask; switch (ev) { /* * Events valid only on counter 0. */ case 0x80: case 0x83: mask = (1 << 0); break; default: mask = ~0; /* Any row index is ok. */ } return (mask & (1 << ri)); } static int ucp_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { uint8_t ev; uint32_t caps; const struct pmc_md_ucp_op_pmcallocate *ucp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row-index value %d", __LINE__, ri)); + if (a->pm_class != PMC_CLASS_UCP) + return (EINVAL); + /* check requested capabilities */ caps = a->pm_caps; if ((UCP_PMC_CAPS & caps) != caps) return (EPERM); ucp = &a->pm_md.pm_ucp; ev = UCP_EVSEL(ucp->pm_ucp_config); switch (uncore_cputype) { case PMC_CPU_INTEL_HASWELL: case PMC_CPU_INTEL_SANDYBRIDGE: if (ucp_event_sb_hw_ok_on_counter(ev, ri) == 0) return (EINVAL); break; default: break; } pm->pm_md.pm_ucp.pm_ucp_evsel = ucp->pm_ucp_config | UCP_EN; return (0); } static int ucp_config_pmc(int cpu, int ri, struct pmc *pm) { KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); PMCDBG3(MDP,CFG,1, "ucp-config cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(uncore_pcpu[cpu] != NULL, ("[uncore,%d] null per-cpu %d", __LINE__, cpu)); uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc = pm; return (0); } static int ucp_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char ucp_name[PMC_NAME_MAX]; phw = &uncore_pcpu[cpu]->pc_uncorepmcs[ri]; (void) snprintf(ucp_name, sizeof(ucp_name), "UCP-%d", ri); if ((error = copystr(ucp_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return (error); pi->pm_class = PMC_CLASS_UCP; if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { pi->pm_enabled = TRUE; *ppmc = phw->phw_pmc; } else { pi->pm_enabled = FALSE; *ppmc = NULL; } return (0); } static int ucp_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc; return (0); } static int ucp_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); pm = uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc; KASSERT(pm, ("[uncore,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); tmp = rdmsr(UCP_PMC0 + ri); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = ucp_perfctr_value_to_reload_count(tmp); else *v = tmp; PMCDBG4(MDP,REA,1, "ucp-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri, ri, *v); return (0); } static int ucp_release_pmc(int cpu, int ri, struct pmc *pm) { (void) pm; PMCDBG3(MDP,REL,1, "ucp-release cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); KASSERT(uncore_pcpu[cpu]->pc_uncorepmcs[ri].phw_pmc == NULL, ("[uncore,%d] PHW pmc non-NULL", __LINE__)); return (0); } static int ucp_start_pmc(int cpu, int ri) { struct pmc *pm; uint32_t evsel; struct uncore_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row-index %d", __LINE__, ri)); cc = uncore_pcpu[cpu]; pm = cc->pc_uncorepmcs[ri].phw_pmc; KASSERT(pm, ("[uncore,%d] starting cpu%d,ri%d with no pmc configured", __LINE__, cpu, ri)); PMCDBG2(MDP,STA,1, "ucp-start cpu=%d ri=%d", cpu, ri); evsel = pm->pm_md.pm_ucp.pm_ucp_evsel; PMCDBG4(MDP,STA,2, "ucp-start/2 cpu=%d ri=%d evselmsr=0x%x evsel=0x%x", cpu, ri, SELECTSEL(uncore_cputype) + ri, evsel); /* Event specific configuration. */ switch (pm->pm_event) { case PMC_EV_UCP_EVENT_0CH_04H_E: case PMC_EV_UCP_EVENT_0CH_08H_E: wrmsr(MSR_GQ_SNOOP_MESF,0x2); break; case PMC_EV_UCP_EVENT_0CH_04H_F: case PMC_EV_UCP_EVENT_0CH_08H_F: wrmsr(MSR_GQ_SNOOP_MESF,0x8); break; case PMC_EV_UCP_EVENT_0CH_04H_M: case PMC_EV_UCP_EVENT_0CH_08H_M: wrmsr(MSR_GQ_SNOOP_MESF,0x1); break; case PMC_EV_UCP_EVENT_0CH_04H_S: case PMC_EV_UCP_EVENT_0CH_08H_S: wrmsr(MSR_GQ_SNOOP_MESF,0x4); break; default: break; } wrmsr(SELECTSEL(uncore_cputype) + ri, evsel); do { cc->pc_resync = 0; cc->pc_globalctrl |= (1ULL << ri); wrmsr(UC_GLOBAL_CTRL, cc->pc_globalctrl); } while (cc->pc_resync != 0); return (0); } static int ucp_stop_pmc(int cpu, int ri) { struct pmc *pm __diagused; struct uncore_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row index %d", __LINE__, ri)); cc = uncore_pcpu[cpu]; pm = cc->pc_uncorepmcs[ri].phw_pmc; KASSERT(pm, ("[uncore,%d] cpu%d ri%d no configured PMC to stop", __LINE__, cpu, ri)); PMCDBG2(MDP,STO,1, "ucp-stop cpu=%d ri=%d", cpu, ri); /* stop hw. */ wrmsr(SELECTSEL(uncore_cputype) + ri, 0); do { cc->pc_resync = 0; cc->pc_globalctrl &= ~(1ULL << ri); wrmsr(UC_GLOBAL_CTRL, cc->pc_globalctrl); } while (cc->pc_resync != 0); return (0); } static int ucp_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; struct uncore_cpu *cc; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[uncore,%d] illegal cpu value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < uncore_ucp_npmc, ("[uncore,%d] illegal row index %d", __LINE__, ri)); cc = uncore_pcpu[cpu]; pm = cc->pc_uncorepmcs[ri].phw_pmc; KASSERT(pm, ("[uncore,%d] cpu%d ri%d no configured PMC to stop", __LINE__, cpu, ri)); PMCDBG4(MDP,WRI,1, "ucp-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri, UCP_PMC0 + ri, v); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = ucp_reload_count_to_perfctr_value(v); /* * Write the new value to the counter. The counter will be in * a stopped state when the pcd_write() entry point is called. */ wrmsr(UCP_PMC0 + ri, v); return (0); } static void ucp_initialize(struct pmc_mdep *md, int maxcpu, int npmc, int pmcwidth) { struct pmc_classdep *pcd; KASSERT(md != NULL, ("[ucp,%d] md is NULL", __LINE__)); PMCDBG0(MDP,INI,1, "ucp-initialize"); pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_UCP]; pcd->pcd_caps = UCP_PMC_CAPS; pcd->pcd_class = PMC_CLASS_UCP; pcd->pcd_num = npmc; pcd->pcd_ri = md->pmd_npmc; pcd->pcd_width = pmcwidth; pcd->pcd_allocate_pmc = ucp_allocate_pmc; pcd->pcd_config_pmc = ucp_config_pmc; pcd->pcd_describe = ucp_describe; pcd->pcd_get_config = ucp_get_config; pcd->pcd_get_msr = NULL; pcd->pcd_pcpu_fini = uncore_pcpu_fini; pcd->pcd_pcpu_init = uncore_pcpu_init; pcd->pcd_read_pmc = ucp_read_pmc; pcd->pcd_release_pmc = ucp_release_pmc; pcd->pcd_start_pmc = ucp_start_pmc; pcd->pcd_stop_pmc = ucp_stop_pmc; pcd->pcd_write_pmc = ucp_write_pmc; md->pmd_npmc += npmc; } int pmc_uncore_initialize(struct pmc_mdep *md, int maxcpu) { uncore_cputype = md->pmd_cputype; uncore_pmcmask = 0; /* * Initialize programmable counters. */ uncore_ucp_npmc = 8; uncore_ucp_width = 48; uncore_pmcmask |= ((1ULL << uncore_ucp_npmc) - 1); ucp_initialize(md, maxcpu, uncore_ucp_npmc, uncore_ucp_width); /* * Initialize fixed function counters, if present. */ uncore_ucf_ri = uncore_ucp_npmc; uncore_ucf_npmc = 1; uncore_ucf_width = 48; ucf_initialize(md, maxcpu, uncore_ucf_npmc, uncore_ucf_width); uncore_pmcmask |= ((1ULL << uncore_ucf_npmc) - 1) << SELECTOFF(uncore_cputype); PMCDBG2(MDP,INI,1,"uncore-init pmcmask=0x%jx ucfri=%d", uncore_pmcmask, uncore_ucf_ri); uncore_pcpu = malloc(sizeof(*uncore_pcpu) * maxcpu, M_PMC, M_ZERO | M_WAITOK); return (0); } void pmc_uncore_finalize(struct pmc_mdep *md) { PMCDBG0(MDP,INI,1, "uncore-finalize"); free(uncore_pcpu, M_PMC); uncore_pcpu = NULL; }