patch-1.3.48 linux/arch/mips/kernel/traps.c

Next file: linux/arch/mips/kernel/tyne-c.c
Previous file: linux/arch/mips/kernel/tlb.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.47/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
@@ -1,7 +1,9 @@
 /*
  *  arch/mips/kernel/traps.c
  *
- *  Copyright (C) 1991, 1992  Linus Torvalds
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
  */
 
 /*
@@ -9,17 +11,23 @@
  * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
  * to mainly kill the offending process (probably by giving it a signal,
  * but possibly by killing it outright if necessary).
+ *
+ * FIXME: This is the place for a fpu emulator.
  */
 #include <linux/head.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/string.h>
+#include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/ptrace.h>
-#include <linux/config.h>
 #include <linux/timer.h>
+#include <linux/mm.h>
 
+#include <asm/vector.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/io.h>
@@ -32,18 +40,15 @@
 	console_loglevel = 15;
 }
 
-#define get_seg_byte(seg,addr) ({ \
-register unsigned char __res; \
-__res = get_user_byte(addr); \
-__res;})
-
-#define get_seg_long(seg,addr) ({ \
-register unsigned long __res; \
-__res = get_user_word(addr); \
-__res;})
-
-extern asmlinkage void deskstation_tyne_handle_int(void);
+/*
+ * Machine specific interrupt handlers
+ */
 extern asmlinkage void acer_pica_61_handle_int(void);
+extern asmlinkage void decstation_handle_int(void);
+extern asmlinkage void deskstation_rpc44_handle_int(void);
+extern asmlinkage void deskstation_tyne_handle_int(void);
+extern asmlinkage void mips_magnum_4000_handle_int(void);
+
 extern asmlinkage void handle_mod(void);
 extern asmlinkage void handle_tlbl(void);
 extern asmlinkage void handle_tlbs(void);
@@ -63,90 +68,60 @@
 extern asmlinkage void handle_watch(void);
 extern asmlinkage void handle_reserved(void);
 
-char *cpu_names[] = CPU_NAMES;
+static char *cpu_names[] = CPU_NAMES;
+
+unsigned long page_colour_mask;
 
 int kstack_depth_to_print = 24;
 
 /*
- * 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.
+ * These constant is for searching for possible module text segments.
+ * 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)
 
 void die_if_kernel(char * str, struct pt_regs * regs, long err)
 {
-	int i;
-	unsigned long *sp, *pc;
-	unsigned long *stack, addr, module_start, module_end;
-	extern char start_kernel, etext;
+	int	i;
+	int	*stack;
+	u32	*sp, *pc, addr, module_start, module_end;
+	extern	char start_kernel, _etext;
 
 	if ((regs->cp0_status & (ST0_ERL|ST0_EXL)) == 0)
 		return;
 
-	sp = (unsigned long *)regs->reg29;
-	pc = (unsigned long *)regs->cp0_epc;
+	sp = (u32 *)regs->reg29;
+	pc = (u32 *)regs->cp0_epc;
 
 	console_verbose();
 	printk("%s: %08lx\n", str, err );
 
-	/*
-	 * Saved main processor registers
-	 */
-	printk("at   : %08lx\n", regs->reg1);
-	printk("v0   : %08lx %08lx\n", regs->reg2, regs->reg3);
-	printk("a0   : %08lx %08lx %08lx %08lx\n",
-	       regs->reg4, regs->reg5, regs->reg6, regs->reg7);
-	printk("t0   : %08lx %08lx %08lx %08lx %08lx\n",
-	       regs->reg8, regs->reg9, regs->reg10, regs->reg11, regs->reg12);
-	printk("t5   : %08lx %08lx %08lx %08lx %08lx\n",
-	       regs->reg13, regs->reg14, regs->reg15, regs->reg24, regs->reg25);
-	printk("s0   : %08lx %08lx %08lx %08lx\n",
-	       regs->reg16, regs->reg17, regs->reg18, regs->reg19);
-	printk("s4   : %08lx %08lx %08lx %08lx\n",
-	       regs->reg20, regs->reg21, regs->reg22, regs->reg23);
-	printk("gp   : %08lx\n", regs->reg28);
-	printk("sp   : %08lx\n", regs->reg29);
-	printk("fp/s8: %08lx\n", regs->reg30);
-	printk("ra   : %08lx\n", regs->reg31);
-
-	/*
-	 * Saved cp0 registers
-	 */
-	printk("epc  : %08lx\nStatus: %08lx\nCause : %08lx\n",
-	       regs->cp0_epc, regs->cp0_status, regs->cp0_cause);
-
-	/*
-	 * Some goodies...
-	 */
-	printk("Int  : %ld\n", regs->interrupt);
+	show_regs(regs);
 
 	/*
 	 * Dump the stack
 	 */
-	if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page)
+	if (STACK_MAGIC != *(u32 *)current->kernel_stack_page)
 		printk("Corrupted stack page\n");
-	printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ",
-		current->comm, current->pid, 0xffff & i,
-	        current->kernel_stack_page);
+	printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ",
+		current->comm, current->pid, current->kernel_stack_page);
 	for(i=0;i<5;i++)
-		printk("%08lx ", *sp++);
-	stack = (unsigned long *) sp;
+		printk("%08x ", *sp++);
+	stack = (int *) sp;
 	for(i=0; i < kstack_depth_to_print; i++) {
-		if (((long) stack & 4095) == 0)
+		if (((u32) stack & (PAGE_SIZE -1)) == 0)
 			break;
 		if (i && ((i % 8) == 0))
 			printk("\n       ");
-		printk("%08lx ", get_seg_long(ss,stack++));
+		printk("%08lx ", get_user(stack++));
 	}
 	printk("\nCall Trace: ");
-	stack = (unsigned long *) sp;
+	stack = (int *)sp;
 	i = 1;
-	module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
+	module_start = VMALLOC_START;
 	module_end = module_start + MODULE_RANGE;
-	while (((long) stack & 4095) != 0) {
-		addr = get_seg_long(ss, stack++);
+	while (((u32)stack & (PAGE_SIZE -1)) != 0) {
+		addr = get_user(stack++);
 		/*
 		 * If the address is either in the text segment of the
 		 * kernel, or in the region which contains vmalloc'ed
@@ -155,103 +130,159 @@
 		 * down the cause of the crash will be able to figure
 		 * out the call path that was taken.
 		 */
-		if (((addr >= (unsigned long) &start_kernel) &&
-		     (addr <= (unsigned long) &etext)) ||
+		if (((addr >= (u32) &start_kernel) &&
+		     (addr <= (u32) &_etext)) ||
 		    ((addr >= module_start) && (addr <= module_end))) {
 			if (i && ((i % 8) == 0))
 				printk("\n       ");
-			printk("%08lx ", addr);
+			printk("%08x ", addr);
 			i++;
 		}
 	}
 
 	printk("\nCode : ");
-	for(i=0;i<5;i++)
-		printk("%08lx ", *pc++);
-	printk("\n");
+	if ((!verify_area(VERIFY_READ, pc, 5 * sizeof(*pc)) ||
+	     KSEGX(pc) == KSEG0 ||
+	     KSEGX(pc) == KSEG1) &&
+	    (((unsigned long) pc & 3) == 0))
+	{
+		for(i=0;i<5;i++)
+			printk("%08x ", *pc++);
+		printk("\n");
+	}
+	else
+		printk("(Bad address in epc)\n");
+while(1);
 	do_exit(SIGSEGV);
 }
 
+static void
+fix_ade(struct pt_regs *regs, int write)
+{
+	printk("Received address error (ade%c)\n", write ? 's' : 'l');
+	panic("Fixing address errors not implemented yet");
+}
+
 void do_adel(struct pt_regs *regs)
 {
+	if(current->tss.mflags & MF_FIXADE)
+	{
+		fix_ade(regs, 0);
+		return;
+	}
+	show_regs(regs);
+while(1);
+	dump_tlb_nonwired();
 	send_sig(SIGSEGV, current, 1);
 }
 
 void do_ades(struct pt_regs *regs)
 {
+	unsigned long	pc = regs->cp0_epc;
+	int	i;
+
+	if(current->tss.mflags & MF_FIXADE)
+	{
+		fix_ade(regs, 1);
+		return;
+	}
+while(1);
+	for(i=0; i<NR_TASKS;i++)
+		if(task[i] && task[i]->pid >= 2)
+		{
+			printk("Process %d\n", task[i]->pid);
+			dump_list_process(task[i], pc);
+		}
+	show_regs(regs);
+	dump_tlb_nonwired();
 	send_sig(SIGSEGV, current, 1);
 }
 
+/*
+ * The ibe/dbe exceptions are signaled by onboard hardware and should get
+ * a board specific handlers to get maximum available information. Bus
+ * errors are always symptom of hardware malfunction or a kernel error.
+ *
+ * FIXME: Linux/68k sends a SIGSEGV for a buserror which seems to be wrong.
+ * This is certainly wrong. Actually, all hardware errors (ades,adel,ibe,dbe)
+ * are bus errors and therefor should send a SIGBUS! (Andy)
+ */
 void do_ibe(struct pt_regs *regs)
 {
-	send_sig(SIGSEGV, current, 1);
+show_regs(regs);
+while(1);
+	send_sig(SIGBUS, current, 1);
 }
 
 void do_dbe(struct pt_regs *regs)
 {
-	send_sig(SIGSEGV, current, 1);
+show_regs(regs);
+while(1);
+	send_sig(SIGBUS, current, 1);
 }
 
 void do_ov(struct pt_regs *regs)
 {
+show_regs(regs);
+while(1);
 	send_sig(SIGFPE, current, 1);
 }
 
 void do_fpe(struct pt_regs *regs)
 {
-	/*
-	 * FIXME: This is the place for a fpu emulator. Not written
-	 * yet and the demand seems to be quite low.
-	 */
-	printk("Caught FPE exception at %lx.\n", regs->cp0_epc);
+show_regs(regs);
+while(1);
 	send_sig(SIGFPE, current, 1);
 }
 
 void do_bp(struct pt_regs *regs)
 {
+show_regs(regs);
+while(1);
 	send_sig(SIGILL, current, 1);
 }
 
 void do_tr(struct pt_regs *regs)
 {
+show_regs(regs);
+while(1);
 	send_sig(SIGILL, current, 1);
 }
 
 void do_ri(struct pt_regs *regs)
 {
+	int	i;
+
+	for(i=0; i<NR_TASKS;i++)
+		if(task[i] && task[i]->pid >= 2)
+		{
+			printk("Process %d\n", task[i]->pid);
+			dump_list_process(task[i], 0x7ffff000);
+		}
+	show_regs(regs);
+while(1);
 	send_sig(SIGILL, current, 1);
 }
 
 void do_cpu(struct pt_regs *regs)
 {
-	unsigned long pc;
-	unsigned int insn;
+	unsigned int cpid;
 
-	/*
-	 * Check whether this was a cp1 instruction
-	 */
-	pc = regs->cp0_epc;
-	if (regs->cp0_cause & (1<<31))
-		pc += 4;
-	insn = *(unsigned int *)pc;
-	insn &= 0xfc000000;
-	switch(insn) {
-		case 0x44000000:
-		case 0xc4000000:
-		case 0xe4000000:
-			printk("CP1 instruction - enabling cp1.\n");
-			regs->cp0_status |= ST0_CU1;
-			/*
-			 * No need to handle branch delay slots
-			 */
-			break;
-		default:
-			/*
-			 * This wasn't a cp1 instruction and therefore illegal.
-			 * Default is to kill the process.
-			 */
-			send_sig(SIGILL, current, 1);
-		}
+	cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
+	switch(cpid)
+	{
+	case 1:
+		regs->cp0_status |= ST0_CU1;
+		break;
+	case 0:
+		/*
+		 * CPU for cp0 can only happen in user mode
+		 */
+	case 2:
+	case 3:
+		send_sig(SIGILL, current, 1);
+		break;
+	}
 }
 
 void do_vcei(struct pt_regs *regs)
@@ -274,10 +305,6 @@
 
 void do_watch(struct pt_regs *regs)
 {
-	/*
-	 * Only possible on R4[04]00. No way to handle this because
-	 * I don't have such a cpu.
-	 */
 	panic("Caught WATCH exception - can't handle yet\n");
 }
 
@@ -285,7 +312,7 @@
 {
 	/*
 	 * Game over - no way to handle this if it ever occurs.
-	 * Most probably caused by a new unknown cpu type or a
+	 * Most probably caused by a new unknown cpu type or
 	 * after another deadly hard/software error.
 	 */
 	panic("Caught reserved exception - can't handle.\n");
@@ -293,12 +320,11 @@
 
 void trap_init(void)
 {
-	int	i;
+	unsigned long	i;
+	void watch_set(unsigned long, unsigned long);
 
-	/*
-	 * FIXME: Mips Magnum R4000 has an EISA bus!
-	 */
-	EISA_bus = 0;
+	if(boot_info.machtype == MACH_MIPS_MAGNUM_4000)
+		EISA_bus = 1;
 
 	/*
 	 * Setup default vectors
@@ -310,30 +336,49 @@
 	 * Handling the following exceptions depends mostly of the cpu type
 	 */
 	switch(boot_info.cputype) {
+	/*
+	 * The R10000 is in most aspects similar to the R4400.  It however
+	 * should get some special optimizations.
+	 */
+	case CPU_R10000:
+		write_32bit_cp0_register(CP0_FRAMEMASK, 0);
+		set_cp0_status(ST0_XX, ST0_XX);
+		page_colour_mask = 0x3000;
+		panic("CPU too expensive - making holiday in the ANDES!");
+		break;
 	case CPU_R4000MC:
 	case CPU_R4400MC:
 	case CPU_R4000SC:
 	case CPU_R4400SC:
 		/*
-		 * Handlers not implemented yet
+		 * Handlers not implemented yet.  If should ever be used -
+		 * otherwise it's a bug in the Linux/MIPS kernel, anyway.
 		 */
 		set_except_vector(14, handle_vcei);
 		set_except_vector(31, handle_vced);
 	case CPU_R4000PC:
 	case CPU_R4400PC:
+	case CPU_R4200:
+     /* case CPU_R4300: */
 		/*
-		 * Handler not implemented yet
+		 * Use watch exception to trap on access to address zero
 		 */
 		set_except_vector(23, handle_watch);
-	case CPU_R4200:
+		watch_set(KSEG0, 3);
 	case CPU_R4600:
 		set_except_vector(1, handle_mod);
 		set_except_vector(2, handle_tlbl);
 		set_except_vector(3, handle_tlbs);
 		set_except_vector(4, handle_adel);
 		set_except_vector(5, handle_ades);
+		/*
+		 * The following two are signaled by onboard hardware and
+		 * should get board specific handlers to get maximum
+		 * available information.
+		 */
 		set_except_vector(6, handle_ibe);
 		set_except_vector(7, handle_dbe);
+
 		set_except_vector(8, handle_sys);
 		set_except_vector(9, handle_bp);
 		set_except_vector(10, handle_ri);
@@ -341,6 +386,15 @@
 		set_except_vector(12, handle_ov);
 		set_except_vector(13, handle_tr);
 		set_except_vector(15, handle_fpe);
+
+		/*
+		 * Compute mask for page_colour().  This is based on the
+		 * size of the data cache.  Does the size of the icache
+		 * need to be accounted for?
+		 */
+		i = read_32bit_cp0_register(CP0_CONFIG);
+		i = (i >> 26) & 7;
+		page_colour_mask = 1 << (12 + i);
 		break;
 	case CPU_R2000:
 	case CPU_R3000:
@@ -353,27 +407,18 @@
 	case CPU_R6000:
 	case CPU_R6000A:
 	case CPU_R8000:
-	case CPU_R10000:
 		printk("Detected unsupported CPU type %s.\n",
-		       cpu_names[boot_info.cputype]);
+			cpu_names[boot_info.cputype]);
 		panic("Can't handle CPU\n");
 		break;
+
 	case CPU_UNKNOWN:
 	default:
-		panic("Unknown type of CPU");
-		}
+		panic("Unknown CPU type");
+	}
 
 	/*
-	 * The interrupt handler depends of both type of the board and cpu
+	 * The interrupt handler mostly depends of the board type.
 	 */
-	switch(boot_info.machtype) {
-	case MACH_DESKSTATION_TYNE:
-		set_except_vector(0, deskstation_tyne_handle_int);
-		break;
-	case MACH_ACER_PICA_61:
-		set_except_vector(0, acer_pica_61_handle_int);
-		break;
-	default:
-		panic("Unknown machine type");
-		}
+	set_except_vector(0, feature->handle_int);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this