hwpmc_mips.c revision 232846
1/*- 2 * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 232846 2012-03-12 01:19:41Z gonzo $"); 30 31#include <sys/param.h> 32#include <sys/pmc.h> 33#include <sys/systm.h> 34 35#include <machine/pmc_mdep.h> 36#include <machine/md_var.h> 37#include <machine/mips_opcode.h> 38#include <machine/vmparam.h> 39 40#if defined(__mips_n64) 41# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 42 ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) 43#else 44# define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ 45 ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) 46#endif 47 48/* 49 * We need some reasonable default to prevent backtrace code 50 * from wandering too far 51 */ 52#define MAX_FUNCTION_SIZE 0x10000 53#define MAX_PROLOGUE_SIZE 0x100 54 55static int 56pmc_next_frame(register_t *pc, register_t *sp) 57{ 58 InstFmt i; 59 uintptr_t va; 60 uint32_t instr, mask; 61 int more, stksize; 62 register_t ra = 0; 63 64 /* Jump here after a nonstandard (interrupt handler) frame */ 65 stksize = 0; 66 67 /* check for bad SP: could foul up next frame */ 68 if (!MIPS_IS_VALID_KERNELADDR(*sp)) { 69 goto error; 70 } 71 72 /* check for bad PC */ 73 if (!MIPS_IS_VALID_KERNELADDR(*pc)) { 74 goto error; 75 } 76 77 /* 78 * Find the beginning of the current subroutine by scanning 79 * backwards from the current PC for the end of the previous 80 * subroutine. 81 */ 82 va = *pc - sizeof(int); 83 while (1) { 84 instr = *((uint32_t *)va); 85 86 /* [d]addiu sp,sp,-X */ 87 if (((instr & 0xffff8000) == 0x27bd8000) 88 || ((instr & 0xffff8000) == 0x67bd8000)) 89 break; 90 91 /* jr ra */ 92 if (instr == 0x03e00008) { 93 /* skip over branch-delay slot instruction */ 94 va += 2 * sizeof(int); 95 break; 96 } 97 98 va -= sizeof(int); 99 } 100 101 /* skip over nulls which might separate .o files */ 102 while ((instr = *((uint32_t *)va)) == 0) 103 va += sizeof(int); 104 105 /* scan forwards to find stack size and any saved registers */ 106 stksize = 0; 107 more = 3; 108 mask = 0; 109 for (; more; va += sizeof(int), 110 more = (more == 3) ? 3 : more - 1) { 111 /* stop if hit our current position */ 112 if (va >= *pc) 113 break; 114 instr = *((uint32_t *)va); 115 i.word = instr; 116 switch (i.JType.op) { 117 case OP_SPECIAL: 118 switch (i.RType.func) { 119 case OP_JR: 120 case OP_JALR: 121 more = 2; /* stop after next instruction */ 122 break; 123 124 case OP_SYSCALL: 125 case OP_BREAK: 126 more = 1; /* stop now */ 127 }; 128 break; 129 130 case OP_BCOND: 131 case OP_J: 132 case OP_JAL: 133 case OP_BEQ: 134 case OP_BNE: 135 case OP_BLEZ: 136 case OP_BGTZ: 137 more = 2; /* stop after next instruction */ 138 break; 139 140 case OP_COP0: 141 case OP_COP1: 142 case OP_COP2: 143 case OP_COP3: 144 switch (i.RType.rs) { 145 case OP_BCx: 146 case OP_BCy: 147 more = 2; /* stop after next instruction */ 148 }; 149 break; 150 151 case OP_SW: 152 case OP_SD: 153 /* look for saved registers on the stack */ 154 if (i.IType.rs != 29) 155 break; 156 /* only restore the first one */ 157 if (mask & (1 << i.IType.rt)) 158 break; 159 mask |= (1 << i.IType.rt); 160 if (i.IType.rt == 31) 161 ra = *((register_t *)(*sp + (short)i.IType.imm)); 162 break; 163 164 case OP_ADDI: 165 case OP_ADDIU: 166 case OP_DADDI: 167 case OP_DADDIU: 168 /* look for stack pointer adjustment */ 169 if (i.IType.rs != 29 || i.IType.rt != 29) 170 break; 171 stksize = -((short)i.IType.imm); 172 } 173 } 174 175 if (!MIPS_IS_VALID_KERNELADDR(ra)) 176 return (-1); 177 178 *pc = ra; 179 *sp += stksize; 180 181 return (0); 182 183error: 184 return (-1); 185} 186 187static int 188pmc_next_uframe(register_t *pc, register_t *sp, register_t *ra) 189{ 190 int offset, registers_on_stack; 191 uint32_t opcode, mask; 192 register_t function_start; 193 int stksize; 194 InstFmt i; 195 196 registers_on_stack = 0; 197 mask = 0; 198 function_start = 0; 199 offset = 0; 200 stksize = 0; 201 202 while (offset < MAX_FUNCTION_SIZE) { 203 opcode = fuword32((void *)(*pc - offset)); 204 205 /* [d]addiu sp, sp, -X*/ 206 if (((opcode & 0xffff8000) == 0x27bd8000) 207 || ((opcode & 0xffff8000) == 0x67bd8000)) { 208 function_start = *pc - offset; 209 registers_on_stack = 1; 210 break; 211 } 212 213 /* lui gp, X */ 214 if ((opcode & 0xffff8000) == 0x3c1c0000) { 215 /* 216 * Function might start with this instruction 217 * Keep an eye on "jr ra" and sp correction 218 * with positive value further on 219 */ 220 function_start = *pc - offset; 221 } 222 223 if (function_start) { 224 /* 225 * Stop looking further. Possible end of 226 * function instruction: it means there is no 227 * stack modifications, sp is unchanged 228 */ 229 230 /* [d]addiu sp,sp,X */ 231 if (((opcode & 0xffff8000) == 0x27bd0000) 232 || ((opcode & 0xffff8000) == 0x67bd0000)) 233 break; 234 235 if (opcode == 0x03e00008) 236 break; 237 } 238 239 offset += sizeof(int); 240 } 241 242 if (!function_start) 243 return (-1); 244 245 if (registers_on_stack) { 246 offset = 0; 247 while ((offset < MAX_PROLOGUE_SIZE) 248 && ((function_start + offset) < *pc)) { 249 i.word = fuword32((void *)(function_start + offset)); 250 switch (i.JType.op) { 251 case OP_SW: 252 /* look for saved registers on the stack */ 253 if (i.IType.rs != 29) 254 break; 255 /* only restore the first one */ 256 if (mask & (1 << i.IType.rt)) 257 break; 258 mask |= (1 << i.IType.rt); 259 if (i.IType.rt == 31) 260 *ra = fuword32((void *)(*sp + (short)i.IType.imm)); 261 break; 262 263#if defined(__mips_n64) 264 case OP_SD: 265 /* look for saved registers on the stack */ 266 if (i.IType.rs != 29) 267 break; 268 /* only restore the first one */ 269 if (mask & (1 << i.IType.rt)) 270 break; 271 mask |= (1 << i.IType.rt); 272 /* ra */ 273 if (i.IType.rt == 31) 274 *ra = fuword64((void *)(*sp + (short)i.IType.imm)); 275 break; 276#endif 277 278 case OP_ADDI: 279 case OP_ADDIU: 280 case OP_DADDI: 281 case OP_DADDIU: 282 /* look for stack pointer adjustment */ 283 if (i.IType.rs != 29 || i.IType.rt != 29) 284 break; 285 stksize = -((short)i.IType.imm); 286 } 287 288 offset += sizeof(int); 289 } 290 } 291 292 /* 293 * We reached the end of backtrace 294 */ 295 if (*pc == *ra) 296 return (-1); 297 298 *pc = *ra; 299 *sp += stksize; 300 301 return (0); 302} 303 304struct pmc_mdep * 305pmc_md_initialize() 306{ 307 /* if (cpu_class == CPU_CLASS_MIPS24K)*/ 308 return pmc_mips24k_initialize(); 309 /* else 310 return NULL;*/ 311} 312 313void 314pmc_md_finalize(struct pmc_mdep *md) 315{ 316 /* if (cpu_class == CPU_CLASS_MIPS24K) */ 317 pmc_mips24k_finalize(md); 318 /* else 319 KASSERT(0, ("[mips,%d] Unknown CPU Class 0x%x", __LINE__, 320 cpu_class));*/ 321} 322 323int 324pmc_save_kernel_callchain(uintptr_t *cc, int nframes, 325 struct trapframe *tf) 326{ 327 register_t pc, ra, sp; 328 int frames = 0; 329 330 pc = (uint64_t)tf->pc; 331 sp = (uint64_t)tf->sp; 332 ra = (uint64_t)tf->ra; 333 334 /* 335 * Unwind, and unwind, and unwind 336 */ 337 while (1) { 338 cc[frames++] = pc; 339 if (frames >= nframes) 340 break; 341 342 if (pmc_next_frame(&pc, &sp) < 0) 343 break; 344 } 345 346 return (frames); 347} 348 349int 350pmc_save_user_callchain(uintptr_t *cc, int nframes, 351 struct trapframe *tf) 352{ 353 register_t pc, ra, sp; 354 int frames = 0; 355 356 pc = (uint64_t)tf->pc; 357 sp = (uint64_t)tf->sp; 358 ra = (uint64_t)tf->ra; 359 360 /* 361 * Unwind, and unwind, and unwind 362 */ 363 while (1) { 364 cc[frames++] = pc; 365 if (frames >= nframes) 366 break; 367 368 if (pmc_next_uframe(&pc, &sp, &ra) < 0) 369 break; 370 } 371 372 return (frames); 373} 374