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