#include <linux/kallsyms.h>
#include <linux/ptrace.h>
#include <linux/version.h>
+#include <linux/kprobes.h>
#ifdef CONFIG_EISA
#include <linux/ioport.h>
#include <asm/nmi.h>
#include <asm/smp.h>
-#include <asm/pgalloc.h>
#include <asm/arch_hooks.h>
+#include <asm/kdebug.h>
#include <linux/irq.h>
#include <linux/module.h>
asmlinkage void machine_check(void);
static int kstack_depth_to_print = 24;
+struct notifier_block *i386die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
-void show_trace(struct task_struct *task, unsigned long * stack)
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&i386die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+ return err;
+}
+
+static int valid_stack_ptr(struct task_struct *task, void *p)
+{
+ if (p <= (void *)task->thread_info)
+ return 0;
+ if (kstack_end(p))
+ return 0;
+ return 1;
+}
+
+#ifdef CONFIG_FRAME_POINTER
+static void print_context_stack(struct task_struct *task, unsigned long *stack,
+ unsigned long ebp)
{
unsigned long addr;
- if (!stack)
- stack = (unsigned long*)&stack;
+ while (valid_stack_ptr(task, (void *)ebp)) {
+ addr = *(unsigned long *)(ebp + 4);
+ printk(" [<%08lx>] ", addr);
+ print_symbol("%s", addr);
+ printk("\n");
+ ebp = *(unsigned long *)ebp;
+ }
+}
+#else
+static void print_context_stack(struct task_struct *task, unsigned long *stack,
+ unsigned long ebp)
+{
+ unsigned long addr;
- printk("Call Trace:");
-#ifdef CONFIG_KALLSYMS
- printk("\n");
+ while (!kstack_end(stack)) {
+ addr = *stack++;
+ if (__kernel_text_address(addr)) {
+ printk(" [<%08lx>]", addr);
+ print_symbol(" %s", addr);
+ printk("\n");
+ }
+ }
+}
#endif
+
+void show_trace(struct task_struct *task, unsigned long * stack)
+{
+ unsigned long ebp;
+
+ if (!task)
+ task = current;
+
+ if (!valid_stack_ptr(task, stack)) {
+ printk("Stack pointer is garbage, not printing trace\n");
+ return;
+ }
+
+ if (task == current) {
+ /* Grab ebp right from our regs */
+ asm ("movl %%ebp, %0" : "=r" (ebp) : );
+ } else {
+ /* ebp is the last reg pushed by switch_to */
+ ebp = *(unsigned long *) task->thread.esp;
+ }
+
while (1) {
struct thread_info *context;
- context = (struct thread_info*) ((unsigned long)stack & (~(THREAD_SIZE - 1)));
- while (!kstack_end(stack)) {
- addr = *stack++;
- if (kernel_text_address(addr)) {
- printk(" [<%08lx>] ", addr);
- print_symbol("%s\n", addr);
- }
- }
+ context = (struct thread_info *)
+ ((unsigned long)stack & (~(THREAD_SIZE - 1)));
+ print_context_stack(task, stack, ebp);
stack = (unsigned long*)context->previous_esp;
if (!stack)
break;
printk(" =======================\n");
}
- printk("\n");
}
void show_stack(struct task_struct *task, unsigned long *esp)
printk("\n ");
printk("%08lx ", *stack++);
}
- printk("\n");
+ printk("\nCall Trace:\n");
show_trace(task, esp);
}
ss = regs->xss & 0xffff;
}
print_modules();
- printk("CPU: %d\nEIP: %04x:[<%08lx>] %s\nEFLAGS: %08lx"
+ printk("CPU: %d\nEIP: %04x:[<%08lx>] %s VLI\nEFLAGS: %08lx"
" (%s) \n",
smp_processor_id(), 0xffff & regs->xcs, regs->eip,
print_tainted(), regs->eflags, UTS_RELEASE);
* time of the fault..
*/
if (in_kernel) {
+ u8 *eip;
printk("\nStack: ");
show_stack(NULL, (unsigned long*)esp);
printk("Code: ");
- if(regs->eip < PAGE_OFFSET)
- goto bad;
- for(i=0;i<20;i++)
- {
+ eip = (u8 *)regs->eip - 43;
+ for (i = 0; i < 64; i++, eip++) {
unsigned char c;
- if(__get_user(c, &((unsigned char*)regs->eip)[i])) {
-bad:
+
+ if (eip < (u8 *)PAGE_OFFSET || __get_user(c, eip)) {
printk(" Bad EIP value.");
break;
}
- printk("%02x ", c);
+ if (eip == (u8 *)regs->eip)
+ printk("<%02x> ", c);
+ else
+ printk("%02x ", c);
}
}
printk("\n");
file = "<bad filename>";
printk("------------[ cut here ]------------\n");
- printk("kernel BUG at %s:%d!\n", file, line);
+ printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
no_bug:
return;
printk("Kernel BUG\n");
}
-spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
-
void die(const char * str, struct pt_regs * regs, long err)
{
+ static struct {
+ spinlock_t lock;
+ u32 lock_owner;
+ int lock_owner_depth;
+ } die = {
+ .lock = SPIN_LOCK_UNLOCKED,
+ .lock_owner = -1,
+ .lock_owner_depth = 0
+ };
static int die_counter;
- int nl = 0;
- console_verbose();
- spin_lock_irq(&die_lock);
- bust_spinlocks(1);
- handle_BUG(regs);
- printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
+ if (die.lock_owner != smp_processor_id()) {
+ console_verbose();
+ spin_lock_irq(&die.lock);
+ die.lock_owner = smp_processor_id();
+ die.lock_owner_depth = 0;
+ bust_spinlocks(1);
+ }
+
+ if (++die.lock_owner_depth < 3) {
+ int nl = 0;
+ handle_BUG(regs);
+ printk(KERN_ALERT "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
#ifdef CONFIG_PREEMPT
- printk("PREEMPT ");
- nl = 1;
+ printk("PREEMPT ");
+ nl = 1;
#endif
#ifdef CONFIG_SMP
- printk("SMP ");
- nl = 1;
+ printk("SMP ");
+ nl = 1;
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
- printk("DEBUG_PAGEALLOC");
- nl = 1;
+ printk("DEBUG_PAGEALLOC");
+ nl = 1;
#endif
- if (nl)
- printk("\n");
- show_registers(regs);
+ if (nl)
+ printk("\n");
+ notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
+ show_registers(regs);
+ } else
+ printk(KERN_ERR "Recursive die() failure, output suppressed\n");
+
bust_spinlocks(0);
- spin_unlock_irq(&die_lock);
+ die.lock_owner = -1;
+ spin_unlock_irq(&die.lock);
if (in_interrupt())
panic("Fatal exception in interrupt");
#define DO_ERROR(trapnr, signr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
}
info.si_signo = signr; \
info.si_errno = 0; \
info.si_code = sicode; \
- info.si_addr = (void *)siaddr; \
+ info.si_addr = (void __user *)siaddr; \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
do_trap(trapnr, signr, str, 0, regs, error_code, &info); \
}
#define DO_VM86_ERROR(trapnr, signr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \
}
info.si_signo = signr; \
info.si_errno = 0; \
info.si_code = sicode; \
- info.si_addr = (void *)siaddr; \
+ info.si_addr = (void __user *)siaddr; \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
}
DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip)
+#ifndef CONFIG_KPROBES
DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
+#endif
DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
-DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2())
+DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
- if (regs->eflags & X86_EFLAGS_IF)
- local_irq_enable();
-
+ int cpu = get_cpu();
+ struct tss_struct *tss = &per_cpu(init_tss, cpu);
+ struct thread_struct *thread = ¤t->thread;
+
+ /*
+ * Perform the lazy TSS's I/O bitmap copy. If the TSS has an
+ * invalid offset set (the LAZY one) and the faulting thread has
+ * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS
+ * and we set the offset field correctly. Then we let the CPU to
+ * restart the faulting instruction.
+ */
+ if (tss->io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY &&
+ thread->io_bitmap_ptr) {
+ memcpy(tss->io_bitmap, thread->io_bitmap_ptr,
+ thread->io_bitmap_max);
+ /*
+ * If the previously set map was extending to higher ports
+ * than the current one, pad extra space with 0xff (no access).
+ */
+ if (thread->io_bitmap_max < tss->io_bitmap_max)
+ memset((char *) tss->io_bitmap +
+ thread->io_bitmap_max, 0xff,
+ tss->io_bitmap_max - thread->io_bitmap_max);
+ tss->io_bitmap_max = thread->io_bitmap_max;
+ tss->io_bitmap_base = IO_BITMAP_OFFSET;
+ put_cpu();
+ return;
+ }
+ put_cpu();
+
if (regs->eflags & VM_MASK)
goto gp_in_vm86;
return;
gp_in_kernel:
- if (!fixup_exception(regs))
+ if (!fixup_exception(regs)) {
+ if (notify_die(DIE_GPF, "general protection fault", regs,
+ error_code, 13, SIGSEGV) == NOTIFY_STOP);
+ return;
die("general protection fault", regs, error_code);
+ }
}
static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
printk("Do you have a strange power saving mode enabled?\n");
}
+static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED;
+
+void die_nmi (struct pt_regs *regs, const char *msg)
+{
+ spin_lock(&nmi_print_lock);
+ /*
+ * We are in trouble anyway, lets at least try
+ * to get a message out.
+ */
+ bust_spinlocks(1);
+ printk(msg);
+ printk(" on CPU%d, eip %08lx, registers:\n",
+ smp_processor_id(), regs->eip);
+ show_registers(regs);
+ printk("console shuts up ...\n");
+ console_silent();
+ spin_unlock(&nmi_print_lock);
+ bust_spinlocks(0);
+ do_exit(SIGSEGV);
+}
+
static void default_do_nmi(struct pt_regs * regs)
{
unsigned char reason = get_nmi_reason();
if (!(reason & 0xc0)) {
+ if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT)
+ == NOTIFY_STOP)
+ return;
#ifdef CONFIG_X86_LOCAL_APIC
/*
* Ok, so this is none of the documented NMI sources,
unknown_nmi_error(reason, regs);
return;
}
+ if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_STOP)
+ return;
if (reason & 0x80)
mem_parity_error(reason, regs);
if (reason & 0x40)
nmi_callback = dummy_nmi_callback;
}
+#ifdef CONFIG_KPROBES
+asmlinkage int do_int3(struct pt_regs *regs, long error_code)
+{
+ if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
+ == NOTIFY_STOP)
+ return 1;
+ /* This is an interrupt gate, because kprobes wants interrupts
+ disabled. Normal trap handlers don't. */
+ restore_interrupts(regs);
+ do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
+ return 0;
+}
+#endif
+
/*
* Our handling of the processor debug registers is non-trivial.
* We do not clear them on entry and exit from the kernel. Therefore
__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
+ if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
+ SIGTRAP) == NOTIFY_STOP)
+ return;
/* It's safe to allow irq's after DR6 has been saved */
if (regs->eflags & X86_EFLAGS_IF)
local_irq_enable();
/* If this is a kernel mode trap, save the user PC on entry to
* the kernel, that's what the debugger can make sense of.
*/
- info.si_addr = ((regs->xcs & 3) == 0) ? (void *)tsk->thread.eip :
- (void *)regs->eip;
+ info.si_addr = ((regs->xcs & 3) == 0) ? (void __user *)tsk->thread.eip
+ : (void __user *)regs->eip;
force_sig_info(SIGTRAP, &info, tsk);
/* Disable additional traps. They'll be re-enabled when
* the correct behaviour even in the presence of the asynchronous
* IRQ13 behaviour
*/
-void math_error(void *eip)
+void math_error(void __user *eip)
{
struct task_struct * task;
siginfo_t info;
asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
{
ignore_fpu_irq = 1;
- math_error((void *)regs->eip);
+ math_error((void __user *)regs->eip);
}
-void simd_math_error(void *eip)
+void simd_math_error(void __user *eip)
{
struct task_struct * task;
siginfo_t info;
if (cpu_has_xmm) {
/* Handle SIMD FPU exceptions on PIII+ processors. */
ignore_fpu_irq = 1;
- simd_math_error((void *)regs->eip);
+ simd_math_error((void __user *)regs->eip);
} else {
/*
* Handle strange cache flush from user space exception
_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}
+/*
+ * This routine sets up an interrupt gate at directory privilege level 3.
+ */
+static inline void set_system_intr_gate(unsigned int n, void *addr)
+{
+ _set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
+}
+
static void __init set_trap_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
set_trap_gate(0,÷_error);
set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
- set_system_gate(3,&int3); /* int3-5 can be called from all */
+ set_system_intr_gate(3, &int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);