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$"); 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 107282658Sjhb PMCDBG2(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); 126282658Sjhb PMCDBG2(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 151282658Sjhb PMCDBG3(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 163282658Sjhb PMCDBG3(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; 290233628Sfabient error = pmc_process_interrupt(cpu, PMC_HR, 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)); 379282658Sjhb PMCDBG1(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 424282658Sjhb PMCDBG1(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 */ 434234598Sfabient pmc_mdep = pmc_mdep_alloc(1); 435233319Sgonzo 436233319Sgonzo pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype; 437233319Sgonzo 438233319Sgonzo pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS]; 439233319Sgonzo pcd->pcd_caps = mips_pmc_spec.ps_capabilities; 440233319Sgonzo pcd->pcd_class = mips_pmc_spec.ps_cpuclass; 441233319Sgonzo pcd->pcd_num = mips_npmcs; 442233319Sgonzo pcd->pcd_ri = pmc_mdep->pmd_npmc; 443233319Sgonzo pcd->pcd_width = mips_pmc_spec.ps_counter_width; 444233319Sgonzo 445233319Sgonzo pcd->pcd_allocate_pmc = mips_allocate_pmc; 446233319Sgonzo pcd->pcd_config_pmc = mips_config_pmc; 447233319Sgonzo pcd->pcd_pcpu_fini = mips_pcpu_fini; 448233319Sgonzo pcd->pcd_pcpu_init = mips_pcpu_init; 449233319Sgonzo pcd->pcd_describe = mips_describe; 450233319Sgonzo pcd->pcd_get_config = mips_get_config; 451233319Sgonzo pcd->pcd_read_pmc = mips_read_pmc; 452233319Sgonzo pcd->pcd_release_pmc = mips_release_pmc; 453233319Sgonzo pcd->pcd_start_pmc = mips_start_pmc; 454233319Sgonzo pcd->pcd_stop_pmc = mips_stop_pmc; 455233319Sgonzo pcd->pcd_write_pmc = mips_write_pmc; 456233319Sgonzo 457233319Sgonzo pmc_mdep->pmd_intr = mips_pmc_intr; 458233319Sgonzo pmc_mdep->pmd_switch_in = mips_pmc_switch_in; 459233319Sgonzo pmc_mdep->pmd_switch_out = mips_pmc_switch_out; 460233319Sgonzo 461233319Sgonzo pmc_mdep->pmd_npmc += mips_npmcs; 462233319Sgonzo 463233319Sgonzo return (pmc_mdep); 464233319Sgonzo} 465233319Sgonzo 466233319Sgonzovoid 467233319Sgonzopmc_mips_finalize(struct pmc_mdep *md) 468233319Sgonzo{ 469233319Sgonzo (void) md; 470233319Sgonzo} 471233319Sgonzo 472233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 473233319Sgonzo 474233319Sgonzostatic int 475232846Sgonzopmc_next_frame(register_t *pc, register_t *sp) 476232846Sgonzo{ 477232846Sgonzo InstFmt i; 478232846Sgonzo uintptr_t va; 479232846Sgonzo uint32_t instr, mask; 480232846Sgonzo int more, stksize; 481232846Sgonzo register_t ra = 0; 482232846Sgonzo 483232846Sgonzo /* Jump here after a nonstandard (interrupt handler) frame */ 484232846Sgonzo stksize = 0; 485232846Sgonzo 486232846Sgonzo /* check for bad SP: could foul up next frame */ 487232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 488232846Sgonzo goto error; 489232846Sgonzo } 490232846Sgonzo 491232846Sgonzo /* check for bad PC */ 492232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 493232846Sgonzo goto error; 494232846Sgonzo } 495232846Sgonzo 496232846Sgonzo /* 497232846Sgonzo * Find the beginning of the current subroutine by scanning 498232846Sgonzo * backwards from the current PC for the end of the previous 499232846Sgonzo * subroutine. 500232846Sgonzo */ 501232846Sgonzo va = *pc - sizeof(int); 502232846Sgonzo while (1) { 503232846Sgonzo instr = *((uint32_t *)va); 504232846Sgonzo 505232846Sgonzo /* [d]addiu sp,sp,-X */ 506232846Sgonzo if (((instr & 0xffff8000) == 0x27bd8000) 507232846Sgonzo || ((instr & 0xffff8000) == 0x67bd8000)) 508232846Sgonzo break; 509232846Sgonzo 510232846Sgonzo /* jr ra */ 511232846Sgonzo if (instr == 0x03e00008) { 512232846Sgonzo /* skip over branch-delay slot instruction */ 513232846Sgonzo va += 2 * sizeof(int); 514232846Sgonzo break; 515232846Sgonzo } 516232846Sgonzo 517232846Sgonzo va -= sizeof(int); 518232846Sgonzo } 519232846Sgonzo 520232846Sgonzo /* skip over nulls which might separate .o files */ 521232846Sgonzo while ((instr = *((uint32_t *)va)) == 0) 522232846Sgonzo va += sizeof(int); 523232846Sgonzo 524232846Sgonzo /* scan forwards to find stack size and any saved registers */ 525232846Sgonzo stksize = 0; 526232846Sgonzo more = 3; 527232846Sgonzo mask = 0; 528232846Sgonzo for (; more; va += sizeof(int), 529232846Sgonzo more = (more == 3) ? 3 : more - 1) { 530232846Sgonzo /* stop if hit our current position */ 531232846Sgonzo if (va >= *pc) 532232846Sgonzo break; 533232846Sgonzo instr = *((uint32_t *)va); 534232846Sgonzo i.word = instr; 535232846Sgonzo switch (i.JType.op) { 536232846Sgonzo case OP_SPECIAL: 537232846Sgonzo switch (i.RType.func) { 538232846Sgonzo case OP_JR: 539232846Sgonzo case OP_JALR: 540232846Sgonzo more = 2; /* stop after next instruction */ 541232846Sgonzo break; 542232846Sgonzo 543232846Sgonzo case OP_SYSCALL: 544232846Sgonzo case OP_BREAK: 545232846Sgonzo more = 1; /* stop now */ 546297793Spfg } 547232846Sgonzo break; 548232846Sgonzo 549232846Sgonzo case OP_BCOND: 550232846Sgonzo case OP_J: 551232846Sgonzo case OP_JAL: 552232846Sgonzo case OP_BEQ: 553232846Sgonzo case OP_BNE: 554232846Sgonzo case OP_BLEZ: 555232846Sgonzo case OP_BGTZ: 556232846Sgonzo more = 2; /* stop after next instruction */ 557232846Sgonzo break; 558232846Sgonzo 559232846Sgonzo case OP_COP0: 560232846Sgonzo case OP_COP1: 561232846Sgonzo case OP_COP2: 562232846Sgonzo case OP_COP3: 563232846Sgonzo switch (i.RType.rs) { 564232846Sgonzo case OP_BCx: 565232846Sgonzo case OP_BCy: 566232846Sgonzo more = 2; /* stop after next instruction */ 567297793Spfg } 568232846Sgonzo break; 569232846Sgonzo 570232846Sgonzo case OP_SW: 571232846Sgonzo case OP_SD: 572232992Sgonzo /* 573232992Sgonzo * SP is being saved using S8(FP). Most likely it indicates 574232992Sgonzo * that SP is modified in the function and we can't get 575232992Sgonzo * its value safely without emulating code backward 576232992Sgonzo * So just bail out on functions like this 577232992Sgonzo */ 578232992Sgonzo if ((i.IType.rs == 30) && (i.IType.rt = 29)) 579232992Sgonzo return (-1); 580232992Sgonzo 581232846Sgonzo /* look for saved registers on the stack */ 582232846Sgonzo if (i.IType.rs != 29) 583232846Sgonzo break; 584232846Sgonzo /* only restore the first one */ 585232846Sgonzo if (mask & (1 << i.IType.rt)) 586232846Sgonzo break; 587232846Sgonzo mask |= (1 << i.IType.rt); 588232846Sgonzo if (i.IType.rt == 31) 589232846Sgonzo ra = *((register_t *)(*sp + (short)i.IType.imm)); 590232846Sgonzo break; 591232846Sgonzo 592232846Sgonzo case OP_ADDI: 593232846Sgonzo case OP_ADDIU: 594232846Sgonzo case OP_DADDI: 595232846Sgonzo case OP_DADDIU: 596232846Sgonzo /* look for stack pointer adjustment */ 597232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 598232846Sgonzo break; 599232846Sgonzo stksize = -((short)i.IType.imm); 600232846Sgonzo } 601232846Sgonzo } 602232846Sgonzo 603232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(ra)) 604232846Sgonzo return (-1); 605232846Sgonzo 606232846Sgonzo *pc = ra; 607232846Sgonzo *sp += stksize; 608232846Sgonzo 609232846Sgonzo return (0); 610232846Sgonzo 611232846Sgonzoerror: 612232846Sgonzo return (-1); 613232846Sgonzo} 614232846Sgonzo 615232846Sgonzostatic int 616232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) 617232846Sgonzo{ 618232846Sgonzo int offset, registers_on_stack; 619232846Sgonzo uint32_t opcode, mask; 620232846Sgonzo register_t function_start; 621232846Sgonzo int stksize; 622232846Sgonzo InstFmt i; 623232846Sgonzo 624232846Sgonzo registers_on_stack = 0; 625232846Sgonzo mask = 0; 626232846Sgonzo function_start = 0; 627232846Sgonzo offset = 0; 628232846Sgonzo stksize = 0; 629232846Sgonzo 630232846Sgonzo while (offset < MAX_FUNCTION_SIZE) { 631232846Sgonzo opcode = fuword32((void *)(*pc - offset)); 632232846Sgonzo 633232846Sgonzo /* [d]addiu sp, sp, -X*/ 634232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd8000) 635232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd8000)) { 636232846Sgonzo function_start = *pc - offset; 637232846Sgonzo registers_on_stack = 1; 638232846Sgonzo break; 639232846Sgonzo } 640232846Sgonzo 641232846Sgonzo /* lui gp, X */ 642232846Sgonzo if ((opcode & 0xffff8000) == 0x3c1c0000) { 643232846Sgonzo /* 644232846Sgonzo * Function might start with this instruction 645232846Sgonzo * Keep an eye on "jr ra" and sp correction 646232846Sgonzo * with positive value further on 647232846Sgonzo */ 648232846Sgonzo function_start = *pc - offset; 649232846Sgonzo } 650232846Sgonzo 651232846Sgonzo if (function_start) { 652232846Sgonzo /* 653232846Sgonzo * Stop looking further. Possible end of 654232846Sgonzo * function instruction: it means there is no 655232846Sgonzo * stack modifications, sp is unchanged 656232846Sgonzo */ 657232846Sgonzo 658232846Sgonzo /* [d]addiu sp,sp,X */ 659232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd0000) 660232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd0000)) 661232846Sgonzo break; 662232846Sgonzo 663232846Sgonzo if (opcode == 0x03e00008) 664232846Sgonzo break; 665232846Sgonzo } 666232846Sgonzo 667232846Sgonzo offset += sizeof(int); 668232846Sgonzo } 669232846Sgonzo 670232846Sgonzo if (!function_start) 671232846Sgonzo return (-1); 672232846Sgonzo 673232846Sgonzo if (registers_on_stack) { 674232846Sgonzo offset = 0; 675232846Sgonzo while ((offset < MAX_PROLOGUE_SIZE) 676232846Sgonzo && ((function_start + offset) < *pc)) { 677232846Sgonzo i.word = fuword32((void *)(function_start + offset)); 678232846Sgonzo switch (i.JType.op) { 679232846Sgonzo case OP_SW: 680232846Sgonzo /* look for saved registers on the stack */ 681232846Sgonzo if (i.IType.rs != 29) 682232846Sgonzo break; 683232846Sgonzo /* only restore the first one */ 684232846Sgonzo if (mask & (1 << i.IType.rt)) 685232846Sgonzo break; 686232846Sgonzo mask |= (1 << i.IType.rt); 687232846Sgonzo if (i.IType.rt == 31) 688232846Sgonzo *ra = fuword32((void *)(*sp + (short)i.IType.imm)); 689232846Sgonzo break; 690232846Sgonzo 691232846Sgonzo#if defined(__mips_n64) 692232846Sgonzo case OP_SD: 693232846Sgonzo /* look for saved registers on the stack */ 694232846Sgonzo if (i.IType.rs != 29) 695232846Sgonzo break; 696232846Sgonzo /* only restore the first one */ 697232846Sgonzo if (mask & (1 << i.IType.rt)) 698232846Sgonzo break; 699232846Sgonzo mask |= (1 << i.IType.rt); 700232846Sgonzo /* ra */ 701232846Sgonzo if (i.IType.rt == 31) 702232846Sgonzo *ra = fuword64((void *)(*sp + (short)i.IType.imm)); 703232846Sgonzo break; 704232846Sgonzo#endif 705232846Sgonzo 706232846Sgonzo case OP_ADDI: 707232846Sgonzo case OP_ADDIU: 708232846Sgonzo case OP_DADDI: 709232846Sgonzo case OP_DADDIU: 710232846Sgonzo /* look for stack pointer adjustment */ 711232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 712232846Sgonzo break; 713232846Sgonzo stksize = -((short)i.IType.imm); 714232846Sgonzo } 715232846Sgonzo 716232846Sgonzo offset += sizeof(int); 717232846Sgonzo } 718232846Sgonzo } 719232846Sgonzo 720232846Sgonzo /* 721232846Sgonzo * We reached the end of backtrace 722232846Sgonzo */ 723232846Sgonzo if (*pc == *ra) 724232846Sgonzo return (-1); 725232846Sgonzo 726232846Sgonzo *pc = *ra; 727232846Sgonzo *sp += stksize; 728232846Sgonzo 729232846Sgonzo return (0); 730232846Sgonzo} 731232846Sgonzo 732233319Sgonzo#endif /* HWPMC_MIPS_BACKTRACE */ 733233319Sgonzo 734204635Sgnnstruct pmc_mdep * 735204635Sgnnpmc_md_initialize() 736204635Sgnn{ 737233319Sgonzo return pmc_mips_initialize(); 738204635Sgnn} 739204635Sgnn 740204635Sgnnvoid 741204635Sgnnpmc_md_finalize(struct pmc_mdep *md) 742204635Sgnn{ 743233319Sgonzo return pmc_mips_finalize(md); 744204635Sgnn} 745204635Sgnn 746204635Sgnnint 747232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes, 748204635Sgnn struct trapframe *tf) 749204635Sgnn{ 750232846Sgonzo register_t pc, ra, sp; 751232846Sgonzo int frames = 0; 752232846Sgonzo 753232992Sgonzo pc = tf->pc; 754232992Sgonzo sp = tf->sp; 755232992Sgonzo ra = tf->ra; 756232846Sgonzo 757233319Sgonzo cc[frames++] = pc; 758233319Sgonzo 759233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 760232846Sgonzo /* 761232846Sgonzo * Unwind, and unwind, and unwind 762232846Sgonzo */ 763232846Sgonzo while (1) { 764232846Sgonzo if (frames >= nframes) 765232846Sgonzo break; 766232846Sgonzo 767232846Sgonzo if (pmc_next_frame(&pc, &sp) < 0) 768232846Sgonzo break; 769233319Sgonzo 770233319Sgonzo cc[frames++] = pc; 771232846Sgonzo } 772233319Sgonzo#endif 773232846Sgonzo 774232846Sgonzo return (frames); 775204635Sgnn} 776204635Sgnn 777204635Sgnnint 778232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes, 779204635Sgnn struct trapframe *tf) 780204635Sgnn{ 781232846Sgonzo register_t pc, ra, sp; 782232846Sgonzo int frames = 0; 783232846Sgonzo 784232992Sgonzo pc = tf->pc; 785232992Sgonzo sp = tf->sp; 786232992Sgonzo ra = tf->ra; 787232846Sgonzo 788233319Sgonzo cc[frames++] = pc; 789233319Sgonzo 790233319Sgonzo#ifdef HWPMC_MIPS_BACKTRACE 791233319Sgonzo 792232846Sgonzo /* 793232846Sgonzo * Unwind, and unwind, and unwind 794232846Sgonzo */ 795232846Sgonzo while (1) { 796232846Sgonzo if (frames >= nframes) 797232846Sgonzo break; 798232846Sgonzo 799232846Sgonzo if (pmc_next_uframe(&pc, &sp, &ra) < 0) 800232846Sgonzo break; 801233319Sgonzo 802233319Sgonzo cc[frames++] = pc; 803232846Sgonzo } 804233319Sgonzo#endif 805232846Sgonzo 806232846Sgonzo return (frames); 807204635Sgnn} 808