hwpmc_mips.c revision 232846
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 232846 2012-03-12 01:19:41Z gonzo $"); 30204635Sgnn 31204635Sgnn#include <sys/param.h> 32204635Sgnn#include <sys/pmc.h> 33204635Sgnn#include <sys/systm.h> 34204635Sgnn 35204635Sgnn#include <machine/pmc_mdep.h> 36204635Sgnn#include <machine/md_var.h> 37232846Sgonzo#include <machine/mips_opcode.h> 38232846Sgonzo#include <machine/vmparam.h> 39204635Sgnn 40232846Sgonzo#if defined(__mips_n64) 41232846Sgonzo# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 42232846Sgonzo ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 43232846Sgonzo#else 44232846Sgonzo# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 45232846Sgonzo ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 46232846Sgonzo#endif 47232846Sgonzo 48232846Sgonzo/* 49232846Sgonzo * We need some reasonable default to prevent backtrace code 50232846Sgonzo * from wandering too far 51232846Sgonzo */ 52232846Sgonzo#define MAX_FUNCTION_SIZE 0x10000 53232846Sgonzo#define MAX_PROLOGUE_SIZE 0x100 54232846Sgonzo 55232846Sgonzostatic int 56232846Sgonzopmc_next_frame(register_t *pc, register_t *sp) 57232846Sgonzo{ 58232846Sgonzo InstFmt i; 59232846Sgonzo uintptr_t va; 60232846Sgonzo uint32_t instr, mask; 61232846Sgonzo int more, stksize; 62232846Sgonzo register_t ra = 0; 63232846Sgonzo 64232846Sgonzo /* Jump here after a nonstandard (interrupt handler) frame */ 65232846Sgonzo stksize = 0; 66232846Sgonzo 67232846Sgonzo /* check for bad SP: could foul up next frame */ 68232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 69232846Sgonzo goto error; 70232846Sgonzo } 71232846Sgonzo 72232846Sgonzo /* check for bad PC */ 73232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 74232846Sgonzo goto error; 75232846Sgonzo } 76232846Sgonzo 77232846Sgonzo /* 78232846Sgonzo * Find the beginning of the current subroutine by scanning 79232846Sgonzo * backwards from the current PC for the end of the previous 80232846Sgonzo * subroutine. 81232846Sgonzo */ 82232846Sgonzo va = *pc - sizeof(int); 83232846Sgonzo while (1) { 84232846Sgonzo instr = *((uint32_t *)va); 85232846Sgonzo 86232846Sgonzo /* [d]addiu sp,sp,-X */ 87232846Sgonzo if (((instr & 0xffff8000) == 0x27bd8000) 88232846Sgonzo || ((instr & 0xffff8000) == 0x67bd8000)) 89232846Sgonzo break; 90232846Sgonzo 91232846Sgonzo /* jr ra */ 92232846Sgonzo if (instr == 0x03e00008) { 93232846Sgonzo /* skip over branch-delay slot instruction */ 94232846Sgonzo va += 2 * sizeof(int); 95232846Sgonzo break; 96232846Sgonzo } 97232846Sgonzo 98232846Sgonzo va -= sizeof(int); 99232846Sgonzo } 100232846Sgonzo 101232846Sgonzo /* skip over nulls which might separate .o files */ 102232846Sgonzo while ((instr = *((uint32_t *)va)) == 0) 103232846Sgonzo va += sizeof(int); 104232846Sgonzo 105232846Sgonzo /* scan forwards to find stack size and any saved registers */ 106232846Sgonzo stksize = 0; 107232846Sgonzo more = 3; 108232846Sgonzo mask = 0; 109232846Sgonzo for (; more; va += sizeof(int), 110232846Sgonzo more = (more == 3) ? 3 : more - 1) { 111232846Sgonzo /* stop if hit our current position */ 112232846Sgonzo if (va >= *pc) 113232846Sgonzo break; 114232846Sgonzo instr = *((uint32_t *)va); 115232846Sgonzo i.word = instr; 116232846Sgonzo switch (i.JType.op) { 117232846Sgonzo case OP_SPECIAL: 118232846Sgonzo switch (i.RType.func) { 119232846Sgonzo case OP_JR: 120232846Sgonzo case OP_JALR: 121232846Sgonzo more = 2; /* stop after next instruction */ 122232846Sgonzo break; 123232846Sgonzo 124232846Sgonzo case OP_SYSCALL: 125232846Sgonzo case OP_BREAK: 126232846Sgonzo more = 1; /* stop now */ 127232846Sgonzo }; 128232846Sgonzo break; 129232846Sgonzo 130232846Sgonzo case OP_BCOND: 131232846Sgonzo case OP_J: 132232846Sgonzo case OP_JAL: 133232846Sgonzo case OP_BEQ: 134232846Sgonzo case OP_BNE: 135232846Sgonzo case OP_BLEZ: 136232846Sgonzo case OP_BGTZ: 137232846Sgonzo more = 2; /* stop after next instruction */ 138232846Sgonzo break; 139232846Sgonzo 140232846Sgonzo case OP_COP0: 141232846Sgonzo case OP_COP1: 142232846Sgonzo case OP_COP2: 143232846Sgonzo case OP_COP3: 144232846Sgonzo switch (i.RType.rs) { 145232846Sgonzo case OP_BCx: 146232846Sgonzo case OP_BCy: 147232846Sgonzo more = 2; /* stop after next instruction */ 148232846Sgonzo }; 149232846Sgonzo break; 150232846Sgonzo 151232846Sgonzo case OP_SW: 152232846Sgonzo case OP_SD: 153232846Sgonzo /* look for saved registers on the stack */ 154232846Sgonzo if (i.IType.rs != 29) 155232846Sgonzo break; 156232846Sgonzo /* only restore the first one */ 157232846Sgonzo if (mask & (1 << i.IType.rt)) 158232846Sgonzo break; 159232846Sgonzo mask |= (1 << i.IType.rt); 160232846Sgonzo if (i.IType.rt == 31) 161232846Sgonzo ra = *((register_t *)(*sp + (short)i.IType.imm)); 162232846Sgonzo break; 163232846Sgonzo 164232846Sgonzo case OP_ADDI: 165232846Sgonzo case OP_ADDIU: 166232846Sgonzo case OP_DADDI: 167232846Sgonzo case OP_DADDIU: 168232846Sgonzo /* look for stack pointer adjustment */ 169232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 170232846Sgonzo break; 171232846Sgonzo stksize = -((short)i.IType.imm); 172232846Sgonzo } 173232846Sgonzo } 174232846Sgonzo 175232846Sgonzo if (!MIPS_IS_VALID_KERNELADDR(ra)) 176232846Sgonzo return (-1); 177232846Sgonzo 178232846Sgonzo *pc = ra; 179232846Sgonzo *sp += stksize; 180232846Sgonzo 181232846Sgonzo return (0); 182232846Sgonzo 183232846Sgonzoerror: 184232846Sgonzo return (-1); 185232846Sgonzo} 186232846Sgonzo 187232846Sgonzostatic int 188232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) 189232846Sgonzo{ 190232846Sgonzo int offset, registers_on_stack; 191232846Sgonzo uint32_t opcode, mask; 192232846Sgonzo register_t function_start; 193232846Sgonzo int stksize; 194232846Sgonzo InstFmt i; 195232846Sgonzo 196232846Sgonzo registers_on_stack = 0; 197232846Sgonzo mask = 0; 198232846Sgonzo function_start = 0; 199232846Sgonzo offset = 0; 200232846Sgonzo stksize = 0; 201232846Sgonzo 202232846Sgonzo while (offset < MAX_FUNCTION_SIZE) { 203232846Sgonzo opcode = fuword32((void *)(*pc - offset)); 204232846Sgonzo 205232846Sgonzo /* [d]addiu sp, sp, -X*/ 206232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd8000) 207232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd8000)) { 208232846Sgonzo function_start = *pc - offset; 209232846Sgonzo registers_on_stack = 1; 210232846Sgonzo break; 211232846Sgonzo } 212232846Sgonzo 213232846Sgonzo /* lui gp, X */ 214232846Sgonzo if ((opcode & 0xffff8000) == 0x3c1c0000) { 215232846Sgonzo /* 216232846Sgonzo * Function might start with this instruction 217232846Sgonzo * Keep an eye on "jr ra" and sp correction 218232846Sgonzo * with positive value further on 219232846Sgonzo */ 220232846Sgonzo function_start = *pc - offset; 221232846Sgonzo } 222232846Sgonzo 223232846Sgonzo if (function_start) { 224232846Sgonzo /* 225232846Sgonzo * Stop looking further. Possible end of 226232846Sgonzo * function instruction: it means there is no 227232846Sgonzo * stack modifications, sp is unchanged 228232846Sgonzo */ 229232846Sgonzo 230232846Sgonzo /* [d]addiu sp,sp,X */ 231232846Sgonzo if (((opcode & 0xffff8000) == 0x27bd0000) 232232846Sgonzo || ((opcode & 0xffff8000) == 0x67bd0000)) 233232846Sgonzo break; 234232846Sgonzo 235232846Sgonzo if (opcode == 0x03e00008) 236232846Sgonzo break; 237232846Sgonzo } 238232846Sgonzo 239232846Sgonzo offset += sizeof(int); 240232846Sgonzo } 241232846Sgonzo 242232846Sgonzo if (!function_start) 243232846Sgonzo return (-1); 244232846Sgonzo 245232846Sgonzo if (registers_on_stack) { 246232846Sgonzo offset = 0; 247232846Sgonzo while ((offset < MAX_PROLOGUE_SIZE) 248232846Sgonzo && ((function_start + offset) < *pc)) { 249232846Sgonzo i.word = fuword32((void *)(function_start + offset)); 250232846Sgonzo switch (i.JType.op) { 251232846Sgonzo case OP_SW: 252232846Sgonzo /* look for saved registers on the stack */ 253232846Sgonzo if (i.IType.rs != 29) 254232846Sgonzo break; 255232846Sgonzo /* only restore the first one */ 256232846Sgonzo if (mask & (1 << i.IType.rt)) 257232846Sgonzo break; 258232846Sgonzo mask |= (1 << i.IType.rt); 259232846Sgonzo if (i.IType.rt == 31) 260232846Sgonzo *ra = fuword32((void *)(*sp + (short)i.IType.imm)); 261232846Sgonzo break; 262232846Sgonzo 263232846Sgonzo#if defined(__mips_n64) 264232846Sgonzo case OP_SD: 265232846Sgonzo /* look for saved registers on the stack */ 266232846Sgonzo if (i.IType.rs != 29) 267232846Sgonzo break; 268232846Sgonzo /* only restore the first one */ 269232846Sgonzo if (mask & (1 << i.IType.rt)) 270232846Sgonzo break; 271232846Sgonzo mask |= (1 << i.IType.rt); 272232846Sgonzo /* ra */ 273232846Sgonzo if (i.IType.rt == 31) 274232846Sgonzo *ra = fuword64((void *)(*sp + (short)i.IType.imm)); 275232846Sgonzo break; 276232846Sgonzo#endif 277232846Sgonzo 278232846Sgonzo case OP_ADDI: 279232846Sgonzo case OP_ADDIU: 280232846Sgonzo case OP_DADDI: 281232846Sgonzo case OP_DADDIU: 282232846Sgonzo /* look for stack pointer adjustment */ 283232846Sgonzo if (i.IType.rs != 29 || i.IType.rt != 29) 284232846Sgonzo break; 285232846Sgonzo stksize = -((short)i.IType.imm); 286232846Sgonzo } 287232846Sgonzo 288232846Sgonzo offset += sizeof(int); 289232846Sgonzo } 290232846Sgonzo } 291232846Sgonzo 292232846Sgonzo /* 293232846Sgonzo * We reached the end of backtrace 294232846Sgonzo */ 295232846Sgonzo if (*pc == *ra) 296232846Sgonzo return (-1); 297232846Sgonzo 298232846Sgonzo *pc = *ra; 299232846Sgonzo *sp += stksize; 300232846Sgonzo 301232846Sgonzo return (0); 302232846Sgonzo} 303232846Sgonzo 304204635Sgnnstruct pmc_mdep * 305204635Sgnnpmc_md_initialize() 306204635Sgnn{ 307204635Sgnn /* if (cpu_class == CPU_CLASS_MIPS24K)*/ 308204635Sgnn return pmc_mips24k_initialize(); 309204635Sgnn /* else 310204635Sgnn return NULL;*/ 311204635Sgnn} 312204635Sgnn 313204635Sgnnvoid 314204635Sgnnpmc_md_finalize(struct pmc_mdep *md) 315204635Sgnn{ 316204635Sgnn /* if (cpu_class == CPU_CLASS_MIPS24K) */ 317204635Sgnn pmc_mips24k_finalize(md); 318204635Sgnn /* else 319204635Sgnn KASSERT(0, ("[mips,%d] Unknown CPU Class 0x%x", __LINE__, 320204635Sgnn cpu_class));*/ 321204635Sgnn} 322204635Sgnn 323204635Sgnnint 324232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes, 325204635Sgnn struct trapframe *tf) 326204635Sgnn{ 327232846Sgonzo register_t pc, ra, sp; 328232846Sgonzo int frames = 0; 329232846Sgonzo 330232846Sgonzo pc = (uint64_t)tf->pc; 331232846Sgonzo sp = (uint64_t)tf->sp; 332232846Sgonzo ra = (uint64_t)tf->ra; 333232846Sgonzo 334232846Sgonzo /* 335232846Sgonzo * Unwind, and unwind, and unwind 336232846Sgonzo */ 337232846Sgonzo while (1) { 338232846Sgonzo cc[frames++] = pc; 339232846Sgonzo if (frames >= nframes) 340232846Sgonzo break; 341232846Sgonzo 342232846Sgonzo if (pmc_next_frame(&pc, &sp) < 0) 343232846Sgonzo break; 344232846Sgonzo } 345232846Sgonzo 346232846Sgonzo return (frames); 347204635Sgnn} 348204635Sgnn 349204635Sgnnint 350232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes, 351204635Sgnn struct trapframe *tf) 352204635Sgnn{ 353232846Sgonzo register_t pc, ra, sp; 354232846Sgonzo int frames = 0; 355232846Sgonzo 356232846Sgonzo pc = (uint64_t)tf->pc; 357232846Sgonzo sp = (uint64_t)tf->sp; 358232846Sgonzo ra = (uint64_t)tf->ra; 359232846Sgonzo 360232846Sgonzo /* 361232846Sgonzo * Unwind, and unwind, and unwind 362232846Sgonzo */ 363232846Sgonzo while (1) { 364232846Sgonzo cc[frames++] = pc; 365232846Sgonzo if (frames >= nframes) 366232846Sgonzo break; 367232846Sgonzo 368232846Sgonzo if (pmc_next_uframe(&pc, &sp, &ra) < 0) 369232846Sgonzo break; 370232846Sgonzo } 371232846Sgonzo 372232846Sgonzo return (frames); 373204635Sgnn} 374