1139790Simp/*- 2131945Smarcel * Copyright (c) 2003, 2004 Marcel Moolenaar 385685Sdfr * All rights reserved. 485685Sdfr * 585685Sdfr * Redistribution and use in source and binary forms, with or without 685685Sdfr * modification, are permitted provided that the following conditions 785685Sdfr * are met: 8117267Smarcel * 985685Sdfr * 1. Redistributions of source code must retain the above copyright 1085685Sdfr * notice, this list of conditions and the following disclaimer. 1185685Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1285685Sdfr * notice, this list of conditions and the following disclaimer in the 1385685Sdfr * documentation and/or other materials provided with the distribution. 1485685Sdfr * 15117267Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16117267Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17117267Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18117267Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19117267Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20117267Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21117267Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22117267Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23117267Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24117267Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2585685Sdfr */ 2685685Sdfr 27117267Smarcel#include <sys/cdefs.h> 28117267Smarcel__FBSDID("$FreeBSD$"); 29117267Smarcel 3085685Sdfr#include <sys/param.h> 31131945Smarcel#include <sys/kdb.h> 3285685Sdfr#include <sys/kernel.h> 3385685Sdfr#include <sys/systm.h> 3485685Sdfr#include <sys/malloc.h> 3585685Sdfr#include <sys/queue.h> 3685685Sdfr 3796912Smarcel#include <machine/frame.h> 38131945Smarcel#include <machine/md_var.h> 39131945Smarcel#include <machine/pcb.h> 4085685Sdfr#include <machine/unwind.h> 4185685Sdfr 42115084Smarcel#include <uwx.h> 4385685Sdfr 44249132Smavstatic MALLOC_DEFINE(M_UNWIND, "Unwind", "Unwind information"); 4585685Sdfr 46115084Smarcelstruct unw_entry { 47115084Smarcel uint64_t ue_start; /* procedure start */ 48115084Smarcel uint64_t ue_end; /* procedure end */ 49115084Smarcel uint64_t ue_info; /* offset to procedure descriptors */ 5085685Sdfr}; 5185685Sdfr 52115084Smarcelstruct unw_table { 53115084Smarcel LIST_ENTRY(unw_table) ut_link; 54115084Smarcel uint64_t ut_base; 55115084Smarcel uint64_t ut_limit; 56115084Smarcel struct unw_entry *ut_start; 57115084Smarcel struct unw_entry *ut_end; 5885685Sdfr}; 5985685Sdfr 60115084SmarcelLIST_HEAD(unw_table_list, unw_table); 6185685Sdfr 62115084Smarcelstatic struct unw_table_list unw_tables; 6385685Sdfr 64131945Smarcel#ifdef KDB 65131945Smarcel#define KDBHEAPSZ 8192 66117267Smarcel 67117267Smarcelstruct mhdr { 68117267Smarcel uint32_t sig; 69117267Smarcel#define MSIG_FREE 0x65657246 /* "Free". */ 70117267Smarcel#define MSIG_USED 0x64657355 /* "Used". */ 71117267Smarcel uint32_t size; 72117267Smarcel int32_t next; 73117267Smarcel int32_t prev; 74117267Smarcel}; 75117267Smarcel 76131945Smarcelstatic struct mhdr *kdbheap; 77131945Smarcel#endif /* KDB */ 78117267Smarcel 79115084Smarcelstatic void * 80115084Smarcelunw_alloc(size_t sz) 8185685Sdfr{ 82131945Smarcel#ifdef KDB 83117267Smarcel struct mhdr *hdr, *hfree; 8485685Sdfr 85131945Smarcel if (kdb_active) { 86117267Smarcel sz = (sz + 15) >> 4; 87131945Smarcel hdr = kdbheap; 88117267Smarcel while (hdr->sig != MSIG_FREE || hdr->size < sz) { 89117267Smarcel if (hdr->next == -1) 90117267Smarcel return (NULL); 91131945Smarcel hdr = kdbheap + hdr->next; 92117267Smarcel } 93117267Smarcel if (hdr->size > sz + 1) { 94117267Smarcel hfree = hdr + sz + 1; 95117267Smarcel hfree->sig = MSIG_FREE; 96117267Smarcel hfree->size = hdr->size - sz - 1; 97131945Smarcel hfree->prev = hdr - kdbheap; 98117267Smarcel hfree->next = hdr->next; 99117267Smarcel hdr->size = sz; 100131945Smarcel hdr->next = hfree - kdbheap; 101117267Smarcel if (hfree->next >= 0) { 102131945Smarcel hfree = kdbheap + hfree->next; 103117267Smarcel hfree->prev = hdr->next; 104117267Smarcel } 105117267Smarcel } 106117267Smarcel hdr->sig = MSIG_USED; 107117267Smarcel return (void*)(hdr + 1); 108117267Smarcel } 109117267Smarcel#endif 110133711Smarcel return (malloc(sz, M_UNWIND, M_NOWAIT)); 11185685Sdfr} 11285685Sdfr 113115084Smarcelstatic void 114115084Smarcelunw_free(void *p) 11585685Sdfr{ 116131945Smarcel#ifdef KDB 117117267Smarcel struct mhdr *hdr, *hfree; 11885685Sdfr 119131945Smarcel if (kdb_active) { 120117267Smarcel hdr = (struct mhdr*)p - 1; 121117267Smarcel if (hdr->sig != MSIG_USED) 122117267Smarcel return; 123117267Smarcel hdr->sig = MSIG_FREE; 124131945Smarcel if (hdr->prev >= 0 && kdbheap[hdr->prev].sig == MSIG_FREE) { 125131945Smarcel hfree = kdbheap + hdr->prev; 126117267Smarcel hfree->size += hdr->size + 1; 127117267Smarcel hfree->next = hdr->next; 128117267Smarcel if (hdr->next >= 0) { 129131945Smarcel hfree = kdbheap + hdr->next; 130117267Smarcel hfree->prev = hdr->prev; 131117267Smarcel } 132117267Smarcel } else if (hdr->next >= 0 && 133131945Smarcel kdbheap[hdr->next].sig == MSIG_FREE) { 134131945Smarcel hfree = kdbheap + hdr->next; 135117267Smarcel hdr->size += hfree->size + 1; 136117267Smarcel hdr->next = hfree->next; 137117267Smarcel if (hdr->next >= 0) { 138131945Smarcel hfree = kdbheap + hdr->next; 139131945Smarcel hfree->prev = hdr - kdbheap; 140117267Smarcel } 141117267Smarcel } 142117267Smarcel return; 143117267Smarcel } 144117267Smarcel#endif 145115084Smarcel free(p, M_UNWIND); 14685685Sdfr} 14785685Sdfr 148115084Smarcelstatic struct unw_table * 149115084Smarcelunw_table_lookup(uint64_t ip) 150105470Smarcel{ 151115084Smarcel struct unw_table *ut; 152105470Smarcel 153115084Smarcel LIST_FOREACH(ut, &unw_tables, ut_link) { 154115084Smarcel if (ip >= ut->ut_base && ip < ut->ut_limit) 155115084Smarcel return (ut); 156105470Smarcel } 157115084Smarcel return (NULL); 158105470Smarcel} 159105470Smarcel 160131945Smarcelstatic uint64_t 161131945Smarcelunw_copyin_from_frame(struct trapframe *tf, uint64_t from) 162131945Smarcel{ 163131945Smarcel uint64_t val; 164131945Smarcel int reg; 165131945Smarcel 166131945Smarcel if (from == UWX_REG_AR_PFS) 167131945Smarcel val = tf->tf_special.pfs; 168131945Smarcel else if (from == UWX_REG_PREDS) 169131945Smarcel val = tf->tf_special.pr; 170131945Smarcel else if (from == UWX_REG_AR_RNAT) 171131945Smarcel val = tf->tf_special.rnat; 172131945Smarcel else if (from == UWX_REG_AR_UNAT) 173131945Smarcel val = tf->tf_special.unat; 174131945Smarcel else if (from >= UWX_REG_GR(0) && from <= UWX_REG_GR(127)) { 175131945Smarcel reg = from - UWX_REG_GR(0); 176131945Smarcel if (reg == 1) 177131945Smarcel val = tf->tf_special.gp; 178131945Smarcel else if (reg == 12) 179131945Smarcel val = tf->tf_special.sp; 180131945Smarcel else if (reg == 13) 181131945Smarcel val = tf->tf_special.tp; 182131945Smarcel else if (reg >= 2 && reg <= 3) 183131945Smarcel val = (&tf->tf_scratch.gr2)[reg - 2]; 184131945Smarcel else if (reg >= 8 && reg <= 11) 185131945Smarcel val = (&tf->tf_scratch.gr8)[reg - 8]; 186131945Smarcel else if (reg >= 14 && reg <= 31) 187131945Smarcel val = (&tf->tf_scratch.gr14)[reg - 14]; 188131945Smarcel else 189131945Smarcel goto oops; 190131945Smarcel } else if (from >= UWX_REG_BR(0) && from <= UWX_REG_BR(7)) { 191131945Smarcel reg = from - UWX_REG_BR(0); 192131945Smarcel if (reg == 0) 193131945Smarcel val = tf->tf_special.rp; 194131945Smarcel else if (reg >= 6 && reg <= 7) 195131945Smarcel val = (&tf->tf_scratch.br6)[reg - 6]; 196131945Smarcel else 197131945Smarcel goto oops; 198131945Smarcel } else 199131945Smarcel goto oops; 200131945Smarcel return (val); 201131945Smarcel 202131945Smarcel oops: 203131945Smarcel printf("UNW: %s(%p, %lx)\n", __func__, tf, from); 204131945Smarcel return (0UL); 205131945Smarcel} 206131945Smarcel 207131945Smarcelstatic uint64_t 208131945Smarcelunw_copyin_from_pcb(struct pcb *pcb, uint64_t from) 209131945Smarcel{ 210131945Smarcel uint64_t val; 211131945Smarcel int reg; 212131945Smarcel 213131945Smarcel if (from == UWX_REG_AR_PFS) 214131945Smarcel val = pcb->pcb_special.pfs; 215131945Smarcel else if (from == UWX_REG_PREDS) 216131945Smarcel val = pcb->pcb_special.pr; 217131945Smarcel else if (from == UWX_REG_AR_RNAT) 218131945Smarcel val = pcb->pcb_special.rnat; 219131945Smarcel else if (from == UWX_REG_AR_UNAT) 220131945Smarcel val = pcb->pcb_special.unat; 221131945Smarcel else if (from >= UWX_REG_GR(0) && from <= UWX_REG_GR(127)) { 222131945Smarcel reg = from - UWX_REG_GR(0); 223131945Smarcel if (reg == 1) 224131945Smarcel val = pcb->pcb_special.gp; 225131945Smarcel else if (reg == 12) 226131945Smarcel val = pcb->pcb_special.sp; 227131945Smarcel else if (reg == 13) 228131945Smarcel val = pcb->pcb_special.tp; 229131945Smarcel else if (reg >= 4 && reg <= 7) 230131945Smarcel val = (&pcb->pcb_preserved.gr4)[reg - 4]; 231131945Smarcel else 232131945Smarcel goto oops; 233131945Smarcel } else if (from >= UWX_REG_BR(0) && from <= UWX_REG_BR(7)) { 234131945Smarcel reg = from - UWX_REG_BR(0); 235131945Smarcel if (reg == 0) 236131945Smarcel val = pcb->pcb_special.rp; 237131945Smarcel else if (reg >= 1 && reg <= 5) 238131945Smarcel val = (&pcb->pcb_preserved.br1)[reg - 1]; 239131945Smarcel else 240131945Smarcel goto oops; 241131945Smarcel } else 242131945Smarcel goto oops; 243131945Smarcel return (val); 244131945Smarcel 245131945Smarcel oops: 246131945Smarcel printf("UNW: %s(%p, %lx)\n", __func__, pcb, from); 247131945Smarcel return (0UL); 248131945Smarcel} 249131945Smarcel 250115084Smarcelstatic int 251115084Smarcelunw_cb_copyin(int req, char *to, uint64_t from, int len, intptr_t tok) 25285685Sdfr{ 253115084Smarcel struct unw_regstate *rs = (void*)tok; 254131945Smarcel uint64_t val; 25585685Sdfr 256115084Smarcel switch (req) { 257115084Smarcel case UWX_COPYIN_UINFO: 258115084Smarcel break; 259115084Smarcel case UWX_COPYIN_MSTACK: 260115084Smarcel *((uint64_t*)to) = *((uint64_t*)from); 261115084Smarcel return (8); 262115084Smarcel case UWX_COPYIN_RSTACK: 263115084Smarcel *((uint64_t*)to) = *((uint64_t*)from); 264115084Smarcel return (8); 265115084Smarcel case UWX_COPYIN_REG: 266131945Smarcel if (rs->frame != NULL) 267131945Smarcel val = unw_copyin_from_frame(rs->frame, from); 268131945Smarcel else if (rs->pcb != NULL) 269131945Smarcel val = unw_copyin_from_pcb(rs->pcb, from); 270131945Smarcel else 271115084Smarcel goto oops; 272131945Smarcel *((uint64_t*)to) = val; 273115084Smarcel return (len); 27485865Sdfr } 27585685Sdfr 276115084Smarcel oops: 277115084Smarcel printf("UNW: %s(%d, %p, %lx, %d, %lx)\n", __func__, req, to, from, 278115084Smarcel len, tok); 279115084Smarcel return (0); 28085685Sdfr} 28185685Sdfr 282115084Smarcelstatic int 283115084Smarcelunw_cb_lookup(int req, uint64_t ip, intptr_t tok, uint64_t **vec) 28485685Sdfr{ 285115084Smarcel struct unw_regstate *rs = (void*)tok; 286115084Smarcel struct unw_table *ut; 28785685Sdfr 288115084Smarcel switch (req) { 289115084Smarcel case UWX_LKUP_LOOKUP: 290115084Smarcel ut = unw_table_lookup(ip); 291115084Smarcel if (ut == NULL) 292115084Smarcel return (UWX_LKUP_NOTFOUND); 293115084Smarcel rs->keyval[0] = UWX_KEY_TBASE; 294115084Smarcel rs->keyval[1] = ut->ut_base; 295115084Smarcel rs->keyval[2] = UWX_KEY_USTART; 296115084Smarcel rs->keyval[3] = (intptr_t)ut->ut_start; 297115084Smarcel rs->keyval[4] = UWX_KEY_UEND; 298115084Smarcel rs->keyval[5] = (intptr_t)ut->ut_end; 299115084Smarcel rs->keyval[6] = 0; 300115084Smarcel rs->keyval[7] = 0; 301115084Smarcel *vec = rs->keyval; 302115084Smarcel return (UWX_LKUP_UTABLE); 303115084Smarcel case UWX_LKUP_FREE: 304115084Smarcel return (0); 30585685Sdfr } 30685685Sdfr 307115084Smarcel return (UWX_LKUP_ERR); 30885685Sdfr} 30985685Sdfr 310115084Smarcelint 311131945Smarcelunw_create_from_frame(struct unw_regstate *rs, struct trapframe *tf) 31285685Sdfr{ 313131945Smarcel uint64_t bsp, ip; 314131945Smarcel int uwxerr; 31585685Sdfr 316115084Smarcel rs->frame = tf; 317131945Smarcel rs->pcb = NULL; 318115084Smarcel rs->env = uwx_init(); 319115084Smarcel if (rs->env == NULL) 320115084Smarcel return (ENOMEM); 32185685Sdfr 322115084Smarcel uwxerr = uwx_register_callbacks(rs->env, (intptr_t)rs, 323115084Smarcel unw_cb_copyin, unw_cb_lookup); 324115084Smarcel if (uwxerr) 325115084Smarcel return (EINVAL); /* XXX */ 32685685Sdfr 327115084Smarcel bsp = tf->tf_special.bspstore + tf->tf_special.ndirty; 328131945Smarcel bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOF(tf->tf_special.cfm)); 329131945Smarcel ip = tf->tf_special.iip + ((tf->tf_special.psr >> 41) & 3); 33085685Sdfr 331131945Smarcel uwxerr = uwx_init_context(rs->env, ip, tf->tf_special.sp, bsp, 332131945Smarcel tf->tf_special.cfm); 333131945Smarcel 334115084Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 33585685Sdfr} 33685685Sdfr 337131945Smarcelint 338131945Smarcelunw_create_from_pcb(struct unw_regstate *rs, struct pcb *pcb) 339131945Smarcel{ 340131945Smarcel uint64_t bsp, cfm, ip; 341131945Smarcel int uwxerr; 342131945Smarcel 343131945Smarcel rs->frame = NULL; 344131945Smarcel rs->pcb = pcb; 345131945Smarcel rs->env = uwx_init(); 346131945Smarcel if (rs->env == NULL) 347131945Smarcel return (ENOMEM); 348131945Smarcel 349131945Smarcel uwxerr = uwx_register_callbacks(rs->env, (intptr_t)rs, 350131945Smarcel unw_cb_copyin, unw_cb_lookup); 351131945Smarcel if (uwxerr) 352131945Smarcel return (EINVAL); /* XXX */ 353131945Smarcel 354131945Smarcel bsp = pcb->pcb_special.bspstore; 355131945Smarcel if (pcb->pcb_special.__spare == ~0UL) { 356131945Smarcel ip = pcb->pcb_special.iip + ((pcb->pcb_special.psr >> 41) & 3); 357131945Smarcel cfm = pcb->pcb_special.cfm; 358131945Smarcel bsp += pcb->pcb_special.ndirty; 359131945Smarcel bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOF(cfm)); 360131945Smarcel } else { 361131945Smarcel ip = pcb->pcb_special.rp; 362131945Smarcel cfm = pcb->pcb_special.pfs; 363131945Smarcel bsp = ia64_bsp_adjust(bsp, -IA64_CFM_SOL(cfm)); 364131945Smarcel } 365131945Smarcel uwxerr = uwx_init_context(rs->env, ip, pcb->pcb_special.sp, bsp, cfm); 366131945Smarcel 367131945Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 368131945Smarcel} 369131945Smarcel 370117267Smarcelvoid 371117267Smarcelunw_delete(struct unw_regstate *rs) 372117267Smarcel{ 373117267Smarcel 374117467Smarcel if (rs->env != NULL) 375117467Smarcel uwx_free(rs->env); 376117267Smarcel} 377117267Smarcel 378115084Smarcelint 379115084Smarcelunw_step(struct unw_regstate *rs) 38085685Sdfr{ 381117467Smarcel int err; 38285685Sdfr 383145137Smarcel switch (uwx_step(rs->env)) { 384145137Smarcel case UWX_ABI_FRAME: 385145137Smarcel err = ERESTART; 386145137Smarcel break; 387145137Smarcel case UWX_BOTTOM: 388145137Smarcel err = EJUSTRETURN; 389145137Smarcel break; 390145137Smarcel case UWX_OK: 391145137Smarcel err = 0; 392145137Smarcel break; 393145137Smarcel default: 394145137Smarcel err = EINVAL; /* XXX */ 395145137Smarcel break; 396145137Smarcel } 397145137Smarcel return (err); 39885685Sdfr} 39985685Sdfr 400115084Smarcelint 401115084Smarcelunw_get_bsp(struct unw_regstate *s, uint64_t *r) 40285685Sdfr{ 403115084Smarcel int uwxerr; 40485685Sdfr 405115084Smarcel uwxerr = uwx_get_reg(s->env, UWX_REG_BSP, r); 406115084Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 40785685Sdfr} 40885685Sdfr 409115084Smarcelint 410115084Smarcelunw_get_cfm(struct unw_regstate *s, uint64_t *r) 41185685Sdfr{ 412115084Smarcel int uwxerr; 41385685Sdfr 414115084Smarcel uwxerr = uwx_get_reg(s->env, UWX_REG_CFM, r); 415115084Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 41685685Sdfr} 41785685Sdfr 418115084Smarcelint 419115084Smarcelunw_get_ip(struct unw_regstate *s, uint64_t *r) 42085685Sdfr{ 421115084Smarcel int uwxerr; 42285685Sdfr 423115084Smarcel uwxerr = uwx_get_reg(s->env, UWX_REG_IP, r); 424115084Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 42585685Sdfr} 42685685Sdfr 427115084Smarcelint 428117467Smarcelunw_get_sp(struct unw_regstate *s, uint64_t *r) 429117467Smarcel{ 430117467Smarcel int uwxerr; 431117467Smarcel 432117467Smarcel uwxerr = uwx_get_reg(s->env, UWX_REG_SP, r); 433117467Smarcel return ((uwxerr) ? EINVAL : 0); /* XXX */ 434117467Smarcel} 435117467Smarcel 436117467Smarcelint 437115084Smarcelunw_table_add(uint64_t base, uint64_t start, uint64_t end) 43885685Sdfr{ 439115084Smarcel struct unw_table *ut; 44085685Sdfr 441117267Smarcel ut = malloc(sizeof(struct unw_table), M_UNWIND, M_WAITOK); 442115084Smarcel ut->ut_base = base; 443115084Smarcel ut->ut_start = (struct unw_entry*)start; 444115084Smarcel ut->ut_end = (struct unw_entry*)end; 445115084Smarcel ut->ut_limit = base + ut->ut_end[-1].ue_end; 446115084Smarcel LIST_INSERT_HEAD(&unw_tables, ut, ut_link); 44785685Sdfr 448115084Smarcel if (bootverbose) 449115084Smarcel printf("UNWIND: table added: base=%lx, start=%lx, end=%lx\n", 450115084Smarcel base, start, end); 45185685Sdfr 452115084Smarcel return (0); 45385685Sdfr} 45485685Sdfr 455115084Smarcelvoid 456115084Smarcelunw_table_remove(uint64_t base) 45785685Sdfr{ 458115084Smarcel struct unw_table *ut; 45985685Sdfr 460115084Smarcel ut = unw_table_lookup(base); 461115084Smarcel if (ut != NULL) { 462115084Smarcel LIST_REMOVE(ut, ut_link); 463115084Smarcel free(ut, M_UNWIND); 464115084Smarcel if (bootverbose) 465115084Smarcel printf("UNWIND: table removed: base=%lx\n", base); 46685685Sdfr } 46785685Sdfr} 46885685Sdfr 46985685Sdfrstatic void 470115084Smarcelunw_initialize(void *dummy __unused) 47185685Sdfr{ 47285685Sdfr 473115084Smarcel LIST_INIT(&unw_tables); 474115084Smarcel uwx_register_alloc_cb(unw_alloc, unw_free); 475131945Smarcel#ifdef KDB 476131945Smarcel kdbheap = malloc(KDBHEAPSZ, M_UNWIND, M_WAITOK); 477131945Smarcel kdbheap->sig = MSIG_FREE; 478131945Smarcel kdbheap->size = (KDBHEAPSZ - sizeof(struct mhdr)) >> 4; 479131945Smarcel kdbheap->next = -1; 480131945Smarcel kdbheap->prev = -1; 481117267Smarcel#endif 48285685Sdfr} 483115084SmarcelSYSINIT(unwind, SI_SUB_KMEM, SI_ORDER_ANY, unw_initialize, 0); 484