1 /*- 2 * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/pmc.h> 37 #include <sys/pmckern.h> 38 39 #include <machine/pmc_mdep.h> 40 #include <machine/cpu.h> 41 42 static int armv7_npmcs; 43 44 struct armv7_event_code_map { 45 enum pmc_event pe_ev; 46 uint8_t pe_code; 47 }; 48 49 /* 50 * Per-processor information. 51 */ 52 struct armv7_cpu { 53 struct pmc_hw *pc_armv7pmcs; 54 }; 55 56 static struct armv7_cpu **armv7_pcpu; 57 58 /* 59 * Interrupt Enable Set Register 60 */ 61 static __inline void 62 armv7_interrupt_enable(uint32_t pmc) 63 { 64 uint32_t reg; 65 66 reg = (1 << pmc); 67 cp15_pminten_set(reg); 68 } 69 70 /* 71 * Interrupt Clear Set Register 72 */ 73 static __inline void 74 armv7_interrupt_disable(uint32_t pmc) 75 { 76 uint32_t reg; 77 78 reg = (1 << pmc); 79 cp15_pminten_clr(reg); 80 } 81 82 /* 83 * Counter Set Enable Register 84 */ 85 static __inline void 86 armv7_counter_enable(unsigned int pmc) 87 { 88 uint32_t reg; 89 90 reg = (1 << pmc); 91 cp15_pmcnten_set(reg); 92 } 93 94 /* 95 * Counter Clear Enable Register 96 */ 97 static __inline void 98 armv7_counter_disable(unsigned int pmc) 99 { 100 uint32_t reg; 101 102 reg = (1 << pmc); 103 cp15_pmcnten_clr(reg); 104 } 105 106 /* 107 * Performance Count Register N 108 */ 109 static uint32_t 110 armv7_pmcn_read(unsigned int pmc) 111 { 112 113 KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 114 115 cp15_pmselr_set(pmc); 116 return (cp15_pmxevcntr_get()); 117 } 118 119 static uint32_t 120 armv7_pmcn_write(unsigned int pmc, uint32_t reg) 121 { 122 123 KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 124 125 cp15_pmselr_set(pmc); 126 cp15_pmxevcntr_set(reg); 127 128 return (reg); 129 } 130 131 static int 132 armv7_allocate_pmc(int cpu, int ri, struct pmc *pm, 133 const struct pmc_op_pmcallocate *a) 134 { 135 struct armv7_cpu *pac; 136 enum pmc_event pe; 137 uint32_t config; 138 uint32_t caps; 139 140 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 141 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 142 KASSERT(ri >= 0 && ri < armv7_npmcs, 143 ("[armv7,%d] illegal row index %d", __LINE__, ri)); 144 145 pac = armv7_pcpu[cpu]; 146 147 caps = a->pm_caps; 148 if (a->pm_class != PMC_CLASS_ARMV7) 149 return (EINVAL); 150 pe = a->pm_ev; 151 152 config = (pe & EVENT_ID_MASK); 153 pm->pm_md.pm_armv7.pm_armv7_evsel = config; 154 155 PMCDBG2(MDP, ALL, 2, "armv7-allocate ri=%d -> config=0x%x", ri, config); 156 157 return 0; 158 } 159 160 161 static int 162 armv7_read_pmc(int cpu, int ri, pmc_value_t *v) 163 { 164 pmc_value_t tmp; 165 struct pmc *pm; 166 167 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 168 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 169 KASSERT(ri >= 0 && ri < armv7_npmcs, 170 ("[armv7,%d] illegal row index %d", __LINE__, ri)); 171 172 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 173 174 if (pm->pm_md.pm_armv7.pm_armv7_evsel == 0xFF) 175 tmp = cp15_pmccntr_get(); 176 else 177 tmp = armv7_pmcn_read(ri); 178 179 PMCDBG2(MDP, REA, 2, "armv7-read id=%d -> %jd", ri, tmp); 180 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 181 *v = ARMV7_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 182 else 183 *v = tmp; 184 185 return 0; 186 } 187 188 static int 189 armv7_write_pmc(int cpu, int ri, pmc_value_t v) 190 { 191 struct pmc *pm; 192 193 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 194 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 195 KASSERT(ri >= 0 && ri < armv7_npmcs, 196 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 197 198 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 199 200 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 201 v = ARMV7_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 202 203 PMCDBG3(MDP, WRI, 1, "armv7-write cpu=%d ri=%d v=%jx", cpu, ri, v); 204 205 if (pm->pm_md.pm_armv7.pm_armv7_evsel == 0xFF) 206 cp15_pmccntr_set(v); 207 else 208 armv7_pmcn_write(ri, v); 209 210 return 0; 211 } 212 213 static int 214 armv7_config_pmc(int cpu, int ri, struct pmc *pm) 215 { 216 struct pmc_hw *phw; 217 218 PMCDBG3(MDP, CFG, 1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 219 220 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 221 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 222 KASSERT(ri >= 0 && ri < armv7_npmcs, 223 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 224 225 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 226 227 KASSERT(pm == NULL || phw->phw_pmc == NULL, 228 ("[armv7,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 229 __LINE__, pm, phw->phw_pmc)); 230 231 phw->phw_pmc = pm; 232 233 return 0; 234 } 235 236 static int 237 armv7_start_pmc(int cpu, int ri) 238 { 239 struct pmc_hw *phw; 240 uint32_t config; 241 struct pmc *pm; 242 243 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 244 pm = phw->phw_pmc; 245 config = pm->pm_md.pm_armv7.pm_armv7_evsel; 246 247 /* 248 * Configure the event selection. 249 */ 250 cp15_pmselr_set(ri); 251 cp15_pmxevtyper_set(config); 252 253 /* 254 * Enable the PMC. 255 */ 256 armv7_interrupt_enable(ri); 257 armv7_counter_enable(ri); 258 259 return 0; 260 } 261 262 static int 263 armv7_stop_pmc(int cpu, int ri) 264 { 265 struct pmc_hw *phw; 266 struct pmc *pm; 267 268 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 269 pm = phw->phw_pmc; 270 271 /* 272 * Disable the PMCs. 273 */ 274 armv7_counter_disable(ri); 275 armv7_interrupt_disable(ri); 276 277 return 0; 278 } 279 280 static int 281 armv7_release_pmc(int cpu, int ri, struct pmc *pmc) 282 { 283 struct pmc_hw *phw; 284 285 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 286 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 287 KASSERT(ri >= 0 && ri < armv7_npmcs, 288 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 289 290 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 291 KASSERT(phw->phw_pmc == NULL, 292 ("[armv7,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 293 294 return 0; 295 } 296 297 static int 298 armv7_intr(int cpu, struct trapframe *tf) 299 { 300 struct armv7_cpu *pc; 301 int retval, ri; 302 struct pmc *pm; 303 int error; 304 int reg; 305 306 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 307 ("[armv7,%d] CPU %d out of range", __LINE__, cpu)); 308 309 retval = 0; 310 pc = armv7_pcpu[cpu]; 311 312 for (ri = 0; ri < armv7_npmcs; ri++) { 313 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 314 if (pm == NULL) 315 continue; 316 if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 317 continue; 318 319 /* Check if counter has overflowed */ 320 if (pm->pm_md.pm_armv7.pm_armv7_evsel == 0xFF) 321 reg = (1 << 31); 322 else 323 reg = (1 << ri); 324 325 if ((cp15_pmovsr_get() & reg) == 0) { 326 continue; 327 } 328 329 /* Clear Overflow Flag */ 330 cp15_pmovsr_set(reg); 331 332 retval = 1; /* Found an interrupting PMC. */ 333 if (pm->pm_state != PMC_STATE_RUNNING) 334 continue; 335 336 error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, 337 TRAPF_USERMODE(tf)); 338 if (error) 339 armv7_stop_pmc(cpu, ri); 340 341 /* Reload sampling count */ 342 armv7_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); 343 } 344 345 return (retval); 346 } 347 348 static int 349 armv7_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 350 { 351 char armv7_name[PMC_NAME_MAX]; 352 struct pmc_hw *phw; 353 int error; 354 355 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 356 ("[armv7,%d], illegal CPU %d", __LINE__, cpu)); 357 KASSERT(ri >= 0 && ri < armv7_npmcs, 358 ("[armv7,%d] row-index %d out of range", __LINE__, ri)); 359 360 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 361 snprintf(armv7_name, sizeof(armv7_name), "ARMV7-%d", ri); 362 if ((error = copystr(armv7_name, pi->pm_name, PMC_NAME_MAX, 363 NULL)) != 0) 364 return error; 365 pi->pm_class = PMC_CLASS_ARMV7; 366 if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 367 pi->pm_enabled = TRUE; 368 *ppmc = phw->phw_pmc; 369 } else { 370 pi->pm_enabled = FALSE; 371 *ppmc = NULL; 372 } 373 374 return (0); 375 } 376 377 static int 378 armv7_get_config(int cpu, int ri, struct pmc **ppm) 379 { 380 381 *ppm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 382 383 return 0; 384 } 385 386 /* 387 * XXX don't know what we should do here. 388 */ 389 static int 390 armv7_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 391 { 392 393 return 0; 394 } 395 396 static int 397 armv7_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 398 { 399 400 return 0; 401 } 402 403 static int 404 armv7_pcpu_init(struct pmc_mdep *md, int cpu) 405 { 406 struct armv7_cpu *pac; 407 struct pmc_hw *phw; 408 struct pmc_cpu *pc; 409 uint32_t pmnc; 410 int first_ri; 411 int i; 412 413 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 414 ("[armv7,%d] wrong cpu number %d", __LINE__, cpu)); 415 PMCDBG1(MDP, INI, 1, "armv7-init cpu=%d", cpu); 416 417 armv7_pcpu[cpu] = pac = malloc(sizeof(struct armv7_cpu), M_PMC, 418 M_WAITOK|M_ZERO); 419 420 pac->pc_armv7pmcs = malloc(sizeof(struct pmc_hw) * armv7_npmcs, 421 M_PMC, M_WAITOK|M_ZERO); 422 pc = pmc_pcpu[cpu]; 423 first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7].pcd_ri; 424 KASSERT(pc != NULL, ("[armv7,%d] NULL per-cpu pointer", __LINE__)); 425 426 for (i = 0, phw = pac->pc_armv7pmcs; i < armv7_npmcs; i++, phw++) { 427 phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 428 PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 429 phw->phw_pmc = NULL; 430 pc->pc_hwpmcs[i + first_ri] = phw; 431 } 432 433 /* Enable unit */ 434 pmnc = cp15_pmcr_get(); 435 pmnc |= ARMV7_PMNC_ENABLE; 436 cp15_pmcr_set(pmnc); 437 438 return 0; 439 } 440 441 static int 442 armv7_pcpu_fini(struct pmc_mdep *md, int cpu) 443 { 444 uint32_t pmnc; 445 446 pmnc = cp15_pmcr_get(); 447 pmnc &= ~ARMV7_PMNC_ENABLE; 448 cp15_pmcr_set(pmnc); 449 450 return 0; 451 } 452 453 struct pmc_mdep * 454 pmc_armv7_initialize() 455 { 456 struct pmc_mdep *pmc_mdep; 457 struct pmc_classdep *pcd; 458 int idcode; 459 int reg; 460 461 reg = cp15_pmcr_get(); 462 armv7_npmcs = (reg >> ARMV7_PMNC_N_SHIFT) & \ 463 ARMV7_PMNC_N_MASK; 464 idcode = (reg & ARMV7_IDCODE_MASK) >> ARMV7_IDCODE_SHIFT; 465 466 PMCDBG1(MDP, INI, 1, "armv7-init npmcs=%d", armv7_npmcs); 467 468 /* 469 * Allocate space for pointers to PMC HW descriptors and for 470 * the MDEP structure used by MI code. 471 */ 472 armv7_pcpu = malloc(sizeof(struct armv7_cpu *) * pmc_cpu_max(), 473 M_PMC, M_WAITOK | M_ZERO); 474 475 /* Just one class */ 476 pmc_mdep = pmc_mdep_alloc(1); 477 478 switch (idcode) { 479 case ARMV7_IDCODE_CORTEX_A9: 480 pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A9; 481 break; 482 default: 483 case ARMV7_IDCODE_CORTEX_A8: 484 /* 485 * On A8 we implemented common events only, 486 * so use it for the rest of machines. 487 */ 488 pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A8; 489 break; 490 } 491 492 pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7]; 493 pcd->pcd_caps = ARMV7_PMC_CAPS; 494 pcd->pcd_class = PMC_CLASS_ARMV7; 495 pcd->pcd_num = armv7_npmcs; 496 pcd->pcd_ri = pmc_mdep->pmd_npmc; 497 pcd->pcd_width = 32; 498 499 pcd->pcd_allocate_pmc = armv7_allocate_pmc; 500 pcd->pcd_config_pmc = armv7_config_pmc; 501 pcd->pcd_pcpu_fini = armv7_pcpu_fini; 502 pcd->pcd_pcpu_init = armv7_pcpu_init; 503 pcd->pcd_describe = armv7_describe; 504 pcd->pcd_get_config = armv7_get_config; 505 pcd->pcd_read_pmc = armv7_read_pmc; 506 pcd->pcd_release_pmc = armv7_release_pmc; 507 pcd->pcd_start_pmc = armv7_start_pmc; 508 pcd->pcd_stop_pmc = armv7_stop_pmc; 509 pcd->pcd_write_pmc = armv7_write_pmc; 510 511 pmc_mdep->pmd_intr = armv7_intr; 512 pmc_mdep->pmd_switch_in = armv7_switch_in; 513 pmc_mdep->pmd_switch_out = armv7_switch_out; 514 515 pmc_mdep->pmd_npmc += armv7_npmcs; 516 517 return (pmc_mdep); 518 } 519 520 void 521 pmc_armv7_finalize(struct pmc_mdep *md) 522 { 523 524 } 525