1184802Sjkoshy/*- 2184802Sjkoshy * Copyright (c) 2008 Joseph Koshy 3184802Sjkoshy * All rights reserved. 4184802Sjkoshy * 5184802Sjkoshy * Redistribution and use in source and binary forms, with or without 6184802Sjkoshy * modification, are permitted provided that the following conditions 7184802Sjkoshy * are met: 8184802Sjkoshy * 1. Redistributions of source code must retain the above copyright 9184802Sjkoshy * notice, this list of conditions and the following disclaimer. 10184802Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright 11184802Sjkoshy * notice, this list of conditions and the following disclaimer in the 12184802Sjkoshy * documentation and/or other materials provided with the distribution. 13184802Sjkoshy * 14184802Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184802Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184802Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184802Sjkoshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184802Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184802Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184802Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184802Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184802Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184802Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184802Sjkoshy * SUCH DAMAGE. 25184802Sjkoshy */ 26184802Sjkoshy 27184802Sjkoshy#include <sys/cdefs.h> 28184802Sjkoshy__FBSDID("$FreeBSD$"); 29184802Sjkoshy 30184802Sjkoshy#include <sys/param.h> 31184802Sjkoshy#include <sys/pmc.h> 32184802Sjkoshy#include <sys/pmckern.h> 33184802Sjkoshy#include <sys/systm.h> 34184802Sjkoshy 35184802Sjkoshy#include <machine/specialreg.h> 36184802Sjkoshy 37184802Sjkoshy/* 38184802Sjkoshy * TSC support. 39184802Sjkoshy */ 40184802Sjkoshy 41184802Sjkoshy#define TSC_CAPS PMC_CAP_READ 42184802Sjkoshy 43184802Sjkoshystruct tsc_descr { 44184802Sjkoshy struct pmc_descr pm_descr; /* "base class" */ 45184802Sjkoshy}; 46184802Sjkoshy 47184802Sjkoshystatic struct tsc_descr tsc_pmcdesc[TSC_NPMCS] = 48184802Sjkoshy{ 49184802Sjkoshy { 50184802Sjkoshy .pm_descr = 51184802Sjkoshy { 52184802Sjkoshy .pd_name = "TSC", 53184802Sjkoshy .pd_class = PMC_CLASS_TSC, 54184802Sjkoshy .pd_caps = TSC_CAPS, 55184802Sjkoshy .pd_width = 64 56184802Sjkoshy } 57184802Sjkoshy } 58184802Sjkoshy}; 59184802Sjkoshy 60184802Sjkoshy/* 61184802Sjkoshy * Per-CPU data structure for TSCs. 62184802Sjkoshy */ 63184802Sjkoshy 64184802Sjkoshystruct tsc_cpu { 65184802Sjkoshy struct pmc_hw tc_hw; 66184802Sjkoshy}; 67184802Sjkoshy 68184802Sjkoshystatic struct tsc_cpu **tsc_pcpu; 69184802Sjkoshy 70184802Sjkoshystatic int 71184802Sjkoshytsc_allocate_pmc(int cpu, int ri, struct pmc *pm, 72184802Sjkoshy const struct pmc_op_pmcallocate *a) 73184802Sjkoshy{ 74184802Sjkoshy (void) cpu; 75184802Sjkoshy 76184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 77184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 78184802Sjkoshy KASSERT(ri >= 0 && ri < TSC_NPMCS, 79184802Sjkoshy ("[tsc,%d] illegal row index %d", __LINE__, ri)); 80184802Sjkoshy 81184802Sjkoshy if (a->pm_class != PMC_CLASS_TSC) 82184802Sjkoshy return (EINVAL); 83184802Sjkoshy 84184802Sjkoshy if ((pm->pm_caps & TSC_CAPS) == 0) 85184802Sjkoshy return (EINVAL); 86184802Sjkoshy 87184802Sjkoshy if ((pm->pm_caps & ~TSC_CAPS) != 0) 88184802Sjkoshy return (EPERM); 89184802Sjkoshy 90184802Sjkoshy if (a->pm_ev != PMC_EV_TSC_TSC || 91184802Sjkoshy a->pm_mode != PMC_MODE_SC) 92184802Sjkoshy return (EINVAL); 93184802Sjkoshy 94184802Sjkoshy return (0); 95184802Sjkoshy} 96184802Sjkoshy 97184802Sjkoshystatic int 98184802Sjkoshytsc_config_pmc(int cpu, int ri, struct pmc *pm) 99184802Sjkoshy{ 100184802Sjkoshy struct pmc_hw *phw; 101184802Sjkoshy 102184802Sjkoshy PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 103184802Sjkoshy 104184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 105184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 106184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 107184802Sjkoshy 108184802Sjkoshy phw = &tsc_pcpu[cpu]->tc_hw; 109184802Sjkoshy 110184802Sjkoshy KASSERT(pm == NULL || phw->phw_pmc == NULL, 111184802Sjkoshy ("[tsc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__, 112184802Sjkoshy pm, phw->phw_pmc)); 113184802Sjkoshy 114184802Sjkoshy phw->phw_pmc = pm; 115184802Sjkoshy 116184802Sjkoshy return (0); 117184802Sjkoshy} 118184802Sjkoshy 119184802Sjkoshystatic int 120184802Sjkoshytsc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 121184802Sjkoshy{ 122184802Sjkoshy int error; 123184802Sjkoshy size_t copied; 124184802Sjkoshy const struct tsc_descr *pd; 125184802Sjkoshy struct pmc_hw *phw; 126184802Sjkoshy 127184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 128184802Sjkoshy ("[tsc,%d] illegal CPU %d", __LINE__, cpu)); 129184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 130184802Sjkoshy 131184802Sjkoshy phw = &tsc_pcpu[cpu]->tc_hw; 132184802Sjkoshy pd = &tsc_pmcdesc[ri]; 133184802Sjkoshy 134184802Sjkoshy if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name, 135184802Sjkoshy PMC_NAME_MAX, &copied)) != 0) 136184802Sjkoshy return (error); 137184802Sjkoshy 138184802Sjkoshy pi->pm_class = pd->pm_descr.pd_class; 139184802Sjkoshy 140184802Sjkoshy if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 141184802Sjkoshy pi->pm_enabled = TRUE; 142184802Sjkoshy *ppmc = phw->phw_pmc; 143184802Sjkoshy } else { 144184802Sjkoshy pi->pm_enabled = FALSE; 145184802Sjkoshy *ppmc = NULL; 146184802Sjkoshy } 147184802Sjkoshy 148184802Sjkoshy return (0); 149184802Sjkoshy} 150184802Sjkoshy 151184802Sjkoshystatic int 152184802Sjkoshytsc_get_config(int cpu, int ri, struct pmc **ppm) 153184802Sjkoshy{ 154184802Sjkoshy (void) ri; 155184802Sjkoshy 156184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 157184802Sjkoshy ("[tsc,%d] illegal CPU %d", __LINE__, cpu)); 158184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 159184802Sjkoshy 160184802Sjkoshy *ppm = tsc_pcpu[cpu]->tc_hw.phw_pmc; 161184802Sjkoshy 162184802Sjkoshy return (0); 163184802Sjkoshy} 164184802Sjkoshy 165184802Sjkoshystatic int 166184802Sjkoshytsc_get_msr(int ri, uint32_t *msr) 167184802Sjkoshy{ 168184802Sjkoshy (void) ri; 169184802Sjkoshy 170184802Sjkoshy KASSERT(ri >= 0 && ri < TSC_NPMCS, 171184802Sjkoshy ("[tsc,%d] ri %d out of range", __LINE__, ri)); 172184802Sjkoshy 173184802Sjkoshy *msr = MSR_TSC; 174184802Sjkoshy 175184802Sjkoshy return (0); 176184802Sjkoshy} 177184802Sjkoshy 178184802Sjkoshystatic int 179184802Sjkoshytsc_pcpu_fini(struct pmc_mdep *md, int cpu) 180184802Sjkoshy{ 181184802Sjkoshy int ri; 182184802Sjkoshy struct pmc_cpu *pc; 183184802Sjkoshy 184184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 185184802Sjkoshy ("[tsc,%d] illegal cpu %d", __LINE__, cpu)); 186184802Sjkoshy KASSERT(tsc_pcpu[cpu] != NULL, ("[tsc,%d] null pcpu", __LINE__)); 187184802Sjkoshy 188184802Sjkoshy free(tsc_pcpu[cpu], M_PMC); 189184802Sjkoshy tsc_pcpu[cpu] = NULL; 190184802Sjkoshy 191184802Sjkoshy ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri; 192184802Sjkoshy 193184802Sjkoshy pc = pmc_pcpu[cpu]; 194184802Sjkoshy pc->pc_hwpmcs[ri] = NULL; 195184802Sjkoshy 196184802Sjkoshy return (0); 197184802Sjkoshy} 198184802Sjkoshy 199184802Sjkoshystatic int 200184802Sjkoshytsc_pcpu_init(struct pmc_mdep *md, int cpu) 201184802Sjkoshy{ 202184802Sjkoshy int ri; 203184802Sjkoshy struct pmc_cpu *pc; 204184802Sjkoshy struct tsc_cpu *tsc_pc; 205184802Sjkoshy 206184802Sjkoshy 207184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 208184802Sjkoshy ("[tsc,%d] illegal cpu %d", __LINE__, cpu)); 209184802Sjkoshy KASSERT(tsc_pcpu, ("[tsc,%d] null pcpu", __LINE__)); 210184802Sjkoshy KASSERT(tsc_pcpu[cpu] == NULL, ("[tsc,%d] non-null per-cpu", 211184802Sjkoshy __LINE__)); 212184802Sjkoshy 213184802Sjkoshy tsc_pc = malloc(sizeof(struct tsc_cpu), M_PMC, M_WAITOK|M_ZERO); 214184802Sjkoshy 215184802Sjkoshy tsc_pc->tc_hw.phw_state = PMC_PHW_FLAG_IS_ENABLED | 216184802Sjkoshy PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) | 217184802Sjkoshy PMC_PHW_FLAG_IS_SHAREABLE; 218184802Sjkoshy 219184802Sjkoshy tsc_pcpu[cpu] = tsc_pc; 220184802Sjkoshy 221184802Sjkoshy ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri; 222184802Sjkoshy 223184802Sjkoshy KASSERT(pmc_pcpu, ("[tsc,%d] null generic pcpu", __LINE__)); 224184802Sjkoshy 225184802Sjkoshy pc = pmc_pcpu[cpu]; 226184802Sjkoshy 227184802Sjkoshy KASSERT(pc, ("[tsc,%d] null generic per-cpu", __LINE__)); 228184802Sjkoshy 229184802Sjkoshy pc->pc_hwpmcs[ri] = &tsc_pc->tc_hw; 230184802Sjkoshy 231184802Sjkoshy return (0); 232184802Sjkoshy} 233184802Sjkoshy 234184802Sjkoshystatic int 235184802Sjkoshytsc_read_pmc(int cpu, int ri, pmc_value_t *v) 236184802Sjkoshy{ 237184802Sjkoshy struct pmc *pm; 238184802Sjkoshy enum pmc_mode mode; 239184802Sjkoshy const struct pmc_hw *phw; 240184802Sjkoshy 241184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 242184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 243184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal ri %d", __LINE__, ri)); 244184802Sjkoshy 245184802Sjkoshy phw = &tsc_pcpu[cpu]->tc_hw; 246184802Sjkoshy pm = phw->phw_pmc; 247184802Sjkoshy 248184802Sjkoshy KASSERT(pm != NULL, 249184802Sjkoshy ("[tsc,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri)); 250184802Sjkoshy 251184802Sjkoshy mode = PMC_TO_MODE(pm); 252184802Sjkoshy 253184802Sjkoshy KASSERT(mode == PMC_MODE_SC, 254184802Sjkoshy ("[tsc,%d] illegal pmc mode %d", __LINE__, mode)); 255184802Sjkoshy 256184802Sjkoshy PMCDBG(MDP,REA,1,"tsc-read id=%d", ri); 257184802Sjkoshy 258184802Sjkoshy *v = rdtsc(); 259184802Sjkoshy 260184802Sjkoshy return (0); 261184802Sjkoshy} 262184802Sjkoshy 263184802Sjkoshystatic int 264184802Sjkoshytsc_release_pmc(int cpu, int ri, struct pmc *pmc) 265184802Sjkoshy{ 266184802Sjkoshy struct pmc_hw *phw; 267184802Sjkoshy 268184802Sjkoshy (void) pmc; 269184802Sjkoshy 270184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 271184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 272184802Sjkoshy KASSERT(ri == 0, 273184802Sjkoshy ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 274184802Sjkoshy 275184802Sjkoshy phw = &tsc_pcpu[cpu]->tc_hw; 276184802Sjkoshy 277184802Sjkoshy KASSERT(phw->phw_pmc == NULL, 278184802Sjkoshy ("[tsc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 279184802Sjkoshy 280184802Sjkoshy /* 281184802Sjkoshy * Nothing to do. 282184802Sjkoshy */ 283184802Sjkoshy return (0); 284184802Sjkoshy} 285184802Sjkoshy 286184802Sjkoshystatic int 287184802Sjkoshytsc_start_pmc(int cpu, int ri) 288184802Sjkoshy{ 289184802Sjkoshy (void) cpu; 290184802Sjkoshy 291184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 292184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 293184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 294184802Sjkoshy 295184802Sjkoshy return (0); /* TSCs are always running. */ 296184802Sjkoshy} 297184802Sjkoshy 298184802Sjkoshystatic int 299184802Sjkoshytsc_stop_pmc(int cpu, int ri) 300184802Sjkoshy{ 301184802Sjkoshy (void) cpu; (void) ri; 302184802Sjkoshy 303184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 304184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 305184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 306184802Sjkoshy 307184802Sjkoshy return (0); /* Cannot actually stop a TSC. */ 308184802Sjkoshy} 309184802Sjkoshy 310184802Sjkoshystatic int 311184802Sjkoshytsc_write_pmc(int cpu, int ri, pmc_value_t v) 312184802Sjkoshy{ 313184802Sjkoshy (void) cpu; (void) ri; (void) v; 314184802Sjkoshy 315184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 316184802Sjkoshy ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 317184802Sjkoshy KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 318184802Sjkoshy 319184802Sjkoshy /* 320184802Sjkoshy * The TSCs are used as timecounters by the kernel, so even 321184802Sjkoshy * though some i386 CPUs support writeable TSCs, we don't 322184802Sjkoshy * support writing changing TSC values through the HWPMC API. 323184802Sjkoshy */ 324184802Sjkoshy return (0); 325184802Sjkoshy} 326184802Sjkoshy 327184802Sjkoshyint 328184802Sjkoshypmc_tsc_initialize(struct pmc_mdep *md, int maxcpu) 329184802Sjkoshy{ 330184802Sjkoshy struct pmc_classdep *pcd; 331184802Sjkoshy 332184802Sjkoshy KASSERT(md != NULL, ("[tsc,%d] md is NULL", __LINE__)); 333184802Sjkoshy KASSERT(md->pmd_nclass >= 1, ("[tsc,%d] dubious md->nclass %d", 334184802Sjkoshy __LINE__, md->pmd_nclass)); 335184802Sjkoshy 336184802Sjkoshy tsc_pcpu = malloc(sizeof(struct tsc_cpu *) * maxcpu, M_PMC, 337184802Sjkoshy M_ZERO|M_WAITOK); 338184802Sjkoshy 339184802Sjkoshy pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC]; 340184802Sjkoshy 341184802Sjkoshy pcd->pcd_caps = PMC_CAP_READ; 342184802Sjkoshy pcd->pcd_class = PMC_CLASS_TSC; 343184802Sjkoshy pcd->pcd_num = TSC_NPMCS; 344184802Sjkoshy pcd->pcd_ri = md->pmd_npmc; 345184802Sjkoshy pcd->pcd_width = 64; 346184802Sjkoshy 347184802Sjkoshy pcd->pcd_allocate_pmc = tsc_allocate_pmc; 348184802Sjkoshy pcd->pcd_config_pmc = tsc_config_pmc; 349184802Sjkoshy pcd->pcd_describe = tsc_describe; 350184802Sjkoshy pcd->pcd_get_config = tsc_get_config; 351184802Sjkoshy pcd->pcd_get_msr = tsc_get_msr; 352184802Sjkoshy pcd->pcd_pcpu_init = tsc_pcpu_init; 353184802Sjkoshy pcd->pcd_pcpu_fini = tsc_pcpu_fini; 354184802Sjkoshy pcd->pcd_read_pmc = tsc_read_pmc; 355184802Sjkoshy pcd->pcd_release_pmc = tsc_release_pmc; 356184802Sjkoshy pcd->pcd_start_pmc = tsc_start_pmc; 357184802Sjkoshy pcd->pcd_stop_pmc = tsc_stop_pmc; 358184802Sjkoshy pcd->pcd_write_pmc = tsc_write_pmc; 359184802Sjkoshy 360184802Sjkoshy md->pmd_npmc += TSC_NPMCS; 361184802Sjkoshy 362184802Sjkoshy return (0); 363184802Sjkoshy} 364184802Sjkoshy 365184802Sjkoshyvoid 366184802Sjkoshypmc_tsc_finalize(struct pmc_mdep *md) 367184802Sjkoshy{ 368184802Sjkoshy#ifdef INVARIANTS 369184802Sjkoshy int i, ncpus; 370184802Sjkoshy 371184802Sjkoshy ncpus = pmc_cpu_max(); 372184802Sjkoshy for (i = 0; i < ncpus; i++) 373184802Sjkoshy KASSERT(tsc_pcpu[i] == NULL, ("[tsc,%d] non-null pcpu cpu %d", 374184802Sjkoshy __LINE__, i)); 375184802Sjkoshy 376184802Sjkoshy KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_class == 377184802Sjkoshy PMC_CLASS_TSC, ("[tsc,%d] class mismatch", __LINE__)); 378184802Sjkoshy 379184802Sjkoshy#else 380184802Sjkoshy (void) md; 381184802Sjkoshy#endif 382184802Sjkoshy 383184802Sjkoshy free(tsc_pcpu, M_PMC); 384184802Sjkoshy tsc_pcpu = NULL; 385184802Sjkoshy} 386