patch-2.1.80 linux/arch/arm/kernel/traps.c

Next file: linux/arch/arm/lib/Makefile
Previous file: linux/arch/arm/kernel/time.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.79/linux/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c
@@ -0,0 +1,306 @@
+/*
+ *  linux/arch/arm/kernel/traps.c
+ *
+ *  Copyright (C) 1995, 1996 Russell King
+ *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
+ */
+
+/*
+ * 'traps.c' handles hardware exceptions after we have saved some state in
+ * 'linux/arch/arm/lib/traps.S'.  Mostly a debugging aid, but will probably
+ * kill the offending process.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/pgtable.h>
+
+extern void fpe_save(struct fp_soft_struct *);
+extern void fpe_restore(struct fp_soft_struct *);
+extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
+extern void c_backtrace (unsigned long fp, int pmode);
+extern int ptrace_cancel_bpt (struct task_struct *);
+
+char *processor_modes[]=
+{ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
+  "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
+  "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
+  "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
+};
+
+static char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
+
+static inline void console_verbose(void)
+{
+	extern int console_loglevel;
+	console_loglevel = 15;
+}
+
+int kstack_depth_to_print = 200;
+
+static int verify_stack_pointer (unsigned long stackptr, int size)
+{
+#if defined(CONFIG_CPU_ARM2) || defined(CONFIG_CPU_ARM3)
+	if (stackptr < 0x02048000 || stackptr + size > 0x03000000)
+        	return -EFAULT;
+#else
+	if (stackptr < 0xc0000000 || stackptr + size > (unsigned long)high_memory)
+		return -EFAULT;
+#endif
+		return 0;
+}
+
+static void dump_stack (unsigned long *start, unsigned long *end, int offset, int max)
+{
+	unsigned long *p;
+	int i;
+
+	for (p = start + offset, i = 0; i < max && p < end; i++, p++) {
+		if (i && (i & 7) == 0)
+			printk ("\n       ");
+		printk ("%08lx ", *p);
+	}
+	printk ("\n");
+}
+
+/*
+ * These constants are for searching for possible module text
+ * segments.  VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
+ * a guess of how much space is likely to be vmalloced.
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define MODULE_RANGE (8*1024*1024)
+
+static void dump_instr (unsigned long pc)
+{
+	unsigned long module_start, module_end;
+	int pmin = -2, pmax = 3, ok = 0;
+	extern char start_kernel, _etext;
+
+	module_start = VMALLOC_START;
+	module_end   = module_start + MODULE_RANGE;
+
+	if ((pc >= (unsigned long) &start_kernel) &&
+	    (pc <= (unsigned long) &_etext)) {
+		if (pc + pmin < (unsigned long) &start_kernel)
+			pmin = ((unsigned long) &start_kernel) - pc;
+		if (pc + pmax > (unsigned long) &_etext)
+			pmax = ((unsigned long) &_etext) - pc;
+		ok = 1;
+	} else if (pc >= module_start && pc <= module_end) {
+		if (pc + pmin < module_start)
+			pmin = module_start - pc;
+		if (pc + pmax > module_end)
+			pmax = module_end - pc;
+		ok = 1;
+	}
+	printk ("Code: ");
+	if (ok) {
+		int i;
+		for (i = pmin; i < pmax; i++)
+			printk("%08lx ", ((unsigned long *)pc)[i]);
+		printk ("\n");
+	} else
+		printk ("pc not in code space\n");
+}	
+
+/*
+ * This function is protected against kernel-mode re-entrancy.  If it
+ * is re-entered it will hang the system since we can't guarantee in
+ * this case that any of the functions that it calls are safe any more.
+ * Even the panic function could be a problem, but we'll give it a go.
+ */
+void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
+{
+	static int died = 0;
+	unsigned long cstack, sstack, frameptr;
+	
+	if (user_mode(regs))
+    		return;
+
+	switch (died) {
+	case 2:
+		while (1);
+	case 1:
+		died ++;
+		panic ("die_if_kernel re-entered.  Major kernel corruption.  Please reboot me!");
+		break;
+	case 0:
+		died ++;
+		break;
+	}
+
+	console_verbose ();
+	printk ("Internal error: %s: %x\n", str, err);
+	printk ("CPU: %d", smp_processor_id());
+	show_regs (regs);
+	printk ("Process %s (pid: %d, stackpage=%08lx)\nStack: ",
+		current->comm, current->pid, 4096+(unsigned long)current);
+
+	cstack = (unsigned long)(regs + 1);
+	sstack = 4096+(unsigned long)current;
+
+	if (*(unsigned long *)sstack != STACK_MAGIC)
+		printk ("*** corrupted stack page\n       ");
+
+	if (verify_stack_pointer (cstack, 4))
+		printk ("%08lx invalid kernel stack pointer\n", cstack);
+	else if(cstack > sstack + 4096)
+		printk("(sp overflow)\n");
+	else if(cstack < sstack)
+		printk("(sp underflow)\n");
+	else
+		dump_stack ((unsigned long *)sstack, (unsigned long *)sstack + 1024,
+			cstack - sstack, kstack_depth_to_print);
+
+	frameptr = regs->ARM_fp;
+	if (frameptr) {
+		if (verify_stack_pointer (frameptr, 4))
+			printk ("Backtrace: invalid frame pointer\n");
+		else {
+			printk("Backtrace: \n");
+			c_backtrace (frameptr, processor_mode(regs));
+		}
+	}
+
+	dump_instr (instruction_pointer(regs));
+	died = 0;
+	if (ret != -1)
+		do_exit (ret);
+	else {
+		cli ();
+		while (1);
+	}
+}
+
+void bad_user_access_alignment (const void *ptr)
+{
+	void *pc;
+	__asm__("mov %0, lr\n": "=r" (pc));
+	printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc);
+	current->tss.error_code = 0;
+	current->tss.trap_no = 11;
+	force_sig (SIGBUS, current);
+/*	die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/
+}
+
+asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
+{
+	current->tss.error_code = 0;
+	current->tss.trap_no = 6;
+	force_sig (SIGILL, current);
+	die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
+}
+
+asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
+{
+	current->tss.error_code = 0;
+	current->tss.trap_no = 11;
+	force_sig (SIGBUS, current);
+	die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
+}
+
+asmlinkage void do_unexp_fiq (struct pt_regs *regs)
+{
+#ifndef CONFIG_IGNORE_FIQ
+	printk ("Hmm.  Unexpected FIQ received, but trying to continue\n");
+	printk ("You may have a hardware problem...\n");
+#endif
+}
+
+asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
+{
+	printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
+		handler[reason],
+		processor_modes[proc_mode]);
+	die_if_kernel ("Oops", regs, 0, -1);
+}
+
+/*
+ * 'math_state_restore()' saves the current math information in the
+ * old math state array, and gets the new ones from the current task.
+ *
+ * We no longer save/restore the math state on every context switch
+ * any more.  We only do this now if it actually gets used.
+ */
+asmlinkage void math_state_restore (void)
+{
+	if (last_task_used_math == current)
+		return;
+	if (last_task_used_math)
+		/*
+		 * Save current fp state into last_task_used_math->tss.fpe_save
+		 */
+		fpe_save (&last_task_used_math->tss.fpstate.soft);
+	last_task_used_math = current;
+	if (current->used_math) {
+	    	/*
+    		 * Restore current fp state from current->tss.fpe_save
+	    	 */
+    		fpe_restore (&current->tss.fpstate.soft);
+	} else {
+    		/*
+	    	 * initialise fp state
+    		 */
+	    	fpe_restore (&init_task.tss.fpstate.soft);
+	    	current->used_math = 1;
+	}
+}
+
+asmlinkage void arm_syscall (int no, struct pt_regs *regs)
+{
+	switch (no) {
+	case 0: /* branch through 0 */
+		printk ("[%d] %s: branch through zero\n", current->pid, current->comm);
+		force_sig (SIGILL, current);
+		if (user_mode(regs)) {
+			show_regs (regs);
+			c_backtrace (regs->ARM_fp, processor_mode(regs));
+		}
+		die_if_kernel ("Oops", regs, 0, SIGILL);
+		break;
+
+	case 1: /* SWI_BREAK_POINT */
+		regs->ARM_pc -= 4; /* Decrement PC by one instruction */
+		ptrace_cancel_bpt (current);
+		force_sig (SIGTRAP, current);
+		break;
+
+	default:
+		printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
+		force_sig (SIGILL, current);
+		if (user_mode(regs)) {
+			show_regs (regs);
+			c_backtrace (regs->ARM_fp, processor_mode(regs));
+		}
+		die_if_kernel ("Oops", regs, no, SIGILL);
+		break;
+	}
+}
+
+asmlinkage void deferred(int n, struct pt_regs *regs)
+{
+	printk ("[%d] %s: old system call %X\n", current->pid, current->comm, n);
+	show_regs (regs);
+	force_sig (SIGILL, current);
+}
+
+asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
+{
+	printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
+}
+
+asmlinkage void arm_invalidptr (const char *function, int size)
+{
+	printk ("Invalid pointer size in %s (PC=%p) size %d\n",
+		function, __builtin_return_address(0), size);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov