linux/arch/parisc/kernel/unwind.c
<<
>>
Prefs
   1/*
   2 * Kernel unwinding support
   3 *
   4 * (c) 2002-2004 Randolph Chung <tausq@debian.org>
   5 *
   6 * Derived partially from the IA64 implementation. The PA-RISC
   7 * Runtime Architecture Document is also a useful reference to
   8 * understand what is happening here
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/sched.h>
  14#include <linux/slab.h>
  15#include <linux/kallsyms.h>
  16
  17#include <asm/uaccess.h>
  18#include <asm/assembly.h>
  19#include <asm/asm-offsets.h>
  20#include <asm/ptrace.h>
  21
  22#include <asm/unwind.h>
  23
  24/* #define DEBUG 1 */
  25#ifdef DEBUG
  26#define dbg(x...) printk(x)
  27#else
  28#define dbg(x...)
  29#endif
  30
  31#define KERNEL_START (KERNEL_BINARY_TEXT_START)
  32
  33extern struct unwind_table_entry __start___unwind[];
  34extern struct unwind_table_entry __stop___unwind[];
  35
  36static spinlock_t unwind_lock;
  37/*
  38 * the kernel unwind block is not dynamically allocated so that
  39 * we can call unwind_init as early in the bootup process as 
  40 * possible (before the slab allocator is initialized)
  41 */
  42static struct unwind_table kernel_unwind_table __read_mostly;
  43static LIST_HEAD(unwind_tables);
  44
  45static inline const struct unwind_table_entry *
  46find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
  47{
  48        const struct unwind_table_entry *e = NULL;
  49        unsigned long lo, hi, mid;
  50
  51        lo = 0; 
  52        hi = table->length - 1; 
  53        
  54        while (lo <= hi) {
  55                mid = (hi - lo) / 2 + lo;
  56                e = &table->table[mid];
  57                if (addr < e->region_start)
  58                        hi = mid - 1;
  59                else if (addr > e->region_end)
  60                        lo = mid + 1;
  61                else
  62                        return e;
  63        }
  64
  65        return NULL;
  66}
  67
  68static const struct unwind_table_entry *
  69find_unwind_entry(unsigned long addr)
  70{
  71        struct unwind_table *table;
  72        const struct unwind_table_entry *e = NULL;
  73
  74        if (addr >= kernel_unwind_table.start && 
  75            addr <= kernel_unwind_table.end)
  76                e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
  77        else 
  78                list_for_each_entry(table, &unwind_tables, list) {
  79                        if (addr >= table->start && 
  80                            addr <= table->end)
  81                                e = find_unwind_entry_in_table(table, addr);
  82                        if (e)
  83                                break;
  84                }
  85
  86        return e;
  87}
  88
  89static void
  90unwind_table_init(struct unwind_table *table, const char *name,
  91                  unsigned long base_addr, unsigned long gp,
  92                  void *table_start, void *table_end)
  93{
  94        struct unwind_table_entry *start = table_start;
  95        struct unwind_table_entry *end = 
  96                (struct unwind_table_entry *)table_end - 1;
  97
  98        table->name = name;
  99        table->base_addr = base_addr;
 100        table->gp = gp;
 101        table->start = base_addr + start->region_start;
 102        table->end = base_addr + end->region_end;
 103        table->table = (struct unwind_table_entry *)table_start;
 104        table->length = end - start + 1;
 105        INIT_LIST_HEAD(&table->list);
 106
 107        for (; start <= end; start++) {
 108                if (start < end && 
 109                    start->region_end > (start+1)->region_start) {
 110                        printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
 111                }
 112
 113                start->region_start += base_addr;
 114                start->region_end += base_addr;
 115        }
 116}
 117
 118static void
 119unwind_table_sort(struct unwind_table_entry *start,
 120                  struct unwind_table_entry *finish)
 121{
 122        struct unwind_table_entry el, *p, *q;
 123
 124        for (p = start + 1; p < finish; ++p) {
 125                if (p[0].region_start < p[-1].region_start) {
 126                        el = *p;
 127                        q = p;
 128                        do {
 129                                q[0] = q[-1];
 130                                --q;
 131                        } while (q > start && 
 132                                 el.region_start < q[-1].region_start);
 133                        *q = el;
 134                }
 135        }
 136}
 137
 138struct unwind_table *
 139unwind_table_add(const char *name, unsigned long base_addr, 
 140                 unsigned long gp,
 141                 void *start, void *end)
 142{
 143        struct unwind_table *table;
 144        unsigned long flags;
 145        struct unwind_table_entry *s = (struct unwind_table_entry *)start;
 146        struct unwind_table_entry *e = (struct unwind_table_entry *)end;
 147
 148        unwind_table_sort(s, e);
 149
 150        table = kmalloc(sizeof(struct unwind_table), GFP_USER);
 151        if (table == NULL)
 152                return NULL;
 153        unwind_table_init(table, name, base_addr, gp, start, end);
 154        spin_lock_irqsave(&unwind_lock, flags);
 155        list_add_tail(&table->list, &unwind_tables);
 156        spin_unlock_irqrestore(&unwind_lock, flags);
 157
 158        return table;
 159}
 160
 161void unwind_table_remove(struct unwind_table *table)
 162{
 163        unsigned long flags;
 164
 165        spin_lock_irqsave(&unwind_lock, flags);
 166        list_del(&table->list);
 167        spin_unlock_irqrestore(&unwind_lock, flags);
 168
 169        kfree(table);
 170}
 171
 172/* Called from setup_arch to import the kernel unwind info */
 173int unwind_init(void)
 174{
 175        long start, stop;
 176        register unsigned long gp __asm__ ("r27");
 177
 178        start = (long)&__start___unwind[0];
 179        stop = (long)&__stop___unwind[0];
 180
 181        spin_lock_init(&unwind_lock);
 182
 183        printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", 
 184            start, stop,
 185            (stop - start) / sizeof(struct unwind_table_entry));
 186
 187        unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
 188                          gp, 
 189                          &__start___unwind[0], &__stop___unwind[0]);
 190#if 0
 191        {
 192                int i;
 193                for (i = 0; i < 10; i++)
 194                {
 195                        printk("region 0x%x-0x%x\n", 
 196                                __start___unwind[i].region_start, 
 197                                __start___unwind[i].region_end);
 198                }
 199        }
 200#endif
 201        return 0;
 202}
 203
 204#ifdef CONFIG_64BIT
 205#define get_func_addr(fptr) fptr[2]
 206#else
 207#define get_func_addr(fptr) fptr[0]
 208#endif
 209
 210static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
 211{
 212        extern void handle_interruption(int, struct pt_regs *);
 213        static unsigned long *hi = (unsigned long *)&handle_interruption;
 214
 215        if (pc == get_func_addr(hi)) {
 216                struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
 217                dbg("Unwinding through handle_interruption()\n");
 218                info->prev_sp = regs->gr[30];
 219                info->prev_ip = regs->iaoq[0];
 220
 221                return 1;
 222        }
 223
 224        return 0;
 225}
 226
 227static void unwind_frame_regs(struct unwind_frame_info *info)
 228{
 229        const struct unwind_table_entry *e;
 230        unsigned long npc;
 231        unsigned int insn;
 232        long frame_size = 0;
 233        int looking_for_rp, rpoffset = 0;
 234
 235        e = find_unwind_entry(info->ip);
 236        if (e == NULL) {
 237                unsigned long sp;
 238                extern char _stext[], _etext[];
 239
 240                dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
 241
 242#ifdef CONFIG_KALLSYMS
 243                /* Handle some frequent special cases.... */
 244                {
 245                        char symname[KSYM_NAME_LEN];
 246                        char *modname;
 247
 248                        kallsyms_lookup(info->ip, NULL, NULL, &modname,
 249                                symname);
 250
 251                        dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
 252
 253                        if (strcmp(symname, "_switch_to_ret") == 0) {
 254                                info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
 255                                info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
 256                                dbg("_switch_to_ret @ %lx - setting "
 257                                    "prev_sp=%lx prev_ip=%lx\n", 
 258                                    info->ip, info->prev_sp, 
 259                                    info->prev_ip);
 260                                return;
 261                        } else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
 262                                   strcmp(symname, "syscall_exit") == 0) {
 263                                info->prev_ip = info->prev_sp = 0;
 264                                return;
 265                        }
 266                }
 267#endif
 268
 269                /* Since we are doing the unwinding blind, we don't know if
 270                   we are adjusting the stack correctly or extracting the rp
 271                   correctly. The rp is checked to see if it belongs to the
 272                   kernel text section, if not we assume we don't have a 
 273                   correct stack frame and we continue to unwind the stack.
 274                   This is not quite correct, and will fail for loadable
 275                   modules. */
 276                sp = info->sp & ~63;
 277                do {
 278                        unsigned long tmp;
 279
 280                        info->prev_sp = sp - 64;
 281                        info->prev_ip = 0;
 282                        if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
 283                                break;
 284                        info->prev_ip = tmp;
 285                        sp = info->prev_sp;
 286                } while (info->prev_ip < (unsigned long)_stext ||
 287                         info->prev_ip > (unsigned long)_etext);
 288
 289                info->rp = 0;
 290
 291                dbg("analyzing func @ %lx with no unwind info, setting "
 292                    "prev_sp=%lx prev_ip=%lx\n", info->ip, 
 293                    info->prev_sp, info->prev_ip);
 294        } else {
 295                dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
 296                    "Save_RP = %d, Millicode = %d size = %u\n", 
 297                    e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
 298                    e->Millicode, e->Total_frame_size);
 299
 300                looking_for_rp = e->Save_RP;
 301
 302                for (npc = e->region_start; 
 303                     (frame_size < (e->Total_frame_size << 3) || 
 304                      looking_for_rp) && 
 305                     npc < info->ip; 
 306                     npc += 4) {
 307
 308                        insn = *(unsigned int *)npc;
 309
 310                        if ((insn & 0xffffc000) == 0x37de0000 ||
 311                            (insn & 0xffe00000) == 0x6fc00000) {
 312                                /* ldo X(sp), sp, or stwm X,D(sp) */
 313                                frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
 314                                        ((insn & 0x3fff) >> 1);
 315                                dbg("analyzing func @ %lx, insn=%08x @ "
 316                                    "%lx, frame_size = %ld\n", info->ip,
 317                                    insn, npc, frame_size);
 318                        } else if ((insn & 0xffe00008) == 0x73c00008) {
 319                                /* std,ma X,D(sp) */
 320                                frame_size += (insn & 0x1 ? -1 << 13 : 0) | 
 321                                        (((insn >> 4) & 0x3ff) << 3);
 322                                dbg("analyzing func @ %lx, insn=%08x @ "
 323                                    "%lx, frame_size = %ld\n", info->ip,
 324                                    insn, npc, frame_size);
 325                        } else if (insn == 0x6bc23fd9) { 
 326                                /* stw rp,-20(sp) */
 327                                rpoffset = 20;
 328                                looking_for_rp = 0;
 329                                dbg("analyzing func @ %lx, insn=stw rp,"
 330                                    "-20(sp) @ %lx\n", info->ip, npc);
 331                        } else if (insn == 0x0fc212c1) {
 332                                /* std rp,-16(sr0,sp) */
 333                                rpoffset = 16;
 334                                looking_for_rp = 0;
 335                                dbg("analyzing func @ %lx, insn=std rp,"
 336                                    "-16(sp) @ %lx\n", info->ip, npc);
 337                        }
 338                }
 339
 340                if (!unwind_special(info, e->region_start, frame_size)) {
 341                        info->prev_sp = info->sp - frame_size;
 342                        if (e->Millicode)
 343                                info->rp = info->r31;
 344                        else if (rpoffset)
 345                                info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
 346                        info->prev_ip = info->rp;
 347                        info->rp = 0;
 348                }
 349
 350                dbg("analyzing func @ %lx, setting prev_sp=%lx "
 351                    "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
 352                    info->prev_ip, npc);
 353        }
 354}
 355
 356void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
 357                       struct pt_regs *regs)
 358{
 359        memset(info, 0, sizeof(struct unwind_frame_info));
 360        info->t = t;
 361        info->sp = regs->gr[30];
 362        info->ip = regs->iaoq[0];
 363        info->rp = regs->gr[2];
 364        info->r31 = regs->gr[31];
 365
 366        dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
 367            t ? (int)t->pid : -1, info->sp, info->ip);
 368}
 369
 370void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
 371{
 372        struct pt_regs *r = &t->thread.regs;
 373        struct pt_regs *r2;
 374
 375        r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
 376        if (!r2)
 377                return;
 378        *r2 = *r;
 379        r2->gr[30] = r->ksp;
 380        r2->iaoq[0] = r->kpc;
 381        unwind_frame_init(info, t, r2);
 382        kfree(r2);
 383}
 384
 385void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
 386{
 387        unwind_frame_init(info, current, regs);
 388}
 389
 390int unwind_once(struct unwind_frame_info *next_frame)
 391{
 392        unwind_frame_regs(next_frame);
 393
 394        if (next_frame->prev_sp == 0 ||
 395            next_frame->prev_ip == 0)
 396                return -1;
 397
 398        next_frame->sp = next_frame->prev_sp;
 399        next_frame->ip = next_frame->prev_ip;
 400        next_frame->prev_sp = 0;
 401        next_frame->prev_ip = 0;
 402
 403        dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
 404            next_frame->t ? (int)next_frame->t->pid : -1, 
 405            next_frame->sp, next_frame->ip);
 406
 407        return 0;
 408}
 409
 410int unwind_to_user(struct unwind_frame_info *info)
 411{
 412        int ret;
 413        
 414        do {
 415                ret = unwind_once(info);
 416        } while (!ret && !(info->ip & 3));
 417
 418        return ret;
 419}
 420