]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/ia64/ia64/unwind.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / ia64 / ia64 / unwind.c
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
44 static MALLOC_DEFINE(M_UNWIND, "Unwind", "Unwind information");
45
46 struct 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
52 struct 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
60 LIST_HEAD(unw_table_list, unw_table);
61
62 static struct unw_table_list unw_tables;
63
64 #ifdef KDB
65 #define KDBHEAPSZ       8192
66
67 struct 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
76 static struct mhdr *kdbheap;
77 #endif /* KDB */
78
79 static void *
80 unw_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
113 static void
114 unw_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
148 static struct unw_table *
149 unw_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
160 static uint64_t
161 unw_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
207 static uint64_t
208 unw_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
250 static int
251 unw_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
282 static int
283 unw_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
310 int
311 unw_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
337 int
338 unw_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
370 void
371 unw_delete(struct unw_regstate *rs)
372 {
373
374         if (rs->env != NULL)
375                 uwx_free(rs->env);
376 }
377
378 int
379 unw_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
400 int
401 unw_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
409 int
410 unw_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
418 int
419 unw_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
427 int
428 unw_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
436 int
437 unw_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
455 void
456 unw_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
469 static void
470 unw_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 }
483 SYSINIT(unwind, SI_SUB_KMEM, SI_ORDER_ANY, unw_initialize, 0);