hwpmc_mips.c revision 233319
1204635Sgnn/*- 2204635Sgnn * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org> 3204635Sgnn * All rights reserved. 4204635Sgnn * 5204635Sgnn * Redistribution and use in source and binary forms, with or without 6204635Sgnn * modification, are permitted provided that the following conditions 7204635Sgnn * are met: 8204635Sgnn * 1. Redistributions of source code must retain the above copyright 9204635Sgnn * notice, this list of conditions and the following disclaimer. 10204635Sgnn * 2. Redistributions in binary form must reproduce the above copyright 11204635Sgnn * notice, this list of conditions and the following disclaimer in the 12204635Sgnn * documentation and/or other materials provided with the distribution. 13204635Sgnn * 14204635Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15204635Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16204635Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204635Sgnn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204635Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204635Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204635Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204635Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204635Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204635Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204635Sgnn * SUCH DAMAGE. 25204635Sgnn * 26204635Sgnn */ 27204635Sgnn 28204635Sgnn#include <sys/cdefs.h> 29204635Sgnn__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 233319 2012-03-22 18:01:23Z gonzo $"); 30204635Sgnn 31233319Sgonzo#include "opt_hwpmc_hooks.h" 32233319Sgonzo 33204635Sgnn#include <sys/param.h> 34204635Sgnn#include <sys/pmc.h> 35233319Sgonzo#include <sys/pmckern.h> 36204635Sgnn#include <sys/systm.h> 37204635Sgnn 38204635Sgnn#include <machine/pmc_mdep.h> 39204635Sgnn#include <machine/md_var.h> 40232846Sgonzo#include <machine/mips_opcode.h> 41232846Sgonzo#include <machine/vmparam.h> 42204635Sgnn 43233319Sgonzoint mips_npmcs; 44233319Sgonzo 45233319Sgonzo/* 46233319Sgonzo * Per-processor information. 47233319Sgonzo */ 48233319Sgonzostruct mips_cpu { 49233319Sgonzo struct pmc_hw *pc_mipspmcs; 50233319Sgonzo}; 51233319Sgonzo 52233319Sgonzostatic struct mips_cpu **mips_pcpu; 53233319Sgonzo 54232846Sgonzo#if defined(__mips_n64) 55232846Sgonzo# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 56232846Sgonzo ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 57232846Sgonzo#else 58232846Sgonzo# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 59232846Sgonzo ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 60232846Sgonzo#endif 61232846Sgonzo 62232846Sgonzo/* 63232846Sgonzo * We need some reasonable default to prevent backtrace code 64232846Sgonzo * from wandering too far 65232846Sgonzo */ 66232846Sgonzo#define MAX_FUNCTION_SIZE 0x10000 67232846Sgonzo#define MAX_PROLOGUE_SIZE 0x100 68232846Sgonzo 69232846Sgonzostatic int 70233319Sgonzomips_allocate_pmc(int cpu, int ri, struct pmc *pm, 71233319Sgonzo const struct pmc_op_pmcallocate *a) 72233319Sgonzo{ 73233319Sgonzo enum pmc_event pe; 74233319Sgonzo uint32_t caps, config, counter; 75233319Sgonzo uint32_t event; 76233319Sgonzo int i; 77233319Sgonzo 78233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 79233319Sgonzo ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 80233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 81233319Sgonzo ("[mips,%d] illegal row index %d", __LINE__, ri)); 82233319Sgonzo 83233319Sgonzo caps = a->pm_caps; 84233319Sgonzo if (a->pm_class != mips_pmc_spec.ps_cpuclass) 85233319Sgonzo return (EINVAL); 86233319Sgonzo pe = a->pm_ev; 87233319Sgonzo counter = MIPS_CTR_ALL; 88233319Sgonzo event = 0; 89233319Sgonzo for (i = 0; i < mips_event_codes_size; i++) { 90233319Sgonzo if (mips_event_codes[i].pe_ev == pe) { 91233319Sgonzo event = mips_event_codes[i].pe_code; 92233319Sgonzo counter = mips_event_codes[i].pe_counter; 93233319Sgonzo break; 94233319Sgonzo } 95233319Sgonzo } 96233319Sgonzo 97233319Sgonzo if (i == mips_event_codes_size) 98233319Sgonzo return (EINVAL); 99233319Sgonzo 100233319Sgonzo if ((counter != MIPS_CTR_ALL) && (counter != ri)) 101233319Sgonzo return (EINVAL); 102233319Sgonzo 103233319Sgonzo config = mips_get_perfctl(cpu, ri, event, caps); 104233319Sgonzo 105233319Sgonzo pm->pm_md.pm_mips_evsel = config; 106233319Sgonzo 107233319Sgonzo PMCDBG(MDP,ALL,2,"mips-allocate ri=%d -> config=0x%x", ri, config); 108233319Sgonzo 109233319Sgonzo return 0; 110233319Sgonzo} 111233319Sgonzo 112233319Sgonzo 113233319Sgonzostatic int 114233319Sgonzomips_read_pmc(int cpu, int ri, pmc_value_t *v) 115233319Sgonzo{ 116233319Sgonzo struct pmc *pm; 117233319Sgonzo pmc_value_t tmp; 118233319Sgonzo 119233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 120233319Sgonzo ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 121233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 122233319Sgonzo ("[mips,%d] illegal row index %d", __LINE__, ri)); 123233319Sgonzo 124233319Sgonzo pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 125233319Sgonzo tmp = mips_pmcn_read(ri); 126233319Sgonzo PMCDBG(MDP,REA,2,"mips-read id=%d -> %jd", ri, tmp); 127233319Sgonzo 128233319Sgonzo if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 129233319Sgonzo *v = tmp - (1UL << (mips_pmc_spec.ps_counter_width - 1)); 130233319Sgonzo else 131233319Sgonzo *v = tmp; 132233319Sgonzo 133233319Sgonzo return 0; 134233319Sgonzo} 135233319Sgonzo 136233319Sgonzostatic int 137233319Sgonzomips_write_pmc(int cpu, int ri, pmc_value_t v) 138233319Sgonzo{ 139233319Sgonzo struct pmc *pm; 140233319Sgonzo 141233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 142233319Sgonzo ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 143233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 144233319Sgonzo ("[mips,%d] illegal row-index %d", __LINE__, ri)); 145233319Sgonzo 146233319Sgonzo pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 147233319Sgonzo 148233319Sgonzo if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 149233319Sgonzo v = (1UL << (mips_pmc_spec.ps_counter_width - 1)) - v; 150233319Sgonzo 151233319Sgonzo PMCDBG(MDP,WRI,1,"mips-write cpu=%d ri=%d v=%jx", cpu, ri, v); 152233319Sgonzo 153233319Sgonzo mips_pmcn_write(ri, v); 154233319Sgonzo 155233319Sgonzo return 0; 156233319Sgonzo} 157233319Sgonzo 158233319Sgonzostatic int 159233319Sgonzomips_config_pmc(int cpu, int ri, struct pmc *pm) 160233319Sgonzo{ 161233319Sgonzo struct pmc_hw *phw; 162233319Sgonzo 163233319Sgonzo PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 164233319Sgonzo 165233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 166233319Sgonzo ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 167233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 168233319Sgonzo ("[mips,%d] illegal row-index %d", __LINE__, ri)); 169233319Sgonzo 170233319Sgonzo phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 171233319Sgonzo 172233319Sgonzo KASSERT(pm == NULL || phw->phw_pmc == NULL, 173233319Sgonzo ("[mips,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 174233319Sgonzo __LINE__, pm, phw->phw_pmc)); 175233319Sgonzo 176233319Sgonzo phw->phw_pmc = pm; 177233319Sgonzo 178233319Sgonzo return 0; 179233319Sgonzo} 180233319Sgonzo 181233319Sgonzostatic int 182233319Sgonzomips_start_pmc(int cpu, int ri) 183233319Sgonzo{ 184233319Sgonzo uint32_t config; 185233319Sgonzo struct pmc *pm; 186233319Sgonzo struct pmc_hw *phw; 187233319Sgonzo 188233319Sgonzo phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 189233319Sgonzo pm = phw->phw_pmc; 190233319Sgonzo config = pm->pm_md.pm_mips_evsel; 191233319Sgonzo 192233319Sgonzo /* Enable the PMC. */ 193233319Sgonzo switch (ri) { 194233319Sgonzo case 0: 195233319Sgonzo mips_wr_perfcnt0(config); 196233319Sgonzo break; 197233319Sgonzo case 1: 198233319Sgonzo mips_wr_perfcnt2(config); 199233319Sgonzo break; 200233319Sgonzo default: 201233319Sgonzo break; 202233319Sgonzo } 203233319Sgonzo 204233319Sgonzo return 0; 205233319Sgonzo} 206233319Sgonzo 207233319Sgonzostatic int 208233319Sgonzomips_stop_pmc(int cpu, int ri) 209233319Sgonzo{ 210233319Sgonzo struct pmc *pm; 211233319Sgonzo struct pmc_hw *phw; 212233319Sgonzo 213233319Sgonzo phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 214233319Sgonzo pm = phw->phw_pmc; 215233319Sgonzo 216233319Sgonzo /* 217233319Sgonzo * Disable the PMCs. 218233319Sgonzo * 219233319Sgonzo * Clearing the entire register turns the counter off as well 220233319Sgonzo * as removes the previously sampled event. 221233319Sgonzo */ 222233319Sgonzo switch (ri) { 223233319Sgonzo case 0: 224233319Sgonzo mips_wr_perfcnt0(0); 225233319Sgonzo break; 226233319Sgonzo case 1: 227233319Sgonzo mips_wr_perfcnt2(0); 228233319Sgonzo break; 229233319Sgonzo default: 230233319Sgonzo break; 231233319Sgonzo } 232233319Sgonzo return 0; 233233319Sgonzo} 234233319Sgonzo 235233319Sgonzostatic int 236233319Sgonzomips_release_pmc(int cpu, int ri, struct pmc *pmc) 237233319Sgonzo{ 238233319Sgonzo struct pmc_hw *phw; 239233319Sgonzo 240233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 241233319Sgonzo ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 242233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 243233319Sgonzo ("[mips,%d] illegal row-index %d", __LINE__, ri)); 244233319Sgonzo 245233319Sgonzo phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 246233319Sgonzo KASSERT(phw->phw_pmc == NULL, 247233319Sgonzo ("[mips,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 248233319Sgonzo 249233319Sgonzo return 0; 250233319Sgonzo} 251233319Sgonzo 252233319Sgonzostatic int 253233319Sgonzomips_pmc_intr(int cpu, struct trapframe *tf) 254233319Sgonzo{ 255233319Sgonzo int error; 256233319Sgonzo int retval, ri; 257233319Sgonzo struct pmc *pm; 258233319Sgonzo struct mips_cpu *pc; 259233319Sgonzo uint32_t r0, r2; 260233319Sgonzo pmc_value_t r; 261233319Sgonzo 262233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 263233319Sgonzo ("[mips,%d] CPU %d out of range", __LINE__, cpu)); 264233319Sgonzo 265233319Sgonzo retval = 0; 266233319Sgonzo pc = mips_pcpu[cpu]; 267233319Sgonzo 268233319Sgonzo /* Stop PMCs without clearing the counter */ 269233319Sgonzo r0 = mips_rd_perfcnt0(); 270233319Sgonzo mips_wr_perfcnt0(r0 & ~(0x1f)); 271233319Sgonzo r2 = mips_rd_perfcnt2(); 272233319Sgonzo mips_wr_perfcnt2(r2 & ~(0x1f)); 273233319Sgonzo 274233319Sgonzo for (ri = 0; ri < mips_npmcs; ri++) { 275233319Sgonzo pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 276233319Sgonzo if (pm == NULL) 277233319Sgonzo continue; 278233319Sgonzo if (! PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 279233319Sgonzo continue; 280233319Sgonzo 281233319Sgonzo r = mips_pmcn_read(ri); 282233319Sgonzo 283233319Sgonzo /* If bit 31 is set, the counter has overflowed */ 284233319Sgonzo if ((r & (1UL << (mips_pmc_spec.ps_counter_width - 1))) == 0) 285233319Sgonzo continue; 286233319Sgonzo 287233319Sgonzo retval = 1; 288233319Sgonzo if (pm->pm_state != PMC_STATE_RUNNING) 289233319Sgonzo continue; 290233319Sgonzo error = pmc_process_interrupt(cpu, pm, tf, 291233319Sgonzo TRAPF_USERMODE(tf)); 292233319Sgonzo if (error) { 293233319Sgonzo /* Clear/disable the relevant counter */ 294233319Sgonzo if (ri == 0) 295233319Sgonzo r0 = 0; 296233319Sgonzo else if (ri == 1) 297233319Sgonzo r2 = 0; 298233319Sgonzo mips_stop_pmc(cpu, ri); 299233319Sgonzo } 300233319Sgonzo 301233319Sgonzo /* Reload sampling count */ 302233319Sgonzo mips_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); 303233319Sgonzo } 304233319Sgonzo 305233319Sgonzo /* 306233319Sgonzo * Re-enable the PMC counters where they left off. 307233319Sgonzo * 308233319Sgonzo * Any counter which overflowed will have its sample count 309233319Sgonzo * reloaded in the loop above. 310233319Sgonzo */ 311233319Sgonzo mips_wr_perfcnt0(r0); 312233319Sgonzo mips_wr_perfcnt2(r2); 313233319Sgonzo 314233319Sgonzo return retval; 315233319Sgonzo} 316233319Sgonzo 317233319Sgonzostatic int 318233319Sgonzomips_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 319233319Sgonzo{ 320233319Sgonzo int error; 321233319Sgonzo struct pmc_hw *phw; 322233319Sgonzo char mips_name[PMC_NAME_MAX]; 323233319Sgonzo 324233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 325233319Sgonzo ("[mips,%d], illegal CPU %d", __LINE__, cpu)); 326233319Sgonzo KASSERT(ri >= 0 && ri < mips_npmcs, 327233319Sgonzo ("[mips,%d] row-index %d out of range", __LINE__, ri)); 328233319Sgonzo 329233319Sgonzo phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 330233319Sgonzo snprintf(mips_name, sizeof(mips_name), "MIPS-%d", ri); 331233319Sgonzo if ((error = copystr(mips_name, pi->pm_name, PMC_NAME_MAX, 332233319Sgonzo NULL)) != 0) 333233319Sgonzo return error; 334233319Sgonzo pi->pm_class = mips_pmc_spec.ps_cpuclass; 335233319Sgonzo if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 336233319Sgonzo pi->pm_enabled = TRUE; 337233319Sgonzo *ppmc = phw->phw_pmc; 338233319Sgonzo } else { 339233319Sgonzo pi->pm_enabled = FALSE; 340233319Sgonzo *ppmc = NULL; 341233319Sgonzo } 342233319Sgonzo 343233319Sgonzo return (0); 344233319Sgonzo} 345233319Sgonzo 346233319Sgonzostatic int 347233319Sgonzomips_get_config(int cpu, int ri, struct pmc **ppm) 348233319Sgonzo{ 349233319Sgonzo *ppm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 350233319Sgonzo 351233319Sgonzo return 0; 352233319Sgonzo} 353233319Sgonzo 354233319Sgonzo/* 355233319Sgonzo * XXX don't know what we should do here. 356233319Sgonzo */ 357233319Sgonzostatic int 358233319Sgonzomips_pmc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 359233319Sgonzo{ 360233319Sgonzo return 0; 361233319Sgonzo} 362233319Sgonzo 363233319Sgonzostatic int 364233319Sgonzomips_pmc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 365233319Sgonzo{ 366233319Sgonzo return 0; 367233319Sgonzo} 368233319Sgonzo 369233319Sgonzostatic int 370233319Sgonzomips_pcpu_init(struct pmc_mdep *md, int cpu) 371233319Sgonzo{ 372233319Sgonzo int first_ri, i; 373233319Sgonzo struct pmc_cpu *pc; 374233319Sgonzo struct mips_cpu *pac; 375233319Sgonzo struct pmc_hw *phw; 376233319Sgonzo 377233319Sgonzo KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 378233319Sgonzo ("[mips,%d] wrong cpu number %d", __LINE__, cpu)); 379233319Sgonzo PMCDBG(MDP,INI,1,"mips-init cpu=%d", cpu); 380233319Sgonzo 381233319Sgonzo mips_pcpu[cpu] = pac = malloc(sizeof(struct mips_cpu), M_PMC, 382233319Sgonzo M_WAITOK|M_ZERO); 383233319Sgonzo pac->pc_mipspmcs = malloc(sizeof(struct pmc_hw) * mips_npmcs, 384233319Sgonzo M_PMC, M_WAITOK|M_ZERO); 385233319Sgonzo pc = pmc_pcpu[cpu]; 386233319Sgonzo first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS].pcd_ri; 387233319Sgonzo KASSERT(pc != NULL, ("[mips,%d] NULL per-cpu pointer", __LINE__)); 388233319Sgonzo 389233319Sgonzo for (i = 0, phw = pac->pc_mipspmcs; i < mips_npmcs; i++, phw++) { 390233319Sgonzo phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 391233319Sgonzo PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 392233319Sgonzo phw->phw_pmc = NULL; 393233319Sgonzo pc->pc_hwpmcs[i + first_ri] = phw; 394233319Sgonzo } 395233319Sgonzo 396233319Sgonzo /* 397233319Sgonzo * Clear the counter control register which has the effect 398233319Sgonzo * of disabling counting. 399233319Sgonzo */ 400233319Sgonzo for (i = 0; i < mips_npmcs; i++) 401233319Sgonzo mips_pmcn_write(i, 0); 402233319Sgonzo 403233319Sgonzo return 0; 404233319Sgonzo} 405233319Sgonzo 406233319Sgonzostatic int 407233319Sgonzomips_pcpu_fini(struct pmc_mdep *md, int cpu) 408233319Sgonzo{ 409233319Sgonzo return 0; 410233319Sgonzo} 411233319Sgonzo 412233319Sgonzostruct pmc_mdep * 413233319Sgonzopmc_mips_initialize() 414233319Sgonzo{ 415233319Sgonzo struct pmc_mdep *pmc_mdep; 416233319Sgonzo struct pmc_classdep *pcd; 417233319Sgonzo 418233319Sgonzo /* 419233319Sgonzo * TODO: Use More bit of PerfCntlX register to detect actual 420233319Sgonzo * number of counters 421233319Sgonzo */ 422233319Sgonzo mips_npmcs = 2; 423233319Sgonzo 424233319Sgonzo PMCDBG(MDP,INI,1,"mips-init npmcs=%d", mips_npmcs); 425233319Sgonzo 426233319Sgonzo /* 427233319Sgonzo * Allocate space for pointers to PMC HW descriptors and for 428233319Sgonzo * the MDEP structure used by MI code. 429233319Sgonzo */ 430233319Sgonzo mips_pcpu = malloc(sizeof(struct mips_cpu *) * pmc_cpu_max(), M_PMC, 431233319Sgonzo M_WAITOK|M_ZERO); 432233319Sgonzo 433233319Sgonzo /* Just one class */ 434233319Sgonzo pmc_mdep = malloc(sizeof(struct pmc_mdep) + sizeof(struct pmc_classdep), 435233319Sgonzo M_PMC, M_WAITOK|M_ZERO); 436233319Sgonzo 437233319Sgonzo pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype; 438233319Sgonzo pmc_mdep->pmd_nclass = 1; 439233319Sgonzo 440233319Sgonzo pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS]; 441233319Sgonzo pcd->pcd_caps = mips_pmc_spec.ps_capabilities; 442233319Sgonzo pcd->pcd_class = mips_pmc_spec.ps_cpuclass; 443233319Sgonzo pcd->pcd_num = mips_npmcs; 444233319Sgonzo pcd->pcd_ri = pmc_mdep->pmd_npmc; 445233319Sgonzo pcd->pcd_width = mips_pmc_spec.ps_counter_width; 446233319Sgonzo 447233319Sgonzo pcd->pcd_allocate_pmc = mips_allocate_pmc; 448233319Sgonzo pcd->pcd_config_pmc = mips_config_pmc; 449233319Sgonzo pcd->pcd_pcpu_fini = mips_pcpu_fini; 450233319Sgonzo pcd->pcd_pcpu_init = mips_pcpu_init; 451233319Sgonzo pcd->pcd_describe = mips_describe; 452233319Sgonzo pcd->pcd_get_config = mips_get_config; 453233319Sgonzo pcd->pcd_read_pmc = mips_read_pmc; 454233319Sgonzo pcd->pcd_release_pmc = mips_release_pmc; 455233319Sgonzo pcd->pcd_start_pmc = mips_start_pmc; 456233319Sgonzo pcd->pcd_stop_pmc = mips_stop_pmc; 457233319Sgonzo pcd->pcd_write_pmc = mips_write_pmc; 458233319Sgonzo 459233319Sgonzo pmc_mdep->pmd_intr = mips_pmc_intr; 460233319Sgonzo pmc_mdep->pmd_switch_in = mips_pmc_switch_in; 461233319Sgonzo pmc_mdep->pmd_switch_out = mips_pmc_switch_out; 462233319Sgonzo 463233319Sgonzo pmc_mdep->pmd_npmc += mips_npmcs; 464233319Sgonzo 465233319Sgonzo return (pmc_mdep); 466233319Sgonzo} 467233319Sgonzo 468233319Sgonzovoid 469233319Sgonzopmc_mips_finalize(struct pmc_mdep *md) 470233319Sgonzo{ 471233319Sgonzo (void) md; 472233319Sgonzo} 473233319Sgonzo 474233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 475233319Sgonzo 476233319Sgonzostatic int 477232846Sgonzopmc_next_frame(register_t *pc, register_t *sp) 478232846Sgonzo{ 479232846Sgonzo InstFmt i; 480232846Sgonzo uintptr_t va; 481232846Sgonzo uint32_t instr, mask; 482232846Sgonzo int more, stksize; 483232846Sgonzo register_t ra = 0; 484232846Sgonzo 485232846Sgonzo /* Jump here after a nonstandard (interrupt handler) frame */ 486232846Sgonzo stksize = 0; 487232846Sgonzo 488232846Sgonzo /* check for bad SP: could foul up next frame */ 489232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 490232846Sgonzo goto error; 491232846Sgonzo } 492232846Sgonzo 493232846Sgonzo /* check for bad PC */ 494232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 495232846Sgonzo goto error; 496232846Sgonzo } 497232846Sgonzo 498232846Sgonzo /* 499232846Sgonzo * Find the beginning of the current subroutine by scanning 500232846Sgonzo * backwards from the current PC for the end of the previous 501232846Sgonzo * subroutine. 502232846Sgonzo */ 503232846Sgonzo va = *pc - sizeof(int); 504232846Sgonzo while (1) { 505232846Sgonzo instr = *((uint32_t *)va); 506232846Sgonzo 507232846Sgonzo /* [d]addiu sp,sp,-X */ 508232846Sgonzo if (((instr & 0xffff8000) == 0x27bd8000) 509232846Sgonzo || ((instr & 0xffff8000) == 0x67bd8000)) 510232846Sgonzo break; 511232846Sgonzo 512232846Sgonzo /* jr ra */ 513232846Sgonzo if (instr == 0x03e00008) { 514232846Sgonzo /* skip over branch-delay slot instruction */ 515232846Sgonzo va += 2 * sizeof(int); 516232846Sgonzo break; 517232846Sgonzo } 518232846Sgonzo 519232846Sgonzo va -= sizeof(int); 520232846Sgonzo } 521232846Sgonzo 522232846Sgonzo /* skip over nulls which might separate .o files */ 523232846Sgonzo while ((instr = *((uint32_t *)va)) == 0) 524232846Sgonzo va += sizeof(int); 525232846Sgonzo 526232846Sgonzo /* scan forwards to find stack size and any saved registers */ 527232846Sgonzo stksize = 0; 528232846Sgonzo more = 3; 529232846Sgonzo mask = 0; 530232846Sgonzo for (; more; va += sizeof(int), 531232846Sgonzo more = (more == 3) ? 3 : more - 1) { 532232846Sgonzo /* stop if hit our current position */ 533232846Sgonzo if (va >= *pc) 534232846Sgonzo break; 535232846Sgonzo instr = *((uint32_t *)va); 536232846Sgonzo i.word = instr; 537232846Sgonzo switch (i.JType.op) { 538232846Sgonzo case OP_SPECIAL: 539232846Sgonzo switch (i.RType.func) { 540232846Sgonzo case OP_JR: 541232846Sgonzo case OP_JALR: 542232846Sgonzo more = 2; /* stop after next instruction */ 543232846Sgonzo break; 544232846Sgonzo 545232846Sgonzo case OP_SYSCALL: 546232846Sgonzo case OP_BREAK: 547232846Sgonzo more = 1; /* stop now */ 548232846Sgonzo }; 549232846Sgonzo break; 550232846Sgonzo 551232846Sgonzo case OP_BCOND: 552232846Sgonzo case OP_J: 553232846Sgonzo case OP_JAL: 554232846Sgonzo case OP_BEQ: 555232846Sgonzo case OP_BNE: 556232846Sgonzo case OP_BLEZ: 557232846Sgonzo case OP_BGTZ: 558232846Sgonzo more = 2; /* stop after next instruction */ 559232846Sgonzo break; 560232846Sgonzo 561232846Sgonzo case OP_COP0: 562232846Sgonzo case OP_COP1: 563232846Sgonzo case OP_COP2: 564232846Sgonzo case OP_COP3: 565232846Sgonzo switch (i.RType.rs) { 566232846Sgonzo case OP_BCx: 567232846Sgonzo case OP_BCy: 568232846Sgonzo more = 2; /* stop after next instruction */ 569232846Sgonzo }; 570232846Sgonzo break; 571232846Sgonzo 572232846Sgonzo case OP_SW: 573232846Sgonzo case OP_SD: 574232992Sgonzo /* 575232992Sgonzo * SP is being saved using S8(FP). Most likely it indicates 576232992Sgonzo * that SP is modified in the function and we can't get 577232992Sgonzo * its value safely without emulating code backward 578232992Sgonzo * So just bail out on functions like this 579232992Sgonzo */ 580232992Sgonzo if ((i.IType.rs == 30) && (i.IType.rt = 29)) 581232992Sgonzo return (-1); 582232992Sgonzo 583232846Sgonzo /* look for saved registers on the stack */ 584232846Sgonzo if (i.IType.rs != 29) 585232846Sgonzo break; 586232846Sgonzo /* only restore the first one */ 587232846Sgonzo if (mask & (1 << i.IType.rt)) 588232846Sgonzo break; 589232846Sgonzo mask |= (1 << i.IType.rt); 590232846Sgonzo if (i.IType.rt == 31) 591232846Sgonzo ra = *((register_t *)(*sp + (short)i.IType.imm)); 592232846Sgonzo break; 593232846Sgonzo 594232846Sgonzo case OP_ADDI: 595232846Sgonzo case OP_ADDIU: 596232846Sgonzo case OP_DADDI: 597232846Sgonzo case OP_DADDIU: 598232846Sgonzo /* look for stack pointer adjustment */ 599232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 600232846Sgonzo break; 601232846Sgonzo stksize = -((short)i.IType.imm); 602232846Sgonzo } 603232846Sgonzo } 604232846Sgonzo 605232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(ra)) 606232846Sgonzo return (-1); 607232846Sgonzo 608232846Sgonzo *pc = ra; 609232846Sgonzo *sp += stksize; 610232846Sgonzo 611232846Sgonzo return (0); 612232846Sgonzo 613232846Sgonzoerror: 614232846Sgonzo return (-1); 615232846Sgonzo} 616232846Sgonzo 617232846Sgonzostatic int 618232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) 619232846Sgonzo{ 620232846Sgonzo int offset, registers_on_stack; 621232846Sgonzo uint32_t opcode, mask; 622232846Sgonzo register_t function_start; 623232846Sgonzo int stksize; 624232846Sgonzo InstFmt i; 625232846Sgonzo 626232846Sgonzo registers_on_stack = 0; 627232846Sgonzo mask = 0; 628232846Sgonzo function_start = 0; 629232846Sgonzo offset = 0; 630232846Sgonzo stksize = 0; 631232846Sgonzo 632232846Sgonzo while (offset < MAX_FUNCTION_SIZE) { 633232846Sgonzo opcode = fuword32((void *)(*pc - offset)); 634232846Sgonzo 635232846Sgonzo /* [d]addiu sp, sp, -X*/ 636232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd8000) 637232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd8000)) { 638232846Sgonzo function_start = *pc - offset; 639232846Sgonzo registers_on_stack = 1; 640232846Sgonzo break; 641232846Sgonzo } 642232846Sgonzo 643232846Sgonzo /* lui gp, X */ 644232846Sgonzo if ((opcode & 0xffff8000) == 0x3c1c0000) { 645232846Sgonzo /* 646232846Sgonzo * Function might start with this instruction 647232846Sgonzo * Keep an eye on "jr ra" and sp correction 648232846Sgonzo * with positive value further on 649232846Sgonzo */ 650232846Sgonzo function_start = *pc - offset; 651232846Sgonzo } 652232846Sgonzo 653232846Sgonzo if (function_start) { 654232846Sgonzo /* 655232846Sgonzo * Stop looking further. Possible end of 656232846Sgonzo * function instruction: it means there is no 657232846Sgonzo * stack modifications, sp is unchanged 658232846Sgonzo */ 659232846Sgonzo 660232846Sgonzo /* [d]addiu sp,sp,X */ 661232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd0000) 662232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd0000)) 663232846Sgonzo break; 664232846Sgonzo 665232846Sgonzo if (opcode == 0x03e00008) 666232846Sgonzo break; 667232846Sgonzo } 668232846Sgonzo 669232846Sgonzo offset += sizeof(int); 670232846Sgonzo } 671232846Sgonzo 672232846Sgonzo if (!function_start) 673232846Sgonzo return (-1); 674232846Sgonzo 675232846Sgonzo if (registers_on_stack) { 676232846Sgonzo offset = 0; 677232846Sgonzo while ((offset < MAX_PROLOGUE_SIZE) 678232846Sgonzo && ((function_start + offset) < *pc)) { 679232846Sgonzo i.word = fuword32((void *)(function_start + offset)); 680232846Sgonzo switch (i.JType.op) { 681232846Sgonzo case OP_SW: 682232846Sgonzo /* look for saved registers on the stack */ 683232846Sgonzo if (i.IType.rs != 29) 684232846Sgonzo break; 685232846Sgonzo /* only restore the first one */ 686232846Sgonzo if (mask & (1 << i.IType.rt)) 687232846Sgonzo break; 688232846Sgonzo mask |= (1 << i.IType.rt); 689232846Sgonzo if (i.IType.rt == 31) 690232846Sgonzo *ra = fuword32((void *)(*sp + (short)i.IType.imm)); 691232846Sgonzo break; 692232846Sgonzo 693232846Sgonzo#if defined(__mips_n64) 694232846Sgonzo case OP_SD: 695232846Sgonzo /* look for saved registers on the stack */ 696232846Sgonzo if (i.IType.rs != 29) 697232846Sgonzo break; 698232846Sgonzo /* only restore the first one */ 699232846Sgonzo if (mask & (1 << i.IType.rt)) 700232846Sgonzo break; 701232846Sgonzo mask |= (1 << i.IType.rt); 702232846Sgonzo /* ra */ 703232846Sgonzo if (i.IType.rt == 31) 704232846Sgonzo *ra = fuword64((void *)(*sp + (short)i.IType.imm)); 705232846Sgonzo break; 706232846Sgonzo#endif 707232846Sgonzo 708232846Sgonzo case OP_ADDI: 709232846Sgonzo case OP_ADDIU: 710232846Sgonzo case OP_DADDI: 711232846Sgonzo case OP_DADDIU: 712232846Sgonzo /* look for stack pointer adjustment */ 713232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 714232846Sgonzo break; 715232846Sgonzo stksize = -((short)i.IType.imm); 716232846Sgonzo } 717232846Sgonzo 718232846Sgonzo offset += sizeof(int); 719232846Sgonzo } 720232846Sgonzo } 721232846Sgonzo 722232846Sgonzo /* 723232846Sgonzo * We reached the end of backtrace 724232846Sgonzo */ 725232846Sgonzo if (*pc == *ra) 726232846Sgonzo return (-1); 727232846Sgonzo 728232846Sgonzo *pc = *ra; 729232846Sgonzo *sp += stksize; 730232846Sgonzo 731232846Sgonzo return (0); 732232846Sgonzo} 733232846Sgonzo 734233319Sgonzo#endif /* HWPMC_MIPS_BACKTRACE */ 735233319Sgonzo 736204635Sgnnstruct pmc_mdep * 737204635Sgnnpmc_md_initialize() 738204635Sgnn{ 739233319Sgonzo return pmc_mips_initialize(); 740204635Sgnn} 741204635Sgnn 742204635Sgnnvoid 743204635Sgnnpmc_md_finalize(struct pmc_mdep *md) 744204635Sgnn{ 745233319Sgonzo return pmc_mips_finalize(md); 746204635Sgnn} 747204635Sgnn 748204635Sgnnint 749232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes, 750204635Sgnn struct trapframe *tf) 751204635Sgnn{ 752232846Sgonzo register_t pc, ra, sp; 753232846Sgonzo int frames = 0; 754232846Sgonzo 755232992Sgonzo pc = tf->pc; 756232992Sgonzo sp = tf->sp; 757232992Sgonzo ra = tf->ra; 758232846Sgonzo 759233319Sgonzo cc[frames++] = pc; 760233319Sgonzo 761233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 762232846Sgonzo /* 763232846Sgonzo * Unwind, and unwind, and unwind 764232846Sgonzo */ 765232846Sgonzo while (1) { 766232846Sgonzo if (frames >= nframes) 767232846Sgonzo break; 768232846Sgonzo 769232846Sgonzo if (pmc_next_frame(&pc, &sp) < 0) 770232846Sgonzo break; 771233319Sgonzo 772233319Sgonzo cc[frames++] = pc; 773232846Sgonzo } 774233319Sgonzo#endif 775232846Sgonzo 776232846Sgonzo return (frames); 777204635Sgnn} 778204635Sgnn 779204635Sgnnint 780232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes, 781204635Sgnn struct trapframe *tf) 782204635Sgnn{ 783232846Sgonzo register_t pc, ra, sp; 784232846Sgonzo int frames = 0; 785232846Sgonzo 786232992Sgonzo pc = tf->pc; 787232992Sgonzo sp = tf->sp; 788232992Sgonzo ra = tf->ra; 789232846Sgonzo 790233319Sgonzo cc[frames++] = pc; 791233319Sgonzo 792233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 793233319Sgonzo 794232846Sgonzo /* 795232846Sgonzo * Unwind, and unwind, and unwind 796232846Sgonzo */ 797232846Sgonzo while (1) { 798232846Sgonzo if (frames >= nframes) 799232846Sgonzo break; 800232846Sgonzo 801232846Sgonzo if (pmc_next_uframe(&pc, &sp, &ra) < 0) 802232846Sgonzo break; 803233319Sgonzo 804233319Sgonzo cc[frames++] = pc; 805232846Sgonzo } 806233319Sgonzo#endif 807232846Sgonzo 808232846Sgonzo return (frames); 809204635Sgnn} 810