1/*- 2 * Copyright (c) 2003, 2004 Marcel Moolenaar 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 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include <sys/param.h> 31#include <sys/kdb.h> 32#include <sys/kernel.h> 33#include <sys/systm.h> 34#include <sys/malloc.h> 35#include <sys/queue.h> 36 37#include <machine/frame.h> 38#include <machine/md_var.h> 39#include <machine/pcb.h> 40#include <machine/unwind.h> 41 42#include <uwx.h> 43 44static MALLOC_DEFINE(M_UNWIND, "Unwind", "Unwind information"); 45 46struct unw_entry { 47 uint64_t ue_start; /* procedure start */ 48 uint64_t ue_end; /* procedure end */ 49 uint64_t ue_info; /* offset to procedure descriptors */ 50}; 51 52struct unw_table { 53 LIST_ENTRY(unw_table) ut_link; 54 uint64_t ut_base; 55 uint64_t ut_limit; 56 struct unw_entry *ut_start; 57 struct unw_entry *ut_end; 58}; 59 60LIST_HEAD(unw_table_list, unw_table); 61 62static struct unw_table_list unw_tables; 63 64#ifdef KDB 65#define KDBHEAPSZ 8192 66 67struct mhdr { 68 uint32_t sig; 69#define MSIG_FREE 0x65657246 /* "Free". */ 70#define MSIG_USED 0x64657355 /* "Used". */ 71 uint32_t size; 72 int32_t next; 73 int32_t prev; 74}; 75 76static struct mhdr *kdbheap; 77#endif /* KDB */ 78 79static void * 80unw_alloc(size_t sz) 81{ 82#ifdef KDB 83 struct mhdr *hdr, *hfree; 84 85 if (kdb_active) { 86 sz = (sz + 15) >> 4; 87 hdr = kdbheap; 88 while (hdr->sig != MSIG_FREE || hdr->size < sz) { 89 if (hdr->next == -1) 90 return (NULL); 91 hdr = kdbheap + hdr->next; 92 } 93 if (hdr->size > sz + 1) { 94 hfree = hdr + sz + 1; 95 hfree->sig = MSIG_FREE; 96 hfree->size = hdr->size - sz - 1; 97 hfree->prev = hdr - kdbheap; 98 hfree->next = hdr->next; 99 hdr->size = sz; 100 hdr->next = hfree - kdbheap; 101 if (hfree->next >= 0) { 102 hfree = kdbheap + hfree->next; 103 hfree->prev = hdr->next; 104 } 105 } 106 hdr->sig = MSIG_USED; 107 return (void*)(hdr + 1); 108 } 109#endif 110 return (malloc(sz, M_UNWIND, M_NOWAIT)); 111} 112 113static void 114unw_free(void *p) 115{ 116#ifdef KDB 117 struct mhdr *hdr, *hfree; 118 119 if (kdb_active) { 120 hdr = (struct mhdr*)p - 1; 121 if (hdr->sig != MSIG_USED) 122 return; 123 hdr->sig = MSIG_FREE; 124 if (hdr->prev >= 0 && kdbheap[hdr->prev].sig == MSIG_FREE) { 125 hfree = kdbheap + hdr->prev; 126 hfree->size += hdr->size + 1; 127 hfree->next = hdr->next; 128 if (hdr->next >= 0) { 129 hfree = kdbheap + hdr->next; 130 hfree->prev = hdr->prev; 131 } 132 } else if (hdr->next >= 0 && 133 kdbheap[hdr->next].sig == MSIG_FREE) { 134 hfree = kdbheap + hdr->next; 135 hdr->size += hfree->size + 1; 136 hdr->next = hfree->next; 137 if (hdr->next >= 0) { 138 hfree = kdbheap + hdr->next; 139 hfree->prev = hdr - kdbheap; 140 } 141 } 142 return; 143 } 144#endif 145 free(p, M_UNWIND); 146} 147 148static struct unw_table * 149unw_table_lookup(uint64_t ip) 150{ 151 struct unw_table *ut; 152 153 LIST_FOREACH(ut, &unw_tables, ut_link) { 154 if (ip >= ut->ut_base && ip < ut->ut_limit) 155 return (ut); 156 } 157 return (NULL); 158} 159 160static uint64_t 161unw_copyin_from_frame(struct trapframe *tf, uint64_t from) 162{ 163 uint64_t val; 164 int reg; 165 166 if (from == UWX_REG_AR_PFS) 167 val = tf->tf_special.pfs; 168 else if (from == UWX_REG_PREDS) 169 val = tf->tf_special.pr; 170 else if (from == UWX_REG_AR_RNAT) 171 val = tf->tf_special.rnat; 172 else if (from == UWX_REG_AR_UNAT) 173 val = tf->tf_special.unat; 174 else if (from >= UWX_REG_GR(0) && from <= UWX_REG_GR(127)) { 175 reg = from - UWX_REG_GR(0); 176 if (reg == 1) 177 val = tf->tf_special.gp; 178 else if (reg == 12) 179 val = tf->tf_special.sp; 180 else if (reg == 13) 181 val = tf->tf_special.tp; 182 else if (reg >= 2 && reg <= 3) 183 val = (&tf->tf_scratch.gr2)[reg - 2]; 184 else if (reg >= 8 && reg <= 11) 185 val = (&tf->tf_scratch.gr8)[reg - 8]; 186 else if (reg >= 14 && reg <= 31) 187 val = (&tf->tf_scratch.gr14)[reg - 14]; 188 else 189 goto oops; 190 } else if (from >= UWX_REG_BR(0) && from <= UWX_REG_BR(7)) { 191 reg = from - UWX_REG_BR(0); 192 if (reg == 0) 193 val = tf->tf_special.rp; 194 else if (reg >= 6 && reg <= 7) 195 val = (&tf->tf_scratch.br6)[reg - 6]; 196 else 197 goto oops; 198 } else 199 goto oops; 200 return (val); 201 202 oops: 203 printf("UNW: %s(%p, %lx)\n", __func__, tf, from); 204 return (0UL); 205} 206 207static uint64_t 208unw_copyin_from_pcb(struct pcb *pcb, uint64_t from) 209{ 210 uint64_t val; 211 int reg; 212 213 if (from == UWX_REG_AR_PFS) 214 val = pcb->pcb_special.pfs; 215 else if (from == UWX_REG_PREDS) 216 val = pcb->pcb_special.pr; 217 else if (from == UWX_REG_AR_RNAT) 218 val = pcb->pcb_special.rnat; 219 else if (from == UWX_REG_AR_UNAT) 220 val = pcb->pcb_special.unat; 221 else if (from >= UWX_REG_GR(0) && from <= UWX_REG_GR(127)) { 222 reg = from - UWX_REG_GR(0); 223 if (reg == 1) 224 val = pcb->pcb_special.gp; 225 else if (reg == 12) 226 val = pcb->pcb_special.sp; 227 else if (reg == 13) 228 val = pcb->pcb_special.tp; 229 else if (reg >= 4 && reg <= 7) 230 val = (&pcb->pcb_preserved.gr4)[reg - 4]; 231 else 232 goto oops; 233 } else if (from >= UWX_REG_BR(0) && from <= UWX_REG_BR(7)) { 234 reg = from - UWX_REG_BR(0); 235 if (reg == 0) 236 val = pcb->pcb_special.rp; 237 else if (reg >= 1 && reg <= 5) 238 val = (&pcb->pcb_preserved.br1)[reg - 1]; 239 else 240 goto oops; 241 } else 242 goto oops; 243 return (val); 244 245 oops: 246 printf("UNW: %s(%p, %lx)\n", __func__, pcb, from); 247 return (0UL); 248} 249 250static int 251unw_cb_copyin(int req, char *to, uint64_t from, int len, intptr_t tok) 252{ 253 struct unw_regstate *rs = (void*)tok; 254 uint64_t val; 255 256 switch (req) { 257 case UWX_COPYIN_UINFO: 258 break; 259 case UWX_COPYIN_MSTACK: 260 *((uint64_t*)to) = *((uint64_t*)from); 261 return (8); 262 case UWX_COPYIN_RSTACK: 263 *((uint64_t*)to) = *((uint64_t*)from); 264 return (8); 265 case UWX_COPYIN_REG: 266 if (rs->frame != NULL) 267 val = unw_copyin_from_frame(rs->frame, from); 268 else if (rs->pcb != NULL) 269 val = unw_copyin_from_pcb(rs->pcb, from); 270 else 271 goto oops; 272 *((uint64_t*)to) = val; 273 return (len); 274 } 275 276 oops: 277 printf("UNW: %s(%d, %p, %lx, %d, %lx)\n", __func__, req, to, from, 278 len, tok); 279 return (0); 280} 281 282static int 283unw_cb_lookup(int req, uint64_t ip, intptr_t tok, uint64_t **vec) 284{ 285 struct unw_regstate *rs = (void*)tok; 286 struct unw_table *ut; 287 288 switch (req) { 289 case UWX_LKUP_LOOKUP: 290 ut = unw_table_lookup(ip); 291 if (ut == NULL) 292 return (UWX_LKUP_NOTFOUND); 293 rs->keyval[0] = UWX_KEY_TBASE; 294 rs->keyval[1] = ut->ut_base; 295 rs->keyval[2] = UWX_KEY_USTART; 296 rs->keyval[3] = (intptr_t)ut->ut_start; 297 rs->keyval[4] = UWX_KEY_UEND; 298 rs->keyval[5] = (intptr_t)ut->ut_end; 299 rs->keyval[6] = 0; 300 rs->keyval[7] = 0; 301 *vec = rs->keyval; 302 return (UWX_LKUP_UTABLE); 303 case UWX_LKUP_FREE: 304 return (0); 305 } 306 307 return (UWX_LKUP_ERR); 308} 309 310int 311unw_create_from_frame(struct unw_regstate *rs, struct trapframe *tf) 312{ 313 uint64_t bsp, ip; 314 int uwxerr; 315 316 rs->frame = tf; 317 rs->pcb = NULL; 318 rs->env = uwx_init(); 319 if (rs->env == NULL) 320 return (ENOMEM); 321 322 uwxerr = uwx_register_callbacks(rs->env, (intptr_t)rs, 323 unw_cb_copyin, unw_cb_lookup); 324 if (uwxerr) 325 return (EINVAL); /* XXX */ 326 327 bsp = tf->tf_special.bspstore + tf->tf_special.ndirty; 328 bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOF(tf->tf_special.cfm)); 329 ip = tf->tf_special.iip + ((tf->tf_special.psr >> 41) & 3); 330 331 uwxerr = uwx_init_context(rs->env, ip, tf->tf_special.sp, bsp, 332 tf->tf_special.cfm); 333 334 return ((uwxerr) ? EINVAL : 0); /* XXX */ 335} 336 337int 338unw_create_from_pcb(struct unw_regstate *rs, struct pcb *pcb) 339{ 340 uint64_t bsp, cfm, ip; 341 int uwxerr; 342 343 rs->frame = NULL; 344 rs->pcb = pcb; 345 rs->env = uwx_init(); 346 if (rs->env == NULL) 347 return (ENOMEM); 348 349 uwxerr = uwx_register_callbacks(rs->env, (intptr_t)rs, 350 unw_cb_copyin, unw_cb_lookup); 351 if (uwxerr) 352 return (EINVAL); /* XXX */ 353 354 bsp = pcb->pcb_special.bspstore; 355 if (pcb->pcb_special.__spare == ~0UL) { 356 ip = pcb->pcb_special.iip + ((pcb->pcb_special.psr >> 41) & 3); 357 cfm = pcb->pcb_special.cfm; 358 bsp += pcb->pcb_special.ndirty; 359 bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOF(cfm)); 360 } else { 361 ip = pcb->pcb_special.rp; 362 cfm = pcb->pcb_special.pfs; 363 bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOL(cfm)); 364 } 365 uwxerr = uwx_init_context(rs->env, ip, pcb->pcb_special.sp, bsp, cfm); 366 367 return ((uwxerr) ? EINVAL : 0); /* XXX */ 368} 369 370void 371unw_delete(struct unw_regstate *rs) 372{ 373 374 if (rs->env != NULL) 375 uwx_free(rs->env); 376} 377 378int 379unw_step(struct unw_regstate *rs) 380{ 381 int err; 382 383 switch (uwx_step(rs->env)) { 384 case UWX_ABI_FRAME: 385 err = ERESTART; 386 break; 387 case UWX_BOTTOM: 388 err = EJUSTRETURN; 389 break; 390 case UWX_OK: 391 err = 0; 392 break; 393 default: 394 err = EINVAL; /* XXX */ 395 break; 396 } 397 return (err); 398} 399 400int 401unw_get_bsp(struct unw_regstate *s, uint64_t *r) 402{ 403 int uwxerr; 404 405 uwxerr = uwx_get_reg(s->env, UWX_REG_BSP, r); 406 return ((uwxerr) ? EINVAL : 0); /* XXX */ 407} 408 409int 410unw_get_cfm(struct unw_regstate *s, uint64_t *r) 411{ 412 int uwxerr; 413 414 uwxerr = uwx_get_reg(s->env, UWX_REG_CFM, r); 415 return ((uwxerr) ? EINVAL : 0); /* XXX */ 416} 417 418int 419unw_get_ip(struct unw_regstate *s, uint64_t *r) 420{ 421 int uwxerr; 422 423 uwxerr = uwx_get_reg(s->env, UWX_REG_IP, r); 424 return ((uwxerr) ? EINVAL : 0); /* XXX */ 425} 426 427int 428unw_get_sp(struct unw_regstate *s, uint64_t *r) 429{ 430 int uwxerr; 431 432 uwxerr = uwx_get_reg(s->env, UWX_REG_SP, r); 433 return ((uwxerr) ? EINVAL : 0); /* XXX */ 434} 435 436int 437unw_table_add(uint64_t base, uint64_t start, uint64_t end) 438{ 439 struct unw_table *ut; 440 441 ut = malloc(sizeof(struct unw_table), M_UNWIND, M_WAITOK); 442 ut->ut_base = base; 443 ut->ut_start = (struct unw_entry*)start; 444 ut->ut_end = (struct unw_entry*)end; 445 ut->ut_limit = base + ut->ut_end[-1].ue_end; 446 LIST_INSERT_HEAD(&unw_tables, ut, ut_link); 447 448 if (bootverbose) 449 printf("UNWIND: table added: base=%lx, start=%lx, end=%lx\n", 450 base, start, end); 451 452 return (0); 453} 454 455void 456unw_table_remove(uint64_t base) 457{ 458 struct unw_table *ut; 459 460 ut = unw_table_lookup(base); 461 if (ut != NULL) { 462 LIST_REMOVE(ut, ut_link); 463 free(ut, M_UNWIND); 464 if (bootverbose) 465 printf("UNWIND: table removed: base=%lx\n", base); 466 } 467} 468 469static void 470unw_initialize(void *dummy __unused) 471{ 472 473 LIST_INIT(&unw_tables); 474 uwx_register_alloc_cb(unw_alloc, unw_free); 475#ifdef KDB 476 kdbheap = malloc(KDBHEAPSZ, M_UNWIND, M_WAITOK); 477 kdbheap->sig = MSIG_FREE; 478 kdbheap->size = (KDBHEAPSZ - sizeof(struct mhdr)) >> 4; 479 kdbheap->next = -1; 480 kdbheap->prev = -1; 481#endif 482} 483SYSINIT(unwind, SI_SUB_KMEM, SI_ORDER_ANY, unw_initialize, 0); 484