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 #define PMC_EV_CPU_CYCLES 0xFF 50 51 /* 52 * Per-processor information. 53 */ 54 struct armv7_cpu { 55 struct pmc_hw *pc_armv7pmcs; 56 }; 57 58 static struct armv7_cpu **armv7_pcpu; 59 60 /* 61 * Interrupt Enable Set Register 62 */ 63 static __inline void 64 armv7_interrupt_enable(uint32_t pmc) 65 { 66 uint32_t reg; 67 68 reg = (1 << pmc); 69 cp15_pminten_set(reg); 70 } 71 72 /* 73 * Interrupt Clear Set Register 74 */ 75 static __inline void 76 armv7_interrupt_disable(uint32_t pmc) 77 { 78 uint32_t reg; 79 80 reg = (1 << pmc); 81 cp15_pminten_clr(reg); 82 } 83 84 /* 85 * Counter Set Enable Register 86 */ 87 static __inline void 88 armv7_counter_enable(unsigned int pmc) 89 { 90 uint32_t reg; 91 92 reg = (1 << pmc); 93 cp15_pmcnten_set(reg); 94 } 95 96 /* 97 * Counter Clear Enable Register 98 */ 99 static __inline void 100 armv7_counter_disable(unsigned int pmc) 101 { 102 uint32_t reg; 103 104 reg = (1 << pmc); 105 cp15_pmcnten_clr(reg); 106 } 107 108 /* 109 * Performance Count Register N 110 */ 111 static uint32_t 112 armv7_pmcn_read(unsigned int pmc, uint32_t evsel) 113 { 114 115 if (evsel == PMC_EV_CPU_CYCLES) { 116 return ((uint32_t)cp15_pmccntr_get()); 117 } 118 119 KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 120 121 cp15_pmselr_set(pmc); 122 return (cp15_pmxevcntr_get()); 123 } 124 125 static uint32_t 126 armv7_pmcn_write(unsigned int pmc, uint32_t reg) 127 { 128 129 KASSERT(pmc < armv7_npmcs, ("%s: illegal PMC number %d", __func__, pmc)); 130 131 cp15_pmselr_set(pmc); 132 cp15_pmxevcntr_set(reg); 133 134 return (reg); 135 } 136 137 static int 138 armv7_allocate_pmc(int cpu, int ri, struct pmc *pm, 139 const struct pmc_op_pmcallocate *a) 140 { 141 struct armv7_cpu *pac; 142 enum pmc_event pe; 143 uint32_t config; 144 uint32_t caps; 145 146 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 147 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 148 KASSERT(ri >= 0 && ri < armv7_npmcs, 149 ("[armv7,%d] illegal row index %d", __LINE__, ri)); 150 151 pac = armv7_pcpu[cpu]; 152 153 caps = a->pm_caps; 154 if (a->pm_class != PMC_CLASS_ARMV7) 155 return (EINVAL); 156 pe = a->pm_ev; 157 158 config = (pe & EVENT_ID_MASK); 159 pm->pm_md.pm_armv7.pm_armv7_evsel = config; 160 161 PMCDBG2(MDP, ALL, 2, "armv7-allocate ri=%d -> config=0x%x", ri, config); 162 163 return 0; 164 } 165 166 167 static int 168 armv7_read_pmc(int cpu, int ri, pmc_value_t *v) 169 { 170 pmc_value_t tmp; 171 struct pmc *pm; 172 register_t s; 173 u_int reg; 174 175 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 176 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 177 KASSERT(ri >= 0 && ri < armv7_npmcs, 178 ("[armv7,%d] illegal row index %d", __LINE__, ri)); 179 180 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 181 182 s = intr_disable(); 183 tmp = armv7_pmcn_read(ri, pm->pm_md.pm_armv7.pm_armv7_evsel); 184 185 /* Check if counter has overflowed */ 186 if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 187 reg = (1u << 31); 188 else 189 reg = (1u << ri); 190 191 if ((cp15_pmovsr_get() & reg) != 0) { 192 /* Clear Overflow Flag */ 193 cp15_pmovsr_set(reg); 194 if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 195 pm->pm_pcpu_state[cpu].pps_overflowcnt++; 196 197 /* Reread counter in case we raced. */ 198 tmp = armv7_pmcn_read(ri, pm->pm_md.pm_armv7.pm_armv7_evsel); 199 } 200 tmp += 0x100000000llu * pm->pm_pcpu_state[cpu].pps_overflowcnt; 201 intr_restore(s); 202 203 PMCDBG2(MDP, REA, 2, "armv7-read id=%d -> %jd", ri, tmp); 204 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 205 *v = ARMV7_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 206 else 207 *v = tmp; 208 209 return 0; 210 } 211 212 static int 213 armv7_write_pmc(int cpu, int ri, pmc_value_t v) 214 { 215 struct pmc *pm; 216 217 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 218 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 219 KASSERT(ri >= 0 && ri < armv7_npmcs, 220 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 221 222 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 223 224 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 225 v = ARMV7_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 226 227 PMCDBG3(MDP, WRI, 1, "armv7-write cpu=%d ri=%d v=%jx", cpu, ri, v); 228 229 pm->pm_pcpu_state[cpu].pps_overflowcnt = v >> 32; 230 if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 231 cp15_pmccntr_set(v); 232 else 233 armv7_pmcn_write(ri, v); 234 235 return 0; 236 } 237 238 static int 239 armv7_config_pmc(int cpu, int ri, struct pmc *pm) 240 { 241 struct pmc_hw *phw; 242 243 PMCDBG3(MDP, CFG, 1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 244 245 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 246 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 247 KASSERT(ri >= 0 && ri < armv7_npmcs, 248 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 249 250 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 251 252 KASSERT(pm == NULL || phw->phw_pmc == NULL, 253 ("[armv7,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 254 __LINE__, pm, phw->phw_pmc)); 255 256 phw->phw_pmc = pm; 257 258 return 0; 259 } 260 261 static int 262 armv7_start_pmc(int cpu, int ri) 263 { 264 struct pmc_hw *phw; 265 uint32_t config; 266 struct pmc *pm; 267 268 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 269 pm = phw->phw_pmc; 270 config = pm->pm_md.pm_armv7.pm_armv7_evsel; 271 272 /* 273 * Configure the event selection. 274 */ 275 if (config != PMC_EV_CPU_CYCLES) { 276 cp15_pmselr_set(ri); 277 cp15_pmxevtyper_set(config); 278 } else 279 ri = 31; 280 281 /* 282 * Enable the PMC. 283 */ 284 armv7_interrupt_enable(ri); 285 armv7_counter_enable(ri); 286 287 return 0; 288 } 289 290 static int 291 armv7_stop_pmc(int cpu, int ri) 292 { 293 struct pmc_hw *phw; 294 struct pmc *pm; 295 uint32_t config; 296 297 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 298 pm = phw->phw_pmc; 299 config = pm->pm_md.pm_armv7.pm_armv7_evsel; 300 if (config == PMC_EV_CPU_CYCLES) 301 ri = 31; 302 303 /* 304 * Disable the PMCs. 305 */ 306 armv7_counter_disable(ri); 307 armv7_interrupt_disable(ri); 308 309 return 0; 310 } 311 312 static int 313 armv7_release_pmc(int cpu, int ri, struct pmc *pmc) 314 { 315 struct pmc_hw *phw; 316 317 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 318 ("[armv7,%d] illegal CPU value %d", __LINE__, cpu)); 319 KASSERT(ri >= 0 && ri < armv7_npmcs, 320 ("[armv7,%d] illegal row-index %d", __LINE__, ri)); 321 322 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 323 KASSERT(phw->phw_pmc == NULL, 324 ("[armv7,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 325 326 return 0; 327 } 328 329 static int 330 armv7_intr(struct trapframe *tf) 331 { 332 struct armv7_cpu *pc; 333 int retval, ri; 334 struct pmc *pm; 335 int error; 336 int reg, cpu; 337 338 cpu = curcpu; 339 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 340 ("[armv7,%d] CPU %d out of range", __LINE__, cpu)); 341 342 retval = 0; 343 pc = armv7_pcpu[cpu]; 344 345 for (ri = 0; ri < armv7_npmcs; ri++) { 346 pm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 347 if (pm == NULL) 348 continue; 349 350 /* Check if counter has overflowed */ 351 if (pm->pm_md.pm_armv7.pm_armv7_evsel == PMC_EV_CPU_CYCLES) 352 reg = (1u << 31); 353 else 354 reg = (1u << ri); 355 356 if ((cp15_pmovsr_get() & reg) == 0) { 357 continue; 358 } 359 360 /* Clear Overflow Flag */ 361 cp15_pmovsr_set(reg); 362 363 retval = 1; /* Found an interrupting PMC. */ 364 365 if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { 366 pm->pm_pcpu_state[cpu].pps_overflowcnt += 1; 367 continue; 368 } 369 if (pm->pm_state != PMC_STATE_RUNNING) 370 continue; 371 372 error = pmc_process_interrupt(PMC_HR, pm, tf); 373 if (error) 374 armv7_stop_pmc(cpu, ri); 375 376 /* Reload sampling count */ 377 armv7_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); 378 } 379 380 return (retval); 381 } 382 383 static int 384 armv7_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 385 { 386 char armv7_name[PMC_NAME_MAX]; 387 struct pmc_hw *phw; 388 int error; 389 390 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 391 ("[armv7,%d], illegal CPU %d", __LINE__, cpu)); 392 KASSERT(ri >= 0 && ri < armv7_npmcs, 393 ("[armv7,%d] row-index %d out of range", __LINE__, ri)); 394 395 phw = &armv7_pcpu[cpu]->pc_armv7pmcs[ri]; 396 snprintf(armv7_name, sizeof(armv7_name), "ARMV7-%d", ri); 397 if ((error = copystr(armv7_name, pi->pm_name, PMC_NAME_MAX, 398 NULL)) != 0) 399 return error; 400 pi->pm_class = PMC_CLASS_ARMV7; 401 if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 402 pi->pm_enabled = TRUE; 403 *ppmc = phw->phw_pmc; 404 } else { 405 pi->pm_enabled = FALSE; 406 *ppmc = NULL; 407 } 408 409 return (0); 410 } 411 412 static int 413 armv7_get_config(int cpu, int ri, struct pmc **ppm) 414 { 415 416 *ppm = armv7_pcpu[cpu]->pc_armv7pmcs[ri].phw_pmc; 417 418 return 0; 419 } 420 421 /* 422 * XXX don't know what we should do here. 423 */ 424 static int 425 armv7_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 426 { 427 428 return 0; 429 } 430 431 static int 432 armv7_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 433 { 434 435 return 0; 436 } 437 438 static int 439 armv7_pcpu_init(struct pmc_mdep *md, int cpu) 440 { 441 struct armv7_cpu *pac; 442 struct pmc_hw *phw; 443 struct pmc_cpu *pc; 444 uint32_t pmnc; 445 int first_ri; 446 int i; 447 448 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 449 ("[armv7,%d] wrong cpu number %d", __LINE__, cpu)); 450 PMCDBG1(MDP, INI, 1, "armv7-init cpu=%d", cpu); 451 452 armv7_pcpu[cpu] = pac = malloc(sizeof(struct armv7_cpu), M_PMC, 453 M_WAITOK|M_ZERO); 454 455 pac->pc_armv7pmcs = malloc(sizeof(struct pmc_hw) * armv7_npmcs, 456 M_PMC, M_WAITOK|M_ZERO); 457 pc = pmc_pcpu[cpu]; 458 first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7].pcd_ri; 459 KASSERT(pc != NULL, ("[armv7,%d] NULL per-cpu pointer", __LINE__)); 460 461 for (i = 0, phw = pac->pc_armv7pmcs; i < armv7_npmcs; i++, phw++) { 462 phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 463 PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 464 phw->phw_pmc = NULL; 465 pc->pc_hwpmcs[i + first_ri] = phw; 466 } 467 468 pmnc = 0xffffffff; 469 cp15_pmcnten_clr(pmnc); 470 cp15_pminten_clr(pmnc); 471 cp15_pmovsr_set(pmnc); 472 473 /* Enable unit */ 474 pmnc = cp15_pmcr_get(); 475 pmnc |= ARMV7_PMNC_ENABLE; 476 cp15_pmcr_set(pmnc); 477 478 return 0; 479 } 480 481 static int 482 armv7_pcpu_fini(struct pmc_mdep *md, int cpu) 483 { 484 uint32_t pmnc; 485 486 pmnc = cp15_pmcr_get(); 487 pmnc &= ~ARMV7_PMNC_ENABLE; 488 cp15_pmcr_set(pmnc); 489 490 pmnc = 0xffffffff; 491 cp15_pmcnten_clr(pmnc); 492 cp15_pminten_clr(pmnc); 493 cp15_pmovsr_set(pmnc); 494 495 return 0; 496 } 497 498 struct pmc_mdep * 499 pmc_armv7_initialize() 500 { 501 struct pmc_mdep *pmc_mdep; 502 struct pmc_classdep *pcd; 503 int idcode; 504 int reg; 505 506 reg = cp15_pmcr_get(); 507 armv7_npmcs = (reg >> ARMV7_PMNC_N_SHIFT) & \ 508 ARMV7_PMNC_N_MASK; 509 idcode = (reg & ARMV7_IDCODE_MASK) >> ARMV7_IDCODE_SHIFT; 510 511 PMCDBG1(MDP, INI, 1, "armv7-init npmcs=%d", armv7_npmcs); 512 513 /* 514 * Allocate space for pointers to PMC HW descriptors and for 515 * the MDEP structure used by MI code. 516 */ 517 armv7_pcpu = malloc(sizeof(struct armv7_cpu *) * pmc_cpu_max(), 518 M_PMC, M_WAITOK | M_ZERO); 519 520 /* Just one class */ 521 pmc_mdep = pmc_mdep_alloc(1); 522 523 switch (idcode) { 524 case ARMV7_IDCODE_CORTEX_A9: 525 pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A9; 526 break; 527 default: 528 case ARMV7_IDCODE_CORTEX_A8: 529 /* 530 * On A8 we implemented common events only, 531 * so use it for the rest of machines. 532 */ 533 pmc_mdep->pmd_cputype = PMC_CPU_ARMV7_CORTEX_A8; 534 break; 535 } 536 537 pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_ARMV7]; 538 pcd->pcd_caps = ARMV7_PMC_CAPS; 539 pcd->pcd_class = PMC_CLASS_ARMV7; 540 pcd->pcd_num = armv7_npmcs; 541 pcd->pcd_ri = pmc_mdep->pmd_npmc; 542 pcd->pcd_width = 32; 543 544 pcd->pcd_allocate_pmc = armv7_allocate_pmc; 545 pcd->pcd_config_pmc = armv7_config_pmc; 546 pcd->pcd_pcpu_fini = armv7_pcpu_fini; 547 pcd->pcd_pcpu_init = armv7_pcpu_init; 548 pcd->pcd_describe = armv7_describe; 549 pcd->pcd_get_config = armv7_get_config; 550 pcd->pcd_read_pmc = armv7_read_pmc; 551 pcd->pcd_release_pmc = armv7_release_pmc; 552 pcd->pcd_start_pmc = armv7_start_pmc; 553 pcd->pcd_stop_pmc = armv7_stop_pmc; 554 pcd->pcd_write_pmc = armv7_write_pmc; 555 556 pmc_mdep->pmd_intr = armv7_intr; 557 pmc_mdep->pmd_switch_in = armv7_switch_in; 558 pmc_mdep->pmd_switch_out = armv7_switch_out; 559 560 pmc_mdep->pmd_npmc += armv7_npmcs; 561 562 return (pmc_mdep); 563 } 564 565 void 566 pmc_armv7_finalize(struct pmc_mdep *md) 567 { 568 569 } 570