Index: head/sys/dev/hwpmc/hwpmc_e500.c =================================================================== --- head/sys/dev/hwpmc/hwpmc_e500.c (revision 334844) +++ head/sys/dev/hwpmc/hwpmc_e500.c (revision 334845) @@ -1,661 +1,661 @@ /*- * 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" #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 E500_PMC_HAS_OVERFLOWED(x) (e500_pmcn_read(x) & (0x1 << 31)) 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); break; case 1: return mfpmr(PMR_PMC1); break; case 2: return mfpmr(PMR_PMC2); break; case 3: return mfpmr(PMR_PMC3); break; 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 int e500_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; 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)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; KASSERT(pm, ("[core,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); tmp = e500_pmcn_read(ri); PMCDBG2(MDP,REA,2,"ppc-read id=%d -> %jd", ri, tmp); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); else *v = tmp; return 0; } static int e500_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; 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)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); PMCDBG3(MDP,WRI,1,"powerpc-write cpu=%d ri=%d v=%jx", cpu, ri, v); e500_pmcn_write(ri, v); return 0; } static int e500_config_pmc(int cpu, int ri, struct pmc *pm) { struct pmc_hw *phw; PMCDBG3(MDP,CFG,1, "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 < E500_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; } static int e500_start_pmc(int cpu, int ri) { uint32_t config; struct pmc *pm; struct pmc_hw *phw; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; config = pm->pm_md.pm_powerpc.pm_powerpc_evsel; 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; default: break; } return 0; } static int e500_stop_pmc(int cpu, int ri) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_pmlc; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; /* * Disable the PMCs. */ 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; default: break; } return 0; } static int e500_pcpu_init(struct pmc_mdep *md, int cpu) { int first_ri, i; struct pmc_cpu *pc; struct powerpc_cpu *pac; struct pmc_hw *phw; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] wrong cpu number %d", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"powerpc-init cpu=%d", cpu); /* Freeze all counters. */ mtpmr(PMR_PMGC0, PMGC_FAC | PMGC_PMIE | PMGC_FCECE); powerpc_pcpu[cpu] = pac = malloc(sizeof(struct powerpc_cpu), M_PMC, M_WAITOK|M_ZERO); pac->pc_ppcpmcs = malloc(sizeof(struct pmc_hw) * E500_MAX_PMCS, M_PMC, M_WAITOK|M_ZERO); pac->pc_class = PMC_CLASS_E500; 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 < E500_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; /* Initialize the PMC to stopped */ e500_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); free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); free(powerpc_pcpu[cpu], M_PMC); return 0; } 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)); 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 int e500_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 < E500_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; } static int -e500_intr(int cpu, struct trapframe *tf) +e500_intr(struct trapframe *tf) { - int i, error, retval; + int i, error, retval, cpu; uint32_t config; struct pmc *pm; struct powerpc_cpu *pac; + 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; pac = powerpc_pcpu[cpu]; config = mfpmr(PMR_PMGC0) & ~PMGC_FAC; /* * look for all PMCs that have interrupted: * - look for a running, sampling PMC which has overflowed * and which has a valid 'struct pmc' association * * If found, we call a helper to process the interrupt. */ for (i = 0; i < E500_MAX_PMCS; i++) { if ((pm = pac->pc_ppcpmcs[i].phw_pmc) == NULL || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { continue; } if (!E500_PMC_HAS_OVERFLOWED(i)) continue; retval = 1; /* Found an interrupting PMC. */ if (pm->pm_state != PMC_STATE_RUNNING) continue; /* Stop the counter if logging fails. */ - error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, - TRAPF_USERMODE(tf)); + error = pmc_process_interrupt(PMC_HR, pm, tf); if (error != 0) e500_stop_pmc(cpu, i); /* reload count. */ e500_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 (retval) mtpmr(PMR_PMGC0, config | PMGC_PMIE); return (retval); } 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 = e500_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 = e500_read_pmc; pcd->pcd_release_pmc = e500_release_pmc; pcd->pcd_start_pmc = e500_start_pmc; pcd->pcd_stop_pmc = e500_stop_pmc; pcd->pcd_write_pmc = e500_write_pmc; pmc_mdep->pmd_npmc += E500_MAX_PMCS; pmc_mdep->pmd_intr = e500_intr; return (0); } Index: head/sys/dev/hwpmc/hwpmc_mips.c =================================================================== --- head/sys/dev/hwpmc/hwpmc_mips.c (revision 334844) +++ head/sys/dev/hwpmc/hwpmc_mips.c (revision 334845) @@ -1,808 +1,809 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010, George V. Neville-Neil * 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 "opt_hwpmc_hooks.h" #include #include #include #include #include #include #include #include int mips_npmcs; /* * Per-processor information. */ struct mips_cpu { struct pmc_hw *pc_mipspmcs; }; static struct mips_cpu **mips_pcpu; #if defined(__mips_n64) # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) #else # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) #endif /* * We need some reasonable default to prevent backtrace code * from wandering too far */ #define MAX_FUNCTION_SIZE 0x10000 #define MAX_PROLOGUE_SIZE 0x100 static int mips_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { enum pmc_event pe; uint32_t caps, config, counter; uint32_t event; int i; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] illegal row index %d", __LINE__, ri)); caps = a->pm_caps; if (a->pm_class != mips_pmc_spec.ps_cpuclass) return (EINVAL); pe = a->pm_ev; counter = MIPS_CTR_ALL; event = 0; for (i = 0; i < mips_event_codes_size; i++) { if (mips_event_codes[i].pe_ev == pe) { event = mips_event_codes[i].pe_code; counter = mips_event_codes[i].pe_counter; break; } } if (i == mips_event_codes_size) return (EINVAL); if ((counter != MIPS_CTR_ALL) && (counter != ri)) return (EINVAL); config = mips_get_perfctl(cpu, ri, event, caps); pm->pm_md.pm_mips_evsel = config; PMCDBG2(MDP,ALL,2,"mips-allocate ri=%d -> config=0x%x", ri, config); return 0; } static int mips_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] illegal row index %d", __LINE__, ri)); pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; tmp = mips_pmcn_read(ri); PMCDBG2(MDP,REA,2,"mips-read id=%d -> %jd", ri, tmp); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = tmp - (1UL << (mips_pmc_spec.ps_counter_width - 1)); else *v = tmp; return 0; } static int mips_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] illegal row-index %d", __LINE__, ri)); pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = (1UL << (mips_pmc_spec.ps_counter_width - 1)) - v; PMCDBG3(MDP,WRI,1,"mips-write cpu=%d ri=%d v=%jx", cpu, ri, v); mips_pmcn_write(ri, v); return 0; } static int mips_config_pmc(int cpu, int ri, struct pmc *pm) { struct pmc_hw *phw; PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] illegal row-index %d", __LINE__, ri)); phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; KASSERT(pm == NULL || phw->phw_pmc == NULL, ("[mips,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__, pm, phw->phw_pmc)); phw->phw_pmc = pm; return 0; } static int mips_start_pmc(int cpu, int ri) { uint32_t config; struct pmc *pm; struct pmc_hw *phw; phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; pm = phw->phw_pmc; config = pm->pm_md.pm_mips_evsel; /* Enable the PMC. */ switch (ri) { case 0: mips_wr_perfcnt0(config); break; case 1: mips_wr_perfcnt2(config); break; default: break; } return 0; } static int mips_stop_pmc(int cpu, int ri) { struct pmc *pm; struct pmc_hw *phw; phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; pm = phw->phw_pmc; /* * Disable the PMCs. * * Clearing the entire register turns the counter off as well * as removes the previously sampled event. */ switch (ri) { case 0: mips_wr_perfcnt0(0); break; case 1: mips_wr_perfcnt2(0); break; default: break; } return 0; } static int mips_release_pmc(int cpu, int ri, struct pmc *pmc) { struct pmc_hw *phw; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] illegal row-index %d", __LINE__, ri)); phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; KASSERT(phw->phw_pmc == NULL, ("[mips,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); return 0; } static int -mips_pmc_intr(int cpu, struct trapframe *tf) +mips_pmc_intr(struct trapframe *tf) { int error; - int retval, ri; + int retval, ri, cpu; struct pmc *pm; struct mips_cpu *pc; uint32_t r0, r2; pmc_value_t r; + cpu = curcpu; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] CPU %d out of range", __LINE__, cpu)); retval = 0; pc = mips_pcpu[cpu]; /* Stop PMCs without clearing the counter */ r0 = mips_rd_perfcnt0(); mips_wr_perfcnt0(r0 & ~(0x1f)); r2 = mips_rd_perfcnt2(); mips_wr_perfcnt2(r2 & ~(0x1f)); for (ri = 0; ri < mips_npmcs; ri++) { pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; if (pm == NULL) continue; if (! PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) continue; r = mips_pmcn_read(ri); /* If bit 31 is set, the counter has overflowed */ if ((r & (1UL << (mips_pmc_spec.ps_counter_width - 1))) == 0) continue; retval = 1; if (pm->pm_state != PMC_STATE_RUNNING) continue; error = pmc_process_interrupt(PMC_HR, pm, tf); if (error) { /* Clear/disable the relevant counter */ if (ri == 0) r0 = 0; else if (ri == 1) r2 = 0; mips_stop_pmc(cpu, ri); } /* Reload sampling count */ mips_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); } /* * Re-enable the PMC counters where they left off. * * Any counter which overflowed will have its sample count * reloaded in the loop above. */ mips_wr_perfcnt0(r0); mips_wr_perfcnt2(r2); return retval; } static int mips_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; struct pmc_hw *phw; char mips_name[PMC_NAME_MAX]; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d], illegal CPU %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < mips_npmcs, ("[mips,%d] row-index %d out of range", __LINE__, ri)); phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; snprintf(mips_name, sizeof(mips_name), "MIPS-%d", ri); if ((error = copystr(mips_name, pi->pm_name, PMC_NAME_MAX, NULL)) != 0) return error; pi->pm_class = mips_pmc_spec.ps_cpuclass; 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 mips_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; return 0; } /* * XXX don't know what we should do here. */ static int mips_pmc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) { return 0; } static int mips_pmc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) { return 0; } static int mips_pcpu_init(struct pmc_mdep *md, int cpu) { int first_ri, i; struct pmc_cpu *pc; struct mips_cpu *pac; struct pmc_hw *phw; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[mips,%d] wrong cpu number %d", __LINE__, cpu)); PMCDBG1(MDP,INI,1,"mips-init cpu=%d", cpu); mips_pcpu[cpu] = pac = malloc(sizeof(struct mips_cpu), M_PMC, M_WAITOK|M_ZERO); pac->pc_mipspmcs = malloc(sizeof(struct pmc_hw) * mips_npmcs, M_PMC, M_WAITOK|M_ZERO); pc = pmc_pcpu[cpu]; first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS].pcd_ri; KASSERT(pc != NULL, ("[mips,%d] NULL per-cpu pointer", __LINE__)); for (i = 0, phw = pac->pc_mipspmcs; i < mips_npmcs; 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; } /* * Clear the counter control register which has the effect * of disabling counting. */ for (i = 0; i < mips_npmcs; i++) mips_pmcn_write(i, 0); return 0; } static int mips_pcpu_fini(struct pmc_mdep *md, int cpu) { return 0; } struct pmc_mdep * pmc_mips_initialize() { struct pmc_mdep *pmc_mdep; struct pmc_classdep *pcd; /* * TODO: Use More bit of PerfCntlX register to detect actual * number of counters */ mips_npmcs = 2; PMCDBG1(MDP,INI,1,"mips-init npmcs=%d", mips_npmcs); /* * Allocate space for pointers to PMC HW descriptors and for * the MDEP structure used by MI code. */ mips_pcpu = malloc(sizeof(struct mips_cpu *) * pmc_cpu_max(), M_PMC, M_WAITOK|M_ZERO); /* Just one class */ pmc_mdep = pmc_mdep_alloc(1); pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype; pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS]; pcd->pcd_caps = mips_pmc_spec.ps_capabilities; pcd->pcd_class = mips_pmc_spec.ps_cpuclass; pcd->pcd_num = mips_npmcs; pcd->pcd_ri = pmc_mdep->pmd_npmc; pcd->pcd_width = mips_pmc_spec.ps_counter_width; pcd->pcd_allocate_pmc = mips_allocate_pmc; pcd->pcd_config_pmc = mips_config_pmc; pcd->pcd_pcpu_fini = mips_pcpu_fini; pcd->pcd_pcpu_init = mips_pcpu_init; pcd->pcd_describe = mips_describe; pcd->pcd_get_config = mips_get_config; pcd->pcd_read_pmc = mips_read_pmc; pcd->pcd_release_pmc = mips_release_pmc; pcd->pcd_start_pmc = mips_start_pmc; pcd->pcd_stop_pmc = mips_stop_pmc; pcd->pcd_write_pmc = mips_write_pmc; pmc_mdep->pmd_intr = mips_pmc_intr; pmc_mdep->pmd_switch_in = mips_pmc_switch_in; pmc_mdep->pmd_switch_out = mips_pmc_switch_out; pmc_mdep->pmd_npmc += mips_npmcs; return (pmc_mdep); } void pmc_mips_finalize(struct pmc_mdep *md) { (void) md; } #ifdef HWPMC_MIPS_BACKTRACE static int pmc_next_frame(register_t *pc, register_t *sp) { InstFmt i; uintptr_t va; uint32_t instr, mask; int more, stksize; register_t ra = 0; /* Jump here after a nonstandard (interrupt handler) frame */ stksize = 0; /* check for bad SP: could foul up next frame */ if (!MIPS_IS_VALID_KERNELADDR(*sp)) { goto error; } /* check for bad PC */ if (!MIPS_IS_VALID_KERNELADDR(*pc)) { goto error; } /* * Find the beginning of the current subroutine by scanning * backwards from the current PC for the end of the previous * subroutine. */ va = *pc - sizeof(int); while (1) { instr = *((uint32_t *)va); /* [d]addiu sp,sp,-X */ if (((instr & 0xffff8000) == 0x27bd8000) || ((instr & 0xffff8000) == 0x67bd8000)) break; /* jr ra */ if (instr == 0x03e00008) { /* skip over branch-delay slot instruction */ va += 2 * sizeof(int); break; } va -= sizeof(int); } /* skip over nulls which might separate .o files */ while ((instr = *((uint32_t *)va)) == 0) va += sizeof(int); /* scan forwards to find stack size and any saved registers */ stksize = 0; more = 3; mask = 0; for (; more; va += sizeof(int), more = (more == 3) ? 3 : more - 1) { /* stop if hit our current position */ if (va >= *pc) break; instr = *((uint32_t *)va); i.word = instr; switch (i.JType.op) { case OP_SPECIAL: switch (i.RType.func) { case OP_JR: case OP_JALR: more = 2; /* stop after next instruction */ break; case OP_SYSCALL: case OP_BREAK: more = 1; /* stop now */ } break; case OP_BCOND: case OP_J: case OP_JAL: case OP_BEQ: case OP_BNE: case OP_BLEZ: case OP_BGTZ: more = 2; /* stop after next instruction */ break; case OP_COP0: case OP_COP1: case OP_COP2: case OP_COP3: switch (i.RType.rs) { case OP_BCx: case OP_BCy: more = 2; /* stop after next instruction */ } break; case OP_SW: case OP_SD: /* * SP is being saved using S8(FP). Most likely it indicates * that SP is modified in the function and we can't get * its value safely without emulating code backward * So just bail out on functions like this */ if ((i.IType.rs == 30) && (i.IType.rt = 29)) return (-1); /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* only restore the first one */ if (mask & (1 << i.IType.rt)) break; mask |= (1 << i.IType.rt); if (i.IType.rt == 31) ra = *((register_t *)(*sp + (short)i.IType.imm)); break; case OP_ADDI: case OP_ADDIU: case OP_DADDI: case OP_DADDIU: /* look for stack pointer adjustment */ if (i.IType.rs != 29 || i.IType.rt != 29) break; stksize = -((short)i.IType.imm); } } if (!MIPS_IS_VALID_KERNELADDR(ra)) return (-1); *pc = ra; *sp += stksize; return (0); error: return (-1); } static int pmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) { int offset, registers_on_stack; uint32_t opcode, mask; register_t function_start; int stksize; InstFmt i; registers_on_stack = 0; mask = 0; function_start = 0; offset = 0; stksize = 0; while (offset < MAX_FUNCTION_SIZE) { opcode = fuword32((void *)(*pc - offset)); /* [d]addiu sp, sp, -X*/ if (((opcode & 0xffff8000) == 0x27bd8000) || ((opcode & 0xffff8000) == 0x67bd8000)) { function_start = *pc - offset; registers_on_stack = 1; break; } /* lui gp, X */ if ((opcode & 0xffff8000) == 0x3c1c0000) { /* * Function might start with this instruction * Keep an eye on "jr ra" and sp correction * with positive value further on */ function_start = *pc - offset; } if (function_start) { /* * Stop looking further. Possible end of * function instruction: it means there is no * stack modifications, sp is unchanged */ /* [d]addiu sp,sp,X */ if (((opcode & 0xffff8000) == 0x27bd0000) || ((opcode & 0xffff8000) == 0x67bd0000)) break; if (opcode == 0x03e00008) break; } offset += sizeof(int); } if (!function_start) return (-1); if (registers_on_stack) { offset = 0; while ((offset < MAX_PROLOGUE_SIZE) && ((function_start + offset) < *pc)) { i.word = fuword32((void *)(function_start + offset)); switch (i.JType.op) { case OP_SW: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* only restore the first one */ if (mask & (1 << i.IType.rt)) break; mask |= (1 << i.IType.rt); if (i.IType.rt == 31) *ra = fuword32((void *)(*sp + (short)i.IType.imm)); break; #if defined(__mips_n64) case OP_SD: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* only restore the first one */ if (mask & (1 << i.IType.rt)) break; mask |= (1 << i.IType.rt); /* ra */ if (i.IType.rt == 31) *ra = fuword64((void *)(*sp + (short)i.IType.imm)); break; #endif case OP_ADDI: case OP_ADDIU: case OP_DADDI: case OP_DADDIU: /* look for stack pointer adjustment */ if (i.IType.rs != 29 || i.IType.rt != 29) break; stksize = -((short)i.IType.imm); } offset += sizeof(int); } } /* * We reached the end of backtrace */ if (*pc == *ra) return (-1); *pc = *ra; *sp += stksize; return (0); } #endif /* HWPMC_MIPS_BACKTRACE */ struct pmc_mdep * pmc_md_initialize() { return pmc_mips_initialize(); } void pmc_md_finalize(struct pmc_mdep *md) { return pmc_mips_finalize(md); } int pmc_save_kernel_callchain(uintptr_t *cc, int nframes, struct trapframe *tf) { register_t pc, ra, sp; int frames = 0; pc = tf->pc; sp = tf->sp; ra = tf->ra; cc[frames++] = pc; #ifdef HWPMC_MIPS_BACKTRACE /* * Unwind, and unwind, and unwind */ while (1) { if (frames >= nframes) break; if (pmc_next_frame(&pc, &sp) < 0) break; cc[frames++] = pc; } #endif return (frames); } int pmc_save_user_callchain(uintptr_t *cc, int nframes, struct trapframe *tf) { register_t pc, ra, sp; int frames = 0; pc = tf->pc; sp = tf->sp; ra = tf->ra; cc[frames++] = pc; #ifdef HWPMC_MIPS_BACKTRACE /* * Unwind, and unwind, and unwind */ while (1) { if (frames >= nframes) break; if (pmc_next_uframe(&pc, &sp, &ra) < 0) break; cc[frames++] = pc; } #endif return (frames); } Index: head/sys/dev/hwpmc/hwpmc_mpc7xxx.c =================================================================== --- head/sys/dev/hwpmc/hwpmc_mpc7xxx.c (revision 334844) +++ head/sys/dev/hwpmc/hwpmc_mpc7xxx.c (revision 334845) @@ -1,754 +1,755 @@ /*- * 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 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 PPC_SET_PMC1SEL(r, x) ((r & ~(SPR_MMCR0_PMC1SEL(0x3f))) | SPR_MMCR0_PMC1SEL(x)) #define PPC_SET_PMC2SEL(r, x) ((r & ~(SPR_MMCR0_PMC2SEL(0x3f))) | SPR_MMCR0_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_PMC6SEL(0x3f))) | SPR_MMCR1_PMC6SEL(x)) /* Change this when we support more than just the 7450. */ #define MPC7XXX_MAX_PMCS 6 #define MPC7XXX_PMC_HAS_OVERFLOWED(x) (mpc7xxx_pmcn_read(x) & (0x1 << 31)) /* * 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). */ struct mpc7xxx_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 */ }; #define PPC_PMC_MASK1 0 #define PPC_PMC_MASK2 1 #define PPC_PMC_MASK3 2 #define PPC_PMC_MASK4 3 #define PPC_PMC_MASK5 4 #define PPC_PMC_MASK6 5 #define PPC_PMC_MASK_ALL 0x3f #define PMC_POWERPC_EVENT(id, mask, number) \ { .pe_ev = PMC_EV_PPC7450_##id, .pe_counter_mask = mask, .pe_code = number } static struct mpc7xxx_event_code_map 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 pmc_value_t mpc7xxx_pmcn_read(unsigned int pmc) { switch (pmc) { case 0: return mfspr(SPR_PMC1); break; case 1: return mfspr(SPR_PMC2); break; case 2: return mfspr(SPR_PMC3); break; case 3: return mfspr(SPR_PMC4); break; case 4: return mfspr(SPR_PMC5); break; case 5: return mfspr(SPR_PMC6); 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, 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; default: panic("Invalid PMC number: %d\n", pmc); } } static int mpc7xxx_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < MPC7XXX_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)); tmp = mpc7xxx_pmcn_read(ri); PMCDBG2(MDP,REA,2,"ppc-read id=%d -> %jd", ri, tmp); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); else *v = tmp; return 0; } static int mpc7xxx_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); PMCDBG3(MDP,WRI,1,"powerpc-write cpu=%d ri=%d v=%jx", cpu, ri, v); mpc7xxx_pmcn_write(ri, v); return 0; } static int mpc7xxx_config_pmc(int cpu, int ri, struct pmc *pm) { struct pmc_hw *phw; PMCDBG3(MDP,CFG,1, "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 < MPC7XXX_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; } static int mpc7xxx_start_pmc(int cpu, int ri) { uint32_t config; struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; config = pm->pm_md.pm_powerpc.pm_powerpc_evsel & ~POWERPC_PMC_ENABLE; /* Enable the PMC. */ switch (ri) { case 0: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, config); mtspr(SPR_MMCR0, pmc_mmcr); break; case 1: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, config); mtspr(SPR_MMCR0, pmc_mmcr); break; case 2: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, config); mtspr(SPR_MMCR1, pmc_mmcr); break; case 3: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, config); mtspr(SPR_MMCR0, pmc_mmcr); break; case 4: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, config); mtspr(SPR_MMCR1, pmc_mmcr); break; case 5: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, config); mtspr(SPR_MMCR1, pmc_mmcr); break; default: break; } /* The mask is inverted (enable is 1) compared to the flags in MMCR0, which * are Freeze flags. */ config = ~pm->pm_md.pm_powerpc.pm_powerpc_evsel & POWERPC_PMC_ENABLE; pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr &= ~SPR_MMCR0_FC; pmc_mmcr |= config; mtspr(SPR_MMCR0, pmc_mmcr); return 0; } static int mpc7xxx_stop_pmc(int cpu, int ri) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; /* * Disable the PMCs. */ switch (ri) { case 0: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, 0); mtspr(SPR_MMCR0, pmc_mmcr); break; case 1: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, 0); mtspr(SPR_MMCR0, pmc_mmcr); break; case 2: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, 0); mtspr(SPR_MMCR1, pmc_mmcr); break; case 3: pmc_mmcr = mfspr(SPR_MMCR0); pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, 0); mtspr(SPR_MMCR0, pmc_mmcr); break; case 4: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, 0); mtspr(SPR_MMCR1, pmc_mmcr); break; case 5: pmc_mmcr = mfspr(SPR_MMCR1); pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, 0); mtspr(SPR_MMCR1, pmc_mmcr); break; default: break; } return 0; } static int mpc7xxx_pcpu_init(struct pmc_mdep *md, int cpu) { int first_ri, i; struct pmc_cpu *pc; struct powerpc_cpu *pac; struct pmc_hw *phw; 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) * MPC7XXX_MAX_PMCS, M_PMC, M_WAITOK|M_ZERO); pac->pc_class = PMC_CLASS_PPC7450; 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 < MPC7XXX_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; } /* Clear the MMCRs, and set FC, to disable all PMCs. */ mtspr(SPR_MMCR0, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE); mtspr(SPR_MMCR1, 0); return 0; } static int mpc7xxx_pcpu_fini(struct pmc_mdep *md, int cpu) { uint32_t mmcr0 = mfspr(SPR_MMCR0); mtmsr(mfmsr() & ~PSL_PMM); mmcr0 |= SPR_MMCR0_FC; mtspr(SPR_MMCR0, mmcr0); free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); free(powerpc_pcpu[cpu], M_PMC); return 0; } static int mpc7xxx_allocate_pmc(int cpu, int ri, struct pmc *pm, const struct pmc_op_pmcallocate *a) { enum pmc_event pe; uint32_t caps, config, counter; int i; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); caps = a->pm_caps; pe = a->pm_ev; for (i = 0; i < nitems(mpc7xxx_event_codes); i++) { if (mpc7xxx_event_codes[i].pe_ev == pe) { config = mpc7xxx_event_codes[i].pe_code; counter = mpc7xxx_event_codes[i].pe_counter_mask; break; } } if (i == nitems(mpc7xxx_event_codes)) 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; PMCDBG2(MDP,ALL,2,"powerpc-allocate ri=%d -> config=0x%x", ri, config); return 0; } static int mpc7xxx_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 < MPC7XXX_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; } static int -mpc7xxx_intr(int cpu, struct trapframe *tf) +mpc7xxx_intr(struct trapframe *tf) { - int i, error, retval; + int i, error, retval, cpu; uint32_t config; struct pmc *pm; struct powerpc_cpu *pac; + 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; pac = powerpc_pcpu[cpu]; config = mfspr(SPR_MMCR0) & ~SPR_MMCR0_FC; /* * look for all PMCs that have interrupted: * - look for a running, sampling PMC which has overflowed * and which has a valid 'struct pmc' association * * If found, we call a helper to process the interrupt. */ for (i = 0; i < MPC7XXX_MAX_PMCS; i++) { if ((pm = pac->pc_ppcpmcs[i].phw_pmc) == NULL || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { continue; } if (!MPC7XXX_PMC_HAS_OVERFLOWED(i)) continue; retval = 1; /* Found an interrupting PMC. */ if (pm->pm_state != PMC_STATE_RUNNING) continue; /* Stop the counter if logging fails. */ error = pmc_process_interrupt(PMC_HR, pm, tf); if (error != 0) mpc7xxx_stop_pmc(cpu, i); /* reload count. */ mpc7xxx_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 (retval) mtspr(SPR_MMCR0, config | SPR_MMCR0_PMXE); return (retval); } 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 = mpc7xxx_allocate_pmc; pcd->pcd_config_pmc = mpc7xxx_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 = mpc7xxx_read_pmc; pcd->pcd_release_pmc = mpc7xxx_release_pmc; pcd->pcd_start_pmc = mpc7xxx_start_pmc; pcd->pcd_stop_pmc = mpc7xxx_stop_pmc; pcd->pcd_write_pmc = mpc7xxx_write_pmc; pmc_mdep->pmd_npmc += MPC7XXX_MAX_PMCS; pmc_mdep->pmd_intr = mpc7xxx_intr; return (0); } Index: head/sys/dev/hwpmc/hwpmc_ppc970.c =================================================================== --- head/sys/dev/hwpmc/hwpmc_ppc970.c (revision 334844) +++ head/sys/dev/hwpmc/hwpmc_ppc970.c (revision 334845) @@ -1,690 +1,691 @@ /*- * 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 /* 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))) #define PPC970_PMC_HAS_OVERFLOWED(x) (ppc970_pmcn_read(x) & (0x1 << 31)) /* 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 */ struct pmc_ppc970_event { enum pmc_event pe_event; uint32_t pe_flags; #define PMC_PPC970_FLAG_PMCS 0x000000ff #define PMC_PPC970_FLAG_PMC1 0x01 #define PMC_PPC970_FLAG_PMC2 0x02 #define PMC_PPC970_FLAG_PMC3 0x04 #define PMC_PPC970_FLAG_PMC4 0x08 #define PMC_PPC970_FLAG_PMC5 0x10 #define PMC_PPC970_FLAG_PMC6 0x20 #define PMC_PPC970_FLAG_PMC7 0x40 #define PMC_PPC970_FLAG_PMC8 0x80 uint32_t pe_code; }; static struct pmc_ppc970_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_PPC970_FLAG_PMC1, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC1, .pe_code = 0x03 }, {PMC_EV_PPC970_GCT_EMPTY, .pe_flags = PMC_PPC970_FLAG_PMC1, .pe_code = 0x04 }, {PMC_EV_PPC970_RUN_CYCLES, .pe_flags = PMC_PPC970_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_PPC970_FLAG_PMC2, .pe_code = 0x3 }, {PMC_EV_PPC970_GROUP_DISPATCH, .pe_flags = PMC_PPC970_FLAG_PMC2, .pe_code = 0x4 }, {PMC_EV_PPC970_BR_MARKED_INSTR_FINISH, .pe_flags = PMC_PPC970_FLAG_PMC2, .pe_code = 0x5 }, {PMC_EV_PPC970_GCT_EMPTY_BY_SRQ_FULL, .pe_flags = PMC_PPC970_FLAG_PMC2, .pe_code = 0xb }, {PMC_EV_PPC970_STOP_COMPLETION, .pe_flags = PMC_PPC970_FLAG_PMC3, .pe_code = 0x1 }, {PMC_EV_PPC970_LSU_EMPTY, .pe_flags = PMC_PPC970_FLAG_PMC3, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_WITH_INTR, .pe_flags = PMC_PPC970_FLAG_PMC3, .pe_code = 0x3 }, {PMC_EV_PPC970_CYCLES_IN_SUPER, .pe_flags = PMC_PPC970_FLAG_PMC3, .pe_code = 0x4 }, {PMC_EV_PPC970_VPU_MARKED_INSTR_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC3, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_IDLE_FXU1_BUSY, .pe_flags = PMC_PPC970_FLAG_PMC4, .pe_code = 0x2 }, {PMC_EV_PPC970_SRQ_EMPTY, .pe_flags = PMC_PPC970_FLAG_PMC4, .pe_code = 0x3 }, {PMC_EV_PPC970_MARKED_GROUP_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC4, .pe_code = 0x4 }, {PMC_EV_PPC970_CR_MARKED_INSTR_FINISH, .pe_flags = PMC_PPC970_FLAG_PMC4, .pe_code = 0x5 }, {PMC_EV_PPC970_DISPATCH_SUCCESS, .pe_flags = PMC_PPC970_FLAG_PMC5, .pe_code = 0x1 }, {PMC_EV_PPC970_FXU0_IDLE_FXU1_IDLE, .pe_flags = PMC_PPC970_FLAG_PMC5, .pe_code = 0x2 }, {PMC_EV_PPC970_ONE_PLUS_INSTR_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC5, .pe_code = 0x3 }, {PMC_EV_PPC970_GROUP_MARKED_IDU, .pe_flags = PMC_PPC970_FLAG_PMC5, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_GROUP_COMPLETE_TIMEOUT, .pe_flags = PMC_PPC970_FLAG_PMC5, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_BUSY_FXU1_BUSY, .pe_flags = PMC_PPC970_FLAG_PMC6, .pe_code = 0x2 }, {PMC_EV_PPC970_MARKED_STORE_SENT_TO_STS, .pe_flags = PMC_PPC970_FLAG_PMC6, .pe_code = 0x3 }, {PMC_EV_PPC970_FXU_MARKED_INSTR_FINISHED, .pe_flags = PMC_PPC970_FLAG_PMC6, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_GROUP_ISSUED, .pe_flags = PMC_PPC970_FLAG_PMC6, .pe_code = 0x5 }, {PMC_EV_PPC970_FXU0_BUSY_FXU1_IDLE, .pe_flags = PMC_PPC970_FLAG_PMC7, .pe_code = 0x2 }, {PMC_EV_PPC970_GROUP_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC7, .pe_code = 0x3 }, {PMC_EV_PPC970_FPU_MARKED_INSTR_COMPLETED, .pe_flags = PMC_PPC970_FLAG_PMC7, .pe_code = 0x4 }, {PMC_EV_PPC970_MARKED_INSTR_FINISH_ANY_UNIT, .pe_flags = PMC_PPC970_FLAG_PMC7, .pe_code = 0x5 }, {PMC_EV_PPC970_EXTERNAL_INTERRUPT, .pe_flags = PMC_PPC970_FLAG_PMC8, .pe_code = 0x2 }, {PMC_EV_PPC970_GROUP_DISPATCH_REJECT, .pe_flags = PMC_PPC970_FLAG_PMC8, .pe_code = 0x3 }, {PMC_EV_PPC970_LSU_MARKED_INSTR_FINISH, .pe_flags = PMC_PPC970_FLAG_PMC8, .pe_code = 0x4 }, {PMC_EV_PPC970_TIMEBASE_EVENT, .pe_flags = PMC_PPC970_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 pmc_value_t ppc970_pmcn_read(unsigned int pmc) { pmc_value_t val; switch (pmc) { case 0: val = mfspr(SPR_970PMC1); break; case 1: val = mfspr(SPR_970PMC2); break; case 2: val = mfspr(SPR_970PMC3); break; case 3: val = mfspr(SPR_970PMC4); break; case 4: val = mfspr(SPR_970PMC5); break; case 5: val = mfspr(SPR_970PMC6); break; case 6: val = mfspr(SPR_970PMC7); break; case 7: val = mfspr(SPR_970PMC8); break; default: panic("Invalid PMC number: %d\n", pmc); } return (val); } static void ppc970_pmcn_write(unsigned int pmc, uint32_t val) { switch (pmc) { case 0: mtspr(SPR_970PMC1, val); break; case 1: mtspr(SPR_970PMC2, val); break; case 2: mtspr(SPR_970PMC3, val); break; case 3: mtspr(SPR_970PMC4, val); break; case 4: mtspr(SPR_970PMC5, val); break; case 5: mtspr(SPR_970PMC6, val); break; case 6: mtspr(SPR_970PMC7, val); break; case 7: mtspr(SPR_970PMC8, val); break; default: panic("Invalid PMC number: %d\n", pmc); } } static int ppc970_config_pmc(int cpu, int ri, struct pmc *pm) { struct pmc_hw *phw; PMCDBG3(MDP,CFG,1, "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 < PPC970_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; } static int ppc970_set_pmc(int cpu, int ri, int config) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; /* * Disable the PMCs. */ switch (ri) { case 0: case 1: pmc_mmcr = mfspr(SPR_970MMCR0); pmc_mmcr = PPC970_SET_MMCR0_PMCSEL(pmc_mmcr, config, ri); mtspr(SPR_970MMCR0, pmc_mmcr); break; case 2: case 3: case 4: case 5: case 6: case 7: pmc_mmcr = mfspr(SPR_970MMCR1); pmc_mmcr = PPC970_SET_MMCR1_PMCSEL(pmc_mmcr, config, ri); mtspr(SPR_970MMCR1, pmc_mmcr); break; } return 0; } static int ppc970_start_pmc(int cpu, int ri) { struct pmc *pm; struct pmc_hw *phw; register_t pmc_mmcr; uint32_t config; int error; phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; pm = phw->phw_pmc; config = pm->pm_md.pm_powerpc.pm_powerpc_evsel & ~POWERPC_PMC_ENABLE; error = ppc970_set_pmc(cpu, ri, config); /* The mask is inverted (enable is 1) compared to the flags in MMCR0, which * are Freeze flags. */ config = ~pm->pm_md.pm_powerpc.pm_powerpc_evsel & POWERPC_PMC_ENABLE; pmc_mmcr = mfspr(SPR_970MMCR0); pmc_mmcr &= ~SPR_MMCR0_FC; pmc_mmcr |= config; mtspr(SPR_970MMCR0, pmc_mmcr); return 0; } static int ppc970_stop_pmc(int cpu, int ri) { return ppc970_set_pmc(cpu, ri, PMC970N_NONE); } static int ppc970_read_pmc(int cpu, int ri, pmc_value_t *v) { struct pmc *pm; pmc_value_t tmp; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < PPC970_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)); tmp = ppc970_pmcn_read(ri); PMCDBG2(MDP,REA,2,"ppc-read id=%d -> %jd", ri, tmp); if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) *v = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); else *v = tmp; return 0; } static int ppc970_write_pmc(int cpu, int ri, pmc_value_t v) { struct pmc *pm; KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); KASSERT(ri >= 0 && ri < PPC970_MAX_PMCS, ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) v = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); PMCDBG3(MDP,WRI,1,"powerpc-write cpu=%d ri=%d v=%jx", cpu, ri, v); ppc970_pmcn_write(ri, v); return 0; } static int -ppc970_intr(int cpu, struct trapframe *tf) +ppc970_intr(struct trapframe *tf) { struct pmc *pm; struct powerpc_cpu *pac; uint32_t config; - int i, error, retval; + int i, error, retval, cpu; + 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; pac = powerpc_pcpu[cpu]; /* * look for all PMCs that have interrupted: * - look for a running, sampling PMC which has overflowed * and which has a valid 'struct pmc' association * * If found, we call a helper to process the interrupt. */ config = mfspr(SPR_970MMCR0) & ~SPR_MMCR0_FC; for (i = 0; i < PPC970_MAX_PMCS; i++) { if ((pm = pac->pc_ppcpmcs[i].phw_pmc) == NULL || !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { continue; } if (!PPC970_PMC_HAS_OVERFLOWED(i)) continue; retval = 1; /* Found an interrupting PMC. */ if (pm->pm_state != PMC_STATE_RUNNING) continue; error = pmc_process_interrupt(PMC_HR, pm, tf); if (error != 0) ppc970_stop_pmc(cpu, i); /* reload sampling count. */ ppc970_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 (retval) mtspr(SPR_970MMCR0, config | SPR_MMCR0_PMXE); return (retval); } static int ppc970_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) * PPC970_MAX_PMCS, M_PMC, M_WAITOK|M_ZERO); pac->pc_class = PMC_CLASS_PPC970; 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 < PPC970_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; } /* Clear the MMCRs, and set FC, to disable all PMCs. */ /* 970 PMC is not counted when set to 0x08 */ mtspr(SPR_970MMCR0, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE | SPR_970MMCR0_PMC1SEL(0x8) | SPR_970MMCR0_PMC2SEL(0x8)); mtspr(SPR_970MMCR1, 0x4218420); return 0; } static int ppc970_pcpu_fini(struct pmc_mdep *md, int cpu) { register_t mmcr0 = mfspr(SPR_MMCR0); mmcr0 |= SPR_MMCR0_FC; mmcr0 &= ~SPR_MMCR0_PMXE; mtspr(SPR_MMCR0, mmcr0); free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); free(powerpc_pcpu[cpu], M_PMC); return 0; } static int ppc970_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 < PPC970_MAX_PMCS, ("[powerpc,%d] illegal row index %d", __LINE__, ri)); caps = a->pm_caps; pe = a->pm_ev; if (pe < PMC_EV_PPC970_FIRST || pe > PMC_EV_PPC970_LAST) return (EINVAL); for (i = 0; i < ppc970_event_codes_size; i++) { if (ppc970_event_codes[i].pe_event == pe) { config = ppc970_event_codes[i].pe_code; counter = ppc970_event_codes[i].pe_flags; break; } } if (i == ppc970_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; PMCDBG2(MDP,ALL,2,"powerpc-allocate ri=%d -> config=0x%x", ri, config); return 0; } static int ppc970_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 < PPC970_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 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 = ppc970_allocate_pmc; pcd->pcd_config_pmc = ppc970_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 = ppc970_read_pmc; pcd->pcd_release_pmc = ppc970_release_pmc; pcd->pcd_start_pmc = ppc970_start_pmc; pcd->pcd_stop_pmc = ppc970_stop_pmc; pcd->pcd_write_pmc = ppc970_write_pmc; pmc_mdep->pmd_npmc += PPC970_MAX_PMCS; pmc_mdep->pmd_intr = ppc970_intr; return (0); }