hwpmc_mips.c revision 234598
133965Sjdp/*- 2130561Sobrien * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org> 360484Sobrien * All rights reserved. 433965Sjdp * 5104834Sobrien * Redistribution and use in source and binary forms, with or without 633965Sjdp * modification, are permitted provided that the following conditions 7104834Sobrien * are met: 8104834Sobrien * 1. Redistributions of source code must retain the above copyright 9104834Sobrien * notice, this list of conditions and the following disclaimer. 10104834Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer in the 12104834Sobrien * documentation and/or other materials provided with the distribution. 13104834Sobrien * 14104834Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15104834Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1633965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17104834Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18104834Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19104834Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2033965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2133965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2233965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23104834Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2433965Sjdp * SUCH DAMAGE. 25104834Sobrien * 26104834Sobrien */ 2733965Sjdp 2833965Sjdp#include <sys/cdefs.h> 2933965Sjdp__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 234598 2012-04-23 08:58:01Z fabient $"); 3033965Sjdp 3133965Sjdp#include "opt_hwpmc_hooks.h" 3261843Sobrien 3333965Sjdp#include <sys/param.h> 3433965Sjdp#include <sys/pmc.h> 35130561Sobrien#include <sys/pmckern.h> 36130561Sobrien#include <sys/systm.h> 37130561Sobrien 3833965Sjdp#include <machine/pmc_mdep.h> 3933965Sjdp#include <machine/md_var.h> 4033965Sjdp#include <machine/mips_opcode.h> 4133965Sjdp#include <machine/vmparam.h> 42130561Sobrien 4333965Sjdpint mips_npmcs; 4433965Sjdp 4533965Sjdp/* 4633965Sjdp * Per-processor information. 4733965Sjdp */ 4833965Sjdpstruct mips_cpu { 4933965Sjdp struct pmc_hw *pc_mipspmcs; 5033965Sjdp}; 5133965Sjdp 5233965Sjdpstatic struct mips_cpu **mips_pcpu; 5333965Sjdp 5433965Sjdp#if defined(__mips_n64) 5533965Sjdp# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 5633965Sjdp ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 5733965Sjdp#else 5833965Sjdp# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 5933965Sjdp ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 6033965Sjdp#endif 6133965Sjdp 6233965Sjdp/* 6333965Sjdp * We need some reasonable default to prevent backtrace code 6433965Sjdp * from wandering too far 6533965Sjdp */ 6633965Sjdp#define MAX_FUNCTION_SIZE 0x10000 6733965Sjdp#define MAX_PROLOGUE_SIZE 0x100 6833965Sjdp 69130561Sobrienstatic int 7033965Sjdpmips_allocate_pmc(int cpu, int ri, struct pmc *pm, 7133965Sjdp const struct pmc_op_pmcallocate *a) 72104834Sobrien{ 7333965Sjdp enum pmc_event pe; 7433965Sjdp uint32_t caps, config, counter; 7561843Sobrien uint32_t event; 7633965Sjdp int i; 77130561Sobrien 7833965Sjdp KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 7933965Sjdp ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 8033965Sjdp KASSERT(ri >= 0 && ri < mips_npmcs, 8133965Sjdp ("[mips,%d] illegal row index %d", __LINE__, ri)); 8233965Sjdp 8360484Sobrien caps = a->pm_caps; 8433965Sjdp if (a->pm_class != mips_pmc_spec.ps_cpuclass) 8533965Sjdp return (EINVAL); 8633965Sjdp pe = a->pm_ev; 8733965Sjdp counter = MIPS_CTR_ALL; 8833965Sjdp event = 0; 8933965Sjdp for (i = 0; i < mips_event_codes_size; i++) { 9033965Sjdp if (mips_event_codes[i].pe_ev == pe) { 9133965Sjdp event = mips_event_codes[i].pe_code; 92130561Sobrien counter = mips_event_codes[i].pe_counter; 9333965Sjdp break; 94104834Sobrien } 9533965Sjdp } 9633965Sjdp 9733965Sjdp if (i == mips_event_codes_size) 98130561Sobrien return (EINVAL); 9933965Sjdp 10033965Sjdp if ((counter != MIPS_CTR_ALL) && (counter != ri)) 10133965Sjdp return (EINVAL); 10233965Sjdp 10333965Sjdp config = mips_get_perfctl(cpu, ri, event, caps); 10433965Sjdp 10533965Sjdp pm->pm_md.pm_mips_evsel = config; 10633965Sjdp 10733965Sjdp PMCDBG(MDP,ALL,2,"mips-allocate ri=%d -> config=0x%x", ri, config); 10833965Sjdp 10960484Sobrien return 0; 11033965Sjdp} 11133965Sjdp 11233965Sjdp 113104834Sobrienstatic int 11433965Sjdpmips_read_pmc(int cpu, int ri, pmc_value_t *v) 11533965Sjdp{ 11633965Sjdp struct pmc *pm; 11733965Sjdp pmc_value_t tmp; 11833965Sjdp 11933965Sjdp KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 12033965Sjdp ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 12133965Sjdp KASSERT(ri >= 0 && ri < mips_npmcs, 12233965Sjdp ("[mips,%d] illegal row index %d", __LINE__, ri)); 12333965Sjdp 12433965Sjdp pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 125130561Sobrien tmp = mips_pmcn_read(ri); 12633965Sjdp PMCDBG(MDP,REA,2,"mips-read id=%d -> %jd", ri, tmp); 12733965Sjdp 128104834Sobrien if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 129104834Sobrien *v = tmp - (1UL << (mips_pmc_spec.ps_counter_width - 1)); 130104834Sobrien else 131104834Sobrien *v = tmp; 132104834Sobrien 133104834Sobrien return 0; 13433965Sjdp} 13533965Sjdp 13633965Sjdpstatic int 137130561Sobrienmips_write_pmc(int cpu, int ri, pmc_value_t v) 13833965Sjdp{ 139104834Sobrien struct pmc *pm; 14033965Sjdp 14133965Sjdp KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 14233965Sjdp ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 14333965Sjdp KASSERT(ri >= 0 && ri < mips_npmcs, 14433965Sjdp ("[mips,%d] illegal row-index %d", __LINE__, ri)); 145104834Sobrien 146104834Sobrien pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 14733965Sjdp 148130561Sobrien if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 14933965Sjdp v = (1UL << (mips_pmc_spec.ps_counter_width - 1)) - v; 15033965Sjdp 15189857Sobrien PMCDBG(MDP,WRI,1,"mips-write cpu=%d ri=%d v=%jx", cpu, ri, v); 15233965Sjdp 153104834Sobrien mips_pmcn_write(ri, v); 15461843Sobrien 15561843Sobrien return 0; 156104834Sobrien} 157104834Sobrien 15833965Sjdpstatic int 159104834Sobrienmips_config_pmc(int cpu, int ri, struct pmc *pm) 160104834Sobrien{ 161104834Sobrien struct pmc_hw *phw; 162104834Sobrien 163104834Sobrien PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 16433965Sjdp 165104834Sobrien KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 16633965Sjdp ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 167104834Sobrien KASSERT(ri >= 0 && ri < mips_npmcs, 168104834Sobrien ("[mips,%d] illegal row-index %d", __LINE__, ri)); 169104834Sobrien 170104834Sobrien phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 171104834Sobrien 172104834Sobrien KASSERT(pm == NULL || phw->phw_pmc == NULL, 173104834Sobrien ("[mips,%d] pm=%p phw->pm=%p hwpmc not unconfigured", 17433965Sjdp __LINE__, pm, phw->phw_pmc)); 175104834Sobrien 17633965Sjdp phw->phw_pmc = pm; 177104834Sobrien 178104834Sobrien return 0; 179104834Sobrien} 180104834Sobrien 181104834Sobrienstatic int 182104834Sobrienmips_start_pmc(int cpu, int ri) 183104834Sobrien{ 184104834Sobrien uint32_t config; 185130561Sobrien struct pmc *pm; 186104834Sobrien struct pmc_hw *phw; 187104834Sobrien 188104834Sobrien phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 189104834Sobrien pm = phw->phw_pmc; 190104834Sobrien config = pm->pm_md.pm_mips_evsel; 191104834Sobrien 192104834Sobrien /* Enable the PMC. */ 193104834Sobrien switch (ri) { 194104834Sobrien case 0: 195104834Sobrien mips_wr_perfcnt0(config); 196104834Sobrien break; 197104834Sobrien case 1: 198104834Sobrien mips_wr_perfcnt2(config); 199104834Sobrien break; 200104834Sobrien default: 201104834Sobrien break; 202104834Sobrien } 203104834Sobrien 204104834Sobrien return 0; 205104834Sobrien} 206104834Sobrien 207104834Sobrienstatic int 208104834Sobrienmips_stop_pmc(int cpu, int ri) 20933965Sjdp{ 21033965Sjdp struct pmc *pm; 21133965Sjdp struct pmc_hw *phw; 212130561Sobrien 21333965Sjdp phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 214104834Sobrien pm = phw->phw_pmc; 21533965Sjdp 21633965Sjdp /* 217104834Sobrien * Disable the PMCs. 21833965Sjdp * 21933965Sjdp * Clearing the entire register turns the counter off as well 22033965Sjdp * as removes the previously sampled event. 22133965Sjdp */ 22233965Sjdp switch (ri) { 223130561Sobrien case 0: 22433965Sjdp mips_wr_perfcnt0(0); 22533965Sjdp break; 22633965Sjdp case 1: 22760484Sobrien mips_wr_perfcnt2(0); 22833965Sjdp break; 22933965Sjdp default: 23033965Sjdp break; 23133965Sjdp } 23233965Sjdp return 0; 23333965Sjdp} 23433965Sjdp 23533965Sjdpstatic int 23633965Sjdpmips_release_pmc(int cpu, int ri, struct pmc *pmc) 23733965Sjdp{ 238130561Sobrien struct pmc_hw *phw; 23933965Sjdp 24033965Sjdp KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 24133965Sjdp ("[mips,%d] illegal CPU value %d", __LINE__, cpu)); 24233965Sjdp KASSERT(ri >= 0 && ri < mips_npmcs, 243130561Sobrien ("[mips,%d] illegal row-index %d", __LINE__, ri)); 24433965Sjdp 245104834Sobrien phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 246104834Sobrien KASSERT(phw->phw_pmc == NULL, 247104834Sobrien ("[mips,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 248104834Sobrien 24933965Sjdp return 0; 250104834Sobrien} 251104834Sobrien 252104834Sobrienstatic int 253104834Sobrienmips_pmc_intr(int cpu, struct trapframe *tf) 254104834Sobrien{ 255104834Sobrien int error; 256104834Sobrien int retval, ri; 257104834Sobrien struct pmc *pm; 258104834Sobrien struct mips_cpu *pc; 259104834Sobrien uint32_t r0, r2; 260104834Sobrien pmc_value_t r; 261104834Sobrien 262104834Sobrien KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 263104834Sobrien ("[mips,%d] CPU %d out of range", __LINE__, cpu)); 264104834Sobrien 265104834Sobrien retval = 0; 266104834Sobrien pc = mips_pcpu[cpu]; 267104834Sobrien 268104834Sobrien /* Stop PMCs without clearing the counter */ 269104834Sobrien r0 = mips_rd_perfcnt0(); 27033965Sjdp mips_wr_perfcnt0(r0 & ~(0x1f)); 27133965Sjdp r2 = mips_rd_perfcnt2(); 27233965Sjdp mips_wr_perfcnt2(r2 & ~(0x1f)); 27333965Sjdp 274130561Sobrien for (ri = 0; ri < mips_npmcs; ri++) { 27533965Sjdp pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 276104834Sobrien if (pm == NULL) 277104834Sobrien continue; 27833965Sjdp if (! PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 27933965Sjdp continue; 28033965Sjdp 281130561Sobrien r = mips_pmcn_read(ri); 28233965Sjdp 283104834Sobrien /* If bit 31 is set, the counter has overflowed */ 284104834Sobrien if ((r & (1UL << (mips_pmc_spec.ps_counter_width - 1))) == 0) 285104834Sobrien continue; 286104834Sobrien 287104834Sobrien retval = 1; 288104834Sobrien if (pm->pm_state != PMC_STATE_RUNNING) 289104834Sobrien continue; 290104834Sobrien error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, 291104834Sobrien TRAPF_USERMODE(tf)); 292104834Sobrien if (error) { 293104834Sobrien /* Clear/disable the relevant counter */ 294104834Sobrien if (ri == 0) 295104834Sobrien r0 = 0; 296104834Sobrien else if (ri == 1) 297104834Sobrien r2 = 0; 298104834Sobrien mips_stop_pmc(cpu, ri); 299104834Sobrien } 300104834Sobrien 301104834Sobrien /* Reload sampling count */ 302104834Sobrien mips_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount); 303104834Sobrien } 304104834Sobrien 305104834Sobrien /* 306104834Sobrien * Re-enable the PMC counters where they left off. 307104834Sobrien * 308104834Sobrien * Any counter which overflowed will have its sample count 309104834Sobrien * reloaded in the loop above. 310104834Sobrien */ 311104834Sobrien mips_wr_perfcnt0(r0); 312104834Sobrien mips_wr_perfcnt2(r2); 313104834Sobrien 314104834Sobrien return retval; 315104834Sobrien} 316104834Sobrien 317104834Sobrienstatic int 31833965Sjdpmips_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 31933965Sjdp{ 32033965Sjdp int error; 32133965Sjdp struct pmc_hw *phw; 32233965Sjdp char mips_name[PMC_NAME_MAX]; 323130561Sobrien 32433965Sjdp KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 325104834Sobrien ("[mips,%d], illegal CPU %d", __LINE__, cpu)); 326104834Sobrien KASSERT(ri >= 0 && ri < mips_npmcs, 327104834Sobrien ("[mips,%d] row-index %d out of range", __LINE__, ri)); 328104834Sobrien 329104834Sobrien phw = &mips_pcpu[cpu]->pc_mipspmcs[ri]; 330104834Sobrien snprintf(mips_name, sizeof(mips_name), "MIPS-%d", ri); 331104834Sobrien if ((error = copystr(mips_name, pi->pm_name, PMC_NAME_MAX, 332104834Sobrien NULL)) != 0) 33333965Sjdp return error; 334104834Sobrien pi->pm_class = mips_pmc_spec.ps_cpuclass; 335104834Sobrien if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 336130561Sobrien pi->pm_enabled = TRUE; 337104834Sobrien *ppmc = phw->phw_pmc; 338104834Sobrien } else { 339104834Sobrien pi->pm_enabled = FALSE; 34033965Sjdp *ppmc = NULL; 34133965Sjdp } 34233965Sjdp 343130561Sobrien return (0); 34433965Sjdp} 345104834Sobrien 346104834Sobrienstatic int 347104834Sobrienmips_get_config(int cpu, int ri, struct pmc **ppm) 348104834Sobrien{ 349104834Sobrien *ppm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc; 350104834Sobrien 351104834Sobrien return 0; 352104834Sobrien} 35333965Sjdp 354104834Sobrien/* 355104834Sobrien * XXX don't know what we should do here. 356104834Sobrien */ 357104834Sobrienstatic int 358104834Sobrienmips_pmc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) 359104834Sobrien{ 360104834Sobrien return 0; 361104834Sobrien} 362104834Sobrien 363104834Sobrienstatic int 364104834Sobrienmips_pmc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) 365104834Sobrien{ 366104834Sobrien return 0; 367104834Sobrien} 368104834Sobrien 369104834Sobrienstatic int 370104834Sobrienmips_pcpu_init(struct pmc_mdep *md, int cpu) 371104834Sobrien{ 372104834Sobrien int first_ri, i; 373104834Sobrien struct pmc_cpu *pc; 374104834Sobrien struct mips_cpu *pac; 375104834Sobrien struct pmc_hw *phw; 376104834Sobrien 377104834Sobrien KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 378104834Sobrien ("[mips,%d] wrong cpu number %d", __LINE__, cpu)); 379104834Sobrien PMCDBG(MDP,INI,1,"mips-init cpu=%d", cpu); 380104834Sobrien 381104834Sobrien mips_pcpu[cpu] = pac = malloc(sizeof(struct mips_cpu), M_PMC, 382104834Sobrien M_WAITOK|M_ZERO); 383104834Sobrien pac->pc_mipspmcs = malloc(sizeof(struct pmc_hw) * mips_npmcs, 384104834Sobrien M_PMC, M_WAITOK|M_ZERO); 385104834Sobrien pc = pmc_pcpu[cpu]; 386104834Sobrien first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS].pcd_ri; 387104834Sobrien KASSERT(pc != NULL, ("[mips,%d] NULL per-cpu pointer", __LINE__)); 388104834Sobrien 389104834Sobrien for (i = 0, phw = pac->pc_mipspmcs; i < mips_npmcs; i++, phw++) { 390104834Sobrien phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 391104834Sobrien PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); 392104834Sobrien phw->phw_pmc = NULL; 393104834Sobrien pc->pc_hwpmcs[i + first_ri] = phw; 394104834Sobrien } 395104834Sobrien 396104834Sobrien /* 397104834Sobrien * Clear the counter control register which has the effect 398104834Sobrien * of disabling counting. 399104834Sobrien */ 400104834Sobrien for (i = 0; i < mips_npmcs; i++) 401104834Sobrien mips_pmcn_write(i, 0); 402104834Sobrien 40333965Sjdp return 0; 40433965Sjdp} 40533965Sjdp 40633965Sjdpstatic int 407104834Sobrienmips_pcpu_fini(struct pmc_mdep *md, int cpu) 40833965Sjdp{ 409130561Sobrien return 0; 41033965Sjdp} 411104834Sobrien 41233965Sjdpstruct pmc_mdep * 413104834Sobrienpmc_mips_initialize() 414104834Sobrien{ 41533965Sjdp struct pmc_mdep *pmc_mdep; 416104834Sobrien struct pmc_classdep *pcd; 417104834Sobrien 418104834Sobrien /* 419104834Sobrien * TODO: Use More bit of PerfCntlX register to detect actual 420104834Sobrien * number of counters 421104834Sobrien */ 422104834Sobrien mips_npmcs = 2; 423104834Sobrien 424104834Sobrien PMCDBG(MDP,INI,1,"mips-init npmcs=%d", mips_npmcs); 425104834Sobrien 426104834Sobrien /* 427104834Sobrien * Allocate space for pointers to PMC HW descriptors and for 428104834Sobrien * the MDEP structure used by MI code. 42933965Sjdp */ 43033965Sjdp mips_pcpu = malloc(sizeof(struct mips_cpu *) * pmc_cpu_max(), M_PMC, 431104834Sobrien M_WAITOK|M_ZERO); 432130561Sobrien 43333965Sjdp /* Just one class */ 43433965Sjdp pmc_mdep = pmc_mdep_alloc(1); 435104834Sobrien 436130561Sobrien pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype; 437104834Sobrien 438104834Sobrien pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS]; 43933965Sjdp pcd->pcd_caps = mips_pmc_spec.ps_capabilities; 440104834Sobrien pcd->pcd_class = mips_pmc_spec.ps_cpuclass; 44133965Sjdp pcd->pcd_num = mips_npmcs; 442130561Sobrien pcd->pcd_ri = pmc_mdep->pmd_npmc; 44333965Sjdp pcd->pcd_width = mips_pmc_spec.ps_counter_width; 444104834Sobrien 445104834Sobrien pcd->pcd_allocate_pmc = mips_allocate_pmc; 446104834Sobrien pcd->pcd_config_pmc = mips_config_pmc; 447104834Sobrien pcd->pcd_pcpu_fini = mips_pcpu_fini; 448104834Sobrien pcd->pcd_pcpu_init = mips_pcpu_init; 449104834Sobrien pcd->pcd_describe = mips_describe; 450104834Sobrien pcd->pcd_get_config = mips_get_config; 451104834Sobrien pcd->pcd_read_pmc = mips_read_pmc; 45233965Sjdp pcd->pcd_release_pmc = mips_release_pmc; 453104834Sobrien pcd->pcd_start_pmc = mips_start_pmc; 454104834Sobrien pcd->pcd_stop_pmc = mips_stop_pmc; 455104834Sobrien pcd->pcd_write_pmc = mips_write_pmc; 45633965Sjdp 457104834Sobrien pmc_mdep->pmd_intr = mips_pmc_intr; 458104834Sobrien pmc_mdep->pmd_switch_in = mips_pmc_switch_in; 459104834Sobrien pmc_mdep->pmd_switch_out = mips_pmc_switch_out; 460104834Sobrien 461104834Sobrien pmc_mdep->pmd_npmc += mips_npmcs; 462104834Sobrien 463104834Sobrien return (pmc_mdep); 46433965Sjdp} 465104834Sobrien 466104834Sobrienvoid 467104834Sobrienpmc_mips_finalize(struct pmc_mdep *md) 468104834Sobrien{ 469104834Sobrien (void) md; 470104834Sobrien} 471104834Sobrien 472104834Sobrien#ifdef HWPMC_MIPS_BACKTRACE 473104834Sobrien 474104834Sobrienstatic int 475104834Sobrienpmc_next_frame(register_t *pc, register_t *sp) 476104834Sobrien{ 47733965Sjdp InstFmt i; 47833965Sjdp uintptr_t va; 479 uint32_t instr, mask; 480 int more, stksize; 481 register_t ra = 0; 482 483 /* Jump here after a nonstandard (interrupt handler) frame */ 484 stksize = 0; 485 486 /* check for bad SP: could foul up next frame */ 487 if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 488 goto error; 489 } 490 491 /* check for bad PC */ 492 if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 493 goto error; 494 } 495 496 /* 497 * Find the beginning of the current subroutine by scanning 498 * backwards from the current PC for the end of the previous 499 * subroutine. 500 */ 501 va = *pc - sizeof(int); 502 while (1) { 503 instr = *((uint32_t *)va); 504 505 /* [d]addiu sp,sp,-X */ 506 if (((instr & 0xffff8000) == 0x27bd8000) 507 || ((instr & 0xffff8000) == 0x67bd8000)) 508 break; 509 510 /* jr ra */ 511 if (instr == 0x03e00008) { 512 /* skip over branch-delay slot instruction */ 513 va += 2 * sizeof(int); 514 break; 515 } 516 517 va -= sizeof(int); 518 } 519 520 /* skip over nulls which might separate .o files */ 521 while ((instr = *((uint32_t *)va)) == 0) 522 va += sizeof(int); 523 524 /* scan forwards to find stack size and any saved registers */ 525 stksize = 0; 526 more = 3; 527 mask = 0; 528 for (; more; va += sizeof(int), 529 more = (more == 3) ? 3 : more - 1) { 530 /* stop if hit our current position */ 531 if (va >= *pc) 532 break; 533 instr = *((uint32_t *)va); 534 i.word = instr; 535 switch (i.JType.op) { 536 case OP_SPECIAL: 537 switch (i.RType.func) { 538 case OP_JR: 539 case OP_JALR: 540 more = 2; /* stop after next instruction */ 541 break; 542 543 case OP_SYSCALL: 544 case OP_BREAK: 545 more = 1; /* stop now */ 546 }; 547 break; 548 549 case OP_BCOND: 550 case OP_J: 551 case OP_JAL: 552 case OP_BEQ: 553 case OP_BNE: 554 case OP_BLEZ: 555 case OP_BGTZ: 556 more = 2; /* stop after next instruction */ 557 break; 558 559 case OP_COP0: 560 case OP_COP1: 561 case OP_COP2: 562 case OP_COP3: 563 switch (i.RType.rs) { 564 case OP_BCx: 565 case OP_BCy: 566 more = 2; /* stop after next instruction */ 567 }; 568 break; 569 570 case OP_SW: 571 case OP_SD: 572 /* 573 * SP is being saved using S8(FP). Most likely it indicates 574 * that SP is modified in the function and we can't get 575 * its value safely without emulating code backward 576 * So just bail out on functions like this 577 */ 578 if ((i.IType.rs == 30) && (i.IType.rt = 29)) 579 return (-1); 580 581 /* look for saved registers on the stack */ 582 if (i.IType.rs != 29) 583 break; 584 /* only restore the first one */ 585 if (mask & (1 << i.IType.rt)) 586 break; 587 mask |= (1 << i.IType.rt); 588 if (i.IType.rt == 31) 589 ra = *((register_t *)(*sp + (short)i.IType.imm)); 590 break; 591 592 case OP_ADDI: 593 case OP_ADDIU: 594 case OP_DADDI: 595 case OP_DADDIU: 596 /* look for stack pointer adjustment */ 597 if (i.IType.rs != 29 || i.IType.rt != 29) 598 break; 599 stksize = -((short)i.IType.imm); 600 } 601 } 602 603 if (!MIPS_IS_VALID_KERNELADDR(ra)) 604 return (-1); 605 606 *pc = ra; 607 *sp += stksize; 608 609 return (0); 610 611error: 612 return (-1); 613} 614 615static int 616pmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) 617{ 618 int offset, registers_on_stack; 619 uint32_t opcode, mask; 620 register_t function_start; 621 int stksize; 622 InstFmt i; 623 624 registers_on_stack = 0; 625 mask = 0; 626 function_start = 0; 627 offset = 0; 628 stksize = 0; 629 630 while (offset < MAX_FUNCTION_SIZE) { 631 opcode = fuword32((void *)(*pc - offset)); 632 633 /* [d]addiu sp, sp, -X*/ 634 if (((opcode & 0xffff8000) == 0x27bd8000) 635 || ((opcode & 0xffff8000) == 0x67bd8000)) { 636 function_start = *pc - offset; 637 registers_on_stack = 1; 638 break; 639 } 640 641 /* lui gp, X */ 642 if ((opcode & 0xffff8000) == 0x3c1c0000) { 643 /* 644 * Function might start with this instruction 645 * Keep an eye on "jr ra" and sp correction 646 * with positive value further on 647 */ 648 function_start = *pc - offset; 649 } 650 651 if (function_start) { 652 /* 653 * Stop looking further. Possible end of 654 * function instruction: it means there is no 655 * stack modifications, sp is unchanged 656 */ 657 658 /* [d]addiu sp,sp,X */ 659 if (((opcode & 0xffff8000) == 0x27bd0000) 660 || ((opcode & 0xffff8000) == 0x67bd0000)) 661 break; 662 663 if (opcode == 0x03e00008) 664 break; 665 } 666 667 offset += sizeof(int); 668 } 669 670 if (!function_start) 671 return (-1); 672 673 if (registers_on_stack) { 674 offset = 0; 675 while ((offset < MAX_PROLOGUE_SIZE) 676 && ((function_start + offset) < *pc)) { 677 i.word = fuword32((void *)(function_start + offset)); 678 switch (i.JType.op) { 679 case OP_SW: 680 /* look for saved registers on the stack */ 681 if (i.IType.rs != 29) 682 break; 683 /* only restore the first one */ 684 if (mask & (1 << i.IType.rt)) 685 break; 686 mask |= (1 << i.IType.rt); 687 if (i.IType.rt == 31) 688 *ra = fuword32((void *)(*sp + (short)i.IType.imm)); 689 break; 690 691#if defined(__mips_n64) 692 case OP_SD: 693 /* look for saved registers on the stack */ 694 if (i.IType.rs != 29) 695 break; 696 /* only restore the first one */ 697 if (mask & (1 << i.IType.rt)) 698 break; 699 mask |= (1 << i.IType.rt); 700 /* ra */ 701 if (i.IType.rt == 31) 702 *ra = fuword64((void *)(*sp + (short)i.IType.imm)); 703 break; 704#endif 705 706 case OP_ADDI: 707 case OP_ADDIU: 708 case OP_DADDI: 709 case OP_DADDIU: 710 /* look for stack pointer adjustment */ 711 if (i.IType.rs != 29 || i.IType.rt != 29) 712 break; 713 stksize = -((short)i.IType.imm); 714 } 715 716 offset += sizeof(int); 717 } 718 } 719 720 /* 721 * We reached the end of backtrace 722 */ 723 if (*pc == *ra) 724 return (-1); 725 726 *pc = *ra; 727 *sp += stksize; 728 729 return (0); 730} 731 732#endif /* HWPMC_MIPS_BACKTRACE */ 733 734struct pmc_mdep * 735pmc_md_initialize() 736{ 737 return pmc_mips_initialize(); 738} 739 740void 741pmc_md_finalize(struct pmc_mdep *md) 742{ 743 return pmc_mips_finalize(md); 744} 745 746int 747pmc_save_kernel_callchain(uintptr_t *cc, int nframes, 748 struct trapframe *tf) 749{ 750 register_t pc, ra, sp; 751 int frames = 0; 752 753 pc = tf->pc; 754 sp = tf->sp; 755 ra = tf->ra; 756 757 cc[frames++] = pc; 758 759#ifdef HWPMC_MIPS_BACKTRACE 760 /* 761 * Unwind, and unwind, and unwind 762 */ 763 while (1) { 764 if (frames >= nframes) 765 break; 766 767 if (pmc_next_frame(&pc, &sp) < 0) 768 break; 769 770 cc[frames++] = pc; 771 } 772#endif 773 774 return (frames); 775} 776 777int 778pmc_save_user_callchain(uintptr_t *cc, int nframes, 779 struct trapframe *tf) 780{ 781 register_t pc, ra, sp; 782 int frames = 0; 783 784 pc = tf->pc; 785 sp = tf->sp; 786 ra = tf->ra; 787 788 cc[frames++] = pc; 789 790#ifdef HWPMC_MIPS_BACKTRACE 791 792 /* 793 * Unwind, and unwind, and unwind 794 */ 795 while (1) { 796 if (frames >= nframes) 797 break; 798 799 if (pmc_next_uframe(&pc, &sp, &ra) < 0) 800 break; 801 802 cc[frames++] = pc; 803 } 804#endif 805 806 return (frames); 807} 808