linux/arch/arm26/kernel/traps.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm26/kernel/traps.c
   3 *
   4 *  Copyright (C) 1995-2002 Russell King
   5 *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
   6 *  Copyright (C) 2003 Ian Molton (ARM26)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 *  'traps.c' handles hardware exceptions after we have saved some state in
  13 *  'linux/arch/arm26/lib/traps.S'.  Mostly a debugging aid, but will probably
  14 *  kill the offending process.
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/types.h>
  19#include <linux/kernel.h>
  20#include <linux/signal.h>
  21#include <linux/sched.h>
  22#include <linux/mm.h>
  23#include <linux/spinlock.h>
  24#include <linux/personality.h>
  25#include <linux/ptrace.h>
  26#include <linux/elf.h>
  27#include <linux/interrupt.h>
  28#include <linux/init.h>
  29
  30#include <asm/atomic.h>
  31#include <asm/io.h>
  32#include <asm/pgtable.h>
  33#include <asm/system.h>
  34#include <asm/uaccess.h>
  35#include <asm/unistd.h>
  36#include <linux/mutex.h>
  37
  38#include "ptrace.h"
  39
  40extern void c_backtrace (unsigned long fp, int pmode);
  41extern void show_pte(struct mm_struct *mm, unsigned long addr);
  42
  43const char *processor_modes[] = { "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" };
  44
  45static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" "*bad reason*"};
  46
  47/*
  48 * Stack pointers should always be within the kernels view of
  49 * physical memory.  If it is not there, then we can't dump
  50 * out any information relating to the stack.
  51 */
  52static int verify_stack(unsigned long sp)
  53{
  54        if (sp < PAGE_OFFSET || (sp > (unsigned long)high_memory && high_memory != 0))
  55                return -EFAULT;
  56
  57        return 0;
  58}
  59
  60/*
  61 * Dump out the contents of some memory nicely...
  62 */
  63static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
  64{
  65        unsigned long p = bottom & ~31;
  66        mm_segment_t fs;
  67        int i;
  68
  69        /*
  70         * We need to switch to kernel mode so that we can use __get_user
  71         * to safely read from kernel space.  Note that we now dump the
  72         * code first, just in case the backtrace kills us.
  73         */
  74        fs = get_fs();
  75        set_fs(KERNEL_DS);
  76
  77        printk("%s", str);
  78        printk("(0x%08lx to 0x%08lx)\n", bottom, top);
  79
  80        for (p = bottom & ~31; p < top;) {
  81                printk("%04lx: ", p & 0xffff);
  82
  83                for (i = 0; i < 8; i++, p += 4) {
  84                        unsigned int val;
  85
  86                        if (p < bottom || p >= top)
  87                                printk("         ");
  88                        else {
  89                                __get_user(val, (unsigned long *)p);
  90                                printk("%08x ", val);
  91                        }
  92                }
  93                printk ("\n");
  94        }
  95
  96        set_fs(fs);
  97}
  98
  99static void dump_instr(struct pt_regs *regs)
 100{
 101        unsigned long addr = instruction_pointer(regs);
 102        const int width = 8;
 103        mm_segment_t fs;
 104        int i;
 105
 106        /*
 107         * We need to switch to kernel mode so that we can use __get_user
 108         * to safely read from kernel space.  Note that we now dump the
 109         * code first, just in case the backtrace kills us.
 110         */
 111        fs = get_fs();
 112        set_fs(KERNEL_DS);
 113
 114        printk("Code: ");
 115        for (i = -4; i < 1; i++) {
 116                unsigned int val, bad;
 117
 118                bad = __get_user(val, &((u32 *)addr)[i]);
 119
 120                if (!bad)
 121                        printk(i == 0 ? "(%0*x) " : "%0*x ", width, val);
 122                else {
 123                        printk("bad PC value.");
 124                        break;
 125                }
 126        }
 127        printk("\n");
 128
 129        set_fs(fs);
 130}
 131
 132/*static*/ void __dump_stack(struct task_struct *tsk, unsigned long sp)
 133{
 134        dump_mem("Stack: ", sp, 8192+(unsigned long)task_stack_page(tsk));
 135}
 136
 137void dump_stack(void)
 138{
 139#ifdef CONFIG_DEBUG_ERRORS
 140        __backtrace();
 141#endif
 142}
 143
 144EXPORT_SYMBOL(dump_stack);
 145
 146//FIXME - was a static fn
 147void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
 148{
 149        unsigned int fp;
 150        int ok = 1;
 151
 152        printk("Backtrace: ");
 153        fp = regs->ARM_fp;
 154        if (!fp) {
 155                printk("no frame pointer");
 156                ok = 0;
 157        } else if (verify_stack(fp)) {
 158                printk("invalid frame pointer 0x%08x", fp);
 159                ok = 0;
 160        } else if (fp < (unsigned long)end_of_stack(tsk))
 161                printk("frame pointer underflow");
 162        printk("\n");
 163
 164        if (ok)
 165                c_backtrace(fp, processor_mode(regs));
 166}
 167
 168/* FIXME - this is probably wrong.. */
 169void show_stack(struct task_struct *task, unsigned long *sp) {
 170        dump_mem("Stack: ", (unsigned long)sp, 8192+(unsigned long)task_stack_page(task));
 171}
 172
 173DEFINE_SPINLOCK(die_lock);
 174
 175/*
 176 * This function is protected against re-entrancy.
 177 */
 178NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
 179{
 180        struct task_struct *tsk = current;
 181
 182        console_verbose();
 183        spin_lock_irq(&die_lock);
 184
 185        printk("Internal error: %s: %x\n", str, err);
 186        printk("CPU: %d\n", smp_processor_id());
 187        show_regs(regs);
 188        printk("Process %s (pid: %d, stack limit = 0x%p)\n",
 189                current->comm, current->pid, end_of_stack(tsk));
 190
 191        if (!user_mode(regs) || in_interrupt()) {
 192                __dump_stack(tsk, (unsigned long)(regs + 1));
 193                dump_backtrace(regs, tsk);
 194                dump_instr(regs);
 195        }
 196while(1);
 197        spin_unlock_irq(&die_lock);
 198        do_exit(SIGSEGV);
 199}
 200
 201void die_if_kernel(const char *str, struct pt_regs *regs, int err)
 202{
 203        if (user_mode(regs))
 204                return;
 205
 206        die(str, regs, err);
 207}
 208
 209static DEFINE_MUTEX(undef_mutex);
 210static int (*undef_hook)(struct pt_regs *);
 211
 212int request_undef_hook(int (*fn)(struct pt_regs *))
 213{
 214        int ret = -EBUSY;
 215
 216        mutex_lock(&undef_mutex);
 217        if (undef_hook == NULL) {
 218                undef_hook = fn;
 219                ret = 0;
 220        }
 221        mutex_unlock(&undef_mutex);
 222
 223        return ret;
 224}
 225
 226int release_undef_hook(int (*fn)(struct pt_regs *))
 227{
 228        int ret = -EINVAL;
 229
 230        mutex_lock(&undef_mutex);
 231        if (undef_hook == fn) {
 232                undef_hook = NULL;
 233                ret = 0;
 234        }
 235        mutex_unlock(&undef_mutex);
 236
 237        return ret;
 238}
 239
 240static int undefined_extension(struct pt_regs *regs, unsigned int op)
 241{
 242        switch (op) {
 243        case 1: /* 0xde01 / 0x?7f001f0 */
 244                ptrace_break(current, regs);
 245                return 0;
 246        }
 247        return 1;
 248}
 249
 250asmlinkage void do_undefinstr(struct pt_regs *regs)
 251{
 252        siginfo_t info;
 253        void *pc;
 254
 255        regs->ARM_pc -= 4;
 256
 257        pc = (unsigned long *)instruction_pointer(regs); /* strip PSR */
 258
 259        if (user_mode(regs)) {
 260                u32 instr;
 261
 262                get_user(instr, (u32 *)pc);
 263
 264                if ((instr & 0x0fff00ff) == 0x07f000f0 &&
 265                    undefined_extension(regs, (instr >> 8) & 255) == 0) {
 266                        regs->ARM_pc += 4;
 267                        return;
 268                }
 269        } else {
 270                if (undef_hook && undef_hook(regs) == 0) {
 271                        regs->ARM_pc += 4;
 272                        return;
 273                }
 274        }
 275
 276#ifdef CONFIG_DEBUG_USER
 277        printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
 278                current->comm, current->pid, pc);
 279        dump_instr(regs);
 280#endif
 281
 282        current->thread.error_code = 0;
 283        current->thread.trap_no = 6;
 284
 285        info.si_signo = SIGILL;
 286        info.si_errno = 0;
 287        info.si_code  = ILL_ILLOPC;
 288        info.si_addr  = pc;
 289
 290        force_sig_info(SIGILL, &info, current);
 291
 292        die_if_kernel("Oops - undefined instruction", regs, 0);
 293}
 294
 295asmlinkage void do_excpt(unsigned long address, struct pt_regs *regs, int mode)
 296{
 297        siginfo_t info;
 298
 299#ifdef CONFIG_DEBUG_USER
 300        printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
 301                current->comm, current->pid, instruction_pointer(regs));
 302        dump_instr(regs);
 303#endif
 304
 305        current->thread.error_code = 0;
 306        current->thread.trap_no = 11;
 307
 308        info.si_signo = SIGBUS;
 309        info.si_errno = 0;
 310        info.si_code  = BUS_ADRERR;
 311        info.si_addr  = (void *)address;
 312
 313        force_sig_info(SIGBUS, &info, current);
 314
 315        die_if_kernel("Oops - address exception", regs, mode);
 316}
 317
 318asmlinkage void do_unexp_fiq (struct pt_regs *regs)
 319{
 320#ifndef CONFIG_IGNORE_FIQ
 321        printk("Hmm.  Unexpected FIQ received, but trying to continue\n");
 322        printk("You may have a hardware problem...\n");
 323#endif
 324}
 325
 326/*
 327 * bad_mode handles the impossible case in the vectors.  If you see one of
 328 * these, then it's extremely serious, and could mean you have buggy hardware.
 329 * It never returns, and never tries to sync.  We hope that we can at least
 330 * dump out some state information...
 331 */
 332asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
 333{
 334        unsigned int vectors = vectors_base();
 335
 336        console_verbose();
 337
 338        printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
 339                handler[reason<5?reason:4], processor_modes[proc_mode]);
 340
 341        /*
 342         * Dump out the vectors and stub routines.  Maybe a better solution
 343         * would be to dump them out only if we detect that they are corrupted.
 344         */
 345        dump_mem(KERN_CRIT "Vectors: ", vectors, vectors + 0x40);
 346        dump_mem(KERN_CRIT "Stubs: ", vectors + 0x200, vectors + 0x4b8);
 347
 348        die("Oops", regs, 0);
 349        local_irq_disable();
 350        panic("bad mode");
 351}
 352
 353static int bad_syscall(int n, struct pt_regs *regs)
 354{
 355        struct thread_info *thread = current_thread_info();
 356        siginfo_t info;
 357
 358        if (current->personality != PER_LINUX && thread->exec_domain->handler) {
 359                thread->exec_domain->handler(n, regs);
 360                return regs->ARM_r0;
 361        }
 362
 363#ifdef CONFIG_DEBUG_USER
 364        printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
 365                current->pid, current->comm, n);
 366        dump_instr(regs);
 367#endif
 368
 369        info.si_signo = SIGILL;
 370        info.si_errno = 0;
 371        info.si_code  = ILL_ILLTRP;
 372        info.si_addr  = (void *)instruction_pointer(regs) - 4;
 373
 374        force_sig_info(SIGILL, &info, current);
 375        die_if_kernel("Oops", regs, n);
 376        return regs->ARM_r0;
 377}
 378
 379static inline void
 380do_cache_op(unsigned long start, unsigned long end, int flags)
 381{
 382        struct vm_area_struct *vma;
 383
 384        if (end < start)
 385                return;
 386
 387        vma = find_vma(current->active_mm, start);
 388        if (vma && vma->vm_start < end) {
 389                if (start < vma->vm_start)
 390                        start = vma->vm_start;
 391                if (end > vma->vm_end)
 392                        end = vma->vm_end;
 393        }
 394}
 395
 396/*
 397 * Handle all unrecognised system calls.
 398 *  0x9f0000 - 0x9fffff are some more esoteric system calls
 399 */
 400#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
 401asmlinkage int arm_syscall(int no, struct pt_regs *regs)
 402{
 403        siginfo_t info;
 404
 405        if ((no >> 16) != 0x9f)
 406                return bad_syscall(no, regs);
 407
 408        switch (no & 0xffff) {
 409        case 0: /* branch through 0 */
 410                info.si_signo = SIGSEGV;
 411                info.si_errno = 0;
 412                info.si_code  = SEGV_MAPERR;
 413                info.si_addr  = NULL;
 414
 415                force_sig_info(SIGSEGV, &info, current);
 416
 417                die_if_kernel("branch through zero", regs, 0);
 418                return 0;
 419
 420        case NR(breakpoint): /* SWI BREAK_POINT */
 421                ptrace_break(current, regs);
 422                return regs->ARM_r0;
 423
 424        case NR(cacheflush):
 425                return 0;
 426
 427        case NR(usr26):
 428                break;
 429
 430        default:
 431                /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
 432                   if not implemented, rather than raising SIGILL.  This
 433                   way the calling program can gracefully determine whether
 434                   a feature is supported.  */
 435                if (no <= 0x7ff)
 436                        return -ENOSYS;
 437                break;
 438        }
 439#ifdef CONFIG_DEBUG_USER
 440        /*
 441         * experience shows that these seem to indicate that
 442         * something catastrophic has happened
 443         */
 444        printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
 445        dump_instr(regs);
 446        if (user_mode(regs)) {
 447                show_regs(regs);
 448                c_backtrace(regs->ARM_fp, processor_mode(regs));
 449        }
 450#endif
 451        info.si_signo = SIGILL;
 452        info.si_errno = 0;
 453        info.si_code  = ILL_ILLTRP;
 454        info.si_addr  = (void *)instruction_pointer(regs) - 4;
 455
 456        force_sig_info(SIGILL, &info, current);
 457        die_if_kernel("Oops", regs, no);
 458        return 0;
 459}
 460
 461void __bad_xchg(volatile void *ptr, int size)
 462{
 463        printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
 464                __builtin_return_address(0), ptr, size);
 465        BUG();
 466}
 467
 468/*
 469 * A data abort trap was taken, but we did not handle the instruction.
 470 * Try to abort the user program, or panic if it was the kernel.
 471 */
 472asmlinkage void
 473baddataabort(int code, unsigned long instr, struct pt_regs *regs)
 474{
 475        unsigned long addr = instruction_pointer(regs);
 476        siginfo_t info;
 477
 478#ifdef CONFIG_DEBUG_USER
 479        printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n",
 480                current->pid, current->comm, code, instr);
 481        dump_instr(regs);
 482        show_pte(current->mm, addr);
 483#endif
 484
 485        info.si_signo = SIGILL;
 486        info.si_errno = 0;
 487        info.si_code  = ILL_ILLOPC;
 488        info.si_addr  = (void *)addr;
 489
 490        force_sig_info(SIGILL, &info, current);
 491        die_if_kernel("unknown data abort code", regs, instr);
 492}
 493
 494volatile void __bug(const char *file, int line, void *data)
 495{
 496        printk(KERN_CRIT"kernel BUG at %s:%d!", file, line);
 497        if (data)
 498                printk(KERN_CRIT" - extra data = %p", data);
 499        printk("\n");
 500        *(int *)0 = 0;
 501}
 502
 503void __readwrite_bug(const char *fn)
 504{
 505        printk("%s called, but not implemented", fn);
 506        BUG();
 507}
 508
 509void __pte_error(const char *file, int line, unsigned long val)
 510{
 511        printk("%s:%d: bad pte %08lx.\n", file, line, val);
 512}
 513
 514void __pmd_error(const char *file, int line, unsigned long val)
 515{
 516        printk("%s:%d: bad pmd %08lx.\n", file, line, val);
 517}
 518
 519void __pgd_error(const char *file, int line, unsigned long val)
 520{
 521        printk("%s:%d: bad pgd %08lx.\n", file, line, val);
 522}
 523
 524asmlinkage void __div0(void)
 525{
 526        printk("Division by zero in kernel.\n");
 527        dump_stack();
 528}
 529
 530void abort(void)
 531{
 532        BUG();
 533
 534        /* if that doesn't kill us, halt */
 535        panic("Oops failed to kill thread");
 536}
 537
 538void __init trap_init(void)
 539{
 540        extern void __trap_init(unsigned long);
 541        unsigned long base = vectors_base();
 542
 543        __trap_init(base);
 544        if (base != 0)
 545                printk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n",
 546                        base);
 547}
 548
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.